diff options
Diffstat (limited to 'lib/iov_iter.c')
| -rw-r--r-- | lib/iov_iter.c | 63 | 
1 files changed, 63 insertions, 0 deletions
diff --git a/lib/iov_iter.c b/lib/iov_iter.c index e68604ae3ced..60abc44385b7 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -786,6 +786,68 @@ void iov_iter_advance(struct iov_iter *i, size_t size)  }  EXPORT_SYMBOL(iov_iter_advance); +void iov_iter_revert(struct iov_iter *i, size_t unroll) +{ +	if (!unroll) +		return; +	i->count += unroll; +	if (unlikely(i->type & ITER_PIPE)) { +		struct pipe_inode_info *pipe = i->pipe; +		int idx = i->idx; +		size_t off = i->iov_offset; +		while (1) { +			size_t n = off - pipe->bufs[idx].offset; +			if (unroll < n) { +				off -= (n - unroll); +				break; +			} +			unroll -= n; +			if (!unroll && idx == i->start_idx) { +				off = 0; +				break; +			} +			if (!idx--) +				idx = pipe->buffers - 1; +			off = pipe->bufs[idx].offset + pipe->bufs[idx].len; +		} +		i->iov_offset = off; +		i->idx = idx; +		pipe_truncate(i); +		return; +	} +	if (unroll <= i->iov_offset) { +		i->iov_offset -= unroll; +		return; +	} +	unroll -= i->iov_offset; +	if (i->type & ITER_BVEC) { +		const struct bio_vec *bvec = i->bvec; +		while (1) { +			size_t n = (--bvec)->bv_len; +			i->nr_segs++; +			if (unroll <= n) { +				i->bvec = bvec; +				i->iov_offset = n - unroll; +				return; +			} +			unroll -= n; +		} +	} else { /* same logics for iovec and kvec */ +		const struct iovec *iov = i->iov; +		while (1) { +			size_t n = (--iov)->iov_len; +			i->nr_segs++; +			if (unroll <= n) { +				i->iov = iov; +				i->iov_offset = n - unroll; +				return; +			} +			unroll -= n; +		} +	} +} +EXPORT_SYMBOL(iov_iter_revert); +  /*   * Return the count of just the current iov_iter segment.   */ @@ -839,6 +901,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction,  	i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);  	i->iov_offset = 0;  	i->count = count; +	i->start_idx = i->idx;  }  EXPORT_SYMBOL(iov_iter_pipe);  | 
