summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/cc/cc.c54
-rw-r--r--sys/netinet/tcp_subr.c63
-rw-r--r--sys/netinet/tcp_var.h1
3 files changed, 70 insertions, 48 deletions
diff --git a/sys/netinet/cc/cc.c b/sys/netinet/cc/cc.c
index f346078..009ebe7 100644
--- a/sys/netinet/cc/cc.c
+++ b/sys/netinet/cc/cc.c
@@ -190,10 +190,7 @@ int
cc_deregister_algo(struct cc_algo *remove_cc)
{
struct cc_algo *funcs, *tmpfuncs;
- struct tcpcb *tp;
- struct inpcb *inp;
int err;
- VNET_ITERATOR_DECL(vnet_iter);
err = ENOENT;
@@ -220,53 +217,14 @@ cc_deregister_algo(struct cc_algo *remove_cc)
}
CC_LIST_WUNLOCK();
- if (!err) {
+ if (!err)
/*
- * Check all active control blocks across all network stacks and
- * change any that are using this algorithm back to newreno. If
- * the algorithm that was in use requires cleanup code to be
- * run, call it.
- *
- * New connections already part way through being initialised
- * with the CC algo we're removing will not race with this code
- * because the INP_INFO_WLOCK is held during initialisation.
- * We therefore don't enter the loop below until the connection
- * list has stabilised.
+ * XXXLAS:
+ * - We may need to handle non-zero return values in future.
+ * - If we add CC framework support for protocols other than
+ * TCP, we may want a more generic way to handle this step.
*/
- VNET_LIST_RLOCK();
- VNET_FOREACH(vnet_iter) {
- CURVNET_SET(vnet_iter);
- INP_INFO_RLOCK(&V_tcbinfo);
- LIST_FOREACH(inp, &V_tcb, inp_list) {
- INP_WLOCK(inp);
- /* Important to skip tcptw structs. */
- if (!(inp->inp_flags & INP_TIMEWAIT) &&
- (tp = intotcpcb(inp)) != NULL) {
- /*
- * By holding INP_WLOCK here, we are
- * assured that the connection is not
- * currently executing inside the CC
- * module's functions i.e. it is safe
- * to make the switch back to newreno.
- */
- if (CC_ALGO(tp) == remove_cc) {
- tmpfuncs = CC_ALGO(tp);
- /*
- * Newreno does not
- * require any init.
- */
- CC_ALGO(tp) = &newreno_cc_algo;
- if (tmpfuncs->cb_destroy != NULL)
- tmpfuncs->cb_destroy(tp->ccv);
- }
- }
- INP_WUNLOCK(inp);
- }
- INP_INFO_RUNLOCK(&V_tcbinfo);
- CURVNET_RESTORE();
- }
- VNET_LIST_RUNLOCK();
- }
+ tcp_ccalgounload(remove_cc);
return (err);
}
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index e863afa..eebc023 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -708,6 +708,69 @@ tcp_newtcpcb(struct inpcb *inp)
}
/*
+ * Switch the congestion control algorithm back to NewReno for any active
+ * control blocks using an algorithm which is about to go away.
+ * This ensures the CC framework can allow the unload to proceed without leaving
+ * any dangling pointers which would trigger a panic.
+ * 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
+tcp_ccalgounload(struct cc_algo *unload_algo)
+{
+ struct cc_algo *tmpalgo;
+ struct inpcb *inp;
+ struct tcpcb *tp;
+ VNET_ITERATOR_DECL(vnet_iter);
+
+ /*
+ * Check all active control blocks across all network stacks and change
+ * any that are using "unload_algo" back to NewReno. If "unload_algo"
+ * requires cleanup code to be run, call it.
+ */
+ VNET_LIST_RLOCK();
+ VNET_FOREACH(vnet_iter) {
+ CURVNET_SET(vnet_iter);
+ INP_INFO_RLOCK(&V_tcbinfo);
+ /*
+ * New connections already part way through being initialised
+ * with the CC algo we're removing will not race with this code
+ * because the INP_INFO_WLOCK is held during initialisation. We
+ * therefore don't enter the loop below until the connection
+ * list has stabilised.
+ */
+ LIST_FOREACH(inp, &V_tcb, inp_list) {
+ INP_WLOCK(inp);
+ /* Important to skip tcptw structs. */
+ if (!(inp->inp_flags & INP_TIMEWAIT) &&
+ (tp = intotcpcb(inp)) != NULL) {
+ /*
+ * By holding INP_WLOCK here, we are assured
+ * that the connection is not currently
+ * executing inside the CC module's functions
+ * i.e. it is safe to make the switch back to
+ * NewReno.
+ */
+ if (CC_ALGO(tp) == unload_algo) {
+ tmpalgo = CC_ALGO(tp);
+ /* NewReno does not require any init. */
+ CC_ALGO(tp) = &newreno_cc_algo;
+ if (tmpalgo->cb_destroy != NULL)
+ tmpalgo->cb_destroy(tp->ccv);
+ }
+ }
+ INP_WUNLOCK(inp);
+ }
+ INP_INFO_RUNLOCK(&V_tcbinfo);
+ CURVNET_RESTORE();
+ }
+ VNET_LIST_RUNLOCK();
+
+ return (0);
+}
+
+/*
* Drop a TCP connection, reporting
* the specified error. If connection is synchronized,
* then send a RST to peer.
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 442c736..7b38667 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -605,6 +605,7 @@ VNET_DECLARE(int, tcp_ecn_maxretries);
#define V_tcp_ecn_maxretries VNET(tcp_ecn_maxretries)
int tcp_addoptions(struct tcpopt *, u_char *);
+int tcp_ccalgounload(struct cc_algo *unload_algo);
struct tcpcb *
tcp_close(struct tcpcb *);
void tcp_discardcb(struct tcpcb *);
OpenPOWER on IntegriCloud