]> Pileus Git - ~andy/linux/blobdiff - lib/scatterlist.c
netconf: add the handler to dump entries
[~andy/linux] / lib / scatterlist.c
index 7874b01e816e815466ccee3a08387b075503c72f..b83c144d731f3707402be43e83d2041ab4df82b7 100644 (file)
@@ -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);