git: 22f37c46b484 - main - if_bnxt: rework interrupt coalescing onto the AGGINT_QCAPS scheme
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 09 Oct 2026 10:12:57 UTC
The branch main has been updated by ssaxena:
URL: https://cgit.FreeBSD.org/src/commit/?id=22f37c46b4841fec22612fd4bc485f0515f27a28
commit 22f37c46b4841fec22612fd4bc485f0515f27a28
Author: Sumit Saxena <sumit.saxena@broadcom.com>
AuthorDate: 2026-08-03 08:52:00 +0000
Commit: Sumit Saxena <ssaxena@FreeBSD.org>
CommitDate: 2026-10-09 08:24:45 +0000
if_bnxt: rework interrupt coalescing onto the AGGINT_QCAPS scheme
Query firmware-advertised coalescing capabilities and program
per-direction rx/tx coalescing settings against them, replacing the
old hardcoded scheme. Add sysctls for the coalescing mode, budget,
and stats-timer knobs the new scheme exposes.
Reviewed by: gallatin
MFC after: 2 weeks
Sponsored by: Broadcom Inc.
Differential Revision: https://reviews.freebsd.org/D58621
---
sys/dev/bnxt/bnxt_en/bnxt.h | 64 ++++++++--
sys/dev/bnxt/bnxt_en/bnxt_hwrm.c | 250 +++++++++++++++++++++++++++---------
sys/dev/bnxt/bnxt_en/bnxt_hwrm.h | 1 +
sys/dev/bnxt/bnxt_en/bnxt_sysctl.c | 253 ++++++++++++++++++++++++++++++++++---
sys/dev/bnxt/bnxt_en/if_bnxt.c | 50 ++++++++
5 files changed, 534 insertions(+), 84 deletions(-)
diff --git a/sys/dev/bnxt/bnxt_en/bnxt.h b/sys/dev/bnxt/bnxt_en/bnxt.h
index e8ffc9fdb057..f93f0d4fe794 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt.h
@@ -813,6 +813,56 @@ struct bnxt_hw_lro {
uint32_t min_agg_len;
};
+#define BNXT_LEGACY_COAL_CMPL_PARAMS \
+ (HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_INT_LAT_TMR_MIN | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_INT_LAT_TMR_MAX | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_TIMER_RESET | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_RING_IDLE | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_NUM_CMPL_DMA_AGGR | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_NUM_CMPL_DMA_AGGR_DURING_INT | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_CMPL_AGGR_DMA_TMR | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_CMPL_AGGR_DMA_TMR_DURING_INT | \
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_NUM_CMPL_AGGR_INT)
+
+#define BNXT_COAL_CMPL_ENABLES \
+ (HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_ENABLES_NUM_CMPL_DMA_AGGR | \
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_ENABLES_CMPL_AGGR_DMA_TMR | \
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_ENABLES_INT_LAT_TMR_MAX | \
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_ENABLES_NUM_CMPL_AGGR_INT)
+
+#define BNXT_COAL_CMPL_MIN_TMR_ENABLE \
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_ENABLES_INT_LAT_TMR_MIN
+
+/* Gates cmpl_aggr_dma_tmr_during_int, but reuses num_cmpl_dma_aggr_during_int's
+ * enable bit - there's no separate bit for the timer field. */
+#define BNXT_COAL_CMPL_AGGR_TMR_DURING_INT_ENABLE \
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_ENABLES_NUM_CMPL_DMA_AGGR_DURING_INT
+
+struct bnxt_coal_cap {
+ uint32_t cmpl_params;
+ uint32_t nq_params;
+ uint16_t num_cmpl_dma_aggr_max;
+ uint16_t num_cmpl_dma_aggr_during_int_max;
+ uint16_t cmpl_aggr_dma_tmr_max;
+ uint16_t cmpl_aggr_dma_tmr_during_int_max;
+ uint16_t int_lat_tmr_min_max;
+ uint16_t int_lat_tmr_max_max;
+ uint16_t num_cmpl_aggr_int_max;
+ uint16_t timer_units;
+};
+
+struct bnxt_coal {
+ uint16_t coal_ticks;
+ uint16_t coal_ticks_irq;
+ uint16_t coal_bufs;
+ uint16_t coal_bufs_irq;
+ uint16_t idle_thresh; /* RING_IDLE enabled when coal ticks < idle_thresh */
+ uint8_t bufs_per_record;
+ uint16_t budget;
+ uint16_t flags;
+ uint8_t timer_reset_during_ring_alloc;
+};
+
/* The hardware supports certain page sizes. Use the supported page sizes
* to allocate the rings.
*/
@@ -1367,15 +1417,12 @@ struct bnxt_softc {
volatile int detached;
struct bnxt_hw_lro hw_lro;
uint8_t wol_filter_id;
- uint16_t rx_coal_usecs;
- uint16_t rx_coal_usecs_irq;
- uint16_t rx_coal_frames;
- uint16_t rx_coal_frames_irq;
- uint16_t tx_coal_usecs;
- uint16_t tx_coal_usecs_irq;
- uint16_t tx_coal_frames;
- uint16_t tx_coal_frames_irq;
+ struct bnxt_coal_cap coal_cap;
+ struct bnxt_coal rx_coal;
+ struct bnxt_coal tx_coal;
+
+ uint32_t stats_coal_ticks;
#define BNXT_USEC_TO_COAL_TIMER(x) ((x) * 25 / 2)
#define BNXT_DEF_STATS_COAL_TICKS 1000000
#define BNXT_MIN_STATS_COAL_TICKS 250000
@@ -1476,6 +1523,7 @@ struct bnxt_softc {
#define BNXT_STATE_FW_NON_FATAL_COND 13
#define BNXT_STATE_FW_ACTIVATE_RESET 14
#define BNXT_STATE_HALF_OPEN 15
+#define BNXT_STATE_UP 16
#define BNXT_NO_FW_ACCESS(bp) \
test_bit(BNXT_STATE_FW_FATAL_COND, &(bp)->state)
struct pci_dev *pdev;
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
index b9cd7174209c..77e43079fd96 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
@@ -2136,6 +2136,9 @@ bnxt_hwrm_ring_alloc(struct bnxt_softc *softc, uint8_t type,
req.nq_ring_id = htole16(softc->nq_rings[idx].ring.phys_id);
req.enables |= htole32(
HWRM_RING_ALLOC_INPUT_ENABLES_NQ_RING_ID_VALID);
+
+ if (softc->rx_coal.timer_reset_during_ring_alloc)
+ req.flags |= HWRM_RING_ALLOC_INPUT_FLAGS_TIMER_RESET;
break;
case HWRM_RING_ALLOC_INPUT_RING_TYPE_NQ:
req.int_mode = HWRM_RING_ALLOC_INPUT_INT_MODE_MSIX;
@@ -3475,81 +3478,208 @@ bnxt_hwrm_free_wol_fltr(struct bnxt_softc *softc)
return hwrm_send_message(softc, &req, sizeof(req));
}
-static void bnxt_hwrm_set_coal_params(struct bnxt_softc *softc, uint32_t max_frames,
- uint32_t buf_tmrs, uint16_t flags,
- struct hwrm_ring_cmpl_ring_cfg_aggint_params_input *req)
+void
+bnxt_hwrm_coal_params_qcaps(struct bnxt_softc *softc)
{
- req->flags = htole16(flags);
- req->num_cmpl_dma_aggr = htole16((uint16_t)max_frames);
- req->num_cmpl_dma_aggr_during_int = htole16(max_frames >> 16);
- req->cmpl_aggr_dma_tmr = htole16((uint16_t)buf_tmrs);
- req->cmpl_aggr_dma_tmr_during_int = htole16(buf_tmrs >> 16);
- /* Minimum time between 2 interrupts set to buf_tmr x 2 */
- req->int_lat_tmr_min = htole16((uint16_t)buf_tmrs * 2);
- req->int_lat_tmr_max = htole16((uint16_t)buf_tmrs * 4);
- req->num_cmpl_aggr_int = htole16((uint16_t)max_frames * 4);
+ struct bnxt_coal_cap *coal_cap = &softc->coal_cap;
+ struct hwrm_ring_aggint_qcaps_output *resp =
+ (void *)softc->hwrm_cmd_resp.idi_vaddr;
+ struct hwrm_ring_aggint_qcaps_input req = {0};
+ int rc;
+
+ coal_cap->cmpl_params = BNXT_LEGACY_COAL_CMPL_PARAMS;
+ coal_cap->num_cmpl_dma_aggr_max = 63;
+ coal_cap->num_cmpl_dma_aggr_during_int_max = 63;
+ coal_cap->cmpl_aggr_dma_tmr_max = 65535;
+ coal_cap->cmpl_aggr_dma_tmr_during_int_max = 65535;
+ coal_cap->int_lat_tmr_min_max = 65535;
+ coal_cap->int_lat_tmr_max_max = 65535;
+ coal_cap->num_cmpl_aggr_int_max = 65535;
+ coal_cap->timer_units = 80;
+
+ if (softc->hwrm_spec_code < 0x10902)
+ return;
+
+ bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_RING_AGGINT_QCAPS);
+
+ BNXT_HWRM_LOCK(softc);
+ rc = _hwrm_send_message(softc, &req, sizeof(req));
+ if (rc) {
+ BNXT_HWRM_UNLOCK(softc);
+ return;
+ }
+
+ coal_cap->cmpl_params = le32toh(resp->cmpl_params);
+ coal_cap->nq_params = le32toh(resp->nq_params);
+ coal_cap->num_cmpl_dma_aggr_max = le16toh(resp->num_cmpl_dma_aggr_max);
+ coal_cap->num_cmpl_dma_aggr_during_int_max =
+ le16toh(resp->num_cmpl_dma_aggr_during_int_max);
+ coal_cap->cmpl_aggr_dma_tmr_max = le16toh(resp->cmpl_aggr_dma_tmr_max);
+ coal_cap->cmpl_aggr_dma_tmr_during_int_max =
+ le16toh(resp->cmpl_aggr_dma_tmr_during_int_max);
+ coal_cap->int_lat_tmr_min_max = le16toh(resp->int_lat_tmr_min_max);
+ coal_cap->int_lat_tmr_max_max = le16toh(resp->int_lat_tmr_max_max);
+ coal_cap->num_cmpl_aggr_int_max = le16toh(resp->num_cmpl_aggr_int_max);
+ coal_cap->timer_units = le16toh(resp->timer_units);
+ BNXT_HWRM_UNLOCK(softc);
}
-int bnxt_hwrm_set_coal(struct bnxt_softc *softc)
+static uint16_t
+bnxt_usec_to_coal_tmr(struct bnxt_softc *softc, uint16_t usec)
{
- int i, rc = 0;
- struct hwrm_ring_cmpl_ring_cfg_aggint_params_input req_rx = {0},
- req_tx = {0}, *req;
- uint16_t max_buf, max_buf_irq;
- uint16_t buf_tmr, buf_tmr_irq;
- uint32_t flags;
+ struct bnxt_coal_cap *coal_cap = &softc->coal_cap;
- bnxt_hwrm_cmd_hdr_init(softc, &req_rx,
- HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS);
- bnxt_hwrm_cmd_hdr_init(softc, &req_tx,
- HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS);
+ return (usec * 1000 / coal_cap->timer_units);
+}
- /* Each rx completion (2 records) should be DMAed immediately.
- * DMA 1/4 of the completion buffers at a time.
- */
- max_buf = min_t(uint16_t, softc->rx_coal_frames / 4, 2);
- /* max_buf must not be zero */
- max_buf = clamp_t(uint16_t, max_buf, 1, 63);
- max_buf_irq = clamp_t(uint16_t, softc->rx_coal_frames_irq, 1, 63);
- buf_tmr = BNXT_USEC_TO_COAL_TIMER(softc->rx_coal_usecs);
- /* buf timer set to 1/4 of interrupt timer */
- buf_tmr = max_t(uint16_t, buf_tmr / 4, 1);
- buf_tmr_irq = BNXT_USEC_TO_COAL_TIMER(softc->rx_coal_usecs_irq);
- buf_tmr_irq = max_t(uint16_t, buf_tmr_irq, 1);
-
- flags = HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_TIMER_RESET;
-
- /* RING_IDLE generates more IRQs for lower latency. Enable it only
- * if coal_usecs is less than 25 us.
+static void
+bnxt_hwrm_set_coal_params(struct bnxt_softc *softc, struct bnxt_coal *hw_coal,
+ struct hwrm_ring_cmpl_ring_cfg_aggint_params_input *req)
+{
+ struct bnxt_coal_cap *coal_cap = &softc->coal_cap;
+ uint16_t val, tmr, max, flags = hw_coal->flags;
+ uint32_t cmpl_params = coal_cap->cmpl_params;
+
+ max = hw_coal->bufs_per_record * 128;
+ if (hw_coal->budget)
+ max = hw_coal->bufs_per_record * hw_coal->budget;
+ max = min_t(uint16_t, max, coal_cap->num_cmpl_aggr_int_max);
+
+ val = clamp_t(uint16_t, hw_coal->coal_bufs, 1, max);
+ req->num_cmpl_aggr_int = htole16(val);
+
+ val = min_t(uint16_t, val, coal_cap->num_cmpl_dma_aggr_max);
+ req->num_cmpl_dma_aggr = htole16(val);
+
+ val = clamp_t(uint16_t, hw_coal->coal_bufs_irq, 1,
+ coal_cap->num_cmpl_dma_aggr_during_int_max);
+ req->num_cmpl_dma_aggr_during_int = htole16(val);
+
+ tmr = bnxt_usec_to_coal_tmr(softc, hw_coal->coal_ticks);
+ tmr = clamp_t(uint16_t, tmr, 1, coal_cap->int_lat_tmr_max_max);
+ req->int_lat_tmr_max = htole16(tmr);
+
+ /* Minimum timer set to 1/2 of the interrupt timer. */
+ if (cmpl_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_INT_LAT_TMR_MIN) {
+ val = tmr / 2;
+ val = clamp_t(uint16_t, val, 1, coal_cap->int_lat_tmr_min_max);
+ req->int_lat_tmr_min = htole16(val);
+ req->enables |= htole16(BNXT_COAL_CMPL_MIN_TMR_ENABLE);
+ }
+
+ /* Buf timer set to 1/4 of the interrupt timer. */
+ val = clamp_t(uint16_t, tmr / 4, 1, coal_cap->cmpl_aggr_dma_tmr_max);
+ req->cmpl_aggr_dma_tmr = htole16(val);
+
+ /*
+ * Gate on CMPL_AGGR_DMA_TMR_DURING_INT, not the similarly-named
+ * NUM_CMPL_DMA_AGGR_DURING_INT bit which covers the unrelated count field.
*/
- if (softc->rx_coal_usecs < 25)
+ if (cmpl_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_CMPL_AGGR_DMA_TMR_DURING_INT) {
+ tmr = bnxt_usec_to_coal_tmr(softc, hw_coal->coal_ticks_irq);
+ val = clamp_t(uint16_t, tmr, 1,
+ coal_cap->cmpl_aggr_dma_tmr_during_int_max);
+ req->cmpl_aggr_dma_tmr_during_int = htole16(val);
+ /*
+ * No dedicated enable bit exists for this field; reusing
+ * NUM_CMPL_DMA_AGGR_DURING_INT's enable bit matches existing behavior.
+ */
+ req->enables |=
+ htole16(BNXT_COAL_CMPL_AGGR_TMR_DURING_INT_ENABLE);
+ }
+
+ if ((cmpl_params & HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_RING_IDLE) &&
+ hw_coal->idle_thresh && hw_coal->coal_ticks < hw_coal->idle_thresh)
flags |= HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_RING_IDLE;
- bnxt_hwrm_set_coal_params(softc, max_buf_irq << 16 | max_buf,
- buf_tmr_irq << 16 | buf_tmr, flags, &req_rx);
-
- /* max_buf must not be zero */
- max_buf = clamp_t(uint16_t, softc->tx_coal_frames, 1, 63);
- max_buf_irq = clamp_t(uint16_t, softc->tx_coal_frames_irq, 1, 63);
- buf_tmr = BNXT_USEC_TO_COAL_TIMER(softc->tx_coal_usecs);
- /* buf timer set to 1/4 of interrupt timer */
- buf_tmr = max_t(uint16_t, buf_tmr / 4, 1);
- buf_tmr_irq = BNXT_USEC_TO_COAL_TIMER(softc->tx_coal_usecs_irq);
- buf_tmr_irq = max_t(uint16_t, buf_tmr_irq, 1);
- flags = HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_TIMER_RESET;
- bnxt_hwrm_set_coal_params(softc, max_buf_irq << 16 | max_buf,
- buf_tmr_irq << 16 | buf_tmr, flags, &req_tx);
+ req->flags = htole16(flags);
+ req->enables |= htole16(BNXT_COAL_CMPL_ENABLES);
+}
- for (i = 0; i < softc->nrxqsets; i++) {
+static int
+bnxt_hwrm_set_coal_nq(struct bnxt_softc *softc, uint32_t ring_id,
+ struct bnxt_coal *hw_coal)
+{
+ struct hwrm_ring_cmpl_ring_cfg_aggint_params_input req = {0};
+ struct bnxt_coal_cap *coal_cap = &softc->coal_cap;
+ uint16_t tmr;
+
+ if (!(coal_cap->nq_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_NQ_PARAMS_INT_LAT_TMR_MIN))
+ return (0);
+
+ bnxt_hwrm_cmd_hdr_init(softc, &req,
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS);
+
+ req.ring_id = htole16(ring_id);
+ req.flags =
+ htole16(HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_IS_NQ);
+
+ tmr = bnxt_usec_to_coal_tmr(softc, hw_coal->coal_ticks) / 2;
+ tmr = clamp_t(uint16_t, tmr, 1, coal_cap->int_lat_tmr_min_max);
+ req.int_lat_tmr_min = htole16(tmr);
+ req.enables |= htole16(BNXT_COAL_CMPL_MIN_TMR_ENABLE);
+
+ return (hwrm_send_message(softc, &req, sizeof(req)));
+}
+
+int bnxt_hwrm_set_coal(struct bnxt_softc *softc)
+{
+ struct hwrm_ring_cmpl_ring_cfg_aggint_params_input req = {0};
+ int i, rc = 0;
+
+ if (!test_bit(BNXT_STATE_UP, &softc->state))
+ return (rc);
+
+ bnxt_hwrm_cmd_hdr_init(softc, &req,
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS);
+ bnxt_hwrm_set_coal_params(softc, &softc->rx_coal, &req);
- req = &req_rx;
- req->ring_id = htole16(softc->grp_info[i].cp_ring_id);
+ for (i = 0; i < softc->nrxqsets; i++) {
+ req.ring_id = htole16(softc->grp_info[i].cp_ring_id);
- rc = hwrm_send_message(softc, req, sizeof(*req));
+ rc = hwrm_send_message(softc, &req, sizeof(req));
if (rc)
break;
+
+ if (BNXT_CHIP_P5_PLUS(softc))
+ rc = bnxt_hwrm_set_coal_nq(softc,
+ softc->nq_rings[i].ring.phys_id, &softc->rx_coal);
+ else
+ rc = bnxt_hwrm_set_coal_nq(softc,
+ softc->rx_cp_rings[i].ring.phys_id, &softc->rx_coal);
+ if (rc) {
+ device_printf(softc->dev,
+ "%s: Rx NQ coalesce setting failed for queue %d\n",
+ __func__, i);
+ break;
+ }
}
- return rc;
+
+ if (softc->fw_cap & BNXT_FLAG_TX_COAL_CMPL) {
+ struct hwrm_ring_cmpl_ring_cfg_aggint_params_input req_tx = {0};
+
+ bnxt_hwrm_cmd_hdr_init(softc, &req_tx,
+ HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS);
+ bnxt_hwrm_set_coal_params(softc, &softc->tx_coal, &req_tx);
+
+ for (i = 0; i < softc->ntxqsets; i++) {
+ req_tx.ring_id =
+ htole16(softc->tx_cp_rings[i].ring.phys_id);
+
+ rc = hwrm_send_message(softc, &req_tx, sizeof(req_tx));
+ if (rc) {
+ device_printf(softc->dev,
+ "%s: Tx coalesce setting failed for queue %d\n",
+ __func__, i);
+ break;
+ }
+ }
+ }
+
+ return (rc);
}
void bnxt_hwrm_ring_info_get(struct bnxt_softc *softc, uint8_t ring_type,
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
index e507b716303d..d3b25a243ec5 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
@@ -128,6 +128,7 @@ uint16_t bnxt_hwrm_get_wol_fltrs(struct bnxt_softc *softc, uint16_t handle);
int bnxt_hwrm_alloc_wol_fltr(struct bnxt_softc *softc);
int bnxt_hwrm_free_wol_fltr(struct bnxt_softc *softc);
int bnxt_hwrm_set_coal(struct bnxt_softc *softc);
+void bnxt_hwrm_coal_params_qcaps(struct bnxt_softc *softc);
int bnxt_hwrm_func_rgtr_async_events(struct bnxt_softc *softc, unsigned long *bmap,
int bmap_size);
int bnxt_hwrm_func_backing_store_qcaps(struct bnxt_softc *softc);
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
index 37a878c9d85f..cb906c38b906 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
@@ -1604,21 +1604,46 @@ bnxt_vlan_strip_sysctl(SYSCTL_HANDLER_ARGS) {
return rc;
}
+/*
+ * sysctl_handle_int() allows any int-sized value; reject anything that
+ * would truncate/overflow when narrowed into the uint16_t coal field.
+ */
+static int
+bnxt_sysctl_check_u16_range(struct bnxt_softc *softc, const char *what,
+ int val, uint16_t mult)
+{
+ int max = mult ? (UINT16_MAX / mult) : UINT16_MAX;
+
+ if (val < 0 || val > max) {
+ device_printf(softc->dev,
+ "%s: value %d out of range [0, %d]\n", what, val, max);
+ return EINVAL;
+ }
+ return 0;
+}
+
static int
bnxt_set_coal_rx_usecs(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->rx_coal_usecs;
+ hw_coal = &softc->rx_coal;
+ val = hw_coal->coal_ticks;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->rx_coal_usecs = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, 0);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_ticks = val;
rc = bnxt_hwrm_set_coal(softc);
return rc;
@@ -1627,18 +1652,33 @@ bnxt_set_coal_rx_usecs(SYSCTL_HANDLER_ARGS) {
static int
bnxt_set_coal_rx_frames(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
+ uint16_t mult;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->rx_coal_frames;
+ hw_coal = &softc->rx_coal;
+ mult = hw_coal->bufs_per_record;
+ if (!mult) {
+ device_printf(softc->dev,
+ "%s: bufs_per_record is zero which is invalid\n", __func__);
+ return EINVAL;
+ }
+
+ val = hw_coal->coal_bufs / mult;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->rx_coal_frames = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, mult);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_bufs = val * mult;
rc = bnxt_hwrm_set_coal(softc);
return rc;
@@ -1647,18 +1687,25 @@ bnxt_set_coal_rx_frames(SYSCTL_HANDLER_ARGS) {
static int
bnxt_set_coal_rx_usecs_irq(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->rx_coal_usecs_irq;
+ hw_coal = &softc->rx_coal;
+ val = hw_coal->coal_ticks_irq;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->rx_coal_usecs_irq = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, 0);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_ticks_irq = val;
rc = bnxt_hwrm_set_coal(softc);
return rc;
@@ -1667,38 +1714,111 @@ bnxt_set_coal_rx_usecs_irq(SYSCTL_HANDLER_ARGS) {
static int
bnxt_set_coal_rx_frames_irq(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
+ uint16_t mult;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->rx_coal_frames_irq;
+ hw_coal = &softc->rx_coal;
+ mult = hw_coal->bufs_per_record;
+ if (!mult) {
+ device_printf(softc->dev,
+ "%s: bufs_per_record is zero which is invalid\n", __func__);
+ return EINVAL;
+ }
+
+ val = hw_coal->coal_bufs_irq / mult;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->rx_coal_frames_irq = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, mult);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_bufs_irq = val * mult;
rc = bnxt_hwrm_set_coal(softc);
return rc;
}
+static int
+bnxt_set_rx_coalesce_mode(SYSCTL_HANDLER_ARGS) {
+ struct bnxt_softc *softc = arg1;
+ struct bnxt_coal_cap *coal_cap;
+ struct bnxt_coal *hw_coal;
+ uint16_t flags;
+ int val;
+ int rc;
+
+ if (softc == NULL)
+ return EBUSY;
+
+ coal_cap = &softc->coal_cap;
+
+ if (coal_cap->cmpl_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_TIMER_RESET) {
+ hw_coal = &softc->rx_coal;
+ flags = hw_coal->flags;
+
+ val = flags & HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_TIMER_RESET;
+ rc = sysctl_handle_int(oidp, &val, 0, req);
+ if (rc || !req->newptr)
+ return rc;
+
+ if (val)
+ flags |= HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_TIMER_RESET;
+ else
+ flags &= ~HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_TIMER_RESET;
+
+ hw_coal->flags = flags;
+ rc = bnxt_hwrm_set_coal(softc);
+ return rc;
+ }
+
+ if (coal_cap->cmpl_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_TMR_RESET_ON_ALLOC) {
+ hw_coal = &softc->rx_coal;
+ val = hw_coal->timer_reset_during_ring_alloc;
+ rc = sysctl_handle_int(oidp, &val, 0, req);
+ if (rc || !req->newptr)
+ return rc;
+
+ /* Takes effect on next ring (re)alloc, so no bnxt_hwrm_set_coal() call needed here. */
+ hw_coal->timer_reset_during_ring_alloc = val ? 1 : 0;
+
+ return rc;
+ }
+
+ return EOPNOTSUPP;
+}
+
static int
bnxt_set_coal_tx_usecs(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->tx_coal_usecs;
+ hw_coal = &softc->tx_coal;
+ val = hw_coal->coal_ticks;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->tx_coal_usecs = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, 0);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_ticks = val;
rc = bnxt_hwrm_set_coal(softc);
return rc;
@@ -1707,18 +1827,33 @@ bnxt_set_coal_tx_usecs(SYSCTL_HANDLER_ARGS) {
static int
bnxt_set_coal_tx_frames(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
+ uint16_t mult;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->tx_coal_frames;
+ hw_coal = &softc->tx_coal;
+ mult = hw_coal->bufs_per_record;
+ if (!mult) {
+ device_printf(softc->dev,
+ "%s: bufs_per_record is zero which is invalid\n", __func__);
+ return EINVAL;
+ }
+
+ val = hw_coal->coal_bufs / mult;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->tx_coal_frames = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, mult);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_bufs = val * mult;
rc = bnxt_hwrm_set_coal(softc);
return rc;
@@ -1727,18 +1862,25 @@ bnxt_set_coal_tx_frames(SYSCTL_HANDLER_ARGS) {
static int
bnxt_set_coal_tx_usecs_irq(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->tx_coal_usecs_irq;
+ hw_coal = &softc->tx_coal;
+ val = hw_coal->coal_ticks_irq;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->tx_coal_usecs_irq = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, 0);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_ticks_irq = val;
rc = bnxt_hwrm_set_coal(softc);
return rc;
@@ -1747,23 +1889,62 @@ bnxt_set_coal_tx_usecs_irq(SYSCTL_HANDLER_ARGS) {
static int
bnxt_set_coal_tx_frames_irq(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
+ uint16_t mult;
int rc;
int val;
if (softc == NULL)
return EBUSY;
- val = softc->tx_coal_frames_irq;
+ hw_coal = &softc->tx_coal;
+ mult = hw_coal->bufs_per_record;
+ if (!mult) {
+ device_printf(softc->dev,
+ "%s: bufs_per_record is zero which is invalid\n", __func__);
+ return EINVAL;
+ }
+
+ val = hw_coal->coal_bufs_irq / mult;
+
rc = sysctl_handle_int(oidp, &val, 0, req);
if (rc || !req->newptr)
return rc;
- softc->tx_coal_frames_irq = val;
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, mult);
+ if (rc)
+ return rc;
+
+ hw_coal->coal_bufs_irq = val * mult;
rc = bnxt_hwrm_set_coal(softc);
return rc;
}
+static int
+bnxt_set_stats_coal_ticks(SYSCTL_HANDLER_ARGS) {
+ struct bnxt_softc *softc = arg1;
+ int rc;
+ int val;
+
+ if (softc == NULL)
+ return EBUSY;
+
+ val = softc->stats_coal_ticks;
+
+ rc = sysctl_handle_int(oidp, &val, 0, req);
+ if (rc || !req->newptr)
+ return rc;
+
+ softc->stats_coal_ticks = val;
+ /*
+ * Stats DMA period is set once at HWRM_STAT_CTX_ALLOC time; there's no
+ * HWRM_STAT_CTX_CFG to reprogram it, so nothing to apply this to yet.
+ */
+
+ return 0;
+}
+
static int
bnxt_set_cagr_tick_res(SYSCTL_HANDLER_ARGS) {
struct bnxt_softc *softc = arg1;
@@ -1788,6 +1969,34 @@ bnxt_set_cagr_tick_res(SYSCTL_HANDLER_ARGS) {
return rc;
}
+static int
+bnxt_set_rx_coal_budget(SYSCTL_HANDLER_ARGS) {
+ struct bnxt_softc *softc = arg1;
+ struct bnxt_coal *hw_coal;
+ int rc;
+ int val;
+
+ if (softc == NULL)
+ return EBUSY;
+
+ hw_coal = &softc->rx_coal;
+
+ val = hw_coal->budget;
+
+ rc = sysctl_handle_int(oidp, &val, 0, req);
+ if (rc || !req->newptr)
+ return rc;
+
+ rc = bnxt_sysctl_check_u16_range(softc, __func__, val, 0);
+ if (rc)
+ return rc;
+
+ hw_coal->budget = val;
+ rc = bnxt_hwrm_set_coal(softc);
+
+ return rc;
+}
+
static int
bnxt_set_tx_host_coal_bds(SYSCTL_HANDLER_ARGS)
{
@@ -1914,6 +2123,10 @@ bnxt_create_config_sysctls_pre(struct bnxt_softc *softc)
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, softc, 0,
bnxt_set_coal_rx_frames_irq, "I",
"interrupt coalescing Rx Frames IRQ");
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rx_coalesce_mode",
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, softc, 0,
+ bnxt_set_rx_coalesce_mode, "I",
+ "set rx_coalesce_mode 0/1");
SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_tx_usecs",
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, softc, 0,
bnxt_set_coal_tx_usecs, "I", "interrupt coalescing Tx Usces");
@@ -1928,9 +2141,17 @@ bnxt_create_config_sysctls_pre(struct bnxt_softc *softc)
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, softc, 0,
bnxt_set_coal_tx_frames_irq, "I",
"interrupt coalescing Tx Frames IRQ");
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_stats_coal_ticks",
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, softc, 0,
+ bnxt_set_stats_coal_ticks, "I",
+ "interrupt coalescing stats coal ticks");
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");
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_rx_coal_budget",
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, softc, 0,
+ bnxt_set_rx_coal_budget, "I",
+ "Rx interrupt coalescing budget");
/*
* Default coal_bds to half the Tx ring depth, clamped between
diff --git a/sys/dev/bnxt/bnxt_en/if_bnxt.c b/sys/dev/bnxt/bnxt_en/if_bnxt.c
index 0757985fa49f..e9c034065a6c 100644
--- a/sys/dev/bnxt/bnxt_en/if_bnxt.c
+++ b/sys/dev/bnxt/bnxt_en/if_bnxt.c
@@ -221,6 +221,7 @@ static int bnxt_detach(if_ctx_t ctx);
/* Device configuration */
static void bnxt_init(if_ctx_t ctx);
static int bnxt_init_hw(if_ctx_t ctx);
+static void bnxt_init_dflt_coal(struct bnxt_softc *softc);
static void bnxt_stop(if_ctx_t ctx);
static void bnxt_if_led_func(if_ctx_t ctx, int onoff);
static bool bnxt_if_led_supported(if_ctx_t ctx);
@@ -2989,6 +2990,9 @@ bnxt_attach_pre(if_ctx_t ctx)
scctx->isc_min_frame_size = BNXT_MIN_FRAME_SIZE;
scctx->isc_txrx = &bnxt_txrx;
+ bnxt_hwrm_coal_params_qcaps(softc);
+ bnxt_init_dflt_coal(softc);
+
if (scctx->isc_nrxd[0] <
((scctx->isc_nrxd[1] * 4) + scctx->isc_nrxd[2]))
device_printf(softc->dev,
@@ -3627,6 +3631,49 @@ skip_aux_init:
return;
}
+static void
+bnxt_init_dflt_coal(struct bnxt_softc *softc)
+{
+ struct bnxt_coal_cap *coal_cap = &softc->coal_cap;
+ struct bnxt_coal *coal;
+ uint16_t flags = 0;
+
+ if (coal_cap->cmpl_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_TIMER_RESET)
+ flags |= HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS_INPUT_FLAGS_TIMER_RESET;
+
+ coal = &softc->rx_coal;
+ if (coal_cap->cmpl_params &
+ HWRM_RING_AGGINT_QCAPS_OUTPUT_CMPL_PARAMS_TMR_RESET_ON_ALLOC)
+ coal->timer_reset_during_ring_alloc = 1;
+
+ /*
+ * Tick values in microseconds.
+ * 1 coal_buf x bufs_per_record = 1 completion record.
+ */
+ coal->coal_ticks = 6;
+ coal->coal_bufs = 12;
+ coal->coal_ticks_irq = 1;
+ if (BNXT_CHIP_P7(softc))
+ coal->coal_bufs_irq = 4;
+ else
+ coal->coal_bufs_irq = 2;
+ coal->idle_thresh = 50;
+ coal->bufs_per_record = 2;
+ coal->budget = 64;
+ coal->flags = flags;
+
+ coal = &softc->tx_coal;
+ coal->coal_ticks = 28;
+ coal->coal_bufs = 30;
+ coal->coal_ticks_irq = 2;
+ coal->coal_bufs_irq = 2;
+ coal->bufs_per_record = 1;
+ coal->flags = flags;
+
+ softc->stats_coal_ticks = BNXT_MIN_STATS_COAL_TICKS;
+}
+
/* Device configuration */
static void
bnxt_init(if_ctx_t ctx)
@@ -3850,6 +3897,8 @@ skip_def_cp_ring:
bnxt_get_port_module_status(softc);
bnxt_media_status(softc->ctx, &ifmr);
bnxt_hwrm_cfa_l2_set_rx_mask(softc, &softc->vnic_info);
+ set_bit(BNXT_STATE_UP, &softc->state);
+ bnxt_hwrm_set_coal(softc);
return (0);
fail:
@@ -3869,6 +3918,7 @@ bnxt_stop(if_ctx_t ctx)
bnxt_ptp_stop_calibration(softc->ptp_cfg);
softc->rx_ts_enabled = false;
}
+ clear_bit(BNXT_STATE_UP, &softc->state);
softc->is_dev_init = false;
bnxt_do_disable_intr(&softc->def_cp_ring);
bnxt_func_reset(softc);