DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH dpdk 1/2] rib: rename nxt flag parameter to mode
From: Robin Jarry @ 2026-05-18  9:59 UTC (permalink / raw)
  To: dev, Vladimir Medvedkin
In-Reply-To: <20260518095900.29655-6-rjarry@redhat.com>

The flag parameter in rte_rib_get_nxt() and rte_rib6_get_nxt() is a
plain int used as a typed enumeration. Give the anonymous enums proper
names (rte_rib_nxt_mode, rte_rib6_nxt_mode) and rename the parameter
from "flag" to "mode" to reflect its purpose.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 lib/rib/rte_rib.c  |  4 ++--
 lib/rib/rte_rib.h  | 12 ++++++------
 lib/rib/rte_rib6.c |  4 ++--
 lib/rib/rte_rib6.h | 14 +++++++-------
 4 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/lib/rib/rte_rib.c b/lib/rib/rte_rib.c
index 046db131ca00..89061829a23c 100644
--- a/lib/rib/rte_rib.c
+++ b/lib/rib/rte_rib.c
@@ -175,7 +175,7 @@ rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth)
 RTE_EXPORT_SYMBOL(rte_rib_get_nxt)
 struct rte_rib_node *
 rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip,
-	uint8_t depth, struct rte_rib_node *last, int flag)
+	uint8_t depth, struct rte_rib_node *last, enum rte_rib_nxt_mode mode)
 {
 	struct rte_rib_node *tmp, *prev = NULL;
 
@@ -205,7 +205,7 @@ rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip,
 				(is_covered(tmp->ip, ip, depth) &&
 				(tmp->depth > depth))) {
 			prev = tmp;
-			if (flag == RTE_RIB_GET_NXT_COVER)
+			if (mode == RTE_RIB_GET_NXT_COVER)
 				return prev;
 		}
 		tmp = (tmp->left) ? tmp->left : tmp->right;
diff --git a/lib/rib/rte_rib.h b/lib/rib/rte_rib.h
index c325b3e3618c..0fabfb2a41a6 100644
--- a/lib/rib/rte_rib.h
+++ b/lib/rib/rte_rib.h
@@ -26,11 +26,11 @@ extern "C" {
 /**
  * rte_rib_get_nxt() flags
  */
-enum {
-	/** flag to get all subroutes in a RIB tree */
+enum rte_rib_nxt_mode {
+	/** get all subroutes in a RIB tree, excluding any exact match top-level route */
 	RTE_RIB_GET_NXT_ALL,
-	/** flag to get first matched subroutes in a RIB tree */
-	RTE_RIB_GET_NXT_COVER
+	/** get first matched subroutes in a RIB tree, excluding any exact match top-level route */
+	RTE_RIB_GET_NXT_COVER,
 };
 
 struct rte_rib;
@@ -120,7 +120,7 @@ rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth);
  *   pointer to the last returned prefix to get next prefix
  *   or
  *   NULL to get first more specific prefix
- * @param flag
+ * @param mode
  *  -RTE_RIB_GET_NXT_ALL
  *   get all prefixes from subtrie
  *  -RTE_RIB_GET_NXT_COVER
@@ -131,7 +131,7 @@ rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth);
  */
 struct rte_rib_node *
 rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip, uint8_t depth,
-	struct rte_rib_node *last, int flag);
+	struct rte_rib_node *last, enum rte_rib_nxt_mode mode);
 
 /**
  * Remove prefix from the RIB
diff --git a/lib/rib/rte_rib6.c b/lib/rib/rte_rib6.c
index ec8ff68e8700..c23881f6247e 100644
--- a/lib/rib/rte_rib6.c
+++ b/lib/rib/rte_rib6.c
@@ -195,7 +195,7 @@ RTE_EXPORT_SYMBOL(rte_rib6_get_nxt)
 struct rte_rib6_node *
 rte_rib6_get_nxt(struct rte_rib6 *rib,
 	const struct rte_ipv6_addr *ip,
-	uint8_t depth, struct rte_rib6_node *last, int flag)
+	uint8_t depth, struct rte_rib6_node *last, enum rte_rib6_nxt_mode mode)
 {
 	struct rte_rib6_node *tmp, *prev = NULL;
 	struct rte_ipv6_addr tmp_ip;
@@ -229,7 +229,7 @@ rte_rib6_get_nxt(struct rte_rib6 *rib,
 				(rte_ipv6_addr_eq_prefix(&tmp->ip, &tmp_ip, depth) &&
 				(tmp->depth > depth))) {
 			prev = tmp;
-			if (flag == RTE_RIB6_GET_NXT_COVER)
+			if (mode == RTE_RIB6_GET_NXT_COVER)
 				return prev;
 		}
 		tmp = (tmp->left != NULL) ? tmp->left : tmp->right;
diff --git a/lib/rib/rte_rib6.h b/lib/rib/rte_rib6.h
index d66d9e7c07c6..ed2761492bc8 100644
--- a/lib/rib/rte_rib6.h
+++ b/lib/rib/rte_rib6.h
@@ -25,13 +25,13 @@ extern "C" {
 #define RTE_RIB6_IPV6_ADDR_SIZE (RTE_DEPRECATED(RTE_RIB6_IPV6_ADDR_SIZE) RTE_IPV6_ADDR_SIZE)
 
 /**
- * rte_rib6_get_nxt() flags
+ * rte_rib6_get_nxt() mode
  */
-enum {
-	/** flag to get all subroutes in a RIB tree */
+enum rte_rib6_nxt_mode {
+	/** get all subroutes in a RIB tree, excluding any exact match top-level route */
 	RTE_RIB6_GET_NXT_ALL,
-	/** flag to get first matched subroutes in a RIB tree */
-	RTE_RIB6_GET_NXT_COVER
+	/** get first matched subroutes in a RIB tree, excluding any exact match top-level route */
+	RTE_RIB6_GET_NXT_COVER,
 };
 
 struct rte_rib6;
@@ -179,7 +179,7 @@ rte_rib6_lookup_exact(struct rte_rib6 *rib,
  *   pointer to the last returned prefix to get next prefix
  *   or
  *   NULL to get first more specific prefix
- * @param flag
+ * @param mode
  *  -RTE_RIB6_GET_NXT_ALL
  *   get all prefixes from subtrie
  *  -RTE_RIB6_GET_NXT_COVER
@@ -191,7 +191,7 @@ rte_rib6_lookup_exact(struct rte_rib6 *rib,
 struct rte_rib6_node *
 rte_rib6_get_nxt(struct rte_rib6 *rib,
 	const struct rte_ipv6_addr *ip,
-	uint8_t depth, struct rte_rib6_node *last, int flag);
+	uint8_t depth, struct rte_rib6_node *last, enum rte_rib6_nxt_mode mode);
 
 /**
  * Remove prefix from the RIB
-- 
2.54.0


^ permalink raw reply related

* [PATCH dpdk 2/2] rib: add mode to include top-level route in traversal
From: Robin Jarry @ 2026-05-18  9:59 UTC (permalink / raw)
  To: dev, Vladimir Medvedkin
In-Reply-To: <20260518095900.29655-6-rjarry@redhat.com>

rte_rib_get_nxt() and rte_rib6_get_nxt() skip the exact match
top-level route when iterating subroutes. This is the expected behavior
in most cases but some users need the full subtree including the root.

Add a RTE_RIB_GET_NXT_ALL_TOP (and RTE_RIB6_GET_NXT_ALL_TOP) mode
that uses >= instead of > when comparing depths so that the top-level
route is returned as well.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 app/test/test_rib.c  | 16 ++++++++++++++++
 app/test/test_rib6.c | 16 ++++++++++++++++
 lib/rib/rte_rib.c    | 12 ++++++++++--
 lib/rib/rte_rib.h    |  4 ++++
 lib/rib/rte_rib6.c   | 12 ++++++++++--
 lib/rib/rte_rib6.h   |  4 ++++
 6 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/app/test/test_rib.c b/app/test/test_rib.c
index a4a683140df3..f56490e67ec0 100644
--- a/app/test/test_rib.c
+++ b/app/test/test_rib.c
@@ -300,6 +300,7 @@ test_tree_traversal(void)
 	uint32_t ip1 = RTE_IPV4(10, 10, 10, 0);
 	uint32_t ip2 = RTE_IPV4(10, 10, 130, 80);
 	uint8_t depth = 30;
+	unsigned int num;
 
 	config.max_nodes = MAX_RULES;
 	config.ext_sz = 0;
@@ -313,11 +314,26 @@ test_tree_traversal(void)
 	node = rte_rib_insert(rib, ip2, depth);
 	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
 
+	node = rte_rib_insert(rib, 0, 0);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert default rule\n");
+
 	node = NULL;
 	node = rte_rib_get_nxt(rib, RTE_IPV4(10, 10, 130, 0), 24, node,
 			RTE_RIB_GET_NXT_ALL);
 	RTE_TEST_ASSERT(node != NULL, "Failed to get rib_node\n");
 
+	num = 0;
+	node = NULL;
+	while ((node = rte_rib_get_nxt(rib, 0, 0, node, RTE_RIB_GET_NXT_ALL)) != NULL)
+		num++;
+	RTE_TEST_ASSERT(num == 2, "Invalid number of routes\n");
+
+	num = 0;
+	node = NULL;
+	while ((node = rte_rib_get_nxt(rib, 0, 0, node, RTE_RIB_GET_NXT_ALL_TOP)) != NULL)
+		num++;
+	RTE_TEST_ASSERT(num == 3, "Default route not returned by rte_rib_get_nxt\n");
+
 	rte_rib_free(rib);
 
 	return TEST_SUCCESS;
diff --git a/app/test/test_rib6.c b/app/test/test_rib6.c
index 0295a9640cfa..542c955eee8c 100644
--- a/app/test/test_rib6.c
+++ b/app/test/test_rib6.c
@@ -300,7 +300,9 @@ test_tree_traversal(void)
 	struct rte_ipv6_addr ip = RTE_IPV6(0x0a00, 0x0282, 0, 0, 0, 0, 0, 0);
 	struct rte_ipv6_addr ip1 = RTE_IPV6(0x0a00, 0x0200, 0, 0, 0, 0, 0, 0);
 	struct rte_ipv6_addr ip2 = RTE_IPV6(0x0a00, 0x0282, 0, 0, 0, 0, 0, 0x0050);
+	struct rte_ipv6_addr unspec = RTE_IPV6(0, 0, 0, 0, 0, 0, 0, 0);
 	uint8_t depth = 126;
+	unsigned int num;
 
 	config.max_nodes = MAX_RULES;
 	config.ext_sz = 0;
@@ -312,11 +314,25 @@ test_tree_traversal(void)
 	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
 	node = rte_rib6_insert(rib, &ip2, depth);
 	RTE_TEST_ASSERT(node != NULL, "Failed to insert rule\n");
+	node = rte_rib6_insert(rib, &unspec, 0);
+	RTE_TEST_ASSERT(node != NULL, "Failed to insert default route\n");
 
 	node = NULL;
 	node = rte_rib6_get_nxt(rib, &ip, 32, node, RTE_RIB6_GET_NXT_ALL);
 	RTE_TEST_ASSERT(node != NULL, "Failed to get rib_node\n");
 
+	num = 0;
+	node = NULL;
+	while ((node = rte_rib6_get_nxt(rib, 0, 0, node, RTE_RIB6_GET_NXT_ALL)) != NULL)
+		num++;
+	RTE_TEST_ASSERT(num == 2, "Invalid number of routes\n");
+
+	num = 0;
+	node = NULL;
+	while ((node = rte_rib6_get_nxt(rib, 0, 0, node, RTE_RIB6_GET_NXT_ALL_TOP)) != NULL)
+		num++;
+	RTE_TEST_ASSERT(num == 3, "Default route not returned by rte_rib6_get_nxt\n");
+
 	rte_rib6_free(rib);
 
 	return TEST_SUCCESS;
diff --git a/lib/rib/rte_rib.c b/lib/rib/rte_rib.c
index 89061829a23c..a3c287e215ec 100644
--- a/lib/rib/rte_rib.c
+++ b/lib/rib/rte_rib.c
@@ -167,6 +167,14 @@ rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth)
 	return __rib_lookup_exact(rib, ip, depth);
 }
 
+static bool
+depth_match(struct rte_rib_node *node, uint8_t depth, int flag)
+{
+	if (flag == RTE_RIB_GET_NXT_ALL_TOP)
+		return node->depth >= depth;
+	return node->depth > depth;
+}
+
 /*
  *  Traverses on subtree and retrieves more specific routes
  *  for a given in args ip/depth prefix
@@ -195,7 +203,7 @@ rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip,
 			tmp = tmp->parent;
 			if (is_valid_node(tmp) &&
 					(is_covered(tmp->ip, ip, depth) &&
-					(tmp->depth > depth)))
+					(depth_match(tmp, depth, mode))))
 				return tmp;
 		}
 		tmp = (tmp->parent) ? tmp->parent->right : NULL;
@@ -203,7 +211,7 @@ rte_rib_get_nxt(struct rte_rib *rib, uint32_t ip,
 	while (tmp) {
 		if (is_valid_node(tmp) &&
 				(is_covered(tmp->ip, ip, depth) &&
-				(tmp->depth > depth))) {
+				(depth_match(tmp, depth, mode)))) {
 			prev = tmp;
 			if (mode == RTE_RIB_GET_NXT_COVER)
 				return prev;
diff --git a/lib/rib/rte_rib.h b/lib/rib/rte_rib.h
index 0fabfb2a41a6..5e72fa9273e2 100644
--- a/lib/rib/rte_rib.h
+++ b/lib/rib/rte_rib.h
@@ -31,6 +31,8 @@ enum rte_rib_nxt_mode {
 	RTE_RIB_GET_NXT_ALL,
 	/** get first matched subroutes in a RIB tree, excluding any exact match top-level route */
 	RTE_RIB_GET_NXT_COVER,
+	/** get all subroutes in a RIB tree, including the exact match top-level route, if any */
+	RTE_RIB_GET_NXT_ALL_TOP,
 };
 
 struct rte_rib;
@@ -125,6 +127,8 @@ rte_rib_lookup_exact(struct rte_rib *rib, uint32_t ip, uint8_t depth);
  *   get all prefixes from subtrie
  *  -RTE_RIB_GET_NXT_COVER
  *   get only first more specific prefix even if it have more specifics
+ *  -RTE_RIB_GET_NXT_ALL_TOP
+ *   get the top-level exact matching prefix, if any
  * @return
  *  pointer to the next more specific prefix
  *  NULL if there is no prefixes left
diff --git a/lib/rib/rte_rib6.c b/lib/rib/rte_rib6.c
index c23881f6247e..5114fb522e3e 100644
--- a/lib/rib/rte_rib6.c
+++ b/lib/rib/rte_rib6.c
@@ -186,6 +186,14 @@ rte_rib6_lookup_exact(struct rte_rib6 *rib,
 	return NULL;
 }
 
+static bool
+depth_match(struct rte_rib6_node *node, uint8_t depth, enum rte_rib6_nxt_mode mode)
+{
+	if (mode == RTE_RIB6_GET_NXT_ALL_TOP)
+		return node->depth >= depth;
+	return node->depth > depth;
+}
+
 /*
  *  Traverses on subtree and retrieves more specific routes
  *  for a given in args ip/depth prefix
@@ -219,7 +227,7 @@ rte_rib6_get_nxt(struct rte_rib6 *rib,
 			tmp = tmp->parent;
 			if (is_valid_node(tmp) &&
 					(rte_ipv6_addr_eq_prefix(&tmp->ip, &tmp_ip, depth) &&
-					(tmp->depth > depth)))
+					(depth_match(tmp, depth, mode))))
 				return tmp;
 		}
 		tmp = (tmp->parent != NULL) ? tmp->parent->right : NULL;
@@ -227,7 +235,7 @@ rte_rib6_get_nxt(struct rte_rib6 *rib,
 	while (tmp) {
 		if (is_valid_node(tmp) &&
 				(rte_ipv6_addr_eq_prefix(&tmp->ip, &tmp_ip, depth) &&
-				(tmp->depth > depth))) {
+				(depth_match(tmp, depth, mode)))) {
 			prev = tmp;
 			if (mode == RTE_RIB6_GET_NXT_COVER)
 				return prev;
diff --git a/lib/rib/rte_rib6.h b/lib/rib/rte_rib6.h
index ed2761492bc8..1d8b56b89b13 100644
--- a/lib/rib/rte_rib6.h
+++ b/lib/rib/rte_rib6.h
@@ -32,6 +32,8 @@ enum rte_rib6_nxt_mode {
 	RTE_RIB6_GET_NXT_ALL,
 	/** get first matched subroutes in a RIB tree, excluding any exact match top-level route */
 	RTE_RIB6_GET_NXT_COVER,
+	/** get all subroutes in a RIB tree, including the exact match top-level route, if any */
+	RTE_RIB6_GET_NXT_ALL_TOP,
 };
 
 struct rte_rib6;
@@ -184,6 +186,8 @@ rte_rib6_lookup_exact(struct rte_rib6 *rib,
  *   get all prefixes from subtrie
  *  -RTE_RIB6_GET_NXT_COVER
  *   get only first more specific prefix even if it have more specifics
+ *  -RTE_RIB6_GET_NXT_ALL_TOP
+ *   get the top-level exact matching prefix, if any
  * @return
  *  pointer to the next more specific prefix
  *  NULL if there is no prefixes left
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH 0/6] add hardening checks to cmdline and cfgfile libs
From: Thomas Monjalon @ 2026-05-18 10:30 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: dev
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>

07/05/2026 16:59, Bruce Richardson:
> Using AI tools to review the cmdline and cfgfile libraries throws up a
> couple of places in the libraries where additional hardening could help
> prevent future issues. A number of these are purely defensive, e.g.
> adding NULL checks to input parameters where a well-behaved app should
> never call the function with a NULL value, and so those are not
> explicitly marked for backport.
> 
> Bruce Richardson (6):
>   cfgfile: add null checks to public APIs
>   cfgfile: prevent issues with overflow on resize
>   cmdline: harden parser result buffer handling
>   cmdline: add explicit help function for bool type
>   cmdline: guard zero-size destination buffers
>   cmdline: add null checks for invalid input

Applied, thanks.




^ permalink raw reply

* [PATCH v2] bus/uacce: support no-iommu mode
From: Chengwen Feng @ 2026-05-18 10:58 UTC (permalink / raw)
  To: thomas; +Cc: dev, liuyonglong, qianweili
In-Reply-To: <20260422075326.34206-1-fengchengwen@huawei.com>

The uacce bus originally only supports devices with SVA capability.
This patch extends the uacce bus to support no-iommu mode for devices
without or not enabling IOMMU/SVA.

For no-iommu mode UACCE devices:
- The device api name is suffixed with _noiommu
- The device flags bit1 was set (UACCE_DEV_FLAG_NOIOMMU)

To support such devices, DPDK device drivers can mark
RTE_UACCE_DRV_SUPPORT_NOIOMMU_MODE in drv_flags to declare capability
of working with no-iommu devices.

This commit also fixes typo UACCE_DEV_FLGA_SVA -> UACCE_DEV_FLAG_SVA.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

---
v2: Fix AI review comment

---
 doc/guides/rel_notes/release_26_07.rst |  4 ++++
 drivers/bus/uacce/bus_uacce_driver.h   |  8 +++++++
 drivers/bus/uacce/uacce.c              | 31 ++++++++++++++++++++------
 3 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index f012d47a4b..1f53e1253e 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -63,6 +63,10 @@ New Features
     ``rte_eal_init`` and the application is responsible for probing each device,
   * ``--auto-probing`` enables the initial bus probing, which is the current default behavior.
 
+* **UACCE bus supports no-iommu mode.**
+
+  Support no-iommu mode for devices without or not enabling IOMMU/SVA.
+
 
 Removed Items
 -------------
diff --git a/drivers/bus/uacce/bus_uacce_driver.h b/drivers/bus/uacce/bus_uacce_driver.h
index c7445778a6..d2ff8a2d53 100644
--- a/drivers/bus/uacce/bus_uacce_driver.h
+++ b/drivers/bus/uacce/bus_uacce_driver.h
@@ -15,6 +15,7 @@
 #include <stdlib.h>
 #include <linux/types.h>
 
+#include <rte_bitops.h>
 #include <rte_compat.h>
 #include <rte_devargs.h>
 #include <dev_driver.h>
@@ -28,6 +29,11 @@ extern "C" {
 #define RTE_UACCE_ALGS_NAME_SIZE	384
 #define RTE_UACCE_ATTR_MAX_SIZE		384
 
+/* UACCE device flag of SVA. */
+#define UACCE_DEV_FLAG_SVA	RTE_BIT32(0)
+/* UACCE device flag of NOIOMMU. */
+#define UACCE_DEV_FLAG_NOIOMMU	RTE_BIT32(1)
+
 /*
  * Definition for queue file region type.
  */
@@ -106,6 +112,8 @@ struct rte_uacce_driver {
 
 /** Device driver supports forward compatibility device */
 #define RTE_UACCE_DRV_FORWARD_COMPATIBILITY_DEV	0x1
+/** Device driver supports NO-IOMMU mode */
+#define RTE_UACCE_DRV_SUPPORT_NOIOMMU_MODE	0x2
 
 /**
  * Get available queue number.
diff --git a/drivers/bus/uacce/uacce.c b/drivers/bus/uacce/uacce.c
index ade2452ad5..70fb5c730a 100644
--- a/drivers/bus/uacce/uacce.c
+++ b/drivers/bus/uacce/uacce.c
@@ -15,7 +15,6 @@
 #include <sys/types.h>
 
 #include <eal_export.h>
-#include <rte_bitops.h>
 #include <rte_common.h>
 #include <rte_devargs.h>
 #include <rte_eal_paging.h>
@@ -28,9 +27,6 @@
 
 #define UACCE_BUS_CLASS_PATH	"/sys/class/uacce"
 
-/* UACCE device flag of SVA. */
-#define UACCE_DEV_FLGA_SVA	RTE_BIT32(0)
-
 /* Support -a uacce:device-name when start DPDK application. */
 #define UACCE_DEV_PREFIX	"uacce:"
 
@@ -151,9 +147,25 @@ uacce_read_attr_u32(const char *dev_root, const char *attr, uint32_t *val)
 static int
 uacce_read_api(struct rte_uacce_device *dev)
 {
-	int ret = uacce_read_attr(dev->dev_root, "api", dev->api, sizeof(dev->api) - 1);
+#define NOIOMMU_SUFFIX		"_noiommu"
+	size_t api_len, sub_len;
+	int ret;
+
+	ret = uacce_read_attr(dev->dev_root, "api", dev->api, sizeof(dev->api) - 1);
 	if (ret < 0)
 		return ret;
+
+	/*
+	 * If the device is in no-iommu mode, the UACCE_DEV_FLAG_NOIOMMU flag
+	 * in its "flags" will be set, and its "api" will contain the suffix
+	 * _noiommu. To facilitate matching with the driver, the suffix is
+	 * removed here.
+	 */
+	api_len = strlen(dev->api);
+	sub_len = strlen(NOIOMMU_SUFFIX);
+	if (api_len > sub_len && strcmp(dev->api + api_len - sub_len, NOIOMMU_SUFFIX) == 0)
+		dev->api[api_len - sub_len] = 0;
+
 	return 0;
 }
 
@@ -196,8 +208,8 @@ uacce_read_qfrt_sz(struct rte_uacce_device *dev)
 static int
 uacce_verify(struct rte_uacce_device *dev)
 {
-	if (!(dev->flags & UACCE_DEV_FLGA_SVA)) {
-		UACCE_BUS_WARN("device %s don't support SVA, skip it!", dev->name);
+	if (!(dev->flags & (UACCE_DEV_FLAG_SVA | UACCE_DEV_FLAG_NOIOMMU))) {
+		UACCE_BUS_WARN("device %s don't support SVA or NOIOMMU, skip it!", dev->name);
 		return 1; /* >0 will skip this device. */
 	}
 
@@ -333,11 +345,16 @@ static bool
 uacce_match(const struct rte_uacce_driver *dr, struct rte_uacce_device *dev)
 {
 	bool forward_compat = !!(dr->drv_flags & RTE_UACCE_DRV_FORWARD_COMPATIBILITY_DEV);
+	bool drv_support_noiommu = !!(dr->drv_flags & RTE_UACCE_DRV_SUPPORT_NOIOMMU_MODE);
+	bool dev_in_noiommu = !!(dev->flags & UACCE_DEV_FLAG_NOIOMMU);
 	uint32_t api_ver = uacce_calc_api_ver(dev->api, NULL);
 	const struct rte_uacce_id *id_table;
 	const char *map;
 	uint32_t len;
 
+	if (dev_in_noiommu && !drv_support_noiommu)
+		return false;
+
 	for (id_table = dr->id_table; id_table->dev_api != NULL; id_table++) {
 		if (!uacce_match_api(dev, forward_compat, id_table))
 			continue;
-- 
2.17.1


^ permalink raw reply related

* Re: [PATCH] devtools: fix regex matching literal plus in patches
From: David Marchand @ 2026-05-18 11:55 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev, Haiyue Wang, stable
In-Reply-To: <20260505140352.3995590-1-thomas@monjalon.net>

On Tue, 5 May 2026 at 16:04, Thomas Monjalon <thomas@monjalon.net> wrote:
>
> In Extended Regular Expressions (ERE) as used in awk,
> '+' is a quantifier, not a literal character.
> The pattern /^+/ matches the start of any line
> instead of only lines beginning with a literal '+'.
> As a result, check_experimental_tags and check_internal_tags
> were matching context and removed lines in diffs, causing false positives.
>
> Use [+] character class to unambiguously match a literal '+'.
>
> Fixes: cfe3aeb170b2 ("remove experimental tags from all symbol definitions")
> Fixes: fba5af82adc8 ("eal: add internal ABI tag definition")
> Cc: stable@dpdk.org
>
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>

Reviewed-by: David Marchand <david.marchand@redhat.com>


-- 
David Marchand


^ permalink raw reply

* Re: [PATCH] pipeline: fix build with sanitizers or debug options
From: Thomas Monjalon @ 2026-05-18 13:05 UTC (permalink / raw)
  To: David Marchand, Dumitrescu, Cristian; +Cc: dev@dpdk.org, stable
In-Reply-To: <DS0PR11MB74428B95BC221BC7F5FBE7B5EB4CA@DS0PR11MB7442.namprd11.prod.outlook.com>

> > Similar to commit 84f5ac9418ea ("pipeline: fix build with ASan").
> > 
> > Here we are again. Depending on options (like debug, or ASan, or UBSan),
> > compilation can fail because of dumb construct like CHECK(0, XXX).
> > Dumb, because such an expression macro expands as: if (0) return -XXX;
> > 
> > ../lib/pipeline/rte_swx_pipeline.c: In function ‘instr_movh_translate’:
> > ../lib/pipeline/rte_swx_pipeline.c:3461:1: error: control reaches end of
> > 	non-void function [-Werror=return-type]
> >  3461 | }
> >       | ^
> > 
> > Remove any such call when at the end of functions, using a regexp:
> > %s/CHECK(0, \(.*\))\(;\n}\)/return -\1\2/
> > 
> > Cc: stable@dpdk.org
> > 
> > Signed-off-by: David Marchand <david.marchand@redhat.com>
> > ---
> 
> Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

Fixes: 2f8168eac37a ("pipeline: add instruction for IPv6 address upper half")
Fixes: cdaa937d3eaa ("pipeline: support selector table")
Fixes: c6b752cdf215 ("pipeline: introduce SWX extern instruction")

Applied, thanks.


Should we remove the other instances of "CHECK(0," ?



^ permalink raw reply

* Re: [PATCH] devtools: fix regex matching literal plus in patches
From: Thomas Monjalon @ 2026-05-18 13:18 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, Haiyue Wang, stable
In-Reply-To: <CAJFAV8wKu9SZKi7TmOMwfj1bNS=2QQ7qDP=kWMPbhFYi-EcKVg@mail.gmail.com>

18/05/2026 13:55, David Marchand:
> On Tue, 5 May 2026 at 16:04, Thomas Monjalon <thomas@monjalon.net> wrote:
> >
> > In Extended Regular Expressions (ERE) as used in awk,
> > '+' is a quantifier, not a literal character.
> > The pattern /^+/ matches the start of any line
> > instead of only lines beginning with a literal '+'.
> > As a result, check_experimental_tags and check_internal_tags
> > were matching context and removed lines in diffs, causing false positives.
> >
> > Use [+] character class to unambiguously match a literal '+'.
> >
> > Fixes: cfe3aeb170b2 ("remove experimental tags from all symbol definitions")
> > Fixes: fba5af82adc8 ("eal: add internal ABI tag definition")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> 
> Reviewed-by: David Marchand <david.marchand@redhat.com>

Applied




^ permalink raw reply

* [PATCH dpdk v5 0/5] Fix and improve VLAN/MPLS parsing in rte_net_get_ptype
From: Robin Jarry @ 2026-05-18 13:27 UTC (permalink / raw)
  To: dev
In-Reply-To: <20260422102814.645299-2-rjarry@redhat.com>

Revert commit 1f250674085a ("net: fix packet type for stacked VLAN")
which introduced a regression causing single VLAN frames to be
misidentified as QinQ due to incorrect use of |= on the ptype.

Replace the separate VLAN and QinQ code paths with a single loop
that handles arbitrarily stacked VLAN tags. Fix the MPLS path to
use the bottom of stack bit instead of a hardcoded label limit,
and parse the L3 protocol of the MPLS payload by inspecting the
first nibble.

Add unit tests for rte_net_get_ptype covering Ethernet, VLAN, QinQ,
MPLS, IPv4/IPv6, and truncated packet scenarios.

v5:

* Split the series in multiple patches.
* Revert the commit that introduced the bug.
* Add support for stacked VLAN/QINQ as suggested by David.
* Fix MPLS bottom of stack detection. Try to guess MPLS payload.
* Add more test cases.

v4:

* changed the approach again. Only set VLAN or QINQ ptype for the first
  encountered vlan/qinq ether type.
* unit tests not changed

v3:

* changed the approach: initialize pkt_type=0 and only set it to
  RTE_PTYPE_L2_ETHER if neither of VLAN nor QINQ matched.
* extended the unit tests to check for header lengths and added ipv6 / tcp
  cases.

v2: added new ptype tests

v3:

* changed the approach: initialize pkt_type=0 and only set it to
  RTE_PTYPE_L2_ETHER if neither of VLAN nor QINQ matched.
* extended the unit tests to check for header lengths and added ipv6 / tcp
  cases.

v2: added new ptype tests

Robin Jarry (5):
  Revert "net: fix packet type for stacked VLAN"
  net: support multiple stacked VLAN tags
  net: add unit tests for rte_net_get_ptype
  net: parse L3 protocol after MPLS labels
  net: add truncated packet tests for rte_net_get_ptype

 app/test/meson.build      |   1 +
 app/test/test_net_ptype.c | 326 ++++++++++++++++++++++++++++++++++++++
 lib/net/rte_net.c         |  72 +++++----
 3 files changed, 369 insertions(+), 30 deletions(-)
 create mode 100644 app/test/test_net_ptype.c

-- 
2.54.0


^ permalink raw reply

* [PATCH dpdk v5 3/5] net: add unit tests for rte_net_get_ptype
From: Robin Jarry @ 2026-05-18 13:27 UTC (permalink / raw)
  To: dev
In-Reply-To: <20260518132712.70913-8-rjarry@redhat.com>

There is no test coverage for rte_net_get_ptype. Add a test suite that
exercises plain Ethernet, single VLAN, QinQ, double VLAN, IPv4 with
options, IPv6, TCP and UDP combinations.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 app/test/meson.build      |   1 +
 app/test/test_net_ptype.c | 231 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 232 insertions(+)
 create mode 100644 app/test/test_net_ptype.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 7d458f9c079a..9f4afb040a46 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -135,6 +135,7 @@ source_file_deps = {
     'test_mp_secondary.c': ['hash'],
     'test_net_ether.c': ['net'],
     'test_net_ip6.c': ['net'],
+    'test_net_ptype.c': ['net'],
     'test_pcapng.c': ['net_null', 'net', 'ethdev', 'pcapng', 'bus_vdev'],
     'test_pdcp.c': ['eventdev', 'pdcp', 'net', 'timer', 'security'],
     'test_pdump.c': ['pdump'] + sample_packet_forward_deps,
diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c
new file mode 100644
index 000000000000..bfe85da13543
--- /dev/null
+++ b/app/test/test_net_ptype.c
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2026 Red Hat, Inc.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_mbuf.h>
+#include <rte_net.h>
+
+#include <rte_test.h>
+#include "test.h"
+
+#define MEMPOOL_CACHE_SIZE 0
+#define MBUF_DATA_SIZE 256
+#define NB_MBUF 128
+
+/* Ether()/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_ether_ipv4_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+	0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+	0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+	0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+	0x89, 0x6f, 0x78,
+};
+
+/* Ether()/IP()/TCP() */
+static const uint8_t pkt_ether_ipv4_tcp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+	0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
+	0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+	0x00, 0x01, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+	0x20, 0x00, 0x91, 0x7c, 0x00, 0x00,
+};
+
+/* Ether()/IPv6()/UDP()/Raw('x') */
+static const uint8_t pkt_ether_ipv6_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00,
+	0x00, 0x00, 0x00, 0x09, 0x11, 0x40, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x35,
+	0x00, 0x35, 0x00, 0x09, 0x87, 0x70, 0x78,
+};
+
+/* Ether()/IPv6()/TCP() */
+static const uint8_t pkt_ether_ipv6_tcp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00,
+	0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+	0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+	0x00, 0x00,
+};
+
+/* Ether()/IP(options='\x00')/UDP()/Raw('x') -- ihl=6 */
+static const uint8_t pkt_ether_ipv4_opts_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x46, 0x00,
+	0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+	0x7b, 0xc9, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35,
+	0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_vlan_ipv4_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+	0x08, 0x00, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+	0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+	0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+	0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IPv6()/TCP() */
+static const uint8_t pkt_vlan_ipv6_tcp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+	0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x14,
+	0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+	0x20, 0x00, 0x8f, 0x7d, 0x00, 0x00,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_qinq_ipv4_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+	0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+	0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+	0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+	0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+	0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_vlan_vlan_ipv4_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+	0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+	0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+	0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+	0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+	0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IPv6()/TCP() */
+static const uint8_t pkt_qinq_ipv6_tcp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+	0x81, 0x00, 0x00, 0x2b, 0x86, 0xdd, 0x60, 0x00,
+	0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+	0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+	0x00, 0x00,
+};
+
+static int
+test_get_ptype(struct rte_mempool *pool, const char *name,
+	       const uint8_t *pktdata, size_t len, uint32_t expected_ptype,
+	       uint8_t expected_l2_len, uint16_t expected_l3_len,
+	       uint8_t expected_l4_len)
+{
+	struct rte_net_hdr_lens hdr_lens;
+	struct rte_mbuf *m;
+	uint32_t ptype;
+	char *data;
+
+	RTE_LOG(INFO, EAL, "%s: %s\n", __func__, name);
+
+	m = rte_pktmbuf_alloc(pool);
+	RTE_TEST_ASSERT_NOT_NULL(m, "cannot allocate mbuf");
+
+	data = rte_pktmbuf_append(m, len);
+	if (data == NULL) {
+		rte_pktmbuf_free(m);
+		RTE_TEST_ASSERT_NOT_NULL(data, "cannot append data");
+	}
+
+	memcpy(data, pktdata, len);
+
+	memset(&hdr_lens, 0, sizeof(hdr_lens));
+	ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
+
+	rte_pktmbuf_free(m);
+
+	RTE_TEST_ASSERT_EQUAL(ptype, expected_ptype,
+		"unexpected ptype: got 0x%x, expected 0x%x",
+		ptype, expected_ptype);
+	RTE_TEST_ASSERT_EQUAL(hdr_lens.l2_len, expected_l2_len,
+		"unexpected l2_len: got %u, expected %u",
+		hdr_lens.l2_len, expected_l2_len);
+	RTE_TEST_ASSERT_EQUAL(hdr_lens.l3_len, expected_l3_len,
+		"unexpected l3_len: got %u, expected %u",
+		hdr_lens.l3_len, expected_l3_len);
+	RTE_TEST_ASSERT_EQUAL(hdr_lens.l4_len, expected_l4_len,
+		"unexpected l4_len: got %u, expected %u",
+		hdr_lens.l4_len, expected_l4_len);
+
+	return 0;
+}
+
+#define test_case(pool, pkt, expected_ptype, l2, l3, l4) \
+	test_get_ptype(pool, #pkt, pkt, sizeof(pkt), expected_ptype, l2, l3, l4)
+
+static int
+test_net_ptype(void)
+{
+	struct rte_mempool *pool;
+	int ret = 0;
+
+	pool = rte_pktmbuf_pool_create("test_ptype_mbuf_pool",
+			NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE,
+			SOCKET_ID_ANY);
+	RTE_TEST_ASSERT_NOT_NULL(pool, "cannot allocate mbuf pool");
+
+	ret |= test_case(pool, pkt_ether_ipv4_udp,
+			 RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+			 14, 20, 8);
+	ret |= test_case(pool, pkt_ether_ipv4_tcp,
+			 RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_TCP,
+			 14, 20, 20);
+	ret |= test_case(pool, pkt_ether_ipv6_udp,
+			 RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP,
+			 14, 40, 8);
+	ret |= test_case(pool, pkt_ether_ipv6_tcp,
+			 RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+			 14, 40, 20);
+	ret |= test_case(pool, pkt_ether_ipv4_opts_udp,
+			 RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT | RTE_PTYPE_L4_UDP,
+			 14, 24, 8);
+	ret |= test_case(pool, pkt_vlan_ipv4_udp,
+			 RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+			 18, 20, 8);
+	ret |= test_case(pool, pkt_vlan_ipv6_tcp,
+			 RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+			 18, 40, 20);
+	ret |= test_case(pool, pkt_qinq_ipv4_udp,
+			 RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+			 22, 20, 8);
+	ret |= test_case(pool, pkt_vlan_vlan_ipv4_udp,
+			 RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+			 22, 20, 8);
+	ret |= test_case(pool, pkt_qinq_ipv6_tcp,
+			 RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+			 22, 40, 20);
+
+	rte_mempool_free(pool);
+
+	return ret;
+}
+
+REGISTER_FAST_TEST(net_ptype_autotest, NOHUGE_OK, ASAN_OK, test_net_ptype);
-- 
2.54.0


^ permalink raw reply related

* [PATCH dpdk v5 1/5] Revert "net: fix packet type for stacked VLAN"
From: Robin Jarry @ 2026-05-18 13:27 UTC (permalink / raw)
  To: dev, Gregory Etelson; +Cc: stable
In-Reply-To: <20260518132712.70913-8-rjarry@redhat.com>

This reverts commit 1f250674085aeb4ffd15ac2519a68efc04faf7ac.

rte_net_get_ptype() now uses |= to set the L2 ptype inside the VLAN
parsing loop. Since pkt_type is already initialized with
RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN (0x6)
results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single VLAN frames
to be misidentified as QinQ.

This was detected while testing DPDK 25.11.1 in grout. The net/tap
driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
L2 header length. With the wrong ptype, l2_len is set to 22 (ether
+ QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
header pointer by 4 bytes. The checksum is then computed on garbage
data, causing valid packets to be dropped.

Bugzilla ID: 1941
Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
Cc: stable@dpdk.org
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 lib/net/rte_net.c | 29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index 458b4814a9c9..c70b57fdc0f8 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -320,9 +320,6 @@ rte_net_skip_ip6_ext(uint16_t proto, const struct rte_mbuf *m, uint32_t *off,
 	return -1;
 }
 
-/* limit number of supported VLAN headers */
-#define RTE_NET_VLAN_MAX_DEPTH 8
-
 /* parse mbuf data to get packet type */
 RTE_EXPORT_SYMBOL(rte_net_get_ptype)
 uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
@@ -332,7 +329,7 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
 	const struct rte_ether_hdr *eh;
 	struct rte_ether_hdr eh_copy;
 	uint32_t pkt_type = RTE_PTYPE_L2_ETHER;
-	uint32_t off = 0, vlan_depth = 0;
+	uint32_t off = 0;
 	uint16_t proto;
 	int ret;
 
@@ -352,26 +349,30 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
 	if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
 		goto l3; /* fast path if packet is IPv4 */
 
-	while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
-	       proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
+	if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
 		const struct rte_vlan_hdr *vh;
 		struct rte_vlan_hdr vh_copy;
 
-		if (++vlan_depth > RTE_NET_VLAN_MAX_DEPTH)
-			return 0;
-		pkt_type |=
-			proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
-				 RTE_PTYPE_L2_ETHER_VLAN :
-				 RTE_PTYPE_L2_ETHER_QINQ;
+		pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
 		vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
 		if (unlikely(vh == NULL))
 			return pkt_type;
 		off += sizeof(*vh);
 		hdr_lens->l2_len += sizeof(*vh);
 		proto = vh->eth_proto;
-	}
+	} else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
+		const struct rte_vlan_hdr *vh;
+		struct rte_vlan_hdr vh_copy;
 
-	if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
+		pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
+		vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
+			&vh_copy);
+		if (unlikely(vh == NULL))
+			return pkt_type;
+		off += 2 * sizeof(*vh);
+		hdr_lens->l2_len += 2 * sizeof(*vh);
+		proto = vh->eth_proto;
+	} else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
 		(proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
 		unsigned int i;
 		const struct rte_mpls_hdr *mh;
-- 
2.54.0


^ permalink raw reply related

* [PATCH dpdk v5 2/5] net: support multiple stacked VLAN tags
From: Robin Jarry @ 2026-05-18 13:27 UTC (permalink / raw)
  To: dev; +Cc: David Marchand
In-Reply-To: <20260518132712.70913-8-rjarry@redhat.com>

The VLAN and QinQ code paths in rte_net_get_ptype handle at most two
tags with duplicated logic. Replace them with a single loop that
consumes all consecutive VLAN/QinQ headers regardless of depth.

Bugzilla ID: 1941
Suggested-by: David Marchand <david.marchand@redhat.com>
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 lib/net/rte_net.c | 35 ++++++++++++++++-------------------
 1 file changed, 16 insertions(+), 19 deletions(-)

diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index c70b57fdc0f8..d3cded961fb5 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -349,29 +349,26 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
 	if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
 		goto l3; /* fast path if packet is IPv4 */
 
-	if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
+	if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) ||
+		(proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ))) {
 		const struct rte_vlan_hdr *vh;
 		struct rte_vlan_hdr vh_copy;
 
-		pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
-		vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
-		if (unlikely(vh == NULL))
-			return pkt_type;
-		off += sizeof(*vh);
-		hdr_lens->l2_len += sizeof(*vh);
-		proto = vh->eth_proto;
-	} else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
-		const struct rte_vlan_hdr *vh;
-		struct rte_vlan_hdr vh_copy;
+		if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN))
+			pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
+		else
+			pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
+
+		do {
+			vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
+			if (unlikely(vh == NULL))
+				return pkt_type;
+			off += sizeof(*vh);
+			hdr_lens->l2_len += sizeof(*vh);
+			proto = vh->eth_proto;
+		} while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
+			proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
 
-		pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
-		vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
-			&vh_copy);
-		if (unlikely(vh == NULL))
-			return pkt_type;
-		off += 2 * sizeof(*vh);
-		hdr_lens->l2_len += 2 * sizeof(*vh);
-		proto = vh->eth_proto;
 	} else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
 		(proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
 		unsigned int i;
-- 
2.54.0


^ permalink raw reply related

* [PATCH dpdk v5 4/5] net: parse L3 protocol after MPLS labels
From: Robin Jarry @ 2026-05-18 13:27 UTC (permalink / raw)
  To: dev
In-Reply-To: <20260518132712.70913-8-rjarry@redhat.com>

rte_net_get_ptype stops at the MPLS layer and never identifies the L3
protocol of the payload. Also, the label parsing uses a fixed maximum of
5 headers instead of checking the bottom of stack bit.

Use the bottom of stack bit to consume all labels and inspect the first
nibble of the payload to determine if it is IPv4 or IPv6.

Add test cases to verify this works. Ensure that an unknown protocol
after MPLS (e.g. ARP) does not produce a bogus L3 type.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 app/test/test_net_ptype.c | 57 +++++++++++++++++++++++++++++++++++++++
 lib/net/rte_net.c         | 34 ++++++++++++++++-------
 2 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c
index bfe85da13543..cc7026077191 100644
--- a/app/test/test_net_ptype.c
+++ b/app/test/test_net_ptype.c
@@ -118,6 +118,51 @@ static const uint8_t pkt_vlan_vlan_ipv4_udp[] = {
 	0x89, 0x6f, 0x78,
 };
 
+/* Ether(type=MPLS)/MPLS(label=42,s=1)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_mpls_ipv4_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02,
+	0xa1, 0x40, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+	0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+	0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+	0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether(type=MPLS)/MPLS(label=42,s=0)/MPLS(label=43,s=1)/IPv6()/TCP() */
+static const uint8_t pkt_mpls2_ipv6_tcp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02,
+	0xa0, 0x40, 0x00, 0x02, 0xb1, 0x40, 0x60, 0x00,
+	0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+	0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+	0x00, 0x00,
+};
+
+/* Ether(type=MPLSM)/MPLS(label=42,s=1)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_mplsm_ipv4_udp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x48, 0x00, 0x02,
+	0xa1, 0x40, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+	0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+	0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+	0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether(type=MPLS)/MPLS(label=42,s=1)/ARP() -- unknown L3 after MPLS */
+static const uint8_t pkt_mpls_arp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02,
+	0xa1, 0x40, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7f, 0x00, 0x00, 0x02,
+};
+
 /* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IPv6()/TCP() */
 static const uint8_t pkt_qinq_ipv6_tcp[] = {
 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
@@ -222,6 +267,18 @@ test_net_ptype(void)
 	ret |= test_case(pool, pkt_qinq_ipv6_tcp,
 			 RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
 			 22, 40, 20);
+	ret |= test_case(pool, pkt_mpls_ipv4_udp,
+			 RTE_PTYPE_L2_ETHER_MPLS | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+			 18, 20, 8);
+	ret |= test_case(pool, pkt_mpls2_ipv6_tcp,
+			 RTE_PTYPE_L2_ETHER_MPLS | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+			 22, 40, 20);
+	ret |= test_case(pool, pkt_mplsm_ipv4_udp,
+			 RTE_PTYPE_L2_ETHER_MPLS | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+			 18, 20, 8);
+	ret |= test_case(pool, pkt_mpls_arp,
+			 RTE_PTYPE_L2_ETHER_MPLS,
+			 18, 0, 0);
 
 	rte_mempool_free(pool);
 
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index d3cded961fb5..3bb5fbc16d43 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -371,22 +371,36 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
 
 	} else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
 		(proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
-		unsigned int i;
 		const struct rte_mpls_hdr *mh;
 		struct rte_mpls_hdr mh_copy;
+		const uint8_t *nimble;
+		uint8_t nimble_copy;
 
-#define MAX_MPLS_HDR 5
-		for (i = 0; i < MAX_MPLS_HDR; i++) {
-			mh = rte_pktmbuf_read(m, off + (i * sizeof(*mh)),
-				sizeof(*mh), &mh_copy);
+		pkt_type = RTE_PTYPE_L2_ETHER_MPLS;
+
+		/* consume all labels until bottom of stack is reached */
+		do {
+			mh = rte_pktmbuf_read(m, off, sizeof(*mh), &mh_copy);
 			if (unlikely(mh == NULL))
 				return pkt_type;
-		}
-		if (i == MAX_MPLS_HDR)
+			off += sizeof(*mh);
+			hdr_lens->l2_len += sizeof(*mh);
+		} while (!mh->bs);
+
+		/* try to guess what is the payload based on the first 4 bits */
+		nimble = rte_pktmbuf_read(m, off, sizeof(*nimble), &nimble_copy);
+		if (nimble == NULL)
 			return pkt_type;
-		pkt_type = RTE_PTYPE_L2_ETHER_MPLS;
-		hdr_lens->l2_len += (sizeof(*mh) * i);
-		return pkt_type;
+		switch (*nimble & 0xf0) {
+		case 0x40:
+			proto = RTE_BE16(RTE_ETHER_TYPE_IPV4);
+			break;
+		case 0x60:
+			proto = RTE_BE16(RTE_ETHER_TYPE_IPV6);
+			break;
+		default:
+			return pkt_type;
+		}
 	}
 
 l3:
-- 
2.54.0


^ permalink raw reply related

* [PATCH dpdk v5 5/5] net: add truncated packet tests for rte_net_get_ptype
From: Robin Jarry @ 2026-05-18 13:27 UTC (permalink / raw)
  To: dev
In-Reply-To: <20260518132712.70913-8-rjarry@redhat.com>

Ensure rte_net_get_ptype handles truncated packets gracefully by
stopping at the last layer it can fully parse.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
 app/test/test_net_ptype.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c
index cc7026077191..332ace5dd929 100644
--- a/app/test/test_net_ptype.c
+++ b/app/test/test_net_ptype.c
@@ -163,6 +163,32 @@ static const uint8_t pkt_mpls_arp[] = {
 	0x00, 0x00, 0x7f, 0x00, 0x00, 0x02,
 };
 
+/* Ether()/Dot1Q() -- VLAN header truncated */
+static const uint8_t pkt_vlan_trunc[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+};
+
+/* Ether(type=MPLS) -- MPLS header truncated */
+static const uint8_t pkt_mpls_trunc[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02,
+};
+
+/* Ether(type=MPLS)/MPLS(label=42,s=1) -- no payload after label */
+static const uint8_t pkt_mpls_no_payload[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x88, 0x47, 0x00, 0x02,
+	0xa1, 0x40,
+};
+
+/* Ether()/IP() -- IPv4 header truncated */
+static const uint8_t pkt_ipv4_trunc[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+	0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+};
+
 /* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IPv6()/TCP() */
 static const uint8_t pkt_qinq_ipv6_tcp[] = {
 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
@@ -279,6 +305,18 @@ test_net_ptype(void)
 	ret |= test_case(pool, pkt_mpls_arp,
 			 RTE_PTYPE_L2_ETHER_MPLS,
 			 18, 0, 0);
+	ret |= test_case(pool, pkt_vlan_trunc,
+			 RTE_PTYPE_L2_ETHER_VLAN,
+			 14, 0, 0);
+	ret |= test_case(pool, pkt_mpls_trunc,
+			 RTE_PTYPE_L2_ETHER_MPLS,
+			 14, 0, 0);
+	ret |= test_case(pool, pkt_mpls_no_payload,
+			 RTE_PTYPE_L2_ETHER_MPLS,
+			 18, 0, 0);
+	ret |= test_case(pool, pkt_ipv4_trunc,
+			 RTE_PTYPE_L2_ETHER,
+			 14, 0, 0);
 
 	rte_mempool_free(pool);
 
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH] spinlock: remove volatile qualifier
From: Thomas Monjalon @ 2026-05-18 13:57 UTC (permalink / raw)
  To: dev; +Cc: stable, Stephen Hemminger, Konstantin Ananyev
In-Reply-To: <6261630.31tnzDBltd@thomas>

04/05/2026 11:06, Thomas Monjalon:
> 04/05/2026 10:37, Thomas Monjalon:
> > The user and count fields of rte_spinlock_recursive_t
> > do not need the volatile qualifier
> > because they are only accessed by the thread holding the lock,
> > which already provides the necessary memory ordering.
> > 
> > Removing volatile aligns with a C++20 deprecation
> > for increment and decrement of volatile variables.
> > 
> > This issue was seen with GCC 16 which changes the default C++ version
> > from -std=gnu++17 to -std=gnu++20.
> > 
> > Fixes: af75078fece3 ("first public release")
> > Cc: stable@dpdk.org
> > 
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> 
> I've just found this has been discussed 4 years ago:
> https://inbox.dpdk.org/dev/20221221083717.135c3f81@hermes.local
> 
> Stephen had found some comments issues that I will fix in another patch.

Adding the compiler messages:

rte_spinlock.h:241:14: error: '++' expression of 'volatile'-qualified type is deprecated [-Werror=volatile]
rte_spinlock.h:252:21: error: '--' expression of 'volatile'-qualified type is deprecated [-Werror=volatile]
rte_spinlock.h:278:14: error: '++' expression of 'volatile'-qualified type is deprecated [-Werror=volatile]



^ permalink raw reply

* Re: [PATCH v4 00/20] Wangxun Fixes
From: Stephen Hemminger @ 2026-05-18 14:54 UTC (permalink / raw)
  To: Zaiyu Wang; +Cc: dev
In-Reply-To: <20260511103604.19724-1-zaiyuwang@trustnetic.com>

On Mon, 11 May 2026 18:35:42 +0800
Zaiyu Wang <zaiyuwang@trustnetic.com> wrote:

> This series fixes several issues found on Wangxun Emerald, Sapphire and
> Amber-lite NICs, with a focus on link-related problems.
> ---

Since these are fixes, they mostly standalone.
If you want I can cherrypick the ones that review cleanly; and you can
then address the ones that have review feedback.

^ permalink raw reply

* [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Thomas Monjalon @ 2026-05-18 15:07 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Konstantin Ananyev, Bruce Richardson,
	Robin Jarry, stable
In-Reply-To: <20260504083714.2904729-1-thomas@monjalon.net>

When compiling with C++20 standard requirement (default in GCC 16),
the increment and decrement of volatile variables are rejected:

rte_spinlock.h:241:14: error:
	'++' expression of 'volatile'-qualified type is deprecated
rte_spinlock.h:252:21: error:
	'--' expression of 'volatile'-qualified type is deprecated
rte_spinlock.h:278:14: error:
	'++' expression of 'volatile'-qualified type is deprecated

The count field of rte_spinlock_recursive_t
does not need the volatile qualifier
because it is only accessed by the thread holding the lock,
which already provides the necessary memory ordering.

The user field can be accessed outside of the lock,
so it must handled as a C11 atomic variable.

Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
v1: drop volatile keyword
v2: make user an atomic variable
---
 lib/eal/include/generic/rte_spinlock.h | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
index c907d4e45c..5d810b682a 100644
--- a/lib/eal/include/generic/rte_spinlock.h
+++ b/lib/eal/include/generic/rte_spinlock.h
@@ -197,8 +197,8 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
  */
 typedef struct {
 	rte_spinlock_t sl; /**< the actual spinlock */
-	volatile int user; /**< core id using lock, -1 for unused */
-	volatile int count; /**< count of time this lock has been called */
+	RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
+	int count; /**< count of time this lock has been called */
 } rte_spinlock_recursive_t;
 
 /**
@@ -215,7 +215,7 @@ typedef struct {
 static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
 {
 	rte_spinlock_init(&slr->sl);
-	slr->user = -1;
+	rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
 	slr->count = 0;
 }
 
@@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
 {
 	int id = rte_gettid();
 
-	if (slr->user != id) {
+	if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
 		rte_spinlock_lock(&slr->sl);
-		slr->user = id;
+		rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);
 	}
 	slr->count++;
 }
@@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
 	__rte_no_thread_safety_analysis
 {
 	if (--(slr->count) == 0) {
-		slr->user = -1;
+		rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
 		rte_spinlock_unlock(&slr->sl);
 	}
-
 }
 
 /**
@@ -266,10 +265,10 @@ static inline int rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr)
 {
 	int id = rte_gettid();
 
-	if (slr->user != id) {
+	if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
 		if (rte_spinlock_trylock(&slr->sl) == 0)
 			return 0;
-		slr->user = id;
+		rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);
 	}
 	slr->count++;
 	return 1;
-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 2/2] spinlock: fix API comments
From: Thomas Monjalon @ 2026-05-18 15:07 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Konstantin Ananyev, Bruce Richardson,
	Robin Jarry, stable, Olivier Matz, Cunming Liang
In-Reply-To: <20260518150819.3332628-1-thomas@monjalon.net>

This is not a read-write lock.

The user field stores the thread ID, not the core ID
since a change in DPDK 2.0.0.

The implementation is architecture-specific in some cases only.

Fixes: ca2e2dab079a ("spinlock: support non-EAL thread")
Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org

Reported-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 lib/eal/include/generic/rte_spinlock.h | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
index 5d810b682a..5c46a7a90f 100644
--- a/lib/eal/include/generic/rte_spinlock.h
+++ b/lib/eal/include/generic/rte_spinlock.h
@@ -7,12 +7,16 @@
 
 /**
  * @file
+ * DPDK spinlocks
  *
- * RTE Spinlocks
+ * This is an API for spinlocks.
+ * This kind of lock simply waits in a loop
+ * repeatedly checking until the lock becomes available.
  *
- * This file defines an API for read-write locks, which are implemented
- * in an architecture-specific way. This kind of lock simply waits in
- * a loop repeatedly checking until the lock becomes available.
+ * Some functions may have an architecture-specific implementation
+ * if RTE_FORCE_INTRINSICS is disabled.
+ * The hardware transactional memory (lock elision) functions have _tm suffix
+ * and are implemented in architecture-specific files.
  *
  * All locks must be initialised before use, and only initialised once.
  */
@@ -197,7 +201,7 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
  */
 typedef struct {
 	rte_spinlock_t sl; /**< the actual spinlock */
-	RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
+	RTE_ATOMIC(int) user; /**< thread id using lock, -1 for unused */
 	int count; /**< count of time this lock has been called */
 } rte_spinlock_recursive_t;
 
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Robin Jarry @ 2026-05-18 15:14 UTC (permalink / raw)
  To: Thomas Monjalon, dev
  Cc: Stephen Hemminger, Konstantin Ananyev, Bruce Richardson, stable
In-Reply-To: <20260518150819.3332628-1-thomas@monjalon.net>

Hey Thomas,

Thomas Monjalon, May 18, 2026 at 17:07:
> When compiling with C++20 standard requirement (default in GCC 16),
> the increment and decrement of volatile variables are rejected:
>
> rte_spinlock.h:241:14: error:
> 	'++' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:252:21: error:
> 	'--' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:278:14: error:
> 	'++' expression of 'volatile'-qualified type is deprecated
>
> The count field of rte_spinlock_recursive_t
> does not need the volatile qualifier
> because it is only accessed by the thread holding the lock,
> which already provides the necessary memory ordering.
>
> The user field can be accessed outside of the lock,
> so it must handled as a C11 atomic variable.
>
> Fixes: af75078fece3 ("first public release")
> Cc: stable@dpdk.org
>
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> ---
> v1: drop volatile keyword
> v2: make user an atomic variable
> ---
>  lib/eal/include/generic/rte_spinlock.h | 17 ++++++++---------
>  1 file changed, 8 insertions(+), 9 deletions(-)
>
> diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
> index c907d4e45c..5d810b682a 100644
> --- a/lib/eal/include/generic/rte_spinlock.h
> +++ b/lib/eal/include/generic/rte_spinlock.h
> @@ -197,8 +197,8 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
>   */
>  typedef struct {
>  	rte_spinlock_t sl; /**< the actual spinlock */
> -	volatile int user; /**< core id using lock, -1 for unused */
> -	volatile int count; /**< count of time this lock has been called */
> +	RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
> +	int count; /**< count of time this lock has been called */
>  } rte_spinlock_recursive_t;
>  
>  /**
> @@ -215,7 +215,7 @@ typedef struct {
>  static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
>  {
>  	rte_spinlock_init(&slr->sl);
> -	slr->user = -1;
> +	rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
>  	slr->count = 0;
>  }
>  
> @@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
>  {
>  	int id = rte_gettid();
>  
> -	if (slr->user != id) {
> +	if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {

This needs to be rte_memory_order_acquire

>  		rte_spinlock_lock(&slr->sl);
> -		slr->user = id;
> +		rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);

And rte_memory_order_release

>  	}
>  	slr->count++;
>  }
> @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
>  	__rte_no_thread_safety_analysis
>  {
>  	if (--(slr->count) == 0) {

This code is completely broken. Any thread can unlock without any check.

I think it should be:

diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
index c907d4e45c39..b1058e4f8b4f 100644
--- a/lib/eal/include/generic/rte_spinlock.h
+++ b/lib/eal/include/generic/rte_spinlock.h
@@ -245,11 +247,17 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
 static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
        __rte_no_thread_safety_analysis
 {
-       if (--(slr->count) == 0) {
-               slr->user = -1;
-               rte_spinlock_unlock(&slr->sl);
-       }
+       int id = rte_gettid();

+       if (rte_atomic_load_explicit(&slr->user, rte_memory_order_acquire) == id) {
+               RTE_ASSERT(slr->count > 0);
+               if (--(slr->count) == 0) {
+                       rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_release);
+                       rte_spinlock_unlock(&slr->sl);
+               }
+       } else {
+               RTE_ASSERT(id != -1 && "unlocked from another thread");
+       }
 }

(not tested)

> -		slr->user = -1;
> +		rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
>  		rte_spinlock_unlock(&slr->sl);
>  	}
> -
>  }
>  
>  /**
> @@ -266,10 +265,10 @@ static inline int rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr)
>  {
>  	int id = rte_gettid();
>  
> -	if (slr->user != id) {
> +	if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {

rte_memory_order_acquire

>  		if (rte_spinlock_trylock(&slr->sl) == 0)
>  			return 0;
> -		slr->user = id;
> +		rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);

rte_memory_order_release

>  	}
>  	slr->count++;
>  	return 1;


-- 
Robin

> Monitored by the American Human Association.


^ permalink raw reply related

* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Thomas Monjalon @ 2026-05-18 15:25 UTC (permalink / raw)
  To: Robin Jarry
  Cc: dev, Stephen Hemminger, Konstantin Ananyev, Bruce Richardson,
	stable
In-Reply-To: <DILWG1XCA7NV.33OR3FAYCVVI0@redhat.com>

18/05/2026 17:14, Robin Jarry:
> Hey Thomas,
> 
> Thomas Monjalon, May 18, 2026 at 17:07:
> > When compiling with C++20 standard requirement (default in GCC 16),
> > the increment and decrement of volatile variables are rejected:
> >
> > rte_spinlock.h:241:14: error:
> > 	'++' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:252:21: error:
> > 	'--' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:278:14: error:
> > 	'++' expression of 'volatile'-qualified type is deprecated
> >
> > The count field of rte_spinlock_recursive_t
> > does not need the volatile qualifier
> > because it is only accessed by the thread holding the lock,
> > which already provides the necessary memory ordering.
> >
> > The user field can be accessed outside of the lock,
> > so it must handled as a C11 atomic variable.
> >
> > Fixes: af75078fece3 ("first public release")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> > ---
> > v1: drop volatile keyword
> > v2: make user an atomic variable
> > ---
> >  typedef struct {
> >  	rte_spinlock_t sl; /**< the actual spinlock */
> > -	volatile int user; /**< core id using lock, -1 for unused */
> > -	volatile int count; /**< count of time this lock has been called */
> > +	RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
> > +	int count; /**< count of time this lock has been called */
> >  } rte_spinlock_recursive_t;
[...]
> > @@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
> >  {
> >  	int id = rte_gettid();
> >  
> > -	if (slr->user != id) {
> > +	if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
> 
> This needs to be rte_memory_order_acquire

The memory ordering is managed with the sl variable.
The variable user has just to be atomic, so relaxed should be enough,
am I wrong?


[...]
> > @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> >  	__rte_no_thread_safety_analysis
> >  {
> >  	if (--(slr->count) == 0) {
> 
> This code is completely broken. Any thread can unlock without any check.

Maybe, but I don't intend to fix recursive unlock in this patch.
The subject is "remove volatile qualifier" (and unblock GCC 16).



^ permalink raw reply

* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Bruce Richardson @ 2026-05-18 15:28 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Robin Jarry, dev, Stephen Hemminger, Konstantin Ananyev, stable
In-Reply-To: <kagkiNyiQQ67llvhX0AB8g@monjalon.net>

On Mon, May 18, 2026 at 05:25:43PM +0200, Thomas Monjalon wrote:
> 18/05/2026 17:14, Robin Jarry:
> > Hey Thomas,
> > 
> > Thomas Monjalon, May 18, 2026 at 17:07:

<snip>

> 
> [...]
> > > @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> > >  	__rte_no_thread_safety_analysis
> > >  {
> > >  	if (--(slr->count) == 0) {
> > 
> > This code is completely broken. Any thread can unlock without any check.
> 
> Maybe, but I don't intend to fix recursive unlock in this patch.
> The subject is "remove volatile qualifier" (and unblock GCC 16).
> 
As with regular mutexes, threads should not go around unlocking when not
holding the lock. Therefore, I think this is fine. [With regular spinlocks
we can't check since we don't track threads, therefore I think we just need
to assume a well-behaved app.]

/Bruce

^ permalink raw reply

* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Stephen Hemminger @ 2026-05-18 16:26 UTC (permalink / raw)
  To: Robin Jarry
  Cc: Thomas Monjalon, dev, Konstantin Ananyev, Bruce Richardson,
	stable
In-Reply-To: <DILWG1XCA7NV.33OR3FAYCVVI0@redhat.com>

On Mon, 18 May 2026 17:14:54 +0200
"Robin Jarry" <rjarry@redhat.com> wrote:

> Hey Thomas,
> 
> Thomas Monjalon, May 18, 2026 at 17:07:
> > When compiling with C++20 standard requirement (default in GCC 16),
> > the increment and decrement of volatile variables are rejected:
> >
> > rte_spinlock.h:241:14: error:
> > 	'++' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:252:21: error:
> > 	'--' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:278:14: error:
> > 	'++' expression of 'volatile'-qualified type is deprecated
> >
> > The count field of rte_spinlock_recursive_t
> > does not need the volatile qualifier
> > because it is only accessed by the thread holding the lock,
> > which already provides the necessary memory ordering.
> >
> > The user field can be accessed outside of the lock,
> > so it must handled as a C11 atomic variable.
> >
> > Fixes: af75078fece3 ("first public release")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> > ---
> > v1: drop volatile keyword
> > v2: make user an atomic variable
> > ---
> >  lib/eal/include/generic/rte_spinlock.h | 17 ++++++++---------
> >  1 file changed, 8 insertions(+), 9 deletions(-)
> >
> > diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
> > index c907d4e45c..5d810b682a 100644
> > --- a/lib/eal/include/generic/rte_spinlock.h
> > +++ b/lib/eal/include/generic/rte_spinlock.h
> > @@ -197,8 +197,8 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
> >   */
> >  typedef struct {
> >  	rte_spinlock_t sl; /**< the actual spinlock */
> > -	volatile int user; /**< core id using lock, -1 for unused */
> > -	volatile int count; /**< count of time this lock has been called */
> > +	RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
> > +	int count; /**< count of time this lock has been called */
> >  } rte_spinlock_recursive_t;
> >  
> >  /**
> > @@ -215,7 +215,7 @@ typedef struct {
> >  static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
> >  {
> >  	rte_spinlock_init(&slr->sl);
> > -	slr->user = -1;
> > +	rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
> >  	slr->count = 0;
> >  }
> >  
> > @@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
> >  {
> >  	int id = rte_gettid();
> >  
> > -	if (slr->user != id) {
> > +	if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {  
> 
> This needs to be rte_memory_order_acquire

No it does not. There is no dependency here.
The acquire is inside the spinlock.

> 
> >  		rte_spinlock_lock(&slr->sl);
> > -		slr->user = id;
> > +		rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);  
> 
> And rte_memory_order_release

Ditto.

> 
> >  	}
> >  	slr->count++;
> >  }
> > @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> >  	__rte_no_thread_safety_analysis
> >  {
> >  	if (--(slr->count) == 0) {  
> 
> This code is completely broken. Any thread can unlock without any check.

I would make an RTE_ASSERT() that id matched current thread id.
Since caller holds lock, no atomic required for that.

^ permalink raw reply

* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Stephen Hemminger @ 2026-05-18 16:28 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: dev, Konstantin Ananyev, Bruce Richardson, Robin Jarry, stable
In-Reply-To: <20260518150819.3332628-1-thomas@monjalon.net>

On Mon, 18 May 2026 17:07:35 +0200
Thomas Monjalon <thomas@monjalon.net> wrote:

> When compiling with C++20 standard requirement (default in GCC 16),
> the increment and decrement of volatile variables are rejected:
> 
> rte_spinlock.h:241:14: error:
> 	'++' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:252:21: error:
> 	'--' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:278:14: error:
> 	'++' expression of 'volatile'-qualified type is deprecated
> 
> The count field of rte_spinlock_recursive_t
> does not need the volatile qualifier
> because it is only accessed by the thread holding the lock,
> which already provides the necessary memory ordering.
> 
> The user field can be accessed outside of the lock,
> so it must handled as a C11 atomic variable.
> 
> Fixes: af75078fece3 ("first public release")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>

I would also suggest renaming 'user' to 'owner' to follow common
practice in other places. It also provides an intentional API
break if somebody is misguided enough to do direct access to the
structure field.

^ permalink raw reply

* [PATCH] eal: silence -Wconstant-logical-operand in RTE_IS_POWER_OF_2
From: Stephen Hemminger @ 2026-05-18 16:33 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Jack Bond-Preston, Gavin Hu,
	Honnappa Nagarahalli, Pablo de Lara

Newer GCC warns when a non-boolean constant is an operand of &&, which
trips whenever RTE_IS_POWER_OF_2 is used in a static_assert with a
power-of-two literal. Make the zero check explicit.

Fixes: 7c872b96983a ("hash: validate hash bucket entries while compiling")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/eal/include/rte_bitops.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h
index aa6ac73abb..d2719ecd5e 100644
--- a/lib/eal/include/rte_bitops.h
+++ b/lib/eal/include/rte_bitops.h
@@ -1299,7 +1299,7 @@ rte_fls_u64(uint64_t x)
 /**
  * Macro to return 1 if n is a power of 2, 0 otherwise
  */
-#define RTE_IS_POWER_OF_2(n) ((n) && !(((n) - 1) & (n)))
+#define RTE_IS_POWER_OF_2(n) ((n) != 0 && !(((n) - 1) & (n)))
 
 /**
  * Returns true if n is a power of 2
-- 
2.53.0


^ permalink raw reply related

* [RFC] eal: enable thread safety analysis for recursive lock
From: Stephen Hemminger @ 2026-05-18 16:34 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger

The recursive spinlock API was annotated with
__rte_no_thread_safety_analysis on each definition, with no capability
annotations on any declaration. As a result, callers received no
static checking: fields marked __rte_guarded_by() on a recursive
spinlock were not verified, and rte_spinlock_recursive_t was not
recognized as a capability at all.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/eal/include/generic/rte_spinlock.h | 28 +++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
index 5d810b682a..e489f846f6 100644
--- a/lib/eal/include/generic/rte_spinlock.h
+++ b/lib/eal/include/generic/rte_spinlock.h
@@ -195,7 +195,7 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
 /**
  * The rte_spinlock_recursive_t type.
  */
-typedef struct {
+typedef struct __rte_capability("recursive_spinlock") {
 	rte_spinlock_t sl; /**< the actual spinlock */
 	RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
 	int count; /**< count of time this lock has been called */
@@ -219,6 +219,32 @@ static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
 	slr->count = 0;
 }
 
+/*
+ * TSA models the recursive spinlock as a single capability: callers
+ * get the same __rte_guarded_by() checking as the non-recursive API.
+ * Nested acquires by the owner are invisible to the analyzer — TSA
+ * has no model for re-entrance, so mismatched recursive lock/unlock
+ * counts remain a runtime concern.
+ *
+ * The definitions use __rte_no_thread_safety_analysis because the
+ * inner rte_spinlock_lock() is conditional on the owner check, which
+ * TSA cannot reason about. Annotations on the declarations are what
+ * callers see.
+ */
+
+static inline void
+rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
+	__rte_acquire_capability(slr);
+
+static inline void
+rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
+	__rte_release_capability(slr);
+
+__rte_warn_unused_result
+static inline int
+rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr)
+	__rte_try_acquire_capability(true, slr);
+
 /**
  * Take the recursive spinlock.
  *
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] eal: silence -Wconstant-logical-operand in RTE_IS_POWER_OF_2
From: Bruce Richardson @ 2026-05-18 16:56 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: dev, stable, Jack Bond-Preston, Gavin Hu, Honnappa Nagarahalli,
	Pablo de Lara
In-Reply-To: <20260518163401.580696-1-stephen@networkplumber.org>

On Mon, May 18, 2026 at 09:33:31AM -0700, Stephen Hemminger wrote:
> Newer GCC warns when a non-boolean constant is an operand of &&, which
> trips whenever RTE_IS_POWER_OF_2 is used in a static_assert with a
> power-of-two literal. Make the zero check explicit.
> 
> Fixes: 7c872b96983a ("hash: validate hash bucket entries while compiling")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>

One further suggestion below, since you are changing this.
> ---
>  lib/eal/include/rte_bitops.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h
> index aa6ac73abb..d2719ecd5e 100644
> --- a/lib/eal/include/rte_bitops.h
> +++ b/lib/eal/include/rte_bitops.h
> @@ -1299,7 +1299,7 @@ rte_fls_u64(uint64_t x)
>  /**
>   * Macro to return 1 if n is a power of 2, 0 otherwise
>   */
> -#define RTE_IS_POWER_OF_2(n) ((n) && !(((n) - 1) & (n)))
> +#define RTE_IS_POWER_OF_2(n) ((n) != 0 && !(((n) - 1) & (n)))
>  

I'd also suggest changing the second part of the && to match, changing the
"!" for explicit "== 0":

#define RTE_IS_POWER_OF_2(n) ((n) != 0 && (((n) - 1) & (n)) == 0)

>  /**
>   * Returns true if n is a power of 2
> -- 
> 2.53.0
> 

^ permalink raw reply


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