|
@@ -318,6 +318,70 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(sg_alloc_table);
|
|
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
|
|
* sg_miter_start - start mapping iteration over a sg list
|
|
* @miter: sg mapping iter to be started
|
|
* @miter: sg mapping iter to be started
|