Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[TCP]: Adjust {p,f}ackets_out correctly in tcp_retransmit_skb()

Well I've only found one potential cause for the assertion
failure in tcp_mark_head_lost. First of all, this can only
occur if cnt > 1 since tp->packets_out is never zero here.
If it did hit zero we'd have much bigger problems.

So cnt is equal to fackets_out - reordering. Normally
fackets_out is less than packets_out. The only reason
I've found that might cause fackets_out to exceed packets_out
is if tcp_fragment is called from tcp_retransmit_skb with a
TSO skb and the current MSS is greater than the MSS stored
in the TSO skb. This might occur as the result of an expiring
dst entry.

In that case, packets_out may decrease (line 1380-1381 in
tcp_output.c). However, fackets_out is unchanged which means
that it may in fact exceed packets_out.

Previously tcp_retrans_try_collapse was the only place where
packets_out can go down and it takes care of this by decrementing
fackets_out.

So we should make sure that fackets_out is reduced by an appropriate
amount here as well.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Herbert Xu and committed by
David S. Miller
b5da623a bc68552f

+10 -4
+10 -4
net/ipv4/tcp_output.c
··· 1370 1370 1371 1371 if (skb->len > cur_mss) { 1372 1372 int old_factor = tcp_skb_pcount(skb); 1373 - int new_factor; 1373 + int diff; 1374 1374 1375 1375 if (tcp_fragment(sk, skb, cur_mss, cur_mss)) 1376 1376 return -ENOMEM; /* We'll try again later. */ 1377 1377 1378 1378 /* New SKB created, account for it. */ 1379 - new_factor = tcp_skb_pcount(skb); 1380 - tp->packets_out -= old_factor - new_factor; 1381 - tp->packets_out += tcp_skb_pcount(skb->next); 1379 + diff = old_factor - tcp_skb_pcount(skb) - 1380 + tcp_skb_pcount(skb->next); 1381 + tp->packets_out -= diff; 1382 + 1383 + if (diff > 0) { 1384 + tp->fackets_out -= diff; 1385 + if ((int)tp->fackets_out < 0) 1386 + tp->fackets_out = 0; 1387 + } 1382 1388 } 1383 1389 1384 1390 /* Collapse two adjacent packets if worthwhile and we can. */