alenlist.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:27k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  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 Silicon Graphics, Inc.
  8.  * Copyright (C) 2000 by Colin Ngam
  9.  */
  10. /* Implementation of Address/Length Lists. */
  11. #include <linux/types.h>
  12. #include <linux/slab.h>
  13. #include <asm/sn/sgi.h>
  14. #include <asm/sn/alenlist.h>
  15. #include <asm/sn/mmzone_sn1.h>
  16. /*
  17.  * Logically, an Address/Length List is a list of Pairs, where each pair
  18.  * holds an Address and a Length, all in some Address Space.  In this
  19.  * context, "Address Space" is a particular Crosstalk Widget address
  20.  * space, a PCI device address space, a VME bus address space, a
  21.  * physical memory address space, etc.
  22.  *
  23.  * The main use for these Lists is to provide a single mechanism that
  24.  * describes where in an address space a DMA occurs.  This allows the
  25.  * various I/O Bus support layers to provide a single interface for
  26.  * DMA mapping and DMA translation without regard to how the DMA target
  27.  * was specified by upper layers.  The upper layers commonly specify a 
  28.  * DMA target via a buf structure page list, a kernel virtual address,
  29.  * a user virtual address, a vector of addresses (a la uio and iov), 
  30.  * or possibly a pfn list.
  31.  *
  32.  * Address/Length Lists also enable drivers to take advantage of their
  33.  * inate scatter/gather capabilities in systems where some address
  34.  * translation may be required between bus adapters.  The driver forms
  35.  * a List that represents physical memory targets.  This list is passed
  36.  * to the various adapters, which apply various translations.  The final
  37.  * list that's returned to the driver is in terms of its local address
  38.  * address space -- addresses which can be passed off to a scatter/gather
  39.  * capable DMA controller.
  40.  *
  41.  * The current implementation is intended to be useful both in kernels
  42.  * that support interrupt threads (INTR_KTHREAD) and in systems that do
  43.  * not support interrupt threads.  Of course, in the latter case, some
  44.  * interfaces can be called only within a suspendable context.
  45.  *
  46.  * Basic operations on Address/Length Lists include:
  47.  * alenlist_create Create a list
  48.  * alenlist_clear Clear a list
  49.  * alenlist_destroy Destroy a list
  50.  * alenlist_append Append a Pair to the end of a list
  51.  * alenlist_replace Replace a Pair in the middle of a list
  52.  * alenlist_get Get an Address/Length Pair from a list
  53.  * alenlist_size Return the number of Pairs in a list
  54.  * alenlist_concat Append one list to the end of another
  55.  * alenlist_clone Create a new copy of a list
  56.  *
  57.  * Operations that convert from upper-level specifications to Address/
  58.  * Length Lists currently include:
  59.  * kvaddr_to_alenlist Convert from a kernel virtual address
  60.  * uvaddr_to_alenlist Convert from a user virtual address
  61.  * buf_to_alenlist Convert from a buf structure
  62.  * alenlist_done Tell system that we're done with an alenlist
  63.  * obtained from a conversion.
  64.  * Additional convenience operations:
  65.  * alenpair_init Create a list and initialize it with a Pair
  66.  * alenpair_get Peek at the first pair on a List
  67.  *
  68.  * A supporting type for Address/Length Lists is an alenlist_cursor_t.  A
  69.  * cursor marks a position in a List, and determines which Pair is fetched
  70.  * by alenlist_get.
  71.  * alenlist_cursor_create Allocate and initialize a cursor
  72.  * alenlist_cursor_destroy Free space consumed by a cursor
  73.  * alenlist_cursor_init (Re-)Initialize a cursor to point 
  74.  * to the start of a list
  75.  * alenlist_cursor_clone Clone a cursor (at the current offset)
  76.  * alenlist_cursor_offset Return the number of bytes into
  77.  * a list that this cursor marks
  78.  * Multiple cursors can point at various points into a List.  Also, each
  79.  * list maintains one "internal cursor" which may be updated by alenlist_clear
  80.  * and alenlist_get.  If calling code simply wishes to scan sequentially
  81.  * through a list starting at the beginning, and if it is the only user of
  82.  * a list, it can rely on this internal cursor rather than managing a 
  83.  * separate explicit cursor.
  84.  *
  85.  * The current implementation allows callers to allocate both cursors and
  86.  * the lists as local stack (structure) variables.  This allows for some
  87.  * extra efficiency at the expense of forward binary compatibility.  It 
  88.  * is recommended that customer drivers refrain from local allocation.
  89.  * In fact, we likely will choose to move the structures out of the public 
  90.  * header file into a private place in order to discourage this usage.
  91.  *
  92.  * Currently, no locking is provided by the alenlist implementation.
  93.  *
  94.  * Implementation notes:
  95.  * For efficiency, Pairs are grouped into "chunks" of, say, 32 Pairs
  96.  * and a List consists of some number of these chunks.  Chunks are completely
  97.  * invisible to calling code.  Chunks should be large enough to hold most
  98.  * standard-sized DMA's, but not so large that they consume excessive space.
  99.  *
  100.  * It is generally expected that Lists will be constructed at one time and
  101.  * scanned at a later time.  It is NOT expected that drivers will scan
  102.  * a List while the List is simultaneously extended, although this is
  103.  * theoretically possible with sufficient upper-level locking.
  104.  *
  105.  * In order to support demands of Real-Time drivers and in order to support
  106.  * swapping under low-memory conditions, we support the concept of a
  107.  * "pre-allocated fixed-sized List".  After creating a List with 
  108.  * alenlist_create, a driver may explicitly grow the list (via "alenlist_grow")
  109.  * to a specific number of Address/Length pairs.  It is guaranteed that future 
  110.  * operations involving this list will never automatically grow the list 
  111.  * (i.e. if growth is ever required, the operation will fail).  Additionally, 
  112.  * operations that use alenlist's (e.g. DMA operations) accept a flag which 
  113.  * causes processing to take place "in-situ"; that is, the input alenlist 
  114.  * entries are replaced with output alenlist entries.  The combination of 
  115.  * pre-allocated Lists and in-situ processing allows us to avoid the 
  116.  * potential deadlock scenario where we sleep (waiting for memory) in the 
  117.  * swap out path.
  118.  *
  119.  * For debugging, we track the number of allocated Lists in alenlist_count
  120.  * the number of allocated chunks in alenlist_chunk_count, and the number
  121.  * of allocate cursors in alenlist_cursor_count.  We also provide a debug 
  122.  * routine, alenlist_show, which dumps the contents of an Address/Length List.
  123.  *
  124.  * Currently, Lists are formed by drivers on-demand.  Eventually, we may
  125.  * associate an alenlist with a buf structure and keep it up to date as
  126.  * we go along.  In that case, buf_to_alenlist simply returns a pointer
  127.  * to the existing List, and increments the Lists's reference count.
  128.  * alenlist_done would decrement the reference count and destroys the List
  129.  * if it was the last reference.
  130.  *
  131.  * Eventually alenlist's may allow better support for user-level scatter/
  132.  * gather operations (e.g. via readv/writev):  With proper support, we
  133.  * could potentially handle a vector of reads with a single scatter/gather
  134.  * DMA operation.  This could be especially useful on NUMA systems where
  135.  * there's more of a reason for users to use vector I/O operations.
  136.  *
  137.  * Eventually, alenlist's may replace kaio lists, vhand page lists,
  138.  * buffer cache pfdat lists, DMA page lists, etc.
  139.  */
  140. /* Opaque data types */
  141. /* An Address/Length pair.  */
  142. typedef struct alen_s {
  143. alenaddr_t al_addr;
  144. size_t al_length;
  145. } alen_t;
  146. /* 
  147.  * Number of elements in one chunk of an Address/Length List.
  148.  *
  149.  * This size should be sufficient to hold at least an "average" size
  150.  * DMA request.  Must be at least 1, and should be a power of 2,
  151.  * for efficiency.
  152.  */
  153. #define ALEN_CHUNK_SZ ((512*1024)/NBPP)
  154. /*
  155.  * A fixed-size set of Address/Length Pairs.  Chunks of Pairs are strung together 
  156.  * to form a complete Address/Length List.  Chunking is entirely hidden within the 
  157.  * alenlist implementation, and it simply makes allocation and growth of lists more 
  158.  * efficient.
  159.  */
  160. typedef struct alenlist_chunk_s {
  161. alen_t alc_pair[ALEN_CHUNK_SZ];/* list of addr/len pairs */
  162. struct alenlist_chunk_s *alc_next; /* point to next chunk of pairs */
  163. } *alenlist_chunk_t;
  164. /* 
  165.  * An Address/Length List.  An Address/Length List is allocated with alenlist_create.  
  166.  * Alternatively, a list can be allocated on the stack (local variable of type 
  167.  * alenlist_t) and initialized with alenpair_init or with a combination of 
  168.  * alenlist_clear and alenlist_append, etc.  Code which statically allocates these
  169.  * structures loses forward binary compatibility!
  170.  *
  171.  * A statically allocated List is sufficiently large to hold ALEN_CHUNK_SZ pairs.
  172.  */
  173. struct alenlist_s {
  174. unsigned short al_flags;
  175. unsigned short al_logical_size; /* logical size of list, in pairs */
  176. unsigned short al_actual_size; /* actual size of list, in pairs */
  177. struct alenlist_chunk_s *al_last_chunk; /* pointer to last logical chunk */
  178. struct alenlist_cursor_s al_cursor; /* internal cursor */
  179. struct alenlist_chunk_s al_chunk; /* initial set of pairs */
  180. alenaddr_t al_compaction_address; /* used to compact pairs */
  181. };
  182. /* al_flags field */
  183. #define AL_FIXED_SIZE 0x1 /* List is pre-allocated, and of fixed size */
  184. zone_t *alenlist_zone = NULL;
  185. zone_t *alenlist_chunk_zone = NULL;
  186. zone_t *alenlist_cursor_zone = NULL;
  187. #if DEBUG
  188. int alenlist_count=0; /* Currently allocated Lists */
  189. int alenlist_chunk_count = 0; /* Currently allocated chunks */
  190. int alenlist_cursor_count = 0; /* Currently allocate cursors */
  191. #define INCR_COUNT(ptr) atomic_inc((ptr));
  192. #define DECR_COUNT(ptr) atomic_dec((ptr));
  193. #else
  194. #define INCR_COUNT(ptr)
  195. #define DECR_COUNT(ptr)
  196. #endif /* DEBUG */
  197. #if DEBUG
  198. static void alenlist_show(alenlist_t);
  199. #endif /* DEBUG */
  200. /*
  201.  * Initialize Address/Length List management.  One time initialization.
  202.  */
  203. void
  204. alenlist_init(void)
  205. {
  206. alenlist_zone = kmem_zone_init(sizeof(struct alenlist_s), "alenlist");
  207. alenlist_chunk_zone = kmem_zone_init(sizeof(struct alenlist_chunk_s), "alchunk");
  208. alenlist_cursor_zone = kmem_zone_init(sizeof(struct alenlist_cursor_s), "alcursor");
  209. #if DEBUG
  210. idbg_addfunc("alenshow", alenlist_show);
  211. #endif /* DEBUG */
  212. }
  213. /*
  214.  * Initialize an Address/Length List cursor.
  215.  */
  216. static void
  217. do_cursor_init(alenlist_t alenlist, alenlist_cursor_t cursorp)
  218. {
  219. cursorp->al_alenlist = alenlist;
  220. cursorp->al_offset = 0;
  221. cursorp->al_chunk = &alenlist->al_chunk;
  222. cursorp->al_index = 0;
  223. cursorp->al_bcount = 0;
  224. }
  225. /*
  226.  * Create an Address/Length List, and clear it.
  227.  * Set the cursor to the beginning.
  228.  */
  229. alenlist_t 
  230. alenlist_create(unsigned flags)
  231. {
  232. alenlist_t alenlist;
  233. alenlist = kmem_zone_alloc(alenlist_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
  234. if (alenlist) {
  235. INCR_COUNT(&alenlist_count);
  236. alenlist->al_flags = 0;
  237. alenlist->al_logical_size = 0;
  238. alenlist->al_actual_size = ALEN_CHUNK_SZ;
  239. alenlist->al_last_chunk = &alenlist->al_chunk;
  240. alenlist->al_chunk.alc_next = NULL;
  241. do_cursor_init(alenlist, &alenlist->al_cursor);
  242. }
  243. return(alenlist);
  244. }
  245. /*
  246.  * Grow an Address/Length List so that all resources needed to contain
  247.  * the specified number of Pairs are pre-allocated.  An Address/Length
  248.  * List that has been explicitly "grown" will never *automatically*
  249.  * grow, shrink, or be destroyed.
  250.  *
  251.  * Pre-allocation is useful for Real-Time drivers and for drivers that
  252.  * may be used along the swap-out path and therefore cannot afford to 
  253.  * sleep until memory is freed.
  254.  * 
  255.  * The cursor is set to the beginning of the list.
  256.  */
  257. int
  258. alenlist_grow(alenlist_t alenlist, size_t npairs)
  259. {
  260. /* 
  261.  * This interface should be used relatively rarely, so
  262.  * the implementation is kept simple: We clear the List,
  263.  * then append npairs bogus entries.  Finally, we mark
  264.  * the list as FIXED_SIZE and re-initialize the internal
  265.  * cursor.
  266.  */
  267. /* 
  268.  * Temporarily mark as non-fixed size, since we're about
  269.  * to shrink and expand it.
  270.  */
  271. alenlist->al_flags &= ~AL_FIXED_SIZE;
  272. /* Free whatever was in the alenlist. */
  273. alenlist_clear(alenlist);
  274. /* Allocate everything that we need via automatic expansion. */
  275. while (npairs--)
  276. if (alenlist_append(alenlist, 0, 0, AL_NOCOMPACT) == ALENLIST_FAILURE)
  277. return(ALENLIST_FAILURE);
  278. /* Now, mark as FIXED_SIZE */
  279. alenlist->al_flags |= AL_FIXED_SIZE;
  280. /* Clear out bogus entries */
  281. alenlist_clear(alenlist);
  282. /* Initialize internal cursor to the beginning */
  283. do_cursor_init(alenlist, &alenlist->al_cursor);
  284. return(ALENLIST_SUCCESS);
  285. }
  286. /*
  287.  * Clear an Address/Length List so that it holds no pairs.
  288.  */
  289. void
  290. alenlist_clear(alenlist_t alenlist)
  291. {
  292. alenlist_chunk_t chunk, freechunk;
  293. /*
  294.  * If this List is not FIXED_SIZE, free all the
  295.  * extra chunks.
  296.  */
  297. if (!(alenlist->al_flags & AL_FIXED_SIZE)) {
  298. /* First, free any extension alenlist chunks */
  299. chunk = alenlist->al_chunk.alc_next;
  300. while (chunk) {
  301. freechunk = chunk;
  302. chunk = chunk->alc_next;
  303. kmem_zone_free(alenlist_chunk_zone, freechunk);
  304. DECR_COUNT(&alenlist_chunk_count);
  305. }
  306. alenlist->al_actual_size = ALEN_CHUNK_SZ;
  307. alenlist->al_chunk.alc_next = NULL;
  308. }
  309. alenlist->al_logical_size = 0;
  310. alenlist->al_last_chunk = &alenlist->al_chunk;
  311. do_cursor_init(alenlist, &alenlist->al_cursor);
  312. }
  313. /*
  314.  * Create and initialize an Address/Length Pair.
  315.  * This is intended for degenerate lists, consisting of a single 
  316.  * address/length pair.
  317.  */
  318. alenlist_t
  319. alenpair_init( alenaddr_t address, 
  320. size_t length)
  321. {
  322. alenlist_t alenlist;
  323. alenlist = alenlist_create(0);
  324. alenlist->al_logical_size = 1;
  325. ASSERT(alenlist->al_last_chunk == &alenlist->al_chunk);
  326. alenlist->al_chunk.alc_pair[0].al_length = length;
  327. alenlist->al_chunk.alc_pair[0].al_addr = address;
  328. return(alenlist);
  329. }
  330. /*
  331.  * Return address/length from a degenerate (1-pair) List, or
  332.  * first pair from a larger list.  Does NOT update the internal cursor,
  333.  * so this is an easy way to peek at a start address.
  334.  */
  335. int
  336. alenpair_get( alenlist_t alenlist,
  337. alenaddr_t *address,
  338. size_t *length)
  339. {
  340. if (alenlist->al_logical_size == 0)
  341. return(ALENLIST_FAILURE);
  342. *length = alenlist->al_chunk.alc_pair[0].al_length;
  343. *address = alenlist->al_chunk.alc_pair[0].al_addr;
  344. return(ALENLIST_SUCCESS);
  345. }
  346. /*
  347.  * Destroy an Address/Length List.
  348.  */
  349. void 
  350. alenlist_destroy(alenlist_t alenlist)
  351. {
  352. if (alenlist == NULL)
  353. return;
  354. /* 
  355.  * Turn off FIXED_SIZE so this List can be 
  356.  * automatically shrunk.
  357.  */
  358. alenlist->al_flags &= ~AL_FIXED_SIZE;
  359. /* Free extension chunks first */
  360. if (alenlist->al_chunk.alc_next)
  361. alenlist_clear(alenlist);
  362. /* Now, free the alenlist itself */
  363. kmem_zone_free(alenlist_zone, alenlist);
  364. DECR_COUNT(&alenlist_count);
  365. }
  366. /*
  367.  * Release an Address/Length List.
  368.  * This is in preparation for a day when alenlist's may be longer-lived, and
  369.  * perhaps associated with a buf structure.  We'd add a reference count, and
  370.  * this routine would decrement the count.  For now, we create alenlist's on
  371.  * on demand and free them when done.  If the driver is not explicitly managing
  372.  * a List for its own use, it should call alenlist_done rather than alenlist_destroy.
  373.  */
  374. void
  375. alenlist_done(alenlist_t alenlist)
  376. {
  377. alenlist_destroy(alenlist);
  378. }
  379. /*
  380.  * Append another address/length to the end of an Address/Length List,
  381.  * growing the list if permitted and necessary.
  382.  *
  383.  * Returns: SUCCESS/FAILURE
  384.  */
  385. int 
  386. alenlist_append( alenlist_t alenlist,  /* append to this list */
  387. alenaddr_t address,  /* address to append */
  388. size_t length, /* length to append */
  389. unsigned flags)
  390. {
  391. alen_t *alenp;
  392. int index, last_index;
  393. index = alenlist->al_logical_size % ALEN_CHUNK_SZ;
  394. if ((alenlist->al_logical_size > 0)) {
  395. /*
  396.  * See if we can compact this new pair in with the previous entry.
  397.  * al_compaction_address holds that value that we'd need to see
  398.  * in order to compact.
  399.  */
  400. if (!(flags & AL_NOCOMPACT) &&
  401.     (alenlist->al_compaction_address == address)) {
  402. last_index = (alenlist->al_logical_size-1) % ALEN_CHUNK_SZ;
  403. alenp = &(alenlist->al_last_chunk->alc_pair[last_index]);
  404. alenp->al_length += length;
  405. alenlist->al_compaction_address += length;
  406. return(ALENLIST_SUCCESS);
  407. }
  408. /*
  409.  * If we're out of room in this chunk, move to a new chunk.
  410.    */
  411. if (index == 0) {
  412. if (alenlist->al_flags & AL_FIXED_SIZE) {
  413. alenlist->al_last_chunk = alenlist->al_last_chunk->alc_next;
  414. /* If we're out of space in a FIXED_SIZE List, quit. */
  415. if (alenlist->al_last_chunk == NULL) {
  416. ASSERT(alenlist->al_logical_size == alenlist->al_actual_size);
  417. return(ALENLIST_FAILURE);
  418. }
  419. } else {
  420. alenlist_chunk_t new_chunk;
  421. new_chunk = kmem_zone_alloc(alenlist_chunk_zone, 
  422. flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
  423. if (new_chunk == NULL)
  424. return(ALENLIST_FAILURE);
  425. alenlist->al_last_chunk->alc_next = new_chunk;
  426. new_chunk->alc_next = NULL;
  427. alenlist->al_last_chunk = new_chunk;
  428. alenlist->al_actual_size += ALEN_CHUNK_SZ;
  429. INCR_COUNT(&alenlist_chunk_count);
  430. }
  431. }
  432. }
  433. alenp = &(alenlist->al_last_chunk->alc_pair[index]);
  434. alenp->al_addr = address;
  435. alenp->al_length = length;
  436. alenlist->al_logical_size++;
  437. alenlist->al_compaction_address = address + length;
  438. return(ALENLIST_SUCCESS);
  439. }
  440. /*
  441.  * Replace an item in an Address/Length List.  Cursor is updated so
  442.  * that alenlist_get will get the next item in the list.  This interface 
  443.  * is not very useful for drivers; but it is useful to bus providers 
  444.  * that need to translate between address spaced in situ.  The old Address
  445.  * and Length are returned.
  446.  */
  447. /* ARGSUSED */
  448. int
  449. alenlist_replace( alenlist_t alenlist,  /* in: replace in this list */
  450. alenlist_cursor_t cursorp,  /* inout: which item to replace */
  451. alenaddr_t *addrp,  /* inout: address */
  452. size_t *lengthp, /* inout: length */
  453. unsigned flags)
  454. {
  455. alen_t *alenp;
  456. alenlist_chunk_t chunk;
  457. unsigned int index;
  458. size_t length;
  459. alenaddr_t addr;
  460. if ((addrp == NULL) || (lengthp == NULL))
  461. return(ALENLIST_FAILURE);
  462. if (alenlist->al_logical_size == 0)
  463. return(ALENLIST_FAILURE);
  464. addr = *addrp;
  465. length = *lengthp;
  466. /* 
  467.  * If no cursor explicitly specified, use the Address/Length List's 
  468.  * internal cursor.
  469.  */
  470. if (cursorp == NULL)
  471. cursorp = &alenlist->al_cursor;
  472. chunk = cursorp->al_chunk;
  473. index = cursorp->al_index;
  474. ASSERT(cursorp->al_alenlist == alenlist);
  475. if (cursorp->al_alenlist != alenlist)
  476. return(ALENLIST_FAILURE);
  477. alenp = &chunk->alc_pair[index];
  478. /* Return old values */
  479. *addrp = alenp->al_length;
  480. *lengthp = alenp->al_addr;
  481. /* Set up new values */
  482. alenp->al_length = length;
  483. alenp->al_addr = addr;
  484. /* Update cursor to point to next item */
  485. cursorp->al_bcount = length;
  486. return(ALENLIST_SUCCESS);
  487. }
  488. /*
  489.  * Initialize a cursor in order to walk an alenlist.
  490.  * An alenlist_cursor always points to the last thing that was obtained
  491.  * from the list.  If al_chunk is NULL, then nothing has yet been obtained.
  492.  *
  493.  * Note: There is an "internal cursor" associated with every Address/Length List.
  494.  * For users that scan sequentially through a List, it is more efficient to
  495.  * simply use the internal cursor.  The caller must insure that no other users
  496.  * will simultaneously scan the List.  The caller can reposition the internal
  497.  * cursor by calling alenlist_cursor_init with a NULL cursorp.
  498.  */
  499. int
  500. alenlist_cursor_init(alenlist_t alenlist, size_t offset, alenlist_cursor_t cursorp)
  501. {
  502. size_t byte_count;
  503. if (cursorp == NULL)
  504. cursorp = &alenlist->al_cursor;
  505. /* Get internal cursor's byte count for use as a hint.
  506.  *
  507.  * If the internal cursor points passed the point that we're interested in,
  508.  * we need to seek forward from the beginning.  Otherwise, we can seek forward
  509.  * from the internal cursor.
  510.  */
  511. if ((offset > 0) &&
  512.    ((byte_count = alenlist_cursor_offset(alenlist, (alenlist_cursor_t)NULL)) <= offset)) {
  513. offset -= byte_count;
  514. alenlist_cursor_clone(alenlist, NULL, cursorp);
  515. } else
  516. do_cursor_init(alenlist, cursorp);
  517. /* We could easily speed this up, but it shouldn't be used very often. */
  518. while (offset != 0) {
  519. alenaddr_t addr;
  520. size_t length;
  521. if (alenlist_get(alenlist, cursorp, offset, &addr, &length, 0) != ALENLIST_SUCCESS)
  522. return(ALENLIST_FAILURE);
  523. offset -= length;
  524. }
  525. return(ALENLIST_SUCCESS);
  526. }
  527. /*
  528.  * Copy a cursor.  The source cursor is either an internal alenlist cursor
  529.  * or an explicit cursor.
  530.  */
  531. int
  532. alenlist_cursor_clone( alenlist_t alenlist, 
  533. alenlist_cursor_t cursorp_in, 
  534. alenlist_cursor_t cursorp_out)
  535. {
  536. ASSERT(cursorp_out);
  537. if (alenlist && cursorp_in)
  538. if (alenlist != cursorp_in->al_alenlist)
  539. return(ALENLIST_FAILURE);
  540. if (alenlist)
  541. *cursorp_out = alenlist->al_cursor; /* small structure copy */
  542. else if (cursorp_in)
  543. *cursorp_out = *cursorp_in; /* small structure copy */
  544. else
  545. return(ALENLIST_FAILURE); /* no source */
  546. return(ALENLIST_SUCCESS);
  547. }
  548. /*
  549.  * Return the number of bytes passed so far according to the specified cursor.
  550.  * If cursorp is NULL, use the alenlist's internal cursor.
  551.  */
  552. size_t
  553. alenlist_cursor_offset(alenlist_t alenlist, alenlist_cursor_t cursorp)
  554. {
  555. ASSERT(!alenlist || !cursorp || (alenlist == cursorp->al_alenlist));
  556. if (cursorp == NULL) {
  557. ASSERT(alenlist);
  558. cursorp = &alenlist->al_cursor;
  559. }
  560. return(cursorp->al_offset);
  561. }
  562. /*
  563.  * Allocate and initialize an Address/Length List cursor.
  564.  */
  565. alenlist_cursor_t
  566. alenlist_cursor_create(alenlist_t alenlist, unsigned flags)
  567. {
  568. alenlist_cursor_t cursorp;
  569. ASSERT(alenlist != NULL);
  570. cursorp = kmem_zone_alloc(alenlist_cursor_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
  571. if (cursorp) {
  572. INCR_COUNT(&alenlist_cursor_count);
  573. alenlist_cursor_init(alenlist, 0, cursorp);
  574. }
  575. return(cursorp);
  576. }
  577. /*
  578.  * Free an Address/Length List cursor.
  579.  */
  580. void
  581. alenlist_cursor_destroy(alenlist_cursor_t cursorp)
  582. {
  583. DECR_COUNT(&alenlist_cursor_count);
  584. kmem_zone_free(alenlist_cursor_zone, cursorp);
  585. }
  586. /*
  587.  * Fetch an address/length pair from an Address/Length List.  Update
  588.  * the "cursor" so that next time this routine is called, we'll get
  589.  * the next address range.  Never return a length that exceeds maxlength
  590.  * (if non-zero).  If maxlength is a power of 2, never return a length 
  591.  * that crosses a maxlength boundary.  [This may seem strange at first,
  592.  * but it's what many drivers want.]
  593.  *
  594.  * Returns: SUCCESS/FAILURE
  595.  */
  596. int
  597. alenlist_get( alenlist_t alenlist,  /* in: get from this list */
  598. alenlist_cursor_t cursorp,  /* inout: which item to get */
  599. size_t maxlength, /* in: at most this length */
  600. alenaddr_t *addrp,  /* out: address */
  601. size_t *lengthp, /* out: length */
  602. unsigned flags)
  603. {
  604. alen_t *alenp;
  605. alenlist_chunk_t chunk;
  606. unsigned int index;
  607. size_t bcount;
  608. size_t length;
  609. /* 
  610.  * If no cursor explicitly specified, use the Address/Length List's 
  611.  * internal cursor.
  612.  */
  613. if (cursorp == NULL) {
  614. if (alenlist->al_logical_size == 0)
  615. return(ALENLIST_FAILURE);
  616. cursorp = &alenlist->al_cursor;
  617. }
  618. chunk = cursorp->al_chunk;
  619. index = cursorp->al_index;
  620. bcount = cursorp->al_bcount;
  621. ASSERT(cursorp->al_alenlist == alenlist);
  622. if (cursorp->al_alenlist != alenlist)
  623. return(ALENLIST_FAILURE);
  624. alenp = &chunk->alc_pair[index];
  625. length = alenp->al_length - bcount;
  626. /* Bump up to next pair, if we're done with this pair. */
  627. if (length == 0) {
  628. cursorp->al_bcount = bcount = 0;
  629. cursorp->al_index = index = (index + 1) % ALEN_CHUNK_SZ;
  630. /* Bump up to next chunk, if we're done with this chunk. */
  631. if (index == 0) {
  632. if (cursorp->al_chunk == alenlist->al_last_chunk)
  633. return(ALENLIST_FAILURE);
  634. chunk = chunk->alc_next;
  635. ASSERT(chunk != NULL);
  636. } else {
  637. /* If in last chunk, don't go beyond end. */
  638. if (cursorp->al_chunk == alenlist->al_last_chunk) {
  639. int last_size = alenlist->al_logical_size % ALEN_CHUNK_SZ;
  640. if (last_size && (index >= last_size))
  641. return(ALENLIST_FAILURE);
  642. }
  643. }
  644. alenp = &chunk->alc_pair[index];
  645. length = alenp->al_length;
  646. }
  647. /* Constrain what we return according to maxlength */
  648. if (maxlength) {
  649. size_t maxlen1 = maxlength - 1;
  650. if ((maxlength & maxlen1) == 0) /* power of 2 */
  651. maxlength -= 
  652.    ((alenp->al_addr + cursorp->al_bcount) & maxlen1);
  653. length = MIN(maxlength, length);
  654. }
  655. /* Update the cursor, if desired. */
  656. if (!(flags & AL_LEAVE_CURSOR)) {
  657. cursorp->al_bcount += length;
  658. cursorp->al_chunk = chunk;
  659. }
  660. *lengthp = length;
  661. *addrp = alenp->al_addr + bcount;
  662. return(ALENLIST_SUCCESS);
  663. }
  664. /*
  665.  * Return the number of pairs in the specified Address/Length List.
  666.  * (For FIXED_SIZE Lists, this returns the logical size of the List, 
  667.  * not the actual capacity of the List.)
  668.  */
  669. int
  670. alenlist_size(alenlist_t alenlist)
  671. {
  672. return(alenlist->al_logical_size);
  673. }
  674. /*
  675.  * Concatenate two Address/Length Lists.
  676.  */
  677. void
  678. alenlist_concat(alenlist_t from,
  679. alenlist_t to)
  680. {
  681. struct alenlist_cursor_s cursor;
  682. alenaddr_t addr;
  683. size_t length;
  684. alenlist_cursor_init(from, 0, &cursor);
  685. while(alenlist_get(from, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
  686. alenlist_append(to, addr, length, 0);
  687. }
  688. /*
  689.  * Create a copy of a list.
  690.  * (Not all attributes of the old list are cloned.  For instance, if
  691.  * a FIXED_SIZE list is cloned, the resulting list is NOT FIXED_SIZE.)
  692.  */
  693. alenlist_t
  694. alenlist_clone(alenlist_t old_list, unsigned flags)
  695. {
  696. alenlist_t new_list;
  697. new_list = alenlist_create(flags);
  698. if (new_list != NULL)
  699. alenlist_concat(old_list, new_list);
  700. return(new_list);
  701. }
  702. /* 
  703.  * Convert a kernel virtual address to a Physical Address/Length List.
  704.  */
  705. alenlist_t
  706. kvaddr_to_alenlist(alenlist_t alenlist, caddr_t kvaddr, size_t length, unsigned flags)
  707. {
  708. alenaddr_t paddr;
  709. long offset;
  710. size_t piece_length;
  711. int created_alenlist;
  712. if (length <=0)
  713. return(NULL);
  714. /* If caller supplied a List, use it.  Otherwise, allocate one. */
  715. if (alenlist == NULL) {
  716. alenlist = alenlist_create(0);
  717. created_alenlist = 1;
  718. } else {
  719. alenlist_clear(alenlist);
  720. created_alenlist = 0;
  721. }
  722. paddr = kvtophys(kvaddr);
  723. offset = poff(kvaddr);
  724. /* Handle first page */
  725. piece_length = MIN(NBPP - offset, length);
  726. if (alenlist_append(alenlist, paddr, piece_length, flags) == ALENLIST_FAILURE)
  727. goto failure;
  728. length -= piece_length;
  729. kvaddr += piece_length;
  730. /* Handle middle pages */
  731. while (length >= NBPP) {
  732. paddr = kvtophys(kvaddr);
  733. if (alenlist_append(alenlist, paddr, NBPP, flags) == ALENLIST_FAILURE)
  734. goto failure;
  735. length -= NBPP;
  736. kvaddr += NBPP;
  737. }
  738. /* Handle last page */
  739. if (length) {
  740. ASSERT(length < NBPP);
  741. paddr = kvtophys(kvaddr);
  742. if (alenlist_append(alenlist, paddr, length, flags) == ALENLIST_FAILURE)
  743. goto failure;
  744. }
  745. alenlist_cursor_init(alenlist, 0, NULL);
  746. return(alenlist);
  747. failure:
  748. if (created_alenlist)
  749. alenlist_destroy(alenlist);
  750. return(NULL);
  751. }
  752. #if DEBUG
  753. static void
  754. alenlist_show(alenlist_t alenlist)
  755. {
  756. struct alenlist_cursor_s cursor;
  757. alenaddr_t addr;
  758. size_t length;
  759. int i = 0;
  760. alenlist_cursor_init(alenlist, 0, &cursor);
  761. qprintf("Address/Length List@0x%x:n", alenlist);
  762. qprintf("logical size=0x%x actual size=0x%x last_chunk at 0x%xn", 
  763. alenlist->al_logical_size, alenlist->al_actual_size, 
  764. alenlist->al_last_chunk);
  765. qprintf("cursor: chunk=0x%x index=%d offset=0x%xn",
  766. alenlist->al_cursor.al_chunk, 
  767. alenlist->al_cursor.al_index,
  768. alenlist->al_cursor.al_bcount);
  769. while(alenlist_get(alenlist, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
  770. qprintf("%d:t0x%lx 0x%lxn", ++i, addr, length);
  771. }
  772. #endif /* DEBUG */