diff options
| author | John W. Linville <linville@tuxdriver.com> | 2012-09-07 15:07:55 -0400 | 
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2012-09-07 15:07:55 -0400 | 
| commit | fac805f8c198092de9a2842efd7f5022e2937b18 (patch) | |
| tree | 7557809c373f97a343c427d8fded0696060394ce /lib/scatterlist.c | |
| parent | 2461c7d60f9f3821274e4acf9019cba8b82c94b5 (diff) | |
| parent | f10723841e624c0726c70356b31d91befed01dd6 (diff) | |
| download | olio-linux-3.10-fac805f8c198092de9a2842efd7f5022e2937b18.tar.xz olio-linux-3.10-fac805f8c198092de9a2842efd7f5022e2937b18.zip  | |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
Diffstat (limited to 'lib/scatterlist.c')
| -rw-r--r-- | lib/scatterlist.c | 72 | 
1 files changed, 64 insertions, 8 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 6096e89bee5..fadae774a20 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -279,14 +279,6 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,  		if (!left)  			sg_mark_end(&sg[sg_size - 1]); -		/* -		 * only really needed for mempool backed sg allocations (like -		 * SCSI), a possible improvement here would be to pass the -		 * table pointer into the allocator and let that clear these -		 * flags -		 */ -		gfp_mask &= ~__GFP_WAIT; -		gfp_mask |= __GFP_HIGH;  		prv = sg;  	} while (left); @@ -319,6 +311,70 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)  EXPORT_SYMBOL(sg_alloc_table);  /** + * sg_alloc_table_from_pages - Allocate and initialize an sg table from + *			       an array of pages + * @sgt:	The sg table header to use + * @pages:	Pointer to an array of page pointers + * @n_pages:	Number of pages in the pages array + * @offset:     Offset from start of the first page to the start of a buffer + * @size:       Number of valid bytes in the buffer (after offset) + * @gfp_mask:	GFP allocation mask + * + *  Description: + *    Allocate and initialize an sg table from a list of pages. Contiguous + *    ranges of the pages are squashed into a single scatterlist node. A user + *    may provide an offset at a start and a size of valid data in a buffer + *    specified by the page array. The returned sg table is released by + *    sg_free_table. + * + * Returns: + *   0 on success, negative error on failure + */ +int sg_alloc_table_from_pages(struct sg_table *sgt, +	struct page **pages, unsigned int n_pages, +	unsigned long offset, unsigned long size, +	gfp_t gfp_mask) +{ +	unsigned int chunks; +	unsigned int i; +	unsigned int cur_page; +	int ret; +	struct scatterlist *s; + +	/* compute number of contiguous chunks */ +	chunks = 1; +	for (i = 1; i < n_pages; ++i) +		if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) +			++chunks; + +	ret = sg_alloc_table(sgt, chunks, gfp_mask); +	if (unlikely(ret)) +		return ret; + +	/* merging chunks and putting them into the scatterlist */ +	cur_page = 0; +	for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { +		unsigned long chunk_size; +		unsigned int j; + +		/* look for the end of the current chunk */ +		for (j = cur_page + 1; j < n_pages; ++j) +			if (page_to_pfn(pages[j]) != +			    page_to_pfn(pages[j - 1]) + 1) +				break; + +		chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; +		sg_set_page(s, pages[cur_page], min(size, chunk_size), offset); +		size -= chunk_size; +		offset = 0; +		cur_page = j; +	} + +	return 0; +} +EXPORT_SYMBOL(sg_alloc_table_from_pages); + +/**   * sg_miter_start - start mapping iteration over a sg list   * @miter: sg mapping iter to be started   * @sgl: sg list to iterate over  |