[PATCH net-next v17 13/15] tls: device: add tracepoints for the KeyUpdate path
From: Rishikesh Jethwani <hidden>
Date: 2026-09-17 22:45:39
Subsystem:
networking [general], networking [tls], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, John Fastabend, Sabrina Dubroca, Linus Torvalds
Add five trace events covering the rekey state machine in
tls_device.c:
tls_device_rekey_start: rekey accepted, inflight=1 marks a
drain window where old-key data is still
queued and dev_add is deferred, whether
this rekey opened the window or landed
behind a still-draining prior one.
inflight=0 means the key was hot-swapped
with no drain; nic_boundary is then the
candidate receive frontier, not a stored
boundary.
tls_device_rekey_reencrypt: old-key undo pass for a boundary
record
tls_device_rekey_done: a drain window's boundary was crossed and
old_aead_recv freed, either by a record
arriving past old_nic_boundary or by a new
rekey superseding a prior one whose era had
already drained. deferred dev_add is issued
if pending.
tls_device_complete_rekey_retry: TX rekey completion hit the
transient -EAGAIN retry in sendmsg,
the next sendmsg retries.
tls_device_complete_rekey_fail: TX rekey completion gave up and
fell back to SW encryption. Emitted
from the fallback path itself, since
the sendmsg call site only sees the
transient retry.
These are independent event markers, not a paired begin/end span. A
rekey_start is not 1:1 with a rekey_done: a rekey that lands behind a
still-draining prior one reports inflight=1 without opening a window of
its own, so several starts can map to a single window and a single done.
The invariant is per-window - each drain window that is opened closes
with exactly one rekey_done. A window can also be abandoned without a
done when a rekey is aborted (tls_sw_ctx_init() failing after the window
opened, or the socket being torn down mid-drain, the latter counted by
TLSRXREKEYABORTED); those paths are not traced.
Signed-off-by: Rishikesh Jethwani <redacted>
---
net/tls/tls_device.c | 36 ++++++++++++-
net/tls/trace.h | 118 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 152 insertions(+), 2 deletions(-)
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
index ac09f356cff9..5f45c097bad3 100644
--- a/net/tls/tls_device.c
+++ b/net/tls/tls_device.c@@ -866,8 +866,16 @@ int tls_device_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) lock_sock(sk); /* Old-key records all ACKed; switch back to HW. */ - if (test_bit(TLS_TX_REKEY_READY, &tls_ctx->flags)) - tls_device_complete_rekey(sk, tls_ctx, true, msg->msg_flags); + if (test_bit(TLS_TX_REKEY_READY, &tls_ctx->flags)) { + rc = tls_device_complete_rekey(sk, tls_ctx, true, msg->msg_flags); + /* Non-zero here is the transient -EAGAIN retry, + * the next sendmsg retries. Hard failures return 0 after + * falling back to SW and emit tls_device_complete_rekey_fail + * from the fallback path. + */ + if (rc) + trace_tls_device_complete_rekey_retry(sk); + } if (tls_device_tx_uses_sw(tls_ctx)) { rc = tls_sw_sendmsg_locked(sk, msg, size);
@@ -1430,10 +1438,15 @@ int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx) return 0; } + trace_tls_device_rekey_reencrypt(sk, rec_start_seq, + ctx->rekey.old_nic_boundary); + return tls_device_reencrypt_old_key(sk, ctx, sw_ctx, tls_ctx); } + trace_tls_device_rekey_done(sk, rec_start_seq, + ctx->rekey.old_nic_boundary); crypto_free_aead(ctx->rekey.old_aead_recv); ctx->rekey.old_aead_recv = NULL;
@@ -1890,6 +1903,13 @@ static int tls_device_complete_rekey(struct sock *sk, struct tls_context *ctx, TLS_DEC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXDEVICE); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXSW); + /* Hard failure: HW rekey gave up and the connection is now pinned to + * SW encryption. The call site only sees the transient -EAGAIN retry + * (rc is not propagated here), so emit the trace from the fallback + * path itself; rc still holds the originating error. + */ + trace_tls_device_complete_rekey_fail(sk, rc); + return 0; }
@@ -2195,11 +2215,21 @@ int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx, * is installed once drain_start crosses rekey.old_nic_boundary. */ context->dev_add_pending = 1; + trace_tls_device_rekey_start(sk, drain_start, + context->rekey.old_nic_boundary, + true); } else { struct tcp_sock *tp = tcp_sk(sk); u32 nic_end; if (context->rekey.old_aead_recv) { + /* Prior rekey's era already drained (drain_start is + * past old_nic_boundary), so retiring its key here + * is a boundary crossing, same as the free in + * tls_device_decrypted(); mark it done. + */ + trace_tls_device_rekey_done(sk, drain_start, + context->rekey.old_nic_boundary); crypto_free_aead(context->rekey.old_aead_recv); context->rekey.old_aead_recv = NULL; }
@@ -2247,6 +2277,8 @@ int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx, context->dev_add_pending = 0; retired_pending = true; } + trace_tls_device_rekey_start(sk, drain_start, nic_end, + before(drain_start, nic_end)); } }
diff --git a/net/tls/trace.h b/net/tls/trace.h
index 2d8ce4ff3265..5b9c1f86d82d 100644
--- a/net/tls/trace.h
+++ b/net/tls/trace.h@@ -192,6 +192,124 @@ TRACE_EVENT(tls_device_tx_resync_send, ) ); +TRACE_EVENT(tls_device_rekey_start, + + TP_PROTO(struct sock *sk, u32 copied_seq, u32 nic_boundary, + bool inflight), + + TP_ARGS(sk, copied_seq, nic_boundary, inflight), + + TP_STRUCT__entry( + __field( struct sock *, sk ) + __field( u32, copied_seq ) + __field( u32, nic_boundary ) + __field( bool, inflight ) + ), + + TP_fast_assign( + __entry->sk = sk; + __entry->copied_seq = copied_seq; + __entry->nic_boundary = nic_boundary; + __entry->inflight = inflight; + ), + + TP_printk( + "sk=%p copied_seq=%u nic_boundary=%u inflight=%d", + __entry->sk, __entry->copied_seq, __entry->nic_boundary, + __entry->inflight + ) +); + +TRACE_EVENT(tls_device_rekey_reencrypt, + + TP_PROTO(struct sock *sk, u32 tcp_seq, u32 nic_boundary), + + TP_ARGS(sk, tcp_seq, nic_boundary), + + TP_STRUCT__entry( + __field( struct sock *, sk ) + __field( u32, tcp_seq ) + __field( u32, nic_boundary ) + ), + + TP_fast_assign( + __entry->sk = sk; + __entry->tcp_seq = tcp_seq; + __entry->nic_boundary = nic_boundary; + ), + + TP_printk( + "sk=%p tcp_seq=%u nic_boundary=%u", + __entry->sk, __entry->tcp_seq, __entry->nic_boundary + ) +); + +TRACE_EVENT(tls_device_rekey_done, + + TP_PROTO(struct sock *sk, u32 tcp_seq, u32 nic_boundary), + + TP_ARGS(sk, tcp_seq, nic_boundary), + + TP_STRUCT__entry( + __field( struct sock *, sk ) + __field( u32, tcp_seq ) + __field( u32, nic_boundary ) + ), + + TP_fast_assign( + __entry->sk = sk; + __entry->tcp_seq = tcp_seq; + __entry->nic_boundary = nic_boundary; + ), + + TP_printk( + "sk=%p tcp_seq=%u nic_boundary=%u", + __entry->sk, __entry->tcp_seq, __entry->nic_boundary + ) +); + +TRACE_EVENT(tls_device_complete_rekey_fail, + + TP_PROTO(struct sock *sk, int rc), + + TP_ARGS(sk, rc), + + TP_STRUCT__entry( + __field( struct sock *, sk ) + __field( int, rc ) + ), + + TP_fast_assign( + __entry->sk = sk; + __entry->rc = rc; + ), + + TP_printk( + "sk=%p rc=%d", + __entry->sk, __entry->rc + ) +); + +TRACE_EVENT(tls_device_complete_rekey_retry, + + TP_PROTO(struct sock *sk), + + TP_ARGS(sk), + + TP_STRUCT__entry( + __field( struct sock *, sk ) + ), + + TP_fast_assign( + __entry->sk = sk; + ), + + TP_printk( + "sk=%p", + __entry->sk + ) +); + #endif /* _TLS_TRACE_H_ */ #undef TRACE_INCLUDE_PATH
--
2.50.1