3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
7 * Copyright (C) 1992 - 1997, 2000-2002 Silicon Graphics, Inc. All rights reserved.
10 /* Implementation of Address/Length Lists. */
13 #include <linux/types.h>
14 #include <linux/slab.h>
15 #include <linux/mmzone.h>
16 #include <asm/sn/sgi.h>
17 #include <asm/sn/alenlist.h>
20 * Logically, an Address/Length List is a list of Pairs, where each pair
21 * holds an Address and a Length, all in some Address Space. In this
22 * context, "Address Space" is a particular Crosstalk Widget address
23 * space, a PCI device address space, a VME bus address space, a
24 * physical memory address space, etc.
26 * The main use for these Lists is to provide a single mechanism that
27 * describes where in an address space a DMA occurs. This allows the
28 * various I/O Bus support layers to provide a single interface for
29 * DMA mapping and DMA translation without regard to how the DMA target
30 * was specified by upper layers. The upper layers commonly specify a
31 * DMA target via a buf structure page list, a kernel virtual address,
32 * a user virtual address, a vector of addresses (a la uio and iov),
33 * or possibly a pfn list.
35 * Address/Length Lists also enable drivers to take advantage of their
36 * inate scatter/gather capabilities in systems where some address
37 * translation may be required between bus adapters. The driver forms
38 * a List that represents physical memory targets. This list is passed
39 * to the various adapters, which apply various translations. The final
40 * list that's returned to the driver is in terms of its local address
41 * address space -- addresses which can be passed off to a scatter/gather
42 * capable DMA controller.
44 * The current implementation is intended to be useful both in kernels
45 * that support interrupt threads (INTR_KTHREAD) and in systems that do
46 * not support interrupt threads. Of course, in the latter case, some
47 * interfaces can be called only within a suspendable context.
49 * Basic operations on Address/Length Lists include:
50 * alenlist_create Create a list
51 * alenlist_clear Clear a list
52 * alenlist_destroy Destroy a list
53 * alenlist_append Append a Pair to the end of a list
54 * alenlist_replace Replace a Pair in the middle of a list
55 * alenlist_get Get an Address/Length Pair from a list
56 * alenlist_size Return the number of Pairs in a list
57 * alenlist_concat Append one list to the end of another
58 * alenlist_clone Create a new copy of a list
60 * Operations that convert from upper-level specifications to Address/
61 * Length Lists currently include:
62 * kvaddr_to_alenlist Convert from a kernel virtual address
63 * uvaddr_to_alenlist Convert from a user virtual address
64 * buf_to_alenlist Convert from a buf structure
65 * alenlist_done Tell system that we're done with an alenlist
66 * obtained from a conversion.
67 * Additional convenience operations:
68 * alenpair_init Create a list and initialize it with a Pair
69 * alenpair_get Peek at the first pair on a List
71 * A supporting type for Address/Length Lists is an alenlist_cursor_t. A
72 * cursor marks a position in a List, and determines which Pair is fetched
74 * alenlist_cursor_create Allocate and initialize a cursor
75 * alenlist_cursor_destroy Free space consumed by a cursor
76 * alenlist_cursor_init (Re-)Initialize a cursor to point
77 * to the start of a list
78 * alenlist_cursor_clone Clone a cursor (at the current offset)
79 * alenlist_cursor_offset Return the number of bytes into
80 * a list that this cursor marks
81 * Multiple cursors can point at various points into a List. Also, each
82 * list maintains one "internal cursor" which may be updated by alenlist_clear
83 * and alenlist_get. If calling code simply wishes to scan sequentially
84 * through a list starting at the beginning, and if it is the only user of
85 * a list, it can rely on this internal cursor rather than managing a
86 * separate explicit cursor.
88 * The current implementation allows callers to allocate both cursors and
89 * the lists as local stack (structure) variables. This allows for some
90 * extra efficiency at the expense of forward binary compatibility. It
91 * is recommended that customer drivers refrain from local allocation.
92 * In fact, we likely will choose to move the structures out of the public
93 * header file into a private place in order to discourage this usage.
95 * Currently, no locking is provided by the alenlist implementation.
97 * Implementation notes:
98 * For efficiency, Pairs are grouped into "chunks" of, say, 32 Pairs
99 * and a List consists of some number of these chunks. Chunks are completely
100 * invisible to calling code. Chunks should be large enough to hold most
101 * standard-sized DMA's, but not so large that they consume excessive space.
103 * It is generally expected that Lists will be constructed at one time and
104 * scanned at a later time. It is NOT expected that drivers will scan
105 * a List while the List is simultaneously extended, although this is
106 * theoretically possible with sufficient upper-level locking.
108 * In order to support demands of Real-Time drivers and in order to support
109 * swapping under low-memory conditions, we support the concept of a
110 * "pre-allocated fixed-sized List". After creating a List with
111 * alenlist_create, a driver may explicitly grow the list (via "alenlist_grow")
112 * to a specific number of Address/Length pairs. It is guaranteed that future
113 * operations involving this list will never automatically grow the list
114 * (i.e. if growth is ever required, the operation will fail). Additionally,
115 * operations that use alenlist's (e.g. DMA operations) accept a flag which
116 * causes processing to take place "in-situ"; that is, the input alenlist
117 * entries are replaced with output alenlist entries. The combination of
118 * pre-allocated Lists and in-situ processing allows us to avoid the
119 * potential deadlock scenario where we sleep (waiting for memory) in the
122 * For debugging, we track the number of allocated Lists in alenlist_count
123 * the number of allocated chunks in alenlist_chunk_count, and the number
124 * of allocate cursors in alenlist_cursor_count. We also provide a debug
125 * routine, alenlist_show, which dumps the contents of an Address/Length List.
127 * Currently, Lists are formed by drivers on-demand. Eventually, we may
128 * associate an alenlist with a buf structure and keep it up to date as
129 * we go along. In that case, buf_to_alenlist simply returns a pointer
130 * to the existing List, and increments the Lists's reference count.
131 * alenlist_done would decrement the reference count and destroys the List
132 * if it was the last reference.
134 * Eventually alenlist's may allow better support for user-level scatter/
135 * gather operations (e.g. via readv/writev): With proper support, we
136 * could potentially handle a vector of reads with a single scatter/gather
137 * DMA operation. This could be especially useful on NUMA systems where
138 * there's more of a reason for users to use vector I/O operations.
140 * Eventually, alenlist's may replace kaio lists, vhand page lists,
141 * buffer cache pfdat lists, DMA page lists, etc.
144 /* Opaque data types */
146 /* An Address/Length pair. */
147 typedef struct alen_s {
153 * Number of elements in one chunk of an Address/Length List.
155 * This size should be sufficient to hold at least an "average" size
156 * DMA request. Must be at least 1, and should be a power of 2,
159 #define ALEN_CHUNK_SZ ((512*1024)/NBPP)
162 * A fixed-size set of Address/Length Pairs. Chunks of Pairs are strung together
163 * to form a complete Address/Length List. Chunking is entirely hidden within the
164 * alenlist implementation, and it simply makes allocation and growth of lists more
167 typedef struct alenlist_chunk_s {
168 alen_t alc_pair[ALEN_CHUNK_SZ];/* list of addr/len pairs */
169 struct alenlist_chunk_s *alc_next; /* point to next chunk of pairs */
173 * An Address/Length List. An Address/Length List is allocated with alenlist_create.
174 * Alternatively, a list can be allocated on the stack (local variable of type
175 * alenlist_t) and initialized with alenpair_init or with a combination of
176 * alenlist_clear and alenlist_append, etc. Code which statically allocates these
177 * structures loses forward binary compatibility!
179 * A statically allocated List is sufficiently large to hold ALEN_CHUNK_SZ pairs.
182 unsigned short al_flags;
183 unsigned short al_logical_size; /* logical size of list, in pairs */
184 unsigned short al_actual_size; /* actual size of list, in pairs */
185 struct alenlist_chunk_s *al_last_chunk; /* pointer to last logical chunk */
186 struct alenlist_cursor_s al_cursor; /* internal cursor */
187 struct alenlist_chunk_s al_chunk; /* initial set of pairs */
188 alenaddr_t al_compaction_address; /* used to compact pairs */
192 #define AL_FIXED_SIZE 0x1 /* List is pre-allocated, and of fixed size */
195 struct zone *alenlist_zone = NULL;
196 struct zone *alenlist_chunk_zone = NULL;
197 struct zone *alenlist_cursor_zone = NULL;
200 int alenlist_count=0; /* Currently allocated Lists */
201 int alenlist_chunk_count = 0; /* Currently allocated chunks */
202 int alenlist_cursor_count = 0; /* Currently allocate cursors */
203 #define INCR_COUNT(ptr) atomic_inc((ptr));
204 #define DECR_COUNT(ptr) atomic_dec((ptr));
206 #define INCR_COUNT(ptr)
207 #define DECR_COUNT(ptr)
211 static void alenlist_show(alenlist_t);
215 * Initialize Address/Length List management. One time initialization.
220 alenlist_zone = snia_kmem_zone_init(sizeof(struct alenlist_s), "alenlist");
221 alenlist_chunk_zone = snia_kmem_zone_init(sizeof(struct alenlist_chunk_s), "alchunk");
222 alenlist_cursor_zone = snia_kmem_zone_init(sizeof(struct alenlist_cursor_s), "alcursor");
224 idbg_addfunc("alenshow", alenlist_show);
230 * Initialize an Address/Length List cursor.
233 do_cursor_init(alenlist_t alenlist, alenlist_cursor_t cursorp)
235 cursorp->al_alenlist = alenlist;
236 cursorp->al_offset = 0;
237 cursorp->al_chunk = &alenlist->al_chunk;
238 cursorp->al_index = 0;
239 cursorp->al_bcount = 0;
244 * Create an Address/Length List, and clear it.
245 * Set the cursor to the beginning.
248 alenlist_create(unsigned flags)
252 alenlist = snia_kmem_zone_alloc(alenlist_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
254 INCR_COUNT(&alenlist_count);
256 alenlist->al_flags = 0;
257 alenlist->al_logical_size = 0;
258 alenlist->al_actual_size = ALEN_CHUNK_SZ;
259 alenlist->al_last_chunk = &alenlist->al_chunk;
260 alenlist->al_chunk.alc_next = NULL;
261 do_cursor_init(alenlist, &alenlist->al_cursor);
269 * Grow an Address/Length List so that all resources needed to contain
270 * the specified number of Pairs are pre-allocated. An Address/Length
271 * List that has been explicitly "grown" will never *automatically*
272 * grow, shrink, or be destroyed.
274 * Pre-allocation is useful for Real-Time drivers and for drivers that
275 * may be used along the swap-out path and therefore cannot afford to
276 * sleep until memory is freed.
278 * The cursor is set to the beginning of the list.
281 alenlist_grow(alenlist_t alenlist, size_t npairs)
284 * This interface should be used relatively rarely, so
285 * the implementation is kept simple: We clear the List,
286 * then append npairs bogus entries. Finally, we mark
287 * the list as FIXED_SIZE and re-initialize the internal
292 * Temporarily mark as non-fixed size, since we're about
293 * to shrink and expand it.
295 alenlist->al_flags &= ~AL_FIXED_SIZE;
297 /* Free whatever was in the alenlist. */
298 alenlist_clear(alenlist);
300 /* Allocate everything that we need via automatic expansion. */
302 if (alenlist_append(alenlist, 0, 0, AL_NOCOMPACT) == ALENLIST_FAILURE)
303 return(ALENLIST_FAILURE);
305 /* Now, mark as FIXED_SIZE */
306 alenlist->al_flags |= AL_FIXED_SIZE;
308 /* Clear out bogus entries */
309 alenlist_clear(alenlist);
311 /* Initialize internal cursor to the beginning */
312 do_cursor_init(alenlist, &alenlist->al_cursor);
314 return(ALENLIST_SUCCESS);
319 * Clear an Address/Length List so that it holds no pairs.
322 alenlist_clear(alenlist_t alenlist)
324 alenlist_chunk_t chunk, freechunk;
327 * If this List is not FIXED_SIZE, free all the
330 if (!(alenlist->al_flags & AL_FIXED_SIZE)) {
331 /* First, free any extension alenlist chunks */
332 chunk = alenlist->al_chunk.alc_next;
335 chunk = chunk->alc_next;
336 snia_kmem_zone_free(alenlist_chunk_zone, freechunk);
337 DECR_COUNT(&alenlist_chunk_count);
339 alenlist->al_actual_size = ALEN_CHUNK_SZ;
340 alenlist->al_chunk.alc_next = NULL;
343 alenlist->al_logical_size = 0;
344 alenlist->al_last_chunk = &alenlist->al_chunk;
345 do_cursor_init(alenlist, &alenlist->al_cursor);
350 * Create and initialize an Address/Length Pair.
351 * This is intended for degenerate lists, consisting of a single
352 * address/length pair.
355 alenpair_init( alenaddr_t address,
360 alenlist = alenlist_create(0);
362 alenlist->al_logical_size = 1;
363 ASSERT(alenlist->al_last_chunk == &alenlist->al_chunk);
364 alenlist->al_chunk.alc_pair[0].al_length = length;
365 alenlist->al_chunk.alc_pair[0].al_addr = address;
371 * Return address/length from a degenerate (1-pair) List, or
372 * first pair from a larger list. Does NOT update the internal cursor,
373 * so this is an easy way to peek at a start address.
376 alenpair_get( alenlist_t alenlist,
380 if (alenlist->al_logical_size == 0)
381 return(ALENLIST_FAILURE);
383 *length = alenlist->al_chunk.alc_pair[0].al_length;
384 *address = alenlist->al_chunk.alc_pair[0].al_addr;
385 return(ALENLIST_SUCCESS);
390 * Destroy an Address/Length List.
393 alenlist_destroy(alenlist_t alenlist)
395 if (alenlist == NULL)
399 * Turn off FIXED_SIZE so this List can be
400 * automatically shrunk.
402 alenlist->al_flags &= ~AL_FIXED_SIZE;
404 /* Free extension chunks first */
405 if (alenlist->al_chunk.alc_next)
406 alenlist_clear(alenlist);
408 /* Now, free the alenlist itself */
409 snia_kmem_zone_free(alenlist_zone, alenlist);
410 DECR_COUNT(&alenlist_count);
414 * Release an Address/Length List.
415 * This is in preparation for a day when alenlist's may be longer-lived, and
416 * perhaps associated with a buf structure. We'd add a reference count, and
417 * this routine would decrement the count. For now, we create alenlist's on
418 * on demand and free them when done. If the driver is not explicitly managing
419 * a List for its own use, it should call alenlist_done rather than alenlist_destroy.
422 alenlist_done(alenlist_t alenlist)
424 alenlist_destroy(alenlist);
429 * Append another address/length to the end of an Address/Length List,
430 * growing the list if permitted and necessary.
432 * Returns: SUCCESS/FAILURE
435 alenlist_append( alenlist_t alenlist, /* append to this list */
436 alenaddr_t address, /* address to append */
437 size_t length, /* length to append */
441 int index, last_index;
443 index = alenlist->al_logical_size % ALEN_CHUNK_SZ;
445 if ((alenlist->al_logical_size > 0)) {
447 * See if we can compact this new pair in with the previous entry.
448 * al_compaction_address holds that value that we'd need to see
449 * in order to compact.
451 if (!(flags & AL_NOCOMPACT) &&
452 (alenlist->al_compaction_address == address)) {
453 last_index = (alenlist->al_logical_size-1) % ALEN_CHUNK_SZ;
454 alenp = &(alenlist->al_last_chunk->alc_pair[last_index]);
455 alenp->al_length += length;
456 alenlist->al_compaction_address += length;
457 return(ALENLIST_SUCCESS);
461 * If we're out of room in this chunk, move to a new chunk.
464 if (alenlist->al_flags & AL_FIXED_SIZE) {
465 alenlist->al_last_chunk = alenlist->al_last_chunk->alc_next;
467 /* If we're out of space in a FIXED_SIZE List, quit. */
468 if (alenlist->al_last_chunk == NULL) {
469 ASSERT(alenlist->al_logical_size == alenlist->al_actual_size);
470 return(ALENLIST_FAILURE);
473 alenlist_chunk_t new_chunk;
475 new_chunk = snia_kmem_zone_alloc(alenlist_chunk_zone,
476 flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
478 if (new_chunk == NULL)
479 return(ALENLIST_FAILURE);
481 alenlist->al_last_chunk->alc_next = new_chunk;
482 new_chunk->alc_next = NULL;
483 alenlist->al_last_chunk = new_chunk;
484 alenlist->al_actual_size += ALEN_CHUNK_SZ;
485 INCR_COUNT(&alenlist_chunk_count);
490 alenp = &(alenlist->al_last_chunk->alc_pair[index]);
491 alenp->al_addr = address;
492 alenp->al_length = length;
494 alenlist->al_logical_size++;
495 alenlist->al_compaction_address = address + length;
497 return(ALENLIST_SUCCESS);
502 * Replace an item in an Address/Length List. Cursor is updated so
503 * that alenlist_get will get the next item in the list. This interface
504 * is not very useful for drivers; but it is useful to bus providers
505 * that need to translate between address spaced in situ. The old Address
506 * and Length are returned.
510 alenlist_replace( alenlist_t alenlist, /* in: replace in this list */
511 alenlist_cursor_t cursorp, /* inout: which item to replace */
512 alenaddr_t *addrp, /* inout: address */
513 size_t *lengthp, /* inout: length */
517 alenlist_chunk_t chunk;
522 if ((addrp == NULL) || (lengthp == NULL))
523 return(ALENLIST_FAILURE);
525 if (alenlist->al_logical_size == 0)
526 return(ALENLIST_FAILURE);
532 * If no cursor explicitly specified, use the Address/Length List's
536 cursorp = &alenlist->al_cursor;
538 chunk = cursorp->al_chunk;
539 index = cursorp->al_index;
541 ASSERT(cursorp->al_alenlist == alenlist);
542 if (cursorp->al_alenlist != alenlist)
543 return(ALENLIST_FAILURE);
545 alenp = &chunk->alc_pair[index];
547 /* Return old values */
548 *addrp = alenp->al_length;
549 *lengthp = alenp->al_addr;
551 /* Set up new values */
552 alenp->al_length = length;
553 alenp->al_addr = addr;
555 /* Update cursor to point to next item */
556 cursorp->al_bcount = length;
558 return(ALENLIST_SUCCESS);
563 * Initialize a cursor in order to walk an alenlist.
564 * An alenlist_cursor always points to the last thing that was obtained
565 * from the list. If al_chunk is NULL, then nothing has yet been obtained.
567 * Note: There is an "internal cursor" associated with every Address/Length List.
568 * For users that scan sequentially through a List, it is more efficient to
569 * simply use the internal cursor. The caller must insure that no other users
570 * will simultaneously scan the List. The caller can reposition the internal
571 * cursor by calling alenlist_cursor_init with a NULL cursorp.
574 alenlist_cursor_init(alenlist_t alenlist, size_t offset, alenlist_cursor_t cursorp)
579 cursorp = &alenlist->al_cursor;
581 /* Get internal cursor's byte count for use as a hint.
583 * If the internal cursor points passed the point that we're interested in,
584 * we need to seek forward from the beginning. Otherwise, we can seek forward
585 * from the internal cursor.
588 ((byte_count = alenlist_cursor_offset(alenlist, (alenlist_cursor_t)NULL)) <= offset)) {
589 offset -= byte_count;
590 alenlist_cursor_clone(alenlist, NULL, cursorp);
592 do_cursor_init(alenlist, cursorp);
594 /* We could easily speed this up, but it shouldn't be used very often. */
595 while (offset != 0) {
599 if (alenlist_get(alenlist, cursorp, offset, &addr, &length, 0) != ALENLIST_SUCCESS)
600 return(ALENLIST_FAILURE);
603 return(ALENLIST_SUCCESS);
608 * Copy a cursor. The source cursor is either an internal alenlist cursor
609 * or an explicit cursor.
612 alenlist_cursor_clone( alenlist_t alenlist,
613 alenlist_cursor_t cursorp_in,
614 alenlist_cursor_t cursorp_out)
618 if (alenlist && cursorp_in)
619 if (alenlist != cursorp_in->al_alenlist)
620 return(ALENLIST_FAILURE);
623 *cursorp_out = alenlist->al_cursor; /* small structure copy */
625 *cursorp_out = *cursorp_in; /* small structure copy */
627 return(ALENLIST_FAILURE); /* no source */
629 return(ALENLIST_SUCCESS);
633 * Return the number of bytes passed so far according to the specified cursor.
634 * If cursorp is NULL, use the alenlist's internal cursor.
637 alenlist_cursor_offset(alenlist_t alenlist, alenlist_cursor_t cursorp)
639 ASSERT(!alenlist || !cursorp || (alenlist == cursorp->al_alenlist));
641 if (cursorp == NULL) {
643 cursorp = &alenlist->al_cursor;
646 return(cursorp->al_offset);
650 * Allocate and initialize an Address/Length List cursor.
653 alenlist_cursor_create(alenlist_t alenlist, unsigned flags)
655 alenlist_cursor_t cursorp;
657 ASSERT(alenlist != NULL);
658 cursorp = snia_kmem_zone_alloc(alenlist_cursor_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
660 INCR_COUNT(&alenlist_cursor_count);
661 alenlist_cursor_init(alenlist, 0, cursorp);
667 * Free an Address/Length List cursor.
670 alenlist_cursor_destroy(alenlist_cursor_t cursorp)
672 DECR_COUNT(&alenlist_cursor_count);
673 snia_kmem_zone_free(alenlist_cursor_zone, cursorp);
678 * Fetch an address/length pair from an Address/Length List. Update
679 * the "cursor" so that next time this routine is called, we'll get
680 * the next address range. Never return a length that exceeds maxlength
681 * (if non-zero). If maxlength is a power of 2, never return a length
682 * that crosses a maxlength boundary. [This may seem strange at first,
683 * but it's what many drivers want.]
685 * Returns: SUCCESS/FAILURE
688 alenlist_get( alenlist_t alenlist, /* in: get from this list */
689 alenlist_cursor_t cursorp, /* inout: which item to get */
690 size_t maxlength, /* in: at most this length */
691 alenaddr_t *addrp, /* out: address */
692 size_t *lengthp, /* out: length */
696 alenlist_chunk_t chunk;
702 * If no cursor explicitly specified, use the Address/Length List's
705 if (cursorp == NULL) {
706 if (alenlist->al_logical_size == 0)
707 return(ALENLIST_FAILURE);
708 cursorp = &alenlist->al_cursor;
711 chunk = cursorp->al_chunk;
712 index = cursorp->al_index;
713 bcount = cursorp->al_bcount;
715 ASSERT(cursorp->al_alenlist == alenlist);
716 if (cursorp->al_alenlist != alenlist)
717 return(ALENLIST_FAILURE);
719 alenp = &chunk->alc_pair[index];
720 length = alenp->al_length - bcount;
722 /* Bump up to next pair, if we're done with this pair. */
724 cursorp->al_bcount = bcount = 0;
725 cursorp->al_index = index = (index + 1) % ALEN_CHUNK_SZ;
727 /* Bump up to next chunk, if we're done with this chunk. */
729 if (cursorp->al_chunk == alenlist->al_last_chunk)
730 return(ALENLIST_FAILURE);
731 chunk = chunk->alc_next;
732 ASSERT(chunk != NULL);
734 /* If in last chunk, don't go beyond end. */
735 if (cursorp->al_chunk == alenlist->al_last_chunk) {
736 int last_size = alenlist->al_logical_size % ALEN_CHUNK_SZ;
737 if (last_size && (index >= last_size))
738 return(ALENLIST_FAILURE);
742 alenp = &chunk->alc_pair[index];
743 length = alenp->al_length;
746 /* Constrain what we return according to maxlength */
748 size_t maxlen1 = maxlength - 1;
750 if ((maxlength & maxlen1) == 0) /* power of 2 */
752 ((alenp->al_addr + cursorp->al_bcount) & maxlen1);
754 length = min(maxlength, length);
757 /* Update the cursor, if desired. */
758 if (!(flags & AL_LEAVE_CURSOR)) {
759 cursorp->al_bcount += length;
760 cursorp->al_chunk = chunk;
764 *addrp = alenp->al_addr + bcount;
766 return(ALENLIST_SUCCESS);
771 * Return the number of pairs in the specified Address/Length List.
772 * (For FIXED_SIZE Lists, this returns the logical size of the List,
773 * not the actual capacity of the List.)
776 alenlist_size(alenlist_t alenlist)
778 return(alenlist->al_logical_size);
783 * Concatenate two Address/Length Lists.
786 alenlist_concat(alenlist_t from,
789 struct alenlist_cursor_s cursor;
793 alenlist_cursor_init(from, 0, &cursor);
795 while(alenlist_get(from, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
796 alenlist_append(to, addr, length, 0);
800 * Create a copy of a list.
801 * (Not all attributes of the old list are cloned. For instance, if
802 * a FIXED_SIZE list is cloned, the resulting list is NOT FIXED_SIZE.)
805 alenlist_clone(alenlist_t old_list, unsigned flags)
809 new_list = alenlist_create(flags);
810 if (new_list != NULL)
811 alenlist_concat(old_list, new_list);
818 * Convert a kernel virtual address to a Physical Address/Length List.
821 kvaddr_to_alenlist(alenlist_t alenlist, caddr_t kvaddr, size_t length, unsigned flags)
826 int created_alenlist;
831 /* If caller supplied a List, use it. Otherwise, allocate one. */
832 if (alenlist == NULL) {
833 alenlist = alenlist_create(0);
834 created_alenlist = 1;
836 alenlist_clear(alenlist);
837 created_alenlist = 0;
840 paddr = kvtophys(kvaddr);
841 offset = poff(kvaddr);
843 /* Handle first page */
844 piece_length = min((size_t)(NBPP - offset), length);
845 if (alenlist_append(alenlist, paddr, piece_length, flags) == ALENLIST_FAILURE)
847 length -= piece_length;
848 kvaddr += piece_length;
850 /* Handle middle pages */
851 while (length >= NBPP) {
852 paddr = kvtophys(kvaddr);
853 if (alenlist_append(alenlist, paddr, NBPP, flags) == ALENLIST_FAILURE)
859 /* Handle last page */
861 ASSERT(length < NBPP);
862 paddr = kvtophys(kvaddr);
863 if (alenlist_append(alenlist, paddr, length, flags) == ALENLIST_FAILURE)
867 alenlist_cursor_init(alenlist, 0, NULL);
871 if (created_alenlist)
872 alenlist_destroy(alenlist);
879 alenlist_show(alenlist_t alenlist)
881 struct alenlist_cursor_s cursor;
886 alenlist_cursor_init(alenlist, 0, &cursor);
888 qprintf("Address/Length List@0x%x:\n", alenlist);
889 qprintf("logical size=0x%x actual size=0x%x last_chunk at 0x%x\n",
890 alenlist->al_logical_size, alenlist->al_actual_size,
891 alenlist->al_last_chunk);
892 qprintf("cursor: chunk=0x%x index=%d offset=0x%x\n",
893 alenlist->al_cursor.al_chunk,
894 alenlist->al_cursor.al_index,
895 alenlist->al_cursor.al_bcount);
896 while(alenlist_get(alenlist, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
897 qprintf("%d:\t0x%lx 0x%lx\n", ++i, addr, length);