From a4a196e7710acab560e2a43ea1ebab51a970f063 Mon Sep 17 00:00:00 2001 From: Andrew Vagin Date: Tue, 13 Nov 2012 23:01:05 +0400 Subject: [PATCH] tcp: fix retransmission in repair mode (v2) Currently if a socket was repaired with a few packet in a write queue, a kernel bug may be triggered: kernel BUG at net/ipv4/tcp_output.c:2330! RIP: 0010:[] tcp_retransmit_skb+0x5ff/0x610 According to the initial realization v3.4-rc2-963-gc0e88ff, all skb-s should look like already posted. This patch fixes code according with this sentence. Here are three points, which were not done in the initial patch: 1. A tcp send head should not be changed 2. Initialize TSO state of a skb 3. Reset the retransmission time v2: don't touch fast paths. Cc: Pavel Emelyanov Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Signed-off-by: Andrey Vagin --- include/net/tcp.h | 1 + net/ipv4/tcp.c | 8 ++++++-- net/ipv4/tcp_output.c | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6feeccd..0ced597 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -544,6 +544,7 @@ extern bool tcp_syn_flood_action(struct sock *sk, extern void tcp_push_one(struct sock *, unsigned int mss_now); extern void tcp_send_ack(struct sock *sk); extern void tcp_send_delayed_ack(struct sock *sk); +extern void tcp_repair_push(struct sock *sk, unsigned int mss_now); /* tcp_input.c */ extern void tcp_cwnd_application_limited(struct sock *sk); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 242e269..985cbe3 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1231,8 +1231,12 @@ wait_for_memory: } out: - if (copied && likely(!tp->repair)) - tcp_push(sk, flags, mss_now, tp->nonagle); + if (copied) { + if (likely(!tp->repair)) + tcp_push(sk, flags, mss_now, tp->nonagle); + else + tcp_repair_push(sk, mss_now); + } release_sock(sk); return copied + copied_syn; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index cfe6ffe..9aac058 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2047,6 +2047,23 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, return !tp->packets_out && tcp_send_head(sk); } +/* Mark all penging frames as 'already sent. */ +void tcp_repair_push(struct sock *sk, unsigned int mss_now) +{ + struct sk_buff *skb; + unsigned int tso_segs; + + while ((skb = tcp_send_head(sk))) { + tso_segs = tcp_init_tso_segs(sk, skb, mss_now); + BUG_ON(!tso_segs); + + /* This one is restored as 'sent out'. + * This call will increment packets_out. + */ + tcp_event_new_data_sent(sk, skb); + } +} + /* Push out any pending frames which were held back due to * TCP_CORK or attempt at coalescing tiny packets. * The socket must be locked by the caller. -- 1.7.11.7