MEDIUM: ssl: implement xprt_set_used and xprt_set_idle to relax context checks
Currently the SSL layer checks the validity of its tasklet's context just
in case it would have been stolen, had the connection been idle. Now it
will be able to be notified by the mux when this situation happens so as
not to have to grab the idle connection lock on each pass. This reuses the
TASK_F_USR1 flag just as the muxes do.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index bc25d02..bd24ae7 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -5765,6 +5765,37 @@
return 0;
}
+/* notify the next xprt that the connection is about to become idle and that it
+ * may be stolen at any time after the function returns and that any tasklet in
+ * the chain must be careful before dereferencing its context.
+ */
+static void ssl_set_idle(struct connection *conn, void *xprt_ctx)
+{
+ struct ssl_sock_ctx *ctx = xprt_ctx;
+
+ if (!ctx || !ctx->wait_event.tasklet)
+ return;
+
+ HA_ATOMIC_OR(&ctx->wait_event.tasklet->state, TASK_F_USR1);
+ if (ctx->xprt)
+ xprt_set_idle(conn, ctx->xprt, ctx->xprt_ctx);
+}
+
+/* notify the next xprt that the connection is not idle anymore and that it may
+ * not be stolen before the next xprt_set_idle().
+ */
+static void ssl_set_used(struct connection *conn, void *xprt_ctx)
+{
+ struct ssl_sock_ctx *ctx = xprt_ctx;
+
+ if (!ctx || !ctx->wait_event.tasklet)
+ return;
+
+ HA_ATOMIC_OR(&ctx->wait_event.tasklet->state, TASK_F_USR1);
+ if (ctx->xprt)
+ xprt_set_used(conn, ctx->xprt, ctx->xprt_ctx);
+}
+
/* Use the provided XPRT as an underlying XPRT, and provide the old one.
* Returns 0 on success, and non-zero on failure.
*/
@@ -5805,17 +5836,26 @@
int conn_in_list;
int ret = 0;
- HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
- if (tl->context == NULL) {
+ if (state & TASK_F_USR1) {
+ /* the tasklet was idling on an idle connection, it might have
+ * been stolen, let's be careful!
+ */
+ HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
+ if (tl->context == NULL) {
+ HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
+ tasklet_free(tl);
+ return NULL;
+ }
+ conn = ctx->conn;
+ conn_in_list = conn->flags & CO_FL_LIST_MASK;
+ if (conn_in_list)
+ conn_delete_from_tree(&conn->hash_node->node);
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
- tasklet_free(tl);
- return NULL;
+ } else {
+ conn = ctx->conn;
+ conn_in_list = 0;
}
- conn = ctx->conn;
- conn_in_list = conn->flags & CO_FL_LIST_MASK;
- if (conn_in_list)
- conn_delete_from_tree(&conn->hash_node->node);
- HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
+
/* First if we're doing an handshake, try that */
if (ctx->conn->flags & CO_FL_SSL_WAIT_HS) {
ssl_sock_handshake(ctx->conn, CO_FL_SSL_WAIT_HS);
@@ -6908,6 +6948,8 @@
.destroy_srv = ssl_sock_free_srv_ctx,
.get_alpn = ssl_sock_get_alpn,
.takeover = ssl_takeover,
+ .set_idle = ssl_set_idle,
+ .set_used = ssl_set_used,
.name = "SSL",
.show_fd = ssl_sock_show_fd,
};