aboutsummaryrefslogtreecommitdiff
path: root/lib/scatterlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/scatterlist.c')
-rw-r--r--lib/scatterlist.c86
1 files changed, 61 insertions, 25 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 7874b01e816..b83c144d731 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -394,6 +394,44 @@ int sg_alloc_table_from_pages(struct sg_table *sgt,
}
EXPORT_SYMBOL(sg_alloc_table_from_pages);
+void __sg_page_iter_start(struct sg_page_iter *piter,
+ struct scatterlist *sglist, unsigned int nents,
+ unsigned long pgoffset)
+{
+ piter->__pg_advance = 0;
+ piter->__nents = nents;
+
+ piter->page = NULL;
+ piter->sg = sglist;
+ piter->sg_pgoffset = pgoffset;
+}
+EXPORT_SYMBOL(__sg_page_iter_start);
+
+static int sg_page_count(struct scatterlist *sg)
+{
+ return PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT;
+}
+
+bool __sg_page_iter_next(struct sg_page_iter *piter)
+{
+ if (!piter->__nents || !piter->sg)
+ return false;
+
+ piter->sg_pgoffset += piter->__pg_advance;
+ piter->__pg_advance = 1;
+
+ while (piter->sg_pgoffset >= sg_page_count(piter->sg)) {
+ piter->sg_pgoffset -= sg_page_count(piter->sg);
+ piter->sg = sg_next(piter->sg);
+ if (!--piter->__nents || !piter->sg)
+ return false;
+ }
+ piter->page = nth_page(sg_page(piter->sg), piter->sg_pgoffset);
+
+ return true;
+}
+EXPORT_SYMBOL(__sg_page_iter_next);
+
/**
* sg_miter_start - start mapping iteration over a sg list
* @miter: sg mapping iter to be started
@@ -411,9 +449,7 @@ void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl,
{
memset(miter, 0, sizeof(struct sg_mapping_iter));
- miter->__sg = sgl;
- miter->__nents = nents;
- miter->__offset = 0;
+ __sg_page_iter_start(&miter->piter, sgl, nents, 0);
WARN_ON(!(flags & (SG_MITER_TO_SG | SG_MITER_FROM_SG)));
miter->__flags = flags;
}
@@ -438,36 +474,35 @@ EXPORT_SYMBOL(sg_miter_start);
*/
bool sg_miter_next(struct sg_mapping_iter *miter)
{
- unsigned int off, len;
-
- /* check for end and drop resources from the last iteration */
- if (!miter->__nents)
- return false;
-
sg_miter_stop(miter);
- /* get to the next sg if necessary. __offset is adjusted by stop */
- while (miter->__offset == miter->__sg->length) {
- if (--miter->__nents) {
- miter->__sg = sg_next(miter->__sg);
- miter->__offset = 0;
- } else
+ /*
+ * Get to the next page if necessary.
+ * __remaining, __offset is adjusted by sg_miter_stop
+ */
+ if (!miter->__remaining) {
+ struct scatterlist *sg;
+ unsigned long pgoffset;
+
+ if (!__sg_page_iter_next(&miter->piter))
return false;
- }
- /* map the next page */
- off = miter->__sg->offset + miter->__offset;
- len = miter->__sg->length - miter->__offset;
+ sg = miter->piter.sg;
+ pgoffset = miter->piter.sg_pgoffset;
- miter->page = nth_page(sg_page(miter->__sg), off >> PAGE_SHIFT);
- off &= ~PAGE_MASK;
- miter->length = min_t(unsigned int, len, PAGE_SIZE - off);
- miter->consumed = miter->length;
+ miter->__offset = pgoffset ? 0 : sg->offset;
+ miter->__remaining = sg->offset + sg->length -
+ (pgoffset << PAGE_SHIFT) - miter->__offset;
+ miter->__remaining = min_t(unsigned long, miter->__remaining,
+ PAGE_SIZE - miter->__offset);
+ }
+ miter->page = miter->piter.page;
+ miter->consumed = miter->length = miter->__remaining;
if (miter->__flags & SG_MITER_ATOMIC)
- miter->addr = kmap_atomic(miter->page) + off;
+ miter->addr = kmap_atomic(miter->page) + miter->__offset;
else
- miter->addr = kmap(miter->page) + off;
+ miter->addr = kmap(miter->page) + miter->__offset;
return true;
}
@@ -494,6 +529,7 @@ void sg_miter_stop(struct sg_mapping_iter *miter)
/* drop resources from the last iteration */
if (miter->addr) {
miter->__offset += miter->consumed;
+ miter->__remaining -= miter->consumed;
if (miter->__flags & SG_MITER_TO_SG)
flush_kernel_dcache_page(miter->page);