iov_iter_npages(): don't bother with iterate_all_kinds()
authorAl Viro <viro@zeniv.linux.org.uk>
Sun, 25 Apr 2021 20:00:48 +0000 (16:00 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 10 Jun 2021 15:45:12 +0000 (11:45 -0400)
note that in bvec case pages can be compound ones - we can't just assume
that each segment is covered by one (sub)page

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
lib/iov_iter.c

index 3fd331b..1c65de1 100644 (file)
@@ -1864,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))
@@ -1884,11 +1921,13 @@ 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))) {
+               npages = pipe_space_for_user(iter_head, i->pipe->tail, i->pipe);
+               return min(npages, maxpages);
+       }
+       if (iov_iter_is_xarray(i)) {
+               size_t size = i->count;
                unsigned offset;
+               int npages;
 
                offset = (i->xarray_start + i->iov_offset) & ~PAGE_MASK;
 
@@ -1900,28 +1939,9 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
                        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;
+               return min(npages, maxpages);
+       }
+       return 0;
 }
 EXPORT_SYMBOL(iov_iter_npages);