DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 09/10] net/mlx5: use bitset for tracking MAC addresses
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev
  Cc: rjarry, cfontain, Dariusz Sosnowski, Viacheslav Ovsiienko,
	Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

EAL provides bitset that does the same as this set of mlx5 macros.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 drivers/common/mlx5/mlx5_common.h  | 16 ----------------
 drivers/net/mlx5/linux/mlx5_os.c   |  8 ++++----
 drivers/net/mlx5/mlx5.h            |  3 ++-
 drivers/net/mlx5/mlx5_trigger.c    |  2 +-
 drivers/net/mlx5/windows/mlx5_os.c |  8 ++++----
 5 files changed, 11 insertions(+), 26 deletions(-)

diff --git a/drivers/common/mlx5/mlx5_common.h b/drivers/common/mlx5/mlx5_common.h
index dbc06aff7e..71985794fa 100644
--- a/drivers/common/mlx5/mlx5_common.h
+++ b/drivers/common/mlx5/mlx5_common.h
@@ -30,22 +30,6 @@
 #define MLX5_PCI_DRIVER_NAME "mlx5_pci"
 #define MLX5_AUXILIARY_DRIVER_NAME "mlx5_auxiliary"
 
-/* Bit-field manipulation. */
-#define BITFIELD_DECLARE(bf, type, size) \
-	type bf[(((size_t)(size) / (sizeof(type) * CHAR_BIT)) + \
-		!!((size_t)(size) % (sizeof(type) * CHAR_BIT)))]
-#define BITFIELD_DEFINE(bf, type, size) \
-	BITFIELD_DECLARE((bf), type, (size)) = { 0 }
-#define BITFIELD_SET(bf, b) \
-	(void)((bf)[((b) / (sizeof((bf)[0]) * CHAR_BIT))] |= \
-		((size_t)1 << ((b) % (sizeof((bf)[0]) * CHAR_BIT))))
-#define BITFIELD_RESET(bf, b) \
-	(void)((bf)[((b) / (sizeof((bf)[0]) * CHAR_BIT))] &= \
-		~((size_t)1 << ((b) % (sizeof((bf)[0]) * CHAR_BIT))))
-#define BITFIELD_ISSET(bf, b) \
-	!!(((bf)[((b) / (sizeof((bf)[0]) * CHAR_BIT))] & \
-		((size_t)1 << ((b) % (sizeof((bf)[0]) * CHAR_BIT)))))
-
 /*
  * Helper macros to work around __VA_ARGS__ limitations in a C99 compliant
  * manner.
diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index 5b6e45df2a..1cad6e1091 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -3384,7 +3384,7 @@ mlx5_os_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
 		mlx5_nl_mac_addr_remove(priv->nl_socket_route,
 					mlx5_ifindex(dev),
 					&dev->data->mac_addrs[index]);
-	BITFIELD_RESET(priv->mac_own, index);
+	rte_bitset_clear(priv->mac_own, index);
 }
 
 /**
@@ -3413,7 +3413,7 @@ mlx5_os_mac_addr_add(struct rte_eth_dev *dev, struct rte_ether_addr *mac,
 					   mlx5_ifindex(dev),
 					   mac);
 	if (!ret)
-		BITFIELD_SET(priv->mac_own, index);
+		rte_bitset_set(priv->mac_own, index);
 
 	return ret;
 }
@@ -3498,12 +3498,12 @@ mlx5_os_mac_addr_flush(struct rte_eth_dev *dev)
 	int i;
 
 	for (i = MLX5_MAX_MAC_ADDRESSES - 1; i >= 0; --i) {
-		if (BITFIELD_ISSET(priv->mac_own, i)) {
+		if (rte_bitset_test(priv->mac_own, i)) {
 			if (vf)
 				mlx5_nl_mac_addr_remove(priv->nl_socket_route,
 							mlx5_ifindex(dev),
 							&dev->data->mac_addrs[i]);
-			BITFIELD_RESET(priv->mac_own, i);
+			rte_bitset_clear(priv->mac_own, i);
 		}
 	}
 }
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index 3ad8bad02f..f7ba8df108 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -14,6 +14,7 @@
 
 #include <rte_pci.h>
 #include <rte_ether.h>
+#include <rte_bitset.h>
 #include <ethdev_driver.h>
 #include <rte_rwlock.h>
 #include <rte_interrupts.h>
@@ -2018,7 +2019,7 @@ struct mlx5_priv {
 	uint32_t dev_port; /* Device port number. */
 	struct rte_pci_device *pci_dev; /* Backend PCI device. */
 	struct rte_ether_addr mac[MLX5_MAX_MAC_ADDRESSES]; /* MAC addresses. */
-	BITFIELD_DECLARE(mac_own, uint64_t, MLX5_MAX_MAC_ADDRESSES);
+	RTE_BITSET_DECLARE(mac_own, MLX5_MAX_MAC_ADDRESSES);
 	/* Bit-field of MAC addresses owned by the PMD. */
 	uint16_t vlan_filter[MLX5_MAX_VLAN_IDS]; /* VLAN filters table. */
 	unsigned int vlan_filter_n; /* Number of configured VLAN filters. */
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index 25847c8ba2..e41e4643e0 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -1907,7 +1907,7 @@ mlx5_traffic_enable(struct rte_eth_dev *dev)
 
 		/* Add flows for unicast and multicast mac addresses added by API. */
 		if (!memcmp(mac, &cmp, sizeof(*mac)) ||
-		    !BITFIELD_ISSET(priv->mac_own, i) ||
+		    !rte_bitset_test(priv->mac_own, i) ||
 		    (dev->data->all_multicast && rte_is_multicast_ether_addr(mac)))
 			continue;
 		memcpy(&unicast.hdr.dst_addr.addr_bytes,
diff --git a/drivers/net/mlx5/windows/mlx5_os.c b/drivers/net/mlx5/windows/mlx5_os.c
index 9acfa8ec84..15de5c22a9 100644
--- a/drivers/net/mlx5/windows/mlx5_os.c
+++ b/drivers/net/mlx5/windows/mlx5_os.c
@@ -699,8 +699,8 @@ mlx5_os_mac_addr_flush(struct rte_eth_dev *dev)
 	int i;
 
 	for (i = MLX5_MAX_MAC_ADDRESSES - 1; i >= 0; --i) {
-		if (BITFIELD_ISSET(priv->mac_own, i))
-			BITFIELD_RESET(priv->mac_own, i);
+		if (rte_bitset_test(priv->mac_own, i))
+			rte_bitset_clear(priv->mac_own, i);
 	}
 }
 
@@ -719,7 +719,7 @@ mlx5_os_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
 	struct mlx5_priv *priv = dev->data->dev_private;
 
 	if (index < MLX5_MAX_MAC_ADDRESSES)
-		BITFIELD_RESET(priv->mac_own, index);
+		rte_bitset_clear(priv->mac_own, index);
 }
 
 /**
@@ -757,7 +757,7 @@ mlx5_os_mac_addr_add(struct rte_eth_dev *dev, struct rte_ether_addr *mac,
 		return -ENOTSUP;
 	}
 	/* Mark this MAC address as owned by the PMD */
-	BITFIELD_SET(priv->mac_own, index);
+	rte_bitset_set(priv->mac_own, index);
 	return 0;
 }
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 08/10] net/mlx5: pass maximum number of unicast MAC to common code
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev
  Cc: rjarry, cfontain, Dariusz Sosnowski, Viacheslav Ovsiienko,
	Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

Isolate how the MAC addresses array is walked through in the common code
by passing the max index at which a unicast MAC address is stored in
dev->data->mac_addrs[].

In the sync callback, the size of the array allocated on the stack is
known by the caller, treat the mac_n field as an input parameter too.

With this change, only net/mlx5 knows about the max number of
unicast/multicast MAC addresses.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 drivers/common/mlx5/linux/mlx5_nl.c | 33 ++++++++++++++++++-----------
 drivers/common/mlx5/linux/mlx5_nl.h |  2 +-
 drivers/common/mlx5/mlx5_common.h   |  8 -------
 drivers/net/mlx5/linux/mlx5_os.c    |  1 +
 drivers/net/mlx5/mlx5.h             |  8 +++++++
 5 files changed, 31 insertions(+), 21 deletions(-)

diff --git a/drivers/common/mlx5/linux/mlx5_nl.c b/drivers/common/mlx5/linux/mlx5_nl.c
index ceb504f84d..152b4cdda1 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.c
+++ b/drivers/common/mlx5/linux/mlx5_nl.c
@@ -468,19 +468,22 @@ mlx5_nl_mac_addr_cb(struct nlmsghdr *nh, void *arg)
 	struct mlx5_nl_mac_addr *data = arg;
 	struct ndmsg *r = NLMSG_DATA(nh);
 	struct rtattr *attribute;
+	int mac_n = 0;
 	int len;
+	int ret;
 
 	len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
 	for (attribute = MLX5_NDA_RTA(r);
 	     RTA_OK(attribute, len);
 	     attribute = RTA_NEXT(attribute, len)) {
 		if (attribute->rta_type == NDA_LLADDR) {
-			if (data->mac_n == MLX5_MAX_MAC_ADDRESSES) {
+			if (mac_n == data->mac_n) {
 				DRV_LOG(WARNING,
 					"not enough room to finalize the"
 					" request");
 				rte_errno = ENOMEM;
-				return -rte_errno;
+				ret = -rte_errno;
+				goto out;
 			}
 #ifdef RTE_PMD_MLX5_DEBUG
 			char m[RTE_ETHER_ADDR_FMT_SIZE];
@@ -489,11 +492,15 @@ mlx5_nl_mac_addr_cb(struct nlmsghdr *nh, void *arg)
 					      RTA_DATA(attribute));
 			DRV_LOG(DEBUG, "bridge MAC address %s", m);
 #endif
-			memcpy(&(*data->mac)[data->mac_n++],
+			memcpy(&(*data->mac)[mac_n++],
 			       RTA_DATA(attribute), RTE_ETHER_ADDR_LEN);
 		}
 	}
-	return 0;
+	ret = 0;
+
+out:
+	data->mac_n = mac_n;
+	return ret;
 }
 
 /**
@@ -505,9 +512,9 @@ mlx5_nl_mac_addr_cb(struct nlmsghdr *nh, void *arg)
  *   Net device interface index.
  * @param mac[out]
  *   Pointer to the array table of MAC addresses to fill.
- *   Its size should be of MLX5_MAX_MAC_ADDRESSES.
- * @param mac_n[out]
- *   Number of entries filled in MAC array.
+ * @param mac_n[in,out]
+ *   Size of the MAC array on input.
+ *   Number of entries filled in MAC array on output.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -532,7 +539,7 @@ mlx5_nl_mac_addr_list(int nlsk_fd, unsigned int iface_idx,
 	};
 	struct mlx5_nl_mac_addr data = {
 		.mac = mac,
-		.mac_n = 0,
+		.mac_n = *mac_n,
 	};
 	uint32_t sn = MLX5_NL_SN_GENERATE;
 	int ret;
@@ -766,16 +773,18 @@ mlx5_nl_mac_addr_remove(int nlsk_fd, unsigned int iface_idx,
  *   Net device interface index.
  * @param mac_addrs
  *   Mac addresses array to sync.
+ * @param uc_n
+ *   Number of UC entries in @p mac_addrs.
  * @param n
  *   @p mac_addrs array size.
  */
 RTE_EXPORT_INTERNAL_SYMBOL(mlx5_nl_mac_addr_sync)
 void
 mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
-		      struct rte_ether_addr *mac_addrs, int n)
+		      struct rte_ether_addr *mac_addrs, int uc_n, int n)
 {
 	struct rte_ether_addr macs[n];
-	int macs_n = 0;
+	int macs_n = n;
 	int i;
 	int ret;
 
@@ -794,7 +803,7 @@ mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
 			continue;
 		if (rte_is_multicast_ether_addr(&macs[i])) {
 			/* Find the first entry available. */
-			for (j = MLX5_MAX_UC_MAC_ADDRESSES; j != n; ++j) {
+			for (j = uc_n; j != n; ++j) {
 				if (rte_is_zero_ether_addr(&mac_addrs[j])) {
 					mac_addrs[j] = macs[i];
 					break;
@@ -802,7 +811,7 @@ mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
 			}
 		} else {
 			/* Find the first entry available. */
-			for (j = 0; j != MLX5_MAX_UC_MAC_ADDRESSES; ++j) {
+			for (j = 0; j != uc_n; ++j) {
 				if (rte_is_zero_ether_addr(&mac_addrs[j])) {
 					mac_addrs[j] = macs[i];
 					break;
diff --git a/drivers/common/mlx5/linux/mlx5_nl.h b/drivers/common/mlx5/linux/mlx5_nl.h
index 500198b654..d71eadeffb 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.h
+++ b/drivers/common/mlx5/linux/mlx5_nl.h
@@ -63,7 +63,7 @@ int mlx5_nl_mac_addr_remove(int nlsk_fd, unsigned int iface_idx,
 			    struct rte_ether_addr *mac);
 __rte_internal
 void mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
-			   struct rte_ether_addr *mac_addrs, int n);
+			   struct rte_ether_addr *mac_addrs, int uc_n, int n);
 __rte_internal
 int mlx5_nl_promisc(int nlsk_fd, unsigned int iface_idx, int enable);
 __rte_internal
diff --git a/drivers/common/mlx5/mlx5_common.h b/drivers/common/mlx5/mlx5_common.h
index 3e66c9e6c8..dbc06aff7e 100644
--- a/drivers/common/mlx5/mlx5_common.h
+++ b/drivers/common/mlx5/mlx5_common.h
@@ -159,14 +159,6 @@ enum {
 	PCI_DEVICE_ID_MELLANOX_CONNECTX10 = 0x1027,
 };
 
-/* Maximum number of simultaneous unicast MAC addresses. */
-#define MLX5_MAX_UC_MAC_ADDRESSES 128
-/* Maximum number of simultaneous Multicast MAC addresses. */
-#define MLX5_MAX_MC_MAC_ADDRESSES 128
-/* Maximum number of simultaneous MAC addresses. */
-#define MLX5_MAX_MAC_ADDRESSES \
-	(MLX5_MAX_UC_MAC_ADDRESSES + MLX5_MAX_MC_MAC_ADDRESSES)
-
 /* Recognized Infiniband device physical port name types. */
 enum mlx5_nl_phys_port_name_type {
 	MLX5_PHYS_PORT_NAME_TYPE_NOTSET = 0, /* Not set. */
diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index 1b241ba9d2..5b6e45df2a 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -1762,6 +1762,7 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
 		mlx5_nl_mac_addr_sync(priv->nl_socket_route,
 				      mlx5_ifindex(eth_dev),
 				      eth_dev->data->mac_addrs,
+				      MLX5_MAX_UC_MAC_ADDRESSES,
 				      MLX5_MAX_MAC_ADDRESSES);
 	priv->ctrl_flows = 0;
 	rte_spinlock_init(&priv->flow_list_lock);
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index 27e6f4e31a..3ad8bad02f 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -82,6 +82,14 @@
 /* Maximum allowed MTU to be reported whenever PMD cannot query it from OS. */
 #define MLX5_ETH_MAX_MTU (9978)
 
+/* Maximum number of simultaneous unicast MAC addresses. */
+#define MLX5_MAX_UC_MAC_ADDRESSES 128
+/* Maximum number of simultaneous Multicast MAC addresses. */
+#define MLX5_MAX_MC_MAC_ADDRESSES 128
+/* Maximum number of simultaneous MAC addresses. */
+#define MLX5_MAX_MAC_ADDRESSES \
+	(MLX5_MAX_UC_MAC_ADDRESSES + MLX5_MAX_MC_MAC_ADDRESSES)
+
 enum mlx5_ipool_index {
 #if defined(HAVE_IBV_FLOW_DV_SUPPORT) || !defined(HAVE_INFINIBAND_VERBS_H)
 	MLX5_IPOOL_DECAP_ENCAP = 0, /* Pool for encap/decap resource. */
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 07/10] net/mlx5: remove redundant MAC address index checks
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev
  Cc: rjarry, cfontain, Dariusz Sosnowski, Viacheslav Ovsiienko,
	Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

On the net/mlx5 side, the OS specific helpers do not need to implement
again checks on the MAC index, the cde from mlx5_mac.c already does this.

Cascading this consideration, validating the MAC index against
MLX5_MAX_MAC_ADDRESSES in common code is also redundant.
The common code only deals with netlink, remove any index concern.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 drivers/common/mlx5/linux/mlx5_nl.c | 17 ++---------------
 drivers/common/mlx5/linux/mlx5_nl.h |  4 ++--
 drivers/net/mlx5/linux/mlx5_os.c    |  9 ++++-----
 3 files changed, 8 insertions(+), 22 deletions(-)

diff --git a/drivers/common/mlx5/linux/mlx5_nl.c b/drivers/common/mlx5/linux/mlx5_nl.c
index 85736738ac..ceb504f84d 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.c
+++ b/drivers/common/mlx5/linux/mlx5_nl.c
@@ -719,8 +719,6 @@ mlx5_nl_vf_mac_addr_modify(int nlsk_fd, unsigned int iface_idx,
  *   Net device interface index.
  * @param mac
  *   MAC address to register.
- * @param index
- *   MAC address index.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -728,16 +726,11 @@ mlx5_nl_vf_mac_addr_modify(int nlsk_fd, unsigned int iface_idx,
 RTE_EXPORT_INTERNAL_SYMBOL(mlx5_nl_mac_addr_add)
 int
 mlx5_nl_mac_addr_add(int nlsk_fd, unsigned int iface_idx,
-		     struct rte_ether_addr *mac, uint32_t index)
+		     struct rte_ether_addr *mac)
 {
 	int ret;
 
 	ret = mlx5_nl_mac_addr_modify(nlsk_fd, iface_idx, mac, 1);
-	if (!ret) {
-		MLX5_ASSERT(index < MLX5_MAX_MAC_ADDRESSES);
-		if (index >= MLX5_MAX_MAC_ADDRESSES)
-			return -EINVAL;
-	}
 	if (ret == -EEXIST)
 		return 0;
 	return ret;
@@ -752,8 +745,6 @@ mlx5_nl_mac_addr_add(int nlsk_fd, unsigned int iface_idx,
  *   Net device interface index.
  * @param mac
  *   MAC address to remove.
- * @param index
- *   MAC address index.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -761,12 +752,8 @@ mlx5_nl_mac_addr_add(int nlsk_fd, unsigned int iface_idx,
 RTE_EXPORT_INTERNAL_SYMBOL(mlx5_nl_mac_addr_remove)
 int
 mlx5_nl_mac_addr_remove(int nlsk_fd, unsigned int iface_idx,
-			struct rte_ether_addr *mac, uint32_t index)
+			struct rte_ether_addr *mac)
 {
-	MLX5_ASSERT(index < MLX5_MAX_MAC_ADDRESSES);
-	if (index >= MLX5_MAX_MAC_ADDRESSES)
-		return -EINVAL;
-
 	return mlx5_nl_mac_addr_modify(nlsk_fd, iface_idx, mac, 0);
 }
 
diff --git a/drivers/common/mlx5/linux/mlx5_nl.h b/drivers/common/mlx5/linux/mlx5_nl.h
index 256ed7e2b7..500198b654 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.h
+++ b/drivers/common/mlx5/linux/mlx5_nl.h
@@ -57,10 +57,10 @@ __rte_internal
 int mlx5_nl_init(int protocol, int groups);
 __rte_internal
 int mlx5_nl_mac_addr_add(int nlsk_fd, unsigned int iface_idx,
-			 struct rte_ether_addr *mac, uint32_t index);
+			 struct rte_ether_addr *mac);
 __rte_internal
 int mlx5_nl_mac_addr_remove(int nlsk_fd, unsigned int iface_idx,
-			    struct rte_ether_addr *mac, uint32_t index);
+			    struct rte_ether_addr *mac);
 __rte_internal
 void mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
 			   struct rte_ether_addr *mac_addrs, int n);
diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index 9fd366b10a..1b241ba9d2 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -3382,9 +3382,8 @@ mlx5_os_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
 	if (vf)
 		mlx5_nl_mac_addr_remove(priv->nl_socket_route,
 					mlx5_ifindex(dev),
-					&dev->data->mac_addrs[index], index);
-	if (index < MLX5_MAX_MAC_ADDRESSES)
-		BITFIELD_RESET(priv->mac_own, index);
+					&dev->data->mac_addrs[index]);
+	BITFIELD_RESET(priv->mac_own, index);
 }
 
 /**
@@ -3411,7 +3410,7 @@ mlx5_os_mac_addr_add(struct rte_eth_dev *dev, struct rte_ether_addr *mac,
 	if (vf)
 		ret = mlx5_nl_mac_addr_add(priv->nl_socket_route,
 					   mlx5_ifindex(dev),
-					   mac, index);
+					   mac);
 	if (!ret)
 		BITFIELD_SET(priv->mac_own, index);
 
@@ -3502,7 +3501,7 @@ mlx5_os_mac_addr_flush(struct rte_eth_dev *dev)
 			if (vf)
 				mlx5_nl_mac_addr_remove(priv->nl_socket_route,
 							mlx5_ifindex(dev),
-							&dev->data->mac_addrs[i], i);
+							&dev->data->mac_addrs[i]);
 			BITFIELD_RESET(priv->mac_own, i);
 		}
 	}
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 06/10] net/mlx5: remove MAC addresses flush helper on Linux
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev
  Cc: rjarry, cfontain, Dariusz Sosnowski, Viacheslav Ovsiienko,
	Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

This helper is exposing internals of net/mlx5 for no good reason.
All this code does is calling the remove helper.
Walk through the list in Linux implementation like the Windows
implementation.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 drivers/common/mlx5/linux/mlx5_nl.c | 40 -----------------------------
 drivers/common/mlx5/linux/mlx5_nl.h |  5 ----
 drivers/net/mlx5/linux/mlx5_os.c    | 13 +++++++---
 3 files changed, 10 insertions(+), 48 deletions(-)

diff --git a/drivers/common/mlx5/linux/mlx5_nl.c b/drivers/common/mlx5/linux/mlx5_nl.c
index 8b19838a7e..85736738ac 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.c
+++ b/drivers/common/mlx5/linux/mlx5_nl.c
@@ -825,46 +825,6 @@ mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
 	}
 }
 
-/**
- * Flush all added MAC addresses.
- *
- * @param[in] nlsk_fd
- *   Netlink socket file descriptor.
- * @param[in] iface_idx
- *   Net device interface index.
- * @param[in] mac_addrs
- *   Mac addresses array to flush.
- * @param n
- *   @p mac_addrs array size.
- * @param mac_own
- *   BITFIELD_DECLARE array to store the mac.
- * @param vf
- *   Flag for a VF device.
- */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_nl_mac_addr_flush)
-void
-mlx5_nl_mac_addr_flush(int nlsk_fd, unsigned int iface_idx,
-		       struct rte_ether_addr *mac_addrs, int n,
-		       uint64_t *mac_own, bool vf)
-{
-	int i;
-
-	if (n <= 0 || n > MLX5_MAX_MAC_ADDRESSES)
-		return;
-
-	for (i = n - 1; i >= 0; --i) {
-		struct rte_ether_addr *m = &mac_addrs[i];
-
-		if (BITFIELD_ISSET(mac_own, i)) {
-			if (vf)
-				mlx5_nl_mac_addr_remove(nlsk_fd,
-							iface_idx,
-							m, i);
-			BITFIELD_RESET(mac_own, i);
-		}
-	}
-}
-
 /**
  * Enable promiscuous / all multicast mode through Netlink.
  *
diff --git a/drivers/common/mlx5/linux/mlx5_nl.h b/drivers/common/mlx5/linux/mlx5_nl.h
index 3f79a73c85..256ed7e2b7 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.h
+++ b/drivers/common/mlx5/linux/mlx5_nl.h
@@ -65,11 +65,6 @@ __rte_internal
 void mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
 			   struct rte_ether_addr *mac_addrs, int n);
 __rte_internal
-void mlx5_nl_mac_addr_flush(int nlsk_fd, unsigned int iface_idx,
-			    struct rte_ether_addr *mac_addrs, int n,
-			    uint64_t *mac_own,
-			    bool vf);
-__rte_internal
 int mlx5_nl_promisc(int nlsk_fd, unsigned int iface_idx, int enable);
 __rte_internal
 int mlx5_nl_allmulti(int nlsk_fd, unsigned int iface_idx, int enable);
diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index adc5878296..9fd366b10a 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -3495,10 +3495,17 @@ mlx5_os_mac_addr_flush(struct rte_eth_dev *dev)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	const int vf = priv->sh->dev_cap.vf;
+	int i;
 
-	mlx5_nl_mac_addr_flush(priv->nl_socket_route, mlx5_ifindex(dev),
-			       dev->data->mac_addrs,
-			       MLX5_MAX_MAC_ADDRESSES, priv->mac_own, vf);
+	for (i = MLX5_MAX_MAC_ADDRESSES - 1; i >= 0; --i) {
+		if (BITFIELD_ISSET(priv->mac_own, i)) {
+			if (vf)
+				mlx5_nl_mac_addr_remove(priv->nl_socket_route,
+							mlx5_ifindex(dev),
+							&dev->data->mac_addrs[i], i);
+			BITFIELD_RESET(priv->mac_own, i);
+		}
+	}
 }
 
 static bool
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 05/10] net/iavf: fix duplicate MAC addresses install
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev; +Cc: rjarry, cfontain, stable, Vladimir Medvedkin, Bruce Richardson
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

On port restart, all MAC addresses get pushed *twice* to the hardware,
once by the driver and once by the eth_dev_mac_restore() in ethdev.

On the other hand, MAC address filters are reset in the hardware
by the PF only when a VF reset is triggered.

Strictly speaking, the mac restore on port (re)start is unneeded,
if no VF reset happened, so we can announce to ethdev that no mac
restoration is needed via a get_restore_flags callback.

Then, move the mac restoration to the VF reset handler.

Fixes: 3d42086def30 ("net/iavf: preserve MAC address with i40e PF Linux driver")
Cc: stable@dpdk.org

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 drivers/net/intel/iavf/iavf_ethdev.c | 31 ++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 9 deletions(-)

diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 179d90ec55..fe54df4b9f 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -134,6 +134,8 @@ static int iavf_dev_vlan_filter_set(struct rte_eth_dev *dev,
 static int iavf_vlan_tpid_set(struct rte_eth_dev *dev,
 			     enum rte_vlan_type vlan_type, uint16_t tpid);
 static int iavf_dev_vlan_offload_set(struct rte_eth_dev *dev, int mask);
+static uint64_t iavf_get_restore_flags(struct rte_eth_dev *dev,
+				       enum rte_eth_dev_operation op);
 static int iavf_dev_rss_reta_update(struct rte_eth_dev *dev,
 				   struct rte_eth_rss_reta_entry64 *reta_conf,
 				   uint16_t reta_size);
@@ -264,6 +266,7 @@ static const struct eth_dev_ops iavf_eth_dev_ops = {
 	.tx_done_cleanup	    = iavf_dev_tx_done_cleanup,
 	.get_monitor_addr           = iavf_get_monitor_addr,
 	.tm_ops_get                 = iavf_tm_ops_get,
+	.get_restore_flags          = iavf_get_restore_flags,
 };
 
 static int
@@ -284,6 +287,13 @@ iavf_tm_ops_get(struct rte_eth_dev *dev,
 	return 0;
 }
 
+static uint64_t
+iavf_get_restore_flags(__rte_unused struct rte_eth_dev *dev,
+		       __rte_unused enum rte_eth_dev_operation op)
+{
+	return RTE_ETH_RESTORE_ALL & ~RTE_ETH_RESTORE_MAC_ADDR;
+}
+
 __rte_unused
 static int
 iavf_vfr_inprogress(struct iavf_hw *hw)
@@ -1074,15 +1084,14 @@ iavf_dev_start(struct rte_eth_dev *dev)
 		rte_intr_enable(intr_handle);
 	}
 
-	/* Set all mac addrs */
-	iavf_add_del_all_mac_addr(adapter, true);
-
-	if (!adapter->mac_primary_set)
-		adapter->mac_primary_set = true;
-
-	/* Set all multicast addresses */
-	iavf_add_del_mc_addr_list(adapter, vf->mc_addrs, vf->mc_addrs_num,
-				  true);
+	if (!adapter->mac_primary_set) {
+		if (iavf_add_del_eth_addr(adapter, &dev->data->mac_addrs[0], true,
+				VIRTCHNL_ETHER_ADDR_PRIMARY) != 0)
+			PMD_DRV_LOG(ERR, "failed to add primary MAC:" RTE_ETHER_ADDR_PRT_FMT,
+				RTE_ETHER_ADDR_BYTES(&dev->data->mac_addrs[0]));
+		else
+			adapter->mac_primary_set = true;
+	}
 
 	rte_spinlock_init(&vf->phc_time_aq_lock);
 
@@ -3424,6 +3433,10 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev, bool vf_initiated_reset)
 		if (ret)
 			goto error;
 
+		/* after a VF reset, all mac addresses got flushed, restore them */
+		iavf_add_del_all_mac_addr(adapter, true);
+		iavf_add_del_mc_addr_list(adapter, vf->mc_addrs, vf->mc_addrs_num, true);
+
 		dev->data->dev_started = 1;
 	}
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 04/10] net/iavf: accept up to 32k unicast MAC addresses
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev; +Cc: rjarry, cfontain, Vladimir Medvedkin
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

E810 hardware provides 32k switch lookups.
Thanks to this, it is possible to allow a lot more secondary mac
addresses than what is possible today.

In practice, the maximum number of macs available per port may be lower
and depends on usage by other (trusted?) VFs on the same PF.
There is no way to figure out this limit but to try adding a mac address
and get an error from the PF driver.

Mailbox exchanges are limited to IAVF_AQ_BUF_SZ, segment messages
accordingly.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since v2:
- added an entry in release notes,
- removed unneeded temp variable,

Changes since v1:
- fixed buffer overflow on mailbox messages during port restart/VF reset,

---
 doc/guides/rel_notes/release_26_07.rst |   5 ++
 drivers/net/intel/iavf/iavf.h          |   5 +-
 drivers/net/intel/iavf/iavf_ethdev.c   |  12 +--
 drivers/net/intel/iavf/iavf_vchnl.c    | 113 ++++++++++++++++++-------
 4 files changed, 95 insertions(+), 40 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 6b6fbe0b1a..4225bd43a0 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -259,6 +259,11 @@ New Features
   Added AGENTS.md file for AI review
   and supporting scripts to review patches and documentation.
 
+* **Updated IAVF ethernet driver.**
+
+  * Increased the maximum number of secondary unicast MAC addresses from 64 to 32k.
+    This increases a VF port memory footprint by ~192kB.
+
 
 Removed Items
 -------------
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 293adaf6c9..47cd1d6311 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -31,7 +31,8 @@
 #define IAVF_IRQ_MAP_NUM_PER_BUF	 128
 #define IAVF_RXTX_QUEUE_CHUNKS_NUM	 2
 
-#define IAVF_NUM_MACADDR_MAX      64
+#define IAVF_UC_MACADDR_MAX      32768
+#define IAVF_MC_MACADDR_MAX      64
 
 #define IAVF_DEV_WATCHDOG_PERIOD     2000 /* microseconds, set 0 to disable*/
 
@@ -255,7 +256,7 @@ struct iavf_info {
 	uint32_t link_speed;
 
 	/* Multicast addrs */
-	struct rte_ether_addr mc_addrs[IAVF_NUM_MACADDR_MAX];
+	struct rte_ether_addr mc_addrs[IAVF_MC_MACADDR_MAX];
 	uint16_t mc_addrs_num;   /* Multicast mac addresses number */
 
 	struct iavf_vsi vsi;
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 80e740ef29..179d90ec55 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -394,10 +394,10 @@ iavf_set_mc_addr_list(struct rte_eth_dev *dev,
 		IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	int err, ret;
 
-	if (mc_addrs_num > IAVF_NUM_MACADDR_MAX) {
+	if (mc_addrs_num > IAVF_MC_MACADDR_MAX) {
 		PMD_DRV_LOG(ERR,
 			    "can't add more than a limited number (%u) of addresses.",
-			    (uint32_t)IAVF_NUM_MACADDR_MAX);
+			    (uint32_t)IAVF_MC_MACADDR_MAX);
 		return -EINVAL;
 	}
 
@@ -1159,7 +1159,7 @@ iavf_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->hash_key_size = vf->vf_res->rss_key_size;
 	dev_info->reta_size = vf->vf_res->rss_lut_size;
 	dev_info->flow_type_rss_offloads = IAVF_RSS_OFFLOAD_ALL;
-	dev_info->max_mac_addrs = IAVF_NUM_MACADDR_MAX;
+	dev_info->max_mac_addrs = IAVF_UC_MACADDR_MAX;
 	dev_info->dev_capa =
 		RTE_ETH_DEV_CAPA_RUNTIME_RX_QUEUE_SETUP |
 		RTE_ETH_DEV_CAPA_RUNTIME_TX_QUEUE_SETUP;
@@ -3051,12 +3051,12 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
 	iavf_set_default_ptype_table(eth_dev);
 
 	/* copy mac addr */
-	eth_dev->data->mac_addrs = rte_zmalloc(
-		"iavf_mac", RTE_ETHER_ADDR_LEN * IAVF_NUM_MACADDR_MAX, 0);
+	eth_dev->data->mac_addrs = rte_calloc("iavf_mac", IAVF_UC_MACADDR_MAX,
+		RTE_ETHER_ADDR_LEN, 0);
 	if (!eth_dev->data->mac_addrs) {
 		PMD_INIT_LOG(ERR, "Failed to allocate %d bytes needed to"
 			     " store MAC addresses",
-			     RTE_ETHER_ADDR_LEN * IAVF_NUM_MACADDR_MAX);
+			     RTE_ETHER_ADDR_LEN * IAVF_UC_MACADDR_MAX);
 		ret = -ENOMEM;
 		goto init_vf_err;
 	}
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index cd6e8325fc..6743183bf9 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -1648,49 +1648,98 @@ iavf_config_irq_map_lv(struct iavf_adapter *adapter, uint16_t num)
 	return 0;
 }
 
-void
-iavf_add_del_all_mac_addr(struct iavf_adapter *adapter, bool add)
+static int
+iavf_add_del_uc_addr_bulk(struct iavf_adapter *adapter, struct rte_ether_addr *addrs,
+			  uint32_t nb_addrs, bool add)
 {
+#define IAVF_ETH_ADDR_PER_REQ \
+	((IAVF_AQ_BUF_SZ - sizeof(struct virtchnl_ether_addr_list)) / \
+	 sizeof(struct virtchnl_ether_addr))
 	struct {
 		struct virtchnl_ether_addr_list list;
-		struct virtchnl_ether_addr addr[IAVF_NUM_MACADDR_MAX];
-	} list_req = {0};
-	struct virtchnl_ether_addr_list *list = &list_req.list;
+		struct virtchnl_ether_addr addr[IAVF_ETH_ADDR_PER_REQ];
+	} cmd_buffer;
+#undef IAVF_ETH_ADDR_PER_REQ
+	struct virtchnl_ether_addr_list *list = &cmd_buffer.list;
 	struct iavf_info *vf = IAVF_DEV_PRIVATE_TO_VF(adapter);
 	uint8_t msg_buf[IAVF_AQ_BUF_SZ] = {0};
-	struct iavf_cmd_info args = {0};
-	int err, i;
-	size_t buf_len;
 
-	for (i = 0; i < IAVF_NUM_MACADDR_MAX; i++) {
-		struct rte_ether_addr *addr = &adapter->dev_data->mac_addrs[i];
-		struct virtchnl_ether_addr *vc_addr = &list->list[list->num_elements];
+	for (uint32_t i = 0; i < nb_addrs; i++) {
+		struct iavf_cmd_info args;
+		uint32_t batch;
+		int err;
 
-		/* ignore empty addresses */
-		if (rte_is_zero_ether_addr(addr))
-			continue;
+		batch = i % RTE_DIM(cmd_buffer.addr);
+
+		if (batch == 0) {
+			memset(&cmd_buffer, 0, sizeof(cmd_buffer));
+			list->vsi_id = vf->vsi_res->vsi_id;
+			list->num_elements = 0;
+		}
+
+		rte_memcpy(list->list[batch].addr, addrs[i].addr_bytes,
+			   sizeof(list->list[batch].addr));
+		list->list[batch].type = VIRTCHNL_ETHER_ADDR_EXTRA;
 		list->num_elements++;
 
-		memcpy(vc_addr->addr, addr->addr_bytes, sizeof(addr->addr_bytes));
-		vc_addr->type = (list->num_elements == 1) ?
-				VIRTCHNL_ETHER_ADDR_PRIMARY :
-				VIRTCHNL_ETHER_ADDR_EXTRA;
+		if (batch != RTE_DIM(cmd_buffer.addr) - 1 && i != nb_addrs - 1)
+			continue;
+
+		memset(&args, 0, sizeof(args));
+		args.ops = add ? VIRTCHNL_OP_ADD_ETH_ADDR : VIRTCHNL_OP_DEL_ETH_ADDR;
+		args.in_args = (uint8_t *)list;
+		args.in_args_size = sizeof(struct virtchnl_ether_addr_list) +
+			sizeof(struct virtchnl_ether_addr) * list->num_elements;
+		args.out_buffer = msg_buf;
+		args.out_size = IAVF_AQ_BUF_SZ;
+		err = iavf_execute_vf_cmd_safe(adapter, &args);
+		if (err != 0) {
+			PMD_DRV_LOG(ERR, "fail to execute command %s for %u macs",
+				add ? "VIRTCHNL_OP_ADD_ETH_ADDR" : "VIRTCHNL_OP_DEL_ETH_ADDR",
+				list->num_elements);
+			return err;
+		}
+
+		PMD_DRV_LOG(DEBUG, "executed command %s for %u macs",
+			add ? "VIRTCHNL_OP_ADD_ETH_ADDR" : "VIRTCHNL_OP_DEL_ETH_ADDR",
+			list->num_elements);
 	}
 
-	/* for some reason PF side checks for buffer being too big, so adjust it down */
-	buf_len = sizeof(struct virtchnl_ether_addr_list) +
-		  sizeof(struct virtchnl_ether_addr) * list->num_elements;
+	return 0;
+}
 
-	list->vsi_id = vf->vsi_res->vsi_id;
-	args.ops = add ? VIRTCHNL_OP_ADD_ETH_ADDR : VIRTCHNL_OP_DEL_ETH_ADDR;
-	args.in_args = (uint8_t *)list;
-	args.in_args_size = buf_len;
-	args.out_buffer = msg_buf;
-	args.out_size = IAVF_AQ_BUF_SZ;
-	err = iavf_execute_vf_cmd_safe(adapter, &args);
-	if (err)
-		PMD_DRV_LOG(ERR, "fail to execute command %s",
-				add ? "OP_ADD_ETHER_ADDRESS" : "OP_DEL_ETHER_ADDRESS");
+void
+iavf_add_del_all_mac_addr(struct iavf_adapter *adapter, bool add)
+{
+	int start = -1;
+	int i;
+
+	/* Handle primary address (index 0) separately */
+	if (!rte_is_zero_ether_addr(&adapter->dev_data->mac_addrs[0]))
+		iavf_add_del_eth_addr(adapter, &adapter->dev_data->mac_addrs[0], add,
+			VIRTCHNL_ETHER_ADDR_PRIMARY);
+
+	/* Process secondary addresses in contiguous blocks */
+	for (i = 1; i < IAVF_UC_MACADDR_MAX; i++) {
+		struct rte_ether_addr *addr = &adapter->dev_data->mac_addrs[i];
+
+		if (!rte_is_zero_ether_addr(addr)) {
+			if (start == -1)
+				start = i;
+			continue;
+		}
+
+		if (start != -1) {
+			iavf_add_del_uc_addr_bulk(adapter, &adapter->dev_data->mac_addrs[start],
+				i - start, add);
+			start = -1;
+		}
+	}
+
+	if (start != -1) {
+		iavf_add_del_uc_addr_bulk(adapter, &adapter->dev_data->mac_addrs[start],
+			i - start, add);
+	}
 }
 
 int
@@ -2281,7 +2330,7 @@ iavf_add_del_mc_addr_list(struct iavf_adapter *adapter,
 	struct iavf_info *vf = IAVF_DEV_PRIVATE_TO_VF(adapter);
 	uint8_t msg_buf[IAVF_AQ_BUF_SZ] = {0};
 	uint8_t cmd_buffer[sizeof(struct virtchnl_ether_addr_list) +
-		(IAVF_NUM_MACADDR_MAX * sizeof(struct virtchnl_ether_addr))];
+		(IAVF_MC_MACADDR_MAX * sizeof(struct virtchnl_ether_addr))];
 	struct virtchnl_ether_addr_list *list;
 	struct iavf_cmd_info args;
 	uint32_t i;
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 03/10] ethdev: hide VMDq internal sizes
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev; +Cc: rjarry, cfontain, Andrew Rybchenko, Thomas Monjalon
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

Hide RTE_ETH_NUM_RECEIVE_MAC_ADDR and RTE_ETH_VMDQ_NUM_UC_HASH_ARRAY
in the driver API as those (ambiguous) macros are only a driver concern.

In practice, this is only used by the bnxt and ixgbe (+ clones) drivers.

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
---
Changes since v2:
- added an entry in release notes,

---
 doc/guides/rel_notes/release_26_07.rst | 3 +++
 lib/ethdev/ethdev_driver.h             | 8 +++++++-
 lib/ethdev/rte_ethdev.h                | 6 ------
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 8e4e02e587..6b6fbe0b1a 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -321,6 +321,9 @@ API Changes
     ``RTE_ETH_MQ_TX_VMDQ_ONLY``).
   * A check was added in ``rte_eth_dev_mac_addr_add`` to validate that the ``pool`` parameter is 0
     when VMDq is not configured.
+  * The ``RTE_ETH_NUM_RECEIVE_MAC_ADDR`` and ``RTE_ETH_VMDQ_NUM_UC_HASH_ARRAY`` macros are VMDq
+    related and are sizes of internal arrays in ethdev that only drivers need to care about.
+    Those macros are moved to the driver only ethdev API.
 
 * **mlx5: promoted driver event and steering management APIs from experimental to stable.**
 
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0f336f9567..294f68504b 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -119,6 +119,12 @@ struct __rte_cache_aligned rte_eth_dev {
 struct rte_eth_dev_sriov;
 struct rte_eth_dev_owner;
 
+/* Definitions used for receive MAC address */
+#define RTE_ETH_NUM_RECEIVE_MAC_ADDR   128 /**< Maximum nb. of receive mac addr. */
+
+/* Definitions used for unicast hash */
+#define RTE_ETH_VMDQ_NUM_UC_HASH_ARRAY 128 /**< Maximum nb. of UC hash array. */
+
 /**
  * @internal
  * The data part, with no function pointers, associated with each Ethernet
@@ -153,7 +159,7 @@ struct __rte_cache_aligned rte_eth_dev_data {
 	 * The first entry (index zero) is the default address.
 	 */
 	struct rte_ether_addr *mac_addrs;
-	/** Bitmap associating MAC addresses to pools */
+	/** Bitmap associating MAC addresses to VMDq pools */
 	uint64_t mac_pool_sel[RTE_ETH_NUM_RECEIVE_MAC_ADDR];
 	/**
 	 * Device Ethernet MAC addresses of hash filtering.
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index e2a5ba1549..bee47549cb 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -903,12 +903,6 @@ rte_eth_rss_hf_refine(uint64_t rss_hf)
 #define RTE_ETH_VLAN_ID_MAX          0x0FFF /**< VLAN ID is in lower 12 bits*/
 /**@}*/
 
-/* Definitions used for receive MAC address */
-#define RTE_ETH_NUM_RECEIVE_MAC_ADDR   128 /**< Maximum nb. of receive mac addr. */
-
-/* Definitions used for unicast hash */
-#define RTE_ETH_VMDQ_NUM_UC_HASH_ARRAY 128 /**< Maximum nb. of UC hash array. */
-
 /**@{@name VMDq Rx mode
  * @see rte_eth_vmdq_rx_conf.rx_mode
  */
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 02/10] ethdev: skip VMDq pools unless configured
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev
  Cc: rjarry, cfontain, Andrew Rybchenko, Nithin Dabilpuram,
	Kiran Kumar K, Sunil Kumar Kori, Satha Rao, Harman Kalra,
	Thomas Monjalon
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

The mac_addr_add API describes that only the 0 pool should be passed
unless VMDq has been enabled, though there was no validation so far.
Add such a check, then cleanup the MAC related operations (adding,
removing, restoring).

As a side effect, the net/cnxk does not need to manually reset the
mac_pool_sel[] array.

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
---
Changes since v3:
- updated doxygen comments,

Changes since v2:
- added an entry in release notes,
- fixed duplicate mac address handling for !vmdq,
- rewrote update of eth_dev_mac_restore to isolate the !vmdq case,

---
 doc/guides/rel_notes/release_26_07.rst |  2 +
 drivers/net/cnxk/cnxk_ethdev_ops.c     |  1 -
 lib/ethdev/rte_ethdev.c                | 52 ++++++++++++++++++--------
 lib/ethdev/rte_ethdev.h                |  2 +-
 4 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 5e9178d36b..8e4e02e587 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -319,6 +319,8 @@ API Changes
   * At port configuration time, the number of VMDq pools advertised by a driver is now used to
     validate VMDq related Rx and Tx modes (``RTE_ETH_MQ_RX_VMDQ_FLAG``, ``RTE_ETH_MQ_TX_VMDQ_DCB``,
     ``RTE_ETH_MQ_TX_VMDQ_ONLY``).
+  * A check was added in ``rte_eth_dev_mac_addr_add`` to validate that the ``pool`` parameter is 0
+    when VMDq is not configured.
 
 * **mlx5: promoted driver event and steering management APIs from experimental to stable.**
 
diff --git a/drivers/net/cnxk/cnxk_ethdev_ops.c b/drivers/net/cnxk/cnxk_ethdev_ops.c
index 0ea3d7e89f..c002a93fe1 100644
--- a/drivers/net/cnxk/cnxk_ethdev_ops.c
+++ b/drivers/net/cnxk/cnxk_ethdev_ops.c
@@ -1240,7 +1240,6 @@ cnxk_nix_mc_addr_list_configure(struct rte_eth_dev *eth_dev, struct rte_ether_ad
 		/* Update address in NIC data structure */
 		rte_ether_addr_copy(&mc_addr_set[i], &data->mac_addrs[j]);
 		rte_ether_addr_copy(&mc_addr_set[i], &dev->dmac_addrs[j]);
-		data->mac_pool_sel[j] = RTE_BIT64(0);
 	}
 
 	roc_nix_npc_promisc_ena_dis(nix, true);
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9e305d98c1..f20cc514a3 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -1677,7 +1677,7 @@ eth_dev_mac_restore(struct rte_eth_dev *dev,
 {
 	struct rte_ether_addr *addr;
 	uint16_t i;
-	uint32_t pool = 0;
+	uint32_t pool;
 	uint64_t pool_mask;
 
 	/* replay MAC address configuration including default MAC */
@@ -1685,9 +1685,11 @@ eth_dev_mac_restore(struct rte_eth_dev *dev,
 	if (dev->dev_ops->mac_addr_set != NULL)
 		dev->dev_ops->mac_addr_set(dev, addr);
 	else if (dev->dev_ops->mac_addr_add != NULL)
-		dev->dev_ops->mac_addr_add(dev, addr, 0, pool);
+		dev->dev_ops->mac_addr_add(dev, addr, 0, 0);
 
 	if (dev->dev_ops->mac_addr_add != NULL) {
+		bool vmdq = (dev->data->dev_conf.rxmode.mq_mode & RTE_ETH_MQ_RX_VMDQ_FLAG) != 0;
+
 		for (i = 1; i < dev_info->max_mac_addrs; i++) {
 			addr = &dev->data->mac_addrs[i];
 
@@ -1695,15 +1697,19 @@ eth_dev_mac_restore(struct rte_eth_dev *dev,
 			if (rte_is_zero_ether_addr(addr))
 				continue;
 
-			pool = 0;
-			pool_mask = dev->data->mac_pool_sel[i];
-
-			do {
-				if (pool_mask & UINT64_C(1))
-					dev->dev_ops->mac_addr_add(dev, addr, i, pool);
-				pool_mask >>= 1;
-				pool++;
-			} while (pool_mask);
+			if (!vmdq) {
+				dev->dev_ops->mac_addr_add(dev, addr, i, 0);
+			} else {
+				pool = 0;
+				pool_mask = dev->data->mac_pool_sel[i];
+
+				do {
+					if (pool_mask & UINT64_C(1))
+						dev->dev_ops->mac_addr_add(dev, addr, i, pool);
+					pool_mask >>= 1;
+					pool++;
+				} while (pool_mask);
+			}
 		}
 	}
 }
@@ -5414,8 +5420,9 @@ rte_eth_dev_mac_addr_add(uint16_t port_id, struct rte_ether_addr *addr,
 			uint32_t pool)
 {
 	struct rte_eth_dev *dev;
-	int index;
 	uint64_t pool_mask;
+	bool vmdq;
+	int index;
 	int ret;
 
 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
@@ -5440,6 +5447,12 @@ rte_eth_dev_mac_addr_add(uint16_t port_id, struct rte_ether_addr *addr,
 		RTE_ETHDEV_LOG_LINE(ERR, "Pool ID must be 0-%d", RTE_ETH_64_POOLS - 1);
 		return -EINVAL;
 	}
+	vmdq = (dev->data->dev_conf.rxmode.mq_mode & RTE_ETH_MQ_RX_VMDQ_FLAG) != 0;
+	if (!vmdq && pool != 0) {
+		RTE_ETHDEV_LOG_LINE(ERR, "Port %u: VMDq is not configured (pool %d)",
+			port_id, pool);
+		return -EINVAL;
+	}
 
 	index = eth_dev_get_mac_addr_index(port_id, addr);
 	if (index < 0) {
@@ -5450,6 +5463,9 @@ rte_eth_dev_mac_addr_add(uint16_t port_id, struct rte_ether_addr *addr,
 			return -ENOSPC;
 		}
 	} else {
+		if (!vmdq)
+			return 0;
+
 		pool_mask = dev->data->mac_pool_sel[index];
 
 		/* Check if both MAC address and pool is already there, and do nothing */
@@ -5464,8 +5480,10 @@ rte_eth_dev_mac_addr_add(uint16_t port_id, struct rte_ether_addr *addr,
 		/* Update address in NIC data structure */
 		rte_ether_addr_copy(addr, &dev->data->mac_addrs[index]);
 
-		/* Update pool bitmap in NIC data structure */
-		dev->data->mac_pool_sel[index] |= RTE_BIT64(pool);
+		if (vmdq) {
+			/* Update pool bitmap in NIC data structure */
+			dev->data->mac_pool_sel[index] |= RTE_BIT64(pool);
+		}
 	}
 
 	ret = eth_err(port_id, ret);
@@ -5510,8 +5528,10 @@ rte_eth_dev_mac_addr_remove(uint16_t port_id, struct rte_ether_addr *addr)
 	/* Update address in NIC data structure */
 	rte_ether_addr_copy(&null_mac_addr, &dev->data->mac_addrs[index]);
 
-	/* reset pool bitmap */
-	dev->data->mac_pool_sel[index] = 0;
+	if ((dev->data->dev_conf.rxmode.mq_mode & RTE_ETH_MQ_RX_VMDQ_FLAG) != 0) {
+		/* reset pool bitmap */
+		dev->data->mac_pool_sel[index] = 0;
+	}
 
 	rte_ethdev_trace_mac_addr_remove(port_id, addr);
 
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index ee400b386f..e2a5ba1549 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -4636,7 +4636,7 @@ int rte_eth_dev_priority_flow_ctrl_set(uint16_t port_id,
  *   - (-ENODEV) if *port* is invalid.
  *   - (-EIO) if device is removed.
  *   - (-ENOSPC) if no more MAC addresses can be added.
- *   - (-EINVAL) if MAC address is invalid.
+ *   - (-EINVAL) if MAC address is invalid or a non 0 pool was passed but VMDq is not enabled.
  */
 int rte_eth_dev_mac_addr_add(uint16_t port_id, struct rte_ether_addr *mac_addr,
 				uint32_t pool);
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 01/10] ethdev: check VMDq availability
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev; +Cc: rjarry, cfontain, Andrew Rybchenko, Thomas Monjalon
In-Reply-To: <20260709160247.1798575-1-david.marchand@redhat.com>

Refuse VMDq related Rx/Tx modes when the driver do not announce VMDq
pools availability.
This will used later as a gate to ignore/reject VMDq related matters.

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
---
Changes since v2:
- added an entry in release notes,
- changed approach: relied on pre-existing dev_info->max_vmdq_pools
  rather than introduce a new device capability,

Changes since v1:
- dropped incorrect VMDq feature announce for bnxt representors, em,
  i40e representors, ipn3ke representors,

---
 doc/guides/rel_notes/release_26_07.rst |  6 ++++++
 lib/ethdev/rte_ethdev.c                | 16 ++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 6352ef27ab..5e9178d36b 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -314,6 +314,12 @@ API Changes
   - ``rte_flow_dynf_metadata_get``
   - ``rte_flow_dynf_metadata_set``
 
+* **ethdev: updated VMDq related API.**
+
+  * At port configuration time, the number of VMDq pools advertised by a driver is now used to
+    validate VMDq related Rx and Tx modes (``RTE_ETH_MQ_RX_VMDQ_FLAG``, ``RTE_ETH_MQ_TX_VMDQ_DCB``,
+    ``RTE_ETH_MQ_TX_VMDQ_ONLY``).
+
 * **mlx5: promoted driver event and steering management APIs from experimental to stable.**
 
   The following mlx5 functions are no longer marked experimental:
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9efeaf77cb..9e305d98c1 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -1581,6 +1581,22 @@ rte_eth_dev_configure(uint16_t port_id, uint16_t nb_rx_q, uint16_t nb_tx_q,
 		goto rollback;
 	}
 
+	if (dev_info.max_vmdq_pools == 0) {
+		if ((dev_conf->rxmode.mq_mode & RTE_ETH_MQ_RX_VMDQ_FLAG) != 0) {
+			RTE_ETHDEV_LOG_LINE(ERR, "Ethdev port_id=%u does not support VMDq rx mode",
+				port_id);
+			ret = -EINVAL;
+			goto rollback;
+		}
+		if (dev_conf->txmode.mq_mode == RTE_ETH_MQ_TX_VMDQ_DCB ||
+				dev_conf->txmode.mq_mode == RTE_ETH_MQ_TX_VMDQ_ONLY) {
+			RTE_ETHDEV_LOG_LINE(ERR, "Ethdev port_id=%u does not support VMDq tx mode",
+				port_id);
+			ret = -EINVAL;
+			goto rollback;
+		}
+	}
+
 	/*
 	 * Setup new number of Rx/Tx queues and reconfigure device.
 	 */
-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 00/10] Remove limitations coming from legacy VMDq
From: David Marchand @ 2026-07-09 16:02 UTC (permalink / raw)
  To: dev; +Cc: rjarry, cfontain
In-Reply-To: <20260403091836.1073484-1-david.marchand@redhat.com>

Since the commit 88ac4396ad29 ("ethdev: add VMDq support"),
VMDq has been imposing a maximum number of mac addresses in the
mac_addr_add/del API.

Nowadays, new Intel drivers do not support the feature and few other
drivers implement this feature.

This series enforces that the driver announces VMDq pools before
using VMDq related features, then remove the limit of number of
mac addresses for others.

Next step could be to remove the VMDq pool notion from the generic API.
However I have some concern about this, as changing the quite stable
mac_addr_add/del API now seems a lot of noise for not much benefit.


-- 
David Marchand

Changes since v3:
- rebased (this series is too late for 26.07),
- update some doxygen comments,
- added mlx5 changes,

Changes since v2:
- changed approach: did not introduce a new device capability,
  relied on already existing dev_info->max_vmdq_pools,
- fixed duplicate mac addition without VMDq,
- updated documentation,

Changes since v1:
- dropped incorrect VMDq feature announce for bnxt representors, em,
  i40e representors, ipn3ke representors,
- fixed buffer overflow on mailbox messages during port restart/VF reset,
- fixed duplicate MAC address installation on port start/restart,

David Marchand (10):
  ethdev: check VMDq availability
  ethdev: skip VMDq pools unless configured
  ethdev: hide VMDq internal sizes
  net/iavf: accept up to 32k unicast MAC addresses
  net/iavf: fix duplicate MAC addresses install
  net/mlx5: remove MAC addresses flush helper on Linux
  net/mlx5: remove redundant MAC address index checks
  net/mlx5: pass maximum number of unicast MAC to common code
  net/mlx5: use bitset for tracking MAC addresses
  net/mlx5: accept more unicast MAC addresses

 doc/guides/rel_notes/release_26_07.rst |  16 ++++
 drivers/common/mlx5/linux/mlx5_nl.c    |  90 +++++---------------
 drivers/common/mlx5/linux/mlx5_nl.h    |  11 +--
 drivers/common/mlx5/mlx5_common.h      |  24 ------
 drivers/common/mlx5/mlx5_devx_cmds.c   |   4 +
 drivers/common/mlx5/mlx5_devx_cmds.h   |   2 +
 drivers/net/cnxk/cnxk_ethdev_ops.c     |   1 -
 drivers/net/intel/iavf/iavf.h          |   5 +-
 drivers/net/intel/iavf/iavf_ethdev.c   |  43 ++++++----
 drivers/net/intel/iavf/iavf_vchnl.c    | 113 ++++++++++++++++++-------
 drivers/net/mlx5/linux/mlx5_os.c       |  61 +++++++++----
 drivers/net/mlx5/mlx5.c                |   9 +-
 drivers/net/mlx5/mlx5.h                |  17 +++-
 drivers/net/mlx5/mlx5_ethdev.c         |   2 +-
 drivers/net/mlx5/mlx5_mac.c            |  22 +++--
 drivers/net/mlx5/mlx5_trigger.c        |  12 +--
 drivers/net/mlx5/windows/mlx5_os.c     |  48 ++++++++---
 lib/ethdev/ethdev_driver.h             |   8 +-
 lib/ethdev/rte_ethdev.c                |  68 +++++++++++----
 lib/ethdev/rte_ethdev.h                |   8 +-
 20 files changed, 340 insertions(+), 224 deletions(-)

-- 
2.54.0


^ permalink raw reply

* Re: [PATCH v4 2/2] dts: add build arguments to test run configuration
From: Koushik Bhargav Nimoji @ 2026-07-09 15:02 UTC (permalink / raw)
  To: Patrick Robb; +Cc: luca.vizzarro, dev, abailey, ahassick, lylavoie
In-Reply-To: <CAK6DuxuiK4h8HKXov8vMam2k3-HwoyjWyWAxC_7qMTFXbDmdxA@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 856 bytes --]

I agree there are some valuable suggestions in the AI code review, I'll
look over those and get a patch out by the end of the day. Thanks Patrick.

On Wed, Jul 8, 2026 at 6:14 PM Patrick Robb <patrickrobb1997@gmail.com>
wrote:

> I think the polish on this patch is okay but also there are some moderate
> importance cleanups that are recommended by the AI code review:
> https://mails.dpdk.org/archives/test-report/2026-June/1011807.html
>
> If you can't get time for this before RC3 I think we can merge this
> series, but some of the cleanups (i.e. safely handling yaml string vs list
> of strings format in the test_run.yaml) are valuable and if you can add
> that either right now for RC3 or early in the 25.11 release window that is
> ideal. Let me know what you think.
>
> Reviewed-by: Patrick Robb <patrickrobb1997@gmail.com>
>

[-- Attachment #2: Type: text/html, Size: 1370 bytes --]

^ permalink raw reply

* RE: [PATCH v8] mempool: improve cache behaviour and performance
From: Wisam Jaddo @ 2026-07-09 13:52 UTC (permalink / raw)
  To: Morten Brørup, NBU-Contact-Thomas Monjalon (EXTERNAL),
	Andrew Rybchenko
  Cc: dev@dpdk.org, Raslan Darawsheh, Maayan Kashani
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F65964@smartserver.smartshare.dk>

Hi,

Thank you for your guidance, the rates are back to normal. After the new configuration.

BRs,
Wisam Jaddo


> -----Original Message-----
> From: Morten Brørup <mb@smartsharesystems.com>
> Sent: Wednesday, July 8, 2026 7:25 PM
> To: Wisam Jaddo <wisamm@nvidia.com>; NBU-Contact-Thomas Monjalon
> (EXTERNAL) <thomas@monjalon.net>; Andrew Rybchenko
> <andrew.rybchenko@oktetlabs.ru>
> Cc: dev@dpdk.org; Raslan Darawsheh <rasland@nvidia.com>; Maayan
> Kashani <mkashani@nvidia.com>
> Subject: RE: [PATCH v8] mempool: improve cache behaviour and performance
> 
> > From: Wisam Jaddo [mailto:wisamm@nvidia.com]
> > Sent: Wednesday, 8 July 2026 17.05
> >
> > Hi,
> >
> > Tested on ARM BF3:
> > command:
> > . /dpdk-testpmd -n 4  -a 0000:03:00.0,dv_flow_en=2  -a
> > 0000:03:00.1,dv_flow_en=2  -a auxiliary:  -a 00:00.0 -c 0x30  -- --
> > mbcache=512 -i  --nb-cores=1  --burst=64 --txd=256 --rxd=256
> > --record- burst-stats --record-core-cycles --auto-start
> 
> Thank you for the detailed response.
> 
> As noted in the release notes with the patch, the effective mempool cache size
> was changed to no longer be 1.5 times the configured cache size. (This change
> fixed bug 1027 [BZ1027].) I.e. with your testpmd parameters the effective
> mbuf mempool cache size was reduced from 768 (1.5 * 512) to 512 mbufs.
> In order to test with the same effective mbuf mempool cache size, you need to
> pass --mbcache=768.
> And since 768 exceeds RTE_MEMPOOL_CACHE_MAX_SIZE (default 512) in
> rte_config.h, you also need to rebuild DPDK with that set to 1024 instead of
> 512.
> 
> [BZ1027] https://bugs.dpdk.org/show_bug.cgi?id=1027
> 
> Please let us know your updated test results, if you decide to retest with --
> mbcache=768 and DPDK rebuilt with RTE_MEMPOOL_CACHE_MAX_SIZE
> 1024.
> 
> >
> > No offloads, plain testpmd with auto-start.
> > On the other end I have ixia generating the ipv4_udp traffic, with
> > dest udp range from 65024 to 65280 "To send different packets with msg
> > size 64Bytes.
> >
> > We send 100% on TX side, and we read what we reactive back on RX side
> > in ixia "using two ports agg."
> > The results that we start to see after this commit is a degradation of
> > 1.8% - 2%
> >
> > BRs,
> > Wisam Jaddo
> >
> > > -----Original Message-----
> > > From: Morten Brørup <mb@smartsharesystems.com>
> > > Sent: Wednesday, July 8, 2026 5:09 PM
> > > To: Wisam Jaddo <wisamm@nvidia.com>; NBU-Contact-Thomas Monjalon
> > > (EXTERNAL) <thomas@monjalon.net>
> > > Cc: dev@dpdk.org; Andrew Rybchenko
> <andrew.rybchenko@oktetlabs.ru>;
> > > Raslan Darawsheh <rasland@nvidia.com>; Maayan Kashani
> > > <mkashani@nvidia.com>
> > > Subject: RE: [PATCH v8] mempool: improve cache behaviour and
> > performance
> > >
> > > > From: Wisam Jaddo [mailto:wisamm@nvidia.com]
> > > > Sent: Wednesday, 8 July 2026 14.28
> > > >
> > > > Hi,
> > > >
> > > > Our internal performance testing are showing degradation starting
> > from
> > > > this commit.
> > > > The degradation is about 1.8% - 2% on small msg szies.
> > >
> > > What do you mean "small message sizes"?
> > >
> > > Also, please share more details about the test case:
> > > Access pattern, e.g. pipelined across multiple threads or run-to-
> > completion? If
> > > possible, link to test case source code?
> > > Get and put burst sizes?
> > > Mempool cache size?
> > > Mempool driver?
> > >
> > > PS: I assume it is about mbuf mempool. Or is it some other object
> > type in the
> > > mempool?
> > >
> > > >
> > > > BRs,
> > > > Wisam Jaddo
> > > >
> > > > > -----Original Message-----
> > > > > From: Thomas Monjalon <thomas@monjalon.net>
> > > > > Sent: Wednesday, June 10, 2026 2:07 PM
> > > > > To: Morten Brørup <mb@smartsharesystems.com>
> > > > > Cc: dev@dpdk.org; Andrew Rybchenko
> > <andrew.rybchenko@oktetlabs.ru>
> > > > > Subject: Re: [PATCH v8] mempool: improve cache behaviour and
> > > > performance
> > > > >
> > > > > 04/06/2026 13:48, Morten Brørup:
> > > > > > This patch refactors the mempool cache to eliminate some
> > > > > > unexpected behaviour and reduce the mempool cache miss rate.
> > > > >
> > > > > Applied, thanks.
> > > > >
> > > > >


^ permalink raw reply

* [PATCH v6] dts: report dut/NIC info during DTS run
From: Koushik Bhargav Nimoji @ 2026-07-09 13:48 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: dev, abailey, ahassick, lylavoie, Koushik Bhargav Nimoji
In-Reply-To: <20260602163647.101815-1-knimoji@iol.unh.edu>

This patch gathers NIC info during a DTS run and writes it to an output
json file. This allows the json file to be used when reporting results
on the DTS results dashboard.

Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
---
v2:
    *Resolved merge conflicts
v3:
    *Fixed an issue with retrieving
     the NIC's hardware version   
v4:
    *Moved nic info gathering step before the nics get
     binded to their respective drivers
    *Condensed some areas of code in order to make them
     more readable
    *Removed redundant None checks and added some where
     required
    *Fixed LshwOutput class to better reflect the lshw
     command output
v5:
    *Changed variable names for code readability
v6:
    *Fixed command result validity checking logic
---
 dts/framework/test_run.py                    | 10 +++
 dts/framework/testbed_model/linux_session.py | 68 ++++++++++++++++++++
 dts/framework/testbed_model/os_session.py    | 11 ++++
 3 files changed, 89 insertions(+)

diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..fea1b52e44 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -98,6 +98,7 @@
         "InternalError" -> "exit":ew
 """
 
+import json
 import random
 from collections import deque
 from collections.abc import Iterable
@@ -347,6 +348,14 @@ def next(self) -> State | None:
         test_run.ctx.dpdk.setup()
         test_run.ctx.topology.setup()
 
+        testrun_nic_info: list[dict[str, str]] = (
+            self.test_run.ctx.sut_node.main_session.get_nic_info()
+        )
+        with open(f"{SETTINGS.output_dir}/dut_info.json", "w") as file:
+            json.dump(testrun_nic_info, file, indent=3)
+
+        self.logger.info(f"DUT NIC info written to: {SETTINGS.output_dir}/dut_info.json")
+
         if test_run.config.use_virtual_functions:
             test_run.ctx.topology.instantiate_vf_ports()
         if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
@@ -370,6 +379,7 @@ def next(self) -> State | None:
         test_run.supported_capabilities = get_supported_capabilities(
             test_run.ctx.sut_node, test_run.ctx.topology, test_run.required_capabilities
         )
+
         return TestRunExecution(test_run, self.result)
 
     def on_error(self, ex: BaseException) -> State | None:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 3a6e97974b..c118f803b6 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -38,6 +38,8 @@ class LshwConfigurationOutput(TypedDict):
     driver: str
     #:
     link: str
+    #:
+    firmware: str
 
 
 class LshwOutput(TypedDict):
@@ -61,6 +63,12 @@ class LshwOutput(TypedDict):
             ...
     """
 
+    #:
+    vendor: NotRequired[str]
+    #:
+    product: NotRequired[str]
+    #:
+    version: NotRequired[str]
     #:
     businfo: str
     #:
@@ -197,6 +205,66 @@ def unbind_ports(self, ports: list[Port]):
         if self._lshw_net_info:
             del self._lshw_net_info
 
+    def get_nic_info(self) -> list[dict[str, str]]:
+        """Overrides :meth`~.os_session.OSSession.get_nic_info`.
+
+        Raises:
+            ConfigurationError: If the NIC info could not be found.
+        """
+        port_to_data = {
+            port.get("businfo"): port for port in self._lshw_net_info if port.get("businfo")
+        }
+
+        all_nic_info: list[dict[str, str]] = []
+        for port in self._config.ports:
+            pci_addr = port.pci
+
+            lshw_result = self.send_command(
+                f"sudo lshw -c network -businfo | grep '{pci_addr}' | cut -d'@' -f1"
+            )
+            if lshw_result.return_code != 0 or lshw_result.stdout == "":
+                raise ConfigurationError(f"Unable to get bus type for port {pci_addr}.")
+            bus_type = lshw_result.stdout
+
+            bus_info = f"{bus_type}@{pci_addr}"
+            nic_port: LshwOutput | None = port_to_data[bus_info]
+            if nic_port is None:
+                raise ConfigurationError(f"Port {pci_addr} could not be found on the node.")
+
+            config: LshwConfigurationOutput | None = nic_port["configuration"]
+            if config is None:
+                raise ConfigurationError(
+                    f"Configuration info for port {pci_addr} could not be found on the node."
+                )
+
+            if "logicalname" not in nic_port:
+                raise ConfigurationError(
+                    f"Logical name for port {pci_addr} could not be found on the node."
+                )
+
+            ethtool_result = self.send_command(
+                f"ethtool {nic_port['logicalname']} | grep 'Speed:' | awk '{{print $2}}'"
+            )
+            if ethtool_result.return_code == 0 and ethtool_result.stdout:
+                nic_speed = ethtool_result.stdout
+            else:
+                self._logger.error(f"Unable to get speed for NIC: {pci_addr}")
+                nic_speed = None
+
+            dut_json = {
+                "make": nic_port["vendor"] if "vendor" in nic_port else "Unknown",
+                "model": nic_port["product"] if "product" in nic_port else "Unknown",
+                "hardware version": nic_port["version"] if "version" in nic_port else "Unknown",
+                "firmware version": config["firmware"] if "firmware" in config else "Unknown",
+                "deviceBusType": bus_type,
+                "deviceId": nic_port["serial"] if "serial" in nic_port else "Unknown",
+                "pmd": config["driver"] if "driver" in config else "Unknown",
+                "speed": nic_speed or "Unknown",
+            }
+            all_nic_info.append(dut_json)
+
+        return all_nic_info
+
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
 
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index f2dc9b20a9..f88427a53d 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -581,6 +581,17 @@ def unbind_ports(self, ports: list[Port]) -> None:
             ports: The list of ports to unbind.
         """
 
+    @abstractmethod
+    def get_nic_info(self) -> list[dict[str, str]]:
+        """Get NIC information.
+
+        Returns:
+            NIC info as a list of dictionaries.
+
+        Raises:
+            ConfigurationError: If the NIC info could not be found.
+        """
+
     @abstractmethod
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Bind `ports` to the given `driver_name`.
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v5] dts: report dut/NIC info during DTS run
From: Koushik Bhargav Nimoji @ 2026-07-09 13:47 UTC (permalink / raw)
  To: Patrick Robb; +Cc: luca.vizzarro, dev, abailey, ahassick, lylavoie
In-Reply-To: <CAK6DuxthwOFOdvLgsEoxu+ShXaiKZWc9+RuVXG9czsazuipzTw@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 778 bytes --]

On Wed, Jul 8, 2026 at 9:05 PM Patrick Robb <patrickrobb1997@gmail.com>
wrote:

> Actually, please see the AI code review of this patch. Specifically, this
> comment looks correct to me. What are your thoughts?
>
> **File:** `dts/framework/testbed_model/linux_session.py`, line 228
>
> The code checks `lshw_result.return_code != 0 and lshw_result.stdout ==
> ""` but the logic is incorrect. If the command fails, you should raise an
> error. The `and` should likely be `or`:
>

This is definitely a valid issue, I will send out a patch fixing this
shortly.


>
> Please read through the other suggestions too. There is some fluff but
> also some good advice.
>

For sure, I will include changes from those suggestions in the subsequent
patch as well.

[-- Attachment #2: Type: text/html, Size: 1352 bytes --]

^ permalink raw reply

* RE: [PATCH v2 5/6] net/iavf: fix leak of flex metadata extraction field
From: Loftus, Ciara @ 2026-07-09 12:36 UTC (permalink / raw)
  To: Richardson, Bruce, dev@dpdk.org
  Cc: Richardson, Bruce, stable@dpdk.org, Medvedkin, Vladimir,
	Haiyue Wang, Jeff Guo
In-Reply-To: <20260703131921.4102325-6-bruce.richardson@intel.com>

> Subject: [PATCH v2 5/6] net/iavf: fix leak of flex metadata extraction field
> 
> Function iavf_init_proto_xtr() allocates vf->proto_xtr as part of the
> device init sequence, but no shutdown function frees this memory again.
> Add an appropriate free call to fix this memory leak.
> 
> Fixes: 12b435bf8f2f ("net/iavf: support flex desc metadata extraction")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/intel/iavf/iavf_ethdev.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/net/intel/iavf/iavf_ethdev.c
> b/drivers/net/intel/iavf/iavf_ethdev.c
> index 1cd8c88384..7f5b103326 100644
> --- a/drivers/net/intel/iavf/iavf_ethdev.c
> +++ b/drivers/net/intel/iavf/iavf_ethdev.c
> @@ -2757,6 +2757,8 @@ iavf_uninit_vf(struct rte_eth_dev *dev)
>  	vf->qos_cap = NULL;
>  	free(vf->qtc_map);
>  	vf->qtc_map = NULL;
> +	rte_free(vf->proto_xtr);
> +	vf->proto_xtr = NULL;

This function iavf_uninit_vf is only called when an error is encountered in
iavf_dev_init and we goto the init_vf_err label. We need to free the memory
in the successful case as well.

> 
>  	rte_free(vf->rss_lut);
>  	vf->rss_lut = NULL;
> --
> 2.53.0


^ permalink raw reply

* RE: [PATCH v2 3/6] net/iavf: fix memory leak on error when adding flow parser
From: Loftus, Ciara @ 2026-07-09 12:35 UTC (permalink / raw)
  To: Richardson, Bruce, dev@dpdk.org
  Cc: Richardson, Bruce, stable@dpdk.org, Medvedkin, Vladimir,
	Yang, Qiming, Zhang, Qi Z
In-Reply-To: <20260703131921.4102325-4-bruce.richardson@intel.com>

> Subject: [PATCH v2 3/6] net/iavf: fix memory leak on error when adding flow
> parser
> 
> In iavf_register_flow_parser, the final "else" branch is an error leg

This should read "iavf_register_parser" instead of
"iavf_register_flow_parser".

Other than that

Acked-by: Ciara Loftus <ciara.loftus@intel.com>

> which returns immediately, without adding the newly allocated
> parser_node to a TAILQ. Add a free call for that parser_node in the
> error case to avoid leaking memory.
> 
> Fixes: ff2d0c345c3b ("net/iavf: support generic flow API")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/intel/iavf/iavf_generic_flow.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/net/intel/iavf/iavf_generic_flow.c
> b/drivers/net/intel/iavf/iavf_generic_flow.c
> index 84bb161bd1..92ca20031c 100644
> --- a/drivers/net/intel/iavf/iavf_generic_flow.c
> +++ b/drivers/net/intel/iavf/iavf_generic_flow.c
> @@ -1911,6 +1911,7 @@ iavf_register_parser(struct iavf_flow_parser
> *parser,
>  		list = &vf->dist_parser_list;
>  		TAILQ_INSERT_HEAD(list, parser_node, node);
>  	} else {
> +		rte_free(parser_node);
>  		return -EINVAL;
>  	}
> 
> --
> 2.53.0


^ permalink raw reply

* RE: [PATCH] net/enic: fix potential null dereference in flow mask check
From: Hyong Youb Kim (hyonkim) @ 2026-07-09 12:07 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Alexey Simakov, John Daley (johndale), Nelson Escobar (neescoba),
	dev@dpdk.org, stable@dpdk.org
In-Reply-To: <Vh3QFBc2TJCkSwMa9Ds4OA@monjalon.net>



> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Thursday, July 9, 2026 9:04 PM
> To: Hyong Youb Kim (hyonkim) <hyonkim@cisco.com>
> Cc: Alexey Simakov <bigalex934@gmail.com>; John Daley (johndale)
> <johndale@cisco.com>; Nelson Escobar (neescoba) <neescoba@cisco.com>;
> dev@dpdk.org; stable@dpdk.org
> Subject: Re: [PATCH] net/enic: fix potential null dereference in flow mask check
> 
> 09/07/2026 11:57, Hyong Youb Kim (hyonkim):
> > From: Alexey Simakov <bigalex934@gmail.com>
> > > --- a/.mailmap
> > > +++ b/.mailmap
> > > @@ -71,6 +71,7 @@ Alexander Skorichenko <askorichenko@netgate.com>
> > >  Alexander Solganik <solganik@gmail.com>
> > >  Alexander V Gutkin <alexander.v.gutkin@intel.com>
> > >  Alexandre Ferrieux <alexandre.ferrieux@orange.com>
> > > +Alexey Simakov <bigalex934@gmail.com>
> > >  Alexey Kardashevskiy <aik@ozlabs.ru>
> > >  Alfredo Cardigliano <cardigliano@ntop.org>
> > >  Ali Alnubani <alialnu@nvidia.com> <alialnu@mellanox.com>
> >
> > Can you remove .mailmap diff?
> 
> Why are you asking that?
> Any new contributor must be in this file.
> If it is not done, I add it manually in the commit.
> 

My bad. I did not know.

Thanks.
-Hyong


^ permalink raw reply

* Re: [PATCH] net/enic: fix potential null dereference in flow mask check
From: Thomas Monjalon @ 2026-07-09 12:03 UTC (permalink / raw)
  To: Hyong Youb Kim (hyonkim)
  Cc: Alexey Simakov, John Daley (johndale), Nelson Escobar (neescoba),
	dev@dpdk.org, stable@dpdk.org
In-Reply-To: <IA3PR11MB8987402CDECFACC6831119E7BFFE2@IA3PR11MB8987.namprd11.prod.outlook.com>

09/07/2026 11:57, Hyong Youb Kim (hyonkim):
> From: Alexey Simakov <bigalex934@gmail.com>
> > --- a/.mailmap
> > +++ b/.mailmap
> > @@ -71,6 +71,7 @@ Alexander Skorichenko <askorichenko@netgate.com>
> >  Alexander Solganik <solganik@gmail.com>
> >  Alexander V Gutkin <alexander.v.gutkin@intel.com>
> >  Alexandre Ferrieux <alexandre.ferrieux@orange.com>
> > +Alexey Simakov <bigalex934@gmail.com>
> >  Alexey Kardashevskiy <aik@ozlabs.ru>
> >  Alfredo Cardigliano <cardigliano@ntop.org>
> >  Ali Alnubani <alialnu@nvidia.com> <alialnu@mellanox.com>
> 
> Can you remove .mailmap diff?

Why are you asking that?
Any new contributor must be in this file.
If it is not done, I add it manually in the commit.



^ permalink raw reply

* [PATCH v2 3/3] net/ice: fix adapter stopped on device start error
From: Ciara Loftus @ 2026-07-09 11:25 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, Ciara Loftus, stable
In-Reply-To: <20260709112556.1765262-1-ciara.loftus@intel.com>

Currently, `adapter_stopped` is cleared during device startup but is not
restored to true if startup fails partway through. Reset `adapter_stopped`
to true on the device startup error path to accurately reflect device state
after a failed start.

Fixes: 437dbd2fd428 ("net/ice: support 1PPS")
Cc: stable@dpdk.org

Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/ice/ice_ethdev.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/intel/ice/ice_ethdev.c b/drivers/net/intel/ice/ice_ethdev.c
index 8db5168b88..76b8ff0a72 100644
--- a/drivers/net/intel/ice/ice_ethdev.c
+++ b/drivers/net/intel/ice/ice_ethdev.c
@@ -4582,6 +4582,8 @@ ice_dev_start(struct rte_eth_dev *dev)
 	for (i = 0; i < nb_txq; i++)
 		ice_tx_queue_stop(dev, i);
 
+	pf->adapter_stopped = true;
+
 	return -EIO;
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 2/3] net/i40e: activate alarm if interrupt delivery unavailable
From: Ciara Loftus @ 2026-07-09 11:25 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, Ciara Loftus, stable
In-Reply-To: <20260709112556.1765262-1-ciara.loftus@intel.com>

If interrupt registration fails eg. on FreeBSD with nic_uio, and Rx
queue interrupts are configured (intr_conf.rxq != 0), the interrupt
handler is never called and ICR0 events, including AdminQ messages
carrying link state notifications, are never processed. In polling
mode (rxq == 0) a periodic alarm already drains these events, but in
the interrupt-enabled configuration no such fallback existed.

This gap has always existed but was partially masked by the blocking link
status poll in the i40e dev_start function, which would delay some time
until the link was up before returning. In this case, the link status was
typically correct after dev_start. However after that delay was removed in
commit 111965395b37 ("net/i40e: fix blocking link wait on device start"),
the link status was often wrong after dev_start, and there was no mechanism
to self-correct.

Rather than restoring the blocking wait removed by that commit which would
re-impose a startup delay on every platform, activate the existing periodic
alarm on platforms where interrupt delivery is unavailable.

Fixes: 111965395b37 ("net/i40e: fix blocking link wait on device start")
Cc: stable@dpdk.org

Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
* Added warning on rte_eal_alarm_set failure
* Only set use_aq_polling if rte_eal_alarm_set succeeds
---
 drivers/net/intel/i40e/i40e_ethdev.c | 11 ++++++++++-
 drivers/net/intel/i40e/i40e_ethdev.h |  2 ++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/net/intel/i40e/i40e_ethdev.c b/drivers/net/intel/i40e/i40e_ethdev.c
index b2694cd33a..b6b2d291ee 100644
--- a/drivers/net/intel/i40e/i40e_ethdev.c
+++ b/drivers/net/intel/i40e/i40e_ethdev.c
@@ -2579,7 +2579,13 @@ i40e_dev_start(struct rte_eth_dev *dev)
 		i40e_dev_link_update(dev, !rte_intr_allow_others(intr_handle));
 		pf->mac_config_on_link_up = !dev->data->dev_link.link_status;
 		/* enable uio intr after callback register */
-		rte_intr_enable(intr_handle);
+		if (rte_intr_enable(intr_handle) != 0) {
+			if (rte_eal_alarm_set(I40E_ALARM_INTERVAL,
+					  i40e_dev_alarm_handler, dev) != 0)
+				PMD_DRV_LOG(WARNING, "Failed to set alarm");
+			else
+				pf->use_aq_polling = true;
+		}
 	}
 
 	i40e_filter_restore(pf);
@@ -2625,6 +2631,9 @@ i40e_dev_stop(struct rte_eth_dev *dev)
 	if (dev->data->dev_conf.intr_conf.rxq == 0) {
 		rte_eal_alarm_cancel(i40e_dev_alarm_handler, dev);
 		rte_intr_enable(intr_handle);
+	} else if (pf->use_aq_polling) {
+		rte_eal_alarm_cancel(i40e_dev_alarm_handler, dev);
+		pf->use_aq_polling = false;
 	}
 
 	/* Disable all queues */
diff --git a/drivers/net/intel/i40e/i40e_ethdev.h b/drivers/net/intel/i40e/i40e_ethdev.h
index 5a009393b0..9d9bde6aeb 100644
--- a/drivers/net/intel/i40e/i40e_ethdev.h
+++ b/drivers/net/intel/i40e/i40e_ethdev.h
@@ -1193,6 +1193,8 @@ struct i40e_pf {
 	bool fw8_3gt;
 	/* MAC config needs re-applying when link first comes up */
 	bool mac_config_on_link_up;
+	/* true when interrupt path unavailable */
+	bool use_aq_polling;
 
 	struct i40e_vf_msg_cfg vf_msg_cfg;
 	uint64_t prev_rx_bytes;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 1/3] net/ice: poll AdminQ if interrupt delivery unavailable
From: Ciara Loftus @ 2026-07-09 11:25 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, Ciara Loftus, stable
In-Reply-To: <20260709112556.1765262-1-ciara.loftus@intel.com>

If interrupt registration fails eg. on FreeBSD with nic_uio, the ice
interrupt handler is never called and AdminQ messages, including
unsolicited link state notifications, are never processed.

This gap has always existed but was partially masked by the blocking link
status poll in the ice dev_start function, which would delay some time
until the link was up before returning. In this case, the link status was
typically correct after dev_start. However after that delay was removed in
commit 2dd7e998550e ("net/ice: revert fix link up when starting device"),
the link status was often wrong after dev_start, and there was no mechanism
to self-correct.

Rather than restoring the blocking wait removed by that commit which
would re-impose a startup delay on every platform, instead activate a
periodic alarm that drains the AdminQ on a 50ms interval, replicating the
role of the interrupt handler on platforms where interrupt delivery is not
supported.

Fixes: 2dd7e998550e ("net/ice: revert fix link up when starting device")
Cc: stable@dpdk.org

Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
* Added warning on rte_eal_alarm_set failure
* Only set use_aq_polling if rte_eal_alarm_set succeeds
* Move alarm cancel in ice_dev_stop to prevent a race
* Remove unnecessary alarm cancel from ice_dev_start error path
---
 drivers/net/intel/ice/ice_ethdev.c | 42 +++++++++++++++++++++++++++++-
 drivers/net/intel/ice/ice_ethdev.h |  1 +
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/net/intel/ice/ice_ethdev.c b/drivers/net/intel/ice/ice_ethdev.c
index 4be5b63c9a..8db5168b88 100644
--- a/drivers/net/intel/ice/ice_ethdev.c
+++ b/drivers/net/intel/ice/ice_ethdev.c
@@ -3,6 +3,7 @@
  */
 
 #include <rte_string_fns.h>
+#include <rte_alarm.h>
 #include <ethdev_pci.h>
 
 #include <ctype.h>
@@ -110,7 +111,11 @@ enum ice_link_state_on_close {
 
 #define ICE_RSS_LUT_GLOBAL_QUEUE_NB	64
 
+/* Polling interval used when the interrupt path is unavailable */
+#define ICE_AQ_ALARM_INTERVAL 50000 /* us */
+
 static int ice_dev_configure(struct rte_eth_dev *dev);
+static void ice_aq_alarm_handler(void *param);
 static int ice_dev_start(struct rte_eth_dev *dev);
 static int ice_dev_stop(struct rte_eth_dev *dev);
 static int ice_dev_close(struct rte_eth_dev *dev);
@@ -1475,6 +1480,28 @@ ice_handle_aq_msg(struct rte_eth_dev *dev)
 	}
 }
 
+/*
+ * Alarm-based AdminQ polling handler that drains the AdminQ to process
+ * async events, then re-arms itself.
+ *
+ * @param param
+ *  The address of parameter (struct rte_eth_dev *) registered before.
+ *
+ * @return
+ *  void
+ */
+static void
+ice_aq_alarm_handler(void *param)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
+	struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (!pf->adapter_stopped) {
+		ice_handle_aq_msg(dev);
+		rte_eal_alarm_set(ICE_AQ_ALARM_INTERVAL, ice_aq_alarm_handler, dev);
+	}
+}
+
 /**
  * Interrupt handler triggered by NIC for handling
  * specific interrupt.
@@ -2961,6 +2988,11 @@ ice_dev_stop(struct rte_eth_dev *dev)
 	pf->adapter_stopped = true;
 	dev->data->dev_started = 0;
 
+	if (pf->use_aq_polling) {
+		rte_eal_alarm_cancel(ice_aq_alarm_handler, dev);
+		pf->use_aq_polling = false;
+	}
+
 	return 0;
 }
 
@@ -4479,7 +4511,15 @@ ice_dev_start(struct rte_eth_dev *dev)
 	if (ice_rxq_intr_setup(dev))
 		return -EIO;
 
-	rte_intr_enable(pci_dev->intr_handle);
+	/* Fall back to polling the AdminQ via an alarm on platforms where
+	 * interrupt delivery is unavailable.
+	 */
+	if (rte_intr_enable(pci_dev->intr_handle) != 0) {
+		if (rte_eal_alarm_set(ICE_AQ_ALARM_INTERVAL, ice_aq_alarm_handler, dev) != 0)
+			PMD_DRV_LOG(WARNING, "Failed to set alarm for AdminQ polling");
+		else
+			pf->use_aq_polling = true;
+	}
 
 	/* Enable receiving broadcast packets and transmitting packets */
 	ice_set_bit(ICE_PROMISC_BCAST_RX, pmask);
diff --git a/drivers/net/intel/ice/ice_ethdev.h b/drivers/net/intel/ice/ice_ethdev.h
index 0a9d75b9cd..7ee3ea8a70 100644
--- a/drivers/net/intel/ice/ice_ethdev.h
+++ b/drivers/net/intel/ice/ice_ethdev.h
@@ -599,6 +599,7 @@ struct ice_pf {
 	struct ice_eth_stats internal_stats;
 	bool offset_loaded;
 	bool adapter_stopped;
+	bool use_aq_polling; /* true when alarm polls AdminQ in place of interrupt */
 	struct ice_flow_list flow_list;
 	rte_spinlock_t flow_ops_lock;
 	bool init_link_up;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 0/3] net/intel: fix link status when interrupt delivery unavailable
From: Ciara Loftus @ 2026-07-09 11:25 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, Ciara Loftus
In-Reply-To: <20260630141627.1035420-1-ciara.loftus@intel.com>

On FreeBSD with nic_uio, interrupt registration fails silently and
hardware events, including link state notifications, are never processed
via the interrupt path.

For ice, this affects the typical polling-mode configuration: the
interrupt handler is never invoked, AdminQ messages go unprocessed,
and link status is not reliable. A periodic alarm is added to drain
the AdminQ when interrupt delivery is unavailable. For i40e, an
alarm already runs unconditionally in polling mode, so that
configuration is unaffected. The gap is the interrupt-mode
configuration, where the existing alarm was not activated as a
fallback when interrupt enable fails; this patch extends it to do so.

v2:
 * Added warning on rte_eal_alarm_set failure (i40e & ice)
 * Only set use_aq_polling if rte_eal_alarm_set succeeds (i40e & ice)
 * Move alarm cancel in ice_dev_stop to prevent a race (ice)
 * Remove unnecessary alarm cancel from ice_dev_start error path (ice)
 * Added general fix for ice dev_start error path

Ciara Loftus (3):
  net/ice: poll AdminQ if interrupt delivery unavailable
  net/i40e: activate alarm if interrupt delivery unavailable
  net/ice: fix adapter stopped on device start error

 drivers/net/intel/i40e/i40e_ethdev.c | 11 ++++++-
 drivers/net/intel/i40e/i40e_ethdev.h |  2 ++
 drivers/net/intel/ice/ice_ethdev.c   | 44 +++++++++++++++++++++++++++-
 drivers/net/intel/ice/ice_ethdev.h   |  1 +
 4 files changed, 56 insertions(+), 2 deletions(-)

-- 
2.43.0


^ permalink raw reply

* RE: [PATCH 1/2] net/ice: poll AdminQ if interrupt delivery unavailable
From: Loftus, Ciara @ 2026-07-09 10:54 UTC (permalink / raw)
  To: Richardson, Bruce; +Cc: dev@dpdk.org, stable@dpdk.org
In-Reply-To: <akT5YUCBeqqcDxie@bricha3-mobl1.ger.corp.intel.com>

> Subject: Re: [PATCH 1/2] net/ice: poll AdminQ if interrupt delivery unavailable
> 
> On Tue, Jun 30, 2026 at 02:16:26PM +0000, Ciara Loftus wrote:
> > If interrupt registration fails eg. on FreeBSD with nic_uio, the ice
> > interrupt handler is never called and AdminQ messages, including
> > unsolicited link state notifications, are never processed.
> >
> > This gap has always existed but was partially masked by the blocking link
> > status poll in the ice dev_start function, which would delay some time
> > until the link was up before returning. In this case, the link status was
> > typically correct after dev_start. However after that delay was removed in
> > 2dd7e99855 ("net/ice: revert fix link up when starting device"), the link
> > status was often wrong after dev_start, and there was no mechanism to
> > self-correct.
> >
> > Rather than restoring the blocking wait removed by that commit which
> > would re-impose a startup delay on every platform, instead activate a
> > periodic alarm that drains the AdminQ on a 50ms interval, replicating the
> > role of the interrupt handler on platforms where interrupt delivery is not
> > supported.
> >
> > Fixes: 2dd7e99855 ("net/ice: revert fix link up when starting device")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> > ---
> 
> This solution seems reasonable. Some thoughts and comments inline below.
> 
> /Bruce
> 
> >  drivers/net/intel/ice/ice_ethdev.c | 45
> +++++++++++++++++++++++++++++-
> >  drivers/net/intel/ice/ice_ethdev.h |  1 +
> >  2 files changed, 45 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/intel/ice/ice_ethdev.c
> b/drivers/net/intel/ice/ice_ethdev.c
> > index 022d92a2f6..447742ff8d 100644
> > --- a/drivers/net/intel/ice/ice_ethdev.c
> > +++ b/drivers/net/intel/ice/ice_ethdev.c
> > @@ -3,6 +3,7 @@
> >   */
> >
> >  #include <rte_string_fns.h>
> > +#include <rte_alarm.h>
> >  #include <ethdev_pci.h>
> >
> >  #include <ctype.h>
> > @@ -108,7 +109,11 @@ enum ice_link_state_on_close {
> >
> >  #define ICE_RSS_LUT_GLOBAL_QUEUE_NB	64
> >
> > +/* Polling interval used when the interrupt path is unavailable */
> > +#define ICE_AQ_ALARM_INTERVAL 50000 /* us */
> > +
> >  static int ice_dev_configure(struct rte_eth_dev *dev);
> > +static void ice_aq_alarm_handler(void *param);
> >  static int ice_dev_start(struct rte_eth_dev *dev);
> >  static int ice_dev_stop(struct rte_eth_dev *dev);
> >  static int ice_dev_close(struct rte_eth_dev *dev);
> > @@ -1473,6 +1478,28 @@ ice_handle_aq_msg(struct rte_eth_dev *dev)
> >  	}
> >  }
> >
> > +/*
> > + * Alarm-based AdminQ polling handler that drains the AdminQ to process
> > + * async events, then re-arms itself.
> > + *
> > + * @param param
> > + *  The address of parameter (struct rte_eth_dev *) registered before.
> > + *
> > + * @return
> > + *  void
> > + */
> > +static void
> > +ice_aq_alarm_handler(void *param)
> > +{
> > +	struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
> > +	struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> > +
> > +	if (!pf->adapter_stopped) {
> > +		ice_handle_aq_msg(dev);
> > +		rte_eal_alarm_set(ICE_AQ_ALARM_INTERVAL,
> ice_aq_alarm_handler, dev);
> > +	}
> > +}
> > +
> >  /**
> >   * Interrupt handler triggered by NIC for handling
> >   * specific interrupt.
> > @@ -2913,6 +2940,11 @@ ice_dev_stop(struct rte_eth_dev *dev)
> >  	rte_intr_efd_disable(intr_handle);
> >  	rte_intr_vec_list_free(intr_handle);
> >
> > +	if (pf->use_aq_polling) {
> > +		rte_eal_alarm_cancel(ice_aq_alarm_handler, dev);
> > +		pf->use_aq_polling = false;
> > +	}
> > +
> 
> Do we actually need to cancel the timer here? Since the callback itself
> only rearms on started devices, we can let the timer expire and become a
> no-op, rather than needing to explicitly cancel it here.

If we don't cancel the timer, it may fire after device memory is freed on
exit. When dev->data->dev_private is accessed in the handler, it will SEGV.
So we need the cancel in dev_stop.

> 
> If so, and based on other suggestions I have below, we may be able to
> remove this block from stop function entirely.
> 
> >  	pf->adapter_stopped = true;
> >  	dev->data->dev_started = 0;
> >
> > @@ -4434,7 +4466,13 @@ ice_dev_start(struct rte_eth_dev *dev)
> >  	if (ice_rxq_intr_setup(dev))
> >  		return -EIO;
> >
> > -	rte_intr_enable(pci_dev->intr_handle);
> > +	/* Fall back to polling the AdminQ via an alarm on platforms where
> > +	 * interrupt delivery is unavailable.
> > +	 */
> > +	if (rte_intr_enable(pci_dev->intr_handle) != 0) {
> > +		pf->use_aq_polling = true;
> > +		rte_eal_alarm_set(ICE_AQ_ALARM_INTERVAL,
> ice_aq_alarm_handler, dev);
> > +	}
> 
> If alarm set fails, we should at least emit a warning, even if there is
> nothing we can really do about it.

Agree, will implement.

> 
> Also, I don't think we need to track use_aq_polling at all. Its only use is
> to cancel the timer, but all places where we cancel the timer are places
> where we mark the port as stopped (with one exception, which I believe is a
> separate error in the code), so the timer will self-cancel at next timeout
> expiry.

As explained above I believe we need to keep the cancel in ice_dev_stop so
I will keep use_aq_polling to guard that cancel. I think it would probably
be fine to unconditionally cancel but I think it is cleaner to only cancel
if we actually set the alarm.

> 
> >
> >  	/* Enable receiving broadcast packets and transmitting packets */
> >  	ice_set_bit(ICE_PROMISC_BCAST_RX, pmask);
> > @@ -4497,6 +4535,11 @@ ice_dev_start(struct rte_eth_dev *dev)
> >  	for (i = 0; i < nb_txq; i++)
> >  		ice_tx_queue_stop(dev, i);
> >
> > +	if (pf->use_aq_polling) {
> > +		rte_eal_alarm_cancel(ice_aq_alarm_handler, dev);
> > +		pf->use_aq_polling = false;
> > +	}
> > +
> 
> As explained above, I don't think we need this block either. If we have an
> error on start, then the port should be marked as stopped and the timer
> won't rearm itself after it expires.

Will remove.

> 
> NOTE: there is an unrelated bug in the code here - at line 4550 in start,
> we set "adapter_stopped = false", but there is still error handling at line
> 4564 which causes us to jump to rx_err and stop all the rx and tx queues
> again, but neglecting to reset adapter_stopped back to true. I think we
> need to either reset it immediately after the rx_err label, or move the
> assignment down till after all potential rollback points are passed.

Will fix in a separate patch.

> 
> >  	return -EIO;
> >  }
> >
> > diff --git a/drivers/net/intel/ice/ice_ethdev.h
> b/drivers/net/intel/ice/ice_ethdev.h
> > index 20e8a13fe9..86b256fc3f 100644
> > --- a/drivers/net/intel/ice/ice_ethdev.h
> > +++ b/drivers/net/intel/ice/ice_ethdev.h
> > @@ -599,6 +599,7 @@ struct ice_pf {
> >  	struct ice_eth_stats internal_stats;
> >  	bool offset_loaded;
> >  	bool adapter_stopped;
> > +	bool use_aq_polling; /* true when interrupt path unavailable */
> >  	struct ice_flow_list flow_list;
> >  	rte_spinlock_t flow_ops_lock;
> >  	bool init_link_up;
> > --
> > 2.43.0
> >

^ permalink raw reply

* [PATCH v9 4/4] net/zxdh: optimize Tx xmit pkts performance
From: Junlong Wang @ 2026-07-09 10:46 UTC (permalink / raw)
  To: stephen; +Cc: dev, Junlong Wang
In-Reply-To: <20260709104637.924861-1-wang.junlong1@zte.com.cn>


[-- Attachment #1.1.1: Type: text/plain, Size: 11577 bytes --]

optimize zxdh_xmit_pkts_packed functions for
xmit pkt performance.

Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
 drivers/net/zxdh/zxdh_ethdev.c |   4 +-
 drivers/net/zxdh/zxdh_queue.h  |   2 +-
 drivers/net/zxdh/zxdh_rxtx.c   | 140 ++++++++++++++-------------------
 drivers/net/zxdh/zxdh_rxtx.h   |  11 ++-
 4 files changed, 65 insertions(+), 92 deletions(-)

diff --git a/drivers/net/zxdh/zxdh_ethdev.c b/drivers/net/zxdh/zxdh_ethdev.c
index fe76139f3d..a217871f0b 100644
--- a/drivers/net/zxdh/zxdh_ethdev.c
+++ b/drivers/net/zxdh/zxdh_ethdev.c
@@ -490,7 +490,7 @@ zxdh_dev_free_mbufs(struct rte_eth_dev *dev)
 		if (!vq)
 			continue;
 		while ((buf = zxdh_queue_detach_unused(vq)) != NULL)
-			rte_pktmbuf_free(buf);
+			rte_pktmbuf_free_seg(buf);
 		PMD_DRV_LOG(DEBUG, "freeing %s[%d] used and unused buf",
 		"rxq", i * 2);
 	}
@@ -499,7 +499,7 @@ zxdh_dev_free_mbufs(struct rte_eth_dev *dev)
 		if (!vq)
 			continue;
 		while ((buf = zxdh_queue_detach_unused(vq)) != NULL)
-			rte_pktmbuf_free(buf);
+			rte_pktmbuf_free_seg(buf);
 		PMD_DRV_LOG(DEBUG, "freeing %s[%d] used and unused buf",
 		"txq", i * 2 + 1);
 	}
diff --git a/drivers/net/zxdh/zxdh_queue.h b/drivers/net/zxdh/zxdh_queue.h
index b079272162..091d1f25db 100644
--- a/drivers/net/zxdh/zxdh_queue.h
+++ b/drivers/net/zxdh/zxdh_queue.h
@@ -374,7 +374,7 @@ zxdh_queue_full(const struct zxdh_virtqueue *vq)
 }
 
 static inline void
-zxdh_queue_store_flags_packed(struct zxdh_vring_packed_desc *dp, uint16_t flags)
+zxdh_queue_store_flags_packed(volatile struct zxdh_vring_packed_desc *dp, uint16_t flags)
 {
 	rte_io_wmb();
 	dp->flags = flags;
diff --git a/drivers/net/zxdh/zxdh_rxtx.c b/drivers/net/zxdh/zxdh_rxtx.c
index ab0510a753..981bcf6203 100644
--- a/drivers/net/zxdh/zxdh_rxtx.c
+++ b/drivers/net/zxdh/zxdh_rxtx.c
@@ -114,6 +114,22 @@
 		RTE_MBUF_F_TX_SEC_OFFLOAD |     \
 		RTE_MBUF_F_TX_UDP_SEG)
 
+#if RTE_CACHE_LINE_SIZE == 128
+#define NEXT_CACHELINE_OFF_16B   8
+#define NEXT_CACHELINE_OFF_8B   16
+#elif RTE_CACHE_LINE_SIZE == 64
+#define NEXT_CACHELINE_OFF_16B   4
+#define NEXT_CACHELINE_OFF_8B    8
+#else
+#define NEXT_CACHELINE_OFF_16B  (RTE_CACHE_LINE_SIZE / 16)
+#define NEXT_CACHELINE_OFF_8B   (RTE_CACHE_LINE_SIZE / 8)
+#endif
+#define N_PER_LOOP  NEXT_CACHELINE_OFF_8B
+#define N_PER_LOOP_MASK (N_PER_LOOP - 1)
+
+#define rxq_get_vq(q) ((q)->vq)
+#define txq_get_vq(q) ((q)->vq)
+
 uint32_t zxdh_outer_l2_type[16] = {
 	0,
 	RTE_PTYPE_L2_ETHER,
@@ -201,43 +217,6 @@ uint32_t zxdh_inner_l4_type[16] = {
 	0,
 };
 
-static void
-zxdh_xmit_cleanup_inorder_packed(struct zxdh_virtqueue *vq, int32_t num)
-{
-	uint16_t used_idx = 0;
-	uint16_t id       = 0;
-	uint16_t curr_id  = 0;
-	uint16_t free_cnt = 0;
-	uint16_t size     = vq->vq_nentries;
-	struct zxdh_vring_packed_desc *desc = vq->vq_packed.ring.desc;
-	struct zxdh_vq_desc_extra     *dxp  = NULL;
-
-	used_idx = vq->vq_used_cons_idx;
-	/* desc_is_used has a load-acquire or rte_io_rmb inside
-	 * and wait for used desc in virtqueue.
-	 */
-	while (num > 0 && desc_is_used(&desc[used_idx], vq)) {
-		id = desc[used_idx].id;
-		do {
-			curr_id = used_idx;
-			dxp = &vq->vq_descx[used_idx];
-			used_idx += dxp->ndescs;
-			free_cnt += dxp->ndescs;
-			num -= dxp->ndescs;
-			if (used_idx >= size) {
-				used_idx -= size;
-				vq->used_wrap_counter ^= 1;
-			}
-			if (dxp->cookie != NULL) {
-				rte_pktmbuf_free(dxp->cookie);
-				dxp->cookie = NULL;
-			}
-		} while (curr_id != id);
-	}
-	vq->vq_used_cons_idx = used_idx;
-	vq->vq_free_cnt += free_cnt;
-}
-
 static inline uint16_t
 zxdh_get_mtu(struct zxdh_virtqueue *vq)
 {
@@ -334,7 +313,7 @@ zxdh_xmit_fill_net_hdr(struct zxdh_virtqueue *vq, struct rte_mbuf *cookie,
 }
 
 static inline void
-zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,
+zxdh_xmit_enqueue_push(struct zxdh_virtnet_tx *txvq,
 						struct rte_mbuf *cookie)
 {
 	struct zxdh_virtqueue *vq = txvq->vq;
@@ -345,7 +324,6 @@ zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,
 	uint8_t hdr_len = vq->hw->dl_net_hdr_len;
 	struct zxdh_vring_packed_desc *dp = &vq->vq_packed.ring.desc[id];
 
-	dxp->ndescs = 1;
 	dxp->cookie = cookie;
 	hdr = rte_pktmbuf_mtod_offset(cookie, struct zxdh_net_hdr_dl *, -hdr_len);
 	zxdh_xmit_fill_net_hdr(vq, cookie, hdr);
@@ -362,51 +340,57 @@ zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,
 }
 
 static inline void
-zxdh_enqueue_xmit_packed(struct zxdh_virtnet_tx *txvq,
+zxdh_xmit_enqueue_append(struct zxdh_virtnet_tx *txvq,
 						struct rte_mbuf *cookie,
 						uint16_t needed)
 {
 	struct zxdh_tx_region *txr = txvq->zxdh_net_hdr_mz->addr;
 	struct zxdh_virtqueue *vq = txvq->vq;
-	uint16_t id = vq->vq_avail_idx;
-	struct zxdh_vq_desc_extra *dxp = &vq->vq_descx[id];
+	struct zxdh_vq_desc_extra *dep = &vq->vq_descx[0];
 	uint16_t head_idx = vq->vq_avail_idx;
 	uint16_t idx = head_idx;
 	struct zxdh_vring_packed_desc *start_dp = vq->vq_packed.ring.desc;
 	struct zxdh_vring_packed_desc *head_dp = &vq->vq_packed.ring.desc[idx];
 	struct zxdh_net_hdr_dl *hdr = NULL;
 
-	uint16_t head_flags = cookie->next ? ZXDH_VRING_DESC_F_NEXT : 0;
+	uint16_t id = vq->vq_avail_idx;
+	struct zxdh_vq_desc_extra *dxp = &vq->vq_descx[id];
 	uint8_t hdr_len = vq->hw->dl_net_hdr_len;
+	uint16_t head_flags = 0;
+	/*
+	 * IMPORTANT: For multi-seg packets, we set the head descriptor's cookie to NULL
+	 * and store each segment's mbuf in its corresponding vq_descx[idx].cookie.
+	 * This is required for the per-descriptor mbuf free in zxdh_xmit_fast_flush()
+	 * which uses rte_pktmbuf_free_seg() to free individual segments.
+	 * Any code path that attempts to read vq_descx[head_id].cookie will see NULL
+	 * and must handle this case appropriately.
+	 */
+	dxp->cookie = NULL;
 
-	dxp->ndescs = needed;
-	dxp->cookie = cookie;
-	head_flags |= vq->cached_flags;
-
+	/* setup first tx ring slot to point to header stored in reserved region. */
 	start_dp[idx].addr = txvq->zxdh_net_hdr_mem + RTE_PTR_DIFF(&txr[idx].tx_hdr, txr);
 	start_dp[idx].len  = hdr_len;
-	head_flags |= ZXDH_VRING_DESC_F_NEXT;
+	start_dp[idx].id = idx;
+	head_flags |= vq->cached_flags | ZXDH_VRING_DESC_F_NEXT;
 	hdr = (void *)&txr[idx].tx_hdr;
 
-	rte_prefetch1(hdr);
+	zxdh_xmit_fill_net_hdr(vq, cookie, hdr);
+
 	idx++;
 	if (idx >= vq->vq_nentries) {
 		idx -= vq->vq_nentries;
 		vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;
 	}
 
-	zxdh_xmit_fill_net_hdr(vq, cookie, hdr);
-
 	do {
 		start_dp[idx].addr = rte_pktmbuf_iova(cookie);
 		start_dp[idx].len  = cookie->data_len;
-		start_dp[idx].id = id;
-		if (likely(idx != head_idx)) {
-			uint16_t flags = cookie->next ? ZXDH_VRING_DESC_F_NEXT : 0;
+		start_dp[idx].id = idx;
 
-			flags |= vq->cached_flags;
-			start_dp[idx].flags = flags;
-		}
+		dep[idx].cookie = cookie;
+		uint16_t flags = cookie->next ? ZXDH_VRING_DESC_F_NEXT : 0;
+		flags |= vq->cached_flags;
+		start_dp[idx].flags = flags;
 
 		idx++;
 		if (idx >= vq->vq_nentries) {
@@ -456,7 +440,7 @@ zxdh_update_packet_stats(struct zxdh_virtnet_stats *stats, struct rte_mbuf *mbuf
 }
 
 static void
-zxdh_xmit_flush(struct zxdh_virtqueue *vq)
+zxdh_xmit_fast_flush(struct zxdh_virtqueue *vq)
 {
 	uint16_t id       = 0;
 	uint16_t curr_id  = 0;
@@ -472,20 +456,22 @@ zxdh_xmit_flush(struct zxdh_virtqueue *vq)
 	 * for a used descriptor in the virtqueue.
 	 */
 	while (desc_is_used(&desc[used_idx], vq)) {
+		rte_prefetch0(&desc[used_idx + NEXT_CACHELINE_OFF_16B]);
 		id = desc[used_idx].id;
 		do {
+			desc[used_idx].id = used_idx;
 			curr_id = used_idx;
 			dxp = &vq->vq_descx[used_idx];
-			used_idx += dxp->ndescs;
-			free_cnt += dxp->ndescs;
-			if (used_idx >= size) {
-				used_idx -= size;
-				vq->used_wrap_counter ^= 1;
-			}
 			if (dxp->cookie != NULL) {
-				rte_pktmbuf_free(dxp->cookie);
+				rte_pktmbuf_free_seg(dxp->cookie);
 				dxp->cookie = NULL;
 			}
+			used_idx += 1;
+			free_cnt += 1;
+			if (unlikely(used_idx == size)) {
+				used_idx = 0;
+				vq->used_wrap_counter ^= 1;
+			}
 		} while (curr_id != id);
 	}
 	vq->vq_used_cons_idx = used_idx;
@@ -499,13 +485,12 @@ zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt
 	struct zxdh_virtqueue  *vq   = txvq->vq;
 	uint16_t nb_tx = 0;
 
-	zxdh_xmit_flush(vq);
+	zxdh_xmit_fast_flush(vq);
 
 	for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
 		struct rte_mbuf *txm = tx_pkts[nb_tx];
 		int32_t can_push     = 0;
 		int32_t slots        = 0;
-		int32_t need         = 0;
 
 		rte_prefetch0(txm);
 		/* optimize ring usage */
@@ -522,26 +507,16 @@ zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt
 		 * default    => number of segments + 1
 		 **/
 		slots = txm->nb_segs + !can_push;
-		need = slots - vq->vq_free_cnt;
 		/* Positive value indicates it need free vring descriptors */
-		if (unlikely(need > 0)) {
-			zxdh_xmit_cleanup_inorder_packed(vq, need);
-			need = slots - vq->vq_free_cnt;
-			if (unlikely(need > 0)) {
-				PMD_TX_LOG(ERR,
-						" No enough %d free tx descriptors to transmit."
-						"freecnt %d",
-						need,
-						vq->vq_free_cnt);
-				break;
-			}
-		}
+
+		if (unlikely(slots >  vq->vq_free_cnt))
+			break;
 
 		/* Enqueue Packet buffers */
 		if (can_push)
-			zxdh_enqueue_xmit_packed_fast(txvq, txm);
+			zxdh_xmit_enqueue_push(txvq, txm);
 		else
-			zxdh_enqueue_xmit_packed(txvq, txm, slots);
+			zxdh_xmit_enqueue_append(txvq, txm, slots);
 		zxdh_update_packet_stats(&txvq->stats, txm);
 	}
 	txvq->stats.packets += nb_tx;
@@ -1070,7 +1045,6 @@ uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint1
 
 		if (unlikely(zxdh_init_mbuf(rxm, len, hw, &vq->rxq) < 0))
 			continue;
-		rcv_pkts[nb_rx] = rxm;
 		zxdh_update_packet_stats(&rxvq->stats, rxm);
 		nb_rx++;
 	}
diff --git a/drivers/net/zxdh/zxdh_rxtx.h b/drivers/net/zxdh/zxdh_rxtx.h
index dba9567414..cf9cecc4fd 100644
--- a/drivers/net/zxdh/zxdh_rxtx.h
+++ b/drivers/net/zxdh/zxdh_rxtx.h
@@ -56,18 +56,17 @@ struct __rte_cache_aligned zxdh_virtnet_rx {
 
 struct __rte_cache_aligned zxdh_virtnet_tx {
 	struct zxdh_virtqueue         *vq;
-
-	rte_iova_t                zxdh_net_hdr_mem; /* hdr for each xmit packet */
-	uint16_t                  queue_id;           /* DPDK queue index. */
-	uint16_t                  port_id;            /* Device port identifier. */
+	const struct rte_memzone *zxdh_net_hdr_mz;  /* memzone to populate hdr. */
+	rte_iova_t               zxdh_net_hdr_mem; /* hdr for each xmit packet */
 	struct zxdh_virtnet_stats      stats;
 	const struct rte_memzone *mz;                 /* mem zone to populate TX ring. */
-	const struct rte_memzone *zxdh_net_hdr_mz;  /* memzone to populate hdr. */
+	uint64_t offloads;
+	uint16_t                  queue_id;           /* DPDK queue index. */
+	uint16_t                  port_id;            /* Device port identifier. */
 };
 
 uint16_t zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
 uint16_t zxdh_xmit_pkts_prepare(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
 uint16_t zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
 uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint16_t nb_pkts);
-
 #endif  /* ZXDH_RXTX_H */
-- 
2.27.0

[-- Attachment #1.1.2: Type: text/html , Size: 29265 bytes --]

^ permalink raw reply related

* [PATCH v9 3/4] net/zxdh: optimize Rx recv pkts performance
From: Junlong Wang @ 2026-07-09 10:46 UTC (permalink / raw)
  To: stephen; +Cc: dev, Junlong Wang
In-Reply-To: <20260709104637.924861-1-wang.junlong1@zte.com.cn>


[-- Attachment #1.1.1: Type: text/plain, Size: 16239 bytes --]

1. Add simple RX recv functions (zxdh_recv_single_pkts)
   for single-segment packet recv.
2. And optimize Rx recv pkts packed ops.
3. Remove unnecessary ZXDH_NET_F_MRG_RXBUF negotiation check and
   some unnecessary statistical counters form the xstats name tables.

Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
 drivers/net/zxdh/zxdh_ethdev.c     |  39 +++++--
 drivers/net/zxdh/zxdh_ethdev_ops.c |  23 ++--
 drivers/net/zxdh/zxdh_ethdev_ops.h |   4 +
 drivers/net/zxdh/zxdh_rxtx.c       | 174 +++++++++++++++++++++++------
 drivers/net/zxdh/zxdh_rxtx.h       |  16 +--
 5 files changed, 193 insertions(+), 63 deletions(-)

diff --git a/drivers/net/zxdh/zxdh_ethdev.c b/drivers/net/zxdh/zxdh_ethdev.c
index a383619419..fe76139f3d 100644
--- a/drivers/net/zxdh/zxdh_ethdev.c
+++ b/drivers/net/zxdh/zxdh_ethdev.c
@@ -1263,18 +1263,43 @@ zxdh_dev_close(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static int32_t
-zxdh_set_rxtx_funcs(struct rte_eth_dev *eth_dev)
+/*
+ * Determine whether the current configuration requires support for scattered
+ * receive; return 1 if scattered receive is required and 0 if not.
+ */
+static int zxdh_scattered_rx(struct rte_eth_dev *eth_dev)
 {
-	struct zxdh_hw *hw = eth_dev->data->dev_private;
+	uint16_t buf_size;
 
-	if (!zxdh_pci_with_feature(hw, ZXDH_NET_F_MRG_RXBUF)) {
-		PMD_DRV_LOG(ERR, "port %u not support rx mergeable", eth_dev->data->port_id);
-		return -1;
+	if (eth_dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_TCP_LRO) {
+		eth_dev->data->lro = 1;
+		return 1;
 	}
+
+	if (eth_dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER)
+		return 1;
+
+	PMD_DRV_LOG(DEBUG, "port %u min_rx_buf_size %u",
+		eth_dev->data->port_id, eth_dev->data->min_rx_buf_size);
+	buf_size = eth_dev->data->min_rx_buf_size - RTE_PKTMBUF_HEADROOM;
+	if (eth_dev->data->mtu + ZXDH_ETH_OVERHEAD > buf_size)
+		return 1;
+
+	return 0;
+}
+
+static int32_t
+zxdh_set_rxtx_funcs(struct rte_eth_dev *eth_dev)
+{
 	eth_dev->tx_pkt_prepare = zxdh_xmit_pkts_prepare;
+	eth_dev->data->scattered_rx = zxdh_scattered_rx(eth_dev);
+
 	eth_dev->tx_pkt_burst = &zxdh_xmit_pkts_packed;
-	eth_dev->rx_pkt_burst = &zxdh_recv_pkts_packed;
+
+	if (eth_dev->data->scattered_rx)
+		eth_dev->rx_pkt_burst = &zxdh_recv_pkts_packed;
+	else
+		eth_dev->rx_pkt_burst = &zxdh_recv_single_pkts;
 
 	return 0;
 }
diff --git a/drivers/net/zxdh/zxdh_ethdev_ops.c b/drivers/net/zxdh/zxdh_ethdev_ops.c
index 50247116d9..9a8e05e941 100644
--- a/drivers/net/zxdh/zxdh_ethdev_ops.c
+++ b/drivers/net/zxdh/zxdh_ethdev_ops.c
@@ -95,10 +95,6 @@ static const struct rte_zxdh_xstats_name_off zxdh_rxq_stat_strings[] = {
 	{"good_bytes",             offsetof(struct zxdh_virtnet_rx, stats.bytes)},
 	{"errors",                 offsetof(struct zxdh_virtnet_rx, stats.errors)},
 	{"idle",                   offsetof(struct zxdh_virtnet_rx, stats.idle)},
-	{"full",                   offsetof(struct zxdh_virtnet_rx, stats.full)},
-	{"norefill",               offsetof(struct zxdh_virtnet_rx, stats.norefill)},
-	{"multicast_packets",      offsetof(struct zxdh_virtnet_rx, stats.multicast)},
-	{"broadcast_packets",      offsetof(struct zxdh_virtnet_rx, stats.broadcast)},
 	{"truncated_err",          offsetof(struct zxdh_virtnet_rx, stats.truncated_err)},
 	{"offload_cfg_err",        offsetof(struct zxdh_virtnet_rx, stats.offload_cfg_err)},
 	{"invalid_hdr_len_err",    offsetof(struct zxdh_virtnet_rx, stats.invalid_hdr_len_err)},
@@ -117,14 +113,12 @@ static const struct rte_zxdh_xstats_name_off zxdh_txq_stat_strings[] = {
 	{"good_packets",           offsetof(struct zxdh_virtnet_tx, stats.packets)},
 	{"good_bytes",             offsetof(struct zxdh_virtnet_tx, stats.bytes)},
 	{"errors",                 offsetof(struct zxdh_virtnet_tx, stats.errors)},
-	{"idle",                   offsetof(struct zxdh_virtnet_tx, stats.idle)},
-	{"norefill",               offsetof(struct zxdh_virtnet_tx, stats.norefill)},
-	{"multicast_packets",      offsetof(struct zxdh_virtnet_tx, stats.multicast)},
-	{"broadcast_packets",      offsetof(struct zxdh_virtnet_tx, stats.broadcast)},
+	{"idle",                 offsetof(struct zxdh_virtnet_tx, stats.idle)},
 	{"truncated_err",          offsetof(struct zxdh_virtnet_tx, stats.truncated_err)},
 	{"offload_cfg_err",        offsetof(struct zxdh_virtnet_tx, stats.offload_cfg_err)},
 	{"invalid_hdr_len_err",    offsetof(struct zxdh_virtnet_tx, stats.invalid_hdr_len_err)},
 	{"no_segs_err",            offsetof(struct zxdh_virtnet_tx, stats.no_segs_err)},
+	{"no_free_tx_desc_err",    offsetof(struct zxdh_virtnet_tx, stats.no_free_tx_desc_err)},
 	{"undersize_packets",      offsetof(struct zxdh_virtnet_tx, stats.size_bins[0])},
 	{"size_64_packets",        offsetof(struct zxdh_virtnet_tx, stats.size_bins[1])},
 	{"size_65_127_packets",    offsetof(struct zxdh_virtnet_tx, stats.size_bins[2])},
@@ -2026,6 +2020,19 @@ int zxdh_dev_mtu_set(struct rte_eth_dev *dev, uint16_t new_mtu)
 	uint16_t vfid = zxdh_vport_to_vfid(hw->vport);
 	int ret;
 
+	/* If device is started, refuse mtu that requires the support of
+	 * scattered packets when this feature has not been enabled before.
+	 */
+	if (dev->data->dev_started) {
+		uint32_t buf_size = dev->data->min_rx_buf_size - RTE_PKTMBUF_HEADROOM;
+		uint8_t need_scatter = (uint32_t)ZXDH_MTU_TO_PKTLEN(new_mtu) > buf_size;
+
+		if (need_scatter != dev->data->scattered_rx) {
+			PMD_DRV_LOG(ERR, "Stop port first.");
+			return -EINVAL;
+		}
+	}
+
 	if (hw->is_pf) {
 		ret = zxdh_get_panel_attr(dev, &panel);
 		if (ret != 0) {
diff --git a/drivers/net/zxdh/zxdh_ethdev_ops.h b/drivers/net/zxdh/zxdh_ethdev_ops.h
index 6dfe4be473..c49d79c232 100644
--- a/drivers/net/zxdh/zxdh_ethdev_ops.h
+++ b/drivers/net/zxdh/zxdh_ethdev_ops.h
@@ -40,6 +40,10 @@
 #define ZXDH_SPM_SPEED_4X_100G         RTE_BIT32(10)
 #define ZXDH_SPM_SPEED_4X_200G         RTE_BIT32(11)
 
+#define ZXDH_VLAN_TAG_LEN   4
+#define ZXDH_ETH_OVERHEAD  (RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + ZXDH_VLAN_TAG_LEN * 2)
+#define ZXDH_MTU_TO_PKTLEN(mtu) ((mtu) + ZXDH_ETH_OVERHEAD)
+
 struct zxdh_np_stats_data {
 	uint64_t n_pkts_dropped;
 	uint64_t n_bytes_dropped;
diff --git a/drivers/net/zxdh/zxdh_rxtx.c b/drivers/net/zxdh/zxdh_rxtx.c
index 93506a4b49..ab0510a753 100644
--- a/drivers/net/zxdh/zxdh_rxtx.c
+++ b/drivers/net/zxdh/zxdh_rxtx.c
@@ -613,10 +613,12 @@ zxdh_dequeue_burst_rx_packed(struct zxdh_virtqueue *vq,
 	uint16_t i, used_idx;
 	uint16_t id;
 
+	used_idx = vq->vq_used_cons_idx;
+	rte_prefetch0(&desc[used_idx]);
+
 	for (i = 0; i < num; i++) {
 		used_idx = vq->vq_used_cons_idx;
-		/**
-		 * desc_is_used has a load-acquire or rte_io_rmb inside
+		/* desc_is_used has a load-acquire or rte_io_rmb inside
 		 * and wait for used desc in virtqueue.
 		 */
 		if (!desc_is_used(&desc[used_idx], vq))
@@ -823,17 +825,52 @@ zxdh_rx_update_mbuf(struct zxdh_hw *hw, struct rte_mbuf *m, struct zxdh_net_hdr_
 	}
 }
 
-static void zxdh_discard_rxbuf(struct zxdh_virtqueue *vq, struct rte_mbuf *m)
+static void refill_desc_unwrap(struct zxdh_virtqueue *vq,
+		struct rte_mbuf **cookie, uint16_t nb_pkts)
 {
-	int32_t error = 0;
-	/*
-	 * Requeue the discarded mbuf. This should always be
-	 * successful since it was just dequeued.
-	 */
-	error = zxdh_enqueue_recv_refill_packed(vq, &m, 1);
-	if (unlikely(error)) {
-		PMD_RX_LOG(ERR, "cannot enqueue discarded mbuf");
-		rte_pktmbuf_free(m);
+	struct zxdh_vring_packed_desc *start_dp = vq->vq_packed.ring.desc;
+	struct zxdh_vq_desc_extra *dxp;
+	uint16_t flags = vq->cached_flags;
+	int32_t i;
+	uint16_t idx;
+
+	idx = vq->vq_avail_idx;
+	for (i = 0; i < nb_pkts; i++) {
+		dxp = &vq->vq_descx[idx];
+		dxp->cookie = (void *)cookie[i];
+		start_dp[idx].addr = rte_mbuf_iova_get(cookie[i]) + RTE_PKTMBUF_HEADROOM;
+		start_dp[idx].len = cookie[i]->buf_len - RTE_PKTMBUF_HEADROOM;
+		zxdh_queue_store_flags_packed(&start_dp[idx], flags);
+		idx++;
+	}
+	vq->vq_avail_idx += nb_pkts;
+	vq->vq_free_cnt = vq->vq_free_cnt - nb_pkts;
+}
+
+static void refill_que_descs(struct zxdh_virtqueue *vq, struct rte_eth_dev *dev)
+{
+	/* free_cnt may include mrg descs */
+	struct rte_mbuf *new_pkts[ZXDH_MBUF_BURST_SZ];
+	uint16_t free_cnt = RTE_MIN(ZXDH_MBUF_BURST_SZ, vq->vq_free_cnt);
+	struct zxdh_virtnet_rx *rxvq = &vq->rxq;
+	uint16_t  unwrap_cnt, left_cnt;
+
+	if (!rte_pktmbuf_alloc_bulk(rxvq->mpool, new_pkts, free_cnt)) {
+		left_cnt = free_cnt;
+		unwrap_cnt = 0;
+		if ((vq->vq_avail_idx + free_cnt) >= vq->vq_nentries) {
+			unwrap_cnt = vq->vq_nentries - vq->vq_avail_idx;
+			left_cnt = free_cnt - unwrap_cnt;
+			refill_desc_unwrap(vq, new_pkts, unwrap_cnt);
+			vq->vq_avail_idx = 0;
+			vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;
+		}
+		if (left_cnt)
+			refill_desc_unwrap(vq, new_pkts + unwrap_cnt, left_cnt);
+
+		rte_io_wmb();
+	} else {
+		dev->data->rx_mbuf_alloc_failed += free_cnt;
 	}
 }
 
@@ -852,7 +889,6 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,
 	uint16_t len = 0;
 	uint32_t seg_num = 0;
 	uint32_t seg_res = 0;
-	uint32_t error = 0;
 	uint16_t hdr_size = 0;
 	uint16_t nb_rx = 0;
 	uint16_t i;
@@ -873,7 +909,8 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,
 		rx_pkts[nb_rx] = rxm;
 		prev = rxm;
 		len = lens[i];
-		header = rte_pktmbuf_mtod(rxm, struct zxdh_net_hdr_ul *);
+		header = (struct zxdh_net_hdr_ul *)((char *)
+					rxm->buf_addr + RTE_PKTMBUF_HEADROOM);
 
 		seg_num  = header->type_hdr.num_buffers;
 
@@ -886,7 +923,7 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,
 			rxvq->stats.invalid_hdr_len_err++;
 			continue;
 		}
-		rxm->data_off += hdr_size;
+		rxm->data_off = RTE_PKTMBUF_HEADROOM + hdr_size;
 		rxm->nb_segs = seg_num;
 		rxm->ol_flags = 0;
 		rcvd_pkt_len = len - hdr_size;
@@ -902,18 +939,19 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,
 			len = lens[i];
 			rxm = rcv_pkts[i];
 			rxm->data_len = len;
+			rxm->data_off = RTE_PKTMBUF_HEADROOM;
 			rcvd_pkt_len += len;
 			prev->next = rxm;
 			prev = rxm;
 			rxm->next = NULL;
-			seg_res -= 1;
+			seg_res--;
 		}
 
 		if (!seg_res) {
 			if (rcvd_pkt_len != rx_pkts[nb_rx]->pkt_len) {
 				PMD_RX_LOG(ERR, "dropped rcvd_pkt_len %d pktlen %d",
 					rcvd_pkt_len, rx_pkts[nb_rx]->pkt_len);
-				zxdh_discard_rxbuf(vq, rx_pkts[nb_rx]);
+				rte_pktmbuf_free(rx_pkts[nb_rx]);
 				rxvq->stats.errors++;
 				rxvq->stats.truncated_err++;
 				continue;
@@ -942,14 +980,14 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,
 			prev->next = rxm;
 			prev = rxm;
 			rxm->next = NULL;
-			extra_idx += 1;
+			extra_idx++;
 		}
 		seg_res -= rcv_cnt;
 		if (!seg_res) {
 			if (unlikely(rcvd_pkt_len != rx_pkts[nb_rx]->pkt_len)) {
 				PMD_RX_LOG(ERR, "dropped rcvd_pkt_len %d pktlen %d",
 					rcvd_pkt_len, rx_pkts[nb_rx]->pkt_len);
-				zxdh_discard_rxbuf(vq, rx_pkts[nb_rx]);
+				rte_pktmbuf_free(rx_pkts[nb_rx]);
 				rxvq->stats.errors++;
 				rxvq->stats.truncated_err++;
 				continue;
@@ -961,26 +999,88 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,
 	rxvq->stats.packets += nb_rx;
 
 refill:
-	/* Allocate new mbuf for the used descriptor */
-	if (likely(!zxdh_queue_full(vq))) {
-		struct rte_mbuf *new_pkts[ZXDH_MBUF_BURST_SZ];
-		/* free_cnt may include mrg descs */
-		uint16_t free_cnt = RTE_MIN(vq->vq_free_cnt, ZXDH_MBUF_BURST_SZ);
-
-		if (!rte_pktmbuf_alloc_bulk(rxvq->mpool, new_pkts, free_cnt)) {
-			error = zxdh_enqueue_recv_refill_packed(vq, new_pkts, free_cnt);
-			if (unlikely(error)) {
-				for (i = 0; i < free_cnt; i++)
-					rte_pktmbuf_free(new_pkts[i]);
-			}
+	if (vq->vq_free_cnt > 0) {
+		struct rte_eth_dev *dev = hw->eth_dev;
+		refill_que_descs(vq, dev);
+		zxdh_queue_notify(vq);
+	}
 
-			if (unlikely(zxdh_queue_kick_prepare_packed(vq)))
-				zxdh_queue_notify(vq);
-		} else {
-			struct rte_eth_dev *dev = hw->eth_dev;
+	return nb_rx;
+}
 
-			dev->data->rx_mbuf_alloc_failed += free_cnt;
-		}
+static inline int zxdh_init_mbuf(struct rte_mbuf *rxm, uint16_t len,
+		struct zxdh_hw *hw, struct zxdh_virtnet_rx *rxvq)
+{
+	uint16_t hdr_size = 0;
+	struct zxdh_net_hdr_ul *header;
+
+	header = rte_pktmbuf_mtod(rxm, struct zxdh_net_hdr_ul *);
+	rxm->ol_flags = 0;
+	rxm->vlan_tci = 0;
+	rxm->vlan_tci_outer = 0;
+
+	hdr_size = header->type_hdr.pd_len << 1;
+	if (unlikely(header->type_hdr.num_buffers != 1)) {
+		PMD_RX_LOG(DEBUG, "hdr_size:%u nb_segs %d is invalid",
+			hdr_size, header->type_hdr.num_buffers);
+		rte_pktmbuf_free(rxm);
+		rxvq->stats.invalid_hdr_len_err++;
+		return -1;
+	}
+	zxdh_rx_update_mbuf(hw, rxm, header);
+
+	rxm->nb_segs = 1;
+	rxm->data_off = RTE_PKTMBUF_HEADROOM + hdr_size;
+	rxm->data_len = len - hdr_size;
+	rxm->port = hw->port_id;
+
+	if (rxm->data_len != rxm->pkt_len) {
+		PMD_RX_LOG(ERR, "dropped rcvd_pkt_len %d pktlen %d  bufaddr %p.",
+					rxm->data_len, rxm->pkt_len, rxm->buf_addr);
+		rte_pktmbuf_free(rxm);
+		rxvq->stats.truncated_err++;
+		rxvq->stats.errors++;
+		return -1;
+	}
+	return 0;
+}
+
+uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint16_t nb_pkts)
+{
+	struct zxdh_virtnet_rx *rxvq = rx_queue;
+	struct zxdh_virtqueue *vq = rxvq->vq;
+	struct zxdh_hw *hw = vq->hw;
+	uint32_t lens[ZXDH_MBUF_BURST_SZ];
+	uint16_t nb_rx = 0;
+	uint16_t num;
+	uint16_t i;
+
+	num = nb_pkts;
+	if (unlikely(num > ZXDH_MBUF_BURST_SZ))
+		num = ZXDH_MBUF_BURST_SZ;
+	num = zxdh_dequeue_burst_rx_packed(vq, rcv_pkts, lens, num);
+	if (num == 0) {
+		rxvq->stats.idle++;
+		goto refill;
+	}
+
+	for (i = 0; i < num; i++) {
+		struct rte_mbuf *rxm = rcv_pkts[i];
+		uint16_t len = lens[i];
+
+		if (unlikely(zxdh_init_mbuf(rxm, len, hw, &vq->rxq) < 0))
+			continue;
+		rcv_pkts[nb_rx] = rxm;
+		zxdh_update_packet_stats(&rxvq->stats, rxm);
+		nb_rx++;
+	}
+	rxvq->stats.packets += nb_rx;
+
+refill:
+	if (vq->vq_free_cnt > 0) {
+		struct rte_eth_dev *dev = hw->eth_dev;
+		refill_que_descs(vq, dev);
+		zxdh_queue_notify(vq);
 	}
 	return nb_rx;
 }
diff --git a/drivers/net/zxdh/zxdh_rxtx.h b/drivers/net/zxdh/zxdh_rxtx.h
index 424048607e..dba9567414 100644
--- a/drivers/net/zxdh/zxdh_rxtx.h
+++ b/drivers/net/zxdh/zxdh_rxtx.h
@@ -36,29 +36,22 @@ struct zxdh_virtnet_stats {
 	uint64_t bytes;
 	uint64_t errors;
 	uint64_t idle;
-	uint64_t full;
-	uint64_t norefill;
-	uint64_t multicast;
-	uint64_t broadcast;
 	uint64_t truncated_err;
 	uint64_t offload_cfg_err;
 	uint64_t invalid_hdr_len_err;
 	uint64_t no_segs_err;
+	uint64_t no_free_tx_desc_err;
 	uint64_t size_bins[8];
 };
 
 struct __rte_cache_aligned zxdh_virtnet_rx {
 	struct zxdh_virtqueue         *vq;
-
-	uint64_t                  mbuf_initializer; /* value to init mbufs. */
 	struct rte_mempool       *mpool;            /* mempool for mbuf allocation */
-	uint16_t                  queue_id;         /* DPDK queue index. */
-	uint16_t                  port_id;          /* Device port identifier. */
 	struct zxdh_virtnet_stats      stats;
 	const struct rte_memzone *mz;               /* mem zone to populate RX ring. */
-
-	/* dummy mbuf, for wraparound when processing RX ring. */
-	struct rte_mbuf           fake_mbuf;
+	uint64_t offloads;
+	uint16_t                  queue_id;         /* DPDK queue index. */
+	uint16_t                  port_id;          /* Device port identifier. */
 };
 
 struct __rte_cache_aligned zxdh_virtnet_tx {
@@ -75,5 +68,6 @@ struct __rte_cache_aligned zxdh_virtnet_tx {
 uint16_t zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
 uint16_t zxdh_xmit_pkts_prepare(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
 uint16_t zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint16_t nb_pkts);
 
 #endif  /* ZXDH_RXTX_H */
-- 
2.27.0

[-- Attachment #1.1.2: Type: text/html , Size: 39105 bytes --]

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox