diff options
-rw-r--r-- | lib/iov_iter.c | 54 |
1 files changed, 31 insertions, 23 deletions
diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 25f572303801..e68604ae3ced 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c | |||
@@ -730,43 +730,50 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, | |||
730 | } | 730 | } |
731 | EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); | 731 | EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); |
732 | 732 | ||
733 | static inline void pipe_truncate(struct iov_iter *i) | ||
734 | { | ||
735 | struct pipe_inode_info *pipe = i->pipe; | ||
736 | if (pipe->nrbufs) { | ||
737 | size_t off = i->iov_offset; | ||
738 | int idx = i->idx; | ||
739 | int nrbufs = (idx - pipe->curbuf) & (pipe->buffers - 1); | ||
740 | if (off) { | ||
741 | pipe->bufs[idx].len = off - pipe->bufs[idx].offset; | ||
742 | idx = next_idx(idx, pipe); | ||
743 | nrbufs++; | ||
744 | } | ||
745 | while (pipe->nrbufs > nrbufs) { | ||
746 | pipe_buf_release(pipe, &pipe->bufs[idx]); | ||
747 | idx = next_idx(idx, pipe); | ||
748 | pipe->nrbufs--; | ||
749 | } | ||
750 | } | ||
751 | } | ||
752 | |||
733 | static void pipe_advance(struct iov_iter *i, size_t size) | 753 | static void pipe_advance(struct iov_iter *i, size_t size) |
734 | { | 754 | { |
735 | struct pipe_inode_info *pipe = i->pipe; | 755 | struct pipe_inode_info *pipe = i->pipe; |
736 | struct pipe_buffer *buf; | ||
737 | int idx = i->idx; | ||
738 | size_t off = i->iov_offset, orig_sz; | ||
739 | |||
740 | if (unlikely(i->count < size)) | 756 | if (unlikely(i->count < size)) |
741 | size = i->count; | 757 | size = i->count; |
742 | orig_sz = size; | ||
743 | |||
744 | if (size) { | 758 | if (size) { |
759 | struct pipe_buffer *buf; | ||
760 | size_t off = i->iov_offset, left = size; | ||
761 | int idx = i->idx; | ||
745 | if (off) /* make it relative to the beginning of buffer */ | 762 | if (off) /* make it relative to the beginning of buffer */ |
746 | size += off - pipe->bufs[idx].offset; | 763 | left += off - pipe->bufs[idx].offset; |
747 | while (1) { | 764 | while (1) { |
748 | buf = &pipe->bufs[idx]; | 765 | buf = &pipe->bufs[idx]; |
749 | if (size <= buf->len) | 766 | if (left <= buf->len) |
750 | break; | 767 | break; |
751 | size -= buf->len; | 768 | left -= buf->len; |
752 | idx = next_idx(idx, pipe); | 769 | idx = next_idx(idx, pipe); |
753 | } | 770 | } |
754 | buf->len = size; | ||
755 | i->idx = idx; | 771 | i->idx = idx; |
756 | off = i->iov_offset = buf->offset + size; | 772 | i->iov_offset = buf->offset + left; |
757 | } | ||
758 | if (off) | ||
759 | idx = next_idx(idx, pipe); | ||
760 | if (pipe->nrbufs) { | ||
761 | int unused = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); | ||
762 | /* [curbuf,unused) is in use. Free [idx,unused) */ | ||
763 | while (idx != unused) { | ||
764 | pipe_buf_release(pipe, &pipe->bufs[idx]); | ||
765 | idx = next_idx(idx, pipe); | ||
766 | pipe->nrbufs--; | ||
767 | } | ||
768 | } | 773 | } |
769 | i->count -= orig_sz; | 774 | i->count -= size; |
775 | /* ... and discard everything past that point */ | ||
776 | pipe_truncate(i); | ||
770 | } | 777 | } |
771 | 778 | ||
772 | void iov_iter_advance(struct iov_iter *i, size_t size) | 779 | void iov_iter_advance(struct iov_iter *i, size_t size) |
@@ -826,6 +833,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction, | |||
826 | size_t count) | 833 | size_t count) |
827 | { | 834 | { |
828 | BUG_ON(direction != ITER_PIPE); | 835 | BUG_ON(direction != ITER_PIPE); |
836 | WARN_ON(pipe->nrbufs == pipe->buffers); | ||
829 | i->type = direction; | 837 | i->type = direction; |
830 | i->pipe = pipe; | 838 | i->pipe = pipe; |
831 | i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); | 839 | i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); |