From 5dce019eee73ce66cc698376dc0f75d1d896a7c2 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 26 Jul 2026 19:13:36 -0700 Subject: [PATCH] Warn at XDP parallel start if the NIC firmware LLDP agent is active On Intel i40e/X710 NICs the firmware LLDP agent applies a default FCoE-aware DCB configuration that splits the queue pairs into two traffic classes (e.g. TC0 = queues 0-7, TC1 = queues 8-15). Hardware RSS only distributes within a traffic class, so the upper half of the receive queues -- and the libtrace processing threads reading them -- silently receive no packets. Every software-visible setting (channel count, RSS indirection table, sysfs queues) looks correct throughout, which makes this painful to diagnose: we lost several days to it and only found it via the driver's debugfs VSI dump (numtc=2). Add linux_get_nic_fw_lldp_enabled(), which looks up the "disable-fw-lldp" private flag by name via ETHTOOL_GDRVINFO / ETHTOOL_GSTRINGS / ETHTOOL_GPFLAGS (driver-agnostic; returns -1 when the driver has no such flag), and warn from linux_xdp_pstart_input() with the one-line remedy (ethtool --set-priv-flags disable-fw-lldp on, a persistent NVM write). --- lib/format_linux_helpers.c | 45 ++++++++++++++++++++++++++++++++++++++ lib/format_linux_helpers.h | 1 + lib/format_linux_xdp.c | 10 +++++++++ 3 files changed, 56 insertions(+) diff --git a/lib/format_linux_helpers.c b/lib/format_linux_helpers.c index e70f4cb4..d31bbffe 100644 --- a/lib/format_linux_helpers.c +++ b/lib/format_linux_helpers.c @@ -203,6 +203,51 @@ int linux_get_nic_flow_rule_count(char *ifname) return nfccmd.rule_cnt; } +int linux_get_nic_fw_lldp_enabled(char *ifname) +{ + + struct ethtool_drvinfo drvinfo = {}; + struct ethtool_gstrings *strings; + struct ethtool_value flags = {}; + uint32_t i; + int ret = -1; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + if (linux_send_ioctl_ethtool(&drvinfo, ifname) != 0) + return -1; + + /* the private flags bitmap ioctl only carries 32 flags */ + if (drvinfo.n_priv_flags == 0 || drvinfo.n_priv_flags > 32) + return -1; + + strings = + calloc(1, sizeof(*strings) + drvinfo.n_priv_flags * ETH_GSTRING_LEN); + if (strings == NULL) + return -1; + + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = ETH_SS_PRIV_FLAGS; + strings->len = drvinfo.n_priv_flags; + if (linux_send_ioctl_ethtool(strings, ifname) != 0) + goto out; + + flags.cmd = ETHTOOL_GPFLAGS; + if (linux_send_ioctl_ethtool(&flags, ifname) != 0) + goto out; + + for (i = 0; i < drvinfo.n_priv_flags; i++) { + char *name = (char *)strings->data + i * ETH_GSTRING_LEN; + if (strncmp(name, "disable-fw-lldp", ETH_GSTRING_LEN) == 0) { + ret = (flags.data & (1U << i)) ? 0 : 1; + break; + } + } + +out: + free(strings); + return ret; +} + static struct ethtool_ringparam * linux_get_nic_rings(struct ethtool_ringparam *ering, char *ifname) { diff --git a/lib/format_linux_helpers.h b/lib/format_linux_helpers.h index 904037a6..9d9249f6 100644 --- a/lib/format_linux_helpers.h +++ b/lib/format_linux_helpers.h @@ -36,6 +36,7 @@ int linux_get_nic_queues(char *ifname); int linux_set_nic_queues(char *ifname, int queues); int linux_set_nic_hasher(char *ifname, enum hasher_types hasher); int linux_get_nic_flow_rule_count(char *ifname); +int linux_get_nic_fw_lldp_enabled(char *ifname); int linux_get_nic_rx_rings(char *ifname); int linux_get_nic_tx_rings(char *ifname); int linux_get_nic_max_rx_rings(char *ifname); diff --git a/lib/format_linux_xdp.c b/lib/format_linux_xdp.c index 7d0a902d..63b31212 100644 --- a/lib/format_linux_xdp.c +++ b/lib/format_linux_xdp.c @@ -684,6 +684,16 @@ static int linux_xdp_pstart_input(libtrace_t *libtrace) libtrace->perpkt_thread_count = max_nic_queues; } + if (linux_get_nic_fw_lldp_enabled(XDP_FORMAT_DATA->cfg.ifname) == 1) { + fprintf(stderr, + "Linux XDP: firmware LLDP agent is enabled on %s, on some " + "NICs (e.g. Intel i40e) this limits RSS to half the receive " + "queues! If some processing threads receive no packets " + "disable it with: ethtool --set-priv-flags %s " + "disable-fw-lldp on\n", + XDP_FORMAT_DATA->cfg.ifname, XDP_FORMAT_DATA->cfg.ifname); + } + /* set the number of nic queues to match number of threads */ if (linux_set_nic_queues(XDP_FORMAT_DATA->cfg.ifname, libtrace->perpkt_thread_count) !=