]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - lib/iov_iter.c
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / lib / iov_iter.c
index 8be175df30753c95692007a5d41503838344d9a5..54c248526b55fc498c996f2a5ea651262fcc7f61 100644 (file)
@@ -83,6 +83,7 @@
                        const struct kvec *kvec;                \
                        struct kvec v;                          \
                        iterate_kvec(i, n, v, kvec, skip, (K))  \
+               } else if (unlikely(i->type & ITER_DISCARD)) {  \
                } else {                                        \
                        const struct iovec *iov;                \
                        struct iovec v;                         \
                        }                                       \
                        i->nr_segs -= kvec - i->kvec;           \
                        i->kvec = kvec;                         \
+               } else if (unlikely(i->type & ITER_DISCARD)) {  \
+                       skip += n;                              \
                } else {                                        \
                        const struct iovec *iov;                \
                        struct iovec v;                         \
@@ -428,17 +431,19 @@ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
 }
 EXPORT_SYMBOL(iov_iter_fault_in_readable);
 
-void iov_iter_init(struct iov_iter *i, int direction,
+void iov_iter_init(struct iov_iter *i, unsigned int direction,
                        const struct iovec *iov, unsigned long nr_segs,
                        size_t count)
 {
+       WARN_ON(direction & ~(READ | WRITE));
+       direction &= READ | WRITE;
+
        /* It will get better.  Eventually... */
        if (uaccess_kernel()) {
-               direction |= ITER_KVEC;
-               i->type = direction;
+               i->type = ITER_KVEC | direction;
                i->kvec = (struct kvec *)iov;
        } else {
-               i->type = direction;
+               i->type = ITER_IOVEC | direction;
                i->iov = iov;
        }
        i->nr_segs = nr_segs;
@@ -555,10 +560,42 @@ static size_t copy_pipe_to_iter(const void *addr, size_t bytes,
        return bytes;
 }
 
+static size_t csum_and_copy_to_pipe_iter(const void *addr, size_t bytes,
+                               __wsum *csum, struct iov_iter *i)
+{
+       struct pipe_inode_info *pipe = i->pipe;
+       size_t n, r;
+       size_t off = 0;
+       __wsum sum = *csum, next;
+       int idx;
+
+       if (!sanity(i))
+               return 0;
+
+       bytes = n = push_pipe(i, bytes, &idx, &r);
+       if (unlikely(!n))
+               return 0;
+       for ( ; n; idx = next_idx(idx, pipe), r = 0) {
+               size_t chunk = min_t(size_t, n, PAGE_SIZE - r);
+               char *p = kmap_atomic(pipe->bufs[idx].page);
+               next = csum_partial_copy_nocheck(addr, p + r, chunk, 0);
+               sum = csum_block_add(sum, next, off);
+               kunmap_atomic(p);
+               i->idx = idx;
+               i->iov_offset = r + chunk;
+               n -= chunk;
+               off += chunk;
+               addr += chunk;
+       }
+       i->count -= bytes;
+       *csum = sum;
+       return bytes;
+}
+
 size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
 {
        const char *from = addr;
-       if (unlikely(i->type & ITER_PIPE))
+       if (unlikely(iov_iter_is_pipe(i)))
                return copy_pipe_to_iter(addr, bytes, i);
        if (iter_is_iovec(i))
                might_fault();
@@ -658,7 +695,7 @@ size_t _copy_to_iter_mcsafe(const void *addr, size_t bytes, struct iov_iter *i)
        const char *from = addr;
        unsigned long rem, curr_addr, s_addr = (unsigned long) addr;
 
-       if (unlikely(i->type & ITER_PIPE))
+       if (unlikely(iov_iter_is_pipe(i)))
                return copy_pipe_to_iter_mcsafe(addr, bytes, i);
        if (iter_is_iovec(i))
                might_fault();
@@ -692,7 +729,7 @@ EXPORT_SYMBOL_GPL(_copy_to_iter_mcsafe);
 size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
 {
        char *to = addr;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                WARN_ON(1);
                return 0;
        }
@@ -712,7 +749,7 @@ EXPORT_SYMBOL(_copy_from_iter);
 bool _copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i)
 {
        char *to = addr;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                WARN_ON(1);
                return false;
        }
@@ -739,7 +776,7 @@ EXPORT_SYMBOL(_copy_from_iter_full);
 size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
 {
        char *to = addr;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                WARN_ON(1);
                return 0;
        }
@@ -773,7 +810,7 @@ EXPORT_SYMBOL(_copy_from_iter_nocache);
 size_t _copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i)
 {
        char *to = addr;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                WARN_ON(1);
                return 0;
        }
@@ -794,7 +831,7 @@ EXPORT_SYMBOL_GPL(_copy_from_iter_flushcache);
 bool _copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i)
 {
        char *to = addr;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                WARN_ON(1);
                return false;
        }
@@ -836,7 +873,9 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
                size_t wanted = copy_to_iter(kaddr + offset, bytes, i);
                kunmap_atomic(kaddr);
                return wanted;
-       } else if (likely(!(i->type & ITER_PIPE)))
+       } else if (unlikely(iov_iter_is_discard(i)))
+               return bytes;
+       else if (likely(!iov_iter_is_pipe(i)))
                return copy_page_to_iter_iovec(page, offset, bytes, i);
        else
                return copy_page_to_iter_pipe(page, offset, bytes, i);
@@ -848,7 +887,7 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
 {
        if (unlikely(!page_copy_sane(page, offset, bytes)))
                return 0;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
                WARN_ON(1);
                return 0;
        }
@@ -888,7 +927,7 @@ static size_t pipe_zero(size_t bytes, struct iov_iter *i)
 
 size_t iov_iter_zero(size_t bytes, struct iov_iter *i)
 {
-       if (unlikely(i->type & ITER_PIPE))
+       if (unlikely(iov_iter_is_pipe(i)))
                return pipe_zero(bytes, i);
        iterate_and_advance(i, bytes, v,
                clear_user(v.iov_base, v.iov_len),
@@ -908,7 +947,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
                kunmap_atomic(kaddr);
                return 0;
        }
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
                kunmap_atomic(kaddr);
                WARN_ON(1);
                return 0;
@@ -972,10 +1011,14 @@ static void pipe_advance(struct iov_iter *i, size_t size)
 
 void iov_iter_advance(struct iov_iter *i, size_t size)
 {
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                pipe_advance(i, size);
                return;
        }
+       if (unlikely(iov_iter_is_discard(i))) {
+               i->count -= size;
+               return;
+       }
        iterate_and_advance(i, size, v, 0, 0, 0)
 }
 EXPORT_SYMBOL(iov_iter_advance);
@@ -987,7 +1030,7 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll)
        if (WARN_ON(unroll > MAX_RW_COUNT))
                return;
        i->count += unroll;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                struct pipe_inode_info *pipe = i->pipe;
                int idx = i->idx;
                size_t off = i->iov_offset;
@@ -1011,12 +1054,14 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll)
                pipe_truncate(i);
                return;
        }
+       if (unlikely(iov_iter_is_discard(i)))
+               return;
        if (unroll <= i->iov_offset) {
                i->iov_offset -= unroll;
                return;
        }
        unroll -= i->iov_offset;
-       if (i->type & ITER_BVEC) {
+       if (iov_iter_is_bvec(i)) {
                const struct bio_vec *bvec = i->bvec;
                while (1) {
                        size_t n = (--bvec)->bv_len;
@@ -1049,23 +1094,25 @@ EXPORT_SYMBOL(iov_iter_revert);
  */
 size_t iov_iter_single_seg_count(const struct iov_iter *i)
 {
-       if (unlikely(i->type & ITER_PIPE))
+       if (unlikely(iov_iter_is_pipe(i)))
                return i->count;        // it is a silly place, anyway
        if (i->nr_segs == 1)
                return i->count;
-       else if (i->type & ITER_BVEC)
+       if (unlikely(iov_iter_is_discard(i)))
+               return i->count;
+       else if (iov_iter_is_bvec(i))
                return min(i->count, i->bvec->bv_len - i->iov_offset);
        else
                return min(i->count, i->iov->iov_len - i->iov_offset);
 }
 EXPORT_SYMBOL(iov_iter_single_seg_count);
 
-void iov_iter_kvec(struct iov_iter *i, int direction,
+void iov_iter_kvec(struct iov_iter *i, unsigned int direction,
                        const struct kvec *kvec, unsigned long nr_segs,
                        size_t count)
 {
-       BUG_ON(!(direction & ITER_KVEC));
-       i->type = direction;
+       WARN_ON(direction & ~(READ | WRITE));
+       i->type = ITER_KVEC | (direction & (READ | WRITE));
        i->kvec = kvec;
        i->nr_segs = nr_segs;
        i->iov_offset = 0;
@@ -1073,12 +1120,12 @@ void iov_iter_kvec(struct iov_iter *i, int direction,
 }
 EXPORT_SYMBOL(iov_iter_kvec);
 
-void iov_iter_bvec(struct iov_iter *i, int direction,
+void iov_iter_bvec(struct iov_iter *i, unsigned int direction,
                        const struct bio_vec *bvec, unsigned long nr_segs,
                        size_t count)
 {
-       BUG_ON(!(direction & ITER_BVEC));
-       i->type = direction;
+       WARN_ON(direction & ~(READ | WRITE));
+       i->type = ITER_BVEC | (direction & (READ | WRITE));
        i->bvec = bvec;
        i->nr_segs = nr_segs;
        i->iov_offset = 0;
@@ -1086,13 +1133,13 @@ void iov_iter_bvec(struct iov_iter *i, int direction,
 }
 EXPORT_SYMBOL(iov_iter_bvec);
 
-void iov_iter_pipe(struct iov_iter *i, int direction,
+void iov_iter_pipe(struct iov_iter *i, unsigned int direction,
                        struct pipe_inode_info *pipe,
                        size_t count)
 {
-       BUG_ON(direction != ITER_PIPE);
+       BUG_ON(direction != READ);
        WARN_ON(pipe->nrbufs == pipe->buffers);
-       i->type = direction;
+       i->type = ITER_PIPE | READ;
        i->pipe = pipe;
        i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
        i->iov_offset = 0;
@@ -1101,12 +1148,30 @@ void iov_iter_pipe(struct iov_iter *i, int direction,
 }
 EXPORT_SYMBOL(iov_iter_pipe);
 
+/**
+ * iov_iter_discard - Initialise an I/O iterator that discards data
+ * @i: The iterator to initialise.
+ * @direction: The direction of the transfer.
+ * @count: The size of the I/O buffer in bytes.
+ *
+ * Set up an I/O iterator that just discards everything that's written to it.
+ * It's only available as a READ iterator.
+ */
+void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count)
+{
+       BUG_ON(direction != READ);
+       i->type = ITER_DISCARD | READ;
+       i->count = count;
+       i->iov_offset = 0;
+}
+EXPORT_SYMBOL(iov_iter_discard);
+
 unsigned long iov_iter_alignment(const struct iov_iter *i)
 {
        unsigned long res = 0;
        size_t size = i->count;
 
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                if (size && i->iov_offset && allocated(&i->pipe->bufs[i->idx]))
                        return size | i->iov_offset;
                return size;
@@ -1125,7 +1190,7 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i)
        unsigned long res = 0;
        size_t size = i->count;
 
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
                WARN_ON(1);
                return ~0U;
        }
@@ -1193,8 +1258,11 @@ ssize_t iov_iter_get_pages(struct iov_iter *i,
        if (maxsize > i->count)
                maxsize = i->count;
 
-       if (unlikely(i->type & ITER_PIPE))
+       if (unlikely(iov_iter_is_pipe(i)))
                return pipe_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));
@@ -1205,7 +1273,7 @@ ssize_t iov_iter_get_pages(struct iov_iter *i,
                        len = maxpages * PAGE_SIZE;
                addr &= ~(PAGE_SIZE - 1);
                n = DIV_ROUND_UP(len, PAGE_SIZE);
-               res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages);
+               res = get_user_pages_fast(addr, n, iov_iter_rw(i) != WRITE, pages);
                if (unlikely(res < 0))
                        return res;
                return (res == n ? len : res * PAGE_SIZE) - *start;
@@ -1270,8 +1338,11 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
        if (maxsize > i->count)
                maxsize = i->count;
 
-       if (unlikely(i->type & ITER_PIPE))
+       if (unlikely(iov_iter_is_pipe(i)))
                return pipe_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));
@@ -1283,7 +1354,7 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
                p = get_pages_array(n);
                if (!p)
                        return -ENOMEM;
-               res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
+               res = get_user_pages_fast(addr, n, iov_iter_rw(i) != WRITE, p);
                if (unlikely(res < 0)) {
                        kvfree(p);
                        return res;
@@ -1313,7 +1384,7 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
        __wsum sum, next;
        size_t off = 0;
        sum = *csum;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
                WARN_ON(1);
                return 0;
        }
@@ -1355,7 +1426,7 @@ bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum,
        __wsum sum, next;
        size_t off = 0;
        sum = *csum;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
                WARN_ON(1);
                return false;
        }
@@ -1399,8 +1470,12 @@ size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum,
        const char *from = addr;
        __wsum sum, next;
        size_t off = 0;
+
+       if (unlikely(iov_iter_is_pipe(i)))
+               return csum_and_copy_to_pipe_iter(addr, bytes, csum, i);
+
        sum = *csum;
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_discard(i))) {
                WARN_ON(1);     /* for now */
                return 0;
        }
@@ -1442,8 +1517,10 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
 
        if (!size)
                return 0;
+       if (unlikely(iov_iter_is_discard(i)))
+               return 0;
 
-       if (unlikely(i->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(i))) {
                struct pipe_inode_info *pipe = i->pipe;
                size_t off;
                int idx;
@@ -1481,11 +1558,13 @@ EXPORT_SYMBOL(iov_iter_npages);
 const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
 {
        *new = *old;
-       if (unlikely(new->type & ITER_PIPE)) {
+       if (unlikely(iov_iter_is_pipe(new))) {
                WARN_ON(1);
                return NULL;
        }
-       if (new->type & ITER_BVEC)
+       if (unlikely(iov_iter_is_discard(new)))
+               return NULL;
+       if (iov_iter_is_bvec(new))
                return new->bvec = kmemdup(new->bvec,
                                    new->nr_segs * sizeof(struct bio_vec),
                                    flags);