tcp: track reorder with sacks
[vpp.git] / src / vnet / tcp / tcp_input.c
index cc373f5..de9d891 100644 (file)
@@ -215,6 +215,7 @@ tcp_rcv_rst (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
       break;
     case TCP_STATE_SYN_SENT:
       /* Do not program ntf because the connection is half-open */
+      tc->rst_state = tc->state;
       tcp_handle_rst (tc);
       break;
     case TCP_STATE_ESTABLISHED:
@@ -423,31 +424,26 @@ acceptable:
 /**
  * Compute smoothed RTT as per VJ's '88 SIGCOMM and RFC6298
  *
- * Note that although the original article, srtt and rttvar are scaled
+ * Note that although in the original article srtt and rttvar are scaled
  * to minimize round-off errors, here we don't. Instead, we rely on
  * better precision time measurements.
+ *
+ * A known limitation of the algorithm is that a drop in rtt results in a
+ * rttvar increase and bigger RTO.
+ *
+ * mrtt must be provided in @ref TCP_TICK multiples, i.e., in us. Note that
+ * timestamps are measured as ms ticks so they must be converted before
+ * calling this function.
  */
 static void
 tcp_estimate_rtt (tcp_connection_t * tc, u32 mrtt)
 {
   int err, diff;
 
-  if (tc->srtt != 0)
-    {
-      err = mrtt - tc->srtt;
-
-      /* XXX Drop in RTT results in RTTVAR increase and bigger RTO.
-       * The increase should be bound */
-      tc->srtt = clib_max ((int) tc->srtt + (err >> 3), 1);
-      diff = (clib_abs (err) - (int) tc->rttvar) >> 2;
-      tc->rttvar = clib_max ((int) tc->rttvar + diff, 1);
-    }
-  else
-    {
-      /* First measurement. */
-      tc->srtt = mrtt;
-      tc->rttvar = mrtt >> 1;
-    }
+  err = mrtt - tc->srtt;
+  tc->srtt = clib_max ((int) tc->srtt + (err >> 3), 1);
+  diff = (clib_abs (err) - (int) tc->rttvar) >> 2;
+  tc->rttvar = clib_max ((int) tc->rttvar + diff, 1);
 }
 
 static inline void
@@ -505,8 +501,8 @@ tcp_update_rtt (tcp_connection_t * tc, tcp_rate_sample_t * rs, u32 ack)
    * seq_lt (tc->snd_una, ack). This is a condition for calling update_rtt */
   else if (tcp_opts_tstamp (&tc->rcv_opts) && tc->rcv_opts.tsecr)
     {
-      u32 now = tcp_tstamp (tc);
-      mrtt = clib_max (now - tc->rcv_opts.tsecr, 1);
+      mrtt = clib_max (tcp_tstamp (tc) - tc->rcv_opts.tsecr, 1);
+      mrtt *= TCP_TSTP_TO_HZ;
     }
 
 estimate_rtt:
@@ -542,8 +538,8 @@ tcp_estimate_initial_rtt (tcp_connection_t * tc)
     }
   else
     {
-      mrtt = tcp_time_now_w_thread (thread_index) - tc->rcv_opts.tsecr;
-      mrtt = clib_max (mrtt, 1);
+      mrtt = tcp_tstamp (tc) - tc->rcv_opts.tsecr;
+      mrtt = clib_max (mrtt, 1) * TCP_TSTP_TO_HZ;
       /* Due to retransmits we don't know the initial mrtt */
       if (tc->rto_boff && mrtt > 1 * THZ)
        mrtt = 1 * THZ;
@@ -551,7 +547,11 @@ tcp_estimate_initial_rtt (tcp_connection_t * tc)
     }
 
   if (mrtt > 0 && mrtt < TCP_RTT_MAX)
-    tcp_estimate_rtt (tc, mrtt);
+    {
+      /* First measurement as per RFC 6298 */
+      tc->srtt = mrtt;
+      tc->rttvar = mrtt >> 1;
+    }
   tcp_update_rto (tc);
 }
 
@@ -689,7 +689,7 @@ tcp_cc_init_congestion (tcp_connection_t * tc)
    * three segments that have left the network and should've been
    * buffered at the receiver XXX */
   if (!tcp_opts_sack_permitted (&tc->rcv_opts))
-    tc->cwnd += 3 * tc->snd_mss;
+    tc->cwnd += TCP_DUPACK_THRESHOLD * tc->snd_mss;
 
   tc->fr_occurences += 1;
   TCP_EVT (TCP_EVT_CC_EVT, tc, 4);
@@ -720,14 +720,6 @@ tcp_cc_is_spurious_retransmit (tcp_connection_t * tc)
   return (tcp_cc_is_spurious_timeout_rxt (tc));
 }
 
-static inline u8
-tcp_should_fastrecover_sack (tcp_connection_t * tc)
-{
-  return (tc->sack_sb.lost_bytes
-         || ((TCP_DUPACK_THRESHOLD - 1) * tc->snd_mss
-             < tc->sack_sb.sacked_bytes));
-}
-
 static inline u8
 tcp_should_fastrecover (tcp_connection_t * tc, u8 has_sack)
 {
@@ -752,8 +744,7 @@ tcp_should_fastrecover (tcp_connection_t * tc, u8 has_sack)
          return 0;
        }
     }
-  return ((tc->rcv_dupacks == TCP_DUPACK_THRESHOLD)
-         || tcp_should_fastrecover_sack (tc));
+  return tc->sack_sb.lost_bytes || tc->rcv_dupacks >= tc->sack_sb.reorder;
 }
 
 static int
@@ -1263,26 +1254,6 @@ tcp_session_enqueue_ooo (tcp_connection_t * tc, vlib_buffer_t * b,
   return TCP_ERROR_ENQUEUED_OOO;
 }
 
-/**
- * Check if ACK could be delayed. If ack can be delayed, it should return
- * true for a full frame. If we're always acking return 0.
- */
-always_inline int
-tcp_can_delack (tcp_connection_t * tc)
-{
-  /* Send ack if ... */
-  if (TCP_ALWAYS_ACK
-      /* just sent a rcv wnd 0
-         || (tc->flags & TCP_CONN_SENT_RCV_WND0) != 0 */
-      /* constrained to send ack */
-      || (tc->flags & TCP_CONN_SNDACK) != 0
-      /* we're almost out of tx wnd */
-      || tcp_available_cc_snd_space (tc) < 4 * tc->snd_mss)
-    return 0;
-
-  return 1;
-}
-
 static int
 tcp_buffer_discard_bytes (vlib_buffer_t * b, u32 n_bytes_to_drop)
 {
@@ -1371,14 +1342,6 @@ in_order:
   /* In order data, enqueue. Fifo figures out by itself if any out-of-order
    * segments can be enqueued after fifo tail offset changes. */
   error = tcp_session_enqueue_data (tc, b, n_data_bytes);
-  if (tcp_can_delack (tc))
-    {
-      if (!tcp_timer_is_active (tc, TCP_TIMER_DELACK))
-       tcp_timer_set (&wrk->timer_wheel, tc, TCP_TIMER_DELACK,
-                      tcp_cfg.delack_time);
-      goto done;
-    }
-
   tcp_program_ack (tc);
 
 done:
@@ -2658,6 +2621,13 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       tcp_connection_init_vars (child);
       child->rto = TCP_RTO_MIN;
 
+      /*
+       * This initializes elog track, must be done before synack.
+       * We also do it before possible tcp_connection_cleanup() as it
+       * generates TCP_EVT_DELETE event.
+       */
+      TCP_EVT (TCP_EVT_SYN_RCVD, child, 1);
+
       if (session_stream_accept (&child->connection, lc->c_s_index,
                                 lc->c_thread_index, 0 /* notify */ ))
        {
@@ -2670,8 +2640,6 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
       tcp_send_synack (child);
 
-      TCP_EVT (TCP_EVT_SYN_RCVD, child, 1);
-
     done:
 
       if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
@@ -3246,12 +3214,14 @@ do {                                                            \
   _(FIN_WAIT_2, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(FIN_WAIT_2, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
+  _(FIN_WAIT_2, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(CLOSE_WAIT, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(CLOSE_WAIT, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
   _(CLOSE_WAIT, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(CLOSE_WAIT, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
+  _(CLOSE_WAIT, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(LAST_ACK, 0, TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
   _(LAST_ACK, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(LAST_ACK, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);