[xarray] iov_iter_npages(): just use DIV_ROUND_UP()
[linux-2.6-microblaze.git] / lib / iov_iter.c
index 2b543be..72c5bb7 100644 (file)
@@ -1328,50 +1328,94 @@ void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count)
 }
 EXPORT_SYMBOL(iov_iter_discard);
 
-unsigned long iov_iter_alignment(const struct iov_iter *i)
+static unsigned long iov_iter_alignment_iovec(const struct iov_iter *i)
 {
        unsigned long res = 0;
        size_t size = i->count;
+       size_t skip = i->iov_offset;
+       unsigned k;
+
+       for (k = 0; k < i->nr_segs; k++, skip = 0) {
+               size_t len = i->iov[k].iov_len - skip;
+               if (len) {
+                       res |= (unsigned long)i->iov[k].iov_base + skip;
+                       if (len > size)
+                               len = size;
+                       res |= len;
+                       size -= len;
+                       if (!size)
+                               break;
+               }
+       }
+       return res;
+}
 
-       if (unlikely(iov_iter_is_pipe(i))) {
+static unsigned long iov_iter_alignment_bvec(const struct iov_iter *i)
+{
+       unsigned res = 0;
+       size_t size = i->count;
+       unsigned skip = i->iov_offset;
+       unsigned k;
+
+       for (k = 0; k < i->nr_segs; k++, skip = 0) {
+               size_t len = i->bvec[k].bv_len - skip;
+               res |= (unsigned long)i->bvec[k].bv_offset + skip;
+               if (len > size)
+                       len = size;
+               res |= len;
+               size -= len;
+               if (!size)
+                       break;
+       }
+       return res;
+}
+
+unsigned long iov_iter_alignment(const struct iov_iter *i)
+{
+       /* iovec and kvec have identical layouts */
+       if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i)))
+               return iov_iter_alignment_iovec(i);
+
+       if (iov_iter_is_bvec(i))
+               return iov_iter_alignment_bvec(i);
+
+       if (iov_iter_is_pipe(i)) {
                unsigned int p_mask = i->pipe->ring_size - 1;
+               size_t size = i->count;
 
                if (size && i->iov_offset && allocated(&i->pipe->bufs[i->head & p_mask]))
                        return size | i->iov_offset;
                return size;
        }
-       if (unlikely(iov_iter_is_xarray(i)))
+
+       if (iov_iter_is_xarray(i))
                return (i->xarray_start + i->iov_offset) | i->count;
-       iterate_all_kinds(i, size, v,
-               (res |= (unsigned long)v.iov_base | v.iov_len, 0),
-               res |= v.bv_offset | v.bv_len,
-               res |= (unsigned long)v.iov_base | v.iov_len,
-               res |= v.bv_offset | v.bv_len
-       )
-       return res;
+
+       return 0;
 }
 EXPORT_SYMBOL(iov_iter_alignment);
 
 unsigned long iov_iter_gap_alignment(const struct iov_iter *i)
 {
        unsigned long res = 0;
+       unsigned long v = 0;
        size_t size = i->count;
+       unsigned k;
 
-       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
-               WARN_ON(1);
+       if (WARN_ON(!iter_is_iovec(i)))
                return ~0U;
-       }
 
-       iterate_all_kinds(i, size, v,
-               (res |= (!res ? 0 : (unsigned long)v.iov_base) |
-                       (size != v.iov_len ? size : 0), 0),
-               (res |= (!res ? 0 : (unsigned long)v.bv_offset) |
-                       (size != v.bv_len ? size : 0)),
-               (res |= (!res ? 0 : (unsigned long)v.iov_base) |
-                       (size != v.iov_len ? size : 0)),
-               (res |= (!res ? 0 : (unsigned long)v.bv_offset) |
-                       (size != v.bv_len ? size : 0))
-               );
+       for (k = 0; k < i->nr_segs; k++) {
+               if (i->iov[k].iov_len) {
+                       unsigned long base = (unsigned long)i->iov[k].iov_base;
+                       if (v) // if not the first one
+                               res |= base | v; // this start | previous end
+                       v = base + i->iov[k].iov_len;
+                       if (size <= i->iov[k].iov_len)
+                               break;
+                       size -= i->iov[k].iov_len;
+               }
+       }
        return res;
 }
 EXPORT_SYMBOL(iov_iter_gap_alignment);
@@ -1406,9 +1450,6 @@ static ssize_t pipe_get_pages(struct iov_iter *i,
        unsigned int iter_head, npages;
        size_t capacity;
 
-       if (!maxsize)
-               return 0;
-
        if (!sanity(i))
                return -EFAULT;
 
@@ -1489,29 +1530,67 @@ static ssize_t iter_xarray_get_pages(struct iov_iter *i,
        return actual;
 }
 
+/* must be done on non-empty ITER_IOVEC one */
+static unsigned long first_iovec_segment(const struct iov_iter *i,
+                                        size_t *size, size_t *start,
+                                        size_t maxsize, unsigned maxpages)
+{
+       size_t skip;
+       long k;
+
+       for (k = 0, skip = i->iov_offset; k < i->nr_segs; k++, skip = 0) {
+               unsigned long addr = (unsigned long)i->iov[k].iov_base + skip;
+               size_t len = i->iov[k].iov_len - skip;
+
+               if (unlikely(!len))
+                       continue;
+               if (len > maxsize)
+                       len = maxsize;
+               len += (*start = addr % PAGE_SIZE);
+               if (len > maxpages * PAGE_SIZE)
+                       len = maxpages * PAGE_SIZE;
+               *size = len;
+               return addr & PAGE_MASK;
+       }
+       BUG(); // if it had been empty, we wouldn't get called
+}
+
+/* must be done on non-empty ITER_BVEC one */
+static struct page *first_bvec_segment(const struct iov_iter *i,
+                                      size_t *size, size_t *start,
+                                      size_t maxsize, unsigned maxpages)
+{
+       struct page *page;
+       size_t skip = i->iov_offset, len;
+
+       len = i->bvec->bv_len - skip;
+       if (len > maxsize)
+               len = maxsize;
+       skip += i->bvec->bv_offset;
+       page = i->bvec->bv_page + skip / PAGE_SIZE;
+       len += (*start = skip % PAGE_SIZE);
+       if (len > maxpages * PAGE_SIZE)
+               len = maxpages * PAGE_SIZE;
+       *size = len;
+       return page;
+}
+
 ssize_t iov_iter_get_pages(struct iov_iter *i,
                   struct page **pages, size_t maxsize, unsigned maxpages,
                   size_t *start)
 {
+       size_t len;
+       int n, res;
+
        if (maxsize > i->count)
                maxsize = i->count;
+       if (!maxsize)
+               return 0;
 
-       if (unlikely(iov_iter_is_pipe(i)))
-               return pipe_get_pages(i, pages, maxsize, maxpages, start);
-       if (unlikely(iov_iter_is_xarray(i)))
-               return iter_xarray_get_pages(i, pages, maxsize, maxpages, start);
-       if (unlikely(iov_iter_is_discard(i)))
-               return -EFAULT;
-
-       iterate_all_kinds(i, maxsize, v, ({
-               unsigned long addr = (unsigned long)v.iov_base;
-               size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
-               int n;
-               int res;
+       if (likely(iter_is_iovec(i))) {
+               unsigned long addr;
 
-               if (len > maxpages * PAGE_SIZE)
-                       len = maxpages * PAGE_SIZE;
-               addr &= ~(PAGE_SIZE - 1);
+               addr = first_iovec_segment(i, &len, start, maxsize, maxpages);
                n = DIV_ROUND_UP(len, PAGE_SIZE);
                res = get_user_pages_fast(addr, n,
                                iov_iter_rw(i) != WRITE ?  FOLL_WRITE : 0,
@@ -1519,17 +1598,21 @@ ssize_t iov_iter_get_pages(struct iov_iter *i,
                if (unlikely(res < 0))
                        return res;
                return (res == n ? len : res * PAGE_SIZE) - *start;
-       0;}),({
-               /* can't be more than PAGE_SIZE */
-               *start = v.bv_offset;
-               get_page(*pages = v.bv_page);
-               return v.bv_len;
-       }),({
-               return -EFAULT;
-       }),
-       0
-       )
-       return 0;
+       }
+       if (iov_iter_is_bvec(i)) {
+               struct page *page;
+
+               page = first_bvec_segment(i, &len, start, maxsize, maxpages);
+               n = DIV_ROUND_UP(len, PAGE_SIZE);
+               while (n--)
+                       get_page(*pages++ = page++);
+               return len - *start;
+       }
+       if (iov_iter_is_pipe(i))
+               return pipe_get_pages(i, pages, maxsize, maxpages, start);
+       if (iov_iter_is_xarray(i))
+               return iter_xarray_get_pages(i, pages, maxsize, maxpages, start);
+       return -EFAULT;
 }
 EXPORT_SYMBOL(iov_iter_get_pages);
 
@@ -1546,9 +1629,6 @@ static ssize_t pipe_get_pages_alloc(struct iov_iter *i,
        unsigned int iter_head, npages;
        ssize_t n;
 
-       if (!maxsize)
-               return 0;
-
        if (!sanity(i))
                return -EFAULT;
 
@@ -1621,24 +1701,18 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
                   size_t *start)
 {
        struct page **p;
+       size_t len;
+       int n, res;
 
        if (maxsize > i->count)
                maxsize = i->count;
+       if (!maxsize)
+               return 0;
 
-       if (unlikely(iov_iter_is_pipe(i)))
-               return pipe_get_pages_alloc(i, pages, maxsize, start);
-       if (unlikely(iov_iter_is_xarray(i)))
-               return iter_xarray_get_pages_alloc(i, pages, maxsize, start);
-       if (unlikely(iov_iter_is_discard(i)))
-               return -EFAULT;
-
-       iterate_all_kinds(i, maxsize, v, ({
-               unsigned long addr = (unsigned long)v.iov_base;
-               size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
-               int n;
-               int res;
+       if (likely(iter_is_iovec(i))) {
+               unsigned long addr;
 
-               addr &= ~(PAGE_SIZE - 1);
+               addr = first_iovec_segment(i, &len, start, maxsize, ~0U);
                n = DIV_ROUND_UP(len, PAGE_SIZE);
                p = get_pages_array(n);
                if (!p)
@@ -1651,19 +1725,24 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
                }
                *pages = p;
                return (res == n ? len : res * PAGE_SIZE) - *start;
-       0;}),({
-               /* can't be more than PAGE_SIZE */
-               *start = v.bv_offset;
-               *pages = p = get_pages_array(1);
+       }
+       if (iov_iter_is_bvec(i)) {
+               struct page *page;
+
+               page = first_bvec_segment(i, &len, start, maxsize, ~0U);
+               n = DIV_ROUND_UP(len, PAGE_SIZE);
+               *pages = p = get_pages_array(n);
                if (!p)
                        return -ENOMEM;
-               get_page(*p = v.bv_page);
-               return v.bv_len;
-       }),({
-               return -EFAULT;
-       }), 0
-       )
-       return 0;
+               while (n--)
+                       get_page(*p++ = page++);
+               return len - *start;
+       }
+       if (iov_iter_is_pipe(i))
+               return pipe_get_pages_alloc(i, pages, maxsize, start);
+       if (iov_iter_is_xarray(i))
+               return iter_xarray_get_pages_alloc(i, pages, maxsize, start);
+       return -EFAULT;
 }
 EXPORT_SYMBOL(iov_iter_get_pages_alloc);
 
@@ -1785,19 +1864,56 @@ size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp,
 }
 EXPORT_SYMBOL(hash_and_copy_to_iter);
 
-int iov_iter_npages(const struct iov_iter *i, int maxpages)
+static int iov_npages(const struct iov_iter *i, int maxpages)
 {
-       size_t size = i->count;
+       size_t skip = i->iov_offset, size = i->count;
+       const struct iovec *p;
        int npages = 0;
 
-       if (!size)
-               return 0;
-       if (unlikely(iov_iter_is_discard(i)))
-               return 0;
+       for (p = i->iov; size; skip = 0, p++) {
+               unsigned offs = offset_in_page(p->iov_base + skip);
+               size_t len = min(p->iov_len - skip, size);
 
-       if (unlikely(iov_iter_is_pipe(i))) {
-               struct pipe_inode_info *pipe = i->pipe;
+               if (len) {
+                       size -= len;
+                       npages += DIV_ROUND_UP(offs + len, PAGE_SIZE);
+                       if (unlikely(npages > maxpages))
+                               return maxpages;
+               }
+       }
+       return npages;
+}
+
+static int bvec_npages(const struct iov_iter *i, int maxpages)
+{
+       size_t skip = i->iov_offset, size = i->count;
+       const struct bio_vec *p;
+       int npages = 0;
+
+       for (p = i->bvec; size; skip = 0, p++) {
+               unsigned offs = (p->bv_offset + skip) % PAGE_SIZE;
+               size_t len = min(p->bv_len - skip, size);
+
+               size -= len;
+               npages += DIV_ROUND_UP(offs + len, PAGE_SIZE);
+               if (unlikely(npages > maxpages))
+                       return maxpages;
+       }
+       return npages;
+}
+
+int iov_iter_npages(const struct iov_iter *i, int maxpages)
+{
+       if (unlikely(!i->count))
+               return 0;
+       /* iovec and kvec have identical layouts */
+       if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i)))
+               return iov_npages(i, maxpages);
+       if (iov_iter_is_bvec(i))
+               return bvec_npages(i, maxpages);
+       if (iov_iter_is_pipe(i)) {
                unsigned int iter_head;
+               int npages;
                size_t off;
 
                if (!sanity(i))
@@ -1805,44 +1921,15 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
 
                data_start(i, &iter_head, &off);
                /* some of this one + all after this one */
-               npages = pipe_space_for_user(iter_head, pipe->tail, pipe);
-               if (npages >= maxpages)
-                       return maxpages;
-       } else if (unlikely(iov_iter_is_xarray(i))) {
-               unsigned offset;
-
-               offset = (i->xarray_start + i->iov_offset) & ~PAGE_MASK;
-
-               npages = 1;
-               if (size > PAGE_SIZE - offset) {
-                       size -= PAGE_SIZE - offset;
-                       npages += size >> PAGE_SHIFT;
-                       size &= ~PAGE_MASK;
-                       if (size)
-                               npages++;
-               }
-               if (npages >= maxpages)
-                       return maxpages;
-       } else iterate_all_kinds(i, size, v, ({
-               unsigned long p = (unsigned long)v.iov_base;
-               npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
-                       - p / PAGE_SIZE;
-               if (npages >= maxpages)
-                       return maxpages;
-       0;}),({
-               npages++;
-               if (npages >= maxpages)
-                       return maxpages;
-       }),({
-               unsigned long p = (unsigned long)v.iov_base;
-               npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
-                       - p / PAGE_SIZE;
-               if (npages >= maxpages)
-                       return maxpages;
-       }),
-       0
-       )
-       return npages;
+               npages = pipe_space_for_user(iter_head, i->pipe->tail, i->pipe);
+               return min(npages, maxpages);
+       }
+       if (iov_iter_is_xarray(i)) {
+               unsigned offset = (i->xarray_start + i->iov_offset) % PAGE_SIZE;
+               int npages = DIV_ROUND_UP(offset + i->count, PAGE_SIZE);
+               return min(npages, maxpages);
+       }
+       return 0;
 }
 EXPORT_SYMBOL(iov_iter_npages);