Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_subr.c
Show First 20 Lines • Show All 2,131 Lines • ▼ Show 20 Lines | #endif /* INET6 */ | ||||
tp->t_fb = tcp_func_set_ptr; | tp->t_fb = tcp_func_set_ptr; | ||||
refcount_acquire(&tp->t_fb->tfb_refcnt); | refcount_acquire(&tp->t_fb->tfb_refcnt); | ||||
rw_runlock(&tcp_function_lock); | rw_runlock(&tcp_function_lock); | ||||
/* | /* | ||||
* Use the current system default CC algorithm. | * Use the current system default CC algorithm. | ||||
*/ | */ | ||||
CC_LIST_RLOCK(); | CC_LIST_RLOCK(); | ||||
KASSERT(!STAILQ_EMPTY(&cc_list), ("cc_list is empty!")); | KASSERT(!STAILQ_EMPTY(&cc_list), ("cc_list is empty!")); | ||||
CC_ALGO(tp) = CC_DEFAULT(); | CC_ALGO(tp) = CC_DEFAULT_ALGO(); | ||||
CC_LIST_RUNLOCK(); | CC_LIST_RUNLOCK(); | ||||
/* | /* | ||||
* The tcpcb will hold a reference on its inpcb until tcp_discardcb() | * The tcpcb will hold a reference on its inpcb until tcp_discardcb() | ||||
* is called. | * is called. | ||||
*/ | */ | ||||
in_pcbref(inp); /* Reference for tcpcb */ | in_pcbref(inp); /* Reference for tcpcb */ | ||||
tp->t_inpcb = inp; | tp->t_inpcb = inp; | ||||
if (CC_ALGO(tp)->cb_init != NULL) | if (CC_ALGO(tp)->cb_init != NULL) | ||||
if (CC_ALGO(tp)->cb_init(tp->ccv) > 0) { | if (CC_ALGO(tp)->cb_init(tp->ccv, NULL) > 0) { | ||||
if (tp->t_fb->tfb_tcp_fb_fini) | if (tp->t_fb->tfb_tcp_fb_fini) | ||||
(*tp->t_fb->tfb_tcp_fb_fini)(tp, 1); | (*tp->t_fb->tfb_tcp_fb_fini)(tp, 1); | ||||
in_pcbrele_wlocked(inp); | in_pcbrele_wlocked(inp); | ||||
refcount_release(&tp->t_fb->tfb_refcnt); | refcount_release(&tp->t_fb->tfb_refcnt); | ||||
uma_zfree(V_tcpcb_zone, tm); | uma_zfree(V_tcpcb_zone, tm); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | if (V_tcp_perconn_stats_enable == 1) | ||||
tp->t_stats = stats_blob_alloc(V_tcp_perconn_stats_dflt_tpl, 0); | tp->t_stats = stats_blob_alloc(V_tcp_perconn_stats_dflt_tpl, 0); | ||||
#endif | #endif | ||||
if (V_tcp_do_lrd) | if (V_tcp_do_lrd) | ||||
tp->t_flags |= TF_LRD; | tp->t_flags |= TF_LRD; | ||||
return (tp); /* XXX */ | return (tp); /* XXX */ | ||||
} | } | ||||
/* | /* | ||||
* Switch the congestion control algorithm back to NewReno for any active | * Switch the congestion control algorithm back to Vnet default for any active | ||||
* control blocks using an algorithm which is about to go away. | * control blocks using an algorithm which is about to go away. If the algorithm | ||||
* This ensures the CC framework can allow the unload to proceed without leaving | * has a cb_init function and it fails (no memory) then the operation fails and | ||||
* any dangling pointers which would trigger a panic. | * the unload will not succeed. | ||||
* Returning non-zero would inform the CC framework that something went wrong | * | ||||
* and it would be unsafe to allow the unload to proceed. However, there is no | |||||
* way for this to occur with this implementation so we always return zero. | |||||
*/ | */ | ||||
int | int | ||||
tcp_ccalgounload(struct cc_algo *unload_algo) | tcp_ccalgounload(struct cc_algo *unload_algo) | ||||
{ | { | ||||
struct cc_algo *tmpalgo; | struct cc_algo *oldalgo, *newalgo; | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
VNET_ITERATOR_DECL(vnet_iter); | VNET_ITERATOR_DECL(vnet_iter); | ||||
/* | /* | ||||
* Check all active control blocks across all network stacks and change | * Check all active control blocks across all network stacks and change | ||||
* any that are using "unload_algo" back to NewReno. If "unload_algo" | * any that are using "unload_algo" back to its default. If "unload_algo" | ||||
* requires cleanup code to be run, call it. | * requires cleanup code to be run, call it. | ||||
*/ | */ | ||||
VNET_LIST_RLOCK(); | VNET_LIST_RLOCK(); | ||||
VNET_FOREACH(vnet_iter) { | VNET_FOREACH(vnet_iter) { | ||||
CURVNET_SET(vnet_iter); | CURVNET_SET(vnet_iter); | ||||
INP_INFO_WLOCK(&V_tcbinfo); | INP_INFO_WLOCK(&V_tcbinfo); | ||||
/* | /* | ||||
* New connections already part way through being initialised | * New connections already part way through being initialised | ||||
* with the CC algo we're removing will not race with this code | * with the CC algo we're removing will not race with this code | ||||
* because the INP_INFO_WLOCK is held during initialisation. We | * because the INP_INFO_WLOCK is held during initialisation. We | ||||
* therefore don't enter the loop below until the connection | * therefore don't enter the loop below until the connection | ||||
* list has stabilised. | * list has stabilised. | ||||
*/ | */ | ||||
newalgo = CC_DEFAULT_ALGO(); | |||||
CK_LIST_FOREACH(inp, &V_tcb, inp_list) { | CK_LIST_FOREACH(inp, &V_tcb, inp_list) { | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
/* Important to skip tcptw structs. */ | /* Important to skip tcptw structs. */ | ||||
if (!(inp->inp_flags & INP_TIMEWAIT) && | if (!(inp->inp_flags & INP_TIMEWAIT) && | ||||
(tp = intotcpcb(inp)) != NULL) { | (tp = intotcpcb(inp)) != NULL) { | ||||
/* | /* | ||||
* By holding INP_WLOCK here, we are assured | * By holding INP_WLOCK here, we are assured | ||||
* that the connection is not currently | * that the connection is not currently | ||||
* executing inside the CC module's functions | * executing inside the CC module's functions. | ||||
* i.e. it is safe to make the switch back to | * We attempt to switch to the Vnets default, | ||||
* NewReno. | * if the init fails then we fail the whole | ||||
* operation and the module unload will fail. | |||||
*/ | */ | ||||
if (CC_ALGO(tp) == unload_algo) { | if (CC_ALGO(tp) == unload_algo) { | ||||
tmpalgo = CC_ALGO(tp); | struct cc_var cc_mem; | ||||
if (tmpalgo->cb_destroy != NULL) | int err; | ||||
tmpalgo->cb_destroy(tp->ccv); | |||||
oldalgo = CC_ALGO(tp); | |||||
memset(&cc_mem, 0, sizeof(cc_mem)); | |||||
cc_mem.ccvc.tcp = tp; | |||||
if (newalgo->cb_init == NULL) { | |||||
/* | |||||
* No init we can skip the | |||||
* dance around a possible failure. | |||||
*/ | |||||
CC_DATA(tp) = NULL; | CC_DATA(tp) = NULL; | ||||
goto proceed; | |||||
} | |||||
err = (newalgo->cb_init)(&cc_mem, NULL); | |||||
if (err) { | |||||
/* | /* | ||||
* NewReno may allocate memory on | * Presumably no memory the caller will | ||||
* demand for certain stateful | * need to try again. | ||||
* configuration as needed, but is | |||||
* coded to never fail on memory | |||||
* allocation failure so it is a safe | |||||
* fallback. | |||||
*/ | */ | ||||
CC_ALGO(tp) = &newreno_cc_algo; | INP_WUNLOCK(inp); | ||||
INP_INFO_WUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | |||||
VNET_LIST_RUNLOCK(); | |||||
return (err); | |||||
} | } | ||||
proceed: | |||||
if (oldalgo->cb_destroy != NULL) | |||||
oldalgo->cb_destroy(tp->ccv); | |||||
CC_ALGO(tp) = newalgo; | |||||
memcpy(tp->ccv, &cc_mem, sizeof(struct cc_var)); | |||||
if (TCPS_HAVEESTABLISHED(tp->t_state) && | |||||
(CC_ALGO(tp)->conn_init != NULL)) { | |||||
/* Yep run the connection init for the new CC */ | |||||
CC_ALGO(tp)->conn_init(tp->ccv); | |||||
} | } | ||||
} | |||||
} | |||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
} | } | ||||
INP_INFO_WUNLOCK(&V_tcbinfo); | INP_INFO_WUNLOCK(&V_tcbinfo); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
VNET_LIST_RUNLOCK(); | VNET_LIST_RUNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Drop a TCP connection, reporting | * Drop a TCP connection, reporting | ||||
* the specified error. If connection is synchronized, | * the specified error. If connection is synchronized, | ||||
* then send a RST to peer. | * then send a RST to peer. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 1,880 Lines • Show Last 20 Lines |