session tcp: support pacer idle timeouts
[vpp.git] / src / vnet / tcp / tcp.h
index 753a1ca..e9247eb 100644 (file)
@@ -31,6 +31,8 @@
 #define TCP_FIB_RECHECK_PERIOD 1 * THZ /**< Recheck every 1s */
 #define TCP_MAX_OPTION_SPACE 40
 #define TCP_CC_DATA_SZ 24
+#define TCP_MAX_GSO_SZ 65536
+#define TCP_RXT_MAX_BURST 10
 
 #define TCP_DUPACK_THRESHOLD   3
 #define TCP_IW_N_SEGMENTS      10
@@ -101,6 +103,29 @@ extern timer_expiration_handler tcp_timer_retransmit_syn_handler;
 #define TCP_RTO_BOFF_MAX 8     /* Max number of retries before reset */
 #define TCP_ESTABLISH_TIME (60 * THZ)  /* Connection establish timeout */
 
+/** Connection configuration flags */
+#define foreach_tcp_cfg_flag                   \
+  _(RATE_SAMPLE, "Rate sampling")              \
+  _(NO_CSUM_OFFLOAD, "No csum offload")        \
+  _(NO_TSO, "TSO off")                         \
+  _(TSO, "TSO")                                        \
+
+typedef enum tcp_cfg_flag_bits_
+{
+#define _(sym, str) TCP_CFG_F_##sym##_BIT,
+  foreach_tcp_cfg_flag
+#undef _
+  TCP_CFG_N_FLAG_BITS
+} tcp_cfg_flag_bits_e;
+
+typedef enum tcp_cfg_flag_
+{
+#define _(sym, str) TCP_CFG_F_##sym = 1 << TCP_CFG_F_##sym##_BIT,
+  foreach_tcp_cfg_flag
+#undef _
+  TCP_CFG_N_FLAGS
+} tcp_cfg_flags_e;
+
 /** TCP connection flags */
 #define foreach_tcp_connection_flag             \
   _(SNDACK, "Send ACK")                         \
@@ -110,16 +135,14 @@ extern timer_expiration_handler tcp_timer_retransmit_syn_handler;
   _(DCNT_PENDING, "Disconnect pending")                \
   _(HALF_OPEN_DONE, "Half-open completed")     \
   _(FINPNDG, "FIN pending")                    \
-  _(FRXT_PENDING, "Fast-retransmit pending")   \
-  _(FRXT_FIRST, "Fast-retransmit first again") \
-  _(DEQ_PENDING, "Pending dequeue acked")      \
+  _(RXT_PENDING, "Retransmit pending")         \
+  _(FRXT_FIRST, "Retransmit first")            \
+  _(DEQ_PENDING, "Dequeue pending ")           \
   _(PSH_PENDING, "PSH pending")                        \
   _(FINRCVD, "FIN received")                   \
-  _(RATE_SAMPLE, "Conn does rate sampling")    \
-  _(TRACK_BURST, "Track burst")                        \
   _(ZERO_RWND_SENT, "Zero RWND sent")          \
 
-typedef enum _tcp_connection_flag_bits
+typedef enum tcp_connection_flag_bits_
 {
 #define _(sym, str) TCP_CONN_##sym##_BIT,
   foreach_tcp_connection_flag
@@ -127,7 +150,7 @@ typedef enum _tcp_connection_flag_bits
   TCP_CONN_N_FLAG_BITS
 } tcp_connection_flag_bits_e;
 
-typedef enum _tcp_connection_flag
+typedef enum tcp_connection_flag_
 {
 #define _(sym, str) TCP_CONN_##sym = 1 << TCP_CONN_##sym##_BIT,
   foreach_tcp_connection_flag
@@ -165,13 +188,14 @@ typedef struct _sack_scoreboard
   u32 sacked_bytes;                    /**< Number of bytes sacked in sb */
   u32 last_sacked_bytes;               /**< Number of bytes last sacked */
   u32 last_bytes_delivered;            /**< Sack bytes delivered to app */
-  u32 snd_una_adv;                     /**< Bytes to add to snd_una */
+  u32 rxt_sacked;                      /**< Rxt bytes last delivered */
   u32 high_sacked;                     /**< Highest byte sacked (fack) */
   u32 high_rxt;                                /**< Highest retransmitted sequence */
   u32 rescue_rxt;                      /**< Rescue sequence number */
   u32 lost_bytes;                      /**< Bytes lost as per RFC6675 */
   u32 last_lost_bytes;                 /**< Number of bytes last lost */
   u32 cur_rxt_hole;                    /**< Retransmitting from this hole */
+  u8 is_reneging;
 
 #if TCP_SCOREBOARD_TRACE
   scoreboard_trace_elt_t *trace;
@@ -218,8 +242,11 @@ sack_scoreboard_hole_t *scoreboard_prev_hole (sack_scoreboard_t * sb,
                                              sack_scoreboard_hole_t * hole);
 sack_scoreboard_hole_t *scoreboard_first_hole (sack_scoreboard_t * sb);
 sack_scoreboard_hole_t *scoreboard_last_hole (sack_scoreboard_t * sb);
+
 void scoreboard_clear (sack_scoreboard_t * sb);
+void scoreboard_clear_reneging (sack_scoreboard_t * sb, u32 start, u32 end);
 void scoreboard_init (sack_scoreboard_t * sb);
+void scoreboard_init_rxt (sack_scoreboard_t * sb, u32 snd_una);
 u8 *format_tcp_scoreboard (u8 * s, va_list * args);
 
 #define TCP_BTS_INVALID_INDEX  ((u32)~0)
@@ -228,6 +255,8 @@ typedef enum tcp_bts_flags_
 {
   TCP_BTS_IS_RXT = 1,
   TCP_BTS_IS_APP_LIMITED = 1 << 1,
+  TCP_BTS_IS_SACKED = 1 << 2,
+  TCP_BTS_IS_RXT_LOST = 1 << 3,
 } __clib_packed tcp_bts_flags_t;
 
 typedef struct tcp_bt_sample_
@@ -239,7 +268,7 @@ typedef struct tcp_bt_sample_
   u64 delivered;               /**< Total delivered bytes for sample */
   f64 delivered_time;          /**< Delivered time when sample taken */
   f64 tx_time;                 /**< Transmit time for the burst */
-  u64 tx_rate;                 /**< Tx pacing rate */
+  f64 first_tx_time;           /**< Connection first tx time at tx */
   tcp_bts_flags_t flags;       /**< Sample flag */
 } tcp_bt_sample_t;
 
@@ -250,7 +279,6 @@ typedef struct tcp_rate_sample_
   f64 prior_time;              /**< Delivered time of sample used for rate */
   f64 interval_time;           /**< Time to ack the bytes delivered */
   f64 rtt_time;                        /**< RTT for sample */
-  u64 tx_rate;                 /**< Tx pacing rate */
   u32 delivered;               /**< Bytes delivered in interval_time */
   u32 acked_and_sacked;                /**< Bytes acked + sacked now */
   u32 lost;                    /**< Bytes lost now */
@@ -304,6 +332,7 @@ typedef struct _tcp_connection
   transport_connection_t connection;  /**< Common transport data. First! */
 
   u8 state;                    /**< TCP state as per tcp_state_t */
+  u8 cfg_flags;                        /**< Connection configuration flags */
   u16 flags;                   /**< Connection flags (see tcp_conn_flags_e) */
   u32 timers[TCP_N_TIMERS];    /**< Timer handles into timer wheel */
 
@@ -359,8 +388,12 @@ typedef struct _tcp_connection
   u32 prev_cwnd;       /**< ssthresh before congestion */
   u32 bytes_acked;     /**< Bytes acknowledged by current segment */
   u32 burst_acked;     /**< Bytes acknowledged in current burst */
-  u32 snd_rxt_bytes;   /**< Retransmitted bytes */
+  u32 snd_rxt_bytes;   /**< Retransmitted bytes during current cc event */
   u32 snd_rxt_ts;      /**< Timestamp when first packet is retransmitted */
+  u32 prr_delivered;   /**< RFC6937 bytes delivered during current event */
+  u32 prr_start;       /**< snd_una when prr starts */
+  u32 rxt_delivered;   /**< Rxt bytes delivered during current cc event */
+  u32 rxt_head;                /**< snd_una last time we re rxted the head */
   u32 tsecr_last_ack;  /**< Timestamp echoed to us in last healthy ACK */
   u32 snd_congestion;  /**< snd_una_max when congestion is detected */
   u32 tx_fifo_size;    /**< Tx fifo size. Used to constrain cwnd */
@@ -392,6 +425,7 @@ typedef struct _tcp_connection
   u64 delivered;               /**< Total bytes delivered to peer */
   u64 app_limited;             /**< Delivered when app-limited detected */
   f64 delivered_time;          /**< Time last bytes were acked */
+  f64 first_tx_time;           /**< Send time for recently delivered/sent */
   tcp_byte_tracker_t *bt;      /**< Tx byte tracker */
 
   tcp_errors_t errors; /**< Soft connection errors */
@@ -400,6 +434,7 @@ typedef struct _tcp_connection
   u32 last_fib_check;  /**< Last time we checked fib route for peer */
   u16 mss;             /**< Our max seg size that includes options */
   u32 timestamp_delta; /**< Offset for timestamp */
+  u32 ipv6_flow_label; /**< flow label for ipv6 header */
 } tcp_connection_t;
 
 /* *INDENT-OFF* */
@@ -417,6 +452,7 @@ struct _tcp_cc_algorithm
   void (*recovered) (tcp_connection_t * tc);
   void (*undo_recovery) (tcp_connection_t * tc);
   void (*event) (tcp_connection_t *tc, tcp_cc_event_t evt);
+  u64 (*get_pacing_rate) (tcp_connection_t *tc);
 };
 /* *INDENT-ON* */
 
@@ -437,6 +473,8 @@ struct _tcp_cc_algorithm
 #define tcp_in_cong_recovery(tc) ((tc)->flags &                \
          (TCP_CONN_FAST_RECOVERY | TCP_CONN_RECOVERY))
 
+#define tcp_csum_offload(tc) (!((tc)->cfg_flags & TCP_CFG_F_NO_CSUM_OFFLOAD))
+
 always_inline void
 tcp_cong_recovery_off (tcp_connection_t * tc)
 {
@@ -473,9 +511,6 @@ typedef struct tcp_worker_ctx_
   /** tx buffer free list */
   u32 *tx_buffers;
 
-  /** tx frames for tcp 4/6 output nodes */
-  vlib_frame_t *tx_frames[2];
-
   /** tx frames for ip 4/6 lookup nodes */
   vlib_frame_t *ip_lookup_tx_frames[2];
 
@@ -520,9 +555,16 @@ typedef struct tcp_configuration_
   /** Enable tx pacing for new connections */
   u8 enable_tx_pacing;
 
+  /** Allow use of TSO whenever available */
+  u8 allow_tso;
+
   /** Default congestion control algorithm type */
   tcp_cc_algorithm_type_e cc_algo;
 
+  /** Min rwnd, as number of snd_mss segments, for update ack to be sent after
+   * a zero rwnd advertisement */
+  u32 rwnd_min_update_ack;
+
   /** Delayed ack time (disabled) */
   u16 delack_time;
 
@@ -696,7 +738,7 @@ always_inline void
 tcp_connection_set_state (tcp_connection_t * tc, tcp_state_t state)
 {
   tc->state = state;
-  TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc);
+  TCP_EVT (TCP_EVT_STATE_CHANGE, tc);
 }
 
 void tcp_connection_close (tcp_connection_t * tc);
@@ -704,6 +746,8 @@ void tcp_connection_cleanup (tcp_connection_t * tc);
 void tcp_connection_del (tcp_connection_t * tc);
 int tcp_half_open_connection_cleanup (tcp_connection_t * tc);
 tcp_connection_t *tcp_connection_alloc (u8 thread_index);
+tcp_connection_t *tcp_connection_alloc_w_base (u8 thread_index,
+                                              tcp_connection_t * base);
 void tcp_connection_free (tcp_connection_t * tc);
 void tcp_connection_reset (tcp_connection_t * tc);
 int tcp_configure_v4_source_address_range (vlib_main_t * vm,
@@ -740,16 +784,15 @@ void tcp_send_reset (tcp_connection_t * tc);
 void tcp_send_syn (tcp_connection_t * tc);
 void tcp_send_synack (tcp_connection_t * tc);
 void tcp_send_fin (tcp_connection_t * tc);
-void tcp_init_mss (tcp_connection_t * tc);
+void tcp_send_ack (tcp_connection_t * tc);
 void tcp_update_burst_snd_vars (tcp_connection_t * tc);
 void tcp_update_rto (tcp_connection_t * tc);
-void tcp_flush_frame_to_output (tcp_worker_ctx_t * wrk, u8 is_ip4);
 void tcp_flush_frames_to_output (tcp_worker_ctx_t * wrk);
 void tcp_send_window_update_ack (tcp_connection_t * tc);
 
 void tcp_program_ack (tcp_connection_t * tc);
 void tcp_program_dupack (tcp_connection_t * tc);
-void tcp_program_fastretransmit (tcp_connection_t * tc);
+void tcp_program_retransmit (tcp_connection_t * tc);
 
 /*
  * Rate estimation
@@ -779,7 +822,7 @@ void tcp_bt_flush_samples (tcp_connection_t * tc);
  *
  * @param tc   tcp connection
  */
-void tcp_bt_track_tx (tcp_connection_t * tc);
+void tcp_bt_track_tx (tcp_connection_t * tc, u32 len);
 /**
  * Track a tcp retransmission
  *
@@ -810,6 +853,7 @@ void tcp_bt_check_app_limited (tcp_connection_t * tc);
  * @param bt   byte tracker
  */
 int tcp_bt_is_sane (tcp_byte_tracker_t * bt);
+u8 *format_tcp_bt (u8 * s, va_list * args);
 
 always_inline u32
 tcp_end_seq (tcp_header_t * th, u32 len)
@@ -837,7 +881,8 @@ tcp_bytes_out (const tcp_connection_t * tc)
   if (tcp_opts_sack_permitted (&tc->rcv_opts))
     return tc->sack_sb.sacked_bytes + tc->sack_sb.lost_bytes;
   else
-    return tc->rcv_dupacks * tc->snd_mss;
+    return clib_min (tc->rcv_dupacks * tc->snd_mss,
+                    tc->snd_nxt - tc->snd_una);
 }
 
 /**
@@ -849,18 +894,9 @@ tcp_flight_size (const tcp_connection_t * tc)
   int flight_size;
 
   flight_size = (int) (tc->snd_nxt - tc->snd_una) - tcp_bytes_out (tc)
-    + tc->snd_rxt_bytes;
+    + tc->snd_rxt_bytes - tc->rxt_delivered;
 
-  if (flight_size < 0)
-    {
-      if (0)
-       clib_warning
-         ("Negative: %u %u %u dupacks %u sacked bytes %u flags %d",
-          tc->snd_una_max - tc->snd_una, tcp_bytes_out (tc),
-          tc->snd_rxt_bytes, tc->rcv_dupacks, tc->sack_sb.sacked_bytes,
-          tc->rcv_opts.flags);
-      return 0;
-    }
+  ASSERT (flight_size >= 0);
 
   return flight_size;
 }
@@ -904,7 +940,8 @@ tcp_cwnd_accumulate (tcp_connection_t * tc, u32 thresh, u32 bytes)
 always_inline u32
 tcp_loss_wnd (const tcp_connection_t * tc)
 {
-  return tc->snd_mss;
+  /* Whatever we have in flight + the packet we're about to send */
+  return tcp_flight_size (tc) + tc->snd_mss;
 }
 
 always_inline u32
@@ -943,22 +980,13 @@ tcp_available_cc_snd_space (const tcp_connection_t * tc)
 always_inline u8
 tcp_is_lost_fin (tcp_connection_t * tc)
 {
-  if ((tc->flags & TCP_CONN_FINSNT) && tc->snd_una_max - tc->snd_una == 1)
+  if ((tc->flags & TCP_CONN_FINSNT) && (tc->snd_una_max - tc->snd_una == 1))
     return 1;
   return 0;
 }
 
 u32 tcp_snd_space (tcp_connection_t * tc);
-int tcp_retransmit_first_unacked (tcp_worker_ctx_t * wrk,
-                                 tcp_connection_t * tc);
-int tcp_fast_retransmit_no_sack (tcp_worker_ctx_t * wrk,
-                                tcp_connection_t * tc, u32 burst_size);
-int tcp_fast_retransmit_sack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc,
-                             u32 burst_size);
-int tcp_fast_retransmit (tcp_worker_ctx_t * wrk, tcp_connection_t * tc,
-                        u32 burst_size);
-void tcp_cc_init_congestion (tcp_connection_t * tc);
-void tcp_cc_fastrecovery_clear (tcp_connection_t * tc);
+int tcp_fastrecovery_prr_snd_space (tcp_connection_t * tc);
 
 fib_node_index_t tcp_lookup_rmt_in_fib (tcp_connection_t * tc);
 
@@ -1027,6 +1055,12 @@ tcp_cc_rcv_cong_ack (tcp_connection_t * tc, tcp_cc_ack_t ack_type,
   tc->cc_algo->rcv_cong_ack (tc, ack_type, rs);
 }
 
+static inline void
+tcp_cc_congestion (tcp_connection_t * tc)
+{
+  tc->cc_algo->congestion (tc);
+}
+
 static inline void
 tcp_cc_loss (tcp_connection_t * tc)
 {
@@ -1053,6 +1087,19 @@ tcp_cc_event (tcp_connection_t * tc, tcp_cc_event_t evt)
     tc->cc_algo->event (tc, evt);
 }
 
+static inline u64
+tcp_cc_get_pacing_rate (tcp_connection_t * tc)
+{
+  if (tc->cc_algo->get_pacing_rate)
+    return tc->cc_algo->get_pacing_rate (tc);
+
+  f64 srtt = clib_min ((f64) tc->srtt * TCP_TICK, tc->mrtt_us);
+
+  /* TODO should constrain to interface's max throughput but
+   * we don't have link speeds for sw ifs ..*/
+  return ((f64) tc->cwnd / srtt);
+}
+
 always_inline void
 tcp_timer_set (tcp_connection_t * tc, u8 timer_id, u32 interval)
 {
@@ -1124,8 +1171,14 @@ tcp_persist_timer_set (tcp_connection_t * tc)
 always_inline void
 tcp_persist_timer_update (tcp_connection_t * tc)
 {
-  tcp_timer_update (tc, TCP_TIMER_PERSIST,
-                   clib_max (tc->rto * TCP_TO_TIMER_TICK, 1));
+  u32 interval;
+
+  if (seq_leq (tc->snd_una, tc->snd_congestion + tc->burst_acked))
+    interval = 1;
+  else
+    interval = clib_max (tc->rto * TCP_TO_TIMER_TICK, 1);
+
+  tcp_timer_update (tc, TCP_TIMER_PERSIST, interval);
 }
 
 always_inline void
@@ -1215,6 +1268,8 @@ vlib_buffer_push_tcp_net_order (vlib_buffer_t * b, u16 sp, u16 dp, u32 seq,
   th->window = wnd;
   th->checksum = 0;
   th->urgent_pointer = 0;
+  vnet_buffer (b)->l4_hdr_offset = (u8 *) th - b->data;
+  b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
   return th;
 }