From 27f398b5f2736c3a572453b4771afef6effa3d1a Mon Sep 17 00:00:00 2001 From: Lukas Sismis Date: Wed, 28 Jan 2026 12:07:50 +0100 Subject: [PATCH] dpdk: collect port stats before device stop Some drivers (e.g. BNXT) fail to report stats after the device is stopped. Move stats collection (DPDKDumpCounters and PrintDPDKPortXstats) to run before rte_eth_dev_stop() in HandleShutdown. Also change PrintDPDKPortXstats error handling from FatalError to graceful return since stats collection failures during shutdown should not crash the application. The commit removes ThreadExitPrintStats callback as the function had no useful features after the stats were moved. Ticket: 8251 --- src/source-dpdk.c | 132 +++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 79 deletions(-) diff --git a/src/source-dpdk.c b/src/source-dpdk.c index 3fd7bf7e07..dfc2d0595f 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -140,7 +140,6 @@ typedef struct DPDKThreadVars_ { } DPDKThreadVars; static TmEcode ReceiveDPDKThreadInit(ThreadVars *, const void *, void **); -static void ReceiveDPDKThreadExitStats(ThreadVars *, void *); static TmEcode ReceiveDPDKThreadDeinit(ThreadVars *, void *); static TmEcode ReceiveDPDKLoop(ThreadVars *tv, void *data, void *slot); @@ -257,7 +256,7 @@ void TmModuleReceiveDPDKRegister(void) tmm_modules[TMM_RECEIVEDPDK].Func = NULL; tmm_modules[TMM_RECEIVEDPDK].PktAcqLoop = ReceiveDPDKLoop; tmm_modules[TMM_RECEIVEDPDK].PktAcqBreakLoop = NULL; - tmm_modules[TMM_RECEIVEDPDK].ThreadExitPrintStats = ReceiveDPDKThreadExitStats; + tmm_modules[TMM_RECEIVEDPDK].ThreadExitPrintStats = NULL; tmm_modules[TMM_RECEIVEDPDK].ThreadDeinit = ReceiveDPDKThreadDeinit; tmm_modules[TMM_RECEIVEDPDK].cap_flags = SC_CAP_NET_RAW; tmm_modules[TMM_RECEIVEDPDK].flags = TM_FLAG_RECEIVE_TM; @@ -481,6 +480,54 @@ static inline void DPDKSegmentedMbufWarning(struct rte_mbuf *mbuf) } } +static void PrintDPDKPortXstats(uint16_t port_id, const char *port_name) +{ + struct rte_eth_xstat *xstats; + struct rte_eth_xstat_name *xstats_names; + + int32_t ret = rte_eth_xstats_get(port_id, NULL, 0); + if (ret < 0) { + SCLogPerf("%s: unable to obtain rte_eth_xstats (%s)", port_name, rte_strerror(-ret)); + return; + } + uint16_t len = (uint16_t)ret; + + xstats = SCCalloc(len, sizeof(*xstats)); + if (xstats == NULL) { + SCLogWarning("Failed to allocate memory for the rte_eth_xstat structure"); + return; + } + + ret = rte_eth_xstats_get(port_id, xstats, len); + if (ret < 0 || ret > len) { + SCFree(xstats); + SCLogPerf("%s: unable to obtain rte_eth_xstats (%s)", port_name, rte_strerror(-ret)); + return; + } + xstats_names = SCCalloc(len, sizeof(*xstats_names)); + if (xstats_names == NULL) { + SCFree(xstats); + SCLogWarning("Failed to allocate memory for the rte_eth_xstat_name array"); + return; + } + ret = rte_eth_xstats_get_names(port_id, xstats_names, len); + if (ret < 0 || ret > len) { + SCFree(xstats); + SCFree(xstats_names); + SCLogPerf( + "%s: unable to obtain names of rte_eth_xstats (%s)", port_name, rte_strerror(-ret)); + return; + } + for (int32_t i = 0; i < len; i++) { + if (xstats[i].value > 0) + SCLogPerf("Port %u (%s) - %s: %" PRIu64, port_id, port_name, xstats_names[i].name, + xstats[i].value); + } + + SCFree(xstats); + SCFree(xstats_names); +} + static void HandleShutdown(DPDKThreadVars *ptv) { SCLogDebug("Stopping Suricata!"); @@ -488,7 +535,11 @@ static void HandleShutdown(DPDKThreadVars *ptv) while (SC_ATOMIC_GET(ptv->workers_sync->worker_checked_in) < ptv->workers_sync->worker_cnt) { rte_delay_us(10); } + // Dump counters while device is still running - some drivers (e.g. BNXT) fail + // to report stats after the device is stopped + DPDKDumpCounters(ptv); if (ptv->queue_id == 0) { + PrintDPDKPortXstats(ptv->port_id, ptv->livedev->dev); rte_delay_us(20); // wait for all threads to get out of the sync loop SC_ATOMIC_SET(ptv->workers_sync->worker_checked_in, 0); // If Suricata runs in peered mode, the peer threads might still want to send @@ -501,7 +552,6 @@ static void HandleShutdown(DPDKThreadVars *ptv) rte_eth_dev_stop(ptv->port_id); } } - DPDKDumpCounters(ptv); } static void PeriodicDPDKDumpCounters(DPDKThreadVars *ptv) @@ -707,82 +757,6 @@ fail: SCReturnInt(TM_ECODE_FAILED); } -static void PrintDPDKPortXstats(uint16_t port_id, const char *port_name) -{ - struct rte_eth_xstat *xstats; - struct rte_eth_xstat_name *xstats_names; - - int32_t ret = rte_eth_xstats_get(port_id, NULL, 0); - if (ret < 0) { - FatalError("Error (%s) getting count of rte_eth_xstats failed on port %s", - rte_strerror(-ret), port_name); - } - uint16_t len = (uint16_t)ret; - - xstats = SCCalloc(len, sizeof(*xstats)); - if (xstats == NULL) - FatalError("Failed to allocate memory for the rte_eth_xstat structure"); - - ret = rte_eth_xstats_get(port_id, xstats, len); - if (ret < 0 || ret > len) { - SCFree(xstats); - FatalError("Error (%s) getting rte_eth_xstats failed on port %s", rte_strerror(-ret), - port_name); - } - xstats_names = SCCalloc(len, sizeof(*xstats_names)); - if (xstats_names == NULL) { - SCFree(xstats); - FatalError("Failed to allocate memory for the rte_eth_xstat_name array"); - } - ret = rte_eth_xstats_get_names(port_id, xstats_names, len); - if (ret < 0 || ret > len) { - SCFree(xstats); - SCFree(xstats_names); - FatalError("Error (%s) getting names of rte_eth_xstats failed on port %s", - rte_strerror(-ret), port_name); - } - for (int32_t i = 0; i < len; i++) { - if (xstats[i].value > 0) - SCLogPerf("Port %u (%s) - %s: %" PRIu64, port_id, port_name, xstats_names[i].name, - xstats[i].value); - } - - SCFree(xstats); - SCFree(xstats_names); -} - -/** - * \brief This function prints stats to the screen at exit. - * \param tv pointer to ThreadVars - * \param data pointer that gets cast into DPDKThreadVars for ptv - */ -static void ReceiveDPDKThreadExitStats(ThreadVars *tv, void *data) -{ - SCEnter(); - int retval; - DPDKThreadVars *ptv = (DPDKThreadVars *)data; - - if (ptv->queue_id == 0) { - struct rte_eth_stats eth_stats; - PrintDPDKPortXstats(ptv->port_id, ptv->livedev->dev); - retval = rte_eth_stats_get(ptv->port_id, ð_stats); - if (unlikely(retval != 0)) { - SCLogError("%s: failed to get stats (%s)", ptv->livedev->dev, strerror(-retval)); - SCReturn; - } - SCLogPerf("%s: total RX stats: packets %" PRIu64 " bytes: %" PRIu64 " missed: %" PRIu64 - " errors: %" PRIu64 " nombufs: %" PRIu64, - ptv->livedev->dev, eth_stats.ipackets, eth_stats.ibytes, eth_stats.imissed, - eth_stats.ierrors, eth_stats.rx_nombuf); - if (ptv->copy_mode == DPDK_COPY_MODE_TAP || ptv->copy_mode == DPDK_COPY_MODE_IPS) - SCLogPerf("%s: total TX stats: packets %" PRIu64 " bytes: %" PRIu64 " errors: %" PRIu64, - ptv->livedev->dev, eth_stats.opackets, eth_stats.obytes, eth_stats.oerrors); - } - - DPDKDumpCounters(ptv); - SCLogPerf("(%s) received packets %" PRIu64, tv->name, ptv->pkts); -} - /** * \brief DeInit function closes dpdk at exit. * \param tv pointer to ThreadVars