From: Michael Chan <michael.chan@broadcom.com>
To: davem@davemloft.net
Cc: netdev@vger.kernel.org, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, andrew+netdev@lunn.ch,
pavan.chebbi@broadcom.com, andrew.gospodarek@broadcom.com
Subject: [PATCH net-next v5 11/15] bnxt_en: Add crypto MPC transmit/completion infrastructure
Date: Thu, 9 Jul 2026 21:23:56 -0700 [thread overview]
Message-ID: <20260710042400.3996847-12-michael.chan@broadcom.com> (raw)
In-Reply-To: <20260710042400.3996847-1-michael.chan@broadcom.com>
Add infrastructure to support sending crypto commands using the
midpath channels (MPCs). bnxt_xmit_crypto_cmd() is used to send a
crypto command and sleep with timeout until the completion is received.
If it times out, we recover by resetting the MPC. The reset is
deferred to bnxt_sp_task() similar to other resets to prevent
deadlock with a concurent close. The concurrent close may have
acquired the netdev_lock and waiting for the MPC operation to
finish.
The next patch will use this infrastructure to offload kTLS connections.
Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Michael Chan <michael.chan@broadcom.com>
---
v5:
Defer the MPC ring reset to bnxt_sp_task() just like other resets to
avoid any possibility od deadlock.
Add more comments to explain the MPC ring disable, stop, start.
v4:
https://lore.kernel.org/netdev/20260629184921.3496727-12-michael.chan@broadcom.com/
Clear MPC entries in bnxt_mpc_ring_reset() if device is going down.
Only clear MPC handle in bnxt_mpc_cmp() if the ring is in normal state to
ensure proper ref counting during reset.
v3:
https://lore.kernel.org/netdev/20260614072407.2761092-12-michael.chan@broadcom.com/
Multiple improvements for the MPC timeout logic, including the use of
refcount to terminate the timeout instead of the arbitrary 200msec poll
wait, add synchronize_net().
v2:
https://lore.kernel.org/netdev/20260512212105.3488258-12-michael.chan@broadcom.com/
---
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 16 +-
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 4 +
.../net/ethernet/broadcom/bnxt/bnxt_crypto.c | 138 +++++++++++-
.../net/ethernet/broadcom/bnxt/bnxt_crypto.h | 95 ++++++++
drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.c | 208 +++++++++++++++++-
drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.h | 14 ++
6 files changed, 470 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index d8b9c39dda5c..316733299e23 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -394,7 +394,7 @@ static void __bnxt_queue_sp_work(struct bnxt *bp)
schedule_work(&bp->sp_task);
}
-static void bnxt_queue_sp_work(struct bnxt *bp, unsigned int event)
+void bnxt_queue_sp_work(struct bnxt *bp, unsigned int event)
{
set_bit(event, &bp->sp_event);
__bnxt_queue_sp_work(bp);
@@ -7720,7 +7720,7 @@ void bnxt_hwrm_cp_ring_free(struct bnxt *bp, struct bnxt_cp_ring_info *cpr)
ring->fw_ring_id = INVALID_HW_RING_ID;
}
-static void bnxt_clear_one_cp_ring(struct bnxt *bp, struct bnxt_cp_ring_info *cpr)
+void bnxt_clear_one_cp_ring(struct bnxt *bp, struct bnxt_cp_ring_info *cpr)
{
struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
int i, size = ring->ring_mem.page_size;
@@ -14424,7 +14424,7 @@ static int bnxt_hwrm_rx_ring_reset(struct bnxt *bp, int ring_nr)
return hwrm_req_send_silent(bp, req);
}
-static void bnxt_reset_task(struct bnxt *bp, bool silent)
+void bnxt_reset_task(struct bnxt *bp, bool silent)
{
if (!silent)
bnxt_dbg_dump_states(bp);
@@ -14873,6 +14873,13 @@ static void bnxt_ulp_restart(struct bnxt *bp)
bnxt_ulp_start(bp);
}
+static void bnxt_mpc_ring_reset_sp(struct bnxt *bp)
+{
+ bnxt_rtnl_lock_sp(bp);
+ bnxt_mpc_ring_reset_task(bp);
+ bnxt_rtnl_unlock_sp(bp);
+}
+
static void bnxt_sp_task(struct work_struct *work)
{
struct bnxt *bp = container_of(work, struct bnxt, sp_task);
@@ -14965,6 +14972,9 @@ static void bnxt_sp_task(struct work_struct *work)
if (test_and_clear_bit(BNXT_RST_RING_SP_EVENT, &bp->sp_event))
bnxt_rx_ring_reset(bp);
+ if (test_and_clear_bit(BNXT_MPC_RESET_SP_EVENT, &bp->sp_event))
+ bnxt_mpc_ring_reset_sp(bp);
+
if (test_and_clear_bit(BNXT_FW_RESET_NOTIFY_SP_EVENT, &bp->sp_event)) {
if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state) ||
test_bit(BNXT_STATE_FW_NON_FATAL_COND, &bp->state))
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index e62b856e78f9..b65538fda3bd 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -2672,6 +2672,7 @@ struct bnxt {
#define BNXT_THERMAL_THRESHOLD_SP_EVENT 22
#define BNXT_FW_ECHO_REQUEST_SP_EVENT 23
#define BNXT_RESTART_ULP_SP_EVENT 24
+#define BNXT_MPC_RESET_SP_EVENT 25
struct delayed_work fw_reset_task;
int fw_reset_state;
@@ -2984,6 +2985,7 @@ static inline enum pkt_hash_types bnxt_rss_ext_op(struct bnxt *bp,
extern const u16 bnxt_bstore_to_trace[];
extern const u16 bnxt_lhint_arr[];
+void bnxt_queue_sp_work(struct bnxt *bp, unsigned int event);
int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
u16 prod, gfp_t gfp);
void bnxt_reuse_rx_data(struct bnxt_rx_ring_info *rxr, u16 cons, void *data);
@@ -3025,6 +3027,7 @@ int bnxt_hwrm_tx_ring_alloc(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
void bnxt_hwrm_tx_ring_free(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
bool close_path);
void bnxt_hwrm_cp_ring_free(struct bnxt *bp, struct bnxt_cp_ring_info *cpr);
+void bnxt_clear_one_cp_ring(struct bnxt *bp, struct bnxt_cp_ring_info *cpr);
int bnxt_total_tx_rings(struct bnxt *bp);
int __bnxt_hwrm_get_tx_rings(struct bnxt *bp, u16 fid, int *tx_rings);
int bnxt_nq_rings_in_use(struct bnxt *bp);
@@ -3071,6 +3074,7 @@ void bnxt_sync_ring_stats(struct bnxt *bp);
bool bnxt_rfs_capable(struct bnxt *bp, bool new_rss_ctx);
int bnxt_dbg_hwrm_rd_reg(struct bnxt *bp, u32 reg_off, u16 num_words,
u32 *reg_buf);
+void bnxt_reset_task(struct bnxt *bp, bool silent);
void bnxt_fw_exception(struct bnxt *bp);
void bnxt_fw_reset(struct bnxt *bp);
int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c
index 72927c32bf0c..27a61173dd7c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c
@@ -5,10 +5,12 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/pci.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
+#include "bnxt_mpc.h"
#include "bnxt_crypto.h"
static u32 bnxt_get_max_crypto_key_ctx(struct bnxt *bp, int key_type)
@@ -42,6 +44,7 @@ void bnxt_alloc_crypto_info(struct bnxt *bp,
u16 max_keys = le16_to_cpu(resp->max_key_ctxs_alloc);
struct bnxt_crypto_info *crypto = bp->crypto_info;
struct bnxt_kctx *kctx;
+ char name[64];
int i;
if (BNXT_VF(bp))
@@ -53,6 +56,15 @@ void bnxt_alloc_crypto_info(struct bnxt *bp,
"Unable to allocate crypto info\n");
return;
}
+ snprintf(name, sizeof(name), "bnxt_crypto-%s",
+ dev_name(&bp->pdev->dev));
+ crypto->mpc_cache =
+ kmem_cache_create(name,
+ sizeof(struct bnxt_crypto_cmd_ctx),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!crypto->mpc_cache)
+ goto alloc_err;
+
for (i = 0; i < BNXT_MAX_CRYPTO_KEY_TYPE; i++) {
kctx = &crypto->kctx[i];
kctx->type = i;
@@ -69,6 +81,10 @@ void bnxt_alloc_crypto_info(struct bnxt *bp,
}
crypto->max_key_ctxs_alloc = max_keys;
bp->fw_cap |= BNXT_FW_CAP_KTLS;
+ return;
+
+alloc_err:
+ kfree(crypto);
}
/**
@@ -119,8 +135,13 @@ void bnxt_clear_crypto(struct bnxt *bp)
*/
void bnxt_free_crypto_info(struct bnxt *bp)
{
+ struct bnxt_crypto_info *crypto = bp->crypto_info;
+
+ if (!crypto)
+ return;
bnxt_clear_crypto(bp);
- kfree(bp->crypto_info);
+ kmem_cache_destroy(crypto->mpc_cache);
+ kfree(crypto);
bp->crypto_info = NULL;
bp->fw_cap &= ~BNXT_FW_CAP_KTLS;
}
@@ -369,6 +390,82 @@ int bnxt_key_ctx_alloc_one(struct bnxt *bp, struct bnxt_kctx *kctx, u8 kind,
return -EAGAIN;
}
+#define BNXT_XMIT_CRYPTO_RETRY_MAX 10
+#define BNXT_XMIT_CRYPTO_MIN_TMO 100
+#define BNXT_XMIT_CRYPTO_MAX_TMO 150
+
+int bnxt_xmit_crypto_cmd(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
+ void *cmd, unsigned int len, unsigned int tmo)
+{
+ struct bnxt_crypto_info *crypto = bp->crypto_info;
+ struct bnxt_crypto_cmd_ctx *ctx = NULL;
+ unsigned long tmo_left, handle = 0;
+ int rc, retry = 0;
+
+ if (tmo) {
+ u32 kid = CE_CMD_KID(cmd);
+
+ ctx = kmem_cache_alloc(crypto->mpc_cache, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ init_completion(&ctx->cmp);
+ handle = (unsigned long)ctx;
+ ctx->kid = kid;
+ ctx->client = txr->tx_ring_struct.mpc_chnl_type;
+ ctx->status = 0;
+ /* One reference for this caller, one for the handle stored in
+ * the TX buf ring. The latter is dropped by
+ * bnxt_crypto_mpc_cmp() when the command is completed normally
+ * or after timeout.
+ */
+ refcount_set(&ctx->refcnt, 2);
+ retry = BNXT_XMIT_CRYPTO_RETRY_MAX;
+ might_sleep();
+ }
+ do {
+ spin_lock_bh(&txr->tx_lock);
+ rc = bnxt_start_xmit_mpc(bp, txr, cmd, len, handle);
+ spin_unlock_bh(&txr->tx_lock);
+ if (rc == -EBUSY && tmo && retry)
+ usleep_range(BNXT_XMIT_CRYPTO_MIN_TMO,
+ BNXT_XMIT_CRYPTO_MAX_TMO);
+ else
+ break;
+ } while (retry--);
+ if (rc || !tmo) {
+ /* The completion will never arrive, drop one reference */
+ if (ctx)
+ refcount_dec(&ctx->refcnt);
+ goto xmit_done;
+ }
+
+ tmo_left = wait_for_completion_timeout(&ctx->cmp, msecs_to_jiffies(tmo));
+ if (!tmo_left) {
+ netdev_warn(bp->dev, "crypto MP cmd %08x timed out\n",
+ *((u32 *)cmd));
+ bnxt_mpc_timeout(bp, txr);
+ rc = -ETIMEDOUT;
+ goto xmit_done;
+ }
+ if (ctx->status == BNXT_CMD_CTX_COMPLETED &&
+ CE_CMPL_STATUS(&ctx->ce_cmp) == CE_CMPL_STATUS_OK)
+ rc = 0;
+ else
+ rc = -EIO;
+xmit_done:
+ if (rc) {
+ u8 status = ctx ? ctx->status : 0;
+
+ netdev_warn(bp->dev,
+ "MPC transmit failed, ring idx %d, op 0x%x, kid 0x%x, status 0x%x\n",
+ txr->bnapi->index, CE_CMD_OP(cmd), CE_CMD_KID(cmd),
+ status);
+ }
+ if (ctx && refcount_dec_and_test(&ctx->refcnt))
+ kmem_cache_free(crypto->mpc_cache, ctx);
+ return rc;
+}
+
int bnxt_crypto_init(struct bnxt *bp)
{
struct bnxt_crypto_info *crypto = bp->crypto_info;
@@ -398,3 +495,42 @@ int bnxt_crypto_init(struct bnxt *bp)
return 0;
}
+
+void bnxt_crypto_mpc_cmp(struct bnxt *bp, u32 client, unsigned long handle,
+ struct bnxt_cmpl_entry cmpl[], u32 entries)
+{
+ struct bnxt_crypto_cmd_ctx *ctx;
+ struct ce_cmpl *cmp = NULL;
+ u32 len, kid;
+
+ if (likely(cmpl))
+ cmp = cmpl[0].cmpl;
+ if (!handle || entries != 1) {
+ if (entries != 1 && cmpl) {
+ netdev_warn(bp->dev, "Invalid entries %d with handle %lx cmpl %08x in %s()\n",
+ entries, handle, *(u32 *)cmp, __func__);
+ }
+ if (!handle)
+ return;
+ }
+ ctx = (void *)handle;
+ ctx->status = BNXT_CMD_CTX_COMPLETED;
+ if (unlikely(!cmpl)) {
+ ctx->status |= BNXT_CMD_CTX_RESET;
+ goto cmp_done;
+ }
+ kid = CE_CMPL_KID(cmp);
+ if (ctx->kid != kid || ctx->client != client || entries != 1) {
+ netdev_warn(bp->dev,
+ "Invalid CE cmpl 0x%08x with entries %d for client %d with status 0x%x, expected kid 0x%x and client %d\n",
+ *(u32 *)cmp, entries, client, ctx->status, ctx->kid,
+ ctx->client);
+ ctx->status |= BNXT_CMD_CTX_ERROR;
+ }
+ len = min_t(u32, cmpl[0].len, sizeof(ctx->ce_cmp));
+ memcpy(&ctx->ce_cmp, cmpl[0].cmpl, len);
+cmp_done:
+ complete(&ctx->cmp);
+ if (refcount_dec_and_test(&ctx->refcnt))
+ kmem_cache_free(bp->crypto_info->mpc_cache, ctx);
+}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h
index f88aa410efdd..8a2cbb7603a8 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h
@@ -4,6 +4,8 @@
#ifndef BNXT_CRYPTO_H
#define BNXT_CRYPTO_H
+#include <linux/refcount.h>
+
#define BNXT_MAX_TX_CRYPTO_KEYS 204800
#define BNXT_MAX_RX_CRYPTO_KEYS 204800
@@ -61,6 +63,80 @@ struct bnxt_crypto_info {
u16 max_key_ctxs_alloc;
struct bnxt_kctx kctx[BNXT_MAX_CRYPTO_KEY_TYPE];
+
+ struct kmem_cache *mpc_cache;
+};
+
+struct ce_delete_cmd {
+ __le32 ctx_kind_kid_opcode;
+ #define CE_DELETE_CMD_OPCODE_MASK 0xfUL
+ #define CE_DELETE_CMD_OPCODE_SFT 0
+ #define CE_DELETE_CMD_OPCODE_DEL 0x2UL
+ #define CE_DELETE_CMD_KID_MASK 0xfffff0UL
+ #define CE_DELETE_CMD_KID_SFT 4
+ #define CE_DELETE_CMD_CTX_KIND_MASK 0x1f000000UL
+ #define CE_DELETE_CMD_CTX_KIND_SFT 24
+ #define CE_DELETE_CMD_CTX_KIND_CK_TX (0x11UL << 24)
+ #define CE_DELETE_CMD_CTX_KIND_CK_RX (0x12UL << 24)
+};
+
+#define CE_CMD_OP_MASK 0x00000fU
+#define CE_CMD_KID_MASK 0xfffff0U
+#define CE_CMD_KID_SFT 4
+
+#define CE_CMD_OP(cmd_p) \
+ (le32_to_cpu(*(__le32 *)(cmd_p)) & CE_CMD_OP_MASK)
+
+#define CE_CMD_KID(cmd_p) \
+ ((le32_to_cpu(*(__le32 *)(cmd_p)) & CE_CMD_KID_MASK) >> CE_CMD_KID_SFT)
+
+struct ce_cmpl {
+ __le16 client_subtype_type;
+ #define CE_CMPL_TYPE_MASK 0x3fUL
+ #define CE_CMPL_TYPE_SFT 0
+ #define CE_CMPL_TYPE_MID_PATH_SHORT 0x1eUL
+ #define CE_CMPL_SUBTYPE_MASK 0xf00UL
+ #define CE_CMPL_SUBTYPE_SFT 8
+ #define CE_CMPL_SUBTYPE_SOLICITED (0x0UL << 8)
+ #define CE_CMPL_SUBTYPE_ERR (0x1UL << 8)
+ #define CE_CMPL_SUBTYPE_RESYNC (0x2UL << 8)
+ #define CE_CMPL_MP_CLIENT_MASK 0xf000UL
+ #define CE_CMPL_MP_CLIENT_SFT 12
+ #define CE_CMPL_MP_CLIENT_TCE (0x0UL << 12)
+ #define CE_CMPL_MP_CLIENT_RCE (0x1UL << 12)
+ __le16 status;
+ #define CE_CMPL_STATUS_MASK 0xfUL
+ #define CE_CMPL_STATUS_SFT 0
+ #define CE_CMPL_STATUS_OK 0x0UL
+ #define CE_CMPL_STATUS_CTX_LD_ERR 0x1UL
+ #define CE_CMPL_STATUS_FID_CHK_ERR 0x2UL
+ #define CE_CMPL_STATUS_CTX_VER_ERR 0x3UL
+ #define CE_CMPL_STATUS_DST_ID_ERR 0x4UL
+ #define CE_CMPL_STATUS_MP_CMD_ERR 0x5UL
+ u32 opaque;
+ __le32 v;
+ #define CE_CMPL_V 0x1UL
+ __le32 kid;
+ #define CE_CMPL_KID_MASK 0xfffffUL
+ #define CE_CMPL_KID_SFT 0
+};
+
+#define CE_CMPL_STATUS(ce_cmpl) \
+ (le16_to_cpu((ce_cmpl)->status) & CE_CMPL_STATUS_MASK)
+
+#define CE_CMPL_KID(ce_cmpl) \
+ (le32_to_cpu((ce_cmpl)->kid) & CE_CMPL_KID_MASK)
+
+struct bnxt_crypto_cmd_ctx {
+ struct completion cmp;
+ struct ce_cmpl ce_cmp;
+ refcount_t refcnt;
+ u32 kid;
+ u16 client;
+ u8 status;
+#define BNXT_CMD_CTX_COMPLETED 0x1
+#define BNXT_CMD_CTX_ERROR 0x2
+#define BNXT_CMD_CTX_RESET 0x4
};
#define BNXT_TCK(crypto) ((crypto)->kctx[BNXT_TX_CRYPTO_KEY_TYPE])
@@ -80,7 +156,11 @@ bool bnxt_kid_valid(struct bnxt_kctx *kctx, u32 id);
void bnxt_free_one_kctx(struct bnxt_kctx *kctx, u32 id);
int bnxt_key_ctx_alloc_one(struct bnxt *bp, struct bnxt_kctx *kctx, u8 kind,
u32 *id);
+int bnxt_xmit_crypto_cmd(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
+ void *cmd, unsigned int len, unsigned int tmo);
int bnxt_crypto_init(struct bnxt *bp);
+void bnxt_crypto_mpc_cmp(struct bnxt *bp, u32 client, unsigned long handle,
+ struct bnxt_cmpl_entry cmpl[], u32 entries);
#else
static inline void bnxt_alloc_crypto_info(struct bnxt *bp,
struct hwrm_func_qcaps_output *resp)
@@ -116,9 +196,24 @@ static inline int bnxt_key_ctx_alloc_one(struct bnxt *bp,
return -EOPNOTSUPP;
}
+static inline int bnxt_xmit_crypto_cmd(struct bnxt *bp,
+ struct bnxt_tx_ring_info *txr,
+ void *cmd, unsigned int len,
+ unsigned int tmo)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int bnxt_crypto_init(struct bnxt *bp)
{
return 0;
}
+
+static inline void bnxt_crypto_mpc_cmp(struct bnxt *bp, u32 client,
+ unsigned long handle,
+ struct bnxt_cmpl_entry cmpl[],
+ u32 entries)
+{
+}
#endif /* CONFIG_BNXT_TLS */
#endif /* BNXT_CRYPTO_H */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.c
index a8d757712b38..71ebeb6ff33a 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.c
@@ -9,6 +9,7 @@
#include "bnxt.h"
#include "bnxt_mpc.h"
+#include "bnxt_crypto.h"
void bnxt_alloc_mpc_info(struct bnxt *bp, u8 mpc_chnls_cap)
{
@@ -501,6 +502,206 @@ int bnxt_start_xmit_mpc(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
return 0;
}
+/* Returns true if the ring is successfully marked as closing. It
+ * means that there will be no more MPC transmissions and NAPI will now
+ * complete any MPC completions on the completion ring with NULL handles
+ * to signal abort.
+ */
+static bool bnxt_disable_mpc_ring(struct bnxt_mpc_info *mpc, int mpc_ring)
+{
+ struct bnxt_tx_ring_info *txr;
+ bool disabled = false;
+ int i;
+
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc_ring >= mpc->mpc_ring_count[i])
+ continue;
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ spin_lock_bh(&txr->tx_lock);
+ if (!READ_ONCE(txr->dev_state)) {
+ disabled = true;
+ WRITE_ONCE(txr->dev_state, BNXT_DEV_STATE_CLOSING);
+ }
+ spin_unlock_bh(&txr->tx_lock);
+ if (!disabled)
+ break;
+ }
+ /* Make sure napi polls see @dev_state change */
+ if (disabled)
+ synchronize_net();
+ return disabled;
+}
+
+static void bnxt_enable_mpc_ring(struct bnxt_mpc_info *mpc, int mpc_ring)
+{
+ struct bnxt_tx_ring_info *txr;
+ int i;
+
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc_ring >= mpc->mpc_ring_count[i])
+ continue;
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ WRITE_ONCE(txr->dev_state, 0);
+ }
+}
+
+static void bnxt_clear_one_mpc_entries(struct bnxt *bp,
+ struct bnxt_tx_ring_info *txr)
+{
+ struct bnxt_sw_mpc_tx_bd *tx_buf;
+ unsigned long handle;
+ int i, max_idx;
+ u32 client;
+
+ max_idx = bp->tx_nr_pages * TX_DESC_CNT;
+
+ for (i = 0; i < max_idx; i++) {
+ tx_buf = &txr->tx_mpc_buf_ring[i];
+ handle = tx_buf->handle;
+ if (handle) {
+ client = txr->tx_ring_struct.mpc_chnl_type;
+ bnxt_crypto_mpc_cmp(bp, client, handle, NULL, 0);
+ tx_buf->handle = 0;
+ }
+ }
+}
+
+static void bnxt_mpc_ring_stop(struct bnxt *bp, struct bnxt_mpc_info *mpc,
+ int mpc_ring)
+{
+ struct bnxt_tx_ring_info *txr;
+ struct bnxt_cp_ring_info *cpr;
+ int i;
+
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc->mpc_ring_count[i] > mpc_ring) {
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ bnxt_hwrm_tx_ring_free(bp, txr, true);
+ }
+ }
+ /* CP ring must be freed at the end to guarantee that the HWRM_DONE
+ * responses for HWRM_RING_FREE can still be seen on the CP ring.
+ */
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc->mpc_ring_count[i] > mpc_ring) {
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ cpr = txr->tx_cpr;
+ if (cpr)
+ bnxt_hwrm_cp_ring_free(bp, cpr);
+ }
+ }
+ /* No new DMA to the CP ring at this point, but we need to wait
+ * for any in-flight NAPI poll to finish before we zero the
+ * completion entries in the CP ring.
+ */
+ synchronize_net();
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc->mpc_ring_count[i] > mpc_ring) {
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ cpr = txr->tx_cpr;
+ if (cpr)
+ bnxt_clear_one_cp_ring(bp, cpr);
+ bnxt_clear_one_mpc_entries(bp, txr);
+ }
+ }
+}
+
+static int bnxt_mpc_ring_start(struct bnxt *bp, struct bnxt_mpc_info *mpc,
+ int mpc_ring)
+{
+ struct bnxt_tx_ring_info *txr;
+ int i, rc;
+
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc->mpc_ring_count[i] > mpc_ring) {
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ txr->tx_prod = 0;
+ txr->tx_cons = 0;
+ txr->tx_hw_cons = 0;
+ rc = bnxt_hwrm_one_mpc_ring_alloc(bp, txr);
+ if (rc)
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/* Called from bnxt_sp_task() with netdev_lock held. If the device is not
+ * open there is nothing to do: the open path will re-allocate and
+ * re-initialize all MPC rings.
+ */
+void bnxt_mpc_ring_reset_task(struct bnxt *bp)
+{
+ struct bnxt_mpc_info *mpc = bp->mpc_info;
+ struct bnxt_tx_ring_info *txr;
+ int i, j;
+
+ if (!mpc || !test_bit(BNXT_STATE_OPEN, &bp->state))
+ return;
+
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ for (j = 0; j < mpc->mpc_ring_count[i]; j++) {
+ txr = &mpc->mpc_rings[i][j];
+ if (READ_ONCE(txr->dev_state) != BNXT_DEV_STATE_CLOSING)
+ continue;
+
+ /* Resets the whole ring (both types) */
+ netdev_warn(bp->dev, "Resetting MPC ring %d\n", j);
+ bnxt_mpc_ring_stop(bp, mpc, j);
+ if (bnxt_mpc_ring_start(bp, mpc, j)) {
+ netdev_err(bp->dev, "Error starting MPC ring %d, resetting device\n",
+ j);
+ bnxt_mpc_ring_stop(bp, mpc, j);
+ bnxt_reset_task(bp, true);
+ /* bnxt_reset_task() will clear everything */
+ return;
+ }
+ bnxt_enable_mpc_ring(mpc, j);
+ }
+ }
+}
+
+static int bnxt_mpc_ring_reset(struct bnxt *bp, int mpc_ring)
+{
+ struct bnxt_mpc_info *mpc = bp->mpc_info;
+ struct bnxt_tx_ring_info *txr;
+ int i;
+
+ if (!mpc)
+ return 0;
+ if (mpc_ring >= mpc->mpc_cp_rings)
+ return -EINVAL;
+
+ if (!bnxt_disable_mpc_ring(mpc, mpc_ring))
+ return 0;
+
+ /* Completion handles will no longer be processed at this point
+ * from NAPI, so we can complete the outstanding commands and
+ * release the command contexts.
+ */
+ for (i = 0; i < BNXT_MPC_TYPE_MAX; i++) {
+ if (mpc->mpc_ring_count[i] > mpc_ring) {
+ txr = &mpc->mpc_rings[i][mpc_ring];
+ bnxt_clear_one_mpc_entries(bp, txr);
+ }
+ }
+
+ /* If the device is going down, the reopen will re-allocate and
+ * re-initialize all MPC rings. Otherwise defer the reset to
+ * bnxt_sp_task() to safely hold the netdev_lock.
+ */
+ if (test_bit(BNXT_STATE_OPEN, &bp->state))
+ bnxt_queue_sp_work(bp, BNXT_MPC_RESET_SP_EVENT);
+ return 0;
+}
+
+int bnxt_mpc_timeout(struct bnxt *bp, struct bnxt_tx_ring_info *txr)
+{
+ if (txr->tx_ring_struct.queue_id == BNXT_MPC_QUEUE_ID)
+ return bnxt_mpc_ring_reset(bp, txr->txq_index);
+ return -EINVAL;
+}
+
static bool bnxt_mpc_unsolicit(struct mpc_cmp *mpcmp)
{
u32 client = MPC_CMP_CLIENT_TYPE(mpcmp);
@@ -517,6 +718,7 @@ int bnxt_mpc_cmp(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, u32 *raw_cons)
u16 cons = RING_CMP(*raw_cons);
struct mpc_cmp *mpcmp, *mpcmp1;
u32 tmp_raw_cons = *raw_cons;
+ unsigned long handle = 0;
u32 client, cmpl_num;
u8 type;
@@ -565,11 +767,15 @@ int bnxt_mpc_cmp(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, u32 *raw_cons)
goto cmp_done;
}
mpc_buf = &txr->tx_mpc_buf_ring[RING_TX(bp, tx_cons)];
- mpc_buf->handle = 0;
+ if (!READ_ONCE(txr->dev_state)) {
+ handle = mpc_buf->handle;
+ mpc_buf->handle = 0;
+ }
tx_cons += mpc_buf->inline_bds;
WRITE_ONCE(txr->tx_cons, tx_cons);
txr->tx_hw_cons = RING_TX(bp, tx_cons);
}
+ bnxt_crypto_mpc_cmp(bp, client, handle, cmpl_entry_arr, cmpl_num);
cmp_done:
*raw_cons = tmp_raw_cons;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.h
index 91fb27ba8090..b0b1e783cdc7 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_mpc.h
@@ -22,6 +22,8 @@ enum bnxt_mpc_type {
#define BNXT_DFLT_MPC_TCE BNXT_MAX_MPC
#define BNXT_DFLT_MPC_RCE BNXT_MAX_MPC
+#define BNXT_MPC_TMO_MSECS 1000
+
struct bnxt_mpc_info {
u8 mpc_chnls_cap;
u8 mpc_cp_rings;
@@ -109,6 +111,8 @@ void bnxt_hwrm_mpc_ring_free(struct bnxt *bp, bool close_path);
struct bnxt_tx_ring_info *bnxt_select_mpc_ring(struct bnxt *bp, int ring_type);
int bnxt_start_xmit_mpc(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
void *data, unsigned int len, unsigned long handle);
+void bnxt_mpc_ring_reset_task(struct bnxt *bp);
+int bnxt_mpc_timeout(struct bnxt *bp, struct bnxt_tx_ring_info *txr);
int bnxt_mpc_cmp(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, u32 *raw_cons);
#else
static inline void bnxt_alloc_mpc_info(struct bnxt *bp, u8 mpc_chnls_cap)
@@ -204,6 +208,16 @@ static inline int bnxt_start_xmit_mpc(struct bnxt *bp,
return -EOPNOTSUPP;
}
+static inline void bnxt_mpc_ring_reset_task(struct bnxt *bp)
+{
+}
+
+static inline int bnxt_mpc_timeout(struct bnxt *bp,
+ struct bnxt_tx_ring_info *txr)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int bnxt_mpc_cmp(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
u32 *raw_cons)
{
--
2.51.0
next prev parent reply other threads:[~2026-07-10 4:25 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 4:23 [PATCH net-next v5 00/15] bnxt_en: Add kTLS TX offload support Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 01/15] bnxt_en: Add Midpath channel information Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 02/15] bnxt_en: Account for the MPC TX and CP rings Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 03/15] bnxt_en: Set default MPC ring count Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 04/15] bnxt_en: Rename xdp_tx_lock to tx_lock Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 05/15] bnxt_en: Allocate and free MPC software structures Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 06/15] bnxt_en: Allocate and free MPC channels from firmware Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 07/15] bnxt_en: Allocate crypto structure and backing store Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 08/15] bnxt_en: Reserve crypto RX and TX key contexts on a PF Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 09/15] bnxt_en: Add infrastructure for crypto key context IDs Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 10/15] bnxt_en: Add MPC transmit and completion functions Michael Chan
2026-07-10 4:23 ` Michael Chan [this message]
2026-07-10 4:23 ` [PATCH net-next v5 12/15] bnxt_en: Support kTLS TX offload by implementing .tls_dev_add/del() Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 13/15] bnxt_en: Implement kTLS TX normal path Michael Chan
2026-07-10 4:23 ` [PATCH net-next v5 14/15] bnxt_en: Add support for inline transmit BDs Michael Chan
2026-07-10 4:24 ` [PATCH net-next v5 15/15] bnxt_en: Add kTLS retransmission support Michael Chan
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=20260710042400.3996847-12-michael.chan@broadcom.com \
--to=michael.chan@broadcom.com \
--cc=andrew+netdev@lunn.ch \
--cc=andrew.gospodarek@broadcom.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pavan.chebbi@broadcom.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