diff options
author | Steve Lin <stlin@nvidia.com> | 2012-12-04 11:53:47 -0800 |
---|---|---|
committer | Mandar Padmawar <mpadmawar@nvidia.com> | 2013-03-06 00:36:33 -0800 |
commit | 4b63cd6432841b6db91bfb5a120cfbc974855c6f (patch) | |
tree | 2bcd33c878f9f22ea90986b77898b4f1f0305583 | |
parent | 0aa10b64ccf9ddc506862481b5e3f209f6285866 (diff) |
CHROMIUM: tcp: Avoid merging segments on the OOO queue into a cloned SKB
TCP tries to merge socket buffers in the out-of-order queue when they
are sequential. This is generally a good thing to save memory and speed
up processing, but can lead to problems when it expands a buffer that
has previously been cloned.
We have encountered a bug with a confused/broken receive queue, that we
think might have been caused by this problem, as the smsc95xx driver
uses cloned buffers in its receive path. Thanks to edumazet for
pointing that out and suggesting this fix.
This patch makes sure that this code path only expands socket buffers
that have not been cloned and just enqueues them normally in the other
case. In upstream kernels starting with 3.5, the affected code has been
refactored into tcp_try_coalesce with commit 1402d36, which has later
been patched to fix this issue in commit 923dd34. The fix has not been
backported into the upstream 3.4 kernel.
BUG=chromium-os:35827
TEST=None
Signed-off-by: Julius Werner <jwerner@chromium.org>
Original-author: Julius Werner
Bug 1206597
Bug 1232607
Change-Id: I08fdf47b42d903b5079ad0985edd491b421ac7ca
Signed-off-by: Steve Lin <stlin@nvidia.com>
Reviewed-on: http://git-master/r/203898
Reviewed-by: Brandon Casey <bcasey@nvidia.com>
Reviewed-by: Mark Kuo <mkuo@nvidia.com>
GVS: Gerrit_Virtual_Submit
-rw-r--r-- | net/ipv4/tcp_input.c | 4 |
1 files changed, 3 insertions, 1 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 56a9c8d0bef1..6b017383119e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4496,7 +4496,9 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) * to avoid future tcp_collapse_ofo_queue(), * probably the most expensive function in tcp stack. */ - if (skb->len <= skb_tailroom(skb1) && !tcp_hdr(skb)->fin) { + if (skb->len <= skb_tailroom(skb1) && + !tcp_hdr(skb)->fin && + !skb_cloned(skb1)) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE); BUG_ON(skb_copy_bits(skb, 0, |