* [PATCH net-next v9 2/5] tls: Fix dangling skb pointer in tls_sw_read_sock()
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
In-Reply-To: <20260429-tls-read-sock-v9-0-39e71aa7810f@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
Two related defects in the receive loop of tls_sw_read_sock()
share a single fix.
Per ISO/IEC 9899:2011 section 6.2.4p2, a pointer value becomes
indeterminate when the object it points to reaches the end of
its lifetime; Annex J.2 classifies the use of such a value as
undefined behavior. consume_skb(skb) in the fully-consumed path
frees the skb, but the "do { } while (skb)" loop condition then
evaluates that freed pointer. Although the value is never
dereferenced -- the loop either continues and overwrites skb,
or exits -- any future change that adds a dereference between
consume_skb() and the loop condition would produce a silent
use-after-free.
Separately, when read_actor() consumes only part of a record
(used < rxm->full_len) but desc->count is still non-zero, the
existing code updates rxm in place and falls through to the
next loop iteration. The next iteration then unconditionally
overwrites skb without freeing or requeuing the partially
consumed buffer, leaking the skb and silently dropping stream
data.
A read_actor returning fewer bytes than offered is in every
case a backpressure signal; the only correct response is to
requeue and exit. Replace the do/while with an explicit
for(;;), requeue unconditionally on partial consume, and break
on exhausted desc->count after a full consume.
Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()")
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 244ac8ed4b01..c58d3b0b0a8a 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2366,7 +2366,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_end;
decrypted = 0;
- do {
+ for (;;) {
if (!skb_queue_empty(&ctx->rx_list)) {
skb = __skb_dequeue(&ctx->rx_list);
rxm = strp_msg(skb);
@@ -2411,14 +2411,13 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
if (used < rxm->full_len) {
rxm->offset += used;
rxm->full_len -= used;
- if (!desc->count)
- goto read_sock_requeue;
- } else {
- consume_skb(skb);
- if (!desc->count)
- skb = NULL;
+ goto read_sock_requeue;
}
- } while (skb);
+ consume_skb(skb);
+ skb = NULL;
+ if (!desc->count)
+ break;
+ }
read_sock_end:
tls_rx_reader_release(sk, ctx);
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v9 3/5] tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
In-Reply-To: <20260429-tls-read-sock-v9-0-39e71aa7810f@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
tls_strp_msg_done() conflates releasing the current record with
checking for the next one via tls_strp_check_rcv(). Batch
processing requires releasing a record without immediately
triggering that check, so the release step is separated into
tls_strp_msg_release(). tls_strp_msg_done() is preserved as a
wrapper for existing callers.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls.h | 1 +
net/tls/tls_strp.c | 15 ++++++++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h
index e8f81a006520..a97f1acef31d 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -193,6 +193,7 @@ 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_msg_release(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);
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index 98e12f0ff57e..a7648ebde162 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -581,7 +581,16 @@ static void tls_strp_work(struct work_struct *w)
release_sock(strp->sk);
}
-void tls_strp_msg_done(struct tls_strparser *strp)
+/**
+ * tls_strp_msg_release - release the current strparser message
+ * @strp: TLS stream parser instance
+ *
+ * Release the current record without triggering a check for the
+ * next record. Callers must invoke tls_strp_check_rcv() before
+ * releasing the socket lock, or queued data will stall until
+ * the next tls_strp_data_ready() event.
+ */
+void tls_strp_msg_release(struct tls_strparser *strp)
{
WARN_ON(!strp->stm.full_len);
@@ -592,7 +601,11 @@ void tls_strp_msg_done(struct tls_strparser *strp)
WRITE_ONCE(strp->msg_ready, 0);
memset(&strp->stm, 0, sizeof(strp->stm));
+}
+void tls_strp_msg_done(struct tls_strparser *strp)
+{
+ tls_strp_msg_release(strp);
tls_strp_check_rcv(strp);
}
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v9 4/5] tls: Suppress spurious saved_data_ready on all receive paths
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever
In-Reply-To: <20260429-tls-read-sock-v9-0-39e71aa7810f@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
Each record release via tls_strp_msg_done() triggers
tls_strp_check_rcv(), which calls tls_rx_msg_ready() and
fires 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. On the splice_read path the
per-record wakeup is similarly unnecessary because the
caller still holds the socket lock.
Replace tls_strp_msg_done() with tls_strp_msg_release()
in all three receive paths (read_sock, recvmsg,
splice_read), deferring the consumer notification to
each path's exit point. Factor tls_rx_msg_ready() out
of tls_strp_read_sock(), and add a @wake parameter to
tls_strp_check_rcv() so callers can parse queued data
without notifying. tls_strp_check_rcv() retains its
no-op-on-msg_ready semantics, so the BH and worker
notification paths fire saved_data_ready() at most once
per parsed record.
The exit points then invoke tls_rx_msg_ready() once,
covering records the inline parse loop left behind for
a subsequent reader. To keep that final notification
idempotent against records BH or the worker has already
announced, tls_strparser gains a msg_announced bit:
tls_rx_msg_ready() sets it when firing saved_data_ready();
the bit is cleared whenever the parsed record is wiped,
by tls_strp_msg_release() 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 happens when recvmsg() satisfies the request
from ctx->rx_list without touching the strparser, is then
a no-op.
With no remaining callers, tls_strp_msg_done() and its
wrapper tls_rx_rec_done() are removed.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
include/net/tls.h | 4 ++++
net/tls/tls.h | 3 +--
net/tls/tls_main.c | 2 +-
net/tls/tls_strp.c | 29 ++++++++++++++++++-----------
net/tls/tls_sw.c | 38 +++++++++++++++++++++++++++++++-------
5 files changed, 55 insertions(+), 21 deletions(-)
diff --git a/include/net/tls.h b/include/net/tls.h
index ebd2550280ae..b5c00a93f6ba 100644
--- a/include/net/tls.h
+++ b/include/net/tls.h
@@ -111,10 +111,14 @@ 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;
diff --git a/net/tls/tls.h b/net/tls/tls.h
index a97f1acef31d..f41dac6305f4 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -192,9 +192,8 @@ 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 wake);
void tls_strp_msg_release(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);
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index fd39acf41a61..c10a3fd7fc17 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -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;
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index a7648ebde162..bf88fad58b9b 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -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,30 @@ 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)
+/**
+ * tls_strp_check_rcv - parse queued data and optionally notify
+ * @strp: TLS stream parser instance
+ * @wake: if true, fire consumer notification when a record is newly
+ * parsed by this call
+ *
+ * Returns immediately when a record is already ready; the wake fires
+ * only on transitions from no-record to record-ready. Callers that
+ * need to notify a waiter about a record parsed by another path
+ * should invoke tls_rx_msg_ready() directly.
+ */
+void tls_strp_check_rcv(struct tls_strparser *strp, bool wake)
{
if (unlikely(strp->stopped) || strp->msg_ready)
return;
if (tls_strp_read_sock(strp) == -ENOMEM)
queue_work(tls_strp_wq, &strp->work);
+ else if (wake && strp->msg_ready)
+ tls_rx_msg_ready(strp);
}
/* Lower sock lock held */
@@ -568,7 +580,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 +589,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);
}
@@ -600,15 +612,10 @@ void tls_strp_msg_release(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_release(strp);
- tls_strp_check_rcv(strp);
-}
-
void tls_strp_stop(struct tls_strparser *strp)
{
strp->stopped = 1;
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index c58d3b0b0a8a..cbb068266bab 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1383,7 +1383,11 @@ 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;
}
@@ -1869,9 +1873,17 @@ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm,
return 1;
}
-static void tls_rx_rec_done(struct tls_sw_context_rx *ctx)
+/* Parse any data left in the lower socket and hand off a single
+ * notification to the next reader. tls_rx_msg_ready() is a no-op
+ * when the current record has already been announced, so paths
+ * that drained ctx->rx_list without touching the strparser do
+ * not re-fire saved_data_ready() for a record BH or the worker
+ * already announced.
+ */
+static void tls_rx_handoff(struct tls_sw_context_rx *ctx)
{
- tls_strp_msg_done(&ctx->strp);
+ tls_strp_check_rcv(&ctx->strp, false);
+ tls_rx_msg_ready(&ctx->strp);
}
/* This function traverses the rx_list in tls receive context to copies the
@@ -2152,7 +2164,7 @@ int tls_sw_recvmsg(struct sock *sk,
err = tls_record_content_type(msg, tls_msg(darg.skb), &control);
if (err <= 0) {
DEBUG_NET_WARN_ON_ONCE(darg.zc);
- tls_rx_rec_done(ctx);
+ tls_strp_msg_release(&ctx->strp);
put_on_rx_list_err:
__skb_queue_tail(&ctx->rx_list, darg.skb);
goto recv_end;
@@ -2166,7 +2178,8 @@ int tls_sw_recvmsg(struct sock *sk,
/* TLS 1.3 may have updated the length by more than overhead */
rxm = strp_msg(darg.skb);
chunk = rxm->full_len;
- tls_rx_rec_done(ctx);
+ tls_strp_msg_release(&ctx->strp);
+ tls_strp_check_rcv(&ctx->strp, false);
if (!darg.zc) {
bool partially_consumed = chunk > len;
@@ -2260,6 +2273,7 @@ int tls_sw_recvmsg(struct sock *sk,
copied += decrypted;
end:
+ tls_rx_handoff(ctx);
tls_rx_reader_unlock(sk, ctx);
if (psock)
sk_psock_put(sk, psock);
@@ -2300,7 +2314,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
if (err < 0)
goto splice_read_end;
- tls_rx_rec_done(ctx);
+ tls_strp_msg_release(&ctx->strp);
skb = darg.skb;
}
@@ -2327,6 +2341,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
consume_skb(skb);
splice_read_end:
+ tls_rx_handoff(ctx);
tls_rx_reader_unlock(sk, ctx);
return copied ? : err;
@@ -2392,7 +2407,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
tlm = tls_msg(skb);
decrypted += rxm->full_len;
- tls_rx_rec_done(ctx);
+ tls_strp_msg_release(&ctx->strp);
}
/* read_sock does not support reading control messages */
@@ -2420,6 +2435,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
}
read_sock_end:
+ tls_rx_handoff(ctx);
tls_rx_reader_release(sk, ctx);
return copied ? : err;
@@ -2504,10 +2520,18 @@ int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb)
return ret;
}
+/* Fire saved_data_ready() at most once per parsed record.
+ * msg_announced is cleared by tls_strp_msg_release() when the
+ * current record is consumed, arming the next announcement.
+ */
void tls_rx_msg_ready(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);
}
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v9 5/5] tls: Flush backlog before waiting for a new record
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever, Hannes Reinecke
In-Reply-To: <20260429-tls-read-sock-v9-0-39e71aa7810f@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
While lock_sock is held, incoming TCP segments land on
sk->sk_backlog rather than sk->sk_receive_queue.
tls_rx_rec_wait() inspects only sk_receive_queue, so backlog
data remains invisible. For non-blocking callers (read_sock,
and recvmsg or splice_read with MSG_DONTWAIT) this causes a
spurious -EAGAIN. For blocking callers it forces an
unnecessary sleep/wakeup cycle.
Flush the backlog inside tls_rx_rec_wait() before checking
sk_receive_queue so the strparser can parse newly-arrived
segments immediately. On the next loop iteration
tls_read_flush_backlog() may redundantly flush, but this
path is cold and the cost is negligible.
Backlog processing can run tcp_reset(), which sets both
sk->sk_err = ECONNRESET and (via tcp_done()) sk->sk_shutdown
= SHUTDOWN_MASK. The pre-existing top-of-loop sk_err check
already ran before the flush, so without further care the
freshly-set error would be masked by the next-line
sk_shutdown test returning 0 (EOF). Re-check sk_err
immediately before the sk_shutdown test so a connection
abort surfaces as -ECONNRESET rather than a clean EOF.
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index cbb068266bab..b888aaa505c0 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1382,6 +1382,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
if (ret < 0)
return ret;
+ if (sk_flush_backlog(sk))
+ released = true;
if (!skb_queue_empty(&sk->sk_receive_queue)) {
/* Defer notification to the exit point;
* this thread will consume the record
@@ -1392,6 +1394,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
break;
}
+ if (sk->sk_err)
+ return sock_error(sk);
if (sk->sk_shutdown & RCV_SHUTDOWN)
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH] amd-xgbe: fix PTP addend overflow causing frozen clock
From: Gregory Fuchedgi via B4 Relay @ 2026-04-29 21:54 UTC (permalink / raw)
To: Raju Rangoju, Prashanth Kumar K R, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran
Cc: netdev, linux-kernel, Gregory Fuchedgi
From: Gregory Fuchedgi <gfuchedgi@gmail.com>
XGBE_PTP_ACT_CLK_FREQ and XGBE_V2_PTP_ACT_CLK_FREQ were 10x too
large (500MHz/1GHz instead of 50MHz/100MHz), causing the computed
addend to overflow the 32-bit tstamp_addend. In the general case
this would result in the clock advancing at the wrong rate. For v2
(PCI), ptpclk_rate is hardcoded to 125MHz, so the addend formula
(ACT_CLK_FREQ << 32) / ptpclk_rate yields exactly 8 * 2^32, and
when stored to the 32-bit tstamp_addend the value is zero. With
addend = 0 the hardware accumulator never overflows and the PTP
clock is fully stopped. For v1 (platform), ptpclk_rate is read from
ACPI/DT so the exact overflow behavior depends on the
firmware-reported frequency.
Define the constants as NSEC_PER_SEC / SSINC so the relationship is
explicit and cannot drift out of sync.
Fixes: fbd47be098b5 ("amd-xgbe: add hardware PTP timestamping support")
Tested-by: Gregory Fuchedgi <gfuchedgi@gmail.com>
Signed-off-by: Gregory Fuchedgi <gfuchedgi@gmail.com>
---
Tested by running ptp4l and verifying successful clock synchronization on v2
(PCI) hardware.
---
drivers/net/ethernet/amd/xgbe/xgbe.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 60b7e53206d1..3d3b09010d48 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -135,11 +135,11 @@
*/
#define XGBE_TSTAMP_SSINC 20
#define XGBE_TSTAMP_SNSINC 0
-#define XGBE_PTP_ACT_CLK_FREQ 500000000
+#define XGBE_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_TSTAMP_SSINC)
#define XGBE_V2_TSTAMP_SSINC 0xA
#define XGBE_V2_TSTAMP_SNSINC 0
-#define XGBE_V2_PTP_ACT_CLK_FREQ 1000000000
+#define XGBE_V2_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_V2_TSTAMP_SSINC)
/* Define maximum supported values */
#define XGBE_MAX_PPS_OUT 4
---
base-commit: 0c7a5ba011d336df4fcd1f667fcc16ea5549be12
change-id: 20260428-fix-xgbe-ptp-addend-ccb1df627622
Best regards,
--
Gregory Fuchedgi <gfuchedgi@gmail.com>
^ permalink raw reply related
* Re: [PATCH net] ice: fix stats array overflow when VF requests more queues
From: Michal Schmidt @ 2026-04-29 21:59 UTC (permalink / raw)
To: Przemek Kitszel
Cc: Tony Nguyen, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Jacob Keller, Petr Oros,
intel-wired-lan, netdev, linux-kernel
In-Reply-To: <2106884f-6914-437f-84eb-262581b9fef7@intel.com>
On Tue, Apr 28, 2026 at 4:00 PM Przemek Kitszel
<przemyslaw.kitszel@intel.com> wrote:
> On 4/27/26 17:18, Michal Schmidt wrote:
> > When a VF increases its queue count via VIRTCHNL_OP_REQUEST_QUEUES,
> > ice_vc_request_qs_msg() sets vf->num_req_qs and triggers a VF reset.
> > The reset calls ice_vf_reconfig_vsi(), which does ice_vsi_decfg()
> > followed by ice_vsi_cfg(). ice_vsi_decfg() does not free the per-ring
> > stats arrays. Inside ice_vsi_cfg_def(), ice_vsi_set_num_qs() updates
> > alloc_txq/alloc_rxq to the new larger value, but
> > ice_vsi_alloc_stat_arrays() returns early because the stats already
> > exist. ice_vsi_alloc_ring_stats() then iterates using the new larger
> > alloc_txq and writes beyond the bounds of the old, smaller
> > tx_ring_stats/rx_ring_stats pointer arrays, corrupting adjacent SLUB
> > metadata.
> >
>
> thank you for reproducing the bug, it is exactly the situation that
> I was facing
> have you tried with my proposed (unfortunately not public yet) fix
> to just combine ice_vsi_alloc_stat_arrays() and
> ice_vsi_realloc_stat_arrays() into one function?
I tried that now and the result is: yes, your patch fixes the bug too.
Michal
^ permalink raw reply
* Re: [PATCH net-next v2 0/4] r8152: Add support for the RTL8159 10Gbit USB Ethernet chip
From: Aleksander Jan Bajkowski @ 2026-04-29 22:01 UTC (permalink / raw)
To: Birger Koblitz, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Andrew Lunn
In-Reply-To: <20260429-rtl8159_net_next-v2-0-bab3cd4e4c66@birger-koblitz.de>
Hi Birger,
I ran a few tests on the RTL8157 and RTL8159. The 1/2.5/5/10G link
establishes correctly. The device also behaves normally under iperf load.
Everything is working fine so far.
On 29/04/2026 19:01, Birger Koblitz wrote:
> Add support for the RTL8159, which is a 10GBit USB-Ethernet adapter
> chip in the RTL815x family of chips.
>
> The RTL8159 re-uses the frame descriptor format and SRAM2 access introduced
> with the RTL8157 as well as most of the setup and PM logic of the RTL8157.
>
> The module was tested with a Lekuo DR59R11 USB-C 10GbE Ethernet Adapter:
> [ 2502.906947] usb 2-1: new SuperSpeed USB device number 3 using xhci_hcd
> [ 2502.927859] usb 2-1: New USB device found, idVendor=0bda, idProduct=815a, bcdDevice=30.00
> [ 2502.927867] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=7
> [ 2502.927871] usb 2-1: Product: USB 10/100/1G/2.5G/5G/10G LAN
> [ 2502.927873] usb 2-1: Manufacturer: Realtek
> [ 2502.927875] usb 2-1: SerialNumber: 000388C9B3B5XXXX
> [ 2503.063745] r8152-cfgselector 2-1: reset SuperSpeed USB device number 3 using xhci_hcd
> [ 2503.123876] r8152 2-1:1.0: Requesting firmware: rtl_nic/rtl8159-1.fw
> [ 2503.126267] r8152 2-1:1.0: PHY firmware installed 0 to be loaded: 20
> [ 2503.156265] r8152 2-1:1.0: load rtl8159-1 v1 2026/01/01 successfully
> [ 2503.270729] r8152 2-1:1.0 eth0: v1.12.13
> [ 2503.289349] r8152 2-1:1.0 enx88c9b3b5xxxx: renamed from eth0
> [ 2507.777055] r8152 2-1:1.0 enx88c9b3b5xxxx: carrier on
>
> The RTL8159 adapter was tested against an AQC107 PCIe-card supporting
> 10GBit/s and an RTL8157 5Gbit USB-Ethernet adapter supporting 5GBit/s for
> performance, link speed and EEE negotiation. Using USB3.2 Gen 2 (20GBit) with
> the RTL8159 USB adapter and running iperf3 against the AQC107 PCIe
> card resulted in 8.96 Gbits/sec transfer speed.
>
> The code is based on the out-of-tree r8152 driver published by Realtek under
> the GPL.
>
> The RTL8159 requires firmware for the PHY in order to achieve a 10GBit link
> speed. Without firmware, only 5GBit were achieved. The firmware can be
> extracted from the out-of-tree r8152 driver-code where it is stored in the
> ram17 u8-array. Code is added to use the existing firmware upload mechanism
> of the driver for the RTL8157/9 PHY firmware code. The firmware will be
> submitted separately to linux-firmware.
>
> Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Tested-by: Aleksander Jan Bajkowski <olek2@wp.pl>
> ---
> Changes in v2:
> - Correct formatting of comments
> - Order case statement values correctly
> - Add error message when backup-restore fails
> - Correct commit message of support for firmware upload
> - Link to v1: https://lore.kernel.org/r/20260428-rtl8159_net_next-v1-0-52d03927b46f@birger-koblitz.de
>
> ---
> Birger Koblitz (4):
> r8152: Add support for 10Gbit Link Speeds and EEE
> r8152: Add support for the RTL8159 chip
> r8152: Add irq mitigation for RTL8157/9
> r8152: Add firmware upload capability for RTL8157/RTL8159
>
> drivers/net/usb/r8152.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 324 insertions(+), 12 deletions(-)
> ---
> base-commit: 35c2c39832e569449b9192fa1afbbc4c66227af7
> change-id: 20260427-rtl8159_net_next-4f778a614fa7
>
> Best regards,
^ permalink raw reply
* Re: [PATCH net v2 2/4] net: macb: drop in-flight Tx SKBs on close
From: Nicolai Buchwitz @ 2026-04-29 22:14 UTC (permalink / raw)
To: Théo Lebrun
Cc: Nicolas Ferre, Claudiu Beznea, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Haavard Skinnemoen,
Jeff Garzik, Paolo Valerio, Conor Dooley, netdev, linux-kernel,
Vladimir Kondratiev, Gregory CLEMENT, Benoît Monin,
Tawfik Bayouk, Thomas Petazzoni, Maxime Chevallier, stable
In-Reply-To: <DI5J5659IHRK.2VDGEBK93OQJP@bootlin.com>
Hi Théo
On 29.4.2026 11:26, Théo Lebrun wrote:
> [...]
>> Side note, not blocking: macb_close() doesn't cancel tx_error_task,
>> so the workqueue handler can race with this loop on tx_skb[]. The
>> exposure is pre-existing, but maybe worth a follow-up adding
>> cancel_work_sync() between napi_disable() and macb_free_consistent().
>
> Yes, noticed that while working on the context swapping series [0].
> The goal here is to improve MACB piecewise, so I won't take that on in
> the current series.
>
> [0]:
> https://lore.kernel.org/all/90f843aa3940bdbabadddce27314c1f1@tipi-net.de/t/#mda18f759c27a4d833084b23605463994632d97e3
> (and the two replies)
Yes, flagged it on that series too. Wasn't my intention to fold it
into this one either, just wanted to make sure it doesn't get lost
with so much in flight on macb right now, which I really appreciate!
> [...]
Cheers,
Nicolai
^ permalink raw reply
* [PATCH net-next v6 0/6] net: mana: Per-vPort EQ and MSI-X interrupt management
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
This series adds per-vPort Event Queue (EQ) allocation and MSI-X interrupt
management for the MANA driver. Previously, all vPorts shared a single set
of EQs. This change enables dedicated EQs per vPort with support for both
dedicated and shared MSI-X vector allocation modes.
Patch 1 moves EQ ownership from mana_context to per-vPort mana_port_context
and exports create/destroy functions for the RDMA driver. Also adds EQ
create/destroy calls to mana_ib_cfg_vport/uncfg_vport so RDMA vPorts get
their own EQs.
Patch 2 adds device capability queries to determine whether MSI-X vectors
should be dedicated per-vPort or shared. When the number of available MSI-X
vectors is insufficient for dedicated allocation, the driver enables sharing
mode with bitmap-based vector assignment.
Patch 3 introduces the GIC (GDMA IRQ Context) abstraction with reference
counting, allowing multiple EQs to safely share a single MSI-X vector.
Patch 4 converts the global EQ allocation in probe/resume to use the new
GIC functions.
Patch 5 adds per-vPort GIC lifecycle management, calling get/put on each
EQ creation and destruction during vPort open/close.
Patch 6 extends the same GIC lifecycle management to the RDMA driver's EQ
allocation path.
Changes in v6:
- Rebased on net-next/main (v7.1-rc1)
Changes in v5:
- Rebased on net-next/main
Changes in v4:
- Rebased on net-next/main 7.0-rc4
- Patch 2: Use MANA_DEF_NUM_QUEUES instead of hardcoded 16 for
max_num_queues clamping
- Patch 3: Track dyn_msix in GIC context instead of re-checking
pci_msix_can_alloc_dyn() on each call; improved remove_irqs iteration
to skip unallocated entries
Changes in v3:
- Rebased on net-next/main
- Patch 1: Added NULL check for mpc->eqs in mana_ib_create_qp_rss() to
prevent NULL pointer dereference when RSS QP is created before a raw QP
has configured the vport and allocated EQs
Changes in v2:
- Rebased on net-next/main (adapted to kzalloc_objs/kzalloc_obj macros,
new GDMA_DRV_CAP_FLAG definitions)
- Patch 2: Fixed misleading comment for max_num_queues vs
max_num_queues_vport in gdma.h
- Patch 3: Fixed spelling typo in gdma_main.c ("difference" -> "different")
Long Li (6):
net: mana: Create separate EQs for each vPort
net: mana: Query device capabilities and configure MSI-X sharing for
EQs
net: mana: Introduce GIC context with refcounting for interrupt
management
net: mana: Use GIC functions to allocate global EQs
net: mana: Allocate interrupt context for each EQ when creating vPort
RDMA/mana_ib: Allocate interrupt contexts on EQs
drivers/infiniband/hw/mana/main.c | 47 ++-
drivers/infiniband/hw/mana/qp.c | 16 +-
.../net/ethernet/microsoft/mana/gdma_main.c | 307 +++++++++++++-----
drivers/net/ethernet/microsoft/mana/mana_en.c | 163 ++++++----
include/net/mana/gdma.h | 32 +-
include/net/mana/mana.h | 7 +-
6 files changed, 416 insertions(+), 156 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH net-next v6 1/6] net: mana: Create separate EQs for each vPort
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
In-Reply-To: <20260429221625.1841150-1-longli@microsoft.com>
To prepare for assigning vPorts to dedicated MSI-X vectors, remove EQ
sharing among the vPorts and create dedicated EQs for each vPort.
Move the EQ definition from struct mana_context to struct mana_port_context
and update related support functions. Export mana_create_eq() and
mana_destroy_eq() for use by the MANA RDMA driver.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v3:
- Added NULL check for mpc->eqs in mana_ib_create_qp_rss()
drivers/infiniband/hw/mana/main.c | 14 ++-
drivers/infiniband/hw/mana/qp.c | 16 ++-
drivers/net/ethernet/microsoft/mana/mana_en.c | 110 ++++++++++--------
include/net/mana/mana.h | 7 +-
4 files changed, 94 insertions(+), 53 deletions(-)
diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c
index ac5e75dd3494..60cc02e4ad10 100644
--- a/drivers/infiniband/hw/mana/main.c
+++ b/drivers/infiniband/hw/mana/main.c
@@ -20,8 +20,10 @@ void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd,
pd->vport_use_count--;
WARN_ON(pd->vport_use_count < 0);
- if (!pd->vport_use_count)
+ if (!pd->vport_use_count) {
+ mana_destroy_eq(mpc);
mana_uncfg_vport(mpc);
+ }
mutex_unlock(&pd->vport_mutex);
}
@@ -55,15 +57,21 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd,
return err;
}
- mutex_unlock(&pd->vport_mutex);
pd->tx_shortform_allowed = mpc->tx_shortform_allowed;
pd->tx_vp_offset = mpc->tx_vp_offset;
+ err = mana_create_eq(mpc);
+ if (err) {
+ mana_uncfg_vport(mpc);
+ pd->vport_use_count--;
+ }
+
+ mutex_unlock(&pd->vport_mutex);
ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n",
mpc->port_handle, pd->pdn, doorbell_id);
- return 0;
+ return err;
}
int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c
index 645581359cee..6f1043383e8c 100644
--- a/drivers/infiniband/hw/mana/qp.c
+++ b/drivers/infiniband/hw/mana/qp.c
@@ -168,7 +168,15 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
cq_spec.gdma_region = cq->queue.gdma_region;
cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE;
cq_spec.modr_ctx_id = 0;
- eq = &mpc->ac->eqs[cq->comp_vector];
+ /* EQs are created when a raw QP configures the vport.
+ * A raw QP must be created before creating rwq_ind_tbl.
+ */
+ if (!mpc->eqs) {
+ ret = -EINVAL;
+ i--;
+ goto fail;
+ }
+ eq = &mpc->eqs[cq->comp_vector % mpc->num_queues];
cq_spec.attached_eq = eq->eq->id;
ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ,
@@ -317,7 +325,11 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE;
cq_spec.modr_ctx_id = 0;
eq_vec = send_cq->comp_vector;
- eq = &mpc->ac->eqs[eq_vec];
+ if (!mpc->eqs) {
+ err = -EINVAL;
+ goto err_destroy_queue;
+ }
+ eq = &mpc->eqs[eq_vec % mpc->num_queues];
cq_spec.attached_eq = eq->eq->id;
err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec,
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index a654b3699c4c..6c709f8b875d 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1609,78 +1609,82 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type,
}
EXPORT_SYMBOL_NS(mana_destroy_wq_obj, "NET_MANA");
-static void mana_destroy_eq(struct mana_context *ac)
+void mana_destroy_eq(struct mana_port_context *apc)
{
+ struct mana_context *ac = apc->ac;
struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct gdma_queue *eq;
int i;
- if (!ac->eqs)
+ if (!apc->eqs)
return;
- debugfs_remove_recursive(ac->mana_eqs_debugfs);
- ac->mana_eqs_debugfs = NULL;
+ debugfs_remove_recursive(apc->mana_eqs_debugfs);
+ apc->mana_eqs_debugfs = NULL;
- for (i = 0; i < gc->max_num_queues; i++) {
- eq = ac->eqs[i].eq;
+ for (i = 0; i < apc->num_queues; i++) {
+ eq = apc->eqs[i].eq;
if (!eq)
continue;
mana_gd_destroy_queue(gc, eq);
}
- kfree(ac->eqs);
- ac->eqs = NULL;
+ kfree(apc->eqs);
+ apc->eqs = NULL;
}
+EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA");
-static void mana_create_eq_debugfs(struct mana_context *ac, int i)
+static void mana_create_eq_debugfs(struct mana_port_context *apc, int i)
{
- struct mana_eq eq = ac->eqs[i];
+ struct mana_eq eq = apc->eqs[i];
char eqnum[32];
sprintf(eqnum, "eq%d", i);
- eq.mana_eq_debugfs = debugfs_create_dir(eqnum, ac->mana_eqs_debugfs);
+ eq.mana_eq_debugfs = debugfs_create_dir(eqnum, apc->mana_eqs_debugfs);
debugfs_create_u32("head", 0400, eq.mana_eq_debugfs, &eq.eq->head);
debugfs_create_u32("tail", 0400, eq.mana_eq_debugfs, &eq.eq->tail);
debugfs_create_file("eq_dump", 0400, eq.mana_eq_debugfs, eq.eq, &mana_dbg_q_fops);
}
-static int mana_create_eq(struct mana_context *ac)
+int mana_create_eq(struct mana_port_context *apc)
{
- struct gdma_dev *gd = ac->gdma_dev;
+ struct gdma_dev *gd = apc->ac->gdma_dev;
struct gdma_context *gc = gd->gdma_context;
struct gdma_queue_spec spec = {};
int err;
int i;
- ac->eqs = kzalloc_objs(struct mana_eq, gc->max_num_queues);
- if (!ac->eqs)
+ WARN_ON(apc->eqs);
+ apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues);
+ if (!apc->eqs)
return -ENOMEM;
spec.type = GDMA_EQ;
spec.monitor_avl_buf = false;
spec.queue_size = EQ_SIZE;
spec.eq.callback = NULL;
- spec.eq.context = ac->eqs;
+ spec.eq.context = apc->eqs;
spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE;
- ac->mana_eqs_debugfs = debugfs_create_dir("EQs", gc->mana_pci_debugfs);
+ apc->mana_eqs_debugfs = debugfs_create_dir("EQs", apc->mana_port_debugfs);
- for (i = 0; i < gc->max_num_queues; i++) {
+ for (i = 0; i < apc->num_queues; i++) {
spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
- err = mana_gd_create_mana_eq(gd, &spec, &ac->eqs[i].eq);
+ err = mana_gd_create_mana_eq(gd, &spec, &apc->eqs[i].eq);
if (err) {
dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err);
goto out;
}
- mana_create_eq_debugfs(ac, i);
+ mana_create_eq_debugfs(apc, i);
}
return 0;
out:
- mana_destroy_eq(ac);
+ mana_destroy_eq(apc);
return err;
}
+EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA");
static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq)
{
@@ -2434,7 +2438,7 @@ static int mana_create_txq(struct mana_port_context *apc,
spec.monitor_avl_buf = false;
spec.queue_size = cq_size;
spec.cq.callback = mana_schedule_napi;
- spec.cq.parent_eq = ac->eqs[i].eq;
+ spec.cq.parent_eq = apc->eqs[i].eq;
spec.cq.context = cq;
err = mana_gd_create_mana_wq_cq(gd, &spec, &cq->gdma_cq);
if (err)
@@ -2827,13 +2831,12 @@ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
static int mana_add_rx_queues(struct mana_port_context *apc,
struct net_device *ndev)
{
- struct mana_context *ac = apc->ac;
struct mana_rxq *rxq;
int err = 0;
int i;
for (i = 0; i < apc->num_queues; i++) {
- rxq = mana_create_rxq(apc, i, &ac->eqs[i], ndev);
+ rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
if (!rxq) {
err = -ENOMEM;
netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err);
@@ -2852,9 +2855,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
return err;
}
-static void mana_destroy_vport(struct mana_port_context *apc)
+static void mana_destroy_rxqs(struct mana_port_context *apc)
{
- struct gdma_dev *gd = apc->ac->gdma_dev;
struct mana_rxq *rxq;
u32 rxq_idx;
@@ -2866,8 +2868,12 @@ static void mana_destroy_vport(struct mana_port_context *apc)
mana_destroy_rxq(apc, rxq, true);
apc->rxqs[rxq_idx] = NULL;
}
+}
+
+static void mana_destroy_vport(struct mana_port_context *apc)
+{
+ struct gdma_dev *gd = apc->ac->gdma_dev;
- mana_destroy_txq(apc);
mana_uncfg_vport(apc);
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
@@ -2888,11 +2894,7 @@ static int mana_create_vport(struct mana_port_context *apc,
return err;
}
- err = mana_cfg_vport(apc, gd->pdid, gd->doorbell);
- if (err)
- return err;
-
- return mana_create_txq(apc, net);
+ return mana_cfg_vport(apc, gd->pdid, gd->doorbell);
}
static int mana_rss_table_alloc(struct mana_port_context *apc)
@@ -3178,21 +3180,36 @@ int mana_alloc_queues(struct net_device *ndev)
err = mana_create_vport(apc, ndev);
if (err) {
- netdev_err(ndev, "Failed to create vPort %u : %d\n", apc->port_idx, err);
+ netdev_err(ndev, "Failed to create vPort %u : %d\n",
+ apc->port_idx, err);
return err;
}
+ err = mana_create_eq(apc);
+ if (err) {
+ netdev_err(ndev, "Failed to create EQ on vPort %u: %d\n",
+ apc->port_idx, err);
+ goto destroy_vport;
+ }
+
+ err = mana_create_txq(apc, ndev);
+ if (err) {
+ netdev_err(ndev, "Failed to create TXQ on vPort %u: %d\n",
+ apc->port_idx, err);
+ goto destroy_eq;
+ }
+
err = netif_set_real_num_tx_queues(ndev, apc->num_queues);
if (err) {
netdev_err(ndev,
"netif_set_real_num_tx_queues () failed for ndev with num_queues %u : %d\n",
apc->num_queues, err);
- goto destroy_vport;
+ goto destroy_txq;
}
err = mana_add_rx_queues(apc, ndev);
if (err)
- goto destroy_vport;
+ goto destroy_rxq;
apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
@@ -3201,7 +3218,7 @@ int mana_alloc_queues(struct net_device *ndev)
netdev_err(ndev,
"netif_set_real_num_rx_queues () failed for ndev with num_queues %u : %d\n",
apc->num_queues, err);
- goto destroy_vport;
+ goto destroy_rxq;
}
mana_rss_table_init(apc);
@@ -3209,19 +3226,25 @@ int mana_alloc_queues(struct net_device *ndev)
err = mana_config_rss(apc, TRI_STATE_TRUE, true, true);
if (err) {
netdev_err(ndev, "Failed to configure RSS table: %d\n", err);
- goto destroy_vport;
+ goto destroy_rxq;
}
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) {
err = mana_pf_register_filter(apc);
if (err)
- goto destroy_vport;
+ goto destroy_rxq;
}
mana_chn_setxdp(apc, mana_xdp_get(apc));
return 0;
+destroy_rxq:
+ mana_destroy_rxqs(apc);
+destroy_txq:
+ mana_destroy_txq(apc);
+destroy_eq:
+ mana_destroy_eq(apc);
destroy_vport:
mana_destroy_vport(apc);
return err;
@@ -3326,6 +3349,9 @@ static int mana_dealloc_queues(struct net_device *ndev)
mana_fence_rqs(apc);
/* Even in err case, still need to cleanup the vPort */
+ mana_destroy_rxqs(apc);
+ mana_destroy_txq(apc);
+ mana_destroy_eq(apc);
mana_destroy_vport(apc);
return 0;
@@ -3646,12 +3672,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
- err = mana_create_eq(ac);
- if (err) {
- dev_err(dev, "Failed to create EQs: %d\n", err);
- goto out;
- }
-
err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
MANA_MICRO_VERSION, &num_ports, &bm_hostmode);
if (err)
@@ -3791,8 +3811,6 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
free_netdev(ndev);
}
- mana_destroy_eq(ac);
-
if (ac->per_port_queue_reset_wq) {
destroy_workqueue(ac->per_port_queue_reset_wq);
ac->per_port_queue_reset_wq = NULL;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 8f721cd4e4a7..2634e9135eed 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -480,8 +480,6 @@ struct mana_context {
u8 bm_hostmode;
struct mana_ethtool_hc_stats hc_stats;
- struct mana_eq *eqs;
- struct dentry *mana_eqs_debugfs;
struct workqueue_struct *per_port_queue_reset_wq;
/* Workqueue for querying hardware stats */
struct delayed_work gf_stats_work;
@@ -501,6 +499,9 @@ struct mana_port_context {
u8 mac_addr[ETH_ALEN];
+ struct mana_eq *eqs;
+ struct dentry *mana_eqs_debugfs;
+
enum TRI_STATE rss_state;
mana_handle_t default_rxobj;
@@ -1034,6 +1035,8 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type,
int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
u32 doorbell_pg_id);
void mana_uncfg_vport(struct mana_port_context *apc);
+int mana_create_eq(struct mana_port_context *apc);
+void mana_destroy_eq(struct mana_port_context *apc);
struct net_device *mana_get_primary_netdev(struct mana_context *ac,
u32 port_index,
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v6 2/6] net: mana: Query device capabilities and configure MSI-X sharing for EQs
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
In-Reply-To: <20260429221625.1841150-1-longli@microsoft.com>
When querying the device, adjust the max number of queues to allow
dedicated MSI-X vectors for each vPort. The number of queues per vPort
is clamped to no less than MANA_DEF_NUM_QUEUES. MSI-X sharing among
vPorts is disabled by default and is only enabled when there are not
enough MSI-X vectors for dedicated allocation.
Rename mana_query_device_cfg() to mana_gd_query_device_cfg() as it is
used at GDMA device probe time for querying device capabilities.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Use MANA_DEF_NUM_QUEUES instead of hardcoded 16 for max_num_queues
clamping
Changes in v2:
- Fixed misleading comment for max_num_queues vs max_num_queues_vport
.../net/ethernet/microsoft/mana/gdma_main.c | 66 ++++++++++++++++---
drivers/net/ethernet/microsoft/mana/mana_en.c | 36 +++++-----
include/net/mana/gdma.h | 13 +++-
3 files changed, 91 insertions(+), 24 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 098fbda0d128..b96859e0aec9 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -149,6 +149,9 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_query_max_resources_resp resp = {};
struct gdma_general_req req = {};
+ unsigned int max_num_queues;
+ u8 bm_hostmode;
+ u16 num_ports;
int err;
mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_MAX_RESOURCES,
@@ -194,6 +197,40 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
if (gc->max_num_queues > gc->num_msix_usable - 1)
gc->max_num_queues = gc->num_msix_usable - 1;
+ err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
+ MANA_MICRO_VERSION, &num_ports, &bm_hostmode);
+ if (err)
+ return err;
+
+ if (!num_ports)
+ return -EINVAL;
+
+ /*
+ * Adjust gc->max_num_queues returned from the SOC to allow dedicated
+ * MSIx for each vPort. Clamp to no less than MANA_DEF_NUM_QUEUES.
+ */
+ max_num_queues = (gc->num_msix_usable - 1) / num_ports;
+ max_num_queues = roundup_pow_of_two(max(max_num_queues, 1U));
+ if (max_num_queues < MANA_DEF_NUM_QUEUES)
+ max_num_queues = MANA_DEF_NUM_QUEUES;
+
+ /*
+ * Use dedicated MSIx for EQs whenever possible, use MSIx sharing for
+ * Ethernet EQs when (max_num_queues * num_ports > num_msix_usable - 1)
+ */
+ max_num_queues = min(gc->max_num_queues, max_num_queues);
+ if (max_num_queues * num_ports > gc->num_msix_usable - 1)
+ gc->msi_sharing = true;
+
+ /* If MSI is shared, use max allowed value */
+ if (gc->msi_sharing)
+ gc->max_num_queues_vport = min(gc->num_msix_usable - 1, gc->max_num_queues);
+ else
+ gc->max_num_queues_vport = max_num_queues;
+
+ dev_info(gc->dev, "MSI sharing mode %d max queues %d\n",
+ gc->msi_sharing, gc->max_num_queues);
+
return 0;
}
@@ -1856,6 +1893,7 @@ static int mana_gd_setup_hwc_irqs(struct pci_dev *pdev)
/* Need 1 interrupt for HWC */
max_irqs = min(num_online_cpus(), MANA_MAX_NUM_QUEUES) + 1;
min_irqs = 2;
+ gc->msi_sharing = true;
}
nvec = pci_alloc_irq_vectors(pdev, min_irqs, max_irqs, PCI_IRQ_MSIX);
@@ -1934,6 +1972,8 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev)
pci_free_irq_vectors(pdev);
+ bitmap_free(gc->msi_bitmap);
+ gc->msi_bitmap = NULL;
gc->max_num_msix = 0;
gc->num_msix_usable = 0;
}
@@ -1968,20 +2008,30 @@ static int mana_gd_setup(struct pci_dev *pdev)
if (err)
goto destroy_hwc;
- err = mana_gd_query_max_resources(pdev);
+ err = mana_gd_detect_devices(pdev);
if (err)
goto destroy_hwc;
- err = mana_gd_setup_remaining_irqs(pdev);
- if (err) {
- dev_err(gc->dev, "Failed to setup remaining IRQs: %d", err);
- goto destroy_hwc;
- }
-
- err = mana_gd_detect_devices(pdev);
+ err = mana_gd_query_max_resources(pdev);
if (err)
goto destroy_hwc;
+ if (!gc->msi_sharing) {
+ gc->msi_bitmap = bitmap_zalloc(gc->num_msix_usable, GFP_KERNEL);
+ if (!gc->msi_bitmap) {
+ err = -ENOMEM;
+ goto destroy_hwc;
+ }
+ /* Set bit for HWC */
+ set_bit(0, gc->msi_bitmap);
+ } else {
+ err = mana_gd_setup_remaining_irqs(pdev);
+ if (err) {
+ dev_err(gc->dev, "Failed to setup remaining IRQs: %d", err);
+ goto destroy_hwc;
+ }
+ }
+
dev_dbg(&pdev->dev, "mana gdma setup successful\n");
return 0;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 6c709f8b875d..e7f734994b5e 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1007,10 +1007,9 @@ static int mana_init_port_context(struct mana_port_context *apc)
return !apc->rxqs ? -ENOMEM : 0;
}
-static int mana_send_request(struct mana_context *ac, void *in_buf,
- u32 in_len, void *out_buf, u32 out_len)
+static int gdma_mana_send_request(struct gdma_context *gc, void *in_buf,
+ u32 in_len, void *out_buf, u32 out_len)
{
- struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct gdma_resp_hdr *resp = out_buf;
struct gdma_req_hdr *req = in_buf;
struct device *dev = gc->dev;
@@ -1044,6 +1043,14 @@ static int mana_send_request(struct mana_context *ac, void *in_buf,
return 0;
}
+static int mana_send_request(struct mana_context *ac, void *in_buf,
+ u32 in_len, void *out_buf, u32 out_len)
+{
+ struct gdma_context *gc = ac->gdma_dev->gdma_context;
+
+ return gdma_mana_send_request(gc, in_buf, in_len, out_buf, out_len);
+}
+
static int mana_verify_resp_hdr(const struct gdma_resp_hdr *resp_hdr,
const enum mana_command_code expected_code,
const u32 min_size)
@@ -1177,11 +1184,10 @@ static void mana_pf_deregister_filter(struct mana_port_context *apc)
err, resp.hdr.status);
}
-static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
- u32 proto_minor_ver, u32 proto_micro_ver,
- u16 *max_num_vports, u8 *bm_hostmode)
+int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
+ u32 proto_minor_ver, u32 proto_micro_ver,
+ u16 *max_num_vports, u8 *bm_hostmode)
{
- struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct mana_query_device_cfg_resp resp = {};
struct mana_query_device_cfg_req req = {};
struct device *dev = gc->dev;
@@ -1196,7 +1202,7 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
req.proto_minor_ver = proto_minor_ver;
req.proto_micro_ver = proto_micro_ver;
- err = mana_send_request(ac, &req, sizeof(req), &resp, sizeof(resp));
+ err = gdma_mana_send_request(gc, &req, sizeof(req), &resp, sizeof(resp));
if (err) {
dev_err(dev, "Failed to query config: %d", err);
return err;
@@ -1230,8 +1236,6 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
else
*bm_hostmode = 0;
- debugfs_create_u16("adapter-MTU", 0400, gc->mana_pci_debugfs, &gc->adapter_mtu);
-
return 0;
}
@@ -3397,7 +3401,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
int err;
ndev = alloc_etherdev_mq(sizeof(struct mana_port_context),
- gc->max_num_queues);
+ gc->max_num_queues_vport);
if (!ndev)
return -ENOMEM;
@@ -3406,9 +3410,9 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
apc = netdev_priv(ndev);
apc->ac = ac;
apc->ndev = ndev;
- apc->max_queues = gc->max_num_queues;
+ apc->max_queues = gc->max_num_queues_vport;
/* Use MANA_DEF_NUM_QUEUES as default, still honoring the HW limit */
- apc->num_queues = min(gc->max_num_queues, MANA_DEF_NUM_QUEUES);
+ apc->num_queues = min(gc->max_num_queues_vport, MANA_DEF_NUM_QUEUES);
apc->tx_queue_size = DEF_TX_BUFFERS_PER_QUEUE;
apc->rx_queue_size = DEF_RX_BUFFERS_PER_QUEUE;
apc->port_handle = INVALID_MANA_HANDLE;
@@ -3672,13 +3676,15 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
- err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
- MANA_MICRO_VERSION, &num_ports, &bm_hostmode);
+ err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
+ MANA_MICRO_VERSION, &num_ports, &bm_hostmode);
if (err)
goto out;
ac->bm_hostmode = bm_hostmode;
+ debugfs_create_u16("adapter-MTU", 0400, gc->mana_pci_debugfs, &gc->adapter_mtu);
+
if (!resuming) {
ac->num_ports = num_ports;
} else {
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 6d836060976a..9c05b1e15c3e 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -399,8 +399,10 @@ struct gdma_context {
struct device *dev;
struct dentry *mana_pci_debugfs;
- /* Per-vPort max number of queues */
+ /* Hardware max number of queues */
unsigned int max_num_queues;
+ /* Per-vPort max number of queues */
+ unsigned int max_num_queues_vport;
unsigned int max_num_msix;
unsigned int num_msix_usable;
struct xarray irq_contexts;
@@ -446,6 +448,12 @@ struct gdma_context {
struct workqueue_struct *service_wq;
unsigned long flags;
+
+ /* Indicate if this device is sharing MSI for EQs on MANA */
+ bool msi_sharing;
+
+ /* Bitmap tracks where MSI is allocated when it is not shared for EQs */
+ unsigned long *msi_bitmap;
};
static inline bool mana_gd_is_mana(struct gdma_dev *gd)
@@ -1018,4 +1026,7 @@ int mana_gd_resume(struct pci_dev *pdev);
bool mana_need_log(struct gdma_context *gc, int err);
+int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
+ u32 proto_minor_ver, u32 proto_micro_ver,
+ u16 *max_num_vports, u8 *bm_hostmode);
#endif /* _GDMA_H */
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v6 3/6] net: mana: Introduce GIC context with refcounting for interrupt management
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
In-Reply-To: <20260429221625.1841150-1-longli@microsoft.com>
To allow Ethernet EQs to use dedicated or shared MSI-X vectors and RDMA
EQs to share the same MSI-X, introduce a GIC (GDMA IRQ Context) with
reference counting. This allows the driver to create an interrupt context
on an assigned or unassigned MSI-X vector and share it across multiple
EQ consumers.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Track dyn_msix in GIC context instead of re-checking
pci_msix_can_alloc_dyn() on each call; improved remove_irqs
iteration to skip unallocated entries
Changes in v2:
- Fixed spelling typo ("difference" -> "different")
.../net/ethernet/microsoft/mana/gdma_main.c | 159 ++++++++++++++++++
include/net/mana/gdma.h | 11 ++
2 files changed, 170 insertions(+)
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index b96859e0aec9..3b6711355002 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1612,6 +1612,164 @@ static irqreturn_t mana_gd_intr(int irq, void *arg)
return IRQ_HANDLED;
}
+void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi)
+{
+ struct pci_dev *dev = to_pci_dev(gc->dev);
+ struct msi_map irq_map;
+ struct gdma_irq_context *gic;
+ int irq;
+
+ mutex_lock(&gc->gic_mutex);
+
+ gic = xa_load(&gc->irq_contexts, msi);
+ if (WARN_ON(!gic)) {
+ mutex_unlock(&gc->gic_mutex);
+ return;
+ }
+
+ if (use_msi_bitmap)
+ gic->bitmap_refs--;
+
+ if (use_msi_bitmap && gic->bitmap_refs == 0)
+ clear_bit(msi, gc->msi_bitmap);
+
+ if (!refcount_dec_and_test(&gic->refcount))
+ goto out;
+
+ irq = pci_irq_vector(dev, msi);
+
+ irq_update_affinity_hint(irq, NULL);
+ free_irq(irq, gic);
+
+ if (gic->dyn_msix) {
+ irq_map.virq = irq;
+ irq_map.index = msi;
+ pci_msix_free_irq(dev, irq_map);
+ }
+
+ xa_erase(&gc->irq_contexts, msi);
+ kfree(gic);
+
+out:
+ mutex_unlock(&gc->gic_mutex);
+}
+EXPORT_SYMBOL_NS(mana_gd_put_gic, "NET_MANA");
+
+/*
+ * Get a GIC (GDMA IRQ Context) on a MSI vector
+ * a MSI can be shared between different EQs, this function supports setting
+ * up separate MSIs using a bitmap, or directly using the MSI index
+ *
+ * @use_msi_bitmap:
+ * True if MSI is assigned by this function on available slots from bitmap.
+ * False if MSI is passed from *msi_requested
+ */
+struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
+ bool use_msi_bitmap,
+ int *msi_requested)
+{
+ struct gdma_irq_context *gic;
+ struct pci_dev *dev = to_pci_dev(gc->dev);
+ struct msi_map irq_map = { };
+ int irq;
+ int msi;
+ int err;
+
+ mutex_lock(&gc->gic_mutex);
+
+ if (use_msi_bitmap) {
+ msi = find_first_zero_bit(gc->msi_bitmap, gc->num_msix_usable);
+ if (msi >= gc->num_msix_usable) {
+ dev_err(gc->dev, "No free MSI vectors available\n");
+ gic = NULL;
+ goto out;
+ }
+ *msi_requested = msi;
+ } else {
+ msi = *msi_requested;
+ }
+
+ gic = xa_load(&gc->irq_contexts, msi);
+ if (gic) {
+ refcount_inc(&gic->refcount);
+ if (use_msi_bitmap) {
+ gic->bitmap_refs++;
+ set_bit(msi, gc->msi_bitmap);
+ }
+ goto out;
+ }
+
+ irq = pci_irq_vector(dev, msi);
+ if (irq == -EINVAL) {
+ irq_map = pci_msix_alloc_irq_at(dev, msi, NULL);
+ if (!irq_map.virq) {
+ err = irq_map.index;
+ dev_err(gc->dev,
+ "Failed to alloc irq_map msi %d err %d\n",
+ msi, err);
+ gic = NULL;
+ goto out;
+ }
+ irq = irq_map.virq;
+ msi = irq_map.index;
+ }
+
+ gic = kzalloc(sizeof(*gic), GFP_KERNEL);
+ if (!gic) {
+ if (irq_map.virq)
+ pci_msix_free_irq(dev, irq_map);
+ goto out;
+ }
+
+ gic->handler = mana_gd_process_eq_events;
+ gic->msi = msi;
+ gic->irq = irq;
+ INIT_LIST_HEAD(&gic->eq_list);
+ spin_lock_init(&gic->lock);
+
+ if (!gic->msi)
+ snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
+ pci_name(dev));
+ else
+ snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_msi%d@pci:%s",
+ gic->msi, pci_name(dev));
+
+ err = request_irq(irq, mana_gd_intr, 0, gic->name, gic);
+ if (err) {
+ dev_err(gc->dev, "Failed to request irq %d %s\n",
+ irq, gic->name);
+ kfree(gic);
+ gic = NULL;
+ if (irq_map.virq)
+ pci_msix_free_irq(dev, irq_map);
+ goto out;
+ }
+
+ gic->dyn_msix = !!irq_map.virq;
+ refcount_set(&gic->refcount, 1);
+ gic->bitmap_refs = use_msi_bitmap ? 1 : 0;
+
+ err = xa_err(xa_store(&gc->irq_contexts, msi, gic, GFP_KERNEL));
+ if (err) {
+ dev_err(gc->dev, "Failed to store irq context for msi %d: %d\n",
+ msi, err);
+ free_irq(irq, gic);
+ kfree(gic);
+ gic = NULL;
+ if (irq_map.virq)
+ pci_msix_free_irq(dev, irq_map);
+ goto out;
+ }
+
+ if (use_msi_bitmap)
+ set_bit(msi, gc->msi_bitmap);
+
+out:
+ mutex_unlock(&gc->gic_mutex);
+ return gic;
+}
+EXPORT_SYMBOL_NS(mana_gd_get_gic, "NET_MANA");
+
int mana_gd_alloc_res_map(u32 res_avail, struct gdma_resource *r)
{
r->map = bitmap_zalloc(res_avail, GFP_KERNEL);
@@ -2101,6 +2259,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_region;
mutex_init(&gc->eq_test_event_mutex);
+ mutex_init(&gc->gic_mutex);
pci_set_drvdata(pdev, gc);
gc->bar0_pa = pci_resource_start(pdev, 0);
gc->bar0_size = pci_resource_len(pdev, 0);
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 9c05b1e15c3e..690208a26121 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -388,6 +388,11 @@ struct gdma_irq_context {
spinlock_t lock;
struct list_head eq_list;
char name[MANA_IRQ_NAME_SZ];
+ unsigned int msi;
+ unsigned int irq;
+ refcount_t refcount;
+ unsigned int bitmap_refs;
+ bool dyn_msix;
};
enum gdma_context_flags {
@@ -449,6 +454,9 @@ struct gdma_context {
unsigned long flags;
+ /* Protect access to GIC context */
+ struct mutex gic_mutex;
+
/* Indicate if this device is sharing MSI for EQs on MANA */
bool msi_sharing;
@@ -1026,6 +1034,9 @@ int mana_gd_resume(struct pci_dev *pdev);
bool mana_need_log(struct gdma_context *gc, int err);
+struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc, bool use_msi_bitmap,
+ int *msi_requested);
+void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi);
int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
u32 proto_minor_ver, u32 proto_micro_ver,
u16 *max_num_vports, u8 *bm_hostmode);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v6 4/6] net: mana: Use GIC functions to allocate global EQs
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
In-Reply-To: <20260429221625.1841150-1-longli@microsoft.com>
Replace the GDMA global interrupt setup code with the new GIC allocation
and release functions for managing interrupt contexts.
Signed-off-by: Long Li <longli@microsoft.com>
---
.../net/ethernet/microsoft/mana/gdma_main.c | 80 +++----------------
1 file changed, 10 insertions(+), 70 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 3b6711355002..ce433a68938f 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1885,30 +1885,13 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
* further used in irq_setup()
*/
for (i = 1; i <= nvec; i++) {
- gic = kzalloc_obj(*gic);
+ gic = mana_gd_get_gic(gc, false, &i);
if (!gic) {
err = -ENOMEM;
goto free_irq;
}
- gic->handler = mana_gd_process_eq_events;
- INIT_LIST_HEAD(&gic->eq_list);
- spin_lock_init(&gic->lock);
-
- snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s",
- i - 1, pci_name(pdev));
-
- /* one pci vector is already allocated for HWC */
- irqs[i - 1] = pci_irq_vector(pdev, i);
- if (irqs[i - 1] < 0) {
- err = irqs[i - 1];
- goto free_current_gic;
- }
-
- err = request_irq(irqs[i - 1], mana_gd_intr, 0, gic->name, gic);
- if (err)
- goto free_current_gic;
- xa_store(&gc->irq_contexts, i, gic, GFP_KERNEL);
+ irqs[i - 1] = gic->irq;
}
/*
@@ -1930,19 +1913,11 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
kfree(irqs);
return 0;
-free_current_gic:
- kfree(gic);
free_irq:
for (i -= 1; i > 0; i--) {
irq = pci_irq_vector(pdev, i);
- gic = xa_load(&gc->irq_contexts, i);
- if (WARN_ON(!gic))
- continue;
-
irq_update_affinity_hint(irq, NULL);
- free_irq(irq, gic);
- xa_erase(&gc->irq_contexts, i);
- kfree(gic);
+ mana_gd_put_gic(gc, false, i);
}
kfree(irqs);
return err;
@@ -1963,34 +1938,13 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
start_irqs = irqs;
for (i = 0; i < nvec; i++) {
- gic = kzalloc_obj(*gic);
+ gic = mana_gd_get_gic(gc, false, &i);
if (!gic) {
err = -ENOMEM;
goto free_irq;
}
- gic->handler = mana_gd_process_eq_events;
- INIT_LIST_HEAD(&gic->eq_list);
- spin_lock_init(&gic->lock);
-
- if (!i)
- snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
- pci_name(pdev));
- else
- snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s",
- i - 1, pci_name(pdev));
-
- irqs[i] = pci_irq_vector(pdev, i);
- if (irqs[i] < 0) {
- err = irqs[i];
- goto free_current_gic;
- }
-
- err = request_irq(irqs[i], mana_gd_intr, 0, gic->name, gic);
- if (err)
- goto free_current_gic;
-
- xa_store(&gc->irq_contexts, i, gic, GFP_KERNEL);
+ irqs[i] = gic->irq;
}
/* If number of IRQ is one extra than number of online CPUs,
@@ -2019,19 +1973,11 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
kfree(start_irqs);
return 0;
-free_current_gic:
- kfree(gic);
free_irq:
for (i -= 1; i >= 0; i--) {
irq = pci_irq_vector(pdev, i);
- gic = xa_load(&gc->irq_contexts, i);
- if (WARN_ON(!gic))
- continue;
-
irq_update_affinity_hint(irq, NULL);
- free_irq(irq, gic);
- xa_erase(&gc->irq_contexts, i);
- kfree(gic);
+ mana_gd_put_gic(gc, false, i);
}
kfree(start_irqs);
@@ -2106,26 +2052,20 @@ static int mana_gd_setup_remaining_irqs(struct pci_dev *pdev)
static void mana_gd_remove_irqs(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
- struct gdma_irq_context *gic;
int irq, i;
if (gc->max_num_msix < 1)
return;
for (i = 0; i < gc->max_num_msix; i++) {
- irq = pci_irq_vector(pdev, i);
- if (irq < 0)
- continue;
-
- gic = xa_load(&gc->irq_contexts, i);
- if (WARN_ON(!gic))
+ if (!xa_load(&gc->irq_contexts, i))
continue;
/* Need to clear the hint before free_irq */
+ irq = pci_irq_vector(pdev, i);
irq_update_affinity_hint(irq, NULL);
- free_irq(irq, gic);
- xa_erase(&gc->irq_contexts, i);
- kfree(gic);
+
+ mana_gd_put_gic(gc, false, i);
}
pci_free_irq_vectors(pdev);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v6 5/6] net: mana: Allocate interrupt context for each EQ when creating vPort
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
In-Reply-To: <20260429221625.1841150-1-longli@microsoft.com>
Use GIC functions to create a dedicated interrupt context or acquire a
shared interrupt context for each EQ when setting up a vPort.
Signed-off-by: Long Li <longli@microsoft.com>
---
drivers/net/ethernet/microsoft/mana/gdma_main.c | 2 +-
drivers/net/ethernet/microsoft/mana/mana_en.c | 17 ++++++++++++++++-
include/net/mana/gdma.h | 1 +
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index ce433a68938f..ccecf2adcfe6 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -851,7 +851,6 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue)
}
spin_unlock_irqrestore(&gic->lock, flags);
- queue->eq.msix_index = INVALID_PCI_MSIX_INDEX;
synchronize_rcu();
}
@@ -966,6 +965,7 @@ static int mana_gd_create_eq(struct gdma_dev *gd,
out:
dev_err(dev, "Failed to create EQ: %d\n", err);
mana_gd_destroy_eq(gc, false, queue);
+ queue->eq.msix_index = INVALID_PCI_MSIX_INDEX;
return err;
}
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index e7f734994b5e..15dcfb009ef0 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1619,6 +1619,7 @@ void mana_destroy_eq(struct mana_port_context *apc)
struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct gdma_queue *eq;
int i;
+ unsigned int msi;
if (!apc->eqs)
return;
@@ -1631,7 +1632,9 @@ void mana_destroy_eq(struct mana_port_context *apc)
if (!eq)
continue;
+ msi = eq->eq.msix_index;
mana_gd_destroy_queue(gc, eq);
+ mana_gd_put_gic(gc, !gc->msi_sharing, msi);
}
kfree(apc->eqs);
@@ -1648,6 +1651,7 @@ static void mana_create_eq_debugfs(struct mana_port_context *apc, int i)
eq.mana_eq_debugfs = debugfs_create_dir(eqnum, apc->mana_eqs_debugfs);
debugfs_create_u32("head", 0400, eq.mana_eq_debugfs, &eq.eq->head);
debugfs_create_u32("tail", 0400, eq.mana_eq_debugfs, &eq.eq->tail);
+ debugfs_create_u32("irq", 0400, eq.mana_eq_debugfs, &eq.eq->eq.irq);
debugfs_create_file("eq_dump", 0400, eq.mana_eq_debugfs, eq.eq, &mana_dbg_q_fops);
}
@@ -1658,6 +1662,7 @@ int mana_create_eq(struct mana_port_context *apc)
struct gdma_queue_spec spec = {};
int err;
int i;
+ struct gdma_irq_context *gic;
WARN_ON(apc->eqs);
apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues);
@@ -1674,12 +1679,22 @@ int mana_create_eq(struct mana_port_context *apc)
apc->mana_eqs_debugfs = debugfs_create_dir("EQs", apc->mana_port_debugfs);
for (i = 0; i < apc->num_queues; i++) {
- spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
+ if (gc->msi_sharing)
+ spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
+
+ gic = mana_gd_get_gic(gc, !gc->msi_sharing, &spec.eq.msix_index);
+ if (!gic) {
+ err = -ENOMEM;
+ goto out;
+ }
+
err = mana_gd_create_mana_eq(gd, &spec, &apc->eqs[i].eq);
if (err) {
dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err);
+ mana_gd_put_gic(gc, !gc->msi_sharing, spec.eq.msix_index);
goto out;
}
+ apc->eqs[i].eq->eq.irq = gic->irq;
mana_create_eq_debugfs(apc, i);
}
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 690208a26121..240d7f1c0733 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -342,6 +342,7 @@ struct gdma_queue {
void *context;
unsigned int msix_index;
+ unsigned int irq;
u32 log2_throttle_limit;
} eq;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v6 6/6] RDMA/mana_ib: Allocate interrupt contexts on EQs
From: Long Li @ 2026-04-29 22:16 UTC (permalink / raw)
To: Long Li, Konstantin Taranov, Jakub Kicinski, David S . Miller,
Paolo Abeni, Eric Dumazet, Andrew Lunn, Jason Gunthorpe,
Leon Romanovsky, Haiyang Zhang, K . Y . Srinivasan, Wei Liu,
Dexuan Cui
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel
In-Reply-To: <20260429221625.1841150-1-longli@microsoft.com>
Use the GIC functions to allocate interrupt contexts for RDMA EQs. These
interrupt contexts may be shared with Ethernet EQs when MSI-X vectors
are limited.
The driver now supports allocating dedicated MSI-X for each EQ. Indicate
this capability through driver capability bits.
Signed-off-by: Long Li <longli@microsoft.com>
---
drivers/infiniband/hw/mana/main.c | 33 ++++++++++++++++++++++++++-----
include/net/mana/gdma.h | 7 +++++--
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c
index 60cc02e4ad10..2267a73f0d6e 100644
--- a/drivers/infiniband/hw/mana/main.c
+++ b/drivers/infiniband/hw/mana/main.c
@@ -748,6 +748,7 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev)
{
struct gdma_context *gc = mdev_to_gc(mdev);
struct gdma_queue_spec spec = {};
+ struct gdma_irq_context *gic;
int err, i;
spec.type = GDMA_EQ;
@@ -758,9 +759,15 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev)
spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE;
spec.eq.msix_index = 0;
+ gic = mana_gd_get_gic(gc, false, &spec.eq.msix_index);
+ if (!gic)
+ return -ENOMEM;
+
err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->fatal_err_eq);
- if (err)
+ if (err) {
+ mana_gd_put_gic(gc, false, 0);
return err;
+ }
mdev->eqs = kzalloc_objs(struct gdma_queue *,
mdev->ib_dev.num_comp_vectors);
@@ -771,31 +778,47 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev)
spec.eq.callback = NULL;
for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) {
spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
+
+ gic = mana_gd_get_gic(gc, false, &spec.eq.msix_index);
+ if (!gic) {
+ err = -ENOMEM;
+ goto destroy_eqs;
+ }
+
err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->eqs[i]);
- if (err)
+ if (err) {
+ mana_gd_put_gic(gc, false, spec.eq.msix_index);
goto destroy_eqs;
+ }
}
return 0;
destroy_eqs:
- while (i-- > 0)
+ while (i-- > 0) {
mana_gd_destroy_queue(gc, mdev->eqs[i]);
+ mana_gd_put_gic(gc, false, (i + 1) % gc->num_msix_usable);
+ }
kfree(mdev->eqs);
destroy_fatal_eq:
mana_gd_destroy_queue(gc, mdev->fatal_err_eq);
+ mana_gd_put_gic(gc, false, 0);
return err;
}
void mana_ib_destroy_eqs(struct mana_ib_dev *mdev)
{
struct gdma_context *gc = mdev_to_gc(mdev);
- int i;
+ int i, msi;
mana_gd_destroy_queue(gc, mdev->fatal_err_eq);
+ mana_gd_put_gic(gc, false, 0);
- for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++)
+ for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) {
mana_gd_destroy_queue(gc, mdev->eqs[i]);
+ msi = (i + 1) % gc->num_msix_usable;
+ mana_gd_put_gic(gc, false, msi);
+ }
kfree(mdev->eqs);
}
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 240d7f1c0733..12502b1b7be1 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -615,6 +615,7 @@ enum {
#define GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG BIT(3)
#define GDMA_DRV_CAP_FLAG_1_GDMA_PAGES_4MB_1GB_2GB BIT(4)
#define GDMA_DRV_CAP_FLAG_1_VARIABLE_INDIRECTION_TABLE_SUPPORT BIT(5)
+#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6)
/* Driver can handle holes (zeros) in the device list */
#define GDMA_DRV_CAP_FLAG_1_DEV_LIST_HOLES_SUP BIT(11)
@@ -631,7 +632,8 @@ enum {
/* Driver detects stalled send queues and recovers them */
#define GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY BIT(18)
-#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6)
+/* Driver supports separate EQ/MSIs for each vPort */
+#define GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT BIT(19)
/* Driver supports linearizing the skb when num_sge exceeds hardware limit */
#define GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE BIT(20)
@@ -659,7 +661,8 @@ enum {
GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE | \
GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY | \
- GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY)
+ GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY | \
+ GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT)
#define GDMA_DRV_CAP_FLAGS2 0
--
2.43.0
^ permalink raw reply related
* [PATCH net 0/7] net: tls: fix a few random bugs
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski
Fix a few random bugs, from external reports and my local scan
with various AI tools. Mostly corner cases in code which I don't
think TLS maintainers would consider "battle tested".
Jakub Kicinski (7):
net: tls: fix silent data drop under pipe back-pressure
selftests: tls: add test for data loss on small pipe
net: tls: fix page pin leak on sendpage_ok() failure
net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg
ring
selftests: bpf: cover wrapped sk_msg ring chaining in ktls TX path
net: tls: fix use-after-free in tls_sw_sendmsg_locked after bpf
verdict
selftests: bpf: cover tls_sw_sendmsg UAF after bpf_exec_tx_verdict
split
net/tls/tls_device.c | 2 +
net/tls/tls_sw.c | 17 ++-
.../selftests/bpf/prog_tests/sockmap_ktls.c | 139 ++++++++++++++++++
.../selftests/bpf/progs/test_sockmap_ktls.c | 10 ++
tools/testing/selftests/net/tls.c | 43 ++++++
5 files changed, 207 insertions(+), 4 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH net 1/7] net: tls: fix silent data drop under pipe back-pressure
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
tls_sw_splice_read() uses len when advancing rxm->offset / rxm->full_len
after skb_splice_bits(), rather than copied (the actual number of bytes
successfully spliced into the pipe). When the destination pipe cannot
accept all the requested bytes, splice_to_pipe() returns fewer bytes
than len, and 'len - copied' of data is effectively skipped over.
Fixes: e062fe99cccd ("tls: splice_read: fix accessing pre-processed records")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: john.fastabend@gmail.com
CC: sd@queasysnail.net
---
net/tls/tls_sw.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 798243eabb1f..2590e855f6a5 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2317,9 +2317,9 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
if (copied < 0)
goto splice_requeue;
- if (chunk < rxm->full_len) {
- rxm->offset += len;
- rxm->full_len -= len;
+ if (copied < rxm->full_len) {
+ rxm->offset += copied;
+ rxm->full_len -= copied;
goto splice_requeue;
}
--
2.54.0
^ permalink raw reply related
* [PATCH net 2/7] selftests: tls: add test for data loss on small pipe
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski, shuah
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
Add selftest for data loss on short splice.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: john.fastabend@gmail.com
CC: sd@queasysnail.net
CC: shuah@kernel.org
CC: linux-kselftest@vger.kernel.org
---
tools/testing/selftests/net/tls.c | 43 +++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 9e2ccea13d70..30a236b8e9f7 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -946,6 +946,49 @@ TEST_F(tls, peek_and_splice)
EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
}
+TEST_F(tls, splice_to_pipe_small)
+{
+ int send_len = TLS_PAYLOAD_MAX_LEN;
+ char mem_send[TLS_PAYLOAD_MAX_LEN];
+ char mem_recv[TLS_PAYLOAD_MAX_LEN];
+ size_t total = 0;
+ int p[2];
+
+ memrnd(mem_send, sizeof(mem_send));
+
+ ASSERT_GE(pipe(p), 0);
+
+ /* Shrink pipe to 1 page (typically 4096 bytes) to force multiple
+ * splice iterations for a 16384-byte TLS record.
+ */
+ EXPECT_GE(fcntl(p[1], F_SETPIPE_SZ, 4096), 4096);
+
+ EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+
+ while (total < (size_t)send_len) {
+ ssize_t spliced, drained;
+
+ spliced = splice(self->cfd, NULL, p[1], NULL,
+ send_len - total, 0);
+ EXPECT_GT(spliced, 0);
+ if (spliced <= 0)
+ break;
+
+ drained = read(p[0], mem_recv + total, spliced);
+ EXPECT_EQ(drained, spliced);
+ if (drained <= 0)
+ break;
+
+ total += drained;
+ }
+
+ EXPECT_EQ(total, (size_t)send_len);
+ EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+
+ close(p[0]);
+ close(p[1]);
+}
+
#define MAX_FRAGS 48
TEST_F(tls, splice_short)
{
--
2.54.0
^ permalink raw reply related
* [PATCH net 3/7] net: tls: fix page pin leak on sendpage_ok() failure
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski, dhowells
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
When iov_iter_extract_pages() is called on a user-backed iterator, it
pins the extracted pages via pin_user_pages_fast(). If the subsequent
sendpage_ok() check fails, both tls_sw_sendmsg_splice() and
tls_push_data() (device offload path) call iov_iter_revert() to unwind
the iterator position, but iov_iter_revert() only adjusts the cursor -
it does not release the page pin taken by the extract.
Fixes: 45e5be844ab6 ("tls/sw: Convert tls_sw_sendpage() to use MSG_SPLICE_PAGES")
Fixes: 24763c9c0980 ("tls/device: Support MSG_SPLICE_PAGES")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: john.fastabend@gmail.com
CC: sd@queasysnail.net
CC: dhowells@redhat.com
---
net/tls/tls_device.c | 2 ++
net/tls/tls_sw.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
index 741aef09bfd3..6aae8d04f976 100644
--- a/net/tls/tls_device.c
+++ b/net/tls/tls_device.c
@@ -508,6 +508,8 @@ static int tls_push_data(struct sock *sk,
copy = rc;
if (WARN_ON_ONCE(!sendpage_ok(zc_pfrag.page))) {
+ if (iov_iter_extract_will_pin(iter))
+ unpin_user_page(zc_pfrag.page);
iov_iter_revert(iter, copy);
rc = -EIO;
goto handle_error;
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 2590e855f6a5..906a1998c630 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1018,6 +1018,8 @@ static int tls_sw_sendmsg_splice(struct sock *sk, struct msghdr *msg,
return part ?: -EIO;
if (WARN_ON_ONCE(!sendpage_ok(page))) {
+ if (iov_iter_extract_will_pin(&msg->msg_iter))
+ unpin_user_page(page);
iov_iter_revert(&msg->msg_iter, part);
return -EIO;
}
--
2.54.0
^ permalink raw reply related
* [PATCH net 4/7] net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg ring
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski,
钱一铭, daniel, jonathan.lemon
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
When an sk_msg scatterlist ring wraps (sg.end < sg.start),
tls_push_record() chains the tail portion of the ring to the head
using sg_chain(). The entry count passed to sg_chain() determines
where the chain pointer is written: at prv[prv_nents - 1].
The current code uses MAX_SKB_FRAGS (17) as the ring size:
sg_chain(&msg_pl->sg.data[msg_pl->sg.start],
MAX_SKB_FRAGS - msg_pl->sg.start + 1,
msg_pl->sg.data);
This places the chain pointer at data[start + (MAX_SKB_FRAGS - start
+ 1) - 1] = data[MAX_SKB_FRAGS] = data[17]. However, since commit
031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") expanded
the ring from MAX_MSG_FRAGS to NR_MSG_FRAG_IDS (18) positions,
data[17] is a valid ring slot that can hold live scatterlist entries.
The chain pointer must land at data[NR_MSG_FRAG_IDS] (index 18), the
reserved chaining slot.
Every other wrapped-ring arithmetic operation in the sk_msg subsystem
(sk_msg_iter_dist, sk_msg_iter_var_next, sk_msg_iter_var_prev,
bpf_msg_pull_data) correctly uses NR_MSG_FRAG_IDS as the ring modulus.
This sg_chain call is the sole remaining use of MAX_SKB_FRAGS for
ring-modulus arithmetic and was introduced after the ring expansion.
Reported-by: 钱一铭 <yimingqian591@gmail.com>
Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: john.fastabend@gmail.com
CC: sd@queasysnail.net
CC: daniel@iogearbox.net
CC: jonathan.lemon@gmail.com
CC: bpf@vger.kernel.org
---
net/tls/tls_sw.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 906a1998c630..600e13effaab 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -802,7 +802,7 @@ static int tls_push_record(struct sock *sk, int flags,
if (msg_pl->sg.end < msg_pl->sg.start) {
sg_chain(&msg_pl->sg.data[msg_pl->sg.start],
- MAX_SKB_FRAGS - msg_pl->sg.start + 1,
+ NR_MSG_FRAG_IDS - msg_pl->sg.start + 1,
msg_pl->sg.data);
}
--
2.54.0
^ permalink raw reply related
* [PATCH net 5/7] selftests: bpf: cover wrapped sk_msg ring chaining in ktls TX path
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski, ast, daniel,
andrii, martin.lau, eddyz87, memxor, song, yonghong.song, jolsa,
shuah, jiayuan.chen, isolodrai
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
Add a regression test for the off-by-one in tls_push_record() where
the sg_chain() entry count was MAX_SKB_FRAGS instead of NR_MSG_FRAG_IDS,
causing the chain pointer to overwrite a live ring slot when an sk_msg
scatterlist ring wrapped (sg.end < sg.start).
The new "tls tx wrapped sg chain" subtest:
1. attaches an SK_MSG program (prog_sk_policy_drop) that drops the first
N bytes of a message via bpf_msg_apply_bytes() + SK_DROP,
2. splices 17 single-byte frags through a kTLS TX socket so the ring
fills to sg.start=16, sg.end=17,
3. removes the socket from the sockmap and sends one more byte, which
wraps sg.end to 0 and exercises the wrap branch in tls_push_record().
Without the fix the kernel hangs on the wrapping send (the corrupted
chain pointer leaves the sg traversal stuck); with the fix the test
completes cleanly.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: ast@kernel.org
CC: daniel@iogearbox.net
CC: andrii@kernel.org
CC: martin.lau@linux.dev
CC: eddyz87@gmail.com
CC: memxor@gmail.com
CC: song@kernel.org
CC: yonghong.song@linux.dev
CC: jolsa@kernel.org
CC: shuah@kernel.org
CC: john.fastabend@gmail.com
CC: jiayuan.chen@linux.dev
CC: isolodrai@meta.com
CC: bpf@vger.kernel.org
CC: linux-kselftest@vger.kernel.org
---
.../selftests/bpf/prog_tests/sockmap_ktls.c | 83 +++++++++++++++++++
.../selftests/bpf/progs/test_sockmap_ktls.c | 8 ++
2 files changed, 91 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
index b87e7f39e15a..8ab7f4cdc614 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
@@ -4,6 +4,7 @@
* Tests for sockmap/sockhash holding kTLS sockets.
*/
#include <error.h>
+#include <fcntl.h>
#include <netinet/tcp.h>
#include <linux/tls.h>
#include "test_progs.h"
@@ -403,6 +404,86 @@ static void test_sockmap_ktls_tx_pop(int family, int sotype)
test_sockmap_ktls__destroy(skel);
}
+static void test_sockmap_ktls_tx_wrapped_chain(int family, int sotype)
+{
+ int c = -1, p = -1, one = 1, prog_fd, map_fd;
+ int pipefd[2] = { -1, -1 };
+ struct test_sockmap_ktls *skel;
+ char byte;
+ ssize_t n;
+ int err, i;
+
+ skel = test_sockmap_ktls__open_and_load();
+ if (!ASSERT_TRUE(skel, "open ktls skel"))
+ return;
+
+ err = create_pair(family, sotype, &c, &p);
+ if (!ASSERT_OK(err, "create_pair()"))
+ goto out;
+
+ prog_fd = bpf_program__fd(skel->progs.prog_sk_policy_drop);
+ map_fd = bpf_map__fd(skel->maps.sock_map);
+
+ err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0);
+ if (!ASSERT_OK(err, "bpf_prog_attach sk msg"))
+ goto out;
+
+ err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST);
+ if (!ASSERT_OK(err, "bpf_map_update_elem(c)"))
+ goto out;
+
+ err = init_ktls_pairs(c, p);
+ if (!ASSERT_OK(err, "init_ktls_pairs(c, p)"))
+ goto out;
+
+ /* packetized pipe so each splice frag becomes its own sg entry */
+ err = pipe2(pipefd, O_DIRECT);
+ if (!ASSERT_OK(err, "pipe2"))
+ goto out;
+ err = fcntl(pipefd[0], F_SETPIPE_SZ, 17 * 4096);
+ if (!ASSERT_GE(err, 17 * 4096, "F_SETPIPE_SZ"))
+ goto out;
+
+ for (i = 0; i < 17; i++) {
+ byte = 'A' + i;
+ if (!ASSERT_EQ(write(pipefd[1], &byte, 1), 1, "write to pipe"))
+ goto out;
+ }
+
+ /* drop the first 16 bytes so sg.start advances to 16 */
+ skel->bss->apply_bytes = 16;
+
+ n = splice(pipefd[0], NULL, c, NULL, 17, 0);
+ if (n < 0)
+ ASSERT_EQ(errno, EACCES, "splice errno");
+
+ err = bpf_map_delete_elem(map_fd, &one);
+ if (!ASSERT_OK(err, "bpf_map_delete_elem"))
+ goto out;
+ usleep(50000);
+
+ while (recv(p, &byte, 1, MSG_DONTWAIT) > 0)
+ ;
+
+ /* this send wraps sg.end to 0 and trips the wrap branch */
+ byte = 'X';
+ n = send(c, &byte, 1, MSG_DONTWAIT);
+ if (n < 0)
+ ASSERT_TRUE(errno == EAGAIN || errno == EACCES || errno == EPIPE,
+ "send errno");
+
+out:
+ if (pipefd[0] != -1)
+ close(pipefd[0]);
+ if (pipefd[1] != -1)
+ close(pipefd[1]);
+ if (c != -1)
+ close(c);
+ if (p != -1)
+ close(p);
+ test_sockmap_ktls__destroy(skel);
+}
+
static void run_tests(int family, enum bpf_map_type map_type)
{
int map;
@@ -429,6 +510,8 @@ static void run_ktls_test(int family, int sotype)
test_sockmap_ktls_tx_no_buf(family, sotype, true);
if (test__start_subtest("tls tx with pop"))
test_sockmap_ktls_tx_pop(family, sotype);
+ if (test__start_subtest("tls tx wrapped sg chain"))
+ test_sockmap_ktls_tx_wrapped_chain(family, sotype);
}
void test_sockmap_ktls(void)
diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c
index 83df4919c224..18de4d7cd816 100644
--- a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c
+++ b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c
@@ -38,3 +38,11 @@ int prog_sk_policy_redir(struct sk_msg_md *msg)
bpf_msg_apply_bytes(msg, apply_bytes);
return bpf_msg_redirect_map(msg, &sock_map, two, 0);
}
+
+SEC("sk_msg")
+int prog_sk_policy_drop(struct sk_msg_md *msg)
+{
+ if (apply_bytes > 0)
+ bpf_msg_apply_bytes(msg, apply_bytes);
+ return SK_DROP;
+}
--
2.54.0
^ permalink raw reply related
* [PATCH net 6/7] net: tls: fix use-after-free in tls_sw_sendmsg_locked after bpf verdict
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski, Alessandro G,
jiayuan.chen, ast
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
After bpf_exec_tx_verdict() returns in the zerocopy path, the local
msg_pl/msg_en pointers may be stale. If a BPF program set apply_bytes
such that tls_push_record() splits the open record via
tls_split_open_record(), ctx->open_rec is replaced with the split
remainder while the original record is pushed to the tx_list and may
be freed by tls_tx_records(). The caller's cached msg_pl/msg_en still
reference the old (now-freed) record.
This is triggered when bpf_exec_tx_verdict() returns -ENOSPC (BPF set
cork_bytes > remaining data) after an internal record split: the code
dereferences msg_pl->cork_bytes on the freed record, causing a UAF.
Reported-by: Alessandro G <ale.grpp@gmail.com>
Fixes: 54a3ecaeeeae ("bpf: fix ktls panic with sockmap")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: john.fastabend@gmail.com
CC: sd@queasysnail.net
CC: jiayuan.chen@linux.dev
CC: ast@kernel.org
CC: bpf@vger.kernel.org
---
net/tls/tls_sw.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 600e13effaab..d086b43fc675 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1157,6 +1157,13 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg,
else if (ret == -ENOMEM)
goto wait_for_memory;
else if (ctx->open_rec && ret == -ENOSPC) {
+ /* bpf_exec_tx_verdict() may have
+ * called tls_split_open_record(),
+ * freeing the old record. Re-fetch.
+ */
+ rec = ctx->open_rec;
+ msg_pl = &rec->msg_plaintext;
+ msg_en = &rec->msg_encrypted;
if (msg_pl->cork_bytes) {
ret = 0;
goto send_end;
--
2.54.0
^ permalink raw reply related
* [PATCH net 7/7] selftests: bpf: cover tls_sw_sendmsg UAF after bpf_exec_tx_verdict split
From: Jakub Kicinski @ 2026-04-29 22:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, bpf,
john.fastabend, sd, linux-kselftest, Jakub Kicinski, andrii,
eddyz87, ast, daniel, martin.lau, memxor, song, yonghong.song,
jolsa, shuah, jiayuan.chen, isolodrai
In-Reply-To: <20260429222944.2139041-1-kuba@kernel.org>
Add a regression test for the use-after-free in tls_sw_sendmsg_locked()
where the cached msg_pl pointer becomes stale after bpf_exec_tx_verdict()
returns -ENOSPC: tls_push_record() may have called
tls_split_open_record() which replaces ctx->open_rec and frees the old
record, but the caller still dereferences msg_pl->cork_bytes.
Reusing prog_sk_policy with apply_bytes=1000 + cork_bytes=800, a single
1500-byte send on a kTLS TX socket in a sockmap drives the split-and-free
path. Without the fix, KASAN reports slab-use-after-free in tls_sw_sendmsg
and the kernel hangs; with the fix the test completes cleanly.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: andrii@kernel.org
CC: eddyz87@gmail.com
CC: ast@kernel.org
CC: daniel@iogearbox.net
CC: martin.lau@linux.dev
CC: memxor@gmail.com
CC: song@kernel.org
CC: yonghong.song@linux.dev
CC: jolsa@kernel.org
CC: shuah@kernel.org
CC: john.fastabend@gmail.com
CC: jiayuan.chen@linux.dev
CC: isolodrai@meta.com
CC: bpf@vger.kernel.org
CC: linux-kselftest@vger.kernel.org
---
.../selftests/bpf/prog_tests/sockmap_ktls.c | 56 +++++++++++++++++++
.../selftests/bpf/progs/test_sockmap_ktls.c | 2 +
2 files changed, 58 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
index 8ab7f4cdc614..d0bc8d39893a 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
@@ -404,6 +404,60 @@ static void test_sockmap_ktls_tx_pop(int family, int sotype)
test_sockmap_ktls__destroy(skel);
}
+static void test_sockmap_ktls_tx_apply_cork_uaf(int family, int sotype)
+{
+ int c = -1, p = -1, one = 1, prog_fd, map_fd;
+ struct test_sockmap_ktls *skel;
+ char buf[1500];
+ ssize_t n;
+ int err;
+
+ skel = test_sockmap_ktls__open_and_load();
+ if (!ASSERT_TRUE(skel, "open ktls skel"))
+ return;
+
+ err = create_pair(family, sotype, &c, &p);
+ if (!ASSERT_OK(err, "create_pair()"))
+ goto out;
+
+ prog_fd = bpf_program__fd(skel->progs.prog_sk_policy);
+ map_fd = bpf_map__fd(skel->maps.sock_map);
+
+ err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0);
+ if (!ASSERT_OK(err, "bpf_prog_attach sk msg"))
+ goto out;
+
+ err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST);
+ if (!ASSERT_OK(err, "bpf_map_update_elem(c)"))
+ goto out;
+
+ /* apply_bytes < send drives tls_split_open_record(); cork_bytes >
+ * remaining returns -ENOSPC after the split frees the old rec
+ */
+ skel->bss->apply_bytes = 1000;
+ skel->bss->cork_byte = 800;
+
+ err = init_ktls_pairs(c, p);
+ if (!ASSERT_OK(err, "init_ktls_pairs(c, p)"))
+ goto out;
+
+ memset(buf, 'A', sizeof(buf));
+ n = send(c, buf, sizeof(buf), MSG_DONTWAIT);
+ if (n < 0)
+ ASSERT_TRUE(errno == ENOSPC || errno == EAGAIN, "send errno");
+
+ n = send(c, buf, sizeof(buf), MSG_DONTWAIT);
+ if (n < 0)
+ ASSERT_TRUE(errno == ENOSPC || errno == EAGAIN, "send errno");
+
+out:
+ if (c != -1)
+ close(c);
+ if (p != -1)
+ close(p);
+ test_sockmap_ktls__destroy(skel);
+}
+
static void test_sockmap_ktls_tx_wrapped_chain(int family, int sotype)
{
int c = -1, p = -1, one = 1, prog_fd, map_fd;
@@ -512,6 +566,8 @@ static void run_ktls_test(int family, int sotype)
test_sockmap_ktls_tx_pop(family, sotype);
if (test__start_subtest("tls tx wrapped sg chain"))
test_sockmap_ktls_tx_wrapped_chain(family, sotype);
+ if (test__start_subtest("tls tx apply cork uaf"))
+ test_sockmap_ktls_tx_apply_cork_uaf(family, sotype);
}
void test_sockmap_ktls(void)
diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c
index 18de4d7cd816..b34f0c0f9f83 100644
--- a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c
+++ b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c
@@ -20,6 +20,8 @@ struct {
SEC("sk_msg")
int prog_sk_policy(struct sk_msg_md *msg)
{
+ if (apply_bytes > 0)
+ bpf_msg_apply_bytes(msg, apply_bytes);
if (cork_byte > 0)
bpf_msg_cork_bytes(msg, cork_byte);
if (push_start > 0 && push_end > 0)
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] net:mctp: split mctp hdr version to ver and rsvd
From: kernel test robot @ 2026-04-29 22:40 UTC (permalink / raw)
To: wit_yuan, jk
Cc: oe-kbuild-all, yuanzhaoming901030, yuanzm2, matt, davem, edumazet,
kuba, pabeni, netdev, linux-kernel
In-Reply-To: <20260409125129.9210-1-yuanzhaoming901030@126.com>
Hi wit_yuan,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/wit_yuan/net-mctp-split-mctp-hdr-version-to-ver-and-rsvd/20260414-044431
base: linus/master
patch link: https://lore.kernel.org/r/20260409125129.9210-1-yuanzhaoming901030%40126.com
patch subject: [PATCH] net:mctp: split mctp hdr version to ver and rsvd
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20260430/202604300641.2Ml6rWOe-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604300641.2Ml6rWOe-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604300641.2Ml6rWOe-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from include/kunit/static_stub.h:18,
from net/mctp/route.c:20:
net/mctp/test/route-test.c: In function 'mctp_test_fragment':
>> net/mctp/test/route-test.c:70:39: error: 'typeof' applied to a bit-field
70 | KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
| ^~~~
include/kunit/test.h:839:22: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
839 | const typeof(left) __left = (left); \
| ^~~~
include/kunit/test.h:1036:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
1036 | KUNIT_BINARY_INT_ASSERTION(test, \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/kunit/test.h:1033:9: note: in expansion of macro 'KUNIT_EXPECT_EQ_MSG'
1033 | KUNIT_EXPECT_EQ_MSG(test, left, right, NULL)
| ^~~~~~~~~~~~~~~~~~~
net/mctp/test/route-test.c:70:17: note: in expansion of macro 'KUNIT_EXPECT_EQ'
70 | KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
| ^~~~~~~~~~~~~~~
net/mctp/test/route-test.c:70:50: error: 'typeof' applied to a bit-field
70 | KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
| ^~~
include/kunit/test.h:840:22: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
840 | const typeof(right) __right = (right); \
| ^~~~~
include/kunit/test.h:1036:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
1036 | KUNIT_BINARY_INT_ASSERTION(test, \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/kunit/test.h:1033:9: note: in expansion of macro 'KUNIT_EXPECT_EQ_MSG'
1033 | KUNIT_EXPECT_EQ_MSG(test, left, right, NULL)
| ^~~~~~~~~~~~~~~~~~~
net/mctp/test/route-test.c:70:17: note: in expansion of macro 'KUNIT_EXPECT_EQ'
70 | KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
| ^~~~~~~~~~~~~~~
vim +/typeof +70 net/mctp/test/route-test.c
161eba50e183ed Jeremy Kerr 2021-10-03 19
161eba50e183ed Jeremy Kerr 2021-10-03 20 static void mctp_test_fragment(struct kunit *test)
161eba50e183ed Jeremy Kerr 2021-10-03 21 {
161eba50e183ed Jeremy Kerr 2021-10-03 22 const struct mctp_frag_test *params;
161eba50e183ed Jeremy Kerr 2021-10-03 23 int rc, i, n, mtu, msgsize;
269936db5eb396 Jeremy Kerr 2025-07-02 24 struct mctp_test_dev *dev;
269936db5eb396 Jeremy Kerr 2025-07-02 25 struct mctp_dst dst;
161eba50e183ed Jeremy Kerr 2021-10-03 26 struct sk_buff *skb;
161eba50e183ed Jeremy Kerr 2021-10-03 27 struct mctp_hdr hdr;
161eba50e183ed Jeremy Kerr 2021-10-03 28 u8 seq;
161eba50e183ed Jeremy Kerr 2021-10-03 29
161eba50e183ed Jeremy Kerr 2021-10-03 30 params = test->param_value;
161eba50e183ed Jeremy Kerr 2021-10-03 31 mtu = params->mtu;
161eba50e183ed Jeremy Kerr 2021-10-03 32 msgsize = params->msgsize;
161eba50e183ed Jeremy Kerr 2021-10-03 33
161eba50e183ed Jeremy Kerr 2021-10-03 34 hdr.ver = 1;
161eba50e183ed Jeremy Kerr 2021-10-03 35 hdr.src = 8;
161eba50e183ed Jeremy Kerr 2021-10-03 36 hdr.dest = 10;
161eba50e183ed Jeremy Kerr 2021-10-03 37 hdr.flags_seq_tag = MCTP_HDR_FLAG_TO;
161eba50e183ed Jeremy Kerr 2021-10-03 38
161eba50e183ed Jeremy Kerr 2021-10-03 39 skb = mctp_test_create_skb(&hdr, msgsize);
161eba50e183ed Jeremy Kerr 2021-10-03 40 KUNIT_ASSERT_TRUE(test, skb);
161eba50e183ed Jeremy Kerr 2021-10-03 41
269936db5eb396 Jeremy Kerr 2025-07-02 42 dev = mctp_test_create_dev();
269936db5eb396 Jeremy Kerr 2025-07-02 43 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
269936db5eb396 Jeremy Kerr 2025-07-02 44
6ab578739a4c1f Jeremy Kerr 2025-11-26 45 mctp_test_dst_setup(test, &dst, dev, mtu);
161eba50e183ed Jeremy Kerr 2021-10-03 46
269936db5eb396 Jeremy Kerr 2025-07-02 47 rc = mctp_do_fragment_route(&dst, skb, mtu, MCTP_TAG_OWNER);
161eba50e183ed Jeremy Kerr 2021-10-03 48 KUNIT_EXPECT_FALSE(test, rc);
161eba50e183ed Jeremy Kerr 2021-10-03 49
6ab578739a4c1f Jeremy Kerr 2025-11-26 50 n = dev->pkts.qlen;
161eba50e183ed Jeremy Kerr 2021-10-03 51 KUNIT_EXPECT_EQ(test, n, params->n_frags);
161eba50e183ed Jeremy Kerr 2021-10-03 52
161eba50e183ed Jeremy Kerr 2021-10-03 53 for (i = 0;; i++) {
161eba50e183ed Jeremy Kerr 2021-10-03 54 struct mctp_hdr *hdr2;
161eba50e183ed Jeremy Kerr 2021-10-03 55 struct sk_buff *skb2;
161eba50e183ed Jeremy Kerr 2021-10-03 56 u8 tag_mask, seq2;
161eba50e183ed Jeremy Kerr 2021-10-03 57 bool first, last;
161eba50e183ed Jeremy Kerr 2021-10-03 58
161eba50e183ed Jeremy Kerr 2021-10-03 59 first = i == 0;
161eba50e183ed Jeremy Kerr 2021-10-03 60 last = i == (n - 1);
161eba50e183ed Jeremy Kerr 2021-10-03 61
6ab578739a4c1f Jeremy Kerr 2025-11-26 62 skb2 = skb_dequeue(&dev->pkts);
161eba50e183ed Jeremy Kerr 2021-10-03 63 if (!skb2)
161eba50e183ed Jeremy Kerr 2021-10-03 64 break;
161eba50e183ed Jeremy Kerr 2021-10-03 65
161eba50e183ed Jeremy Kerr 2021-10-03 66 hdr2 = mctp_hdr(skb2);
161eba50e183ed Jeremy Kerr 2021-10-03 67
161eba50e183ed Jeremy Kerr 2021-10-03 68 tag_mask = MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO;
161eba50e183ed Jeremy Kerr 2021-10-03 69
161eba50e183ed Jeremy Kerr 2021-10-03 @70 KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
161eba50e183ed Jeremy Kerr 2021-10-03 71 KUNIT_EXPECT_EQ(test, hdr2->src, hdr.src);
161eba50e183ed Jeremy Kerr 2021-10-03 72 KUNIT_EXPECT_EQ(test, hdr2->dest, hdr.dest);
161eba50e183ed Jeremy Kerr 2021-10-03 73 KUNIT_EXPECT_EQ(test, hdr2->flags_seq_tag & tag_mask,
161eba50e183ed Jeremy Kerr 2021-10-03 74 hdr.flags_seq_tag & tag_mask);
161eba50e183ed Jeremy Kerr 2021-10-03 75
161eba50e183ed Jeremy Kerr 2021-10-03 76 KUNIT_EXPECT_EQ(test,
161eba50e183ed Jeremy Kerr 2021-10-03 77 !!(hdr2->flags_seq_tag & MCTP_HDR_FLAG_SOM), first);
161eba50e183ed Jeremy Kerr 2021-10-03 78 KUNIT_EXPECT_EQ(test,
161eba50e183ed Jeremy Kerr 2021-10-03 79 !!(hdr2->flags_seq_tag & MCTP_HDR_FLAG_EOM), last);
161eba50e183ed Jeremy Kerr 2021-10-03 80
161eba50e183ed Jeremy Kerr 2021-10-03 81 seq2 = (hdr2->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) &
161eba50e183ed Jeremy Kerr 2021-10-03 82 MCTP_HDR_SEQ_MASK;
161eba50e183ed Jeremy Kerr 2021-10-03 83
161eba50e183ed Jeremy Kerr 2021-10-03 84 if (first) {
161eba50e183ed Jeremy Kerr 2021-10-03 85 seq = seq2;
161eba50e183ed Jeremy Kerr 2021-10-03 86 } else {
161eba50e183ed Jeremy Kerr 2021-10-03 87 seq++;
161eba50e183ed Jeremy Kerr 2021-10-03 88 KUNIT_EXPECT_EQ(test, seq2, seq & MCTP_HDR_SEQ_MASK);
161eba50e183ed Jeremy Kerr 2021-10-03 89 }
161eba50e183ed Jeremy Kerr 2021-10-03 90
161eba50e183ed Jeremy Kerr 2021-10-03 91 if (!last)
161eba50e183ed Jeremy Kerr 2021-10-03 92 KUNIT_EXPECT_EQ(test, skb2->len, mtu);
161eba50e183ed Jeremy Kerr 2021-10-03 93 else
161eba50e183ed Jeremy Kerr 2021-10-03 94 KUNIT_EXPECT_LE(test, skb2->len, mtu);
161eba50e183ed Jeremy Kerr 2021-10-03 95
161eba50e183ed Jeremy Kerr 2021-10-03 96 kfree_skb(skb2);
161eba50e183ed Jeremy Kerr 2021-10-03 97 }
161eba50e183ed Jeremy Kerr 2021-10-03 98
6ab578739a4c1f Jeremy Kerr 2025-11-26 99 mctp_dst_release(&dst);
269936db5eb396 Jeremy Kerr 2025-07-02 100 mctp_test_destroy_dev(dev);
161eba50e183ed Jeremy Kerr 2021-10-03 101 }
161eba50e183ed Jeremy Kerr 2021-10-03 102
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [RFC PATCH net-next 1/2] net: napi: Fix interrupts permanently disabled during busy poll
From: Jakub Kicinski @ 2026-04-29 22:52 UTC (permalink / raw)
To: Dragos Tatulea
Cc: Martin Karsten, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Daniel Borkmann, Björn Töpel,
Gal Pressman, Tariq Toukan, Joe Damato, Frederik Deweerdt, netdev,
linux-kernel
In-Reply-To: <plfaoeyx3xfujleux6gmmlku3ancnp73g4aca7ep53zijbhjka@3ylcijhron2m>
On Wed, 29 Apr 2026 08:13:55 +0000 Dragos Tatulea wrote:
> On Tue, Apr 28, 2026 at 05:31:54PM -0700, Jakub Kicinski wrote:
> > On Tue, 28 Apr 2026 20:04:13 -0400 Martin Karsten wrote:
> > > Labelling this with number 4. might be misleading, sorry! The concern is
> > > that a short enough timer (compared to the duration of the driver poll)
> > > can be triggered before the NAPI_STATE_SCHED bit is cleared at the end
> > > of Step 3.3.
> >
> > Ah. Just say that :D Two pages of buggy text, y'all would have been
> > better off using this one paragraph as the commit message.
> > Please don't use AI for generating commit messages if that's the cause.
> > It really is spectacularly shit at it.
> I take the blame for this. Funnily enough, the text was written mostly
> without AI... Just wanted to present the interactions in a more explanatory
> way.
Heh, I guess I blame everything on AI these days :)
> Do you prefer the short version from Martin or an improved version of
> the long explanation?
That's what I'd do. The explanation should focus on the fact that the
current code arms the timer before it releases the ownership (clearing
STATE_SCHED). The intention of the __busy_poll_stop() outro is to either
schedule NAPI, arm the IRQ or the timer.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox