diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/scatterlist.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 6096e89bee55..e719adf695bf 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c | |||
@@ -319,6 +319,70 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) | |||
319 | EXPORT_SYMBOL(sg_alloc_table); | 319 | EXPORT_SYMBOL(sg_alloc_table); |
320 | 320 | ||
321 | /** | 321 | /** |
322 | * sg_alloc_table_from_pages - Allocate and initialize an sg table from | ||
323 | * an array of pages | ||
324 | * @sgt: The sg table header to use | ||
325 | * @pages: Pointer to an array of page pointers | ||
326 | * @n_pages: Number of pages in the pages array | ||
327 | * @offset: Offset from start of the first page to the start of a buffer | ||
328 | * @size: Number of valid bytes in the buffer (after offset) | ||
329 | * @gfp_mask: GFP allocation mask | ||
330 | * | ||
331 | * Description: | ||
332 | * Allocate and initialize an sg table from a list of pages. Contiguous | ||
333 | * ranges of the pages are squashed into a single scatterlist node. A user | ||
334 | * may provide an offset at a start and a size of valid data in a buffer | ||
335 | * specified by the page array. The returned sg table is released by | ||
336 | * sg_free_table. | ||
337 | * | ||
338 | * Returns: | ||
339 | * 0 on success, negative error on failure | ||
340 | */ | ||
341 | int sg_alloc_table_from_pages(struct sg_table *sgt, | ||
342 | struct page **pages, unsigned int n_pages, | ||
343 | unsigned long offset, unsigned long size, | ||
344 | gfp_t gfp_mask) | ||
345 | { | ||
346 | unsigned int chunks; | ||
347 | unsigned int i; | ||
348 | unsigned int cur_page; | ||
349 | int ret; | ||
350 | struct scatterlist *s; | ||
351 | |||
352 | /* compute number of contiguous chunks */ | ||
353 | chunks = 1; | ||
354 | for (i = 1; i < n_pages; ++i) | ||
355 | if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) | ||
356 | ++chunks; | ||
357 | |||
358 | ret = sg_alloc_table(sgt, chunks, gfp_mask); | ||
359 | if (unlikely(ret)) | ||
360 | return ret; | ||
361 | |||
362 | /* merging chunks and putting them into the scatterlist */ | ||
363 | cur_page = 0; | ||
364 | for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { | ||
365 | unsigned long chunk_size; | ||
366 | unsigned int j; | ||
367 | |||
368 | /* look for the end of the current chunk */ | ||
369 | for (j = cur_page + 1; j < n_pages; ++j) | ||
370 | if (page_to_pfn(pages[j]) != | ||
371 | page_to_pfn(pages[j - 1]) + 1) | ||
372 | break; | ||
373 | |||
374 | chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; | ||
375 | sg_set_page(s, pages[cur_page], min(size, chunk_size), offset); | ||
376 | size -= chunk_size; | ||
377 | offset = 0; | ||
378 | cur_page = j; | ||
379 | } | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | EXPORT_SYMBOL(sg_alloc_table_from_pages); | ||
384 | |||
385 | /** | ||
322 | * sg_miter_start - start mapping iteration over a sg list | 386 | * sg_miter_start - start mapping iteration over a sg list |
323 | * @miter: sg mapping iter to be started | 387 | * @miter: sg mapping iter to be started |
324 | * @sgl: sg list to iterate over | 388 | * @sgl: sg list to iterate over |