diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-02-17 18:42:24 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-21 03:31:21 -0400 |
commit | ff76ab9e03a50a4df26329e547e75f865a2bfa9f (patch) | |
tree | 6111fb43d038f57b12e2ebf02aa237cf18fcfed2 | |
parent | e485875dff38c79479ba42f80f0230d181a77a56 (diff) |
new privimitive: iov_iter_revert()
commit 27c0e3748e41ca79171ffa3e97415a20af6facd0 upstream.
opposite to iov_iter_advance(); the caller is responsible for never
using it to move back past the initial position.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | include/linux/uio.h | 6 | ||||
-rw-r--r-- | lib/iov_iter.c | 63 |
2 files changed, 68 insertions, 1 deletions
diff --git a/include/linux/uio.h b/include/linux/uio.h index 6e22b544d039..c146ebc69c53 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h | |||
@@ -39,7 +39,10 @@ struct iov_iter { | |||
39 | }; | 39 | }; |
40 | union { | 40 | union { |
41 | unsigned long nr_segs; | 41 | unsigned long nr_segs; |
42 | int idx; | 42 | struct { |
43 | int idx; | ||
44 | int start_idx; | ||
45 | }; | ||
43 | }; | 46 | }; |
44 | }; | 47 | }; |
45 | 48 | ||
@@ -81,6 +84,7 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to); | |||
81 | size_t iov_iter_copy_from_user_atomic(struct page *page, | 84 | size_t iov_iter_copy_from_user_atomic(struct page *page, |
82 | struct iov_iter *i, unsigned long offset, size_t bytes); | 85 | struct iov_iter *i, unsigned long offset, size_t bytes); |
83 | void iov_iter_advance(struct iov_iter *i, size_t bytes); | 86 | void iov_iter_advance(struct iov_iter *i, size_t bytes); |
87 | void iov_iter_revert(struct iov_iter *i, size_t bytes); | ||
84 | int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); | 88 | int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); |
85 | size_t iov_iter_single_seg_count(const struct iov_iter *i); | 89 | size_t iov_iter_single_seg_count(const struct iov_iter *i); |
86 | size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, | 90 | size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, |
diff --git a/lib/iov_iter.c b/lib/iov_iter.c index efb0b4d267a1..a75ea633b5c4 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c | |||
@@ -734,6 +734,68 @@ void iov_iter_advance(struct iov_iter *i, size_t size) | |||
734 | } | 734 | } |
735 | EXPORT_SYMBOL(iov_iter_advance); | 735 | EXPORT_SYMBOL(iov_iter_advance); |
736 | 736 | ||
737 | void iov_iter_revert(struct iov_iter *i, size_t unroll) | ||
738 | { | ||
739 | if (!unroll) | ||
740 | return; | ||
741 | i->count += unroll; | ||
742 | if (unlikely(i->type & ITER_PIPE)) { | ||
743 | struct pipe_inode_info *pipe = i->pipe; | ||
744 | int idx = i->idx; | ||
745 | size_t off = i->iov_offset; | ||
746 | while (1) { | ||
747 | size_t n = off - pipe->bufs[idx].offset; | ||
748 | if (unroll < n) { | ||
749 | off -= (n - unroll); | ||
750 | break; | ||
751 | } | ||
752 | unroll -= n; | ||
753 | if (!unroll && idx == i->start_idx) { | ||
754 | off = 0; | ||
755 | break; | ||
756 | } | ||
757 | if (!idx--) | ||
758 | idx = pipe->buffers - 1; | ||
759 | off = pipe->bufs[idx].offset + pipe->bufs[idx].len; | ||
760 | } | ||
761 | i->iov_offset = off; | ||
762 | i->idx = idx; | ||
763 | pipe_truncate(i); | ||
764 | return; | ||
765 | } | ||
766 | if (unroll <= i->iov_offset) { | ||
767 | i->iov_offset -= unroll; | ||
768 | return; | ||
769 | } | ||
770 | unroll -= i->iov_offset; | ||
771 | if (i->type & ITER_BVEC) { | ||
772 | const struct bio_vec *bvec = i->bvec; | ||
773 | while (1) { | ||
774 | size_t n = (--bvec)->bv_len; | ||
775 | i->nr_segs++; | ||
776 | if (unroll <= n) { | ||
777 | i->bvec = bvec; | ||
778 | i->iov_offset = n - unroll; | ||
779 | return; | ||
780 | } | ||
781 | unroll -= n; | ||
782 | } | ||
783 | } else { /* same logics for iovec and kvec */ | ||
784 | const struct iovec *iov = i->iov; | ||
785 | while (1) { | ||
786 | size_t n = (--iov)->iov_len; | ||
787 | i->nr_segs++; | ||
788 | if (unroll <= n) { | ||
789 | i->iov = iov; | ||
790 | i->iov_offset = n - unroll; | ||
791 | return; | ||
792 | } | ||
793 | unroll -= n; | ||
794 | } | ||
795 | } | ||
796 | } | ||
797 | EXPORT_SYMBOL(iov_iter_revert); | ||
798 | |||
737 | /* | 799 | /* |
738 | * Return the count of just the current iov_iter segment. | 800 | * Return the count of just the current iov_iter segment. |
739 | */ | 801 | */ |
@@ -787,6 +849,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction, | |||
787 | i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); | 849 | i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); |
788 | i->iov_offset = 0; | 850 | i->iov_offset = 0; |
789 | i->count = count; | 851 | i->count = count; |
852 | i->start_idx = i->idx; | ||
790 | } | 853 | } |
791 | EXPORT_SYMBOL(iov_iter_pipe); | 854 | EXPORT_SYMBOL(iov_iter_pipe); |
792 | 855 | ||