Messages in this thread Patch in this message | | | Subject | [PATCH 03/24] iov_iter: Add I/O discard iterator | From | David Howells <> | Date | Sat, 20 Oct 2018 02:10:59 +0100 |
| |
Add a new iterator, ITER_DISCARD, that can only be used in READ mode and just discards any data copied to it.
This is useful in a network filesystem for discarding any unwanted data sent by a server.
Signed-off-by: David Howells <dhowells@redhat.com> ---
include/linux/uio.h | 7 ++++ lib/iov_iter.c | 96 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 79 insertions(+), 24 deletions(-)
diff --git a/include/linux/uio.h b/include/linux/uio.h index f445d5cbb571..58efe514b253 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -26,6 +26,7 @@ enum iter_type { ITER_KVEC, ITER_BVEC, ITER_PIPE, + ITER_DISCARD, }; struct iov_iter { @@ -73,6 +74,11 @@ static inline bool iov_iter_is_pipe(const struct iov_iter *i) return iov_iter_type(i) == ITER_PIPE; } +static inline bool iov_iter_is_discard(const struct iov_iter *i) +{ + return iov_iter_type(i) == ITER_DISCARD; +} + static inline unsigned char iov_iter_rw(const struct iov_iter *i) { return i->iter_dir; @@ -221,6 +227,7 @@ void iov_iter_bvec(struct iov_iter *i, unsigned int direction, const struct bio_ unsigned long nr_segs, size_t count); void iov_iter_pipe(struct iov_iter *i, unsigned int direction, struct pipe_inode_info *pipe, size_t count); +void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count); ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages, size_t maxsize, unsigned maxpages, size_t *start); ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages, diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 3d8c459e7cd8..2288488e8aaa 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -91,6 +91,9 @@ case ITER_PIPE: { \ break; \ } \ + case ITER_DISCARD: { \ + break; \ + } \ case ITER_IOVEC: { \ const struct iovec *iov; \ struct iovec v; \ @@ -144,6 +147,10 @@ case ITER_PIPE: { \ break; \ } \ + case ITER_DISCARD: { \ + skip += n; \ + break; \ + } \ } \ i->count -= n; \ i->iov_offset = skip; \ @@ -448,6 +455,7 @@ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes) break; case ITER_KVEC: case ITER_BVEC: + case ITER_DISCARD: break; } return 0; @@ -870,6 +878,8 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, return copy_page_to_iter_iovec(page, offset, bytes, i); case ITER_PIPE: return copy_page_to_iter_pipe(page, offset, bytes, i); + case ITER_DISCARD: + return bytes; } BUG(); } @@ -882,6 +892,7 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, return 0; switch (iov_iter_type(i)) { case ITER_PIPE: + case ITER_DISCARD: break; case ITER_BVEC: case ITER_KVEC: { @@ -945,7 +956,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, kunmap_atomic(kaddr); return 0; } - if (unlikely(iov_iter_is_pipe(i))) { + if (unlikely(iov_iter_is_pipe(i) || iov_iter_type(i) == ITER_DISCARD)) { kunmap_atomic(kaddr); WARN_ON(1); return 0; @@ -1018,6 +1029,9 @@ void iov_iter_advance(struct iov_iter *i, size_t size) case ITER_BVEC: iterate_and_advance(i, size, v, 0, 0, 0); return; + case ITER_DISCARD: + i->count -= size; + return; } BUG(); } @@ -1060,6 +1074,9 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll) } unroll -= i->iov_offset; switch (iov_iter_type(i)) { + case ITER_DISCARD: + i->iov_offset = 0; + return; case ITER_BVEC: { const struct bio_vec *bvec = i->bvec; while (1) { @@ -1103,6 +1120,7 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i) return i->count; switch (iov_iter_type(i)) { case ITER_PIPE: + case ITER_DISCARD: return i->count; // it is a silly place, anyway case ITER_BVEC: return min(i->count, i->bvec->bv_len - i->iov_offset); @@ -1158,6 +1176,25 @@ void iov_iter_pipe(struct iov_iter *i, unsigned 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->iter_dir = READ; + i->iter_type = ITER_DISCARD; + 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; @@ -1182,7 +1219,7 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i) unsigned long res = 0; size_t size = i->count; - if (unlikely(iov_iter_is_pipe(i))) { + if (unlikely(iov_iter_is_pipe(i) || iov_iter_type(i) == ITER_DISCARD)) { WARN_ON(1); return ~0U; } @@ -1253,6 +1290,7 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, switch (iov_iter_type(i)) { case ITER_PIPE: return pipe_get_pages(i, pages, maxsize, maxpages, start); + case ITER_DISCARD: case ITER_KVEC: return -EFAULT; case ITER_IOVEC: @@ -1336,6 +1374,7 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, switch (iov_iter_type(i)) { case ITER_PIPE: return pipe_get_pages_alloc(i, pages, maxsize, start); + case ITER_DISCARD: case ITER_KVEC: return -EFAULT; case ITER_IOVEC: @@ -1382,7 +1421,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(iov_iter_is_pipe(i))) { + if (unlikely(iov_iter_is_pipe(i) || iov_iter_type(i) == ITER_DISCARD)) { WARN_ON(1); return 0; } @@ -1424,7 +1463,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(iov_iter_is_pipe(i))) { + if (unlikely(iov_iter_is_pipe(i) || iov_iter_type(i) == ITER_DISCARD)) { WARN_ON(1); return false; } @@ -1469,7 +1508,7 @@ size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum, __wsum sum, next; size_t off = 0; sum = *csum; - if (unlikely(iov_iter_is_pipe(i))) { + if (unlikely(iov_iter_is_pipe(i) || iov_iter_type(i) == ITER_DISCARD)) { WARN_ON(1); /* for now */ return 0; } @@ -1512,7 +1551,8 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages) if (!size) return 0; - if (unlikely(iov_iter_is_pipe(i))) { + switch (iov_iter_type(i)) { + case ITER_PIPE: { struct pipe_inode_info *pipe = i->pipe; size_t off; int idx; @@ -1525,24 +1565,30 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages) npages = ((pipe->curbuf - idx - 1) & (pipe->buffers - 1)) + 1; 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; - }) - ) + } + case ITER_DISCARD: + return 0; + + default: + 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; + }) + ) + } return npages; } EXPORT_SYMBOL(iov_iter_npages); @@ -1563,6 +1609,8 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags) return new->iov = kmemdup(new->iov, new->nr_segs * sizeof(struct iovec), flags); + case ITER_DISCARD: + return NULL; } WARN_ON(1);
| |