From: Tariq Toukan <tariqt@nvidia.com>
To: Rishikesh Jethwani <rjethwani@everpuredata.com>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>,
"John Fastabend" <john.fastabend@gmail.com>,
Kuniyuki Iwashima <kuniyu@google.com>,
Neal Cardwell <ncardwell@google.com>, <netdev@vger.kernel.org>,
Paolo Abeni <pabeni@redhat.com>,
Sabrina Dubroca <sd@queasysnail.net>,
Willem de Bruijn <willemb@google.com>
Cc: Alexander Graf <graf@amazon.com>,
Aviad Yehezkel <aviadye@mellanox.com>,
Bobby Eshleman <bobbyeshleman@meta.com>,
Boris Pismenny <borisp@mellanox.com>,
Boris Pismenny <borisp@nvidia.com>,
Carolina Jubran <cjubran@nvidia.com>,
Costa Shulyupin <costa.shul@redhat.com>,
Dragos Tatulea <dtatulea@nvidia.com>,
Eran Ben Elisha <eranbe@mellanox.com>,
Gal Pressman <gal@nvidia.com>, Ilya Lesokhin <ilyal@mellanox.com>,
Jacob Keller <jacob.e.keller@intel.com>,
Jonathan Corbet <corbet@lwn.net>,
Leon Romanovsky <leon@kernel.org>, <linux-doc@vger.kernel.org>,
open list <linux-kernel@vger.kernel.org>,
<linux-rdma@vger.kernel.org>, Mark Bloch <mbloch@nvidia.com>,
Martin Karsten <mkarsten@uwaterloo.ca>,
Randy Dunlap <rdunlap@infradead.org>,
Richard Cochran <richardcochran@gmail.com>,
"Saeed Mahameed" <saeedm@mellanox.com>,
Saeed Mahameed <saeedm@nvidia.com>,
"Shuah Khan" <skhan@linuxfoundation.org>,
Simon Horman <horms@kernel.org>,
"Stanislav Fomichev" <sdf@fomichev.me>,
Stanislav Fomichev <sdf.kernel@gmail.com>,
Stefano Garzarella <sgarzare@redhat.com>,
Tariq Toukan <tariqt@mellanox.com>,
Tariq Toukan <tariqt@nvidia.com>,
Wang Zhan <wang.zhan@smartx.com>, "William Tu" <witu@nvidia.com>
Subject: [RFC net-next 4/7] net/mlx5e: Drain old TX SQs before activating new channels
Date: Mon, 9 Nov 2026 13:47:31 +0200 [thread overview]
Message-ID: <20261109114734.2087878-5-tariqt@nvidia.com> (raw)
In-Reply-To: <20261109114734.2087878-1-tariqt@nvidia.com>
On devices with tis_tir_td_order, commit 3b88a535a8e1 ("net/mlx5e:
Defer channels closure to reduce interface down time") closes the old
channels only after the new ones are activated. Their SQs are
deactivated before that, which rings a final doorbell, but nothing
waits for the HW to execute their pending WQEs: the completions of a
deactivated SQ are not polled, and the SQ is destroyed only when its
channel is closed. Meanwhile, the new SQ of the same TX queue starts
transmitting. A flow's WQEs still pending on the old SQ can then
execute after its newer WQEs on the new SQ, as WQE fences order only
within an SQ.
Besides reordering packets, this breaks kTLS TX offload. The WQEs of a
connection update its TIS crypto and record tracking state as they
execute. When old WQEs execute after new ones, e.g. because a PFC
pause held the old SQ back, the TIS state gets out of sync with the
TCP stream, and the connection's records go out corrupted.
Before activating the new channels, drain the TX SQs of the old ones:
reap their completions until all their WQEs have completed. If they
do not drain in time, e.g. under a long pause, warn, and close the old
channels before activating the new ones, as before that commit. Move
the deferred closure into mlx5e_switch_priv_channels(), next to that
decision.
Pointed out by Rishikesh Jethwani while discussing kTLS TX TIS reuse
for TLS 1.3 rekey on netdev.
Reproduced on ConnectX-6 Dx, with 248 channels, by changing the
channel count between 247 and 248 every 2 seconds while 32 kTLS TX
offloaded connections stream pattern data to a software kTLS receiver
that checks every byte. On this setup, the new channels are activated
about 230 ms after the old ones are deactivated, so the old SQs must
still hold WQEs by then: with every TX queue rate limited to 10 Mb/s,
9 of 41 connections failed decryption in 60 seconds, and 11 of 43 in
120 seconds. With this patch, no record failed in five such runs, 7
minutes in total; the drain timed out and fell back to closing the old
channels first 7 times. At 1 Gb/s per queue, the old SQs always
drained in time.
Fixes: 3b88a535a8e1 ("net/mlx5e: Defer channels closure to reduce interface down time")
Reported-by: Rishikesh Jethwani <rjethwani@everpuredata.com>
Closes: https://lore.kernel.org/netdev/CAKaoeS1hj9XuM7ojLBCjGA=EXc2M-b+eRjC2Sk=8L_8XYeJ18w@mail.gmail.com/
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/en/txrx.h | 1 +
.../net/ethernet/mellanox/mlx5/core/en_main.c | 88 ++++++++++++++++++-
.../net/ethernet/mellanox/mlx5/core/en_tx.c | 41 +++++++--
3 files changed, 118 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
index 24e91279243e..b5bd1b6e8601 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
@@ -112,6 +112,7 @@ struct mlx5e_xmit_data_frags {
netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev);
bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget);
+bool mlx5e_txqsq_drain(struct mlx5e_txqsq *sq);
void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq);
static inline bool
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index c3c2c5720c6f..a721e30bcaa8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -3421,6 +3421,82 @@ static int mlx5e_switch_priv_params(struct mlx5e_priv *priv,
return 0;
}
+/* Time for the TX SQs of deactivated channels to complete their WQEs. */
+#define MLX5E_SQ_DRAIN_TIMEOUT 100 /* msecs */
+
+/* Returns 1 if the SQ still has pending WQEs, and records the first such SQ. */
+static int mlx5e_drain_txqsq(struct mlx5e_txqsq *sq,
+ struct mlx5e_txqsq **pending)
+{
+ if (mlx5e_txqsq_drain(sq))
+ return 0;
+
+ if (!*pending)
+ *pending = sq;
+
+ return 1;
+}
+
+/* Returns the number of the channel's TX SQs that still have pending WQEs. */
+static int mlx5e_drain_channel_txqsqs(struct mlx5e_channel *c,
+ struct mlx5e_txqsq **pending)
+{
+ struct mlx5e_txqsq __rcu **qos_sqs;
+ int num_pending = 0;
+ int i;
+
+ for (i = 0; i < c->num_tc; i++)
+ num_pending += mlx5e_drain_txqsq(&c->sq[i], pending);
+
+ qos_sqs = mlx5e_state_dereference(c->priv, c->qos_sqs);
+ if (!qos_sqs)
+ return num_pending;
+
+ for (i = 0; i < c->qos_sqs_size; i++) {
+ struct mlx5e_txqsq *sq;
+
+ sq = mlx5e_state_dereference(c->priv, qos_sqs[i]);
+ if (sq)
+ num_pending += mlx5e_drain_txqsq(sq, pending);
+ }
+
+ return num_pending;
+}
+
+/* Wait for the TX SQs of deactivated channels to complete all their WQEs,
+ * so that none of them executes concurrently with the new SQs.
+ * The PTP SQs are not waited for, they carry PTP event packets only.
+ */
+static bool mlx5e_drain_channels_txqsqs(struct mlx5e_channels *chs)
+{
+ unsigned long exp_time;
+
+ exp_time = jiffies + msecs_to_jiffies(MLX5E_SQ_DRAIN_TIMEOUT);
+ for (;;) {
+ struct mlx5e_txqsq *pending = NULL;
+ int num_pending = 0;
+ int i;
+
+ for (i = 0; i < chs->num; i++)
+ num_pending += mlx5e_drain_channel_txqsqs(chs->c[i],
+ &pending);
+
+ if (!num_pending)
+ return true;
+
+ if (time_after(jiffies, exp_time)) {
+ netdev_warn(pending->netdev,
+ "Old TX SQs not drained in %d msecs (%d pending, e.g. SQ 0x%x txq %d cc 0x%x pc 0x%x), closing the old channels first\n",
+ MLX5E_SQ_DRAIN_TIMEOUT, num_pending,
+ pending->sqn, pending->txq_ix,
+ pending->cc, pending->pc);
+ return false;
+ }
+
+ usleep_range(10, 20);
+ }
+}
+
static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
struct mlx5e_channels *old_chs,
struct mlx5e_channels *new_chs,
@@ -3428,6 +3504,7 @@ static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
void *context)
{
struct net_device *netdev = priv->netdev;
+ bool defer_close = false;
int carrier_ok;
int err = 0;
@@ -3450,7 +3527,10 @@ static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
}
mlx5e_set_xdp_feature(priv);
- if (!MLX5_CAP_GEN(priv->mdev, tis_tir_td_order))
+
+ defer_close = MLX5_CAP_GEN(priv->mdev, tis_tir_td_order) &&
+ mlx5e_drain_channels_txqsqs(old_chs);
+ if (!defer_close)
mlx5e_close_channels(old_chs);
priv->profile->update_rx(priv);
@@ -3462,6 +3542,9 @@ static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
if (carrier_ok)
netif_carrier_on(netdev);
+ if (defer_close)
+ mlx5e_close_channels(old_chs);
+
return err;
}
@@ -3499,9 +3582,6 @@ int mlx5e_safe_switch_params(struct mlx5e_priv *priv,
if (err)
goto err_close;
- if (MLX5_CAP_GEN(priv->mdev, tis_tir_td_order))
- mlx5e_close_channels(old_chs);
-
kfree(new_chs);
kfree(old_chs);
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index bee6f0bf1eff..eb20e3d14d46 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -780,10 +780,11 @@ void mlx5e_txqsq_wake(struct mlx5e_txqsq *sq)
}
}
-bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
+/* Returns the number of polled CQEs. */
+static int mlx5e_poll_tx_cqes(struct mlx5e_txqsq *sq, int napi_budget)
{
+ struct mlx5e_cq *cq = &sq->cq;
struct mlx5e_sq_stats *stats;
- struct mlx5e_txqsq *sq;
struct mlx5_cqe64 *cqe;
u32 dma_fifo_cc;
u32 nbytes;
@@ -791,14 +792,9 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
u16 sqcc;
int i;
- sq = container_of(cq, struct mlx5e_txqsq, cq);
-
- if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
- return false;
-
cqe = mlx5_cqwq_get_cqe(&cq->wq);
if (!cqe)
- return false;
+ return 0;
stats = sq->stats;
@@ -891,11 +887,40 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
netdev_tx_completed_queue(sq->txq, npkts, nbytes);
+ return i;
+}
+
+bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
+{
+ struct mlx5e_txqsq *sq = container_of(cq, struct mlx5e_txqsq, cq);
+ int i;
+
+ if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
+ return false;
+
+ i = mlx5e_poll_tx_cqes(sq, napi_budget);
+ if (!i)
+ return false;
+
mlx5e_txqsq_wake(sq);
return (i == MLX5E_TX_CQ_POLL_BUDGET);
}
+/* Reap the completions of a deactivated SQ, whose NAPI is disabled, without
+ * waking its TX queue. Returns true once all its WQEs have completed.
+ */
+bool mlx5e_txqsq_drain(struct mlx5e_txqsq *sq)
+{
+ int polled;
+
+ do {
+ polled = mlx5e_poll_tx_cqes(sq, 0);
+ } while (polled == MLX5E_TX_CQ_POLL_BUDGET);
+
+ return sq->cc == sq->pc;
+}
+
static void mlx5e_tx_wi_kfree_fifo_skbs(struct mlx5e_txqsq *sq, struct mlx5e_tx_wqe_info *wi)
{
int i;
--
2.44.0
next prev parent reply other threads:[~2026-11-09 11:49 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 11:47 [RFC net-next 0/7] [Cover Letter] net/mlx5e: kTLS TX fixes for WQEs in flight Tariq Toukan
2026-11-09 11:47 ` [RFC net-next 1/7] net/mlx5e: kTLS, Hold a dropped skb until its posted WQEs complete Tariq Toukan
2026-11-09 11:47 ` [RFC net-next 2/7] net/mlx5e: kTLS, Resync TX fully after its WQEs were lost Tariq Toukan
2026-11-09 11:47 ` [RFC net-next 3/7] net/mlx5e: kTLS, Post TX context params only for skbs that use them Tariq Toukan
2026-11-09 11:47 ` Tariq Toukan [this message]
2026-11-09 11:47 ` [RFC net-next 5/7] net: tls: keep the TX queue of a device-offloaded socket stable Tariq Toukan
2026-11-09 11:47 ` [RFC net-next 6/7] net/mlx5e: kTLS, Keep the TX queue of an offloaded " Tariq Toukan
2026-11-09 11:47 ` [RFC net-next 7/7] net/mlx5e: kTLS, Keep a TX connection on one SQ at a time Tariq Toukan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109114734.2087878-5-tariqt@nvidia.com \
--to=tariqt@nvidia.com \
--cc=andrew+netdev@lunn.ch \
--cc=aviadye@mellanox.com \
--cc=bobbyeshleman@meta.com \
--cc=borisp@mellanox.com \
--cc=borisp@nvidia.com \
--cc=cjubran@nvidia.com \
--cc=corbet@lwn.net \
--cc=costa.shul@redhat.com \
--cc=davem@davemloft.net \
--cc=dtatulea@nvidia.com \
--cc=edumazet@kernel.org \
--cc=eranbe@mellanox.com \
--cc=gal@nvidia.com \
--cc=graf@amazon.com \
--cc=horms@kernel.org \
--cc=ilyal@mellanox.com \
--cc=jacob.e.keller@intel.com \
--cc=john.fastabend@gmail.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=leon@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=mbloch@nvidia.com \
--cc=mkarsten@uwaterloo.ca \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=rdunlap@infradead.org \
--cc=richardcochran@gmail.com \
--cc=rjethwani@everpuredata.com \
--cc=saeedm@mellanox.com \
--cc=saeedm@nvidia.com \
--cc=sd@queasysnail.net \
--cc=sdf.kernel@gmail.com \
--cc=sdf@fomichev.me \
--cc=sgarzare@redhat.com \
--cc=skhan@linuxfoundation.org \
--cc=tariqt@mellanox.com \
--cc=wang.zhan@smartx.com \
--cc=willemb@google.com \
--cc=witu@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®