Commit c7caf8d3 authored by Ilpo Jrvinen's avatar Ilpo Jrvinen Committed by David S. Miller

[TCP]: Fix reord detection due to snd_una covered holes

Fixes subtle bug like the one with fastpath_cnt_hint happening
due to the way the GSO and hints interact. Because hints are not
reset when just a GSOed skb is partially ACKed, there's no
guarantee that the relevant part of the write queue is going to
be processed in sacktag at all (skbs below snd_una) because
fastpath hint can fast forward the entrypoint.

This was also on the way of future reductions in sacktag's skb
processing. Also future cleanups in sacktag can be made after
this (in 2.6.25).

This may make reordering update in tcp_try_undo_partial
redundant but I'm not too sure so I left it there.
Signed-off-by: default avatarIlpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8dd71c5d
...@@ -1417,11 +1417,6 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_ ...@@ -1417,11 +1417,6 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
if ((dup_sack && in_sack) && if ((dup_sack && in_sack) &&
(sacked&TCPCB_SACKED_ACKED)) (sacked&TCPCB_SACKED_ACKED))
reord = min(fack_count, reord); reord = min(fack_count, reord);
} else {
/* If it was in a hole, we detected reordering. */
if (fack_count < prior_fackets &&
!(sacked&TCPCB_SACKED_ACKED))
reord = min(fack_count, reord);
} }
/* Nothing to do; acked frame is about to be dropped. */ /* Nothing to do; acked frame is about to be dropped. */
...@@ -2634,7 +2629,8 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) ...@@ -2634,7 +2629,8 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb)
* is before the ack sequence we can discard it as it's confirmed to have * is before the ack sequence we can discard it as it's confirmed to have
* arrived at the other end. * arrived at the other end.
*/ */
static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p) static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p,
int prior_fackets)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
const struct inet_connection_sock *icsk = inet_csk(sk); const struct inet_connection_sock *icsk = inet_csk(sk);
...@@ -2643,6 +2639,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p) ...@@ -2643,6 +2639,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p)
int fully_acked = 1; int fully_acked = 1;
int flag = 0; int flag = 0;
int prior_packets = tp->packets_out; int prior_packets = tp->packets_out;
u32 cnt = 0;
u32 reord = tp->packets_out;
s32 seq_rtt = -1; s32 seq_rtt = -1;
ktime_t last_ackt = net_invalid_timestamp(); ktime_t last_ackt = net_invalid_timestamp();
...@@ -2683,10 +2681,14 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p) ...@@ -2683,10 +2681,14 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p)
if ((flag & FLAG_DATA_ACKED) || if ((flag & FLAG_DATA_ACKED) ||
(packets_acked > 1)) (packets_acked > 1))
flag |= FLAG_NONHEAD_RETRANS_ACKED; flag |= FLAG_NONHEAD_RETRANS_ACKED;
} else if (seq_rtt < 0) { } else {
seq_rtt = now - scb->when; if (seq_rtt < 0) {
if (fully_acked) seq_rtt = now - scb->when;
last_ackt = skb->tstamp; if (fully_acked)
last_ackt = skb->tstamp;
}
if (!(sacked & TCPCB_SACKED_ACKED))
reord = min(cnt, reord);
} }
if (sacked & TCPCB_SACKED_ACKED) if (sacked & TCPCB_SACKED_ACKED)
...@@ -2697,12 +2699,16 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p) ...@@ -2697,12 +2699,16 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p)
if ((sacked & TCPCB_URG) && tp->urg_mode && if ((sacked & TCPCB_URG) && tp->urg_mode &&
!before(end_seq, tp->snd_up)) !before(end_seq, tp->snd_up))
tp->urg_mode = 0; tp->urg_mode = 0;
} else if (seq_rtt < 0) { } else {
seq_rtt = now - scb->when; if (seq_rtt < 0) {
if (fully_acked) seq_rtt = now - scb->when;
last_ackt = skb->tstamp; if (fully_acked)
last_ackt = skb->tstamp;
}
reord = min(cnt, reord);
} }
tp->packets_out -= packets_acked; tp->packets_out -= packets_acked;
cnt += packets_acked;
/* Initial outgoing SYN's get put onto the write_queue /* Initial outgoing SYN's get put onto the write_queue
* just like anything else we transmit. It is not * just like anything else we transmit. It is not
...@@ -2734,13 +2740,18 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p) ...@@ -2734,13 +2740,18 @@ static int tcp_clean_rtx_queue(struct sock *sk, s32 *seq_rtt_p)
tcp_ack_update_rtt(sk, flag, seq_rtt); tcp_ack_update_rtt(sk, flag, seq_rtt);
tcp_rearm_rto(sk); tcp_rearm_rto(sk);
if (tcp_is_reno(tp)) {
tcp_remove_reno_sacks(sk, pkts_acked);
} else {
/* Non-retransmitted hole got filled? That's reordering */
if (reord < prior_fackets)
tcp_update_reordering(sk, tp->fackets_out - reord, 0);
}
tp->fackets_out -= min(pkts_acked, tp->fackets_out); tp->fackets_out -= min(pkts_acked, tp->fackets_out);
/* hint's skb might be NULL but we don't need to care */ /* hint's skb might be NULL but we don't need to care */
tp->fastpath_cnt_hint -= min_t(u32, pkts_acked, tp->fastpath_cnt_hint -= min_t(u32, pkts_acked,
tp->fastpath_cnt_hint); tp->fastpath_cnt_hint);
if (tcp_is_reno(tp))
tcp_remove_reno_sacks(sk, pkts_acked);
if (ca_ops->pkts_acked) { if (ca_ops->pkts_acked) {
s32 rtt_us = -1; s32 rtt_us = -1;
...@@ -3023,6 +3034,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) ...@@ -3023,6 +3034,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
u32 ack_seq = TCP_SKB_CB(skb)->seq; u32 ack_seq = TCP_SKB_CB(skb)->seq;
u32 ack = TCP_SKB_CB(skb)->ack_seq; u32 ack = TCP_SKB_CB(skb)->ack_seq;
u32 prior_in_flight; u32 prior_in_flight;
u32 prior_fackets;
s32 seq_rtt; s32 seq_rtt;
int prior_packets; int prior_packets;
int frto_cwnd = 0; int frto_cwnd = 0;
...@@ -3047,6 +3059,8 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) ...@@ -3047,6 +3059,8 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
tp->bytes_acked += min(ack - prior_snd_una, tp->mss_cache); tp->bytes_acked += min(ack - prior_snd_una, tp->mss_cache);
} }
prior_fackets = tp->fackets_out;
if (!(flag&FLAG_SLOWPATH) && after(ack, prior_snd_una)) { if (!(flag&FLAG_SLOWPATH) && after(ack, prior_snd_una)) {
/* Window is constant, pure forward advance. /* Window is constant, pure forward advance.
* No more checks are required. * No more checks are required.
...@@ -3088,7 +3102,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) ...@@ -3088,7 +3102,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
prior_in_flight = tcp_packets_in_flight(tp); prior_in_flight = tcp_packets_in_flight(tp);
/* See if we can take anything off of the retransmit queue. */ /* See if we can take anything off of the retransmit queue. */
flag |= tcp_clean_rtx_queue(sk, &seq_rtt); flag |= tcp_clean_rtx_queue(sk, &seq_rtt, prior_fackets);
/* Guarantee sacktag reordering detection against wrap-arounds */ /* Guarantee sacktag reordering detection against wrap-arounds */
if (before(tp->frto_highmark, tp->snd_una)) if (before(tp->frto_highmark, tp->snd_una))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment