tcp: fix for splice receive when used with software LRO
If an skb has nr_frags set to zero but its frag_list is not empty (as it can happen if software LRO is enabled), and a previous tcp_read_sock has consumed the linear part of the skb, then __skb_splice_bits: (a) incorrectly reports an error and (b) forgets to update the offset to account for the linear part Any of the two problems will cause the subsequent __skb_splice_bits call (the one that handles the frag_list skbs) to either skip data, or, if the unadjusted offset is greater then the size of the next skb in the frag_list, make tcp_splice_read loop forever. Signed-off-by: Octavian Purdila <opurdila@ixiacom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
57413ebc4e
commit
db43a282d3
@ -1292,12 +1292,14 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
|||||||
{
|
{
|
||||||
unsigned int nr_pages = spd->nr_pages;
|
unsigned int nr_pages = spd->nr_pages;
|
||||||
unsigned int poff, plen, len, toff, tlen;
|
unsigned int poff, plen, len, toff, tlen;
|
||||||
int headlen, seg;
|
int headlen, seg, error = 0;
|
||||||
|
|
||||||
toff = *offset;
|
toff = *offset;
|
||||||
tlen = *total_len;
|
tlen = *total_len;
|
||||||
if (!tlen)
|
if (!tlen) {
|
||||||
|
error = 1;
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if the offset is greater than the linear part, go directly to
|
* if the offset is greater than the linear part, go directly to
|
||||||
@ -1339,7 +1341,8 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
|||||||
* just jump directly to update and return, no point
|
* just jump directly to update and return, no point
|
||||||
* in going over fragments when the output is full.
|
* in going over fragments when the output is full.
|
||||||
*/
|
*/
|
||||||
if (spd_fill_page(spd, virt_to_page(p), plen, poff, skb))
|
error = spd_fill_page(spd, virt_to_page(p), plen, poff, skb);
|
||||||
|
if (error)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
tlen -= plen;
|
tlen -= plen;
|
||||||
@ -1369,7 +1372,8 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
|||||||
if (!plen)
|
if (!plen)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (spd_fill_page(spd, f->page, plen, poff, skb))
|
error = spd_fill_page(spd, f->page, plen, poff, skb);
|
||||||
|
if (error)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
tlen -= plen;
|
tlen -= plen;
|
||||||
@ -1382,7 +1386,10 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
err:
|
err:
|
||||||
return 1;
|
/* update the offset to reflect the linear part skip, if any */
|
||||||
|
if (!error)
|
||||||
|
*offset = toff;
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user