@ -863,6 +863,9 @@ void tcp_enter_cwr(struct sock *sk, const int set_ssthresh)
*/
*/
static void tcp_disable_fack ( struct tcp_sock * tp )
static void tcp_disable_fack ( struct tcp_sock * tp )
{
{
/* RFC3517 uses different metric in lost marker => reset on change */
if ( tcp_is_fack ( tp ) )
tp - > lost_skb_hint = NULL ;
tp - > rx_opt . sack_ok & = ~ 2 ;
tp - > rx_opt . sack_ok & = ~ 2 ;
}
}
@ -1470,6 +1473,13 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
tp - > sacked_out + = tcp_skb_pcount ( skb ) ;
tp - > sacked_out + = tcp_skb_pcount ( skb ) ;
fack_count + = tcp_skb_pcount ( skb ) ;
fack_count + = tcp_skb_pcount ( skb ) ;
/* Lost marker hint past SACKed? Tweak RFC3517 cnt */
if ( ! tcp_is_fack ( tp ) & & ( tp - > lost_skb_hint ! = NULL ) & &
before ( TCP_SKB_CB ( skb ) - > seq ,
TCP_SKB_CB ( tp - > lost_skb_hint ) - > seq ) )
tp - > lost_cnt_hint + = tcp_skb_pcount ( skb ) ;
if ( fack_count > tp - > fackets_out )
if ( fack_count > tp - > fackets_out )
tp - > fackets_out = fack_count ;
tp - > fackets_out = fack_count ;
@ -1504,7 +1514,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
flag & = ~ FLAG_ONLY_ORIG_SACKED ;
flag & = ~ FLAG_ONLY_ORIG_SACKED ;
}
}
if ( tp - > retrans_out & &
if ( tcp_is_fack ( tp ) & & t p - > retrans_out & &
after ( highest_sack_end_seq , tp - > lost_retrans_low ) & &
after ( highest_sack_end_seq , tp - > lost_retrans_low ) & &
icsk - > icsk_ca_state = = TCP_CA_Recovery )
icsk - > icsk_ca_state = = TCP_CA_Recovery )
flag | = tcp_mark_lost_retrans ( sk , highest_sack_end_seq ) ;
flag | = tcp_mark_lost_retrans ( sk , highest_sack_end_seq ) ;
@ -1858,6 +1868,26 @@ static inline int tcp_fackets_out(struct tcp_sock *tp)
return tcp_is_reno ( tp ) ? tp - > sacked_out + 1 : tp - > fackets_out ;
return tcp_is_reno ( tp ) ? tp - > sacked_out + 1 : tp - > fackets_out ;
}
}
/* Heurestics to calculate number of duplicate ACKs. There's no dupACKs
* counter when SACK is enabled ( without SACK , sacked_out is used for
* that purpose ) .
*
* Instead , with FACK TCP uses fackets_out that includes both SACKed
* segments up to the highest received SACK block so far and holes in
* between them .
*
* With reordering , holes may still be in flight , so RFC3517 recovery
* uses pure sacked_out ( total number of SACKed segments ) even though
* it violates the RFC that uses duplicate ACKs , often these are equal
* but when e . g . out - of - window ACKs or packet duplication occurs ,
* they differ . Since neither occurs due to loss , TCP should really
* ignore them .
*/
static inline int tcp_dupack_heurestics ( struct tcp_sock * tp )
{
return tcp_is_fack ( tp ) ? tp - > fackets_out : tp - > sacked_out + 1 ;
}
static inline int tcp_skb_timedout ( struct sock * sk , struct sk_buff * skb )
static inline int tcp_skb_timedout ( struct sock * sk , struct sk_buff * skb )
{
{
return ( tcp_time_stamp - TCP_SKB_CB ( skb ) - > when > inet_csk ( sk ) - > icsk_rto ) ;
return ( tcp_time_stamp - TCP_SKB_CB ( skb ) - > when > inet_csk ( sk ) - > icsk_rto ) ;
@ -1978,13 +2008,13 @@ static int tcp_time_to_recover(struct sock *sk)
return 1 ;
return 1 ;
/* Not-A-Trick#2 : Classic rule... */
/* Not-A-Trick#2 : Classic rule... */
if ( tcp_fackets_out ( tp ) > tp - > reordering )
if ( tcp_dupack_heurestics ( tp ) > tp - > reordering )
return 1 ;
return 1 ;
/* Trick#3 : when we use RFC2988 timer restart, fast
/* Trick#3 : when we use RFC2988 timer restart, fast
* retransmit can be triggered by timeout of queue head .
* retransmit can be triggered by timeout of queue head .
*/
*/
if ( tcp_head_timedout ( sk ) )
if ( tcp_is_fack ( tp ) & & tcp_ head_timedout ( sk ) )
return 1 ;
return 1 ;
/* Trick#4: It is still not OK... But will it be useful to delay
/* Trick#4: It is still not OK... But will it be useful to delay
@ -2017,8 +2047,10 @@ static void tcp_verify_retransmit_hint(struct tcp_sock *tp,
tp - > retransmit_skb_hint = NULL ;
tp - > retransmit_skb_hint = NULL ;
}
}
/* Mark head of queue up as lost. */
/* Mark head of queue up as lost. With RFC3517 SACK, the packets is
static void tcp_mark_head_lost ( struct sock * sk , int packets )
* is against sacked " cnt " , otherwise it ' s against facked " cnt "
*/
static void tcp_mark_head_lost ( struct sock * sk , int packets , int fast_rexmit )
{
{
struct tcp_sock * tp = tcp_sk ( sk ) ;
struct tcp_sock * tp = tcp_sk ( sk ) ;
struct sk_buff * skb ;
struct sk_buff * skb ;
@ -2040,8 +2072,13 @@ static void tcp_mark_head_lost(struct sock *sk, int packets)
/* this is not the most efficient way to do this... */
/* this is not the most efficient way to do this... */
tp - > lost_skb_hint = skb ;
tp - > lost_skb_hint = skb ;
tp - > lost_cnt_hint = cnt ;
tp - > lost_cnt_hint = cnt ;
cnt + = tcp_skb_pcount ( skb ) ;
if ( cnt > packets | | after ( TCP_SKB_CB ( skb ) - > end_seq , tp - > high_seq ) )
if ( tcp_is_fack ( tp ) | |
( TCP_SKB_CB ( skb ) - > sacked & TCPCB_SACKED_ACKED ) )
cnt + = tcp_skb_pcount ( skb ) ;
if ( ( ( ! fast_rexmit | | ( tp - > lost_out > 0 ) ) & & ( cnt > packets ) ) | |
after ( TCP_SKB_CB ( skb ) - > end_seq , tp - > high_seq ) )
break ;
break ;
if ( ! ( TCP_SKB_CB ( skb ) - > sacked & ( TCPCB_SACKED_ACKED | TCPCB_LOST ) ) ) {
if ( ! ( TCP_SKB_CB ( skb ) - > sacked & ( TCPCB_SACKED_ACKED | TCPCB_LOST ) ) ) {
TCP_SKB_CB ( skb ) - > sacked | = TCPCB_LOST ;
TCP_SKB_CB ( skb ) - > sacked | = TCPCB_LOST ;
@ -2054,17 +2091,22 @@ static void tcp_mark_head_lost(struct sock *sk, int packets)
/* Account newly detected lost packet(s) */
/* Account newly detected lost packet(s) */
static void tcp_update_scoreboard ( struct sock * sk )
static void tcp_update_scoreboard ( struct sock * sk , int fast_rexmit )
{
{
struct tcp_sock * tp = tcp_sk ( sk ) ;
struct tcp_sock * tp = tcp_sk ( sk ) ;
if ( tcp_is_fack ( tp ) ) {
if ( tcp_is_reno ( tp ) ) {
tcp_mark_head_lost ( sk , 1 , fast_rexmit ) ;
} else if ( tcp_is_fack ( tp ) ) {
int lost = tp - > fackets_out - tp - > reordering ;
int lost = tp - > fackets_out - tp - > reordering ;
if ( lost < = 0 )
if ( lost < = 0 )
lost = 1 ;
lost = 1 ;
tcp_mark_head_lost ( sk , lost ) ;
tcp_mark_head_lost ( sk , lost , fast_rexmit ) ;
} else {
} else {
tcp_mark_head_lost ( sk , 1 ) ;
int sacked_upto = tp - > sacked_out - tp - > reordering ;
if ( sacked_upto < 0 )
sacked_upto = 0 ;
tcp_mark_head_lost ( sk , sacked_upto , fast_rexmit ) ;
}
}
/* New heuristics: it is possible only after we switched
/* New heuristics: it is possible only after we switched
@ -2072,7 +2114,7 @@ static void tcp_update_scoreboard(struct sock *sk)
* Hence , we can detect timed out packets during fast
* Hence , we can detect timed out packets during fast
* retransmit without falling to slow start .
* retransmit without falling to slow start .
*/
*/
if ( ! tcp_is_reno ( tp ) & & tcp_head_timedout ( sk ) ) {
if ( tcp_is_fack ( tp ) & & tcp_head_timedout ( sk ) ) {
struct sk_buff * skb ;
struct sk_buff * skb ;
skb = tp - > scoreboard_skb_hint ? tp - > scoreboard_skb_hint
skb = tp - > scoreboard_skb_hint ? tp - > scoreboard_skb_hint
@ -2245,7 +2287,7 @@ static int tcp_try_undo_partial(struct sock *sk, int acked)
{
{
struct tcp_sock * tp = tcp_sk ( sk ) ;
struct tcp_sock * tp = tcp_sk ( sk ) ;
/* Partial ACK arrived. Force Hoe's retransmit. */
/* Partial ACK arrived. Force Hoe's retransmit. */
int failed = tcp_is_reno ( tp ) | | tp - > fackets_out > tp - > reordering ;
int failed = tcp_is_reno ( tp ) | | ( tcp_fackets_out ( tp ) > tp - > reordering ) ;
if ( tcp_may_undo ( tp ) ) {
if ( tcp_may_undo ( tp ) ) {
/* Plain luck! Hole if filled with delayed
/* Plain luck! Hole if filled with delayed
@ -2379,7 +2421,8 @@ tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
struct tcp_sock * tp = tcp_sk ( sk ) ;
struct tcp_sock * tp = tcp_sk ( sk ) ;
int is_dupack = ! ( flag & ( FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP ) ) ;
int is_dupack = ! ( flag & ( FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP ) ) ;
int do_lost = is_dupack | | ( ( flag & FLAG_DATA_SACKED ) & &
int do_lost = is_dupack | | ( ( flag & FLAG_DATA_SACKED ) & &
( tp - > fackets_out > tp - > reordering ) ) ;
( tcp_fackets_out ( tp ) > tp - > reordering ) ) ;
int fast_rexmit = 0 ;
/* Some technical things:
/* Some technical things:
* 1. Reno does not count dupacks ( sacked_out ) automatically . */
* 1. Reno does not count dupacks ( sacked_out ) automatically . */
@ -2399,11 +2442,11 @@ tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
return ;
return ;
/* C. Process data loss notification, provided it is valid. */
/* C. Process data loss notification, provided it is valid. */
if ( ( flag & FLAG_DATA_LOST ) & &
if ( tcp_is_fack ( tp ) & & ( flag & FLAG_DATA_LOST ) & &
before ( tp - > snd_una , tp - > high_seq ) & &
before ( tp - > snd_una , tp - > high_seq ) & &
icsk - > icsk_ca_state ! = TCP_CA_Open & &
icsk - > icsk_ca_state ! = TCP_CA_Open & &
tp - > fackets_out > tp - > reordering ) {
tp - > fackets_out > tp - > reordering ) {
tcp_mark_head_lost ( sk , tp - > fackets_out - tp - > reordering ) ;
tcp_mark_head_lost ( sk , tp - > fackets_out - tp - > reordering , 0 ) ;
NET_INC_STATS_BH ( LINUX_MIB_TCPLOSS ) ;
NET_INC_STATS_BH ( LINUX_MIB_TCPLOSS ) ;
}
}
@ -2522,10 +2565,11 @@ tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
tp - > bytes_acked = 0 ;
tp - > bytes_acked = 0 ;
tp - > snd_cwnd_cnt = 0 ;
tp - > snd_cwnd_cnt = 0 ;
tcp_set_ca_state ( sk , TCP_CA_Recovery ) ;
tcp_set_ca_state ( sk , TCP_CA_Recovery ) ;
fast_rexmit = 1 ;
}
}
if ( do_lost | | tcp_head_timedout ( sk ) )
if ( do_lost | | ( tcp_is_fack ( tp ) & & tcp_head_timedout ( sk ) ) )
tcp_update_scoreboard ( sk ) ;
tcp_update_scoreboard ( sk , fast_rexmit ) ;
tcp_cwnd_down ( sk , flag ) ;
tcp_cwnd_down ( sk , flag ) ;
tcp_xmit_retransmit_queue ( sk ) ;
tcp_xmit_retransmit_queue ( sk ) ;
}
}