forked from luck/tmp_suning_uos_patched
net: Fix oops when splicing skbs from a frag_list.
Lennert Buytenhek wrote:
> Since 4fb6699481
("net: Optimize memory
> usage when splicing from sockets.") I'm seeing this oops (e.g. in
> 2.6.30-rc3) when splicing from a TCP socket to /dev/null on a driver
> (mv643xx_eth) that uses LRO in the skb mode (lro_receive_skb) rather
> than the frag mode:
My patch incorrectly assumed skb->sk was always valid, but for
"frag_listed" skbs we can only use skb->sk of their parent.
Reported-by: Lennert Buytenhek <buytenh@wantstofly.org>
Debugged-by: Lennert Buytenhek <buytenh@wantstofly.org>
Tested-by: Lennert Buytenhek <buytenh@wantstofly.org>
Signed-off-by: Jarek Poplawski <jarkao2@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
93af7aca44
commit
7a67e56fd3
@ -1365,9 +1365,8 @@ static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
|
|||||||
|
|
||||||
static inline struct page *linear_to_page(struct page *page, unsigned int *len,
|
static inline struct page *linear_to_page(struct page *page, unsigned int *len,
|
||||||
unsigned int *offset,
|
unsigned int *offset,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb, struct sock *sk)
|
||||||
{
|
{
|
||||||
struct sock *sk = skb->sk;
|
|
||||||
struct page *p = sk->sk_sndmsg_page;
|
struct page *p = sk->sk_sndmsg_page;
|
||||||
unsigned int off;
|
unsigned int off;
|
||||||
|
|
||||||
@ -1405,13 +1404,14 @@ static inline struct page *linear_to_page(struct page *page, unsigned int *len,
|
|||||||
*/
|
*/
|
||||||
static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
|
static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
|
||||||
unsigned int *len, unsigned int offset,
|
unsigned int *len, unsigned int offset,
|
||||||
struct sk_buff *skb, int linear)
|
struct sk_buff *skb, int linear,
|
||||||
|
struct sock *sk)
|
||||||
{
|
{
|
||||||
if (unlikely(spd->nr_pages == PIPE_BUFFERS))
|
if (unlikely(spd->nr_pages == PIPE_BUFFERS))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (linear) {
|
if (linear) {
|
||||||
page = linear_to_page(page, len, &offset, skb);
|
page = linear_to_page(page, len, &offset, skb, sk);
|
||||||
if (!page)
|
if (!page)
|
||||||
return 1;
|
return 1;
|
||||||
} else
|
} else
|
||||||
@ -1442,7 +1442,8 @@ static inline void __segment_seek(struct page **page, unsigned int *poff,
|
|||||||
static inline int __splice_segment(struct page *page, unsigned int poff,
|
static inline int __splice_segment(struct page *page, unsigned int poff,
|
||||||
unsigned int plen, unsigned int *off,
|
unsigned int plen, unsigned int *off,
|
||||||
unsigned int *len, struct sk_buff *skb,
|
unsigned int *len, struct sk_buff *skb,
|
||||||
struct splice_pipe_desc *spd, int linear)
|
struct splice_pipe_desc *spd, int linear,
|
||||||
|
struct sock *sk)
|
||||||
{
|
{
|
||||||
if (!*len)
|
if (!*len)
|
||||||
return 1;
|
return 1;
|
||||||
@ -1465,7 +1466,7 @@ static inline int __splice_segment(struct page *page, unsigned int poff,
|
|||||||
/* the linear region may spread across several pages */
|
/* the linear region may spread across several pages */
|
||||||
flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
|
flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
|
||||||
|
|
||||||
if (spd_fill_page(spd, page, &flen, poff, skb, linear))
|
if (spd_fill_page(spd, page, &flen, poff, skb, linear, sk))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
__segment_seek(&page, &poff, &plen, flen);
|
__segment_seek(&page, &poff, &plen, flen);
|
||||||
@ -1481,8 +1482,8 @@ static inline int __splice_segment(struct page *page, unsigned int poff,
|
|||||||
* pipe is full or if we already spliced the requested length.
|
* pipe is full or if we already spliced the requested length.
|
||||||
*/
|
*/
|
||||||
static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
||||||
unsigned int *len,
|
unsigned int *len, struct splice_pipe_desc *spd,
|
||||||
struct splice_pipe_desc *spd)
|
struct sock *sk)
|
||||||
{
|
{
|
||||||
int seg;
|
int seg;
|
||||||
|
|
||||||
@ -1492,7 +1493,7 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
|||||||
if (__splice_segment(virt_to_page(skb->data),
|
if (__splice_segment(virt_to_page(skb->data),
|
||||||
(unsigned long) skb->data & (PAGE_SIZE - 1),
|
(unsigned long) skb->data & (PAGE_SIZE - 1),
|
||||||
skb_headlen(skb),
|
skb_headlen(skb),
|
||||||
offset, len, skb, spd, 1))
|
offset, len, skb, spd, 1, sk))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1502,7 +1503,7 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
|||||||
const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];
|
const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];
|
||||||
|
|
||||||
if (__splice_segment(f->page, f->page_offset, f->size,
|
if (__splice_segment(f->page, f->page_offset, f->size,
|
||||||
offset, len, skb, spd, 0))
|
offset, len, skb, spd, 0, sk))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1528,12 +1529,13 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
|
|||||||
.ops = &sock_pipe_buf_ops,
|
.ops = &sock_pipe_buf_ops,
|
||||||
.spd_release = sock_spd_release,
|
.spd_release = sock_spd_release,
|
||||||
};
|
};
|
||||||
|
struct sock *sk = skb->sk;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* __skb_splice_bits() only fails if the output has no room left,
|
* __skb_splice_bits() only fails if the output has no room left,
|
||||||
* so no point in going over the frag_list for the error case.
|
* so no point in going over the frag_list for the error case.
|
||||||
*/
|
*/
|
||||||
if (__skb_splice_bits(skb, &offset, &tlen, &spd))
|
if (__skb_splice_bits(skb, &offset, &tlen, &spd, sk))
|
||||||
goto done;
|
goto done;
|
||||||
else if (!tlen)
|
else if (!tlen)
|
||||||
goto done;
|
goto done;
|
||||||
@ -1545,14 +1547,13 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
|
|||||||
struct sk_buff *list = skb_shinfo(skb)->frag_list;
|
struct sk_buff *list = skb_shinfo(skb)->frag_list;
|
||||||
|
|
||||||
for (; list && tlen; list = list->next) {
|
for (; list && tlen; list = list->next) {
|
||||||
if (__skb_splice_bits(list, &offset, &tlen, &spd))
|
if (__skb_splice_bits(list, &offset, &tlen, &spd, sk))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (spd.nr_pages) {
|
if (spd.nr_pages) {
|
||||||
struct sock *sk = skb->sk;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user