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 v3 09/15] bnxt_en: Add infrastructure for crypto key context IDs
Date: Sun, 14 Jun 2026 00:24:01 -0700 [thread overview]
Message-ID: <20260614072407.2761092-10-michael.chan@broadcom.com> (raw)
In-Reply-To: <20260614072407.2761092-1-michael.chan@broadcom.com>
Each kTLS connection requires a crypto key context ID (KID). These KIDs
are allocated from the firmware in batches. Add data structure to store
these IDs. The bnxt_kid_info structure stores a batch of IDs and it can be
linked as we allocate more batches. There is a bitmap in the structure
to keep track of which ones are in use. Add APIs to allocate and free
these KIDs.
Once allocated, these KIDs are not freed during run-time. They are
re-used for new connections. FW reset or HWRM_FUNC_RESET will free
all KIDs. Call bnxt_clear_crypto() to clear all KIDs in the driver's
structures during these reset events.
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>
---
v3:
Use a larger (12-bit) epoch value for the keys.
Improve comments, kerneldoc, and dmesg.
Make sure bnxt_clear_crypto() is called during shutdown with the
BNXT_STATE_OPEN flag cleared.
v2:
https://lore.kernel.org/netdev/20260512212105.3488258-10-michael.chan@broadcom.com/
---
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 10 +
.../net/ethernet/broadcom/bnxt/bnxt_crypto.c | 284 ++++++++++++++++++
.../net/ethernet/broadcom/bnxt/bnxt_crypto.h | 66 ++++
3 files changed, 360 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 730af6aee0c8..6eef518a96b0 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -12854,6 +12854,8 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
bnxt_ulp_irq_stop(bp);
bnxt_free_ctx_mem(bp, false);
bnxt_dcb_free(bp);
+ if (fw_reset || caps_change)
+ bnxt_clear_crypto(bp);
rc = bnxt_fw_init_one(bp);
if (rc) {
clear_bit(BNXT_STATE_FW_RESET_DET, &bp->state);
@@ -14590,6 +14592,7 @@ static void bnxt_fw_reset_close(struct bnxt *bp)
bnxt_hwrm_func_drv_unrgtr(bp);
if (pci_is_enabled(bp->pdev))
pci_disable_device(bp->pdev);
+ bnxt_clear_crypto(bp);
bnxt_free_ctx_mem(bp, false);
}
@@ -17234,6 +17237,12 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rc = bnxt_dl_register(bp);
if (rc)
goto init_err_dl;
+ rc = bnxt_crypto_init(bp);
+ if (rc) {
+ bnxt_free_crypto_info(bp);
+ netdev_warn(bp->dev, "Failed to initialize crypto offload, err = %d\n",
+ rc);
+ }
INIT_LIST_HEAD(&bp->usr_fltr_list);
@@ -17460,6 +17469,7 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev,
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
+ bnxt_clear_crypto(bp);
bnxt_free_ctx_mem(bp, false);
netdev_unlock(netdev);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c
index e1047b4284ea..94dfbcc460c7 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.c
@@ -8,6 +8,7 @@
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
+#include "bnxt_hwrm.h"
#include "bnxt_crypto.h"
static u32 bnxt_get_max_crypto_key_ctx(struct bnxt *bp, int key_type)
@@ -55,6 +56,10 @@ void bnxt_alloc_crypto_info(struct bnxt *bp,
for (i = 0; i < BNXT_MAX_CRYPTO_KEY_TYPE; i++) {
kctx = &crypto->kctx[i];
kctx->type = i;
+ INIT_LIST_HEAD(&kctx->list);
+ spin_lock_init(&kctx->lock);
+ atomic_set(&kctx->alloc_pending, 0);
+ init_waitqueue_head(&kctx->alloc_pending_wq);
}
bp->crypto_info = crypto;
}
@@ -66,6 +71,43 @@ void bnxt_alloc_crypto_info(struct bnxt *bp,
bp->fw_cap |= BNXT_FW_CAP_KTLS;
}
+/**
+ * bnxt_clear_crypto - Clear all crypto key contexts
+ * @bp: pointer to bnxt device
+ *
+ * Clears all key context allocations during shutdown or firmware reset.
+ * Frees all key info structures and bitmaps, and increments the epoch
+ * counter to invalidate any outstanding key references.
+ *
+ * This function assumes serialization (called during shutdown) and does
+ * not use locking.
+ *
+ * Context: Process context during shutdown/reset
+ */
+void bnxt_clear_crypto(struct bnxt *bp)
+{
+ struct bnxt_crypto_info *crypto = bp->crypto_info;
+ struct bnxt_kid_info *kid, *tmp;
+ struct bnxt_kctx *kctx;
+ int i;
+
+ if (!crypto)
+ return;
+
+ /* Only called when shutting down or FW reset with BNXT_STATE_OPEN
+ * cleared, so no concurrent access. No protection needed.
+ */
+ for (i = 0; i < BNXT_MAX_CRYPTO_KEY_TYPE; i++) {
+ kctx = &crypto->kctx[i];
+ list_for_each_entry_safe(kid, tmp, &kctx->list, list) {
+ list_del(&kid->list);
+ kfree(kid);
+ }
+ kctx->total_alloc = 0;
+ kctx->epoch++;
+ }
+}
+
/**
* bnxt_free_crypto_info - Free crypto offload resources
* @bp: pointer to bnxt device
@@ -77,6 +119,7 @@ void bnxt_alloc_crypto_info(struct bnxt *bp,
*/
void bnxt_free_crypto_info(struct bnxt *bp)
{
+ bnxt_clear_crypto(bp);
kfree(bp->crypto_info);
bp->crypto_info = NULL;
}
@@ -112,3 +155,244 @@ void bnxt_hwrm_reserve_pf_key_ctxs(struct bnxt *bp,
if (rx)
req->enables |= cpu_to_le32(FUNC_CFG_REQ_ENABLES_KTLS_RX_KEY_CTXS);
}
+
+static int bnxt_key_ctx_store(struct bnxt_kctx *kctx, __le32 *key_buf, u32 num,
+ bool contig, u8 kind, u32 *id)
+{
+ struct bnxt_kid_info *kid;
+ u32 i;
+
+ for (i = 0; i < num; ) {
+ kid = kzalloc_obj(*kid);
+ /* If we cannot store the IDs, they will be lost and only
+ * reclaimed by the FW during reset/reinit.
+ */
+ if (!kid)
+ return -ENOMEM;
+ kid->start_id = le32_to_cpu(key_buf[i]);
+ kid->type = kctx->type;
+ kid->kind = kind;
+ if (contig)
+ kid->count = num;
+ else
+ kid->count = 1;
+ bitmap_set(kid->ids, 0, kid->count);
+ if (id && !i) {
+ clear_bit(0, kid->ids);
+ *id = BNXT_SET_KID(kctx, kid->start_id);
+ }
+ spin_lock(&kctx->lock);
+ list_add_tail_rcu(&kid->list, &kctx->list);
+ WRITE_ONCE(kctx->total_alloc,
+ READ_ONCE(kctx->total_alloc) + kid->count);
+ spin_unlock(&kctx->lock);
+ i += kid->count;
+ }
+ return 0;
+}
+
+/* Note that the driver does not free the key contexts. They are freed
+ * by the FW during FLR and HWRM_FUNC_RESET.
+ */
+static int bnxt_hwrm_key_ctx_alloc(struct bnxt *bp, struct bnxt_kctx *kctx,
+ u8 kind, u32 num, u32 *id)
+{
+ struct bnxt_crypto_info *crypto = bp->crypto_info;
+ struct hwrm_func_key_ctx_alloc_output *resp;
+ struct hwrm_func_key_ctx_alloc_input *req;
+ dma_addr_t mapping;
+ int pending_count;
+ __le32 *key_buf;
+ u32 num_alloc;
+ bool contig;
+ int rc;
+
+ num = min3(num, crypto->max_key_ctxs_alloc, (u32)BNXT_KID_BATCH_SIZE);
+ rc = hwrm_req_init(bp, req, HWRM_FUNC_KEY_CTX_ALLOC);
+ if (rc)
+ return rc;
+
+ key_buf = hwrm_req_dma_slice(bp, req, num * 4, &mapping);
+ if (!key_buf) {
+ rc = -ENOMEM;
+ goto key_alloc_exit;
+ }
+ req->dma_bufr_size_bytes = cpu_to_le32(num * 4);
+ req->host_dma_addr = cpu_to_le64(mapping);
+ resp = hwrm_req_hold(bp, req);
+
+ req->key_ctx_type = kctx->type;
+ req->num_key_ctxs = cpu_to_le16(num);
+
+ pending_count = atomic_inc_return(&kctx->alloc_pending);
+ rc = hwrm_req_send(bp, req);
+ atomic_dec(&kctx->alloc_pending);
+ if (rc)
+ goto key_alloc_exit_wake;
+
+ num_alloc = le16_to_cpu(resp->num_key_ctxs_allocated);
+ if (num_alloc > num) {
+ netdev_warn(bp->dev,
+ "FW allocated more type %d keys (%d) than requested (%d)\n",
+ kctx->type, num_alloc, num);
+ } else if (!num_alloc) {
+ netdev_warn(bp->dev,
+ "FW allocated 0 type %d keys\n", kctx->type);
+ rc = -ENOENT;
+ goto key_alloc_exit_wake;
+ } else {
+ num = num_alloc;
+ }
+ contig = resp->flags &
+ FUNC_KEY_CTX_ALLOC_RESP_FLAGS_KEY_CTXS_CONTIGUOUS;
+ rc = bnxt_key_ctx_store(kctx, key_buf, num, contig, kind, id);
+
+key_alloc_exit_wake:
+ if (pending_count >= BNXT_KCTX_ALLOC_PENDING_MAX)
+ wake_up_all(&kctx->alloc_pending_wq);
+key_alloc_exit:
+ hwrm_req_drop(bp, req);
+ return rc;
+}
+
+bool bnxt_kid_valid(struct bnxt_kctx *kctx, u32 id)
+{
+ struct bnxt_kid_info *kid;
+ bool valid = false;
+ u32 epoch;
+
+ epoch = BNXT_KID_EPOCH(id);
+ if (epoch != kctx->epoch)
+ return false;
+
+ id = BNXT_KID_HW(id);
+ rcu_read_lock();
+ list_for_each_entry_rcu(kid, &kctx->list, list) {
+ if (id >= kid->start_id && id < kid->start_id + kid->count) {
+ if (!test_bit(id - kid->start_id, kid->ids)) {
+ valid = true;
+ break;
+ }
+ }
+ }
+ rcu_read_unlock();
+ return valid;
+}
+
+static int bnxt_alloc_one_kctx(struct bnxt_kctx *kctx, u8 kind, u32 *id)
+{
+ struct bnxt_kid_info *kid;
+ int rc = -ENOMEM;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(kid, &kctx->list, list) {
+ u32 idx = 0;
+
+ if (kid->kind != kind)
+ continue;
+ do {
+ idx = find_next_bit(kid->ids, kid->count, idx);
+ if (idx >= kid->count)
+ break;
+ if (test_and_clear_bit(idx, kid->ids)) {
+ *id = BNXT_SET_KID(kctx, kid->start_id + idx);
+ rc = 0;
+ goto alloc_done;
+ }
+ } while (1);
+ }
+
+alloc_done:
+ rcu_read_unlock();
+ return rc;
+}
+
+/**
+ * bnxt_free_one_ctx - Free a key context for later re-use
+ * @kctx: pointer to bnxt_kctx key context structure
+ * @id: Key context ID
+ *
+ * This function is called to free a key context ID when the offload
+ * using the ID has successfully terminated or aborted. If the offload
+ * cannot be terminated, the caller should not call this function to free
+ * the ID. The ID will only be recycled by the FW during reset/reinit.
+ */
+void bnxt_free_one_kctx(struct bnxt_kctx *kctx, u32 id)
+{
+ struct bnxt_kid_info *kid;
+
+ id = BNXT_KID_HW(id);
+ rcu_read_lock();
+ list_for_each_entry_rcu(kid, &kctx->list, list) {
+ if (id >= kid->start_id && id < kid->start_id + kid->count) {
+ set_bit(id - kid->start_id, kid->ids);
+ break;
+ }
+ }
+ rcu_read_unlock();
+}
+
+#define BNXT_KCTX_ALLOC_RETRY_MAX 3
+
+int bnxt_key_ctx_alloc_one(struct bnxt *bp, struct bnxt_kctx *kctx, u8 kind,
+ u32 *id)
+{
+ int rc, retry = 0;
+
+ while (retry++ < BNXT_KCTX_ALLOC_RETRY_MAX) {
+ rc = bnxt_alloc_one_kctx(kctx, kind, id);
+ if (!rc)
+ return 0;
+
+ /* When approaching the max, multiple threads may proceed
+ * and exceed the max. Some may fail the serialized HWRM call
+ * later when the max is exceeded.
+ */
+ if ((READ_ONCE(kctx->total_alloc) + BNXT_KID_BATCH_SIZE) >
+ kctx->max_ctx)
+ return -ENOSPC;
+
+ if (!BNXT_KCTX_ALLOC_OK(kctx)) {
+ wait_event(kctx->alloc_pending_wq,
+ BNXT_KCTX_ALLOC_OK(kctx));
+ continue;
+ }
+ rc = bnxt_hwrm_key_ctx_alloc(bp, kctx, kind,
+ BNXT_KID_BATCH_SIZE, id);
+ if (!rc)
+ return 0;
+ }
+ return -EAGAIN;
+}
+
+int bnxt_crypto_init(struct bnxt *bp)
+{
+ struct bnxt_crypto_info *crypto = bp->crypto_info;
+ struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
+ struct bnxt_hw_crypto_resc *crypto_resc;
+ int rc;
+
+ if (!crypto || !BNXT_SUPPORTS_KTLS(bp))
+ return 0;
+
+ crypto_resc = &hw_resc->crypto_resc;
+ BNXT_TCK(crypto).max_ctx = crypto_resc->resv_tx_key_ctxs;
+ BNXT_RCK(crypto).max_ctx = crypto_resc->resv_rx_key_ctxs;
+
+ if (!BNXT_TCK(crypto).max_ctx || !BNXT_RCK(crypto).max_ctx) {
+ bnxt_free_crypto_info(bp);
+ return 0;
+ }
+
+ rc = bnxt_hwrm_key_ctx_alloc(bp, &BNXT_TCK(crypto), BNXT_CTX_KIND_CK_TX,
+ BNXT_KID_BATCH_SIZE, NULL);
+ if (rc)
+ return rc;
+
+ rc = bnxt_hwrm_key_ctx_alloc(bp, &BNXT_RCK(crypto), BNXT_CTX_KIND_CK_RX,
+ BNXT_KID_BATCH_SIZE, NULL);
+ if (rc)
+ return rc;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h
index e090491006db..c5a5081b31fa 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_crypto.h
@@ -16,11 +16,43 @@ enum bnxt_crypto_type {
BNXT_MAX_CRYPTO_KEY_TYPE,
};
+#define BNXT_KID_BATCH_SIZE 128
+
+struct bnxt_kid_info {
+ struct list_head list;
+ u8 type;
+ u8 kind;
+ u32 start_id;
+ u32 count;
+ DECLARE_BITMAP(ids, BNXT_KID_BATCH_SIZE);
+};
+
struct bnxt_kctx {
+ struct list_head list;
+ /* to serialize update to the linked list and total_alloc */
+ spinlock_t lock;
u8 type;
+ u16 epoch;
+ u32 total_alloc;
u32 max_ctx;
+ atomic_t alloc_pending;
+#define BNXT_KCTX_ALLOC_PENDING_MAX 8
+ wait_queue_head_t alloc_pending_wq;
};
+#define BNXT_KID_HW_MASK 0x000fffff
+#define BNXT_KID_HW(kid) ((kid) & BNXT_KID_HW_MASK)
+#define BNXT_KID_EPOCH_MASK 0xfff00000
+#define BNXT_KID_EPOCH_SHIFT 20
+#define BNXT_KID_EPOCH(kid) (((kid) & BNXT_KID_EPOCH_MASK) >> \
+ BNXT_KID_EPOCH_SHIFT)
+
+#define BNXT_SET_KID(kctx, kid) \
+ ((kid) | ((u32)(kctx)->epoch << BNXT_KID_EPOCH_SHIFT))
+
+#define BNXT_KCTX_ALLOC_OK(kctx) \
+ (atomic_read(&((kctx)->alloc_pending)) < BNXT_KCTX_ALLOC_PENDING_MAX)
+
struct bnxt_crypto_info {
u16 max_key_ctxs_alloc;
@@ -30,18 +62,31 @@ struct bnxt_crypto_info {
#define BNXT_TCK(crypto) ((crypto)->kctx[BNXT_TX_CRYPTO_KEY_TYPE])
#define BNXT_RCK(crypto) ((crypto)->kctx[BNXT_RX_CRYPTO_KEY_TYPE])
+#define BNXT_CTX_KIND_CK_TX 0x11
+#define BNXT_CTX_KIND_CK_RX 0x12
+
#ifdef CONFIG_BNXT_TLS
void bnxt_alloc_crypto_info(struct bnxt *bp,
struct hwrm_func_qcaps_output *resp);
+void bnxt_clear_crypto(struct bnxt *bp);
void bnxt_free_crypto_info(struct bnxt *bp);
void bnxt_hwrm_reserve_pf_key_ctxs(struct bnxt *bp,
struct hwrm_func_cfg_input *req);
+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_crypto_init(struct bnxt *bp);
#else
static inline void bnxt_alloc_crypto_info(struct bnxt *bp,
struct hwrm_func_qcaps_output *resp)
{
}
+static inline void bnxt_clear_crypto(struct bnxt *bp)
+{
+}
+
static inline void bnxt_free_crypto_info(struct bnxt *bp)
{
}
@@ -50,5 +95,26 @@ static inline void bnxt_hwrm_reserve_pf_key_ctxs(struct bnxt *bp,
struct hwrm_func_cfg_input *req)
{
}
+
+static inline bool bnxt_kid_valid(struct bnxt_kctx *kctx, u32 id)
+{
+ return false;
+}
+
+static inline void bnxt_free_one_kctx(struct bnxt_kctx *kctx, u32 id)
+{
+}
+
+static inline int bnxt_key_ctx_alloc_one(struct bnxt *bp,
+ struct bnxt_kctx *kctx, u8 kind,
+ u32 *id)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int bnxt_crypto_init(struct bnxt *bp)
+{
+ return 0;
+}
#endif /* CONFIG_BNXT_TLS */
#endif /* BNXT_CRYPTO_H */
--
2.51.0
next prev parent reply other threads:[~2026-06-14 7:25 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-14 7:23 [PATCH net-next v3 00/15] bnxt_en: Add kTLS TX offload support Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 01/15] bnxt_en: Add Midpath channel information Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 02/15] bnxt_en: Account for the MPC TX and CP rings Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 03/15] bnxt_en: Set default MPC ring count Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 04/15] bnxt_en: Rename xdp_tx_lock to tx_lock Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 05/15] bnxt_en: Allocate and free MPC software structures Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 06/15] bnxt_en: Allocate and free MPC channels from firmware Michael Chan
2026-06-14 7:23 ` [PATCH net-next v3 07/15] bnxt_en: Allocate crypto structure and backing store Michael Chan
2026-06-14 7:24 ` [PATCH net-next v3 08/15] bnxt_en: Reserve crypto RX and TX key contexts on a PF Michael Chan
2026-06-14 7:24 ` Michael Chan [this message]
2026-06-14 7:24 ` [PATCH net-next v3 10/15] bnxt_en: Add MPC transmit and completion functions Michael Chan
2026-06-14 7:24 ` [PATCH net-next v3 11/15] bnxt_en: Add crypto MPC transmit/completion infrastructure Michael Chan
2026-06-14 7:24 ` [PATCH net-next v3 12/15] bnxt_en: Support kTLS TX offload by implementing .tls_dev_add/del() Michael Chan
2026-06-14 7:24 ` [PATCH net-next v3 13/15] bnxt_en: Implement kTLS TX normal path Michael Chan
2026-06-14 7:24 ` [PATCH net-next v3 14/15] bnxt_en: Add support for inline transmit BDs Michael Chan
2026-06-14 7:24 ` [PATCH net-next v3 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=20260614072407.2761092-10-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