]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/tcp_subr.c
xnu-3248.40.184.tar.gz
[apple/xnu.git] / bsd / netinet / tcp_subr.c
index 6fafa0f5f853b481cabd900abf4f5ac82c9e3767..88d18875a4913b37296dc39ef9d06722e6c08e1b 100644 (file)
@@ -1134,6 +1134,51 @@ tcp_getrt_rtt(struct tcpcb *tp, struct rtentry *rt)
        }
 }
 
+static inline void
+tcp_update_ecn_perf_stats(struct tcpcb *tp,
+    struct if_tcp_ecn_perf_stat *stat)
+{
+       u_int64_t curval, oldval;
+       struct inpcb *inp = tp->t_inpcb;
+       stat->total_txpkts += inp->inp_stat->txpackets;
+       stat->total_rxpkts += inp->inp_stat->rxpackets;
+       stat->total_rxmitpkts += tp->t_stat.rxmitpkts;
+       stat->total_oopkts += tp->t_rcvoopack;
+       stat->total_reorderpkts += (tp->t_reordered_pkts + tp->t_pawsdrop +
+           tp->t_dsack_sent + tp->t_dsack_recvd);
+
+       /* Average RTT */
+       curval = (tp->t_srtt >> TCP_RTT_SHIFT);
+       if (curval > 0 && tp->t_rttupdated >= 16) {
+               if (stat->rtt_avg == 0) {
+                       stat->rtt_avg = curval;
+               } else {
+                       oldval = stat->rtt_avg;
+                       stat->rtt_avg =
+                           ((oldval << 4) - oldval + curval) >> 4;
+               }
+       }
+
+       /* RTT variance */
+       curval = tp->t_rttvar >> TCP_RTTVAR_SHIFT;
+       if (curval > 0 && tp->t_rttupdated >= 16) {
+               if (stat->rtt_var == 0) {
+                       stat->rtt_var = curval;
+               } else {
+                       oldval = stat->rtt_var;
+                       stat->rtt_var =
+                           ((oldval << 4) - oldval + curval) >> 4;
+               }
+       }
+
+       /* Total number of SACK recovery episodes */
+       stat->sack_episodes += tp->t_sack_recovery_episode;
+
+       if (inp->inp_socket->so_error == ECONNRESET)
+               stat->rst_drop++;
+       return;
+}
+
 /*
  * Close a TCP control block:
  *     discard all space held by the tcp
@@ -1316,22 +1361,90 @@ no_valid_rt:
 
        /* free the reassembly queue, if any */
        (void) tcp_freeq(tp);
+
+       /* Collect ECN related statistics */
+       if (tp->ecn_flags & TE_SETUPSENT) {
+               if (tp->ecn_flags & TE_CLIENT_SETUP) {
+                       INP_INC_IFNET_STAT(inp, ecn_client_setup);
+                       if (TCP_ECN_ENABLED(tp)) {
+                               INP_INC_IFNET_STAT(inp,
+                                   ecn_client_success);
+                       } else if (tp->ecn_flags & TE_LOST_SYN) {
+                               INP_INC_IFNET_STAT(inp, ecn_syn_lost);
+                       } else {
+                               INP_INC_IFNET_STAT(inp,
+                                   ecn_peer_nosupport);
+                       }
+               } else {
+                       INP_INC_IFNET_STAT(inp, ecn_server_setup);
+                       if (TCP_ECN_ENABLED(tp)) {
+                               INP_INC_IFNET_STAT(inp,
+                                   ecn_server_success);
+                       } else if (tp->ecn_flags & TE_LOST_SYNACK) {
+                               INP_INC_IFNET_STAT(inp,
+                                   ecn_synack_lost);
+                       } else {
+                               INP_INC_IFNET_STAT(inp,
+                                   ecn_peer_nosupport);
+                       }
+               }
+       } else {
+               INP_INC_IFNET_STAT(inp, ecn_off_conn);
+       }
        if (TCP_ECN_ENABLED(tp)) {
-               if (tp->ecn_flags & TE_RECV_ECN_CE)
+               if (tp->ecn_flags & TE_RECV_ECN_CE) {
                        tcpstat.tcps_ecn_conn_recv_ce++;
-               if (tp->ecn_flags & TE_RECV_ECN_ECE)
+                       INP_INC_IFNET_STAT(inp, ecn_conn_recv_ce);
+               }
+               if (tp->ecn_flags & TE_RECV_ECN_ECE) {
                        tcpstat.tcps_ecn_conn_recv_ece++;
+                       INP_INC_IFNET_STAT(inp, ecn_conn_recv_ece);
+               }
                if (tp->ecn_flags & (TE_RECV_ECN_CE | TE_RECV_ECN_ECE)) {
                        if (tp->t_stat.txretransmitbytes > 0 ||
-                           tp->t_stat.rxoutoforderbytes > 0)
+                           tp->t_stat.rxoutoforderbytes > 0) {
                                tcpstat.tcps_ecn_conn_pl_ce++;
-                       else
+                               INP_INC_IFNET_STAT(inp, ecn_conn_plce);
+                       } else {
                                tcpstat.tcps_ecn_conn_nopl_ce++;
+                               INP_INC_IFNET_STAT(inp, ecn_conn_noplce);
+                       }
                } else {
                        if (tp->t_stat.txretransmitbytes > 0 ||
-                           tp->t_stat.rxoutoforderbytes > 0)
+                           tp->t_stat.rxoutoforderbytes > 0) {
                                tcpstat.tcps_ecn_conn_plnoce++;
+                               INP_INC_IFNET_STAT(inp, ecn_conn_plnoce);
+                       }
+               }
+       }
+
+       /* Aggregate performance stats */
+       if (inp->inp_last_outifp != NULL && !(tp->t_flags & TF_LOCAL)) {
+               struct ifnet *ifp = inp->inp_last_outifp;
+               ifnet_lock_shared(ifp);
+               if ((ifp->if_refflags & (IFRF_ATTACHED | IFRF_DETACHING)) ==
+                   IFRF_ATTACHED) {
+                       if (inp->inp_vflag & INP_IPV6) {
+                               ifp->if_ipv6_stat->timestamp = net_uptime();
+                               if (TCP_ECN_ENABLED(tp)) {
+                                       tcp_update_ecn_perf_stats(tp,
+                                           &ifp->if_ipv6_stat->ecn_on);
+                               } else {
+                                       tcp_update_ecn_perf_stats(tp,
+                                           &ifp->if_ipv6_stat->ecn_off);
+                               }
+                       } else {
+                               ifp->if_ipv4_stat->timestamp = net_uptime();
+                               if (TCP_ECN_ENABLED(tp)) {
+                                       tcp_update_ecn_perf_stats(tp,
+                                           &ifp->if_ipv4_stat->ecn_on);
+                               } else {
+                                       tcp_update_ecn_perf_stats(tp,
+                                           &ifp->if_ipv4_stat->ecn_off);
+                               }
+                       }
                }
+               ifnet_lock_done(ifp);
        }
 
        tcp_free_sackholes(tp);
@@ -1598,8 +1711,8 @@ tcpcb_to_otcpcb(struct tcpcb *tp, struct otcpcb *otp)
        otp->ts_recent = tp->ts_recent;
        otp->ts_recent_age = tp->ts_recent_age;
        otp->last_ack_sent = tp->last_ack_sent;
-       otp->cc_send = tp->cc_send;
-       otp->cc_recv = tp->cc_recv;
+       otp->cc_send = 0;
+       otp->cc_recv = 0;
        otp->snd_recover = tp->snd_recover;
        otp->snd_cwnd_prev = tp->snd_cwnd_prev;
        otp->snd_ssthresh_prev = tp->snd_ssthresh_prev;
@@ -1781,8 +1894,8 @@ tcpcb_to_xtcpcb64(struct tcpcb *tp, struct xtcpcb64 *otp)
         otp->ts_recent = tp->ts_recent;
         otp->ts_recent_age = tp->ts_recent_age;
         otp->last_ack_sent = tp->last_ack_sent;
-        otp->cc_send = tp->cc_send;
-        otp->cc_recv = tp->cc_recv;
+        otp->cc_send = 0;
+        otp->cc_recv = 0;
         otp->snd_recover = tp->snd_recover;
         otp->snd_cwnd_prev = tp->snd_cwnd_prev;
         otp->snd_ssthresh_prev = tp->snd_ssthresh_prev;
@@ -2451,6 +2564,7 @@ tcp_rtlookup(inp, input_ifscope)
                tcp_set_tso(tp, rt->rt_ifp);
                soif2kcl(inp->inp_socket,
                    (rt->rt_ifp->if_eflags & IFEF_2KCL));
+               tcp_set_ecn(tp, rt->rt_ifp);
        }
 
        /* Note if the peer is local */
@@ -2557,6 +2671,7 @@ tcp_rtlookup6(inp, input_ifscope)
                tcp_set_tso(tp, rt->rt_ifp);
                soif2kcl(inp->inp_socket,
                    (rt->rt_ifp->if_eflags & IFEF_2KCL));
+               tcp_set_ecn(tp, rt->rt_ifp);
        }
 
        /* Note if the peer is local */