diff options
author | tuexen <tuexen@FreeBSD.org> | 2015-03-10 19:49:25 +0000 |
---|---|---|
committer | tuexen <tuexen@FreeBSD.org> | 2015-03-10 19:49:25 +0000 |
commit | 484c3b2b6145fa469df480c58203daeb3c1aa206 (patch) | |
tree | 4141eaf0fa5f9da4dcaf9b3b44d94deda353ee3c /sys/netinet | |
parent | 1d8f4435d6eb461b9c516e914a6264387e6e27a0 (diff) | |
download | FreeBSD-src-484c3b2b6145fa469df480c58203daeb3c1aa206.zip FreeBSD-src-484c3b2b6145fa469df480c58203daeb3c1aa206.tar.gz |
Add a SCTP socket option to limit the cwnd for each path.
MFC after: 1 month
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/sctp.h | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_cc_functions.c | 45 | ||||
-rw-r--r-- | sys/netinet/sctp_input.c | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.c | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.h | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_peeloff.c | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_structs.h | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_usrreq.c | 63 | ||||
-rw-r--r-- | sys/netinet/sctputil.c | 1 |
9 files changed, 104 insertions, 11 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 9b795ed..8a033d8 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -128,6 +128,7 @@ struct sctp_paramhdr { #define SCTP_RECONFIG_SUPPORTED 0x00000029 #define SCTP_NRSACK_SUPPORTED 0x00000030 #define SCTP_PKTDROP_SUPPORTED 0x00000031 +#define SCTP_MAX_CWND 0x00000032 /* * read-only options diff --git a/sys/netinet/sctp_cc_functions.c b/sys/netinet/sctp_cc_functions.c index e32faf9..17a897b 100644 --- a/sys/netinet/sctp_cc_functions.c +++ b/sys/netinet/sctp_cc_functions.c @@ -53,6 +53,19 @@ __FBSDID("$FreeBSD$"); #define SHIFT_MPTCP_MULTI 8 static void +sctp_enforce_cwnd_limit(struct sctp_association *assoc, struct sctp_nets *net) +{ + if ((assoc->max_cwnd > 0) && + (net->cwnd > assoc->max_cwnd) && + (net->cwnd > (net->mtu - sizeof(struct sctphdr)))) { + net->cwnd = assoc->max_cwnd; + if (net->cwnd < (net->mtu - sizeof(struct sctphdr))) { + net->cwnd = net->mtu - sizeof(struct sctphdr); + } + } +} + +static void sctp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_association *assoc; @@ -80,6 +93,7 @@ sctp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net) net->cwnd = net->mtu - sizeof(struct sctphdr); } } + sctp_enforce_cwnd_limit(assoc, net); net->ssthresh = assoc->peers_rwnd; SDT_PROBE(sctp, cwnd, net, init, stcb->asoc.my_vtag, ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), net, @@ -178,6 +192,7 @@ sctp_cwnd_update_after_fr(struct sctp_tcb *stcb, } } net->cwnd = net->ssthresh; + sctp_enforce_cwnd_limit(asoc, net); SDT_PROBE(sctp, cwnd, net, fr, stcb->asoc.my_vtag, ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), net, old_cwnd, net->cwnd); @@ -426,6 +441,7 @@ cc_bw_decrease(struct sctp_tcb *stcb, struct sctp_nets *net, uint64_t nbw, uint6 if ((net->cc_mod.rtcc.vol_reduce) && (inst_ind != SCTP_INST_GAINING)) { net->cwnd += net->mtu; + sctp_enforce_cwnd_limit(&stcb->asoc, net); net->cc_mod.rtcc.vol_reduce--; } net->cc_mod.rtcc.last_step_state = 2; @@ -457,6 +473,7 @@ cc_bw_decrease(struct sctp_tcb *stcb, struct sctp_nets *net, uint64_t nbw, uint6 if ((net->cc_mod.rtcc.vol_reduce) && (inst_ind != SCTP_INST_GAINING)) { net->cwnd += net->mtu; + sctp_enforce_cwnd_limit(&stcb->asoc, net); net->cc_mod.rtcc.vol_reduce--; } net->cc_mod.rtcc.last_step_state = 3; @@ -488,6 +505,7 @@ cc_bw_decrease(struct sctp_tcb *stcb, struct sctp_nets *net, uint64_t nbw, uint6 if ((net->cc_mod.rtcc.vol_reduce) && (inst_ind != SCTP_INST_GAINING)) { net->cwnd += net->mtu; + sctp_enforce_cwnd_limit(&stcb->asoc, net); net->cc_mod.rtcc.vol_reduce--; } net->cc_mod.rtcc.last_step_state = 4; @@ -882,6 +900,7 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, break; } net->cwnd += incr; + sctp_enforce_cwnd_limit(asoc, net); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, incr, SCTP_CWND_LOG_FROM_SS); @@ -948,6 +967,7 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, break; } net->cwnd += incr; + sctp_enforce_cwnd_limit(asoc, net); SDT_PROBE(sctp, cwnd, net, ack, stcb->asoc.my_vtag, ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), @@ -1227,6 +1247,7 @@ sctp_cwnd_update_after_packet_dropped(struct sctp_tcb *stcb, /* We always have 1 MTU */ net->cwnd = net->mtu; } + sctp_enforce_cwnd_limit(&stcb->asoc, net); if (net->cwnd - old_cwnd != 0) { /* log only changes */ SDT_PROBE(sctp, cwnd, net, pd, @@ -1251,6 +1272,7 @@ sctp_cwnd_update_after_output(struct sctp_tcb *stcb, net->ssthresh = net->cwnd; if (burst_limit) { net->cwnd = (net->flight_size + (burst_limit * net->mtu)); + sctp_enforce_cwnd_limit(&stcb->asoc, net); SDT_PROBE(sctp, cwnd, net, bl, stcb->asoc.my_vtag, ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), @@ -1589,6 +1611,7 @@ static void sctp_hs_cwnd_increase(struct sctp_tcb *stcb, struct sctp_nets *net) { int cur_val, i, indx, incr; + int old_cwnd = net->cwnd; cur_val = net->cwnd >> 10; indx = SCTP_HS_TABLE_SIZE - 1; @@ -1597,14 +1620,8 @@ sctp_hs_cwnd_increase(struct sctp_tcb *stcb, struct sctp_nets *net) /* normal mode */ if (net->net_ack > net->mtu) { net->cwnd += net->mtu; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { - sctp_log_cwnd(stcb, net, net->mtu, SCTP_CWND_LOG_FROM_SS); - } } else { net->cwnd += net->net_ack; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { - sctp_log_cwnd(stcb, net, net->net_ack, SCTP_CWND_LOG_FROM_SS); - } } } else { for (i = net->last_hs_used; i < SCTP_HS_TABLE_SIZE; i++) { @@ -1616,9 +1633,10 @@ sctp_hs_cwnd_increase(struct sctp_tcb *stcb, struct sctp_nets *net) net->last_hs_used = indx; incr = ((sctp_cwnd_adjust[indx].increase) << 10); net->cwnd += incr; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { - sctp_log_cwnd(stcb, net, incr, SCTP_CWND_LOG_FROM_SS); - } + } + sctp_enforce_cwnd_limit(&stcb->asoc, net); + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { + sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_SS); } } @@ -1657,6 +1675,7 @@ sctp_hs_cwnd_decrease(struct sctp_tcb *stcb, struct sctp_nets *net) net->last_hs_used = indx; } } + sctp_enforce_cwnd_limit(&stcb->asoc, net); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_FR); } @@ -1788,9 +1807,7 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb, if (net->cwnd <= net->ssthresh) { /* We are in slow start */ if (net->flight_size + net->net_ack >= net->cwnd) { - sctp_hs_cwnd_increase(stcb, net); - } else { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { sctp_log_cwnd(stcb, net, net->net_ack, @@ -1804,6 +1821,7 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb, (net->partial_bytes_acked >= net->cwnd)) { net->partial_bytes_acked -= net->cwnd; net->cwnd += net->mtu; + sctp_enforce_cwnd_limit(asoc, net); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, net->mtu, SCTP_CWND_LOG_FROM_CA); @@ -2042,6 +2060,7 @@ htcp_cong_avoid(struct sctp_tcb *stcb, struct sctp_nets *net) SCTP_CWND_LOG_FROM_SS); } } + sctp_enforce_cwnd_limit(&stcb->asoc, net); } else { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { sctp_log_cwnd(stcb, net, net->net_ack, @@ -2063,6 +2082,7 @@ htcp_cong_avoid(struct sctp_tcb *stcb, struct sctp_nets *net) */ net->cwnd += net->mtu; net->partial_bytes_acked = 0; + sctp_enforce_cwnd_limit(&stcb->asoc, net); htcp_alpha_update(&net->cc_mod.htcp_ca); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, net->mtu, @@ -2109,6 +2129,7 @@ sctp_htcp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net) */ net->cwnd = min((net->mtu * 4), max((2 * net->mtu), SCTP_INITIAL_CWND)); net->ssthresh = stcb->asoc.peers_rwnd; + sctp_enforce_cwnd_limit(&stcb->asoc, net); htcp_init(net); if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_CWND_MONITOR_ENABLE | SCTP_CWND_LOGGING_ENABLE)) { @@ -2212,6 +2233,7 @@ sctp_htcp_cwnd_update_after_fr(struct sctp_tcb *stcb, htcp_reset(&net->cc_mod.htcp_ca); net->ssthresh = htcp_recalc_ssthresh(net); net->cwnd = net->ssthresh; + sctp_enforce_cwnd_limit(asoc, net); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_FR); @@ -2291,6 +2313,7 @@ sctp_htcp_cwnd_update_after_ecn_echo(struct sctp_tcb *stcb, net->RTO <<= 1; } net->cwnd = net->ssthresh; + sctp_enforce_cwnd_limit(&stcb->asoc, net); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_SAT); } diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 44932e0..4fab986 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -2763,6 +2763,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, inp->sctp_mobility_features = (*inp_p)->sctp_mobility_features; inp->sctp_socket = so; inp->sctp_frag_point = (*inp_p)->sctp_frag_point; + inp->max_cwnd = (*inp_p)->max_cwnd; inp->sctp_cmt_on_off = (*inp_p)->sctp_cmt_on_off; inp->ecn_supported = (*inp_p)->ecn_supported; inp->prsctp_supported = (*inp_p)->prsctp_supported; diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 701a0c2..592cf22 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -2474,6 +2474,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) inp->sctp_associd_counter = 1; inp->partial_delivery_point = SCTP_SB_LIMIT_RCV(so) >> SCTP_PARTIAL_DELIVERY_SHIFT; inp->sctp_frag_point = SCTP_DEFAULT_MAXSEGMENT; + inp->max_cwnd = 0; inp->sctp_cmt_on_off = SCTP_BASE_SYSCTL(sctp_cmt_on_off); inp->ecn_supported = (uint8_t) SCTP_BASE_SYSCTL(sctp_ecn_enable); inp->prsctp_supported = (uint8_t) SCTP_BASE_SYSCTL(sctp_pr_enable); diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index c9183b1..963b89f 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -404,6 +404,7 @@ struct sctp_inpcb { uint32_t sctp_frag_point; uint32_t partial_delivery_point; uint32_t sctp_context; + uint32_t max_cwnd; uint8_t local_strreset_support; uint32_t sctp_cmt_on_off; uint8_t ecn_supported; diff --git a/sys/netinet/sctp_peeloff.c b/sys/netinet/sctp_peeloff.c index cf41d29f..b702476 100644 --- a/sys/netinet/sctp_peeloff.c +++ b/sys/netinet/sctp_peeloff.c @@ -127,6 +127,7 @@ sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id) n_inp->pktdrop_supported = inp->pktdrop_supported; n_inp->partial_delivery_point = inp->partial_delivery_point; n_inp->sctp_context = inp->sctp_context; + n_inp->max_cwnd = inp->max_cwnd; n_inp->local_strreset_support = inp->local_strreset_support; n_inp->inp_starting_point_for_iterator = NULL; /* copy in the authentication parameters from the original endpoint */ diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index a8d3a97..b1df17d 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -1199,6 +1199,7 @@ struct sctp_association { uint8_t sctp_cmt_pf; uint8_t use_precise_time; uint64_t sctp_features; + uint32_t max_cwnd; uint16_t port; /* remote UDP encapsulation port */ /* * The mapping array is used to track out of order sequences above diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 6e40654..3384def 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -3694,6 +3694,33 @@ flags_out: } break; } + case SCTP_MAX_CWND: + { + struct sctp_assoc_value *av; + + SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, *optsize); + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + + if (stcb) { + av->assoc_value = stcb->asoc.max_cwnd; + SCTP_TCB_UNLOCK(stcb); + } else { + if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || + (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) || + (av->assoc_id == SCTP_FUTURE_ASSOC)) { + SCTP_INP_RLOCK(inp); + av->assoc_value = inp->max_cwnd; + SCTP_INP_RUNLOCK(inp); + } else { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } + } + if (error == 0) { + *optsize = sizeof(struct sctp_assoc_value); + } + break; + } default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT); error = ENOPROTOOPT; @@ -6572,6 +6599,42 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } break; } + case SCTP_MAX_CWND: + { + struct sctp_assoc_value *av; + struct sctp_nets *net; + + SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, optsize); + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + + if (stcb) { + stcb->asoc.max_cwnd = av->assoc_value; + if (stcb->asoc.max_cwnd > 0) { + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if ((net->cwnd > stcb->asoc.max_cwnd) && + (net->cwnd > (net->mtu - sizeof(struct sctphdr)))) { + net->cwnd = stcb->asoc.max_cwnd; + if (net->cwnd < (net->mtu - sizeof(struct sctphdr))) { + net->cwnd = net->mtu - sizeof(struct sctphdr); + } + } + } + } + SCTP_TCB_UNLOCK(stcb); + } else { + if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || + (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) || + (av->assoc_id == SCTP_FUTURE_ASSOC)) { + SCTP_INP_WLOCK(inp); + inp->max_cwnd = av->assoc_value; + SCTP_INP_WUNLOCK(inp); + } else { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } + } + break; + } default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT); error = ENOPROTOOPT; diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 57c2032..801f03b 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -936,6 +936,7 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, asoc->sctp_frag_point = inp->sctp_frag_point; asoc->sctp_features = inp->sctp_features; asoc->default_dscp = inp->sctp_ep.default_dscp; + asoc->max_cwnd = inp->max_cwnd; #ifdef INET6 if (inp->sctp_ep.default_flowlabel) { asoc->default_flowlabel = inp->sctp_ep.default_flowlabel; |