[PATCH 03/24] iov_iter: Add I/O discard iterator

From: David Howells
Date: Fri Oct 19 2018 - 21:11:05 EST


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@xxxxxxxxxx>
---

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);