Index: sys/netinet/tcp_sack.c =================================================================== --- sys/netinet/tcp_sack.c +++ sys/netinet/tcp_sack.c @@ -604,6 +604,12 @@ SEQ_GT(sack.end, tp->snd_una) && SEQ_LEQ(sack.end, tp->snd_max)) { sack_blocks[num_sack_blks++] = sack; + } else if (SEQ_LEQ(sack.start, th_ack) && + SEQ_LEQ(sack.end, th_ack)) { + /* + * Its a D-SACK block. + */ + tcp_record_dsack(tp, sack.start, sack.end, 0); } } } Index: sys/netinet/tcp_stacks/bbr.c =================================================================== --- sys/netinet/tcp_stacks/bbr.c +++ sys/netinet/tcp_stacks/bbr.c @@ -7593,14 +7593,12 @@ } sack_blocks[num_sack_blks] = sack; num_sack_blks++; -#ifdef NETFLIX_STATS } else if (SEQ_LEQ(sack.start, th_ack) && SEQ_LEQ(sack.end, th_ack)) { /* * Its a D-SACK block. */ - tcp_record_dsack(sack.start, sack.end); -#endif + tcp_record_dsack(tp, sack.start, sack.end, 0); } } if (num_sack_blks == 0) Index: sys/netinet/tcp_stacks/rack.c =================================================================== --- sys/netinet/tcp_stacks/rack.c +++ sys/netinet/tcp_stacks/rack.c @@ -9402,11 +9402,12 @@ } #endif -static void +static int rack_note_dsack(struct tcp_rack *rack, tcp_seq start, tcp_seq end) { uint32_t am, l_end; + int was_tlp = 0; if (SEQ_GT(end, start)) am = end - start; @@ -9422,6 +9423,7 @@ * our previous retransmit TLP. */ rack_log_dsack_event(rack, 7, __LINE__, start, end); + was_tlp = 1; goto skip_dsack_round; } if (rack->rc_last_sent_tlp_seq_valid) { @@ -9433,6 +9435,7 @@ * for reordering purposes. */ rack_log_dsack_event(rack, 7, __LINE__, start, end); + was_tlp = 1; goto skip_dsack_round; } } @@ -9462,6 +9465,7 @@ rack->r_ctl.retran_during_recovery = 0; rack->r_ctl.dsack_byte_cnt = 0; } + return (was_tlp); } static void @@ -9614,13 +9618,13 @@ num_sack_blks++; } else if (SEQ_LEQ(sack.start, th_ack) && SEQ_LEQ(sack.end, th_ack)) { -#ifdef NETFLIX_STATS + int was_tlp; + + was_tlp = rack_note_dsack(rack, sack.start, sack.end); /* * Its a D-SACK block. */ - tcp_record_dsack(sack.start, sack.end); -#endif - rack_note_dsack(rack, sack.start, sack.end); + tcp_record_dsack(tp, sack.start, sack.end, was_tlp); } } if (rack->rc_dsack_round_seen) { Index: sys/netinet/tcp_subr.c =================================================================== --- sys/netinet/tcp_subr.c +++ sys/netinet/tcp_subr.c @@ -390,6 +390,30 @@ struct tcp_funchead t_functions; static struct tcp_function_block *tcp_func_set_ptr = &tcp_def_funcblk; +void +tcp_record_dsack(struct tcpcb *tp, tcp_seq start, tcp_seq end, int tlp) +{ + TCPSTAT_INC(tcps_dsack_count); + tp->t_dsack_pack++; + if (tlp == 0) { + if (SEQ_GT(end, start)) { + tp->t_dsack_bytes += (end - start); + TCPSTAT_ADD(tcps_dsack_bytes, (end - start)); + } else { + tp->t_dsack_tlp_bytes += (start - end); + TCPSTAT_ADD(tcps_dsack_bytes, (start - end)); + } + } else { + if (SEQ_GT(end, start)) { + tp->t_dsack_bytes += (end - start); + TCPSTAT_ADD(tcps_dsack_tlp_bytes, (end - start)); + } else { + tp->t_dsack_tlp_bytes += (start - end); + TCPSTAT_ADD(tcps_dsack_tlp_bytes, (start - end)); + } + } +} + static struct tcp_function_block * find_tcp_functions_locked(struct tcp_function_set *fs) { @@ -4003,6 +4027,9 @@ xt->t_snd_wnd = tp->snd_wnd; xt->t_snd_cwnd = tp->snd_cwnd; xt->t_snd_ssthresh = tp->snd_ssthresh; + xt->t_dsack_bytes = tp->t_dsack_bytes; + xt->t_dsack_tlp_bytes = tp->t_dsack_tlp_bytes; + xt->t_dsack_pack = tp->t_dsack_pack; xt->t_maxseg = tp->t_maxseg; xt->xt_ecn = (tp->t_flags2 & TF2_ECN_PERMIT) ? 1 : 0 + (tp->t_flags2 & TF2_ACE_PERMIT) ? 2 : 0; Index: sys/netinet/tcp_var.h =================================================================== --- sys/netinet/tcp_var.h +++ sys/netinet/tcp_var.h @@ -265,6 +265,9 @@ uint64_t t_sndtlpbyte; /* total tail loss probe bytes sent */ uint64_t t_sndbytes; /* total bytes sent */ uint64_t t_snd_rxt_bytes; /* total bytes retransmitted */ + uint32_t t_dsack_bytes; /* Total number of dsack bytes we have received */ + uint32_t t_dsack_tlp_bytes; /* Total number of dsack bytes we have received for TLPs sent */ + uint32_t t_dsack_pack; /* Total dsack packets we have recieved */ uint8_t t_tfo_client_cookie_len; /* TCP Fast Open client cookie length */ uint32_t t_end_info_status; /* Status flag of end info */ @@ -703,7 +706,12 @@ uint64_t tcps_tunneled_pkts; /* Packets encap's in UDP received */ uint64_t tcps_tunneled_errs; /* Packets that had errors that were UDP encaped */ - uint64_t _pad[9]; /* 6 UTO, 3 TBD */ + /* Dsack related stats */ + uint64_t tcps_dsack_count; /* Number of ACKs arriving with DSACKs */ + uint64_t tcps_dsack_bytes; /* Number of bytes DSACK'ed no TLP */ + uint64_t tcps_dsack_tlp_bytes; /* Number of bytes DSACK'ed due to TLPs */ + + uint64_t _pad[6]; /* 3 UTO, 3 TBD */ }; #define tcps_rcvmemdrop tcps_rcvreassfull /* compat */ @@ -801,9 +809,12 @@ uint32_t t_rcv_wnd; /* (s) */ uint32_t t_snd_wnd; /* (s) */ uint32_t xt_ecn; /* (s) */ + uint32_t t_dsack_bytes; /* (n) */ + uint32_t t_dsack_tlp_bytes; /* (n) */ + uint32_t t_dsack_pack; /* (n) */ uint16_t xt_encaps_port; /* (s) */ int16_t spare16; - int32_t spare32[25]; + int32_t spare32[22]; } __aligned(8); #ifdef _KERNEL @@ -1064,6 +1075,7 @@ struct mbuf *, int); void tcp_setpersist(struct tcpcb *); void tcp_slowtimo(void); +void tcp_record_dsack(struct tcpcb *tp, tcp_seq start, tcp_seq end, int tlp); struct tcptemp * tcpip_maketemplate(struct inpcb *); void tcpip_fillheaders(struct inpcb *, uint16_t, void *, void *); Index: usr.bin/netstat/inet.c =================================================================== --- usr.bin/netstat/inet.c +++ usr.bin/netstat/inet.c @@ -695,6 +695,12 @@ "{N:/window probe%s}\n"); p(tcps_rcvwinupd, "\t\t{:receive-window-update-packets/%ju} " "{N:/window update packet%s}\n"); + p(tcps_dsack_count, "\t\t{:received-with-dsack-packets/%ju} " + "{N:/packet%s received with dsack}\n"); + p(tcps_dsack_bytes, "\t\t{:received-with-dsack-bytes/%ju} " + "{N:/dsack byte%s received (no TLP involved)}\n"); + p(tcps_dsack_tlp_bytes, "\t\t{:received-with-dsack-bytes-tlp/%ju} " + "{N:/dsack byte%s received (TLP responsible)}\n"); p(tcps_rcvafterclose, "\t\t{:received-after-close-packets/%ju} " "{N:/packet%s received after close}\n"); p(tcps_rcvbadsum, "\t\t{:discard-bad-checksum/%ju} "