git: d1619865da1c - main - if_bnxt: add support for HW generic stats

From: Sumit Saxena <ssaxena_at_FreeBSD.org>
Date: Fri, 09 Oct 2026 10:12:50 UTC
The branch main has been updated by ssaxena:

URL: https://cgit.FreeBSD.org/src/commit/?id=d1619865da1cd4c5b557158b4bbf666e0f7f040b

commit d1619865da1cd4c5b557158b4bbf666e0f7f040b
Author:     Sumit Saxena <sumit.saxena@broadcom.com>
AuthorDate: 2026-07-31 16:00:58 +0000
Commit:     Sumit Saxena <ssaxena@FreeBSD.org>
CommitDate: 2026-10-09 08:24:44 +0000

    if_bnxt: add support for HW generic stats
    
    HW generic stats provide information about PCIe transactions and
    out-of-order doorbell writes, useful for debug. Allocate a DMA buffer
    for them (disabling BNXT_FW_CAP_GENERIC_STATS if the allocation
    fails), fetch them via HWRM_STAT_GENERIC_QSTATS once per admin-status
    tick on P5+ chips, and expose them to userspace under
    dev.bnxt.<unit>.hwstats.generic_stats.* via sysctl.
    
    Reviewed by:    gallatin
    MFC after:      2 weeks
    Sponsored by:   Broadcom Inc.
    Differential Revision:  https://reviews.freebsd.org/D58614
---
 sys/dev/bnxt/bnxt_en/bnxt.h        |  3 ++
 sys/dev/bnxt/bnxt_en/bnxt_hwrm.c   | 20 ++++++++
 sys/dev/bnxt/bnxt_en/bnxt_hwrm.h   |  1 +
 sys/dev/bnxt/bnxt_en/bnxt_sysctl.c | 94 ++++++++++++++++++++++++++++++++++++++
 sys/dev/bnxt/bnxt_en/bnxt_sysctl.h |  1 +
 sys/dev/bnxt/bnxt_en/if_bnxt.c     | 15 ++++++
 6 files changed, 134 insertions(+)

diff --git a/sys/dev/bnxt/bnxt_en/bnxt.h b/sys/dev/bnxt/bnxt_en/bnxt.h
index 09092cf7d59e..763748974458 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt.h
@@ -1294,6 +1294,9 @@ struct bnxt_softc {
 	struct tx_port_stats_ext *tx_port_stats_ext;
 	struct rx_port_stats_ext *rx_port_stats_ext;
 
+	struct iflib_dma_info	hw_generic_stats;
+	struct generic_sw_hw_stats	*generic_stats;
+
 	uint16_t		fw_rx_stats_ext_size;
 	uint16_t		fw_tx_stats_ext_size;
 	uint16_t		hw_ring_stats_size;
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
index 680bb593c4b4..73afb62b21ad 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
@@ -2230,6 +2230,26 @@ bnxt_hwrm_port_qstats(struct bnxt_softc *softc)
 
 	return rc;
 }
+
+int bnxt_hwrm_generic_qstats(struct bnxt_softc *bp, u8 flags)
+{
+	struct hwrm_stat_generic_qstats_input req = {0};
+	int rc;
+
+	if (!(bp->fw_cap & BNXT_FW_CAP_GENERIC_STATS))
+		return 0;
+
+	bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_STAT_GENERIC_QSTATS);
+
+	req.flags = flags;
+	req.generic_stat_size = sizeof(struct generic_sw_hw_stats);
+	req.generic_stat_host_addr = htole64(bp->hw_generic_stats.idi_paddr);
+
+	rc = hwrm_send_message(bp, &req, sizeof(req));
+
+	return rc;
+}
+
 static int bnxt_hwrm_pri2cos_idx(struct bnxt_softc *softc, uint32_t path_dir)
 {
 	struct hwrm_queue_pri2cos_qcfg_input req = {0};
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
index 641a1e9ebe3a..e507b716303d 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
@@ -68,6 +68,7 @@ int bnxt_hwrm_stat_ctx_alloc(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr,
     uint64_t paddr);
 int bnxt_hwrm_stat_ctx_free(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr);
 int bnxt_hwrm_port_qstats(struct bnxt_softc *softc);
+int bnxt_hwrm_generic_qstats(struct bnxt_softc *softc, uint8_t flags);
 int bnxt_hwrm_port_qstats_ext(struct bnxt_softc *softc);
 int bnxt_hwrm_port_led_qcaps(struct bnxt_softc *softc);
 int bnxt_hwrm_port_led_cfg(struct bnxt_softc *softc, bool led_on);
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
index fad3982f5581..37a878c9d85f 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.c
@@ -336,6 +336,100 @@ bnxt_create_tx_sysctls(struct bnxt_softc *softc, int txr)
 	return 0;
 }
 
+int
+bnxt_create_generic_stats_sysctls(struct bnxt_softc *softc)
+{
+	struct sysctl_oid *oid;
+	char	name[32];
+	char	desc[64];
+
+	sprintf(name, "generic_stats");
+	sprintf(desc, "Generic Stats");
+	oid = SYSCTL_ADD_NODE(&softc->hw_stats,
+	    SYSCTL_CHILDREN(softc->hw_stats_oid), OID_AUTO, name,
+		CTLFLAG_RD | CTLFLAG_MPSAFE, 0, desc);
+	if (!oid)
+		return ENOMEM;
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_statistics_tx_tlp", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_statistics_tx_tlp, "pcie_statistics_tx_tlp");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_statistics_rx_tlp", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_statistics_rx_tlp, "pcie_statistics_rx_tlp");
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_hdr_posted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_hdr_posted, "pcie_credit_fc_hdr_posted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_hdr_nonposted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_hdr_nonposted, "pcie_credit_fc_hdr_nonposted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_hdr_cmpl", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_hdr_cmpl, "pcie_credit_fc_hdr_cmpl");
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_data_posted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_data_posted, "pcie_credit_fc_data_posted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_data_nonposted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_data_nonposted, "pcie_credit_fc_data_nonposted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_data_cmpl", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_data_cmpl, "pcie_credit_fc_data_cmpl");
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_tgt_nonposted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_tgt_nonposted, "pcie_credit_fc_tgt_nonposted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_tgt_data_posted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_tgt_data_posted, "pcie_credit_fc_tgt_data_posted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_tgt_hdr_posted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_tgt_hdr_posted, "pcie_credit_fc_tgt_hdr_posted");
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_cmpl_hdr_posted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_cmpl_hdr_posted, "pcie_credit_fc_cmpl_hdr_posted");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_credit_fc_cmpl_data_posted", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_credit_fc_cmpl_data_posted, "pcie_credit_fc_cmpl_data_posted");
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_cmpl_longest", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_cmpl_longest, "pcie_cmpl_longest");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "pcie_cmpl_shortest", CTLFLAG_RD,
+	    &softc->generic_stats->pcie_cmpl_shortest, "pcie_cmpl_shortest");
+
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "cache_miss_count_cfcq", CTLFLAG_RD,
+	    &softc->generic_stats->cache_miss_count_cfcq, "cache_miss_count_cfcq");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "cache_miss_count_cfcs", CTLFLAG_RD,
+	    &softc->generic_stats->cache_miss_count_cfcs, "cache_miss_count_cfcs");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "cache_miss_count_cfcc", CTLFLAG_RD,
+	    &softc->generic_stats->cache_miss_count_cfcc, "cache_miss_count_cfcc");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "cache_miss_count_cfcm", CTLFLAG_RD,
+	    &softc->generic_stats->cache_miss_count_cfcm, "cache_miss_count_cfcm");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "hw_db_recov_dbs_dropped", CTLFLAG_RD,
+	    &softc->generic_stats->hw_db_recov_dbs_dropped, "hw_db_recov_dbs_dropped");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "hw_db_recov_drops_serviced", CTLFLAG_RD,
+	    &softc->generic_stats->hw_db_recov_drops_serviced, "hw_db_recov_drops_serviced");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "hw_db_recov_dbs_recovered", CTLFLAG_RD,
+	    &softc->generic_stats->hw_db_recov_dbs_recovered, "hw_db_recov_dbs_recovered");
+	SYSCTL_ADD_QUAD(&softc->hw_stats, SYSCTL_CHILDREN(oid), OID_AUTO,
+	    "hw_db_recov_oo_drop_count", CTLFLAG_RD,
+	    &softc->generic_stats->hw_db_recov_oo_drop_count, "hw_db_recov_oo_drop_count");
+
+	return 0;
+}
+
 int
 bnxt_create_port_stats_sysctls(struct bnxt_softc *softc)
 {
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.h b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.h
index 317cf06ae4b2..2a4531c69779 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_sysctl.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt_sysctl.h
@@ -32,6 +32,7 @@
 int bnxt_init_sysctl_ctx(struct bnxt_softc *softc);
 int bnxt_free_sysctl_ctx(struct bnxt_softc *softc);
 int bnxt_create_port_stats_sysctls(struct bnxt_softc *softc);
+int bnxt_create_generic_stats_sysctls(struct bnxt_softc *softc);
 int bnxt_create_tx_sysctls(struct bnxt_softc *softc, int txr);
 int bnxt_create_rx_sysctls(struct bnxt_softc *softc, int rxr);
 int bnxt_create_ver_sysctls(struct bnxt_softc *softc);
diff --git a/sys/dev/bnxt/bnxt_en/if_bnxt.c b/sys/dev/bnxt/bnxt_en/if_bnxt.c
index 6566c1e8f442..e0e606189fdb 100644
--- a/sys/dev/bnxt/bnxt_en/if_bnxt.c
+++ b/sys/dev/bnxt/bnxt_en/if_bnxt.c
@@ -735,6 +735,7 @@ bnxt_queues_free(if_ctx_t ctx)
 	// Free RX queues
 	for (i=0; i<softc->nrxqsets; i++)
 		iflib_dma_free(&softc->rx_stats[i]);
+	iflib_dma_free(&softc->hw_generic_stats);
 	iflib_dma_free(&softc->hw_tx_port_stats);
 	iflib_dma_free(&softc->hw_rx_port_stats);
 	iflib_dma_free(&softc->hw_tx_port_stats_ext);
@@ -808,6 +809,17 @@ bnxt_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs,
  */
 #define BNXT_PORT_STAT_PADDING  512
 
+	rc = iflib_dma_alloc(ctx, sizeof(struct generic_sw_hw_stats), &softc->hw_generic_stats, 0);
+	/* Generic stats are optional */
+	if (rc) {
+		softc->fw_cap &= ~BNXT_FW_CAP_GENERIC_STATS;
+		iflib_dma_free(&softc->hw_generic_stats);
+	} else {
+		bus_dmamap_sync(softc->hw_generic_stats.idi_tag,
+		    softc->hw_generic_stats.idi_map, BUS_DMASYNC_PREREAD);
+		softc->generic_stats = (void *) softc->hw_generic_stats.idi_vaddr;
+	}
+
 	rc = iflib_dma_alloc(ctx, sizeof(struct rx_port_stats) + BNXT_PORT_STAT_PADDING,
 	    &softc->hw_rx_port_stats, 0);
 	if (rc)
@@ -916,6 +928,7 @@ bnxt_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs,
 		bnxt_create_rx_sysctls(softc, i);
 	}
 
+	bnxt_create_generic_stats_sysctls(softc);
 	/*
 	 * When SR-IOV is enabled, avoid each VF sending PORT_QSTATS
          * HWRM every sec with which firmware timeouts can happen
@@ -4256,6 +4269,8 @@ bnxt_update_admin_status(if_ctx_t ctx)
 			bit_clear(softc->state_bv, BNXT_STATE_LINK_CHANGE);
 			bnxt_media_status(softc->ctx, &ifmr);
 		}
+
+		bnxt_hwrm_generic_qstats(softc, 0);
 	}
 
 	return;