Commit 9ea9ce04 authored by David Howells's avatar David Howells

iov_iter: Add I/O discard iterator

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: default avatarDavid Howells <dhowells@redhat.com>
parent aa563d7b
...@@ -26,6 +26,7 @@ enum iter_type { ...@@ -26,6 +26,7 @@ enum iter_type {
ITER_KVEC = 2, ITER_KVEC = 2,
ITER_BVEC = 4, ITER_BVEC = 4,
ITER_PIPE = 8, ITER_PIPE = 8,
ITER_DISCARD = 16,
}; };
struct iov_iter { struct iov_iter {
...@@ -72,6 +73,11 @@ static inline bool iov_iter_is_pipe(const struct iov_iter *i) ...@@ -72,6 +73,11 @@ static inline bool iov_iter_is_pipe(const struct iov_iter *i)
return iov_iter_type(i) == ITER_PIPE; 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) static inline unsigned char iov_iter_rw(const struct iov_iter *i)
{ {
return i->type & (READ | WRITE); return i->type & (READ | WRITE);
...@@ -220,6 +226,7 @@ void iov_iter_bvec(struct iov_iter *i, unsigned int direction, const struct bio_ ...@@ -220,6 +226,7 @@ void iov_iter_bvec(struct iov_iter *i, unsigned int direction, const struct bio_
unsigned long nr_segs, size_t count); unsigned long nr_segs, size_t count);
void iov_iter_pipe(struct iov_iter *i, unsigned int direction, struct pipe_inode_info *pipe, void iov_iter_pipe(struct iov_iter *i, unsigned int direction, struct pipe_inode_info *pipe,
size_t count); 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, ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages,
size_t maxsize, unsigned maxpages, size_t *start); size_t maxsize, unsigned maxpages, size_t *start);
ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages, ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages,
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
const struct kvec *kvec; \ const struct kvec *kvec; \
struct kvec v; \ struct kvec v; \
iterate_kvec(i, n, v, kvec, skip, (K)) \ iterate_kvec(i, n, v, kvec, skip, (K)) \
} else if (unlikely(i->type & ITER_DISCARD)) { \
} else { \ } else { \
const struct iovec *iov; \ const struct iovec *iov; \
struct iovec v; \ struct iovec v; \
...@@ -114,6 +115,8 @@ ...@@ -114,6 +115,8 @@
} \ } \
i->nr_segs -= kvec - i->kvec; \ i->nr_segs -= kvec - i->kvec; \
i->kvec = kvec; \ i->kvec = kvec; \
} else if (unlikely(i->type & ITER_DISCARD)) { \
skip += n; \
} else { \ } else { \
const struct iovec *iov; \ const struct iovec *iov; \
struct iovec v; \ struct iovec v; \
...@@ -838,7 +841,9 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, ...@@ -838,7 +841,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); size_t wanted = copy_to_iter(kaddr + offset, bytes, i);
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
return wanted; return wanted;
} else if (likely(!iov_iter_is_pipe(i))) } 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); return copy_page_to_iter_iovec(page, offset, bytes, i);
else else
return copy_page_to_iter_pipe(page, offset, bytes, i); return copy_page_to_iter_pipe(page, offset, bytes, i);
...@@ -850,7 +855,7 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, ...@@ -850,7 +855,7 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
{ {
if (unlikely(!page_copy_sane(page, offset, bytes))) if (unlikely(!page_copy_sane(page, offset, bytes)))
return 0; return 0;
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
WARN_ON(1); WARN_ON(1);
return 0; return 0;
} }
...@@ -910,7 +915,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, ...@@ -910,7 +915,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
return 0; return 0;
} }
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
WARN_ON(1); WARN_ON(1);
return 0; return 0;
...@@ -978,6 +983,10 @@ void iov_iter_advance(struct iov_iter *i, size_t size) ...@@ -978,6 +983,10 @@ void iov_iter_advance(struct iov_iter *i, size_t size)
pipe_advance(i, size); pipe_advance(i, size);
return; return;
} }
if (unlikely(iov_iter_is_discard(i))) {
i->count -= size;
return;
}
iterate_and_advance(i, size, v, 0, 0, 0) iterate_and_advance(i, size, v, 0, 0, 0)
} }
EXPORT_SYMBOL(iov_iter_advance); EXPORT_SYMBOL(iov_iter_advance);
...@@ -1013,6 +1022,8 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll) ...@@ -1013,6 +1022,8 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll)
pipe_truncate(i); pipe_truncate(i);
return; return;
} }
if (unlikely(iov_iter_is_discard(i)))
return;
if (unroll <= i->iov_offset) { if (unroll <= i->iov_offset) {
i->iov_offset -= unroll; i->iov_offset -= unroll;
return; return;
...@@ -1055,6 +1066,8 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i) ...@@ -1055,6 +1066,8 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i)
return i->count; // it is a silly place, anyway return i->count; // it is a silly place, anyway
if (i->nr_segs == 1) if (i->nr_segs == 1)
return i->count; return i->count;
if (unlikely(iov_iter_is_discard(i)))
return i->count;
else if (iov_iter_is_bvec(i)) else if (iov_iter_is_bvec(i))
return min(i->count, i->bvec->bv_len - i->iov_offset); return min(i->count, i->bvec->bv_len - i->iov_offset);
else else
...@@ -1103,6 +1116,24 @@ void iov_iter_pipe(struct iov_iter *i, unsigned int direction, ...@@ -1103,6 +1116,24 @@ void iov_iter_pipe(struct iov_iter *i, unsigned int direction,
} }
EXPORT_SYMBOL(iov_iter_pipe); 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 iov_iter_alignment(const struct iov_iter *i)
{ {
unsigned long res = 0; unsigned long res = 0;
...@@ -1127,7 +1158,7 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i) ...@@ -1127,7 +1158,7 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i)
unsigned long res = 0; unsigned long res = 0;
size_t size = i->count; size_t size = i->count;
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
WARN_ON(1); WARN_ON(1);
return ~0U; return ~0U;
} }
...@@ -1197,6 +1228,9 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, ...@@ -1197,6 +1228,9 @@ ssize_t iov_iter_get_pages(struct iov_iter *i,
if (unlikely(iov_iter_is_pipe(i))) if (unlikely(iov_iter_is_pipe(i)))
return pipe_get_pages(i, pages, maxsize, maxpages, start); return pipe_get_pages(i, pages, maxsize, maxpages, start);
if (unlikely(iov_iter_is_discard(i)))
return -EFAULT;
iterate_all_kinds(i, maxsize, v, ({ iterate_all_kinds(i, maxsize, v, ({
unsigned long addr = (unsigned long)v.iov_base; unsigned long addr = (unsigned long)v.iov_base;
size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1)); size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
...@@ -1274,6 +1308,9 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, ...@@ -1274,6 +1308,9 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
if (unlikely(iov_iter_is_pipe(i))) if (unlikely(iov_iter_is_pipe(i)))
return pipe_get_pages_alloc(i, pages, maxsize, start); return pipe_get_pages_alloc(i, pages, maxsize, start);
if (unlikely(iov_iter_is_discard(i)))
return -EFAULT;
iterate_all_kinds(i, maxsize, v, ({ iterate_all_kinds(i, maxsize, v, ({
unsigned long addr = (unsigned long)v.iov_base; unsigned long addr = (unsigned long)v.iov_base;
size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1)); size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
...@@ -1315,7 +1352,7 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, ...@@ -1315,7 +1352,7 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
__wsum sum, next; __wsum sum, next;
size_t off = 0; size_t off = 0;
sum = *csum; sum = *csum;
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
WARN_ON(1); WARN_ON(1);
return 0; return 0;
} }
...@@ -1357,7 +1394,7 @@ bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum, ...@@ -1357,7 +1394,7 @@ bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum,
__wsum sum, next; __wsum sum, next;
size_t off = 0; size_t off = 0;
sum = *csum; sum = *csum;
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
WARN_ON(1); WARN_ON(1);
return false; return false;
} }
...@@ -1402,7 +1439,7 @@ size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum, ...@@ -1402,7 +1439,7 @@ size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum,
__wsum sum, next; __wsum sum, next;
size_t off = 0; size_t off = 0;
sum = *csum; sum = *csum;
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) {
WARN_ON(1); /* for now */ WARN_ON(1); /* for now */
return 0; return 0;
} }
...@@ -1444,6 +1481,8 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages) ...@@ -1444,6 +1481,8 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
if (!size) if (!size)
return 0; return 0;
if (unlikely(iov_iter_is_discard(i)))
return 0;
if (unlikely(iov_iter_is_pipe(i))) { if (unlikely(iov_iter_is_pipe(i))) {
struct pipe_inode_info *pipe = i->pipe; struct pipe_inode_info *pipe = i->pipe;
...@@ -1487,6 +1526,8 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags) ...@@ -1487,6 +1526,8 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
WARN_ON(1); WARN_ON(1);
return NULL; return NULL;
} }
if (unlikely(iov_iter_is_discard(new)))
return NULL;
if (iov_iter_is_bvec(new)) if (iov_iter_is_bvec(new))
return new->bvec = kmemdup(new->bvec, return new->bvec = kmemdup(new->bvec,
new->nr_segs * sizeof(struct bio_vec), new->nr_segs * sizeof(struct bio_vec),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment