+ int rv = 0;
+
+ switch (attr->type)
+ {
+ case TRANSPORT_ENDPT_ATTR_NEXT_OUTPUT_NODE:
+ tc->next_node_index = attr->next_output_node & 0xffffffff;
+ tc->next_node_opaque = attr->next_output_node >> 32;
+ break;
+ case TRANSPORT_ENDPT_ATTR_MSS:
+ tc->mss = attr->mss;
+ tc->snd_mss = clib_min (tc->snd_mss, tc->mss);
+ break;
+ case TRANSPORT_ENDPT_ATTR_FLAGS:
+ if (attr->flags & TRANSPORT_ENDPT_ATTR_F_CSUM_OFFLOAD)
+ tc->cfg_flags |= TCP_CFG_F_NO_CSUM_OFFLOAD;
+ else
+ tc->cfg_flags &= ~TCP_CFG_F_NO_CSUM_OFFLOAD;
+ if (attr->flags & TRANSPORT_ENDPT_ATTR_F_GSO)
+ {
+ if (!(tc->cfg_flags & TCP_CFG_F_TSO))
+ tcp_check_gso (tc);
+ tc->cfg_flags &= ~TCP_CFG_F_NO_TSO;
+ }
+ else
+ {
+ tc->cfg_flags |= TCP_CFG_F_NO_TSO;
+ tc->cfg_flags &= ~TCP_CFG_F_TSO;
+ }
+ if (attr->flags & TRANSPORT_ENDPT_ATTR_F_RATE_SAMPLING)
+ {
+ if (!(tc->cfg_flags & TCP_CFG_F_RATE_SAMPLE))
+ tcp_bt_init (tc);
+ tc->cfg_flags |= TCP_CFG_F_RATE_SAMPLE;
+ }
+ else
+ {
+ if (tc->cfg_flags & TCP_CFG_F_RATE_SAMPLE)
+ tcp_bt_cleanup (tc);
+ tc->cfg_flags &= ~TCP_CFG_F_RATE_SAMPLE;
+ }
+ break;
+ case TRANSPORT_ENDPT_ATTR_CC_ALGO:
+ if (tc->cc_algo == tcp_cc_algo_get (attr->cc_algo))
+ break;
+ tcp_cc_cleanup (tc);
+ tc->cc_algo = tcp_cc_algo_get (attr->cc_algo);
+ tcp_cc_init (tc);
+ break;
+ default:
+ rv = -1;
+ break;
+ }
+
+ return rv;
+}
+
+static int
+tcp_get_attribute (tcp_connection_t *tc, transport_endpt_attr_t *attr)
+{
+ int rv = 0;
+ u64 non;
+
+ switch (attr->type)
+ {
+ case TRANSPORT_ENDPT_ATTR_NEXT_OUTPUT_NODE:
+ non = (u64) tc->next_node_opaque << 32 | tc->next_node_index;
+ attr->next_output_node = non;
+ break;
+ case TRANSPORT_ENDPT_ATTR_MSS:
+ attr->mss = tc->snd_mss;
+ break;
+ case TRANSPORT_ENDPT_ATTR_FLAGS:
+ attr->flags = 0;
+ if (!(tc->cfg_flags & TCP_CFG_F_NO_CSUM_OFFLOAD))
+ attr->flags |= TRANSPORT_ENDPT_ATTR_F_CSUM_OFFLOAD;
+ if (tc->cfg_flags & TCP_CFG_F_TSO)
+ attr->flags |= TRANSPORT_ENDPT_ATTR_F_GSO;
+ if (tc->cfg_flags & TCP_CFG_F_RATE_SAMPLE)
+ attr->flags |= TRANSPORT_ENDPT_ATTR_F_RATE_SAMPLING;
+ break;
+ case TRANSPORT_ENDPT_ATTR_CC_ALGO:
+ attr->cc_algo = tc->cc_algo - tcp_main.cc_algos;
+ break;
+ default:
+ rv = -1;
+ break;
+ }
+
+ return rv;
+}
+
+static int
+tcp_session_attribute (u32 conn_index, u32 thread_index, u8 is_get,
+ transport_endpt_attr_t *attr)
+{
+ tcp_connection_t *tc = tcp_connection_get (conn_index, thread_index);
+
+ if (PREDICT_FALSE (!tc))
+ return -1;
+
+ if (is_get)
+ return tcp_get_attribute (tc, attr);
+ else
+ return tcp_set_attribute (tc, attr);
+}
+
+static u16
+tcp_session_cal_goal_size (tcp_connection_t * tc)
+{
+ u16 goal_size = tc->snd_mss;
+
+ goal_size = tcp_cfg.max_gso_size - tc->snd_mss % tcp_cfg.max_gso_size;
+ goal_size = clib_min (goal_size, tc->snd_wnd / 2);
+
+ return goal_size > tc->snd_mss ? goal_size : tc->snd_mss;
+}
+
+always_inline u32
+tcp_round_snd_space (tcp_connection_t * tc, u32 snd_space)
+{
+ if (PREDICT_FALSE (tc->snd_wnd < tc->snd_mss))
+ {
+ return tc->snd_wnd <= snd_space ? tc->snd_wnd : 0;
+ }
+
+ /* If not snd_wnd constrained and we can't write at least a segment,
+ * don't try at all */
+ if (PREDICT_FALSE (snd_space < tc->snd_mss))
+ return snd_space < tc->cwnd ? 0 : snd_space;
+
+ /* round down to mss multiple */
+ return snd_space - (snd_space % tc->snd_mss);