aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorImre Deak <imre.deak@intel.com>2013-02-27 20:02:56 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-27 22:10:10 -0500
commita321e91b6d73ed011ffceed384c40d2785cf723b (patch)
tree525598b796028211e615676f5e3182c630f992f1
parent5d96bf4d928c883635774d0aba1773f275de055c (diff)
lib/scatterlist: add simple page iterator
Add an iterator to walk through a scatter list a page at a time starting at a specific page offset. As opposed to the mapping iterator this is meant to be small, performing well even in simple loops like collecting all pages on the scatterlist into an array or setting up an iommu table based on the pages' DMA address. Signed-off-by: Imre Deak <imre.deak@intel.com> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Tejun Heo <tj@kernel.org> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Tested-by: Stephen Warren <swarren@wwwdotorg.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/scatterlist.h35
-rw-r--r--lib/scatterlist.c38
2 files changed, 73 insertions, 0 deletions
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 4bd6c06eb28e..788a853aa7a7 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -231,6 +231,41 @@ size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
231 */ 231 */
232#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) 232#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
233 233
234/*
235 * sg page iterator
236 *
237 * Iterates over sg entries page-by-page. On each successful iteration,
238 * @piter->page points to the current page, @piter->sg to the sg holding this
239 * page and @piter->sg_pgoffset to the page's page offset within the sg. The
240 * iteration will stop either when a maximum number of sg entries was reached
241 * or a terminating sg (sg_last(sg) == true) was reached.
242 */
243struct sg_page_iter {
244 struct page *page; /* current page */
245 struct scatterlist *sg; /* sg holding the page */
246 unsigned int sg_pgoffset; /* page offset within the sg */
247
248 /* these are internal states, keep away */
249 unsigned int __nents; /* remaining sg entries */
250 int __pg_advance; /* nr pages to advance at the
251 * next step */
252};
253
254bool __sg_page_iter_next(struct sg_page_iter *piter);
255void __sg_page_iter_start(struct sg_page_iter *piter,
256 struct scatterlist *sglist, unsigned int nents,
257 unsigned long pgoffset);
258
259/**
260 * for_each_sg_page - iterate over the pages of the given sg list
261 * @sglist: sglist to iterate over
262 * @piter: page iterator to hold current page, sg, sg_pgoffset
263 * @nents: maximum number of sg entries to iterate over
264 * @pgoffset: starting page offset
265 */
266#define for_each_sg_page(sglist, piter, nents, pgoffset) \
267 for (__sg_page_iter_start((piter), (sglist), (nents), (pgoffset)); \
268 __sg_page_iter_next(piter);)
234 269
235/* 270/*
236 * Mapping sg iterator 271 * Mapping sg iterator
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 7874b01e816e..a1d15647d7db 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -394,6 +394,44 @@ int sg_alloc_table_from_pages(struct sg_table *sgt,
394} 394}
395EXPORT_SYMBOL(sg_alloc_table_from_pages); 395EXPORT_SYMBOL(sg_alloc_table_from_pages);
396 396
397void __sg_page_iter_start(struct sg_page_iter *piter,
398 struct scatterlist *sglist, unsigned int nents,
399 unsigned long pgoffset)
400{
401 piter->__pg_advance = 0;
402 piter->__nents = nents;
403
404 piter->page = NULL;
405 piter->sg = sglist;
406 piter->sg_pgoffset = pgoffset;
407}
408EXPORT_SYMBOL(__sg_page_iter_start);
409
410static int sg_page_count(struct scatterlist *sg)
411{
412 return PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT;
413}
414
415bool __sg_page_iter_next(struct sg_page_iter *piter)
416{
417 if (!piter->__nents || !piter->sg)
418 return false;
419
420 piter->sg_pgoffset += piter->__pg_advance;
421 piter->__pg_advance = 1;
422
423 while (piter->sg_pgoffset >= sg_page_count(piter->sg)) {
424 piter->sg_pgoffset -= sg_page_count(piter->sg);
425 piter->sg = sg_next(piter->sg);
426 if (!--piter->__nents || !piter->sg)
427 return false;
428 }
429 piter->page = nth_page(sg_page(piter->sg), piter->sg_pgoffset);
430
431 return true;
432}
433EXPORT_SYMBOL(__sg_page_iter_next);
434
397/** 435/**
398 * sg_miter_start - start mapping iteration over a sg list 436 * sg_miter_start - start mapping iteration over a sg list
399 * @miter: sg mapping iter to be started 437 * @miter: sg mapping iter to be started