diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-09-22 16:02:19 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-09-27 00:58:44 -0400 |
commit | 7a269ffad72f3604b8982fa09c387670e0d2ee14 (patch) | |
tree | 3655a22066bcb1011e5e1b717a00a108143dfd85 /net/ipv4 | |
parent | 0bdb0bd0139f3b6afa252de1487e3ce82a494db9 (diff) |
tcp: ECN blackhole should not force quickack mode
While playing with a new ADSL box at home, I discovered that ECN
blackhole can trigger suboptimal quickack mode on linux : We send one
ACK for each incoming data frame, without any delay and eventual
piggyback.
This is because TCP_ECN_check_ce() considers that if no ECT is seen on a
segment, this is because this segment was a retransmit.
Refine this heuristic and apply it only if we seen ECT in a previous
segment, to detect ECN blackhole at IP level.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Jamal Hadi Salim <jhs@mojatatu.com>
CC: Jerry Chu <hkchu@google.com>
CC: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
CC: Jim Gettys <jg@freedesktop.org>
CC: Dave Taht <dave.taht@gmail.com>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/tcp_input.c | 23 |
1 files changed, 16 insertions, 7 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a5d01b183cf7..5a4408c55155 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -217,16 +217,25 @@ static inline void TCP_ECN_withdraw_cwr(struct tcp_sock *tp) | |||
217 | tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; | 217 | tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; |
218 | } | 218 | } |
219 | 219 | ||
220 | static inline void TCP_ECN_check_ce(struct tcp_sock *tp, struct sk_buff *skb) | 220 | static inline void TCP_ECN_check_ce(struct tcp_sock *tp, const struct sk_buff *skb) |
221 | { | 221 | { |
222 | if (tp->ecn_flags & TCP_ECN_OK) { | 222 | if (!(tp->ecn_flags & TCP_ECN_OK)) |
223 | if (INET_ECN_is_ce(TCP_SKB_CB(skb)->flags)) | 223 | return; |
224 | tp->ecn_flags |= TCP_ECN_DEMAND_CWR; | 224 | |
225 | switch (TCP_SKB_CB(skb)->flags & INET_ECN_MASK) { | ||
226 | case INET_ECN_NOT_ECT: | ||
225 | /* Funny extension: if ECT is not set on a segment, | 227 | /* Funny extension: if ECT is not set on a segment, |
226 | * it is surely retransmit. It is not in ECN RFC, | 228 | * and we already seen ECT on a previous segment, |
227 | * but Linux follows this rule. */ | 229 | * it is probably a retransmit. |
228 | else if (INET_ECN_is_not_ect((TCP_SKB_CB(skb)->flags))) | 230 | */ |
231 | if (tp->ecn_flags & TCP_ECN_SEEN) | ||
229 | tcp_enter_quickack_mode((struct sock *)tp); | 232 | tcp_enter_quickack_mode((struct sock *)tp); |
233 | break; | ||
234 | case INET_ECN_CE: | ||
235 | tp->ecn_flags |= TCP_ECN_DEMAND_CWR; | ||
236 | /* fallinto */ | ||
237 | default: | ||
238 | tp->ecn_flags |= TCP_ECN_SEEN; | ||
230 | } | 239 | } |
231 | } | 240 | } |
232 | 241 | ||