| From a2252d0dd0d7345a6266c6c1c5c3e7ff7025c207 Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Wed, 9 Jan 2013 20:59:09 +0000 |
| Subject: tcp: fix splice() and tcp collapsing interaction |
| |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| [ Upstream commit f26845b43c75d3f32f98d194c1327b5b1e6b3fb0 ] |
| |
| Under unusual circumstances, TCP collapse can split a big GRO TCP packet |
| while its being used in a splice(socket->pipe) operation. |
| |
| skb_splice_bits() releases the socket lock before calling |
| splice_to_pipe(). |
| |
| [ 1081.353685] WARNING: at net/ipv4/tcp.c:1330 tcp_cleanup_rbuf+0x4d/0xfc() |
| [ 1081.371956] Hardware name: System x3690 X5 -[7148Z68]- |
| [ 1081.391820] cleanup rbuf bug: copied AD3BCF1 seq AD370AF rcvnxt AD3CF13 |
| |
| To fix this problem, we must eat skbs in tcp_recv_skb(). |
| |
| Remove the inline keyword from tcp_recv_skb() definition since |
| it has three call sites. |
| |
| Reported-by: Christian Becker <c.becker@traviangames.com> |
| Cc: Willy Tarreau <w@1wt.eu> |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Tested-by: Willy Tarreau <w@1wt.eu> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/tcp.c | 13 ++++++++++--- |
| 1 file changed, 10 insertions(+), 3 deletions(-) |
| |
| --- a/net/ipv4/tcp.c |
| +++ b/net/ipv4/tcp.c |
| @@ -1427,12 +1427,12 @@ static void tcp_service_net_dma(struct s |
| } |
| #endif |
| |
| -static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) |
| +static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) |
| { |
| struct sk_buff *skb; |
| u32 offset; |
| |
| - skb_queue_walk(&sk->sk_receive_queue, skb) { |
| + while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) { |
| offset = seq - TCP_SKB_CB(skb)->seq; |
| if (tcp_hdr(skb)->syn) |
| offset--; |
| @@ -1440,6 +1440,11 @@ static inline struct sk_buff *tcp_recv_s |
| *off = offset; |
| return skb; |
| } |
| + /* This looks weird, but this can happen if TCP collapsing |
| + * splitted a fat GRO packet, while we released socket lock |
| + * in skb_splice_bits() |
| + */ |
| + sk_eat_skb(sk, skb, false); |
| } |
| return NULL; |
| } |
| @@ -1519,8 +1524,10 @@ int tcp_read_sock(struct sock *sk, read_ |
| tcp_rcv_space_adjust(sk); |
| |
| /* Clean up data we have read: This will do ACK frames. */ |
| - if (copied > 0) |
| + if (copied > 0) { |
| + tcp_recv_skb(sk, seq, &offset); |
| tcp_cleanup_rbuf(sk, copied); |
| + } |
| return copied; |
| } |
| EXPORT_SYMBOL(tcp_read_sock); |