⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 alenlist.c

📁 上传linux-jx2410的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: alenlist.c,v 1.1.1.1 2004/02/04 12:55:32 laputa Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Colin Ngam *//* Implementation of Address/Length Lists. */#include <linux/types.h>#include <linux/slab.h>#include <asm/sn/sgi.h>#include <asm/sn/alenlist.h>#include <asm/sn/mmzone_sn1.h>/* * Logically, an Address/Length List is a list of Pairs, where each pair * holds an Address and a Length, all in some Address Space.  In this * context, "Address Space" is a particular Crosstalk Widget address * space, a PCI device address space, a VME bus address space, a * physical memory address space, etc. * * The main use for these Lists is to provide a single mechanism that * describes where in an address space a DMA occurs.  This allows the * various I/O Bus support layers to provide a single interface for * DMA mapping and DMA translation without regard to how the DMA target * was specified by upper layers.  The upper layers commonly specify a  * DMA target via a buf structure page list, a kernel virtual address, * a user virtual address, a vector of addresses (a la uio and iov),  * or possibly a pfn list. * * Address/Length Lists also enable drivers to take advantage of their * inate scatter/gather capabilities in systems where some address * translation may be required between bus adapters.  The driver forms * a List that represents physical memory targets.  This list is passed * to the various adapters, which apply various translations.  The final * list that's returned to the driver is in terms of its local address * address space -- addresses which can be passed off to a scatter/gather * capable DMA controller. * * The current implementation is intended to be useful both in kernels * that support interrupt threads (INTR_KTHREAD) and in systems that do * not support interrupt threads.  Of course, in the latter case, some * interfaces can be called only within a suspendable context. * * Basic operations on Address/Length Lists include: *	alenlist_create		Create a list *	alenlist_clear		Clear a list *	alenlist_destroy	Destroy a list *	alenlist_append		Append a Pair to the end of a list *	alenlist_replace	Replace a Pair in the middle of a list *	alenlist_get		Get an Address/Length Pair from a list *	alenlist_size		Return the number of Pairs in a list *	alenlist_concat		Append one list to the end of another *	alenlist_clone		Create a new copy of a list * * Operations that convert from upper-level specifications to Address/ * Length Lists currently include: *	kvaddr_to_alenlist	Convert from a kernel virtual address *	uvaddr_to_alenlist	Convert from a user virtual address *	buf_to_alenlist		Convert from a buf structure *	alenlist_done		Tell system that we're done with an alenlist *				obtained from a conversion. * Additional convenience operations: *	alenpair_init		Create a list and initialize it with a Pair *	alenpair_get		Peek at the first pair on a List * * A supporting type for Address/Length Lists is an alenlist_cursor_t.  A * cursor marks a position in a List, and determines which Pair is fetched * by alenlist_get. *	alenlist_cursor_create	Allocate and initialize a cursor *	alenlist_cursor_destroy	Free space consumed by a cursor *	alenlist_cursor_init	(Re-)Initialize a cursor to point  *				to the start of a list *	alenlist_cursor_clone	Clone a cursor (at the current offset) *	alenlist_cursor_offset	Return the number of bytes into *				a list that this cursor marks * Multiple cursors can point at various points into a List.  Also, each * list maintains one "internal cursor" which may be updated by alenlist_clear * and alenlist_get.  If calling code simply wishes to scan sequentially * through a list starting at the beginning, and if it is the only user of * a list, it can rely on this internal cursor rather than managing a  * separate explicit cursor. * * The current implementation allows callers to allocate both cursors and * the lists as local stack (structure) variables.  This allows for some * extra efficiency at the expense of forward binary compatibility.  It  * is recommended that customer drivers refrain from local allocation. * In fact, we likely will choose to move the structures out of the public  * header file into a private place in order to discourage this usage. * * Currently, no locking is provided by the alenlist implementation. * * Implementation notes: * For efficiency, Pairs are grouped into "chunks" of, say, 32 Pairs * and a List consists of some number of these chunks.  Chunks are completely * invisible to calling code.  Chunks should be large enough to hold most * standard-sized DMA's, but not so large that they consume excessive space. * * It is generally expected that Lists will be constructed at one time and * scanned at a later time.  It is NOT expected that drivers will scan * a List while the List is simultaneously extended, although this is * theoretically possible with sufficient upper-level locking. * * In order to support demands of Real-Time drivers and in order to support * swapping under low-memory conditions, we support the concept of a * "pre-allocated fixed-sized List".  After creating a List with  * alenlist_create, a driver may explicitly grow the list (via "alenlist_grow") * to a specific number of Address/Length pairs.  It is guaranteed that future  * operations involving this list will never automatically grow the list  * (i.e. if growth is ever required, the operation will fail).  Additionally,  * operations that use alenlist's (e.g. DMA operations) accept a flag which  * causes processing to take place "in-situ"; that is, the input alenlist  * entries are replaced with output alenlist entries.  The combination of  * pre-allocated Lists and in-situ processing allows us to avoid the  * potential deadlock scenario where we sleep (waiting for memory) in the  * swap out path. * * For debugging, we track the number of allocated Lists in alenlist_count * the number of allocated chunks in alenlist_chunk_count, and the number * of allocate cursors in alenlist_cursor_count.  We also provide a debug  * routine, alenlist_show, which dumps the contents of an Address/Length List. * * Currently, Lists are formed by drivers on-demand.  Eventually, we may * associate an alenlist with a buf structure and keep it up to date as * we go along.  In that case, buf_to_alenlist simply returns a pointer * to the existing List, and increments the Lists's reference count. * alenlist_done would decrement the reference count and destroys the List * if it was the last reference. * * Eventually alenlist's may allow better support for user-level scatter/ * gather operations (e.g. via readv/writev):  With proper support, we * could potentially handle a vector of reads with a single scatter/gather * DMA operation.  This could be especially useful on NUMA systems where * there's more of a reason for users to use vector I/O operations. * * Eventually, alenlist's may replace kaio lists, vhand page lists, * buffer cache pfdat lists, DMA page lists, etc. *//* Opaque data types *//* An Address/Length pair.  */typedef struct alen_s {	alenaddr_t	al_addr;	size_t		al_length;} alen_t;/*  * Number of elements in one chunk of an Address/Length List. * * This size should be sufficient to hold at least an "average" size * DMA request.  Must be at least 1, and should be a power of 2, * for efficiency. */#define ALEN_CHUNK_SZ ((512*1024)/NBPP)/* * A fixed-size set of Address/Length Pairs.  Chunks of Pairs are strung together  * to form a complete Address/Length List.  Chunking is entirely hidden within the  * alenlist implementation, and it simply makes allocation and growth of lists more  * efficient. */typedef struct alenlist_chunk_s {	alen_t			alc_pair[ALEN_CHUNK_SZ];/* list of addr/len pairs */	struct alenlist_chunk_s *alc_next;		/* point to next chunk of pairs */} *alenlist_chunk_t;/*  * An Address/Length List.  An Address/Length List is allocated with alenlist_create.   * Alternatively, a list can be allocated on the stack (local variable of type  * alenlist_t) and initialized with alenpair_init or with a combination of  * alenlist_clear and alenlist_append, etc.  Code which statically allocates these * structures loses forward binary compatibility! * * A statically allocated List is sufficiently large to hold ALEN_CHUNK_SZ pairs. */struct alenlist_s {	unsigned short		al_flags;	unsigned short		al_logical_size;	/* logical size of list, in pairs */	unsigned short		al_actual_size;		/* actual size of list, in pairs */	struct alenlist_chunk_s	*al_last_chunk;		/* pointer to last logical chunk */	struct alenlist_cursor_s al_cursor;		/* internal cursor */	struct alenlist_chunk_s	al_chunk;		/* initial set of pairs */	alenaddr_t		al_compaction_address;	/* used to compact pairs */};/* al_flags field */#define AL_FIXED_SIZE	0x1	/* List is pre-allocated, and of fixed size */zone_t *alenlist_zone = NULL;zone_t *alenlist_chunk_zone = NULL;zone_t *alenlist_cursor_zone = NULL;#if DEBUGint alenlist_count=0;		/* Currently allocated Lists */int alenlist_chunk_count = 0;	/* Currently allocated chunks */int alenlist_cursor_count = 0;	/* Currently allocate cursors */#define INCR_COUNT(ptr) atomic_inc((ptr));#define DECR_COUNT(ptr) atomic_dec((ptr));#else#define INCR_COUNT(ptr)#define DECR_COUNT(ptr)#endif /* DEBUG */#if DEBUGstatic void alenlist_show(alenlist_t);#endif /* DEBUG *//* * Initialize Address/Length List management.  One time initialization. */voidalenlist_init(void){	alenlist_zone = kmem_zone_init(sizeof(struct alenlist_s), "alenlist");	alenlist_chunk_zone = kmem_zone_init(sizeof(struct alenlist_chunk_s), "alchunk");	alenlist_cursor_zone = kmem_zone_init(sizeof(struct alenlist_cursor_s), "alcursor");#if DEBUG	idbg_addfunc("alenshow", alenlist_show);#endif /* DEBUG */}/* * Initialize an Address/Length List cursor. */static voiddo_cursor_init(alenlist_t alenlist, alenlist_cursor_t cursorp){	cursorp->al_alenlist = alenlist;	cursorp->al_offset = 0;	cursorp->al_chunk = &alenlist->al_chunk;	cursorp->al_index = 0;	cursorp->al_bcount = 0;}/* * Create an Address/Length List, and clear it. * Set the cursor to the beginning. */alenlist_t alenlist_create(unsigned flags){	alenlist_t alenlist;	alenlist = kmem_zone_alloc(alenlist_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);	if (alenlist) {		INCR_COUNT(&alenlist_count);		alenlist->al_flags = 0;		alenlist->al_logical_size = 0;		alenlist->al_actual_size = ALEN_CHUNK_SZ;		alenlist->al_last_chunk = &alenlist->al_chunk;		alenlist->al_chunk.alc_next = NULL;		do_cursor_init(alenlist, &alenlist->al_cursor);	}	return(alenlist);}/* * Grow an Address/Length List so that all resources needed to contain * the specified number of Pairs are pre-allocated.  An Address/Length * List that has been explicitly "grown" will never *automatically* * grow, shrink, or be destroyed. * * Pre-allocation is useful for Real-Time drivers and for drivers that * may be used along the swap-out path and therefore cannot afford to  * sleep until memory is freed. *  * The cursor is set to the beginning of the list. */intalenlist_grow(alenlist_t alenlist, size_t npairs){	/* 	 * This interface should be used relatively rarely, so	 * the implementation is kept simple: We clear the List,	 * then append npairs bogus entries.  Finally, we mark	 * the list as FIXED_SIZE and re-initialize the internal	 * cursor.	 */	/* 	 * Temporarily mark as non-fixed size, since we're about	 * to shrink and expand it.	 */	alenlist->al_flags &= ~AL_FIXED_SIZE;	/* Free whatever was in the alenlist. */	alenlist_clear(alenlist);	/* Allocate everything that we need via automatic expansion. */	while (npairs--)		if (alenlist_append(alenlist, 0, 0, AL_NOCOMPACT) == ALENLIST_FAILURE)			return(ALENLIST_FAILURE);	/* Now, mark as FIXED_SIZE */	alenlist->al_flags |= AL_FIXED_SIZE;	/* Clear out bogus entries */	alenlist_clear(alenlist);	/* Initialize internal cursor to the beginning */	do_cursor_init(alenlist, &alenlist->al_cursor);	return(ALENLIST_SUCCESS);}/* * Clear an Address/Length List so that it holds no pairs. */voidalenlist_clear(alenlist_t alenlist){	alenlist_chunk_t chunk, freechunk;	/*	 * If this List is not FIXED_SIZE, free all the	 * extra chunks.	 */	if (!(alenlist->al_flags & AL_FIXED_SIZE)) {		/* First, free any extension alenlist chunks */		chunk = alenlist->al_chunk.alc_next;		while (chunk) {			freechunk = chunk;			chunk = chunk->alc_next;			kmem_zone_free(alenlist_chunk_zone, freechunk);			DECR_COUNT(&alenlist_chunk_count);		}		alenlist->al_actual_size = ALEN_CHUNK_SZ;		alenlist->al_chunk.alc_next = NULL;	}	alenlist->al_logical_size = 0;	alenlist->al_last_chunk = &alenlist->al_chunk;	do_cursor_init(alenlist, &alenlist->al_cursor);}/* * Create and initialize an Address/Length Pair. * This is intended for degenerate lists, consisting of a single  * address/length pair. */alenlist_talenpair_init(	alenaddr_t address, 		size_t length){	alenlist_t alenlist;	alenlist = alenlist_create(0);	alenlist->al_logical_size = 1;	ASSERT(alenlist->al_last_chunk == &alenlist->al_chunk);	alenlist->al_chunk.alc_pair[0].al_length = length;	alenlist->al_chunk.alc_pair[0].al_addr = address;	return(alenlist);}/* * Return address/length from a degenerate (1-pair) List, or * first pair from a larger list.  Does NOT update the internal cursor, * so this is an easy way to peek at a start address. */intalenpair_get(	alenlist_t alenlist,		alenaddr_t *address,		size_t *length){	if (alenlist->al_logical_size == 0)		return(ALENLIST_FAILURE);	*length = alenlist->al_chunk.alc_pair[0].al_length;	*address = alenlist->al_chunk.alc_pair[0].al_addr;	return(ALENLIST_SUCCESS);}/* * Destroy an Address/Length List. */void alenlist_destroy(alenlist_t alenlist){	if (alenlist == NULL)		return;	/* 	 * Turn off FIXED_SIZE so this List can be 	 * automatically shrunk.	 */	alenlist->al_flags &= ~AL_FIXED_SIZE;	/* Free extension chunks first */	if (alenlist->al_chunk.alc_next)		alenlist_clear(alenlist);	/* Now, free the alenlist itself */	kmem_zone_free(alenlist_zone, alenlist);	DECR_COUNT(&alenlist_count);}/* * Release an Address/Length List. * This is in preparation for a day when alenlist's may be longer-lived, and * perhaps associated with a buf structure.  We'd add a reference count, and * this routine would decrement the count.  For now, we create alenlist's on * on demand and free them when done.  If the driver is not explicitly managing * a List for its own use, it should call alenlist_done rather than alenlist_destroy. */voidalenlist_done(alenlist_t alenlist){	alenlist_destroy(alenlist);}/* * Append another address/length to the end of an Address/Length List, * growing the list if permitted and necessary. * * Returns: SUCCESS/FAILURE */int alenlist_append(	alenlist_t alenlist, 		/* append to this list */			alenaddr_t address, 		/* address to append */			size_t length,			/* length to append */			unsigned flags){	alen_t *alenp;	int index, last_index;	index = alenlist->al_logical_size % ALEN_CHUNK_SZ;	if ((alenlist->al_logical_size > 0)) {		/*		 * See if we can compact this new pair in with the previous entry.		 * al_compaction_address holds that value that we'd need to see		 * in order to compact.		 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -