Commit 0e4c0ee5 authored by David S. Miller's avatar David S. Miller
parents ea6b1720 32786821
...@@ -39,7 +39,10 @@ struct iov_iter { ...@@ -39,7 +39,10 @@ struct iov_iter {
}; };
union { union {
unsigned long nr_segs; unsigned long nr_segs;
int idx; struct {
int idx;
int start_idx;
};
}; };
}; };
...@@ -81,6 +84,7 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to); ...@@ -81,6 +84,7 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to);
size_t iov_iter_copy_from_user_atomic(struct page *page, size_t iov_iter_copy_from_user_atomic(struct page *page,
struct iov_iter *i, unsigned long offset, size_t bytes); struct iov_iter *i, unsigned long offset, size_t bytes);
void iov_iter_advance(struct iov_iter *i, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes);
void iov_iter_revert(struct iov_iter *i, size_t bytes);
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
size_t iov_iter_single_seg_count(const struct iov_iter *i); size_t iov_iter_single_seg_count(const struct iov_iter *i);
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
......
...@@ -786,6 +786,68 @@ void iov_iter_advance(struct iov_iter *i, size_t size) ...@@ -786,6 +786,68 @@ void iov_iter_advance(struct iov_iter *i, size_t size)
} }
EXPORT_SYMBOL(iov_iter_advance); 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. * Return the count of just the current iov_iter segment.
*/ */
...@@ -839,6 +901,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction, ...@@ -839,6 +901,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction,
i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
i->iov_offset = 0; i->iov_offset = 0;
i->count = count; i->count = count;
i->start_idx = i->idx;
} }
EXPORT_SYMBOL(iov_iter_pipe); EXPORT_SYMBOL(iov_iter_pipe);
......
...@@ -398,7 +398,7 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset, ...@@ -398,7 +398,7 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset,
struct iov_iter *to, int len) struct iov_iter *to, int len)
{ {
int start = skb_headlen(skb); int start = skb_headlen(skb);
int i, copy = start - offset; int i, copy = start - offset, start_off = offset, n;
struct sk_buff *frag_iter; struct sk_buff *frag_iter;
trace_skb_copy_datagram_iovec(skb, len); trace_skb_copy_datagram_iovec(skb, len);
...@@ -407,11 +407,12 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset, ...@@ -407,11 +407,12 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset,
if (copy > 0) { if (copy > 0) {
if (copy > len) if (copy > len)
copy = len; copy = len;
if (copy_to_iter(skb->data + offset, copy, to) != copy) n = copy_to_iter(skb->data + offset, copy, to);
offset += n;
if (n != copy)
goto short_copy; goto short_copy;
if ((len -= copy) == 0) if ((len -= copy) == 0)
return 0; return 0;
offset += copy;
} }
/* Copy paged appendix. Hmm... why does this look so complicated? */ /* Copy paged appendix. Hmm... why does this look so complicated? */
...@@ -425,13 +426,14 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset, ...@@ -425,13 +426,14 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset,
if ((copy = end - offset) > 0) { if ((copy = end - offset) > 0) {
if (copy > len) if (copy > len)
copy = len; copy = len;
if (copy_page_to_iter(skb_frag_page(frag), n = copy_page_to_iter(skb_frag_page(frag),
frag->page_offset + offset - frag->page_offset + offset -
start, copy, to) != copy) start, copy, to);
offset += n;
if (n != copy)
goto short_copy; goto short_copy;
if (!(len -= copy)) if (!(len -= copy))
return 0; return 0;
offset += copy;
} }
start = end; start = end;
} }
...@@ -463,6 +465,7 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset, ...@@ -463,6 +465,7 @@ int skb_copy_datagram_iter(const struct sk_buff *skb, int offset,
*/ */
fault: fault:
iov_iter_revert(to, offset - start_off);
return -EFAULT; return -EFAULT;
short_copy: short_copy:
...@@ -613,7 +616,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, ...@@ -613,7 +616,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
__wsum *csump) __wsum *csump)
{ {
int start = skb_headlen(skb); int start = skb_headlen(skb);
int i, copy = start - offset; int i, copy = start - offset, start_off = offset;
struct sk_buff *frag_iter; struct sk_buff *frag_iter;
int pos = 0; int pos = 0;
int n; int n;
...@@ -623,11 +626,11 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, ...@@ -623,11 +626,11 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
if (copy > len) if (copy > len)
copy = len; copy = len;
n = csum_and_copy_to_iter(skb->data + offset, copy, csump, to); n = csum_and_copy_to_iter(skb->data + offset, copy, csump, to);
offset += n;
if (n != copy) if (n != copy)
goto fault; goto fault;
if ((len -= copy) == 0) if ((len -= copy) == 0)
return 0; return 0;
offset += copy;
pos = copy; pos = copy;
} }
...@@ -649,12 +652,12 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, ...@@ -649,12 +652,12 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
offset - start, copy, offset - start, copy,
&csum2, to); &csum2, to);
kunmap(page); kunmap(page);
offset += n;
if (n != copy) if (n != copy)
goto fault; goto fault;
*csump = csum_block_add(*csump, csum2, pos); *csump = csum_block_add(*csump, csum2, pos);
if (!(len -= copy)) if (!(len -= copy))
return 0; return 0;
offset += copy;
pos += copy; pos += copy;
} }
start = end; start = end;
...@@ -687,6 +690,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, ...@@ -687,6 +690,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
return 0; return 0;
fault: fault:
iov_iter_revert(to, offset - start_off);
return -EFAULT; return -EFAULT;
} }
...@@ -771,6 +775,7 @@ int skb_copy_and_csum_datagram_msg(struct sk_buff *skb, ...@@ -771,6 +775,7 @@ int skb_copy_and_csum_datagram_msg(struct sk_buff *skb,
} }
return 0; return 0;
csum_error: csum_error:
iov_iter_revert(&msg->msg_iter, chunk);
return -EINVAL; return -EINVAL;
fault: fault:
return -EFAULT; return -EFAULT;
......
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