]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tls: Suppress spurious saved_data_ready on all receive paths
authorChuck Lever <chuck.lever@oracle.com>
Thu, 4 Jun 2026 17:48:28 +0000 (13:48 -0400)
committerJakub Kicinski <kuba@kernel.org>
Tue, 9 Jun 2026 03:10:21 +0000 (20:10 -0700)
Each record release via tls_strp_msg_done() triggered
tls_strp_check_rcv(), which called tls_rx_msg_ready() and
fired saved_data_ready(). During a multi-record receive, the
first N-1 wakeups are pure overhead: the caller is already
running and will pick up subsequent records on the next loop
iteration. The recvmsg and splice_read paths share this waste.

Suppress per-record notifications and emit a single one on
reader exit. tls_rx_rec_done() releases the current record
and parses the next without announcing; tls_strp_check_rcv()
gains a bool announce parameter so callers can request the
quiet form. tls_rx_reader_release() fires the deferred
announce on exit through tls_rx_msg_maybe_announce(), an
idempotent helper that calls saved_data_ready() only when a
record is parsed and has not yet been announced.

To keep the final notification idempotent against records that
the BH or the worker has already announced, tls_strparser gains
a msg_announced bit. tls_rx_msg_maybe_announce() sets the bit
when firing saved_data_ready(); the bit is cleared whenever
the parsed record is wiped, by tls_strp_msg_consume() on
consumption or by tls_strp_msg_load() when the lower socket
loses bytes from under the parse. A second call for the same
parsed record -- as when recvmsg() satisfies the request from
ctx->rx_list without touching the strparser -- becomes a
no-op.

With no remaining callers, tls_strp_msg_done() is removed.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/20260604-tls-read-sock-v12-5-b114efa6e3e2@oracle.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/tls.h
net/tls/tls.h
net/tls/tls_main.c
net/tls/tls_strp.c
net/tls/tls_sw.c

index ebd2550280ae241a1a13c6bf1c1bd8a49db998c8..3811943288b307c1932abbf8e06f75d74e4ccc46 100644 (file)
@@ -111,11 +111,16 @@ struct tls_sw_context_tx {
 struct tls_strparser {
        struct sock *sk;
 
+       /* Bitfield word and msg_ready are serialized by the lower
+        * socket lock; BH and worker contexts both acquire it.
+        */
        u32 mark : 8;
        u32 stopped : 1;
        u32 copy_mode : 1;
        u32 mixed_decrypted : 1;
 
+       u32 msg_announced : 1;
+
        bool msg_ready;
 
        struct strp_msg stm;
index cb0091e03f41728cce09d8b020dfe803e461f332..60a37bdaaa25067421e86b57df36254fb28481a6 100644 (file)
@@ -193,12 +193,11 @@ void tls_strp_stop(struct tls_strparser *strp);
 int tls_strp_init(struct tls_strparser *strp, struct sock *sk);
 void tls_strp_data_ready(struct tls_strparser *strp);
 
-void tls_strp_check_rcv(struct tls_strparser *strp);
+void tls_strp_check_rcv(struct tls_strparser *strp, bool announce);
 void tls_strp_msg_consume(struct tls_strparser *strp);
-void tls_strp_msg_done(struct tls_strparser *strp);
 
 int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb);
-void tls_rx_msg_ready(struct tls_strparser *strp);
+void tls_rx_msg_maybe_announce(struct tls_strparser *strp);
 
 bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh);
 int tls_strp_msg_cow(struct tls_sw_context_rx *ctx);
index fd39acf41a618b59ce55e5528d94a06bcae1fa96..c10a3fd7fc17d85d965f8fb5d00a886387c82e54 100644 (file)
@@ -769,7 +769,7 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
        } else {
                struct tls_sw_context_rx *rx_ctx = tls_sw_ctx_rx(ctx);
 
-               tls_strp_check_rcv(&rx_ctx->strp);
+               tls_strp_check_rcv(&rx_ctx->strp, true);
        }
        return 0;
 
index e7aaee6efe6e9d70086cc50b0dd711394f532385..61b10c697eccbb3ebfa4ad40b78eba473292fa5f 100644 (file)
@@ -368,7 +368,6 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb,
                desc->count = 0;
 
                WRITE_ONCE(strp->msg_ready, 1);
-               tls_rx_msg_ready(strp);
        }
 
        return ret;
@@ -492,6 +491,7 @@ bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
        if (!strp->copy_mode && force_refresh) {
                if (unlikely(tcp_inq(strp->sk) < strp->stm.full_len)) {
                        WRITE_ONCE(strp->msg_ready, 0);
+                       strp->msg_announced = 0;
                        memset(&strp->stm, 0, sizeof(strp->stm));
                        return false;
                }
@@ -539,18 +539,24 @@ static int tls_strp_read_sock(struct tls_strparser *strp)
                return tls_strp_read_copy(strp, false);
 
        WRITE_ONCE(strp->msg_ready, 1);
-       tls_rx_msg_ready(strp);
 
        return 0;
 }
 
-void tls_strp_check_rcv(struct tls_strparser *strp)
+/* Parse queued data. When @announce is true and parsing produces a
+ * newly-ready record, fire the consumer notification. Callers that
+ * need to notify a waiter about a record parsed by another path
+ * should invoke tls_rx_msg_maybe_announce() directly.
+ */
+void tls_strp_check_rcv(struct tls_strparser *strp, bool announce)
 {
        if (unlikely(strp->stopped) || strp->msg_ready)
                return;
 
        if (tls_strp_read_sock(strp) == -ENOMEM)
                queue_work(tls_strp_wq, &strp->work);
+       else if (announce && strp->msg_ready)
+               tls_rx_msg_maybe_announce(strp);
 }
 
 /* Lower sock lock held */
@@ -568,7 +574,7 @@ void tls_strp_data_ready(struct tls_strparser *strp)
                return;
        }
 
-       tls_strp_check_rcv(strp);
+       tls_strp_check_rcv(strp, true);
 }
 
 static void tls_strp_work(struct work_struct *w)
@@ -577,7 +583,7 @@ static void tls_strp_work(struct work_struct *w)
                container_of(w, struct tls_strparser, work);
 
        lock_sock(strp->sk);
-       tls_strp_check_rcv(strp);
+       tls_strp_check_rcv(strp, true);
        release_sock(strp->sk);
 }
 
@@ -596,15 +602,10 @@ void tls_strp_msg_consume(struct tls_strparser *strp)
                tls_strp_flush_anchor_copy(strp);
 
        WRITE_ONCE(strp->msg_ready, 0);
+       strp->msg_announced = 0;
        memset(&strp->stm, 0, sizeof(strp->stm));
 }
 
-void tls_strp_msg_done(struct tls_strparser *strp)
-{
-       tls_strp_msg_consume(strp);
-       tls_strp_check_rcv(strp);
-}
-
 void tls_strp_stop(struct tls_strparser *strp)
 {
        strp->stopped = 1;
index 798f2535ddf77bb7d949f65bdff192b4fafffeca..df4cdf11f784bed9c34efb4a580fc99588df96e3 100644 (file)
@@ -1401,7 +1401,10 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
                        return ret;
 
                if (!skb_queue_empty(&sk->sk_receive_queue)) {
-                       tls_strp_check_rcv(&ctx->strp);
+                       /* Defer notification to the exit point; this thread
+                        * will consume the record directly.
+                        */
+                       tls_strp_check_rcv(&ctx->strp, false);
                        if (tls_strp_msg_ready(ctx))
                                break;
                }
@@ -1887,9 +1890,13 @@ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm,
        return 1;
 }
 
+/* The deferred announce is fired once on reader exit by
+ * tls_rx_reader_release().
+ */
 static void tls_rx_rec_done(struct tls_sw_context_rx *ctx)
 {
-       tls_strp_msg_done(&ctx->strp);
+       tls_strp_msg_consume(&ctx->strp);
+       tls_strp_check_rcv(&ctx->strp, false);
 }
 
 /* This function traverses the rx_list in tls receive context to copies the
@@ -2044,6 +2051,12 @@ static int tls_rx_reader_lock(struct sock *sk, struct tls_sw_context_rx *ctx,
 
 static void tls_rx_reader_release(struct sock *sk, struct tls_sw_context_rx *ctx)
 {
+       /* Fire any deferred announce once per reader so that a record
+        * parsed but not yet announced becomes visible to the next
+        * reader. The call is idempotent through msg_announced.
+        */
+       tls_rx_msg_maybe_announce(&ctx->strp);
+
        if (unlikely(ctx->reader_contended)) {
                if (wq_has_sleeper(&ctx->wq))
                        wake_up(&ctx->wq);
@@ -2520,10 +2533,18 @@ read_failure:
        return ret;
 }
 
-void tls_rx_msg_ready(struct tls_strparser *strp)
+/* Fire saved_data_ready() at most once per parsed record. The
+ * msg_announced bit is cleared by tls_strp_msg_consume() when the
+ * record is consumed, arming the next announcement.
+ */
+void tls_rx_msg_maybe_announce(struct tls_strparser *strp)
 {
        struct tls_sw_context_rx *ctx;
 
+       if (!READ_ONCE(strp->msg_ready) || strp->msg_announced)
+               return;
+       strp->msg_announced = 1;
+
        ctx = container_of(strp, struct tls_sw_context_rx, strp);
        ctx->saved_data_ready(strp->sk);
 }