2.5.70 update
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / sn / io / alenlist.c
1 /* $Id$
2  *
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
5  * for more details.
6  *
7  * Copyright (C) 1992 - 1997, 2000-2002 Silicon Graphics, Inc.  All rights reserved.
8  */
9
10 /* Implementation of Address/Length Lists. */
11
12
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>
18
19 /*
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.
25  *
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.
34  *
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.
43  *
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.
48  *
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
59  *
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
70  *
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
73  * by alenlist_get.
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.
87  *
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.
94  *
95  * Currently, no locking is provided by the alenlist implementation.
96  *
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.
102  *
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.
107  *
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 
120  * swap out path.
121  *
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.
126  *
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.
133  *
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.
139  *
140  * Eventually, alenlist's may replace kaio lists, vhand page lists,
141  * buffer cache pfdat lists, DMA page lists, etc.
142  */
143
144 /* Opaque data types */
145
146 /* An Address/Length pair.  */
147 typedef struct alen_s {
148         alenaddr_t      al_addr;
149         size_t          al_length;
150 } alen_t;
151
152 /* 
153  * Number of elements in one chunk of an Address/Length List.
154  *
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,
157  * for efficiency.
158  */
159 #define ALEN_CHUNK_SZ ((512*1024)/NBPP)
160
161 /*
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 
165  * efficient.
166  */
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 */
170 } *alenlist_chunk_t;
171
172 /* 
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!
178  *
179  * A statically allocated List is sufficiently large to hold ALEN_CHUNK_SZ pairs.
180  */
181 struct alenlist_s {
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 */
189 };
190
191 /* al_flags field */
192 #define AL_FIXED_SIZE   0x1     /* List is pre-allocated, and of fixed size */
193
194
195 struct zone *alenlist_zone = NULL;
196 struct zone *alenlist_chunk_zone = NULL;
197 struct zone *alenlist_cursor_zone = NULL;
198
199 #if DEBUG
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));
205 #else
206 #define INCR_COUNT(ptr)
207 #define DECR_COUNT(ptr)
208 #endif /* DEBUG */
209
210 #if DEBUG
211 static void alenlist_show(alenlist_t);
212 #endif /* DEBUG */
213
214 /*
215  * Initialize Address/Length List management.  One time initialization.
216  */
217 void
218 alenlist_init(void)
219 {
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");
223 #if DEBUG
224         idbg_addfunc("alenshow", alenlist_show);
225 #endif /* DEBUG */
226 }
227
228
229 /*
230  * Initialize an Address/Length List cursor.
231  */
232 static void
233 do_cursor_init(alenlist_t alenlist, alenlist_cursor_t cursorp)
234 {
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;
240 }
241
242
243 /*
244  * Create an Address/Length List, and clear it.
245  * Set the cursor to the beginning.
246  */
247 alenlist_t 
248 alenlist_create(unsigned flags)
249 {
250         alenlist_t alenlist;
251
252         alenlist = snia_kmem_zone_alloc(alenlist_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
253         if (alenlist) {
254                 INCR_COUNT(&alenlist_count);
255
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);
262         }
263
264         return(alenlist);
265 }
266
267
268 /*
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.
273  *
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.
277  * 
278  * The cursor is set to the beginning of the list.
279  */
280 int
281 alenlist_grow(alenlist_t alenlist, size_t npairs)
282 {
283         /* 
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
288          * cursor.
289          */
290
291         /* 
292          * Temporarily mark as non-fixed size, since we're about
293          * to shrink and expand it.
294          */
295         alenlist->al_flags &= ~AL_FIXED_SIZE;
296
297         /* Free whatever was in the alenlist. */
298         alenlist_clear(alenlist);
299
300         /* Allocate everything that we need via automatic expansion. */
301         while (npairs--)
302                 if (alenlist_append(alenlist, 0, 0, AL_NOCOMPACT) == ALENLIST_FAILURE)
303                         return(ALENLIST_FAILURE);
304
305         /* Now, mark as FIXED_SIZE */
306         alenlist->al_flags |= AL_FIXED_SIZE;
307
308         /* Clear out bogus entries */
309         alenlist_clear(alenlist);
310
311         /* Initialize internal cursor to the beginning */
312         do_cursor_init(alenlist, &alenlist->al_cursor);
313
314         return(ALENLIST_SUCCESS);
315 }
316
317
318 /*
319  * Clear an Address/Length List so that it holds no pairs.
320  */
321 void
322 alenlist_clear(alenlist_t alenlist)
323 {
324         alenlist_chunk_t chunk, freechunk;
325
326         /*
327          * If this List is not FIXED_SIZE, free all the
328          * extra chunks.
329          */
330         if (!(alenlist->al_flags & AL_FIXED_SIZE)) {
331                 /* First, free any extension alenlist chunks */
332                 chunk = alenlist->al_chunk.alc_next;
333                 while (chunk) {
334                         freechunk = chunk;
335                         chunk = chunk->alc_next;
336                         snia_kmem_zone_free(alenlist_chunk_zone, freechunk);
337                         DECR_COUNT(&alenlist_chunk_count);
338                 }
339                 alenlist->al_actual_size = ALEN_CHUNK_SZ;
340                 alenlist->al_chunk.alc_next = NULL;
341         }
342
343         alenlist->al_logical_size = 0;
344         alenlist->al_last_chunk = &alenlist->al_chunk;
345         do_cursor_init(alenlist, &alenlist->al_cursor);
346 }
347
348
349 /*
350  * Create and initialize an Address/Length Pair.
351  * This is intended for degenerate lists, consisting of a single 
352  * address/length pair.
353  */
354 alenlist_t
355 alenpair_init(  alenaddr_t address, 
356                 size_t length)
357 {
358         alenlist_t alenlist;
359
360         alenlist = alenlist_create(0);
361
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;
366
367         return(alenlist);
368 }
369
370 /*
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.
374  */
375 int
376 alenpair_get(   alenlist_t alenlist,
377                 alenaddr_t *address,
378                 size_t *length)
379 {
380         if (alenlist->al_logical_size == 0)
381                 return(ALENLIST_FAILURE);
382
383         *length = alenlist->al_chunk.alc_pair[0].al_length;
384         *address = alenlist->al_chunk.alc_pair[0].al_addr;
385         return(ALENLIST_SUCCESS);
386 }
387
388
389 /*
390  * Destroy an Address/Length List.
391  */
392 void 
393 alenlist_destroy(alenlist_t alenlist)
394 {
395         if (alenlist == NULL)
396                 return;
397
398         /* 
399          * Turn off FIXED_SIZE so this List can be 
400          * automatically shrunk.
401          */
402         alenlist->al_flags &= ~AL_FIXED_SIZE;
403
404         /* Free extension chunks first */
405         if (alenlist->al_chunk.alc_next)
406                 alenlist_clear(alenlist);
407
408         /* Now, free the alenlist itself */
409         snia_kmem_zone_free(alenlist_zone, alenlist);
410         DECR_COUNT(&alenlist_count);
411 }
412
413 /*
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.
420  */
421 void
422 alenlist_done(alenlist_t alenlist)
423 {
424         alenlist_destroy(alenlist);
425 }
426
427
428 /*
429  * Append another address/length to the end of an Address/Length List,
430  * growing the list if permitted and necessary.
431  *
432  * Returns: SUCCESS/FAILURE
433  */
434 int 
435 alenlist_append(        alenlist_t alenlist,            /* append to this list */
436                         alenaddr_t address,             /* address to append */
437                         size_t length,                  /* length to append */
438                         unsigned flags)
439 {
440         alen_t *alenp;
441         int index, last_index;
442
443         index = alenlist->al_logical_size % ALEN_CHUNK_SZ;
444
445         if ((alenlist->al_logical_size > 0)) {
446                 /*
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.
450                  */
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);
458                 }
459
460                 /*
461                  * If we're out of room in this chunk, move to a new chunk.
462                  */
463                 if (index == 0) {
464                         if (alenlist->al_flags & AL_FIXED_SIZE) {
465                                 alenlist->al_last_chunk = alenlist->al_last_chunk->alc_next;
466
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);
471                                 }
472                         } else {
473                                 alenlist_chunk_t new_chunk;
474
475                                 new_chunk = snia_kmem_zone_alloc(alenlist_chunk_zone, 
476                                                         flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
477
478                                 if (new_chunk == NULL)
479                                         return(ALENLIST_FAILURE);
480
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);
486                         }
487                 }
488         }
489
490         alenp = &(alenlist->al_last_chunk->alc_pair[index]);
491         alenp->al_addr = address;
492         alenp->al_length = length;
493         
494         alenlist->al_logical_size++;
495         alenlist->al_compaction_address = address + length;
496
497         return(ALENLIST_SUCCESS);
498 }
499
500
501 /*
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.
507  */
508 /* ARGSUSED */
509 int
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 */
514                         unsigned flags)
515 {
516         alen_t *alenp;
517         alenlist_chunk_t chunk;
518         unsigned int index;
519         size_t length;
520         alenaddr_t addr;
521
522         if ((addrp == NULL) || (lengthp == NULL))
523                 return(ALENLIST_FAILURE);
524
525         if (alenlist->al_logical_size == 0)
526                 return(ALENLIST_FAILURE);
527
528         addr = *addrp;
529         length = *lengthp;
530
531         /* 
532          * If no cursor explicitly specified, use the Address/Length List's 
533          * internal cursor.
534          */
535         if (cursorp == NULL)
536                 cursorp = &alenlist->al_cursor;
537
538         chunk = cursorp->al_chunk;
539         index = cursorp->al_index;
540
541         ASSERT(cursorp->al_alenlist == alenlist);
542         if (cursorp->al_alenlist != alenlist)
543                 return(ALENLIST_FAILURE);
544
545         alenp = &chunk->alc_pair[index];
546
547         /* Return old values */
548         *addrp = alenp->al_length;
549         *lengthp = alenp->al_addr;
550
551         /* Set up new values */
552         alenp->al_length = length;
553         alenp->al_addr = addr;
554
555         /* Update cursor to point to next item */
556         cursorp->al_bcount = length;
557
558         return(ALENLIST_SUCCESS);
559 }
560
561
562 /*
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.
566  *
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.
572  */
573 int
574 alenlist_cursor_init(alenlist_t alenlist, size_t offset, alenlist_cursor_t cursorp)
575 {
576         size_t byte_count;
577
578         if (cursorp == NULL)
579                 cursorp = &alenlist->al_cursor;
580
581         /* Get internal cursor's byte count for use as a hint.
582          *
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.
586          */
587         if ((offset > 0) &&
588            ((byte_count = alenlist_cursor_offset(alenlist, (alenlist_cursor_t)NULL)) <= offset)) {
589                 offset -= byte_count;
590                 alenlist_cursor_clone(alenlist, NULL, cursorp);
591         } else
592                 do_cursor_init(alenlist, cursorp);
593
594         /* We could easily speed this up, but it shouldn't be used very often. */
595         while (offset != 0) {
596                 alenaddr_t addr;
597                 size_t length;
598
599                 if (alenlist_get(alenlist, cursorp, offset, &addr, &length, 0) != ALENLIST_SUCCESS)
600                         return(ALENLIST_FAILURE);
601                 offset -= length;
602         }
603         return(ALENLIST_SUCCESS);
604 }
605
606
607 /*
608  * Copy a cursor.  The source cursor is either an internal alenlist cursor
609  * or an explicit cursor.
610  */
611 int
612 alenlist_cursor_clone(  alenlist_t alenlist, 
613                         alenlist_cursor_t cursorp_in, 
614                         alenlist_cursor_t cursorp_out)
615 {
616         ASSERT(cursorp_out);
617
618         if (alenlist && cursorp_in)
619                 if (alenlist != cursorp_in->al_alenlist)
620                         return(ALENLIST_FAILURE);
621
622         if (alenlist)
623                 *cursorp_out = alenlist->al_cursor; /* small structure copy */
624         else if (cursorp_in)
625                 *cursorp_out = *cursorp_in; /* small structure copy */
626         else
627                 return(ALENLIST_FAILURE); /* no source */
628
629         return(ALENLIST_SUCCESS);
630 }
631
632 /*
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.
635  */
636 size_t
637 alenlist_cursor_offset(alenlist_t alenlist, alenlist_cursor_t cursorp)
638 {
639         ASSERT(!alenlist || !cursorp || (alenlist == cursorp->al_alenlist));
640
641         if (cursorp == NULL) {
642                 ASSERT(alenlist);
643                 cursorp = &alenlist->al_cursor;
644         }
645
646         return(cursorp->al_offset);
647 }
648
649 /*
650  * Allocate and initialize an Address/Length List cursor.
651  */
652 alenlist_cursor_t
653 alenlist_cursor_create(alenlist_t alenlist, unsigned flags)
654 {
655         alenlist_cursor_t cursorp;
656
657         ASSERT(alenlist != NULL);
658         cursorp = snia_kmem_zone_alloc(alenlist_cursor_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
659         if (cursorp) {
660                 INCR_COUNT(&alenlist_cursor_count);
661                 alenlist_cursor_init(alenlist, 0, cursorp);
662         }
663         return(cursorp);
664 }
665
666 /*
667  * Free an Address/Length List cursor.
668  */
669 void
670 alenlist_cursor_destroy(alenlist_cursor_t cursorp)
671 {
672         DECR_COUNT(&alenlist_cursor_count);
673         snia_kmem_zone_free(alenlist_cursor_zone, cursorp);
674 }
675
676
677 /*
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.]
684  *
685  * Returns: SUCCESS/FAILURE
686  */
687 int
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 */
693                 unsigned flags)
694 {
695         alen_t *alenp;
696         alenlist_chunk_t chunk;
697         unsigned int index;
698         size_t bcount;
699         size_t length;
700
701         /* 
702          * If no cursor explicitly specified, use the Address/Length List's 
703          * internal cursor.
704          */
705         if (cursorp == NULL) {
706                 if (alenlist->al_logical_size == 0)
707                         return(ALENLIST_FAILURE);
708                 cursorp = &alenlist->al_cursor;
709         }
710
711         chunk = cursorp->al_chunk;
712         index = cursorp->al_index;
713         bcount = cursorp->al_bcount;
714
715         ASSERT(cursorp->al_alenlist == alenlist);
716         if (cursorp->al_alenlist != alenlist)
717                 return(ALENLIST_FAILURE);
718
719         alenp = &chunk->alc_pair[index];
720         length = alenp->al_length - bcount;
721
722         /* Bump up to next pair, if we're done with this pair. */
723         if (length == 0) {
724                 cursorp->al_bcount = bcount = 0;
725                 cursorp->al_index = index = (index + 1) % ALEN_CHUNK_SZ;
726
727                 /* Bump up to next chunk, if we're done with this chunk. */
728                 if (index == 0) {
729                         if (cursorp->al_chunk == alenlist->al_last_chunk)
730                                 return(ALENLIST_FAILURE);
731                         chunk = chunk->alc_next;
732                         ASSERT(chunk != NULL);
733                 } else {
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);
739                         }
740                 }
741
742                 alenp = &chunk->alc_pair[index];
743                 length = alenp->al_length;
744         }
745
746         /* Constrain what we return according to maxlength */
747         if (maxlength) {
748                 size_t maxlen1 = maxlength - 1;
749
750                 if ((maxlength & maxlen1) == 0) /* power of 2 */
751                         maxlength -= 
752                            ((alenp->al_addr + cursorp->al_bcount) & maxlen1);
753
754                 length = min(maxlength, length);
755         }
756
757         /* Update the cursor, if desired. */
758         if (!(flags & AL_LEAVE_CURSOR)) {
759                 cursorp->al_bcount += length;
760                 cursorp->al_chunk = chunk;
761         }
762
763         *lengthp = length;
764         *addrp = alenp->al_addr + bcount;
765
766         return(ALENLIST_SUCCESS);
767 }
768
769
770 /*
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.)
774  */
775 int
776 alenlist_size(alenlist_t alenlist)
777 {
778         return(alenlist->al_logical_size);
779 }
780
781
782 /*
783  * Concatenate two Address/Length Lists.
784  */
785 void
786 alenlist_concat(alenlist_t from,
787                 alenlist_t to)
788 {
789         struct alenlist_cursor_s cursor;
790         alenaddr_t addr;
791         size_t length;
792
793         alenlist_cursor_init(from, 0, &cursor);
794
795         while(alenlist_get(from, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
796                 alenlist_append(to, addr, length, 0);
797 }
798
799 /*
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.)
803  */
804 alenlist_t
805 alenlist_clone(alenlist_t old_list, unsigned flags)
806 {
807         alenlist_t new_list;
808
809         new_list = alenlist_create(flags);
810         if (new_list != NULL)
811                 alenlist_concat(old_list, new_list);
812
813         return(new_list);
814 }
815
816
817 /* 
818  * Convert a kernel virtual address to a Physical Address/Length List.
819  */
820 alenlist_t
821 kvaddr_to_alenlist(alenlist_t alenlist, caddr_t kvaddr, size_t length, unsigned flags)
822 {
823         alenaddr_t paddr;
824         long offset;
825         size_t piece_length;
826         int created_alenlist;
827
828         if (length <=0)
829                 return(NULL);
830
831         /* If caller supplied a List, use it.  Otherwise, allocate one. */
832         if (alenlist == NULL) {
833                 alenlist = alenlist_create(0);
834                 created_alenlist = 1;
835         } else {
836                 alenlist_clear(alenlist);
837                 created_alenlist = 0;
838         }
839
840         paddr = kvtophys(kvaddr);
841         offset = poff(kvaddr);
842
843         /* Handle first page */
844         piece_length = min((size_t)(NBPP - offset), length);
845         if (alenlist_append(alenlist, paddr, piece_length, flags) == ALENLIST_FAILURE)
846                 goto failure;
847         length -= piece_length;
848         kvaddr += piece_length;
849
850         /* Handle middle pages */
851         while (length >= NBPP) {
852                 paddr = kvtophys(kvaddr);
853                 if (alenlist_append(alenlist, paddr, NBPP, flags) == ALENLIST_FAILURE)
854                         goto failure;
855                 length -= NBPP;
856                 kvaddr += NBPP;
857         }
858
859         /* Handle last page */
860         if (length) {
861                 ASSERT(length < NBPP);
862                 paddr = kvtophys(kvaddr);
863                 if (alenlist_append(alenlist, paddr, length, flags) == ALENLIST_FAILURE)
864                         goto failure;
865         }
866
867         alenlist_cursor_init(alenlist, 0, NULL);
868         return(alenlist);
869
870 failure:
871         if (created_alenlist)
872                 alenlist_destroy(alenlist);
873         return(NULL);
874 }
875
876
877 #if DEBUG
878 static void
879 alenlist_show(alenlist_t alenlist)
880 {
881         struct alenlist_cursor_s cursor;
882         alenaddr_t addr;
883         size_t length;
884         int i = 0;
885
886         alenlist_cursor_init(alenlist, 0, &cursor);
887
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);
898 }
899 #endif /* DEBUG */