git: 9e69060da5ea - main - if_bnxt: add HW-based TX completion coalescing
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 09 Oct 2026 10:12:33 UTC
The branch main has been updated by ssaxena:
URL: https://cgit.FreeBSD.org/src/commit/?id=9e69060da5eaa4a596be96a4a84b6484661466aa
commit 9e69060da5eaa4a596be96a4a84b6484661466aa
Author: Sumit Saxena <sumit.saxena@broadcom.com>
AuthorDate: 2026-08-03 09:06:21 +0000
Commit: Sumit Saxena <ssaxena@FreeBSD.org>
CommitDate: 2026-10-09 08:24:42 +0000
if_bnxt: add HW-based TX completion coalescing
When firmware advertises BNXT_FLAG_TX_COAL_CMPL, request coalesced
completions via cmpl_coal_cnt on TX ring alloc and decode the
resulting tx_cmpl_coal_t records using a free-running SQ consumer
index per ring, instead of one completion per BD. Add the
tx_hw_coal_cnt sysctl to tune the firmware coalescing count code.
Reviewed by: gallatin
MFC after: 2 weeks
Sponsored by: Broadcom Inc.
Differential Revision: https://reviews.freebsd.org/D58599
---
sys/dev/bnxt/bnxt_en/bnxt.h | 8 ++++++++
sys/dev/bnxt/bnxt_en/bnxt_hwrm.c | 4 ++++
sys/dev/bnxt/bnxt_en/bnxt_sysctl.c | 33 +++++++++++++++++++++++++++++++++
sys/dev/bnxt/bnxt_en/bnxt_txrx.c | 31 +++++++++++++++++++++++++++++++
sys/dev/bnxt/bnxt_en/if_bnxt.c | 1 +
5 files changed, 77 insertions(+)
diff --git a/sys/dev/bnxt/bnxt_en/bnxt.h b/sys/dev/bnxt/bnxt_en/bnxt.h
index 35f1b9e97a00..ac91661eda7f 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt.h
@@ -615,6 +615,7 @@ struct bnxt_ring {
u32 db_key32;
};
uint32_t db_ring_mask;
+ uint32_t free_flow_cons; /* HW TX-completion-coalescing SQ consumer */
uint32_t db_epoch_mask;
uint8_t db_epoch_shift;
@@ -1321,6 +1322,7 @@ struct bnxt_softc {
#define BNXT_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO BIT_ULL(47)
#define BNXT_FW_CAP_ENABLE_RDMA_SRIOV BIT_ULL(48)
#define BNXT_FW_CAP_RSS_TCAM BIT_ULL(49)
+ #define BNXT_FLAG_TX_COAL_CMPL BIT_ULL(51)
#define BNXT_FW_CAP_SW_MAX_RESOURCE_LIMITS BIT_ULL(61)
#define BNXT_SW_RES_LMT(bp) ((bp)->fw_cap & BNXT_FW_CAP_SW_MAX_RESOURCE_LIMITS)
@@ -1431,6 +1433,12 @@ struct bnxt_softc {
#define BNXT_CAGR_TICK_RES_MAX 0x4
#define BNXT_CAGR_TICK_RES_OFFSET 16
uint32_t cagr_tick_res;
+ /* Firmware coalesces completions autonomously based on cmpl_coal_cnt
+ * (only valid when BNXT_FLAG_TX_COAL_CMPL is set); raw codes map to
+ * counts 4,8,12,16,24,32,48,64,96,128,192,256,320,384,MAX. */
+#define BNXT_TX_HW_COAL_CNT_DEFAULT \
+ HWRM_RING_ALLOC_INPUT_CMPL_COAL_CNT_COAL_64 /* code 8 = up to 64 pkts */
+ uint8_t tx_hw_coal_cnt;
};
struct bnxt_filter_info {
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
index 11d40a1a528a..6d5785a6fd5e 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
@@ -1278,6 +1278,8 @@ bnxt_hwrm_func_qcaps(struct bnxt_softc *softc)
if (BNXT_PF(softc) &&
(flags_ext & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_EXT_VF_CFG_ASYNC_FOR_PF_SUPPORTED))
softc->fw_cap |= BNXT_FW_CAP_VF_CFG_FOR_PF;
+ if (flags_ext & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_EXT_TX_COAL_CMPL_CAP)
+ softc->fw_cap |= BNXT_FLAG_TX_COAL_CMPL;
flags_ext2 = htole32(resp->flags_ext2);
if (flags_ext2 & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_EXT2_RX_ALL_PKTS_TIMESTAMPS_SUPPORTED)
@@ -1984,6 +1986,8 @@ bnxt_hwrm_ring_alloc(struct bnxt_softc *softc, uint8_t type,
req.stat_ctx_id = htole32(cp_ring->stats_ctx_id);
req.enables |= htole32(
HWRM_RING_ALLOC_INPUT_ENABLES_STAT_CTX_ID_VALID);
+ if (softc->fw_cap & BNXT_FLAG_TX_COAL_CMPL)
+ req.cmpl_coal_cnt = softc->tx_hw_coal_cnt;
break;
case HWRM_RING_ALLOC_INPUT_RING_TYPE_RX:
if (!BNXT_CHIP_P5_PLUS(softc))
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
index c8a0f1962a13..729d0aa1f5c7 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
@@ -1580,6 +1580,30 @@ bnxt_set_cagr_tick_res(SYSCTL_HANDLER_ARGS) {
return rc;
}
+static int
+bnxt_set_tx_hw_coal_cnt(SYSCTL_HANDLER_ARGS)
+{
+ struct bnxt_softc *softc = arg1;
+ uint32_t val;
+ int rc;
+
+ if (softc == NULL)
+ return EBUSY;
+
+ val = softc->tx_hw_coal_cnt;
+
+ rc = sysctl_handle_int(oidp, (int *)&val, 0, req);
+ if (rc || !req->newptr)
+ return rc;
+
+ if (val > HWRM_RING_ALLOC_INPUT_CMPL_COAL_CNT_LAST)
+ return EINVAL;
+
+ softc->tx_hw_coal_cnt = (uint8_t)val;
+
+ return 0;
+}
+
static
void simulate_reset(struct bnxt_softc *bp, char *fwcli_string)
{
@@ -1675,6 +1699,15 @@ bnxt_create_config_sysctls_pre(struct bnxt_softc *softc)
SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "cagr_tick_res",
CTLTYPE_INT | CTLFLAG_RDTUN | CTLFLAG_MPSAFE, softc, 0,
bnxt_set_cagr_tick_res, "I", "CAGR tick resolution");
+
+ softc->tx_hw_coal_cnt = BNXT_TX_HW_COAL_CNT_DEFAULT;
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "tx_hw_coal_cnt",
+ CTLTYPE_INT | CTLFLAG_RDTUN | CTLFLAG_MPSAFE, softc, 0,
+ bnxt_set_tx_hw_coal_cnt, "I",
+ "HW-driven TX coalesced-completion packet count code for ring_alloc "
+ "(0=off 1=4 2=8 3=12 4=16 5=24 6=32 7=48 8=64[default] "
+ "9=96 10=128 11=192 12=256 13=320 14=384 15=MAX; "
+ "requires BNXT_FLAG_TX_COAL_CMPL firmware capability)");
SYSCTL_ADD_U32(ctx, children, OID_AUTO, "flags", CTLFLAG_RD,
&softc->flags, 0, "flags");
SYSCTL_ADD_U64(ctx, children, OID_AUTO, "fw_cap", CTLFLAG_RD,
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_txrx.c b/sys/dev/bnxt/bnxt_en/bnxt_txrx.c
index 8e95cca12a65..40b02744c6c1 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_txrx.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_txrx.c
@@ -225,6 +225,7 @@ bnxt_isc_txd_credits_update(void *sc, uint16_t txqid, bool clear)
struct bnxt_softc *softc = (struct bnxt_softc *)sc;
struct bnxt_cp_ring *cpr = &softc->tx_cp_rings[txqid];
struct tx_cmpl *cmpl = (struct tx_cmpl *)cpr->ring.vaddr;
+ struct bnxt_ring *txr;
int avail = 0;
uint32_t cons = cpr->cons;
uint32_t raw_cons = cpr->raw_cons;
@@ -266,6 +267,36 @@ bnxt_isc_txd_credits_update(void *sc, uint16_t txqid, bool clear)
if (!clear)
goto done;
break;
+ case TX_CMPL_COAL_TYPE_TX_L2_COAL:
+ if (softc->fw_cap & BNXT_FLAG_TX_COAL_CMPL) {
+ tx_cmpl_coal_t *cmpl_coal = (tx_cmpl_coal_t *)cmpl;
+ uint32_t sq_cons;
+
+ err = (le16toh(cmpl_coal[cons].errors_v) &
+ TX_CMPL_COAL_ERRORS_BUFFER_ERROR_MASK) >>
+ TX_CMPL_COAL_ERRORS_BUFFER_ERROR_SFT;
+ if (err)
+ device_printf(softc->dev,
+ "TX completion error %u\n", err);
+
+ sq_cons = le32toh(cmpl_coal[cons].sq_cons_idx) &
+ TX_CMPL_COAL_SQ_CONS_IDX_MASK;
+
+ txr = &softc->tx_rings[txqid];
+ avail += (sq_cons - txr->free_flow_cons) &
+ txr->db_ring_mask;
+
+ /*
+ * Only the consuming (clear) pass advances free_flow_cons;
+ * peek calls must not claim unreclaimed SQ entries. Gating
+ * on clear also means only gtaskq writes it, so no atomics.
+ */
+ if (!clear)
+ goto done;
+ txr->free_flow_cons = sq_cons;
+ break;
+ }
+ /* FALLTHROUGH */
default:
if (type & 1) {
NEXT_CP_CONS_V(&cpr->ring, cons, v_bit);
diff --git a/sys/dev/bnxt/bnxt_en/if_bnxt.c b/sys/dev/bnxt/bnxt_en/if_bnxt.c
index 188311b2d7d4..512afb450494 100644
--- a/sys/dev/bnxt/bnxt_en/if_bnxt.c
+++ b/sys/dev/bnxt/bnxt_en/if_bnxt.c
@@ -3512,6 +3512,7 @@ skip_def_cp_ring:
HWRM_RING_ALLOC_INPUT_RING_TYPE_TX);
if (rc)
goto fail;
+ softc->tx_rings[i].free_flow_cons = 0;
softc->db_ops.bnxt_db_tx(&softc->tx_rings[i], 0);
}