DPDK patches and discussions
 help / color / mirror / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download: 
* [PATCH v5 3/4] drivers: move iavf common folder to iavf net
  @ 2025-02-10 16:44  2%   ` Bruce Richardson
  2025-02-11 14:12  0%     ` Stokes, Ian
  0 siblings, 1 reply; 153+ results
From: Bruce Richardson @ 2025-02-10 16:44 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson

The common/iavf driver folder contains the base code for the iavf
driver, which is also linked against by the ice driver and others.
However, there is no need for this to be in common, and we can
move it to the net/intel/iavf as a base code driver. This involves
updating dependencies that were on common/iavf to net/iavf

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 devtools/libabigail.abignore                       |  1 +
 doc/guides/rel_notes/release_25_03.rst             |  5 ++++-
 drivers/common/iavf/version.map                    | 13 -------------
 drivers/common/meson.build                         |  1 -
 .../{common/iavf => net/intel/iavf/base}/README    |  0
 .../iavf => net/intel/iavf/base}/iavf_adminq.c     |  0
 .../iavf => net/intel/iavf/base}/iavf_adminq.h     |  0
 .../iavf => net/intel/iavf/base}/iavf_adminq_cmd.h |  0
 .../iavf => net/intel/iavf/base}/iavf_alloc.h      |  0
 .../iavf => net/intel/iavf/base}/iavf_common.c     |  0
 .../iavf => net/intel/iavf/base}/iavf_devids.h     |  0
 .../iavf => net/intel/iavf/base}/iavf_impl.c       |  0
 .../iavf => net/intel/iavf/base}/iavf_osdep.h      |  0
 .../iavf => net/intel/iavf/base}/iavf_prototype.h  |  0
 .../iavf => net/intel/iavf/base}/iavf_register.h   |  0
 .../iavf => net/intel/iavf/base}/iavf_status.h     |  0
 .../iavf => net/intel/iavf/base}/iavf_type.h       |  0
 .../iavf => net/intel/iavf/base}/meson.build       |  0
 .../iavf => net/intel/iavf/base}/virtchnl.h        |  0
 .../intel/iavf/base}/virtchnl_inline_ipsec.h       |  0
 drivers/net/intel/iavf/meson.build                 | 13 +++++++++----
 drivers/net/intel/iavf/version.map                 | 14 ++++++++++++++
 drivers/net/intel/ice/meson.build                  |  7 +++----
 drivers/net/intel/idpf/meson.build                 |  2 +-
 24 files changed, 32 insertions(+), 24 deletions(-)
 delete mode 100644 drivers/common/iavf/version.map
 rename drivers/{common/iavf => net/intel/iavf/base}/README (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_adminq.c (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_adminq.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_adminq_cmd.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_alloc.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_common.c (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_devids.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_impl.c (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_osdep.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_prototype.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_register.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_status.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/iavf_type.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/meson.build (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/virtchnl.h (100%)
 rename drivers/{common/iavf => net/intel/iavf/base}/virtchnl_inline_ipsec.h (100%)

diff --git a/devtools/libabigail.abignore b/devtools/libabigail.abignore
index b7daca4841..ce501632b3 100644
--- a/devtools/libabigail.abignore
+++ b/devtools/libabigail.abignore
@@ -23,6 +23,7 @@
 ; This is not a libabigail rule (see check-abi.sh).
 ; This is used for driver removal and other special cases like mlx glue libs.
 ;
+; SKIP_LIBRARY=librte_common_iavf
 ; SKIP_LIBRARY=librte_common_idpf
 ; SKIP_LIBRARY=librte_common_mlx5_glue
 ; SKIP_LIBRARY=librte_net_mlx4_glue
diff --git a/doc/guides/rel_notes/release_25_03.rst b/doc/guides/rel_notes/release_25_03.rst
index 2338a97e76..d2e8b03107 100644
--- a/doc/guides/rel_notes/release_25_03.rst
+++ b/doc/guides/rel_notes/release_25_03.rst
@@ -182,9 +182,12 @@ API Changes
   ``-Denable_drivers=net/intel/e1000``.
 
 * The driver ``common/idpf`` has been merged into the ``net/intel/idpf`` driver.
-  This change should have no impact to end applications, but,
+  Similarly, the ``common/iavf`` driver has been merged into the ``net/intel/iavf`` driver.
+  These changes should have no impact to end applications, but,
   when specifying the ``idpf`` or ``cpfl`` net drivers to meson via ``-Denable_drivers`` option,
   there is no longer any need to also specify the ``common/idpf`` driver.
+  In the same way, when specifying the ``iavf`` or ``ice`` net drivers,
+  there is no need to also specify the ``common/iavf`` driver.
   Note, however, ``net/intel/cpfl`` driver now depends upon the ``net/intel/idpf`` driver.
 
 
diff --git a/drivers/common/iavf/version.map b/drivers/common/iavf/version.map
deleted file mode 100644
index 6c1427cca4..0000000000
--- a/drivers/common/iavf/version.map
+++ /dev/null
@@ -1,13 +0,0 @@
-INTERNAL {
-	global:
-
-	iavf_aq_send_msg_to_pf;
-	iavf_clean_arq_element;
-	iavf_init_adminq;
-	iavf_set_mac_type;
-	iavf_shutdown_adminq;
-	iavf_vf_parse_hw_config;
-	iavf_vf_reset;
-
-	local: *;
-};
diff --git a/drivers/common/meson.build b/drivers/common/meson.build
index e1e3149d8f..dc096aab0a 100644
--- a/drivers/common/meson.build
+++ b/drivers/common/meson.build
@@ -5,7 +5,6 @@ std_deps = ['eal']
 drivers = [
         'cpt',
         'dpaax',
-        'iavf',
         'ionic',
         'mvep',
         'octeontx',
diff --git a/drivers/common/iavf/README b/drivers/net/intel/iavf/base/README
similarity index 100%
rename from drivers/common/iavf/README
rename to drivers/net/intel/iavf/base/README
diff --git a/drivers/common/iavf/iavf_adminq.c b/drivers/net/intel/iavf/base/iavf_adminq.c
similarity index 100%
rename from drivers/common/iavf/iavf_adminq.c
rename to drivers/net/intel/iavf/base/iavf_adminq.c
diff --git a/drivers/common/iavf/iavf_adminq.h b/drivers/net/intel/iavf/base/iavf_adminq.h
similarity index 100%
rename from drivers/common/iavf/iavf_adminq.h
rename to drivers/net/intel/iavf/base/iavf_adminq.h
diff --git a/drivers/common/iavf/iavf_adminq_cmd.h b/drivers/net/intel/iavf/base/iavf_adminq_cmd.h
similarity index 100%
rename from drivers/common/iavf/iavf_adminq_cmd.h
rename to drivers/net/intel/iavf/base/iavf_adminq_cmd.h
diff --git a/drivers/common/iavf/iavf_alloc.h b/drivers/net/intel/iavf/base/iavf_alloc.h
similarity index 100%
rename from drivers/common/iavf/iavf_alloc.h
rename to drivers/net/intel/iavf/base/iavf_alloc.h
diff --git a/drivers/common/iavf/iavf_common.c b/drivers/net/intel/iavf/base/iavf_common.c
similarity index 100%
rename from drivers/common/iavf/iavf_common.c
rename to drivers/net/intel/iavf/base/iavf_common.c
diff --git a/drivers/common/iavf/iavf_devids.h b/drivers/net/intel/iavf/base/iavf_devids.h
similarity index 100%
rename from drivers/common/iavf/iavf_devids.h
rename to drivers/net/intel/iavf/base/iavf_devids.h
diff --git a/drivers/common/iavf/iavf_impl.c b/drivers/net/intel/iavf/base/iavf_impl.c
similarity index 100%
rename from drivers/common/iavf/iavf_impl.c
rename to drivers/net/intel/iavf/base/iavf_impl.c
diff --git a/drivers/common/iavf/iavf_osdep.h b/drivers/net/intel/iavf/base/iavf_osdep.h
similarity index 100%
rename from drivers/common/iavf/iavf_osdep.h
rename to drivers/net/intel/iavf/base/iavf_osdep.h
diff --git a/drivers/common/iavf/iavf_prototype.h b/drivers/net/intel/iavf/base/iavf_prototype.h
similarity index 100%
rename from drivers/common/iavf/iavf_prototype.h
rename to drivers/net/intel/iavf/base/iavf_prototype.h
diff --git a/drivers/common/iavf/iavf_register.h b/drivers/net/intel/iavf/base/iavf_register.h
similarity index 100%
rename from drivers/common/iavf/iavf_register.h
rename to drivers/net/intel/iavf/base/iavf_register.h
diff --git a/drivers/common/iavf/iavf_status.h b/drivers/net/intel/iavf/base/iavf_status.h
similarity index 100%
rename from drivers/common/iavf/iavf_status.h
rename to drivers/net/intel/iavf/base/iavf_status.h
diff --git a/drivers/common/iavf/iavf_type.h b/drivers/net/intel/iavf/base/iavf_type.h
similarity index 100%
rename from drivers/common/iavf/iavf_type.h
rename to drivers/net/intel/iavf/base/iavf_type.h
diff --git a/drivers/common/iavf/meson.build b/drivers/net/intel/iavf/base/meson.build
similarity index 100%
rename from drivers/common/iavf/meson.build
rename to drivers/net/intel/iavf/base/meson.build
diff --git a/drivers/common/iavf/virtchnl.h b/drivers/net/intel/iavf/base/virtchnl.h
similarity index 100%
rename from drivers/common/iavf/virtchnl.h
rename to drivers/net/intel/iavf/base/virtchnl.h
diff --git a/drivers/common/iavf/virtchnl_inline_ipsec.h b/drivers/net/intel/iavf/base/virtchnl_inline_ipsec.h
similarity index 100%
rename from drivers/common/iavf/virtchnl_inline_ipsec.h
rename to drivers/net/intel/iavf/base/virtchnl_inline_ipsec.h
diff --git a/drivers/net/intel/iavf/meson.build b/drivers/net/intel/iavf/meson.build
index d9b605f55a..c823d618e3 100644
--- a/drivers/net/intel/iavf/meson.build
+++ b/drivers/net/intel/iavf/meson.build
@@ -7,9 +7,13 @@ endif
 
 testpmd_sources = files('iavf_testpmd.c')
 
-deps += ['common_iavf', 'security', 'cryptodev']
+deps += ['security', 'cryptodev']
 
 sources = files(
+        'base/iavf_adminq.c',
+        'base/iavf_common.c',
+        'base/iavf_impl.c',
+
         'iavf_ethdev.c',
         'iavf_rxtx.c',
         'iavf_vchnl.c',
@@ -20,8 +24,9 @@ sources = files(
         'iavf_ipsec_crypto.c',
         'iavf_fsub.c',
 )
+includes += include_directories('base')
 
-if arch_subdir == 'x86' and is_variable('static_rte_common_iavf')
+if arch_subdir == 'x86'
     sources += files('iavf_rxtx_vec_sse.c')
 
     if is_windows and cc.get_id() != 'clang'
@@ -30,7 +35,7 @@ if arch_subdir == 'x86' and is_variable('static_rte_common_iavf')
 
     iavf_avx2_lib = static_library('iavf_avx2_lib',
             'iavf_rxtx_vec_avx2.c',
-            dependencies: [static_rte_ethdev, static_rte_common_iavf],
+            dependencies: [static_rte_ethdev],
             include_directories: includes,
             c_args: [cflags, '-mavx2'])
     objs += iavf_avx2_lib.extract_objects('iavf_rxtx_vec_avx2.c')
@@ -43,7 +48,7 @@ if arch_subdir == 'x86' and is_variable('static_rte_common_iavf')
         endif
         iavf_avx512_lib = static_library('iavf_avx512_lib',
                 'iavf_rxtx_vec_avx512.c',
-                dependencies: [static_rte_ethdev, static_rte_common_iavf],
+                dependencies: [static_rte_ethdev],
                 include_directories: includes,
                 c_args: avx512_args)
         objs += iavf_avx512_lib.extract_objects('iavf_rxtx_vec_avx512.c')
diff --git a/drivers/net/intel/iavf/version.map b/drivers/net/intel/iavf/version.map
index 98de64cca2..d18dea64dd 100644
--- a/drivers/net/intel/iavf/version.map
+++ b/drivers/net/intel/iavf/version.map
@@ -17,3 +17,17 @@ EXPERIMENTAL {
 	# added in 21.11
 	rte_pmd_ifd_dynflag_proto_xtr_ipsec_crypto_said_mask;
 };
+
+INTERNAL {
+	global:
+
+	iavf_aq_send_msg_to_pf;
+	iavf_clean_arq_element;
+	iavf_init_adminq;
+	iavf_set_mac_type;
+	iavf_shutdown_adminq;
+	iavf_vf_parse_hw_config;
+	iavf_vf_reset;
+
+	local: *;
+};
diff --git a/drivers/net/intel/ice/meson.build b/drivers/net/intel/ice/meson.build
index beaf21e176..5faf887386 100644
--- a/drivers/net/intel/ice/meson.build
+++ b/drivers/net/intel/ice/meson.build
@@ -18,7 +18,7 @@ sources = files(
 
 testpmd_sources = files('ice_testpmd.c')
 
-deps += ['hash', 'net', 'common_iavf']
+deps += ['hash', 'net', 'net_iavf']
 includes += include_directories('base')
 
 if arch_subdir == 'x86'
@@ -30,7 +30,7 @@ if arch_subdir == 'x86'
 
     ice_avx2_lib = static_library('ice_avx2_lib',
             'ice_rxtx_vec_avx2.c',
-            dependencies: [static_rte_ethdev, static_rte_kvargs, static_rte_hash],
+            dependencies: [static_rte_ethdev, static_rte_hash],
             include_directories: includes,
             c_args: [cflags, '-mavx2'])
     objs += ice_avx2_lib.extract_objects('ice_rxtx_vec_avx2.c')
@@ -43,8 +43,7 @@ if arch_subdir == 'x86'
         endif
         ice_avx512_lib = static_library('ice_avx512_lib',
                 'ice_rxtx_vec_avx512.c',
-                dependencies: [static_rte_ethdev,
-                    static_rte_kvargs, static_rte_hash],
+                dependencies: [static_rte_ethdev, static_rte_hash],
                 include_directories: includes,
                 c_args: avx512_args)
         objs += ice_avx512_lib.extract_objects('ice_rxtx_vec_avx512.c')
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index 87bc39f76e..d69254484b 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -7,7 +7,7 @@ if is_windows
     subdir_done()
 endif
 
-includes += include_directories('../../../common/iavf')
+includes += include_directories('../iavf/base')
 
 sources = files(
         'idpf_common_device.c',
-- 
2.43.0


^ permalink raw reply	[relevance 2%]

* [PATCH v6] net: add thread-safe crc api
  @ 2025-02-10 21:27  4% ` Arkadiusz Kusztal
  2025-02-11  6:23  0%   ` Stephen Hemminger
  2025-02-11  9:02  4%   ` [PATCH v7] " Arkadiusz Kusztal
  0 siblings, 2 replies; 153+ results
From: Arkadiusz Kusztal @ 2025-02-10 21:27 UTC (permalink / raw)
  To: dev; +Cc: ferruh.yigit, kai.ji, brian.dooley, stephen, Arkadiusz Kusztal

The current net CRC API is not thread-safe, this patch
solves this by adding another, thread-safe API functions.
This API is also safe to use across multiple processes,
yet with limitations on max-simd-bitwidth, which will be checked only by
the process that created the CRC context; all other processes
(that did not create the context) will use the highest possible
SIMD extension that was built with the binary, but no higher than the one
requested by the CRC context.

Since the change of the API at this point is an ABI break,
these API symbols are versioned with the _26 suffix.

Signed-off-by: Arkadiusz Kusztal <arkadiuszx.kusztal@intel.com>
---
v2:
- added multi-process safety
v3:
- made the crc context opaque
- versioned old APIs
v4:
- exported rte_net_crc_free symbol
v5:
- fixed unclear comments in release notes section
- aligned `fall-through` comments
v6:
- fixed typos and code formatting
- added entry to the nullfree.cocci script
- added malloc attributes
- reverted copyright changes

 app/test/test_crc.c                    | 167 ++++++++++---------------
 devtools/cocci/nullfree.cocci          |   3 +
 doc/guides/rel_notes/release_25_03.rst |   5 +
 drivers/crypto/qat/qat_sym.h           |   6 +-
 drivers/crypto/qat/qat_sym_session.c   |   8 ++
 drivers/crypto/qat/qat_sym_session.h   |   2 +
 lib/net/meson.build                    |   2 +
 lib/net/net_crc.h                      |  16 +++
 lib/net/rte_net_crc.c                  | 127 ++++++++++++++++++-
 lib/net/rte_net_crc.h                  |  38 ++++--
 lib/net/version.map                    |   6 +
 11 files changed, 267 insertions(+), 113 deletions(-)

diff --git a/app/test/test_crc.c b/app/test/test_crc.c
index b85fca35fe..f18eff7217 100644
--- a/app/test/test_crc.c
+++ b/app/test/test_crc.c
@@ -44,131 +44,100 @@ static const uint32_t crc32_vec_res = 0xb491aab4;
 static const uint32_t crc32_vec1_res = 0xac54d294;
 static const uint32_t crc32_vec2_res = 0xefaae02f;
 static const uint32_t crc16_vec_res = 0x6bec;
-static const uint16_t crc16_vec1_res = 0x8cdd;
-static const uint16_t crc16_vec2_res = 0xec5b;
+static const uint32_t crc16_vec1_res = 0x8cdd;
+static const uint32_t crc16_vec2_res = 0xec5b;
 
 static int
-crc_calc(const uint8_t *vec,
-	uint32_t vec_len,
-	enum rte_net_crc_type type)
+crc_all_algs(const char *desc, enum rte_net_crc_type type,
+	const uint8_t *data, int data_len, uint32_t res)
 {
-	/* compute CRC */
-	uint32_t ret = rte_net_crc_calc(vec, vec_len, type);
+	struct rte_net_crc *ctx;
+	uint32_t crc;
+	int ret = TEST_SUCCESS;
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_SCALAR, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s SCALAR\n", desc);
+		debug_hexdump(stdout, "SCALAR", &crc, 4);
+		ret = TEST_FAILED;
+	}
+	rte_net_crc_free(ctx);
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_SSE42, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s SSE42\n", desc);
+		debug_hexdump(stdout, "SSE", &crc, 4);
+		ret = TEST_FAILED;
+	}
 
-	/* dump data on console */
-	debug_hexdump(stdout, NULL, vec, vec_len);
+	rte_net_crc_free(ctx);
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_AVX512, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s AVX512\n", desc);
+		debug_hexdump(stdout, "AVX512", &crc, 4);
+		ret = TEST_FAILED;
+	}
+	rte_net_crc_free(ctx);
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_NEON, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s NEON\n", desc);
+		debug_hexdump(stdout, "NEON", &crc, 4);
+		ret = TEST_FAILED;
+	}
+	rte_net_crc_free(ctx);
 
-	return  ret;
+	return ret;
 }
 
 static int
-test_crc_calc(void)
-{
+crc_autotest(void)
+{	uint8_t *test_data;
 	uint32_t i;
-	enum rte_net_crc_type type;
-	uint8_t *test_data;
-	uint32_t result;
-	int error;
+	int ret = TEST_SUCCESS;
 
 	/* 32-bit ethernet CRC: Test 1 */
-	type = RTE_NET_CRC32_ETH;
-
-	result = crc_calc(crc_vec, CRC_VEC_LEN, type);
-	if (result != crc32_vec_res)
-		return -1;
+	ret = crc_all_algs("32-bit ethernet CRC: Test 1", RTE_NET_CRC32_ETH, crc_vec,
+		sizeof(crc_vec), crc32_vec_res);
 
 	/* 32-bit ethernet CRC: Test 2 */
 	test_data = rte_zmalloc(NULL, CRC32_VEC_LEN1, 0);
 	if (test_data == NULL)
 		return -7;
-
 	for (i = 0; i < CRC32_VEC_LEN1; i += 12)
 		rte_memcpy(&test_data[i], crc32_vec1, 12);
-
-	result = crc_calc(test_data, CRC32_VEC_LEN1, type);
-	if (result != crc32_vec1_res) {
-		error = -2;
-		goto fail;
-	}
+	ret |= crc_all_algs("32-bit ethernet CRC: Test 2", RTE_NET_CRC32_ETH, test_data,
+		CRC32_VEC_LEN1, crc32_vec1_res);
 
 	/* 32-bit ethernet CRC: Test 3 */
+	memset(test_data, 0, CRC32_VEC_LEN1);
 	for (i = 0; i < CRC32_VEC_LEN2; i += 12)
 		rte_memcpy(&test_data[i], crc32_vec1, 12);
-
-	result = crc_calc(test_data, CRC32_VEC_LEN2, type);
-	if (result != crc32_vec2_res) {
-		error = -3;
-		goto fail;
-	}
+	ret |= crc_all_algs("32-bit ethernet CRC: Test 3", RTE_NET_CRC32_ETH, test_data,
+		CRC32_VEC_LEN2, crc32_vec2_res);
 
 	/* 16-bit CCITT CRC:  Test 4 */
-	type = RTE_NET_CRC16_CCITT;
-	result = crc_calc(crc_vec, CRC_VEC_LEN, type);
-	if (result != crc16_vec_res) {
-		error = -4;
-		goto fail;
-	}
-	/* 16-bit CCITT CRC:  Test 5 */
-	result = crc_calc(crc16_vec1, CRC16_VEC_LEN1, type);
-	if (result != crc16_vec1_res) {
-		error = -5;
-		goto fail;
-	}
-	/* 16-bit CCITT CRC:  Test 6 */
-	result = crc_calc(crc16_vec2, CRC16_VEC_LEN2, type);
-	if (result != crc16_vec2_res) {
-		error = -6;
-		goto fail;
-	}
-
-	rte_free(test_data);
-	return 0;
-
-fail:
-	rte_free(test_data);
-	return error;
-}
-
-static int
-test_crc(void)
-{
-	int ret;
-	/* set CRC scalar mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_SCALAR);
-
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test_crc (scalar): failed (%d)\n", ret);
-		return ret;
-	}
-	/* set CRC sse4.2 mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_SSE42);
+	crc_all_algs("16-bit CCITT CRC:  Test 4", RTE_NET_CRC16_CCITT, crc_vec,
+		sizeof(crc_vec), crc16_vec_res);
 
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test_crc (x86_64_SSE4.2): failed (%d)\n", ret);
-		return ret;
-	}
-
-	/* set CRC avx512 mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_AVX512);
-
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test crc (x86_64 AVX512): failed (%d)\n", ret);
-		return ret;
-	}
-
-	/* set CRC neon mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_NEON);
+	/* 16-bit CCITT CRC:  Test 5 */
+	ret |= crc_all_algs("16-bit CCITT CRC:  Test 5", RTE_NET_CRC16_CCITT, crc16_vec1,
+		CRC16_VEC_LEN1, crc16_vec1_res);
 
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test crc (arm64 neon pmull): failed (%d)\n", ret);
-		return ret;
-	}
+	/* 16-bit CCITT CRC:  Test 6 */
+	ret |= crc_all_algs("16-bit CCITT CRC:  Test 6", RTE_NET_CRC16_CCITT, crc16_vec2,
+		CRC16_VEC_LEN2, crc16_vec2_res);
 
-	return 0;
+	return ret;
 }
 
-REGISTER_FAST_TEST(crc_autotest, true, true, test_crc);
+REGISTER_FAST_TEST(crc_autotest, true, true, crc_autotest);
diff --git a/devtools/cocci/nullfree.cocci b/devtools/cocci/nullfree.cocci
index c0526a2a3f..e7417b69ff 100644
--- a/devtools/cocci/nullfree.cocci
+++ b/devtools/cocci/nullfree.cocci
@@ -138,4 +138,7 @@ expression E;
 |
 - if (E != NULL) BN_free(E);
 + BN_free(E);
+|
+- if (E != NULL) rte_net_crc_free(E);
++ rte_net_crc_free(E);
 )
diff --git a/doc/guides/rel_notes/release_25_03.rst b/doc/guides/rel_notes/release_25_03.rst
index 2b139fc35b..424a0252cb 100644
--- a/doc/guides/rel_notes/release_25_03.rst
+++ b/doc/guides/rel_notes/release_25_03.rst
@@ -161,6 +161,11 @@ API Changes
   but to enable/disable these drivers via Meson option requires use of the new paths.
   For example, ``-Denable_drivers=/net/i40e`` becomes ``-Denable_drivers=/net/intel/i40e``.
 
+* net: Changed the API for CRC calculation to be thread safe.
+  An opaque context argument was introduced to the net CRC API containing
+  the algorithm type and length. This argument is added to
+  to ``rte_net_crc_calc``, ``rte_net_crc_set_alg`` and freed with ``rte_net_crc_free``.
+  These functions are versioned to retain binary compatiabilty until the next LTS release.
 
 ABI Changes
 -----------
diff --git a/drivers/crypto/qat/qat_sym.h b/drivers/crypto/qat/qat_sym.h
index f42336d7ed..849e047615 100644
--- a/drivers/crypto/qat/qat_sym.h
+++ b/drivers/crypto/qat/qat_sym.h
@@ -267,8 +267,7 @@ qat_crc_verify(struct qat_sym_session *ctx, struct rte_crypto_op *op)
 		crc_data = rte_pktmbuf_mtod_offset(sym_op->m_src, uint8_t *,
 				crc_data_ofs);
 
-		crc = rte_net_crc_calc(crc_data, crc_data_len,
-				RTE_NET_CRC32_ETH);
+		crc = rte_net_crc_calc(ctx->crc, crc_data, crc_data_len);
 
 		if (crc != *(uint32_t *)(crc_data + crc_data_len))
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
@@ -291,8 +290,7 @@ qat_crc_generate(struct qat_sym_session *ctx,
 		crc_data = rte_pktmbuf_mtod_offset(sym_op->m_src, uint8_t *,
 				sym_op->auth.data.offset);
 		crc = (uint32_t *)(crc_data + crc_data_len);
-		*crc = rte_net_crc_calc(crc_data, crc_data_len,
-				RTE_NET_CRC32_ETH);
+		*crc = rte_net_crc_calc(ctx->crc, crc_data, crc_data_len);
 	}
 }
 
diff --git a/drivers/crypto/qat/qat_sym_session.c b/drivers/crypto/qat/qat_sym_session.c
index 50d687fd37..7200022adf 100644
--- a/drivers/crypto/qat/qat_sym_session.c
+++ b/drivers/crypto/qat/qat_sym_session.c
@@ -3174,6 +3174,14 @@ qat_sec_session_set_docsis_parameters(struct rte_cryptodev *dev,
 		ret = qat_sym_session_configure_crc(dev, xform, session);
 		if (ret < 0)
 			return ret;
+	} else {
+		/* Initialize crc algorithm */
+		session->crc = rte_net_crc_set_alg(RTE_NET_CRC_AVX512,
+			RTE_NET_CRC32_ETH);
+		if (session->crc == NULL) {
+			QAT_LOG(ERR, "Cannot initialize CRC context");
+			return -1;
+		}
 	}
 	qat_sym_session_finalize(session);
 
diff --git a/drivers/crypto/qat/qat_sym_session.h b/drivers/crypto/qat/qat_sym_session.h
index 2ca6c8ddf5..2ef2066646 100644
--- a/drivers/crypto/qat/qat_sym_session.h
+++ b/drivers/crypto/qat/qat_sym_session.h
@@ -7,6 +7,7 @@
 #include <rte_crypto.h>
 #include <cryptodev_pmd.h>
 #include <rte_security.h>
+#include <rte_net_crc.h>
 
 #include "qat_common.h"
 #include "icp_qat_hw.h"
@@ -149,6 +150,7 @@ struct qat_sym_session {
 	uint8_t is_zuc256;
 	uint8_t is_wireless;
 	uint32_t slice_types;
+	struct rte_net_crc *crc;
 	enum qat_sym_proto_flag qat_proto_flag;
 	qat_sym_build_request_t build_request[2];
 #ifndef RTE_QAT_OPENSSL
diff --git a/lib/net/meson.build b/lib/net/meson.build
index 8afcc4ed37..b26b377e8e 100644
--- a/lib/net/meson.build
+++ b/lib/net/meson.build
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2020 Intel Corporation
 
+use_function_versioning=true
+
 headers = files(
         'rte_cksum.h',
         'rte_ip.h',
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 7a74d5406c..a9a6c9542c 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -5,6 +5,22 @@
 #ifndef _NET_CRC_H_
 #define _NET_CRC_H_
 
+#include "rte_net_crc.h"
+
+void
+rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
+
+struct rte_net_crc *
+rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type);
+
+uint32_t
+rte_net_crc_calc_v25(const void *data,
+	uint32_t data_len, enum rte_net_crc_type type);
+
+uint32_t
+rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len);
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 346c285c15..2fb3eec231 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -10,6 +10,8 @@
 #include <rte_net_crc.h>
 #include <rte_log.h>
 #include <rte_vect.h>
+#include <rte_function_versioning.h>
+#include <rte_malloc.h>
 
 #include "net_crc.h"
 
@@ -38,11 +40,20 @@ rte_crc32_eth_handler(const uint8_t *data, uint32_t data_len);
 typedef uint32_t
 (*rte_net_crc_handler)(const uint8_t *data, uint32_t data_len);
 
+struct rte_net_crc {
+	enum rte_net_crc_alg alg;
+	enum rte_net_crc_type type;
+};
+
 static rte_net_crc_handler handlers_default[] = {
 	[RTE_NET_CRC16_CCITT] = rte_crc16_ccitt_default_handler,
 	[RTE_NET_CRC32_ETH] = rte_crc32_eth_default_handler,
 };
 
+static struct {
+	rte_net_crc_handler f[RTE_NET_CRC_REQS];
+} handlers_dpdk26[RTE_NET_CRC_AVX512 + 1];
+
 static const rte_net_crc_handler *handlers = handlers_default;
 
 static const rte_net_crc_handler handlers_scalar[] = {
@@ -286,10 +297,56 @@ rte_crc32_eth_default_handler(const uint8_t *data, uint32_t data_len)
 	return handlers[RTE_NET_CRC32_ETH](data, data_len);
 }
 
+static void
+handlers_init(enum rte_net_crc_alg alg)
+{
+	handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] = rte_crc16_ccitt_handler;
+	handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] = rte_crc32_eth_handler;
+
+	switch (alg) {
+	case RTE_NET_CRC_AVX512:
+#ifdef CC_X86_64_AVX512_VPCLMULQDQ_SUPPORT
+		if (AVX512_VPCLMULQDQ_CPU_SUPPORTED) {
+			handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] =
+				rte_crc16_ccitt_avx512_handler;
+			handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] =
+				rte_crc32_eth_avx512_handler;
+			break;
+		}
+#endif
+		/* fall-through */
+	case RTE_NET_CRC_SSE42:
+#ifdef CC_X86_64_SSE42_PCLMULQDQ_SUPPORT
+		if (SSE42_PCLMULQDQ_CPU_SUPPORTED) {
+			handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] =
+				rte_crc16_ccitt_sse42_handler;
+			handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] =
+				rte_crc32_eth_sse42_handler;
+		}
+#endif
+		break;
+	case RTE_NET_CRC_NEON:
+#ifdef CC_ARM64_NEON_PMULL_SUPPORT
+		if (NEON_PMULL_CPU_SUPPORTED) {
+			handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] =
+				rte_crc16_ccitt_neon_handler;
+			handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] =
+				rte_crc32_eth_neon_handler;
+			break;
+		}
+#endif
+		/* fall-through */
+	case RTE_NET_CRC_SCALAR:
+		/* fall-through */
+	default:
+		break;
+	}
+}
+
 /* Public API */
 
 void
-rte_net_crc_set_alg(enum rte_net_crc_alg alg)
+rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -316,9 +373,59 @@ rte_net_crc_set_alg(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
+VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
+
+struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type)
+{
+	uint16_t max_simd_bitwidth;
+	struct rte_net_crc *crc;
+
+	crc = rte_zmalloc(NULL, sizeof(struct rte_net_crc), 0);
+	if (crc == NULL)
+		return NULL;
+	max_simd_bitwidth = rte_vect_get_max_simd_bitwidth();
+	crc->type = type;
+	crc->alg = RTE_NET_CRC_SCALAR;
+
+	switch (alg) {
+	case RTE_NET_CRC_AVX512:
+		if (max_simd_bitwidth >= RTE_VECT_SIMD_512) {
+			crc->alg = RTE_NET_CRC_AVX512;
+			return crc;
+		}
+		/* fall-through */
+	case RTE_NET_CRC_SSE42:
+		if (max_simd_bitwidth >= RTE_VECT_SIMD_128) {
+			crc->alg = RTE_NET_CRC_SSE42;
+			return crc;
+		}
+		break;
+	case RTE_NET_CRC_NEON:
+		if (max_simd_bitwidth >= RTE_VECT_SIMD_128) {
+			crc->alg = RTE_NET_CRC_NEON;
+			return crc;
+		}
+		break;
+	case RTE_NET_CRC_SCALAR:
+		/* fall-through */
+	default:
+		break;
+	}
+	return crc;
+}
+BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
+MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
+	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
+	rte_net_crc_set_alg_v26);
+
+void rte_net_crc_free(struct rte_net_crc *crc)
+{
+	rte_free(crc);
+}
 
 uint32_t
-rte_net_crc_calc(const void *data,
+rte_net_crc_calc_v25(const void *data,
 	uint32_t data_len,
 	enum rte_net_crc_type type)
 {
@@ -330,6 +437,18 @@ rte_net_crc_calc(const void *data,
 
 	return ret;
 }
+VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
+
+uint32_t
+rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len)
+{
+	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
+}
+BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
+MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len),
+	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
@@ -338,4 +457,8 @@ RTE_INIT(rte_net_crc_init)
 	sse42_pclmulqdq_init();
 	avx512_vpclmulqdq_init();
 	neon_pmull_init();
+	handlers_init(RTE_NET_CRC_SCALAR);
+	handlers_init(RTE_NET_CRC_NEON);
+	handlers_init(RTE_NET_CRC_SSE42);
+	handlers_init(RTE_NET_CRC_AVX512);
 }
diff --git a/lib/net/rte_net_crc.h b/lib/net/rte_net_crc.h
index 72d3e10ff6..6c14b8ab6c 100644
--- a/lib/net/rte_net_crc.h
+++ b/lib/net/rte_net_crc.h
@@ -26,8 +26,21 @@ enum rte_net_crc_alg {
 	RTE_NET_CRC_AVX512,
 };
 
+/** CRC context (algorithm, type) */
+struct rte_net_crc;
+
 /**
- * This API set the CRC computation algorithm (i.e. scalar version,
+ * Frees the memory space pointed to by the CRC context pointer.
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ctx
+ *   Pointer to the CRC context
+ */
+void
+rte_net_crc_free(struct rte_net_crc *crc);
+
+/**
+ * This API set the CRC context (i.e. scalar version,
  * x86 64-bit sse4.2 intrinsic version, etc.) and internal data
  * structure.
  *
@@ -37,27 +50,36 @@ enum rte_net_crc_alg {
  *   - RTE_NET_CRC_SSE42 (Use 64-bit SSE4.2 intrinsic)
  *   - RTE_NET_CRC_NEON (Use ARM Neon intrinsic)
  *   - RTE_NET_CRC_AVX512 (Use 512-bit AVX intrinsic)
+ * @param type
+ *   CRC type (enum rte_net_crc_type)
+ *
+ * @return
+ *   Pointer to the CRC context
  */
-void
-rte_net_crc_set_alg(enum rte_net_crc_alg alg);
+struct rte_net_crc *
+rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type type)
+	__rte_malloc __rte_dealloc(rte_net_crc_free, 1);
 
 /**
  * CRC compute API
  *
+ * Note:
+ * The command line argument --force-max-simd-bitwidth will be ignored
+ * by processes that have not created this CRC context.
+ *
+ * @param ctx
+ *   Pointer to the CRC context
  * @param data
  *   Pointer to the packet data for CRC computation
  * @param data_len
  *   Data length for CRC computation
- * @param type
- *   CRC type (enum rte_net_crc_type)
  *
  * @return
  *   CRC value
  */
 uint32_t
-rte_net_crc_calc(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type);
+rte_net_crc_calc(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len);
 
 #ifdef __cplusplus
 }
diff --git a/lib/net/version.map b/lib/net/version.map
index bec4ce23ea..d03f3f6ad0 100644
--- a/lib/net/version.map
+++ b/lib/net/version.map
@@ -5,6 +5,7 @@ DPDK_25 {
 	rte_ether_format_addr;
 	rte_ether_unformat_addr;
 	rte_net_crc_calc;
+	rte_net_crc_free;
 	rte_net_crc_set_alg;
 	rte_net_get_ptype;
 	rte_net_make_rarp_packet;
@@ -12,3 +13,8 @@ DPDK_25 {
 
 	local: *;
 };
+
+DPDK_26 {
+	rte_net_crc_calc;
+	rte_net_crc_set_alg;
+} DPDK_25;
-- 
2.34.1


^ permalink raw reply	[relevance 4%]

* Re: [PATCH v6] net: add thread-safe crc api
  2025-02-10 21:27  4% ` [PATCH v6] " Arkadiusz Kusztal
@ 2025-02-11  6:23  0%   ` Stephen Hemminger
  2025-02-11  8:35  0%     ` Kusztal, ArkadiuszX
  2025-02-11  9:02  4%   ` [PATCH v7] " Arkadiusz Kusztal
  1 sibling, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-02-11  6:23 UTC (permalink / raw)
  To: Arkadiusz Kusztal; +Cc: dev, ferruh.yigit, kai.ji, brian.dooley

On Mon, 10 Feb 2025 21:27:10 +0000
Arkadiusz Kusztal <arkadiuszx.kusztal@intel.com> wrote:

> The current net CRC API is not thread-safe, this patch
> solves this by adding another, thread-safe API functions.
> This API is also safe to use across multiple processes,
> yet with limitations on max-simd-bitwidth, which will be checked only by
> the process that created the CRC context; all other processes
> (that did not create the context) will use the highest possible
> SIMD extension that was built with the binary, but no higher than the one
> requested by the CRC context.
> 
> Since the change of the API at this point is an ABI break,
> these API symbols are versioned with the _26 suffix.
> 
> Signed-off-by: Arkadiusz Kusztal <arkadiuszx.kusztal@intel.com>
> ---

Thanks for updating so quick, but the problem is you need to move
the prototype for rte_net_crc_free() to get it to work.

-------------------------------BEGIN LOGS----------------------------
####################################################################################
#### [Begin job log] "ubuntu-22.04-gcc-mini" at step Build and test
####################################################################################
      |         ^~~~~~~~~~~~
/home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:61:53: error: expected ‘)’ before numeric constant
   61 |         __rte_malloc __rte_dealloc(rte_net_crc_free, 1);
      |                                                     ^~
      |                                                     )
/home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:60:1: error: old-style parameter declarations in prototyped function definition
   60 | rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type type)
      | ^~~~~~~~~~~~~~~~~~~
buildtools/chkincs/chkincs.p/rte_net_crc.c:3: error: expected ‘{’ at end of input
In file included from buildtools/chkincs/chkincs.p/rte_net_crc.c:1:
/home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:60:42: error: unused parameter ‘alg’ [-Werror=unused-parameter]
   60 | rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type type)
      |                     ~~~~~~~~~~~~~~~~~~~~~^~~
/home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:60:69: error: unused parameter ‘type’ [-Werror=unused-parameter]
   60 | rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type type)
      |                                               ~~~~~~~~~~~~~~~~~~~~~~^~~~
buildtools/chkincs/chkincs.p/rte_net_crc.c:3: error: control reaches end of non-void function [-Werror=return-type]
cc1: all warnings being treated as errors
[629/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-generated_rte_mpls.c.o
[630/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-generated_rte_arp.c.o
[631/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-generated_rte_ether.c.o
[632/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-generated_rte_net.c.o

^ permalink raw reply	[relevance 0%]

* RE: [PATCH v6] net: add thread-safe crc api
  2025-02-11  6:23  0%   ` Stephen Hemminger
@ 2025-02-11  8:35  0%     ` Kusztal, ArkadiuszX
  0 siblings, 0 replies; 153+ results
From: Kusztal, ArkadiuszX @ 2025-02-11  8:35 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev, ferruh.yigit, Ji, Kai, Dooley, Brian



> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Tuesday, February 11, 2025 7:24 AM
> To: Kusztal, ArkadiuszX <arkadiuszx.kusztal@intel.com>
> Cc: dev@dpdk.org; ferruh.yigit@amd.com; Ji, Kai <kai.ji@intel.com>; Dooley,
> Brian <brian.dooley@intel.com>
> Subject: Re: [PATCH v6] net: add thread-safe crc api
> 
> On Mon, 10 Feb 2025 21:27:10 +0000
> Arkadiusz Kusztal <arkadiuszx.kusztal@intel.com> wrote:
> 
> > The current net CRC API is not thread-safe, this patch solves this by
> > adding another, thread-safe API functions.
> > This API is also safe to use across multiple processes, yet with
> > limitations on max-simd-bitwidth, which will be checked only by the
> > process that created the CRC context; all other processes (that did
> > not create the context) will use the highest possible SIMD extension
> > that was built with the binary, but no higher than the one requested
> > by the CRC context.
> >
> > Since the change of the API at this point is an ABI break, these API
> > symbols are versioned with the _26 suffix.
> >
> > Signed-off-by: Arkadiusz Kusztal <arkadiuszx.kusztal@intel.com>
> > ---
> 
> Thanks for updating so quick, but the problem is you need to move the
> prototype for rte_net_crc_free() to get it to work.
Prototype of ` rte_net_crc_free` function was moved up in this patch, so it is visible. For me, the problem seems to be that the `rte_net_crc.h` is not self-contained (due to malloc addition), and the compiler thinks that the unknown attributes aliases are actual function body. I will fix that in v7. 
> 
> -------------------------------BEGIN LOGS----------------------------
> #################################################################
> ###################
> #### [Begin job log] "ubuntu-22.04-gcc-mini" at step Build and test
> #################################################################
> ###################
>       |         ^~~~~~~~~~~~
> /home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:61:53: error: expected ‘)’
> before numeric constant
>    61 |         __rte_malloc __rte_dealloc(rte_net_crc_free, 1);
>       |                                                     ^~
>       |                                                     )
> /home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:60:1: error: old-style
> parameter declarations in prototyped function definition
>    60 | rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type
> type)
>       | ^~~~~~~~~~~~~~~~~~~
> buildtools/chkincs/chkincs.p/rte_net_crc.c:3: error: expected ‘{’ at end of input
> In file included from buildtools/chkincs/chkincs.p/rte_net_crc.c:1:
> /home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:60:42: error: unused
> parameter ‘alg’ [-Werror=unused-parameter]
>    60 | rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type
> type)
>       |                     ~~~~~~~~~~~~~~~~~~~~~^~~
> /home/runner/work/dpdk/dpdk/lib/net/rte_net_crc.h:60:69: error: unused
> parameter ‘type’ [-Werror=unused-parameter]
>    60 | rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type
> type)
>       |                                               ~~~~~~~~~~~~~~~~~~~~~~^~~~
> buildtools/chkincs/chkincs.p/rte_net_crc.c:3: error: control reaches end of non-
> void function [-Werror=return-type]
> cc1: all warnings being treated as errors [629/2123] Compiling C object
> buildtools/chkincs/chkincs.p/meson-generated_rte_mpls.c.o
> [630/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-
> generated_rte_arp.c.o
> [631/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-
> generated_rte_ether.c.o
> [632/2123] Compiling C object buildtools/chkincs/chkincs.p/meson-
> generated_rte_net.c.o

^ permalink raw reply	[relevance 0%]

* [PATCH v7] net: add thread-safe crc api
  2025-02-10 21:27  4% ` [PATCH v6] " Arkadiusz Kusztal
  2025-02-11  6:23  0%   ` Stephen Hemminger
@ 2025-02-11  9:02  4%   ` Arkadiusz Kusztal
  1 sibling, 0 replies; 153+ results
From: Arkadiusz Kusztal @ 2025-02-11  9:02 UTC (permalink / raw)
  To: dev; +Cc: ferruh.yigit, kai.ji, brian.dooley, stephen, Arkadiusz Kusztal

The current net CRC API is not thread-safe, this patch
solves this by adding another, thread-safe API functions.
This API is also safe to use across multiple processes,
yet with limitations on max-simd-bitwidth, which will be checked only by
the process that created the CRC context; all other processes
(that did not create the context) will use the highest possible
SIMD extension that was built with the binary, but no higher than the one
requested by the CRC context.

Since the change of the API at this point is an ABI break,
these API symbols are versioned with the _26 suffix.

Signed-off-by: Arkadiusz Kusztal <arkadiuszx.kusztal@intel.com>
---
v2:
- added multi-process safety
v3:
- made the crc context opaque
- versioned old APIs
v4:
- exported rte_net_crc_free symbol
v5:
- fixed unclear comments in release notes section
- aligned `fall-through` comments
v6:
- fixed typos and code formatting
- added entry to the nullfree.cocci script
- added malloc attributes
- reverted copyright changes
v7:
- made net_crc header self-contained

 app/test/test_crc.c                    | 167 ++++++++++---------------
 devtools/cocci/nullfree.cocci          |   3 +
 doc/guides/rel_notes/release_25_03.rst |   5 +
 drivers/crypto/qat/qat_sym.h           |   6 +-
 drivers/crypto/qat/qat_sym_session.c   |   8 ++
 drivers/crypto/qat/qat_sym_session.h   |   2 +
 lib/net/meson.build                    |   2 +
 lib/net/net_crc.h                      |  16 +++
 lib/net/rte_net_crc.c                  | 127 ++++++++++++++++++-
 lib/net/rte_net_crc.h                  |  39 ++++--
 lib/net/version.map                    |   6 +
 11 files changed, 268 insertions(+), 113 deletions(-)

diff --git a/app/test/test_crc.c b/app/test/test_crc.c
index b85fca35fe..f18eff7217 100644
--- a/app/test/test_crc.c
+++ b/app/test/test_crc.c
@@ -44,131 +44,100 @@ static const uint32_t crc32_vec_res = 0xb491aab4;
 static const uint32_t crc32_vec1_res = 0xac54d294;
 static const uint32_t crc32_vec2_res = 0xefaae02f;
 static const uint32_t crc16_vec_res = 0x6bec;
-static const uint16_t crc16_vec1_res = 0x8cdd;
-static const uint16_t crc16_vec2_res = 0xec5b;
+static const uint32_t crc16_vec1_res = 0x8cdd;
+static const uint32_t crc16_vec2_res = 0xec5b;
 
 static int
-crc_calc(const uint8_t *vec,
-	uint32_t vec_len,
-	enum rte_net_crc_type type)
+crc_all_algs(const char *desc, enum rte_net_crc_type type,
+	const uint8_t *data, int data_len, uint32_t res)
 {
-	/* compute CRC */
-	uint32_t ret = rte_net_crc_calc(vec, vec_len, type);
+	struct rte_net_crc *ctx;
+	uint32_t crc;
+	int ret = TEST_SUCCESS;
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_SCALAR, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s SCALAR\n", desc);
+		debug_hexdump(stdout, "SCALAR", &crc, 4);
+		ret = TEST_FAILED;
+	}
+	rte_net_crc_free(ctx);
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_SSE42, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s SSE42\n", desc);
+		debug_hexdump(stdout, "SSE", &crc, 4);
+		ret = TEST_FAILED;
+	}
 
-	/* dump data on console */
-	debug_hexdump(stdout, NULL, vec, vec_len);
+	rte_net_crc_free(ctx);
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_AVX512, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s AVX512\n", desc);
+		debug_hexdump(stdout, "AVX512", &crc, 4);
+		ret = TEST_FAILED;
+	}
+	rte_net_crc_free(ctx);
+
+	ctx = rte_net_crc_set_alg(RTE_NET_CRC_NEON, type);
+	TEST_ASSERT_NOT_NULL(ctx, "cannot allocate the CRC context");
+	crc = rte_net_crc_calc(ctx, data, data_len);
+	if (crc != res) {
+		RTE_LOG(ERR, USER1, "TEST FAILED: %s NEON\n", desc);
+		debug_hexdump(stdout, "NEON", &crc, 4);
+		ret = TEST_FAILED;
+	}
+	rte_net_crc_free(ctx);
 
-	return  ret;
+	return ret;
 }
 
 static int
-test_crc_calc(void)
-{
+crc_autotest(void)
+{	uint8_t *test_data;
 	uint32_t i;
-	enum rte_net_crc_type type;
-	uint8_t *test_data;
-	uint32_t result;
-	int error;
+	int ret = TEST_SUCCESS;
 
 	/* 32-bit ethernet CRC: Test 1 */
-	type = RTE_NET_CRC32_ETH;
-
-	result = crc_calc(crc_vec, CRC_VEC_LEN, type);
-	if (result != crc32_vec_res)
-		return -1;
+	ret = crc_all_algs("32-bit ethernet CRC: Test 1", RTE_NET_CRC32_ETH, crc_vec,
+		sizeof(crc_vec), crc32_vec_res);
 
 	/* 32-bit ethernet CRC: Test 2 */
 	test_data = rte_zmalloc(NULL, CRC32_VEC_LEN1, 0);
 	if (test_data == NULL)
 		return -7;
-
 	for (i = 0; i < CRC32_VEC_LEN1; i += 12)
 		rte_memcpy(&test_data[i], crc32_vec1, 12);
-
-	result = crc_calc(test_data, CRC32_VEC_LEN1, type);
-	if (result != crc32_vec1_res) {
-		error = -2;
-		goto fail;
-	}
+	ret |= crc_all_algs("32-bit ethernet CRC: Test 2", RTE_NET_CRC32_ETH, test_data,
+		CRC32_VEC_LEN1, crc32_vec1_res);
 
 	/* 32-bit ethernet CRC: Test 3 */
+	memset(test_data, 0, CRC32_VEC_LEN1);
 	for (i = 0; i < CRC32_VEC_LEN2; i += 12)
 		rte_memcpy(&test_data[i], crc32_vec1, 12);
-
-	result = crc_calc(test_data, CRC32_VEC_LEN2, type);
-	if (result != crc32_vec2_res) {
-		error = -3;
-		goto fail;
-	}
+	ret |= crc_all_algs("32-bit ethernet CRC: Test 3", RTE_NET_CRC32_ETH, test_data,
+		CRC32_VEC_LEN2, crc32_vec2_res);
 
 	/* 16-bit CCITT CRC:  Test 4 */
-	type = RTE_NET_CRC16_CCITT;
-	result = crc_calc(crc_vec, CRC_VEC_LEN, type);
-	if (result != crc16_vec_res) {
-		error = -4;
-		goto fail;
-	}
-	/* 16-bit CCITT CRC:  Test 5 */
-	result = crc_calc(crc16_vec1, CRC16_VEC_LEN1, type);
-	if (result != crc16_vec1_res) {
-		error = -5;
-		goto fail;
-	}
-	/* 16-bit CCITT CRC:  Test 6 */
-	result = crc_calc(crc16_vec2, CRC16_VEC_LEN2, type);
-	if (result != crc16_vec2_res) {
-		error = -6;
-		goto fail;
-	}
-
-	rte_free(test_data);
-	return 0;
-
-fail:
-	rte_free(test_data);
-	return error;
-}
-
-static int
-test_crc(void)
-{
-	int ret;
-	/* set CRC scalar mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_SCALAR);
-
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test_crc (scalar): failed (%d)\n", ret);
-		return ret;
-	}
-	/* set CRC sse4.2 mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_SSE42);
+	crc_all_algs("16-bit CCITT CRC:  Test 4", RTE_NET_CRC16_CCITT, crc_vec,
+		sizeof(crc_vec), crc16_vec_res);
 
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test_crc (x86_64_SSE4.2): failed (%d)\n", ret);
-		return ret;
-	}
-
-	/* set CRC avx512 mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_AVX512);
-
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test crc (x86_64 AVX512): failed (%d)\n", ret);
-		return ret;
-	}
-
-	/* set CRC neon mode */
-	rte_net_crc_set_alg(RTE_NET_CRC_NEON);
+	/* 16-bit CCITT CRC:  Test 5 */
+	ret |= crc_all_algs("16-bit CCITT CRC:  Test 5", RTE_NET_CRC16_CCITT, crc16_vec1,
+		CRC16_VEC_LEN1, crc16_vec1_res);
 
-	ret = test_crc_calc();
-	if (ret < 0) {
-		printf("test crc (arm64 neon pmull): failed (%d)\n", ret);
-		return ret;
-	}
+	/* 16-bit CCITT CRC:  Test 6 */
+	ret |= crc_all_algs("16-bit CCITT CRC:  Test 6", RTE_NET_CRC16_CCITT, crc16_vec2,
+		CRC16_VEC_LEN2, crc16_vec2_res);
 
-	return 0;
+	return ret;
 }
 
-REGISTER_FAST_TEST(crc_autotest, true, true, test_crc);
+REGISTER_FAST_TEST(crc_autotest, true, true, crc_autotest);
diff --git a/devtools/cocci/nullfree.cocci b/devtools/cocci/nullfree.cocci
index c0526a2a3f..e7417b69ff 100644
--- a/devtools/cocci/nullfree.cocci
+++ b/devtools/cocci/nullfree.cocci
@@ -138,4 +138,7 @@ expression E;
 |
 - if (E != NULL) BN_free(E);
 + BN_free(E);
+|
+- if (E != NULL) rte_net_crc_free(E);
++ rte_net_crc_free(E);
 )
diff --git a/doc/guides/rel_notes/release_25_03.rst b/doc/guides/rel_notes/release_25_03.rst
index 3ef6f8f427..2693011b8f 100644
--- a/doc/guides/rel_notes/release_25_03.rst
+++ b/doc/guides/rel_notes/release_25_03.rst
@@ -165,6 +165,11 @@ API Changes
   but to enable/disable these drivers via Meson option requires use of the new paths.
   For example, ``-Denable_drivers=/net/i40e`` becomes ``-Denable_drivers=/net/intel/i40e``.
 
+* net: Changed the API for CRC calculation to be thread safe.
+  An opaque context argument was introduced to the net CRC API containing
+  the algorithm type and length. This argument is added to
+  to ``rte_net_crc_calc``, ``rte_net_crc_set_alg`` and freed with ``rte_net_crc_free``.
+  These functions are versioned to retain binary compatiabilty until the next LTS release.
 
 ABI Changes
 -----------
diff --git a/drivers/crypto/qat/qat_sym.h b/drivers/crypto/qat/qat_sym.h
index f42336d7ed..849e047615 100644
--- a/drivers/crypto/qat/qat_sym.h
+++ b/drivers/crypto/qat/qat_sym.h
@@ -267,8 +267,7 @@ qat_crc_verify(struct qat_sym_session *ctx, struct rte_crypto_op *op)
 		crc_data = rte_pktmbuf_mtod_offset(sym_op->m_src, uint8_t *,
 				crc_data_ofs);
 
-		crc = rte_net_crc_calc(crc_data, crc_data_len,
-				RTE_NET_CRC32_ETH);
+		crc = rte_net_crc_calc(ctx->crc, crc_data, crc_data_len);
 
 		if (crc != *(uint32_t *)(crc_data + crc_data_len))
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
@@ -291,8 +290,7 @@ qat_crc_generate(struct qat_sym_session *ctx,
 		crc_data = rte_pktmbuf_mtod_offset(sym_op->m_src, uint8_t *,
 				sym_op->auth.data.offset);
 		crc = (uint32_t *)(crc_data + crc_data_len);
-		*crc = rte_net_crc_calc(crc_data, crc_data_len,
-				RTE_NET_CRC32_ETH);
+		*crc = rte_net_crc_calc(ctx->crc, crc_data, crc_data_len);
 	}
 }
 
diff --git a/drivers/crypto/qat/qat_sym_session.c b/drivers/crypto/qat/qat_sym_session.c
index 50d687fd37..7200022adf 100644
--- a/drivers/crypto/qat/qat_sym_session.c
+++ b/drivers/crypto/qat/qat_sym_session.c
@@ -3174,6 +3174,14 @@ qat_sec_session_set_docsis_parameters(struct rte_cryptodev *dev,
 		ret = qat_sym_session_configure_crc(dev, xform, session);
 		if (ret < 0)
 			return ret;
+	} else {
+		/* Initialize crc algorithm */
+		session->crc = rte_net_crc_set_alg(RTE_NET_CRC_AVX512,
+			RTE_NET_CRC32_ETH);
+		if (session->crc == NULL) {
+			QAT_LOG(ERR, "Cannot initialize CRC context");
+			return -1;
+		}
 	}
 	qat_sym_session_finalize(session);
 
diff --git a/drivers/crypto/qat/qat_sym_session.h b/drivers/crypto/qat/qat_sym_session.h
index 2ca6c8ddf5..2ef2066646 100644
--- a/drivers/crypto/qat/qat_sym_session.h
+++ b/drivers/crypto/qat/qat_sym_session.h
@@ -7,6 +7,7 @@
 #include <rte_crypto.h>
 #include <cryptodev_pmd.h>
 #include <rte_security.h>
+#include <rte_net_crc.h>
 
 #include "qat_common.h"
 #include "icp_qat_hw.h"
@@ -149,6 +150,7 @@ struct qat_sym_session {
 	uint8_t is_zuc256;
 	uint8_t is_wireless;
 	uint32_t slice_types;
+	struct rte_net_crc *crc;
 	enum qat_sym_proto_flag qat_proto_flag;
 	qat_sym_build_request_t build_request[2];
 #ifndef RTE_QAT_OPENSSL
diff --git a/lib/net/meson.build b/lib/net/meson.build
index 8afcc4ed37..b26b377e8e 100644
--- a/lib/net/meson.build
+++ b/lib/net/meson.build
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2020 Intel Corporation
 
+use_function_versioning=true
+
 headers = files(
         'rte_cksum.h',
         'rte_ip.h',
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 7a74d5406c..a9a6c9542c 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -5,6 +5,22 @@
 #ifndef _NET_CRC_H_
 #define _NET_CRC_H_
 
+#include "rte_net_crc.h"
+
+void
+rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
+
+struct rte_net_crc *
+rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type);
+
+uint32_t
+rte_net_crc_calc_v25(const void *data,
+	uint32_t data_len, enum rte_net_crc_type type);
+
+uint32_t
+rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len);
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 346c285c15..2fb3eec231 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -10,6 +10,8 @@
 #include <rte_net_crc.h>
 #include <rte_log.h>
 #include <rte_vect.h>
+#include <rte_function_versioning.h>
+#include <rte_malloc.h>
 
 #include "net_crc.h"
 
@@ -38,11 +40,20 @@ rte_crc32_eth_handler(const uint8_t *data, uint32_t data_len);
 typedef uint32_t
 (*rte_net_crc_handler)(const uint8_t *data, uint32_t data_len);
 
+struct rte_net_crc {
+	enum rte_net_crc_alg alg;
+	enum rte_net_crc_type type;
+};
+
 static rte_net_crc_handler handlers_default[] = {
 	[RTE_NET_CRC16_CCITT] = rte_crc16_ccitt_default_handler,
 	[RTE_NET_CRC32_ETH] = rte_crc32_eth_default_handler,
 };
 
+static struct {
+	rte_net_crc_handler f[RTE_NET_CRC_REQS];
+} handlers_dpdk26[RTE_NET_CRC_AVX512 + 1];
+
 static const rte_net_crc_handler *handlers = handlers_default;
 
 static const rte_net_crc_handler handlers_scalar[] = {
@@ -286,10 +297,56 @@ rte_crc32_eth_default_handler(const uint8_t *data, uint32_t data_len)
 	return handlers[RTE_NET_CRC32_ETH](data, data_len);
 }
 
+static void
+handlers_init(enum rte_net_crc_alg alg)
+{
+	handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] = rte_crc16_ccitt_handler;
+	handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] = rte_crc32_eth_handler;
+
+	switch (alg) {
+	case RTE_NET_CRC_AVX512:
+#ifdef CC_X86_64_AVX512_VPCLMULQDQ_SUPPORT
+		if (AVX512_VPCLMULQDQ_CPU_SUPPORTED) {
+			handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] =
+				rte_crc16_ccitt_avx512_handler;
+			handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] =
+				rte_crc32_eth_avx512_handler;
+			break;
+		}
+#endif
+		/* fall-through */
+	case RTE_NET_CRC_SSE42:
+#ifdef CC_X86_64_SSE42_PCLMULQDQ_SUPPORT
+		if (SSE42_PCLMULQDQ_CPU_SUPPORTED) {
+			handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] =
+				rte_crc16_ccitt_sse42_handler;
+			handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] =
+				rte_crc32_eth_sse42_handler;
+		}
+#endif
+		break;
+	case RTE_NET_CRC_NEON:
+#ifdef CC_ARM64_NEON_PMULL_SUPPORT
+		if (NEON_PMULL_CPU_SUPPORTED) {
+			handlers_dpdk26[alg].f[RTE_NET_CRC16_CCITT] =
+				rte_crc16_ccitt_neon_handler;
+			handlers_dpdk26[alg].f[RTE_NET_CRC32_ETH] =
+				rte_crc32_eth_neon_handler;
+			break;
+		}
+#endif
+		/* fall-through */
+	case RTE_NET_CRC_SCALAR:
+		/* fall-through */
+	default:
+		break;
+	}
+}
+
 /* Public API */
 
 void
-rte_net_crc_set_alg(enum rte_net_crc_alg alg)
+rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -316,9 +373,59 @@ rte_net_crc_set_alg(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
+VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
+
+struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type)
+{
+	uint16_t max_simd_bitwidth;
+	struct rte_net_crc *crc;
+
+	crc = rte_zmalloc(NULL, sizeof(struct rte_net_crc), 0);
+	if (crc == NULL)
+		return NULL;
+	max_simd_bitwidth = rte_vect_get_max_simd_bitwidth();
+	crc->type = type;
+	crc->alg = RTE_NET_CRC_SCALAR;
+
+	switch (alg) {
+	case RTE_NET_CRC_AVX512:
+		if (max_simd_bitwidth >= RTE_VECT_SIMD_512) {
+			crc->alg = RTE_NET_CRC_AVX512;
+			return crc;
+		}
+		/* fall-through */
+	case RTE_NET_CRC_SSE42:
+		if (max_simd_bitwidth >= RTE_VECT_SIMD_128) {
+			crc->alg = RTE_NET_CRC_SSE42;
+			return crc;
+		}
+		break;
+	case RTE_NET_CRC_NEON:
+		if (max_simd_bitwidth >= RTE_VECT_SIMD_128) {
+			crc->alg = RTE_NET_CRC_NEON;
+			return crc;
+		}
+		break;
+	case RTE_NET_CRC_SCALAR:
+		/* fall-through */
+	default:
+		break;
+	}
+	return crc;
+}
+BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
+MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
+	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
+	rte_net_crc_set_alg_v26);
+
+void rte_net_crc_free(struct rte_net_crc *crc)
+{
+	rte_free(crc);
+}
 
 uint32_t
-rte_net_crc_calc(const void *data,
+rte_net_crc_calc_v25(const void *data,
 	uint32_t data_len,
 	enum rte_net_crc_type type)
 {
@@ -330,6 +437,18 @@ rte_net_crc_calc(const void *data,
 
 	return ret;
 }
+VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
+
+uint32_t
+rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len)
+{
+	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
+}
+BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
+MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len),
+	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
@@ -338,4 +457,8 @@ RTE_INIT(rte_net_crc_init)
 	sse42_pclmulqdq_init();
 	avx512_vpclmulqdq_init();
 	neon_pmull_init();
+	handlers_init(RTE_NET_CRC_SCALAR);
+	handlers_init(RTE_NET_CRC_NEON);
+	handlers_init(RTE_NET_CRC_SSE42);
+	handlers_init(RTE_NET_CRC_AVX512);
 }
diff --git a/lib/net/rte_net_crc.h b/lib/net/rte_net_crc.h
index 72d3e10ff6..3f350b3001 100644
--- a/lib/net/rte_net_crc.h
+++ b/lib/net/rte_net_crc.h
@@ -6,6 +6,7 @@
 #define _RTE_NET_CRC_H_
 
 #include <stdint.h>
+#include <rte_common.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -26,8 +27,21 @@ enum rte_net_crc_alg {
 	RTE_NET_CRC_AVX512,
 };
 
+/** CRC context (algorithm, type) */
+struct rte_net_crc;
+
 /**
- * This API set the CRC computation algorithm (i.e. scalar version,
+ * Frees the memory space pointed to by the CRC context pointer.
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ctx
+ *   Pointer to the CRC context
+ */
+void
+rte_net_crc_free(struct rte_net_crc *crc);
+
+/**
+ * This API set the CRC context (i.e. scalar version,
  * x86 64-bit sse4.2 intrinsic version, etc.) and internal data
  * structure.
  *
@@ -37,27 +51,36 @@ enum rte_net_crc_alg {
  *   - RTE_NET_CRC_SSE42 (Use 64-bit SSE4.2 intrinsic)
  *   - RTE_NET_CRC_NEON (Use ARM Neon intrinsic)
  *   - RTE_NET_CRC_AVX512 (Use 512-bit AVX intrinsic)
+ * @param type
+ *   CRC type (enum rte_net_crc_type)
+ *
+ * @return
+ *   Pointer to the CRC context
  */
-void
-rte_net_crc_set_alg(enum rte_net_crc_alg alg);
+struct rte_net_crc *
+rte_net_crc_set_alg(enum rte_net_crc_alg alg, enum rte_net_crc_type type)
+	__rte_malloc __rte_dealloc(rte_net_crc_free, 1);
 
 /**
  * CRC compute API
  *
+ * Note:
+ * The command line argument --force-max-simd-bitwidth will be ignored
+ * by processes that have not created this CRC context.
+ *
+ * @param ctx
+ *   Pointer to the CRC context
  * @param data
  *   Pointer to the packet data for CRC computation
  * @param data_len
  *   Data length for CRC computation
- * @param type
- *   CRC type (enum rte_net_crc_type)
  *
  * @return
  *   CRC value
  */
 uint32_t
-rte_net_crc_calc(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type);
+rte_net_crc_calc(const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len);
 
 #ifdef __cplusplus
 }
diff --git a/lib/net/version.map b/lib/net/version.map
index bec4ce23ea..d03f3f6ad0 100644
--- a/lib/net/version.map
+++ b/lib/net/version.map
@@ -5,6 +5,7 @@ DPDK_25 {
 	rte_ether_format_addr;
 	rte_ether_unformat_addr;
 	rte_net_crc_calc;
+	rte_net_crc_free;
 	rte_net_crc_set_alg;
 	rte_net_get_ptype;
 	rte_net_make_rarp_packet;
@@ -12,3 +13,8 @@ DPDK_25 {
 
 	local: *;
 };
+
+DPDK_26 {
+	rte_net_crc_calc;
+	rte_net_crc_set_alg;
+} DPDK_25;
-- 
2.34.1


^ permalink raw reply	[relevance 4%]

* RE: [PATCH v5 3/4] drivers: move iavf common folder to iavf net
  2025-02-10 16:44  2%   ` [PATCH v5 3/4] drivers: move iavf common folder to iavf net Bruce Richardson
@ 2025-02-11 14:12  0%     ` Stokes, Ian
  0 siblings, 0 replies; 153+ results
From: Stokes, Ian @ 2025-02-11 14:12 UTC (permalink / raw)
  To: Richardson, Bruce, dev; +Cc: Richardson, Bruce

> The common/iavf driver folder contains the base code for the iavf
> driver, which is also linked against by the ice driver and others.
> However, there is no need for this to be in common, and we can
> move it to the net/intel/iavf as a base code driver. This involves
> updating dependencies that were on common/iavf to net/iavf
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  devtools/libabigail.abignore                       |  1 +
>  doc/guides/rel_notes/release_25_03.rst             |  5 ++++-
>  drivers/common/iavf/version.map                    | 13 -------------
>  drivers/common/meson.build                         |  1 -
>  .../{common/iavf => net/intel/iavf/base}/README    |  0
>  .../iavf => net/intel/iavf/base}/iavf_adminq.c     |  0
>  .../iavf => net/intel/iavf/base}/iavf_adminq.h     |  0
>  .../iavf => net/intel/iavf/base}/iavf_adminq_cmd.h |  0
>  .../iavf => net/intel/iavf/base}/iavf_alloc.h      |  0
>  .../iavf => net/intel/iavf/base}/iavf_common.c     |  0
>  .../iavf => net/intel/iavf/base}/iavf_devids.h     |  0
>  .../iavf => net/intel/iavf/base}/iavf_impl.c       |  0
>  .../iavf => net/intel/iavf/base}/iavf_osdep.h      |  0
>  .../iavf => net/intel/iavf/base}/iavf_prototype.h  |  0
>  .../iavf => net/intel/iavf/base}/iavf_register.h   |  0
>  .../iavf => net/intel/iavf/base}/iavf_status.h     |  0
>  .../iavf => net/intel/iavf/base}/iavf_type.h       |  0
>  .../iavf => net/intel/iavf/base}/meson.build       |  0
>  .../iavf => net/intel/iavf/base}/virtchnl.h        |  0
>  .../intel/iavf/base}/virtchnl_inline_ipsec.h       |  0
>  drivers/net/intel/iavf/meson.build                 | 13 +++++++++----
>  drivers/net/intel/iavf/version.map                 | 14 ++++++++++++++
>  drivers/net/intel/ice/meson.build                  |  7 +++----
>  drivers/net/intel/idpf/meson.build                 |  2 +-
>  24 files changed, 32 insertions(+), 24 deletions(-)
>  delete mode 100644 drivers/common/iavf/version.map
>  rename drivers/{common/iavf => net/intel/iavf/base}/README (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_adminq.c (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_adminq.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_adminq_cmd.h
> (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_alloc.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_common.c
> (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_devids.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_impl.c (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_osdep.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_prototype.h
> (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_register.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_status.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/iavf_type.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/meson.build (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/virtchnl.h (100%)
>  rename drivers/{common/iavf => net/intel/iavf/base}/virtchnl_inline_ipsec.h
> (100%)
> 
> diff --git a/devtools/libabigail.abignore b/devtools/libabigail.abignore
> index b7daca4841..ce501632b3 100644
> --- a/devtools/libabigail.abignore
> +++ b/devtools/libabigail.abignore
> @@ -23,6 +23,7 @@
>  ; This is not a libabigail rule (see check-abi.sh).
>  ; This is used for driver removal and other special cases like mlx glue libs.
>  ;
> +; SKIP_LIBRARY=librte_common_iavf
>  ; SKIP_LIBRARY=librte_common_idpf
>  ; SKIP_LIBRARY=librte_common_mlx5_glue
>  ; SKIP_LIBRARY=librte_net_mlx4_glue
> diff --git a/doc/guides/rel_notes/release_25_03.rst
> b/doc/guides/rel_notes/release_25_03.rst
> index 2338a97e76..d2e8b03107 100644
> --- a/doc/guides/rel_notes/release_25_03.rst
> +++ b/doc/guides/rel_notes/release_25_03.rst
> @@ -182,9 +182,12 @@ API Changes
>    ``-Denable_drivers=net/intel/e1000``.
> 
>  * The driver ``common/idpf`` has been merged into the ``net/intel/idpf``
> driver.
> -  This change should have no impact to end applications, but,
> +  Similarly, the ``common/iavf`` driver has been merged into the
> ``net/intel/iavf`` driver.
> +  These changes should have no impact to end applications, but,
>    when specifying the ``idpf`` or ``cpfl`` net drivers to meson via ``-
> Denable_drivers`` option,
>    there is no longer any need to also specify the ``common/idpf`` driver.
> +  In the same way, when specifying the ``iavf`` or ``ice`` net drivers,
> +  there is no need to also specify the ``common/iavf`` driver.
>    Note, however, ``net/intel/cpfl`` driver now depends upon the
> ``net/intel/idpf`` driver.
> 
> 
> diff --git a/drivers/common/iavf/version.map
> b/drivers/common/iavf/version.map
> deleted file mode 100644
> index 6c1427cca4..0000000000
> --- a/drivers/common/iavf/version.map
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -INTERNAL {
> -	global:
> -
> -	iavf_aq_send_msg_to_pf;
> -	iavf_clean_arq_element;
> -	iavf_init_adminq;
> -	iavf_set_mac_type;
> -	iavf_shutdown_adminq;
> -	iavf_vf_parse_hw_config;
> -	iavf_vf_reset;
> -
> -	local: *;
> -};
> diff --git a/drivers/common/meson.build b/drivers/common/meson.build
> index e1e3149d8f..dc096aab0a 100644
> --- a/drivers/common/meson.build
> +++ b/drivers/common/meson.build
> @@ -5,7 +5,6 @@ std_deps = ['eal']
>  drivers = [
>          'cpt',
>          'dpaax',
> -        'iavf',
>          'ionic',
>          'mvep',
>          'octeontx',
> diff --git a/drivers/common/iavf/README
> b/drivers/net/intel/iavf/base/README
> similarity index 100%
> rename from drivers/common/iavf/README
> rename to drivers/net/intel/iavf/base/README
> diff --git a/drivers/common/iavf/iavf_adminq.c
> b/drivers/net/intel/iavf/base/iavf_adminq.c
> similarity index 100%
> rename from drivers/common/iavf/iavf_adminq.c
> rename to drivers/net/intel/iavf/base/iavf_adminq.c
> diff --git a/drivers/common/iavf/iavf_adminq.h
> b/drivers/net/intel/iavf/base/iavf_adminq.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_adminq.h
> rename to drivers/net/intel/iavf/base/iavf_adminq.h
> diff --git a/drivers/common/iavf/iavf_adminq_cmd.h
> b/drivers/net/intel/iavf/base/iavf_adminq_cmd.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_adminq_cmd.h
> rename to drivers/net/intel/iavf/base/iavf_adminq_cmd.h
> diff --git a/drivers/common/iavf/iavf_alloc.h
> b/drivers/net/intel/iavf/base/iavf_alloc.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_alloc.h
> rename to drivers/net/intel/iavf/base/iavf_alloc.h
> diff --git a/drivers/common/iavf/iavf_common.c
> b/drivers/net/intel/iavf/base/iavf_common.c
> similarity index 100%
> rename from drivers/common/iavf/iavf_common.c
> rename to drivers/net/intel/iavf/base/iavf_common.c
> diff --git a/drivers/common/iavf/iavf_devids.h
> b/drivers/net/intel/iavf/base/iavf_devids.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_devids.h
> rename to drivers/net/intel/iavf/base/iavf_devids.h
> diff --git a/drivers/common/iavf/iavf_impl.c
> b/drivers/net/intel/iavf/base/iavf_impl.c
> similarity index 100%
> rename from drivers/common/iavf/iavf_impl.c
> rename to drivers/net/intel/iavf/base/iavf_impl.c
> diff --git a/drivers/common/iavf/iavf_osdep.h
> b/drivers/net/intel/iavf/base/iavf_osdep.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_osdep.h
> rename to drivers/net/intel/iavf/base/iavf_osdep.h
> diff --git a/drivers/common/iavf/iavf_prototype.h
> b/drivers/net/intel/iavf/base/iavf_prototype.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_prototype.h
> rename to drivers/net/intel/iavf/base/iavf_prototype.h
> diff --git a/drivers/common/iavf/iavf_register.h
> b/drivers/net/intel/iavf/base/iavf_register.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_register.h
> rename to drivers/net/intel/iavf/base/iavf_register.h
> diff --git a/drivers/common/iavf/iavf_status.h
> b/drivers/net/intel/iavf/base/iavf_status.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_status.h
> rename to drivers/net/intel/iavf/base/iavf_status.h
> diff --git a/drivers/common/iavf/iavf_type.h
> b/drivers/net/intel/iavf/base/iavf_type.h
> similarity index 100%
> rename from drivers/common/iavf/iavf_type.h
> rename to drivers/net/intel/iavf/base/iavf_type.h
> diff --git a/drivers/common/iavf/meson.build
> b/drivers/net/intel/iavf/base/meson.build
> similarity index 100%
> rename from drivers/common/iavf/meson.build
> rename to drivers/net/intel/iavf/base/meson.build
> diff --git a/drivers/common/iavf/virtchnl.h
> b/drivers/net/intel/iavf/base/virtchnl.h
> similarity index 100%
> rename from drivers/common/iavf/virtchnl.h
> rename to drivers/net/intel/iavf/base/virtchnl.h
> diff --git a/drivers/common/iavf/virtchnl_inline_ipsec.h
> b/drivers/net/intel/iavf/base/virtchnl_inline_ipsec.h
> similarity index 100%
> rename from drivers/common/iavf/virtchnl_inline_ipsec.h
> rename to drivers/net/intel/iavf/base/virtchnl_inline_ipsec.h
> diff --git a/drivers/net/intel/iavf/meson.build
> b/drivers/net/intel/iavf/meson.build
> index d9b605f55a..c823d618e3 100644
> --- a/drivers/net/intel/iavf/meson.build
> +++ b/drivers/net/intel/iavf/meson.build
> @@ -7,9 +7,13 @@ endif
> 
>  testpmd_sources = files('iavf_testpmd.c')
> 
> -deps += ['common_iavf', 'security', 'cryptodev']
> +deps += ['security', 'cryptodev']
> 
>  sources = files(
> +        'base/iavf_adminq.c',
> +        'base/iavf_common.c',
> +        'base/iavf_impl.c',
> +
>          'iavf_ethdev.c',
>          'iavf_rxtx.c',
>          'iavf_vchnl.c',
> @@ -20,8 +24,9 @@ sources = files(
>          'iavf_ipsec_crypto.c',
>          'iavf_fsub.c',
>  )
> +includes += include_directories('base')
> 
> -if arch_subdir == 'x86' and is_variable('static_rte_common_iavf')
> +if arch_subdir == 'x86'
>      sources += files('iavf_rxtx_vec_sse.c')
> 
>      if is_windows and cc.get_id() != 'clang'
> @@ -30,7 +35,7 @@ if arch_subdir == 'x86' and
> is_variable('static_rte_common_iavf')
> 
>      iavf_avx2_lib = static_library('iavf_avx2_lib',
>              'iavf_rxtx_vec_avx2.c',
> -            dependencies: [static_rte_ethdev, static_rte_common_iavf],
> +            dependencies: [static_rte_ethdev],
>              include_directories: includes,
>              c_args: [cflags, '-mavx2'])
>      objs += iavf_avx2_lib.extract_objects('iavf_rxtx_vec_avx2.c')
> @@ -43,7 +48,7 @@ if arch_subdir == 'x86' and
> is_variable('static_rte_common_iavf')
>          endif
>          iavf_avx512_lib = static_library('iavf_avx512_lib',
>                  'iavf_rxtx_vec_avx512.c',
> -                dependencies: [static_rte_ethdev, static_rte_common_iavf],
> +                dependencies: [static_rte_ethdev],
>                  include_directories: includes,
>                  c_args: avx512_args)
>          objs += iavf_avx512_lib.extract_objects('iavf_rxtx_vec_avx512.c')
> diff --git a/drivers/net/intel/iavf/version.map
> b/drivers/net/intel/iavf/version.map
> index 98de64cca2..d18dea64dd 100644
> --- a/drivers/net/intel/iavf/version.map
> +++ b/drivers/net/intel/iavf/version.map
> @@ -17,3 +17,17 @@ EXPERIMENTAL {
>  	# added in 21.11
>  	rte_pmd_ifd_dynflag_proto_xtr_ipsec_crypto_said_mask;
>  };
> +
> +INTERNAL {
> +	global:
> +
> +	iavf_aq_send_msg_to_pf;
> +	iavf_clean_arq_element;
> +	iavf_init_adminq;
> +	iavf_set_mac_type;
> +	iavf_shutdown_adminq;
> +	iavf_vf_parse_hw_config;
> +	iavf_vf_reset;
> +
> +	local: *;
> +};
> diff --git a/drivers/net/intel/ice/meson.build
> b/drivers/net/intel/ice/meson.build
> index beaf21e176..5faf887386 100644
> --- a/drivers/net/intel/ice/meson.build
> +++ b/drivers/net/intel/ice/meson.build
> @@ -18,7 +18,7 @@ sources = files(
> 
>  testpmd_sources = files('ice_testpmd.c')
> 
> -deps += ['hash', 'net', 'common_iavf']
> +deps += ['hash', 'net', 'net_iavf']
>  includes += include_directories('base')
> 
>  if arch_subdir == 'x86'
> @@ -30,7 +30,7 @@ if arch_subdir == 'x86'
> 
>      ice_avx2_lib = static_library('ice_avx2_lib',
>              'ice_rxtx_vec_avx2.c',
> -            dependencies: [static_rte_ethdev, static_rte_kvargs, static_rte_hash],
> +            dependencies: [static_rte_ethdev, static_rte_hash],
>              include_directories: includes,
>              c_args: [cflags, '-mavx2'])
>      objs += ice_avx2_lib.extract_objects('ice_rxtx_vec_avx2.c')
> @@ -43,8 +43,7 @@ if arch_subdir == 'x86'
>          endif
>          ice_avx512_lib = static_library('ice_avx512_lib',
>                  'ice_rxtx_vec_avx512.c',
> -                dependencies: [static_rte_ethdev,
> -                    static_rte_kvargs, static_rte_hash],
> +                dependencies: [static_rte_ethdev, static_rte_hash],
>                  include_directories: includes,
>                  c_args: avx512_args)
>          objs += ice_avx512_lib.extract_objects('ice_rxtx_vec_avx512.c')
> diff --git a/drivers/net/intel/idpf/meson.build
> b/drivers/net/intel/idpf/meson.build
> index 87bc39f76e..d69254484b 100644
> --- a/drivers/net/intel/idpf/meson.build
> +++ b/drivers/net/intel/idpf/meson.build
> @@ -7,7 +7,7 @@ if is_windows
>      subdir_done()
>  endif
> 
> -includes += include_directories('../../../common/iavf')
> +includes += include_directories('../iavf/base')
> 
>  sources = files(
>          'idpf_common_device.c',
> --
> 2.43.0

Patch looks good to me Bruce.

Acked-by: Ian Stokes <ian.stokes@intel.com>


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v5 02/11] eal: add new secure free function
  @ 2025-02-12  6:46  3%       ` Stephen Hemminger
  0 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-02-12  6:46 UTC (permalink / raw)
  To: fengchengwen; +Cc: dev, Anatoly Burakov, Tyler Retzlaff

On Wed, 12 Feb 2025 10:01:13 +0800
fengchengwen <fengchengwen@huawei.com> wrote:

> On 2025/2/12 1:35, Stephen Hemminger wrote:
> > Although internally rte_free does poison the buffer in most
> > cases, it is useful to have function that explicitly does
> > this to avoid any security issues.
> > 
> > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> > ---
> >  lib/eal/common/rte_malloc.c  | 30 ++++++++++++++++++++++++------
> >  lib/eal/include/rte_malloc.h | 18 ++++++++++++++++++
> >  lib/eal/version.map          |  3 +++
> >  3 files changed, 45 insertions(+), 6 deletions(-)
> > 
> > diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c
> > index 3eed4d4be6..c9e0f4724f 100644
> > --- a/lib/eal/common/rte_malloc.c
> > +++ b/lib/eal/common/rte_malloc.c
> > @@ -15,6 +15,7 @@
> >  #include <rte_eal_memconfig.h>
> >  #include <rte_common.h>
> >  #include <rte_spinlock.h>
> > +#include <rte_string_fns.h>
> >  
> >  #include <eal_trace_internal.h>
> >  
> > @@ -27,27 +28,44 @@
> >  
> >  
> >  /* Free the memory space back to heap */
> > -static void
> > -mem_free(void *addr, const bool trace_ena)
> > +static inline void
> > +mem_free(void *addr, const bool trace_ena, bool zero)
> >  {
> > +	struct malloc_elem *elem;
> > +
> >  	if (trace_ena)
> >  		rte_eal_trace_mem_free(addr);
> >  
> > -	if (addr == NULL) return;
> > -	if (malloc_heap_free(malloc_elem_from_data(addr)) < 0)
> > +	if (addr == NULL)
> > +		return;
> > +
> > +	elem = malloc_elem_from_data(addr);
> > +	if (zero) {
> > +		size_t data_len = elem->size - MALLOC_ELEM_OVERHEAD;  
> 
> this will make rte_malloc know the layout of malloc-elem.
> Prefer to add extra paramter, e.g. malloc_heap_free(elem, bool zero)

Don't understand, these are functions inside the rte_malloc implementation file.
The layout of malloc_elem is already known here and nothing visible in API or ABI is changing.
>   
> > +
> > +/**
> > + * Frees the memory space pointed to by the provided pointer
> > + * and guarantees it will be zero'd before reuse.
> > + *
> > + * This pointer must have been returned by a previous call to
> > + * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
> > + * rte_free() is undefined if the pointer does not match this requirement.  
> 
> Suggest add notice: The value may be cleared twice, which affects the performance.

That could easily change with a little work, this is only for crypto keys
so performance doesn't matter.

> > + *
> > + * If the pointer is NULL, the function does nothing.
> > + *
> > + * @param ptr
> > + *   The pointer to memory to be freed.
> > + */
> > +__rte_experimental
> > +void
> > +rte_free_sensitive(void *ptr);  
> 
> one line is OK.
> void rte_free_sensitive(void *ptr);

Yes it could be on one line, and more compact is my preferred style.
But other functions in this file and DPDK style guide say it should be on its own line.





^ permalink raw reply	[relevance 3%]

* Re: [PATCH v22 00/27] remove use of VLAs for Windows
  @ 2025-02-18 14:22  0%       ` David Marchand
  2025-02-19 14:28  0%         ` Konstantin Ananyev
  0 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-02-18 14:22 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: Andre Muezerie, dev, thomas, honnappa.nagarahalli

On Fri, Feb 7, 2025 at 3:23 PM Konstantin Ananyev
<konstantin.ananyev@huawei.com> wrote:
> > > As per guidance technical board meeting 2024/04/17. This series
> > > removes the use of VLAs from code built for Windows for all 3
> > > toolchains. If there are additional opportunities to convert VLAs
> > > to regular C arrays please provide the details for incorporation
> > > into the series.
> > >
> > > MSVC does not support VLAs, replace VLAs with standard C arrays
> > > or alloca(). alloca() is available for all toolchain/platform
> > > combinations officially supported by DPDK.
> >
> > - I have one concern wrt patch 7.
> > This changes the API/ABI of the RCU library.
> > ABI can't be broken in the 25.03 release.
> >
> > Since MSVC builds do not include RCU yet, I skipped this change and
> > adjusted this libray meson.build.
> >
> > Konstantin, do you think patch 7 could be rewritten to make use of
> > alloca() and avoid an API change?
> > https://patchwork.dpdk.org/project/dpdk/patch/1738805610-17507-8-git-send-email-andremue@linux.microsoft.com/
>
> I am not big fan of alloca() approach, but yes it is surely possible.

Can you please explain your reluctance?


> BTW, why it is considered ad API/ABI change?
> Because we introduce extra limit on max allowable size?

Yes, this is what was mentionned in the commitlog.


> If that would help somehow, we can make it even bigger: 1K or so.

Strictly speaking, it is still an API change.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* RE: [PATCH v22 00/27] remove use of VLAs for Windows
  2025-02-18 14:22  0%       ` David Marchand
@ 2025-02-19 14:28  0%         ` Konstantin Ananyev
  0 siblings, 0 replies; 153+ results
From: Konstantin Ananyev @ 2025-02-19 14:28 UTC (permalink / raw)
  To: David Marchand; +Cc: Andre Muezerie, dev, thomas, honnappa.nagarahalli


> > > > As per guidance technical board meeting 2024/04/17. This series
> > > > removes the use of VLAs from code built for Windows for all 3
> > > > toolchains. If there are additional opportunities to convert VLAs
> > > > to regular C arrays please provide the details for incorporation
> > > > into the series.
> > > >
> > > > MSVC does not support VLAs, replace VLAs with standard C arrays
> > > > or alloca(). alloca() is available for all toolchain/platform
> > > > combinations officially supported by DPDK.
> > >
> > > - I have one concern wrt patch 7.
> > > This changes the API/ABI of the RCU library.
> > > ABI can't be broken in the 25.03 release.
> > >
> > > Since MSVC builds do not include RCU yet, I skipped this change and
> > > adjusted this libray meson.build.
> > >
> > > Konstantin, do you think patch 7 could be rewritten to make use of
> > > alloca() and avoid an API change?
> > > https://patchwork.dpdk.org/project/dpdk/patch/1738805610-17507-8-git-send-email-andremue@linux.microsoft.com/
> >
> > I am not big fan of alloca() approach, but yes it is surely possible.
> 
> Can you please explain your reluctance?

Mostly conceptual: using alloca() doesn't really differ from simply using VLA,
in fact it makes code looks uglier.
I understand that we do want MSVC enabled, and in many cases such mechanical
replacement is ok, but probably better to avoid  it whenever possible.  

> 
> 
> > BTW, why it is considered ad API/ABI change?
> > Because we introduce extra limit on max allowable size?
> 
> Yes, this is what was mentionned in the commitlog.
> 
> 
> > If that would help somehow, we can make it even bigger: 1K or so.
> 
> Strictly speaking, it is still an API change.
 
Ok, then I suppose we have 3 options:
1) wait for 25.11 to apply these changes
2) use alloca()
3) come-up with some smarter approach.

For 3) - I don't have any good ideas right now.
One option would be to allow ring_enqueue/ring_dequeue to accept custom copy_elem() functions as a parameter.
That would solve an issue, as in that case we wouldn't need to make temp copy of data on the stack,
but that's probably too big changes for such small thing.
So I am ok with both 1) and 2).
In fact - it is probably possible to go with 2) for now, and then switch to 1) or 3) in 25.11


^ permalink raw reply	[relevance 0%]

* [RFC PATCH v18] mempool: fix mempool cache size
  @ 2025-02-21 15:13  4% ` Morten Brørup
  2025-02-21 19:05  3% ` [RFC PATCH v19] " Morten Brørup
  2025-02-21 20:27  3% ` [RFC PATCH v20] " Morten Brørup
  2 siblings, 0 replies; 153+ results
From: Morten Brørup @ 2025-02-21 15:13 UTC (permalink / raw)
  To: dev; +Cc: Morten Brørup

NOTE: THIS VERSION DOES NOT BREAK THE API/ABI.

First, a per-lcore mempool cache could hold 50 % more than the cache's
size.
Since application developers do not expect this behavior, it could lead to
application failure.
This patch fixes this bug without breaking the API/ABI, by using the
mempool cache's "size" instead of the "flushthresh" as the threshold for
how many objects can be held in a mempool cache.
Note: The "flushthresh" field can be removed from the cache structure in a
future API/ABI breaking release, which must be announced in advance.

Second, requests to fetch a number of objects from the backend driver
exceeding the cache's size (but less than RTE_MEMPOOL_CACHE_MAX_SIZE) were
copied twice; first to the cache, and from there to the destination.
Such superfluous copying through the mempool cache degrades the
performance in these cases.
This patch also fixes this misbehavior, so when fetching more objects from
the driver than the mempool cache's size, they are fetched directly to the
destination.

The internal macro to calculate the cache flush threshold was updated to
reflect the new flush threshold of 1 * size instead of 1.5 * size.

The function rte_mempool_do_generic_put() for adding objects to a mempool
was modified as follows:
- When determining if the cache has sufficient room for the request
  without flushing, compare to the cache's size (cache->size) instead of
  the obsolete flush threshold (cache->flushthresh).
- The comparison for the request being too big, which is considered
  unlikely, was moved down and out of the code path where the cache has
  sufficient room for the added objects, which is considered the most
  likely code path.

The function rte_mempool_do_generic_get() for getting objects from a
mempool was refactored as follows:
- Handling a request for a constant number of objects was merged with
  handling a request for a nonconstant number of objects, and a note about
  compiler loop unrolling in the constant case was added.
- When determining if the remaining part of a request to be dequeued from
  the backend is too big to be copied via the cache, compare to the
  cache's size (cache->size) instead of the max possible cache size
  (RTE_MEMPOOL_CACHE_MAX_SIZE).
- When refilling the cache, the target fill level was reduced from the
  full cache size to half the cache size. This allows some room for a
  put() request following a get() request where the cache was refilled,
  without "flapping" between draining and refilling the entire cache.
  Note: Before this patch, the distance between the flush threshold and
  the refill level was also half a cache size.
- A copy of cache->len in the local variable "len" is no longer needed,
  so it was removed.

Furthermore, some likely()/unlikely()'s were added to a few inline
functions; most prominently rte_mempool_default_cache(), which is used by
both rte_mempool_put_bulk() and rte_mempool_get_bulk().

And finally, some comments were updated.

Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
---
v18:
* Start over from scratch, to avoid API/ABI breakage.
v17:
* Update rearm in idpf driver.
v16:
* Fix bug in rte_mempool_do_generic_put() regarding criteria for flush.
v15:
* Changed back cache bypass limit from n >= RTE_MEMPOOL_CACHE_MAX_SIZE to
  n > RTE_MEMPOOL_CACHE_MAX_SIZE.
* Removed cache size limit from serving via cache.
v14:
* Change rte_mempool_do_generic_put() back from add-then-flush to
  flush-then-add.
  Keep the target cache fill level of ca. 1/2 size of the cache.
v13:
* Target a cache fill level of ca. 1/2 size of the cache when flushing and
  refilling; based on an assumption of equal probability of get and put,
  instead of assuming a higher probability of put being followed by
  another put, and get being followed by another get.
* Reduce the amount of changes to the drivers.
v12:
* Do not init mempool caches with size zero; they don't exist.
  Bug introduced in v10.
v11:
* Removed rte_mempool_do_generic_get_split().
v10:
* Initialize mempool caches, regardless of size zero.
  This to fix compiler warning about out of bounds access.
v9:
* Removed factor 1.5 from description of cache_size parameter to
  rte_mempool_create().
* Refactored rte_mempool_do_generic_put() to eliminate some gotos.
  No functional change.
* Removed check for n >= RTE_MEMPOOL_CACHE_MAX_SIZE in
  rte_mempool_do_generic_get(); it caused the function to fail when the
  request could not be served from the backend alone, but it could be
  served from the cache and the backend.
* Refactored rte_mempool_do_generic_get_split() to make it shorter.
* When getting objects directly from the backend, use burst size aligned
  with either CPU cache line size or mempool cache size.
v8:
* Rewrote rte_mempool_do_generic_put() to get rid of transaction
  splitting. Use a method similar to the existing put method with fill
  followed by flush if overfilled.
  This also made rte_mempool_do_generic_put_split() obsolete.
* When flushing the cache as much as we can, use burst size aligned with
  either CPU cache line size or mempool cache size.
v7:
* Increased max mempool cache size from 512 to 1024 objects.
  Mainly for CI performance test purposes.
  Originally, the max mempool cache size was 768 objects, and used a fixed
  size array of 1024 objects in the mempool cache structure.
v6:
* Fix v5 incomplete implementation of passing large requests directly to
  the backend.
* Use memcpy instead of rte_memcpy where compiler complains about it.
* Added const to some function parameters.
v5:
* Moved helper functions back into the header file, for improved
  performance.
* Pass large requests directly to the backend. This also simplifies the
  code.
v4:
* Updated subject to reflect that misleading names are considered bugs.
* Rewrote patch description to provide more details about the bugs fixed.
  (Mattias Rönnblom)
* Moved helper functions, not to be inlined, to mempool C file.
  (Mattias Rönnblom)
* Pass requests for n >= RTE_MEMPOOL_CACHE_MAX_SIZE objects known at build
  time directly to backend driver, to avoid calling the helper functions.
  This also fixes the compiler warnings about out of bounds array access.
v3:
* Removed __attribute__(assume).
v2:
* Removed mempool perf test; not part of patch set.
---
 lib/mempool/rte_mempool.c |  5 +-
 lib/mempool/rte_mempool.h | 98 +++++++++++++++------------------------
 2 files changed, 40 insertions(+), 63 deletions(-)

diff --git a/lib/mempool/rte_mempool.c b/lib/mempool/rte_mempool.c
index 1e4f24783c..cddc896442 100644
--- a/lib/mempool/rte_mempool.c
+++ b/lib/mempool/rte_mempool.c
@@ -50,10 +50,9 @@ static void
 mempool_event_callback_invoke(enum rte_mempool_event event,
 			      struct rte_mempool *mp);
 
-/* Note: avoid using floating point since that compiler
- * may not think that is constant.
+/* Note: This is no longer 1.5 * size, but simply 1 * size.
  */
-#define CALC_CACHE_FLUSHTHRESH(c) (((c) * 3) / 2)
+#define CALC_CACHE_FLUSHTHRESH(c) (c)
 
 #if defined(RTE_ARCH_X86)
 /*
diff --git a/lib/mempool/rte_mempool.h b/lib/mempool/rte_mempool.h
index c495cc012f..1200301ae9 100644
--- a/lib/mempool/rte_mempool.h
+++ b/lib/mempool/rte_mempool.h
@@ -791,7 +791,7 @@ rte_mempool_ops_dequeue_bulk(struct rte_mempool *mp,
 	rte_mempool_trace_ops_dequeue_bulk(mp, obj_table, n);
 	ops = rte_mempool_get_ops(mp->ops_index);
 	ret = ops->dequeue(mp, obj_table, n);
-	if (ret == 0) {
+	if (likely(ret == 0)) {
 		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_bulk, 1);
 		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_objs, n);
 	}
@@ -1044,7 +1044,7 @@ rte_mempool_free(struct rte_mempool *mp);
  *   If cache_size is non-zero, the rte_mempool library will try to
  *   limit the accesses to the common lockless pool, by maintaining a
  *   per-lcore object cache. This argument must be lower or equal to
- *   RTE_MEMPOOL_CACHE_MAX_SIZE and n / 1.5. It is advised to choose
+ *   RTE_MEMPOOL_CACHE_MAX_SIZE and n. It is advised to choose
  *   cache_size to have "n modulo cache_size == 0": if this is
  *   not the case, some elements will always stay in the pool and will
  *   never be used. The access to the per-lcore table is of course
@@ -1333,10 +1333,10 @@ rte_mempool_cache_free(struct rte_mempool_cache *cache);
 static __rte_always_inline struct rte_mempool_cache *
 rte_mempool_default_cache(struct rte_mempool *mp, unsigned lcore_id)
 {
-	if (mp->cache_size == 0)
+	if (unlikely(mp->cache_size == 0))
 		return NULL;
 
-	if (lcore_id >= RTE_MAX_LCORE)
+	if (unlikely(lcore_id >= RTE_MAX_LCORE))
 		return NULL;
 
 	rte_mempool_trace_default_cache(mp, lcore_id,
@@ -1383,32 +1383,30 @@ rte_mempool_do_generic_put(struct rte_mempool *mp, void * const *obj_table,
 {
 	void **cache_objs;
 
-	/* No cache provided */
+	/* No cache provided? */
 	if (unlikely(cache == NULL))
 		goto driver_enqueue;
 
-	/* increment stat now, adding in mempool always success */
+	/* Increment stats now, adding in mempool always succeeds. */
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_bulk, 1);
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_objs, n);
 
-	/* The request itself is too big for the cache */
-	if (unlikely(n > cache->flushthresh))
-		goto driver_enqueue_stats_incremented;
-
-	/*
-	 * The cache follows the following algorithm:
-	 *   1. If the objects cannot be added to the cache without crossing
-	 *      the flush threshold, flush the cache to the backend.
-	 *   2. Add the objects to the cache.
-	 */
-
-	if (cache->len + n <= cache->flushthresh) {
+	if (likely(cache->len + n <= cache->size)) {
+		/* Sufficient room in the cache for the objects. */
 		cache_objs = &cache->objs[cache->len];
 		cache->len += n;
-	} else {
+	} else if (n <= cache->size) {
+		/*
+		 * The cache is big enough for the objects, but - as detected by
+		 * the comparison above - has insufficient room for them.
+		 * Flush the cache to make room for the objects.
+		 */
 		cache_objs = &cache->objs[0];
 		rte_mempool_ops_enqueue_bulk(mp, cache_objs, cache->len);
 		cache->len = n;
+	} else {
+		/* The request itself is too big for the cache. */
+		goto driver_enqueue_stats_incremented;
 	}
 
 	/* Add the objects to the cache. */
@@ -1512,10 +1510,10 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 {
 	int ret;
 	unsigned int remaining;
-	uint32_t index, len;
+	uint32_t index;
 	void **cache_objs;
 
-	/* No cache provided */
+	/* No cache provided? */
 	if (unlikely(cache == NULL)) {
 		remaining = n;
 		goto driver_dequeue;
@@ -1524,11 +1522,11 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 	/* The cache is a stack, so copy will be in reverse order. */
 	cache_objs = &cache->objs[cache->len];
 
-	if (__rte_constant(n) && n <= cache->len) {
+	if (likely(n <= cache->len)) {
 		/*
-		 * The request size is known at build time, and
-		 * the entire request can be satisfied from the cache,
-		 * so let the compiler unroll the fixed length copy loop.
+		 * The entire request can be satisfied from the cache.
+		 * Note: If the request size is known at build time,
+		 * the compiler will unroll the fixed length copy loop.
 		 */
 		cache->len -= n;
 		for (index = 0; index < n; index++)
@@ -1540,55 +1538,35 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 		return 0;
 	}
 
-	/*
-	 * Use the cache as much as we have to return hot objects first.
-	 * If the request size 'n' is known at build time, the above comparison
-	 * ensures that n > cache->len here, so omit RTE_MIN().
-	 */
-	len = __rte_constant(n) ? cache->len : RTE_MIN(n, cache->len);
-	cache->len -= len;
-	remaining = n - len;
-	for (index = 0; index < len; index++)
+	/* Use the cache as much as we have to return hot objects first. */
+	for (index = 0; index < cache->len; index++)
 		*obj_table++ = *--cache_objs;
+	remaining = n - cache->len;
+	cache->len = 0;
 
-	/*
-	 * If the request size 'n' is known at build time, the case
-	 * where the entire request can be satisfied from the cache
-	 * has already been handled above, so omit handling it here.
-	 */
-	if (!__rte_constant(n) && remaining == 0) {
-		/* The entire request is satisfied from the cache. */
-
-		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
-		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_objs, n);
-
-		return 0;
-	}
-
-	/* if dequeue below would overflow mem allocated for cache */
-	if (unlikely(remaining > RTE_MEMPOOL_CACHE_MAX_SIZE))
+	/* The remaining request is too big for the cache? */
+	if (unlikely(remaining > cache->size))
 		goto driver_dequeue;
 
-	/* Fill the cache from the backend; fetch size + remaining objects. */
+	/* Fill the cache from the backend; fetch remaining objects + size / 2. */
 	ret = rte_mempool_ops_dequeue_bulk(mp, cache->objs,
-			cache->size + remaining);
+			remaining + cache->size / 2);
 	if (unlikely(ret < 0)) {
 		/*
-		 * We are buffer constrained, and not able to allocate
-		 * cache + remaining.
+		 * We are buffer constrained, and not able to fetch all that.
 		 * Do not fill the cache, just satisfy the remaining part of
 		 * the request directly from the backend.
 		 */
 		goto driver_dequeue;
 	}
 
+	cache->len = cache->size / 2;
+
 	/* Satisfy the remaining part of the request from the filled cache. */
-	cache_objs = &cache->objs[cache->size + remaining];
+	cache_objs = &cache->objs[cache->len + remaining];
 	for (index = 0; index < remaining; index++)
 		*obj_table++ = *--cache_objs;
 
-	cache->len = cache->size;
-
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_objs, n);
 
@@ -1599,7 +1577,7 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 	/* Get remaining objects directly from the backend. */
 	ret = rte_mempool_ops_dequeue_bulk(mp, obj_table, remaining);
 
-	if (ret < 0) {
+	if (unlikely(ret < 0)) {
 		if (likely(cache != NULL)) {
 			cache->len = n - remaining;
 			/*
@@ -1650,7 +1628,7 @@ rte_mempool_generic_get(struct rte_mempool *mp, void **obj_table,
 {
 	int ret;
 	ret = rte_mempool_do_generic_get(mp, obj_table, n, cache);
-	if (ret == 0)
+	if (likely(ret == 0))
 		RTE_MEMPOOL_CHECK_COOKIES(mp, obj_table, n, 1);
 	rte_mempool_trace_generic_get(mp, obj_table, n, cache);
 	return ret;
@@ -1741,7 +1719,7 @@ rte_mempool_get_contig_blocks(struct rte_mempool *mp,
 	int ret;
 
 	ret = rte_mempool_ops_dequeue_contig_blocks(mp, first_obj_table, n);
-	if (ret == 0) {
+	if (likely(ret == 0)) {
 		RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
 		RTE_MEMPOOL_STAT_ADD(mp, get_success_blks, n);
 		RTE_MEMPOOL_CONTIG_BLOCKS_CHECK_COOKIES(mp, first_obj_table, n,
-- 
2.43.0


^ permalink raw reply	[relevance 4%]

* [v3 4/6] crypto/virtio: add vDPA backend
  @ 2025-02-21 17:41  1% ` Gowrishankar Muthukrishnan
  0 siblings, 0 replies; 153+ results
From: Gowrishankar Muthukrishnan @ 2025-02-21 17:41 UTC (permalink / raw)
  To: dev, Jay Zhou; +Cc: anoobj, Akhil Goyal, Gowrishankar Muthukrishnan

Add vDPA backend to virtio_user crypto.

Signed-off-by: Gowrishankar Muthukrishnan <gmuthukrishn@marvell.com>
---
 drivers/crypto/virtio/meson.build             |   7 +
 drivers/crypto/virtio/virtio_cryptodev.c      |  57 +-
 drivers/crypto/virtio/virtio_cryptodev.h      |   3 +
 drivers/crypto/virtio/virtio_logs.h           |   6 +-
 drivers/crypto/virtio/virtio_pci.h            |   7 +
 drivers/crypto/virtio/virtio_ring.h           |   6 -
 drivers/crypto/virtio/virtio_user/vhost.h     |  90 ++
 .../crypto/virtio/virtio_user/vhost_vdpa.c    | 710 ++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.c      | 767 ++++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.h      |  85 ++
 drivers/crypto/virtio/virtio_user_cryptodev.c | 575 +++++++++++++
 11 files changed, 2283 insertions(+), 30 deletions(-)
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost.h
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost_vdpa.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.h
 create mode 100644 drivers/crypto/virtio/virtio_user_cryptodev.c

diff --git a/drivers/crypto/virtio/meson.build b/drivers/crypto/virtio/meson.build
index d2c3b3ad07..3763e86746 100644
--- a/drivers/crypto/virtio/meson.build
+++ b/drivers/crypto/virtio/meson.build
@@ -16,3 +16,10 @@ sources = files(
         'virtio_rxtx.c',
         'virtqueue.c',
 )
+
+if is_linux
+    sources += files('virtio_user_cryptodev.c',
+        'virtio_user/vhost_vdpa.c',
+        'virtio_user/virtio_user_dev.c')
+    deps += ['bus_vdev']
+endif
diff --git a/drivers/crypto/virtio/virtio_cryptodev.c b/drivers/crypto/virtio/virtio_cryptodev.c
index 92fea557ab..bc737f1e68 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.c
+++ b/drivers/crypto/virtio/virtio_cryptodev.c
@@ -544,24 +544,12 @@ virtio_crypto_init_device(struct rte_cryptodev *cryptodev,
 	return 0;
 }
 
-/*
- * This function is based on probe() function
- * It returns 0 on success.
- */
-static int
-crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
-		struct rte_cryptodev_pmd_init_params *init_params)
+int
+crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev)
 {
-	struct rte_cryptodev *cryptodev;
 	struct virtio_crypto_hw *hw;
 
-	PMD_INIT_FUNC_TRACE();
-
-	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
-					init_params);
-	if (cryptodev == NULL)
-		return -ENODEV;
-
 	cryptodev->driver_id = cryptodev_virtio_driver_id;
 	cryptodev->dev_ops = &virtio_crypto_dev_ops;
 
@@ -578,16 +566,41 @@ crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
 	hw->dev_id = cryptodev->data->dev_id;
 	hw->virtio_dev_capabilities = virtio_capabilities;
 
-	VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
-		cryptodev->data->dev_id, pci_dev->id.vendor_id,
-		pci_dev->id.device_id);
+	if (pci_dev) {
+		/* pci device init */
+		VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
+			cryptodev->data->dev_id, pci_dev->id.vendor_id,
+			pci_dev->id.device_id);
 
-	/* pci device init */
-	if (vtpci_cryptodev_init(pci_dev, hw))
+		if (vtpci_cryptodev_init(pci_dev, hw))
+			return -1;
+	}
+
+	if (virtio_crypto_init_device(cryptodev, features) < 0)
 		return -1;
 
-	if (virtio_crypto_init_device(cryptodev,
-			VIRTIO_CRYPTO_PMD_GUEST_FEATURES) < 0)
+	return 0;
+}
+
+/*
+ * This function is based on probe() function
+ * It returns 0 on success.
+ */
+static int
+crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
+		struct rte_cryptodev_pmd_init_params *init_params)
+{
+	struct rte_cryptodev *cryptodev;
+
+	PMD_INIT_FUNC_TRACE();
+
+	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
+					init_params);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_CRYPTO_PMD_GUEST_FEATURES,
+			pci_dev) < 0)
 		return -1;
 
 	rte_cryptodev_pmd_probing_finish(cryptodev);
diff --git a/drivers/crypto/virtio/virtio_cryptodev.h b/drivers/crypto/virtio/virtio_cryptodev.h
index f8498246e2..fad73d54a8 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.h
+++ b/drivers/crypto/virtio/virtio_cryptodev.h
@@ -76,4 +76,7 @@ uint16_t virtio_crypto_pkt_rx_burst(void *tx_queue,
 		struct rte_crypto_op **tx_pkts,
 		uint16_t nb_pkts);
 
+int crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev);
+
 #endif /* _VIRTIO_CRYPTODEV_H_ */
diff --git a/drivers/crypto/virtio/virtio_logs.h b/drivers/crypto/virtio/virtio_logs.h
index 988514919f..1cc51f7990 100644
--- a/drivers/crypto/virtio/virtio_logs.h
+++ b/drivers/crypto/virtio/virtio_logs.h
@@ -15,8 +15,10 @@ extern int virtio_crypto_logtype_init;
 
 #define PMD_INIT_FUNC_TRACE() PMD_INIT_LOG(DEBUG, " >>")
 
-extern int virtio_crypto_logtype_init;
-#define RTE_LOGTYPE_VIRTIO_CRYPTO_INIT virtio_crypto_logtype_init
+extern int virtio_crypto_logtype_driver;
+#define RTE_LOGTYPE_VIRTIO_CRYPTO_DRIVER virtio_crypto_logtype_driver
+#define PMD_DRV_LOG(level, ...) \
+	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_DRIVER, "%s(): ", __func__, __VA_ARGS__)
 
 #define VIRTIO_CRYPTO_INIT_LOG_IMPL(level, ...) \
 	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_INIT, "%s(): ", __func__, __VA_ARGS__)
diff --git a/drivers/crypto/virtio/virtio_pci.h b/drivers/crypto/virtio/virtio_pci.h
index 79945cb88e..c75777e005 100644
--- a/drivers/crypto/virtio/virtio_pci.h
+++ b/drivers/crypto/virtio/virtio_pci.h
@@ -20,6 +20,9 @@ struct virtqueue;
 #define VIRTIO_CRYPTO_PCI_VENDORID 0x1AF4
 #define VIRTIO_CRYPTO_PCI_DEVICEID 0x1054
 
+/* VirtIO device IDs. */
+#define VIRTIO_ID_CRYPTO  20
+
 /* VirtIO ABI version, this must match exactly. */
 #define VIRTIO_PCI_ABI_VERSION 0
 
@@ -56,8 +59,12 @@ struct virtqueue;
 #define VIRTIO_CONFIG_STATUS_DRIVER    0x02
 #define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
 #define VIRTIO_CONFIG_STATUS_FEATURES_OK 0x08
+#define VIRTIO_CONFIG_STATUS_DEV_NEED_RESET	0x40
 #define VIRTIO_CONFIG_STATUS_FAILED    0x80
 
+/* The alignment to use between consumer and producer parts of vring. */
+#define VIRTIO_VRING_ALIGN 4096
+
 /*
  * Each virtqueue indirect descriptor list must be physically contiguous.
  * To allow us to malloc(9) each list individually, limit the number
diff --git a/drivers/crypto/virtio/virtio_ring.h b/drivers/crypto/virtio/virtio_ring.h
index c74d1172b7..4b418f6e60 100644
--- a/drivers/crypto/virtio/virtio_ring.h
+++ b/drivers/crypto/virtio/virtio_ring.h
@@ -181,12 +181,6 @@ vring_init_packed(struct vring_packed *vr, uint8_t *p, rte_iova_t iova,
 				sizeof(struct vring_packed_desc_event)), align);
 }
 
-static inline void
-vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align)
-{
-	vring_init_split(vr, p, 0, align, num);
-}
-
 /*
  * The following is used with VIRTIO_RING_F_EVENT_IDX.
  * Assuming a given event_idx value from the other size, if we have
diff --git a/drivers/crypto/virtio/virtio_user/vhost.h b/drivers/crypto/virtio/virtio_user/vhost.h
new file mode 100644
index 0000000000..29cc1a14d4
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#ifndef _VIRTIO_USER_VHOST_H
+#define _VIRTIO_USER_VHOST_H
+
+#include <stdint.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <rte_errno.h>
+
+#include "../virtio_logs.h"
+
+struct vhost_vring_state {
+	unsigned int index;
+	unsigned int num;
+};
+
+struct vhost_vring_file {
+	unsigned int index;
+	int fd;
+};
+
+struct vhost_vring_addr {
+	unsigned int index;
+	/* Option flags. */
+	unsigned int flags;
+	/* Flag values: */
+	/* Whether log address is valid. If set enables logging. */
+#define VHOST_VRING_F_LOG 0
+
+	/* Start of array of descriptors (virtually contiguous) */
+	uint64_t desc_user_addr;
+	/* Used structure address. Must be 32 bit aligned */
+	uint64_t used_user_addr;
+	/* Available structure address. Must be 16 bit aligned */
+	uint64_t avail_user_addr;
+	/* Logging support. */
+	/* Log writes to used structure, at offset calculated from specified
+	 * address. Address must be 32 bit aligned.
+	 */
+	uint64_t log_guest_addr;
+};
+
+#ifndef VHOST_BACKEND_F_IOTLB_MSG_V2
+#define VHOST_BACKEND_F_IOTLB_MSG_V2 1
+#endif
+
+#ifndef VHOST_BACKEND_F_IOTLB_BATCH
+#define VHOST_BACKEND_F_IOTLB_BATCH 2
+#endif
+
+struct virtio_user_dev;
+
+struct virtio_user_backend_ops {
+	int (*setup)(struct virtio_user_dev *dev);
+	int (*destroy)(struct virtio_user_dev *dev);
+	int (*get_backend_features)(uint64_t *features);
+	int (*set_owner)(struct virtio_user_dev *dev);
+	int (*get_features)(struct virtio_user_dev *dev, uint64_t *features);
+	int (*set_features)(struct virtio_user_dev *dev, uint64_t features);
+	int (*set_memory_table)(struct virtio_user_dev *dev);
+	int (*set_vring_num)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*get_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_call)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_kick)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_addr)(struct virtio_user_dev *dev, struct vhost_vring_addr *addr);
+	int (*get_status)(struct virtio_user_dev *dev, uint8_t *status);
+	int (*set_status)(struct virtio_user_dev *dev, uint8_t status);
+	int (*get_config)(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len);
+	int (*set_config)(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off,
+			uint32_t len);
+	int (*cvq_enable)(struct virtio_user_dev *dev, int enable);
+	int (*enable_qp)(struct virtio_user_dev *dev, uint16_t pair_idx, int enable);
+	int (*dma_map)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*dma_unmap)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*update_link_state)(struct virtio_user_dev *dev);
+	int (*server_disconnect)(struct virtio_user_dev *dev);
+	int (*server_reconnect)(struct virtio_user_dev *dev);
+	int (*get_intr_fd)(struct virtio_user_dev *dev);
+	int (*map_notification_area)(struct virtio_user_dev *dev);
+	int (*unmap_notification_area)(struct virtio_user_dev *dev);
+};
+
+extern struct virtio_user_backend_ops virtio_ops_vdpa;
+
+#endif
diff --git a/drivers/crypto/virtio/virtio_user/vhost_vdpa.c b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
new file mode 100644
index 0000000000..b5839875e6
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
@@ -0,0 +1,710 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rte_memory.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+#include "../virtio_pci.h"
+
+struct vhost_vdpa_data {
+	int vhostfd;
+	uint64_t protocol_features;
+};
+
+#define VHOST_VDPA_SUPPORTED_BACKEND_FEATURES		\
+	(1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2	|	\
+	1ULL << VHOST_BACKEND_F_IOTLB_BATCH)
+
+/* vhost kernel & vdpa ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
+#define VHOST_VDPA_GET_DEVICE_ID _IOR(VHOST_VIRTIO, 0x70, __u32)
+#define VHOST_VDPA_GET_STATUS _IOR(VHOST_VIRTIO, 0x71, __u8)
+#define VHOST_VDPA_SET_STATUS _IOW(VHOST_VIRTIO, 0x72, __u8)
+#define VHOST_VDPA_GET_CONFIG _IOR(VHOST_VIRTIO, 0x73, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_CONFIG _IOW(VHOST_VIRTIO, 0x74, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_VRING_ENABLE _IOW(VHOST_VIRTIO, 0x75, struct vhost_vring_state)
+#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64)
+#define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64)
+
+/* no alignment requirement */
+struct vhost_iotlb_msg {
+	uint64_t iova;
+	uint64_t size;
+	uint64_t uaddr;
+#define VHOST_ACCESS_RO      0x1
+#define VHOST_ACCESS_WO      0x2
+#define VHOST_ACCESS_RW      0x3
+	uint8_t perm;
+#define VHOST_IOTLB_MISS           1
+#define VHOST_IOTLB_UPDATE         2
+#define VHOST_IOTLB_INVALIDATE     3
+#define VHOST_IOTLB_ACCESS_FAIL    4
+#define VHOST_IOTLB_BATCH_BEGIN    5
+#define VHOST_IOTLB_BATCH_END      6
+	uint8_t type;
+};
+
+#define VHOST_IOTLB_MSG_V2 0x2
+
+struct vhost_vdpa_config {
+	uint32_t off;
+	uint32_t len;
+	uint8_t buf[];
+};
+
+struct vhost_msg {
+	uint32_t type;
+	uint32_t reserved;
+	union {
+		struct vhost_iotlb_msg iotlb;
+		uint8_t padding[64];
+	};
+};
+
+static int
+vhost_vdpa_ioctl(int fd, uint64_t request, void *arg)
+{
+	int ret;
+
+	ret = ioctl(fd, request, arg);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Vhost-vDPA ioctl %"PRIu64" failed (%s)",
+				request, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_owner(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_OWNER, NULL);
+}
+
+static int
+vhost_vdpa_get_protocol_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_BACKEND_FEATURES, features);
+}
+
+static int
+vhost_vdpa_set_protocol_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_BACKEND_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_get_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int ret;
+
+	ret = vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_FEATURES, features);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get features");
+		return -1;
+	}
+
+	/* Negotiated vDPA backend features */
+	ret = vhost_vdpa_get_protocol_features(dev, &data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to get backend features");
+		return -1;
+	}
+
+	data->protocol_features &= VHOST_VDPA_SUPPORTED_BACKEND_FEATURES;
+
+	ret = vhost_vdpa_set_protocol_features(dev, data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to set backend features");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	/* WORKAROUND */
+	features |= 1ULL << VIRTIO_F_IOMMU_PLATFORM;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_iotlb_batch_begin(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_BEGIN;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch begin (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_iotlb_batch_end(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_END;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch end (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_UPDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.uaddr = (uint64_t)(uintptr_t)addr;
+	msg.iotlb.size = len;
+	msg.iotlb.perm = VHOST_ACCESS_RW;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", addr: %p, len: 0x%zx",
+			__func__, iova, addr, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB update (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_unmap(struct virtio_user_dev *dev, __rte_unused void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_INVALIDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.size = len;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", len: 0x%zx",
+			__func__, iova, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB invalidate (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_map(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_dma_unmap_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_unmap(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_map_contig(const struct rte_memseg_list *msl,
+		const struct rte_memseg *ms, size_t len, void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	if (msl->external)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, len);
+}
+
+static int
+vhost_vdpa_map(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
+		void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	/* skip external memory that isn't a heap */
+	if (msl->external && !msl->heap)
+		return 0;
+
+	/* skip any segments with invalid IOVA addresses */
+	if (ms->iova == RTE_BAD_IOVA)
+		return 0;
+
+	/* if IOVA mode is VA, we've already mapped the internal segments */
+	if (!msl->external && rte_eal_iova_mode() == RTE_IOVA_VA)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, ms->len);
+}
+
+static int
+vhost_vdpa_set_memory_table(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	vhost_vdpa_dma_unmap(dev, NULL, 0, SIZE_MAX);
+
+	if (rte_eal_iova_mode() == RTE_IOVA_VA) {
+		/* with IOVA as VA mode, we can get away with mapping contiguous
+		 * chunks rather than going page-by-page.
+		 */
+		ret = rte_memseg_contig_walk_thread_unsafe(
+				vhost_vdpa_map_contig, dev);
+		if (ret)
+			goto batch_end;
+		/* we have to continue the walk because we've skipped the
+		 * external segments during the config walk.
+		 */
+	}
+	ret = rte_memseg_walk_thread_unsafe(vhost_vdpa_map, dev);
+
+batch_end:
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_vring_enable(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_VRING_ENABLE, state);
+}
+
+static int
+vhost_vdpa_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_NUM, state);
+}
+
+static int
+vhost_vdpa_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_CALL, file);
+}
+
+static int
+vhost_vdpa_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_KICK, file);
+}
+
+static int
+vhost_vdpa_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_ADDR, addr);
+}
+
+static int
+vhost_vdpa_get_status(struct virtio_user_dev *dev, uint8_t *status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_GET_STATUS, status);
+}
+
+static int
+vhost_vdpa_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_STATUS, &status);
+}
+
+static int
+vhost_vdpa_get_config(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_GET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+		goto out;
+	}
+
+	memcpy(data, config->buf, len);
+out:
+	free(config);
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_config(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	memcpy(config->buf, data, len);
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_SET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to set vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+	}
+
+	free(config);
+
+	return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost vdpa backend.
+ *
+ * @return
+ *   - (-1) if fail to set up;
+ *   - (>=0) if successful.
+ */
+static int
+vhost_vdpa_setup(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data;
+	uint32_t did = (uint32_t)-1;
+
+	data = malloc(sizeof(*data));
+	if (!data) {
+		PMD_DRV_LOG(ERR, "(%s) Faidle to allocate backend data", dev->path);
+		return -1;
+	}
+
+	data->vhostfd = open(dev->path, O_RDWR);
+	if (data->vhostfd < 0) {
+		PMD_DRV_LOG(ERR, "Failed to open %s: %s",
+				dev->path, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	if (ioctl(data->vhostfd, VHOST_VDPA_GET_DEVICE_ID, &did) < 0 ||
+			did != VIRTIO_ID_CRYPTO) {
+		PMD_DRV_LOG(ERR, "Invalid vdpa device ID: %u", did);
+		close(data->vhostfd);
+		free(data);
+		return -1;
+	}
+
+	dev->backend_data = data;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_destroy(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	if (!data)
+		return 0;
+
+	close(data->vhostfd);
+
+	free(data);
+	dev->backend_data = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_cvq_enable(struct virtio_user_dev *dev, int enable)
+{
+	struct vhost_vring_state state = {
+		.index = dev->max_queue_pairs,
+		.num   = enable,
+	};
+
+	return vhost_vdpa_set_vring_enable(dev, &state);
+}
+
+static int
+vhost_vdpa_enable_queue_pair(struct virtio_user_dev *dev,
+				uint16_t pair_idx,
+				int enable)
+{
+	struct vhost_vring_state state = {
+		.index = pair_idx,
+		.num   = enable,
+	};
+
+	if (dev->qp_enabled[pair_idx] == enable)
+		return 0;
+
+	if (vhost_vdpa_set_vring_enable(dev, &state))
+		return -1;
+
+	dev->qp_enabled[pair_idx] = enable;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_backend_features(uint64_t *features)
+{
+	*features = 0;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_update_link_state(struct virtio_user_dev *dev)
+{
+	/* TODO: It is W/A until a cleaner approach to find cpt status */
+	dev->crypto_status = VIRTIO_CRYPTO_S_HW_READY;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_intr_fd(struct virtio_user_dev *dev __rte_unused)
+{
+	/* No link state interrupt with Vhost-vDPA */
+	return -1;
+}
+
+static int
+vhost_vdpa_get_nr_vrings(struct virtio_user_dev *dev)
+{
+	int nr_vrings = dev->max_queue_pairs;
+
+	return nr_vrings;
+}
+
+static int
+vhost_vdpa_unmap_notification_area(struct virtio_user_dev *dev)
+{
+	int i, nr_vrings;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	for (i = 0; i < nr_vrings; i++) {
+		if (dev->notify_area[i])
+			munmap(dev->notify_area[i], getpagesize());
+	}
+	free(dev->notify_area);
+	dev->notify_area = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_map_notification_area(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int nr_vrings, i, page_size = getpagesize();
+	uint16_t **notify_area;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	/* CQ is another vring */
+	nr_vrings++;
+
+	notify_area = malloc(nr_vrings * sizeof(*notify_area));
+	if (!notify_area) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to allocate notify area array", dev->path);
+		return -1;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		notify_area[i] = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED | MAP_FILE,
+					data->vhostfd, i * page_size);
+		if (notify_area[i] == MAP_FAILED) {
+			PMD_DRV_LOG(ERR, "(%s) Map failed for notify address of queue %d",
+					dev->path, i);
+			i--;
+			goto map_err;
+		}
+	}
+	dev->notify_area = notify_area;
+
+	return 0;
+
+map_err:
+	for (; i >= 0; i--)
+		munmap(notify_area[i], page_size);
+	free(notify_area);
+
+	return -1;
+}
+
+struct virtio_user_backend_ops virtio_crypto_ops_vdpa = {
+	.setup = vhost_vdpa_setup,
+	.destroy = vhost_vdpa_destroy,
+	.get_backend_features = vhost_vdpa_get_backend_features,
+	.set_owner = vhost_vdpa_set_owner,
+	.get_features = vhost_vdpa_get_features,
+	.set_features = vhost_vdpa_set_features,
+	.set_memory_table = vhost_vdpa_set_memory_table,
+	.set_vring_num = vhost_vdpa_set_vring_num,
+	.set_vring_base = vhost_vdpa_set_vring_base,
+	.get_vring_base = vhost_vdpa_get_vring_base,
+	.set_vring_call = vhost_vdpa_set_vring_call,
+	.set_vring_kick = vhost_vdpa_set_vring_kick,
+	.set_vring_addr = vhost_vdpa_set_vring_addr,
+	.get_status = vhost_vdpa_get_status,
+	.set_status = vhost_vdpa_set_status,
+	.get_config = vhost_vdpa_get_config,
+	.set_config = vhost_vdpa_set_config,
+	.cvq_enable = vhost_vdpa_cvq_enable,
+	.enable_qp = vhost_vdpa_enable_queue_pair,
+	.dma_map = vhost_vdpa_dma_map_batch,
+	.dma_unmap = vhost_vdpa_dma_unmap_batch,
+	.update_link_state = vhost_vdpa_update_link_state,
+	.get_intr_fd = vhost_vdpa_get_intr_fd,
+	.map_notification_area = vhost_vdpa_map_notification_area,
+	.unmap_notification_area = vhost_vdpa_unmap_notification_area,
+};
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.c b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
new file mode 100644
index 0000000000..248df11ccc
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
@@ -0,0 +1,767 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <rte_alarm.h>
+#include <rte_string_fns.h>
+#include <rte_eal_memconfig.h>
+#include <rte_malloc.h>
+#include <rte_io.h>
+
+#include "vhost.h"
+#include "virtio_logs.h"
+#include "cryptodev_pmd.h"
+#include "virtio_crypto.h"
+#include "virtio_cvq.h"
+#include "virtio_user_dev.h"
+#include "virtqueue.h"
+
+#define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
+
+const char * const crypto_virtio_user_backend_strings[] = {
+	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
+	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
+};
+
+static int
+virtio_user_uninit_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	if (dev->kickfds[queue_sel] >= 0) {
+		close(dev->kickfds[queue_sel]);
+		dev->kickfds[queue_sel] = -1;
+	}
+
+	if (dev->callfds[queue_sel] >= 0) {
+		close(dev->callfds[queue_sel]);
+		dev->callfds[queue_sel] = -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_init_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* May use invalid flag, but some backend uses kickfd and
+	 * callfd as criteria to judge if dev is alive. so finally we
+	 * use real event_fd.
+	 */
+	dev->callfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->callfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup callfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+	dev->kickfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->kickfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup kickfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_destroy_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	struct vhost_vring_state state;
+	int ret;
+
+	state.index = queue_sel;
+	ret = dev->ops->get_vring_base(dev, &state);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to destroy queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
+	 * firstly because vhost depends on this msg to allocate virtqueue
+	 * pair.
+	 */
+	struct vhost_vring_file file;
+	int ret;
+
+	file.index = queue_sel;
+	file.fd = dev->callfds[queue_sel];
+	ret = dev->ops->set_vring_call(dev, &file);
+	if (ret < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	int ret;
+	struct vhost_vring_file file;
+	struct vhost_vring_state state;
+	struct vring *vring = &dev->vrings.split[queue_sel];
+	struct vring_packed *pq_vring = &dev->vrings.packed[queue_sel];
+	uint64_t desc_addr, avail_addr, used_addr;
+	struct vhost_vring_addr addr = {
+		.index = queue_sel,
+		.log_guest_addr = 0,
+		.flags = 0, /* disable log */
+	};
+
+	if (queue_sel == dev->max_queue_pairs) {
+		if (!dev->scvq) {
+			PMD_INIT_LOG(ERR, "(%s) Shadow control queue expected but missing",
+					dev->path);
+			goto err;
+		}
+
+		/* Use shadow control queue information */
+		vring = &dev->scvq->vq_split.ring;
+		pq_vring = &dev->scvq->vq_packed.ring;
+	}
+
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
+		desc_addr = pq_vring->desc_iova;
+		avail_addr = desc_addr + pq_vring->num * sizeof(struct vring_packed_desc);
+		used_addr =  RTE_ALIGN_CEIL(avail_addr + sizeof(struct vring_packed_desc_event),
+						VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	} else {
+		desc_addr = vring->desc_iova;
+		avail_addr = desc_addr + vring->num * sizeof(struct vring_desc);
+		used_addr = RTE_ALIGN_CEIL((uintptr_t)(&vring->avail->ring[vring->num]),
+					VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	}
+
+	state.index = queue_sel;
+	state.num = vring->num;
+	ret = dev->ops->set_vring_num(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	state.index = queue_sel;
+	state.num = 0; /* no reservation */
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
+		state.num |= (1 << 15);
+	ret = dev->ops->set_vring_base(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	ret = dev->ops->set_vring_addr(dev, &addr);
+	if (ret < 0)
+		goto err;
+
+	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
+	 * lastly because vhost depends on this msg to judge if
+	 * virtio is ready.
+	 */
+	file.index = queue_sel;
+	file.fd = dev->kickfds[queue_sel];
+	ret = dev->ops->set_vring_kick(dev, &file);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+err:
+	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel);
+
+	return -1;
+}
+
+static int
+virtio_user_foreach_queue(struct virtio_user_dev *dev,
+			int (*fn)(struct virtio_user_dev *, uint32_t))
+{
+	uint32_t i, nr_vq;
+
+	nr_vq = dev->max_queue_pairs;
+
+	for (i = 0; i < nr_vq; i++)
+		if (fn(dev, i) < 0)
+			return -1;
+
+	return 0;
+}
+
+int
+crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev)
+{
+	uint64_t features;
+	int ret = -1;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 0: tell vhost to create queues */
+	if (virtio_user_foreach_queue(dev, virtio_user_create_queue) < 0)
+		goto error;
+
+	features = dev->features;
+
+	ret = dev->ops->set_features(dev, features);
+	if (ret < 0)
+		goto error;
+	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
+error:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return ret;
+}
+
+int
+crypto_virtio_user_start_device(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	/*
+	 * XXX workaround!
+	 *
+	 * We need to make sure that the locks will be
+	 * taken in the correct order to avoid deadlocks.
+	 *
+	 * Before releasing this lock, this thread should
+	 * not trigger any memory hotplug events.
+	 *
+	 * This is a temporary workaround, and should be
+	 * replaced when we get proper supports from the
+	 * memory subsystem in the future.
+	 */
+	rte_mcfg_mem_read_lock();
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 2: share memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Step 3: kick queues */
+	ret = virtio_user_foreach_queue(dev, virtio_user_kick_queue);
+	if (ret < 0)
+		goto error;
+
+	ret = virtio_user_kick_queue(dev, dev->max_queue_pairs);
+	if (ret < 0)
+		goto error;
+
+	/* Step 4: enable queues */
+	for (int i = 0; i < dev->max_queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto error;
+	}
+
+	dev->started = true;
+
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	return 0;
+error:
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path);
+
+	/* TODO: free resource here or caller to check */
+	return -1;
+}
+
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev)
+{
+	uint32_t i;
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	if (!dev->started)
+		goto out;
+
+	for (i = 0; i < dev->max_queue_pairs; ++i) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	if (dev->scvq) {
+		ret = dev->ops->cvq_enable(dev, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	/* Stop the backend. */
+	if (virtio_user_foreach_queue(dev, virtio_user_destroy_queue) < 0)
+		goto err;
+
+	dev->started = false;
+
+out:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return 0;
+err:
+	pthread_mutex_unlock(&dev->mutex);
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path);
+
+	return -1;
+}
+
+static int
+virtio_user_dev_init_max_queue_pairs(struct virtio_user_dev *dev, uint32_t user_max_qp)
+{
+	int ret;
+
+	if (!dev->ops->get_config) {
+		dev->max_queue_pairs = user_max_qp;
+		return 0;
+	}
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&dev->max_queue_pairs,
+			offsetof(struct virtio_crypto_config, max_dataqueues),
+			sizeof(uint16_t));
+	if (ret) {
+		/*
+		 * We need to know the max queue pair from the device so that
+		 * the control queue gets the right index.
+		 */
+		dev->max_queue_pairs = 1;
+		PMD_DRV_LOG(ERR, "(%s) Failed to get max queue pairs from device", dev->path);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_dev_init_cipher_services(struct virtio_user_dev *dev)
+{
+	struct virtio_crypto_config config;
+	int ret;
+
+	dev->crypto_services = RTE_BIT32(VIRTIO_CRYPTO_SERVICE_CIPHER);
+	dev->cipher_algo = 0;
+	dev->auth_algo = 0;
+	dev->akcipher_algo = 0;
+
+	if (!dev->ops->get_config)
+		return 0;
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&config,	0, sizeof(config));
+	if (ret) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to get crypto config from device", dev->path);
+		return ret;
+	}
+
+	dev->crypto_services = config.crypto_services;
+	dev->cipher_algo = ((uint64_t)config.cipher_algo_h << 32) |
+						config.cipher_algo_l;
+	dev->hash_algo = config.hash_algo;
+	dev->auth_algo = ((uint64_t)config.mac_algo_h << 32) |
+						config.mac_algo_l;
+	dev->aead_algo = config.aead_algo;
+	dev->akcipher_algo = config.akcipher_algo;
+	return 0;
+}
+
+static int
+virtio_user_dev_init_notify(struct virtio_user_dev *dev)
+{
+
+	if (virtio_user_foreach_queue(dev, virtio_user_init_notify_queue) < 0)
+		goto err;
+
+	if (dev->device_features & (1ULL << VIRTIO_F_NOTIFICATION_DATA))
+		if (dev->ops->map_notification_area &&
+				dev->ops->map_notification_area(dev))
+			goto err;
+
+	return 0;
+err:
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	return -1;
+}
+
+static void
+virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
+{
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	if (dev->ops->unmap_notification_area && dev->notify_area)
+		dev->ops->unmap_notification_area(dev);
+}
+
+static void
+virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
+			const void *addr,
+			size_t len __rte_unused,
+			void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+	struct rte_memseg_list *msl;
+	uint16_t i;
+	int ret = 0;
+
+	/* ignore externally allocated memory */
+	msl = rte_mem_virt2memseg_list(addr);
+	if (msl->external)
+		return;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	if (dev->started == false)
+		goto exit;
+
+	/* Step 1: pause the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto exit;
+	}
+
+	/* Step 2: update memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto exit;
+
+	/* Step 3: resume the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto exit;
+	}
+
+exit:
+	pthread_mutex_unlock(&dev->mutex);
+
+	if (ret < 0)
+		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+	if (dev->is_server) {
+		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
+			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
+			return -1;
+		}
+	}
+
+	switch (dev->backend_type) {
+	case VIRTIO_USER_BACKEND_VHOST_VDPA:
+		dev->ops = &virtio_crypto_ops_vdpa;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_alloc_vrings(struct virtio_user_dev *dev)
+{
+	int i, size, nr_vrings;
+	bool packed_ring = !!(dev->device_features & (1ull << VIRTIO_F_RING_PACKED));
+
+	nr_vrings = dev->max_queue_pairs + 1;
+
+	dev->callfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->callfds), 0);
+	if (!dev->callfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc callfds", dev->path);
+		return -1;
+	}
+
+	dev->kickfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->kickfds), 0);
+	if (!dev->kickfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc kickfds", dev->path);
+		goto free_callfds;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		dev->callfds[i] = -1;
+		dev->kickfds[i] = -1;
+	}
+
+	if (packed_ring)
+		size = sizeof(*dev->vrings.packed);
+	else
+		size = sizeof(*dev->vrings.split);
+	dev->vrings.ptr = rte_zmalloc("virtio_user_dev", nr_vrings * size, 0);
+	if (!dev->vrings.ptr) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc vrings metadata", dev->path);
+		goto free_kickfds;
+	}
+
+	if (packed_ring) {
+		dev->packed_queues = rte_zmalloc("virtio_user_dev",
+				nr_vrings * sizeof(*dev->packed_queues), 0);
+		if (!dev->packed_queues) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to alloc packed queues metadata",
+					dev->path);
+			goto free_vrings;
+		}
+	}
+
+	dev->qp_enabled = rte_zmalloc("virtio_user_dev",
+			nr_vrings * sizeof(*dev->qp_enabled), 0);
+	if (!dev->qp_enabled) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc QP enable states", dev->path);
+		goto free_packed_queues;
+	}
+
+	return 0;
+
+free_packed_queues:
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+free_vrings:
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+free_kickfds:
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+free_callfds:
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+
+	return -1;
+}
+
+static void
+virtio_user_free_vrings(struct virtio_user_dev *dev)
+{
+	rte_free(dev->qp_enabled);
+	dev->qp_enabled = NULL;
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+}
+
+#define VIRTIO_USER_SUPPORTED_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_HASH       | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+int
+crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server)
+{
+	uint64_t backend_features;
+
+	pthread_mutex_init(&dev->mutex, NULL);
+	strlcpy(dev->path, path, PATH_MAX);
+
+	dev->started = 0;
+	dev->queue_pairs = 1; /* mq disabled by default */
+	dev->max_queue_pairs = queues; /* initialize to user requested value for kernel backend */
+	dev->queue_size = queue_size;
+	dev->is_server = server;
+	dev->frontend_features = 0;
+	dev->unsupported_features = 0;
+	dev->backend_type = VIRTIO_USER_BACKEND_VHOST_VDPA;
+	dev->hw.modern = 1;
+
+	if (virtio_user_dev_setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->set_owner(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
+		goto destroy;
+	}
+
+	if (dev->ops->get_backend_features(&backend_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
+		goto destroy;
+	}
+
+	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
+
+	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_max_queue_pairs(dev, queues)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get max queue pairs", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_cipher_services(dev)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get cipher services", dev->path);
+		goto destroy;
+	}
+
+	dev->frontend_features &= ~dev->unsupported_features;
+	dev->device_features &= ~dev->unsupported_features;
+
+	if (virtio_user_alloc_vrings(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to allocate vring metadata", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_notify(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path);
+		goto free_vrings;
+	}
+
+	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
+				virtio_user_mem_event_cb, dev)) {
+		if (rte_errno != ENOTSUP) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback",
+					dev->path);
+			goto notify_uninit;
+		}
+	}
+
+	return 0;
+
+notify_uninit:
+	virtio_user_dev_uninit_notify(dev);
+free_vrings:
+	virtio_user_free_vrings(dev);
+destroy:
+	dev->ops->destroy(dev);
+
+	return -1;
+}
+
+void
+crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev)
+{
+	crypto_virtio_user_stop_device(dev);
+
+	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
+
+	virtio_user_dev_uninit_notify(dev);
+
+	virtio_user_free_vrings(dev);
+
+	if (dev->is_server)
+		unlink(dev->path);
+
+	dev->ops->destroy(dev);
+}
+
+#define CVQ_MAX_DATA_DESCS 32
+
+static inline void *
+virtio_user_iova2virt(struct virtio_user_dev *dev __rte_unused, rte_iova_t iova)
+{
+	if (rte_eal_iova_mode() == RTE_IOVA_VA)
+		return (void *)(uintptr_t)iova;
+	else
+		return rte_mem_iova2virt(iova);
+}
+
+static inline int
+desc_is_avail(struct vring_packed_desc *desc, bool wrap_counter)
+{
+	uint16_t flags = rte_atomic_load_explicit(&desc->flags, rte_memory_order_acquire);
+
+	return wrap_counter == !!(flags & VRING_PACKED_DESC_F_AVAIL) &&
+		wrap_counter != !!(flags & VRING_PACKED_DESC_F_USED);
+}
+
+int
+crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	dev->status = status;
+	ret = dev->ops->set_status(dev, status);
+	if (ret && ret != -ENOTSUP)
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path);
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev)
+{
+	int ret;
+	uint8_t status;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	ret = dev->ops->get_status(dev, &status);
+	if (!ret) {
+		dev->status = status;
+		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):"
+			"\t-RESET: %u "
+			"\t-ACKNOWLEDGE: %u "
+			"\t-DRIVER: %u "
+			"\t-DRIVER_OK: %u "
+			"\t-FEATURES_OK: %u "
+			"\t-DEVICE_NEED_RESET: %u "
+			"\t-FAILED: %u",
+			dev->status,
+			(dev->status == VIRTIO_CONFIG_STATUS_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
+	} else if (ret != -ENOTSUP) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path);
+	}
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
+{
+	if (dev->ops->update_link_state)
+		return dev->ops->update_link_state(dev);
+
+	return 0;
+}
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.h b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
new file mode 100644
index 0000000000..9cd9856e5d
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#ifndef _VIRTIO_USER_DEV_H
+#define _VIRTIO_USER_DEV_H
+
+#include <limits.h>
+#include <stdbool.h>
+
+#include "../virtio_pci.h"
+#include "../virtio_ring.h"
+
+extern struct virtio_user_backend_ops virtio_crypto_ops_vdpa;
+
+enum virtio_user_backend_type {
+	VIRTIO_USER_BACKEND_UNKNOWN,
+	VIRTIO_USER_BACKEND_VHOST_USER,
+	VIRTIO_USER_BACKEND_VHOST_VDPA,
+};
+
+struct virtio_user_queue {
+	uint16_t used_idx;
+	bool avail_wrap_counter;
+	bool used_wrap_counter;
+};
+
+struct virtio_user_dev {
+	struct virtio_crypto_hw hw;
+	enum virtio_user_backend_type backend_type;
+	bool		is_server;  /* server or client mode */
+
+	int		*callfds;
+	int		*kickfds;
+	uint16_t	max_queue_pairs;
+	uint16_t	queue_pairs;
+	uint32_t	queue_size;
+	uint64_t	features; /* the negotiated features with driver,
+				   * and will be sync with device
+				   */
+	uint64_t	device_features; /* supported features by device */
+	uint64_t	frontend_features; /* enabled frontend features */
+	uint64_t	unsupported_features; /* unsupported features mask */
+	uint8_t		status;
+	uint32_t	crypto_status;
+	uint32_t	crypto_services;
+	uint64_t	cipher_algo;
+	uint32_t	hash_algo;
+	uint64_t	auth_algo;
+	uint32_t	aead_algo;
+	uint32_t	akcipher_algo;
+	char		path[PATH_MAX];
+
+	union {
+		void			*ptr;
+		struct vring		*split;
+		struct vring_packed	*packed;
+	} vrings;
+
+	struct virtio_user_queue *packed_queues;
+	bool		*qp_enabled;
+
+	struct virtio_user_backend_ops *ops;
+	pthread_mutex_t	mutex;
+	bool		started;
+
+	bool			hw_cvq;
+	struct virtqueue	*scvq;
+
+	void *backend_data;
+
+	uint16_t **notify_area;
+};
+
+int crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev);
+int crypto_virtio_user_start_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server);
+void crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status);
+int crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev);
+extern const char * const crypto_virtio_user_backend_strings[];
+#endif
diff --git a/drivers/crypto/virtio/virtio_user_cryptodev.c b/drivers/crypto/virtio/virtio_user_cryptodev.c
new file mode 100644
index 0000000000..6dfdb76268
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user_cryptodev.c
@@ -0,0 +1,575 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <rte_malloc.h>
+#include <rte_kvargs.h>
+#include <bus_vdev_driver.h>
+#include <rte_cryptodev.h>
+#include <cryptodev_pmd.h>
+#include <rte_alarm.h>
+#include <rte_cycles.h>
+#include <rte_io.h>
+
+#include "virtio_user/virtio_user_dev.h"
+#include "virtio_user/vhost.h"
+#include "virtio_cryptodev.h"
+#include "virtio_logs.h"
+#include "virtio_pci.h"
+#include "virtqueue.h"
+
+#define virtio_user_get_dev(hwp) container_of(hwp, struct virtio_user_dev, hw)
+
+static void
+virtio_user_read_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		     void *dst, int length __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (offset == offsetof(struct virtio_crypto_config, status)) {
+		crypto_virtio_user_dev_update_link_state(dev);
+		*(uint32_t *)dst = dev->crypto_status;
+	} else if (offset == offsetof(struct virtio_crypto_config, max_dataqueues))
+		*(uint16_t *)dst = dev->max_queue_pairs;
+	else if (offset == offsetof(struct virtio_crypto_config, crypto_services))
+		*(uint32_t *)dst = dev->crypto_services;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_l))
+		*(uint32_t *)dst = dev->cipher_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_h))
+		*(uint32_t *)dst = dev->cipher_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, hash_algo))
+		*(uint32_t *)dst = dev->hash_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_l))
+		*(uint32_t *)dst = dev->auth_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_h))
+		*(uint32_t *)dst = dev->auth_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, aead_algo))
+		*(uint32_t *)dst = dev->aead_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, akcipher_algo))
+		*(uint32_t *)dst = dev->akcipher_algo;
+}
+
+static void
+virtio_user_write_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		      const void *src, int length)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(src);
+
+	PMD_DRV_LOG(ERR, "not supported offset=%zu, len=%d",
+		    offset, length);
+}
+
+static void
+virtio_user_reset(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+		crypto_virtio_user_stop_device(dev);
+}
+
+static void
+virtio_user_set_status(struct virtio_crypto_hw *hw, uint8_t status)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint8_t old_status = dev->status;
+
+	if (status & VIRTIO_CONFIG_STATUS_FEATURES_OK &&
+			~old_status & VIRTIO_CONFIG_STATUS_FEATURES_OK) {
+		crypto_virtio_user_dev_set_features(dev);
+		/* Feature negotiation should be only done in probe time.
+		 * So we skip any more request here.
+		 */
+		dev->status |= VIRTIO_CONFIG_STATUS_FEATURES_OK;
+	}
+
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) {
+		if (crypto_virtio_user_start_device(dev)) {
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	} else if (status == VIRTIO_CONFIG_STATUS_RESET) {
+		virtio_user_reset(hw);
+	}
+
+	crypto_virtio_user_dev_set_status(dev, status);
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK && dev->scvq) {
+		if (dev->ops->cvq_enable(dev, 1) < 0) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to start ctrlq", dev->path);
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	}
+}
+
+static uint8_t
+virtio_user_get_status(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	crypto_virtio_user_dev_update_status(dev);
+
+	return dev->status;
+}
+
+#define VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_RING_F_INDIRECT_DESC      | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+static uint64_t
+virtio_user_get_features(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* unmask feature bits defined in vhost user protocol */
+	return (dev->device_features | dev->frontend_features) &
+		VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES;
+}
+
+static void
+virtio_user_set_features(struct virtio_crypto_hw *hw, uint64_t features)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	dev->features = features & (dev->device_features | dev->frontend_features);
+}
+
+static uint8_t
+virtio_user_get_isr(struct virtio_crypto_hw *hw __rte_unused)
+{
+	/* rxq interrupts and config interrupt are separated in virtio-user,
+	 * here we only report config change.
+	 */
+	return VIRTIO_PCI_CAP_ISR_CFG;
+}
+
+static uint16_t
+virtio_user_set_config_irq(struct virtio_crypto_hw *hw __rte_unused,
+		    uint16_t vec __rte_unused)
+{
+	return 0;
+}
+
+static uint16_t
+virtio_user_set_queue_irq(struct virtio_crypto_hw *hw __rte_unused,
+			  struct virtqueue *vq __rte_unused,
+			  uint16_t vec)
+{
+	/* pretend we have done that */
+	return vec;
+}
+
+/* This function is to get the queue size, aka, number of descs, of a specified
+ * queue. Different with the VHOST_USER_GET_QUEUE_NUM, which is used to get the
+ * max supported queues.
+ */
+static uint16_t
+virtio_user_get_queue_num(struct virtio_crypto_hw *hw, uint16_t queue_id __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* Currently, each queue has same queue size */
+	return dev->queue_size;
+}
+
+static void
+virtio_user_setup_queue_packed(struct virtqueue *vq,
+			       struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	struct vring_packed *vring;
+	uint64_t desc_addr;
+	uint64_t avail_addr;
+	uint64_t used_addr;
+	uint16_t i;
+
+	vring  = &dev->vrings.packed[queue_idx];
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries *
+		sizeof(struct vring_packed_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr +
+			   sizeof(struct vring_packed_desc_event),
+			   VIRTIO_VRING_ALIGN);
+	vring->num = vq->vq_nentries;
+	vring->desc_iova = vq->vq_ring_mem;
+	vring->desc = (void *)(uintptr_t)desc_addr;
+	vring->driver = (void *)(uintptr_t)avail_addr;
+	vring->device = (void *)(uintptr_t)used_addr;
+	dev->packed_queues[queue_idx].avail_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_idx = 0;
+
+	for (i = 0; i < vring->num; i++)
+		vring->desc[i].flags = 0;
+}
+
+static void
+virtio_user_setup_queue_split(struct virtqueue *vq, struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	uint64_t desc_addr, avail_addr, used_addr;
+
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
+							 ring[vq->vq_nentries]),
+				   VIRTIO_VRING_ALIGN);
+
+	dev->vrings.split[queue_idx].num = vq->vq_nentries;
+	dev->vrings.split[queue_idx].desc_iova = vq->vq_ring_mem;
+	dev->vrings.split[queue_idx].desc = (void *)(uintptr_t)desc_addr;
+	dev->vrings.split[queue_idx].avail = (void *)(uintptr_t)avail_addr;
+	dev->vrings.split[queue_idx].used = (void *)(uintptr_t)used_addr;
+}
+
+static int
+virtio_user_setup_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (vtpci_with_packed_queue(hw))
+		virtio_user_setup_queue_packed(vq, dev);
+	else
+		virtio_user_setup_queue_split(vq, dev);
+
+	if (dev->notify_area)
+		vq->notify_addr = dev->notify_area[vq->vq_queue_index];
+
+	if (virtcrypto_cq_to_vq(hw->cvq) == vq)
+		dev->scvq = virtcrypto_cq_to_vq(hw->cvq);
+
+	return 0;
+}
+
+static void
+virtio_user_del_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(vq);
+}
+
+static void
+virtio_user_notify_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint64_t notify_data = 1;
+
+	if (!dev->notify_area) {
+		if (write(dev->kickfds[vq->vq_queue_index], &notify_data,
+			  sizeof(notify_data)) < 0)
+			PMD_DRV_LOG(ERR, "failed to kick backend: %s",
+				    strerror(errno));
+		return;
+	} else if (!vtpci_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) {
+		rte_write16(vq->vq_queue_index, vq->notify_addr);
+		return;
+	}
+
+	if (vtpci_with_packed_queue(hw)) {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:30]: avail index
+		 * Bit[31]: avail wrap counter
+		 */
+		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
+				VRING_PACKED_DESC_F_AVAIL)) << 31) |
+				((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	} else {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:31]: avail index
+		 */
+		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	}
+	rte_write32(notify_data, vq->notify_addr);
+}
+
+const struct virtio_pci_ops crypto_virtio_user_ops = {
+	.read_dev_cfg	= virtio_user_read_dev_config,
+	.write_dev_cfg	= virtio_user_write_dev_config,
+	.reset		= virtio_user_reset,
+	.get_status	= virtio_user_get_status,
+	.set_status	= virtio_user_set_status,
+	.get_features	= virtio_user_get_features,
+	.set_features	= virtio_user_set_features,
+	.get_isr	= virtio_user_get_isr,
+	.set_config_irq	= virtio_user_set_config_irq,
+	.set_queue_irq	= virtio_user_set_queue_irq,
+	.get_queue_num	= virtio_user_get_queue_num,
+	.setup_queue	= virtio_user_setup_queue,
+	.del_queue	= virtio_user_del_queue,
+	.notify_queue	= virtio_user_notify_queue,
+};
+
+static const char * const valid_args[] = {
+#define VIRTIO_USER_ARG_QUEUES_NUM     "queues"
+	VIRTIO_USER_ARG_QUEUES_NUM,
+#define VIRTIO_USER_ARG_QUEUE_SIZE     "queue_size"
+	VIRTIO_USER_ARG_QUEUE_SIZE,
+#define VIRTIO_USER_ARG_PATH           "path"
+	VIRTIO_USER_ARG_PATH,
+	NULL
+};
+
+#define VIRTIO_USER_DEF_Q_NUM	1
+#define VIRTIO_USER_DEF_Q_SZ	256
+#define VIRTIO_USER_DEF_SERVER_MODE	0
+
+static int
+get_string_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	if (!value || !extra_args)
+		return -EINVAL;
+
+	*(char **)extra_args = strdup(value);
+
+	if (!*(char **)extra_args)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int
+get_integer_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	uint64_t integer = 0;
+	if (!value || !extra_args)
+		return -EINVAL;
+	errno = 0;
+	integer = strtoull(value, NULL, 0);
+	/* extra_args keeps default value, it should be replaced
+	 * only in case of successful parsing of the 'value' arg
+	 */
+	if (errno == 0)
+		*(uint64_t *)extra_args = integer;
+	return -errno;
+}
+
+static struct rte_cryptodev *
+virtio_user_cryptodev_alloc(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev_pmd_init_params init_params = {
+		.name = "",
+		.private_data_size = sizeof(struct virtio_user_dev),
+	};
+	struct rte_cryptodev_data *data;
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	struct virtio_crypto_hw *hw;
+
+	init_params.socket_id = vdev->device.numa_node;
+	init_params.private_data_size = sizeof(struct virtio_user_dev);
+	cryptodev = rte_cryptodev_pmd_create(vdev->device.name, &vdev->device, &init_params);
+	if (cryptodev == NULL) {
+		PMD_INIT_LOG(ERR, "failed to create cryptodev vdev");
+		return NULL;
+	}
+
+	data = cryptodev->data;
+	dev = data->dev_private;
+	hw = &dev->hw;
+
+	hw->dev_id = data->dev_id;
+	VTPCI_OPS(hw) = &crypto_virtio_user_ops;
+
+	return cryptodev;
+}
+
+static void
+virtio_user_cryptodev_free(struct rte_cryptodev *cryptodev)
+{
+	rte_cryptodev_pmd_destroy(cryptodev);
+}
+
+static int
+virtio_user_pmd_probe(struct rte_vdev_device *vdev)
+{
+	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
+	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
+	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
+	struct rte_cryptodev *cryptodev = NULL;
+	struct rte_kvargs *kvlist = NULL;
+	struct virtio_user_dev *dev;
+	char *path = NULL;
+	int ret;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(vdev), valid_args);
+
+	if (!kvlist) {
+		PMD_INIT_LOG(ERR, "error when parsing param");
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PATH) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PATH,
+					&get_string_arg, &path) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_PATH);
+			goto end;
+		}
+	} else {
+		PMD_INIT_LOG(ERR, "arg %s is mandatory for virtio_user",
+				VIRTIO_USER_ARG_PATH);
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUES_NUM) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUES_NUM,
+					&get_integer_arg, &queues) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUES_NUM);
+			goto end;
+		}
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE,
+					&get_integer_arg, &queue_size) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUE_SIZE);
+			goto end;
+		}
+	}
+
+	cryptodev = virtio_user_cryptodev_alloc(vdev);
+	if (!cryptodev) {
+		PMD_INIT_LOG(ERR, "virtio_user fails to alloc device");
+		goto end;
+	}
+
+	dev = cryptodev->data->dev_private;
+	if (crypto_virtio_user_dev_init(dev, path, queues, queue_size,
+			server_mode) < 0) {
+		PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES,
+			NULL) < 0) {
+		PMD_INIT_LOG(ERR, "crypto_virtio_dev_init fails");
+		crypto_virtio_user_dev_uninit(dev);
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	rte_cryptodev_pmd_probing_finish(cryptodev);
+
+	ret = 0;
+end:
+	rte_kvargs_free(kvlist);
+	free(path);
+	return ret;
+}
+
+static int
+virtio_user_pmd_remove(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev *cryptodev;
+	const char *name;
+	int devid;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	PMD_DRV_LOG(INFO, "Removing %s", name);
+
+	devid = rte_cryptodev_get_dev_id(name);
+	if (devid < 0)
+		return -EINVAL;
+
+	rte_cryptodev_stop(devid);
+
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (rte_cryptodev_pmd_destroy(cryptodev) < 0) {
+		PMD_DRV_LOG(ERR, "Failed to remove %s", name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_map(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_map)
+		return dev->ops->dma_map(dev, addr, iova, len);
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_unmap(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_unmap)
+		return dev->ops->dma_unmap(dev, addr, iova, len);
+
+	return 0;
+}
+
+static struct rte_vdev_driver virtio_user_driver = {
+	.probe = virtio_user_pmd_probe,
+	.remove = virtio_user_pmd_remove,
+	.dma_map = virtio_user_pmd_dma_map,
+	.dma_unmap = virtio_user_pmd_dma_unmap,
+};
+
+static struct cryptodev_driver virtio_crypto_drv;
+
+uint8_t cryptodev_virtio_user_driver_id;
+
+RTE_PMD_REGISTER_VDEV(crypto_virtio_user, virtio_user_driver);
+RTE_PMD_REGISTER_CRYPTO_DRIVER(virtio_crypto_drv,
+	virtio_user_driver.driver,
+	cryptodev_virtio_user_driver_id);
+RTE_PMD_REGISTER_PARAM_STRING(crypto_virtio_user,
+	"path=<path> "
+	"queues=<int> "
+	"queue_size=<int>");
-- 
2.25.1


^ permalink raw reply	[relevance 1%]

* [RFC PATCH v19] mempool: fix mempool cache size
    2025-02-21 15:13  4% ` [RFC PATCH v18] mempool: fix mempool " Morten Brørup
@ 2025-02-21 19:05  3% ` Morten Brørup
  2025-02-21 20:27  3% ` [RFC PATCH v20] " Morten Brørup
  2 siblings, 0 replies; 153+ results
From: Morten Brørup @ 2025-02-21 19:05 UTC (permalink / raw)
  To: dev; +Cc: Morten Brørup

NOTE: THIS VERSION DOES NOT BREAK THE API/ABI.

First, a per-lcore mempool cache could hold 50 % more than the cache's
size.
Since application developers do not expect this behavior, it could lead to
application failure.
This patch fixes this bug without breaking the API/ABI, by using the
mempool cache's "size" instead of the "flushthresh" as the threshold for
how many objects can be held in a mempool cache.
Note: The "flushthresh" field can be removed from the cache structure in a
future API/ABI breaking release, which must be announced in advance.

Second, requests to fetch a number of objects from the backend driver
exceeding the cache's size (but less than RTE_MEMPOOL_CACHE_MAX_SIZE) were
copied twice; first to the cache, and from there to the destination.
Such superfluous copying through the mempool cache degrades the
performance in these cases.
This patch also fixes this misbehavior, so when fetching more objects from
the driver than the mempool cache's size, they are fetched directly to the
destination.

The internal macro to calculate the cache flush threshold was updated to
reflect the new flush threshold of 1 * size instead of 1.5 * size.

The function rte_mempool_do_generic_put() for adding objects to a mempool
was modified as follows:
- When determining if the cache has sufficient room for the request
  without flushing, compare to the cache's size (cache->size) instead of
  the obsolete flush threshold (cache->flushthresh).
- The comparison for the request being too big, which is considered
  unlikely, was moved down and out of the code path where the cache has
  sufficient room for the added objects, which is considered the most
  likely code path.
- Added __rte_assume() about the cache size, for compiler optimization
  when "n" is compile time constant.
- Added __rte_assume() about "ret", for compiler optimization of
  rte_mempool_generic_get() considering the return value of
  rte_mempool_do_generic_get().

The function rte_mempool_do_generic_get() for getting objects from a
mempool was refactored as follows:
- Handling a request for a constant number of objects was merged with
  handling a request for a nonconstant number of objects, and a note about
  compiler loop unrolling in the constant case was added.
- When determining if the remaining part of a request to be dequeued from
  the backend is too big to be copied via the cache, compare to the
  cache's size (cache->size) instead of the max possible cache size
  (RTE_MEMPOOL_CACHE_MAX_SIZE).
- When refilling the cache, the target fill level was reduced from the
  full cache size to half the cache size. This allows some room for a
  put() request following a get() request where the cache was refilled,
  without "flapping" between draining and refilling the entire cache.
  Note: Before this patch, the distance between the flush threshold and
  the refill level was also half a cache size.
- A copy of cache->len in the local variable "len" is no longer needed,
  so it was removed.
- Added a group of __rte_assume()'s, for compiler optimization when "n" is
  compile time constant.

Some comments were also updated.

Furthermore, some likely()/unlikely()'s were added to a few inline
functions; most prominently rte_mempool_default_cache(), which is used by
both rte_mempool_put_bulk() and rte_mempool_get_bulk().

And finally, RTE_ASSERT()'s were added to check the return values of the
mempool driver dequeue() and enqueue() operations.

Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
---
v19:
* Added __rte_assume()'s and RTE_ASSERT()'s.
v18:
* Start over from scratch, to avoid API/ABI breakage.
v17:
* Update rearm in idpf driver.
v16:
* Fix bug in rte_mempool_do_generic_put() regarding criteria for flush.
v15:
* Changed back cache bypass limit from n >= RTE_MEMPOOL_CACHE_MAX_SIZE to
  n > RTE_MEMPOOL_CACHE_MAX_SIZE.
* Removed cache size limit from serving via cache.
v14:
* Change rte_mempool_do_generic_put() back from add-then-flush to
  flush-then-add.
  Keep the target cache fill level of ca. 1/2 size of the cache.
v13:
* Target a cache fill level of ca. 1/2 size of the cache when flushing and
  refilling; based on an assumption of equal probability of get and put,
  instead of assuming a higher probability of put being followed by
  another put, and get being followed by another get.
* Reduce the amount of changes to the drivers.
v12:
* Do not init mempool caches with size zero; they don't exist.
  Bug introduced in v10.
v11:
* Removed rte_mempool_do_generic_get_split().
v10:
* Initialize mempool caches, regardless of size zero.
  This to fix compiler warning about out of bounds access.
v9:
* Removed factor 1.5 from description of cache_size parameter to
  rte_mempool_create().
* Refactored rte_mempool_do_generic_put() to eliminate some gotos.
  No functional change.
* Removed check for n >= RTE_MEMPOOL_CACHE_MAX_SIZE in
  rte_mempool_do_generic_get(); it caused the function to fail when the
  request could not be served from the backend alone, but it could be
  served from the cache and the backend.
* Refactored rte_mempool_do_generic_get_split() to make it shorter.
* When getting objects directly from the backend, use burst size aligned
  with either CPU cache line size or mempool cache size.
v8:
* Rewrote rte_mempool_do_generic_put() to get rid of transaction
  splitting. Use a method similar to the existing put method with fill
  followed by flush if overfilled.
  This also made rte_mempool_do_generic_put_split() obsolete.
* When flushing the cache as much as we can, use burst size aligned with
  either CPU cache line size or mempool cache size.
v7:
* Increased max mempool cache size from 512 to 1024 objects.
  Mainly for CI performance test purposes.
  Originally, the max mempool cache size was 768 objects, and used a fixed
  size array of 1024 objects in the mempool cache structure.
v6:
* Fix v5 incomplete implementation of passing large requests directly to
  the backend.
* Use memcpy instead of rte_memcpy where compiler complains about it.
* Added const to some function parameters.
v5:
* Moved helper functions back into the header file, for improved
  performance.
* Pass large requests directly to the backend. This also simplifies the
  code.
v4:
* Updated subject to reflect that misleading names are considered bugs.
* Rewrote patch description to provide more details about the bugs fixed.
  (Mattias Rönnblom)
* Moved helper functions, not to be inlined, to mempool C file.
  (Mattias Rönnblom)
* Pass requests for n >= RTE_MEMPOOL_CACHE_MAX_SIZE objects known at build
  time directly to backend driver, to avoid calling the helper functions.
  This also fixes the compiler warnings about out of bounds array access.
v3:
* Removed __attribute__(assume).
v2:
* Removed mempool perf test; not part of patch set.
---
 lib/mempool/rte_mempool.c |   5 +-
 lib/mempool/rte_mempool.h | 105 ++++++++++++++++----------------------
 2 files changed, 47 insertions(+), 63 deletions(-)

diff --git a/lib/mempool/rte_mempool.c b/lib/mempool/rte_mempool.c
index 1e4f24783c..cddc896442 100644
--- a/lib/mempool/rte_mempool.c
+++ b/lib/mempool/rte_mempool.c
@@ -50,10 +50,9 @@ static void
 mempool_event_callback_invoke(enum rte_mempool_event event,
 			      struct rte_mempool *mp);
 
-/* Note: avoid using floating point since that compiler
- * may not think that is constant.
+/* Note: This is no longer 1.5 * size, but simply 1 * size.
  */
-#define CALC_CACHE_FLUSHTHRESH(c) (((c) * 3) / 2)
+#define CALC_CACHE_FLUSHTHRESH(c) (c)
 
 #if defined(RTE_ARCH_X86)
 /*
diff --git a/lib/mempool/rte_mempool.h b/lib/mempool/rte_mempool.h
index c495cc012f..7742677c01 100644
--- a/lib/mempool/rte_mempool.h
+++ b/lib/mempool/rte_mempool.h
@@ -791,7 +791,8 @@ rte_mempool_ops_dequeue_bulk(struct rte_mempool *mp,
 	rte_mempool_trace_ops_dequeue_bulk(mp, obj_table, n);
 	ops = rte_mempool_get_ops(mp->ops_index);
 	ret = ops->dequeue(mp, obj_table, n);
-	if (ret == 0) {
+	RTE_ASSERT(ret <= 0);
+	if (likely(ret == 0)) {
 		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_bulk, 1);
 		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_objs, n);
 	}
@@ -848,6 +849,7 @@ rte_mempool_ops_enqueue_bulk(struct rte_mempool *mp, void * const *obj_table,
 	rte_mempool_trace_ops_enqueue_bulk(mp, obj_table, n);
 	ops = rte_mempool_get_ops(mp->ops_index);
 	ret = ops->enqueue(mp, obj_table, n);
+	RTE_ASSERT(ret <= 0);
 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
 	if (unlikely(ret < 0))
 		RTE_MEMPOOL_LOG(CRIT, "cannot enqueue %u objects to mempool %s",
@@ -1044,7 +1046,7 @@ rte_mempool_free(struct rte_mempool *mp);
  *   If cache_size is non-zero, the rte_mempool library will try to
  *   limit the accesses to the common lockless pool, by maintaining a
  *   per-lcore object cache. This argument must be lower or equal to
- *   RTE_MEMPOOL_CACHE_MAX_SIZE and n / 1.5. It is advised to choose
+ *   RTE_MEMPOOL_CACHE_MAX_SIZE and n. It is advised to choose
  *   cache_size to have "n modulo cache_size == 0": if this is
  *   not the case, some elements will always stay in the pool and will
  *   never be used. The access to the per-lcore table is of course
@@ -1333,10 +1335,10 @@ rte_mempool_cache_free(struct rte_mempool_cache *cache);
 static __rte_always_inline struct rte_mempool_cache *
 rte_mempool_default_cache(struct rte_mempool *mp, unsigned lcore_id)
 {
-	if (mp->cache_size == 0)
+	if (unlikely(mp->cache_size == 0))
 		return NULL;
 
-	if (lcore_id >= RTE_MAX_LCORE)
+	if (unlikely(lcore_id >= RTE_MAX_LCORE))
 		return NULL;
 
 	rte_mempool_trace_default_cache(mp, lcore_id,
@@ -1383,32 +1385,33 @@ rte_mempool_do_generic_put(struct rte_mempool *mp, void * const *obj_table,
 {
 	void **cache_objs;
 
-	/* No cache provided */
+	/* No cache provided? */
 	if (unlikely(cache == NULL))
 		goto driver_enqueue;
 
-	/* increment stat now, adding in mempool always success */
+	/* Increment stats now, adding in mempool always succeeds. */
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_bulk, 1);
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_objs, n);
 
-	/* The request itself is too big for the cache */
-	if (unlikely(n > cache->flushthresh))
-		goto driver_enqueue_stats_incremented;
-
-	/*
-	 * The cache follows the following algorithm:
-	 *   1. If the objects cannot be added to the cache without crossing
-	 *      the flush threshold, flush the cache to the backend.
-	 *   2. Add the objects to the cache.
-	 */
-
-	if (cache->len + n <= cache->flushthresh) {
+	__rte_assume(cache->size <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	__rte_assume(cache->len <= cache->size);
+	if (likely(cache->len + n <= cache->size)) {
+		/* Sufficient room in the cache for the objects. */
 		cache_objs = &cache->objs[cache->len];
 		cache->len += n;
-	} else {
+	} else if (n <= cache->size) {
+		/*
+		 * The cache is big enough for the objects, but - as detected by
+		 * the comparison above - has insufficient room for them.
+		 * Flush the cache to make room for the objects.
+		 */
 		cache_objs = &cache->objs[0];
 		rte_mempool_ops_enqueue_bulk(mp, cache_objs, cache->len);
 		cache->len = n;
+	} else {
+		/* The request itself is too big for the cache. */
+		goto driver_enqueue_stats_incremented;
 	}
 
 	/* Add the objects to the cache. */
@@ -1512,10 +1515,10 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 {
 	int ret;
 	unsigned int remaining;
-	uint32_t index, len;
+	uint32_t index;
 	void **cache_objs;
 
-	/* No cache provided */
+	/* No cache provided? */
 	if (unlikely(cache == NULL)) {
 		remaining = n;
 		goto driver_dequeue;
@@ -1524,11 +1527,12 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 	/* The cache is a stack, so copy will be in reverse order. */
 	cache_objs = &cache->objs[cache->len];
 
-	if (__rte_constant(n) && n <= cache->len) {
+	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	if (likely(n <= cache->len)) {
 		/*
-		 * The request size is known at build time, and
-		 * the entire request can be satisfied from the cache,
-		 * so let the compiler unroll the fixed length copy loop.
+		 * The entire request can be satisfied from the cache.
+		 * Note: If the request size is known at build time,
+		 * the compiler will unroll the fixed length copy loop.
 		 */
 		cache->len -= n;
 		for (index = 0; index < n; index++)
@@ -1540,55 +1544,35 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 		return 0;
 	}
 
-	/*
-	 * Use the cache as much as we have to return hot objects first.
-	 * If the request size 'n' is known at build time, the above comparison
-	 * ensures that n > cache->len here, so omit RTE_MIN().
-	 */
-	len = __rte_constant(n) ? cache->len : RTE_MIN(n, cache->len);
-	cache->len -= len;
-	remaining = n - len;
-	for (index = 0; index < len; index++)
+	/* Use the cache as much as we have to return hot objects first. */
+	for (index = 0; index < cache->len; index++)
 		*obj_table++ = *--cache_objs;
+	remaining = n - cache->len;
+	cache->len = 0;
 
-	/*
-	 * If the request size 'n' is known at build time, the case
-	 * where the entire request can be satisfied from the cache
-	 * has already been handled above, so omit handling it here.
-	 */
-	if (!__rte_constant(n) && remaining == 0) {
-		/* The entire request is satisfied from the cache. */
-
-		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
-		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_objs, n);
-
-		return 0;
-	}
-
-	/* if dequeue below would overflow mem allocated for cache */
-	if (unlikely(remaining > RTE_MEMPOOL_CACHE_MAX_SIZE))
+	/* The remaining request is too big for the cache? */
+	if (unlikely(remaining > cache->size))
 		goto driver_dequeue;
 
-	/* Fill the cache from the backend; fetch size + remaining objects. */
+	/* Fill the cache from the backend; fetch size / 2 + remaining objects. */
 	ret = rte_mempool_ops_dequeue_bulk(mp, cache->objs,
-			cache->size + remaining);
+			cache->size / 2 + remaining);
 	if (unlikely(ret < 0)) {
 		/*
-		 * We are buffer constrained, and not able to allocate
-		 * cache + remaining.
+		 * We are buffer constrained, and not able to fetch all that.
 		 * Do not fill the cache, just satisfy the remaining part of
 		 * the request directly from the backend.
 		 */
 		goto driver_dequeue;
 	}
 
+	cache->len = cache->size / 2;
+
 	/* Satisfy the remaining part of the request from the filled cache. */
-	cache_objs = &cache->objs[cache->size + remaining];
+	cache_objs = &cache->objs[cache->len + remaining];
 	for (index = 0; index < remaining; index++)
 		*obj_table++ = *--cache_objs;
 
-	cache->len = cache->size;
-
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_objs, n);
 
@@ -1599,7 +1583,7 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 	/* Get remaining objects directly from the backend. */
 	ret = rte_mempool_ops_dequeue_bulk(mp, obj_table, remaining);
 
-	if (ret < 0) {
+	if (unlikely(ret < 0)) {
 		if (likely(cache != NULL)) {
 			cache->len = n - remaining;
 			/*
@@ -1619,6 +1603,7 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 			RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
 			RTE_MEMPOOL_STAT_ADD(mp, get_success_objs, n);
 		}
+		__rte_assume(ret == 0);
 	}
 
 	return ret;
@@ -1650,7 +1635,7 @@ rte_mempool_generic_get(struct rte_mempool *mp, void **obj_table,
 {
 	int ret;
 	ret = rte_mempool_do_generic_get(mp, obj_table, n, cache);
-	if (ret == 0)
+	if (likely(ret == 0))
 		RTE_MEMPOOL_CHECK_COOKIES(mp, obj_table, n, 1);
 	rte_mempool_trace_generic_get(mp, obj_table, n, cache);
 	return ret;
@@ -1741,7 +1726,7 @@ rte_mempool_get_contig_blocks(struct rte_mempool *mp,
 	int ret;
 
 	ret = rte_mempool_ops_dequeue_contig_blocks(mp, first_obj_table, n);
-	if (ret == 0) {
+	if (likely(ret == 0)) {
 		RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
 		RTE_MEMPOOL_STAT_ADD(mp, get_success_blks, n);
 		RTE_MEMPOOL_CONTIG_BLOCKS_CHECK_COOKIES(mp, first_obj_table, n,
-- 
2.43.0


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] sched: fix wrr parameter data type
  @ 2025-02-21 19:14  3% ` Stephen Hemminger
  0 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-02-21 19:14 UTC (permalink / raw)
  To: Megha Ajmera; +Cc: dev, jasvinder.singh, cristian.dumitrescu

On Fri, 21 Feb 2025 18:17:55 +0530
Megha Ajmera <megha.ajmera@intel.com> wrote:

> wrr tokens getting truncated to uint8_t in wrr_store function() due to
> type mismatch. This patch chnages the data type to uint16_t.
> 
> Fixes: e16b06da0908 ("sched: remove WRR from strict priority TC queues")
> 
> Signed-off-by: Megha Ajmera <megha.ajmera@intel.com>
> ---
>  lib/sched/rte_sched.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/lib/sched/rte_sched.c b/lib/sched/rte_sched.c
> index d8ee4e7e91..dcef44b91b 100644
> --- a/lib/sched/rte_sched.c
> +++ b/lib/sched/rte_sched.c
> @@ -66,7 +66,7 @@ struct __rte_cache_aligned rte_sched_pipe {
>  	uint64_t tc_credits[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
>  
>  	/* Weighted Round Robin (WRR) */
> -	uint8_t wrr_tokens[RTE_SCHED_BE_QUEUES_PER_PIPE];
> +	uint16_t wrr_tokens[RTE_SCHED_BE_QUEUES_PER_PIPE];
>  
>  	/* TC oversubscription */
>  	uint64_t tc_ov_credits;

This would be a change in ABI.

^ permalink raw reply	[relevance 3%]

* [RFC PATCH v20] mempool: fix mempool cache size
    2025-02-21 15:13  4% ` [RFC PATCH v18] mempool: fix mempool " Morten Brørup
  2025-02-21 19:05  3% ` [RFC PATCH v19] " Morten Brørup
@ 2025-02-21 20:27  3% ` Morten Brørup
  2 siblings, 0 replies; 153+ results
From: Morten Brørup @ 2025-02-21 20:27 UTC (permalink / raw)
  To: dev; +Cc: Morten Brørup

NOTE: THIS VERSION DOES NOT BREAK THE API/ABI.

First, a per-lcore mempool cache could hold 50 % more than the cache's
size.
Since application developers do not expect this behavior, it could lead to
application failure.
This patch fixes this bug without breaking the API/ABI, by using the
mempool cache's "size" instead of the "flushthresh" as the threshold for
how many objects can be held in a mempool cache.
Note: The "flushthresh" field can be removed from the cache structure in a
future API/ABI breaking release, which must be announced in advance.

Second, requests to fetch a number of objects from the backend driver
exceeding the cache's size (but less than RTE_MEMPOOL_CACHE_MAX_SIZE) were
copied twice; first to the cache, and from there to the destination.
Such superfluous copying through the mempool cache degrades the
performance in these cases.
This patch also fixes this misbehavior, so when fetching more objects from
the driver than the mempool cache's size, they are fetched directly to the
destination.

The internal macro to calculate the cache flush threshold was updated to
reflect the new flush threshold of 1 * size instead of 1.5 * size.

The function rte_mempool_do_generic_put() for adding objects to a mempool
was modified as follows:
- When determining if the cache has sufficient room for the request
  without flushing, compare to the cache's size (cache->size) instead of
  the obsolete flush threshold (cache->flushthresh).
- The comparison for the request being too big, which is considered
  unlikely, was moved down and out of the code path where the cache has
  sufficient room for the added objects, which is considered the most
  likely code path.
- Added __rte_assume() about the cache size, for compiler optimization
  when "n" is compile time constant.
- Added __rte_assume() about "ret", for compiler optimization of
  rte_mempool_generic_get() considering the return value of
  rte_mempool_do_generic_get().

The function rte_mempool_do_generic_get() for getting objects from a
mempool was refactored as follows:
- Handling a request for a constant number of objects was merged with
  handling a request for a nonconstant number of objects, and a note about
  compiler loop unrolling in the constant case was added.
- When determining if the remaining part of a request to be dequeued from
  the backend is too big to be copied via the cache, compare to the
  cache's size (cache->size) instead of the max possible cache size
  (RTE_MEMPOOL_CACHE_MAX_SIZE).
- When refilling the cache, the target fill level was reduced from the
  full cache size to half the cache size. This allows some room for a
  put() request following a get() request where the cache was refilled,
  without "flapping" between draining and refilling the entire cache.
  Note: Before this patch, the distance between the flush threshold and
  the refill level was also half a cache size.
- A copy of cache->len in the local variable "len" is no longer needed,
  so it was removed.
- Added a group of __rte_assume()'s, for compiler optimization when "n" is
  compile time constant.

Some comments were also updated.

Furthermore, some likely()/unlikely()'s were added to a few inline
functions; most prominently rte_mempool_default_cache(), which is used by
both rte_mempool_put_bulk() and rte_mempool_get_bulk().

And finally, RTE_ASSERT()'s were added to check the return values of the
mempool driver dequeue() and enqueue() operations.

Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
---
v20:
* Added more __rte_assume()'s to fix build error with GCC 11.4.1 and
  GCC 11.5.0 in call to mempool_get_bulk() with compile time constant "n"
  larger than RTE_MEMPOOL_CACHE_MAX_SIZE.
v19:
* Added __rte_assume()'s and RTE_ASSERT()'s.
v18:
* Start over from scratch, to avoid API/ABI breakage.
v17:
* Update rearm in idpf driver.
v16:
* Fix bug in rte_mempool_do_generic_put() regarding criteria for flush.
v15:
* Changed back cache bypass limit from n >= RTE_MEMPOOL_CACHE_MAX_SIZE to
  n > RTE_MEMPOOL_CACHE_MAX_SIZE.
* Removed cache size limit from serving via cache.
v14:
* Change rte_mempool_do_generic_put() back from add-then-flush to
  flush-then-add.
  Keep the target cache fill level of ca. 1/2 size of the cache.
v13:
* Target a cache fill level of ca. 1/2 size of the cache when flushing and
  refilling; based on an assumption of equal probability of get and put,
  instead of assuming a higher probability of put being followed by
  another put, and get being followed by another get.
* Reduce the amount of changes to the drivers.
v12:
* Do not init mempool caches with size zero; they don't exist.
  Bug introduced in v10.
v11:
* Removed rte_mempool_do_generic_get_split().
v10:
* Initialize mempool caches, regardless of size zero.
  This to fix compiler warning about out of bounds access.
v9:
* Removed factor 1.5 from description of cache_size parameter to
  rte_mempool_create().
* Refactored rte_mempool_do_generic_put() to eliminate some gotos.
  No functional change.
* Removed check for n >= RTE_MEMPOOL_CACHE_MAX_SIZE in
  rte_mempool_do_generic_get(); it caused the function to fail when the
  request could not be served from the backend alone, but it could be
  served from the cache and the backend.
* Refactored rte_mempool_do_generic_get_split() to make it shorter.
* When getting objects directly from the backend, use burst size aligned
  with either CPU cache line size or mempool cache size.
v8:
* Rewrote rte_mempool_do_generic_put() to get rid of transaction
  splitting. Use a method similar to the existing put method with fill
  followed by flush if overfilled.
  This also made rte_mempool_do_generic_put_split() obsolete.
* When flushing the cache as much as we can, use burst size aligned with
  either CPU cache line size or mempool cache size.
v7:
* Increased max mempool cache size from 512 to 1024 objects.
  Mainly for CI performance test purposes.
  Originally, the max mempool cache size was 768 objects, and used a fixed
  size array of 1024 objects in the mempool cache structure.
v6:
* Fix v5 incomplete implementation of passing large requests directly to
  the backend.
* Use memcpy instead of rte_memcpy where compiler complains about it.
* Added const to some function parameters.
v5:
* Moved helper functions back into the header file, for improved
  performance.
* Pass large requests directly to the backend. This also simplifies the
  code.
v4:
* Updated subject to reflect that misleading names are considered bugs.
* Rewrote patch description to provide more details about the bugs fixed.
  (Mattias Rönnblom)
* Moved helper functions, not to be inlined, to mempool C file.
  (Mattias Rönnblom)
* Pass requests for n >= RTE_MEMPOOL_CACHE_MAX_SIZE objects known at build
  time directly to backend driver, to avoid calling the helper functions.
  This also fixes the compiler warnings about out of bounds array access.
v3:
* Removed __attribute__(assume).
v2:
* Removed mempool perf test; not part of patch set.
---
 lib/mempool/rte_mempool.c |   5 +-
 lib/mempool/rte_mempool.h | 108 +++++++++++++++++---------------------
 2 files changed, 50 insertions(+), 63 deletions(-)

diff --git a/lib/mempool/rte_mempool.c b/lib/mempool/rte_mempool.c
index 1e4f24783c..cddc896442 100644
--- a/lib/mempool/rte_mempool.c
+++ b/lib/mempool/rte_mempool.c
@@ -50,10 +50,9 @@ static void
 mempool_event_callback_invoke(enum rte_mempool_event event,
 			      struct rte_mempool *mp);
 
-/* Note: avoid using floating point since that compiler
- * may not think that is constant.
+/* Note: This is no longer 1.5 * size, but simply 1 * size.
  */
-#define CALC_CACHE_FLUSHTHRESH(c) (((c) * 3) / 2)
+#define CALC_CACHE_FLUSHTHRESH(c) (c)
 
 #if defined(RTE_ARCH_X86)
 /*
diff --git a/lib/mempool/rte_mempool.h b/lib/mempool/rte_mempool.h
index c495cc012f..de1b41d899 100644
--- a/lib/mempool/rte_mempool.h
+++ b/lib/mempool/rte_mempool.h
@@ -791,7 +791,8 @@ rte_mempool_ops_dequeue_bulk(struct rte_mempool *mp,
 	rte_mempool_trace_ops_dequeue_bulk(mp, obj_table, n);
 	ops = rte_mempool_get_ops(mp->ops_index);
 	ret = ops->dequeue(mp, obj_table, n);
-	if (ret == 0) {
+	RTE_ASSERT(ret <= 0);
+	if (likely(ret == 0)) {
 		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_bulk, 1);
 		RTE_MEMPOOL_STAT_ADD(mp, get_common_pool_objs, n);
 	}
@@ -848,6 +849,7 @@ rte_mempool_ops_enqueue_bulk(struct rte_mempool *mp, void * const *obj_table,
 	rte_mempool_trace_ops_enqueue_bulk(mp, obj_table, n);
 	ops = rte_mempool_get_ops(mp->ops_index);
 	ret = ops->enqueue(mp, obj_table, n);
+	RTE_ASSERT(ret <= 0);
 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
 	if (unlikely(ret < 0))
 		RTE_MEMPOOL_LOG(CRIT, "cannot enqueue %u objects to mempool %s",
@@ -1044,7 +1046,7 @@ rte_mempool_free(struct rte_mempool *mp);
  *   If cache_size is non-zero, the rte_mempool library will try to
  *   limit the accesses to the common lockless pool, by maintaining a
  *   per-lcore object cache. This argument must be lower or equal to
- *   RTE_MEMPOOL_CACHE_MAX_SIZE and n / 1.5. It is advised to choose
+ *   RTE_MEMPOOL_CACHE_MAX_SIZE and n. It is advised to choose
  *   cache_size to have "n modulo cache_size == 0": if this is
  *   not the case, some elements will always stay in the pool and will
  *   never be used. The access to the per-lcore table is of course
@@ -1333,10 +1335,10 @@ rte_mempool_cache_free(struct rte_mempool_cache *cache);
 static __rte_always_inline struct rte_mempool_cache *
 rte_mempool_default_cache(struct rte_mempool *mp, unsigned lcore_id)
 {
-	if (mp->cache_size == 0)
+	if (unlikely(mp->cache_size == 0))
 		return NULL;
 
-	if (lcore_id >= RTE_MAX_LCORE)
+	if (unlikely(lcore_id >= RTE_MAX_LCORE))
 		return NULL;
 
 	rte_mempool_trace_default_cache(mp, lcore_id,
@@ -1383,32 +1385,33 @@ rte_mempool_do_generic_put(struct rte_mempool *mp, void * const *obj_table,
 {
 	void **cache_objs;
 
-	/* No cache provided */
+	/* No cache provided? */
 	if (unlikely(cache == NULL))
 		goto driver_enqueue;
 
-	/* increment stat now, adding in mempool always success */
+	/* Increment stats now, adding in mempool always succeeds. */
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_bulk, 1);
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, put_objs, n);
 
-	/* The request itself is too big for the cache */
-	if (unlikely(n > cache->flushthresh))
-		goto driver_enqueue_stats_incremented;
-
-	/*
-	 * The cache follows the following algorithm:
-	 *   1. If the objects cannot be added to the cache without crossing
-	 *      the flush threshold, flush the cache to the backend.
-	 *   2. Add the objects to the cache.
-	 */
-
-	if (cache->len + n <= cache->flushthresh) {
+	__rte_assume(cache->size <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	__rte_assume(cache->len <= cache->size);
+	if (likely(cache->len + n <= cache->size)) {
+		/* Sufficient room in the cache for the objects. */
 		cache_objs = &cache->objs[cache->len];
 		cache->len += n;
-	} else {
+	} else if (n <= cache->size) {
+		/*
+		 * The cache is big enough for the objects, but - as detected by
+		 * the comparison above - has insufficient room for them.
+		 * Flush the cache to make room for the objects.
+		 */
 		cache_objs = &cache->objs[0];
 		rte_mempool_ops_enqueue_bulk(mp, cache_objs, cache->len);
 		cache->len = n;
+	} else {
+		/* The request itself is too big for the cache. */
+		goto driver_enqueue_stats_incremented;
 	}
 
 	/* Add the objects to the cache. */
@@ -1512,10 +1515,10 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 {
 	int ret;
 	unsigned int remaining;
-	uint32_t index, len;
+	uint32_t index;
 	void **cache_objs;
 
-	/* No cache provided */
+	/* No cache provided? */
 	if (unlikely(cache == NULL)) {
 		remaining = n;
 		goto driver_dequeue;
@@ -1524,11 +1527,12 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 	/* The cache is a stack, so copy will be in reverse order. */
 	cache_objs = &cache->objs[cache->len];
 
-	if (__rte_constant(n) && n <= cache->len) {
+	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	if (likely(n <= cache->len)) {
 		/*
-		 * The request size is known at build time, and
-		 * the entire request can be satisfied from the cache,
-		 * so let the compiler unroll the fixed length copy loop.
+		 * The entire request can be satisfied from the cache.
+		 * Note: If the request size is known at build time,
+		 * the compiler will unroll the fixed length copy loop.
 		 */
 		cache->len -= n;
 		for (index = 0; index < n; index++)
@@ -1540,55 +1544,38 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 		return 0;
 	}
 
-	/*
-	 * Use the cache as much as we have to return hot objects first.
-	 * If the request size 'n' is known at build time, the above comparison
-	 * ensures that n > cache->len here, so omit RTE_MIN().
-	 */
-	len = __rte_constant(n) ? cache->len : RTE_MIN(n, cache->len);
-	cache->len -= len;
-	remaining = n - len;
-	for (index = 0; index < len; index++)
+	/* Use the cache as much as we have to return hot objects first. */
+	for (index = 0; index < cache->len; index++)
 		*obj_table++ = *--cache_objs;
+	remaining = n - cache->len;
+	cache->len = 0;
 
-	/*
-	 * If the request size 'n' is known at build time, the case
-	 * where the entire request can be satisfied from the cache
-	 * has already been handled above, so omit handling it here.
-	 */
-	if (!__rte_constant(n) && remaining == 0) {
-		/* The entire request is satisfied from the cache. */
-
-		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
-		RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_objs, n);
-
-		return 0;
-	}
-
-	/* if dequeue below would overflow mem allocated for cache */
-	if (unlikely(remaining > RTE_MEMPOOL_CACHE_MAX_SIZE))
+	/* The remaining request is too big for the cache? */
+	__rte_assume(cache->size <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	if (unlikely(remaining > cache->size))
 		goto driver_dequeue;
 
-	/* Fill the cache from the backend; fetch size + remaining objects. */
+	/* Fill the cache from the backend; fetch size / 2 + remaining objects. */
 	ret = rte_mempool_ops_dequeue_bulk(mp, cache->objs,
-			cache->size + remaining);
+			cache->size / 2 + remaining);
 	if (unlikely(ret < 0)) {
 		/*
-		 * We are buffer constrained, and not able to allocate
-		 * cache + remaining.
+		 * We are buffer constrained, and not able to fetch all that.
 		 * Do not fill the cache, just satisfy the remaining part of
 		 * the request directly from the backend.
 		 */
 		goto driver_dequeue;
 	}
 
+	cache->len = cache->size / 2;
+
 	/* Satisfy the remaining part of the request from the filled cache. */
-	cache_objs = &cache->objs[cache->size + remaining];
+	__rte_assume(cache->len <= RTE_MEMPOOL_CACHE_MAX_SIZE / 2);
+	__rte_assume(remaining <= RTE_MEMPOOL_CACHE_MAX_SIZE);
+	cache_objs = &cache->objs[cache->len + remaining];
 	for (index = 0; index < remaining; index++)
 		*obj_table++ = *--cache_objs;
 
-	cache->len = cache->size;
-
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_bulk, 1);
 	RTE_MEMPOOL_CACHE_STAT_ADD(cache, get_success_objs, n);
 
@@ -1599,7 +1586,7 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 	/* Get remaining objects directly from the backend. */
 	ret = rte_mempool_ops_dequeue_bulk(mp, obj_table, remaining);
 
-	if (ret < 0) {
+	if (unlikely(ret < 0)) {
 		if (likely(cache != NULL)) {
 			cache->len = n - remaining;
 			/*
@@ -1619,6 +1606,7 @@ rte_mempool_do_generic_get(struct rte_mempool *mp, void **obj_table,
 			RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
 			RTE_MEMPOOL_STAT_ADD(mp, get_success_objs, n);
 		}
+		__rte_assume(ret == 0);
 	}
 
 	return ret;
@@ -1650,7 +1638,7 @@ rte_mempool_generic_get(struct rte_mempool *mp, void **obj_table,
 {
 	int ret;
 	ret = rte_mempool_do_generic_get(mp, obj_table, n, cache);
-	if (ret == 0)
+	if (likely(ret == 0))
 		RTE_MEMPOOL_CHECK_COOKIES(mp, obj_table, n, 1);
 	rte_mempool_trace_generic_get(mp, obj_table, n, cache);
 	return ret;
@@ -1741,7 +1729,7 @@ rte_mempool_get_contig_blocks(struct rte_mempool *mp,
 	int ret;
 
 	ret = rte_mempool_ops_dequeue_contig_blocks(mp, first_obj_table, n);
-	if (ret == 0) {
+	if (likely(ret == 0)) {
 		RTE_MEMPOOL_STAT_ADD(mp, get_success_bulk, 1);
 		RTE_MEMPOOL_STAT_ADD(mp, get_success_blks, n);
 		RTE_MEMPOOL_CONTIG_BLOCKS_CHECK_COOKIES(mp, first_obj_table, n,
-- 
2.43.0


^ permalink raw reply	[relevance 3%]

* RE: [EXTERNAL] [PATCH v8] graph: mcore: optimize graph search
  @ 2025-02-22  6:59  0%   ` Kiran Kumar Kokkilagadda
  0 siblings, 0 replies; 153+ results
From: Kiran Kumar Kokkilagadda @ 2025-02-22  6:59 UTC (permalink / raw)
  To: Huichao Cai, Jerin Jacob, Nithin Kumar Dabilpuram, yanzhirun_163; +Cc: dev



> -----Original Message-----
> From: Huichao Cai <chcchc88@163.com>
> Sent: Friday, February 7, 2025 7:10 AM
> To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar Kokkilagadda
> <kirankumark@marvell.com>; Nithin Kumar Dabilpuram
> <ndabilpuram@marvell.com>; yanzhirun_163@163.com
> Cc: dev@dpdk.org
> Subject: [EXTERNAL] [PATCH v8] graph: mcore: optimize graph search
> 
> In the function __rte_graph_mcore_dispatch_sched_node_enqueue, use a
> slower loop to search for the graph, modify the search logic to record the
> result of the first search, and use this record for subsequent searches to
> improve search speed. 
> In the function __rte_graph_mcore_dispatch_sched_node_enqueue,
> use a slower loop to search for the graph, modify the search logic to record the
> result of the first search, and use this record for subsequent searches to
> improve search speed.
> 
> Signed-off-by: Huichao Cai <chcchc88@163.com>
> ---

Acked-by: Kiran Kumar Kokkilagadda <kirankumark@marvell.com>



>  devtools/libabigail.abignore               |  5 +++++
>  doc/guides/rel_notes/release_25_03.rst     |  1 +
>  lib/graph/rte_graph_model_mcore_dispatch.c | 11 +++++++----
>  lib/graph/rte_graph_worker_common.h        |  1 +
>  4 files changed, 14 insertions(+), 4 deletions(-)
> 
> diff --git a/devtools/libabigail.abignore b/devtools/libabigail.abignore index
> 21b8cd6113..8876aaee2e 100644
> --- a/devtools/libabigail.abignore
> +++ b/devtools/libabigail.abignore
> @@ -33,3 +33,8 @@
>  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>  ; Temporary exceptions till next major ABI version ;
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> +[suppress_type]
> +        name = rte_node
> +        has_size_change = no
> +        has_data_member_inserted_between =
> +{offset_after(original_process), offset_of(xstat_off)}
> \ No newline at end of file
> diff --git a/doc/guides/rel_notes/release_25_03.rst
> b/doc/guides/rel_notes/release_25_03.rst
> index 269ab6f68a..16a888fd19 100644
> --- a/doc/guides/rel_notes/release_25_03.rst
> +++ b/doc/guides/rel_notes/release_25_03.rst
> @@ -150,6 +150,7 @@ ABI Changes
> 
>  * No ABI change that would break compatibility with 24.11.
> 
> +* graph: Added ``graph`` field to the ``dispatch`` structure in the ``rte_node``
> structure.
> 
>  Known Issues
>  ------------
> diff --git a/lib/graph/rte_graph_model_mcore_dispatch.c
> b/lib/graph/rte_graph_model_mcore_dispatch.c
> index a590fc9497..a81d338227 100644
> --- a/lib/graph/rte_graph_model_mcore_dispatch.c
> +++ b/lib/graph/rte_graph_model_mcore_dispatch.c
> @@ -118,11 +118,14 @@
> __rte_graph_mcore_dispatch_sched_node_enqueue(struct rte_node *node,
>  					      struct rte_graph_rq_head *rq)  {
>  	const unsigned int lcore_id = node->dispatch.lcore_id;
> -	struct rte_graph *graph;
> +	struct rte_graph *graph = node->dispatch.graph;
> 
> -	SLIST_FOREACH(graph, rq, next)
> -		if (graph->dispatch.lcore_id == lcore_id)
> -			break;
> +	if (unlikely((!graph) || (graph->dispatch.lcore_id != lcore_id))) {
> +		SLIST_FOREACH(graph, rq, next)
> +			if (graph->dispatch.lcore_id == lcore_id)
> +				break;
> +		node->dispatch.graph = graph;
> +	}
> 
>  	return graph != NULL ? __graph_sched_node_enqueue(node, graph) :
> false;  } diff --git a/lib/graph/rte_graph_worker_common.h
> b/lib/graph/rte_graph_worker_common.h
> index d3ec88519d..aef0f65673 100644
> --- a/lib/graph/rte_graph_worker_common.h
> +++ b/lib/graph/rte_graph_worker_common.h
> @@ -110,6 +110,7 @@ struct __rte_cache_aligned rte_node {
>  			unsigned int lcore_id;  /**< Node running lcore. */
>  			uint64_t total_sched_objs; /**< Number of objects
> scheduled. */
>  			uint64_t total_sched_fail; /**< Number of scheduled
> failure. */
> +			struct rte_graph *graph;  /**< Graph corresponding to
> lcore_id. */
>  		} dispatch;
>  	};
> 
> --
> 2.33.0


^ permalink raw reply	[relevance 0%]

* [v4 4/6] crypto/virtio: add vDPA backend
  @ 2025-02-22  9:16  1% ` Gowrishankar Muthukrishnan
  0 siblings, 0 replies; 153+ results
From: Gowrishankar Muthukrishnan @ 2025-02-22  9:16 UTC (permalink / raw)
  To: dev, Jay Zhou; +Cc: anoobj, Akhil Goyal, Gowrishankar Muthukrishnan

Add vDPA backend to virtio_user crypto.

Signed-off-by: Gowrishankar Muthukrishnan <gmuthukrishn@marvell.com>
---
Depends-on: series-34682 ("vhost: add RSA support")
v4:
 - fixed CI issue.
---
 drivers/crypto/virtio/meson.build             |   7 +
 drivers/crypto/virtio/virtio_cryptodev.c      |  57 +-
 drivers/crypto/virtio/virtio_cryptodev.h      |   3 +
 drivers/crypto/virtio/virtio_logs.h           |   6 +-
 drivers/crypto/virtio/virtio_pci.h            |   7 +
 drivers/crypto/virtio/virtio_ring.h           |   6 -
 drivers/crypto/virtio/virtio_user/vhost.h     |  90 +++
 .../crypto/virtio/virtio_user/vhost_vdpa.c    | 710 +++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.c      | 749 ++++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.h      |  85 ++
 drivers/crypto/virtio/virtio_user_cryptodev.c | 575 ++++++++++++++
 11 files changed, 2265 insertions(+), 30 deletions(-)
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost.h
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost_vdpa.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.h
 create mode 100644 drivers/crypto/virtio/virtio_user_cryptodev.c

diff --git a/drivers/crypto/virtio/meson.build b/drivers/crypto/virtio/meson.build
index d2c3b3ad07..3763e86746 100644
--- a/drivers/crypto/virtio/meson.build
+++ b/drivers/crypto/virtio/meson.build
@@ -16,3 +16,10 @@ sources = files(
         'virtio_rxtx.c',
         'virtqueue.c',
 )
+
+if is_linux
+    sources += files('virtio_user_cryptodev.c',
+        'virtio_user/vhost_vdpa.c',
+        'virtio_user/virtio_user_dev.c')
+    deps += ['bus_vdev']
+endif
diff --git a/drivers/crypto/virtio/virtio_cryptodev.c b/drivers/crypto/virtio/virtio_cryptodev.c
index 92fea557ab..bc737f1e68 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.c
+++ b/drivers/crypto/virtio/virtio_cryptodev.c
@@ -544,24 +544,12 @@ virtio_crypto_init_device(struct rte_cryptodev *cryptodev,
 	return 0;
 }
 
-/*
- * This function is based on probe() function
- * It returns 0 on success.
- */
-static int
-crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
-		struct rte_cryptodev_pmd_init_params *init_params)
+int
+crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev)
 {
-	struct rte_cryptodev *cryptodev;
 	struct virtio_crypto_hw *hw;
 
-	PMD_INIT_FUNC_TRACE();
-
-	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
-					init_params);
-	if (cryptodev == NULL)
-		return -ENODEV;
-
 	cryptodev->driver_id = cryptodev_virtio_driver_id;
 	cryptodev->dev_ops = &virtio_crypto_dev_ops;
 
@@ -578,16 +566,41 @@ crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
 	hw->dev_id = cryptodev->data->dev_id;
 	hw->virtio_dev_capabilities = virtio_capabilities;
 
-	VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
-		cryptodev->data->dev_id, pci_dev->id.vendor_id,
-		pci_dev->id.device_id);
+	if (pci_dev) {
+		/* pci device init */
+		VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
+			cryptodev->data->dev_id, pci_dev->id.vendor_id,
+			pci_dev->id.device_id);
 
-	/* pci device init */
-	if (vtpci_cryptodev_init(pci_dev, hw))
+		if (vtpci_cryptodev_init(pci_dev, hw))
+			return -1;
+	}
+
+	if (virtio_crypto_init_device(cryptodev, features) < 0)
 		return -1;
 
-	if (virtio_crypto_init_device(cryptodev,
-			VIRTIO_CRYPTO_PMD_GUEST_FEATURES) < 0)
+	return 0;
+}
+
+/*
+ * This function is based on probe() function
+ * It returns 0 on success.
+ */
+static int
+crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
+		struct rte_cryptodev_pmd_init_params *init_params)
+{
+	struct rte_cryptodev *cryptodev;
+
+	PMD_INIT_FUNC_TRACE();
+
+	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
+					init_params);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_CRYPTO_PMD_GUEST_FEATURES,
+			pci_dev) < 0)
 		return -1;
 
 	rte_cryptodev_pmd_probing_finish(cryptodev);
diff --git a/drivers/crypto/virtio/virtio_cryptodev.h b/drivers/crypto/virtio/virtio_cryptodev.h
index f8498246e2..fad73d54a8 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.h
+++ b/drivers/crypto/virtio/virtio_cryptodev.h
@@ -76,4 +76,7 @@ uint16_t virtio_crypto_pkt_rx_burst(void *tx_queue,
 		struct rte_crypto_op **tx_pkts,
 		uint16_t nb_pkts);
 
+int crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev);
+
 #endif /* _VIRTIO_CRYPTODEV_H_ */
diff --git a/drivers/crypto/virtio/virtio_logs.h b/drivers/crypto/virtio/virtio_logs.h
index 988514919f..1cc51f7990 100644
--- a/drivers/crypto/virtio/virtio_logs.h
+++ b/drivers/crypto/virtio/virtio_logs.h
@@ -15,8 +15,10 @@ extern int virtio_crypto_logtype_init;
 
 #define PMD_INIT_FUNC_TRACE() PMD_INIT_LOG(DEBUG, " >>")
 
-extern int virtio_crypto_logtype_init;
-#define RTE_LOGTYPE_VIRTIO_CRYPTO_INIT virtio_crypto_logtype_init
+extern int virtio_crypto_logtype_driver;
+#define RTE_LOGTYPE_VIRTIO_CRYPTO_DRIVER virtio_crypto_logtype_driver
+#define PMD_DRV_LOG(level, ...) \
+	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_DRIVER, "%s(): ", __func__, __VA_ARGS__)
 
 #define VIRTIO_CRYPTO_INIT_LOG_IMPL(level, ...) \
 	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_INIT, "%s(): ", __func__, __VA_ARGS__)
diff --git a/drivers/crypto/virtio/virtio_pci.h b/drivers/crypto/virtio/virtio_pci.h
index 79945cb88e..c75777e005 100644
--- a/drivers/crypto/virtio/virtio_pci.h
+++ b/drivers/crypto/virtio/virtio_pci.h
@@ -20,6 +20,9 @@ struct virtqueue;
 #define VIRTIO_CRYPTO_PCI_VENDORID 0x1AF4
 #define VIRTIO_CRYPTO_PCI_DEVICEID 0x1054
 
+/* VirtIO device IDs. */
+#define VIRTIO_ID_CRYPTO  20
+
 /* VirtIO ABI version, this must match exactly. */
 #define VIRTIO_PCI_ABI_VERSION 0
 
@@ -56,8 +59,12 @@ struct virtqueue;
 #define VIRTIO_CONFIG_STATUS_DRIVER    0x02
 #define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
 #define VIRTIO_CONFIG_STATUS_FEATURES_OK 0x08
+#define VIRTIO_CONFIG_STATUS_DEV_NEED_RESET	0x40
 #define VIRTIO_CONFIG_STATUS_FAILED    0x80
 
+/* The alignment to use between consumer and producer parts of vring. */
+#define VIRTIO_VRING_ALIGN 4096
+
 /*
  * Each virtqueue indirect descriptor list must be physically contiguous.
  * To allow us to malloc(9) each list individually, limit the number
diff --git a/drivers/crypto/virtio/virtio_ring.h b/drivers/crypto/virtio/virtio_ring.h
index c74d1172b7..4b418f6e60 100644
--- a/drivers/crypto/virtio/virtio_ring.h
+++ b/drivers/crypto/virtio/virtio_ring.h
@@ -181,12 +181,6 @@ vring_init_packed(struct vring_packed *vr, uint8_t *p, rte_iova_t iova,
 				sizeof(struct vring_packed_desc_event)), align);
 }
 
-static inline void
-vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align)
-{
-	vring_init_split(vr, p, 0, align, num);
-}
-
 /*
  * The following is used with VIRTIO_RING_F_EVENT_IDX.
  * Assuming a given event_idx value from the other size, if we have
diff --git a/drivers/crypto/virtio/virtio_user/vhost.h b/drivers/crypto/virtio/virtio_user/vhost.h
new file mode 100644
index 0000000000..29cc1a14d4
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#ifndef _VIRTIO_USER_VHOST_H
+#define _VIRTIO_USER_VHOST_H
+
+#include <stdint.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <rte_errno.h>
+
+#include "../virtio_logs.h"
+
+struct vhost_vring_state {
+	unsigned int index;
+	unsigned int num;
+};
+
+struct vhost_vring_file {
+	unsigned int index;
+	int fd;
+};
+
+struct vhost_vring_addr {
+	unsigned int index;
+	/* Option flags. */
+	unsigned int flags;
+	/* Flag values: */
+	/* Whether log address is valid. If set enables logging. */
+#define VHOST_VRING_F_LOG 0
+
+	/* Start of array of descriptors (virtually contiguous) */
+	uint64_t desc_user_addr;
+	/* Used structure address. Must be 32 bit aligned */
+	uint64_t used_user_addr;
+	/* Available structure address. Must be 16 bit aligned */
+	uint64_t avail_user_addr;
+	/* Logging support. */
+	/* Log writes to used structure, at offset calculated from specified
+	 * address. Address must be 32 bit aligned.
+	 */
+	uint64_t log_guest_addr;
+};
+
+#ifndef VHOST_BACKEND_F_IOTLB_MSG_V2
+#define VHOST_BACKEND_F_IOTLB_MSG_V2 1
+#endif
+
+#ifndef VHOST_BACKEND_F_IOTLB_BATCH
+#define VHOST_BACKEND_F_IOTLB_BATCH 2
+#endif
+
+struct virtio_user_dev;
+
+struct virtio_user_backend_ops {
+	int (*setup)(struct virtio_user_dev *dev);
+	int (*destroy)(struct virtio_user_dev *dev);
+	int (*get_backend_features)(uint64_t *features);
+	int (*set_owner)(struct virtio_user_dev *dev);
+	int (*get_features)(struct virtio_user_dev *dev, uint64_t *features);
+	int (*set_features)(struct virtio_user_dev *dev, uint64_t features);
+	int (*set_memory_table)(struct virtio_user_dev *dev);
+	int (*set_vring_num)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*get_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_call)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_kick)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_addr)(struct virtio_user_dev *dev, struct vhost_vring_addr *addr);
+	int (*get_status)(struct virtio_user_dev *dev, uint8_t *status);
+	int (*set_status)(struct virtio_user_dev *dev, uint8_t status);
+	int (*get_config)(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len);
+	int (*set_config)(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off,
+			uint32_t len);
+	int (*cvq_enable)(struct virtio_user_dev *dev, int enable);
+	int (*enable_qp)(struct virtio_user_dev *dev, uint16_t pair_idx, int enable);
+	int (*dma_map)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*dma_unmap)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*update_link_state)(struct virtio_user_dev *dev);
+	int (*server_disconnect)(struct virtio_user_dev *dev);
+	int (*server_reconnect)(struct virtio_user_dev *dev);
+	int (*get_intr_fd)(struct virtio_user_dev *dev);
+	int (*map_notification_area)(struct virtio_user_dev *dev);
+	int (*unmap_notification_area)(struct virtio_user_dev *dev);
+};
+
+extern struct virtio_user_backend_ops virtio_ops_vdpa;
+
+#endif
diff --git a/drivers/crypto/virtio/virtio_user/vhost_vdpa.c b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
new file mode 100644
index 0000000000..b5839875e6
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
@@ -0,0 +1,710 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rte_memory.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+#include "../virtio_pci.h"
+
+struct vhost_vdpa_data {
+	int vhostfd;
+	uint64_t protocol_features;
+};
+
+#define VHOST_VDPA_SUPPORTED_BACKEND_FEATURES		\
+	(1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2	|	\
+	1ULL << VHOST_BACKEND_F_IOTLB_BATCH)
+
+/* vhost kernel & vdpa ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
+#define VHOST_VDPA_GET_DEVICE_ID _IOR(VHOST_VIRTIO, 0x70, __u32)
+#define VHOST_VDPA_GET_STATUS _IOR(VHOST_VIRTIO, 0x71, __u8)
+#define VHOST_VDPA_SET_STATUS _IOW(VHOST_VIRTIO, 0x72, __u8)
+#define VHOST_VDPA_GET_CONFIG _IOR(VHOST_VIRTIO, 0x73, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_CONFIG _IOW(VHOST_VIRTIO, 0x74, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_VRING_ENABLE _IOW(VHOST_VIRTIO, 0x75, struct vhost_vring_state)
+#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64)
+#define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64)
+
+/* no alignment requirement */
+struct vhost_iotlb_msg {
+	uint64_t iova;
+	uint64_t size;
+	uint64_t uaddr;
+#define VHOST_ACCESS_RO      0x1
+#define VHOST_ACCESS_WO      0x2
+#define VHOST_ACCESS_RW      0x3
+	uint8_t perm;
+#define VHOST_IOTLB_MISS           1
+#define VHOST_IOTLB_UPDATE         2
+#define VHOST_IOTLB_INVALIDATE     3
+#define VHOST_IOTLB_ACCESS_FAIL    4
+#define VHOST_IOTLB_BATCH_BEGIN    5
+#define VHOST_IOTLB_BATCH_END      6
+	uint8_t type;
+};
+
+#define VHOST_IOTLB_MSG_V2 0x2
+
+struct vhost_vdpa_config {
+	uint32_t off;
+	uint32_t len;
+	uint8_t buf[];
+};
+
+struct vhost_msg {
+	uint32_t type;
+	uint32_t reserved;
+	union {
+		struct vhost_iotlb_msg iotlb;
+		uint8_t padding[64];
+	};
+};
+
+static int
+vhost_vdpa_ioctl(int fd, uint64_t request, void *arg)
+{
+	int ret;
+
+	ret = ioctl(fd, request, arg);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Vhost-vDPA ioctl %"PRIu64" failed (%s)",
+				request, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_owner(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_OWNER, NULL);
+}
+
+static int
+vhost_vdpa_get_protocol_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_BACKEND_FEATURES, features);
+}
+
+static int
+vhost_vdpa_set_protocol_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_BACKEND_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_get_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int ret;
+
+	ret = vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_FEATURES, features);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get features");
+		return -1;
+	}
+
+	/* Negotiated vDPA backend features */
+	ret = vhost_vdpa_get_protocol_features(dev, &data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to get backend features");
+		return -1;
+	}
+
+	data->protocol_features &= VHOST_VDPA_SUPPORTED_BACKEND_FEATURES;
+
+	ret = vhost_vdpa_set_protocol_features(dev, data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to set backend features");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	/* WORKAROUND */
+	features |= 1ULL << VIRTIO_F_IOMMU_PLATFORM;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_iotlb_batch_begin(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_BEGIN;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch begin (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_iotlb_batch_end(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_END;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch end (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_UPDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.uaddr = (uint64_t)(uintptr_t)addr;
+	msg.iotlb.size = len;
+	msg.iotlb.perm = VHOST_ACCESS_RW;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", addr: %p, len: 0x%zx",
+			__func__, iova, addr, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB update (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_unmap(struct virtio_user_dev *dev, __rte_unused void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_INVALIDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.size = len;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", len: 0x%zx",
+			__func__, iova, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB invalidate (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_map(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_dma_unmap_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_unmap(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_map_contig(const struct rte_memseg_list *msl,
+		const struct rte_memseg *ms, size_t len, void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	if (msl->external)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, len);
+}
+
+static int
+vhost_vdpa_map(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
+		void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	/* skip external memory that isn't a heap */
+	if (msl->external && !msl->heap)
+		return 0;
+
+	/* skip any segments with invalid IOVA addresses */
+	if (ms->iova == RTE_BAD_IOVA)
+		return 0;
+
+	/* if IOVA mode is VA, we've already mapped the internal segments */
+	if (!msl->external && rte_eal_iova_mode() == RTE_IOVA_VA)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, ms->len);
+}
+
+static int
+vhost_vdpa_set_memory_table(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	vhost_vdpa_dma_unmap(dev, NULL, 0, SIZE_MAX);
+
+	if (rte_eal_iova_mode() == RTE_IOVA_VA) {
+		/* with IOVA as VA mode, we can get away with mapping contiguous
+		 * chunks rather than going page-by-page.
+		 */
+		ret = rte_memseg_contig_walk_thread_unsafe(
+				vhost_vdpa_map_contig, dev);
+		if (ret)
+			goto batch_end;
+		/* we have to continue the walk because we've skipped the
+		 * external segments during the config walk.
+		 */
+	}
+	ret = rte_memseg_walk_thread_unsafe(vhost_vdpa_map, dev);
+
+batch_end:
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_vring_enable(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_VRING_ENABLE, state);
+}
+
+static int
+vhost_vdpa_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_NUM, state);
+}
+
+static int
+vhost_vdpa_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_CALL, file);
+}
+
+static int
+vhost_vdpa_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_KICK, file);
+}
+
+static int
+vhost_vdpa_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_ADDR, addr);
+}
+
+static int
+vhost_vdpa_get_status(struct virtio_user_dev *dev, uint8_t *status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_GET_STATUS, status);
+}
+
+static int
+vhost_vdpa_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_STATUS, &status);
+}
+
+static int
+vhost_vdpa_get_config(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_GET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+		goto out;
+	}
+
+	memcpy(data, config->buf, len);
+out:
+	free(config);
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_config(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	memcpy(config->buf, data, len);
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_SET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to set vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+	}
+
+	free(config);
+
+	return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost vdpa backend.
+ *
+ * @return
+ *   - (-1) if fail to set up;
+ *   - (>=0) if successful.
+ */
+static int
+vhost_vdpa_setup(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data;
+	uint32_t did = (uint32_t)-1;
+
+	data = malloc(sizeof(*data));
+	if (!data) {
+		PMD_DRV_LOG(ERR, "(%s) Faidle to allocate backend data", dev->path);
+		return -1;
+	}
+
+	data->vhostfd = open(dev->path, O_RDWR);
+	if (data->vhostfd < 0) {
+		PMD_DRV_LOG(ERR, "Failed to open %s: %s",
+				dev->path, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	if (ioctl(data->vhostfd, VHOST_VDPA_GET_DEVICE_ID, &did) < 0 ||
+			did != VIRTIO_ID_CRYPTO) {
+		PMD_DRV_LOG(ERR, "Invalid vdpa device ID: %u", did);
+		close(data->vhostfd);
+		free(data);
+		return -1;
+	}
+
+	dev->backend_data = data;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_destroy(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	if (!data)
+		return 0;
+
+	close(data->vhostfd);
+
+	free(data);
+	dev->backend_data = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_cvq_enable(struct virtio_user_dev *dev, int enable)
+{
+	struct vhost_vring_state state = {
+		.index = dev->max_queue_pairs,
+		.num   = enable,
+	};
+
+	return vhost_vdpa_set_vring_enable(dev, &state);
+}
+
+static int
+vhost_vdpa_enable_queue_pair(struct virtio_user_dev *dev,
+				uint16_t pair_idx,
+				int enable)
+{
+	struct vhost_vring_state state = {
+		.index = pair_idx,
+		.num   = enable,
+	};
+
+	if (dev->qp_enabled[pair_idx] == enable)
+		return 0;
+
+	if (vhost_vdpa_set_vring_enable(dev, &state))
+		return -1;
+
+	dev->qp_enabled[pair_idx] = enable;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_backend_features(uint64_t *features)
+{
+	*features = 0;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_update_link_state(struct virtio_user_dev *dev)
+{
+	/* TODO: It is W/A until a cleaner approach to find cpt status */
+	dev->crypto_status = VIRTIO_CRYPTO_S_HW_READY;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_intr_fd(struct virtio_user_dev *dev __rte_unused)
+{
+	/* No link state interrupt with Vhost-vDPA */
+	return -1;
+}
+
+static int
+vhost_vdpa_get_nr_vrings(struct virtio_user_dev *dev)
+{
+	int nr_vrings = dev->max_queue_pairs;
+
+	return nr_vrings;
+}
+
+static int
+vhost_vdpa_unmap_notification_area(struct virtio_user_dev *dev)
+{
+	int i, nr_vrings;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	for (i = 0; i < nr_vrings; i++) {
+		if (dev->notify_area[i])
+			munmap(dev->notify_area[i], getpagesize());
+	}
+	free(dev->notify_area);
+	dev->notify_area = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_map_notification_area(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int nr_vrings, i, page_size = getpagesize();
+	uint16_t **notify_area;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	/* CQ is another vring */
+	nr_vrings++;
+
+	notify_area = malloc(nr_vrings * sizeof(*notify_area));
+	if (!notify_area) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to allocate notify area array", dev->path);
+		return -1;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		notify_area[i] = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED | MAP_FILE,
+					data->vhostfd, i * page_size);
+		if (notify_area[i] == MAP_FAILED) {
+			PMD_DRV_LOG(ERR, "(%s) Map failed for notify address of queue %d",
+					dev->path, i);
+			i--;
+			goto map_err;
+		}
+	}
+	dev->notify_area = notify_area;
+
+	return 0;
+
+map_err:
+	for (; i >= 0; i--)
+		munmap(notify_area[i], page_size);
+	free(notify_area);
+
+	return -1;
+}
+
+struct virtio_user_backend_ops virtio_crypto_ops_vdpa = {
+	.setup = vhost_vdpa_setup,
+	.destroy = vhost_vdpa_destroy,
+	.get_backend_features = vhost_vdpa_get_backend_features,
+	.set_owner = vhost_vdpa_set_owner,
+	.get_features = vhost_vdpa_get_features,
+	.set_features = vhost_vdpa_set_features,
+	.set_memory_table = vhost_vdpa_set_memory_table,
+	.set_vring_num = vhost_vdpa_set_vring_num,
+	.set_vring_base = vhost_vdpa_set_vring_base,
+	.get_vring_base = vhost_vdpa_get_vring_base,
+	.set_vring_call = vhost_vdpa_set_vring_call,
+	.set_vring_kick = vhost_vdpa_set_vring_kick,
+	.set_vring_addr = vhost_vdpa_set_vring_addr,
+	.get_status = vhost_vdpa_get_status,
+	.set_status = vhost_vdpa_set_status,
+	.get_config = vhost_vdpa_get_config,
+	.set_config = vhost_vdpa_set_config,
+	.cvq_enable = vhost_vdpa_cvq_enable,
+	.enable_qp = vhost_vdpa_enable_queue_pair,
+	.dma_map = vhost_vdpa_dma_map_batch,
+	.dma_unmap = vhost_vdpa_dma_unmap_batch,
+	.update_link_state = vhost_vdpa_update_link_state,
+	.get_intr_fd = vhost_vdpa_get_intr_fd,
+	.map_notification_area = vhost_vdpa_map_notification_area,
+	.unmap_notification_area = vhost_vdpa_unmap_notification_area,
+};
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.c b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
new file mode 100644
index 0000000000..c8478d72ce
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
@@ -0,0 +1,749 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <rte_alarm.h>
+#include <rte_string_fns.h>
+#include <rte_eal_memconfig.h>
+#include <rte_malloc.h>
+#include <rte_io.h>
+
+#include "vhost.h"
+#include "virtio_logs.h"
+#include "cryptodev_pmd.h"
+#include "virtio_crypto.h"
+#include "virtio_cvq.h"
+#include "virtio_user_dev.h"
+#include "virtqueue.h"
+
+#define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
+
+const char * const crypto_virtio_user_backend_strings[] = {
+	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
+	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
+};
+
+static int
+virtio_user_uninit_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	if (dev->kickfds[queue_sel] >= 0) {
+		close(dev->kickfds[queue_sel]);
+		dev->kickfds[queue_sel] = -1;
+	}
+
+	if (dev->callfds[queue_sel] >= 0) {
+		close(dev->callfds[queue_sel]);
+		dev->callfds[queue_sel] = -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_init_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* May use invalid flag, but some backend uses kickfd and
+	 * callfd as criteria to judge if dev is alive. so finally we
+	 * use real event_fd.
+	 */
+	dev->callfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->callfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup callfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+	dev->kickfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->kickfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup kickfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_destroy_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	struct vhost_vring_state state;
+	int ret;
+
+	state.index = queue_sel;
+	ret = dev->ops->get_vring_base(dev, &state);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to destroy queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
+	 * firstly because vhost depends on this msg to allocate virtqueue
+	 * pair.
+	 */
+	struct vhost_vring_file file;
+	int ret;
+
+	file.index = queue_sel;
+	file.fd = dev->callfds[queue_sel];
+	ret = dev->ops->set_vring_call(dev, &file);
+	if (ret < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	int ret;
+	struct vhost_vring_file file;
+	struct vhost_vring_state state;
+	struct vring *vring = &dev->vrings.split[queue_sel];
+	struct vring_packed *pq_vring = &dev->vrings.packed[queue_sel];
+	uint64_t desc_addr, avail_addr, used_addr;
+	struct vhost_vring_addr addr = {
+		.index = queue_sel,
+		.log_guest_addr = 0,
+		.flags = 0, /* disable log */
+	};
+
+	if (queue_sel == dev->max_queue_pairs) {
+		if (!dev->scvq) {
+			PMD_INIT_LOG(ERR, "(%s) Shadow control queue expected but missing",
+					dev->path);
+			goto err;
+		}
+
+		/* Use shadow control queue information */
+		vring = &dev->scvq->vq_split.ring;
+		pq_vring = &dev->scvq->vq_packed.ring;
+	}
+
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
+		desc_addr = pq_vring->desc_iova;
+		avail_addr = desc_addr + pq_vring->num * sizeof(struct vring_packed_desc);
+		used_addr =  RTE_ALIGN_CEIL(avail_addr + sizeof(struct vring_packed_desc_event),
+						VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	} else {
+		desc_addr = vring->desc_iova;
+		avail_addr = desc_addr + vring->num * sizeof(struct vring_desc);
+		used_addr = RTE_ALIGN_CEIL((uintptr_t)(&vring->avail->ring[vring->num]),
+					VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	}
+
+	state.index = queue_sel;
+	state.num = vring->num;
+	ret = dev->ops->set_vring_num(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	state.index = queue_sel;
+	state.num = 0; /* no reservation */
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
+		state.num |= (1 << 15);
+	ret = dev->ops->set_vring_base(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	ret = dev->ops->set_vring_addr(dev, &addr);
+	if (ret < 0)
+		goto err;
+
+	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
+	 * lastly because vhost depends on this msg to judge if
+	 * virtio is ready.
+	 */
+	file.index = queue_sel;
+	file.fd = dev->kickfds[queue_sel];
+	ret = dev->ops->set_vring_kick(dev, &file);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+err:
+	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel);
+
+	return -1;
+}
+
+static int
+virtio_user_foreach_queue(struct virtio_user_dev *dev,
+			int (*fn)(struct virtio_user_dev *, uint32_t))
+{
+	uint32_t i, nr_vq;
+
+	nr_vq = dev->max_queue_pairs;
+
+	for (i = 0; i < nr_vq; i++)
+		if (fn(dev, i) < 0)
+			return -1;
+
+	return 0;
+}
+
+int
+crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev)
+{
+	uint64_t features;
+	int ret = -1;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 0: tell vhost to create queues */
+	if (virtio_user_foreach_queue(dev, virtio_user_create_queue) < 0)
+		goto error;
+
+	features = dev->features;
+
+	ret = dev->ops->set_features(dev, features);
+	if (ret < 0)
+		goto error;
+	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
+error:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return ret;
+}
+
+int
+crypto_virtio_user_start_device(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	/*
+	 * XXX workaround!
+	 *
+	 * We need to make sure that the locks will be
+	 * taken in the correct order to avoid deadlocks.
+	 *
+	 * Before releasing this lock, this thread should
+	 * not trigger any memory hotplug events.
+	 *
+	 * This is a temporary workaround, and should be
+	 * replaced when we get proper supports from the
+	 * memory subsystem in the future.
+	 */
+	rte_mcfg_mem_read_lock();
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 2: share memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Step 3: kick queues */
+	ret = virtio_user_foreach_queue(dev, virtio_user_kick_queue);
+	if (ret < 0)
+		goto error;
+
+	ret = virtio_user_kick_queue(dev, dev->max_queue_pairs);
+	if (ret < 0)
+		goto error;
+
+	/* Step 4: enable queues */
+	for (int i = 0; i < dev->max_queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto error;
+	}
+
+	dev->started = true;
+
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	return 0;
+error:
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path);
+
+	/* TODO: free resource here or caller to check */
+	return -1;
+}
+
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev)
+{
+	uint32_t i;
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	if (!dev->started)
+		goto out;
+
+	for (i = 0; i < dev->max_queue_pairs; ++i) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	if (dev->scvq) {
+		ret = dev->ops->cvq_enable(dev, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	/* Stop the backend. */
+	if (virtio_user_foreach_queue(dev, virtio_user_destroy_queue) < 0)
+		goto err;
+
+	dev->started = false;
+
+out:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return 0;
+err:
+	pthread_mutex_unlock(&dev->mutex);
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path);
+
+	return -1;
+}
+
+static int
+virtio_user_dev_init_max_queue_pairs(struct virtio_user_dev *dev, uint32_t user_max_qp)
+{
+	int ret;
+
+	if (!dev->ops->get_config) {
+		dev->max_queue_pairs = user_max_qp;
+		return 0;
+	}
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&dev->max_queue_pairs,
+			offsetof(struct virtio_crypto_config, max_dataqueues),
+			sizeof(uint16_t));
+	if (ret) {
+		/*
+		 * We need to know the max queue pair from the device so that
+		 * the control queue gets the right index.
+		 */
+		dev->max_queue_pairs = 1;
+		PMD_DRV_LOG(ERR, "(%s) Failed to get max queue pairs from device", dev->path);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_dev_init_cipher_services(struct virtio_user_dev *dev)
+{
+	struct virtio_crypto_config config;
+	int ret;
+
+	dev->crypto_services = RTE_BIT32(VIRTIO_CRYPTO_SERVICE_CIPHER);
+	dev->cipher_algo = 0;
+	dev->auth_algo = 0;
+	dev->akcipher_algo = 0;
+
+	if (!dev->ops->get_config)
+		return 0;
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&config,	0, sizeof(config));
+	if (ret) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to get crypto config from device", dev->path);
+		return ret;
+	}
+
+	dev->crypto_services = config.crypto_services;
+	dev->cipher_algo = ((uint64_t)config.cipher_algo_h << 32) |
+						config.cipher_algo_l;
+	dev->hash_algo = config.hash_algo;
+	dev->auth_algo = ((uint64_t)config.mac_algo_h << 32) |
+						config.mac_algo_l;
+	dev->aead_algo = config.aead_algo;
+	dev->akcipher_algo = config.akcipher_algo;
+	return 0;
+}
+
+static int
+virtio_user_dev_init_notify(struct virtio_user_dev *dev)
+{
+
+	if (virtio_user_foreach_queue(dev, virtio_user_init_notify_queue) < 0)
+		goto err;
+
+	if (dev->device_features & (1ULL << VIRTIO_F_NOTIFICATION_DATA))
+		if (dev->ops->map_notification_area &&
+				dev->ops->map_notification_area(dev))
+			goto err;
+
+	return 0;
+err:
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	return -1;
+}
+
+static void
+virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
+{
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	if (dev->ops->unmap_notification_area && dev->notify_area)
+		dev->ops->unmap_notification_area(dev);
+}
+
+static void
+virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
+			const void *addr,
+			size_t len __rte_unused,
+			void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+	struct rte_memseg_list *msl;
+	uint16_t i;
+	int ret = 0;
+
+	/* ignore externally allocated memory */
+	msl = rte_mem_virt2memseg_list(addr);
+	if (msl->external)
+		return;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	if (dev->started == false)
+		goto exit;
+
+	/* Step 1: pause the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto exit;
+	}
+
+	/* Step 2: update memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto exit;
+
+	/* Step 3: resume the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto exit;
+	}
+
+exit:
+	pthread_mutex_unlock(&dev->mutex);
+
+	if (ret < 0)
+		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+	if (dev->is_server) {
+		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
+			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
+			return -1;
+		}
+	}
+
+	switch (dev->backend_type) {
+	case VIRTIO_USER_BACKEND_VHOST_VDPA:
+		dev->ops = &virtio_crypto_ops_vdpa;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_alloc_vrings(struct virtio_user_dev *dev)
+{
+	int i, size, nr_vrings;
+	bool packed_ring = !!(dev->device_features & (1ull << VIRTIO_F_RING_PACKED));
+
+	nr_vrings = dev->max_queue_pairs + 1;
+
+	dev->callfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->callfds), 0);
+	if (!dev->callfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc callfds", dev->path);
+		return -1;
+	}
+
+	dev->kickfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->kickfds), 0);
+	if (!dev->kickfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc kickfds", dev->path);
+		goto free_callfds;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		dev->callfds[i] = -1;
+		dev->kickfds[i] = -1;
+	}
+
+	if (packed_ring)
+		size = sizeof(*dev->vrings.packed);
+	else
+		size = sizeof(*dev->vrings.split);
+	dev->vrings.ptr = rte_zmalloc("virtio_user_dev", nr_vrings * size, 0);
+	if (!dev->vrings.ptr) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc vrings metadata", dev->path);
+		goto free_kickfds;
+	}
+
+	if (packed_ring) {
+		dev->packed_queues = rte_zmalloc("virtio_user_dev",
+				nr_vrings * sizeof(*dev->packed_queues), 0);
+		if (!dev->packed_queues) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to alloc packed queues metadata",
+					dev->path);
+			goto free_vrings;
+		}
+	}
+
+	dev->qp_enabled = rte_zmalloc("virtio_user_dev",
+			nr_vrings * sizeof(*dev->qp_enabled), 0);
+	if (!dev->qp_enabled) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc QP enable states", dev->path);
+		goto free_packed_queues;
+	}
+
+	return 0;
+
+free_packed_queues:
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+free_vrings:
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+free_kickfds:
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+free_callfds:
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+
+	return -1;
+}
+
+static void
+virtio_user_free_vrings(struct virtio_user_dev *dev)
+{
+	rte_free(dev->qp_enabled);
+	dev->qp_enabled = NULL;
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+}
+
+#define VIRTIO_USER_SUPPORTED_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_HASH       | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+int
+crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server)
+{
+	uint64_t backend_features;
+
+	pthread_mutex_init(&dev->mutex, NULL);
+	strlcpy(dev->path, path, PATH_MAX);
+
+	dev->started = 0;
+	dev->queue_pairs = 1; /* mq disabled by default */
+	dev->max_queue_pairs = queues; /* initialize to user requested value for kernel backend */
+	dev->queue_size = queue_size;
+	dev->is_server = server;
+	dev->frontend_features = 0;
+	dev->unsupported_features = 0;
+	dev->backend_type = VIRTIO_USER_BACKEND_VHOST_VDPA;
+	dev->hw.modern = 1;
+
+	if (virtio_user_dev_setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->set_owner(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
+		goto destroy;
+	}
+
+	if (dev->ops->get_backend_features(&backend_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
+		goto destroy;
+	}
+
+	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
+
+	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_max_queue_pairs(dev, queues)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get max queue pairs", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_cipher_services(dev)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get cipher services", dev->path);
+		goto destroy;
+	}
+
+	dev->frontend_features &= ~dev->unsupported_features;
+	dev->device_features &= ~dev->unsupported_features;
+
+	if (virtio_user_alloc_vrings(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to allocate vring metadata", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_notify(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path);
+		goto free_vrings;
+	}
+
+	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
+				virtio_user_mem_event_cb, dev)) {
+		if (rte_errno != ENOTSUP) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback",
+					dev->path);
+			goto notify_uninit;
+		}
+	}
+
+	return 0;
+
+notify_uninit:
+	virtio_user_dev_uninit_notify(dev);
+free_vrings:
+	virtio_user_free_vrings(dev);
+destroy:
+	dev->ops->destroy(dev);
+
+	return -1;
+}
+
+void
+crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev)
+{
+	crypto_virtio_user_stop_device(dev);
+
+	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
+
+	virtio_user_dev_uninit_notify(dev);
+
+	virtio_user_free_vrings(dev);
+
+	if (dev->is_server)
+		unlink(dev->path);
+
+	dev->ops->destroy(dev);
+}
+
+#define CVQ_MAX_DATA_DESCS 32
+
+int
+crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	dev->status = status;
+	ret = dev->ops->set_status(dev, status);
+	if (ret && ret != -ENOTSUP)
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path);
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev)
+{
+	int ret;
+	uint8_t status;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	ret = dev->ops->get_status(dev, &status);
+	if (!ret) {
+		dev->status = status;
+		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):"
+			"\t-RESET: %u "
+			"\t-ACKNOWLEDGE: %u "
+			"\t-DRIVER: %u "
+			"\t-DRIVER_OK: %u "
+			"\t-FEATURES_OK: %u "
+			"\t-DEVICE_NEED_RESET: %u "
+			"\t-FAILED: %u",
+			dev->status,
+			(dev->status == VIRTIO_CONFIG_STATUS_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
+	} else if (ret != -ENOTSUP) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path);
+	}
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
+{
+	if (dev->ops->update_link_state)
+		return dev->ops->update_link_state(dev);
+
+	return 0;
+}
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.h b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
new file mode 100644
index 0000000000..9cd9856e5d
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#ifndef _VIRTIO_USER_DEV_H
+#define _VIRTIO_USER_DEV_H
+
+#include <limits.h>
+#include <stdbool.h>
+
+#include "../virtio_pci.h"
+#include "../virtio_ring.h"
+
+extern struct virtio_user_backend_ops virtio_crypto_ops_vdpa;
+
+enum virtio_user_backend_type {
+	VIRTIO_USER_BACKEND_UNKNOWN,
+	VIRTIO_USER_BACKEND_VHOST_USER,
+	VIRTIO_USER_BACKEND_VHOST_VDPA,
+};
+
+struct virtio_user_queue {
+	uint16_t used_idx;
+	bool avail_wrap_counter;
+	bool used_wrap_counter;
+};
+
+struct virtio_user_dev {
+	struct virtio_crypto_hw hw;
+	enum virtio_user_backend_type backend_type;
+	bool		is_server;  /* server or client mode */
+
+	int		*callfds;
+	int		*kickfds;
+	uint16_t	max_queue_pairs;
+	uint16_t	queue_pairs;
+	uint32_t	queue_size;
+	uint64_t	features; /* the negotiated features with driver,
+				   * and will be sync with device
+				   */
+	uint64_t	device_features; /* supported features by device */
+	uint64_t	frontend_features; /* enabled frontend features */
+	uint64_t	unsupported_features; /* unsupported features mask */
+	uint8_t		status;
+	uint32_t	crypto_status;
+	uint32_t	crypto_services;
+	uint64_t	cipher_algo;
+	uint32_t	hash_algo;
+	uint64_t	auth_algo;
+	uint32_t	aead_algo;
+	uint32_t	akcipher_algo;
+	char		path[PATH_MAX];
+
+	union {
+		void			*ptr;
+		struct vring		*split;
+		struct vring_packed	*packed;
+	} vrings;
+
+	struct virtio_user_queue *packed_queues;
+	bool		*qp_enabled;
+
+	struct virtio_user_backend_ops *ops;
+	pthread_mutex_t	mutex;
+	bool		started;
+
+	bool			hw_cvq;
+	struct virtqueue	*scvq;
+
+	void *backend_data;
+
+	uint16_t **notify_area;
+};
+
+int crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev);
+int crypto_virtio_user_start_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server);
+void crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status);
+int crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev);
+extern const char * const crypto_virtio_user_backend_strings[];
+#endif
diff --git a/drivers/crypto/virtio/virtio_user_cryptodev.c b/drivers/crypto/virtio/virtio_user_cryptodev.c
new file mode 100644
index 0000000000..992e8fb43b
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user_cryptodev.c
@@ -0,0 +1,575 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <rte_malloc.h>
+#include <rte_kvargs.h>
+#include <bus_vdev_driver.h>
+#include <rte_cryptodev.h>
+#include <cryptodev_pmd.h>
+#include <rte_alarm.h>
+#include <rte_cycles.h>
+#include <rte_io.h>
+
+#include "virtio_user/virtio_user_dev.h"
+#include "virtio_user/vhost.h"
+#include "virtio_cryptodev.h"
+#include "virtio_logs.h"
+#include "virtio_pci.h"
+#include "virtqueue.h"
+
+#define virtio_user_get_dev(hwp) container_of(hwp, struct virtio_user_dev, hw)
+
+static void
+virtio_user_read_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		     void *dst, int length __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (offset == offsetof(struct virtio_crypto_config, status)) {
+		crypto_virtio_user_dev_update_link_state(dev);
+		*(uint32_t *)dst = dev->crypto_status;
+	} else if (offset == offsetof(struct virtio_crypto_config, max_dataqueues))
+		*(uint16_t *)dst = dev->max_queue_pairs;
+	else if (offset == offsetof(struct virtio_crypto_config, crypto_services))
+		*(uint32_t *)dst = dev->crypto_services;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_l))
+		*(uint32_t *)dst = dev->cipher_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_h))
+		*(uint32_t *)dst = dev->cipher_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, hash_algo))
+		*(uint32_t *)dst = dev->hash_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_l))
+		*(uint32_t *)dst = dev->auth_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_h))
+		*(uint32_t *)dst = dev->auth_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, aead_algo))
+		*(uint32_t *)dst = dev->aead_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, akcipher_algo))
+		*(uint32_t *)dst = dev->akcipher_algo;
+}
+
+static void
+virtio_user_write_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		      const void *src, int length)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(src);
+
+	PMD_DRV_LOG(ERR, "not supported offset=%zu, len=%d",
+		    offset, length);
+}
+
+static void
+virtio_user_reset(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+		crypto_virtio_user_stop_device(dev);
+}
+
+static void
+virtio_user_set_status(struct virtio_crypto_hw *hw, uint8_t status)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint8_t old_status = dev->status;
+
+	if (status & VIRTIO_CONFIG_STATUS_FEATURES_OK &&
+			~old_status & VIRTIO_CONFIG_STATUS_FEATURES_OK) {
+		crypto_virtio_user_dev_set_features(dev);
+		/* Feature negotiation should be only done in probe time.
+		 * So we skip any more request here.
+		 */
+		dev->status |= VIRTIO_CONFIG_STATUS_FEATURES_OK;
+	}
+
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) {
+		if (crypto_virtio_user_start_device(dev)) {
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	} else if (status == VIRTIO_CONFIG_STATUS_RESET) {
+		virtio_user_reset(hw);
+	}
+
+	crypto_virtio_user_dev_set_status(dev, status);
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK && dev->scvq) {
+		if (dev->ops->cvq_enable(dev, 1) < 0) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to start ctrlq", dev->path);
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	}
+}
+
+static uint8_t
+virtio_user_get_status(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	crypto_virtio_user_dev_update_status(dev);
+
+	return dev->status;
+}
+
+#define VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_RING_F_INDIRECT_DESC      | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+static uint64_t
+virtio_user_get_features(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* unmask feature bits defined in vhost user protocol */
+	return (dev->device_features | dev->frontend_features) &
+		VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES;
+}
+
+static void
+virtio_user_set_features(struct virtio_crypto_hw *hw, uint64_t features)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	dev->features = features & (dev->device_features | dev->frontend_features);
+}
+
+static uint8_t
+virtio_user_get_isr(struct virtio_crypto_hw *hw __rte_unused)
+{
+	/* rxq interrupts and config interrupt are separated in virtio-user,
+	 * here we only report config change.
+	 */
+	return VIRTIO_PCI_CAP_ISR_CFG;
+}
+
+static uint16_t
+virtio_user_set_config_irq(struct virtio_crypto_hw *hw __rte_unused,
+		    uint16_t vec __rte_unused)
+{
+	return 0;
+}
+
+static uint16_t
+virtio_user_set_queue_irq(struct virtio_crypto_hw *hw __rte_unused,
+			  struct virtqueue *vq __rte_unused,
+			  uint16_t vec)
+{
+	/* pretend we have done that */
+	return vec;
+}
+
+/* This function is to get the queue size, aka, number of descs, of a specified
+ * queue. Different with the VHOST_USER_GET_QUEUE_NUM, which is used to get the
+ * max supported queues.
+ */
+static uint16_t
+virtio_user_get_queue_num(struct virtio_crypto_hw *hw, uint16_t queue_id __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* Currently, each queue has same queue size */
+	return dev->queue_size;
+}
+
+static void
+virtio_user_setup_queue_packed(struct virtqueue *vq,
+			       struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	struct vring_packed *vring;
+	uint64_t desc_addr;
+	uint64_t avail_addr;
+	uint64_t used_addr;
+	uint16_t i;
+
+	vring  = &dev->vrings.packed[queue_idx];
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries *
+		sizeof(struct vring_packed_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr +
+			   sizeof(struct vring_packed_desc_event),
+			   VIRTIO_VRING_ALIGN);
+	vring->num = vq->vq_nentries;
+	vring->desc_iova = vq->vq_ring_mem;
+	vring->desc = (void *)(uintptr_t)desc_addr;
+	vring->driver = (void *)(uintptr_t)avail_addr;
+	vring->device = (void *)(uintptr_t)used_addr;
+	dev->packed_queues[queue_idx].avail_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_idx = 0;
+
+	for (i = 0; i < vring->num; i++)
+		vring->desc[i].flags = 0;
+}
+
+static void
+virtio_user_setup_queue_split(struct virtqueue *vq, struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	uint64_t desc_addr, avail_addr, used_addr;
+
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
+							 ring[vq->vq_nentries]),
+				   VIRTIO_VRING_ALIGN);
+
+	dev->vrings.split[queue_idx].num = vq->vq_nentries;
+	dev->vrings.split[queue_idx].desc_iova = vq->vq_ring_mem;
+	dev->vrings.split[queue_idx].desc = (void *)(uintptr_t)desc_addr;
+	dev->vrings.split[queue_idx].avail = (void *)(uintptr_t)avail_addr;
+	dev->vrings.split[queue_idx].used = (void *)(uintptr_t)used_addr;
+}
+
+static int
+virtio_user_setup_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (vtpci_with_packed_queue(hw))
+		virtio_user_setup_queue_packed(vq, dev);
+	else
+		virtio_user_setup_queue_split(vq, dev);
+
+	if (dev->notify_area)
+		vq->notify_addr = dev->notify_area[vq->vq_queue_index];
+
+	if (virtcrypto_cq_to_vq(hw->cvq) == vq)
+		dev->scvq = virtcrypto_cq_to_vq(hw->cvq);
+
+	return 0;
+}
+
+static void
+virtio_user_del_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(vq);
+}
+
+static void
+virtio_user_notify_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint64_t notify_data = 1;
+
+	if (!dev->notify_area) {
+		if (write(dev->kickfds[vq->vq_queue_index], &notify_data,
+			  sizeof(notify_data)) < 0)
+			PMD_DRV_LOG(ERR, "failed to kick backend: %s",
+				    strerror(errno));
+		return;
+	} else if (!vtpci_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) {
+		rte_write16(vq->vq_queue_index, vq->notify_addr);
+		return;
+	}
+
+	if (vtpci_with_packed_queue(hw)) {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:30]: avail index
+		 * Bit[31]: avail wrap counter
+		 */
+		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
+				VRING_PACKED_DESC_F_AVAIL)) << 31) |
+				((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	} else {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:31]: avail index
+		 */
+		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	}
+	rte_write32(notify_data, vq->notify_addr);
+}
+
+const struct virtio_pci_ops crypto_virtio_user_ops = {
+	.read_dev_cfg	= virtio_user_read_dev_config,
+	.write_dev_cfg	= virtio_user_write_dev_config,
+	.reset		= virtio_user_reset,
+	.get_status	= virtio_user_get_status,
+	.set_status	= virtio_user_set_status,
+	.get_features	= virtio_user_get_features,
+	.set_features	= virtio_user_set_features,
+	.get_isr	= virtio_user_get_isr,
+	.set_config_irq	= virtio_user_set_config_irq,
+	.set_queue_irq	= virtio_user_set_queue_irq,
+	.get_queue_num	= virtio_user_get_queue_num,
+	.setup_queue	= virtio_user_setup_queue,
+	.del_queue	= virtio_user_del_queue,
+	.notify_queue	= virtio_user_notify_queue,
+};
+
+static const char * const valid_args[] = {
+#define VIRTIO_USER_ARG_QUEUES_NUM     "queues"
+	VIRTIO_USER_ARG_QUEUES_NUM,
+#define VIRTIO_USER_ARG_QUEUE_SIZE     "queue_size"
+	VIRTIO_USER_ARG_QUEUE_SIZE,
+#define VIRTIO_USER_ARG_PATH           "path"
+	VIRTIO_USER_ARG_PATH,
+	NULL
+};
+
+#define VIRTIO_USER_DEF_Q_NUM	1
+#define VIRTIO_USER_DEF_Q_SZ	256
+#define VIRTIO_USER_DEF_SERVER_MODE	0
+
+static int
+get_string_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	if (!value || !extra_args)
+		return -EINVAL;
+
+	*(char **)extra_args = strdup(value);
+
+	if (!*(char **)extra_args)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int
+get_integer_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	uint64_t integer = 0;
+	if (!value || !extra_args)
+		return -EINVAL;
+	errno = 0;
+	integer = strtoull(value, NULL, 0);
+	/* extra_args keeps default value, it should be replaced
+	 * only in case of successful parsing of the 'value' arg
+	 */
+	if (errno == 0)
+		*(uint64_t *)extra_args = integer;
+	return -errno;
+}
+
+static struct rte_cryptodev *
+virtio_user_cryptodev_alloc(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev_pmd_init_params init_params = {
+		.name = "",
+		.private_data_size = sizeof(struct virtio_user_dev),
+	};
+	struct rte_cryptodev_data *data;
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	struct virtio_crypto_hw *hw;
+
+	init_params.socket_id = vdev->device.numa_node;
+	init_params.private_data_size = sizeof(struct virtio_user_dev);
+	cryptodev = rte_cryptodev_pmd_create(vdev->device.name, &vdev->device, &init_params);
+	if (cryptodev == NULL) {
+		PMD_INIT_LOG(ERR, "failed to create cryptodev vdev");
+		return NULL;
+	}
+
+	data = cryptodev->data;
+	dev = data->dev_private;
+	hw = &dev->hw;
+
+	hw->dev_id = data->dev_id;
+	VTPCI_OPS(hw) = &crypto_virtio_user_ops;
+
+	return cryptodev;
+}
+
+static void
+virtio_user_cryptodev_free(struct rte_cryptodev *cryptodev)
+{
+	rte_cryptodev_pmd_destroy(cryptodev);
+}
+
+static int
+virtio_user_pmd_probe(struct rte_vdev_device *vdev)
+{
+	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
+	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
+	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
+	struct rte_cryptodev *cryptodev = NULL;
+	struct rte_kvargs *kvlist = NULL;
+	struct virtio_user_dev *dev;
+	char *path = NULL;
+	int ret = -1;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(vdev), valid_args);
+
+	if (!kvlist) {
+		PMD_INIT_LOG(ERR, "error when parsing param");
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PATH) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PATH,
+					&get_string_arg, &path) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_PATH);
+			goto end;
+		}
+	} else {
+		PMD_INIT_LOG(ERR, "arg %s is mandatory for virtio_user",
+				VIRTIO_USER_ARG_PATH);
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUES_NUM) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUES_NUM,
+					&get_integer_arg, &queues) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUES_NUM);
+			goto end;
+		}
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE,
+					&get_integer_arg, &queue_size) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUE_SIZE);
+			goto end;
+		}
+	}
+
+	cryptodev = virtio_user_cryptodev_alloc(vdev);
+	if (!cryptodev) {
+		PMD_INIT_LOG(ERR, "virtio_user fails to alloc device");
+		goto end;
+	}
+
+	dev = cryptodev->data->dev_private;
+	if (crypto_virtio_user_dev_init(dev, path, queues, queue_size,
+			server_mode) < 0) {
+		PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES,
+			NULL) < 0) {
+		PMD_INIT_LOG(ERR, "crypto_virtio_dev_init fails");
+		crypto_virtio_user_dev_uninit(dev);
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	rte_cryptodev_pmd_probing_finish(cryptodev);
+
+	ret = 0;
+end:
+	rte_kvargs_free(kvlist);
+	free(path);
+	return ret;
+}
+
+static int
+virtio_user_pmd_remove(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev *cryptodev;
+	const char *name;
+	int devid;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	PMD_DRV_LOG(INFO, "Removing %s", name);
+
+	devid = rte_cryptodev_get_dev_id(name);
+	if (devid < 0)
+		return -EINVAL;
+
+	rte_cryptodev_stop(devid);
+
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (rte_cryptodev_pmd_destroy(cryptodev) < 0) {
+		PMD_DRV_LOG(ERR, "Failed to remove %s", name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_map(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_map)
+		return dev->ops->dma_map(dev, addr, iova, len);
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_unmap(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_unmap)
+		return dev->ops->dma_unmap(dev, addr, iova, len);
+
+	return 0;
+}
+
+static struct rte_vdev_driver virtio_user_driver = {
+	.probe = virtio_user_pmd_probe,
+	.remove = virtio_user_pmd_remove,
+	.dma_map = virtio_user_pmd_dma_map,
+	.dma_unmap = virtio_user_pmd_dma_unmap,
+};
+
+static struct cryptodev_driver virtio_crypto_drv;
+
+uint8_t cryptodev_virtio_user_driver_id;
+
+RTE_PMD_REGISTER_VDEV(crypto_virtio_user, virtio_user_driver);
+RTE_PMD_REGISTER_CRYPTO_DRIVER(virtio_crypto_drv,
+	virtio_user_driver.driver,
+	cryptodev_virtio_user_driver_id);
+RTE_PMD_REGISTER_PARAM_STRING(crypto_virtio_user,
+	"path=<path> "
+	"queues=<int> "
+	"queue_size=<int>");
-- 
2.25.1


^ permalink raw reply	[relevance 1%]

* [v5 4/6] crypto/virtio: add vDPA backend
  @ 2025-02-26 18:58  1% ` Gowrishankar Muthukrishnan
  0 siblings, 0 replies; 153+ results
From: Gowrishankar Muthukrishnan @ 2025-02-26 18:58 UTC (permalink / raw)
  To: dev, Jay Zhou; +Cc: anoobj, Akhil Goyal, Gowrishankar Muthukrishnan

Add vDPA backend to virtio_user crypto.

Signed-off-by: Gowrishankar Muthukrishnan <gmuthukrishn@marvell.com>
---
 drivers/crypto/virtio/meson.build             |   7 +
 drivers/crypto/virtio/virtio_cryptodev.c      |  57 +-
 drivers/crypto/virtio/virtio_cryptodev.h      |   3 +
 drivers/crypto/virtio/virtio_logs.h           |   6 +-
 drivers/crypto/virtio/virtio_pci.h            |   7 +
 drivers/crypto/virtio/virtio_ring.h           |   6 -
 drivers/crypto/virtio/virtio_user/vhost.h     |  90 +++
 .../crypto/virtio/virtio_user/vhost_vdpa.c    | 710 +++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.c      | 749 ++++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.h      |  85 ++
 drivers/crypto/virtio/virtio_user_cryptodev.c | 575 ++++++++++++++
 11 files changed, 2265 insertions(+), 30 deletions(-)
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost.h
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost_vdpa.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.h
 create mode 100644 drivers/crypto/virtio/virtio_user_cryptodev.c

diff --git a/drivers/crypto/virtio/meson.build b/drivers/crypto/virtio/meson.build
index d2c3b3ad07..3763e86746 100644
--- a/drivers/crypto/virtio/meson.build
+++ b/drivers/crypto/virtio/meson.build
@@ -16,3 +16,10 @@ sources = files(
         'virtio_rxtx.c',
         'virtqueue.c',
 )
+
+if is_linux
+    sources += files('virtio_user_cryptodev.c',
+        'virtio_user/vhost_vdpa.c',
+        'virtio_user/virtio_user_dev.c')
+    deps += ['bus_vdev']
+endif
diff --git a/drivers/crypto/virtio/virtio_cryptodev.c b/drivers/crypto/virtio/virtio_cryptodev.c
index 92fea557ab..bc737f1e68 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.c
+++ b/drivers/crypto/virtio/virtio_cryptodev.c
@@ -544,24 +544,12 @@ virtio_crypto_init_device(struct rte_cryptodev *cryptodev,
 	return 0;
 }
 
-/*
- * This function is based on probe() function
- * It returns 0 on success.
- */
-static int
-crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
-		struct rte_cryptodev_pmd_init_params *init_params)
+int
+crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev)
 {
-	struct rte_cryptodev *cryptodev;
 	struct virtio_crypto_hw *hw;
 
-	PMD_INIT_FUNC_TRACE();
-
-	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
-					init_params);
-	if (cryptodev == NULL)
-		return -ENODEV;
-
 	cryptodev->driver_id = cryptodev_virtio_driver_id;
 	cryptodev->dev_ops = &virtio_crypto_dev_ops;
 
@@ -578,16 +566,41 @@ crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
 	hw->dev_id = cryptodev->data->dev_id;
 	hw->virtio_dev_capabilities = virtio_capabilities;
 
-	VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
-		cryptodev->data->dev_id, pci_dev->id.vendor_id,
-		pci_dev->id.device_id);
+	if (pci_dev) {
+		/* pci device init */
+		VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
+			cryptodev->data->dev_id, pci_dev->id.vendor_id,
+			pci_dev->id.device_id);
 
-	/* pci device init */
-	if (vtpci_cryptodev_init(pci_dev, hw))
+		if (vtpci_cryptodev_init(pci_dev, hw))
+			return -1;
+	}
+
+	if (virtio_crypto_init_device(cryptodev, features) < 0)
 		return -1;
 
-	if (virtio_crypto_init_device(cryptodev,
-			VIRTIO_CRYPTO_PMD_GUEST_FEATURES) < 0)
+	return 0;
+}
+
+/*
+ * This function is based on probe() function
+ * It returns 0 on success.
+ */
+static int
+crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
+		struct rte_cryptodev_pmd_init_params *init_params)
+{
+	struct rte_cryptodev *cryptodev;
+
+	PMD_INIT_FUNC_TRACE();
+
+	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
+					init_params);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_CRYPTO_PMD_GUEST_FEATURES,
+			pci_dev) < 0)
 		return -1;
 
 	rte_cryptodev_pmd_probing_finish(cryptodev);
diff --git a/drivers/crypto/virtio/virtio_cryptodev.h b/drivers/crypto/virtio/virtio_cryptodev.h
index f8498246e2..fad73d54a8 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.h
+++ b/drivers/crypto/virtio/virtio_cryptodev.h
@@ -76,4 +76,7 @@ uint16_t virtio_crypto_pkt_rx_burst(void *tx_queue,
 		struct rte_crypto_op **tx_pkts,
 		uint16_t nb_pkts);
 
+int crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev);
+
 #endif /* _VIRTIO_CRYPTODEV_H_ */
diff --git a/drivers/crypto/virtio/virtio_logs.h b/drivers/crypto/virtio/virtio_logs.h
index 988514919f..1cc51f7990 100644
--- a/drivers/crypto/virtio/virtio_logs.h
+++ b/drivers/crypto/virtio/virtio_logs.h
@@ -15,8 +15,10 @@ extern int virtio_crypto_logtype_init;
 
 #define PMD_INIT_FUNC_TRACE() PMD_INIT_LOG(DEBUG, " >>")
 
-extern int virtio_crypto_logtype_init;
-#define RTE_LOGTYPE_VIRTIO_CRYPTO_INIT virtio_crypto_logtype_init
+extern int virtio_crypto_logtype_driver;
+#define RTE_LOGTYPE_VIRTIO_CRYPTO_DRIVER virtio_crypto_logtype_driver
+#define PMD_DRV_LOG(level, ...) \
+	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_DRIVER, "%s(): ", __func__, __VA_ARGS__)
 
 #define VIRTIO_CRYPTO_INIT_LOG_IMPL(level, ...) \
 	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_INIT, "%s(): ", __func__, __VA_ARGS__)
diff --git a/drivers/crypto/virtio/virtio_pci.h b/drivers/crypto/virtio/virtio_pci.h
index 79945cb88e..c75777e005 100644
--- a/drivers/crypto/virtio/virtio_pci.h
+++ b/drivers/crypto/virtio/virtio_pci.h
@@ -20,6 +20,9 @@ struct virtqueue;
 #define VIRTIO_CRYPTO_PCI_VENDORID 0x1AF4
 #define VIRTIO_CRYPTO_PCI_DEVICEID 0x1054
 
+/* VirtIO device IDs. */
+#define VIRTIO_ID_CRYPTO  20
+
 /* VirtIO ABI version, this must match exactly. */
 #define VIRTIO_PCI_ABI_VERSION 0
 
@@ -56,8 +59,12 @@ struct virtqueue;
 #define VIRTIO_CONFIG_STATUS_DRIVER    0x02
 #define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
 #define VIRTIO_CONFIG_STATUS_FEATURES_OK 0x08
+#define VIRTIO_CONFIG_STATUS_DEV_NEED_RESET	0x40
 #define VIRTIO_CONFIG_STATUS_FAILED    0x80
 
+/* The alignment to use between consumer and producer parts of vring. */
+#define VIRTIO_VRING_ALIGN 4096
+
 /*
  * Each virtqueue indirect descriptor list must be physically contiguous.
  * To allow us to malloc(9) each list individually, limit the number
diff --git a/drivers/crypto/virtio/virtio_ring.h b/drivers/crypto/virtio/virtio_ring.h
index c74d1172b7..4b418f6e60 100644
--- a/drivers/crypto/virtio/virtio_ring.h
+++ b/drivers/crypto/virtio/virtio_ring.h
@@ -181,12 +181,6 @@ vring_init_packed(struct vring_packed *vr, uint8_t *p, rte_iova_t iova,
 				sizeof(struct vring_packed_desc_event)), align);
 }
 
-static inline void
-vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align)
-{
-	vring_init_split(vr, p, 0, align, num);
-}
-
 /*
  * The following is used with VIRTIO_RING_F_EVENT_IDX.
  * Assuming a given event_idx value from the other size, if we have
diff --git a/drivers/crypto/virtio/virtio_user/vhost.h b/drivers/crypto/virtio/virtio_user/vhost.h
new file mode 100644
index 0000000000..29cc1a14d4
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#ifndef _VIRTIO_USER_VHOST_H
+#define _VIRTIO_USER_VHOST_H
+
+#include <stdint.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <rte_errno.h>
+
+#include "../virtio_logs.h"
+
+struct vhost_vring_state {
+	unsigned int index;
+	unsigned int num;
+};
+
+struct vhost_vring_file {
+	unsigned int index;
+	int fd;
+};
+
+struct vhost_vring_addr {
+	unsigned int index;
+	/* Option flags. */
+	unsigned int flags;
+	/* Flag values: */
+	/* Whether log address is valid. If set enables logging. */
+#define VHOST_VRING_F_LOG 0
+
+	/* Start of array of descriptors (virtually contiguous) */
+	uint64_t desc_user_addr;
+	/* Used structure address. Must be 32 bit aligned */
+	uint64_t used_user_addr;
+	/* Available structure address. Must be 16 bit aligned */
+	uint64_t avail_user_addr;
+	/* Logging support. */
+	/* Log writes to used structure, at offset calculated from specified
+	 * address. Address must be 32 bit aligned.
+	 */
+	uint64_t log_guest_addr;
+};
+
+#ifndef VHOST_BACKEND_F_IOTLB_MSG_V2
+#define VHOST_BACKEND_F_IOTLB_MSG_V2 1
+#endif
+
+#ifndef VHOST_BACKEND_F_IOTLB_BATCH
+#define VHOST_BACKEND_F_IOTLB_BATCH 2
+#endif
+
+struct virtio_user_dev;
+
+struct virtio_user_backend_ops {
+	int (*setup)(struct virtio_user_dev *dev);
+	int (*destroy)(struct virtio_user_dev *dev);
+	int (*get_backend_features)(uint64_t *features);
+	int (*set_owner)(struct virtio_user_dev *dev);
+	int (*get_features)(struct virtio_user_dev *dev, uint64_t *features);
+	int (*set_features)(struct virtio_user_dev *dev, uint64_t features);
+	int (*set_memory_table)(struct virtio_user_dev *dev);
+	int (*set_vring_num)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*get_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_call)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_kick)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_addr)(struct virtio_user_dev *dev, struct vhost_vring_addr *addr);
+	int (*get_status)(struct virtio_user_dev *dev, uint8_t *status);
+	int (*set_status)(struct virtio_user_dev *dev, uint8_t status);
+	int (*get_config)(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len);
+	int (*set_config)(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off,
+			uint32_t len);
+	int (*cvq_enable)(struct virtio_user_dev *dev, int enable);
+	int (*enable_qp)(struct virtio_user_dev *dev, uint16_t pair_idx, int enable);
+	int (*dma_map)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*dma_unmap)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*update_link_state)(struct virtio_user_dev *dev);
+	int (*server_disconnect)(struct virtio_user_dev *dev);
+	int (*server_reconnect)(struct virtio_user_dev *dev);
+	int (*get_intr_fd)(struct virtio_user_dev *dev);
+	int (*map_notification_area)(struct virtio_user_dev *dev);
+	int (*unmap_notification_area)(struct virtio_user_dev *dev);
+};
+
+extern struct virtio_user_backend_ops virtio_ops_vdpa;
+
+#endif
diff --git a/drivers/crypto/virtio/virtio_user/vhost_vdpa.c b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
new file mode 100644
index 0000000000..b5839875e6
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
@@ -0,0 +1,710 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rte_memory.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+#include "../virtio_pci.h"
+
+struct vhost_vdpa_data {
+	int vhostfd;
+	uint64_t protocol_features;
+};
+
+#define VHOST_VDPA_SUPPORTED_BACKEND_FEATURES		\
+	(1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2	|	\
+	1ULL << VHOST_BACKEND_F_IOTLB_BATCH)
+
+/* vhost kernel & vdpa ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
+#define VHOST_VDPA_GET_DEVICE_ID _IOR(VHOST_VIRTIO, 0x70, __u32)
+#define VHOST_VDPA_GET_STATUS _IOR(VHOST_VIRTIO, 0x71, __u8)
+#define VHOST_VDPA_SET_STATUS _IOW(VHOST_VIRTIO, 0x72, __u8)
+#define VHOST_VDPA_GET_CONFIG _IOR(VHOST_VIRTIO, 0x73, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_CONFIG _IOW(VHOST_VIRTIO, 0x74, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_VRING_ENABLE _IOW(VHOST_VIRTIO, 0x75, struct vhost_vring_state)
+#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64)
+#define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64)
+
+/* no alignment requirement */
+struct vhost_iotlb_msg {
+	uint64_t iova;
+	uint64_t size;
+	uint64_t uaddr;
+#define VHOST_ACCESS_RO      0x1
+#define VHOST_ACCESS_WO      0x2
+#define VHOST_ACCESS_RW      0x3
+	uint8_t perm;
+#define VHOST_IOTLB_MISS           1
+#define VHOST_IOTLB_UPDATE         2
+#define VHOST_IOTLB_INVALIDATE     3
+#define VHOST_IOTLB_ACCESS_FAIL    4
+#define VHOST_IOTLB_BATCH_BEGIN    5
+#define VHOST_IOTLB_BATCH_END      6
+	uint8_t type;
+};
+
+#define VHOST_IOTLB_MSG_V2 0x2
+
+struct vhost_vdpa_config {
+	uint32_t off;
+	uint32_t len;
+	uint8_t buf[];
+};
+
+struct vhost_msg {
+	uint32_t type;
+	uint32_t reserved;
+	union {
+		struct vhost_iotlb_msg iotlb;
+		uint8_t padding[64];
+	};
+};
+
+static int
+vhost_vdpa_ioctl(int fd, uint64_t request, void *arg)
+{
+	int ret;
+
+	ret = ioctl(fd, request, arg);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Vhost-vDPA ioctl %"PRIu64" failed (%s)",
+				request, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_owner(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_OWNER, NULL);
+}
+
+static int
+vhost_vdpa_get_protocol_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_BACKEND_FEATURES, features);
+}
+
+static int
+vhost_vdpa_set_protocol_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_BACKEND_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_get_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int ret;
+
+	ret = vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_FEATURES, features);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get features");
+		return -1;
+	}
+
+	/* Negotiated vDPA backend features */
+	ret = vhost_vdpa_get_protocol_features(dev, &data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to get backend features");
+		return -1;
+	}
+
+	data->protocol_features &= VHOST_VDPA_SUPPORTED_BACKEND_FEATURES;
+
+	ret = vhost_vdpa_set_protocol_features(dev, data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to set backend features");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	/* WORKAROUND */
+	features |= 1ULL << VIRTIO_F_IOMMU_PLATFORM;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_iotlb_batch_begin(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_BEGIN;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch begin (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_iotlb_batch_end(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_END;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch end (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_UPDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.uaddr = (uint64_t)(uintptr_t)addr;
+	msg.iotlb.size = len;
+	msg.iotlb.perm = VHOST_ACCESS_RW;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", addr: %p, len: 0x%zx",
+			__func__, iova, addr, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB update (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_unmap(struct virtio_user_dev *dev, __rte_unused void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_INVALIDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.size = len;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", len: 0x%zx",
+			__func__, iova, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB invalidate (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_map(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_dma_unmap_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_unmap(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_map_contig(const struct rte_memseg_list *msl,
+		const struct rte_memseg *ms, size_t len, void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	if (msl->external)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, len);
+}
+
+static int
+vhost_vdpa_map(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
+		void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	/* skip external memory that isn't a heap */
+	if (msl->external && !msl->heap)
+		return 0;
+
+	/* skip any segments with invalid IOVA addresses */
+	if (ms->iova == RTE_BAD_IOVA)
+		return 0;
+
+	/* if IOVA mode is VA, we've already mapped the internal segments */
+	if (!msl->external && rte_eal_iova_mode() == RTE_IOVA_VA)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, ms->len);
+}
+
+static int
+vhost_vdpa_set_memory_table(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	vhost_vdpa_dma_unmap(dev, NULL, 0, SIZE_MAX);
+
+	if (rte_eal_iova_mode() == RTE_IOVA_VA) {
+		/* with IOVA as VA mode, we can get away with mapping contiguous
+		 * chunks rather than going page-by-page.
+		 */
+		ret = rte_memseg_contig_walk_thread_unsafe(
+				vhost_vdpa_map_contig, dev);
+		if (ret)
+			goto batch_end;
+		/* we have to continue the walk because we've skipped the
+		 * external segments during the config walk.
+		 */
+	}
+	ret = rte_memseg_walk_thread_unsafe(vhost_vdpa_map, dev);
+
+batch_end:
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_vring_enable(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_VRING_ENABLE, state);
+}
+
+static int
+vhost_vdpa_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_NUM, state);
+}
+
+static int
+vhost_vdpa_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_CALL, file);
+}
+
+static int
+vhost_vdpa_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_KICK, file);
+}
+
+static int
+vhost_vdpa_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_ADDR, addr);
+}
+
+static int
+vhost_vdpa_get_status(struct virtio_user_dev *dev, uint8_t *status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_GET_STATUS, status);
+}
+
+static int
+vhost_vdpa_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_STATUS, &status);
+}
+
+static int
+vhost_vdpa_get_config(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_GET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+		goto out;
+	}
+
+	memcpy(data, config->buf, len);
+out:
+	free(config);
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_config(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	memcpy(config->buf, data, len);
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_SET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to set vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+	}
+
+	free(config);
+
+	return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost vdpa backend.
+ *
+ * @return
+ *   - (-1) if fail to set up;
+ *   - (>=0) if successful.
+ */
+static int
+vhost_vdpa_setup(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data;
+	uint32_t did = (uint32_t)-1;
+
+	data = malloc(sizeof(*data));
+	if (!data) {
+		PMD_DRV_LOG(ERR, "(%s) Faidle to allocate backend data", dev->path);
+		return -1;
+	}
+
+	data->vhostfd = open(dev->path, O_RDWR);
+	if (data->vhostfd < 0) {
+		PMD_DRV_LOG(ERR, "Failed to open %s: %s",
+				dev->path, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	if (ioctl(data->vhostfd, VHOST_VDPA_GET_DEVICE_ID, &did) < 0 ||
+			did != VIRTIO_ID_CRYPTO) {
+		PMD_DRV_LOG(ERR, "Invalid vdpa device ID: %u", did);
+		close(data->vhostfd);
+		free(data);
+		return -1;
+	}
+
+	dev->backend_data = data;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_destroy(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	if (!data)
+		return 0;
+
+	close(data->vhostfd);
+
+	free(data);
+	dev->backend_data = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_cvq_enable(struct virtio_user_dev *dev, int enable)
+{
+	struct vhost_vring_state state = {
+		.index = dev->max_queue_pairs,
+		.num   = enable,
+	};
+
+	return vhost_vdpa_set_vring_enable(dev, &state);
+}
+
+static int
+vhost_vdpa_enable_queue_pair(struct virtio_user_dev *dev,
+				uint16_t pair_idx,
+				int enable)
+{
+	struct vhost_vring_state state = {
+		.index = pair_idx,
+		.num   = enable,
+	};
+
+	if (dev->qp_enabled[pair_idx] == enable)
+		return 0;
+
+	if (vhost_vdpa_set_vring_enable(dev, &state))
+		return -1;
+
+	dev->qp_enabled[pair_idx] = enable;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_backend_features(uint64_t *features)
+{
+	*features = 0;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_update_link_state(struct virtio_user_dev *dev)
+{
+	/* TODO: It is W/A until a cleaner approach to find cpt status */
+	dev->crypto_status = VIRTIO_CRYPTO_S_HW_READY;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_intr_fd(struct virtio_user_dev *dev __rte_unused)
+{
+	/* No link state interrupt with Vhost-vDPA */
+	return -1;
+}
+
+static int
+vhost_vdpa_get_nr_vrings(struct virtio_user_dev *dev)
+{
+	int nr_vrings = dev->max_queue_pairs;
+
+	return nr_vrings;
+}
+
+static int
+vhost_vdpa_unmap_notification_area(struct virtio_user_dev *dev)
+{
+	int i, nr_vrings;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	for (i = 0; i < nr_vrings; i++) {
+		if (dev->notify_area[i])
+			munmap(dev->notify_area[i], getpagesize());
+	}
+	free(dev->notify_area);
+	dev->notify_area = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_map_notification_area(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int nr_vrings, i, page_size = getpagesize();
+	uint16_t **notify_area;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	/* CQ is another vring */
+	nr_vrings++;
+
+	notify_area = malloc(nr_vrings * sizeof(*notify_area));
+	if (!notify_area) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to allocate notify area array", dev->path);
+		return -1;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		notify_area[i] = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED | MAP_FILE,
+					data->vhostfd, i * page_size);
+		if (notify_area[i] == MAP_FAILED) {
+			PMD_DRV_LOG(ERR, "(%s) Map failed for notify address of queue %d",
+					dev->path, i);
+			i--;
+			goto map_err;
+		}
+	}
+	dev->notify_area = notify_area;
+
+	return 0;
+
+map_err:
+	for (; i >= 0; i--)
+		munmap(notify_area[i], page_size);
+	free(notify_area);
+
+	return -1;
+}
+
+struct virtio_user_backend_ops virtio_crypto_ops_vdpa = {
+	.setup = vhost_vdpa_setup,
+	.destroy = vhost_vdpa_destroy,
+	.get_backend_features = vhost_vdpa_get_backend_features,
+	.set_owner = vhost_vdpa_set_owner,
+	.get_features = vhost_vdpa_get_features,
+	.set_features = vhost_vdpa_set_features,
+	.set_memory_table = vhost_vdpa_set_memory_table,
+	.set_vring_num = vhost_vdpa_set_vring_num,
+	.set_vring_base = vhost_vdpa_set_vring_base,
+	.get_vring_base = vhost_vdpa_get_vring_base,
+	.set_vring_call = vhost_vdpa_set_vring_call,
+	.set_vring_kick = vhost_vdpa_set_vring_kick,
+	.set_vring_addr = vhost_vdpa_set_vring_addr,
+	.get_status = vhost_vdpa_get_status,
+	.set_status = vhost_vdpa_set_status,
+	.get_config = vhost_vdpa_get_config,
+	.set_config = vhost_vdpa_set_config,
+	.cvq_enable = vhost_vdpa_cvq_enable,
+	.enable_qp = vhost_vdpa_enable_queue_pair,
+	.dma_map = vhost_vdpa_dma_map_batch,
+	.dma_unmap = vhost_vdpa_dma_unmap_batch,
+	.update_link_state = vhost_vdpa_update_link_state,
+	.get_intr_fd = vhost_vdpa_get_intr_fd,
+	.map_notification_area = vhost_vdpa_map_notification_area,
+	.unmap_notification_area = vhost_vdpa_unmap_notification_area,
+};
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.c b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
new file mode 100644
index 0000000000..c8478d72ce
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
@@ -0,0 +1,749 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <rte_alarm.h>
+#include <rte_string_fns.h>
+#include <rte_eal_memconfig.h>
+#include <rte_malloc.h>
+#include <rte_io.h>
+
+#include "vhost.h"
+#include "virtio_logs.h"
+#include "cryptodev_pmd.h"
+#include "virtio_crypto.h"
+#include "virtio_cvq.h"
+#include "virtio_user_dev.h"
+#include "virtqueue.h"
+
+#define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
+
+const char * const crypto_virtio_user_backend_strings[] = {
+	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
+	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
+};
+
+static int
+virtio_user_uninit_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	if (dev->kickfds[queue_sel] >= 0) {
+		close(dev->kickfds[queue_sel]);
+		dev->kickfds[queue_sel] = -1;
+	}
+
+	if (dev->callfds[queue_sel] >= 0) {
+		close(dev->callfds[queue_sel]);
+		dev->callfds[queue_sel] = -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_init_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* May use invalid flag, but some backend uses kickfd and
+	 * callfd as criteria to judge if dev is alive. so finally we
+	 * use real event_fd.
+	 */
+	dev->callfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->callfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup callfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+	dev->kickfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->kickfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup kickfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_destroy_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	struct vhost_vring_state state;
+	int ret;
+
+	state.index = queue_sel;
+	ret = dev->ops->get_vring_base(dev, &state);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to destroy queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
+	 * firstly because vhost depends on this msg to allocate virtqueue
+	 * pair.
+	 */
+	struct vhost_vring_file file;
+	int ret;
+
+	file.index = queue_sel;
+	file.fd = dev->callfds[queue_sel];
+	ret = dev->ops->set_vring_call(dev, &file);
+	if (ret < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	int ret;
+	struct vhost_vring_file file;
+	struct vhost_vring_state state;
+	struct vring *vring = &dev->vrings.split[queue_sel];
+	struct vring_packed *pq_vring = &dev->vrings.packed[queue_sel];
+	uint64_t desc_addr, avail_addr, used_addr;
+	struct vhost_vring_addr addr = {
+		.index = queue_sel,
+		.log_guest_addr = 0,
+		.flags = 0, /* disable log */
+	};
+
+	if (queue_sel == dev->max_queue_pairs) {
+		if (!dev->scvq) {
+			PMD_INIT_LOG(ERR, "(%s) Shadow control queue expected but missing",
+					dev->path);
+			goto err;
+		}
+
+		/* Use shadow control queue information */
+		vring = &dev->scvq->vq_split.ring;
+		pq_vring = &dev->scvq->vq_packed.ring;
+	}
+
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
+		desc_addr = pq_vring->desc_iova;
+		avail_addr = desc_addr + pq_vring->num * sizeof(struct vring_packed_desc);
+		used_addr =  RTE_ALIGN_CEIL(avail_addr + sizeof(struct vring_packed_desc_event),
+						VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	} else {
+		desc_addr = vring->desc_iova;
+		avail_addr = desc_addr + vring->num * sizeof(struct vring_desc);
+		used_addr = RTE_ALIGN_CEIL((uintptr_t)(&vring->avail->ring[vring->num]),
+					VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	}
+
+	state.index = queue_sel;
+	state.num = vring->num;
+	ret = dev->ops->set_vring_num(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	state.index = queue_sel;
+	state.num = 0; /* no reservation */
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
+		state.num |= (1 << 15);
+	ret = dev->ops->set_vring_base(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	ret = dev->ops->set_vring_addr(dev, &addr);
+	if (ret < 0)
+		goto err;
+
+	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
+	 * lastly because vhost depends on this msg to judge if
+	 * virtio is ready.
+	 */
+	file.index = queue_sel;
+	file.fd = dev->kickfds[queue_sel];
+	ret = dev->ops->set_vring_kick(dev, &file);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+err:
+	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel);
+
+	return -1;
+}
+
+static int
+virtio_user_foreach_queue(struct virtio_user_dev *dev,
+			int (*fn)(struct virtio_user_dev *, uint32_t))
+{
+	uint32_t i, nr_vq;
+
+	nr_vq = dev->max_queue_pairs;
+
+	for (i = 0; i < nr_vq; i++)
+		if (fn(dev, i) < 0)
+			return -1;
+
+	return 0;
+}
+
+int
+crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev)
+{
+	uint64_t features;
+	int ret = -1;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 0: tell vhost to create queues */
+	if (virtio_user_foreach_queue(dev, virtio_user_create_queue) < 0)
+		goto error;
+
+	features = dev->features;
+
+	ret = dev->ops->set_features(dev, features);
+	if (ret < 0)
+		goto error;
+	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
+error:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return ret;
+}
+
+int
+crypto_virtio_user_start_device(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	/*
+	 * XXX workaround!
+	 *
+	 * We need to make sure that the locks will be
+	 * taken in the correct order to avoid deadlocks.
+	 *
+	 * Before releasing this lock, this thread should
+	 * not trigger any memory hotplug events.
+	 *
+	 * This is a temporary workaround, and should be
+	 * replaced when we get proper supports from the
+	 * memory subsystem in the future.
+	 */
+	rte_mcfg_mem_read_lock();
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 2: share memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Step 3: kick queues */
+	ret = virtio_user_foreach_queue(dev, virtio_user_kick_queue);
+	if (ret < 0)
+		goto error;
+
+	ret = virtio_user_kick_queue(dev, dev->max_queue_pairs);
+	if (ret < 0)
+		goto error;
+
+	/* Step 4: enable queues */
+	for (int i = 0; i < dev->max_queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto error;
+	}
+
+	dev->started = true;
+
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	return 0;
+error:
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path);
+
+	/* TODO: free resource here or caller to check */
+	return -1;
+}
+
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev)
+{
+	uint32_t i;
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	if (!dev->started)
+		goto out;
+
+	for (i = 0; i < dev->max_queue_pairs; ++i) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	if (dev->scvq) {
+		ret = dev->ops->cvq_enable(dev, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	/* Stop the backend. */
+	if (virtio_user_foreach_queue(dev, virtio_user_destroy_queue) < 0)
+		goto err;
+
+	dev->started = false;
+
+out:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return 0;
+err:
+	pthread_mutex_unlock(&dev->mutex);
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path);
+
+	return -1;
+}
+
+static int
+virtio_user_dev_init_max_queue_pairs(struct virtio_user_dev *dev, uint32_t user_max_qp)
+{
+	int ret;
+
+	if (!dev->ops->get_config) {
+		dev->max_queue_pairs = user_max_qp;
+		return 0;
+	}
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&dev->max_queue_pairs,
+			offsetof(struct virtio_crypto_config, max_dataqueues),
+			sizeof(uint16_t));
+	if (ret) {
+		/*
+		 * We need to know the max queue pair from the device so that
+		 * the control queue gets the right index.
+		 */
+		dev->max_queue_pairs = 1;
+		PMD_DRV_LOG(ERR, "(%s) Failed to get max queue pairs from device", dev->path);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_dev_init_cipher_services(struct virtio_user_dev *dev)
+{
+	struct virtio_crypto_config config;
+	int ret;
+
+	dev->crypto_services = RTE_BIT32(VIRTIO_CRYPTO_SERVICE_CIPHER);
+	dev->cipher_algo = 0;
+	dev->auth_algo = 0;
+	dev->akcipher_algo = 0;
+
+	if (!dev->ops->get_config)
+		return 0;
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&config,	0, sizeof(config));
+	if (ret) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to get crypto config from device", dev->path);
+		return ret;
+	}
+
+	dev->crypto_services = config.crypto_services;
+	dev->cipher_algo = ((uint64_t)config.cipher_algo_h << 32) |
+						config.cipher_algo_l;
+	dev->hash_algo = config.hash_algo;
+	dev->auth_algo = ((uint64_t)config.mac_algo_h << 32) |
+						config.mac_algo_l;
+	dev->aead_algo = config.aead_algo;
+	dev->akcipher_algo = config.akcipher_algo;
+	return 0;
+}
+
+static int
+virtio_user_dev_init_notify(struct virtio_user_dev *dev)
+{
+
+	if (virtio_user_foreach_queue(dev, virtio_user_init_notify_queue) < 0)
+		goto err;
+
+	if (dev->device_features & (1ULL << VIRTIO_F_NOTIFICATION_DATA))
+		if (dev->ops->map_notification_area &&
+				dev->ops->map_notification_area(dev))
+			goto err;
+
+	return 0;
+err:
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	return -1;
+}
+
+static void
+virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
+{
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	if (dev->ops->unmap_notification_area && dev->notify_area)
+		dev->ops->unmap_notification_area(dev);
+}
+
+static void
+virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
+			const void *addr,
+			size_t len __rte_unused,
+			void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+	struct rte_memseg_list *msl;
+	uint16_t i;
+	int ret = 0;
+
+	/* ignore externally allocated memory */
+	msl = rte_mem_virt2memseg_list(addr);
+	if (msl->external)
+		return;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	if (dev->started == false)
+		goto exit;
+
+	/* Step 1: pause the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto exit;
+	}
+
+	/* Step 2: update memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto exit;
+
+	/* Step 3: resume the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto exit;
+	}
+
+exit:
+	pthread_mutex_unlock(&dev->mutex);
+
+	if (ret < 0)
+		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+	if (dev->is_server) {
+		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
+			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
+			return -1;
+		}
+	}
+
+	switch (dev->backend_type) {
+	case VIRTIO_USER_BACKEND_VHOST_VDPA:
+		dev->ops = &virtio_crypto_ops_vdpa;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_alloc_vrings(struct virtio_user_dev *dev)
+{
+	int i, size, nr_vrings;
+	bool packed_ring = !!(dev->device_features & (1ull << VIRTIO_F_RING_PACKED));
+
+	nr_vrings = dev->max_queue_pairs + 1;
+
+	dev->callfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->callfds), 0);
+	if (!dev->callfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc callfds", dev->path);
+		return -1;
+	}
+
+	dev->kickfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->kickfds), 0);
+	if (!dev->kickfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc kickfds", dev->path);
+		goto free_callfds;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		dev->callfds[i] = -1;
+		dev->kickfds[i] = -1;
+	}
+
+	if (packed_ring)
+		size = sizeof(*dev->vrings.packed);
+	else
+		size = sizeof(*dev->vrings.split);
+	dev->vrings.ptr = rte_zmalloc("virtio_user_dev", nr_vrings * size, 0);
+	if (!dev->vrings.ptr) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc vrings metadata", dev->path);
+		goto free_kickfds;
+	}
+
+	if (packed_ring) {
+		dev->packed_queues = rte_zmalloc("virtio_user_dev",
+				nr_vrings * sizeof(*dev->packed_queues), 0);
+		if (!dev->packed_queues) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to alloc packed queues metadata",
+					dev->path);
+			goto free_vrings;
+		}
+	}
+
+	dev->qp_enabled = rte_zmalloc("virtio_user_dev",
+			nr_vrings * sizeof(*dev->qp_enabled), 0);
+	if (!dev->qp_enabled) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc QP enable states", dev->path);
+		goto free_packed_queues;
+	}
+
+	return 0;
+
+free_packed_queues:
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+free_vrings:
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+free_kickfds:
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+free_callfds:
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+
+	return -1;
+}
+
+static void
+virtio_user_free_vrings(struct virtio_user_dev *dev)
+{
+	rte_free(dev->qp_enabled);
+	dev->qp_enabled = NULL;
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+}
+
+#define VIRTIO_USER_SUPPORTED_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_HASH       | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+int
+crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server)
+{
+	uint64_t backend_features;
+
+	pthread_mutex_init(&dev->mutex, NULL);
+	strlcpy(dev->path, path, PATH_MAX);
+
+	dev->started = 0;
+	dev->queue_pairs = 1; /* mq disabled by default */
+	dev->max_queue_pairs = queues; /* initialize to user requested value for kernel backend */
+	dev->queue_size = queue_size;
+	dev->is_server = server;
+	dev->frontend_features = 0;
+	dev->unsupported_features = 0;
+	dev->backend_type = VIRTIO_USER_BACKEND_VHOST_VDPA;
+	dev->hw.modern = 1;
+
+	if (virtio_user_dev_setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->set_owner(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
+		goto destroy;
+	}
+
+	if (dev->ops->get_backend_features(&backend_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
+		goto destroy;
+	}
+
+	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
+
+	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_max_queue_pairs(dev, queues)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get max queue pairs", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_cipher_services(dev)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get cipher services", dev->path);
+		goto destroy;
+	}
+
+	dev->frontend_features &= ~dev->unsupported_features;
+	dev->device_features &= ~dev->unsupported_features;
+
+	if (virtio_user_alloc_vrings(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to allocate vring metadata", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_notify(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path);
+		goto free_vrings;
+	}
+
+	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
+				virtio_user_mem_event_cb, dev)) {
+		if (rte_errno != ENOTSUP) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback",
+					dev->path);
+			goto notify_uninit;
+		}
+	}
+
+	return 0;
+
+notify_uninit:
+	virtio_user_dev_uninit_notify(dev);
+free_vrings:
+	virtio_user_free_vrings(dev);
+destroy:
+	dev->ops->destroy(dev);
+
+	return -1;
+}
+
+void
+crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev)
+{
+	crypto_virtio_user_stop_device(dev);
+
+	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
+
+	virtio_user_dev_uninit_notify(dev);
+
+	virtio_user_free_vrings(dev);
+
+	if (dev->is_server)
+		unlink(dev->path);
+
+	dev->ops->destroy(dev);
+}
+
+#define CVQ_MAX_DATA_DESCS 32
+
+int
+crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	dev->status = status;
+	ret = dev->ops->set_status(dev, status);
+	if (ret && ret != -ENOTSUP)
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path);
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev)
+{
+	int ret;
+	uint8_t status;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	ret = dev->ops->get_status(dev, &status);
+	if (!ret) {
+		dev->status = status;
+		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):"
+			"\t-RESET: %u "
+			"\t-ACKNOWLEDGE: %u "
+			"\t-DRIVER: %u "
+			"\t-DRIVER_OK: %u "
+			"\t-FEATURES_OK: %u "
+			"\t-DEVICE_NEED_RESET: %u "
+			"\t-FAILED: %u",
+			dev->status,
+			(dev->status == VIRTIO_CONFIG_STATUS_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
+	} else if (ret != -ENOTSUP) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path);
+	}
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
+{
+	if (dev->ops->update_link_state)
+		return dev->ops->update_link_state(dev);
+
+	return 0;
+}
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.h b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
new file mode 100644
index 0000000000..9cd9856e5d
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#ifndef _VIRTIO_USER_DEV_H
+#define _VIRTIO_USER_DEV_H
+
+#include <limits.h>
+#include <stdbool.h>
+
+#include "../virtio_pci.h"
+#include "../virtio_ring.h"
+
+extern struct virtio_user_backend_ops virtio_crypto_ops_vdpa;
+
+enum virtio_user_backend_type {
+	VIRTIO_USER_BACKEND_UNKNOWN,
+	VIRTIO_USER_BACKEND_VHOST_USER,
+	VIRTIO_USER_BACKEND_VHOST_VDPA,
+};
+
+struct virtio_user_queue {
+	uint16_t used_idx;
+	bool avail_wrap_counter;
+	bool used_wrap_counter;
+};
+
+struct virtio_user_dev {
+	struct virtio_crypto_hw hw;
+	enum virtio_user_backend_type backend_type;
+	bool		is_server;  /* server or client mode */
+
+	int		*callfds;
+	int		*kickfds;
+	uint16_t	max_queue_pairs;
+	uint16_t	queue_pairs;
+	uint32_t	queue_size;
+	uint64_t	features; /* the negotiated features with driver,
+				   * and will be sync with device
+				   */
+	uint64_t	device_features; /* supported features by device */
+	uint64_t	frontend_features; /* enabled frontend features */
+	uint64_t	unsupported_features; /* unsupported features mask */
+	uint8_t		status;
+	uint32_t	crypto_status;
+	uint32_t	crypto_services;
+	uint64_t	cipher_algo;
+	uint32_t	hash_algo;
+	uint64_t	auth_algo;
+	uint32_t	aead_algo;
+	uint32_t	akcipher_algo;
+	char		path[PATH_MAX];
+
+	union {
+		void			*ptr;
+		struct vring		*split;
+		struct vring_packed	*packed;
+	} vrings;
+
+	struct virtio_user_queue *packed_queues;
+	bool		*qp_enabled;
+
+	struct virtio_user_backend_ops *ops;
+	pthread_mutex_t	mutex;
+	bool		started;
+
+	bool			hw_cvq;
+	struct virtqueue	*scvq;
+
+	void *backend_data;
+
+	uint16_t **notify_area;
+};
+
+int crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev);
+int crypto_virtio_user_start_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server);
+void crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status);
+int crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev);
+extern const char * const crypto_virtio_user_backend_strings[];
+#endif
diff --git a/drivers/crypto/virtio/virtio_user_cryptodev.c b/drivers/crypto/virtio/virtio_user_cryptodev.c
new file mode 100644
index 0000000000..992e8fb43b
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user_cryptodev.c
@@ -0,0 +1,575 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <rte_malloc.h>
+#include <rte_kvargs.h>
+#include <bus_vdev_driver.h>
+#include <rte_cryptodev.h>
+#include <cryptodev_pmd.h>
+#include <rte_alarm.h>
+#include <rte_cycles.h>
+#include <rte_io.h>
+
+#include "virtio_user/virtio_user_dev.h"
+#include "virtio_user/vhost.h"
+#include "virtio_cryptodev.h"
+#include "virtio_logs.h"
+#include "virtio_pci.h"
+#include "virtqueue.h"
+
+#define virtio_user_get_dev(hwp) container_of(hwp, struct virtio_user_dev, hw)
+
+static void
+virtio_user_read_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		     void *dst, int length __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (offset == offsetof(struct virtio_crypto_config, status)) {
+		crypto_virtio_user_dev_update_link_state(dev);
+		*(uint32_t *)dst = dev->crypto_status;
+	} else if (offset == offsetof(struct virtio_crypto_config, max_dataqueues))
+		*(uint16_t *)dst = dev->max_queue_pairs;
+	else if (offset == offsetof(struct virtio_crypto_config, crypto_services))
+		*(uint32_t *)dst = dev->crypto_services;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_l))
+		*(uint32_t *)dst = dev->cipher_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_h))
+		*(uint32_t *)dst = dev->cipher_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, hash_algo))
+		*(uint32_t *)dst = dev->hash_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_l))
+		*(uint32_t *)dst = dev->auth_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_h))
+		*(uint32_t *)dst = dev->auth_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, aead_algo))
+		*(uint32_t *)dst = dev->aead_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, akcipher_algo))
+		*(uint32_t *)dst = dev->akcipher_algo;
+}
+
+static void
+virtio_user_write_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		      const void *src, int length)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(src);
+
+	PMD_DRV_LOG(ERR, "not supported offset=%zu, len=%d",
+		    offset, length);
+}
+
+static void
+virtio_user_reset(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+		crypto_virtio_user_stop_device(dev);
+}
+
+static void
+virtio_user_set_status(struct virtio_crypto_hw *hw, uint8_t status)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint8_t old_status = dev->status;
+
+	if (status & VIRTIO_CONFIG_STATUS_FEATURES_OK &&
+			~old_status & VIRTIO_CONFIG_STATUS_FEATURES_OK) {
+		crypto_virtio_user_dev_set_features(dev);
+		/* Feature negotiation should be only done in probe time.
+		 * So we skip any more request here.
+		 */
+		dev->status |= VIRTIO_CONFIG_STATUS_FEATURES_OK;
+	}
+
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) {
+		if (crypto_virtio_user_start_device(dev)) {
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	} else if (status == VIRTIO_CONFIG_STATUS_RESET) {
+		virtio_user_reset(hw);
+	}
+
+	crypto_virtio_user_dev_set_status(dev, status);
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK && dev->scvq) {
+		if (dev->ops->cvq_enable(dev, 1) < 0) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to start ctrlq", dev->path);
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	}
+}
+
+static uint8_t
+virtio_user_get_status(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	crypto_virtio_user_dev_update_status(dev);
+
+	return dev->status;
+}
+
+#define VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_RING_F_INDIRECT_DESC      | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+static uint64_t
+virtio_user_get_features(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* unmask feature bits defined in vhost user protocol */
+	return (dev->device_features | dev->frontend_features) &
+		VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES;
+}
+
+static void
+virtio_user_set_features(struct virtio_crypto_hw *hw, uint64_t features)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	dev->features = features & (dev->device_features | dev->frontend_features);
+}
+
+static uint8_t
+virtio_user_get_isr(struct virtio_crypto_hw *hw __rte_unused)
+{
+	/* rxq interrupts and config interrupt are separated in virtio-user,
+	 * here we only report config change.
+	 */
+	return VIRTIO_PCI_CAP_ISR_CFG;
+}
+
+static uint16_t
+virtio_user_set_config_irq(struct virtio_crypto_hw *hw __rte_unused,
+		    uint16_t vec __rte_unused)
+{
+	return 0;
+}
+
+static uint16_t
+virtio_user_set_queue_irq(struct virtio_crypto_hw *hw __rte_unused,
+			  struct virtqueue *vq __rte_unused,
+			  uint16_t vec)
+{
+	/* pretend we have done that */
+	return vec;
+}
+
+/* This function is to get the queue size, aka, number of descs, of a specified
+ * queue. Different with the VHOST_USER_GET_QUEUE_NUM, which is used to get the
+ * max supported queues.
+ */
+static uint16_t
+virtio_user_get_queue_num(struct virtio_crypto_hw *hw, uint16_t queue_id __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* Currently, each queue has same queue size */
+	return dev->queue_size;
+}
+
+static void
+virtio_user_setup_queue_packed(struct virtqueue *vq,
+			       struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	struct vring_packed *vring;
+	uint64_t desc_addr;
+	uint64_t avail_addr;
+	uint64_t used_addr;
+	uint16_t i;
+
+	vring  = &dev->vrings.packed[queue_idx];
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries *
+		sizeof(struct vring_packed_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr +
+			   sizeof(struct vring_packed_desc_event),
+			   VIRTIO_VRING_ALIGN);
+	vring->num = vq->vq_nentries;
+	vring->desc_iova = vq->vq_ring_mem;
+	vring->desc = (void *)(uintptr_t)desc_addr;
+	vring->driver = (void *)(uintptr_t)avail_addr;
+	vring->device = (void *)(uintptr_t)used_addr;
+	dev->packed_queues[queue_idx].avail_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_idx = 0;
+
+	for (i = 0; i < vring->num; i++)
+		vring->desc[i].flags = 0;
+}
+
+static void
+virtio_user_setup_queue_split(struct virtqueue *vq, struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	uint64_t desc_addr, avail_addr, used_addr;
+
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
+							 ring[vq->vq_nentries]),
+				   VIRTIO_VRING_ALIGN);
+
+	dev->vrings.split[queue_idx].num = vq->vq_nentries;
+	dev->vrings.split[queue_idx].desc_iova = vq->vq_ring_mem;
+	dev->vrings.split[queue_idx].desc = (void *)(uintptr_t)desc_addr;
+	dev->vrings.split[queue_idx].avail = (void *)(uintptr_t)avail_addr;
+	dev->vrings.split[queue_idx].used = (void *)(uintptr_t)used_addr;
+}
+
+static int
+virtio_user_setup_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (vtpci_with_packed_queue(hw))
+		virtio_user_setup_queue_packed(vq, dev);
+	else
+		virtio_user_setup_queue_split(vq, dev);
+
+	if (dev->notify_area)
+		vq->notify_addr = dev->notify_area[vq->vq_queue_index];
+
+	if (virtcrypto_cq_to_vq(hw->cvq) == vq)
+		dev->scvq = virtcrypto_cq_to_vq(hw->cvq);
+
+	return 0;
+}
+
+static void
+virtio_user_del_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(vq);
+}
+
+static void
+virtio_user_notify_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint64_t notify_data = 1;
+
+	if (!dev->notify_area) {
+		if (write(dev->kickfds[vq->vq_queue_index], &notify_data,
+			  sizeof(notify_data)) < 0)
+			PMD_DRV_LOG(ERR, "failed to kick backend: %s",
+				    strerror(errno));
+		return;
+	} else if (!vtpci_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) {
+		rte_write16(vq->vq_queue_index, vq->notify_addr);
+		return;
+	}
+
+	if (vtpci_with_packed_queue(hw)) {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:30]: avail index
+		 * Bit[31]: avail wrap counter
+		 */
+		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
+				VRING_PACKED_DESC_F_AVAIL)) << 31) |
+				((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	} else {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:31]: avail index
+		 */
+		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	}
+	rte_write32(notify_data, vq->notify_addr);
+}
+
+const struct virtio_pci_ops crypto_virtio_user_ops = {
+	.read_dev_cfg	= virtio_user_read_dev_config,
+	.write_dev_cfg	= virtio_user_write_dev_config,
+	.reset		= virtio_user_reset,
+	.get_status	= virtio_user_get_status,
+	.set_status	= virtio_user_set_status,
+	.get_features	= virtio_user_get_features,
+	.set_features	= virtio_user_set_features,
+	.get_isr	= virtio_user_get_isr,
+	.set_config_irq	= virtio_user_set_config_irq,
+	.set_queue_irq	= virtio_user_set_queue_irq,
+	.get_queue_num	= virtio_user_get_queue_num,
+	.setup_queue	= virtio_user_setup_queue,
+	.del_queue	= virtio_user_del_queue,
+	.notify_queue	= virtio_user_notify_queue,
+};
+
+static const char * const valid_args[] = {
+#define VIRTIO_USER_ARG_QUEUES_NUM     "queues"
+	VIRTIO_USER_ARG_QUEUES_NUM,
+#define VIRTIO_USER_ARG_QUEUE_SIZE     "queue_size"
+	VIRTIO_USER_ARG_QUEUE_SIZE,
+#define VIRTIO_USER_ARG_PATH           "path"
+	VIRTIO_USER_ARG_PATH,
+	NULL
+};
+
+#define VIRTIO_USER_DEF_Q_NUM	1
+#define VIRTIO_USER_DEF_Q_SZ	256
+#define VIRTIO_USER_DEF_SERVER_MODE	0
+
+static int
+get_string_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	if (!value || !extra_args)
+		return -EINVAL;
+
+	*(char **)extra_args = strdup(value);
+
+	if (!*(char **)extra_args)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int
+get_integer_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	uint64_t integer = 0;
+	if (!value || !extra_args)
+		return -EINVAL;
+	errno = 0;
+	integer = strtoull(value, NULL, 0);
+	/* extra_args keeps default value, it should be replaced
+	 * only in case of successful parsing of the 'value' arg
+	 */
+	if (errno == 0)
+		*(uint64_t *)extra_args = integer;
+	return -errno;
+}
+
+static struct rte_cryptodev *
+virtio_user_cryptodev_alloc(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev_pmd_init_params init_params = {
+		.name = "",
+		.private_data_size = sizeof(struct virtio_user_dev),
+	};
+	struct rte_cryptodev_data *data;
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	struct virtio_crypto_hw *hw;
+
+	init_params.socket_id = vdev->device.numa_node;
+	init_params.private_data_size = sizeof(struct virtio_user_dev);
+	cryptodev = rte_cryptodev_pmd_create(vdev->device.name, &vdev->device, &init_params);
+	if (cryptodev == NULL) {
+		PMD_INIT_LOG(ERR, "failed to create cryptodev vdev");
+		return NULL;
+	}
+
+	data = cryptodev->data;
+	dev = data->dev_private;
+	hw = &dev->hw;
+
+	hw->dev_id = data->dev_id;
+	VTPCI_OPS(hw) = &crypto_virtio_user_ops;
+
+	return cryptodev;
+}
+
+static void
+virtio_user_cryptodev_free(struct rte_cryptodev *cryptodev)
+{
+	rte_cryptodev_pmd_destroy(cryptodev);
+}
+
+static int
+virtio_user_pmd_probe(struct rte_vdev_device *vdev)
+{
+	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
+	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
+	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
+	struct rte_cryptodev *cryptodev = NULL;
+	struct rte_kvargs *kvlist = NULL;
+	struct virtio_user_dev *dev;
+	char *path = NULL;
+	int ret = -1;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(vdev), valid_args);
+
+	if (!kvlist) {
+		PMD_INIT_LOG(ERR, "error when parsing param");
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PATH) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PATH,
+					&get_string_arg, &path) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_PATH);
+			goto end;
+		}
+	} else {
+		PMD_INIT_LOG(ERR, "arg %s is mandatory for virtio_user",
+				VIRTIO_USER_ARG_PATH);
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUES_NUM) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUES_NUM,
+					&get_integer_arg, &queues) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUES_NUM);
+			goto end;
+		}
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE,
+					&get_integer_arg, &queue_size) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUE_SIZE);
+			goto end;
+		}
+	}
+
+	cryptodev = virtio_user_cryptodev_alloc(vdev);
+	if (!cryptodev) {
+		PMD_INIT_LOG(ERR, "virtio_user fails to alloc device");
+		goto end;
+	}
+
+	dev = cryptodev->data->dev_private;
+	if (crypto_virtio_user_dev_init(dev, path, queues, queue_size,
+			server_mode) < 0) {
+		PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES,
+			NULL) < 0) {
+		PMD_INIT_LOG(ERR, "crypto_virtio_dev_init fails");
+		crypto_virtio_user_dev_uninit(dev);
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	rte_cryptodev_pmd_probing_finish(cryptodev);
+
+	ret = 0;
+end:
+	rte_kvargs_free(kvlist);
+	free(path);
+	return ret;
+}
+
+static int
+virtio_user_pmd_remove(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev *cryptodev;
+	const char *name;
+	int devid;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	PMD_DRV_LOG(INFO, "Removing %s", name);
+
+	devid = rte_cryptodev_get_dev_id(name);
+	if (devid < 0)
+		return -EINVAL;
+
+	rte_cryptodev_stop(devid);
+
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (rte_cryptodev_pmd_destroy(cryptodev) < 0) {
+		PMD_DRV_LOG(ERR, "Failed to remove %s", name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_map(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_map)
+		return dev->ops->dma_map(dev, addr, iova, len);
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_unmap(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_unmap)
+		return dev->ops->dma_unmap(dev, addr, iova, len);
+
+	return 0;
+}
+
+static struct rte_vdev_driver virtio_user_driver = {
+	.probe = virtio_user_pmd_probe,
+	.remove = virtio_user_pmd_remove,
+	.dma_map = virtio_user_pmd_dma_map,
+	.dma_unmap = virtio_user_pmd_dma_unmap,
+};
+
+static struct cryptodev_driver virtio_crypto_drv;
+
+uint8_t cryptodev_virtio_user_driver_id;
+
+RTE_PMD_REGISTER_VDEV(crypto_virtio_user, virtio_user_driver);
+RTE_PMD_REGISTER_CRYPTO_DRIVER(virtio_crypto_drv,
+	virtio_user_driver.driver,
+	cryptodev_virtio_user_driver_id);
+RTE_PMD_REGISTER_PARAM_STRING(crypto_virtio_user,
+	"path=<path> "
+	"queues=<int> "
+	"queue_size=<int>");
-- 
2.25.1


^ permalink raw reply	[relevance 1%]

* [PATCH v4 2/5] dmadev: avoid copies in tracepoints
  @ 2025-03-04 16:06  4%   ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-04 16:06 UTC (permalink / raw)
  To: dev; +Cc: Thomas Monjalon, Chengwen Feng, Kevin Laatz, Bruce Richardson

No need to copy values in intermediate variables.
Use the right trace point emitters.
Update the pcie struct to avoid aliasing warning.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since v3:
- added anonymous union around pcie struct (which triggered an abidiff
  warning that needs waiving) and kept original call to
  rte_trace_point_emit_u64,

Changes since v2:
- split this change into multiple changes,
  only kept trivial parts in this patch,

---
 devtools/libabigail.abignore  |  5 +++++
 lib/dmadev/rte_dmadev.h       | 29 ++++++++++++++++-------------
 lib/dmadev/rte_dmadev_trace.h | 20 ++++++--------------
 3 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/devtools/libabigail.abignore b/devtools/libabigail.abignore
index ce501632b3..88aa1ec981 100644
--- a/devtools/libabigail.abignore
+++ b/devtools/libabigail.abignore
@@ -36,3 +36,8 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Temporary exceptions till next major ABI version ;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+[suppress_type]
+        name = rte_dma_port_param
+        type_kind = struct
+        has_size_change = no
+        has_data_member = {pcie}
diff --git a/lib/dmadev/rte_dmadev.h b/lib/dmadev/rte_dmadev.h
index 2f9304a9db..26f9d4b095 100644
--- a/lib/dmadev/rte_dmadev.h
+++ b/lib/dmadev/rte_dmadev.h
@@ -523,19 +523,22 @@ struct rte_dma_port_param {
 		 * and capabilities.
 		 */
 		__extension__
-		struct {
-			uint64_t coreid : 4; /**< PCIe core id used. */
-			uint64_t pfid : 8; /**< PF id used. */
-			uint64_t vfen : 1; /**< VF enable bit. */
-			uint64_t vfid : 16; /**< VF id used. */
-			/** The pasid filed in TLP packet. */
-			uint64_t pasid : 20;
-			/** The attributes filed in TLP packet. */
-			uint64_t attr : 3;
-			/** The processing hint filed in TLP packet. */
-			uint64_t ph : 2;
-			/** The steering tag filed in TLP packet. */
-			uint64_t st : 16;
+		union {
+			struct {
+				uint64_t coreid : 4; /**< PCIe core id used. */
+				uint64_t pfid : 8; /**< PF id used. */
+				uint64_t vfen : 1; /**< VF enable bit. */
+				uint64_t vfid : 16; /**< VF id used. */
+				/** The pasid filed in TLP packet. */
+				uint64_t pasid : 20;
+				/** The attributes filed in TLP packet. */
+				uint64_t attr : 3;
+				/** The processing hint filed in TLP packet. */
+				uint64_t ph : 2;
+				/** The steering tag filed in TLP packet. */
+				uint64_t st : 16;
+			};
+			uint64_t val;
 		} pcie;
 	};
 	uint64_t reserved[2]; /**< Reserved for future fields. */
diff --git a/lib/dmadev/rte_dmadev_trace.h b/lib/dmadev/rte_dmadev_trace.h
index be089c065c..1beb938168 100644
--- a/lib/dmadev/rte_dmadev_trace.h
+++ b/lib/dmadev/rte_dmadev_trace.h
@@ -46,11 +46,10 @@ RTE_TRACE_POINT(
 	const struct rte_dma_conf __dev_conf = {0};
 	dev_conf = &__dev_conf;
 #endif /* _RTE_TRACE_POINT_REGISTER_H_ */
-	int enable_silent = (int)dev_conf->enable_silent;
 	rte_trace_point_emit_i16(dev_id);
 	rte_trace_point_emit_u16(dev_conf->nb_vchans);
 	rte_trace_point_emit_u16(dev_conf->priority);
-	rte_trace_point_emit_int(enable_silent);
+	rte_trace_point_emit_u8(dev_conf->enable_silent);
 	rte_trace_point_emit_int(ret);
 )
 
@@ -83,21 +82,14 @@ RTE_TRACE_POINT(
 	const struct rte_dma_vchan_conf __conf = {0};
 	conf = &__conf;
 #endif /* _RTE_TRACE_POINT_REGISTER_H_ */
-	int src_port_type = conf->src_port.port_type;
-	int dst_port_type = conf->dst_port.port_type;
-	int direction = conf->direction;
-	uint64_t src_pcie_cfg;
-	uint64_t dst_pcie_cfg;
 	rte_trace_point_emit_i16(dev_id);
 	rte_trace_point_emit_u16(vchan);
-	rte_trace_point_emit_int(direction);
+	rte_trace_point_emit_int(conf->direction);
 	rte_trace_point_emit_u16(conf->nb_desc);
-	rte_trace_point_emit_int(src_port_type);
-	memcpy(&src_pcie_cfg, &conf->src_port.pcie, sizeof(uint64_t));
-	rte_trace_point_emit_u64(src_pcie_cfg);
-	memcpy(&dst_pcie_cfg, &conf->dst_port.pcie, sizeof(uint64_t));
-	rte_trace_point_emit_int(dst_port_type);
-	rte_trace_point_emit_u64(dst_pcie_cfg);
+	rte_trace_point_emit_int(conf->src_port.port_type);
+	rte_trace_point_emit_u64(conf->src_port.pcie.val);
+	rte_trace_point_emit_int(conf->dst_port.port_type);
+	rte_trace_point_emit_u64(conf->dst_port.pcie.val);
 	rte_trace_point_emit_ptr(conf->auto_free.m2d.pool);
 	rte_trace_point_emit_int(ret);
 )
-- 
2.48.1


^ permalink raw reply	[relevance 4%]

* [v6 4/6] crypto/virtio: add vDPA backend
  @ 2025-03-05  6:16  1% ` Gowrishankar Muthukrishnan
  0 siblings, 0 replies; 153+ results
From: Gowrishankar Muthukrishnan @ 2025-03-05  6:16 UTC (permalink / raw)
  To: dev, Jay Zhou; +Cc: anoobj, Akhil Goyal, Gowrishankar Muthukrishnan

Add vDPA backend to virtio_user crypto.

Signed-off-by: Gowrishankar Muthukrishnan <gmuthukrishn@marvell.com>
---
 drivers/crypto/virtio/meson.build             |   7 +
 drivers/crypto/virtio/virtio_cryptodev.c      |  57 +-
 drivers/crypto/virtio/virtio_cryptodev.h      |   3 +
 drivers/crypto/virtio/virtio_logs.h           |   6 +-
 drivers/crypto/virtio/virtio_pci.h            |   7 +
 drivers/crypto/virtio/virtio_ring.h           |   6 -
 drivers/crypto/virtio/virtio_user/vhost.h     |  90 +++
 .../crypto/virtio/virtio_user/vhost_vdpa.c    | 710 +++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.c      | 749 ++++++++++++++++++
 .../virtio/virtio_user/virtio_user_dev.h      |  85 ++
 drivers/crypto/virtio/virtio_user_cryptodev.c | 575 ++++++++++++++
 11 files changed, 2265 insertions(+), 30 deletions(-)
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost.h
 create mode 100644 drivers/crypto/virtio/virtio_user/vhost_vdpa.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.c
 create mode 100644 drivers/crypto/virtio/virtio_user/virtio_user_dev.h
 create mode 100644 drivers/crypto/virtio/virtio_user_cryptodev.c

diff --git a/drivers/crypto/virtio/meson.build b/drivers/crypto/virtio/meson.build
index d2c3b3ad07..3763e86746 100644
--- a/drivers/crypto/virtio/meson.build
+++ b/drivers/crypto/virtio/meson.build
@@ -16,3 +16,10 @@ sources = files(
         'virtio_rxtx.c',
         'virtqueue.c',
 )
+
+if is_linux
+    sources += files('virtio_user_cryptodev.c',
+        'virtio_user/vhost_vdpa.c',
+        'virtio_user/virtio_user_dev.c')
+    deps += ['bus_vdev']
+endif
diff --git a/drivers/crypto/virtio/virtio_cryptodev.c b/drivers/crypto/virtio/virtio_cryptodev.c
index 92fea557ab..bc737f1e68 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.c
+++ b/drivers/crypto/virtio/virtio_cryptodev.c
@@ -544,24 +544,12 @@ virtio_crypto_init_device(struct rte_cryptodev *cryptodev,
 	return 0;
 }
 
-/*
- * This function is based on probe() function
- * It returns 0 on success.
- */
-static int
-crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
-		struct rte_cryptodev_pmd_init_params *init_params)
+int
+crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev)
 {
-	struct rte_cryptodev *cryptodev;
 	struct virtio_crypto_hw *hw;
 
-	PMD_INIT_FUNC_TRACE();
-
-	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
-					init_params);
-	if (cryptodev == NULL)
-		return -ENODEV;
-
 	cryptodev->driver_id = cryptodev_virtio_driver_id;
 	cryptodev->dev_ops = &virtio_crypto_dev_ops;
 
@@ -578,16 +566,41 @@ crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
 	hw->dev_id = cryptodev->data->dev_id;
 	hw->virtio_dev_capabilities = virtio_capabilities;
 
-	VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
-		cryptodev->data->dev_id, pci_dev->id.vendor_id,
-		pci_dev->id.device_id);
+	if (pci_dev) {
+		/* pci device init */
+		VIRTIO_CRYPTO_INIT_LOG_DBG("dev %d vendorID=0x%x deviceID=0x%x",
+			cryptodev->data->dev_id, pci_dev->id.vendor_id,
+			pci_dev->id.device_id);
 
-	/* pci device init */
-	if (vtpci_cryptodev_init(pci_dev, hw))
+		if (vtpci_cryptodev_init(pci_dev, hw))
+			return -1;
+	}
+
+	if (virtio_crypto_init_device(cryptodev, features) < 0)
 		return -1;
 
-	if (virtio_crypto_init_device(cryptodev,
-			VIRTIO_CRYPTO_PMD_GUEST_FEATURES) < 0)
+	return 0;
+}
+
+/*
+ * This function is based on probe() function
+ * It returns 0 on success.
+ */
+static int
+crypto_virtio_create(const char *name, struct rte_pci_device *pci_dev,
+		struct rte_cryptodev_pmd_init_params *init_params)
+{
+	struct rte_cryptodev *cryptodev;
+
+	PMD_INIT_FUNC_TRACE();
+
+	cryptodev = rte_cryptodev_pmd_create(name, &pci_dev->device,
+					init_params);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_CRYPTO_PMD_GUEST_FEATURES,
+			pci_dev) < 0)
 		return -1;
 
 	rte_cryptodev_pmd_probing_finish(cryptodev);
diff --git a/drivers/crypto/virtio/virtio_cryptodev.h b/drivers/crypto/virtio/virtio_cryptodev.h
index f8498246e2..fad73d54a8 100644
--- a/drivers/crypto/virtio/virtio_cryptodev.h
+++ b/drivers/crypto/virtio/virtio_cryptodev.h
@@ -76,4 +76,7 @@ uint16_t virtio_crypto_pkt_rx_burst(void *tx_queue,
 		struct rte_crypto_op **tx_pkts,
 		uint16_t nb_pkts);
 
+int crypto_virtio_dev_init(struct rte_cryptodev *cryptodev, uint64_t features,
+		struct rte_pci_device *pci_dev);
+
 #endif /* _VIRTIO_CRYPTODEV_H_ */
diff --git a/drivers/crypto/virtio/virtio_logs.h b/drivers/crypto/virtio/virtio_logs.h
index 988514919f..1cc51f7990 100644
--- a/drivers/crypto/virtio/virtio_logs.h
+++ b/drivers/crypto/virtio/virtio_logs.h
@@ -15,8 +15,10 @@ extern int virtio_crypto_logtype_init;
 
 #define PMD_INIT_FUNC_TRACE() PMD_INIT_LOG(DEBUG, " >>")
 
-extern int virtio_crypto_logtype_init;
-#define RTE_LOGTYPE_VIRTIO_CRYPTO_INIT virtio_crypto_logtype_init
+extern int virtio_crypto_logtype_driver;
+#define RTE_LOGTYPE_VIRTIO_CRYPTO_DRIVER virtio_crypto_logtype_driver
+#define PMD_DRV_LOG(level, ...) \
+	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_DRIVER, "%s(): ", __func__, __VA_ARGS__)
 
 #define VIRTIO_CRYPTO_INIT_LOG_IMPL(level, ...) \
 	RTE_LOG_LINE_PREFIX(level, VIRTIO_CRYPTO_INIT, "%s(): ", __func__, __VA_ARGS__)
diff --git a/drivers/crypto/virtio/virtio_pci.h b/drivers/crypto/virtio/virtio_pci.h
index 79945cb88e..c75777e005 100644
--- a/drivers/crypto/virtio/virtio_pci.h
+++ b/drivers/crypto/virtio/virtio_pci.h
@@ -20,6 +20,9 @@ struct virtqueue;
 #define VIRTIO_CRYPTO_PCI_VENDORID 0x1AF4
 #define VIRTIO_CRYPTO_PCI_DEVICEID 0x1054
 
+/* VirtIO device IDs. */
+#define VIRTIO_ID_CRYPTO  20
+
 /* VirtIO ABI version, this must match exactly. */
 #define VIRTIO_PCI_ABI_VERSION 0
 
@@ -56,8 +59,12 @@ struct virtqueue;
 #define VIRTIO_CONFIG_STATUS_DRIVER    0x02
 #define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
 #define VIRTIO_CONFIG_STATUS_FEATURES_OK 0x08
+#define VIRTIO_CONFIG_STATUS_DEV_NEED_RESET	0x40
 #define VIRTIO_CONFIG_STATUS_FAILED    0x80
 
+/* The alignment to use between consumer and producer parts of vring. */
+#define VIRTIO_VRING_ALIGN 4096
+
 /*
  * Each virtqueue indirect descriptor list must be physically contiguous.
  * To allow us to malloc(9) each list individually, limit the number
diff --git a/drivers/crypto/virtio/virtio_ring.h b/drivers/crypto/virtio/virtio_ring.h
index c74d1172b7..4b418f6e60 100644
--- a/drivers/crypto/virtio/virtio_ring.h
+++ b/drivers/crypto/virtio/virtio_ring.h
@@ -181,12 +181,6 @@ vring_init_packed(struct vring_packed *vr, uint8_t *p, rte_iova_t iova,
 				sizeof(struct vring_packed_desc_event)), align);
 }
 
-static inline void
-vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align)
-{
-	vring_init_split(vr, p, 0, align, num);
-}
-
 /*
  * The following is used with VIRTIO_RING_F_EVENT_IDX.
  * Assuming a given event_idx value from the other size, if we have
diff --git a/drivers/crypto/virtio/virtio_user/vhost.h b/drivers/crypto/virtio/virtio_user/vhost.h
new file mode 100644
index 0000000000..29cc1a14d4
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#ifndef _VIRTIO_USER_VHOST_H
+#define _VIRTIO_USER_VHOST_H
+
+#include <stdint.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <rte_errno.h>
+
+#include "../virtio_logs.h"
+
+struct vhost_vring_state {
+	unsigned int index;
+	unsigned int num;
+};
+
+struct vhost_vring_file {
+	unsigned int index;
+	int fd;
+};
+
+struct vhost_vring_addr {
+	unsigned int index;
+	/* Option flags. */
+	unsigned int flags;
+	/* Flag values: */
+	/* Whether log address is valid. If set enables logging. */
+#define VHOST_VRING_F_LOG 0
+
+	/* Start of array of descriptors (virtually contiguous) */
+	uint64_t desc_user_addr;
+	/* Used structure address. Must be 32 bit aligned */
+	uint64_t used_user_addr;
+	/* Available structure address. Must be 16 bit aligned */
+	uint64_t avail_user_addr;
+	/* Logging support. */
+	/* Log writes to used structure, at offset calculated from specified
+	 * address. Address must be 32 bit aligned.
+	 */
+	uint64_t log_guest_addr;
+};
+
+#ifndef VHOST_BACKEND_F_IOTLB_MSG_V2
+#define VHOST_BACKEND_F_IOTLB_MSG_V2 1
+#endif
+
+#ifndef VHOST_BACKEND_F_IOTLB_BATCH
+#define VHOST_BACKEND_F_IOTLB_BATCH 2
+#endif
+
+struct virtio_user_dev;
+
+struct virtio_user_backend_ops {
+	int (*setup)(struct virtio_user_dev *dev);
+	int (*destroy)(struct virtio_user_dev *dev);
+	int (*get_backend_features)(uint64_t *features);
+	int (*set_owner)(struct virtio_user_dev *dev);
+	int (*get_features)(struct virtio_user_dev *dev, uint64_t *features);
+	int (*set_features)(struct virtio_user_dev *dev, uint64_t features);
+	int (*set_memory_table)(struct virtio_user_dev *dev);
+	int (*set_vring_num)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*get_vring_base)(struct virtio_user_dev *dev, struct vhost_vring_state *state);
+	int (*set_vring_call)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_kick)(struct virtio_user_dev *dev, struct vhost_vring_file *file);
+	int (*set_vring_addr)(struct virtio_user_dev *dev, struct vhost_vring_addr *addr);
+	int (*get_status)(struct virtio_user_dev *dev, uint8_t *status);
+	int (*set_status)(struct virtio_user_dev *dev, uint8_t status);
+	int (*get_config)(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len);
+	int (*set_config)(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off,
+			uint32_t len);
+	int (*cvq_enable)(struct virtio_user_dev *dev, int enable);
+	int (*enable_qp)(struct virtio_user_dev *dev, uint16_t pair_idx, int enable);
+	int (*dma_map)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*dma_unmap)(struct virtio_user_dev *dev, void *addr, uint64_t iova, size_t len);
+	int (*update_link_state)(struct virtio_user_dev *dev);
+	int (*server_disconnect)(struct virtio_user_dev *dev);
+	int (*server_reconnect)(struct virtio_user_dev *dev);
+	int (*get_intr_fd)(struct virtio_user_dev *dev);
+	int (*map_notification_area)(struct virtio_user_dev *dev);
+	int (*unmap_notification_area)(struct virtio_user_dev *dev);
+};
+
+extern struct virtio_user_backend_ops virtio_ops_vdpa;
+
+#endif
diff --git a/drivers/crypto/virtio/virtio_user/vhost_vdpa.c b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
new file mode 100644
index 0000000000..b5839875e6
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/vhost_vdpa.c
@@ -0,0 +1,710 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rte_memory.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+#include "../virtio_pci.h"
+
+struct vhost_vdpa_data {
+	int vhostfd;
+	uint64_t protocol_features;
+};
+
+#define VHOST_VDPA_SUPPORTED_BACKEND_FEATURES		\
+	(1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2	|	\
+	1ULL << VHOST_BACKEND_F_IOTLB_BATCH)
+
+/* vhost kernel & vdpa ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
+#define VHOST_VDPA_GET_DEVICE_ID _IOR(VHOST_VIRTIO, 0x70, __u32)
+#define VHOST_VDPA_GET_STATUS _IOR(VHOST_VIRTIO, 0x71, __u8)
+#define VHOST_VDPA_SET_STATUS _IOW(VHOST_VIRTIO, 0x72, __u8)
+#define VHOST_VDPA_GET_CONFIG _IOR(VHOST_VIRTIO, 0x73, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_CONFIG _IOW(VHOST_VIRTIO, 0x74, struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_VRING_ENABLE _IOW(VHOST_VIRTIO, 0x75, struct vhost_vring_state)
+#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64)
+#define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64)
+
+/* no alignment requirement */
+struct vhost_iotlb_msg {
+	uint64_t iova;
+	uint64_t size;
+	uint64_t uaddr;
+#define VHOST_ACCESS_RO      0x1
+#define VHOST_ACCESS_WO      0x2
+#define VHOST_ACCESS_RW      0x3
+	uint8_t perm;
+#define VHOST_IOTLB_MISS           1
+#define VHOST_IOTLB_UPDATE         2
+#define VHOST_IOTLB_INVALIDATE     3
+#define VHOST_IOTLB_ACCESS_FAIL    4
+#define VHOST_IOTLB_BATCH_BEGIN    5
+#define VHOST_IOTLB_BATCH_END      6
+	uint8_t type;
+};
+
+#define VHOST_IOTLB_MSG_V2 0x2
+
+struct vhost_vdpa_config {
+	uint32_t off;
+	uint32_t len;
+	uint8_t buf[];
+};
+
+struct vhost_msg {
+	uint32_t type;
+	uint32_t reserved;
+	union {
+		struct vhost_iotlb_msg iotlb;
+		uint8_t padding[64];
+	};
+};
+
+static int
+vhost_vdpa_ioctl(int fd, uint64_t request, void *arg)
+{
+	int ret;
+
+	ret = ioctl(fd, request, arg);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Vhost-vDPA ioctl %"PRIu64" failed (%s)",
+				request, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_owner(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_OWNER, NULL);
+}
+
+static int
+vhost_vdpa_get_protocol_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_BACKEND_FEATURES, features);
+}
+
+static int
+vhost_vdpa_set_protocol_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_BACKEND_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_get_features(struct virtio_user_dev *dev, uint64_t *features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int ret;
+
+	ret = vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_FEATURES, features);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get features");
+		return -1;
+	}
+
+	/* Negotiated vDPA backend features */
+	ret = vhost_vdpa_get_protocol_features(dev, &data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to get backend features");
+		return -1;
+	}
+
+	data->protocol_features &= VHOST_VDPA_SUPPORTED_BACKEND_FEATURES;
+
+	ret = vhost_vdpa_set_protocol_features(dev, data->protocol_features);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to set backend features");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_set_features(struct virtio_user_dev *dev, uint64_t features)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	/* WORKAROUND */
+	features |= 1ULL << VIRTIO_F_IOMMU_PLATFORM;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_FEATURES, &features);
+}
+
+static int
+vhost_vdpa_iotlb_batch_begin(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_BEGIN;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch begin (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_iotlb_batch_end(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)))
+		return 0;
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_BATCH_END;
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB batch end (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_UPDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.uaddr = (uint64_t)(uintptr_t)addr;
+	msg.iotlb.size = len;
+	msg.iotlb.perm = VHOST_ACCESS_RW;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", addr: %p, len: 0x%zx",
+			__func__, iova, addr, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB update (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_unmap(struct virtio_user_dev *dev, __rte_unused void *addr,
+				  uint64_t iova, size_t len)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	struct vhost_msg msg = {};
+
+	if (!(data->protocol_features & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2))) {
+		PMD_DRV_LOG(ERR, "IOTLB_MSG_V2 not supported by the backend.");
+		return -1;
+	}
+
+	msg.type = VHOST_IOTLB_MSG_V2;
+	msg.iotlb.type = VHOST_IOTLB_INVALIDATE;
+	msg.iotlb.iova = iova;
+	msg.iotlb.size = len;
+
+	PMD_DRV_LOG(DEBUG, "%s: iova: 0x%" PRIx64 ", len: 0x%zx",
+			__func__, iova, len);
+
+	if (write(data->vhostfd, &msg, sizeof(msg)) != sizeof(msg)) {
+		PMD_DRV_LOG(ERR, "Failed to send IOTLB invalidate (%s)",
+				strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+vhost_vdpa_dma_map_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_map(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_dma_unmap_batch(struct virtio_user_dev *dev, void *addr,
+				  uint64_t iova, size_t len)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	ret = vhost_vdpa_dma_unmap(dev, addr, iova, len);
+
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_map_contig(const struct rte_memseg_list *msl,
+		const struct rte_memseg *ms, size_t len, void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	if (msl->external)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, len);
+}
+
+static int
+vhost_vdpa_map(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
+		void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+
+	/* skip external memory that isn't a heap */
+	if (msl->external && !msl->heap)
+		return 0;
+
+	/* skip any segments with invalid IOVA addresses */
+	if (ms->iova == RTE_BAD_IOVA)
+		return 0;
+
+	/* if IOVA mode is VA, we've already mapped the internal segments */
+	if (!msl->external && rte_eal_iova_mode() == RTE_IOVA_VA)
+		return 0;
+
+	return vhost_vdpa_dma_map(dev, ms->addr, ms->iova, ms->len);
+}
+
+static int
+vhost_vdpa_set_memory_table(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	if (vhost_vdpa_iotlb_batch_begin(dev) < 0)
+		return -1;
+
+	vhost_vdpa_dma_unmap(dev, NULL, 0, SIZE_MAX);
+
+	if (rte_eal_iova_mode() == RTE_IOVA_VA) {
+		/* with IOVA as VA mode, we can get away with mapping contiguous
+		 * chunks rather than going page-by-page.
+		 */
+		ret = rte_memseg_contig_walk_thread_unsafe(
+				vhost_vdpa_map_contig, dev);
+		if (ret)
+			goto batch_end;
+		/* we have to continue the walk because we've skipped the
+		 * external segments during the config walk.
+		 */
+	}
+	ret = rte_memseg_walk_thread_unsafe(vhost_vdpa_map, dev);
+
+batch_end:
+	if (vhost_vdpa_iotlb_batch_end(dev) < 0)
+		return -1;
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_vring_enable(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_VRING_ENABLE, state);
+}
+
+static int
+vhost_vdpa_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_NUM, state);
+}
+
+static int
+vhost_vdpa_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_GET_VRING_BASE, state);
+}
+
+static int
+vhost_vdpa_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_CALL, file);
+}
+
+static int
+vhost_vdpa_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_KICK, file);
+}
+
+static int
+vhost_vdpa_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_SET_VRING_ADDR, addr);
+}
+
+static int
+vhost_vdpa_get_status(struct virtio_user_dev *dev, uint8_t *status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_GET_STATUS, status);
+}
+
+static int
+vhost_vdpa_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	return vhost_vdpa_ioctl(data->vhostfd, VHOST_VDPA_SET_STATUS, &status);
+}
+
+static int
+vhost_vdpa_get_config(struct virtio_user_dev *dev, uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_GET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to get vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+		goto out;
+	}
+
+	memcpy(data, config->buf, len);
+out:
+	free(config);
+
+	return ret;
+}
+
+static int
+vhost_vdpa_set_config(struct virtio_user_dev *dev, const uint8_t *data, uint32_t off, uint32_t len)
+{
+	struct vhost_vdpa_data *vdpa_data = dev->backend_data;
+	struct vhost_vdpa_config *config;
+	int ret = 0;
+
+	config = malloc(sizeof(*config) + len);
+	if (!config) {
+		PMD_DRV_LOG(ERR, "Failed to allocate vDPA config data");
+		return -1;
+	}
+
+	config->off = off;
+	config->len = len;
+
+	memcpy(config->buf, data, len);
+
+	ret = vhost_vdpa_ioctl(vdpa_data->vhostfd, VHOST_VDPA_SET_CONFIG, config);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to set vDPA config (offset 0x%x, len 0x%x)", off, len);
+		ret = -1;
+	}
+
+	free(config);
+
+	return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost vdpa backend.
+ *
+ * @return
+ *   - (-1) if fail to set up;
+ *   - (>=0) if successful.
+ */
+static int
+vhost_vdpa_setup(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data;
+	uint32_t did = (uint32_t)-1;
+
+	data = malloc(sizeof(*data));
+	if (!data) {
+		PMD_DRV_LOG(ERR, "(%s) Faidle to allocate backend data", dev->path);
+		return -1;
+	}
+
+	data->vhostfd = open(dev->path, O_RDWR);
+	if (data->vhostfd < 0) {
+		PMD_DRV_LOG(ERR, "Failed to open %s: %s",
+				dev->path, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	if (ioctl(data->vhostfd, VHOST_VDPA_GET_DEVICE_ID, &did) < 0 ||
+			did != VIRTIO_ID_CRYPTO) {
+		PMD_DRV_LOG(ERR, "Invalid vdpa device ID: %u", did);
+		close(data->vhostfd);
+		free(data);
+		return -1;
+	}
+
+	dev->backend_data = data;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_destroy(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+
+	if (!data)
+		return 0;
+
+	close(data->vhostfd);
+
+	free(data);
+	dev->backend_data = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_cvq_enable(struct virtio_user_dev *dev, int enable)
+{
+	struct vhost_vring_state state = {
+		.index = dev->max_queue_pairs,
+		.num   = enable,
+	};
+
+	return vhost_vdpa_set_vring_enable(dev, &state);
+}
+
+static int
+vhost_vdpa_enable_queue_pair(struct virtio_user_dev *dev,
+				uint16_t pair_idx,
+				int enable)
+{
+	struct vhost_vring_state state = {
+		.index = pair_idx,
+		.num   = enable,
+	};
+
+	if (dev->qp_enabled[pair_idx] == enable)
+		return 0;
+
+	if (vhost_vdpa_set_vring_enable(dev, &state))
+		return -1;
+
+	dev->qp_enabled[pair_idx] = enable;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_backend_features(uint64_t *features)
+{
+	*features = 0;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_update_link_state(struct virtio_user_dev *dev)
+{
+	/* TODO: It is W/A until a cleaner approach to find cpt status */
+	dev->crypto_status = VIRTIO_CRYPTO_S_HW_READY;
+	return 0;
+}
+
+static int
+vhost_vdpa_get_intr_fd(struct virtio_user_dev *dev __rte_unused)
+{
+	/* No link state interrupt with Vhost-vDPA */
+	return -1;
+}
+
+static int
+vhost_vdpa_get_nr_vrings(struct virtio_user_dev *dev)
+{
+	int nr_vrings = dev->max_queue_pairs;
+
+	return nr_vrings;
+}
+
+static int
+vhost_vdpa_unmap_notification_area(struct virtio_user_dev *dev)
+{
+	int i, nr_vrings;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	for (i = 0; i < nr_vrings; i++) {
+		if (dev->notify_area[i])
+			munmap(dev->notify_area[i], getpagesize());
+	}
+	free(dev->notify_area);
+	dev->notify_area = NULL;
+
+	return 0;
+}
+
+static int
+vhost_vdpa_map_notification_area(struct virtio_user_dev *dev)
+{
+	struct vhost_vdpa_data *data = dev->backend_data;
+	int nr_vrings, i, page_size = getpagesize();
+	uint16_t **notify_area;
+
+	nr_vrings = vhost_vdpa_get_nr_vrings(dev);
+
+	/* CQ is another vring */
+	nr_vrings++;
+
+	notify_area = malloc(nr_vrings * sizeof(*notify_area));
+	if (!notify_area) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to allocate notify area array", dev->path);
+		return -1;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		notify_area[i] = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED | MAP_FILE,
+					data->vhostfd, i * page_size);
+		if (notify_area[i] == MAP_FAILED) {
+			PMD_DRV_LOG(ERR, "(%s) Map failed for notify address of queue %d",
+					dev->path, i);
+			i--;
+			goto map_err;
+		}
+	}
+	dev->notify_area = notify_area;
+
+	return 0;
+
+map_err:
+	for (; i >= 0; i--)
+		munmap(notify_area[i], page_size);
+	free(notify_area);
+
+	return -1;
+}
+
+struct virtio_user_backend_ops virtio_crypto_ops_vdpa = {
+	.setup = vhost_vdpa_setup,
+	.destroy = vhost_vdpa_destroy,
+	.get_backend_features = vhost_vdpa_get_backend_features,
+	.set_owner = vhost_vdpa_set_owner,
+	.get_features = vhost_vdpa_get_features,
+	.set_features = vhost_vdpa_set_features,
+	.set_memory_table = vhost_vdpa_set_memory_table,
+	.set_vring_num = vhost_vdpa_set_vring_num,
+	.set_vring_base = vhost_vdpa_set_vring_base,
+	.get_vring_base = vhost_vdpa_get_vring_base,
+	.set_vring_call = vhost_vdpa_set_vring_call,
+	.set_vring_kick = vhost_vdpa_set_vring_kick,
+	.set_vring_addr = vhost_vdpa_set_vring_addr,
+	.get_status = vhost_vdpa_get_status,
+	.set_status = vhost_vdpa_set_status,
+	.get_config = vhost_vdpa_get_config,
+	.set_config = vhost_vdpa_set_config,
+	.cvq_enable = vhost_vdpa_cvq_enable,
+	.enable_qp = vhost_vdpa_enable_queue_pair,
+	.dma_map = vhost_vdpa_dma_map_batch,
+	.dma_unmap = vhost_vdpa_dma_unmap_batch,
+	.update_link_state = vhost_vdpa_update_link_state,
+	.get_intr_fd = vhost_vdpa_get_intr_fd,
+	.map_notification_area = vhost_vdpa_map_notification_area,
+	.unmap_notification_area = vhost_vdpa_unmap_notification_area,
+};
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.c b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
new file mode 100644
index 0000000000..c8478d72ce
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.c
@@ -0,0 +1,749 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <rte_alarm.h>
+#include <rte_string_fns.h>
+#include <rte_eal_memconfig.h>
+#include <rte_malloc.h>
+#include <rte_io.h>
+
+#include "vhost.h"
+#include "virtio_logs.h"
+#include "cryptodev_pmd.h"
+#include "virtio_crypto.h"
+#include "virtio_cvq.h"
+#include "virtio_user_dev.h"
+#include "virtqueue.h"
+
+#define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
+
+const char * const crypto_virtio_user_backend_strings[] = {
+	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
+	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
+};
+
+static int
+virtio_user_uninit_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	if (dev->kickfds[queue_sel] >= 0) {
+		close(dev->kickfds[queue_sel]);
+		dev->kickfds[queue_sel] = -1;
+	}
+
+	if (dev->callfds[queue_sel] >= 0) {
+		close(dev->callfds[queue_sel]);
+		dev->callfds[queue_sel] = -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_init_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* May use invalid flag, but some backend uses kickfd and
+	 * callfd as criteria to judge if dev is alive. so finally we
+	 * use real event_fd.
+	 */
+	dev->callfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->callfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup callfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+	dev->kickfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (dev->kickfds[queue_sel] < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to setup kickfd for queue %u: %s",
+				dev->path, queue_sel, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_destroy_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	struct vhost_vring_state state;
+	int ret;
+
+	state.index = queue_sel;
+	ret = dev->ops->get_vring_base(dev, &state);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to destroy queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
+	 * firstly because vhost depends on this msg to allocate virtqueue
+	 * pair.
+	 */
+	struct vhost_vring_file file;
+	int ret;
+
+	file.index = queue_sel;
+	file.fd = dev->callfds[queue_sel];
+	ret = dev->ops->set_vring_call(dev, &file);
+	if (ret < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
+{
+	int ret;
+	struct vhost_vring_file file;
+	struct vhost_vring_state state;
+	struct vring *vring = &dev->vrings.split[queue_sel];
+	struct vring_packed *pq_vring = &dev->vrings.packed[queue_sel];
+	uint64_t desc_addr, avail_addr, used_addr;
+	struct vhost_vring_addr addr = {
+		.index = queue_sel,
+		.log_guest_addr = 0,
+		.flags = 0, /* disable log */
+	};
+
+	if (queue_sel == dev->max_queue_pairs) {
+		if (!dev->scvq) {
+			PMD_INIT_LOG(ERR, "(%s) Shadow control queue expected but missing",
+					dev->path);
+			goto err;
+		}
+
+		/* Use shadow control queue information */
+		vring = &dev->scvq->vq_split.ring;
+		pq_vring = &dev->scvq->vq_packed.ring;
+	}
+
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
+		desc_addr = pq_vring->desc_iova;
+		avail_addr = desc_addr + pq_vring->num * sizeof(struct vring_packed_desc);
+		used_addr =  RTE_ALIGN_CEIL(avail_addr + sizeof(struct vring_packed_desc_event),
+						VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	} else {
+		desc_addr = vring->desc_iova;
+		avail_addr = desc_addr + vring->num * sizeof(struct vring_desc);
+		used_addr = RTE_ALIGN_CEIL((uintptr_t)(&vring->avail->ring[vring->num]),
+					VIRTIO_VRING_ALIGN);
+
+		addr.desc_user_addr = desc_addr;
+		addr.avail_user_addr = avail_addr;
+		addr.used_user_addr = used_addr;
+	}
+
+	state.index = queue_sel;
+	state.num = vring->num;
+	ret = dev->ops->set_vring_num(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	state.index = queue_sel;
+	state.num = 0; /* no reservation */
+	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
+		state.num |= (1 << 15);
+	ret = dev->ops->set_vring_base(dev, &state);
+	if (ret < 0)
+		goto err;
+
+	ret = dev->ops->set_vring_addr(dev, &addr);
+	if (ret < 0)
+		goto err;
+
+	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
+	 * lastly because vhost depends on this msg to judge if
+	 * virtio is ready.
+	 */
+	file.index = queue_sel;
+	file.fd = dev->kickfds[queue_sel];
+	ret = dev->ops->set_vring_kick(dev, &file);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+err:
+	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel);
+
+	return -1;
+}
+
+static int
+virtio_user_foreach_queue(struct virtio_user_dev *dev,
+			int (*fn)(struct virtio_user_dev *, uint32_t))
+{
+	uint32_t i, nr_vq;
+
+	nr_vq = dev->max_queue_pairs;
+
+	for (i = 0; i < nr_vq; i++)
+		if (fn(dev, i) < 0)
+			return -1;
+
+	return 0;
+}
+
+int
+crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev)
+{
+	uint64_t features;
+	int ret = -1;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 0: tell vhost to create queues */
+	if (virtio_user_foreach_queue(dev, virtio_user_create_queue) < 0)
+		goto error;
+
+	features = dev->features;
+
+	ret = dev->ops->set_features(dev, features);
+	if (ret < 0)
+		goto error;
+	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
+error:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return ret;
+}
+
+int
+crypto_virtio_user_start_device(struct virtio_user_dev *dev)
+{
+	int ret;
+
+	/*
+	 * XXX workaround!
+	 *
+	 * We need to make sure that the locks will be
+	 * taken in the correct order to avoid deadlocks.
+	 *
+	 * Before releasing this lock, this thread should
+	 * not trigger any memory hotplug events.
+	 *
+	 * This is a temporary workaround, and should be
+	 * replaced when we get proper supports from the
+	 * memory subsystem in the future.
+	 */
+	rte_mcfg_mem_read_lock();
+	pthread_mutex_lock(&dev->mutex);
+
+	/* Step 2: share memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Step 3: kick queues */
+	ret = virtio_user_foreach_queue(dev, virtio_user_kick_queue);
+	if (ret < 0)
+		goto error;
+
+	ret = virtio_user_kick_queue(dev, dev->max_queue_pairs);
+	if (ret < 0)
+		goto error;
+
+	/* Step 4: enable queues */
+	for (int i = 0; i < dev->max_queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto error;
+	}
+
+	dev->started = true;
+
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	return 0;
+error:
+	pthread_mutex_unlock(&dev->mutex);
+	rte_mcfg_mem_read_unlock();
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path);
+
+	/* TODO: free resource here or caller to check */
+	return -1;
+}
+
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev)
+{
+	uint32_t i;
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	if (!dev->started)
+		goto out;
+
+	for (i = 0; i < dev->max_queue_pairs; ++i) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	if (dev->scvq) {
+		ret = dev->ops->cvq_enable(dev, 0);
+		if (ret < 0)
+			goto err;
+	}
+
+	/* Stop the backend. */
+	if (virtio_user_foreach_queue(dev, virtio_user_destroy_queue) < 0)
+		goto err;
+
+	dev->started = false;
+
+out:
+	pthread_mutex_unlock(&dev->mutex);
+
+	return 0;
+err:
+	pthread_mutex_unlock(&dev->mutex);
+
+	PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path);
+
+	return -1;
+}
+
+static int
+virtio_user_dev_init_max_queue_pairs(struct virtio_user_dev *dev, uint32_t user_max_qp)
+{
+	int ret;
+
+	if (!dev->ops->get_config) {
+		dev->max_queue_pairs = user_max_qp;
+		return 0;
+	}
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&dev->max_queue_pairs,
+			offsetof(struct virtio_crypto_config, max_dataqueues),
+			sizeof(uint16_t));
+	if (ret) {
+		/*
+		 * We need to know the max queue pair from the device so that
+		 * the control queue gets the right index.
+		 */
+		dev->max_queue_pairs = 1;
+		PMD_DRV_LOG(ERR, "(%s) Failed to get max queue pairs from device", dev->path);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_dev_init_cipher_services(struct virtio_user_dev *dev)
+{
+	struct virtio_crypto_config config;
+	int ret;
+
+	dev->crypto_services = RTE_BIT32(VIRTIO_CRYPTO_SERVICE_CIPHER);
+	dev->cipher_algo = 0;
+	dev->auth_algo = 0;
+	dev->akcipher_algo = 0;
+
+	if (!dev->ops->get_config)
+		return 0;
+
+	ret = dev->ops->get_config(dev, (uint8_t *)&config,	0, sizeof(config));
+	if (ret) {
+		PMD_DRV_LOG(ERR, "(%s) Failed to get crypto config from device", dev->path);
+		return ret;
+	}
+
+	dev->crypto_services = config.crypto_services;
+	dev->cipher_algo = ((uint64_t)config.cipher_algo_h << 32) |
+						config.cipher_algo_l;
+	dev->hash_algo = config.hash_algo;
+	dev->auth_algo = ((uint64_t)config.mac_algo_h << 32) |
+						config.mac_algo_l;
+	dev->aead_algo = config.aead_algo;
+	dev->akcipher_algo = config.akcipher_algo;
+	return 0;
+}
+
+static int
+virtio_user_dev_init_notify(struct virtio_user_dev *dev)
+{
+
+	if (virtio_user_foreach_queue(dev, virtio_user_init_notify_queue) < 0)
+		goto err;
+
+	if (dev->device_features & (1ULL << VIRTIO_F_NOTIFICATION_DATA))
+		if (dev->ops->map_notification_area &&
+				dev->ops->map_notification_area(dev))
+			goto err;
+
+	return 0;
+err:
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	return -1;
+}
+
+static void
+virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
+{
+	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
+
+	if (dev->ops->unmap_notification_area && dev->notify_area)
+		dev->ops->unmap_notification_area(dev);
+}
+
+static void
+virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
+			const void *addr,
+			size_t len __rte_unused,
+			void *arg)
+{
+	struct virtio_user_dev *dev = arg;
+	struct rte_memseg_list *msl;
+	uint16_t i;
+	int ret = 0;
+
+	/* ignore externally allocated memory */
+	msl = rte_mem_virt2memseg_list(addr);
+	if (msl->external)
+		return;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	if (dev->started == false)
+		goto exit;
+
+	/* Step 1: pause the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 0);
+		if (ret < 0)
+			goto exit;
+	}
+
+	/* Step 2: update memory regions */
+	ret = dev->ops->set_memory_table(dev);
+	if (ret < 0)
+		goto exit;
+
+	/* Step 3: resume the active queues */
+	for (i = 0; i < dev->queue_pairs; i++) {
+		ret = dev->ops->enable_qp(dev, i, 1);
+		if (ret < 0)
+			goto exit;
+	}
+
+exit:
+	pthread_mutex_unlock(&dev->mutex);
+
+	if (ret < 0)
+		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+	if (dev->is_server) {
+		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
+			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
+			return -1;
+		}
+	}
+
+	switch (dev->backend_type) {
+	case VIRTIO_USER_BACKEND_VHOST_VDPA:
+		dev->ops = &virtio_crypto_ops_vdpa;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+virtio_user_alloc_vrings(struct virtio_user_dev *dev)
+{
+	int i, size, nr_vrings;
+	bool packed_ring = !!(dev->device_features & (1ull << VIRTIO_F_RING_PACKED));
+
+	nr_vrings = dev->max_queue_pairs + 1;
+
+	dev->callfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->callfds), 0);
+	if (!dev->callfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc callfds", dev->path);
+		return -1;
+	}
+
+	dev->kickfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->kickfds), 0);
+	if (!dev->kickfds) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc kickfds", dev->path);
+		goto free_callfds;
+	}
+
+	for (i = 0; i < nr_vrings; i++) {
+		dev->callfds[i] = -1;
+		dev->kickfds[i] = -1;
+	}
+
+	if (packed_ring)
+		size = sizeof(*dev->vrings.packed);
+	else
+		size = sizeof(*dev->vrings.split);
+	dev->vrings.ptr = rte_zmalloc("virtio_user_dev", nr_vrings * size, 0);
+	if (!dev->vrings.ptr) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc vrings metadata", dev->path);
+		goto free_kickfds;
+	}
+
+	if (packed_ring) {
+		dev->packed_queues = rte_zmalloc("virtio_user_dev",
+				nr_vrings * sizeof(*dev->packed_queues), 0);
+		if (!dev->packed_queues) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to alloc packed queues metadata",
+					dev->path);
+			goto free_vrings;
+		}
+	}
+
+	dev->qp_enabled = rte_zmalloc("virtio_user_dev",
+			nr_vrings * sizeof(*dev->qp_enabled), 0);
+	if (!dev->qp_enabled) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to alloc QP enable states", dev->path);
+		goto free_packed_queues;
+	}
+
+	return 0;
+
+free_packed_queues:
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+free_vrings:
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+free_kickfds:
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+free_callfds:
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+
+	return -1;
+}
+
+static void
+virtio_user_free_vrings(struct virtio_user_dev *dev)
+{
+	rte_free(dev->qp_enabled);
+	dev->qp_enabled = NULL;
+	rte_free(dev->packed_queues);
+	dev->packed_queues = NULL;
+	rte_free(dev->vrings.ptr);
+	dev->vrings.ptr = NULL;
+	rte_free(dev->kickfds);
+	dev->kickfds = NULL;
+	rte_free(dev->callfds);
+	dev->callfds = NULL;
+}
+
+#define VIRTIO_USER_SUPPORTED_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_HASH       | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+int
+crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server)
+{
+	uint64_t backend_features;
+
+	pthread_mutex_init(&dev->mutex, NULL);
+	strlcpy(dev->path, path, PATH_MAX);
+
+	dev->started = 0;
+	dev->queue_pairs = 1; /* mq disabled by default */
+	dev->max_queue_pairs = queues; /* initialize to user requested value for kernel backend */
+	dev->queue_size = queue_size;
+	dev->is_server = server;
+	dev->frontend_features = 0;
+	dev->unsupported_features = 0;
+	dev->backend_type = VIRTIO_USER_BACKEND_VHOST_VDPA;
+	dev->hw.modern = 1;
+
+	if (virtio_user_dev_setup(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
+		return -1;
+	}
+
+	if (dev->ops->set_owner(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
+		goto destroy;
+	}
+
+	if (dev->ops->get_backend_features(&backend_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
+		goto destroy;
+	}
+
+	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
+
+	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_max_queue_pairs(dev, queues)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get max queue pairs", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_cipher_services(dev)) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get cipher services", dev->path);
+		goto destroy;
+	}
+
+	dev->frontend_features &= ~dev->unsupported_features;
+	dev->device_features &= ~dev->unsupported_features;
+
+	if (virtio_user_alloc_vrings(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to allocate vring metadata", dev->path);
+		goto destroy;
+	}
+
+	if (virtio_user_dev_init_notify(dev) < 0) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path);
+		goto free_vrings;
+	}
+
+	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
+				virtio_user_mem_event_cb, dev)) {
+		if (rte_errno != ENOTSUP) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback",
+					dev->path);
+			goto notify_uninit;
+		}
+	}
+
+	return 0;
+
+notify_uninit:
+	virtio_user_dev_uninit_notify(dev);
+free_vrings:
+	virtio_user_free_vrings(dev);
+destroy:
+	dev->ops->destroy(dev);
+
+	return -1;
+}
+
+void
+crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev)
+{
+	crypto_virtio_user_stop_device(dev);
+
+	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
+
+	virtio_user_dev_uninit_notify(dev);
+
+	virtio_user_free_vrings(dev);
+
+	if (dev->is_server)
+		unlink(dev->path);
+
+	dev->ops->destroy(dev);
+}
+
+#define CVQ_MAX_DATA_DESCS 32
+
+int
+crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
+{
+	int ret;
+
+	pthread_mutex_lock(&dev->mutex);
+	dev->status = status;
+	ret = dev->ops->set_status(dev, status);
+	if (ret && ret != -ENOTSUP)
+		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path);
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev)
+{
+	int ret;
+	uint8_t status;
+
+	pthread_mutex_lock(&dev->mutex);
+
+	ret = dev->ops->get_status(dev, &status);
+	if (!ret) {
+		dev->status = status;
+		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):"
+			"\t-RESET: %u "
+			"\t-ACKNOWLEDGE: %u "
+			"\t-DRIVER: %u "
+			"\t-DRIVER_OK: %u "
+			"\t-FEATURES_OK: %u "
+			"\t-DEVICE_NEED_RESET: %u "
+			"\t-FAILED: %u",
+			dev->status,
+			(dev->status == VIRTIO_CONFIG_STATUS_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET),
+			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
+	} else if (ret != -ENOTSUP) {
+		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path);
+	}
+
+	pthread_mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+int
+crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
+{
+	if (dev->ops->update_link_state)
+		return dev->ops->update_link_state(dev);
+
+	return 0;
+}
diff --git a/drivers/crypto/virtio/virtio_user/virtio_user_dev.h b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
new file mode 100644
index 0000000000..9cd9856e5d
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user/virtio_user_dev.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell.
+ */
+
+#ifndef _VIRTIO_USER_DEV_H
+#define _VIRTIO_USER_DEV_H
+
+#include <limits.h>
+#include <stdbool.h>
+
+#include "../virtio_pci.h"
+#include "../virtio_ring.h"
+
+extern struct virtio_user_backend_ops virtio_crypto_ops_vdpa;
+
+enum virtio_user_backend_type {
+	VIRTIO_USER_BACKEND_UNKNOWN,
+	VIRTIO_USER_BACKEND_VHOST_USER,
+	VIRTIO_USER_BACKEND_VHOST_VDPA,
+};
+
+struct virtio_user_queue {
+	uint16_t used_idx;
+	bool avail_wrap_counter;
+	bool used_wrap_counter;
+};
+
+struct virtio_user_dev {
+	struct virtio_crypto_hw hw;
+	enum virtio_user_backend_type backend_type;
+	bool		is_server;  /* server or client mode */
+
+	int		*callfds;
+	int		*kickfds;
+	uint16_t	max_queue_pairs;
+	uint16_t	queue_pairs;
+	uint32_t	queue_size;
+	uint64_t	features; /* the negotiated features with driver,
+				   * and will be sync with device
+				   */
+	uint64_t	device_features; /* supported features by device */
+	uint64_t	frontend_features; /* enabled frontend features */
+	uint64_t	unsupported_features; /* unsupported features mask */
+	uint8_t		status;
+	uint32_t	crypto_status;
+	uint32_t	crypto_services;
+	uint64_t	cipher_algo;
+	uint32_t	hash_algo;
+	uint64_t	auth_algo;
+	uint32_t	aead_algo;
+	uint32_t	akcipher_algo;
+	char		path[PATH_MAX];
+
+	union {
+		void			*ptr;
+		struct vring		*split;
+		struct vring_packed	*packed;
+	} vrings;
+
+	struct virtio_user_queue *packed_queues;
+	bool		*qp_enabled;
+
+	struct virtio_user_backend_ops *ops;
+	pthread_mutex_t	mutex;
+	bool		started;
+
+	bool			hw_cvq;
+	struct virtqueue	*scvq;
+
+	void *backend_data;
+
+	uint16_t **notify_area;
+};
+
+int crypto_virtio_user_dev_set_features(struct virtio_user_dev *dev);
+int crypto_virtio_user_start_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_stop_device(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
+			int queue_size, int server);
+void crypto_virtio_user_dev_uninit(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status);
+int crypto_virtio_user_dev_update_status(struct virtio_user_dev *dev);
+int crypto_virtio_user_dev_update_link_state(struct virtio_user_dev *dev);
+extern const char * const crypto_virtio_user_backend_strings[];
+#endif
diff --git a/drivers/crypto/virtio/virtio_user_cryptodev.c b/drivers/crypto/virtio/virtio_user_cryptodev.c
new file mode 100644
index 0000000000..992e8fb43b
--- /dev/null
+++ b/drivers/crypto/virtio/virtio_user_cryptodev.c
@@ -0,0 +1,575 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <rte_malloc.h>
+#include <rte_kvargs.h>
+#include <bus_vdev_driver.h>
+#include <rte_cryptodev.h>
+#include <cryptodev_pmd.h>
+#include <rte_alarm.h>
+#include <rte_cycles.h>
+#include <rte_io.h>
+
+#include "virtio_user/virtio_user_dev.h"
+#include "virtio_user/vhost.h"
+#include "virtio_cryptodev.h"
+#include "virtio_logs.h"
+#include "virtio_pci.h"
+#include "virtqueue.h"
+
+#define virtio_user_get_dev(hwp) container_of(hwp, struct virtio_user_dev, hw)
+
+static void
+virtio_user_read_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		     void *dst, int length __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (offset == offsetof(struct virtio_crypto_config, status)) {
+		crypto_virtio_user_dev_update_link_state(dev);
+		*(uint32_t *)dst = dev->crypto_status;
+	} else if (offset == offsetof(struct virtio_crypto_config, max_dataqueues))
+		*(uint16_t *)dst = dev->max_queue_pairs;
+	else if (offset == offsetof(struct virtio_crypto_config, crypto_services))
+		*(uint32_t *)dst = dev->crypto_services;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_l))
+		*(uint32_t *)dst = dev->cipher_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, cipher_algo_h))
+		*(uint32_t *)dst = dev->cipher_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, hash_algo))
+		*(uint32_t *)dst = dev->hash_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_l))
+		*(uint32_t *)dst = dev->auth_algo & 0xFFFF;
+	else if (offset == offsetof(struct virtio_crypto_config, mac_algo_h))
+		*(uint32_t *)dst = dev->auth_algo >> 32;
+	else if (offset == offsetof(struct virtio_crypto_config, aead_algo))
+		*(uint32_t *)dst = dev->aead_algo;
+	else if (offset == offsetof(struct virtio_crypto_config, akcipher_algo))
+		*(uint32_t *)dst = dev->akcipher_algo;
+}
+
+static void
+virtio_user_write_dev_config(struct virtio_crypto_hw *hw, size_t offset,
+		      const void *src, int length)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(src);
+
+	PMD_DRV_LOG(ERR, "not supported offset=%zu, len=%d",
+		    offset, length);
+}
+
+static void
+virtio_user_reset(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+		crypto_virtio_user_stop_device(dev);
+}
+
+static void
+virtio_user_set_status(struct virtio_crypto_hw *hw, uint8_t status)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint8_t old_status = dev->status;
+
+	if (status & VIRTIO_CONFIG_STATUS_FEATURES_OK &&
+			~old_status & VIRTIO_CONFIG_STATUS_FEATURES_OK) {
+		crypto_virtio_user_dev_set_features(dev);
+		/* Feature negotiation should be only done in probe time.
+		 * So we skip any more request here.
+		 */
+		dev->status |= VIRTIO_CONFIG_STATUS_FEATURES_OK;
+	}
+
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) {
+		if (crypto_virtio_user_start_device(dev)) {
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	} else if (status == VIRTIO_CONFIG_STATUS_RESET) {
+		virtio_user_reset(hw);
+	}
+
+	crypto_virtio_user_dev_set_status(dev, status);
+	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK && dev->scvq) {
+		if (dev->ops->cvq_enable(dev, 1) < 0) {
+			PMD_INIT_LOG(ERR, "(%s) Failed to start ctrlq", dev->path);
+			crypto_virtio_user_dev_update_status(dev);
+			return;
+		}
+	}
+}
+
+static uint8_t
+virtio_user_get_status(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	crypto_virtio_user_dev_update_status(dev);
+
+	return dev->status;
+}
+
+#define VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES   \
+	(1ULL << VIRTIO_CRYPTO_SERVICE_CIPHER     | \
+	 1ULL << VIRTIO_CRYPTO_SERVICE_AKCIPHER   | \
+	 1ULL << VIRTIO_F_VERSION_1               | \
+	 1ULL << VIRTIO_F_IN_ORDER                | \
+	 1ULL << VIRTIO_F_RING_PACKED             | \
+	 1ULL << VIRTIO_F_NOTIFICATION_DATA       | \
+	 1ULL << VIRTIO_RING_F_INDIRECT_DESC      | \
+	 1ULL << VIRTIO_F_ORDER_PLATFORM)
+
+static uint64_t
+virtio_user_get_features(struct virtio_crypto_hw *hw)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* unmask feature bits defined in vhost user protocol */
+	return (dev->device_features | dev->frontend_features) &
+		VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES;
+}
+
+static void
+virtio_user_set_features(struct virtio_crypto_hw *hw, uint64_t features)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	dev->features = features & (dev->device_features | dev->frontend_features);
+}
+
+static uint8_t
+virtio_user_get_isr(struct virtio_crypto_hw *hw __rte_unused)
+{
+	/* rxq interrupts and config interrupt are separated in virtio-user,
+	 * here we only report config change.
+	 */
+	return VIRTIO_PCI_CAP_ISR_CFG;
+}
+
+static uint16_t
+virtio_user_set_config_irq(struct virtio_crypto_hw *hw __rte_unused,
+		    uint16_t vec __rte_unused)
+{
+	return 0;
+}
+
+static uint16_t
+virtio_user_set_queue_irq(struct virtio_crypto_hw *hw __rte_unused,
+			  struct virtqueue *vq __rte_unused,
+			  uint16_t vec)
+{
+	/* pretend we have done that */
+	return vec;
+}
+
+/* This function is to get the queue size, aka, number of descs, of a specified
+ * queue. Different with the VHOST_USER_GET_QUEUE_NUM, which is used to get the
+ * max supported queues.
+ */
+static uint16_t
+virtio_user_get_queue_num(struct virtio_crypto_hw *hw, uint16_t queue_id __rte_unused)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	/* Currently, each queue has same queue size */
+	return dev->queue_size;
+}
+
+static void
+virtio_user_setup_queue_packed(struct virtqueue *vq,
+			       struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	struct vring_packed *vring;
+	uint64_t desc_addr;
+	uint64_t avail_addr;
+	uint64_t used_addr;
+	uint16_t i;
+
+	vring  = &dev->vrings.packed[queue_idx];
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries *
+		sizeof(struct vring_packed_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr +
+			   sizeof(struct vring_packed_desc_event),
+			   VIRTIO_VRING_ALIGN);
+	vring->num = vq->vq_nentries;
+	vring->desc_iova = vq->vq_ring_mem;
+	vring->desc = (void *)(uintptr_t)desc_addr;
+	vring->driver = (void *)(uintptr_t)avail_addr;
+	vring->device = (void *)(uintptr_t)used_addr;
+	dev->packed_queues[queue_idx].avail_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_wrap_counter = true;
+	dev->packed_queues[queue_idx].used_idx = 0;
+
+	for (i = 0; i < vring->num; i++)
+		vring->desc[i].flags = 0;
+}
+
+static void
+virtio_user_setup_queue_split(struct virtqueue *vq, struct virtio_user_dev *dev)
+{
+	uint16_t queue_idx = vq->vq_queue_index;
+	uint64_t desc_addr, avail_addr, used_addr;
+
+	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
+	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
+	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
+							 ring[vq->vq_nentries]),
+				   VIRTIO_VRING_ALIGN);
+
+	dev->vrings.split[queue_idx].num = vq->vq_nentries;
+	dev->vrings.split[queue_idx].desc_iova = vq->vq_ring_mem;
+	dev->vrings.split[queue_idx].desc = (void *)(uintptr_t)desc_addr;
+	dev->vrings.split[queue_idx].avail = (void *)(uintptr_t)avail_addr;
+	dev->vrings.split[queue_idx].used = (void *)(uintptr_t)used_addr;
+}
+
+static int
+virtio_user_setup_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+	if (vtpci_with_packed_queue(hw))
+		virtio_user_setup_queue_packed(vq, dev);
+	else
+		virtio_user_setup_queue_split(vq, dev);
+
+	if (dev->notify_area)
+		vq->notify_addr = dev->notify_area[vq->vq_queue_index];
+
+	if (virtcrypto_cq_to_vq(hw->cvq) == vq)
+		dev->scvq = virtcrypto_cq_to_vq(hw->cvq);
+
+	return 0;
+}
+
+static void
+virtio_user_del_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	RTE_SET_USED(hw);
+	RTE_SET_USED(vq);
+}
+
+static void
+virtio_user_notify_queue(struct virtio_crypto_hw *hw, struct virtqueue *vq)
+{
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+	uint64_t notify_data = 1;
+
+	if (!dev->notify_area) {
+		if (write(dev->kickfds[vq->vq_queue_index], &notify_data,
+			  sizeof(notify_data)) < 0)
+			PMD_DRV_LOG(ERR, "failed to kick backend: %s",
+				    strerror(errno));
+		return;
+	} else if (!vtpci_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) {
+		rte_write16(vq->vq_queue_index, vq->notify_addr);
+		return;
+	}
+
+	if (vtpci_with_packed_queue(hw)) {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:30]: avail index
+		 * Bit[31]: avail wrap counter
+		 */
+		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
+				VRING_PACKED_DESC_F_AVAIL)) << 31) |
+				((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	} else {
+		/* Bit[0:15]: vq queue index
+		 * Bit[16:31]: avail index
+		 */
+		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
+				vq->vq_queue_index;
+	}
+	rte_write32(notify_data, vq->notify_addr);
+}
+
+const struct virtio_pci_ops crypto_virtio_user_ops = {
+	.read_dev_cfg	= virtio_user_read_dev_config,
+	.write_dev_cfg	= virtio_user_write_dev_config,
+	.reset		= virtio_user_reset,
+	.get_status	= virtio_user_get_status,
+	.set_status	= virtio_user_set_status,
+	.get_features	= virtio_user_get_features,
+	.set_features	= virtio_user_set_features,
+	.get_isr	= virtio_user_get_isr,
+	.set_config_irq	= virtio_user_set_config_irq,
+	.set_queue_irq	= virtio_user_set_queue_irq,
+	.get_queue_num	= virtio_user_get_queue_num,
+	.setup_queue	= virtio_user_setup_queue,
+	.del_queue	= virtio_user_del_queue,
+	.notify_queue	= virtio_user_notify_queue,
+};
+
+static const char * const valid_args[] = {
+#define VIRTIO_USER_ARG_QUEUES_NUM     "queues"
+	VIRTIO_USER_ARG_QUEUES_NUM,
+#define VIRTIO_USER_ARG_QUEUE_SIZE     "queue_size"
+	VIRTIO_USER_ARG_QUEUE_SIZE,
+#define VIRTIO_USER_ARG_PATH           "path"
+	VIRTIO_USER_ARG_PATH,
+	NULL
+};
+
+#define VIRTIO_USER_DEF_Q_NUM	1
+#define VIRTIO_USER_DEF_Q_SZ	256
+#define VIRTIO_USER_DEF_SERVER_MODE	0
+
+static int
+get_string_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	if (!value || !extra_args)
+		return -EINVAL;
+
+	*(char **)extra_args = strdup(value);
+
+	if (!*(char **)extra_args)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int
+get_integer_arg(const char *key __rte_unused,
+		const char *value, void *extra_args)
+{
+	uint64_t integer = 0;
+	if (!value || !extra_args)
+		return -EINVAL;
+	errno = 0;
+	integer = strtoull(value, NULL, 0);
+	/* extra_args keeps default value, it should be replaced
+	 * only in case of successful parsing of the 'value' arg
+	 */
+	if (errno == 0)
+		*(uint64_t *)extra_args = integer;
+	return -errno;
+}
+
+static struct rte_cryptodev *
+virtio_user_cryptodev_alloc(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev_pmd_init_params init_params = {
+		.name = "",
+		.private_data_size = sizeof(struct virtio_user_dev),
+	};
+	struct rte_cryptodev_data *data;
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	struct virtio_crypto_hw *hw;
+
+	init_params.socket_id = vdev->device.numa_node;
+	init_params.private_data_size = sizeof(struct virtio_user_dev);
+	cryptodev = rte_cryptodev_pmd_create(vdev->device.name, &vdev->device, &init_params);
+	if (cryptodev == NULL) {
+		PMD_INIT_LOG(ERR, "failed to create cryptodev vdev");
+		return NULL;
+	}
+
+	data = cryptodev->data;
+	dev = data->dev_private;
+	hw = &dev->hw;
+
+	hw->dev_id = data->dev_id;
+	VTPCI_OPS(hw) = &crypto_virtio_user_ops;
+
+	return cryptodev;
+}
+
+static void
+virtio_user_cryptodev_free(struct rte_cryptodev *cryptodev)
+{
+	rte_cryptodev_pmd_destroy(cryptodev);
+}
+
+static int
+virtio_user_pmd_probe(struct rte_vdev_device *vdev)
+{
+	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
+	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
+	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
+	struct rte_cryptodev *cryptodev = NULL;
+	struct rte_kvargs *kvlist = NULL;
+	struct virtio_user_dev *dev;
+	char *path = NULL;
+	int ret = -1;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(vdev), valid_args);
+
+	if (!kvlist) {
+		PMD_INIT_LOG(ERR, "error when parsing param");
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PATH) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PATH,
+					&get_string_arg, &path) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_PATH);
+			goto end;
+		}
+	} else {
+		PMD_INIT_LOG(ERR, "arg %s is mandatory for virtio_user",
+				VIRTIO_USER_ARG_PATH);
+		goto end;
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUES_NUM) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUES_NUM,
+					&get_integer_arg, &queues) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUES_NUM);
+			goto end;
+		}
+	}
+
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE,
+					&get_integer_arg, &queue_size) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+					VIRTIO_USER_ARG_QUEUE_SIZE);
+			goto end;
+		}
+	}
+
+	cryptodev = virtio_user_cryptodev_alloc(vdev);
+	if (!cryptodev) {
+		PMD_INIT_LOG(ERR, "virtio_user fails to alloc device");
+		goto end;
+	}
+
+	dev = cryptodev->data->dev_private;
+	if (crypto_virtio_user_dev_init(dev, path, queues, queue_size,
+			server_mode) < 0) {
+		PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	if (crypto_virtio_dev_init(cryptodev, VIRTIO_USER_CRYPTO_PMD_GUEST_FEATURES,
+			NULL) < 0) {
+		PMD_INIT_LOG(ERR, "crypto_virtio_dev_init fails");
+		crypto_virtio_user_dev_uninit(dev);
+		virtio_user_cryptodev_free(cryptodev);
+		goto end;
+	}
+
+	rte_cryptodev_pmd_probing_finish(cryptodev);
+
+	ret = 0;
+end:
+	rte_kvargs_free(kvlist);
+	free(path);
+	return ret;
+}
+
+static int
+virtio_user_pmd_remove(struct rte_vdev_device *vdev)
+{
+	struct rte_cryptodev *cryptodev;
+	const char *name;
+	int devid;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	PMD_DRV_LOG(INFO, "Removing %s", name);
+
+	devid = rte_cryptodev_get_dev_id(name);
+	if (devid < 0)
+		return -EINVAL;
+
+	rte_cryptodev_stop(devid);
+
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -ENODEV;
+
+	if (rte_cryptodev_pmd_destroy(cryptodev) < 0) {
+		PMD_DRV_LOG(ERR, "Failed to remove %s", name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_map(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_map)
+		return dev->ops->dma_map(dev, addr, iova, len);
+
+	return 0;
+}
+
+static int virtio_user_pmd_dma_unmap(struct rte_vdev_device *vdev, void *addr,
+		uint64_t iova, size_t len)
+{
+	struct rte_cryptodev *cryptodev;
+	struct virtio_user_dev *dev;
+	const char *name;
+
+	if (!vdev)
+		return -EINVAL;
+
+	name = rte_vdev_device_name(vdev);
+	cryptodev = rte_cryptodev_pmd_get_named_dev(name);
+	if (cryptodev == NULL)
+		return -EINVAL;
+
+	dev = cryptodev->data->dev_private;
+
+	if (dev->ops->dma_unmap)
+		return dev->ops->dma_unmap(dev, addr, iova, len);
+
+	return 0;
+}
+
+static struct rte_vdev_driver virtio_user_driver = {
+	.probe = virtio_user_pmd_probe,
+	.remove = virtio_user_pmd_remove,
+	.dma_map = virtio_user_pmd_dma_map,
+	.dma_unmap = virtio_user_pmd_dma_unmap,
+};
+
+static struct cryptodev_driver virtio_crypto_drv;
+
+uint8_t cryptodev_virtio_user_driver_id;
+
+RTE_PMD_REGISTER_VDEV(crypto_virtio_user, virtio_user_driver);
+RTE_PMD_REGISTER_CRYPTO_DRIVER(virtio_crypto_drv,
+	virtio_user_driver.driver,
+	cryptodev_virtio_user_driver_id);
+RTE_PMD_REGISTER_PARAM_STRING(crypto_virtio_user,
+	"path=<path> "
+	"queues=<int> "
+	"queue_size=<int>");
-- 
2.25.1


^ permalink raw reply	[relevance 1%]

* [RFC] eal: add new function versioning macros
@ 2025-03-05 21:23  6% David Marchand
  2025-03-06 12:50  6% ` [RFC v2 1/2] " David Marchand
                   ` (5 more replies)
  0 siblings, 6 replies; 153+ results
From: David Marchand @ 2025-03-05 21:23 UTC (permalink / raw)
  To: dev; +Cc: thomas, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the function symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
versioning functions that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a function is just a call to a single macro:
- VERSION_FUNCTION (resp. VERSION_FUNCTION_EXPERIMENTAL), for keeping an
  old implementation code under a versioned function,
- DEFAULT_FUNCTION, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

Documentation has been updated though it needs some polishing.
The experimental macro has not been tested.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 doc/guides/contributing/abi_versioning.rst | 133 ++++-----------------
 lib/eal/include/rte_function_versioning.h  |  27 +++++
 lib/net/rte_net_crc.c                      |  41 +++----
 3 files changed, 69 insertions(+), 132 deletions(-)

diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..b83383fd0b 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -277,86 +277,52 @@ list of exported symbols when DPDK is compiled as a shared library.
 
 Next, we need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``VERSION_FUNCTION``, passing the current ABI version,
+the function return type, and the function name, then the full implementation of the
+function.
 
 .. code-block:: c
 
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ +VERSION_FUNCTION(21,
+ +struct rte_acl_ctx *,
+ +rte_acl_create, (const struct rte_acl_param *param)
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
-
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -}
+ +})
 
 Remembering to also add the rte_function_versioning.h header to the requisite c
 file where these changes are being made. The macro instructs the linker to
 create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``DEFAULT_FUNCTION``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   DEFAULT_FUNCTION(22,
+   struct rte_acl_ctx *,
+   rte_acl_create, (const struct rte_acl_param *param, int debug);
    {
         struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
 
         ctx->debug = debug;
 
         return ctx;
-   }
-
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+   })
 
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it, on the next shared library rebuild, there will be two versions of
 rte_acl_create, an old DPDK_21 version, used by previously built applications,
@@ -365,43 +331,10 @@ and a new DPDK_22 version, used by future built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -519,26 +452,19 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   DEFAULT_FUNCTION(22,
+   struct rte_acl_ctx *,
+   rte_acl_create, (const struct rte_acl_param *param)
    {
    ...
-   }
-
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
+   })
 
+   VERSION_FUNCTION_EXPERIMENTAL(
    struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   rte_acl_create, (const struct rte_acl_param *param)
    {
       return rte_acl_create(param);
-   }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+   })
 
 In the map file, we map the symbol to both the ``EXPERIMENTAL``
 and ``DPDK_22`` version nodes.
@@ -564,13 +490,6 @@ and ``DPDK_22`` version nodes.
         rte_acl_create;
    };
 
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
index eb6dd2bc17..7a33a45928 100644
--- a/lib/eal/include/rte_function_versioning.h
+++ b/lib/eal/include/rte_function_versioning.h
@@ -96,4 +96,31 @@
  */
 #endif
 
+#ifdef RTE_BUILD_SHARED_LIB
+
+#define VERSION_FUNCTION(ver, type, name, ...) \
+__rte_used type name ## _v ## ver __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver));
+
+#define VERSION_FUNCTION_EXPERIMENTAL(type, name, ...) \
+__rte_used type name ## _exp __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL")
+
+#define DEFAULT_FUNCTION(ver, type, name, ...) \
+__rte_used type name ## _v ## ver __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver));
+
+#else /* !RTE_BUILD_SHARED_LIB */
+
+#define VERSION_FUNCTION(ver, type, name, ...) \
+type name ## _v ## ver __VA_ARGS__
+
+#define VERSION_FUNCTION_EXPERIMENTAL(type, name, ...) \
+type name ## _exp __VA_ARGS__
+
+#define DEFAULT_FUNCTION(ver, type, name, ...) \
+type name __VA_ARGS__
+
+#endif /* RTE_BUILD_SHARED_LIB */
+
 #endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 2fb3eec231..a1c17e0735 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -345,8 +345,9 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+VERSION_FUNCTION(25,
+void,
+rte_net_crc_set_alg, (enum rte_net_crc_alg alg)
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -372,11 +373,11 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 
 	if (handlers == NULL)
 		handlers = handlers_scalar;
-}
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
+})
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+DEFAULT_FUNCTION(26,
+struct rte_net_crc *,
+rte_net_crc_set_alg, (enum rte_net_crc_alg alg, enum rte_net_crc_type type)
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -413,21 +414,16 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 		break;
 	}
 	return crc;
-}
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
+})
 
 void rte_net_crc_free(struct rte_net_crc *crc)
 {
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+VERSION_FUNCTION(25,
+uint32_t,
+rte_net_crc_calc, (const void *data, uint32_t data_len, enum rte_net_crc_type type)
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -436,19 +432,14 @@ rte_net_crc_calc_v25(const void *data,
 	ret = f_handle(data, data_len);
 
 	return ret;
-}
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
+})
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+DEFAULT_FUNCTION(26,
+uint32_t,
+rte_net_crc_calc, (const struct rte_net_crc *ctx, const void *data, const uint32_t data_len)
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
-}
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
+})
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.48.1


^ permalink raw reply	[relevance 6%]

* [RFC v2 1/2] eal: add new function versioning macros
  2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
@ 2025-03-06 12:50  6% ` David Marchand
  2025-03-06 12:50  7%   ` [RFC v2 2/2] build: generate symbol maps David Marchand
  2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-06 12:50 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
exporting and versioning symbols that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a symbol is just a call to a single macro:
- RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
  keeping an old implementation code under a versioned function (resp.
  experimental function),
- RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

Documentation has been updated though it needs some polishing.
The experimental macro has not been tested.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v1:
- renamed and prefixed macros,
- reindented in prevision of second patch,

---
 doc/guides/contributing/abi_versioning.rst | 130 ++++-----------------
 lib/eal/include/rte_function_versioning.h  |  27 +++++
 lib/net/rte_net_crc.c                      |  30 ++---
 3 files changed, 57 insertions(+), 130 deletions(-)

diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..c4baf6433a 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -277,86 +277,49 @@ list of exported symbols when DPDK is compiled as a shared library.
 
 Next, we need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
+the function return type, and the function name, then the full implementation of the
+function.
 
 .. code-block:: c
 
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param)
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
-
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -}
+ +})
 
 Remembering to also add the rte_function_versioning.h header to the requisite c
 file where these changes are being made. The macro instructs the linker to
 create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
+        int debug);
    {
         struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
 
         ctx->debug = debug;
 
         return ctx;
-   }
-
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+   })
 
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it, on the next shared library rebuild, there will be two versions of
 rte_acl_create, an old DPDK_21 version, used by previously built applications,
@@ -365,43 +328,10 @@ and a new DPDK_22 version, used by future built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -519,26 +449,17 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create,
+        (const struct rte_acl_param *param)
    {
    ...
-   }
+   })
 
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
-
-   struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   RTE_VERSION_EXPERIMENTAL_SYMBOL(struct rte_acl_ctx *, rte_acl_create,
+        (const struct rte_acl_param *param)
    {
       return rte_acl_create(param);
-   }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+   })
 
 In the map file, we map the symbol to both the ``EXPERIMENTAL``
 and ``DPDK_22`` version nodes.
@@ -564,13 +485,6 @@ and ``DPDK_22`` version nodes.
         rte_acl_create;
    };
 
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
index eb6dd2bc17..259b960ef5 100644
--- a/lib/eal/include/rte_function_versioning.h
+++ b/lib/eal/include/rte_function_versioning.h
@@ -96,4 +96,31 @@
  */
 #endif
 
+#ifdef RTE_BUILD_SHARED_LIB
+
+#define RTE_VERSION_SYMBOL(ver, type, name, ...) \
+__rte_used type name ## _v ## ver __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver));
+
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, ...) \
+__rte_used type name ## _exp __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL")
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, ...) \
+__rte_used type name ## _v ## ver __VA_ARGS__ \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver));
+
+#else /* !RTE_BUILD_SHARED_LIB */
+
+#define RTE_VERSION_SYMBOL(ver, type, name, ...) \
+type name ## _v ## ver __VA_ARGS__
+
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, ...) \
+type name ## _exp __VA_ARGS__
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, ...) \
+type name __VA_ARGS__
+
+#endif /* RTE_BUILD_SHARED_LIB */
+
 #endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 2fb3eec231..dd93d43c2e 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -345,8 +345,7 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+RTE_VERSION_SYMBOL(25, void, rte_net_crc_set_alg, (enum rte_net_crc_alg alg)
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -372,10 +371,9 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 
 	if (handlers == NULL)
 		handlers = handlers_scalar;
-}
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
+})
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
+RTE_DEFAULT_SYMBOL(26, struct rte_net_crc *, rte_net_crc_set_alg, (enum rte_net_crc_alg alg,
 	enum rte_net_crc_type type)
 {
 	uint16_t max_simd_bitwidth;
@@ -413,20 +411,14 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 		break;
 	}
 	return crc;
-}
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
+})
 
 void rte_net_crc_free(struct rte_net_crc *crc)
 {
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
+RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
 	enum rte_net_crc_type type)
 {
 	uint32_t ret;
@@ -436,19 +428,13 @@ rte_net_crc_calc_v25(const void *data,
 	ret = f_handle(data, data_len);
 
 	return ret;
-}
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
+})
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
+RTE_DEFAULT_SYMBOL(26, uint32_t, rte_net_crc_calc, (const struct rte_net_crc *ctx,
 	const void *data, const uint32_t data_len)
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
-}
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
+})
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.48.1


^ permalink raw reply	[relevance 6%]

* [RFC v2 2/2] build: generate symbol maps
  2025-03-06 12:50  6% ` [RFC v2 1/2] " David Marchand
@ 2025-03-06 12:50  7%   ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-06 12:50 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Jasvinder Singh

Rather than maintain a file in parallel of the code, symbols to be
exported can be marked with a token.
The build framework then generates map files in a format that can satisfy
GNU linker.

Apply those macros to lib/net as an example.

Documentation is missing.
Converting from .map to Windows export file is not done.
Checks on map files are left in place, though they could be removed once
the whole tree is converted.
Experimental and internal symbol types are not handled.
Probably something else is missing, but this patch is still at RFC level.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 buildtools/gen-version-map.py | 65 +++++++++++++++++++++++++++++++++++
 buildtools/meson.build        |  1 +
 drivers/meson.build           |  2 --
 lib/meson.build               | 19 ++++++++--
 lib/net/rte_arp.c             |  1 +
 lib/net/rte_ether.c           |  3 ++
 lib/net/rte_net.c             |  2 ++
 lib/net/rte_net_crc.c         |  1 +
 lib/net/version.map           | 23 -------------
 meson.build                   |  3 +-
 10 files changed, 91 insertions(+), 29 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 delete mode 100644 lib/net/version.map

diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
new file mode 100755
index 0000000000..2b03f328ea
--- /dev/null
+++ b/buildtools/gen-version-map.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2024 Red Hat, Inc.
+
+"""Generate a version map file used by GNU linker."""
+
+import re
+import sys
+
+# From meson.build
+sym_export_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^,]+)\)$")
+# From rte_function_versioning.h
+sym_ver_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+sym_default_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+
+with open("../ABI_VERSION") as f:
+    abi = re.match("([0-9]+).[0-9]", f.readline()).group(1)
+
+symbols = {}
+
+for file in sys.argv[2:]:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            node = None
+            symbol = None
+            if sym_export_regexp.match(ln):
+                symbol = sym_export_regexp.match(ln).group(1)
+            elif sym_ver_regexp.match(ln):
+                node = sym_ver_regexp.match(ln).group(1)
+                symbol = sym_ver_regexp.match(ln).group(2)
+            elif sym_default_regexp.match(ln):
+                node = sym_default_regexp.match(ln).group(1)
+                symbol = sym_default_regexp.match(ln).group(2)
+
+            if not symbol:
+                continue
+
+            if not node:
+                node = abi
+            if node not in symbols:
+                symbols[node] = []
+            symbols[node].append(symbol)
+
+with open(sys.argv[1], "w") as outfile:
+    local_token = False
+    if abi in symbols:
+        outfile.writelines(f"DPDK_{abi} {{\n\tglobal:\n\n")
+        for symbol in sorted(symbols[abi]):
+            outfile.writelines(f"\t{symbol};\n")
+        outfile.writelines("\n")
+        if not local_token:
+            outfile.writelines("\tlocal: *;\n")
+            local_token = True
+        outfile.writelines("};\n")
+        del symbols[abi]
+    for key in sorted(symbols.keys()):
+        outfile.writelines(f"DPDK_{key} {{\n\tglobal:\n\n")
+        for symbol in sorted(symbols[key]):
+            outfile.writelines(f"\t{symbol};\n")
+        outfile.writelines("\n")
+        if not local_token:
+            outfile.writelines("\tlocal: *;\n")
+            local_token = True
+        outfile.writelines(f"}} DPDK_{abi};\n")
+        del symbols[key]
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 4e2c1217a2..b745e9afa4 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -16,6 +16,7 @@ else
     py3 = ['meson', 'runpython']
 endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
+gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
diff --git a/drivers/meson.build b/drivers/meson.build
index 05391a575d..d5fe3749c4 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -5,8 +5,6 @@ if is_ms_compiler
     subdir_done()
 endif
 
-fs = import('fs')
-
 # Defines the order of dependencies evaluation
 subdirs = [
         'common',
diff --git a/lib/meson.build b/lib/meson.build
index ce92cb5537..4db1864241 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -110,6 +110,7 @@ endif
 default_cflags = machine_args
 default_cflags += ['-DALLOW_EXPERIMENTAL_API']
 default_cflags += ['-DALLOW_INTERNAL_API']
+default_cflags += ['-DRTE_EXPORT_SYMBOL(a)=']
 
 if cc.has_argument('-Wno-format-truncation')
     default_cflags += '-Wno-format-truncation'
@@ -254,6 +255,9 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
+    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
+    lk_deps = []
+
     if not use_function_versioning or is_windows
         # use pre-build objects to build shared lib
         sources = []
@@ -262,10 +266,19 @@ foreach l:libraries
         # for compat we need to rebuild with
         # RTE_BUILD_SHARED_LIB defined
         cflags += '-DRTE_BUILD_SHARED_LIB'
-    endif
 
-    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-    lk_deps = [version_map]
+        # POC: generate version.map if absent.
+        if not fs.is_file(version_map)
+            map_file = custom_target(libname + '_map',
+                    command: [gen_version_map, '@OUTPUT@', '@INPUT@'],
+                    input: sources,
+                    output: '@0@_version.map'.format(libname))
+            version_map = map_file.full_path()
+            lk_deps += [map_file]
+        else
+            lk_deps += [version_map]
+        endif
+    endif
 
     if is_ms_linker
         def_file = custom_target(libname + '_def',
diff --git a/lib/net/rte_arp.c b/lib/net/rte_arp.c
index 22af519586..cd0f49a7a9 100644
--- a/lib/net/rte_arp.c
+++ b/lib/net/rte_arp.c
@@ -47,3 +47,4 @@ rte_net_make_rarp_packet(struct rte_mempool *mpool,
 
 	return mbuf;
 }
+RTE_EXPORT_SYMBOL(rte_net_make_rarp_packet)
diff --git a/lib/net/rte_ether.c b/lib/net/rte_ether.c
index f59c20289d..9d02db1676 100644
--- a/lib/net/rte_ether.c
+++ b/lib/net/rte_ether.c
@@ -17,6 +17,7 @@ rte_eth_random_addr(uint8_t *addr)
 	addr[0] &= (uint8_t)~RTE_ETHER_GROUP_ADDR;	/* clear multicast bit */
 	addr[0] |= RTE_ETHER_LOCAL_ADMIN_ADDR;	/* set local assignment bit */
 }
+RTE_EXPORT_SYMBOL(rte_eth_random_addr)
 
 void
 rte_ether_format_addr(char *buf, uint16_t size,
@@ -25,6 +26,7 @@ rte_ether_format_addr(char *buf, uint16_t size,
 	snprintf(buf, size, RTE_ETHER_ADDR_PRT_FMT,
 		RTE_ETHER_ADDR_BYTES(eth_addr));
 }
+RTE_EXPORT_SYMBOL(rte_ether_format_addr)
 
 static int8_t get_xdigit(char ch)
 {
@@ -153,3 +155,4 @@ rte_ether_unformat_addr(const char *s, struct rte_ether_addr *ea)
 	rte_errno = EINVAL;
 	return -1;
 }
+RTE_EXPORT_SYMBOL(rte_ether_unformat_addr)
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index 0c32e78a13..9a1bc3fb7d 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -306,6 +306,7 @@ rte_net_skip_ip6_ext(uint16_t proto, const struct rte_mbuf *m, uint32_t *off,
 	}
 	return -1;
 }
+RTE_EXPORT_SYMBOL(rte_net_skip_ip6_ext)
 
 /* parse mbuf data to get packet type */
 uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
@@ -601,3 +602,4 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
 
 	return pkt_type;
 }
+RTE_EXPORT_SYMBOL(rte_net_get_ptype)
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index dd93d43c2e..03be816509 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -417,6 +417,7 @@ void rte_net_crc_free(struct rte_net_crc *crc)
 {
 	rte_free(crc);
 }
+RTE_EXPORT_SYMBOL(rte_net_crc_free)
 
 RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
 	enum rte_net_crc_type type)
diff --git a/lib/net/version.map b/lib/net/version.map
deleted file mode 100644
index f4dd673fa3..0000000000
--- a/lib/net/version.map
+++ /dev/null
@@ -1,23 +0,0 @@
-DPDK_25 {
-	global:
-
-	rte_eth_random_addr;
-	rte_ether_format_addr;
-	rte_ether_unformat_addr;
-	rte_net_crc_calc;
-	rte_net_crc_free;
-	rte_net_crc_set_alg;
-	rte_net_get_ptype;
-	rte_net_make_rarp_packet;
-	rte_net_skip_ip6_ext;
-
-	local: *;
-};
-
-DPDK_26 {
-	global:
-
-	rte_net_crc_calc;
-	rte_net_crc_set_alg;
-
-} DPDK_25;
diff --git a/meson.build b/meson.build
index 8436d1dff8..dfb4cb3aee 100644
--- a/meson.build
+++ b/meson.build
@@ -13,10 +13,11 @@ project('DPDK', 'c',
         meson_version: '>= 0.57'
 )
 
+fs = import('fs')
+
 # check for developer mode
 developer_mode = false
 if get_option('developer_mode').auto()
-    fs = import('fs')
     developer_mode = fs.exists('.git')
 else
     developer_mode = get_option('developer_mode').enabled()
-- 
2.48.1


^ permalink raw reply	[relevance 7%]

* Re: [PATCH] ethdev: fix get_reg_info
  @ 2025-03-07  9:33  3%   ` fengchengwen
  0 siblings, 0 replies; 153+ results
From: fengchengwen @ 2025-03-07  9:33 UTC (permalink / raw)
  To: Stephen Hemminger, Thierry Herbelot; +Cc: dev, Thomas Monjalon, stable

On 2025/2/20 2:45, Stephen Hemminger wrote:
> On Tue, 18 Feb 2025 12:58:28 +0100
> Thierry Herbelot <thierry.herbelot@6wind.com> wrote:
> 
>> 'width' and 'offset' are input parameters when dumping the register
>> info of an Ethernet device. They should be copied in the new request
>> before calling the device callback function.
>>
>> Fixes: 083db2ed9e9 ('ethdev: add report of register names and filter')
>> Cc: stable@dpdk.org
>>
>> Signed-off-by: Thierry Herbelot <thierry.herbelot@6wind.com>
> 
> Why does the ethdev code create an on stack temporary variable.
> Looks like it only wants to make sure that names element is NULL.

It mainly for ABI compatibility.

The original solution is to add an ext API (rte_eth_dev_get_reg_info_ext) and deprecate the original API (rte_eth_dev_get_reg_info).

> 
> Really should be one function and when extended fields were added
> should have used API versioning.
> Probably too late now, although rte_eth_dev_get_reg_info_ext()
> is an experimental API.




^ permalink raw reply	[relevance 3%]

* [RFC v3 0/8] Symbol versioning and export rework
  2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
  2025-03-06 12:50  6% ` [RFC v2 1/2] " David Marchand
@ 2025-03-11  9:55  3% ` David Marchand
  2025-03-11  9:56 13%   ` [RFC v3 3/8] eal: rework function versioning macros David Marchand
                     ` (3 more replies)
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
                   ` (3 subsequent siblings)
  5 siblings, 4 replies; 153+ results
From: David Marchand @ 2025-03-11  9:55 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

So far, each DPDK library (or driver) exposing symbols in an ABI had to
maintain a version.map and use some macros for symbol versioning,
specially crafted with the GNU linker in mind.

This series proposes to rework the whole principle, and instead rely on
marking the symbol exports in the source code itself, then let it to the
build framework to produce a version script adapted to the linker in use
(think GNU linker vs MSVC linker).

This greatly simplifies versioning symbols: a developer does not need to
know anything about version.map, or that a versioned symbol must be
renamed with _v26, annotated with __vsym, exported in a header etc...

Checking symbol maps becomes unnecessary since generated by the build
framework.

Updating to a new ABI is just a matter of bumping the value in
ABI_VERSION.


Comments please.


-- 
David Marchand

Changes since RFC v2:
- updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
  signature is enclosed in the macro,
- dropped invalid exports for some dead symbols or inline helpers,
- updated documentation and tooling,
- converted the whole tree (via a local script of mine),

David Marchand (8):
  lib: remove incorrect exported symbols
  drivers: remove incorrect exported symbols
  eal: rework function versioning macros
  buildtools: display version when listing symbols
  build: generate symbol maps
  build: mark exported symbols
  build: use dynamically generated version maps
  build: remove static version maps

 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   9 +-
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/gen-version-map.py                 | 111 ++++
 buildtools/map-list-symbol.sh                 |  15 +-
 buildtools/map_to_win.py                      |  41 --
 buildtools/meson.build                        |   2 +-
 config/meson.build                            |   2 +
 config/rte_export.h                           |  16 +
 devtools/check-symbol-change.py               |  90 +++
 devtools/check-symbol-change.sh               | 186 ------
 devtools/check-symbol-maps.sh                 | 115 ----
 devtools/checkpatches.sh                      |   4 +-
 devtools/update-abi.sh                        |  46 --
 devtools/update_version_map_abi.py            | 210 -------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/abi_versioning.rst    | 385 ++----------
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 +++++----
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/baseband/acc/rte_acc100_pmd.c         |   1 +
 drivers/baseband/acc/version.map              |  10 -
 .../fpga_5gnr_fec/rte_fpga_5gnr_fec.c         |   1 +
 drivers/baseband/fpga_5gnr_fec/version.map    |  11 -
 drivers/baseband/fpga_lte_fec/fpga_lte_fec.c  |   1 +
 drivers/baseband/fpga_lte_fec/version.map     |  10 -
 drivers/bus/auxiliary/auxiliary_common.c      |   2 +
 drivers/bus/auxiliary/version.map             |   8 -
 drivers/bus/cdx/cdx.c                         |   4 +
 drivers/bus/cdx/cdx_vfio.c                    |   4 +
 drivers/bus/cdx/version.map                   |  14 -
 drivers/bus/dpaa/dpaa_bus.c                   | 104 ++++
 drivers/bus/dpaa/version.map                  | 109 ----
 drivers/bus/fslmc/fslmc_bus.c                 |   4 +
 drivers/bus/fslmc/fslmc_vfio.c                |  12 +
 drivers/bus/fslmc/mc/dpbp.c                   |   6 +
 drivers/bus/fslmc/mc/dpci.c                   |   3 +
 drivers/bus/fslmc/mc/dpcon.c                  |   6 +
 drivers/bus/fslmc/mc/dpdmai.c                 |   8 +
 drivers/bus/fslmc/mc/dpio.c                   |  13 +
 drivers/bus/fslmc/mc/dpmng.c                  |   2 +
 drivers/bus/fslmc/mc/mc_sys.c                 |   1 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpbp.c      |   3 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpci.c      |   2 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpio.c      |  11 +
 drivers/bus/fslmc/qbman/qbman_debug.c         |   2 +
 drivers/bus/fslmc/qbman/qbman_portal.c        |  41 ++
 drivers/bus/fslmc/version.map                 | 129 ----
 drivers/bus/ifpga/ifpga_bus.c                 |   3 +
 drivers/bus/ifpga/version.map                 |   9 -
 drivers/bus/pci/bsd/pci.c                     |  10 +
 drivers/bus/pci/linux/pci.c                   |  10 +
 drivers/bus/pci/pci_common.c                  |  10 +
 drivers/bus/pci/version.map                   |  43 --
 drivers/bus/pci/windows/pci.c                 |  10 +
 drivers/bus/platform/platform.c               |   2 +
 drivers/bus/platform/version.map              |  10 -
 drivers/bus/uacce/uacce.c                     |   9 +
 drivers/bus/uacce/version.map                 |  15 -
 drivers/bus/vdev/vdev.c                       |   6 +
 drivers/bus/vdev/version.map                  |  17 -
 drivers/bus/vmbus/linux/vmbus_bus.c           |   6 +
 drivers/bus/vmbus/version.map                 |  33 -
 drivers/bus/vmbus/vmbus_channel.c             |  13 +
 drivers/bus/vmbus/vmbus_common.c              |   3 +
 drivers/common/cnxk/cnxk_security.c           |  12 +
 drivers/common/cnxk/cnxk_utils.c              |   1 +
 drivers/common/cnxk/roc_platform.c            | 559 +++++++++++++++++
 drivers/common/cnxk/roc_se.h                  |   1 -
 drivers/common/cnxk/version.map               | 578 ------------------
 drivers/common/cpt/cpt_fpm_tables.c           |   2 +
 drivers/common/cpt/cpt_pmd_ops_helper.c       |   3 +
 drivers/common/cpt/version.map                |  11 -
 drivers/common/dpaax/caamflib.c               |   1 +
 drivers/common/dpaax/dpaa_of.c                |  12 +
 drivers/common/dpaax/dpaax_iova_table.c       |   6 +
 drivers/common/dpaax/version.map              |  25 -
 drivers/common/ionic/ionic_common_uio.c       |   4 +
 drivers/common/ionic/version.map              |  10 -
 .../common/mlx5/linux/mlx5_common_auxiliary.c |   1 +
 drivers/common/mlx5/linux/mlx5_common_os.c    |   9 +
 drivers/common/mlx5/linux/mlx5_common_verbs.c |   3 +
 drivers/common/mlx5/linux/mlx5_glue.c         |   1 +
 drivers/common/mlx5/linux/mlx5_nl.c           |  21 +
 drivers/common/mlx5/mlx5_common.c             |   9 +
 drivers/common/mlx5/mlx5_common_devx.c        |   9 +
 drivers/common/mlx5/mlx5_common_mp.c          |   8 +
 drivers/common/mlx5/mlx5_common_mr.c          |  11 +
 drivers/common/mlx5/mlx5_common_pci.c         |   2 +
 drivers/common/mlx5/mlx5_common_utils.c       |  11 +
 drivers/common/mlx5/mlx5_devx_cmds.c          |  51 ++
 drivers/common/mlx5/mlx5_malloc.c             |   4 +
 drivers/common/mlx5/version.map               | 174 ------
 drivers/common/mlx5/windows/mlx5_common_os.c  |   5 +
 drivers/common/mlx5/windows/mlx5_glue.c       |   3 +-
 drivers/common/mvep/mvep_common.c             |   2 +
 drivers/common/mvep/version.map               |   8 -
 drivers/common/nfp/nfp_common.c               |   7 +
 drivers/common/nfp/nfp_common_pci.c           |   1 +
 drivers/common/nfp/nfp_dev.c                  |   1 +
 drivers/common/nfp/version.map                |  16 -
 drivers/common/nitrox/nitrox_device.c         |   1 +
 drivers/common/nitrox/nitrox_logs.c           |   1 +
 drivers/common/nitrox/nitrox_qp.c             |   2 +
 drivers/common/nitrox/version.map             |  10 -
 drivers/common/octeontx/octeontx_mbox.c       |   6 +
 drivers/common/octeontx/version.map           |  12 -
 drivers/common/sfc_efx/sfc_efx.c              | 273 +++++++++
 drivers/common/sfc_efx/sfc_efx_mcdi.c         |   2 +
 drivers/common/sfc_efx/version.map            | 302 ---------
 drivers/crypto/cnxk/cn10k_cryptodev_ops.c     |   7 +
 drivers/crypto/cnxk/cn9k_cryptodev_ops.c      |   2 +
 drivers/crypto/cnxk/cnxk_cryptodev_ops.c      |   7 +
 drivers/crypto/cnxk/version.map               |  30 -
 drivers/crypto/dpaa2_sec/dpaa2_sec_dpseci.c   |   2 +
 drivers/crypto/dpaa2_sec/version.map          |   8 -
 drivers/crypto/dpaa_sec/dpaa_sec.c            |   2 +
 drivers/crypto/dpaa_sec/version.map           |   8 -
 drivers/crypto/octeontx/otx_cryptodev_ops.c   |   2 +
 drivers/crypto/octeontx/version.map           |  12 -
 .../scheduler/rte_cryptodev_scheduler.c       |  10 +
 drivers/crypto/scheduler/version.map          |  16 -
 drivers/dma/cnxk/cnxk_dmadev_fp.c             |   4 +
 drivers/dma/cnxk/version.map                  |  10 -
 drivers/event/cnxk/cnxk_worker.c              |   2 +
 drivers/event/cnxk/version.map                |  11 -
 drivers/event/dlb2/rte_pmd_dlb2.c             |   1 +
 drivers/event/dlb2/version.map                |  10 -
 drivers/mempool/cnxk/cn10k_hwpool_ops.c       |   3 +
 drivers/mempool/cnxk/version.map              |  12 -
 drivers/mempool/dpaa/dpaa_mempool.c           |   2 +
 drivers/mempool/dpaa/version.map              |   8 -
 drivers/mempool/dpaa2/dpaa2_hw_mempool.c      |   5 +
 drivers/mempool/dpaa2/version.map             |  16 -
 drivers/meson.build                           |  72 +--
 drivers/net/atlantic/rte_pmd_atlantic.c       |   6 +
 drivers/net/atlantic/version.map              |  15 -
 drivers/net/bnxt/rte_pmd_bnxt.c               |  16 +
 drivers/net/bnxt/version.map                  |  22 -
 drivers/net/bonding/rte_eth_bond_8023ad.c     |  12 +
 drivers/net/bonding/rte_eth_bond_api.c        |  15 +
 drivers/net/bonding/version.map               |  33 -
 drivers/net/cnxk/cnxk_ethdev.c                |   3 +
 drivers/net/cnxk/cnxk_ethdev_sec.c            |   9 +
 drivers/net/cnxk/version.map                  |  27 -
 drivers/net/dpaa/dpaa_ethdev.c                |   3 +
 drivers/net/dpaa/version.map                  |  14 -
 drivers/net/dpaa2/dpaa2_ethdev.c              |  11 +
 drivers/net/dpaa2/dpaa2_mux.c                 |   3 +
 drivers/net/dpaa2/dpaa2_rxtx.c                |   1 +
 drivers/net/dpaa2/version.map                 |  35 --
 drivers/net/intel/i40e/rte_pmd_i40e.c         |  39 ++
 drivers/net/intel/i40e/version.map            |  55 --
 drivers/net/intel/iavf/iavf_ethdev.c          |   9 +
 drivers/net/intel/iavf/iavf_rxtx.c            |   8 +
 drivers/net/intel/iavf/version.map            |  33 -
 drivers/net/intel/ice/ice_diagnose.c          |   3 +
 drivers/net/intel/ice/version.map             |  16 -
 drivers/net/intel/idpf/idpf_common_device.c   |  10 +
 drivers/net/intel/idpf/idpf_common_rxtx.c     |  33 +
 drivers/net/intel/idpf/idpf_common_virtchnl.c |  29 +
 drivers/net/intel/idpf/version.map            |  80 ---
 drivers/net/intel/ipn3ke/ipn3ke_ethdev.c      |   1 +
 drivers/net/intel/ipn3ke/version.map          |   9 -
 drivers/net/intel/ixgbe/rte_pmd_ixgbe.c       |  37 ++
 drivers/net/intel/ixgbe/version.map           |  49 --
 drivers/net/mlx5/mlx5.c                       |   1 +
 drivers/net/mlx5/mlx5_flow.c                  |   4 +
 drivers/net/mlx5/mlx5_rx.c                    |   2 +
 drivers/net/mlx5/mlx5_rxq.c                   |   2 +
 drivers/net/mlx5/mlx5_tx.c                    |   1 +
 drivers/net/mlx5/mlx5_txq.c                   |   3 +
 drivers/net/mlx5/version.map                  |  28 -
 drivers/net/octeontx/octeontx_ethdev.c        |   1 +
 drivers/net/octeontx/version.map              |   7 -
 drivers/net/ring/rte_eth_ring.c               |   2 +
 drivers/net/ring/version.map                  |   8 -
 drivers/net/softnic/rte_eth_softnic.c         |   1 +
 drivers/net/softnic/rte_eth_softnic_thread.c  |   1 +
 drivers/net/softnic/version.map               |   8 -
 drivers/net/vhost/rte_eth_vhost.c             |   2 +
 drivers/net/vhost/version.map                 |   8 -
 drivers/power/kvm_vm/guest_channel.c          |   2 +
 drivers/power/kvm_vm/version.map              |   8 -
 drivers/raw/cnxk_rvu_lf/cnxk_rvu_lf.c         |  10 +
 drivers/raw/cnxk_rvu_lf/version.map           |  16 -
 drivers/raw/ifpga/rte_pmd_ifpga.c             |  11 +
 drivers/raw/ifpga/version.map                 |  17 -
 drivers/version.map                           |   3 -
 lib/acl/acl_bld.c                             |   1 +
 lib/acl/acl_run_scalar.c                      |   1 +
 lib/acl/rte_acl.c                             |  11 +
 lib/acl/version.map                           |  19 -
 lib/argparse/rte_argparse.c                   |   2 +
 lib/argparse/version.map                      |   9 -
 lib/bbdev/bbdev_trace_points.c                |   2 +
 lib/bbdev/rte_bbdev.c                         |  31 +
 lib/bbdev/version.map                         |  47 --
 lib/bitratestats/rte_bitrate.c                |   4 +
 lib/bitratestats/version.map                  |  10 -
 lib/bpf/bpf.c                                 |   2 +
 lib/bpf/bpf_convert.c                         |   1 +
 lib/bpf/bpf_dump.c                            |   1 +
 lib/bpf/bpf_exec.c                            |   2 +
 lib/bpf/bpf_load.c                            |   1 +
 lib/bpf/bpf_load_elf.c                        |   1 +
 lib/bpf/bpf_pkt.c                             |   4 +
 lib/bpf/bpf_stub.c                            |   2 +
 lib/bpf/version.map                           |  18 -
 lib/cfgfile/rte_cfgfile.c                     |  17 +
 lib/cfgfile/version.map                       |  23 -
 lib/cmdline/cmdline.c                         |   9 +
 lib/cmdline/cmdline_cirbuf.c                  |  19 +
 lib/cmdline/cmdline_parse.c                   |   4 +
 lib/cmdline/cmdline_parse_bool.c              |   1 +
 lib/cmdline/cmdline_parse_etheraddr.c         |   3 +
 lib/cmdline/cmdline_parse_ipaddr.c            |   3 +
 lib/cmdline/cmdline_parse_num.c               |   3 +
 lib/cmdline/cmdline_parse_portlist.c          |   3 +
 lib/cmdline/cmdline_parse_string.c            |   5 +
 lib/cmdline/cmdline_rdline.c                  |  15 +
 lib/cmdline/cmdline_socket.c                  |   3 +
 lib/cmdline/cmdline_vt100.c                   |   2 +
 lib/cmdline/version.map                       |  82 ---
 lib/compressdev/rte_comp.c                    |   6 +
 lib/compressdev/rte_compressdev.c             |  25 +
 lib/compressdev/rte_compressdev_pmd.c         |   3 +
 lib/compressdev/version.map                   |  40 --
 lib/cryptodev/cryptodev_pmd.c                 |   7 +
 lib/cryptodev/cryptodev_trace_points.c        |   3 +
 lib/cryptodev/rte_cryptodev.c                 |  83 +++
 lib/cryptodev/version.map                     | 114 ----
 lib/dispatcher/rte_dispatcher.c               |  13 +
 lib/dispatcher/version.map                    |  20 -
 lib/distributor/rte_distributor.c             |   9 +
 lib/distributor/version.map                   |  15 -
 lib/dmadev/rte_dmadev.c                       |  19 +
 lib/dmadev/rte_dmadev_trace_points.c          |   7 +
 lib/dmadev/version.map                        |  47 --
 lib/eal/arm/rte_cpuflags.c                    |   3 +
 lib/eal/arm/rte_hypervisor.c                  |   1 +
 lib/eal/arm/rte_power_intrinsics.c            |   4 +
 lib/eal/common/eal_common_bus.c               |  10 +
 lib/eal/common/eal_common_class.c             |   4 +
 lib/eal/common/eal_common_config.c            |   7 +
 lib/eal/common/eal_common_cpuflags.c          |   1 +
 lib/eal/common/eal_common_debug.c             |   2 +
 lib/eal/common/eal_common_dev.c               |  19 +
 lib/eal/common/eal_common_devargs.c           |   9 +
 lib/eal/common/eal_common_errno.c             |   2 +
 lib/eal/common/eal_common_fbarray.c           |  26 +
 lib/eal/common/eal_common_hexdump.c           |   2 +
 lib/eal/common/eal_common_hypervisor.c        |   1 +
 lib/eal/common/eal_common_interrupts.c        |  27 +
 lib/eal/common/eal_common_launch.c            |   5 +
 lib/eal/common/eal_common_lcore.c             |  17 +
 lib/eal/common/eal_common_lcore_var.c         |   1 +
 lib/eal/common/eal_common_mcfg.c              |  20 +
 lib/eal/common/eal_common_memory.c            |  29 +
 lib/eal/common/eal_common_memzone.c           |   9 +
 lib/eal/common/eal_common_options.c           |   4 +
 lib/eal/common/eal_common_proc.c              |   8 +
 lib/eal/common/eal_common_string_fns.c        |   3 +
 lib/eal/common/eal_common_tailqs.c            |   3 +
 lib/eal/common/eal_common_thread.c            |  14 +
 lib/eal/common/eal_common_timer.c             |   4 +
 lib/eal/common/eal_common_trace.c             |  15 +
 lib/eal/common/eal_common_trace_ctf.c         |   1 +
 lib/eal/common/eal_common_trace_points.c      |  18 +
 lib/eal/common/eal_common_trace_utils.c       |   1 +
 lib/eal/common/eal_common_uuid.c              |   4 +
 lib/eal/common/rte_bitset.c                   |   1 +
 lib/eal/common/rte_keepalive.c                |   6 +
 lib/eal/common/rte_malloc.c                   |  22 +
 lib/eal/common/rte_random.c                   |   4 +
 lib/eal/common/rte_reciprocal.c               |   2 +
 lib/eal/common/rte_service.c                  |  31 +
 lib/eal/common/rte_version.c                  |   7 +
 lib/eal/freebsd/eal.c                         |  22 +
 lib/eal/freebsd/eal_alarm.c                   |   2 +
 lib/eal/freebsd/eal_dev.c                     |   4 +
 lib/eal/freebsd/eal_interrupts.c              |  19 +
 lib/eal/freebsd/eal_memory.c                  |   3 +
 lib/eal/freebsd/eal_thread.c                  |   2 +
 lib/eal/freebsd/eal_timer.c                   |   1 +
 lib/eal/include/rte_function_versioning.h     |  96 ++-
 lib/eal/linux/eal.c                           |   7 +
 lib/eal/linux/eal_alarm.c                     |   2 +
 lib/eal/linux/eal_dev.c                       |   4 +
 lib/eal/linux/eal_interrupts.c                |  19 +
 lib/eal/linux/eal_memory.c                    |   3 +
 lib/eal/linux/eal_thread.c                    |   2 +
 lib/eal/linux/eal_timer.c                     |   4 +
 lib/eal/linux/eal_vfio.c                      |  16 +
 lib/eal/loongarch/rte_cpuflags.c              |   3 +
 lib/eal/loongarch/rte_hypervisor.c            |   1 +
 lib/eal/loongarch/rte_power_intrinsics.c      |   4 +
 lib/eal/ppc/rte_cpuflags.c                    |   3 +
 lib/eal/ppc/rte_hypervisor.c                  |   1 +
 lib/eal/ppc/rte_power_intrinsics.c            |   4 +
 lib/eal/riscv/rte_cpuflags.c                  |   3 +
 lib/eal/riscv/rte_hypervisor.c                |   1 +
 lib/eal/riscv/rte_power_intrinsics.c          |   4 +
 lib/eal/unix/eal_debug.c                      |   2 +
 lib/eal/unix/eal_filesystem.c                 |   1 +
 lib/eal/unix/eal_firmware.c                   |   1 +
 lib/eal/unix/eal_unix_memory.c                |   4 +
 lib/eal/unix/eal_unix_timer.c                 |   1 +
 lib/eal/unix/rte_thread.c                     |  13 +
 lib/eal/version.map                           | 451 --------------
 lib/eal/windows/eal.c                         |  11 +
 lib/eal/windows/eal_alarm.c                   |   2 +
 lib/eal/windows/eal_debug.c                   |   1 +
 lib/eal/windows/eal_dev.c                     |   4 +
 lib/eal/windows/eal_interrupts.c              |  19 +
 lib/eal/windows/eal_memory.c                  |   7 +
 lib/eal/windows/eal_mp.c                      |   6 +
 lib/eal/windows/eal_thread.c                  |   1 +
 lib/eal/windows/eal_timer.c                   |   1 +
 lib/eal/windows/rte_thread.c                  |  14 +
 lib/eal/x86/rte_cpuflags.c                    |   3 +
 lib/eal/x86/rte_hypervisor.c                  |   1 +
 lib/eal/x86/rte_power_intrinsics.c            |   4 +
 lib/eal/x86/rte_spinlock.c                    |   1 +
 lib/efd/rte_efd.c                             |   7 +
 lib/efd/version.map                           |  13 -
 lib/ethdev/ethdev_driver.c                    |  24 +
 lib/ethdev/ethdev_linux_ethtool.c             |   3 +
 lib/ethdev/ethdev_private.c                   |   2 +
 lib/ethdev/ethdev_trace_points.c              |   6 +
 lib/ethdev/rte_ethdev.c                       | 168 +++++
 lib/ethdev/rte_ethdev_cman.c                  |   4 +
 lib/ethdev/rte_flow.c                         |  64 ++
 lib/ethdev/rte_mtr.c                          |  21 +
 lib/ethdev/rte_tm.c                           |  31 +
 lib/ethdev/version.map                        | 378 ------------
 lib/eventdev/eventdev_private.c               |   2 +
 lib/eventdev/eventdev_trace_points.c          |  11 +
 lib/eventdev/rte_event_crypto_adapter.c       |  15 +
 lib/eventdev/rte_event_dma_adapter.c          |  15 +
 lib/eventdev/rte_event_eth_rx_adapter.c       |  23 +
 lib/eventdev/rte_event_eth_tx_adapter.c       |  17 +
 lib/eventdev/rte_event_ring.c                 |   4 +
 lib/eventdev/rte_event_timer_adapter.c        |  11 +
 lib/eventdev/rte_eventdev.c                   |  46 ++
 lib/eventdev/version.map                      | 179 ------
 lib/fib/rte_fib.c                             |  10 +
 lib/fib/rte_fib6.c                            |   9 +
 lib/fib/version.map                           |  31 -
 lib/gpudev/gpudev.c                           |  32 +
 lib/gpudev/version.map                        |  44 --
 lib/graph/graph.c                             |  16 +
 lib/graph/graph_debug.c                       |   1 +
 lib/graph/graph_stats.c                       |   4 +
 lib/graph/node.c                              |  11 +
 lib/graph/rte_graph_model_mcore_dispatch.c    |   3 +
 lib/graph/rte_graph_worker.c                  |   3 +
 lib/graph/version.map                         |  61 --
 lib/gro/rte_gro.c                             |   6 +
 lib/gro/version.map                           |  12 -
 lib/gso/rte_gso.c                             |   1 +
 lib/gso/version.map                           |   7 -
 lib/hash/rte_cuckoo_hash.c                    |  27 +
 lib/hash/rte_fbk_hash.c                       |   3 +
 lib/hash/rte_hash_crc.c                       |   2 +
 lib/hash/rte_thash.c                          |  12 +
 lib/hash/rte_thash_gf2_poly_math.c            |   1 +
 lib/hash/rte_thash_gfni.c                     |   2 +
 lib/hash/version.map                          |  66 --
 lib/ip_frag/rte_ip_frag_common.c              |   5 +
 lib/ip_frag/rte_ipv4_fragmentation.c          |   2 +
 lib/ip_frag/rte_ipv4_reassembly.c             |   1 +
 lib/ip_frag/rte_ipv6_fragmentation.c          |   1 +
 lib/ip_frag/rte_ipv6_reassembly.c             |   1 +
 lib/ip_frag/version.map                       |  16 -
 lib/ipsec/ipsec_sad.c                         |   6 +
 lib/ipsec/ipsec_telemetry.c                   |   2 +
 lib/ipsec/sa.c                                |   4 +
 lib/ipsec/ses.c                               |   1 +
 lib/ipsec/version.map                         |  23 -
 lib/jobstats/rte_jobstats.c                   |  14 +
 lib/jobstats/version.map                      |  20 -
 lib/kvargs/rte_kvargs.c                       |   8 +
 lib/kvargs/version.map                        |  14 -
 lib/latencystats/rte_latencystats.c           |   5 +
 lib/latencystats/version.map                  |  11 -
 lib/log/log.c                                 |  22 +
 lib/log/log_color.c                           |   1 +
 lib/log/log_internal.h                        |   3 -
 lib/log/log_syslog.c                          |   1 +
 lib/log/log_timestamp.c                       |   1 +
 lib/log/version.map                           |  37 --
 lib/lpm/rte_lpm.c                             |   8 +
 lib/lpm/rte_lpm6.c                            |  10 +
 lib/lpm/version.map                           |  24 -
 lib/mbuf/rte_mbuf.c                           |  17 +
 lib/mbuf/rte_mbuf_dyn.c                       |   9 +
 lib/mbuf/rte_mbuf_pool_ops.c                  |   5 +
 lib/mbuf/rte_mbuf_ptype.c                     |   8 +
 lib/mbuf/version.map                          |  45 --
 lib/member/rte_member.c                       |  13 +
 lib/member/version.map                        |  19 -
 lib/mempool/mempool_trace_points.c            |  10 +
 lib/mempool/rte_mempool.c                     |  27 +
 lib/mempool/rte_mempool_ops.c                 |   4 +
 lib/mempool/rte_mempool_ops_default.c         |   4 +
 lib/mempool/version.map                       |  65 --
 lib/meson.build                               |  56 +-
 lib/meter/rte_meter.c                         |   6 +
 lib/meter/version.map                         |  12 -
 lib/metrics/rte_metrics.c                     |   8 +
 lib/metrics/rte_metrics_telemetry.c           |  11 +
 lib/metrics/version.map                       |  26 -
 lib/mldev/mldev_utils.c                       |   2 +
 lib/mldev/mldev_utils_neon.c                  |  18 +
 lib/mldev/mldev_utils_neon_bfloat16.c         |   2 +
 lib/mldev/mldev_utils_scalar.c                |  18 +
 lib/mldev/mldev_utils_scalar_bfloat16.c       |   2 +
 lib/mldev/rte_mldev.c                         |  37 ++
 lib/mldev/rte_mldev_pmd.c                     |   2 +
 lib/mldev/version.map                         |  74 ---
 lib/net/net_crc.h                             |  15 -
 lib/net/rte_arp.c                             |   1 +
 lib/net/rte_ether.c                           |   3 +
 lib/net/rte_net.c                             |   2 +
 lib/net/rte_net_crc.c                         |  29 +-
 lib/net/version.map                           |  23 -
 lib/node/ethdev_ctrl.c                        |   2 +
 lib/node/ip4_lookup.c                         |   1 +
 lib/node/ip4_reassembly.c                     |   1 +
 lib/node/ip4_rewrite.c                        |   1 +
 lib/node/ip6_lookup.c                         |   1 +
 lib/node/ip6_rewrite.c                        |   1 +
 lib/node/udp4_input.c                         |   2 +
 lib/node/version.map                          |  25 -
 lib/pcapng/rte_pcapng.c                       |   7 +
 lib/pcapng/version.map                        |  13 -
 lib/pci/rte_pci.c                             |   3 +
 lib/pci/version.map                           |   9 -
 lib/pdcp/rte_pdcp.c                           |   5 +
 lib/pdcp/version.map                          |  16 -
 lib/pdump/rte_pdump.c                         |   9 +
 lib/pdump/version.map                         |  15 -
 lib/pipeline/rte_pipeline.c                   |  23 +
 lib/pipeline/rte_port_in_action.c             |   8 +
 lib/pipeline/rte_swx_ctl.c                    |  17 +
 lib/pipeline/rte_swx_ipsec.c                  |   7 +
 lib/pipeline/rte_swx_pipeline.c               |  73 +++
 lib/pipeline/rte_table_action.c               |  16 +
 lib/pipeline/version.map                      | 172 ------
 lib/port/rte_port_ethdev.c                    |   3 +
 lib/port/rte_port_eventdev.c                  |   3 +
 lib/port/rte_port_fd.c                        |   3 +
 lib/port/rte_port_frag.c                      |   2 +
 lib/port/rte_port_ras.c                       |   2 +
 lib/port/rte_port_ring.c                      |   6 +
 lib/port/rte_port_sched.c                     |   2 +
 lib/port/rte_port_source_sink.c               |   2 +
 lib/port/rte_port_sym_crypto.c                |   3 +
 lib/port/rte_swx_port_ethdev.c                |   2 +
 lib/port/rte_swx_port_fd.c                    |   2 +
 lib/port/rte_swx_port_ring.c                  |   2 +
 lib/port/rte_swx_port_source_sink.c           |   3 +
 lib/port/version.map                          |  50 --
 lib/power/power_common.c                      |   8 +
 lib/power/rte_power_cpufreq.c                 |  18 +
 lib/power/rte_power_pmd_mgmt.c                |  10 +
 lib/power/rte_power_qos.c                     |   2 +
 lib/power/rte_power_uncore.c                  |  14 +
 lib/power/version.map                         |  71 ---
 lib/rawdev/rte_rawdev.c                       |  30 +
 lib/rawdev/version.map                        |  36 --
 lib/rcu/rte_rcu_qsbr.c                        |  11 +
 lib/rcu/version.map                           |  17 -
 lib/regexdev/rte_regexdev.c                   |  26 +
 lib/regexdev/version.map                      |  40 --
 lib/reorder/rte_reorder.c                     |  11 +
 lib/reorder/version.map                       |  27 -
 lib/rib/rte_rib.c                             |  14 +
 lib/rib/rte_rib6.c                            |  14 +
 lib/rib/version.map                           |  34 --
 lib/ring/rte_ring.c                           |  11 +
 lib/ring/rte_soring.c                         |   3 +
 lib/ring/soring.c                             |  16 +
 lib/ring/version.map                          |  42 --
 lib/sched/rte_approx.c                        |   1 +
 lib/sched/rte_pie.c                           |   2 +
 lib/sched/rte_red.c                           |   6 +
 lib/sched/rte_sched.c                         |  15 +
 lib/sched/version.map                         |  30 -
 lib/security/rte_security.c                   |  20 +
 lib/security/version.map                      |  37 --
 lib/stack/rte_stack.c                         |   3 +
 lib/stack/version.map                         |   9 -
 lib/table/rte_swx_table_em.c                  |   2 +
 lib/table/rte_swx_table_learner.c             |  10 +
 lib/table/rte_swx_table_selector.c            |   6 +
 lib/table/rte_swx_table_wm.c                  |   1 +
 lib/table/rte_table_acl.c                     |   1 +
 lib/table/rte_table_array.c                   |   1 +
 lib/table/rte_table_hash_cuckoo.c             |   1 +
 lib/table/rte_table_hash_ext.c                |   1 +
 lib/table/rte_table_hash_key16.c              |   2 +
 lib/table/rte_table_hash_key32.c              |   2 +
 lib/table/rte_table_hash_key8.c               |   2 +
 lib/table/rte_table_hash_lru.c                |   1 +
 lib/table/rte_table_lpm.c                     |   1 +
 lib/table/rte_table_lpm_ipv6.c                |   1 +
 lib/table/rte_table_stub.c                    |   1 +
 lib/table/version.map                         |  53 --
 lib/telemetry/telemetry.c                     |   3 +
 lib/telemetry/telemetry_data.c                |  17 +
 lib/telemetry/telemetry_legacy.c              |   1 +
 lib/telemetry/version.map                     |  40 --
 lib/timer/rte_timer.c                         |  18 +
 lib/timer/version.map                         |  24 -
 lib/vhost/socket.c                            |  16 +
 lib/vhost/vdpa.c                              |  11 +
 lib/vhost/version.map                         | 111 ----
 lib/vhost/vhost.c                             |  41 ++
 lib/vhost/vhost_crypto.c                      |   6 +
 lib/vhost/vhost_user.c                        |   2 +
 lib/vhost/virtio_net.c                        |   7 +
 523 files changed, 4654 insertions(+), 6507 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 delete mode 100644 buildtools/map_to_win.py
 create mode 100644 config/rte_export.h
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py
 delete mode 100644 drivers/baseband/acc/version.map
 delete mode 100644 drivers/baseband/fpga_5gnr_fec/version.map
 delete mode 100644 drivers/baseband/fpga_lte_fec/version.map
 delete mode 100644 drivers/bus/auxiliary/version.map
 delete mode 100644 drivers/bus/cdx/version.map
 delete mode 100644 drivers/bus/dpaa/version.map
 delete mode 100644 drivers/bus/fslmc/version.map
 delete mode 100644 drivers/bus/ifpga/version.map
 delete mode 100644 drivers/bus/pci/version.map
 delete mode 100644 drivers/bus/platform/version.map
 delete mode 100644 drivers/bus/uacce/version.map
 delete mode 100644 drivers/bus/vdev/version.map
 delete mode 100644 drivers/bus/vmbus/version.map
 delete mode 100644 drivers/common/cnxk/version.map
 delete mode 100644 drivers/common/cpt/version.map
 delete mode 100644 drivers/common/dpaax/version.map
 delete mode 100644 drivers/common/ionic/version.map
 delete mode 100644 drivers/common/mlx5/version.map
 delete mode 100644 drivers/common/mvep/version.map
 delete mode 100644 drivers/common/nfp/version.map
 delete mode 100644 drivers/common/nitrox/version.map
 delete mode 100644 drivers/common/octeontx/version.map
 delete mode 100644 drivers/common/sfc_efx/version.map
 delete mode 100644 drivers/crypto/cnxk/version.map
 delete mode 100644 drivers/crypto/dpaa2_sec/version.map
 delete mode 100644 drivers/crypto/dpaa_sec/version.map
 delete mode 100644 drivers/crypto/octeontx/version.map
 delete mode 100644 drivers/crypto/scheduler/version.map
 delete mode 100644 drivers/dma/cnxk/version.map
 delete mode 100644 drivers/event/cnxk/version.map
 delete mode 100644 drivers/event/dlb2/version.map
 delete mode 100644 drivers/mempool/cnxk/version.map
 delete mode 100644 drivers/mempool/dpaa/version.map
 delete mode 100644 drivers/mempool/dpaa2/version.map
 delete mode 100644 drivers/net/atlantic/version.map
 delete mode 100644 drivers/net/bnxt/version.map
 delete mode 100644 drivers/net/bonding/version.map
 delete mode 100644 drivers/net/cnxk/version.map
 delete mode 100644 drivers/net/dpaa/version.map
 delete mode 100644 drivers/net/dpaa2/version.map
 delete mode 100644 drivers/net/intel/i40e/version.map
 delete mode 100644 drivers/net/intel/iavf/version.map
 delete mode 100644 drivers/net/intel/ice/version.map
 delete mode 100644 drivers/net/intel/idpf/version.map
 delete mode 100644 drivers/net/intel/ipn3ke/version.map
 delete mode 100644 drivers/net/intel/ixgbe/version.map
 delete mode 100644 drivers/net/mlx5/version.map
 delete mode 100644 drivers/net/octeontx/version.map
 delete mode 100644 drivers/net/ring/version.map
 delete mode 100644 drivers/net/softnic/version.map
 delete mode 100644 drivers/net/vhost/version.map
 delete mode 100644 drivers/power/kvm_vm/version.map
 delete mode 100644 drivers/raw/cnxk_rvu_lf/version.map
 delete mode 100644 drivers/raw/ifpga/version.map
 delete mode 100644 drivers/version.map
 delete mode 100644 lib/acl/version.map
 delete mode 100644 lib/argparse/version.map
 delete mode 100644 lib/bbdev/version.map
 delete mode 100644 lib/bitratestats/version.map
 delete mode 100644 lib/bpf/version.map
 delete mode 100644 lib/cfgfile/version.map
 delete mode 100644 lib/cmdline/version.map
 delete mode 100644 lib/compressdev/version.map
 delete mode 100644 lib/cryptodev/version.map
 delete mode 100644 lib/dispatcher/version.map
 delete mode 100644 lib/distributor/version.map
 delete mode 100644 lib/dmadev/version.map
 delete mode 100644 lib/eal/version.map
 delete mode 100644 lib/efd/version.map
 delete mode 100644 lib/ethdev/version.map
 delete mode 100644 lib/eventdev/version.map
 delete mode 100644 lib/fib/version.map
 delete mode 100644 lib/gpudev/version.map
 delete mode 100644 lib/graph/version.map
 delete mode 100644 lib/gro/version.map
 delete mode 100644 lib/gso/version.map
 delete mode 100644 lib/hash/version.map
 delete mode 100644 lib/ip_frag/version.map
 delete mode 100644 lib/ipsec/version.map
 delete mode 100644 lib/jobstats/version.map
 delete mode 100644 lib/kvargs/version.map
 delete mode 100644 lib/latencystats/version.map
 delete mode 100644 lib/log/version.map
 delete mode 100644 lib/lpm/version.map
 delete mode 100644 lib/mbuf/version.map
 delete mode 100644 lib/member/version.map
 delete mode 100644 lib/mempool/version.map
 delete mode 100644 lib/meter/version.map
 delete mode 100644 lib/metrics/version.map
 delete mode 100644 lib/mldev/version.map
 delete mode 100644 lib/net/version.map
 delete mode 100644 lib/node/version.map
 delete mode 100644 lib/pcapng/version.map
 delete mode 100644 lib/pci/version.map
 delete mode 100644 lib/pdcp/version.map
 delete mode 100644 lib/pdump/version.map
 delete mode 100644 lib/pipeline/version.map
 delete mode 100644 lib/port/version.map
 delete mode 100644 lib/power/version.map
 delete mode 100644 lib/rawdev/version.map
 delete mode 100644 lib/rcu/version.map
 delete mode 100644 lib/regexdev/version.map
 delete mode 100644 lib/reorder/version.map
 delete mode 100644 lib/rib/version.map
 delete mode 100644 lib/ring/version.map
 delete mode 100644 lib/sched/version.map
 delete mode 100644 lib/security/version.map
 delete mode 100644 lib/stack/version.map
 delete mode 100644 lib/table/version.map
 delete mode 100644 lib/telemetry/version.map
 delete mode 100644 lib/timer/version.map
 delete mode 100644 lib/vhost/version.map

-- 
2.48.1


^ permalink raw reply	[relevance 3%]

* [RFC v3 3/8] eal: rework function versioning macros
  2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
@ 2025-03-11  9:56 13%   ` David Marchand
  2025-03-13 16:53  0%     ` Bruce Richardson
  2025-03-11  9:56 18%   ` [RFC v3 5/8] build: generate symbol maps David Marchand
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-11  9:56 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
exporting and versioning symbols that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a symbol is just a call to a single macro:
- RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
  keeping an old implementation code under a versioned function (resp.
  experimental function),
- RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

Update lib/net accordingly.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v2:

Changes since RFC v1:
- renamed and prefixed macros,
- reindented in prevision of second patch,

---
 doc/guides/contributing/abi_versioning.rst | 165 +++++----------------
 lib/eal/include/rte_function_versioning.h  |  96 +++++-------
 lib/net/net_crc.h                          |  15 --
 lib/net/rte_net_crc.c                      |  28 +---
 4 files changed, 77 insertions(+), 227 deletions(-)

diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..88dd776b4c 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -138,27 +138,20 @@ macros are used in conjunction with the ``version.map`` file for
 a given library to allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
-The macros exported are:
+The macros are:
 
-* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
-  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
+* ``RTE_VERSION_SYMBOL(ver, type, name, args``: Creates a symbol version table
+  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
+  ``<name>_v<ver>``.
 
-* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
-  the linker to bind references to symbol ``b`` to the internal symbol
-  ``be``.
+* ``RTE_DEFAULT_SYMBO(ver, type, name, args)``: Creates a symbol version entry
+  instructing the linker to bind references to symbol ``<name>`` to the internal
+  symbol ``<name>_v<ver>``.
 
-* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
-  fully qualified function ``p``, so that if a symbol becomes versioned, it
-  can still be mapped back to the public symbol name.
-
-* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
-  ``be`` to signal that it is being used as an implementation of a particular
-  version of symbol ``b``.
-
-* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
-  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
-  The macro is used when a symbol matures to become part of the stable ABI, to
-  provide an alias to experimental until the next major ABI version.
+* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
+  but for experimental API symbols. The macro is used when a symbol matures
+  to become part of the stable ABI, to provide an alias to experimental
+  until the next major ABI version.
 
 .. _example_abi_macro_usage:
 
@@ -277,49 +270,36 @@ list of exported symbols when DPDK is compiled as a shared library.
 
 Next, we need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
+the function return type, and the function name and its arguments.
 
 .. code-block:: c
 
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
-
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ }
 
 Remembering to also add the rte_function_versioning.h header to the requisite c
 file where these changes are being made. The macro instructs the linker to
 create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
+        int debug))
    {
         struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
 
@@ -328,35 +308,9 @@ function name, with the ``v22`` suffix, and implement it appropriately.
         return ctx;
    }
 
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
-
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it, on the next shared library rebuild, there will be two versions of
 rte_acl_create, an old DPDK_21 version, used by previously built applications,
@@ -365,43 +319,10 @@ and a new DPDK_22 version, used by future built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -519,26 +440,17 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create,
+        (const struct rte_acl_param *param))
    {
    ...
    }
 
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
-
-   struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   RTE_VERSION_EXPERIMENTAL_SYMBOL(struct rte_acl_ctx *, rte_acl_create,
+        (const struct rte_acl_param *param))
    {
       return rte_acl_create(param);
    }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
 In the map file, we map the symbol to both the ``EXPERIMENTAL``
 and ``DPDK_22`` version nodes.
@@ -564,13 +476,6 @@ and ``DPDK_22`` version nodes.
         rte_acl_create;
    };
 
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -616,10 +521,10 @@ Next remove the corresponding versioned export.
 
 .. code-block:: c
 
- -VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
 
 
-Note that the internal function definition could also be removed, but its used
+Note that the internal function definition must also be removed, but its used
 in our example by the newer version ``v22``, so we leave it in place and declare
 it as static. This is a coding style choice.
 
@@ -663,16 +568,18 @@ In the case of our map above, it would transform to look as follows
         local: *;
  };
 
-Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Then any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
 .. code-block:: c
 
- -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21);
- +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+ -RTE_DEFAULT_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
+        int debug))
+ -RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
+        int debug))
 
-Lastly, any VERSION_SYMBOL macros that point to the old version nodes
+Lastly, any RTE_VERSION_SYMBOL macros that point to the old version nodes
 should be removed, taking care to preserve any code that is shared
 with the new version node.
 
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
index eb6dd2bc17..0020ce4885 100644
--- a/lib/eal/include/rte_function_versioning.h
+++ b/lib/eal/include/rte_function_versioning.h
@@ -11,8 +11,6 @@
 #error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
 #endif
 
-#ifdef RTE_BUILD_SHARED_LIB
-
 /*
  * Provides backwards compatibility when updating exported functions.
  * When a symbol is exported from a library to provide an API, it also provides a
@@ -20,80 +18,54 @@
  * arguments, etc.  On occasion that function may need to change to accommodate
  * new functionality, behavior, etc.  When that occurs, it is desirable to
  * allow for backwards compatibility for a time with older binaries that are
- * dynamically linked to the dpdk.  To support that, the __vsym and
- * VERSION_SYMBOL macros are created.  They, in conjunction with the
- * version.map file for a given library allow for multiple versions of
- * a symbol to exist in a shared library so that older binaries need not be
- * immediately recompiled.
- *
- * Refer to the guidelines document in the docs subdirectory for details on the
- * use of these macros
+ * dynamically linked to the dpdk.
  */
 
-/*
- * Macro Parameters:
- * b - function base name
- * e - function version extension, to be concatenated with base name
- * n - function symbol version string to be applied
- * f - function prototype
- * p - full function symbol name
- */
+#ifdef RTE_BUILD_SHARED_LIB
 
 /*
- * VERSION_SYMBOL
- * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
- * function name <b><e>
+ * RTE_VERSION_SYMBOL
+ * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
+ * function name <name>_v<ver>.
  */
-#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
+#define RTE_VERSION_SYMBOL(ver, type, name, args) \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
 
 /*
- * VERSION_SYMBOL_EXPERIMENTAL
- * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
- * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
- * to provide an alias to experimental for some time.
+ * RTE_VERSION_EXPERIMENTAL_SYMBOL
+ * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
+ * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
  */
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
+__rte_used type name ## _exp args; \
+type name ## _exp args
 
 /*
- * BIND_DEFAULT_SYMBOL
+ * RTE_DEFAULT_SYMBOL
  * Creates a symbol version entry instructing the linker to bind references to
- * symbol <b> to the internal symbol <b><e>
+ * symbol <name> to the internal symbol <name>_v<ver>.
  */
-#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
 
-/*
- * __vsym
- * Annotation to be used in declaration of the internal symbol <b><e> to signal
- * that it is being used as an implementation of a particular version of symbol
- * <b>.
- */
-#define __vsym __rte_used
+#else /* !RTE_BUILD_SHARED_LIB */
 
-/*
- * MAP_STATIC_SYMBOL
- * If a function has been bifurcated into multiple versions, none of which
- * are defined as the exported symbol name in the map file, this macro can be
- * used to alias a specific version of the symbol to its exported name.  For
- * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
- * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
- * building a shared library, this macro can be used to map either foo_v1 or
- * foo_v2 to the symbol foo when building a static library, e.g.:
- * MAP_STATIC_SYMBOL(void foo(), foo_v2);
- */
-#define MAP_STATIC_SYMBOL(f, p)
+#define RTE_VERSION_SYMBOL(ver, type, name, args) \
+type name ## _v ## ver args; \
+type name ## _v ## ver args
 
-#else
-/*
- * No symbol versioning in use
- */
-#define VERSION_SYMBOL(b, e, n)
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
-#define __vsym
-#define BIND_DEFAULT_SYMBOL(b, e, n)
-#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
-/*
- * RTE_BUILD_SHARED_LIB=n
- */
-#endif
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
+type name ## _exp args; \
+type name ## _exp args
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
+type name args
+
+#endif /* RTE_BUILD_SHARED_LIB */
 
 #endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 4930e2f0b3..320b0edca8 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -7,21 +7,6 @@
 
 #include "rte_net_crc.h"
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
-
-struct rte_net_crc *
-rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len, enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len);
-
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 2fb3eec231..1943d46295 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -345,8 +345,7 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+RTE_VERSION_SYMBOL(25, void, rte_net_crc_set_alg, (enum rte_net_crc_alg alg))
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -373,10 +372,9 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+RTE_DEFAULT_SYMBOL(26, struct rte_net_crc *, rte_net_crc_set_alg, (enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type))
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -414,20 +412,14 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 	}
 	return crc;
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
 
 void rte_net_crc_free(struct rte_net_crc *crc)
 {
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
+	enum rte_net_crc_type type))
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -437,18 +429,12 @@ rte_net_crc_calc_v25(const void *data,
 
 	return ret;
 }
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+RTE_DEFAULT_SYMBOL(26, uint32_t, rte_net_crc_calc, (const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len))
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.48.1


^ permalink raw reply	[relevance 13%]

* [RFC v3 5/8] build: generate symbol maps
  2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
  2025-03-11  9:56 13%   ` [RFC v3 3/8] eal: rework function versioning macros David Marchand
@ 2025-03-11  9:56 18%   ` David Marchand
  2025-03-13 17:26  0%     ` Bruce Richardson
  2025-03-14 15:27  0%     ` Andre Muezerie
  2025-03-11  9:56 16%   ` [RFC v3 7/8] build: use dynamically generated version maps David Marchand
  2025-03-11 10:18  3%   ` [RFC v3 0/8] Symbol versioning and export rework Morten Brørup
  3 siblings, 2 replies; 153+ results
From: David Marchand @ 2025-03-11  9:56 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

Rather than maintain a file in parallel of the code, symbols to be
exported can be marked with a token RTE_EXPORT_*SYMBOL.

From those marks, the build framework generates map files only for
symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
becomes unnecessary).

The build framework directly creates a map file in the format that the
linker expects (rather than converting from GNU linker to MSVC linker).

Empty maps are allowed again as a replacement for drivers/version.map.

The symbol check is updated to only support the new format.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v2:
- because of MSVC limitations wrt macro passed via cmdline,
  used an internal header for defining RTE_EXPORT_* macros,
- updated documentation and tooling,

---
 MAINTAINERS                                |   2 +
 buildtools/gen-version-map.py              | 111 ++++++++++
 buildtools/map-list-symbol.sh              |  10 +-
 buildtools/meson.build                     |   1 +
 config/meson.build                         |   2 +
 config/rte_export.h                        |  16 ++
 devtools/check-symbol-change.py            |  90 +++++++++
 devtools/check-symbol-maps.sh              |  14 --
 devtools/checkpatches.sh                   |   2 +-
 doc/guides/contributing/abi_versioning.rst | 224 ++-------------------
 drivers/meson.build                        |  94 +++++----
 drivers/version.map                        |   3 -
 lib/meson.build                            |  91 ++++++---
 13 files changed, 371 insertions(+), 289 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 create mode 100644 config/rte_export.h
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100644 drivers/version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 312e6fcee5..04772951d3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
 F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
+F: devtools/check-symbol-change.py
 F: devtools/check-symbol-change.sh
 F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
@@ -127,6 +128,7 @@ F: config/
 F: buildtools/check-symbols.sh
 F: buildtools/chkincs/
 F: buildtools/call-sphinx-build.py
+F: buildtools/gen-version-map.py
 F: buildtools/get-cpu-count.py
 F: buildtools/get-numa-count.py
 F: buildtools/list-dir-globs.py
diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
new file mode 100755
index 0000000000..b160aa828b
--- /dev/null
+++ b/buildtools/gen-version-map.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2024 Red Hat, Inc.
+
+"""Generate a version map file used by GNU or MSVC linker."""
+
+import re
+import sys
+
+# From rte_export.h
+export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
+export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# From rte_function_versioning.h
+ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+
+with open(sys.argv[2]) as f:
+    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
+
+symbols = {}
+
+for file in sys.argv[4:]:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            node = None
+            symbol = None
+            comment = None
+            if export_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                node = abi
+                symbol = export_sym_regexp.match(ln).group(1)
+            elif ver_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
+                symbol = ver_sym_regexp.match(ln).group(2)
+            elif ver_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = ver_exp_sym_regexp.match(ln).group(1)
+            elif default_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
+                symbol = default_sym_regexp.match(ln).group(2)
+
+            if not symbol:
+                continue
+
+            if node not in symbols:
+                symbols[node] = {}
+            symbols[node][symbol] = comment
+
+if sys.argv[1] == 'msvc':
+    with open(sys.argv[3], "w") as outfile:
+        outfile.writelines(f"EXPORTS\n")
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            for symbol in sorted(symbols[key].keys()):
+                outfile.writelines(f"\t{symbol}\n")
+            del symbols[key]
+else:
+    with open(sys.argv[3], "w") as outfile:
+        local_token = False
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
+            for symbol in sorted(symbols[key].keys()):
+                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                outfile.writelines(f"\t{prefix}{symbol};")
+                comment = symbols[key][symbol]
+                if comment:
+                    outfile.writelines(f"{comment}")
+                outfile.writelines("\n")
+            outfile.writelines("\n")
+            if not local_token:
+                outfile.writelines("\tlocal: *;\n")
+                local_token = True
+            outfile.writelines("};\n")
+            del symbols[key]
+        for key in sorted(symbols.keys()):
+            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
+            for symbol in sorted(symbols[key].keys()):
+                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                outfile.writelines(f"\t{prefix}{symbol};")
+                comment = symbols[key][symbol]
+                if comment:
+                    outfile.writelines(f"{comment}")
+                outfile.writelines("\n")
+            outfile.writelines(f"}} {abi};\n")
+            if not local_token:
+                outfile.writelines("\tlocal: *;\n")
+                local_token = True
+            del symbols[key]
+        # No exported symbol, add a catch all
+        if not local_token:
+            outfile.writelines(f"{abi} {{\n")
+            outfile.writelines("\tlocal: *;\n")
+            local_token = True
+            outfile.writelines("};\n")
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index eb98451d8e..0829df4be5 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -62,10 +62,14 @@ for file in $@; do
 		if (current_section == "") {
 			next;
 		}
+		symbol_version = current_version
+		if (/^[^}].*[^:*]; # added in /) {
+			symbol_version = $5
+		}
 		if ("'$version'" != "") {
-			if ("'$version'" == "unset" && current_version != "") {
+			if ("'$version'" == "unset" && symbol_version != "") {
 				next;
-			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
+			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
 				next;
 			}
 		}
@@ -73,7 +77,7 @@ for file in $@; do
 		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
 			ret = 0;
 			if ("'$quiet'" == "") {
-				print "'$file' "current_section" "$1" "current_version;
+				print "'$file' "current_section" "$1" "symbol_version;
 			}
 			if ("'$symbol'" != "all") {
 				exit 0;
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 4e2c1217a2..b745e9afa4 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -16,6 +16,7 @@ else
     py3 = ['meson', 'runpython']
 endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
+gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
diff --git a/config/meson.build b/config/meson.build
index f31fef216c..54657055fb 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -303,8 +303,10 @@ endif
 # add -include rte_config to cflags
 if is_ms_compiler
     add_project_arguments('/FI', 'rte_config.h', language: 'c')
+    add_project_arguments('/FI', 'rte_export.h', language: 'c')
 else
     add_project_arguments('-include', 'rte_config.h', language: 'c')
+    add_project_arguments('-include', 'rte_export.h', language: 'c')
 endif
 
 # enable extra warnings and disable any unwanted warnings
diff --git a/config/rte_export.h b/config/rte_export.h
new file mode 100644
index 0000000000..83d871fe11
--- /dev/null
+++ b/config/rte_export.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef RTE_EXPORT_H
+#define RTE_EXPORT_H
+
+/* *Internal* macros for exporting symbols, used by the build system.
+ * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
+ * version this symbol was introduced in.
+ */
+#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
+#define RTE_EXPORT_INTERNAL_SYMBOL(a)
+#define RTE_EXPORT_SYMBOL(a)
+
+#endif /* RTE_EXPORT_H */
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
new file mode 100755
index 0000000000..09709e4f06
--- /dev/null
+++ b/devtools/check-symbol-change.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Check exported symbols change in a patch."""
+
+import re
+import sys
+
+file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
+# From rte_export.h
+export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
+export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# TODO, handle versioned symbols from rte_function_versioning.h
+# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+
+symbols = {}
+
+for file in sys.argv[1:]:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            if file_header_regexp.match(ln):
+                if file_header_regexp.match(ln).group(2) == "lib":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+                elif file_header_regexp.match(ln).group(3) == "intel":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
+                else:
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+
+                if lib not in symbols:
+                    symbols[lib] = {}
+                continue
+
+            if export_exp_sym_regexp.match(ln):
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                node = 'EXPERIMENTAL'
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                symbol = export_sym_regexp.match(ln).group(1)
+                node = 'stable'
+            else:
+                continue
+
+            if symbol not in symbols[lib]:
+                symbols[lib][symbol] = {}
+            added = ln[0] == '+'
+            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if added:
+                symbols[lib][symbol]['added'] = node
+            else:
+                symbols[lib][symbol]['removed'] = node
+
+    for lib in sorted(symbols.keys()):
+        error = False
+        for symbol in sorted(symbols[lib].keys()):
+            if 'removed' not in symbols[lib][symbol]:
+                # Symbol addition
+                node = symbols[lib][symbol]['added']
+                if node == 'stable':
+                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
+                    error = True
+                else:
+                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
+                continue
+
+            if 'added' not in symbols[lib][symbol]:
+                # Symbol removal
+                node = symbols[lib][symbol]['added']
+                if node == 'stable':
+                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
+                    print(f"Please check it has gone though the deprecation process.")
+                continue
+
+            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
+                # Symbol was moved around
+                continue
+
+            # Symbol modifications
+            added = symbols[lib][symbol]['added']
+            removed = symbols[lib][symbol]['removed']
+            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
+            print(f"Please check it has gone though the deprecation process.")
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
index 6121f78ec6..fcd3931e5d 100755
--- a/devtools/check-symbol-maps.sh
+++ b/devtools/check-symbol-maps.sh
@@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
     ret=1
 fi
 
-find_empty_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
-    done
-}
-
-empty_maps=$(find_empty_maps $@)
-if [ -n "$empty_maps" ] ; then
-    echo "Found empty maps:"
-    echo "$empty_maps"
-    ret=1
-fi
-
 find_bad_format_maps ()
 {
     abi_version=$(cut -d'.' -f 1 ABI_VERSION)
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index 003bb49e04..7dcac7c8c9 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
 PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
 SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
 LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
-NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
+NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
 options="$options $DPDK_CHECKPATCH_OPTIONS"
 
 print_usage () {
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 88dd776b4c..addbb24b9e 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -58,12 +58,12 @@ persists over multiple releases.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/librte_acl_exports.map
  DPDK_21 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/librte_eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -77,7 +77,7 @@ that library.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/librte_acl_exports.map
  DPDK_21 {
         global:
  ...
@@ -88,7 +88,7 @@ that library.
  } DPDK_21;
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/librte_eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -100,12 +100,12 @@ how this may be done.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/librte_acl_exports.map
  DPDK_22 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/librte_eal_exports.map
  DPDK_22 {
         global:
  ...
@@ -134,8 +134,7 @@ linked to the DPDK.
 
 To support backward compatibility the ``rte_function_versioning.h``
 header file provides macros to use when updating exported functions. These
-macros are used in conjunction with the ``version.map`` file for
-a given library to allow multiple versions of a symbol to exist in a shared
+macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
 The macros are:
@@ -169,6 +168,7 @@ Assume we have a function as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param)
  {
@@ -187,6 +187,7 @@ private, is safe), but it also requires modifying the code as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param, int debug)
  {
@@ -203,78 +204,16 @@ The addition of a parameter to the function is ABI breaking as the function is
 public, and existing application may use it in its current form. However, the
 compatibility macros in DPDK allow a developer to use symbol versioning so that
 multiple functions can be mapped to the same public symbol based on when an
-application was linked to it. To see how this is done, we start with the
-requisite libraries version map file. Initially the version map file for the acl
-library looks like this
+application was linked to it.
 
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-This file needs to be modified as follows
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-
-   } DPDK_21;
-
-The addition of the new block tells the linker that a new version node
-``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
-the symbols from the DPDK_21 node. This list is directly translated into a
-list of exported symbols when DPDK is compiled as a shared library.
-
-Next, we need to specify in the code which function maps to the rte_acl_create
+We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
 we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
-the function return type, and the function name and its arguments.
+the function return type, the function name and its arguments.
 
 .. code-block:: c
 
+ -RTE_EXPORT_SYMBOL(rte_acl_create)
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
  +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
@@ -293,6 +232,7 @@ We have now mapped the original rte_acl_create symbol to the original function
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
+
 Next, we need to create the new version of the symbol. We create a new
 function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
@@ -312,9 +252,9 @@ The macro instructs the linker to create the new default symbol
 ``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
 (declared by the macro).
 
-And that's it, on the next shared library rebuild, there will be two versions of
-rte_acl_create, an old DPDK_21 version, used by previously built applications,
-and a new DPDK_22 version, used by future built applications.
+And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
+an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
+used by future built applications.
 
 .. note::
 
@@ -364,6 +304,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
@@ -371,27 +312,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
    ...
    }
 
-In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL``
-version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 When we promote the symbol to the stable ABI, we simply strip the
-``__rte_experimental`` annotation from the function and move the symbol from the
-``EXPERIMENTAL`` node, to the node of the next major ABI version as follow.
+``__rte_experimental`` annotation from the function.
 
 .. code-block:: c
 
@@ -399,31 +321,13 @@ When we promote the symbol to the stable ABI, we simply strip the
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
           ...
    }
 
-We then update the map file, adding the symbol ``rte_acl_create``
-to the ``DPDK_22`` version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-
 Although there are strictly no guarantees or commitments associated with
 :ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer
 an alias to experimental. The process to add an alias to experimental,
@@ -452,30 +356,6 @@ and ``DPDK_22`` version nodes.
       return rte_acl_create(param);
    }
 
-In the map file, we map the symbol to both the ``EXPERIMENTAL``
-and ``DPDK_22`` version nodes.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -484,38 +364,7 @@ ________________________________
 Lets assume that you've done the above updates, and in preparation for the next
 major ABI version you decide you would like to retire the old version of the
 function. After having gone through the ABI deprecation announcement process,
-removal is easy. Start by removing the symbol from the requisite version map
-file:
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
- -      rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-   } DPDK_21;
-
+removal is easy.
 
 Next remove the corresponding versioned export.
 
@@ -539,36 +388,7 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
-To do this, start by modifying the version map file, such that all symbols from
-the node to be removed are merged into the next node in the map.
-
-In the case of our map above, it would transform to look as follows
-
-.. code-block:: none
-
-   DPDK_22 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
-        rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
- };
-
-Then any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index 05391a575d..c8bc556f1a 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -245,14 +245,14 @@ foreach subpath:subdirs
                 dependencies: static_deps,
                 c_args: cflags)
         objs += tmp_lib.extract_all_objects(recursive: true)
-        sources = custom_target(out_filename,
+        sources_pmd_info = custom_target(out_filename,
                 command: [pmdinfo, tmp_lib.full_path(), '@OUTPUT@', pmdinfogen],
                 output: out_filename,
                 depends: [tmp_lib])
 
         # now build the static driver
         static_lib = static_library(lib_name,
-                sources,
+                sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: static_deps,
@@ -262,48 +262,72 @@ foreach subpath:subdirs
         # now build the shared driver
         version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
 
-        lk_deps = []
-        lk_args = []
         if not fs.is_file(version_map)
-            version_map = '@0@/version.map'.format(meson.current_source_dir())
-            lk_deps += [version_map]
-        else
-            lk_deps += [version_map]
-            if not is_windows and developer_mode
-                # on unix systems check the output of the
-                # check-symbols.sh script, using it as a
-                # dependency of the .so build
-                lk_deps += custom_target(lib_name + '.sym_chk',
-                        command: [check_symbols, version_map, '@INPUT@'],
-                        capture: true,
-                        input: static_lib,
-                        output: lib_name + '.sym_chk')
+            if is_ms_linker
+                link_mode = 'msvc'
+            elif is_windows
+                link_mode = 'mingw'
+            else
+                link_mode = 'gnu'
             endif
-        endif
+            version_map = custom_target(lib_name + '_map',
+                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                    input: sources,
+                    output: 'lib@0@_exports.map'.format(lib_name))
+            version_map_path = version_map.full_path()
+            version_map_dep = [version_map]
+            lk_deps = [version_map]
 
-        if is_windows
             if is_ms_linker
-                def_file = custom_target(lib_name + '_def',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_exports.def'.format(lib_name))
-                lk_deps += [def_file]
-
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
+                if is_ms_compiler
+                    lk_args = ['/def:' + version_map.full_path()]
+                else
+                    lk_args = ['-Wl,/def:' + version_map.full_path()]
+                endif
             else
-                mingw_map = custom_target(lib_name + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(lib_name))
-                lk_deps += [mingw_map]
-
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
             endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            version_map_path = version_map
+            version_map_dep = []
+            lk_deps = [version_map]
+
+            if is_windows
+                if is_ms_linker
+                    def_file = custom_target(lib_name + '_def',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_exports.def'.format(lib_name))
+                    lk_deps += [def_file]
+
+                    lk_args = ['-Wl,/def:' + def_file.full_path()]
+                else
+                    mingw_map = custom_target(lib_name + '_mingw',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_mingw.map'.format(lib_name))
+                    lk_deps += [mingw_map]
+
+                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                endif
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
+        endif
+
+        if not is_windows and developer_mode
+            # on unix systems check the output of the
+            # check-symbols.sh script, using it as a
+            # dependency of the .so build
+            lk_deps += custom_target(lib_name + '.sym_chk',
+                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    capture: true,
+                    input: static_lib,
+                    output: lib_name + '.sym_chk',
+                    depends: version_map_dep)
         endif
 
-        shared_lib = shared_library(lib_name, sources,
+        shared_lib = shared_library(lib_name, sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: shared_deps,
diff --git a/drivers/version.map b/drivers/version.map
deleted file mode 100644
index 17cc97bda6..0000000000
--- a/drivers/version.map
+++ /dev/null
@@ -1,3 +0,0 @@
-DPDK_25 {
-	local: *;
-};
diff --git a/lib/meson.build b/lib/meson.build
index ce92cb5537..b6bac02b48 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
+fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -254,42 +255,60 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not use_function_versioning or is_windows
-        # use pre-build objects to build shared lib
-        sources = []
-        objs += static_lib.extract_all_objects(recursive: false)
-    else
-        # for compat we need to rebuild with
-        # RTE_BUILD_SHARED_LIB defined
-        cflags += '-DRTE_BUILD_SHARED_LIB'
-    endif
-
-    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-    lk_deps = [version_map]
-
-    if is_ms_linker
-        def_file = custom_target(libname + '_def',
-                command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                input: version_map,
-                output: '@0@_exports.def'.format(libname))
-        lk_deps += [def_file]
+    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
+        if is_ms_linker
+            link_mode = 'msvc'
+        elif is_windows
+            link_mode = 'mingw'
+        else
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(libname + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources,
+                output: 'lib@0@_exports.map'.format(libname))
+        version_map_path = version_map.full_path()
+        version_map_dep = [version_map]
+        lk_deps = [version_map]
 
-        if is_ms_compiler
-            lk_args = ['/def:' + def_file.full_path()]
+        if is_ms_linker
+            if is_ms_compiler
+                lk_args = ['/def:' + version_map.full_path()]
+            else
+                lk_args = ['-Wl,/def:' + version_map.full_path()]
+            endif
         else
-            lk_args = ['-Wl,/def:' + def_file.full_path()]
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
     else
-        if is_windows
-            mingw_map = custom_target(libname + '_mingw',
+        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
+        version_map_path = version_map
+        version_map_dep = []
+        lk_deps = [version_map]
+        if is_ms_linker
+            def_file = custom_target(libname + '_def',
                     command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
                     input: version_map,
-                    output: '@0@_mingw.map'.format(libname))
-            lk_deps += [mingw_map]
+                    output: '@0@_exports.def'.format(libname))
+            lk_deps += [def_file]
 
-            lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            if is_ms_compiler
+                lk_args = ['/def:' + def_file.full_path()]
+            else
+                lk_args = ['-Wl,/def:' + def_file.full_path()]
+            endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            if is_windows
+                mingw_map = custom_target(libname + '_mingw',
+                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                        input: version_map,
+                        output: '@0@_mingw.map'.format(libname))
+                lk_deps += [mingw_map]
+
+                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
         endif
     endif
 
@@ -298,11 +317,21 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols,
-                    version_map, '@INPUT@'],
+                command: [check_symbols, version_map_path, '@INPUT@'],
                 capture: true,
                 input: static_lib,
-                output: name + '.sym_chk')
+                output: name + '.sym_chk',
+                depends: version_map_dep)
+    endif
+
+    if not use_function_versioning or is_windows
+        # use pre-build objects to build shared lib
+        sources = []
+        objs += static_lib.extract_all_objects(recursive: false)
+    else
+        # for compat we need to rebuild with
+        # RTE_BUILD_SHARED_LIB defined
+        cflags += '-DRTE_BUILD_SHARED_LIB'
     endif
 
     shared_lib = shared_library(libname,
-- 
2.48.1


^ permalink raw reply	[relevance 18%]

* [RFC v3 7/8] build: use dynamically generated version maps
  2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
  2025-03-11  9:56 13%   ` [RFC v3 3/8] eal: rework function versioning macros David Marchand
  2025-03-11  9:56 18%   ` [RFC v3 5/8] build: generate symbol maps David Marchand
@ 2025-03-11  9:56 16%   ` David Marchand
  2025-03-11 10:18  3%   ` [RFC v3 0/8] Symbol versioning and export rework Morten Brørup
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-11  9:56 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Aaron Conole, Michael Santana

Switch to always dynamically generate version maps.

As the map files get generated, tooling around checking, converting,
updating etc.. static version maps can be removed.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   7 -
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/map-list-symbol.sh                 |   7 +-
 buildtools/map_to_win.py                      |  41 ---
 buildtools/meson.build                        |   1 -
 devtools/check-symbol-change.sh               | 186 -----------
 devtools/check-symbol-maps.sh                 | 101 ------
 devtools/checkpatches.sh                      |   2 +-
 devtools/update-abi.sh                        |  46 ---
 devtools/update_version_map_abi.py            | 210 ------------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/meson.build                           |  74 ++---
 lib/meson.build                               |  73 ++---
 17 files changed, 188 insertions(+), 931 deletions(-)
 delete mode 100644 buildtools/map_to_win.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fba46b920f..e97b5cdb8b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,6 @@ jobs:
         failed=
         devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
         devtools/check-meson.py || failed=true
-        devtools/check-symbol-maps.sh || failed=true
         [ -z "$failed" ]
   ubuntu-vm-builds:
     name: ${{ join(matrix.config.*, '-') }}
diff --git a/MAINTAINERS b/MAINTAINERS
index 04772951d3..9474189035 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
 F: MAINTAINERS
 F: devtools/build-dict.sh
 F: devtools/check-abi.sh
-F: devtools/check-abi-version.sh
 F: devtools/check-doc-vs-code.sh
 F: devtools/check-dup-includes.sh
 F: devtools/check-maintainers.sh
@@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
 F: devtools/check-symbol-change.py
-F: devtools/check-symbol-change.sh
-F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
 F: devtools/get-maintainer.sh
 F: devtools/git-log-fixes.sh
 F: devtools/load-devel-config
 F: devtools/parse-flow-support.sh
 F: devtools/process-iwyu.py
-F: devtools/update-abi.sh
 F: devtools/update-patches.py
-F: devtools/update_version_map_abi.py
 F: devtools/libabigail.abignore
 F: devtools/words-case.txt
 F: license/
@@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/common/
 F: lib/eal/unix/
 F: lib/eal/include/
-F: lib/eal/version.map
 F: doc/guides/prog_guide/env_abstraction_layer.rst
 F: app/test/test_alarm.c
 F: app/test/test_atomic.c
@@ -396,7 +390,6 @@ Windows support
 M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
 M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/windows/
-F: buildtools/map_to_win.py
 F: doc/guides/windows_gsg/
 
 Windows memory allocation
diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
index b8ac24391e..0d6745ec14 100755
--- a/buildtools/check-symbols.sh
+++ b/buildtools/check-symbols.sh
@@ -7,29 +7,12 @@ OBJFILE=$2
 
 ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
 LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
-CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
-
-# added check for "make -C test/" usage
-if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
-then
-	exit 0
-fi
-
-if [ -d $MAPFILE ]
-then
-	exit 0
-fi
-
 DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
 trap 'rm -f "$DUMPFILE"' EXIT
 objdump -t $OBJFILE >$DUMPFILE
 
 ret=0
 
-if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
-	ret=1
-fi
-
 for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
 do
 	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
@@ -37,8 +20,7 @@ do
 		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as experimental
-		but is listed in version map
+		$SYM is not flagged as experimental but is exported as an experimental symbol
 		Please add __rte_experimental to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -53,9 +35,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as experimental
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as experimental but is not exported as an experimental symbol
+		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
@@ -67,8 +48,7 @@ do
 		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as internal
-		but is listed in version map
+		$SYM is not flagged as internal but is exported as an internal symbol
 		Please add __rte_internal to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -83,9 +63,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as internal
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as internal but is not exported as an internal symbol
+		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index 0829df4be5..962d5f3271 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -42,7 +42,6 @@ for file in $@; do
 	cat "$file" |awk '
 	BEGIN {
 		current_section = "";
-		current_version = "";
 		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
 			ret = 0;
 		} else {
@@ -54,15 +53,11 @@ for file in $@; do
 			current_section = $1;
 		}
 	}
-	/.*}/ { current_section = ""; current_version = ""; }
-	/^\t# added in / {
-		current_version=$4;
-	}
+	/.*}/ { current_section = ""; }
 	/^[^}].*[^:*];/ {
 		if (current_section == "") {
 			next;
 		}
-		symbol_version = current_version
 		if (/^[^}].*[^:*]; # added in /) {
 			symbol_version = $5
 		}
diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
deleted file mode 100644
index aa1752cacd..0000000000
--- a/buildtools/map_to_win.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-import sys
-
-
-def is_function_line(ln):
-    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
-
-# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
-def create_mingw_map_file(input_map, output_map):
-    with open(input_map) as f_in, open(output_map, 'w') as f_out:
-        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
-
-def main(args):
-    if not args[1].endswith('version.map') or \
-            not args[2].endswith('exports.def') and \
-            not args[2].endswith('mingw.map'):
-        return 1
-
-    if args[2].endswith('mingw.map'):
-        create_mingw_map_file(args[1], args[2])
-        return 0
-
-# generate def file from map file.
-# This works taking indented lines only which end with a ";" and which don't
-# have a colon in them, i.e. the lines defining functions only.
-    else:
-        with open(args[1]) as f_in:
-            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
-                         if is_function_line(ln)]
-            functions = ["EXPORTS\n"] + functions
-
-    with open(args[2], 'w') as f_out:
-        f_out.writelines(functions)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv))
diff --git a/buildtools/meson.build b/buildtools/meson.build
index b745e9afa4..1cd1ce02fd 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -18,7 +18,6 @@ endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
 gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
-map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
 get_cpu_count_cmd = py3 + files('get-cpu-count.py')
 get_numa_count_cmd = py3 + files('get-numa-count.py')
diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
deleted file mode 100755
index 8992214ac8..0000000000
--- a/devtools/check-symbol-change.sh
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
-
-build_map_changes()
-{
-	local fname="$1"
-	local mapdb="$2"
-
-	cat "$fname" | awk '
-		# Initialize our variables
-		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
-
-		# Anything that starts with + or -, followed by an a
-		# and ends in the string .map is the name of our map file
-		# This may appear multiple times in a patch if multiple
-		# map files are altered, and all section/symbol names
-		# appearing between a triggering of this rule and the
-		# next trigger of this rule are associated with this file
-		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
-
-		# The previous rule catches all .map files, anything else
-		# indicates we left the map chunk.
-		/[-+] [ab]\// {in_map=0}
-
-		# Triggering this rule, which starts a line and ends it
-		# with a { identifies a versioned section.  The section name is
-		# the rest of the line with the + and { symbols removed.
-		# Triggering this rule sets in_sec to 1, which actives the
-		# symbol rule below
-		/^.*{/ {
-			gsub("+", "");
-			if (in_map == 1) {
-				sec=$(NF-1); in_sec=1;
-			}
-		}
-
-		# This rule identifies the end of a section, and disables the
-		# symbol rule
-		/.*}/ {in_sec=0}
-
-		# This rule matches on a + followed by any characters except a :
-		# (which denotes a global vs local segment), and ends with a ;.
-		# The semicolon is removed and the symbol is printed with its
-		# association file name and version section, along with an
-		# indicator that the symbol is a new addition.  Note this rule
-		# only works if we have found a version section in the rule
-		# above (hence the in_sec check) And found a map file (the
-		# in_map check).  If we are not in a map chunk, do nothing.  If
-		# we are in a map chunk but not a section chunk, record it as
-		# unknown.
-		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " add"
-				} else {
-					print map " " sym " unknown add"
-				}
-			}
-		}
-
-		# This is the same rule as above, but the rule matches on a
-		# leading - rather than a +, denoting that the symbol is being
-		# removed.
-		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " del"
-				} else {
-					print map " " sym " unknown del"
-				}
-			}
-		}' > "$mapdb"
-
-		sort -u "$mapdb" > "$mapdb.2"
-		mv -f "$mapdb.2" "$mapdb"
-
-}
-
-is_stable_section() {
-	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
-}
-
-check_for_rule_violations()
-{
-	local mapdb="$1"
-	local mname
-	local symname
-	local secname
-	local ar
-	local ret=0
-
-	while read mname symname secname ar
-	do
-		if [ "$ar" = "add" ]
-		then
-
-			if [ "$secname" = "unknown" ]
-			then
-				# Just inform the user of this occurrence, but
-				# don't flag it as an error
-				echo -n "INFO: symbol $symname is added but "
-				echo -n "patch has insufficient context "
-				echo -n "to determine the section name "
-				echo -n "please ensure the version is "
-				echo "EXPERIMENTAL"
-				continue
-			fi
-
-			oldsecname=$(sed -n \
-			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
-
-			# A symbol can not enter a stable section directly
-			if [ -z "$oldsecname" ]
-			then
-				if ! is_stable_section $secname
-				then
-					echo -n "INFO: symbol $symname has "
-					echo -n "been added to the "
-					echo -n "$secname section of the "
-					echo "version map"
-					continue
-				else
-					echo -n "ERROR: symbol $symname "
-					echo -n "is added in the $secname "
-					echo -n "section, but is expected to "
-					echo -n "be added in the EXPERIMENTAL "
-					echo "section of the version map"
-					ret=1
-					continue
-				fi
-			fi
-
-			# This symbol is moving inside a section, nothing to do
-			if [ "$oldsecname" = "$secname" ]
-			then
-				continue
-			fi
-
-			# This symbol is moving between two sections (the
-			# original section is a stable section).
-			# This can be legit, just warn.
-			if is_stable_section $oldsecname
-			then
-				echo -n "INFO: symbol $symname is being "
-				echo -n "moved from $oldsecname to $secname. "
-				echo -n "Ensure that it has gone through the "
-				echo "deprecation process"
-				continue
-			fi
-		else
-
-			if ! grep -q "$mname $symname .* add" "$mapdb" && \
-			   is_stable_section $secname
-			then
-				# Just inform users that stable
-				# symbols need to go through a deprecation
-				# process
-				echo -n "INFO: symbol $symname is being "
-				echo -n "removed, ensure that it has "
-				echo "gone through the deprecation process"
-			fi
-		fi
-	done < "$mapdb"
-
-	return $ret
-}
-
-trap clean_and_exit_on_sig EXIT
-
-mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
-patch=$1
-exit_code=1
-
-clean_and_exit_on_sig()
-{
-	rm -f "$mapfile"
-	exit $exit_code
-}
-
-build_map_changes "$patch" "$mapfile"
-check_for_rule_violations "$mapfile"
-exit_code=$?
-rm -f "$mapfile"
-
-exit $exit_code
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
deleted file mode 100755
index fcd3931e5d..0000000000
--- a/devtools/check-symbol-maps.sh
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright 2018 Mellanox Technologies, Ltd
-
-cd $(dirname $0)/..
-
-# speed up by ignoring Unicode details
-export LC_ALL=C
-
-if [ $# = 0 ] ; then
-    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
-fi
-
-ret=0
-
-find_orphan_symbols ()
-{
-    for map in $@ ; do
-        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
-            if echo $sym | grep -q '^per_lcore_' ; then
-                symsrc=${sym#per_lcore_}
-            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
-                symsrc=${sym#__}
-            else
-                symsrc=$sym
-            fi
-            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
-                echo "$map: $sym"
-            fi
-        done
-    done
-}
-
-orphan_symbols=$(find_orphan_symbols $@)
-if [ -n "$orphan_symbols" ] ; then
-    echo "Found only in symbol map file:"
-    echo "$orphan_symbols" | sed 's,^,\t,'
-    ret=1
-fi
-
-find_duplicate_symbols ()
-{
-    for map in $@ ; do
-        buildtools/map-list-symbol.sh $map | \
-            sort | uniq -c | grep -v " 1 $map" || true
-    done
-}
-
-duplicate_symbols=$(find_duplicate_symbols $@)
-if [ -n "$duplicate_symbols" ] ; then
-    echo "Found duplicates in symbol map file:"
-    echo "$duplicate_symbols"
-    ret=1
-fi
-
-local_miss_maps=$(grep -L 'local: \*;' $@ || true)
-if [ -n "$local_miss_maps" ] ; then
-    echo "Found maps without local catch-all:"
-    echo "$local_miss_maps"
-    ret=1
-fi
-
-find_bad_format_maps ()
-{
-    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
-    next_abi_version=$((abi_version + 1))
-    for map in $@ ; do
-        cat $map | awk '
-            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
-            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
-            /^$/ { next; } # empty line
-            /^\t(global:|local: \*;)$/ { next; } # qualifiers
-            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
-            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
-            { print $0; }' || echo $map
-    done
-}
-
-bad_format_maps=$(find_bad_format_maps $@)
-if [ -n "$bad_format_maps" ] ; then
-    echo "Found badly formatted maps:"
-    echo "$bad_format_maps"
-    ret=1
-fi
-
-find_non_versioned_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
-            echo $map
-    done
-}
-
-non_versioned_maps=$(find_non_versioned_maps $@)
-if [ -n "$non_versioned_maps" ] ; then
-    echo "Found non versioned maps:"
-    echo "$non_versioned_maps"
-    ret=1
-fi
-
-exit $ret
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index 7dcac7c8c9..1f3c551b31 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -9,7 +9,7 @@
 # - DPDK_CHECKPATCH_OPTIONS
 . $(dirname $(readlink -f $0))/load-devel-config
 
-VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
+VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
 
 # Enable codespell by default. This can be overwritten from a config file.
 # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
deleted file mode 100755
index 45437f3c3b..0000000000
--- a/devtools/update-abi.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-abi_version=$1
-abi_version_file="./ABI_VERSION"
-update_path="lib drivers"
-
-# check ABI version format string
-check_abi_version() {
-      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
-}
-
-if [ -z "$1" ]; then
-      # output to stderr
-      >&2 echo "Please provide ABI version"
-      exit 1
-fi
-
-# check version string format
-if ! check_abi_version $abi_version ; then
-      # output to stderr
-      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
-      exit 1
-fi
-
-if [ -n "$2" ]; then
-      abi_version_file=$2
-fi
-
-if [ -n "$3" ]; then
-      # drop $1 and $2
-      shift 2
-      # assign all other arguments as update paths
-      update_path=$@
-fi
-
-echo "New ABI version:" $abi_version
-echo "ABI_VERSION path:" $abi_version_file
-echo "Path to update:" $update_path
-
-echo $abi_version > $abi_version_file
-
-find $update_path -name version.map -exec \
-      devtools/update_version_map_abi.py {} \
-      $abi_version \; -print
diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
deleted file mode 100755
index d17b02a327..0000000000
--- a/devtools/update_version_map_abi.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-"""
-A Python program that updates and merges all available stable ABI versions into
-one ABI version, while leaving experimental ABI exactly as it is. The intended
-ABI version is supplied via command-line parameter. This script is to be called
-from the devtools/update-abi.sh utility.
-"""
-
-import argparse
-import sys
-import re
-
-
-def __parse_map_file(f_in):
-    # match function name, followed by semicolon, followed by EOL or comments,
-    # optionally with whitespace in between each item
-    func_line_regex = re.compile(r"\s*"
-                                 r"(?P<line>"
-                                 r"(?P<func>[a-zA-Z_0-9]+)"
-                                 r"\s*"
-                                 r";"
-                                 r"\s*"
-                                 r"(?P<comment>#.+)?"
-                                 r")"
-                                 r"\s*"
-                                 r"$")
-    # match section name, followed by opening bracked, followed by EOL,
-    # optionally with whitespace in between each item
-    section_begin_regex = re.compile(r"\s*"
-                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
-                                     r"\s*"
-                                     r"{"
-                                     r"\s*"
-                                     r"$")
-    # match closing bracket, optionally followed by section name (for when we
-    # inherit from another ABI version), followed by semicolon, followed by
-    # EOL, optionally with whitespace in between each item
-    section_end_regex = re.compile(r"\s*"
-                                   r"}"
-                                   r"\s*"
-                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
-                                   r"\s*"
-                                   r";"
-                                   r"\s*"
-                                   r"$")
-
-    # for stable ABI, we don't care about which version introduced which
-    # function, we just flatten the list. there are dupes in certain files, so
-    # use a set instead of a list
-    stable_lines = set()
-    # copy experimental section as is
-    experimental_lines = []
-    # copy internal section as is
-    internal_lines = []
-    in_experimental = False
-    in_internal = False
-    has_stable = False
-
-    # gather all functions
-    for line in f_in:
-        # clean up the line
-        line = line.strip('\n').strip()
-
-        # is this an end of section?
-        match = section_end_regex.match(line)
-        if match:
-            # whatever section this was, it's not active any more
-            in_experimental = False
-            in_internal = False
-            continue
-
-        # if we're in the middle of experimental section, we need to copy
-        # the section verbatim, so just add the line
-        if in_experimental:
-            experimental_lines += [line]
-            continue
-
-        # if we're in the middle of internal section, we need to copy
-        # the section verbatim, so just add the line
-        if in_internal:
-            internal_lines += [line]
-            continue
-
-        # skip empty lines
-        if not line:
-            continue
-
-        # is this a beginning of a new section?
-        match = section_begin_regex.match(line)
-        if match:
-            cur_section = match.group("version")
-            # is it experimental?
-            in_experimental = cur_section == "EXPERIMENTAL"
-            # is it internal?
-            in_internal = cur_section == "INTERNAL"
-            if not in_experimental and not in_internal:
-                has_stable = True
-            continue
-
-        # is this a function?
-        match = func_line_regex.match(line)
-        if match:
-            stable_lines.add(match.group("line"))
-
-    return has_stable, stable_lines, experimental_lines, internal_lines
-
-
-def __generate_stable_abi(f_out, abi_major, lines):
-    # print ABI version header
-    print("DPDK_{} {{".format(abi_major), file=f_out)
-
-    # print global section if it exists
-    if lines:
-        print("\tglobal:", file=f_out)
-        # blank line
-        print(file=f_out)
-
-        # print all stable lines, alphabetically sorted
-        for line in sorted(lines):
-            print("\t{}".format(line), file=f_out)
-
-        # another blank line
-        print(file=f_out)
-
-    # print local section
-    print("\tlocal: *;", file=f_out)
-
-    # end stable version
-    print("};", file=f_out)
-
-
-def __generate_experimental_abi(f_out, lines):
-    # start experimental section
-    print("EXPERIMENTAL {", file=f_out)
-
-    # print all experimental lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __generate_internal_abi(f_out, lines):
-    # start internal section
-    print("INTERNAL {", file=f_out)
-
-    # print all internal lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __main():
-    arg_parser = argparse.ArgumentParser(
-        description='Merge versions in linker version script.')
-
-    arg_parser.add_argument("map_file", type=str,
-                            help='path to linker version script file '
-                                 '(pattern: version.map)')
-    arg_parser.add_argument("abi_version", type=str,
-                            help='target ABI version (pattern: MAJOR.MINOR)')
-
-    parsed = arg_parser.parse_args()
-
-    if not parsed.map_file.endswith('version.map'):
-        print("Invalid input file: {}".format(parsed.map_file),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-
-    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
-        print("Invalid ABI version: {}".format(parsed.abi_version),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-    abi_major = parsed.abi_version.split('.')[0]
-
-    with open(parsed.map_file) as f_in:
-        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
-
-    with open(parsed.map_file, 'w') as f_out:
-        need_newline = has_stable and experimental_lines
-        if has_stable:
-            __generate_stable_abi(f_out, abi_major, stable_lines)
-        if need_newline:
-            # separate sections with a newline
-            print(file=f_out)
-        if experimental_lines:
-            __generate_experimental_abi(f_out, experimental_lines)
-        if internal_lines:
-            if has_stable or experimental_lines:
-              # separate sections with a newline
-              print(file=f_out)
-            __generate_internal_abi(f_out, internal_lines)
-
-
-if __name__ == "__main__":
-    __main()
diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
index d96153c6b2..f03a7467ac 100644
--- a/doc/guides/contributing/abi_policy.rst
+++ b/doc/guides/contributing/abi_policy.rst
@@ -330,31 +330,14 @@ become part of a tracked ABI version.
 
 Note that marking an API as experimental is a multi step process.
 To mark an API as experimental, the symbols which are desired to be exported
-must be placed in an EXPERIMENTAL version block in the corresponding libraries'
-version map script.
+must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
+sources.
 Experimental symbols must be commented so that it is clear in which DPDK
 version they were introduced.
 
-.. code-block:: none
-
-   EXPERIMENTAL {
-           global:
-
-           # added in 20.11
-           rte_foo_init;
-           rte_foo_configure;
-
-           # added in 21.02
-           rte_foo_cleanup;
-   ...
-
 Secondly, the corresponding prototypes of those exported functions (in the
 development header files), must be marked with the ``__rte_experimental`` tag
 (see ``rte_compat.h``).
-The DPDK build makefiles perform a check to ensure that the map file and the
-C code reflect the same list of symbols.
-This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
-during compilation in the corresponding library Makefile.
 
 In addition to tagging the code with ``__rte_experimental``,
 the doxygen markup must also contain the EXPERIMENTAL string,
diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
index 1ebc79ca3c..43e27bbd0a 100644
--- a/doc/guides/contributing/coding_style.rst
+++ b/doc/guides/contributing/coding_style.rst
@@ -1018,13 +1018,6 @@ name
 	sources are stored in a directory ``lib/xyz``, this value should
 	never be needed for new libraries.
 
-.. note::
-
-	The name value also provides the name used to find the function version
-	map file, as part of the build process, so if the directory name and
-	library names differ, the ``version.map`` file should be named
-	consistently with the library, not the directory
-
 objs
 	**Default Value = []**.
 	This variable can be used to pass to the library build some pre-built
diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
index 4debb07b98..a06d8a2a3b 100644
--- a/doc/guides/contributing/img/patch_cheatsheet.svg
+++ b/doc/guides/contributing/img/patch_cheatsheet.svg
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    version="1.1"
    width="210mm"
    height="297mm"
    id="svg2985"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="patch_cheatsheet.svg">
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="patch_cheatsheet.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
   <sodipodi:namedview
      pagecolor="#ffffff"
      bordercolor="#666666"
@@ -23,18 +23,22 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:window-width="1920"
-     inkscape:window-height="1017"
+     inkscape:window-height="975"
      id="namedview274"
      showgrid="false"
      inkscape:zoom="0.89702958"
-     inkscape:cx="246.07409"
-     inkscape:cy="416.76022"
-     inkscape:window-x="1072"
-     inkscape:window-y="-8"
+     inkscape:cx="546.24732"
+     inkscape:cy="385.71749"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
      inkscape:window-maximized="1"
      inkscape:current-layer="layer1"
      inkscape:document-rotation="0"
-     inkscape:snap-grids="false" />
+     inkscape:snap-grids="false"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm" />
   <defs
      id="defs3">
     <linearGradient
@@ -906,7 +910,7 @@
              style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
              id="tspan4092-8-7-6-9-7"
              y="855.79816"
-             x="460.18405">****</tspan></text>
+             x="460.18405">***</tspan></text>
       </g>
     </g>
     <text
@@ -1132,161 +1136,126 @@
            id="tspan4092-8-6-3-1-8-4-4-55-7"
            style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
     </g>
+    <text
+       x="424.10629"
+       y="363.21423"
+       id="text4090-8"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="363.21423"
+         id="tspan4092-8"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
+    <text
+       x="424.10629"
+       y="393.60123"
+       id="text4090-8-5"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="393.60123"
+         id="tspan4092-8-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
+    <text
+       x="424.10629"
+       y="424.20575"
+       id="text4090-8-5-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="424.20575"
+         id="tspan4092-8-5-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
+    <text
+       x="424.10629"
+       y="453.10339"
+       id="text4090-8-5-6-9-4"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="453.10339"
+         id="tspan4092-8-5-5-3-4"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
+    <text
+       x="424.10629"
+       y="514.09497"
+       id="text4090-8-5-6-9-4-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="514.09497"
+         id="tspan4092-8-5-5-3-4-0"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
+    <text
+       x="425.12708"
+       y="544.91718"
+       id="text4090-8-5-6-9-4-6-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="425.12708"
+         y="544.91718"
+         id="tspan4092-8-5-5-3-4-0-6"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
-       id="g3605">
-      <text
-         x="42.176418"
-         y="1020.4383"
-         id="text4090-8-7-8-7-6-3-8-4"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="42.176418"
-           y="1020.4383"
-           id="tspan4092-8-6-3-1-8-4-4-55"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
-      <text
-         x="30.942892"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="30.942892"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-      <text
-         x="25.247679"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="25.247679"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-    </g>
-    <g
-       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
-       id="g3275">
+       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
+       id="g3334">
       <g
-         id="g3341">
+         id="g3267"
+         transform="translate(-13.517932,3.1531035)">
         <text
-           x="394.78601"
-           y="390.17807"
-           id="text4090-8"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="390.17807"
-             id="tspan4092-8"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
-        <text
-           x="394.78601"
-           y="420.24835"
-           id="text4090-8-5"
+           x="660.46729"
+           y="468.01297"
+           id="text4090-8-1-8-9-1-4-1"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="420.24835"
-             id="tspan4092-8-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
-        <text
-           x="394.78601"
-           y="450.53394"
-           id="text4090-8-5-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="450.53394"
-             id="tspan4092-8-5-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
-        <text
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="660.46729"
+             y="468.01297"
+             id="tspan4092-8-7-6-9-7-0-7"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+      </g>
+      <text
+         x="394.78601"
+         y="483.59955"
+         id="text4090-8-5-6-9"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="513.13031"
-           id="text4090-8-5-6-9-4"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="513.13031"
-             id="tspan4092-8-5-5-3-4"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
-        <text
+           y="483.59955"
+           id="tspan4092-8-5-5-3"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+    </g>
+    <g
+       id="g3428"
+       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
+      <text
+         x="394.78601"
+         y="541.38928"
+         id="text4090-8-5-6-9-4-6-1"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="573.48621"
-           id="text4090-8-5-6-9-4-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="573.48621"
-             id="tspan4092-8-5-5-3-4-0"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
+           y="541.38928"
+           id="tspan4092-8-5-5-3-4-0-7"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
+      <g
+         transform="translate(-119.92979,57.949844)"
+         id="g3267-9">
         <text
-           x="395.79617"
-           y="603.98718"
-           id="text4090-8-5-6-9-4-6-6"
+           x="628.93628"
+           y="473.13675"
+           id="text4090-8-1-8-9-1-4-1-4"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="395.79617"
-             y="603.98718"
-             id="tspan4092-8-5-5-3-4-0-6"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
-        <g
-           transform="translate(0,-0.83470152)"
-           id="g3334">
-          <g
-             id="g3267"
-             transform="translate(-13.517932,3.1531035)">
-            <text
-               x="660.46729"
-               y="468.01297"
-               id="text4090-8-1-8-9-1-4-1"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="660.46729"
-                 y="468.01297"
-                 id="tspan4092-8-7-6-9-7-0-7"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
-          </g>
-          <text
-             x="394.78601"
-             y="483.59955"
-             id="text4090-8-5-6-9"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="483.59955"
-               id="tspan4092-8-5-5-3"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
-        </g>
-        <g
-           id="g3428"
-           transform="translate(0,0.88137813)">
-          <text
-             x="394.78601"
-             y="541.38928"
-             id="text4090-8-5-6-9-4-6-1"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="541.38928"
-               id="tspan4092-8-5-5-3-4-0-7"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
-          <g
-             transform="translate(-119.92979,57.949844)"
-             id="g3267-9">
-            <text
-               x="628.93628"
-               y="473.13675"
-               id="text4090-8-1-8-9-1-4-1-4"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="628.93628"
-                 y="473.13675"
-                 id="tspan4092-8-7-6-9-7-0-7-8"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
-          </g>
-        </g>
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="628.93628"
+             y="473.13675"
+             id="tspan4092-8-7-6-9-7-0-7-8"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
       </g>
     </g>
     <text
@@ -1301,7 +1270,7 @@
          id="tspan4092-8-5-5-3-4-0-6-2-11-0"
          style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3595">
       <text
          x="30.942892"
@@ -1332,7 +1301,7 @@
            x="19.552465"
            y="1037.0271"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
       <text
          x="42.830166"
          y="1033.2393"
@@ -1345,7 +1314,7 @@
            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
     </g>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3619">
       <text
          x="42.212418"
@@ -1396,7 +1365,7 @@
            x="14.016749"
            y="1049.8527"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
     </g>
     <rect
        width="196.44218"
diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index d21ee288b2..8ad6b6e715 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
 
   * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
 
-* New external functions should be added to the local ``version.map`` file. See
-  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
-  guides. New external functions should also be added in alphabetical order.
+* New external functions should be exported.
+  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
+  guides.
 
 * Any new API function should be used in ``/app`` test directory.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index c8bc556f1a..6904d34eee 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -5,8 +5,6 @@ if is_ms_compiler
     subdir_done()
 endif
 
-fs = import('fs')
-
 # Defines the order of dependencies evaluation
 subdirs = [
         'common',
@@ -260,59 +258,27 @@ foreach subpath:subdirs
                 install: true)
 
         # now build the shared driver
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
-
-        if not fs.is_file(version_map)
-            if is_ms_linker
-                link_mode = 'msvc'
-            elif is_windows
-                link_mode = 'mingw'
-            else
-                link_mode = 'gnu'
-            endif
-            version_map = custom_target(lib_name + '_map',
-                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                    input: sources,
-                    output: 'lib@0@_exports.map'.format(lib_name))
-            version_map_path = version_map.full_path()
-            version_map_dep = [version_map]
-            lk_deps = [version_map]
-
-            if is_ms_linker
-                if is_ms_compiler
-                    lk_args = ['/def:' + version_map.full_path()]
-                else
-                    lk_args = ['-Wl,/def:' + version_map.full_path()]
-                endif
-            else
-                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-            endif
+        if is_ms_linker
+            link_mode = 'msvc'
+        elif is_windows
+            link_mode = 'mingw'
         else
-            version_map_path = version_map
-            version_map_dep = []
-            lk_deps = [version_map]
-
-            if is_windows
-                if is_ms_linker
-                    def_file = custom_target(lib_name + '_def',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_exports.def'.format(lib_name))
-                    lk_deps += [def_file]
-
-                    lk_args = ['-Wl,/def:' + def_file.full_path()]
-                else
-                    mingw_map = custom_target(lib_name + '_mingw',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_mingw.map'.format(lib_name))
-                    lk_deps += [mingw_map]
-
-                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-                endif
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(lib_name + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources,
+                output: 'lib@0@_exports.map'.format(lib_name))
+        lk_deps = [version_map]
+
+        if is_ms_linker
+            if is_ms_compiler
+                lk_args = ['/def:' + version_map.full_path()]
             else
-                lk_args = ['-Wl,--version-script=' + version_map]
+                lk_args = ['-Wl,/def:' + version_map.full_path()]
             endif
+        else
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
 
         if not is_windows and developer_mode
@@ -320,11 +286,11 @@ foreach subpath:subdirs
             # check-symbols.sh script, using it as a
             # dependency of the .so build
             lk_deps += custom_target(lib_name + '.sym_chk',
-                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
                     capture: true,
                     input: static_lib,
                     output: lib_name + '.sym_chk',
-                    depends: version_map_dep)
+                    depends: [version_map])
         endif
 
         shared_lib = shared_library(lib_name, sources_pmd_info,
diff --git a/lib/meson.build b/lib/meson.build
index b6bac02b48..f143bc202b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
-fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -255,61 +254,27 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
-        if is_ms_linker
-            link_mode = 'msvc'
-        elif is_windows
-            link_mode = 'mingw'
-        else
-            link_mode = 'gnu'
-        endif
-        version_map = custom_target(libname + '_map',
-                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                input: sources,
-                output: 'lib@0@_exports.map'.format(libname))
-        version_map_path = version_map.full_path()
-        version_map_dep = [version_map]
-        lk_deps = [version_map]
-
-        if is_ms_linker
-            if is_ms_compiler
-                lk_args = ['/def:' + version_map.full_path()]
-            else
-                lk_args = ['-Wl,/def:' + version_map.full_path()]
-            endif
-        else
-            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-        endif
+    if is_ms_linker
+        link_mode = 'msvc'
+    elif is_windows
+        link_mode = 'mingw'
     else
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-        version_map_path = version_map
-        version_map_dep = []
-        lk_deps = [version_map]
-        if is_ms_linker
-            def_file = custom_target(libname + '_def',
-                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                    input: version_map,
-                    output: '@0@_exports.def'.format(libname))
-            lk_deps += [def_file]
+        link_mode = 'gnu'
+    endif
+    version_map = custom_target(libname + '_map',
+            command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+            input: sources,
+            output: 'lib@0@_exports.map'.format(libname))
+    lk_deps = [version_map]
 
-            if is_ms_compiler
-                lk_args = ['/def:' + def_file.full_path()]
-            else
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
-            endif
+    if is_ms_linker
+        if is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
         else
-            if is_windows
-                mingw_map = custom_target(libname + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(libname))
-                lk_deps += [mingw_map]
-
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
         endif
+    else
+        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
     endif
 
     if developer_mode and not is_windows
@@ -317,11 +282,11 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols, version_map_path, '@INPUT@'],
+                command: [check_symbols, version_map.full_path(), '@INPUT@'],
                 capture: true,
                 input: static_lib,
                 output: name + '.sym_chk',
-                depends: version_map_dep)
+                depends: [version_map])
     endif
 
     if not use_function_versioning or is_windows
-- 
2.48.1


^ permalink raw reply	[relevance 16%]

* RE: [RFC v3 0/8] Symbol versioning and export rework
  2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
                     ` (2 preceding siblings ...)
  2025-03-11  9:56 16%   ` [RFC v3 7/8] build: use dynamically generated version maps David Marchand
@ 2025-03-11 10:18  3%   ` Morten Brørup
  2025-03-11 13:43  0%     ` David Marchand
  3 siblings, 1 reply; 153+ results
From: Morten Brørup @ 2025-03-11 10:18 UTC (permalink / raw)
  To: David Marchand, dev; +Cc: thomas, bruce.richardson, andremue

> From: David Marchand [mailto:david.marchand@redhat.com]
> Sent: Tuesday, 11 March 2025 10.56
> 
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
> 
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to
> the
> build framework to produce a version script adapted to the linker in
> use
> (think GNU linker vs MSVC linker).
> 
> This greatly simplifies versioning symbols: a developer does not need
> to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
> 
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
> 
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
> 
> 
> Comments please.

Excellent. I'm all for automating this!

Feature creep:

Have you thought about how this (or related automation) can possibly also benefit the CI, e.g. for ABI breakage testing?

Or possible benefits to (automated) documentation of versioned functions?
Or possible benefits to remembering all versioned ABIs when writing the release notes?


^ permalink raw reply	[relevance 3%]

* Re: [RFC v3 0/8] Symbol versioning and export rework
  2025-03-11 10:18  3%   ` [RFC v3 0/8] Symbol versioning and export rework Morten Brørup
@ 2025-03-11 13:43  0%     ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-11 13:43 UTC (permalink / raw)
  To: Morten Brørup; +Cc: dev, thomas, bruce.richardson, andremue

On Tue, Mar 11, 2025 at 11:18 AM Morten Brørup <mb@smartsharesystems.com> wrote:
>
> > From: David Marchand [mailto:david.marchand@redhat.com]
> > Sent: Tuesday, 11 March 2025 10.56
> >
> > So far, each DPDK library (or driver) exposing symbols in an ABI had to
> > maintain a version.map and use some macros for symbol versioning,
> > specially crafted with the GNU linker in mind.
> >
> > This series proposes to rework the whole principle, and instead rely on
> > marking the symbol exports in the source code itself, then let it to
> > the
> > build framework to produce a version script adapted to the linker in
> > use
> > (think GNU linker vs MSVC linker).
> >
> > This greatly simplifies versioning symbols: a developer does not need
> > to
> > know anything about version.map, or that a versioned symbol must be
> > renamed with _v26, annotated with __vsym, exported in a header etc...
> >
> > Checking symbol maps becomes unnecessary since generated by the build
> > framework.
> >
> > Updating to a new ABI is just a matter of bumping the value in
> > ABI_VERSION.
> >
> >
> > Comments please.
>
> Excellent. I'm all for automating this!
>
> Feature creep:
>
> Have you thought about how this (or related automation) can possibly also benefit the CI, e.g. for ABI breakage testing?
>
> Or possible benefits to (automated) documentation of versioned functions?
> Or possible benefits to remembering all versioned ABIs when writing the release notes?

Not really, this series is already touching enough code and needs in
detail reviews.
A simple ack is pointless.

I prefer focusing on just making this part right.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* RE: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels without monitoring enabled
  @ 2025-03-12  0:33  4%   ` Long Li
  2025-03-12 15:36  0%     ` Stephen Hemminger
  0 siblings, 1 reply; 153+ results
From: Long Li @ 2025-03-12  0:33 UTC (permalink / raw)
  To: Stephen Hemminger, longli; +Cc: Wei Hu, dev

> Subject: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels without
> monitoring enabled
> 
> On Mon, 10 Mar 2025 14:42:51 -0700
> longli@linuxonhyperv.com wrote:
> 
> > From: Long Li <longli@microsoft.com>
> >
> > Hyperv may expose VMBUS channels without monitoring enabled. In this
> > case, it programs almost all the data traffic to VF.
> >
> > This patchset enabled vmbus/netvsc to use channels without monitoring
> > enabled.
> 
> 
> CI still reports a build issue

There are ABI changes to rte_vmbus_* calls. This patch added rte_vmbus_device* as the 1st parameter to those calls.

This will be a breaking change, and it only affects hn_netvsc as it's the only PMD using the vmbus.

Reading ./doc/guides/contributing/abi_policy.rst, I think the best option is to use RTE_NEXT_ABI. But I can't find its definition in the code base.

Please advise on how to proceed with making those breaking ABI changes.

Thanks,
Long

^ permalink raw reply	[relevance 4%]

* Re: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels without monitoring enabled
  2025-03-12  0:33  4%   ` [EXTERNAL] " Long Li
@ 2025-03-12 15:36  0%     ` Stephen Hemminger
  2025-03-26 21:53  0%       ` Long Li
  0 siblings, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-03-12 15:36 UTC (permalink / raw)
  To: Long Li; +Cc: longli, Wei Hu, dev

On Wed, 12 Mar 2025 00:33:52 +0000
Long Li <longli@microsoft.com> wrote:

> > Subject: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels without
> > monitoring enabled
> > 
> > On Mon, 10 Mar 2025 14:42:51 -0700
> > longli@linuxonhyperv.com wrote:
> >   
> > > From: Long Li <longli@microsoft.com>
> > >
> > > Hyperv may expose VMBUS channels without monitoring enabled. In this
> > > case, it programs almost all the data traffic to VF.
> > >
> > > This patchset enabled vmbus/netvsc to use channels without monitoring
> > > enabled.  
> > 
> > 
> > CI still reports a build issue  
> 
> There are ABI changes to rte_vmbus_* calls. This patch added rte_vmbus_device* as the 1st parameter to those calls.
> 
> This will be a breaking change, and it only affects hn_netvsc as it's the only PMD using the vmbus.
> 
> Reading ./doc/guides/contributing/abi_policy.rst, I think the best option is to use RTE_NEXT_ABI. But I can't find its definition in the code base.
> 
> Please advise on how to proceed with making those breaking ABI changes.
> 
> Thanks,
> Long

Can't take it as is, here are some options:

1. Version the API even though should only be used internally. Use API versioning
   as transistion until 25.11.
2. Wait for 25.11 and just fix it now, and do deprecation notice now.

3. Mark the API's as internal (in 25.11) and do deprecation notice now.

4. Make new functions with different names, and mark old ones as deprecated, then remove in 25.11


^ permalink raw reply	[relevance 0%]

* Re: [RFC v3 3/8] eal: rework function versioning macros
  2025-03-11  9:56 13%   ` [RFC v3 3/8] eal: rework function versioning macros David Marchand
@ 2025-03-13 16:53  0%     ` Bruce Richardson
  2025-03-13 17:09  0%       ` David Marchand
  0 siblings, 1 reply; 153+ results
From: Bruce Richardson @ 2025-03-13 16:53 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas, andremue, Tyler Retzlaff, Jasvinder Singh

On Tue, Mar 11, 2025 at 10:56:01AM +0100, David Marchand wrote:
> For versioning symbols:
> - MSVC uses pragmas on the symbol,
> - GNU linker uses special asm directives,
> 
> To accommodate both GNU linker and MSVC linker, introduce new macros for
> exporting and versioning symbols that will surround the whole function.
> 
> This has the advantage of hiding all the ugly details in the macros.
> Now versioning a symbol is just a call to a single macro:
> - RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
>   keeping an old implementation code under a versioned function (resp.
>   experimental function),
> - RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
>   and handling the static link special case, instead of
>   BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,
> 
> Update lib/net accordingly.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---

A few review comments on the docs inline below. See nothing wrong from
initial review of code changes.

/Bruce

> Changes since RFC v2:
> 
> Changes since RFC v1:
> - renamed and prefixed macros,
> - reindented in prevision of second patch,
> 
> ---
>  doc/guides/contributing/abi_versioning.rst | 165 +++++----------------
>  lib/eal/include/rte_function_versioning.h  |  96 +++++-------
>  lib/net/net_crc.h                          |  15 --
>  lib/net/rte_net_crc.c                      |  28 +---
>  4 files changed, 77 insertions(+), 227 deletions(-)
> 
> diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
> index 7afd1c1886..88dd776b4c 100644
> --- a/doc/guides/contributing/abi_versioning.rst
> +++ b/doc/guides/contributing/abi_versioning.rst
> @@ -138,27 +138,20 @@ macros are used in conjunction with the ``version.map`` file for
>  a given library to allow multiple versions of a symbol to exist in a shared
>  library so that older binaries need not be immediately recompiled.
>  
> -The macros exported are:
> +The macros are:
>  
> -* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
> -  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
> +* ``RTE_VERSION_SYMBOL(ver, type, name, args``: Creates a symbol version table

Missing closing brace .........................^ here

> +  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
> +  ``<name>_v<ver>``.
>  
> -* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
> -  the linker to bind references to symbol ``b`` to the internal symbol
> -  ``be``.
> +* ``RTE_DEFAULT_SYMBO(ver, type, name, args)``: Creates a symbol version entry

s/SYMBO/SYMBOL/

> +  instructing the linker to bind references to symbol ``<name>`` to the internal
> +  symbol ``<name>_v<ver>``.
>  
> -* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
> -  fully qualified function ``p``, so that if a symbol becomes versioned, it
> -  can still be mapped back to the public symbol name.
> -
> -* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
> -  ``be`` to signal that it is being used as an implementation of a particular
> -  version of symbol ``b``.
> -
> -* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
> -  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
> -  The macro is used when a symbol matures to become part of the stable ABI, to
> -  provide an alias to experimental until the next major ABI version.
> +* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
> +  but for experimental API symbols. The macro is used when a symbol matures
> +  to become part of the stable ABI, to provide an alias to experimental
> +  until the next major ABI version.

Just to clarify - this is where we create two names/aliases for the one
function, so it can be found either as an experimental version or a stable
one, right? In that way it's actually quite different from
RTE_VERSION_SYMBOL which is used to define a *NEW" version of an existing
function, i.e. two functions rather than one function with two names.

>  
>  .. _example_abi_macro_usage:
>  
> @@ -277,49 +270,36 @@ list of exported symbols when DPDK is compiled as a shared library.
>  
>  Next, we need to specify in the code which function maps to the rte_acl_create
>  symbol at which versions.  First, at the site of the initial symbol definition,
> -we need to update the function so that it is uniquely named, and not in conflict
> -with the public symbol name
> +we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
> +the function return type, and the function name and its arguments.
>  
>  .. code-block:: c
>  
>   -struct rte_acl_ctx *
>   -rte_acl_create(const struct rte_acl_param *param)
> - +struct rte_acl_ctx * __vsym
> - +rte_acl_create_v21(const struct rte_acl_param *param)
> + +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
>   {
>          size_t sz;
>          struct rte_acl_ctx *ctx;
>          ...
> -
> -Note that the base name of the symbol was kept intact, as this is conducive to
> -the macros used for versioning symbols and we have annotated the function as
> -``__vsym``, an implementation of a versioned symbol . That is our next step,
> -mapping this new symbol name to the initial symbol name at version node 21.
> -Immediately after the function, we add the VERSION_SYMBOL macro.
> -
> -.. code-block:: c
> -
> -   #include <rte_function_versioning.h>
> -
> -   ...
> -   VERSION_SYMBOL(rte_acl_create, _v21, 21);
> + }
>  
>  Remembering to also add the rte_function_versioning.h header to the requisite c
>  file where these changes are being made. The macro instructs the linker to
>  create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
> -in older builds, but now points to the above newly named function. We have now
> -mapped the original rte_acl_create symbol to the original function (but with a
> -new name).
> +in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
> +We have now mapped the original rte_acl_create symbol to the original function
> +(but with a new name).
>  
>  Please see the section :ref:`Enabling versioning macros
>  <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
> -Next, we need to create the new ``v22`` version of the symbol. We create a new
> -function name, with the ``v22`` suffix, and implement it appropriately.
> +Next, we need to create the new version of the symbol. We create a new
> +function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
>  
>  .. code-block:: c
>  
> -   struct rte_acl_ctx * __vsym
> -   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
> +   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
> +        int debug))

Not directly relevant to the changes in this patch, but since this is
documentation which doesn't actually need to be based on a real-life
example, we should maybe come up with example functions which are short and
don't need line wrapping. This example would be just as
effective/instructive with a return value of "int" and parameter type
without a "const" qualifier. :-)

>     {
>          struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
>  
> @@ -328,35 +308,9 @@ function name, with the ``v22`` suffix, and implement it appropriately.
>          return ctx;
>     }
>  
> -This code serves as our new API call. Its the same as our old call, but adds the
> -new parameter in place. Next we need to map this function to the new default
> -symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
> -we add the BIND_DEFAULT_SYMBOL macro.
> -
> -.. code-block:: c
> -
> -   #include <rte_function_versioning.h>
> -
> -   ...
> -   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
> -
>  The macro instructs the linker to create the new default symbol
> -``rte_acl_create@DPDK_22``, which points to the above newly named function.
> -
> -We finally modify the prototype of the call in the public header file,
> -such that it contains both versions of the symbol and the public API.
> -
> -.. code-block:: c
> -
> -   struct rte_acl_ctx *
> -   rte_acl_create(const struct rte_acl_param *param);
> -
> -   struct rte_acl_ctx * __vsym
> -   rte_acl_create_v21(const struct rte_acl_param *param);
> -
> -   struct rte_acl_ctx * __vsym
> -   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
> -
> +``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
> +(declared by the macro).
>  
>  And that's it, on the next shared library rebuild, there will be two versions of
>  rte_acl_create, an old DPDK_21 version, used by previously built applications,
> @@ -365,43 +319,10 @@ and a new DPDK_22 version, used by future built applications.
>  .. note::
>  
>     **Before you leave**, please take care reviewing the sections on
> -   :ref:`mapping static symbols <mapping_static_symbols>`,
>     :ref:`enabling versioning macros <enabling_versioning_macros>`,
>     and :ref:`ABI deprecation <abi_deprecation>`.
>  
>  
> -.. _mapping_static_symbols:
> -
> -Mapping static symbols
> -______________________
> -
> -Now we've taken what was a public symbol, and duplicated it into two uniquely
> -and differently named symbols. We've then mapped each of those back to the
> -public symbol ``rte_acl_create`` with different version tags. This only applies
> -to dynamic linking, as static linking has no notion of versioning. That leaves
> -this code in a position of no longer having a symbol simply named
> -``rte_acl_create`` and a static build will fail on that missing symbol.
> -
> -To correct this, we can simply map a function of our choosing back to the public
> -symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
> -assumption is that the most recent version of the symbol is the one you want to
> -map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
> -defined, we add this
> -
> -
> -.. code-block:: c
> -
> -   struct rte_acl_ctx * __vsym
> -   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
> -   {
> -        ...
> -   }
> -   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
> -
> -That tells the compiler that, when building a static library, any calls to the
> -symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
> -
> -
>  .. _enabling_versioning_macros:
>  
>  Enabling versioning macros
> @@ -519,26 +440,17 @@ and ``DPDK_22`` version nodes.
>      * Create an acl context object for apps to
>      * manipulate
>      */
> -   struct rte_acl_ctx *
> -   rte_acl_create(const struct rte_acl_param *param)
> +   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create,
> +        (const struct rte_acl_param *param))
>     {
>     ...
>     }
>  
> -   __rte_experimental
> -   struct rte_acl_ctx *
> -   rte_acl_create_e(const struct rte_acl_param *param)
> -   {
> -      return rte_acl_create(param);
> -   }
> -   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
> -
> -   struct rte_acl_ctx *
> -   rte_acl_create_v22(const struct rte_acl_param *param)
> +   RTE_VERSION_EXPERIMENTAL_SYMBOL(struct rte_acl_ctx *, rte_acl_create,
> +        (const struct rte_acl_param *param))
>     {
>        return rte_acl_create(param);
>     }
> -   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
>  
>  In the map file, we map the symbol to both the ``EXPERIMENTAL``
>  and ``DPDK_22`` version nodes.
> @@ -564,13 +476,6 @@ and ``DPDK_22`` version nodes.
>          rte_acl_create;
>     };
>  
> -.. note::
> -
> -   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
> -   when aliasing to experimental you will also need to take care of
> -   :ref:`mapping static symbols <mapping_static_symbols>`.
> -
> -
>  .. _abi_deprecation:
>  
>  Deprecating part of a public API
> @@ -616,10 +521,10 @@ Next remove the corresponding versioned export.
>  
>  .. code-block:: c
>  
> - -VERSION_SYMBOL(rte_acl_create, _v21, 21);
> + -RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
>  
>  
> -Note that the internal function definition could also be removed, but its used
> +Note that the internal function definition must also be removed, but its used

its -> it's (or "it is" if you want the longer version).

>  in our example by the newer version ``v22``, so we leave it in place and declare
>  it as static. This is a coding style choice.
>  
> @@ -663,16 +568,18 @@ In the case of our map above, it would transform to look as follows
>          local: *;
>   };
>  
> -Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
> +Then any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
>  updated to point to the new version node in any header files for all affected
>  symbols.
>  
>  .. code-block:: c
>  
> - -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21);
> - +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
> + -RTE_DEFAULT_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
> +        int debug))
> + -RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
> +        int debug))
>  
> -Lastly, any VERSION_SYMBOL macros that point to the old version nodes
> +Lastly, any RTE_VERSION_SYMBOL macros that point to the old version nodes
>  should be removed, taking care to preserve any code that is shared
>  with the new version node.
>  
> diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
> index eb6dd2bc17..0020ce4885 100644
> --- a/lib/eal/include/rte_function_versioning.h
> +++ b/lib/eal/include/rte_function_versioning.h
> @@ -11,8 +11,6 @@
>  #error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
>  #endif
>  
> -#ifdef RTE_BUILD_SHARED_LIB
> -
>  /*
>   * Provides backwards compatibility when updating exported functions.
>   * When a symbol is exported from a library to provide an API, it also provides a
> @@ -20,80 +18,54 @@
>   * arguments, etc.  On occasion that function may need to change to accommodate
>   * new functionality, behavior, etc.  When that occurs, it is desirable to
>   * allow for backwards compatibility for a time with older binaries that are
> - * dynamically linked to the dpdk.  To support that, the __vsym and
> - * VERSION_SYMBOL macros are created.  They, in conjunction with the
> - * version.map file for a given library allow for multiple versions of
> - * a symbol to exist in a shared library so that older binaries need not be
> - * immediately recompiled.
> - *
> - * Refer to the guidelines document in the docs subdirectory for details on the
> - * use of these macros
> + * dynamically linked to the dpdk.
>   */
>  
> -/*
> - * Macro Parameters:
> - * b - function base name
> - * e - function version extension, to be concatenated with base name
> - * n - function symbol version string to be applied
> - * f - function prototype
> - * p - full function symbol name
> - */
> +#ifdef RTE_BUILD_SHARED_LIB
>  
>  /*
> - * VERSION_SYMBOL
> - * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
> - * function name <b><e>
> + * RTE_VERSION_SYMBOL
> + * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
> + * function name <name>_v<ver>.
>   */
> -#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
> +#define RTE_VERSION_SYMBOL(ver, type, name, args) \
> +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
> +__rte_used type name ## _v ## ver args; \
> +type name ## _v ## ver args
>  
>  /*
> - * VERSION_SYMBOL_EXPERIMENTAL
> - * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
> - * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
> - * to provide an alias to experimental for some time.
> + * RTE_VERSION_EXPERIMENTAL_SYMBOL
> + * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
> + * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
>   */
> -#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
> +#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
> +__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
> +__rte_used type name ## _exp args; \
> +type name ## _exp args
>  
>  /*
> - * BIND_DEFAULT_SYMBOL
> + * RTE_DEFAULT_SYMBOL
>   * Creates a symbol version entry instructing the linker to bind references to
> - * symbol <b> to the internal symbol <b><e>
> + * symbol <name> to the internal symbol <name>_v<ver>.
>   */
> -#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
> +#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
> +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
> +__rte_used type name ## _v ## ver args; \
> +type name ## _v ## ver args
>  
> -/*
> - * __vsym
> - * Annotation to be used in declaration of the internal symbol <b><e> to signal
> - * that it is being used as an implementation of a particular version of symbol
> - * <b>.
> - */
> -#define __vsym __rte_used
> +#else /* !RTE_BUILD_SHARED_LIB */
>  
> -/*
> - * MAP_STATIC_SYMBOL
> - * If a function has been bifurcated into multiple versions, none of which
> - * are defined as the exported symbol name in the map file, this macro can be
> - * used to alias a specific version of the symbol to its exported name.  For
> - * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
> - * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
> - * building a shared library, this macro can be used to map either foo_v1 or
> - * foo_v2 to the symbol foo when building a static library, e.g.:
> - * MAP_STATIC_SYMBOL(void foo(), foo_v2);
> - */
> -#define MAP_STATIC_SYMBOL(f, p)
> +#define RTE_VERSION_SYMBOL(ver, type, name, args) \
> +type name ## _v ## ver args; \
> +type name ## _v ## ver args
>  
> -#else
> -/*
> - * No symbol versioning in use
> - */
> -#define VERSION_SYMBOL(b, e, n)
> -#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
> -#define __vsym
> -#define BIND_DEFAULT_SYMBOL(b, e, n)
> -#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
> -/*
> - * RTE_BUILD_SHARED_LIB=n
> - */
> -#endif
> +#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
> +type name ## _exp args; \
> +type name ## _exp args
> +
> +#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
> +type name args
> +
> +#endif /* RTE_BUILD_SHARED_LIB */
>  
>  #endif /* _RTE_FUNCTION_VERSIONING_H_ */

Changes to this file look ok to me.

<snip>

^ permalink raw reply	[relevance 0%]

* Re: [RFC v3 3/8] eal: rework function versioning macros
  2025-03-13 16:53  0%     ` Bruce Richardson
@ 2025-03-13 17:09  0%       ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-13 17:09 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: dev, thomas, andremue, Tyler Retzlaff, Jasvinder Singh

On Thu, Mar 13, 2025 at 5:54 PM Bruce Richardson
<bruce.richardson@intel.com> wrote:
> > diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
> > index 7afd1c1886..88dd776b4c 100644
> > --- a/doc/guides/contributing/abi_versioning.rst
> > +++ b/doc/guides/contributing/abi_versioning.rst
> > @@ -138,27 +138,20 @@ macros are used in conjunction with the ``version.map`` file for
> >  a given library to allow multiple versions of a symbol to exist in a shared
> >  library so that older binaries need not be immediately recompiled.
> >
> > -The macros exported are:
> > +The macros are:
> >
> > -* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
> > -  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
> > +* ``RTE_VERSION_SYMBOL(ver, type, name, args``: Creates a symbol version table
>
> Missing closing brace .........................^ here
>
> > +  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
> > +  ``<name>_v<ver>``.
> >
> > -* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
> > -  the linker to bind references to symbol ``b`` to the internal symbol
> > -  ``be``.
> > +* ``RTE_DEFAULT_SYMBO(ver, type, name, args)``: Creates a symbol version entry
>
> s/SYMBO/SYMBOL/

Good catch thanks...

>
> > +  instructing the linker to bind references to symbol ``<name>`` to the internal
> > +  symbol ``<name>_v<ver>``.
> >
> > -* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
> > -  fully qualified function ``p``, so that if a symbol becomes versioned, it
> > -  can still be mapped back to the public symbol name.
> > -
> > -* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
> > -  ``be`` to signal that it is being used as an implementation of a particular
> > -  version of symbol ``b``.
> > -
> > -* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
> > -  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
> > -  The macro is used when a symbol matures to become part of the stable ABI, to
> > -  provide an alias to experimental until the next major ABI version.
> > +* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
> > +  but for experimental API symbols. The macro is used when a symbol matures
> > +  to become part of the stable ABI, to provide an alias to experimental
> > +  until the next major ABI version.
>
> Just to clarify - this is where we create two names/aliases for the one
> function, so it can be found either as an experimental version or a stable
> one, right? In that way it's actually quite different from
> RTE_VERSION_SYMBOL which is used to define a *NEW" version of an existing
> function, i.e. two functions rather than one function with two names.

I did not change the behavior, it is still mapping a symbol to an
actual implementation (there is an example later in this doc).
The previous description about an alias was probably inaccurate.


>
> >
> >  .. _example_abi_macro_usage:
> >
> > @@ -277,49 +270,36 @@ list of exported symbols when DPDK is compiled as a shared library.
> >
> >  Next, we need to specify in the code which function maps to the rte_acl_create
> >  symbol at which versions.  First, at the site of the initial symbol definition,
> > -we need to update the function so that it is uniquely named, and not in conflict
> > -with the public symbol name
> > +we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
> > +the function return type, and the function name and its arguments.
> >
> >  .. code-block:: c
> >
> >   -struct rte_acl_ctx *
> >   -rte_acl_create(const struct rte_acl_param *param)
> > - +struct rte_acl_ctx * __vsym
> > - +rte_acl_create_v21(const struct rte_acl_param *param)
> > + +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
> >   {
> >          size_t sz;
> >          struct rte_acl_ctx *ctx;
> >          ...
> > -
> > -Note that the base name of the symbol was kept intact, as this is conducive to
> > -the macros used for versioning symbols and we have annotated the function as
> > -``__vsym``, an implementation of a versioned symbol . That is our next step,
> > -mapping this new symbol name to the initial symbol name at version node 21.
> > -Immediately after the function, we add the VERSION_SYMBOL macro.
> > -
> > -.. code-block:: c
> > -
> > -   #include <rte_function_versioning.h>
> > -
> > -   ...
> > -   VERSION_SYMBOL(rte_acl_create, _v21, 21);
> > + }
> >
> >  Remembering to also add the rte_function_versioning.h header to the requisite c
> >  file where these changes are being made. The macro instructs the linker to
> >  create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
> > -in older builds, but now points to the above newly named function. We have now
> > -mapped the original rte_acl_create symbol to the original function (but with a
> > -new name).
> > +in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
> > +We have now mapped the original rte_acl_create symbol to the original function
> > +(but with a new name).
> >
> >  Please see the section :ref:`Enabling versioning macros
> >  <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
> > -Next, we need to create the new ``v22`` version of the symbol. We create a new
> > -function name, with the ``v22`` suffix, and implement it appropriately.
> > +Next, we need to create the new version of the symbol. We create a new
> > +function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
> >
> >  .. code-block:: c
> >
> > -   struct rte_acl_ctx * __vsym
> > -   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
> > +   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param,
> > +        int debug))
>
> Not directly relevant to the changes in this patch, but since this is
> documentation which doesn't actually need to be based on a real-life
> example, we should maybe come up with example functions which are short and
> don't need line wrapping. This example would be just as
> effective/instructive with a return value of "int" and parameter type
> without a "const" qualifier. :-)

Yep, I'll simplify the example.


>
> >     {
> >          struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
> >
> > @@ -328,35 +308,9 @@ function name, with the ``v22`` suffix, and implement it appropriately.
> >          return ctx;
> >     }
> >
> > -This code serves as our new API call. Its the same as our old call, but adds the
> > -new parameter in place. Next we need to map this function to the new default
> > -symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
> > -we add the BIND_DEFAULT_SYMBOL macro.
> > -
> > -.. code-block:: c
> > -
> > -   #include <rte_function_versioning.h>
> > -
> > -   ...
> > -   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
> > -
> >  The macro instructs the linker to create the new default symbol
> > -``rte_acl_create@DPDK_22``, which points to the above newly named function.
> > -
> > -We finally modify the prototype of the call in the public header file,
> > -such that it contains both versions of the symbol and the public API.
> > -
> > -.. code-block:: c
> > -
> > -   struct rte_acl_ctx *
> > -   rte_acl_create(const struct rte_acl_param *param);
> > -
> > -   struct rte_acl_ctx * __vsym
> > -   rte_acl_create_v21(const struct rte_acl_param *param);
> > -
> > -   struct rte_acl_ctx * __vsym
> > -   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
> > -
> > +``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
> > +(declared by the macro).
> >
> >  And that's it, on the next shared library rebuild, there will be two versions of
> >  rte_acl_create, an old DPDK_21 version, used by previously built applications,
> > @@ -365,43 +319,10 @@ and a new DPDK_22 version, used by future built applications.
> >  .. note::
> >
> >     **Before you leave**, please take care reviewing the sections on
> > -   :ref:`mapping static symbols <mapping_static_symbols>`,
> >     :ref:`enabling versioning macros <enabling_versioning_macros>`,
> >     and :ref:`ABI deprecation <abi_deprecation>`.
> >
> >
> > -.. _mapping_static_symbols:
> > -
> > -Mapping static symbols
> > -______________________
> > -
> > -Now we've taken what was a public symbol, and duplicated it into two uniquely
> > -and differently named symbols. We've then mapped each of those back to the
> > -public symbol ``rte_acl_create`` with different version tags. This only applies
> > -to dynamic linking, as static linking has no notion of versioning. That leaves
> > -this code in a position of no longer having a symbol simply named
> > -``rte_acl_create`` and a static build will fail on that missing symbol.
> > -
> > -To correct this, we can simply map a function of our choosing back to the public
> > -symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
> > -assumption is that the most recent version of the symbol is the one you want to
> > -map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
> > -defined, we add this
> > -
> > -
> > -.. code-block:: c
> > -
> > -   struct rte_acl_ctx * __vsym
> > -   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
> > -   {
> > -        ...
> > -   }
> > -   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
> > -
> > -That tells the compiler that, when building a static library, any calls to the
> > -symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
> > -
> > -
> >  .. _enabling_versioning_macros:
> >
> >  Enabling versioning macros
> > @@ -519,26 +440,17 @@ and ``DPDK_22`` version nodes.
> >      * Create an acl context object for apps to
> >      * manipulate
> >      */
> > -   struct rte_acl_ctx *
> > -   rte_acl_create(const struct rte_acl_param *param)
> > +   RTE_DEFAULT_SYMBOL(22, struct rte_acl_ctx *, rte_acl_create,
> > +        (const struct rte_acl_param *param))
> >     {
> >     ...
> >     }
> >
> > -   __rte_experimental
> > -   struct rte_acl_ctx *
> > -   rte_acl_create_e(const struct rte_acl_param *param)
> > -   {
> > -      return rte_acl_create(param);
> > -   }
> > -   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
> > -
> > -   struct rte_acl_ctx *
> > -   rte_acl_create_v22(const struct rte_acl_param *param)
> > +   RTE_VERSION_EXPERIMENTAL_SYMBOL(struct rte_acl_ctx *, rte_acl_create,
> > +        (const struct rte_acl_param *param))
> >     {
> >        return rte_acl_create(param);
> >     }
> > -   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
> >
> >  In the map file, we map the symbol to both the ``EXPERIMENTAL``
> >  and ``DPDK_22`` version nodes.
> > @@ -564,13 +476,6 @@ and ``DPDK_22`` version nodes.
> >          rte_acl_create;
> >     };
> >
> > -.. note::
> > -
> > -   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
> > -   when aliasing to experimental you will also need to take care of
> > -   :ref:`mapping static symbols <mapping_static_symbols>`.
> > -
> > -
> >  .. _abi_deprecation:
> >
> >  Deprecating part of a public API
> > @@ -616,10 +521,10 @@ Next remove the corresponding versioned export.
> >
> >  .. code-block:: c
> >
> > - -VERSION_SYMBOL(rte_acl_create, _v21, 21);
> > + -RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
> >
> >
> > -Note that the internal function definition could also be removed, but its used
> > +Note that the internal function definition must also be removed, but its used
>
> its -> it's (or "it is" if you want the longer version).

Ok, I'll fix this too.

>
> >  in our example by the newer version ``v22``, so we leave it in place and declare
> >  it as static. This is a coding style choice.
> >

[snip]

> > diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
> > index eb6dd2bc17..0020ce4885 100644
> > --- a/lib/eal/include/rte_function_versioning.h
> > +++ b/lib/eal/include/rte_function_versioning.h
> > @@ -11,8 +11,6 @@
> >  #error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
> >  #endif
> >
> > -#ifdef RTE_BUILD_SHARED_LIB
> > -
> >  /*
> >   * Provides backwards compatibility when updating exported functions.
> >   * When a symbol is exported from a library to provide an API, it also provides a
> > @@ -20,80 +18,54 @@
> >   * arguments, etc.  On occasion that function may need to change to accommodate
> >   * new functionality, behavior, etc.  When that occurs, it is desirable to
> >   * allow for backwards compatibility for a time with older binaries that are
> > - * dynamically linked to the dpdk.  To support that, the __vsym and
> > - * VERSION_SYMBOL macros are created.  They, in conjunction with the
> > - * version.map file for a given library allow for multiple versions of
> > - * a symbol to exist in a shared library so that older binaries need not be
> > - * immediately recompiled.
> > - *
> > - * Refer to the guidelines document in the docs subdirectory for details on the
> > - * use of these macros
> > + * dynamically linked to the dpdk.
> >   */
> >
> > -/*
> > - * Macro Parameters:
> > - * b - function base name
> > - * e - function version extension, to be concatenated with base name
> > - * n - function symbol version string to be applied
> > - * f - function prototype
> > - * p - full function symbol name
> > - */
> > +#ifdef RTE_BUILD_SHARED_LIB
> >
> >  /*
> > - * VERSION_SYMBOL
> > - * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
> > - * function name <b><e>
> > + * RTE_VERSION_SYMBOL
> > + * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
> > + * function name <name>_v<ver>.
> >   */
> > -#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
> > +#define RTE_VERSION_SYMBOL(ver, type, name, args) \
> > +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
> > +__rte_used type name ## _v ## ver args; \
> > +type name ## _v ## ver args
> >
> >  /*
> > - * VERSION_SYMBOL_EXPERIMENTAL
> > - * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
> > - * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
> > - * to provide an alias to experimental for some time.
> > + * RTE_VERSION_EXPERIMENTAL_SYMBOL
> > + * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
> > + * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
> >   */
> > -#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
> > +#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
> > +__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
> > +__rte_used type name ## _exp args; \
> > +type name ## _exp args
> >
> >  /*
> > - * BIND_DEFAULT_SYMBOL
> > + * RTE_DEFAULT_SYMBOL
> >   * Creates a symbol version entry instructing the linker to bind references to
> > - * symbol <b> to the internal symbol <b><e>
> > + * symbol <name> to the internal symbol <name>_v<ver>.
> >   */
> > -#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
> > +#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
> > +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
> > +__rte_used type name ## _v ## ver args; \
> > +type name ## _v ## ver args
> >
> > -/*
> > - * __vsym
> > - * Annotation to be used in declaration of the internal symbol <b><e> to signal
> > - * that it is being used as an implementation of a particular version of symbol
> > - * <b>.
> > - */
> > -#define __vsym __rte_used
> > +#else /* !RTE_BUILD_SHARED_LIB */
> >
> > -/*
> > - * MAP_STATIC_SYMBOL
> > - * If a function has been bifurcated into multiple versions, none of which
> > - * are defined as the exported symbol name in the map file, this macro can be
> > - * used to alias a specific version of the symbol to its exported name.  For
> > - * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
> > - * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
> > - * building a shared library, this macro can be used to map either foo_v1 or
> > - * foo_v2 to the symbol foo when building a static library, e.g.:
> > - * MAP_STATIC_SYMBOL(void foo(), foo_v2);
> > - */
> > -#define MAP_STATIC_SYMBOL(f, p)
> > +#define RTE_VERSION_SYMBOL(ver, type, name, args) \
> > +type name ## _v ## ver args; \
> > +type name ## _v ## ver args
> >
> > -#else
> > -/*
> > - * No symbol versioning in use
> > - */
> > -#define VERSION_SYMBOL(b, e, n)
> > -#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
> > -#define __vsym
> > -#define BIND_DEFAULT_SYMBOL(b, e, n)
> > -#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
> > -/*
> > - * RTE_BUILD_SHARED_LIB=n
> > - */
> > -#endif
> > +#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
> > +type name ## _exp args; \
> > +type name ## _exp args
> > +
> > +#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
> > +type name args
> > +
> > +#endif /* RTE_BUILD_SHARED_LIB */
> >
> >  #endif /* _RTE_FUNCTION_VERSIONING_H_ */
>
> Changes to this file look ok to me.

Thank you Bruce.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* Re: [RFC v3 5/8] build: generate symbol maps
  2025-03-11  9:56 18%   ` [RFC v3 5/8] build: generate symbol maps David Marchand
@ 2025-03-13 17:26  0%     ` Bruce Richardson
  2025-03-14 15:38  0%       ` David Marchand
  2025-03-14 15:27  0%     ` Andre Muezerie
  1 sibling, 1 reply; 153+ results
From: Bruce Richardson @ 2025-03-13 17:26 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas, andremue

On Tue, Mar 11, 2025 at 10:56:03AM +0100, David Marchand wrote:
> Rather than maintain a file in parallel of the code, symbols to be
> exported can be marked with a token RTE_EXPORT_*SYMBOL.
> 
> From those marks, the build framework generates map files only for
> symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
> becomes unnecessary).
> 
> The build framework directly creates a map file in the format that the
> linker expects (rather than converting from GNU linker to MSVC linker).
> 
> Empty maps are allowed again as a replacement for drivers/version.map.
> 
> The symbol check is updated to only support the new format.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>

Some comments inline below.
/Bruce

> ---
> Changes since RFC v2:
> - because of MSVC limitations wrt macro passed via cmdline,
>   used an internal header for defining RTE_EXPORT_* macros,
> - updated documentation and tooling,
> 
> ---
>  MAINTAINERS                                |   2 +
>  buildtools/gen-version-map.py              | 111 ++++++++++
>  buildtools/map-list-symbol.sh              |  10 +-
>  buildtools/meson.build                     |   1 +
>  config/meson.build                         |   2 +
>  config/rte_export.h                        |  16 ++
>  devtools/check-symbol-change.py            |  90 +++++++++
>  devtools/check-symbol-maps.sh              |  14 --
>  devtools/checkpatches.sh                   |   2 +-
>  doc/guides/contributing/abi_versioning.rst | 224 ++-------------------
>  drivers/meson.build                        |  94 +++++----
>  drivers/version.map                        |   3 -
>  lib/meson.build                            |  91 ++++++---
>  13 files changed, 371 insertions(+), 289 deletions(-)
>  create mode 100755 buildtools/gen-version-map.py
>  create mode 100644 config/rte_export.h
>  create mode 100755 devtools/check-symbol-change.py
>  delete mode 100644 drivers/version.map
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 312e6fcee5..04772951d3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
>  F: devtools/check-forbidden-tokens.awk
>  F: devtools/check-git-log.sh
>  F: devtools/check-spdx-tag.sh
> +F: devtools/check-symbol-change.py
>  F: devtools/check-symbol-change.sh
>  F: devtools/check-symbol-maps.sh
>  F: devtools/checkpatches.sh
> @@ -127,6 +128,7 @@ F: config/
>  F: buildtools/check-symbols.sh
>  F: buildtools/chkincs/
>  F: buildtools/call-sphinx-build.py
> +F: buildtools/gen-version-map.py
>  F: buildtools/get-cpu-count.py
>  F: buildtools/get-numa-count.py
>  F: buildtools/list-dir-globs.py
> diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
> new file mode 100755
> index 0000000000..b160aa828b
> --- /dev/null
> +++ b/buildtools/gen-version-map.py
> @@ -0,0 +1,111 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (c) 2024 Red Hat, Inc.
> +
> +"""Generate a version map file used by GNU or MSVC linker."""
> +

While it's an internal build script not to be run by users directly, I
believe a short one-line usage here might be useful, since the code below
is directly referencing sys.argv[N] values. That makes it easier for the
user to know what they are.

Alternatively, assign them to proper names at the top of the script e.g.:
	scriptname, link_mode, abi_version_file, output, *input = sys.argv

Final alternative (which may be a bit overkill) is to use argparse.

> +import re
> +import sys
> +
> +# From rte_export.h
> +export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
> +export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> +export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
> +# From rte_function_versioning.h
> +ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> +default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +
> +with open(sys.argv[2]) as f:
> +    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
> +
> +symbols = {}
> +
> +for file in sys.argv[4:]:
> +    with open(file, encoding="utf-8") as f:
> +        for ln in f.readlines():
> +            node = None
> +            symbol = None
> +            comment = None
> +            if export_exp_sym_regexp.match(ln):
> +                node = 'EXPERIMENTAL'
> +                symbol = export_exp_sym_regexp.match(ln).group(1)
> +                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
> +            elif export_int_sym_regexp.match(ln):
> +                node = 'INTERNAL'
> +                symbol = export_int_sym_regexp.match(ln).group(1)
> +            elif export_sym_regexp.match(ln):
> +                node = abi
> +                symbol = export_sym_regexp.match(ln).group(1)
> +            elif ver_sym_regexp.match(ln):
> +                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
> +                symbol = ver_sym_regexp.match(ln).group(2)
> +            elif ver_exp_sym_regexp.match(ln):
> +                node = 'EXPERIMENTAL'
> +                symbol = ver_exp_sym_regexp.match(ln).group(1)
> +            elif default_sym_regexp.match(ln):
> +                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
> +                symbol = default_sym_regexp.match(ln).group(2)
> +
> +            if not symbol:
> +                continue
> +
> +            if node not in symbols:
> +                symbols[node] = {}
> +            symbols[node][symbol] = comment
> +
> +if sys.argv[1] == 'msvc':
> +    with open(sys.argv[3], "w") as outfile:
> +        outfile.writelines(f"EXPORTS\n")
> +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> +            if key not in symbols:
> +                continue
> +            for symbol in sorted(symbols[key].keys()):
> +                outfile.writelines(f"\t{symbol}\n")
> +            del symbols[key]
> +else:
> +    with open(sys.argv[3], "w") as outfile:
> +        local_token = False
> +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> +            if key not in symbols:
> +                continue
> +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> +            for symbol in sorted(symbols[key].keys()):
> +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> +                    prefix = '__emutls_v.'
> +                else:
> +                    prefix = ''
> +                outfile.writelines(f"\t{prefix}{symbol};")
> +                comment = symbols[key][symbol]
> +                if comment:
> +                    outfile.writelines(f"{comment}")
> +                outfile.writelines("\n")

How about using "" rather than None for the default comment so you can
always just do a print of "{prefix}{symbol};{comment}\n". The fact that
writelines doesn't output a "\n" is a little confusing here, so maybe use
"print" instead.

	print("f\t{prefix}{symbol};{comment}", file=outfile)

> +            outfile.writelines("\n")
> +            if not local_token:
> +                outfile.writelines("\tlocal: *;\n")
> +                local_token = True
> +            outfile.writelines("};\n")
> +            del symbols[key]
> +        for key in sorted(symbols.keys()):
> +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> +            for symbol in sorted(symbols[key].keys()):
> +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> +                    prefix = '__emutls_v.'
> +                else:
> +                    prefix = ''
> +                outfile.writelines(f"\t{prefix}{symbol};")
> +                comment = symbols[key][symbol]
> +                if comment:
> +                    outfile.writelines(f"{comment}")
> +                outfile.writelines("\n")
> +            outfile.writelines(f"}} {abi};\n")
> +            if not local_token:
> +                outfile.writelines("\tlocal: *;\n")
> +                local_token = True
> +            del symbols[key]
> +        # No exported symbol, add a catch all
> +        if not local_token:
> +            outfile.writelines(f"{abi} {{\n")
> +            outfile.writelines("\tlocal: *;\n")
> +            local_token = True
> +            outfile.writelines("};\n")
> diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
> index eb98451d8e..0829df4be5 100755
> --- a/buildtools/map-list-symbol.sh
> +++ b/buildtools/map-list-symbol.sh
> @@ -62,10 +62,14 @@ for file in $@; do
>  		if (current_section == "") {
>  			next;
>  		}
> +		symbol_version = current_version
> +		if (/^[^}].*[^:*]; # added in /) {
> +			symbol_version = $5
> +		}
>  		if ("'$version'" != "") {
> -			if ("'$version'" == "unset" && current_version != "") {
> +			if ("'$version'" == "unset" && symbol_version != "") {
>  				next;
> -			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
> +			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
>  				next;
>  			}
>  		}
> @@ -73,7 +77,7 @@ for file in $@; do
>  		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
>  			ret = 0;
>  			if ("'$quiet'" == "") {
> -				print "'$file' "current_section" "$1" "current_version;
> +				print "'$file' "current_section" "$1" "symbol_version;
>  			}
>  			if ("'$symbol'" != "all") {
>  				exit 0;
> diff --git a/buildtools/meson.build b/buildtools/meson.build
> index 4e2c1217a2..b745e9afa4 100644
> --- a/buildtools/meson.build
> +++ b/buildtools/meson.build
> @@ -16,6 +16,7 @@ else
>      py3 = ['meson', 'runpython']
>  endif
>  echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
> +gen_version_map = py3 + files('gen-version-map.py')
>  list_dir_globs = py3 + files('list-dir-globs.py')
>  map_to_win_cmd = py3 + files('map_to_win.py')
>  sphinx_wrapper = py3 + files('call-sphinx-build.py')
> diff --git a/config/meson.build b/config/meson.build
> index f31fef216c..54657055fb 100644
> --- a/config/meson.build
> +++ b/config/meson.build
> @@ -303,8 +303,10 @@ endif
>  # add -include rte_config to cflags
>  if is_ms_compiler
>      add_project_arguments('/FI', 'rte_config.h', language: 'c')
> +    add_project_arguments('/FI', 'rte_export.h', language: 'c')
>  else
>      add_project_arguments('-include', 'rte_config.h', language: 'c')
> +    add_project_arguments('-include', 'rte_export.h', language: 'c')
>  endif
>  
>  # enable extra warnings and disable any unwanted warnings
> diff --git a/config/rte_export.h b/config/rte_export.h
> new file mode 100644
> index 0000000000..83d871fe11
> --- /dev/null
> +++ b/config/rte_export.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright (c) 2025 Red Hat, Inc.
> + */
> +
> +#ifndef RTE_EXPORT_H
> +#define RTE_EXPORT_H
> +
> +/* *Internal* macros for exporting symbols, used by the build system.
> + * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
> + * version this symbol was introduced in.
> + */
> +#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
> +#define RTE_EXPORT_INTERNAL_SYMBOL(a)
> +#define RTE_EXPORT_SYMBOL(a)
> +
> +#endif /* RTE_EXPORT_H */
> diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
> new file mode 100755
> index 0000000000..09709e4f06
> --- /dev/null
> +++ b/devtools/check-symbol-change.py
> @@ -0,0 +1,90 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (c) 2025 Red Hat, Inc.
> +
> +"""Check exported symbols change in a patch."""
> +
> +import re
> +import sys
> +
> +file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
> +# From rte_export.h
> +export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
> +export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> +export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
> +# TODO, handle versioned symbols from rte_function_versioning.h
> +# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> +# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +
> +symbols = {}
> +
> +for file in sys.argv[1:]:
> +    with open(file, encoding="utf-8") as f:
> +        for ln in f.readlines():
> +            if file_header_regexp.match(ln):
> +                if file_header_regexp.match(ln).group(2) == "lib":
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> +                elif file_header_regexp.match(ln).group(3) == "intel":
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
> +                else:
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> +
> +                if lib not in symbols:
> +                    symbols[lib] = {}
> +                continue
> +
> +            if export_exp_sym_regexp.match(ln):
> +                symbol = export_exp_sym_regexp.match(ln).group(1)
> +                node = 'EXPERIMENTAL'
> +            elif export_int_sym_regexp.match(ln):
> +                node = 'INTERNAL'
> +                symbol = export_int_sym_regexp.match(ln).group(1)
> +            elif export_sym_regexp.match(ln):
> +                symbol = export_sym_regexp.match(ln).group(1)
> +                node = 'stable'
> +            else:
> +                continue
> +
> +            if symbol not in symbols[lib]:
> +                symbols[lib][symbol] = {}
> +            added = ln[0] == '+'
> +            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
> +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> +            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
> +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> +            if added:
> +                symbols[lib][symbol]['added'] = node
> +            else:
> +                symbols[lib][symbol]['removed'] = node
> +
> +    for lib in sorted(symbols.keys()):
> +        error = False
> +        for symbol in sorted(symbols[lib].keys()):
> +            if 'removed' not in symbols[lib][symbol]:
> +                # Symbol addition
> +                node = symbols[lib][symbol]['added']
> +                if node == 'stable':
> +                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
> +                    error = True
> +                else:
> +                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
> +                continue
> +
> +            if 'added' not in symbols[lib][symbol]:
> +                # Symbol removal
> +                node = symbols[lib][symbol]['added']
> +                if node == 'stable':
> +                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
> +                    print(f"Please check it has gone though the deprecation process.")
> +                continue
> +
> +            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
> +                # Symbol was moved around
> +                continue
> +
> +            # Symbol modifications
> +            added = symbols[lib][symbol]['added']
> +            removed = symbols[lib][symbol]['removed']
> +            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
> +            print(f"Please check it has gone though the deprecation process.")
> diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
> index 6121f78ec6..fcd3931e5d 100755
> --- a/devtools/check-symbol-maps.sh
> +++ b/devtools/check-symbol-maps.sh
> @@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
>      ret=1
>  fi
>  
> -find_empty_maps ()
> -{
> -    for map in $@ ; do
> -        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
> -    done
> -}
> -
> -empty_maps=$(find_empty_maps $@)
> -if [ -n "$empty_maps" ] ; then
> -    echo "Found empty maps:"
> -    echo "$empty_maps"
> -    ret=1
> -fi
> -
>  find_bad_format_maps ()
>  {
>      abi_version=$(cut -d'.' -f 1 ABI_VERSION)
> diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
> index 003bb49e04..7dcac7c8c9 100755
> --- a/devtools/checkpatches.sh
> +++ b/devtools/checkpatches.sh
> @@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
>  PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
>  SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
>  LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
> -NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
> +NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
>  options="$options $DPDK_CHECKPATCH_OPTIONS"
>  
>  print_usage () {
> diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
> index 88dd776b4c..addbb24b9e 100644
> --- a/doc/guides/contributing/abi_versioning.rst
> +++ b/doc/guides/contributing/abi_versioning.rst
> @@ -58,12 +58,12 @@ persists over multiple releases.
>  
>  .. code-block:: none
>  
> - $ head ./lib/acl/version.map
> + $ head ./build/lib/librte_acl_exports.map

I must admit I'm not a fan of these long filenames. How about just
"acl_exports.map"?

>   DPDK_21 {
>          global:
>   ...
>  
> - $ head ./lib/eal/version.map
> + $ head ./build/lib/librte_eal_exports.map
>   DPDK_21 {
>          global:
>   ...
> @@ -77,7 +77,7 @@ that library.
>  
>  .. code-block:: none
>  
> - $ head ./lib/acl/version.map
> + $ head ./build/lib/librte_acl_exports.map
>   DPDK_21 {
>          global:
>   ...
> @@ -88,7 +88,7 @@ that library.
>   } DPDK_21;
>   ...
>  
> - $ head ./lib/eal/version.map
> + $ head ./build/lib/librte_eal_exports.map
>   DPDK_21 {
>          global:
>   ...
> @@ -100,12 +100,12 @@ how this may be done.
>  
>  .. code-block:: none
>  
> - $ head ./lib/acl/version.map
> + $ head ./build/lib/librte_acl_exports.map
>   DPDK_22 {
>          global:
>   ...
>  
> - $ head ./lib/eal/version.map
> + $ head ./build/lib/librte_eal_exports.map
>   DPDK_22 {
>          global:
>   ...
> @@ -134,8 +134,7 @@ linked to the DPDK.
>  
>  To support backward compatibility the ``rte_function_versioning.h``
>  header file provides macros to use when updating exported functions. These
> -macros are used in conjunction with the ``version.map`` file for
> -a given library to allow multiple versions of a symbol to exist in a shared
> +macros allow multiple versions of a symbol to exist in a shared
>  library so that older binaries need not be immediately recompiled.
>  
>  The macros are:
> @@ -169,6 +168,7 @@ Assume we have a function as follows
>    * Create an acl context object for apps to
>    * manipulate
>    */
> + RTE_EXPORT_SYMBOL(rte_acl_create)
>   struct rte_acl_ctx *
>   rte_acl_create(const struct rte_acl_param *param)
>   {
> @@ -187,6 +187,7 @@ private, is safe), but it also requires modifying the code as follows
>    * Create an acl context object for apps to
>    * manipulate
>    */
> + RTE_EXPORT_SYMBOL(rte_acl_create)
>   struct rte_acl_ctx *
>   rte_acl_create(const struct rte_acl_param *param, int debug)
>   {
> @@ -203,78 +204,16 @@ The addition of a parameter to the function is ABI breaking as the function is
>  public, and existing application may use it in its current form. However, the
>  compatibility macros in DPDK allow a developer to use symbol versioning so that
>  multiple functions can be mapped to the same public symbol based on when an
> -application was linked to it. To see how this is done, we start with the
> -requisite libraries version map file. Initially the version map file for the acl
> -library looks like this
> +application was linked to it.
>  
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -
> -        rte_acl_add_rules;
> -        rte_acl_build;
> -        rte_acl_classify;
> -        rte_acl_classify_alg;
> -        rte_acl_classify_scalar;
> -        rte_acl_create;
> -        rte_acl_dump;
> -        rte_acl_find_existing;
> -        rte_acl_free;
> -        rte_acl_ipv4vlan_add_rules;
> -        rte_acl_ipv4vlan_build;
> -        rte_acl_list_dump;
> -        rte_acl_reset;
> -        rte_acl_reset_rules;
> -        rte_acl_set_ctx_classify;
> -
> -        local: *;
> -   };
> -
> -This file needs to be modified as follows
> -
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -
> -        rte_acl_add_rules;
> -        rte_acl_build;
> -        rte_acl_classify;
> -        rte_acl_classify_alg;
> -        rte_acl_classify_scalar;
> -        rte_acl_create;
> -        rte_acl_dump;
> -        rte_acl_find_existing;
> -        rte_acl_free;
> -        rte_acl_ipv4vlan_add_rules;
> -        rte_acl_ipv4vlan_build;
> -        rte_acl_list_dump;
> -        rte_acl_reset;
> -        rte_acl_reset_rules;
> -        rte_acl_set_ctx_classify;
> -
> -        local: *;
> -   };
> -
> -   DPDK_22 {
> -        global:
> -        rte_acl_create;
> -
> -   } DPDK_21;
> -
> -The addition of the new block tells the linker that a new version node
> -``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
> -the symbols from the DPDK_21 node. This list is directly translated into a
> -list of exported symbols when DPDK is compiled as a shared library.
> -
> -Next, we need to specify in the code which function maps to the rte_acl_create
> +We need to specify in the code which function maps to the rte_acl_create
>  symbol at which versions.  First, at the site of the initial symbol definition,
>  we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
> -the function return type, and the function name and its arguments.
> +the function return type, the function name and its arguments.

Good fix, though technically not relevant to this patch.

>  
>  .. code-block:: c
>  
> + -RTE_EXPORT_SYMBOL(rte_acl_create)
>   -struct rte_acl_ctx *
>   -rte_acl_create(const struct rte_acl_param *param)
>   +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
> @@ -293,6 +232,7 @@ We have now mapped the original rte_acl_create symbol to the original function
>  
>  Please see the section :ref:`Enabling versioning macros
>  <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
> +

Ditto.

>  Next, we need to create the new version of the symbol. We create a new
>  function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
>  
> @@ -312,9 +252,9 @@ The macro instructs the linker to create the new default symbol
>  ``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
>  (declared by the macro).
>  
> -And that's it, on the next shared library rebuild, there will be two versions of
> -rte_acl_create, an old DPDK_21 version, used by previously built applications,
> -and a new DPDK_22 version, used by future built applications.
> +And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
> +an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
> +used by future built applications.

nit: not sure what others think but "future built" sounds strange to me?
How about "later built" or "newly built"?

>  
>  .. note::
>  
> @@ -364,6 +304,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
>      * Create an acl context object for apps to
>      * manipulate
>      */
<snip>

^ permalink raw reply	[relevance 0%]

* Community CI Meeting Minutes - January 9, 2025
@ 2025-03-13 22:54  4% Patrick Robb
  0 siblings, 0 replies; 153+ results
From: Patrick Robb @ 2025-03-13 22:54 UTC (permalink / raw)
  To: dev; +Cc: ci

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

#####################################################################
January 9, 2025
Attendees
. Patrick Robb
. Aaron Conole
. Luca Vizzarro
. Dean Marx
. Cody Cheng
. Aaron Conole
. Nicholas Pratte
. Matthew McGovern
. Manit Mahajan

#####################################################################
Minutes

=====================================================================
General Announcements
* Happy New Year!
* 25.03 release schedule is:
   * (-rc1): 7 February 2025
   * (-rc2): 28 February 2025
   * (-rc3): 7 March 2025
   * Release: 26 March 2025

=====================================================================
CI Status

---------------------------------------------------------------------
UNH-IOL Community Lab
* OvS install now requires python 3.7, which opensuse 15.6 does not ship
with. So, options are to either upgrade python on this test container, or
discontinue the test for this distro.
* Lab Dashboard updates:
   * We are adding a patch series search feature for searching by series
title or series # id. This will deploy before end of week.
   * Working on embedding the code coverage reports in the dashboard
   * Working on embedding the code coverage reports in the dashboard such
that the reports don’t have to be manually downloaded and opened by users
      * We should also begin running the coverage reports on a per series
basis
         * Can produce json output from gcov/lcov, and compare the
“basline” json coverage to a new result
* ABI testing: We have been running ABI tests for 24.11 and main for the
past few weeks, but still need to backfill ABI results for patches
submitted immediately after 24.11 released. We can put these into queue
today.
* The lab will have a little downtime next week for infra maintenance,
which will be announced the day before on the mailing list.

---------------------------------------------------------------------
Intel Lab
* None

---------------------------------------------------------------------
Github Actions
* None

---------------------------------------------------------------------
Loongarch Lab
* None

=====================================================================
DTS Improvements & Test Development
* Series Patrick should review:
   * Dean: queue start stop
   * Nick: ethertype test suite
   * Softnic testsuite
      * V3 is submitted
      * * Perf testing:
   * UNH team has been investigating performance traffic generators
      * Trex
         * Have just been testing the trex api for setting traffic streams,
collecting stats etc.
      * Dperf
      * Need to decide on collecting stats via trex API vs testpmd when
writing the single core forwarding perf test
   * Luca proposes an abstraction for creating files on the DTS nodes, such
that we don’t have to manually create and copy files requires for DTS
testsuites.

=====================================================================
Any other business
* Next Meeting January 23
* Azure:
   * Currently using a “Lisa” test framework for managing nodes and running
some tests
   * Aiming to leverage DTS in the future
   * Can we get 2 nodes on the same l2 networking?
      * Not really on Azure, so we would need to provide support for tests
through l3 routing.

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

^ permalink raw reply	[relevance 4%]

* [PATCH] raw/cnxk_gpio: switch to character based GPIO interface
@ 2025-03-14 12:57  1% Tomasz Duszynski
  2025-03-24  8:28  1% ` [PATCH v2] " Tomasz Duszynski
  0 siblings, 1 reply; 153+ results
From: Tomasz Duszynski @ 2025-03-14 12:57 UTC (permalink / raw)
  To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj

The direct passthrough interrupt mechanism, which allowed bypassing the
kernel, was obscure and is no longer supported. So this driver won't
work with latest SDK kernels. Additionally, the sysfs GPIO control
interface has been deprecated by Linux kernel itself.

That said, this change updates the PMD to use the current GPIO interface
ensuring compatibility with current kernel standards while improving
maintainability and security.

Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
---
 doc/guides/rawdevs/cnxk_gpio.rst           |  37 +-
 drivers/raw/cnxk_gpio/cnxk_gpio.c          | 518 ++++++++++++---------
 drivers/raw/cnxk_gpio/cnxk_gpio.h          |  17 +-
 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c      | 216 ---------
 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 234 ++--------
 drivers/raw/cnxk_gpio/meson.build          |   1 -
 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h  |  57 ++-
 7 files changed, 427 insertions(+), 653 deletions(-)
 delete mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c

diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst
index 954d3b8905..8084dd4adb 100644
--- a/doc/guides/rawdevs/cnxk_gpio.rst
+++ b/doc/guides/rawdevs/cnxk_gpio.rst
@@ -6,31 +6,20 @@ Marvell CNXK GPIO Driver
 
 CNXK GPIO PMD configures and manages GPIOs available on the system using
 standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies
-both on standard sysfs GPIO interface provided by the Linux kernel and GPIO
-kernel driver custom interface allowing one to install userspace interrupt
-handlers.
+on standard kernel GPIO character device interface.
 
 Features
 --------
 
 Following features are available:
 
-- export/unexport a GPIO
-- read/write specific value from/to exported GPIO
+- read/write specific value from/to GPIO
 - set GPIO direction
 - set GPIO edge that triggers interrupt
 - set GPIO active low
 - register interrupt handler for specific GPIO
 - multiprocess aware
 
-Requirements
-------------
-
-PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface
-for installing interrupt handlers for low latency signal processing.
-
-Driver is shipped with Marvell SDK.
-
 Limitations
 -----------
 
@@ -43,20 +32,20 @@ Device Setup
 CNXK GPIO PMD binds to virtual device which gets created by passing
 `--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter
 tells PMD which GPIO controller should be used. Available controllers are
-available under `/sys/class/gpio`. For further details on how Linux represents
-GPIOs in userspace please refer to
-`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_.
+`/dev/gpiochipN` character devices. For further details on
+how Linux represents GPIOs in userspace please refer to
+`gpio-cdev <https://www.kernel.org/doc/Documentation/ABI/testing/gpio-cdev>`_.
 
 If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically
 sort list of available gpiochips is used.
 
 .. code-block:: console
 
-   $ ls /sys/class/gpio
-   export gpiochip448 unexport
+   $ ls /dev/gpiochip*
+   /dev/gpiochip0
 
 In above scenario only one GPIO controller is present hence
-`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL.
+`--vdev=cnxk_gpio,gpiochip=0` should be passed to EAL.
 
 Before performing actual data transfer one needs to call
 ``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The
@@ -65,7 +54,7 @@ being controllable or not. Thus it is user responsibility to pick the proper
 ones. The latter call simply returns queue capacity.
 
 In order to allow using only subset of available GPIOs `allowlist` PMD param may
-be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]`
+be used. For example passing `--vdev=cnxk_gpio,gpiochip=0,allowlist=[0,1,2,3]`
 to EAL will deny using all GPIOs except those specified explicitly in the
 `allowlist`.
 
@@ -179,12 +168,12 @@ Request interrupt
 
 Message is used to install custom interrupt handler.
 
-Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``.
+Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2``.
 
-Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt
+Payload needs to be set to ``struct cnxk_gpio_irq2`` which describes interrupt
 being requested.
 
-Consider using ``rte_pmd_gpio_register_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_register_irq2()`` wrapper.
 
 Free interrupt
 ~~~~~~~~~~~~~~
@@ -193,7 +182,7 @@ Message is used to remove installed interrupt handler.
 
 Message must have type set to ``CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ``.
 
-Consider using ``rte_pmd_gpio_unregister_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_unregister_irq()`` wrapper.
 
 Self test
 ---------
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c
index 329ac28a27..800f2cf1c3 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c
@@ -3,11 +3,19 @@
  */
 
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+#include <regex.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 
 #include <bus_vdev_driver.h>
 #include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_interrupts.h>
 #include <rte_kvargs.h>
 #include <rte_lcore.h>
 #include <rte_rawdev_pmd.h>
@@ -17,9 +25,8 @@
 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"
 
-#define CNXK_GPIO_BUFSZ 128
-#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio"
 #define CNXK_GPIO_PARAMS_MZ_NAME "cnxk_gpio_params_mz"
+#define CNXK_GPIO_INVALID_FD (-1)
 
 struct cnxk_gpio_params {
 	unsigned int num;
@@ -40,6 +47,31 @@ cnxk_gpio_format_name(char *name, size_t len)
 	snprintf(name, len, "cnxk_gpio");
 }
 
+static int
+cnxk_gpio_ioctl(struct cnxk_gpio *gpio, unsigned long cmd, void *arg)
+{
+	return ioctl(gpio->fd, cmd, arg) ? -errno : 0;
+}
+
+static int
+cnxk_gpio_gpiochip_ioctl(struct cnxk_gpiochip *gpiochip, unsigned long cmd, void *arg)
+{
+	char path[PATH_MAX];
+	int ret = 0, fd;
+
+	snprintf(path, sizeof(path), "/dev/gpiochip%d", gpiochip->num);
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -errno;
+
+	if (ioctl(fd, cmd, arg))
+		ret = -errno;
+
+	close(fd);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_filter_gpiochip(const struct dirent *dirent)
 {
@@ -54,8 +86,7 @@ cnxk_gpio_set_defaults(struct cnxk_gpio_params *params)
 	struct dirent **namelist;
 	int ret = 0, n;
 
-	n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip,
-		    alphasort);
+	n = scandir("/dev", &namelist, cnxk_gpio_filter_gpiochip, alphasort);
 	if (n < 0 || n == 0)
 		return -ENODEV;
 
@@ -143,7 +174,7 @@ cnxk_gpio_parse_arg(struct rte_kvargs *kvlist, const char *arg, arg_handler_t ha
 static int
 cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 {
-	size_t len = sizeof(**params);
+	size_t len = sizeof(**params) + 1;
 	const char *allowlist = NULL;
 	struct rte_kvargs *kvlist;
 	int ret;
@@ -163,11 +194,13 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 
 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_ALLOWLIST, cnxk_gpio_parse_arg_allowlist,
 				  &allowlist);
-	if (ret < 0)
+	if (ret < 0) {
+		ret = -EINVAL;
 		goto out;
+	}
 
 	if (allowlist)
-		len += strlen(allowlist) + 1;
+		len += strlen(allowlist);
 
 	*params = cnxk_gpio_params_reserve(len);
 	if (!(*params)) {
@@ -175,7 +208,8 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 		goto out;
 	}
 
-	strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);
+	if (allowlist)
+		strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);
 
 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_GPIOCHIP, cnxk_gpio_parse_arg_gpiochip,
 				  &(*params)->num);
@@ -188,6 +222,24 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 	return ret;
 }
 
+static bool
+cnxk_gpio_allowlist_valid(const char *allowlist)
+{
+	bool ret = false;
+	regex_t regex;
+
+	/* [gpio0<,gpio1,...,gpioN>], where '<...>' is optional part */
+	if (regcomp(&regex, "^\\[[0-9]+(,[0-9]+)*\\]$", REG_EXTENDED))
+		return ret;
+
+	if (!regexec(&regex, allowlist, 0, NULL, 0))
+		ret = true;
+
+	regfree(&regex);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 {
@@ -199,6 +251,17 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	if (!list)
 		return -ENOMEM;
 
+	/* no gpios provided so allow all */
+	if (!*allowlist) {
+		for (i = 0; i < gpiochip->num_gpios; i++)
+			list[queue++] = i;
+
+		goto out_done;
+	}
+
+	if (!cnxk_gpio_allowlist_valid(allowlist))
+		return -EINVAL;
+
 	allowlist = strdup(allowlist);
 	if (!allowlist) {
 		ret = -ENOMEM;
@@ -239,6 +302,7 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	} while ((token = strtok(NULL, ",")));
 
 	free(allowlist);
+out_done:
 	gpiochip->allowlist = list;
 	gpiochip->num_queues = queue;
 
@@ -250,88 +314,6 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	return ret;
 }
 
-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-static int
-cnxk_gpio_read_attr_int(char *attr, int *val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret;
-
-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	ret = sscanf(buf, "%d", val);
-	if (ret < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr(const char *attr, const char *val)
-{
-	FILE *fp;
-	int ret;
-
-	if (!val)
-		return -EINVAL;
-
-	fp = fopen(attr, "w");
-	if (!fp)
-		return -errno;
-
-	ret = fprintf(fp, "%s", val);
-	if (ret < 0) {
-		fclose(fp);
-		return ret;
-	}
-
-	ret = fclose(fp);
-	if (ret)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr_int(const char *attr, int val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-
-	snprintf(buf, sizeof(buf), "%d", val);
-
-	return cnxk_gpio_write_attr(attr, buf);
-}
-
 static bool
 cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 {
@@ -353,14 +335,16 @@ cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 }
 
 static bool
-cnxk_gpio_exists(int num)
+cnxk_gpio_available(struct cnxk_gpio *gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	struct stat st;
+	struct gpio_v2_line_info info = { .offset = gpio->num };
+	int ret;
 
-	snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, num);
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return false;
 
-	return !stat(buf, &st);
+	return !(info.flags & GPIO_V2_LINE_FLAG_USED);
 }
 
 static int
@@ -368,9 +352,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 		      rte_rawdev_obj_t queue_conf, size_t queue_conf_size)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
+	struct gpio_v2_line_request req = {0};
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int ret;
 
 	RTE_SET_USED(queue_conf);
 	RTE_SET_USED(queue_conf_size);
@@ -386,33 +370,87 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 	if (!gpio)
 		return -ENOMEM;
 
-	num = cnxk_queue_to_gpio(gpiochip, queue_id);
-	gpio->num = num + gpiochip->base;
+	gpio->num = cnxk_queue_to_gpio(gpiochip, queue_id);
+	gpio->fd = CNXK_GPIO_INVALID_FD;
 	gpio->gpiochip = gpiochip;
 
-	if (!cnxk_gpio_exists(gpio->num)) {
-		snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH);
-		ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-		if (ret) {
-			rte_free(gpio);
-			return ret;
-		}
-	} else {
-		CNXK_GPIO_LOG(WARNING, "using existing gpio%d", gpio->num);
+	if (!cnxk_gpio_available(gpio)) {
+		rte_free(gpio);
+		return -EBUSY;
+	}
+
+	cnxk_gpio_format_name(req.consumer, sizeof(req.consumer));
+	req.offsets[req.num_lines] = gpio->num;
+	req.num_lines = 1;
+
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINE_IOCTL, &req);
+	if (ret) {
+		rte_free(gpio);
+		return ret;
+	}
+
+	gpio->fd = req.fd;
+	gpiochip->gpios[gpio->num] = gpio;
+
+	return 0;
+}
+
+static void
+cnxk_gpio_intr_handler(void *data)
+{
+	struct gpio_v2_line_event event;
+	struct cnxk_gpio *gpio = data;
+	int ret;
+
+	ret = read(gpio->fd, &event, sizeof(event));
+	if (ret != sizeof(event)) {
+		CNXK_GPIO_LOG(ERR, "failed to read gpio%d event data", gpio->num);
+		goto out;
+	}
+	if ((unsigned int)gpio->num != event.offset) {
+		CNXK_GPIO_LOG(ERR, "expected event from gpio%d, received from gpio%d",
+			      gpio->num, event.offset);
+		goto out;
 	}
 
-	gpiochip->gpios[num] = gpio;
+	if (gpio->intr.handler2)
+		(gpio->intr.handler2)(&event, gpio->intr.data);
+	else if (gpio->intr.handler)
+		(gpio->intr.handler)(gpio->num, gpio->intr.data);
+out:
+	rte_intr_ack(gpio->intr.intr_handle);
+}
+
+static int
+cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+{
+	int ret;
+
+	if (!gpio->intr.intr_handle)
+		return 0;
+
+	ret = rte_intr_disable(gpio->intr.intr_handle);
+	if (ret)
+		return ret;
+
+	ret = rte_intr_callback_unregister_sync(gpio->intr.intr_handle, cnxk_gpio_intr_handler,
+						(void *)-1);
+	if (ret)
+		return ret;
+
+	rte_intr_instance_free(gpio->intr.intr_handle);
+	gpio->intr.intr_handle = NULL;
 
 	return 0;
 }
 
+
 static int
 cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int num;
 
 	if (!cnxk_gpio_queue_valid(gpiochip, queue_id))
 		return -EINVAL;
@@ -421,10 +459,11 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 	if (!gpio)
 		return -ENODEV;
 
-	snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH);
-	ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-	if (ret)
-		return ret;
+	if (gpio->intr.intr_handle)
+		cnxk_gpio_unregister_irq(gpio);
+
+	if (gpio->fd != CNXK_GPIO_INVALID_FD)
+		close(gpio->fd);
 
 	num = cnxk_queue_to_gpio(gpiochip, queue_id);
 	gpiochip->gpios[num] = NULL;
@@ -462,134 +501,218 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev)
 
 static const struct {
 	enum cnxk_gpio_pin_edge edge;
-	const char *name;
-} cnxk_gpio_edge_name[] = {
-	{ CNXK_GPIO_PIN_EDGE_NONE, "none" },
-	{ CNXK_GPIO_PIN_EDGE_FALLING, "falling" },
-	{ CNXK_GPIO_PIN_EDGE_RISING, "rising" },
-	{ CNXK_GPIO_PIN_EDGE_BOTH, "both" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_edge_flag[] = {
+	{ CNXK_GPIO_PIN_EDGE_NONE, 0 },
+	{ CNXK_GPIO_PIN_EDGE_FALLING, GPIO_V2_LINE_FLAG_EDGE_FALLING },
+	{ CNXK_GPIO_PIN_EDGE_RISING, GPIO_V2_LINE_FLAG_EDGE_RISING },
+	{ CNXK_GPIO_PIN_EDGE_BOTH, GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING },
 };
 
-static const char *
-cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge)
+static enum gpio_v2_line_flag
+cnxk_gpio_edge_to_flag(enum cnxk_gpio_pin_edge edge)
 {
 	unsigned int i;
 
-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (cnxk_gpio_edge_name[i].edge == edge)
-			return cnxk_gpio_edge_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if (cnxk_gpio_edge_flag[i].edge == edge)
+			break;
 	}
 
-	return NULL;
+	return cnxk_gpio_edge_flag[i].flag;
 }
 
 static enum cnxk_gpio_pin_edge
-cnxk_gpio_name_to_edge(const char *name)
+cnxk_gpio_flag_to_edge(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;
 
-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (!strcmp(cnxk_gpio_edge_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if ((cnxk_gpio_edge_flag[i].flag & flag) == cnxk_gpio_edge_flag[i].flag)
 			break;
 	}
 
-	return cnxk_gpio_edge_name[i].edge;
+	return cnxk_gpio_edge_flag[i].edge;
 }
 
 static const struct {
 	enum cnxk_gpio_pin_dir dir;
-	const char *name;
-} cnxk_gpio_dir_name[] = {
-	{ CNXK_GPIO_PIN_DIR_IN, "in" },
-	{ CNXK_GPIO_PIN_DIR_OUT, "out" },
-	{ CNXK_GPIO_PIN_DIR_HIGH, "high" },
-	{ CNXK_GPIO_PIN_DIR_LOW, "low" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_dir_flag[] = {
+	{ CNXK_GPIO_PIN_DIR_IN, GPIO_V2_LINE_FLAG_INPUT },
+	{ CNXK_GPIO_PIN_DIR_OUT, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_HIGH, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_LOW, GPIO_V2_LINE_FLAG_OUTPUT },
 };
 
-static const char *
-cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir)
+static enum gpio_v2_line_flag
+cnxk_gpio_dir_to_flag(enum cnxk_gpio_pin_dir dir)
 {
 	unsigned int i;
 
-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (cnxk_gpio_dir_name[i].dir == dir)
-			return cnxk_gpio_dir_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if (cnxk_gpio_dir_flag[i].dir == dir)
+			break;
 	}
 
-	return NULL;
+	return cnxk_gpio_dir_flag[i].flag;
 }
 
 static enum cnxk_gpio_pin_dir
-cnxk_gpio_name_to_dir(const char *name)
+cnxk_gpio_flag_to_dir(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;
 
-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (!strcmp(cnxk_gpio_dir_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if ((cnxk_gpio_dir_flag[i].flag & flag) == cnxk_gpio_dir_flag[i].flag)
 			break;
 	}
 
-	return cnxk_gpio_dir_name[i].dir;
+	return cnxk_gpio_dir_flag[i].dir;
 }
 
 static int
-cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
+cnxk_gpio_register_irq_compat(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq,
+			      struct cnxk_gpio_irq2 *irq2)
 {
+	struct rte_intr_handle *intr_handle;
 	int ret;
 
-	ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu);
-	if (ret)
-		return ret;
+	if (!irq && !irq2)
+		return -EINVAL;
+
+	if ((irq && !irq->handler) || (irq2 && !irq2->handler))
+		return -EINVAL;
+
+	if (gpio->intr.intr_handle)
+		return -EEXIST;
+
+	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+	if (!intr_handle)
+		return -ENOMEM;
+
+	if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_VDEV)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_fd_set(intr_handle, gpio->fd)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_callback_register(intr_handle, cnxk_gpio_intr_handler, gpio)) {
+		ret = -rte_errno;
+		goto out;
+	}
 
-	gpio->handler = irq->handler;
-	gpio->data = irq->data;
-	gpio->cpu = irq->cpu;
+	gpio->intr.intr_handle = intr_handle;
+
+	if (irq) {
+		gpio->intr.data = irq->data;
+		gpio->intr.handler = irq->handler;
+	} else {
+		gpio->intr.data = irq2->data;
+		gpio->intr.handler2 = irq2->handler;
+	}
+
+	if (rte_intr_enable(gpio->intr.intr_handle)) {
+		ret = -EINVAL;
+		goto out;
+	}
 
 	return 0;
+out:
+	rte_intr_instance_free(intr_handle);
+
+	return ret;
 }
 
 static int
-cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
+{
+	CNXK_GPIO_LOG(WARNING, "using deprecated interrupt registration api");
+
+	return cnxk_gpio_register_irq_compat(gpio, irq, NULL);
+}
+
+static int
+cnxk_gpio_register_irq2(struct cnxk_gpio *gpio, struct cnxk_gpio_irq2 *irq)
 {
-	return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base);
+	return cnxk_gpio_register_irq_compat(gpio, NULL, irq);
 }
 
 static int
 cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 {
 	struct cnxk_gpio_msg *msg = rbuf->buf_addr;
+	struct gpio_v2_line_values values = {0};
+	struct gpio_v2_line_config config = {0};
+	struct gpio_v2_line_info info = {0};
 	enum cnxk_gpio_pin_edge edge;
 	enum cnxk_gpio_pin_dir dir;
-	char buf[CNXK_GPIO_BUFSZ];
 	void *rsp = NULL;
-	int ret, val, n;
+	int ret;
+
+	info.offset = gpio->num;
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return ret;
 
-	n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH,
-		     gpio->num);
+	info.flags &= ~GPIO_V2_LINE_FLAG_USED;
 
 	switch (msg->type) {
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data);
+		values.bits = *(int *)msg->data ?  RTE_BIT64(gpio->num) : 0;
+		values.mask = RTE_BIT64(gpio->num);
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
 		edge = *(enum cnxk_gpio_pin_edge *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge));
+		info.flags &= ~(GPIO_V2_LINE_FLAG_EDGE_RISING | GPIO_V2_LINE_FLAG_EDGE_FALLING);
+		info.flags |= cnxk_gpio_edge_to_flag(edge);
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
 		dir = *(enum cnxk_gpio_pin_dir *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir));
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = cnxk_gpio_dir_to_flag(dir);
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		if (dir == CNXK_GPIO_PIN_DIR_HIGH || dir == CNXK_GPIO_PIN_DIR_LOW) {
+			config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
+			config.attrs[config.num_attrs].attr.values = dir == CNXK_GPIO_PIN_DIR_HIGH ?
+								     RTE_BIT64(gpio->num) : 0;
+			config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+			config.num_attrs++;
+		}
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		val = *(int *)msg->data;
-		ret = cnxk_gpio_write_attr_int(buf, val);
+		if (*(int *)msg->data)
+			info.flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+		else
+			info.flags &= ~GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
+		values.mask = RTE_BIT64(gpio->num);
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
 		if (ret)
 			break;
 
@@ -597,47 +720,35 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 		if (!rsp)
 			return -ENOMEM;
 
-		*(int *)rsp = val;
+		*(int *)rsp = !!(values.bits & RTE_BIT64(gpio->num));
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;
 
-		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf);
+		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_flag_to_edge(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
-		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0);
+		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;
 
-		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf);
+		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_flag_to_dir(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(int), 0);
 		if (!rsp)
 			return -ENOMEM;
 
-		*(int *)rsp = val;
+		*(int *)rsp = !!(info.flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
 		break;
 	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ:
 		ret = cnxk_gpio_register_irq(gpio, (struct cnxk_gpio_irq *)msg->data);
 		break;
+	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2:
+		ret = cnxk_gpio_register_irq2(gpio, (struct cnxk_gpio_irq2 *)msg->data);
+		break;
 	case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ:
 		ret = cnxk_gpio_unregister_irq(gpio);
 		break;
@@ -731,11 +842,11 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = {
 static int
 cnxk_gpio_probe(struct rte_vdev_device *dev)
 {
+	struct gpiochip_info gpiochip_info;
 	char name[RTE_RAWDEV_NAME_MAX_LEN];
 	struct cnxk_gpio_params *params;
 	struct cnxk_gpiochip *gpiochip;
 	struct rte_rawdev *rawdev;
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;
 
 	cnxk_gpio_format_name(name, sizeof(name));
@@ -762,25 +873,14 @@ cnxk_gpio_probe(struct rte_vdev_device *dev)
 
 	gpiochip->num = params->num;
 
-	ret = cnxk_gpio_irq_init(gpiochip);
-	if (ret)
-		goto out;
-
-	/* read gpio base */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base);
-	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
-		goto out;
-	}
-
 	/* read number of available gpios */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios);
+	ret = cnxk_gpio_gpiochip_ioctl(gpiochip, GPIO_GET_CHIPINFO_IOCTL, &gpiochip_info);
 	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
+		CNXK_GPIO_LOG(ERR, "failed to read /dev/gpiochip%d info", gpiochip->num);
 		goto out;
 	}
+
+	gpiochip->num_gpios = gpiochip_info.lines;
 	gpiochip->num_queues = gpiochip->num_gpios;
 
 	ret = cnxk_gpio_parse_allowlist(gpiochip, params->allowlist);
@@ -827,15 +927,11 @@ cnxk_gpio_remove(struct rte_vdev_device *dev)
 		if (!gpio)
 			continue;
 
-		if (gpio->handler)
-			cnxk_gpio_unregister_irq(gpio);
-
 		cnxk_gpio_queue_release(rawdev, gpio->num);
 	}
 
 	rte_free(gpiochip->allowlist);
 	rte_free(gpiochip->gpios);
-	cnxk_gpio_irq_fini();
 	cnxk_gpio_params_release();
 	rte_rawdev_pmd_release(rawdev);
 
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h
index 94c8e36977..adc7f90936 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h
@@ -11,20 +11,24 @@ extern int cnxk_logtype_gpio;
 #define CNXK_GPIO_LOG(level, ...) \
 	RTE_LOG_LINE(level, CNXK_GPIO, __VA_ARGS__)
 
+struct gpio_v2_line_event;
 struct cnxk_gpiochip;
 
 struct cnxk_gpio {
 	struct cnxk_gpiochip *gpiochip;
+	struct {
+		struct rte_intr_handle *intr_handle;
+		void (*handler)(int gpio, void *data);
+		void (*handler2)(struct gpio_v2_line_event *event, void *data);
+		void *data;
+	} intr;
 	void *rsp;
 	int num;
-	void (*handler)(int gpio, void *data);
-	void *data;
-	int cpu;
+	int fd;
 };
 
 struct cnxk_gpiochip {
 	int num;
-	int base;
 	int num_gpios;
 	int num_queues;
 	struct cnxk_gpio **gpios;
@@ -33,9 +37,4 @@ struct cnxk_gpiochip {
 
 int cnxk_gpio_selftest(uint16_t dev_id);
 
-int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip);
-void cnxk_gpio_irq_fini(void);
-int cnxk_gpio_irq_request(int gpio, int cpu);
-int cnxk_gpio_irq_free(int gpio);
-
 #endif /* _CNXK_GPIO_H_ */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
deleted file mode 100644
index 2fa8e69899..0000000000
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(C) 2021 Marvell.
- */
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/queue.h>
-#include <unistd.h>
-
-#include <rte_rawdev_pmd.h>
-
-#include <roc_api.h>
-
-#include "cnxk_gpio.h"
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_SET_GPIO_HANDLER                                               \
-	_IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data)
-#define OTX_IOC_CLR_GPIO_HANDLER                                               \
-	_IO(OTX_IOC_MAGIC, 2)
-
-struct otx_gpio_usr_data {
-	uint64_t isr_base;
-	uint64_t sp;
-	uint64_t cpu;
-	uint64_t gpio_num;
-};
-
-struct cnxk_gpio_irq_stack {
-	LIST_ENTRY(cnxk_gpio_irq_stack) next;
-	void *sp_buffer;
-	int cpu;
-	int inuse;
-};
-
-struct cnxk_gpio_irqchip {
-	int fd;
-	/* serialize access to this struct */
-	pthread_mutex_t lock;
-	LIST_HEAD(, cnxk_gpio_irq_stack) stacks;
-
-	struct cnxk_gpiochip *gpiochip;
-};
-
-static struct cnxk_gpio_irqchip *irqchip;
-
-static void
-cnxk_gpio_irq_stack_free(int cpu)
-{
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (!stack)
-		return;
-
-	if (stack->inuse)
-		stack->inuse--;
-
-	if (stack->inuse == 0) {
-		LIST_REMOVE(stack, next);
-		rte_free(stack->sp_buffer);
-		rte_free(stack);
-	}
-}
-
-static void *
-cnxk_gpio_irq_stack_alloc(int cpu)
-{
-#define ARM_STACK_ALIGNMENT (2 * sizeof(void *))
-#define IRQ_STACK_SIZE 0x200000
-
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (stack) {
-		stack->inuse++;
-		return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-	}
-
-	stack = rte_malloc(NULL, sizeof(*stack), 0);
-	if (!stack)
-		return NULL;
-
-	stack->sp_buffer =
-		rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT);
-	if (!stack->sp_buffer) {
-		rte_free(stack);
-		return NULL;
-	}
-
-	stack->cpu = cpu;
-	stack->inuse = 1;
-	LIST_INSERT_HEAD(&irqchip->stacks, stack, next);
-
-	return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-}
-
-static void
-cnxk_gpio_irq_handler(int gpio_num)
-{
-	struct cnxk_gpiochip *gpiochip = irqchip->gpiochip;
-	struct cnxk_gpio *gpio;
-
-	if (gpio_num >= gpiochip->num_gpios)
-		goto out;
-
-	gpio = gpiochip->gpios[gpio_num];
-	if (likely(gpio->handler))
-		gpio->handler(gpio_num, gpio->data);
-
-out:
-	roc_atf_ret();
-}
-
-int
-cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip)
-{
-	if (irqchip)
-		return 0;
-
-	irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0);
-	if (!irqchip)
-		return -ENOMEM;
-
-	irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (irqchip->fd < 0) {
-		rte_free(irqchip);
-		return -errno;
-	}
-
-	pthread_mutex_init(&irqchip->lock, NULL);
-	LIST_INIT(&irqchip->stacks);
-	irqchip->gpiochip = gpiochip;
-
-	return 0;
-}
-
-void
-cnxk_gpio_irq_fini(void)
-{
-	if (!irqchip)
-		return;
-
-	close(irqchip->fd);
-	rte_free(irqchip);
-	irqchip = NULL;
-}
-
-int
-cnxk_gpio_irq_request(int gpio, int cpu)
-{
-	struct otx_gpio_usr_data data;
-	void *sp;
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	sp = cnxk_gpio_irq_stack_alloc(cpu);
-	if (!sp) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
-
-	data.isr_base = (uint64_t)cnxk_gpio_irq_handler;
-	data.sp = (uint64_t)sp;
-	data.cpu = (uint64_t)cpu;
-	data.gpio_num = (uint64_t)gpio;
-
-	mlockall(MCL_CURRENT | MCL_FUTURE);
-	ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data);
-	if (ret) {
-		ret = -errno;
-		goto out_free_stack;
-	}
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-
-out_free_stack:
-	cnxk_gpio_irq_stack_free(cpu);
-out_unlock:
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return ret;
-}
-
-int
-cnxk_gpio_irq_free(int gpio)
-{
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio);
-	if (ret) {
-		pthread_mutex_unlock(&irqchip->lock);
-		return -errno;
-	}
-
-	cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu);
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-}
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
index a0d9942f20..4e04811fe5 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
@@ -8,97 +8,27 @@
 #include <unistd.h>
 
 #include <rte_cycles.h>
+#include <rte_io.h>
 #include <rte_rawdev.h>
 #include <rte_rawdev_pmd.h>
-#include <rte_service.h>
 
 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"
 
-#define CNXK_GPIO_BUFSZ 128
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_TRIGGER_GPIO_HANDLER                                           \
-	_IO(OTX_IOC_MAGIC, 3)
-
-static int fd;
-
-static int
-cnxk_gpio_attr_exists(const char *attr)
-{
-	struct stat st;
-
-	return !stat(attr, &st);
-}
-
-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                  \
-	if (err) {                                                             \
-		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, \
-			##__VA_ARGS__, err);                                   \
-		goto out;                                                      \
-	}                                                                      \
+#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                                      \
+	if (err) {                                                                                 \
+		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, ##__VA_ARGS__, err); \
+		goto out;                                                                          \
+	}                                                                                          \
 } while (0)
 
 static int
-cnxk_gpio_validate_attr(char *attr, const char *expected)
+cnxk_gpio_test_input(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;
 
-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	if (strncmp(buf, expected, sizeof(buf)))
-		return -EIO;
-
-	return 0;
-}
-
-#define CNXK_GPIO_PATH_FMT "/sys/class/gpio/gpio%d"
-
-static int
-cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, n;
-
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
-
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
-	ret = cnxk_gpio_validate_attr(buf, "in");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1) |
 	      rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
@@ -107,29 +37,17 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 		CNXK_GPIO_ERR_STR(ret, "input pin overwritten");
 	}
 
-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "falling");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "rising");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "both");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	/*
 	 * calling this makes sure kernel driver switches off inverted
@@ -141,44 +59,36 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 	return ret;
 }
 
-static int
-cnxk_gpio_trigger_irq(int gpio)
-{
-	int ret;
-
-	ret = ioctl(fd, OTX_IOC_TRIGGER_GPIO_HANDLER, gpio);
-
-	return ret == -1 ? -errno : 0;
-}
+static uint32_t triggered;
 
 static void
-cnxk_gpio_irq_handler(int gpio, void *data)
+cnxk_gpio_irq_handler(struct gpio_v2_line_event *event, void *data)
 {
-	*(int *)data = gpio;
+	int gpio = (int)(size_t)data;
+
+	if ((int)event->offset != gpio)
+		CNXK_GPIO_LOG(ERR, "event from gpio%d instead of gpio%d", event->offset, gpio);
+
+	rte_write32(1, &triggered);
 }
 
 static int
 cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 {
-	int irq_data, ret;
+	int ret;
 
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
 
-	irq_data = 0;
-	ret = rte_pmd_gpio_register_irq(dev_id, gpio, rte_lcore_id(),
-					cnxk_gpio_irq_handler, &irq_data);
+	ret = rte_pmd_gpio_register_irq2(dev_id, gpio, cnxk_gpio_irq_handler, (int *)(size_t)gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to register irq handler");
 
-	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio,
-					    CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to enable interrupt");
 
-	ret = cnxk_gpio_trigger_irq(gpio);
-	CNXK_GPIO_ERR_STR(ret, "failed to trigger irq");
-	rte_delay_ms(1);
-	ret = *(volatile int *)&irq_data == gpio ? 0 : -EIO;
-	CNXK_GPIO_ERR_STR(ret, "failed to test irq");
+	rte_delay_ms(2);
+	rte_read32(&triggered);
+	CNXK_GPIO_ERR_STR(!triggered, "failed to trigger irq");
 
 	ret = rte_pmd_gpio_disable_interrupt(dev_id, gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to disable interrupt");
@@ -193,24 +103,15 @@ cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 }
 
 static int
-cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
+cnxk_gpio_test_output(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, val, n;
+	int ret, val;
 
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
-
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_OUT);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to out");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val)
@@ -219,64 +120,41 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
 
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val != 1)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
 
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_LOW);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to low");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_HIGH);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to high");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
+
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	/* this one should succeed */
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 
 	ret = rte_pmd_gpio_get_pin_active_low(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read active_low");
@@ -284,23 +162,24 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
 
-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
 
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 0)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 0", val);
 
-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
 out:
 	return ret;
 }
@@ -309,17 +188,13 @@ int
 cnxk_gpio_selftest(uint16_t dev_id)
 {
 	struct cnxk_gpio_queue_conf conf;
-	struct cnxk_gpiochip *gpiochip;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct rte_rawdev *rawdev;
 	unsigned int queues, i;
-	struct cnxk_gpio *gpio;
 	int ret, ret2;
 
 	rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio");
 	if (!rawdev)
 		return -ENODEV;
-	gpiochip = rawdev->dev_private;
 
 	queues = rte_rawdev_queue_count(dev_id);
 	if (queues == 0)
@@ -329,10 +204,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	if (ret)
 		return ret;
 
-	fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (fd < 0)
-		return -errno;
-
 	for (i = 0; i < queues; i++) {
 		ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf));
 		if (ret) {
@@ -355,15 +226,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			goto out;
 		}
 
-		gpio = gpiochip->gpios[conf.gpio];
-		snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, gpio->num);
-		if (!cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s does not exist", buf);
-			ret = -ENOENT;
-			goto release;
-		}
-
-		ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_input(dev_id, conf.gpio);
 		if (ret)
 			goto release;
 
@@ -371,7 +234,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 		if (ret)
 			goto release;
 
-		ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_output(dev_id, conf.gpio);
 release:
 		ret2 = ret;
 		ret = rte_rawdev_queue_release(dev_id, i);
@@ -381,12 +244,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			break;
 		}
 
-		if (cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s still exists", buf);
-			ret = -EIO;
-			break;
-		}
-
 		if (ret2) {
 			ret = ret2;
 			break;
@@ -394,7 +251,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	}
 
 out:
-	close(fd);
 	rte_rawdev_stop(dev_id);
 
 	return ret;
diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build
index 9d9a527392..372f3d9f46 100644
--- a/drivers/raw/cnxk_gpio/meson.build
+++ b/drivers/raw/cnxk_gpio/meson.build
@@ -5,7 +5,6 @@
 deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs']
 sources = files(
         'cnxk_gpio.c',
-        'cnxk_gpio_irq.c',
         'cnxk_gpio_selftest.c',
 )
 headers = files('rte_pmd_cnxk_gpio.h')
diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
index 80a37be9c7..d0b165639c 100644
--- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
@@ -5,6 +5,8 @@
 #ifndef _RTE_PMD_CNXK_GPIO_H_
 #define _RTE_PMD_CNXK_GPIO_H_
 
+#include <linux/gpio.h>
+
 #include <rte_malloc.h>
 #include <rte_memcpy.h>
 #include <rte_rawdev.h>
@@ -48,8 +50,10 @@ enum cnxk_gpio_msg_type {
 	CNXK_GPIO_MSG_TYPE_GET_PIN_DIR,
 	/** Type used to read inverted logic state */
 	CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW,
-	/** Type used to register interrupt handler */
+	/** Type used to register interrupt handler (deprecated) */
 	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ,
+	/** Type used to register interrupt handler */
+	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
 	/** Type used to remove interrupt handler */
 	CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ,
 };
@@ -79,7 +83,7 @@ enum cnxk_gpio_pin_dir {
 };
 
 /**
- * GPIO interrupt handler
+ * GPIO interrupt handler (deprecated)
  *
  * @param gpio
  *   Zero-based GPIO number
@@ -97,6 +101,23 @@ struct cnxk_gpio_irq {
 	int cpu;
 };
 
+/**
+ * GPIO interrupt handler
+ *
+ * @param event
+ *   Pointer to gpio event data
+ * @param data
+ *   Cookie passed to interrupt handler
+ */
+typedef void (*cnxk_gpio_irq_handler2_t)(struct gpio_v2_line_event *event, void *data);
+
+struct cnxk_gpio_irq2 {
+	/** Interrupt handler */
+	cnxk_gpio_irq_handler2_t handler;
+	/** User data passed to irq handler */
+	void *data;
+};
+
 struct cnxk_gpio_msg {
 	/** Message type */
 	enum cnxk_gpio_msg_type type;
@@ -338,7 +359,7 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val)
 }
 
 /**
- * Attach interrupt handler to GPIO
+ * Attach interrupt handler to GPIO (deprecated)
  *
  * @param dev_id
  *   The identifier of the device
@@ -371,6 +392,36 @@ rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu,
 	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
 }
 
+/**
+ * Attach interrupt handler to GPIO
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @param gpio
+ *   Zero-based GPIO number
+ * @param handler
+ *   Interrupt handler to be executed
+ * @param data
+ *   Data to be passed to interrupt handler
+ *
+ * @return
+ *   Returns 0 on success, negative error code otherwise
+ */
+static __rte_always_inline int
+rte_pmd_gpio_register_irq2(uint16_t dev_id, int gpio, cnxk_gpio_irq_handler2_t handler, void *data)
+{
+	struct cnxk_gpio_irq2 irq = {
+		.handler = handler,
+		.data = data,
+	};
+	struct cnxk_gpio_msg msg = {
+		.type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
+		.data = &irq,
+	};
+
+	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
+}
+
 /**
  * Detach interrupt handler from GPIO
  *
-- 
2.34.1


^ permalink raw reply	[relevance 1%]

* Re: [RFC v3 5/8] build: generate symbol maps
  2025-03-11  9:56 18%   ` [RFC v3 5/8] build: generate symbol maps David Marchand
  2025-03-13 17:26  0%     ` Bruce Richardson
@ 2025-03-14 15:27  0%     ` Andre Muezerie
  2025-03-14 15:51  4%       ` David Marchand
  1 sibling, 1 reply; 153+ results
From: Andre Muezerie @ 2025-03-14 15:27 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas, bruce.richardson

On Tue, Mar 11, 2025 at 10:56:03AM +0100, David Marchand wrote:
> Rather than maintain a file in parallel of the code, symbols to be
> exported can be marked with a token RTE_EXPORT_*SYMBOL.
> 
> >From those marks, the build framework generates map files only for
> symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
> becomes unnecessary).
> 
> The build framework directly creates a map file in the format that the
> linker expects (rather than converting from GNU linker to MSVC linker).
> 
> Empty maps are allowed again as a replacement for drivers/version.map.
> 
> The symbol check is updated to only support the new format.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---
> Changes since RFC v2:
> - because of MSVC limitations wrt macro passed via cmdline,
>   used an internal header for defining RTE_EXPORT_* macros,
> - updated documentation and tooling,
> 
> ---
>  MAINTAINERS                                |   2 +
>  buildtools/gen-version-map.py              | 111 ++++++++++
>  buildtools/map-list-symbol.sh              |  10 +-
>  buildtools/meson.build                     |   1 +
>  config/meson.build                         |   2 +
>  config/rte_export.h                        |  16 ++
>  devtools/check-symbol-change.py            |  90 +++++++++
>  devtools/check-symbol-maps.sh              |  14 --
>  devtools/checkpatches.sh                   |   2 +-
>  doc/guides/contributing/abi_versioning.rst | 224 ++-------------------
>  drivers/meson.build                        |  94 +++++----
>  drivers/version.map                        |   3 -
>  lib/meson.build                            |  91 ++++++---
>  13 files changed, 371 insertions(+), 289 deletions(-)
>  create mode 100755 buildtools/gen-version-map.py
>  create mode 100644 config/rte_export.h
>  create mode 100755 devtools/check-symbol-change.py
>  delete mode 100644 drivers/version.map
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 312e6fcee5..04772951d3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
>  F: devtools/check-forbidden-tokens.awk
>  F: devtools/check-git-log.sh
>  F: devtools/check-spdx-tag.sh
> +F: devtools/check-symbol-change.py
>  F: devtools/check-symbol-change.sh
>  F: devtools/check-symbol-maps.sh
>  F: devtools/checkpatches.sh
> @@ -127,6 +128,7 @@ F: config/
>  F: buildtools/check-symbols.sh
>  F: buildtools/chkincs/
>  F: buildtools/call-sphinx-build.py
> +F: buildtools/gen-version-map.py
>  F: buildtools/get-cpu-count.py
>  F: buildtools/get-numa-count.py
>  F: buildtools/list-dir-globs.py
> diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
> new file mode 100755
> index 0000000000..b160aa828b
> --- /dev/null
> +++ b/buildtools/gen-version-map.py
> @@ -0,0 +1,111 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (c) 2024 Red Hat, Inc.

2025?
I appreciate that Python was chosen instead of sh/bash.

> +
> +"""Generate a version map file used by GNU or MSVC linker."""
> +
> +import re
> +import sys
> +
> +# From rte_export.h
> +export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
> +export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> +export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
> +# From rte_function_versioning.h
> +ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> +default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +
> +with open(sys.argv[2]) as f:
> +    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
> +
> +symbols = {}
> +
> +for file in sys.argv[4:]:
> +    with open(file, encoding="utf-8") as f:
> +        for ln in f.readlines():
> +            node = None
> +            symbol = None
> +            comment = None
> +            if export_exp_sym_regexp.match(ln):
> +                node = 'EXPERIMENTAL'
> +                symbol = export_exp_sym_regexp.match(ln).group(1)
> +                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
> +            elif export_int_sym_regexp.match(ln):
> +                node = 'INTERNAL'
> +                symbol = export_int_sym_regexp.match(ln).group(1)
> +            elif export_sym_regexp.match(ln):
> +                node = abi
> +                symbol = export_sym_regexp.match(ln).group(1)
> +            elif ver_sym_regexp.match(ln):
> +                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
> +                symbol = ver_sym_regexp.match(ln).group(2)
> +            elif ver_exp_sym_regexp.match(ln):
> +                node = 'EXPERIMENTAL'
> +                symbol = ver_exp_sym_regexp.match(ln).group(1)
> +            elif default_sym_regexp.match(ln):
> +                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
> +                symbol = default_sym_regexp.match(ln).group(2)
> +
> +            if not symbol:
> +                continue
> +
> +            if node not in symbols:
> +                symbols[node] = {}
> +            symbols[node][symbol] = comment
> +
> +if sys.argv[1] == 'msvc':
> +    with open(sys.argv[3], "w") as outfile:
> +        outfile.writelines(f"EXPORTS\n")
> +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> +            if key not in symbols:
> +                continue
> +            for symbol in sorted(symbols[key].keys()):
> +                outfile.writelines(f"\t{symbol}\n")
> +            del symbols[key]
> +else:
> +    with open(sys.argv[3], "w") as outfile:

Consider having output file samples documented, perhaps in this script itself, to make
it easier to understand what this script it doing and highlight the differences between
the formats supported (msvc, etc).

> +        local_token = False
> +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> +            if key not in symbols:
> +                continue
> +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> +            for symbol in sorted(symbols[key].keys()):
> +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> +                    prefix = '__emutls_v.'
> +                else:
> +                    prefix = ''
> +                outfile.writelines(f"\t{prefix}{symbol};")
> +                comment = symbols[key][symbol]
> +                if comment:
> +                    outfile.writelines(f"{comment}")
> +                outfile.writelines("\n")
> +            outfile.writelines("\n")
> +            if not local_token:
> +                outfile.writelines("\tlocal: *;\n")
> +                local_token = True
> +            outfile.writelines("};\n")
> +            del symbols[key]
> +        for key in sorted(symbols.keys()):
> +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> +            for symbol in sorted(symbols[key].keys()):
> +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> +                    prefix = '__emutls_v.'
> +                else:
> +                    prefix = ''
> +                outfile.writelines(f"\t{prefix}{symbol};")
> +                comment = symbols[key][symbol]
> +                if comment:
> +                    outfile.writelines(f"{comment}")
> +                outfile.writelines("\n")
> +            outfile.writelines(f"}} {abi};\n")
> +            if not local_token:
> +                outfile.writelines("\tlocal: *;\n")
> +                local_token = True
> +            del symbols[key]
> +        # No exported symbol, add a catch all
> +        if not local_token:
> +            outfile.writelines(f"{abi} {{\n")
> +            outfile.writelines("\tlocal: *;\n")
> +            local_token = True
> +            outfile.writelines("};\n")
> diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
> index eb98451d8e..0829df4be5 100755
> --- a/buildtools/map-list-symbol.sh
> +++ b/buildtools/map-list-symbol.sh
> @@ -62,10 +62,14 @@ for file in $@; do
>  		if (current_section == "") {
>  			next;
>  		}
> +		symbol_version = current_version
> +		if (/^[^}].*[^:*]; # added in /) {
> +			symbol_version = $5
> +		}
>  		if ("'$version'" != "") {
> -			if ("'$version'" == "unset" && current_version != "") {
> +			if ("'$version'" == "unset" && symbol_version != "") {
>  				next;
> -			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
> +			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
>  				next;
>  			}
>  		}
> @@ -73,7 +77,7 @@ for file in $@; do
>  		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
>  			ret = 0;
>  			if ("'$quiet'" == "") {
> -				print "'$file' "current_section" "$1" "current_version;
> +				print "'$file' "current_section" "$1" "symbol_version;
>  			}
>  			if ("'$symbol'" != "all") {
>  				exit 0;
> diff --git a/buildtools/meson.build b/buildtools/meson.build
> index 4e2c1217a2..b745e9afa4 100644
> --- a/buildtools/meson.build
> +++ b/buildtools/meson.build
> @@ -16,6 +16,7 @@ else
>      py3 = ['meson', 'runpython']
>  endif
>  echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
> +gen_version_map = py3 + files('gen-version-map.py')
>  list_dir_globs = py3 + files('list-dir-globs.py')
>  map_to_win_cmd = py3 + files('map_to_win.py')
>  sphinx_wrapper = py3 + files('call-sphinx-build.py')
> diff --git a/config/meson.build b/config/meson.build
> index f31fef216c..54657055fb 100644
> --- a/config/meson.build
> +++ b/config/meson.build
> @@ -303,8 +303,10 @@ endif
>  # add -include rte_config to cflags
>  if is_ms_compiler
>      add_project_arguments('/FI', 'rte_config.h', language: 'c')
> +    add_project_arguments('/FI', 'rte_export.h', language: 'c')
>  else
>      add_project_arguments('-include', 'rte_config.h', language: 'c')
> +    add_project_arguments('-include', 'rte_export.h', language: 'c')
>  endif
>  
>  # enable extra warnings and disable any unwanted warnings
> diff --git a/config/rte_export.h b/config/rte_export.h
> new file mode 100644
> index 0000000000..83d871fe11
> --- /dev/null
> +++ b/config/rte_export.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright (c) 2025 Red Hat, Inc.
> + */
> +
> +#ifndef RTE_EXPORT_H
> +#define RTE_EXPORT_H
> +
> +/* *Internal* macros for exporting symbols, used by the build system.
> + * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
> + * version this symbol was introduced in.
> + */
> +#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
> +#define RTE_EXPORT_INTERNAL_SYMBOL(a)
> +#define RTE_EXPORT_SYMBOL(a)
> +
> +#endif /* RTE_EXPORT_H */
> diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
> new file mode 100755
> index 0000000000..09709e4f06
> --- /dev/null
> +++ b/devtools/check-symbol-change.py
> @@ -0,0 +1,90 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (c) 2025 Red Hat, Inc.
> +
> +"""Check exported symbols change in a patch."""
> +
> +import re
> +import sys
> +
> +file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
> +# From rte_export.h
> +export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
> +export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> +export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
> +# TODO, handle versioned symbols from rte_function_versioning.h
> +# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> +# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> +
> +symbols = {}
> +
> +for file in sys.argv[1:]:
> +    with open(file, encoding="utf-8") as f:
> +        for ln in f.readlines():
> +            if file_header_regexp.match(ln):
> +                if file_header_regexp.match(ln).group(2) == "lib":
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> +                elif file_header_regexp.match(ln).group(3) == "intel":
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
> +                else:
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> +
> +                if lib not in symbols:
> +                    symbols[lib] = {}
> +                continue
> +
> +            if export_exp_sym_regexp.match(ln):
> +                symbol = export_exp_sym_regexp.match(ln).group(1)
> +                node = 'EXPERIMENTAL'
> +            elif export_int_sym_regexp.match(ln):
> +                node = 'INTERNAL'
> +                symbol = export_int_sym_regexp.match(ln).group(1)
> +            elif export_sym_regexp.match(ln):
> +                symbol = export_sym_regexp.match(ln).group(1)
> +                node = 'stable'
> +            else:
> +                continue
> +
> +            if symbol not in symbols[lib]:
> +                symbols[lib][symbol] = {}
> +            added = ln[0] == '+'
> +            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
> +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> +            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
> +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> +            if added:
> +                symbols[lib][symbol]['added'] = node
> +            else:
> +                symbols[lib][symbol]['removed'] = node
> +
> +    for lib in sorted(symbols.keys()):
> +        error = False
> +        for symbol in sorted(symbols[lib].keys()):
> +            if 'removed' not in symbols[lib][symbol]:
> +                # Symbol addition
> +                node = symbols[lib][symbol]['added']
> +                if node == 'stable':
> +                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
> +                    error = True
> +                else:
> +                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
> +                continue
> +
> +            if 'added' not in symbols[lib][symbol]:
> +                # Symbol removal
> +                node = symbols[lib][symbol]['added']
> +                if node == 'stable':
> +                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")

Some people would argue that WARN instead of INFO is more appropriate because some attention
is needed from the user. INFO many times is just ignored.

> +                    print(f"Please check it has gone though the deprecation process.")
> +                continue
> +
> +            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
> +                # Symbol was moved around
> +                continue
> +
> +            # Symbol modifications
> +            added = symbols[lib][symbol]['added']
> +            removed = symbols[lib][symbol]['removed']
> +            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")

Perhaps use WARN instead of INFO.

> +            print(f"Please check it has gone though the deprecation process.")
> diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
> index 6121f78ec6..fcd3931e5d 100755
> --- a/devtools/check-symbol-maps.sh
> +++ b/devtools/check-symbol-maps.sh
> @@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
>      ret=1
>  fi
>  
> -find_empty_maps ()
> -{
> -    for map in $@ ; do
> -        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
> -    done
> -}
> -
> -empty_maps=$(find_empty_maps $@)
> -if [ -n "$empty_maps" ] ; then
> -    echo "Found empty maps:"
> -    echo "$empty_maps"
> -    ret=1
> -fi
> -
>  find_bad_format_maps ()
>  {
>      abi_version=$(cut -d'.' -f 1 ABI_VERSION)
> diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
> index 003bb49e04..7dcac7c8c9 100755
> --- a/devtools/checkpatches.sh
> +++ b/devtools/checkpatches.sh
> @@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
>  PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
>  SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
>  LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
> -NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
> +NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
>  options="$options $DPDK_CHECKPATCH_OPTIONS"
>  
>  print_usage () {
> diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
> index 88dd776b4c..addbb24b9e 100644
> --- a/doc/guides/contributing/abi_versioning.rst
> +++ b/doc/guides/contributing/abi_versioning.rst
> @@ -58,12 +58,12 @@ persists over multiple releases.
>  
>  .. code-block:: none
>  
> - $ head ./lib/acl/version.map
> + $ head ./build/lib/librte_acl_exports.map

I like the new file names, they are much better.

>   DPDK_21 {
>          global:
>   ...
>  
> - $ head ./lib/eal/version.map
> + $ head ./build/lib/librte_eal_exports.map
>   DPDK_21 {
>          global:
>   ...
> @@ -77,7 +77,7 @@ that library.
>  
>  .. code-block:: none
>  
> - $ head ./lib/acl/version.map
> + $ head ./build/lib/librte_acl_exports.map
>   DPDK_21 {
>          global:
>   ...
> @@ -88,7 +88,7 @@ that library.
>   } DPDK_21;
>   ...
>  
> - $ head ./lib/eal/version.map
> + $ head ./build/lib/librte_eal_exports.map
>   DPDK_21 {
>          global:
>   ...
> @@ -100,12 +100,12 @@ how this may be done.
>  
>  .. code-block:: none
>  
> - $ head ./lib/acl/version.map
> + $ head ./build/lib/librte_acl_exports.map
>   DPDK_22 {
>          global:
>   ...
>  
> - $ head ./lib/eal/version.map
> + $ head ./build/lib/librte_eal_exports.map
>   DPDK_22 {
>          global:
>   ...
> @@ -134,8 +134,7 @@ linked to the DPDK.
>  
>  To support backward compatibility the ``rte_function_versioning.h``
>  header file provides macros to use when updating exported functions. These
> -macros are used in conjunction with the ``version.map`` file for
> -a given library to allow multiple versions of a symbol to exist in a shared
> +macros allow multiple versions of a symbol to exist in a shared
>  library so that older binaries need not be immediately recompiled.
>  
>  The macros are:
> @@ -169,6 +168,7 @@ Assume we have a function as follows
>    * Create an acl context object for apps to
>    * manipulate
>    */
> + RTE_EXPORT_SYMBOL(rte_acl_create)
>   struct rte_acl_ctx *
>   rte_acl_create(const struct rte_acl_param *param)
>   {
> @@ -187,6 +187,7 @@ private, is safe), but it also requires modifying the code as follows
>    * Create an acl context object for apps to
>    * manipulate
>    */
> + RTE_EXPORT_SYMBOL(rte_acl_create)
>   struct rte_acl_ctx *
>   rte_acl_create(const struct rte_acl_param *param, int debug)
>   {
> @@ -203,78 +204,16 @@ The addition of a parameter to the function is ABI breaking as the function is
>  public, and existing application may use it in its current form. However, the
>  compatibility macros in DPDK allow a developer to use symbol versioning so that
>  multiple functions can be mapped to the same public symbol based on when an
> -application was linked to it. To see how this is done, we start with the
> -requisite libraries version map file. Initially the version map file for the acl
> -library looks like this
> +application was linked to it.
>  
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -
> -        rte_acl_add_rules;
> -        rte_acl_build;
> -        rte_acl_classify;
> -        rte_acl_classify_alg;
> -        rte_acl_classify_scalar;
> -        rte_acl_create;
> -        rte_acl_dump;
> -        rte_acl_find_existing;
> -        rte_acl_free;
> -        rte_acl_ipv4vlan_add_rules;
> -        rte_acl_ipv4vlan_build;
> -        rte_acl_list_dump;
> -        rte_acl_reset;
> -        rte_acl_reset_rules;
> -        rte_acl_set_ctx_classify;
> -
> -        local: *;
> -   };
> -
> -This file needs to be modified as follows
> -
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -
> -        rte_acl_add_rules;
> -        rte_acl_build;
> -        rte_acl_classify;
> -        rte_acl_classify_alg;
> -        rte_acl_classify_scalar;
> -        rte_acl_create;
> -        rte_acl_dump;
> -        rte_acl_find_existing;
> -        rte_acl_free;
> -        rte_acl_ipv4vlan_add_rules;
> -        rte_acl_ipv4vlan_build;
> -        rte_acl_list_dump;
> -        rte_acl_reset;
> -        rte_acl_reset_rules;
> -        rte_acl_set_ctx_classify;
> -
> -        local: *;
> -   };
> -
> -   DPDK_22 {
> -        global:
> -        rte_acl_create;
> -
> -   } DPDK_21;
> -
> -The addition of the new block tells the linker that a new version node
> -``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
> -the symbols from the DPDK_21 node. This list is directly translated into a
> -list of exported symbols when DPDK is compiled as a shared library.
> -
> -Next, we need to specify in the code which function maps to the rte_acl_create
> +We need to specify in the code which function maps to the rte_acl_create
>  symbol at which versions.  First, at the site of the initial symbol definition,
>  we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
> -the function return type, and the function name and its arguments.
> +the function return type, the function name and its arguments.
>  
>  .. code-block:: c
>  
> + -RTE_EXPORT_SYMBOL(rte_acl_create)
>   -struct rte_acl_ctx *
>   -rte_acl_create(const struct rte_acl_param *param)
>   +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
> @@ -293,6 +232,7 @@ We have now mapped the original rte_acl_create symbol to the original function
>  
>  Please see the section :ref:`Enabling versioning macros
>  <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
> +
>  Next, we need to create the new version of the symbol. We create a new
>  function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
>  
> @@ -312,9 +252,9 @@ The macro instructs the linker to create the new default symbol
>  ``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
>  (declared by the macro).
>  
> -And that's it, on the next shared library rebuild, there will be two versions of
> -rte_acl_create, an old DPDK_21 version, used by previously built applications,
> -and a new DPDK_22 version, used by future built applications.
> +And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
> +an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
> +used by future built applications.
>  
>  .. note::
>  
> @@ -364,6 +304,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
>      * Create an acl context object for apps to
>      * manipulate
>      */
> +   RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
>     __rte_experimental
>     struct rte_acl_ctx *
>     rte_acl_create(const struct rte_acl_param *param)
> @@ -371,27 +312,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
>     ...
>     }
>  
> -In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL``
> -version node.
> -
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -        ...
> -
> -        local: *;
> -   };
> -
> -   EXPERIMENTAL {
> -        global:
> -
> -        rte_acl_create;
> -   };
> -
>  When we promote the symbol to the stable ABI, we simply strip the
> -``__rte_experimental`` annotation from the function and move the symbol from the
> -``EXPERIMENTAL`` node, to the node of the next major ABI version as follow.
> +``__rte_experimental`` annotation from the function.
>  
>  .. code-block:: c
>  
> @@ -399,31 +321,13 @@ When we promote the symbol to the stable ABI, we simply strip the
>      * Create an acl context object for apps to
>      * manipulate
>      */
> +   RTE_EXPORT_SYMBOL(rte_acl_create)
>     struct rte_acl_ctx *
>     rte_acl_create(const struct rte_acl_param *param)
>     {
>            ...
>     }
>  
> -We then update the map file, adding the symbol ``rte_acl_create``
> -to the ``DPDK_22`` version node.
> -
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -        ...
> -
> -        local: *;
> -   };
> -
> -   DPDK_22 {
> -        global:
> -
> -        rte_acl_create;
> -   } DPDK_21;
> -
> -
>  Although there are strictly no guarantees or commitments associated with
>  :ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer
>  an alias to experimental. The process to add an alias to experimental,
> @@ -452,30 +356,6 @@ and ``DPDK_22`` version nodes.
>        return rte_acl_create(param);
>     }
>  
> -In the map file, we map the symbol to both the ``EXPERIMENTAL``
> -and ``DPDK_22`` version nodes.
> -
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -        ...
> -
> -        local: *;
> -   };
> -
> -   DPDK_22 {
> -        global:
> -
> -        rte_acl_create;
> -   } DPDK_21;
> -
> -   EXPERIMENTAL {
> -        global:
> -
> -        rte_acl_create;
> -   };
> -
>  .. _abi_deprecation:
>  
>  Deprecating part of a public API
> @@ -484,38 +364,7 @@ ________________________________
>  Lets assume that you've done the above updates, and in preparation for the next
>  major ABI version you decide you would like to retire the old version of the
>  function. After having gone through the ABI deprecation announcement process,
> -removal is easy. Start by removing the symbol from the requisite version map
> -file:
> -
> -.. code-block:: none
> -
> -   DPDK_21 {
> -        global:
> -
> -        rte_acl_add_rules;
> -        rte_acl_build;
> -        rte_acl_classify;
> -        rte_acl_classify_alg;
> -        rte_acl_classify_scalar;
> -        rte_acl_dump;
> - -      rte_acl_create
> -        rte_acl_find_existing;
> -        rte_acl_free;
> -        rte_acl_ipv4vlan_add_rules;
> -        rte_acl_ipv4vlan_build;
> -        rte_acl_list_dump;
> -        rte_acl_reset;
> -        rte_acl_reset_rules;
> -        rte_acl_set_ctx_classify;
> -
> -        local: *;
> -   };
> -
> -   DPDK_22 {
> -        global:
> -        rte_acl_create;
> -   } DPDK_21;
> -
> +removal is easy.
>  
>  Next remove the corresponding versioned export.
>  
> @@ -539,36 +388,7 @@ of a major ABI version. If a version node completely specifies an API, then
>  removing part of it, typically makes it incomplete. In those cases it is better
>  to remove the entire node.
>  
> -To do this, start by modifying the version map file, such that all symbols from
> -the node to be removed are merged into the next node in the map.
> -
> -In the case of our map above, it would transform to look as follows
> -
> -.. code-block:: none
> -
> -   DPDK_22 {
> -        global:
> -
> -        rte_acl_add_rules;
> -        rte_acl_build;
> -        rte_acl_classify;
> -        rte_acl_classify_alg;
> -        rte_acl_classify_scalar;
> -        rte_acl_dump;
> -        rte_acl_create
> -        rte_acl_find_existing;
> -        rte_acl_free;
> -        rte_acl_ipv4vlan_add_rules;
> -        rte_acl_ipv4vlan_build;
> -        rte_acl_list_dump;
> -        rte_acl_reset;
> -        rte_acl_reset_rules;
> -        rte_acl_set_ctx_classify;
> -
> -        local: *;
> - };
> -
> -Then any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
> +Any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
>  updated to point to the new version node in any header files for all affected
>  symbols.
>  
> diff --git a/drivers/meson.build b/drivers/meson.build
> index 05391a575d..c8bc556f1a 100644
> --- a/drivers/meson.build
> +++ b/drivers/meson.build
> @@ -245,14 +245,14 @@ foreach subpath:subdirs
>                  dependencies: static_deps,
>                  c_args: cflags)
>          objs += tmp_lib.extract_all_objects(recursive: true)
> -        sources = custom_target(out_filename,
> +        sources_pmd_info = custom_target(out_filename,
>                  command: [pmdinfo, tmp_lib.full_path(), '@OUTPUT@', pmdinfogen],
>                  output: out_filename,
>                  depends: [tmp_lib])
>  
>          # now build the static driver
>          static_lib = static_library(lib_name,
> -                sources,
> +                sources_pmd_info,
>                  objects: objs,
>                  include_directories: includes,
>                  dependencies: static_deps,
> @@ -262,48 +262,72 @@ foreach subpath:subdirs
>          # now build the shared driver
>          version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
>  
> -        lk_deps = []
> -        lk_args = []
>          if not fs.is_file(version_map)
> -            version_map = '@0@/version.map'.format(meson.current_source_dir())
> -            lk_deps += [version_map]
> -        else
> -            lk_deps += [version_map]
> -            if not is_windows and developer_mode
> -                # on unix systems check the output of the
> -                # check-symbols.sh script, using it as a
> -                # dependency of the .so build
> -                lk_deps += custom_target(lib_name + '.sym_chk',
> -                        command: [check_symbols, version_map, '@INPUT@'],
> -                        capture: true,
> -                        input: static_lib,
> -                        output: lib_name + '.sym_chk')
> +            if is_ms_linker
> +                link_mode = 'msvc'
> +            elif is_windows
> +                link_mode = 'mingw'
> +            else
> +                link_mode = 'gnu'
>              endif
> -        endif
> +            version_map = custom_target(lib_name + '_map',
> +                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> +                    input: sources,
> +                    output: 'lib@0@_exports.map'.format(lib_name))
> +            version_map_path = version_map.full_path()
> +            version_map_dep = [version_map]
> +            lk_deps = [version_map]
>  
> -        if is_windows
>              if is_ms_linker
> -                def_file = custom_target(lib_name + '_def',
> -                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                        input: version_map,
> -                        output: '@0@_exports.def'.format(lib_name))
> -                lk_deps += [def_file]
> -
> -                lk_args = ['-Wl,/def:' + def_file.full_path()]
> +                if is_ms_compiler
> +                    lk_args = ['/def:' + version_map.full_path()]
> +                else
> +                    lk_args = ['-Wl,/def:' + version_map.full_path()]
> +                endif
>              else
> -                mingw_map = custom_target(lib_name + '_mingw',
> -                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                        input: version_map,
> -                        output: '@0@_mingw.map'.format(lib_name))
> -                lk_deps += [mingw_map]
> -
> -                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> +                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
>              endif
>          else
> -            lk_args = ['-Wl,--version-script=' + version_map]
> +            version_map_path = version_map
> +            version_map_dep = []
> +            lk_deps = [version_map]
> +
> +            if is_windows
> +                if is_ms_linker
> +                    def_file = custom_target(lib_name + '_def',
> +                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> +                            input: version_map,
> +                            output: '@0@_exports.def'.format(lib_name))
> +                    lk_deps += [def_file]
> +
> +                    lk_args = ['-Wl,/def:' + def_file.full_path()]
> +                else
> +                    mingw_map = custom_target(lib_name + '_mingw',
> +                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> +                            input: version_map,
> +                            output: '@0@_mingw.map'.format(lib_name))
> +                    lk_deps += [mingw_map]
> +
> +                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> +                endif
> +            else
> +                lk_args = ['-Wl,--version-script=' + version_map]
> +            endif
> +        endif
> +
> +        if not is_windows and developer_mode
> +            # on unix systems check the output of the
> +            # check-symbols.sh script, using it as a
> +            # dependency of the .so build
> +            lk_deps += custom_target(lib_name + '.sym_chk',
> +                    command: [check_symbols, version_map_path, '@INPUT@'],
> +                    capture: true,
> +                    input: static_lib,
> +                    output: lib_name + '.sym_chk',
> +                    depends: version_map_dep)
>          endif
>  
> -        shared_lib = shared_library(lib_name, sources,
> +        shared_lib = shared_library(lib_name, sources_pmd_info,
>                  objects: objs,
>                  include_directories: includes,
>                  dependencies: shared_deps,
> diff --git a/drivers/version.map b/drivers/version.map
> deleted file mode 100644
> index 17cc97bda6..0000000000
> --- a/drivers/version.map
> +++ /dev/null
> @@ -1,3 +0,0 @@
> -DPDK_25 {
> -	local: *;
> -};
> diff --git a/lib/meson.build b/lib/meson.build
> index ce92cb5537..b6bac02b48 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -1,6 +1,7 @@
>  # SPDX-License-Identifier: BSD-3-Clause
>  # Copyright(c) 2017-2019 Intel Corporation
>  
> +fs = import('fs')
>  
>  # process all libraries equally, as far as possible
>  # "core" libs first, then others alphabetically as far as possible
> @@ -254,42 +255,60 @@ foreach l:libraries
>              include_directories: includes,
>              dependencies: static_deps)
>  
> -    if not use_function_versioning or is_windows
> -        # use pre-build objects to build shared lib
> -        sources = []
> -        objs += static_lib.extract_all_objects(recursive: false)
> -    else
> -        # for compat we need to rebuild with
> -        # RTE_BUILD_SHARED_LIB defined
> -        cflags += '-DRTE_BUILD_SHARED_LIB'
> -    endif
> -
> -    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
> -    lk_deps = [version_map]
> -
> -    if is_ms_linker
> -        def_file = custom_target(libname + '_def',
> -                command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                input: version_map,
> -                output: '@0@_exports.def'.format(libname))
> -        lk_deps += [def_file]
> +    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
> +        if is_ms_linker
> +            link_mode = 'msvc'
> +        elif is_windows
> +            link_mode = 'mingw'
> +        else
> +            link_mode = 'gnu'
> +        endif
> +        version_map = custom_target(libname + '_map',
> +                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> +                input: sources,
> +                output: 'lib@0@_exports.map'.format(libname))
> +        version_map_path = version_map.full_path()
> +        version_map_dep = [version_map]
> +        lk_deps = [version_map]
>  
> -        if is_ms_compiler
> -            lk_args = ['/def:' + def_file.full_path()]
> +        if is_ms_linker
> +            if is_ms_compiler
> +                lk_args = ['/def:' + version_map.full_path()]
> +            else
> +                lk_args = ['-Wl,/def:' + version_map.full_path()]
> +            endif
>          else
> -            lk_args = ['-Wl,/def:' + def_file.full_path()]
> +            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
>          endif
>      else
> -        if is_windows
> -            mingw_map = custom_target(libname + '_mingw',
> +        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
> +        version_map_path = version_map
> +        version_map_dep = []
> +        lk_deps = [version_map]
> +        if is_ms_linker
> +            def_file = custom_target(libname + '_def',
>                      command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
>                      input: version_map,
> -                    output: '@0@_mingw.map'.format(libname))
> -            lk_deps += [mingw_map]
> +                    output: '@0@_exports.def'.format(libname))
> +            lk_deps += [def_file]
>  
> -            lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> +            if is_ms_compiler
> +                lk_args = ['/def:' + def_file.full_path()]
> +            else
> +                lk_args = ['-Wl,/def:' + def_file.full_path()]
> +            endif
>          else
> -            lk_args = ['-Wl,--version-script=' + version_map]
> +            if is_windows
> +                mingw_map = custom_target(libname + '_mingw',
> +                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> +                        input: version_map,
> +                        output: '@0@_mingw.map'.format(libname))
> +                lk_deps += [mingw_map]
> +
> +                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> +            else
> +                lk_args = ['-Wl,--version-script=' + version_map]
> +            endif
>          endif
>      endif
>  
> @@ -298,11 +317,21 @@ foreach l:libraries
>          # check-symbols.sh script, using it as a
>          # dependency of the .so build
>          lk_deps += custom_target(name + '.sym_chk',
> -                command: [check_symbols,
> -                    version_map, '@INPUT@'],
> +                command: [check_symbols, version_map_path, '@INPUT@'],
>                  capture: true,
>                  input: static_lib,
> -                output: name + '.sym_chk')
> +                output: name + '.sym_chk',
> +                depends: version_map_dep)
> +    endif
> +
> +    if not use_function_versioning or is_windows
> +        # use pre-build objects to build shared lib
> +        sources = []
> +        objs += static_lib.extract_all_objects(recursive: false)
> +    else
> +        # for compat we need to rebuild with
> +        # RTE_BUILD_SHARED_LIB defined
> +        cflags += '-DRTE_BUILD_SHARED_LIB'
>      endif
>  
>      shared_lib = shared_library(libname,
> -- 
> 2.48.1

^ permalink raw reply	[relevance 0%]

* Re: [RFC v3 5/8] build: generate symbol maps
  2025-03-13 17:26  0%     ` Bruce Richardson
@ 2025-03-14 15:38  0%       ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-14 15:38 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: dev, thomas, andremue

On Thu, Mar 13, 2025 at 6:27 PM Bruce Richardson
<bruce.richardson@intel.com> wrote:
>
> On Tue, Mar 11, 2025 at 10:56:03AM +0100, David Marchand wrote:
> > Rather than maintain a file in parallel of the code, symbols to be
> > exported can be marked with a token RTE_EXPORT_*SYMBOL.
> >
> > From those marks, the build framework generates map files only for
> > symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
> > becomes unnecessary).
> >
> > The build framework directly creates a map file in the format that the
> > linker expects (rather than converting from GNU linker to MSVC linker).
> >
> > Empty maps are allowed again as a replacement for drivers/version.map.
> >
> > The symbol check is updated to only support the new format.
> >
> > Signed-off-by: David Marchand <david.marchand@redhat.com>
>
> Some comments inline below.
> /Bruce
>
> > ---
> > Changes since RFC v2:
> > - because of MSVC limitations wrt macro passed via cmdline,
> >   used an internal header for defining RTE_EXPORT_* macros,
> > - updated documentation and tooling,
> >
> > ---
> >  MAINTAINERS                                |   2 +
> >  buildtools/gen-version-map.py              | 111 ++++++++++
> >  buildtools/map-list-symbol.sh              |  10 +-
> >  buildtools/meson.build                     |   1 +
> >  config/meson.build                         |   2 +
> >  config/rte_export.h                        |  16 ++
> >  devtools/check-symbol-change.py            |  90 +++++++++
> >  devtools/check-symbol-maps.sh              |  14 --
> >  devtools/checkpatches.sh                   |   2 +-
> >  doc/guides/contributing/abi_versioning.rst | 224 ++-------------------
> >  drivers/meson.build                        |  94 +++++----
> >  drivers/version.map                        |   3 -
> >  lib/meson.build                            |  91 ++++++---
> >  13 files changed, 371 insertions(+), 289 deletions(-)
> >  create mode 100755 buildtools/gen-version-map.py
> >  create mode 100644 config/rte_export.h
> >  create mode 100755 devtools/check-symbol-change.py
> >  delete mode 100644 drivers/version.map
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 312e6fcee5..04772951d3 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
> >  F: devtools/check-forbidden-tokens.awk
> >  F: devtools/check-git-log.sh
> >  F: devtools/check-spdx-tag.sh
> > +F: devtools/check-symbol-change.py
> >  F: devtools/check-symbol-change.sh
> >  F: devtools/check-symbol-maps.sh
> >  F: devtools/checkpatches.sh
> > @@ -127,6 +128,7 @@ F: config/
> >  F: buildtools/check-symbols.sh
> >  F: buildtools/chkincs/
> >  F: buildtools/call-sphinx-build.py
> > +F: buildtools/gen-version-map.py
> >  F: buildtools/get-cpu-count.py
> >  F: buildtools/get-numa-count.py
> >  F: buildtools/list-dir-globs.py
> > diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
> > new file mode 100755
> > index 0000000000..b160aa828b
> > --- /dev/null
> > +++ b/buildtools/gen-version-map.py
> > @@ -0,0 +1,111 @@
> > +#!/usr/bin/env python3
> > +# SPDX-License-Identifier: BSD-3-Clause
> > +# Copyright (c) 2024 Red Hat, Inc.
> > +
> > +"""Generate a version map file used by GNU or MSVC linker."""
> > +
>
> While it's an internal build script not to be run by users directly, I
> believe a short one-line usage here might be useful, since the code below
> is directly referencing sys.argv[N] values. That makes it easier for the
> user to know what they are.
>
> Alternatively, assign them to proper names at the top of the script e.g.:
>         scriptname, link_mode, abi_version_file, output, *input = sys.argv

I like this simple form.

>
> Final alternative (which may be a bit overkill) is to use argparse.
>
> > +import re
> > +import sys
> > +
> > +# From rte_export.h
> > +export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
> > +export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> > +export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
> > +# From rte_function_versioning.h
> > +ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> > +default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +
> > +with open(sys.argv[2]) as f:
> > +    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
> > +
> > +symbols = {}
> > +
> > +for file in sys.argv[4:]:
> > +    with open(file, encoding="utf-8") as f:
> > +        for ln in f.readlines():
> > +            node = None
> > +            symbol = None
> > +            comment = None
> > +            if export_exp_sym_regexp.match(ln):
> > +                node = 'EXPERIMENTAL'
> > +                symbol = export_exp_sym_regexp.match(ln).group(1)
> > +                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
> > +            elif export_int_sym_regexp.match(ln):
> > +                node = 'INTERNAL'
> > +                symbol = export_int_sym_regexp.match(ln).group(1)
> > +            elif export_sym_regexp.match(ln):
> > +                node = abi
> > +                symbol = export_sym_regexp.match(ln).group(1)
> > +            elif ver_sym_regexp.match(ln):
> > +                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
> > +                symbol = ver_sym_regexp.match(ln).group(2)
> > +            elif ver_exp_sym_regexp.match(ln):
> > +                node = 'EXPERIMENTAL'
> > +                symbol = ver_exp_sym_regexp.match(ln).group(1)
> > +            elif default_sym_regexp.match(ln):
> > +                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
> > +                symbol = default_sym_regexp.match(ln).group(2)
> > +
> > +            if not symbol:
> > +                continue
> > +
> > +            if node not in symbols:
> > +                symbols[node] = {}
> > +            symbols[node][symbol] = comment
> > +
> > +if sys.argv[1] == 'msvc':
> > +    with open(sys.argv[3], "w") as outfile:
> > +        outfile.writelines(f"EXPORTS\n")
> > +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> > +            if key not in symbols:
> > +                continue
> > +            for symbol in sorted(symbols[key].keys()):
> > +                outfile.writelines(f"\t{symbol}\n")
> > +            del symbols[key]
> > +else:
> > +    with open(sys.argv[3], "w") as outfile:
> > +        local_token = False
> > +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> > +            if key not in symbols:
> > +                continue
> > +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> > +            for symbol in sorted(symbols[key].keys()):
> > +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> > +                    prefix = '__emutls_v.'
> > +                else:
> > +                    prefix = ''
> > +                outfile.writelines(f"\t{prefix}{symbol};")
> > +                comment = symbols[key][symbol]
> > +                if comment:
> > +                    outfile.writelines(f"{comment}")
> > +                outfile.writelines("\n")
>
> How about using "" rather than None for the default comment so you can
> always just do a print of "{prefix}{symbol};{comment}\n". The fact that
> writelines doesn't output a "\n" is a little confusing here, so maybe use
> "print" instead.
>
>         print("f\t{prefix}{symbol};{comment}", file=outfile)

Yes, better.


>
> > +            outfile.writelines("\n")
> > +            if not local_token:
> > +                outfile.writelines("\tlocal: *;\n")
> > +                local_token = True
> > +            outfile.writelines("};\n")
> > +            del symbols[key]
> > +        for key in sorted(symbols.keys()):
> > +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> > +            for symbol in sorted(symbols[key].keys()):
> > +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> > +                    prefix = '__emutls_v.'
> > +                else:
> > +                    prefix = ''
> > +                outfile.writelines(f"\t{prefix}{symbol};")
> > +                comment = symbols[key][symbol]
> > +                if comment:
> > +                    outfile.writelines(f"{comment}")
> > +                outfile.writelines("\n")
> > +            outfile.writelines(f"}} {abi};\n")
> > +            if not local_token:
> > +                outfile.writelines("\tlocal: *;\n")
> > +                local_token = True
> > +            del symbols[key]
> > +        # No exported symbol, add a catch all
> > +        if not local_token:
> > +            outfile.writelines(f"{abi} {{\n")
> > +            outfile.writelines("\tlocal: *;\n")
> > +            local_token = True
> > +            outfile.writelines("};\n")
> > diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
> > index eb98451d8e..0829df4be5 100755
> > --- a/buildtools/map-list-symbol.sh
> > +++ b/buildtools/map-list-symbol.sh
> > @@ -62,10 +62,14 @@ for file in $@; do
> >               if (current_section == "") {
> >                       next;
> >               }
> > +             symbol_version = current_version
> > +             if (/^[^}].*[^:*]; # added in /) {
> > +                     symbol_version = $5
> > +             }
> >               if ("'$version'" != "") {
> > -                     if ("'$version'" == "unset" && current_version != "") {
> > +                     if ("'$version'" == "unset" && symbol_version != "") {
> >                               next;
> > -                     } else if ("'$version'" != "unset" && "'$version'" != current_version) {
> > +                     } else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
> >                               next;
> >                       }
> >               }
> > @@ -73,7 +77,7 @@ for file in $@; do
> >               if ("'$symbol'" == "all" || $1 == "'$symbol'") {
> >                       ret = 0;
> >                       if ("'$quiet'" == "") {
> > -                             print "'$file' "current_section" "$1" "current_version;
> > +                             print "'$file' "current_section" "$1" "symbol_version;
> >                       }
> >                       if ("'$symbol'" != "all") {
> >                               exit 0;
> > diff --git a/buildtools/meson.build b/buildtools/meson.build
> > index 4e2c1217a2..b745e9afa4 100644
> > --- a/buildtools/meson.build
> > +++ b/buildtools/meson.build
> > @@ -16,6 +16,7 @@ else
> >      py3 = ['meson', 'runpython']
> >  endif
> >  echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
> > +gen_version_map = py3 + files('gen-version-map.py')
> >  list_dir_globs = py3 + files('list-dir-globs.py')
> >  map_to_win_cmd = py3 + files('map_to_win.py')
> >  sphinx_wrapper = py3 + files('call-sphinx-build.py')
> > diff --git a/config/meson.build b/config/meson.build
> > index f31fef216c..54657055fb 100644
> > --- a/config/meson.build
> > +++ b/config/meson.build
> > @@ -303,8 +303,10 @@ endif
> >  # add -include rte_config to cflags
> >  if is_ms_compiler
> >      add_project_arguments('/FI', 'rte_config.h', language: 'c')
> > +    add_project_arguments('/FI', 'rte_export.h', language: 'c')
> >  else
> >      add_project_arguments('-include', 'rte_config.h', language: 'c')
> > +    add_project_arguments('-include', 'rte_export.h', language: 'c')
> >  endif
> >
> >  # enable extra warnings and disable any unwanted warnings
> > diff --git a/config/rte_export.h b/config/rte_export.h
> > new file mode 100644
> > index 0000000000..83d871fe11
> > --- /dev/null
> > +++ b/config/rte_export.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright (c) 2025 Red Hat, Inc.
> > + */
> > +
> > +#ifndef RTE_EXPORT_H
> > +#define RTE_EXPORT_H
> > +
> > +/* *Internal* macros for exporting symbols, used by the build system.
> > + * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
> > + * version this symbol was introduced in.
> > + */
> > +#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
> > +#define RTE_EXPORT_INTERNAL_SYMBOL(a)
> > +#define RTE_EXPORT_SYMBOL(a)
> > +
> > +#endif /* RTE_EXPORT_H */
> > diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
> > new file mode 100755
> > index 0000000000..09709e4f06
> > --- /dev/null
> > +++ b/devtools/check-symbol-change.py
> > @@ -0,0 +1,90 @@
> > +#!/usr/bin/env python3
> > +# SPDX-License-Identifier: BSD-3-Clause
> > +# Copyright (c) 2025 Red Hat, Inc.
> > +
> > +"""Check exported symbols change in a patch."""
> > +
> > +import re
> > +import sys
> > +
> > +file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
> > +# From rte_export.h
> > +export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
> > +export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> > +export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
> > +# TODO, handle versioned symbols from rte_function_versioning.h
> > +# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> > +# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +
> > +symbols = {}
> > +
> > +for file in sys.argv[1:]:
> > +    with open(file, encoding="utf-8") as f:
> > +        for ln in f.readlines():
> > +            if file_header_regexp.match(ln):
> > +                if file_header_regexp.match(ln).group(2) == "lib":
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> > +                elif file_header_regexp.match(ln).group(3) == "intel":
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
> > +                else:
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> > +
> > +                if lib not in symbols:
> > +                    symbols[lib] = {}
> > +                continue
> > +
> > +            if export_exp_sym_regexp.match(ln):
> > +                symbol = export_exp_sym_regexp.match(ln).group(1)
> > +                node = 'EXPERIMENTAL'
> > +            elif export_int_sym_regexp.match(ln):
> > +                node = 'INTERNAL'
> > +                symbol = export_int_sym_regexp.match(ln).group(1)
> > +            elif export_sym_regexp.match(ln):
> > +                symbol = export_sym_regexp.match(ln).group(1)
> > +                node = 'stable'
> > +            else:
> > +                continue
> > +
> > +            if symbol not in symbols[lib]:
> > +                symbols[lib][symbol] = {}
> > +            added = ln[0] == '+'
> > +            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
> > +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> > +            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
> > +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> > +            if added:
> > +                symbols[lib][symbol]['added'] = node
> > +            else:
> > +                symbols[lib][symbol]['removed'] = node
> > +
> > +    for lib in sorted(symbols.keys()):
> > +        error = False
> > +        for symbol in sorted(symbols[lib].keys()):
> > +            if 'removed' not in symbols[lib][symbol]:
> > +                # Symbol addition
> > +                node = symbols[lib][symbol]['added']
> > +                if node == 'stable':
> > +                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
> > +                    error = True
> > +                else:
> > +                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
> > +                continue
> > +
> > +            if 'added' not in symbols[lib][symbol]:
> > +                # Symbol removal
> > +                node = symbols[lib][symbol]['added']
> > +                if node == 'stable':
> > +                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
> > +                    print(f"Please check it has gone though the deprecation process.")
> > +                continue
> > +
> > +            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
> > +                # Symbol was moved around
> > +                continue
> > +
> > +            # Symbol modifications
> > +            added = symbols[lib][symbol]['added']
> > +            removed = symbols[lib][symbol]['removed']
> > +            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
> > +            print(f"Please check it has gone though the deprecation process.")
> > diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
> > index 6121f78ec6..fcd3931e5d 100755
> > --- a/devtools/check-symbol-maps.sh
> > +++ b/devtools/check-symbol-maps.sh
> > @@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
> >      ret=1
> >  fi
> >
> > -find_empty_maps ()
> > -{
> > -    for map in $@ ; do
> > -        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
> > -    done
> > -}
> > -
> > -empty_maps=$(find_empty_maps $@)
> > -if [ -n "$empty_maps" ] ; then
> > -    echo "Found empty maps:"
> > -    echo "$empty_maps"
> > -    ret=1
> > -fi
> > -
> >  find_bad_format_maps ()
> >  {
> >      abi_version=$(cut -d'.' -f 1 ABI_VERSION)
> > diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
> > index 003bb49e04..7dcac7c8c9 100755
> > --- a/devtools/checkpatches.sh
> > +++ b/devtools/checkpatches.sh
> > @@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
> >  PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
> >  SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
> >  LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
> > -NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
> > +NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
> >  options="$options $DPDK_CHECKPATCH_OPTIONS"
> >
> >  print_usage () {
> > diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
> > index 88dd776b4c..addbb24b9e 100644
> > --- a/doc/guides/contributing/abi_versioning.rst
> > +++ b/doc/guides/contributing/abi_versioning.rst
> > @@ -58,12 +58,12 @@ persists over multiple releases.
> >
> >  .. code-block:: none
> >
> > - $ head ./lib/acl/version.map
> > + $ head ./build/lib/librte_acl_exports.map
>
> I must admit I'm not a fan of these long filenames. How about just
> "acl_exports.map"?

I used the same prefix as other targets in ninja, I usually rely on
auto-completion.
But other than that, I don't mind.


>
> >   DPDK_21 {
> >          global:
> >   ...
> >
> > - $ head ./lib/eal/version.map
> > + $ head ./build/lib/librte_eal_exports.map
> >   DPDK_21 {
> >          global:
> >   ...
> > @@ -77,7 +77,7 @@ that library.
> >
> >  .. code-block:: none
> >
> > - $ head ./lib/acl/version.map
> > + $ head ./build/lib/librte_acl_exports.map
> >   DPDK_21 {
> >          global:
> >   ...
> > @@ -88,7 +88,7 @@ that library.
> >   } DPDK_21;
> >   ...
> >
> > - $ head ./lib/eal/version.map
> > + $ head ./build/lib/librte_eal_exports.map
> >   DPDK_21 {
> >          global:
> >   ...
> > @@ -100,12 +100,12 @@ how this may be done.
> >
> >  .. code-block:: none
> >
> > - $ head ./lib/acl/version.map
> > + $ head ./build/lib/librte_acl_exports.map
> >   DPDK_22 {
> >          global:
> >   ...
> >
> > - $ head ./lib/eal/version.map
> > + $ head ./build/lib/librte_eal_exports.map
> >   DPDK_22 {
> >          global:
> >   ...
> > @@ -134,8 +134,7 @@ linked to the DPDK.
> >
> >  To support backward compatibility the ``rte_function_versioning.h``
> >  header file provides macros to use when updating exported functions. These
> > -macros are used in conjunction with the ``version.map`` file for
> > -a given library to allow multiple versions of a symbol to exist in a shared
> > +macros allow multiple versions of a symbol to exist in a shared
> >  library so that older binaries need not be immediately recompiled.
> >
> >  The macros are:
> > @@ -169,6 +168,7 @@ Assume we have a function as follows
> >    * Create an acl context object for apps to
> >    * manipulate
> >    */
> > + RTE_EXPORT_SYMBOL(rte_acl_create)
> >   struct rte_acl_ctx *
> >   rte_acl_create(const struct rte_acl_param *param)
> >   {
> > @@ -187,6 +187,7 @@ private, is safe), but it also requires modifying the code as follows
> >    * Create an acl context object for apps to
> >    * manipulate
> >    */
> > + RTE_EXPORT_SYMBOL(rte_acl_create)
> >   struct rte_acl_ctx *
> >   rte_acl_create(const struct rte_acl_param *param, int debug)
> >   {
> > @@ -203,78 +204,16 @@ The addition of a parameter to the function is ABI breaking as the function is
> >  public, and existing application may use it in its current form. However, the
> >  compatibility macros in DPDK allow a developer to use symbol versioning so that
> >  multiple functions can be mapped to the same public symbol based on when an
> > -application was linked to it. To see how this is done, we start with the
> > -requisite libraries version map file. Initially the version map file for the acl
> > -library looks like this
> > +application was linked to it.
> >
> > -.. code-block:: none
> > -
> > -   DPDK_21 {
> > -        global:
> > -
> > -        rte_acl_add_rules;
> > -        rte_acl_build;
> > -        rte_acl_classify;
> > -        rte_acl_classify_alg;
> > -        rte_acl_classify_scalar;
> > -        rte_acl_create;
> > -        rte_acl_dump;
> > -        rte_acl_find_existing;
> > -        rte_acl_free;
> > -        rte_acl_ipv4vlan_add_rules;
> > -        rte_acl_ipv4vlan_build;
> > -        rte_acl_list_dump;
> > -        rte_acl_reset;
> > -        rte_acl_reset_rules;
> > -        rte_acl_set_ctx_classify;
> > -
> > -        local: *;
> > -   };
> > -
> > -This file needs to be modified as follows
> > -
> > -.. code-block:: none
> > -
> > -   DPDK_21 {
> > -        global:
> > -
> > -        rte_acl_add_rules;
> > -        rte_acl_build;
> > -        rte_acl_classify;
> > -        rte_acl_classify_alg;
> > -        rte_acl_classify_scalar;
> > -        rte_acl_create;
> > -        rte_acl_dump;
> > -        rte_acl_find_existing;
> > -        rte_acl_free;
> > -        rte_acl_ipv4vlan_add_rules;
> > -        rte_acl_ipv4vlan_build;
> > -        rte_acl_list_dump;
> > -        rte_acl_reset;
> > -        rte_acl_reset_rules;
> > -        rte_acl_set_ctx_classify;
> > -
> > -        local: *;
> > -   };
> > -
> > -   DPDK_22 {
> > -        global:
> > -        rte_acl_create;
> > -
> > -   } DPDK_21;
> > -
> > -The addition of the new block tells the linker that a new version node
> > -``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
> > -the symbols from the DPDK_21 node. This list is directly translated into a
> > -list of exported symbols when DPDK is compiled as a shared library.
> > -
> > -Next, we need to specify in the code which function maps to the rte_acl_create
> > +We need to specify in the code which function maps to the rte_acl_create
> >  symbol at which versions.  First, at the site of the initial symbol definition,
> >  we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
> > -the function return type, and the function name and its arguments.
> > +the function return type, the function name and its arguments.
>
> Good fix, though technically not relevant to this patch.

Indeed..
>
> >
> >  .. code-block:: c
> >
> > + -RTE_EXPORT_SYMBOL(rte_acl_create)
> >   -struct rte_acl_ctx *
> >   -rte_acl_create(const struct rte_acl_param *param)
> >   +RTE_VERSION_SYMBOL(21, struct rte_acl_ctx *, rte_acl_create, (const struct rte_acl_param *param))
> > @@ -293,6 +232,7 @@ We have now mapped the original rte_acl_create symbol to the original function
> >
> >  Please see the section :ref:`Enabling versioning macros
> >  <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
> > +
>
> Ditto.
>
> >  Next, we need to create the new version of the symbol. We create a new
> >  function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
> >
> > @@ -312,9 +252,9 @@ The macro instructs the linker to create the new default symbol
> >  ``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
> >  (declared by the macro).
> >
> > -And that's it, on the next shared library rebuild, there will be two versions of
> > -rte_acl_create, an old DPDK_21 version, used by previously built applications,
> > -and a new DPDK_22 version, used by future built applications.
> > +And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
> > +an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
> > +used by future built applications.
>
> nit: not sure what others think but "future built" sounds strange to me?
> How about "later built" or "newly built"?

newly sounds better to me.

>
> >
> >  .. note::
> >
> > @@ -364,6 +304,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
> >      * Create an acl context object for apps to
> >      * manipulate
> >      */
> <snip>
>


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* Re: [RFC v3 5/8] build: generate symbol maps
  2025-03-14 15:27  0%     ` Andre Muezerie
@ 2025-03-14 15:51  4%       ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-14 15:51 UTC (permalink / raw)
  To: Andre Muezerie; +Cc: dev, thomas, bruce.richardson

On Fri, Mar 14, 2025 at 4:28 PM Andre Muezerie
<andremue@linux.microsoft.com> wrote:
>
> On Tue, Mar 11, 2025 at 10:56:03AM +0100, David Marchand wrote:
> > Rather than maintain a file in parallel of the code, symbols to be
> > exported can be marked with a token RTE_EXPORT_*SYMBOL.
> >
> > >From those marks, the build framework generates map files only for
> > symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
> > becomes unnecessary).
> >
> > The build framework directly creates a map file in the format that the
> > linker expects (rather than converting from GNU linker to MSVC linker).
> >
> > Empty maps are allowed again as a replacement for drivers/version.map.
> >
> > The symbol check is updated to only support the new format.
> >
> > Signed-off-by: David Marchand <david.marchand@redhat.com>
> > ---
> > Changes since RFC v2:
> > - because of MSVC limitations wrt macro passed via cmdline,
> >   used an internal header for defining RTE_EXPORT_* macros,
> > - updated documentation and tooling,
> >
> > ---
> >  MAINTAINERS                                |   2 +
> >  buildtools/gen-version-map.py              | 111 ++++++++++
> >  buildtools/map-list-symbol.sh              |  10 +-
> >  buildtools/meson.build                     |   1 +
> >  config/meson.build                         |   2 +
> >  config/rte_export.h                        |  16 ++
> >  devtools/check-symbol-change.py            |  90 +++++++++
> >  devtools/check-symbol-maps.sh              |  14 --
> >  devtools/checkpatches.sh                   |   2 +-
> >  doc/guides/contributing/abi_versioning.rst | 224 ++-------------------
> >  drivers/meson.build                        |  94 +++++----
> >  drivers/version.map                        |   3 -
> >  lib/meson.build                            |  91 ++++++---
> >  13 files changed, 371 insertions(+), 289 deletions(-)
> >  create mode 100755 buildtools/gen-version-map.py
> >  create mode 100644 config/rte_export.h
> >  create mode 100755 devtools/check-symbol-change.py
> >  delete mode 100644 drivers/version.map
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 312e6fcee5..04772951d3 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
> >  F: devtools/check-forbidden-tokens.awk
> >  F: devtools/check-git-log.sh
> >  F: devtools/check-spdx-tag.sh
> > +F: devtools/check-symbol-change.py
> >  F: devtools/check-symbol-change.sh
> >  F: devtools/check-symbol-maps.sh
> >  F: devtools/checkpatches.sh
> > @@ -127,6 +128,7 @@ F: config/
> >  F: buildtools/check-symbols.sh
> >  F: buildtools/chkincs/
> >  F: buildtools/call-sphinx-build.py
> > +F: buildtools/gen-version-map.py
> >  F: buildtools/get-cpu-count.py
> >  F: buildtools/get-numa-count.py
> >  F: buildtools/list-dir-globs.py
> > diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
> > new file mode 100755
> > index 0000000000..b160aa828b
> > --- /dev/null
> > +++ b/buildtools/gen-version-map.py
> > @@ -0,0 +1,111 @@
> > +#!/usr/bin/env python3
> > +# SPDX-License-Identifier: BSD-3-Clause
> > +# Copyright (c) 2024 Red Hat, Inc.
>
> 2025?

Well, technically, I had written the first version of this script in 2024 :-).
But I'll align to the rest of the patch.

> I appreciate that Python was chosen instead of sh/bash.
>
> > +
> > +"""Generate a version map file used by GNU or MSVC linker."""
> > +
> > +import re
> > +import sys
> > +
> > +# From rte_export.h
> > +export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
> > +export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> > +export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
> > +# From rte_function_versioning.h
> > +ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> > +default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +
> > +with open(sys.argv[2]) as f:
> > +    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
> > +
> > +symbols = {}
> > +
> > +for file in sys.argv[4:]:
> > +    with open(file, encoding="utf-8") as f:
> > +        for ln in f.readlines():
> > +            node = None
> > +            symbol = None
> > +            comment = None
> > +            if export_exp_sym_regexp.match(ln):
> > +                node = 'EXPERIMENTAL'
> > +                symbol = export_exp_sym_regexp.match(ln).group(1)
> > +                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
> > +            elif export_int_sym_regexp.match(ln):
> > +                node = 'INTERNAL'
> > +                symbol = export_int_sym_regexp.match(ln).group(1)
> > +            elif export_sym_regexp.match(ln):
> > +                node = abi
> > +                symbol = export_sym_regexp.match(ln).group(1)
> > +            elif ver_sym_regexp.match(ln):
> > +                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
> > +                symbol = ver_sym_regexp.match(ln).group(2)
> > +            elif ver_exp_sym_regexp.match(ln):
> > +                node = 'EXPERIMENTAL'
> > +                symbol = ver_exp_sym_regexp.match(ln).group(1)
> > +            elif default_sym_regexp.match(ln):
> > +                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
> > +                symbol = default_sym_regexp.match(ln).group(2)
> > +
> > +            if not symbol:
> > +                continue
> > +
> > +            if node not in symbols:
> > +                symbols[node] = {}
> > +            symbols[node][symbol] = comment
> > +
> > +if sys.argv[1] == 'msvc':
> > +    with open(sys.argv[3], "w") as outfile:
> > +        outfile.writelines(f"EXPORTS\n")
> > +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> > +            if key not in symbols:
> > +                continue
> > +            for symbol in sorted(symbols[key].keys()):
> > +                outfile.writelines(f"\t{symbol}\n")
> > +            del symbols[key]
> > +else:
> > +    with open(sys.argv[3], "w") as outfile:
>
> Consider having output file samples documented, perhaps in this script itself, to make
> it easier to understand what this script it doing and highlight the differences between
> the formats supported (msvc, etc).

I am not sure I follow.

The differences between the format is not something "normal" DPDK
contributors/developers should care about.
DPDK documentation was giving (too much) details on the version.map
gnu linker stuff, and I would prefer we stop documenting this.
Instead, the focus should be on the new sets of export macros, which
serve as an abstraction.


>
> > +        local_token = False
> > +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> > +            if key not in symbols:
> > +                continue
> > +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> > +            for symbol in sorted(symbols[key].keys()):
> > +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> > +                    prefix = '__emutls_v.'
> > +                else:
> > +                    prefix = ''
> > +                outfile.writelines(f"\t{prefix}{symbol};")
> > +                comment = symbols[key][symbol]
> > +                if comment:
> > +                    outfile.writelines(f"{comment}")
> > +                outfile.writelines("\n")
> > +            outfile.writelines("\n")
> > +            if not local_token:
> > +                outfile.writelines("\tlocal: *;\n")
> > +                local_token = True
> > +            outfile.writelines("};\n")
> > +            del symbols[key]
> > +        for key in sorted(symbols.keys()):
> > +            outfile.writelines(f"{key} {{\n\tglobal:\n\n")
> > +            for symbol in sorted(symbols[key].keys()):
> > +                if sys.argv[1] == 'mingw' and symbol.startswith('per_lcore'):
> > +                    prefix = '__emutls_v.'
> > +                else:
> > +                    prefix = ''
> > +                outfile.writelines(f"\t{prefix}{symbol};")
> > +                comment = symbols[key][symbol]
> > +                if comment:
> > +                    outfile.writelines(f"{comment}")
> > +                outfile.writelines("\n")
> > +            outfile.writelines(f"}} {abi};\n")
> > +            if not local_token:
> > +                outfile.writelines("\tlocal: *;\n")
> > +                local_token = True
> > +            del symbols[key]
> > +        # No exported symbol, add a catch all
> > +        if not local_token:
> > +            outfile.writelines(f"{abi} {{\n")
> > +            outfile.writelines("\tlocal: *;\n")
> > +            local_token = True
> > +            outfile.writelines("};\n")
> > diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
> > index eb98451d8e..0829df4be5 100755
> > --- a/buildtools/map-list-symbol.sh
> > +++ b/buildtools/map-list-symbol.sh
> > @@ -62,10 +62,14 @@ for file in $@; do
> >               if (current_section == "") {
> >                       next;
> >               }
> > +             symbol_version = current_version
> > +             if (/^[^}].*[^:*]; # added in /) {
> > +                     symbol_version = $5
> > +             }
> >               if ("'$version'" != "") {
> > -                     if ("'$version'" == "unset" && current_version != "") {
> > +                     if ("'$version'" == "unset" && symbol_version != "") {
> >                               next;
> > -                     } else if ("'$version'" != "unset" && "'$version'" != current_version) {
> > +                     } else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
> >                               next;
> >                       }
> >               }
> > @@ -73,7 +77,7 @@ for file in $@; do
> >               if ("'$symbol'" == "all" || $1 == "'$symbol'") {
> >                       ret = 0;
> >                       if ("'$quiet'" == "") {
> > -                             print "'$file' "current_section" "$1" "current_version;
> > +                             print "'$file' "current_section" "$1" "symbol_version;
> >                       }
> >                       if ("'$symbol'" != "all") {
> >                               exit 0;
> > diff --git a/buildtools/meson.build b/buildtools/meson.build
> > index 4e2c1217a2..b745e9afa4 100644
> > --- a/buildtools/meson.build
> > +++ b/buildtools/meson.build
> > @@ -16,6 +16,7 @@ else
> >      py3 = ['meson', 'runpython']
> >  endif
> >  echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
> > +gen_version_map = py3 + files('gen-version-map.py')
> >  list_dir_globs = py3 + files('list-dir-globs.py')
> >  map_to_win_cmd = py3 + files('map_to_win.py')
> >  sphinx_wrapper = py3 + files('call-sphinx-build.py')
> > diff --git a/config/meson.build b/config/meson.build
> > index f31fef216c..54657055fb 100644
> > --- a/config/meson.build
> > +++ b/config/meson.build
> > @@ -303,8 +303,10 @@ endif
> >  # add -include rte_config to cflags
> >  if is_ms_compiler
> >      add_project_arguments('/FI', 'rte_config.h', language: 'c')
> > +    add_project_arguments('/FI', 'rte_export.h', language: 'c')
> >  else
> >      add_project_arguments('-include', 'rte_config.h', language: 'c')
> > +    add_project_arguments('-include', 'rte_export.h', language: 'c')
> >  endif
> >
> >  # enable extra warnings and disable any unwanted warnings
> > diff --git a/config/rte_export.h b/config/rte_export.h
> > new file mode 100644
> > index 0000000000..83d871fe11
> > --- /dev/null
> > +++ b/config/rte_export.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright (c) 2025 Red Hat, Inc.
> > + */
> > +
> > +#ifndef RTE_EXPORT_H
> > +#define RTE_EXPORT_H
> > +
> > +/* *Internal* macros for exporting symbols, used by the build system.
> > + * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
> > + * version this symbol was introduced in.
> > + */
> > +#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
> > +#define RTE_EXPORT_INTERNAL_SYMBOL(a)
> > +#define RTE_EXPORT_SYMBOL(a)
> > +
> > +#endif /* RTE_EXPORT_H */
> > diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
> > new file mode 100755
> > index 0000000000..09709e4f06
> > --- /dev/null
> > +++ b/devtools/check-symbol-change.py
> > @@ -0,0 +1,90 @@
> > +#!/usr/bin/env python3
> > +# SPDX-License-Identifier: BSD-3-Clause
> > +# Copyright (c) 2025 Red Hat, Inc.
> > +
> > +"""Check exported symbols change in a patch."""
> > +
> > +import re
> > +import sys
> > +
> > +file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
> > +# From rte_export.h
> > +export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
> > +export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
> > +export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
> > +# TODO, handle versioned symbols from rte_function_versioning.h
> > +# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
> > +# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
> > +
> > +symbols = {}
> > +
> > +for file in sys.argv[1:]:
> > +    with open(file, encoding="utf-8") as f:
> > +        for ln in f.readlines():
> > +            if file_header_regexp.match(ln):
> > +                if file_header_regexp.match(ln).group(2) == "lib":
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> > +                elif file_header_regexp.match(ln).group(3) == "intel":
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
> > +                else:
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> > +
> > +                if lib not in symbols:
> > +                    symbols[lib] = {}
> > +                continue
> > +
> > +            if export_exp_sym_regexp.match(ln):
> > +                symbol = export_exp_sym_regexp.match(ln).group(1)
> > +                node = 'EXPERIMENTAL'
> > +            elif export_int_sym_regexp.match(ln):
> > +                node = 'INTERNAL'
> > +                symbol = export_int_sym_regexp.match(ln).group(1)
> > +            elif export_sym_regexp.match(ln):
> > +                symbol = export_sym_regexp.match(ln).group(1)
> > +                node = 'stable'
> > +            else:
> > +                continue
> > +
> > +            if symbol not in symbols[lib]:
> > +                symbols[lib][symbol] = {}
> > +            added = ln[0] == '+'
> > +            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
> > +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> > +            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
> > +                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
> > +            if added:
> > +                symbols[lib][symbol]['added'] = node
> > +            else:
> > +                symbols[lib][symbol]['removed'] = node
> > +
> > +    for lib in sorted(symbols.keys()):
> > +        error = False
> > +        for symbol in sorted(symbols[lib].keys()):
> > +            if 'removed' not in symbols[lib][symbol]:
> > +                # Symbol addition
> > +                node = symbols[lib][symbol]['added']
> > +                if node == 'stable':
> > +                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
> > +                    error = True
> > +                else:
> > +                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
> > +                continue
> > +
> > +            if 'added' not in symbols[lib][symbol]:
> > +                # Symbol removal
> > +                node = symbols[lib][symbol]['added']
> > +                if node == 'stable':
> > +                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
>
> Some people would argue that WARN instead of INFO is more appropriate because some attention
> is needed from the user. INFO many times is just ignored.

True, though the ABI check is supposed to fail with a big ERROR :-).

I would have to remember why we put INFO initially (I am just
reimplementing the .sh check that existed on static maps).
I think Thomas was the one who wanted it as INFO...


>
> > +                    print(f"Please check it has gone though the deprecation process.")
> > +                continue
> > +
> > +            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
> > +                # Symbol was moved around
> > +                continue
> > +
> > +            # Symbol modifications
> > +            added = symbols[lib][symbol]['added']
> > +            removed = symbols[lib][symbol]['removed']
> > +            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
>
> Perhaps use WARN instead of INFO.

On this part, I disagree.
Moving from a non stable (like experimental) ABI to stable or other
non stable ABI (like internal) is not an issue.


>
> > +            print(f"Please check it has gone though the deprecation process.")

[snip]


-- 
David Marchand


^ permalink raw reply	[relevance 4%]

* [RFC v4 0/8] Symbol versioning and export rework
  2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
  2025-03-06 12:50  6% ` [RFC v2 1/2] " David Marchand
  2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
@ 2025-03-17 15:42  3% ` David Marchand
  2025-03-17 15:42 16%   ` [RFC v4 3/8] eal: rework function versioning macros David Marchand
                     ` (4 more replies)
  2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
                   ` (2 subsequent siblings)
  5 siblings, 5 replies; 153+ results
From: David Marchand @ 2025-03-17 15:42 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

So far, each DPDK library (or driver) exposing symbols in an ABI had to
maintain a version.map and use some macros for symbol versioning,
specially crafted with the GNU linker in mind.

This series proposes to rework the whole principle, and instead rely on
marking the symbol exports in the source code itself, then let it to the
build framework to produce a version script adapted to the linker in use
(think GNU linker vs MSVC linker).

This greatly simplifies versioning symbols: a developer does not need to
know anything about version.map, or that a versioned symbol must be
renamed with _v26, annotated with __vsym, exported in a header etc...

Checking symbol maps becomes unnecessary since generated by the build
framework.

Updating to a new ABI is just a matter of bumping the value in
ABI_VERSION.


Comments please.


-- 
David Marchand

Depends-on: series-34869 ("remove driver-specific logic for AVX builds")

Changes since RFC v3:
- fixed/simplified documentation,
- rebased on top of Bruce series for common handling of AVX sources,

Changes since RFC v2:
- updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
  signature is enclosed in the macro,
- dropped invalid exports for some dead symbols or inline helpers,
- updated documentation and tooling,
- converted the whole tree (via a local script of mine),

David Marchand (8):
  lib: remove incorrect exported symbols
  drivers: remove incorrect exported symbols
  eal: rework function versioning macros
  buildtools: display version when listing symbols
  build: generate symbol maps
  build: mark exported symbols
  build: use dynamically generated version maps
  build: remove static version maps

 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   9 +-
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/gen-version-map.py                 | 106 ++++
 buildtools/map-list-symbol.sh                 |  15 +-
 buildtools/map_to_win.py                      |  41 --
 buildtools/meson.build                        |   2 +-
 config/meson.build                            |   4 +-
 config/rte_export.h                           |  16 +
 devtools/check-spdx-tag.sh                    |   2 +-
 devtools/check-symbol-change.py               |  90 +++
 devtools/check-symbol-change.sh               | 186 ------
 devtools/check-symbol-maps.sh                 | 115 ----
 devtools/checkpatches.sh                      |   4 +-
 devtools/update-abi.sh                        |  46 --
 devtools/update_version_map_abi.py            | 210 -------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/abi_versioning.rst    | 408 ++-----------
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 +++++----
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/baseband/acc/rte_acc100_pmd.c         |   1 +
 drivers/baseband/acc/version.map              |  10 -
 .../fpga_5gnr_fec/rte_fpga_5gnr_fec.c         |   1 +
 drivers/baseband/fpga_5gnr_fec/version.map    |  11 -
 drivers/baseband/fpga_lte_fec/fpga_lte_fec.c  |   1 +
 drivers/baseband/fpga_lte_fec/version.map     |  10 -
 drivers/bus/auxiliary/auxiliary_common.c      |   2 +
 drivers/bus/auxiliary/version.map             |   8 -
 drivers/bus/cdx/cdx.c                         |   4 +
 drivers/bus/cdx/cdx_vfio.c                    |   4 +
 drivers/bus/cdx/version.map                   |  14 -
 drivers/bus/dpaa/dpaa_bus.c                   | 104 ++++
 drivers/bus/dpaa/version.map                  | 109 ----
 drivers/bus/fslmc/fslmc_bus.c                 |   4 +
 drivers/bus/fslmc/fslmc_vfio.c                |  12 +
 drivers/bus/fslmc/mc/dpbp.c                   |   6 +
 drivers/bus/fslmc/mc/dpci.c                   |   3 +
 drivers/bus/fslmc/mc/dpcon.c                  |   6 +
 drivers/bus/fslmc/mc/dpdmai.c                 |   8 +
 drivers/bus/fslmc/mc/dpio.c                   |  13 +
 drivers/bus/fslmc/mc/dpmng.c                  |   2 +
 drivers/bus/fslmc/mc/mc_sys.c                 |   1 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpbp.c      |   3 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpci.c      |   2 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpio.c      |  11 +
 drivers/bus/fslmc/qbman/qbman_debug.c         |   2 +
 drivers/bus/fslmc/qbman/qbman_portal.c        |  41 ++
 drivers/bus/fslmc/version.map                 | 129 ----
 drivers/bus/ifpga/ifpga_bus.c                 |   3 +
 drivers/bus/ifpga/version.map                 |   9 -
 drivers/bus/pci/bsd/pci.c                     |  10 +
 drivers/bus/pci/linux/pci.c                   |  10 +
 drivers/bus/pci/pci_common.c                  |  10 +
 drivers/bus/pci/version.map                   |  43 --
 drivers/bus/pci/windows/pci.c                 |  10 +
 drivers/bus/platform/platform.c               |   2 +
 drivers/bus/platform/version.map              |  10 -
 drivers/bus/uacce/uacce.c                     |   9 +
 drivers/bus/uacce/version.map                 |  15 -
 drivers/bus/vdev/vdev.c                       |   6 +
 drivers/bus/vdev/version.map                  |  17 -
 drivers/bus/vmbus/linux/vmbus_bus.c           |   6 +
 drivers/bus/vmbus/version.map                 |  33 -
 drivers/bus/vmbus/vmbus_channel.c             |  13 +
 drivers/bus/vmbus/vmbus_common.c              |   3 +
 drivers/common/cnxk/cnxk_security.c           |  12 +
 drivers/common/cnxk/cnxk_utils.c              |   1 +
 drivers/common/cnxk/roc_platform.c            | 559 +++++++++++++++++
 drivers/common/cnxk/roc_se.h                  |   1 -
 drivers/common/cnxk/version.map               | 578 ------------------
 drivers/common/cpt/cpt_fpm_tables.c           |   2 +
 drivers/common/cpt/cpt_pmd_ops_helper.c       |   3 +
 drivers/common/cpt/version.map                |  11 -
 drivers/common/dpaax/caamflib.c               |   1 +
 drivers/common/dpaax/dpaa_of.c                |  12 +
 drivers/common/dpaax/dpaax_iova_table.c       |   6 +
 drivers/common/dpaax/version.map              |  25 -
 drivers/common/ionic/ionic_common_uio.c       |   4 +
 drivers/common/ionic/version.map              |  10 -
 .../common/mlx5/linux/mlx5_common_auxiliary.c |   1 +
 drivers/common/mlx5/linux/mlx5_common_os.c    |   9 +
 drivers/common/mlx5/linux/mlx5_common_verbs.c |   3 +
 drivers/common/mlx5/linux/mlx5_glue.c         |   1 +
 drivers/common/mlx5/linux/mlx5_nl.c           |  21 +
 drivers/common/mlx5/mlx5_common.c             |   9 +
 drivers/common/mlx5/mlx5_common_devx.c        |   9 +
 drivers/common/mlx5/mlx5_common_mp.c          |   8 +
 drivers/common/mlx5/mlx5_common_mr.c          |  11 +
 drivers/common/mlx5/mlx5_common_pci.c         |   2 +
 drivers/common/mlx5/mlx5_common_utils.c       |  11 +
 drivers/common/mlx5/mlx5_devx_cmds.c          |  51 ++
 drivers/common/mlx5/mlx5_malloc.c             |   4 +
 drivers/common/mlx5/version.map               | 174 ------
 drivers/common/mlx5/windows/mlx5_common_os.c  |   5 +
 drivers/common/mlx5/windows/mlx5_glue.c       |   3 +-
 drivers/common/mvep/mvep_common.c             |   2 +
 drivers/common/mvep/version.map               |   8 -
 drivers/common/nfp/nfp_common.c               |   7 +
 drivers/common/nfp/nfp_common_pci.c           |   1 +
 drivers/common/nfp/nfp_dev.c                  |   1 +
 drivers/common/nfp/version.map                |  16 -
 drivers/common/nitrox/nitrox_device.c         |   1 +
 drivers/common/nitrox/nitrox_logs.c           |   1 +
 drivers/common/nitrox/nitrox_qp.c             |   2 +
 drivers/common/nitrox/version.map             |  10 -
 drivers/common/octeontx/octeontx_mbox.c       |   6 +
 drivers/common/octeontx/version.map           |  12 -
 drivers/common/sfc_efx/sfc_efx.c              | 273 +++++++++
 drivers/common/sfc_efx/sfc_efx_mcdi.c         |   2 +
 drivers/common/sfc_efx/version.map            | 302 ---------
 drivers/crypto/cnxk/cn10k_cryptodev_ops.c     |   7 +
 drivers/crypto/cnxk/cn9k_cryptodev_ops.c      |   2 +
 drivers/crypto/cnxk/cnxk_cryptodev_ops.c      |   7 +
 drivers/crypto/cnxk/version.map               |  30 -
 drivers/crypto/dpaa2_sec/dpaa2_sec_dpseci.c   |   2 +
 drivers/crypto/dpaa2_sec/version.map          |   8 -
 drivers/crypto/dpaa_sec/dpaa_sec.c            |   2 +
 drivers/crypto/dpaa_sec/version.map           |   8 -
 drivers/crypto/octeontx/otx_cryptodev_ops.c   |   2 +
 drivers/crypto/octeontx/version.map           |  12 -
 .../scheduler/rte_cryptodev_scheduler.c       |  10 +
 drivers/crypto/scheduler/version.map          |  16 -
 drivers/dma/cnxk/cnxk_dmadev_fp.c             |   4 +
 drivers/dma/cnxk/version.map                  |  10 -
 drivers/event/cnxk/cnxk_worker.c              |   2 +
 drivers/event/cnxk/version.map                |  11 -
 drivers/event/dlb2/rte_pmd_dlb2.c             |   1 +
 drivers/event/dlb2/version.map                |  10 -
 drivers/mempool/cnxk/cn10k_hwpool_ops.c       |   3 +
 drivers/mempool/cnxk/version.map              |  12 -
 drivers/mempool/dpaa/dpaa_mempool.c           |   2 +
 drivers/mempool/dpaa/version.map              |   8 -
 drivers/mempool/dpaa2/dpaa2_hw_mempool.c      |   5 +
 drivers/mempool/dpaa2/version.map             |  16 -
 drivers/meson.build                           |  74 +--
 drivers/net/atlantic/rte_pmd_atlantic.c       |   6 +
 drivers/net/atlantic/version.map              |  15 -
 drivers/net/bnxt/rte_pmd_bnxt.c               |  16 +
 drivers/net/bnxt/version.map                  |  22 -
 drivers/net/bonding/rte_eth_bond_8023ad.c     |  12 +
 drivers/net/bonding/rte_eth_bond_api.c        |  15 +
 drivers/net/bonding/version.map               |  33 -
 drivers/net/cnxk/cnxk_ethdev.c                |   3 +
 drivers/net/cnxk/cnxk_ethdev_sec.c            |   9 +
 drivers/net/cnxk/version.map                  |  27 -
 drivers/net/dpaa/dpaa_ethdev.c                |   3 +
 drivers/net/dpaa/version.map                  |  14 -
 drivers/net/dpaa2/dpaa2_ethdev.c              |  11 +
 drivers/net/dpaa2/dpaa2_mux.c                 |   3 +
 drivers/net/dpaa2/dpaa2_rxtx.c                |   1 +
 drivers/net/dpaa2/version.map                 |  35 --
 drivers/net/intel/i40e/rte_pmd_i40e.c         |  39 ++
 drivers/net/intel/i40e/version.map            |  55 --
 drivers/net/intel/iavf/iavf_ethdev.c          |   9 +
 drivers/net/intel/iavf/iavf_rxtx.c            |   8 +
 drivers/net/intel/iavf/version.map            |  33 -
 drivers/net/intel/ice/ice_diagnose.c          |   3 +
 drivers/net/intel/ice/version.map             |  16 -
 drivers/net/intel/idpf/idpf_common_device.c   |  10 +
 drivers/net/intel/idpf/idpf_common_rxtx.c     |  24 +
 .../net/intel/idpf/idpf_common_rxtx_avx2.c    |   2 +
 .../net/intel/idpf/idpf_common_rxtx_avx512.c  |   5 +
 drivers/net/intel/idpf/idpf_common_virtchnl.c |  29 +
 drivers/net/intel/idpf/version.map            |  80 ---
 drivers/net/intel/ipn3ke/ipn3ke_ethdev.c      |   1 +
 drivers/net/intel/ipn3ke/version.map          |   9 -
 drivers/net/intel/ixgbe/rte_pmd_ixgbe.c       |  37 ++
 drivers/net/intel/ixgbe/version.map           |  49 --
 drivers/net/mlx5/mlx5.c                       |   1 +
 drivers/net/mlx5/mlx5_flow.c                  |   4 +
 drivers/net/mlx5/mlx5_rx.c                    |   2 +
 drivers/net/mlx5/mlx5_rxq.c                   |   2 +
 drivers/net/mlx5/mlx5_tx.c                    |   1 +
 drivers/net/mlx5/mlx5_txq.c                   |   3 +
 drivers/net/mlx5/version.map                  |  28 -
 drivers/net/octeontx/octeontx_ethdev.c        |   1 +
 drivers/net/octeontx/version.map              |   7 -
 drivers/net/ring/rte_eth_ring.c               |   2 +
 drivers/net/ring/version.map                  |   8 -
 drivers/net/softnic/rte_eth_softnic.c         |   1 +
 drivers/net/softnic/rte_eth_softnic_thread.c  |   1 +
 drivers/net/softnic/version.map               |   8 -
 drivers/net/vhost/rte_eth_vhost.c             |   2 +
 drivers/net/vhost/version.map                 |   8 -
 drivers/power/kvm_vm/guest_channel.c          |   2 +
 drivers/power/kvm_vm/version.map              |   8 -
 drivers/raw/cnxk_rvu_lf/cnxk_rvu_lf.c         |  10 +
 drivers/raw/cnxk_rvu_lf/version.map           |  16 -
 drivers/raw/ifpga/rte_pmd_ifpga.c             |  11 +
 drivers/raw/ifpga/version.map                 |  17 -
 drivers/version.map                           |   3 -
 lib/acl/acl_bld.c                             |   1 +
 lib/acl/acl_run_scalar.c                      |   1 +
 lib/acl/rte_acl.c                             |  11 +
 lib/acl/version.map                           |  19 -
 lib/argparse/rte_argparse.c                   |   2 +
 lib/argparse/version.map                      |   9 -
 lib/bbdev/bbdev_trace_points.c                |   2 +
 lib/bbdev/rte_bbdev.c                         |  31 +
 lib/bbdev/version.map                         |  47 --
 lib/bitratestats/rte_bitrate.c                |   4 +
 lib/bitratestats/version.map                  |  10 -
 lib/bpf/bpf.c                                 |   2 +
 lib/bpf/bpf_convert.c                         |   1 +
 lib/bpf/bpf_dump.c                            |   1 +
 lib/bpf/bpf_exec.c                            |   2 +
 lib/bpf/bpf_load.c                            |   1 +
 lib/bpf/bpf_load_elf.c                        |   1 +
 lib/bpf/bpf_pkt.c                             |   4 +
 lib/bpf/bpf_stub.c                            |   2 +
 lib/bpf/version.map                           |  18 -
 lib/cfgfile/rte_cfgfile.c                     |  17 +
 lib/cfgfile/version.map                       |  23 -
 lib/cmdline/cmdline.c                         |   9 +
 lib/cmdline/cmdline_cirbuf.c                  |  19 +
 lib/cmdline/cmdline_parse.c                   |   4 +
 lib/cmdline/cmdline_parse_bool.c              |   1 +
 lib/cmdline/cmdline_parse_etheraddr.c         |   3 +
 lib/cmdline/cmdline_parse_ipaddr.c            |   3 +
 lib/cmdline/cmdline_parse_num.c               |   3 +
 lib/cmdline/cmdline_parse_portlist.c          |   3 +
 lib/cmdline/cmdline_parse_string.c            |   5 +
 lib/cmdline/cmdline_rdline.c                  |  15 +
 lib/cmdline/cmdline_socket.c                  |   3 +
 lib/cmdline/cmdline_vt100.c                   |   2 +
 lib/cmdline/version.map                       |  82 ---
 lib/compressdev/rte_comp.c                    |   6 +
 lib/compressdev/rte_compressdev.c             |  25 +
 lib/compressdev/rte_compressdev_pmd.c         |   3 +
 lib/compressdev/version.map                   |  40 --
 lib/cryptodev/cryptodev_pmd.c                 |   7 +
 lib/cryptodev/cryptodev_trace_points.c        |   3 +
 lib/cryptodev/rte_cryptodev.c                 |  83 +++
 lib/cryptodev/version.map                     | 114 ----
 lib/dispatcher/rte_dispatcher.c               |  13 +
 lib/dispatcher/version.map                    |  20 -
 lib/distributor/rte_distributor.c             |   9 +
 lib/distributor/version.map                   |  15 -
 lib/dmadev/rte_dmadev.c                       |  19 +
 lib/dmadev/rte_dmadev_trace_points.c          |   7 +
 lib/dmadev/version.map                        |  47 --
 lib/eal/arm/rte_cpuflags.c                    |   3 +
 lib/eal/arm/rte_hypervisor.c                  |   1 +
 lib/eal/arm/rte_power_intrinsics.c            |   4 +
 lib/eal/common/eal_common_bus.c               |  10 +
 lib/eal/common/eal_common_class.c             |   4 +
 lib/eal/common/eal_common_config.c            |   7 +
 lib/eal/common/eal_common_cpuflags.c          |   1 +
 lib/eal/common/eal_common_debug.c             |   2 +
 lib/eal/common/eal_common_dev.c               |  19 +
 lib/eal/common/eal_common_devargs.c           |   9 +
 lib/eal/common/eal_common_errno.c             |   2 +
 lib/eal/common/eal_common_fbarray.c           |  26 +
 lib/eal/common/eal_common_hexdump.c           |   2 +
 lib/eal/common/eal_common_hypervisor.c        |   1 +
 lib/eal/common/eal_common_interrupts.c        |  27 +
 lib/eal/common/eal_common_launch.c            |   5 +
 lib/eal/common/eal_common_lcore.c             |  17 +
 lib/eal/common/eal_common_lcore_var.c         |   1 +
 lib/eal/common/eal_common_mcfg.c              |  20 +
 lib/eal/common/eal_common_memory.c            |  29 +
 lib/eal/common/eal_common_memzone.c           |   9 +
 lib/eal/common/eal_common_options.c           |   4 +
 lib/eal/common/eal_common_proc.c              |   8 +
 lib/eal/common/eal_common_string_fns.c        |   3 +
 lib/eal/common/eal_common_tailqs.c            |   3 +
 lib/eal/common/eal_common_thread.c            |  14 +
 lib/eal/common/eal_common_timer.c             |   4 +
 lib/eal/common/eal_common_trace.c             |  15 +
 lib/eal/common/eal_common_trace_ctf.c         |   1 +
 lib/eal/common/eal_common_trace_points.c      |  18 +
 lib/eal/common/eal_common_trace_utils.c       |   1 +
 lib/eal/common/eal_common_uuid.c              |   4 +
 lib/eal/common/rte_bitset.c                   |   1 +
 lib/eal/common/rte_keepalive.c                |   6 +
 lib/eal/common/rte_malloc.c                   |  22 +
 lib/eal/common/rte_random.c                   |   4 +
 lib/eal/common/rte_reciprocal.c               |   2 +
 lib/eal/common/rte_service.c                  |  31 +
 lib/eal/common/rte_version.c                  |   7 +
 lib/eal/freebsd/eal.c                         |  22 +
 lib/eal/freebsd/eal_alarm.c                   |   2 +
 lib/eal/freebsd/eal_dev.c                     |   4 +
 lib/eal/freebsd/eal_interrupts.c              |  19 +
 lib/eal/freebsd/eal_memory.c                  |   3 +
 lib/eal/freebsd/eal_thread.c                  |   2 +
 lib/eal/freebsd/eal_timer.c                   |   1 +
 lib/eal/include/rte_function_versioning.h     |  96 ++-
 lib/eal/linux/eal.c                           |   7 +
 lib/eal/linux/eal_alarm.c                     |   2 +
 lib/eal/linux/eal_dev.c                       |   4 +
 lib/eal/linux/eal_interrupts.c                |  19 +
 lib/eal/linux/eal_memory.c                    |   3 +
 lib/eal/linux/eal_thread.c                    |   2 +
 lib/eal/linux/eal_timer.c                     |   4 +
 lib/eal/linux/eal_vfio.c                      |  16 +
 lib/eal/loongarch/rte_cpuflags.c              |   3 +
 lib/eal/loongarch/rte_hypervisor.c            |   1 +
 lib/eal/loongarch/rte_power_intrinsics.c      |   4 +
 lib/eal/ppc/rte_cpuflags.c                    |   3 +
 lib/eal/ppc/rte_hypervisor.c                  |   1 +
 lib/eal/ppc/rte_power_intrinsics.c            |   4 +
 lib/eal/riscv/rte_cpuflags.c                  |   3 +
 lib/eal/riscv/rte_hypervisor.c                |   1 +
 lib/eal/riscv/rte_power_intrinsics.c          |   4 +
 lib/eal/unix/eal_debug.c                      |   2 +
 lib/eal/unix/eal_filesystem.c                 |   1 +
 lib/eal/unix/eal_firmware.c                   |   1 +
 lib/eal/unix/eal_unix_memory.c                |   4 +
 lib/eal/unix/eal_unix_timer.c                 |   1 +
 lib/eal/unix/rte_thread.c                     |  13 +
 lib/eal/version.map                           | 451 --------------
 lib/eal/windows/eal.c                         |  11 +
 lib/eal/windows/eal_alarm.c                   |   2 +
 lib/eal/windows/eal_debug.c                   |   1 +
 lib/eal/windows/eal_dev.c                     |   4 +
 lib/eal/windows/eal_interrupts.c              |  19 +
 lib/eal/windows/eal_memory.c                  |   7 +
 lib/eal/windows/eal_mp.c                      |   6 +
 lib/eal/windows/eal_thread.c                  |   1 +
 lib/eal/windows/eal_timer.c                   |   1 +
 lib/eal/windows/rte_thread.c                  |  14 +
 lib/eal/x86/rte_cpuflags.c                    |   3 +
 lib/eal/x86/rte_hypervisor.c                  |   1 +
 lib/eal/x86/rte_power_intrinsics.c            |   4 +
 lib/eal/x86/rte_spinlock.c                    |   1 +
 lib/efd/rte_efd.c                             |   7 +
 lib/efd/version.map                           |  13 -
 lib/ethdev/ethdev_driver.c                    |  24 +
 lib/ethdev/ethdev_linux_ethtool.c             |   3 +
 lib/ethdev/ethdev_private.c                   |   2 +
 lib/ethdev/ethdev_trace_points.c              |   6 +
 lib/ethdev/rte_ethdev.c                       | 168 +++++
 lib/ethdev/rte_ethdev_cman.c                  |   4 +
 lib/ethdev/rte_flow.c                         |  64 ++
 lib/ethdev/rte_mtr.c                          |  21 +
 lib/ethdev/rte_tm.c                           |  31 +
 lib/ethdev/version.map                        | 378 ------------
 lib/eventdev/eventdev_private.c               |   2 +
 lib/eventdev/eventdev_trace_points.c          |  11 +
 lib/eventdev/rte_event_crypto_adapter.c       |  15 +
 lib/eventdev/rte_event_dma_adapter.c          |  15 +
 lib/eventdev/rte_event_eth_rx_adapter.c       |  23 +
 lib/eventdev/rte_event_eth_tx_adapter.c       |  17 +
 lib/eventdev/rte_event_ring.c                 |   4 +
 lib/eventdev/rte_event_timer_adapter.c        |  11 +
 lib/eventdev/rte_eventdev.c                   |  46 ++
 lib/eventdev/version.map                      | 179 ------
 lib/fib/rte_fib.c                             |  10 +
 lib/fib/rte_fib6.c                            |   9 +
 lib/fib/version.map                           |  31 -
 lib/gpudev/gpudev.c                           |  32 +
 lib/gpudev/version.map                        |  44 --
 lib/graph/graph.c                             |  16 +
 lib/graph/graph_debug.c                       |   1 +
 lib/graph/graph_stats.c                       |   4 +
 lib/graph/node.c                              |  11 +
 lib/graph/rte_graph_model_mcore_dispatch.c    |   3 +
 lib/graph/rte_graph_worker.c                  |   3 +
 lib/graph/version.map                         |  61 --
 lib/gro/rte_gro.c                             |   6 +
 lib/gro/version.map                           |  12 -
 lib/gso/rte_gso.c                             |   1 +
 lib/gso/version.map                           |   7 -
 lib/hash/rte_cuckoo_hash.c                    |  27 +
 lib/hash/rte_fbk_hash.c                       |   3 +
 lib/hash/rte_hash_crc.c                       |   2 +
 lib/hash/rte_thash.c                          |  12 +
 lib/hash/rte_thash_gf2_poly_math.c            |   1 +
 lib/hash/rte_thash_gfni.c                     |   2 +
 lib/hash/version.map                          |  66 --
 lib/ip_frag/rte_ip_frag_common.c              |   5 +
 lib/ip_frag/rte_ipv4_fragmentation.c          |   2 +
 lib/ip_frag/rte_ipv4_reassembly.c             |   1 +
 lib/ip_frag/rte_ipv6_fragmentation.c          |   1 +
 lib/ip_frag/rte_ipv6_reassembly.c             |   1 +
 lib/ip_frag/version.map                       |  16 -
 lib/ipsec/ipsec_sad.c                         |   6 +
 lib/ipsec/ipsec_telemetry.c                   |   2 +
 lib/ipsec/sa.c                                |   4 +
 lib/ipsec/ses.c                               |   1 +
 lib/ipsec/version.map                         |  23 -
 lib/jobstats/rte_jobstats.c                   |  14 +
 lib/jobstats/version.map                      |  20 -
 lib/kvargs/rte_kvargs.c                       |   8 +
 lib/kvargs/version.map                        |  14 -
 lib/latencystats/rte_latencystats.c           |   5 +
 lib/latencystats/version.map                  |  11 -
 lib/log/log.c                                 |  22 +
 lib/log/log_color.c                           |   1 +
 lib/log/log_internal.h                        |   3 -
 lib/log/log_syslog.c                          |   1 +
 lib/log/log_timestamp.c                       |   1 +
 lib/log/version.map                           |  37 --
 lib/lpm/rte_lpm.c                             |   8 +
 lib/lpm/rte_lpm6.c                            |  10 +
 lib/lpm/version.map                           |  24 -
 lib/mbuf/rte_mbuf.c                           |  17 +
 lib/mbuf/rte_mbuf_dyn.c                       |   9 +
 lib/mbuf/rte_mbuf_pool_ops.c                  |   5 +
 lib/mbuf/rte_mbuf_ptype.c                     |   8 +
 lib/mbuf/version.map                          |  45 --
 lib/member/rte_member.c                       |  13 +
 lib/member/version.map                        |  19 -
 lib/mempool/mempool_trace_points.c            |  10 +
 lib/mempool/rte_mempool.c                     |  27 +
 lib/mempool/rte_mempool_ops.c                 |   4 +
 lib/mempool/rte_mempool_ops_default.c         |   4 +
 lib/mempool/version.map                       |  65 --
 lib/meson.build                               |  62 +-
 lib/meter/rte_meter.c                         |   6 +
 lib/meter/version.map                         |  12 -
 lib/metrics/rte_metrics.c                     |   8 +
 lib/metrics/rte_metrics_telemetry.c           |  11 +
 lib/metrics/version.map                       |  26 -
 lib/mldev/mldev_utils.c                       |   2 +
 lib/mldev/mldev_utils_neon.c                  |  18 +
 lib/mldev/mldev_utils_neon_bfloat16.c         |   2 +
 lib/mldev/mldev_utils_scalar.c                |  18 +
 lib/mldev/mldev_utils_scalar_bfloat16.c       |   2 +
 lib/mldev/rte_mldev.c                         |  37 ++
 lib/mldev/rte_mldev_pmd.c                     |   2 +
 lib/mldev/version.map                         |  74 ---
 lib/net/net_crc.h                             |  15 -
 lib/net/rte_arp.c                             |   1 +
 lib/net/rte_ether.c                           |   3 +
 lib/net/rte_net.c                             |   2 +
 lib/net/rte_net_crc.c                         |  29 +-
 lib/net/version.map                           |  23 -
 lib/node/ethdev_ctrl.c                        |   2 +
 lib/node/ip4_lookup.c                         |   1 +
 lib/node/ip4_reassembly.c                     |   1 +
 lib/node/ip4_rewrite.c                        |   1 +
 lib/node/ip6_lookup.c                         |   1 +
 lib/node/ip6_rewrite.c                        |   1 +
 lib/node/udp4_input.c                         |   2 +
 lib/node/version.map                          |  25 -
 lib/pcapng/rte_pcapng.c                       |   7 +
 lib/pcapng/version.map                        |  13 -
 lib/pci/rte_pci.c                             |   3 +
 lib/pci/version.map                           |   9 -
 lib/pdcp/rte_pdcp.c                           |   5 +
 lib/pdcp/version.map                          |  16 -
 lib/pdump/rte_pdump.c                         |   9 +
 lib/pdump/version.map                         |  15 -
 lib/pipeline/rte_pipeline.c                   |  23 +
 lib/pipeline/rte_port_in_action.c             |   8 +
 lib/pipeline/rte_swx_ctl.c                    |  17 +
 lib/pipeline/rte_swx_ipsec.c                  |   7 +
 lib/pipeline/rte_swx_pipeline.c               |  73 +++
 lib/pipeline/rte_table_action.c               |  16 +
 lib/pipeline/version.map                      | 172 ------
 lib/port/rte_port_ethdev.c                    |   3 +
 lib/port/rte_port_eventdev.c                  |   3 +
 lib/port/rte_port_fd.c                        |   3 +
 lib/port/rte_port_frag.c                      |   2 +
 lib/port/rte_port_ras.c                       |   2 +
 lib/port/rte_port_ring.c                      |   6 +
 lib/port/rte_port_sched.c                     |   2 +
 lib/port/rte_port_source_sink.c               |   2 +
 lib/port/rte_port_sym_crypto.c                |   3 +
 lib/port/rte_swx_port_ethdev.c                |   2 +
 lib/port/rte_swx_port_fd.c                    |   2 +
 lib/port/rte_swx_port_ring.c                  |   2 +
 lib/port/rte_swx_port_source_sink.c           |   3 +
 lib/port/version.map                          |  50 --
 lib/power/power_common.c                      |   8 +
 lib/power/rte_power_cpufreq.c                 |  18 +
 lib/power/rte_power_pmd_mgmt.c                |  10 +
 lib/power/rte_power_qos.c                     |   2 +
 lib/power/rte_power_uncore.c                  |  14 +
 lib/power/version.map                         |  71 ---
 lib/rawdev/rte_rawdev.c                       |  30 +
 lib/rawdev/version.map                        |  36 --
 lib/rcu/rte_rcu_qsbr.c                        |  11 +
 lib/rcu/version.map                           |  17 -
 lib/regexdev/rte_regexdev.c                   |  26 +
 lib/regexdev/version.map                      |  40 --
 lib/reorder/rte_reorder.c                     |  11 +
 lib/reorder/version.map                       |  27 -
 lib/rib/rte_rib.c                             |  14 +
 lib/rib/rte_rib6.c                            |  14 +
 lib/rib/version.map                           |  34 --
 lib/ring/rte_ring.c                           |  11 +
 lib/ring/rte_soring.c                         |   3 +
 lib/ring/soring.c                             |  16 +
 lib/ring/version.map                          |  42 --
 lib/sched/rte_approx.c                        |   1 +
 lib/sched/rte_pie.c                           |   2 +
 lib/sched/rte_red.c                           |   6 +
 lib/sched/rte_sched.c                         |  15 +
 lib/sched/version.map                         |  30 -
 lib/security/rte_security.c                   |  20 +
 lib/security/version.map                      |  37 --
 lib/stack/rte_stack.c                         |   3 +
 lib/stack/version.map                         |   9 -
 lib/table/rte_swx_table_em.c                  |   2 +
 lib/table/rte_swx_table_learner.c             |  10 +
 lib/table/rte_swx_table_selector.c            |   6 +
 lib/table/rte_swx_table_wm.c                  |   1 +
 lib/table/rte_table_acl.c                     |   1 +
 lib/table/rte_table_array.c                   |   1 +
 lib/table/rte_table_hash_cuckoo.c             |   1 +
 lib/table/rte_table_hash_ext.c                |   1 +
 lib/table/rte_table_hash_key16.c              |   2 +
 lib/table/rte_table_hash_key32.c              |   2 +
 lib/table/rte_table_hash_key8.c               |   2 +
 lib/table/rte_table_hash_lru.c                |   1 +
 lib/table/rte_table_lpm.c                     |   1 +
 lib/table/rte_table_lpm_ipv6.c                |   1 +
 lib/table/rte_table_stub.c                    |   1 +
 lib/table/version.map                         |  53 --
 lib/telemetry/telemetry.c                     |   3 +
 lib/telemetry/telemetry_data.c                |  17 +
 lib/telemetry/telemetry_legacy.c              |   1 +
 lib/telemetry/version.map                     |  40 --
 lib/timer/rte_timer.c                         |  18 +
 lib/timer/version.map                         |  24 -
 lib/vhost/socket.c                            |  16 +
 lib/vhost/vdpa.c                              |  11 +
 lib/vhost/version.map                         | 111 ----
 lib/vhost/vhost.c                             |  41 ++
 lib/vhost/vhost_crypto.c                      |   6 +
 lib/vhost/vhost_user.c                        |   2 +
 lib/vhost/virtio_net.c                        |   7 +
 526 files changed, 4661 insertions(+), 6528 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 delete mode 100644 buildtools/map_to_win.py
 create mode 100644 config/rte_export.h
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py
 delete mode 100644 drivers/baseband/acc/version.map
 delete mode 100644 drivers/baseband/fpga_5gnr_fec/version.map
 delete mode 100644 drivers/baseband/fpga_lte_fec/version.map
 delete mode 100644 drivers/bus/auxiliary/version.map
 delete mode 100644 drivers/bus/cdx/version.map
 delete mode 100644 drivers/bus/dpaa/version.map
 delete mode 100644 drivers/bus/fslmc/version.map
 delete mode 100644 drivers/bus/ifpga/version.map
 delete mode 100644 drivers/bus/pci/version.map
 delete mode 100644 drivers/bus/platform/version.map
 delete mode 100644 drivers/bus/uacce/version.map
 delete mode 100644 drivers/bus/vdev/version.map
 delete mode 100644 drivers/bus/vmbus/version.map
 delete mode 100644 drivers/common/cnxk/version.map
 delete mode 100644 drivers/common/cpt/version.map
 delete mode 100644 drivers/common/dpaax/version.map
 delete mode 100644 drivers/common/ionic/version.map
 delete mode 100644 drivers/common/mlx5/version.map
 delete mode 100644 drivers/common/mvep/version.map
 delete mode 100644 drivers/common/nfp/version.map
 delete mode 100644 drivers/common/nitrox/version.map
 delete mode 100644 drivers/common/octeontx/version.map
 delete mode 100644 drivers/common/sfc_efx/version.map
 delete mode 100644 drivers/crypto/cnxk/version.map
 delete mode 100644 drivers/crypto/dpaa2_sec/version.map
 delete mode 100644 drivers/crypto/dpaa_sec/version.map
 delete mode 100644 drivers/crypto/octeontx/version.map
 delete mode 100644 drivers/crypto/scheduler/version.map
 delete mode 100644 drivers/dma/cnxk/version.map
 delete mode 100644 drivers/event/cnxk/version.map
 delete mode 100644 drivers/event/dlb2/version.map
 delete mode 100644 drivers/mempool/cnxk/version.map
 delete mode 100644 drivers/mempool/dpaa/version.map
 delete mode 100644 drivers/mempool/dpaa2/version.map
 delete mode 100644 drivers/net/atlantic/version.map
 delete mode 100644 drivers/net/bnxt/version.map
 delete mode 100644 drivers/net/bonding/version.map
 delete mode 100644 drivers/net/cnxk/version.map
 delete mode 100644 drivers/net/dpaa/version.map
 delete mode 100644 drivers/net/dpaa2/version.map
 delete mode 100644 drivers/net/intel/i40e/version.map
 delete mode 100644 drivers/net/intel/iavf/version.map
 delete mode 100644 drivers/net/intel/ice/version.map
 delete mode 100644 drivers/net/intel/idpf/version.map
 delete mode 100644 drivers/net/intel/ipn3ke/version.map
 delete mode 100644 drivers/net/intel/ixgbe/version.map
 delete mode 100644 drivers/net/mlx5/version.map
 delete mode 100644 drivers/net/octeontx/version.map
 delete mode 100644 drivers/net/ring/version.map
 delete mode 100644 drivers/net/softnic/version.map
 delete mode 100644 drivers/net/vhost/version.map
 delete mode 100644 drivers/power/kvm_vm/version.map
 delete mode 100644 drivers/raw/cnxk_rvu_lf/version.map
 delete mode 100644 drivers/raw/ifpga/version.map
 delete mode 100644 drivers/version.map
 delete mode 100644 lib/acl/version.map
 delete mode 100644 lib/argparse/version.map
 delete mode 100644 lib/bbdev/version.map
 delete mode 100644 lib/bitratestats/version.map
 delete mode 100644 lib/bpf/version.map
 delete mode 100644 lib/cfgfile/version.map
 delete mode 100644 lib/cmdline/version.map
 delete mode 100644 lib/compressdev/version.map
 delete mode 100644 lib/cryptodev/version.map
 delete mode 100644 lib/dispatcher/version.map
 delete mode 100644 lib/distributor/version.map
 delete mode 100644 lib/dmadev/version.map
 delete mode 100644 lib/eal/version.map
 delete mode 100644 lib/efd/version.map
 delete mode 100644 lib/ethdev/version.map
 delete mode 100644 lib/eventdev/version.map
 delete mode 100644 lib/fib/version.map
 delete mode 100644 lib/gpudev/version.map
 delete mode 100644 lib/graph/version.map
 delete mode 100644 lib/gro/version.map
 delete mode 100644 lib/gso/version.map
 delete mode 100644 lib/hash/version.map
 delete mode 100644 lib/ip_frag/version.map
 delete mode 100644 lib/ipsec/version.map
 delete mode 100644 lib/jobstats/version.map
 delete mode 100644 lib/kvargs/version.map
 delete mode 100644 lib/latencystats/version.map
 delete mode 100644 lib/log/version.map
 delete mode 100644 lib/lpm/version.map
 delete mode 100644 lib/mbuf/version.map
 delete mode 100644 lib/member/version.map
 delete mode 100644 lib/mempool/version.map
 delete mode 100644 lib/meter/version.map
 delete mode 100644 lib/metrics/version.map
 delete mode 100644 lib/mldev/version.map
 delete mode 100644 lib/net/version.map
 delete mode 100644 lib/node/version.map
 delete mode 100644 lib/pcapng/version.map
 delete mode 100644 lib/pci/version.map
 delete mode 100644 lib/pdcp/version.map
 delete mode 100644 lib/pdump/version.map
 delete mode 100644 lib/pipeline/version.map
 delete mode 100644 lib/port/version.map
 delete mode 100644 lib/power/version.map
 delete mode 100644 lib/rawdev/version.map
 delete mode 100644 lib/rcu/version.map
 delete mode 100644 lib/regexdev/version.map
 delete mode 100644 lib/reorder/version.map
 delete mode 100644 lib/rib/version.map
 delete mode 100644 lib/ring/version.map
 delete mode 100644 lib/sched/version.map
 delete mode 100644 lib/security/version.map
 delete mode 100644 lib/stack/version.map
 delete mode 100644 lib/table/version.map
 delete mode 100644 lib/telemetry/version.map
 delete mode 100644 lib/timer/version.map
 delete mode 100644 lib/vhost/version.map

-- 
2.48.1


^ permalink raw reply	[relevance 3%]

* [RFC v4 3/8] eal: rework function versioning macros
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
@ 2025-03-17 15:42 16%   ` David Marchand
  2025-03-17 15:43 18%   ` [RFC v4 5/8] build: generate symbol maps David Marchand
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-17 15:42 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
exporting and versioning symbols that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a symbol is just a call to a single macro:
- RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
  keeping an old implementation code under a versioned function (resp.
  experimental function),
- RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

Update lib/net accordingly.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v3:
- fixed documentation and simplified examples,

Changes since RFC v1:
- renamed and prefixed macros,
- reindented in prevision of second patch,

---
 doc/guides/contributing/abi_versioning.rst | 189 +++++----------------
 lib/eal/include/rte_function_versioning.h  |  96 ++++-------
 lib/net/net_crc.h                          |  15 --
 lib/net/rte_net_crc.c                      |  28 +--
 4 files changed, 88 insertions(+), 240 deletions(-)

diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..21f8f8cd14 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -138,27 +138,20 @@ macros are used in conjunction with the ``version.map`` file for
 a given library to allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
-The macros exported are:
+The macros are:
 
-* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
-  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
+* ``RTE_VERSION_SYMBOL(ver, type, name, args)``: Creates a symbol version table
+  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
+  ``<name>_v<ver>``.
 
-* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
-  the linker to bind references to symbol ``b`` to the internal symbol
-  ``be``.
+* ``RTE_DEFAULT_SYMBOL(ver, type, name, args)``: Creates a symbol version entry
+  instructing the linker to bind references to symbol ``<name>`` to the internal
+  symbol ``<name>_v<ver>``.
 
-* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
-  fully qualified function ``p``, so that if a symbol becomes versioned, it
-  can still be mapped back to the public symbol name.
-
-* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
-  ``be`` to signal that it is being used as an implementation of a particular
-  version of symbol ``b``.
-
-* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
-  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
-  The macro is used when a symbol matures to become part of the stable ABI, to
-  provide an alias to experimental until the next major ABI version.
+* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
+  but for experimental API symbols. The macro is used when a symbol matures
+  to become part of the stable ABI, to provide an alias to experimental
+  until the next major ABI version.
 
 .. _example_abi_macro_usage:
 
@@ -176,8 +169,8 @@ Assume we have a function as follows
   * Create an acl context object for apps to
   * manipulate
   */
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param)
+ int
+ rte_acl_create(struct rte_acl_param *param)
  {
         ...
  }
@@ -194,8 +187,8 @@ private, is safe), but it also requires modifying the code as follows
   * Create an acl context object for apps to
   * manipulate
   */
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param, int debug)
+ int
+ rte_acl_create(struct rte_acl_param *param, int debug)
  {
         ...
  }
@@ -277,86 +270,49 @@ list of exported symbols when DPDK is compiled as a shared library.
 
 Next, we need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
+the function return type, the function name and its arguments.
 
 .. code-block:: c
 
- -struct rte_acl_ctx *
- -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ -int
+ -rte_acl_create(struct rte_acl_param *param)
+ +RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
-
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ }
 
 Remembering to also add the rte_function_versioning.h header to the requisite c
 file where these changes are being made. The macro instructs the linker to
 create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+in older builds, but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
    {
-        struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
+        int ret = rte_acl_create_v21(param);
 
-        ctx->debug = debug;
+        if (debug) {
+        ...
+        }
 
-        return ctx;
+        return ret;
    }
 
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
-
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it, on the next shared library rebuild, there will be two versions of
 rte_acl_create, an old DPDK_21 version, used by previously built applications,
@@ -365,43 +321,10 @@ and a new DPDK_22 version, used by future built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -444,8 +367,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     * manipulate
     */
    __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
    ...
    }
@@ -478,8 +401,8 @@ When we promote the symbol to the stable ABI, we simply strip the
     * Create an acl context object for apps to
     * manipulate
     */
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
           ...
    }
@@ -519,26 +442,15 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param))
    {
    ...
    }
 
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
-
-   struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_acl_create, (struct rte_acl_param *param))
    {
       return rte_acl_create(param);
    }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
 In the map file, we map the symbol to both the ``EXPERIMENTAL``
 and ``DPDK_22`` version nodes.
@@ -564,13 +476,6 @@ and ``DPDK_22`` version nodes.
         rte_acl_create;
    };
 
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -616,10 +521,10 @@ Next remove the corresponding versioned export.
 
 .. code-block:: c
 
- -VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
 
 
-Note that the internal function definition could also be removed, but its used
+Note that the internal function definition must also be removed, but it is used
 in our example by the newer version ``v22``, so we leave it in place and declare
 it as static. This is a coding style choice.
 
@@ -663,16 +568,16 @@ In the case of our map above, it would transform to look as follows
         local: *;
  };
 
-Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Then any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
 .. code-block:: c
 
- -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21);
- +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+ -RTE_DEFAULT_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param, int debug))
+ +RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
 
-Lastly, any VERSION_SYMBOL macros that point to the old version nodes
+Lastly, any RTE_VERSION_SYMBOL macros that point to the old version nodes
 should be removed, taking care to preserve any code that is shared
 with the new version node.
 
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
index eb6dd2bc17..0020ce4885 100644
--- a/lib/eal/include/rte_function_versioning.h
+++ b/lib/eal/include/rte_function_versioning.h
@@ -11,8 +11,6 @@
 #error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
 #endif
 
-#ifdef RTE_BUILD_SHARED_LIB
-
 /*
  * Provides backwards compatibility when updating exported functions.
  * When a symbol is exported from a library to provide an API, it also provides a
@@ -20,80 +18,54 @@
  * arguments, etc.  On occasion that function may need to change to accommodate
  * new functionality, behavior, etc.  When that occurs, it is desirable to
  * allow for backwards compatibility for a time with older binaries that are
- * dynamically linked to the dpdk.  To support that, the __vsym and
- * VERSION_SYMBOL macros are created.  They, in conjunction with the
- * version.map file for a given library allow for multiple versions of
- * a symbol to exist in a shared library so that older binaries need not be
- * immediately recompiled.
- *
- * Refer to the guidelines document in the docs subdirectory for details on the
- * use of these macros
+ * dynamically linked to the dpdk.
  */
 
-/*
- * Macro Parameters:
- * b - function base name
- * e - function version extension, to be concatenated with base name
- * n - function symbol version string to be applied
- * f - function prototype
- * p - full function symbol name
- */
+#ifdef RTE_BUILD_SHARED_LIB
 
 /*
- * VERSION_SYMBOL
- * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
- * function name <b><e>
+ * RTE_VERSION_SYMBOL
+ * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
+ * function name <name>_v<ver>.
  */
-#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
+#define RTE_VERSION_SYMBOL(ver, type, name, args) \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
 
 /*
- * VERSION_SYMBOL_EXPERIMENTAL
- * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
- * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
- * to provide an alias to experimental for some time.
+ * RTE_VERSION_EXPERIMENTAL_SYMBOL
+ * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
+ * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
  */
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
+__rte_used type name ## _exp args; \
+type name ## _exp args
 
 /*
- * BIND_DEFAULT_SYMBOL
+ * RTE_DEFAULT_SYMBOL
  * Creates a symbol version entry instructing the linker to bind references to
- * symbol <b> to the internal symbol <b><e>
+ * symbol <name> to the internal symbol <name>_v<ver>.
  */
-#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
 
-/*
- * __vsym
- * Annotation to be used in declaration of the internal symbol <b><e> to signal
- * that it is being used as an implementation of a particular version of symbol
- * <b>.
- */
-#define __vsym __rte_used
+#else /* !RTE_BUILD_SHARED_LIB */
 
-/*
- * MAP_STATIC_SYMBOL
- * If a function has been bifurcated into multiple versions, none of which
- * are defined as the exported symbol name in the map file, this macro can be
- * used to alias a specific version of the symbol to its exported name.  For
- * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
- * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
- * building a shared library, this macro can be used to map either foo_v1 or
- * foo_v2 to the symbol foo when building a static library, e.g.:
- * MAP_STATIC_SYMBOL(void foo(), foo_v2);
- */
-#define MAP_STATIC_SYMBOL(f, p)
+#define RTE_VERSION_SYMBOL(ver, type, name, args) \
+type name ## _v ## ver args; \
+type name ## _v ## ver args
 
-#else
-/*
- * No symbol versioning in use
- */
-#define VERSION_SYMBOL(b, e, n)
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
-#define __vsym
-#define BIND_DEFAULT_SYMBOL(b, e, n)
-#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
-/*
- * RTE_BUILD_SHARED_LIB=n
- */
-#endif
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) \
+type name ## _exp args; \
+type name ## _exp args
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) \
+type name args
+
+#endif /* RTE_BUILD_SHARED_LIB */
 
 #endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 4930e2f0b3..320b0edca8 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -7,21 +7,6 @@
 
 #include "rte_net_crc.h"
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
-
-struct rte_net_crc *
-rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len, enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len);
-
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 2fb3eec231..1943d46295 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -345,8 +345,7 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+RTE_VERSION_SYMBOL(25, void, rte_net_crc_set_alg, (enum rte_net_crc_alg alg))
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -373,10 +372,9 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+RTE_DEFAULT_SYMBOL(26, struct rte_net_crc *, rte_net_crc_set_alg, (enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type))
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -414,20 +412,14 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 	}
 	return crc;
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
 
 void rte_net_crc_free(struct rte_net_crc *crc)
 {
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
+	enum rte_net_crc_type type))
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -437,18 +429,12 @@ rte_net_crc_calc_v25(const void *data,
 
 	return ret;
 }
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+RTE_DEFAULT_SYMBOL(26, uint32_t, rte_net_crc_calc, (const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len))
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.48.1


^ permalink raw reply	[relevance 16%]

* [RFC v4 5/8] build: generate symbol maps
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
  2025-03-17 15:42 16%   ` [RFC v4 3/8] eal: rework function versioning macros David Marchand
@ 2025-03-17 15:43 18%   ` David Marchand
  2025-03-17 15:43 16%   ` [RFC v4 7/8] build: use dynamically generated version maps David Marchand
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-17 15:43 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

Rather than maintain a file in parallel of the code, symbols to be
exported can be marked with a token RTE_EXPORT_*SYMBOL.

From those marks, the build framework generates map files only for
symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
becomes unnecessary).

The build framework directly creates a map file in the format that the
linker expects (rather than converting from GNU linker to MSVC linker).

Empty maps are allowed again as a replacement for drivers/version.map.

The symbol check is updated to only support the new format.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v3:
- polished python,
- fixed doc updates not belonging to this patch,
- renamed map files,
- changed msvc->mslinker as link mode,
- added parsing of AVX sources,

Changes since RFC v2:
- because of MSVC limitations wrt macro passed via cmdline,
  used an internal header for defining RTE_EXPORT_* macros,
- updated documentation and tooling,

---
 MAINTAINERS                                |   2 +
 buildtools/gen-version-map.py              | 106 ++++++++++
 buildtools/map-list-symbol.sh              |  10 +-
 buildtools/meson.build                     |   1 +
 config/meson.build                         |   4 +-
 config/rte_export.h                        |  16 ++
 devtools/check-symbol-change.py            |  90 +++++++++
 devtools/check-symbol-maps.sh              |  14 --
 devtools/checkpatches.sh                   |   2 +-
 doc/guides/contributing/abi_versioning.rst | 221 ++-------------------
 drivers/meson.build                        |  96 +++++----
 drivers/version.map                        |   3 -
 lib/meson.build                            |  89 ++++++---
 13 files changed, 363 insertions(+), 291 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 create mode 100644 config/rte_export.h
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100644 drivers/version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 82f6e2f917..63754e76e9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
 F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
+F: devtools/check-symbol-change.py
 F: devtools/check-symbol-change.sh
 F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
@@ -127,6 +128,7 @@ F: config/
 F: buildtools/check-symbols.sh
 F: buildtools/chkincs/
 F: buildtools/call-sphinx-build.py
+F: buildtools/gen-version-map.py
 F: buildtools/get-cpu-count.py
 F: buildtools/get-numa-count.py
 F: buildtools/list-dir-globs.py
diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
new file mode 100755
index 0000000000..3364c224c0
--- /dev/null
+++ b/buildtools/gen-version-map.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Generate a version map file used by GNU or MSVC linker."""
+
+import re
+import sys
+
+scriptname, link_mode, abi_version_file, output, *files = sys.argv
+
+# From rte_export.h
+export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
+export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# From rte_function_versioning.h
+ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+
+with open(abi_version_file) as f:
+    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
+
+symbols = {}
+
+for file in files:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            node = None
+            symbol = None
+            comment = ''
+            if export_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                node = abi
+                symbol = export_sym_regexp.match(ln).group(1)
+            elif ver_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
+                symbol = ver_sym_regexp.match(ln).group(2)
+            elif ver_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = ver_exp_sym_regexp.match(ln).group(1)
+            elif default_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
+                symbol = default_sym_regexp.match(ln).group(2)
+
+            if not symbol:
+                continue
+
+            if node not in symbols:
+                symbols[node] = {}
+            symbols[node][symbol] = comment
+
+if link_mode == 'msvc':
+    with open(output, "w") as outfile:
+        print(f"EXPORTS", file=outfile)
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            for symbol in sorted(symbols[key].keys()):
+                print(f"\t{symbol}", file=outfile)
+            del symbols[key]
+else:
+    with open(output, "w") as outfile:
+        local_token = False
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            print(f"{key} {{\n\tglobal:\n", file=outfile)
+            for symbol in sorted(symbols[key].keys()):
+                if link_mode == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                comment = symbols[key][symbol]
+                print(f"\t{prefix}{symbol};{comment}", file=outfile)
+            if not local_token:
+                print("\n\tlocal: *;", file=outfile)
+                local_token = True
+            print("};", file=outfile)
+            del symbols[key]
+        for key in sorted(symbols.keys()):
+            print(f"{key} {{\n\tglobal:\n", file=outfile)
+            for symbol in sorted(symbols[key].keys()):
+                if link_mode == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                comment = symbols[key][symbol]
+                print(f"\t{prefix}{symbol};{comment}", file=outfile)
+            print(f"}} {abi};", file=outfile)
+            if not local_token:
+                print("\n\tlocal: *;", file=outfile)
+                local_token = True
+            del symbols[key]
+        # No exported symbol, add a catch all
+        if not local_token:
+            print(f"{abi} {{", file=outfile)
+            print("\n\tlocal: *;", file=outfile)
+            local_token = True
+            print("};", file=outfile)
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index eb98451d8e..0829df4be5 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -62,10 +62,14 @@ for file in $@; do
 		if (current_section == "") {
 			next;
 		}
+		symbol_version = current_version
+		if (/^[^}].*[^:*]; # added in /) {
+			symbol_version = $5
+		}
 		if ("'$version'" != "") {
-			if ("'$version'" == "unset" && current_version != "") {
+			if ("'$version'" == "unset" && symbol_version != "") {
 				next;
-			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
+			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
 				next;
 			}
 		}
@@ -73,7 +77,7 @@ for file in $@; do
 		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
 			ret = 0;
 			if ("'$quiet'" == "") {
-				print "'$file' "current_section" "$1" "current_version;
+				print "'$file' "current_section" "$1" "symbol_version;
 			}
 			if ("'$symbol'" != "all") {
 				exit 0;
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 4e2c1217a2..b745e9afa4 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -16,6 +16,7 @@ else
     py3 = ['meson', 'runpython']
 endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
+gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
diff --git a/config/meson.build b/config/meson.build
index f31fef216c..f2d5401ea2 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -300,11 +300,13 @@ if cc.get_id() == 'clang' and dpdk_conf.get('RTE_ARCH_64') == false
     dpdk_extra_ldflags += '-latomic'
 endif
 
-# add -include rte_config to cflags
+# add -include some headers to cflags
 if is_ms_compiler
     add_project_arguments('/FI', 'rte_config.h', language: 'c')
+    add_project_arguments('/FI', 'rte_export.h', language: 'c')
 else
     add_project_arguments('-include', 'rte_config.h', language: 'c')
+    add_project_arguments('-include', 'rte_export.h', language: 'c')
 endif
 
 # enable extra warnings and disable any unwanted warnings
diff --git a/config/rte_export.h b/config/rte_export.h
new file mode 100644
index 0000000000..83d871fe11
--- /dev/null
+++ b/config/rte_export.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef RTE_EXPORT_H
+#define RTE_EXPORT_H
+
+/* *Internal* macros for exporting symbols, used by the build system.
+ * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
+ * version this symbol was introduced in.
+ */
+#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
+#define RTE_EXPORT_INTERNAL_SYMBOL(a)
+#define RTE_EXPORT_SYMBOL(a)
+
+#endif /* RTE_EXPORT_H */
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
new file mode 100755
index 0000000000..09709e4f06
--- /dev/null
+++ b/devtools/check-symbol-change.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Check exported symbols change in a patch."""
+
+import re
+import sys
+
+file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
+# From rte_export.h
+export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
+export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# TODO, handle versioned symbols from rte_function_versioning.h
+# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+
+symbols = {}
+
+for file in sys.argv[1:]:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            if file_header_regexp.match(ln):
+                if file_header_regexp.match(ln).group(2) == "lib":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+                elif file_header_regexp.match(ln).group(3) == "intel":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
+                else:
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+
+                if lib not in symbols:
+                    symbols[lib] = {}
+                continue
+
+            if export_exp_sym_regexp.match(ln):
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                node = 'EXPERIMENTAL'
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                symbol = export_sym_regexp.match(ln).group(1)
+                node = 'stable'
+            else:
+                continue
+
+            if symbol not in symbols[lib]:
+                symbols[lib][symbol] = {}
+            added = ln[0] == '+'
+            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if added:
+                symbols[lib][symbol]['added'] = node
+            else:
+                symbols[lib][symbol]['removed'] = node
+
+    for lib in sorted(symbols.keys()):
+        error = False
+        for symbol in sorted(symbols[lib].keys()):
+            if 'removed' not in symbols[lib][symbol]:
+                # Symbol addition
+                node = symbols[lib][symbol]['added']
+                if node == 'stable':
+                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
+                    error = True
+                else:
+                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
+                continue
+
+            if 'added' not in symbols[lib][symbol]:
+                # Symbol removal
+                node = symbols[lib][symbol]['added']
+                if node == 'stable':
+                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
+                    print(f"Please check it has gone though the deprecation process.")
+                continue
+
+            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
+                # Symbol was moved around
+                continue
+
+            # Symbol modifications
+            added = symbols[lib][symbol]['added']
+            removed = symbols[lib][symbol]['removed']
+            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
+            print(f"Please check it has gone though the deprecation process.")
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
index 6121f78ec6..fcd3931e5d 100755
--- a/devtools/check-symbol-maps.sh
+++ b/devtools/check-symbol-maps.sh
@@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
     ret=1
 fi
 
-find_empty_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
-    done
-}
-
-empty_maps=$(find_empty_maps $@)
-if [ -n "$empty_maps" ] ; then
-    echo "Found empty maps:"
-    echo "$empty_maps"
-    ret=1
-fi
-
 find_bad_format_maps ()
 {
     abi_version=$(cut -d'.' -f 1 ABI_VERSION)
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index c9088bb403..9180c2b070 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
 PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
 SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
 LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
-NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
+NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
 options="$options $DPDK_CHECKPATCH_OPTIONS"
 
 print_usage () {
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 21f8f8cd14..cbcbedfaf0 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -58,12 +58,12 @@ persists over multiple releases.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -77,7 +77,7 @@ that library.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
@@ -88,7 +88,7 @@ that library.
  } DPDK_21;
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -100,12 +100,12 @@ how this may be done.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_22 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_22 {
         global:
  ...
@@ -134,8 +134,7 @@ linked to the DPDK.
 
 To support backward compatibility the ``rte_function_versioning.h``
 header file provides macros to use when updating exported functions. These
-macros are used in conjunction with the ``version.map`` file for
-a given library to allow multiple versions of a symbol to exist in a shared
+macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
 The macros are:
@@ -169,6 +168,7 @@ Assume we have a function as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  int
  rte_acl_create(struct rte_acl_param *param)
  {
@@ -187,6 +187,7 @@ private, is safe), but it also requires modifying the code as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  int
  rte_acl_create(struct rte_acl_param *param, int debug)
  {
@@ -203,78 +204,16 @@ The addition of a parameter to the function is ABI breaking as the function is
 public, and existing application may use it in its current form. However, the
 compatibility macros in DPDK allow a developer to use symbol versioning so that
 multiple functions can be mapped to the same public symbol based on when an
-application was linked to it. To see how this is done, we start with the
-requisite libraries version map file. Initially the version map file for the acl
-library looks like this
+application was linked to it.
 
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-This file needs to be modified as follows
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-
-   } DPDK_21;
-
-The addition of the new block tells the linker that a new version node
-``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
-the symbols from the DPDK_21 node. This list is directly translated into a
-list of exported symbols when DPDK is compiled as a shared library.
-
-Next, we need to specify in the code which function maps to the rte_acl_create
+We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
 we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
 the function return type, the function name and its arguments.
 
 .. code-block:: c
 
+ -RTE_EXPORT_SYMBOL(rte_acl_create)
  -int
  -rte_acl_create(struct rte_acl_param *param)
  +RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
@@ -314,9 +253,9 @@ The macro instructs the linker to create the new default symbol
 ``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
 (declared by the macro).
 
-And that's it, on the next shared library rebuild, there will be two versions of
-rte_acl_create, an old DPDK_21 version, used by previously built applications,
-and a new DPDK_22 version, used by future built applications.
+And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
+an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
+used by newly built applications.
 
 .. note::
 
@@ -366,6 +305,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
    int
    rte_acl_create(struct rte_acl_param *param)
@@ -373,27 +313,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
    ...
    }
 
-In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL``
-version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 When we promote the symbol to the stable ABI, we simply strip the
-``__rte_experimental`` annotation from the function and move the symbol from the
-``EXPERIMENTAL`` node, to the node of the next major ABI version as follow.
+``__rte_experimental`` annotation from the function.
 
 .. code-block:: c
 
@@ -401,31 +322,13 @@ When we promote the symbol to the stable ABI, we simply strip the
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    int
    rte_acl_create(struct rte_acl_param *param)
    {
           ...
    }
 
-We then update the map file, adding the symbol ``rte_acl_create``
-to the ``DPDK_22`` version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-
 Although there are strictly no guarantees or commitments associated with
 :ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer
 an alias to experimental. The process to add an alias to experimental,
@@ -452,30 +355,6 @@ and ``DPDK_22`` version nodes.
       return rte_acl_create(param);
    }
 
-In the map file, we map the symbol to both the ``EXPERIMENTAL``
-and ``DPDK_22`` version nodes.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -484,38 +363,7 @@ ________________________________
 Lets assume that you've done the above updates, and in preparation for the next
 major ABI version you decide you would like to retire the old version of the
 function. After having gone through the ABI deprecation announcement process,
-removal is easy. Start by removing the symbol from the requisite version map
-file:
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
- -      rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-   } DPDK_21;
-
+removal is easy.
 
 Next remove the corresponding versioned export.
 
@@ -539,36 +387,7 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
-To do this, start by modifying the version map file, such that all symbols from
-the node to be removed are merged into the next node in the map.
-
-In the case of our map above, it would transform to look as follows
-
-.. code-block:: none
-
-   DPDK_22 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
-        rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
- };
-
-Then any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index c42c7764bf..b5434715f3 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -275,14 +275,14 @@ foreach subpath:subdirs
                 dependencies: static_deps,
                 c_args: cflags)
         objs += tmp_lib.extract_all_objects(recursive: true)
-        sources = custom_target(out_filename,
+        sources_pmd_info = custom_target(out_filename,
                 command: [pmdinfo, tmp_lib.full_path(), '@OUTPUT@', pmdinfogen],
                 output: out_filename,
                 depends: [tmp_lib])
 
         # now build the static driver
         static_lib = static_library(lib_name,
-                sources,
+                sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: static_deps,
@@ -292,48 +292,70 @@ foreach subpath:subdirs
         # now build the shared driver
         version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
 
-        lk_deps = []
-        lk_args = []
         if not fs.is_file(version_map)
-            version_map = '@0@/version.map'.format(meson.current_source_dir())
-            lk_deps += [version_map]
-        else
-            lk_deps += [version_map]
-            if not is_windows and developer_mode
-                # on unix systems check the output of the
-                # check-symbols.sh script, using it as a
-                # dependency of the .so build
-                lk_deps += custom_target(lib_name + '.sym_chk',
-                        command: [check_symbols, version_map, '@INPUT@'],
-                        capture: true,
-                        input: static_lib,
-                        output: lib_name + '.sym_chk')
-            endif
-        endif
-
-        if is_windows
             if is_ms_linker
-                def_file = custom_target(lib_name + '_def',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_exports.def'.format(lib_name))
-                lk_deps += [def_file]
-
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
+                link_mode = 'mslinker'
+            elif is_windows
+                link_mode = 'mingw'
             else
-                mingw_map = custom_target(lib_name + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(lib_name))
-                lk_deps += [mingw_map]
-
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                link_mode = 'gnu'
+            endif
+            version_map = custom_target(lib_name + '_map',
+                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                    input: sources + sources_avx2 + sources_avx512,
+                    output: '_'.join(class, name, 'exports.map'))
+            version_map_path = version_map.full_path()
+            version_map_dep = [version_map]
+            lk_deps = [version_map]
+
+            if is_ms_linker and is_ms_compiler
+                lk_args = ['/def:' + version_map.full_path()]
+            elif is_ms_linker
+                lk_args = ['-Wl,/def:' + version_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
             endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            version_map_path = version_map
+            version_map_dep = []
+            lk_deps = [version_map]
+
+            if is_windows
+                if is_ms_linker
+                    def_file = custom_target(lib_name + '_def',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_exports.def'.format(lib_name))
+                    lk_deps += [def_file]
+
+                    lk_args = ['-Wl,/def:' + def_file.full_path()]
+                else
+                    mingw_map = custom_target(lib_name + '_mingw',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_mingw.map'.format(lib_name))
+                    lk_deps += [mingw_map]
+
+                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                endif
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
+        endif
+
+        if not is_windows and developer_mode
+            # on unix systems check the output of the
+            # check-symbols.sh script, using it as a
+            # dependency of the .so build
+            lk_deps += custom_target(lib_name + '.sym_chk',
+                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    capture: true,
+                    input: static_lib,
+                    output: lib_name + '.sym_chk',
+                    depends: version_map_dep)
         endif
 
-        shared_lib = shared_library(lib_name, sources,
+        shared_lib = shared_library(lib_name, sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: shared_deps,
diff --git a/drivers/version.map b/drivers/version.map
deleted file mode 100644
index 17cc97bda6..0000000000
--- a/drivers/version.map
+++ /dev/null
@@ -1,3 +0,0 @@
-DPDK_25 {
-	local: *;
-};
diff --git a/lib/meson.build b/lib/meson.build
index ce92cb5537..952ae4696f 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
+fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -254,42 +255,58 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not use_function_versioning or is_windows
-        # use pre-build objects to build shared lib
-        sources = []
-        objs += static_lib.extract_all_objects(recursive: false)
-    else
-        # for compat we need to rebuild with
-        # RTE_BUILD_SHARED_LIB defined
-        cflags += '-DRTE_BUILD_SHARED_LIB'
-    endif
-
-    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-    lk_deps = [version_map]
-
-    if is_ms_linker
-        def_file = custom_target(libname + '_def',
-                command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                input: version_map,
-                output: '@0@_exports.def'.format(libname))
-        lk_deps += [def_file]
+    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
+        else
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(libname + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources,
+                output: '_'.join(name, 'exports.map'))
+        version_map_path = version_map.full_path()
+        version_map_dep = [version_map]
+        lk_deps = [version_map]
 
-        if is_ms_compiler
-            lk_args = ['/def:' + def_file.full_path()]
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
         else
-            lk_args = ['-Wl,/def:' + def_file.full_path()]
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
     else
-        if is_windows
-            mingw_map = custom_target(libname + '_mingw',
+        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
+        version_map_path = version_map
+        version_map_dep = []
+        lk_deps = [version_map]
+        if is_ms_linker
+            def_file = custom_target(libname + '_def',
                     command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
                     input: version_map,
-                    output: '@0@_mingw.map'.format(libname))
-            lk_deps += [mingw_map]
+                    output: '@0@_exports.def'.format(libname))
+            lk_deps += [def_file]
 
-            lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            if is_ms_compiler
+                lk_args = ['/def:' + def_file.full_path()]
+            else
+                lk_args = ['-Wl,/def:' + def_file.full_path()]
+            endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            if is_windows
+                mingw_map = custom_target(libname + '_mingw',
+                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                        input: version_map,
+                        output: '@0@_mingw.map'.format(libname))
+                lk_deps += [mingw_map]
+
+                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
         endif
     endif
 
@@ -298,11 +315,21 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols,
-                    version_map, '@INPUT@'],
+                command: [check_symbols, version_map_path, '@INPUT@'],
                 capture: true,
                 input: static_lib,
-                output: name + '.sym_chk')
+                output: name + '.sym_chk',
+                depends: version_map_dep)
+    endif
+
+    if not use_function_versioning or is_windows
+        # use pre-build objects to build shared lib
+        sources = []
+        objs += static_lib.extract_all_objects(recursive: false)
+    else
+        # for compat we need to rebuild with
+        # RTE_BUILD_SHARED_LIB defined
+        cflags += '-DRTE_BUILD_SHARED_LIB'
     endif
 
     shared_lib = shared_library(libname,
-- 
2.48.1


^ permalink raw reply	[relevance 18%]

* [RFC v4 7/8] build: use dynamically generated version maps
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
  2025-03-17 15:42 16%   ` [RFC v4 3/8] eal: rework function versioning macros David Marchand
  2025-03-17 15:43 18%   ` [RFC v4 5/8] build: generate symbol maps David Marchand
@ 2025-03-17 15:43 16%   ` David Marchand
  2025-03-18  8:19  0%   ` [RFC v4 0/8] Symbol versioning and export rework David Marchand
  2025-03-26 12:02  0%   ` David Marchand
  4 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-17 15:43 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Aaron Conole, Michael Santana

Switch to always dynamically generate version maps.

As the map files get generated, tooling around checking, converting,
updating etc.. static version maps can be removed.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   7 -
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/map-list-symbol.sh                 |   7 +-
 buildtools/map_to_win.py                      |  41 ---
 buildtools/meson.build                        |   1 -
 devtools/check-symbol-change.sh               | 186 -----------
 devtools/check-symbol-maps.sh                 | 101 ------
 devtools/checkpatches.sh                      |   2 +-
 devtools/update-abi.sh                        |  46 ---
 devtools/update_version_map_abi.py            | 210 ------------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/meson.build                           |  74 ++---
 lib/meson.build                               |  73 ++---
 17 files changed, 188 insertions(+), 931 deletions(-)
 delete mode 100644 buildtools/map_to_win.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fba46b920f..e97b5cdb8b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,6 @@ jobs:
         failed=
         devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
         devtools/check-meson.py || failed=true
-        devtools/check-symbol-maps.sh || failed=true
         [ -z "$failed" ]
   ubuntu-vm-builds:
     name: ${{ join(matrix.config.*, '-') }}
diff --git a/MAINTAINERS b/MAINTAINERS
index 63754e76e9..cd9dcbb1f7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
 F: MAINTAINERS
 F: devtools/build-dict.sh
 F: devtools/check-abi.sh
-F: devtools/check-abi-version.sh
 F: devtools/check-doc-vs-code.sh
 F: devtools/check-dup-includes.sh
 F: devtools/check-maintainers.sh
@@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
 F: devtools/check-symbol-change.py
-F: devtools/check-symbol-change.sh
-F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
 F: devtools/get-maintainer.sh
 F: devtools/git-log-fixes.sh
 F: devtools/load-devel-config
 F: devtools/parse-flow-support.sh
 F: devtools/process-iwyu.py
-F: devtools/update-abi.sh
 F: devtools/update-patches.py
-F: devtools/update_version_map_abi.py
 F: devtools/libabigail.abignore
 F: devtools/words-case.txt
 F: license/
@@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/common/
 F: lib/eal/unix/
 F: lib/eal/include/
-F: lib/eal/version.map
 F: doc/guides/prog_guide/env_abstraction_layer.rst
 F: app/test/test_alarm.c
 F: app/test/test_atomic.c
@@ -396,7 +390,6 @@ Windows support
 M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
 M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/windows/
-F: buildtools/map_to_win.py
 F: doc/guides/windows_gsg/
 
 Windows memory allocation
diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
index b8ac24391e..0d6745ec14 100755
--- a/buildtools/check-symbols.sh
+++ b/buildtools/check-symbols.sh
@@ -7,29 +7,12 @@ OBJFILE=$2
 
 ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
 LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
-CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
-
-# added check for "make -C test/" usage
-if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
-then
-	exit 0
-fi
-
-if [ -d $MAPFILE ]
-then
-	exit 0
-fi
-
 DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
 trap 'rm -f "$DUMPFILE"' EXIT
 objdump -t $OBJFILE >$DUMPFILE
 
 ret=0
 
-if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
-	ret=1
-fi
-
 for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
 do
 	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
@@ -37,8 +20,7 @@ do
 		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as experimental
-		but is listed in version map
+		$SYM is not flagged as experimental but is exported as an experimental symbol
 		Please add __rte_experimental to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -53,9 +35,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as experimental
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as experimental but is not exported as an experimental symbol
+		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
@@ -67,8 +48,7 @@ do
 		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as internal
-		but is listed in version map
+		$SYM is not flagged as internal but is exported as an internal symbol
 		Please add __rte_internal to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -83,9 +63,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as internal
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as internal but is not exported as an internal symbol
+		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index 0829df4be5..962d5f3271 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -42,7 +42,6 @@ for file in $@; do
 	cat "$file" |awk '
 	BEGIN {
 		current_section = "";
-		current_version = "";
 		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
 			ret = 0;
 		} else {
@@ -54,15 +53,11 @@ for file in $@; do
 			current_section = $1;
 		}
 	}
-	/.*}/ { current_section = ""; current_version = ""; }
-	/^\t# added in / {
-		current_version=$4;
-	}
+	/.*}/ { current_section = ""; }
 	/^[^}].*[^:*];/ {
 		if (current_section == "") {
 			next;
 		}
-		symbol_version = current_version
 		if (/^[^}].*[^:*]; # added in /) {
 			symbol_version = $5
 		}
diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
deleted file mode 100644
index aa1752cacd..0000000000
--- a/buildtools/map_to_win.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-import sys
-
-
-def is_function_line(ln):
-    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
-
-# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
-def create_mingw_map_file(input_map, output_map):
-    with open(input_map) as f_in, open(output_map, 'w') as f_out:
-        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
-
-def main(args):
-    if not args[1].endswith('version.map') or \
-            not args[2].endswith('exports.def') and \
-            not args[2].endswith('mingw.map'):
-        return 1
-
-    if args[2].endswith('mingw.map'):
-        create_mingw_map_file(args[1], args[2])
-        return 0
-
-# generate def file from map file.
-# This works taking indented lines only which end with a ";" and which don't
-# have a colon in them, i.e. the lines defining functions only.
-    else:
-        with open(args[1]) as f_in:
-            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
-                         if is_function_line(ln)]
-            functions = ["EXPORTS\n"] + functions
-
-    with open(args[2], 'w') as f_out:
-        f_out.writelines(functions)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv))
diff --git a/buildtools/meson.build b/buildtools/meson.build
index b745e9afa4..1cd1ce02fd 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -18,7 +18,6 @@ endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
 gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
-map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
 get_cpu_count_cmd = py3 + files('get-cpu-count.py')
 get_numa_count_cmd = py3 + files('get-numa-count.py')
diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
deleted file mode 100755
index 8992214ac8..0000000000
--- a/devtools/check-symbol-change.sh
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
-
-build_map_changes()
-{
-	local fname="$1"
-	local mapdb="$2"
-
-	cat "$fname" | awk '
-		# Initialize our variables
-		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
-
-		# Anything that starts with + or -, followed by an a
-		# and ends in the string .map is the name of our map file
-		# This may appear multiple times in a patch if multiple
-		# map files are altered, and all section/symbol names
-		# appearing between a triggering of this rule and the
-		# next trigger of this rule are associated with this file
-		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
-
-		# The previous rule catches all .map files, anything else
-		# indicates we left the map chunk.
-		/[-+] [ab]\// {in_map=0}
-
-		# Triggering this rule, which starts a line and ends it
-		# with a { identifies a versioned section.  The section name is
-		# the rest of the line with the + and { symbols removed.
-		# Triggering this rule sets in_sec to 1, which actives the
-		# symbol rule below
-		/^.*{/ {
-			gsub("+", "");
-			if (in_map == 1) {
-				sec=$(NF-1); in_sec=1;
-			}
-		}
-
-		# This rule identifies the end of a section, and disables the
-		# symbol rule
-		/.*}/ {in_sec=0}
-
-		# This rule matches on a + followed by any characters except a :
-		# (which denotes a global vs local segment), and ends with a ;.
-		# The semicolon is removed and the symbol is printed with its
-		# association file name and version section, along with an
-		# indicator that the symbol is a new addition.  Note this rule
-		# only works if we have found a version section in the rule
-		# above (hence the in_sec check) And found a map file (the
-		# in_map check).  If we are not in a map chunk, do nothing.  If
-		# we are in a map chunk but not a section chunk, record it as
-		# unknown.
-		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " add"
-				} else {
-					print map " " sym " unknown add"
-				}
-			}
-		}
-
-		# This is the same rule as above, but the rule matches on a
-		# leading - rather than a +, denoting that the symbol is being
-		# removed.
-		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " del"
-				} else {
-					print map " " sym " unknown del"
-				}
-			}
-		}' > "$mapdb"
-
-		sort -u "$mapdb" > "$mapdb.2"
-		mv -f "$mapdb.2" "$mapdb"
-
-}
-
-is_stable_section() {
-	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
-}
-
-check_for_rule_violations()
-{
-	local mapdb="$1"
-	local mname
-	local symname
-	local secname
-	local ar
-	local ret=0
-
-	while read mname symname secname ar
-	do
-		if [ "$ar" = "add" ]
-		then
-
-			if [ "$secname" = "unknown" ]
-			then
-				# Just inform the user of this occurrence, but
-				# don't flag it as an error
-				echo -n "INFO: symbol $symname is added but "
-				echo -n "patch has insufficient context "
-				echo -n "to determine the section name "
-				echo -n "please ensure the version is "
-				echo "EXPERIMENTAL"
-				continue
-			fi
-
-			oldsecname=$(sed -n \
-			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
-
-			# A symbol can not enter a stable section directly
-			if [ -z "$oldsecname" ]
-			then
-				if ! is_stable_section $secname
-				then
-					echo -n "INFO: symbol $symname has "
-					echo -n "been added to the "
-					echo -n "$secname section of the "
-					echo "version map"
-					continue
-				else
-					echo -n "ERROR: symbol $symname "
-					echo -n "is added in the $secname "
-					echo -n "section, but is expected to "
-					echo -n "be added in the EXPERIMENTAL "
-					echo "section of the version map"
-					ret=1
-					continue
-				fi
-			fi
-
-			# This symbol is moving inside a section, nothing to do
-			if [ "$oldsecname" = "$secname" ]
-			then
-				continue
-			fi
-
-			# This symbol is moving between two sections (the
-			# original section is a stable section).
-			# This can be legit, just warn.
-			if is_stable_section $oldsecname
-			then
-				echo -n "INFO: symbol $symname is being "
-				echo -n "moved from $oldsecname to $secname. "
-				echo -n "Ensure that it has gone through the "
-				echo "deprecation process"
-				continue
-			fi
-		else
-
-			if ! grep -q "$mname $symname .* add" "$mapdb" && \
-			   is_stable_section $secname
-			then
-				# Just inform users that stable
-				# symbols need to go through a deprecation
-				# process
-				echo -n "INFO: symbol $symname is being "
-				echo -n "removed, ensure that it has "
-				echo "gone through the deprecation process"
-			fi
-		fi
-	done < "$mapdb"
-
-	return $ret
-}
-
-trap clean_and_exit_on_sig EXIT
-
-mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
-patch=$1
-exit_code=1
-
-clean_and_exit_on_sig()
-{
-	rm -f "$mapfile"
-	exit $exit_code
-}
-
-build_map_changes "$patch" "$mapfile"
-check_for_rule_violations "$mapfile"
-exit_code=$?
-rm -f "$mapfile"
-
-exit $exit_code
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
deleted file mode 100755
index fcd3931e5d..0000000000
--- a/devtools/check-symbol-maps.sh
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright 2018 Mellanox Technologies, Ltd
-
-cd $(dirname $0)/..
-
-# speed up by ignoring Unicode details
-export LC_ALL=C
-
-if [ $# = 0 ] ; then
-    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
-fi
-
-ret=0
-
-find_orphan_symbols ()
-{
-    for map in $@ ; do
-        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
-            if echo $sym | grep -q '^per_lcore_' ; then
-                symsrc=${sym#per_lcore_}
-            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
-                symsrc=${sym#__}
-            else
-                symsrc=$sym
-            fi
-            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
-                echo "$map: $sym"
-            fi
-        done
-    done
-}
-
-orphan_symbols=$(find_orphan_symbols $@)
-if [ -n "$orphan_symbols" ] ; then
-    echo "Found only in symbol map file:"
-    echo "$orphan_symbols" | sed 's,^,\t,'
-    ret=1
-fi
-
-find_duplicate_symbols ()
-{
-    for map in $@ ; do
-        buildtools/map-list-symbol.sh $map | \
-            sort | uniq -c | grep -v " 1 $map" || true
-    done
-}
-
-duplicate_symbols=$(find_duplicate_symbols $@)
-if [ -n "$duplicate_symbols" ] ; then
-    echo "Found duplicates in symbol map file:"
-    echo "$duplicate_symbols"
-    ret=1
-fi
-
-local_miss_maps=$(grep -L 'local: \*;' $@ || true)
-if [ -n "$local_miss_maps" ] ; then
-    echo "Found maps without local catch-all:"
-    echo "$local_miss_maps"
-    ret=1
-fi
-
-find_bad_format_maps ()
-{
-    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
-    next_abi_version=$((abi_version + 1))
-    for map in $@ ; do
-        cat $map | awk '
-            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
-            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
-            /^$/ { next; } # empty line
-            /^\t(global:|local: \*;)$/ { next; } # qualifiers
-            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
-            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
-            { print $0; }' || echo $map
-    done
-}
-
-bad_format_maps=$(find_bad_format_maps $@)
-if [ -n "$bad_format_maps" ] ; then
-    echo "Found badly formatted maps:"
-    echo "$bad_format_maps"
-    ret=1
-fi
-
-find_non_versioned_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
-            echo $map
-    done
-}
-
-non_versioned_maps=$(find_non_versioned_maps $@)
-if [ -n "$non_versioned_maps" ] ; then
-    echo "Found non versioned maps:"
-    echo "$non_versioned_maps"
-    ret=1
-fi
-
-exit $ret
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index 9180c2b070..c111b0fef3 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -9,7 +9,7 @@
 # - DPDK_CHECKPATCH_OPTIONS
 . $(dirname $(readlink -f $0))/load-devel-config
 
-VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
+VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
 
 # Enable codespell by default. This can be overwritten from a config file.
 # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
deleted file mode 100755
index 45437f3c3b..0000000000
--- a/devtools/update-abi.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-abi_version=$1
-abi_version_file="./ABI_VERSION"
-update_path="lib drivers"
-
-# check ABI version format string
-check_abi_version() {
-      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
-}
-
-if [ -z "$1" ]; then
-      # output to stderr
-      >&2 echo "Please provide ABI version"
-      exit 1
-fi
-
-# check version string format
-if ! check_abi_version $abi_version ; then
-      # output to stderr
-      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
-      exit 1
-fi
-
-if [ -n "$2" ]; then
-      abi_version_file=$2
-fi
-
-if [ -n "$3" ]; then
-      # drop $1 and $2
-      shift 2
-      # assign all other arguments as update paths
-      update_path=$@
-fi
-
-echo "New ABI version:" $abi_version
-echo "ABI_VERSION path:" $abi_version_file
-echo "Path to update:" $update_path
-
-echo $abi_version > $abi_version_file
-
-find $update_path -name version.map -exec \
-      devtools/update_version_map_abi.py {} \
-      $abi_version \; -print
diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
deleted file mode 100755
index d17b02a327..0000000000
--- a/devtools/update_version_map_abi.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-"""
-A Python program that updates and merges all available stable ABI versions into
-one ABI version, while leaving experimental ABI exactly as it is. The intended
-ABI version is supplied via command-line parameter. This script is to be called
-from the devtools/update-abi.sh utility.
-"""
-
-import argparse
-import sys
-import re
-
-
-def __parse_map_file(f_in):
-    # match function name, followed by semicolon, followed by EOL or comments,
-    # optionally with whitespace in between each item
-    func_line_regex = re.compile(r"\s*"
-                                 r"(?P<line>"
-                                 r"(?P<func>[a-zA-Z_0-9]+)"
-                                 r"\s*"
-                                 r";"
-                                 r"\s*"
-                                 r"(?P<comment>#.+)?"
-                                 r")"
-                                 r"\s*"
-                                 r"$")
-    # match section name, followed by opening bracked, followed by EOL,
-    # optionally with whitespace in between each item
-    section_begin_regex = re.compile(r"\s*"
-                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
-                                     r"\s*"
-                                     r"{"
-                                     r"\s*"
-                                     r"$")
-    # match closing bracket, optionally followed by section name (for when we
-    # inherit from another ABI version), followed by semicolon, followed by
-    # EOL, optionally with whitespace in between each item
-    section_end_regex = re.compile(r"\s*"
-                                   r"}"
-                                   r"\s*"
-                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
-                                   r"\s*"
-                                   r";"
-                                   r"\s*"
-                                   r"$")
-
-    # for stable ABI, we don't care about which version introduced which
-    # function, we just flatten the list. there are dupes in certain files, so
-    # use a set instead of a list
-    stable_lines = set()
-    # copy experimental section as is
-    experimental_lines = []
-    # copy internal section as is
-    internal_lines = []
-    in_experimental = False
-    in_internal = False
-    has_stable = False
-
-    # gather all functions
-    for line in f_in:
-        # clean up the line
-        line = line.strip('\n').strip()
-
-        # is this an end of section?
-        match = section_end_regex.match(line)
-        if match:
-            # whatever section this was, it's not active any more
-            in_experimental = False
-            in_internal = False
-            continue
-
-        # if we're in the middle of experimental section, we need to copy
-        # the section verbatim, so just add the line
-        if in_experimental:
-            experimental_lines += [line]
-            continue
-
-        # if we're in the middle of internal section, we need to copy
-        # the section verbatim, so just add the line
-        if in_internal:
-            internal_lines += [line]
-            continue
-
-        # skip empty lines
-        if not line:
-            continue
-
-        # is this a beginning of a new section?
-        match = section_begin_regex.match(line)
-        if match:
-            cur_section = match.group("version")
-            # is it experimental?
-            in_experimental = cur_section == "EXPERIMENTAL"
-            # is it internal?
-            in_internal = cur_section == "INTERNAL"
-            if not in_experimental and not in_internal:
-                has_stable = True
-            continue
-
-        # is this a function?
-        match = func_line_regex.match(line)
-        if match:
-            stable_lines.add(match.group("line"))
-
-    return has_stable, stable_lines, experimental_lines, internal_lines
-
-
-def __generate_stable_abi(f_out, abi_major, lines):
-    # print ABI version header
-    print("DPDK_{} {{".format(abi_major), file=f_out)
-
-    # print global section if it exists
-    if lines:
-        print("\tglobal:", file=f_out)
-        # blank line
-        print(file=f_out)
-
-        # print all stable lines, alphabetically sorted
-        for line in sorted(lines):
-            print("\t{}".format(line), file=f_out)
-
-        # another blank line
-        print(file=f_out)
-
-    # print local section
-    print("\tlocal: *;", file=f_out)
-
-    # end stable version
-    print("};", file=f_out)
-
-
-def __generate_experimental_abi(f_out, lines):
-    # start experimental section
-    print("EXPERIMENTAL {", file=f_out)
-
-    # print all experimental lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __generate_internal_abi(f_out, lines):
-    # start internal section
-    print("INTERNAL {", file=f_out)
-
-    # print all internal lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __main():
-    arg_parser = argparse.ArgumentParser(
-        description='Merge versions in linker version script.')
-
-    arg_parser.add_argument("map_file", type=str,
-                            help='path to linker version script file '
-                                 '(pattern: version.map)')
-    arg_parser.add_argument("abi_version", type=str,
-                            help='target ABI version (pattern: MAJOR.MINOR)')
-
-    parsed = arg_parser.parse_args()
-
-    if not parsed.map_file.endswith('version.map'):
-        print("Invalid input file: {}".format(parsed.map_file),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-
-    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
-        print("Invalid ABI version: {}".format(parsed.abi_version),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-    abi_major = parsed.abi_version.split('.')[0]
-
-    with open(parsed.map_file) as f_in:
-        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
-
-    with open(parsed.map_file, 'w') as f_out:
-        need_newline = has_stable and experimental_lines
-        if has_stable:
-            __generate_stable_abi(f_out, abi_major, stable_lines)
-        if need_newline:
-            # separate sections with a newline
-            print(file=f_out)
-        if experimental_lines:
-            __generate_experimental_abi(f_out, experimental_lines)
-        if internal_lines:
-            if has_stable or experimental_lines:
-              # separate sections with a newline
-              print(file=f_out)
-            __generate_internal_abi(f_out, internal_lines)
-
-
-if __name__ == "__main__":
-    __main()
diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
index d96153c6b2..f03a7467ac 100644
--- a/doc/guides/contributing/abi_policy.rst
+++ b/doc/guides/contributing/abi_policy.rst
@@ -330,31 +330,14 @@ become part of a tracked ABI version.
 
 Note that marking an API as experimental is a multi step process.
 To mark an API as experimental, the symbols which are desired to be exported
-must be placed in an EXPERIMENTAL version block in the corresponding libraries'
-version map script.
+must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
+sources.
 Experimental symbols must be commented so that it is clear in which DPDK
 version they were introduced.
 
-.. code-block:: none
-
-   EXPERIMENTAL {
-           global:
-
-           # added in 20.11
-           rte_foo_init;
-           rte_foo_configure;
-
-           # added in 21.02
-           rte_foo_cleanup;
-   ...
-
 Secondly, the corresponding prototypes of those exported functions (in the
 development header files), must be marked with the ``__rte_experimental`` tag
 (see ``rte_compat.h``).
-The DPDK build makefiles perform a check to ensure that the map file and the
-C code reflect the same list of symbols.
-This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
-during compilation in the corresponding library Makefile.
 
 In addition to tagging the code with ``__rte_experimental``,
 the doxygen markup must also contain the EXPERIMENTAL string,
diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
index 1ebc79ca3c..43e27bbd0a 100644
--- a/doc/guides/contributing/coding_style.rst
+++ b/doc/guides/contributing/coding_style.rst
@@ -1018,13 +1018,6 @@ name
 	sources are stored in a directory ``lib/xyz``, this value should
 	never be needed for new libraries.
 
-.. note::
-
-	The name value also provides the name used to find the function version
-	map file, as part of the build process, so if the directory name and
-	library names differ, the ``version.map`` file should be named
-	consistently with the library, not the directory
-
 objs
 	**Default Value = []**.
 	This variable can be used to pass to the library build some pre-built
diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
index 4debb07b98..a06d8a2a3b 100644
--- a/doc/guides/contributing/img/patch_cheatsheet.svg
+++ b/doc/guides/contributing/img/patch_cheatsheet.svg
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    version="1.1"
    width="210mm"
    height="297mm"
    id="svg2985"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="patch_cheatsheet.svg">
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="patch_cheatsheet.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
   <sodipodi:namedview
      pagecolor="#ffffff"
      bordercolor="#666666"
@@ -23,18 +23,22 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:window-width="1920"
-     inkscape:window-height="1017"
+     inkscape:window-height="975"
      id="namedview274"
      showgrid="false"
      inkscape:zoom="0.89702958"
-     inkscape:cx="246.07409"
-     inkscape:cy="416.76022"
-     inkscape:window-x="1072"
-     inkscape:window-y="-8"
+     inkscape:cx="546.24732"
+     inkscape:cy="385.71749"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
      inkscape:window-maximized="1"
      inkscape:current-layer="layer1"
      inkscape:document-rotation="0"
-     inkscape:snap-grids="false" />
+     inkscape:snap-grids="false"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm" />
   <defs
      id="defs3">
     <linearGradient
@@ -906,7 +910,7 @@
              style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
              id="tspan4092-8-7-6-9-7"
              y="855.79816"
-             x="460.18405">****</tspan></text>
+             x="460.18405">***</tspan></text>
       </g>
     </g>
     <text
@@ -1132,161 +1136,126 @@
            id="tspan4092-8-6-3-1-8-4-4-55-7"
            style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
     </g>
+    <text
+       x="424.10629"
+       y="363.21423"
+       id="text4090-8"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="363.21423"
+         id="tspan4092-8"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
+    <text
+       x="424.10629"
+       y="393.60123"
+       id="text4090-8-5"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="393.60123"
+         id="tspan4092-8-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
+    <text
+       x="424.10629"
+       y="424.20575"
+       id="text4090-8-5-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="424.20575"
+         id="tspan4092-8-5-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
+    <text
+       x="424.10629"
+       y="453.10339"
+       id="text4090-8-5-6-9-4"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="453.10339"
+         id="tspan4092-8-5-5-3-4"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
+    <text
+       x="424.10629"
+       y="514.09497"
+       id="text4090-8-5-6-9-4-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="514.09497"
+         id="tspan4092-8-5-5-3-4-0"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
+    <text
+       x="425.12708"
+       y="544.91718"
+       id="text4090-8-5-6-9-4-6-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="425.12708"
+         y="544.91718"
+         id="tspan4092-8-5-5-3-4-0-6"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
-       id="g3605">
-      <text
-         x="42.176418"
-         y="1020.4383"
-         id="text4090-8-7-8-7-6-3-8-4"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="42.176418"
-           y="1020.4383"
-           id="tspan4092-8-6-3-1-8-4-4-55"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
-      <text
-         x="30.942892"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="30.942892"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-      <text
-         x="25.247679"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="25.247679"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-    </g>
-    <g
-       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
-       id="g3275">
+       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
+       id="g3334">
       <g
-         id="g3341">
+         id="g3267"
+         transform="translate(-13.517932,3.1531035)">
         <text
-           x="394.78601"
-           y="390.17807"
-           id="text4090-8"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="390.17807"
-             id="tspan4092-8"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
-        <text
-           x="394.78601"
-           y="420.24835"
-           id="text4090-8-5"
+           x="660.46729"
+           y="468.01297"
+           id="text4090-8-1-8-9-1-4-1"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="420.24835"
-             id="tspan4092-8-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
-        <text
-           x="394.78601"
-           y="450.53394"
-           id="text4090-8-5-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="450.53394"
-             id="tspan4092-8-5-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
-        <text
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="660.46729"
+             y="468.01297"
+             id="tspan4092-8-7-6-9-7-0-7"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+      </g>
+      <text
+         x="394.78601"
+         y="483.59955"
+         id="text4090-8-5-6-9"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="513.13031"
-           id="text4090-8-5-6-9-4"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="513.13031"
-             id="tspan4092-8-5-5-3-4"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
-        <text
+           y="483.59955"
+           id="tspan4092-8-5-5-3"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+    </g>
+    <g
+       id="g3428"
+       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
+      <text
+         x="394.78601"
+         y="541.38928"
+         id="text4090-8-5-6-9-4-6-1"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="573.48621"
-           id="text4090-8-5-6-9-4-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="573.48621"
-             id="tspan4092-8-5-5-3-4-0"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
+           y="541.38928"
+           id="tspan4092-8-5-5-3-4-0-7"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
+      <g
+         transform="translate(-119.92979,57.949844)"
+         id="g3267-9">
         <text
-           x="395.79617"
-           y="603.98718"
-           id="text4090-8-5-6-9-4-6-6"
+           x="628.93628"
+           y="473.13675"
+           id="text4090-8-1-8-9-1-4-1-4"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="395.79617"
-             y="603.98718"
-             id="tspan4092-8-5-5-3-4-0-6"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
-        <g
-           transform="translate(0,-0.83470152)"
-           id="g3334">
-          <g
-             id="g3267"
-             transform="translate(-13.517932,3.1531035)">
-            <text
-               x="660.46729"
-               y="468.01297"
-               id="text4090-8-1-8-9-1-4-1"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="660.46729"
-                 y="468.01297"
-                 id="tspan4092-8-7-6-9-7-0-7"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
-          </g>
-          <text
-             x="394.78601"
-             y="483.59955"
-             id="text4090-8-5-6-9"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="483.59955"
-               id="tspan4092-8-5-5-3"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
-        </g>
-        <g
-           id="g3428"
-           transform="translate(0,0.88137813)">
-          <text
-             x="394.78601"
-             y="541.38928"
-             id="text4090-8-5-6-9-4-6-1"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="541.38928"
-               id="tspan4092-8-5-5-3-4-0-7"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
-          <g
-             transform="translate(-119.92979,57.949844)"
-             id="g3267-9">
-            <text
-               x="628.93628"
-               y="473.13675"
-               id="text4090-8-1-8-9-1-4-1-4"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="628.93628"
-                 y="473.13675"
-                 id="tspan4092-8-7-6-9-7-0-7-8"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
-          </g>
-        </g>
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="628.93628"
+             y="473.13675"
+             id="tspan4092-8-7-6-9-7-0-7-8"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
       </g>
     </g>
     <text
@@ -1301,7 +1270,7 @@
          id="tspan4092-8-5-5-3-4-0-6-2-11-0"
          style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3595">
       <text
          x="30.942892"
@@ -1332,7 +1301,7 @@
            x="19.552465"
            y="1037.0271"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
       <text
          x="42.830166"
          y="1033.2393"
@@ -1345,7 +1314,7 @@
            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
     </g>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3619">
       <text
          x="42.212418"
@@ -1396,7 +1365,7 @@
            x="14.016749"
            y="1049.8527"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
     </g>
     <rect
        width="196.44218"
diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index d21ee288b2..8ad6b6e715 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
 
   * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
 
-* New external functions should be added to the local ``version.map`` file. See
-  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
-  guides. New external functions should also be added in alphabetical order.
+* New external functions should be exported.
+  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
+  guides.
 
 * Any new API function should be used in ``/app`` test directory.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index b5434715f3..0e13e29e5d 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -5,8 +5,6 @@ if is_ms_compiler
     subdir_done()
 endif
 
-fs = import('fs')
-
 # Defines the order of dependencies evaluation
 subdirs = [
         'common',
@@ -290,57 +288,25 @@ foreach subpath:subdirs
                 install: true)
 
         # now build the shared driver
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
-
-        if not fs.is_file(version_map)
-            if is_ms_linker
-                link_mode = 'mslinker'
-            elif is_windows
-                link_mode = 'mingw'
-            else
-                link_mode = 'gnu'
-            endif
-            version_map = custom_target(lib_name + '_map',
-                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                    input: sources + sources_avx2 + sources_avx512,
-                    output: '_'.join(class, name, 'exports.map'))
-            version_map_path = version_map.full_path()
-            version_map_dep = [version_map]
-            lk_deps = [version_map]
-
-            if is_ms_linker and is_ms_compiler
-                lk_args = ['/def:' + version_map.full_path()]
-            elif is_ms_linker
-                lk_args = ['-Wl,/def:' + version_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-            endif
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
         else
-            version_map_path = version_map
-            version_map_dep = []
-            lk_deps = [version_map]
-
-            if is_windows
-                if is_ms_linker
-                    def_file = custom_target(lib_name + '_def',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_exports.def'.format(lib_name))
-                    lk_deps += [def_file]
-
-                    lk_args = ['-Wl,/def:' + def_file.full_path()]
-                else
-                    mingw_map = custom_target(lib_name + '_mingw',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_mingw.map'.format(lib_name))
-                    lk_deps += [mingw_map]
-
-                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-                endif
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(lib_name + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources + sources_avx2 + sources_avx512,
+                output: '_'.join(class, name, 'exports.map'))
+        lk_deps = [version_map]
+
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
+        else
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
 
         if not is_windows and developer_mode
@@ -348,11 +314,11 @@ foreach subpath:subdirs
             # check-symbols.sh script, using it as a
             # dependency of the .so build
             lk_deps += custom_target(lib_name + '.sym_chk',
-                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
                     capture: true,
                     input: static_lib,
                     output: lib_name + '.sym_chk',
-                    depends: version_map_dep)
+                    depends: [version_map])
         endif
 
         shared_lib = shared_library(lib_name, sources_pmd_info,
diff --git a/lib/meson.build b/lib/meson.build
index 952ae4696f..841e9513dc 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
-fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -255,59 +254,25 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
-        if is_ms_linker
-            link_mode = 'mslinker'
-        elif is_windows
-            link_mode = 'mingw'
-        else
-            link_mode = 'gnu'
-        endif
-        version_map = custom_target(libname + '_map',
-                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                input: sources,
-                output: '_'.join(name, 'exports.map'))
-        version_map_path = version_map.full_path()
-        version_map_dep = [version_map]
-        lk_deps = [version_map]
-
-        if is_ms_linker and is_ms_compiler
-            lk_args = ['/def:' + version_map.full_path()]
-        elif is_ms_linker
-            lk_args = ['-Wl,/def:' + version_map.full_path()]
-        else
-            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-        endif
+    if is_ms_linker
+        link_mode = 'mslinker'
+    elif is_windows
+        link_mode = 'mingw'
     else
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-        version_map_path = version_map
-        version_map_dep = []
-        lk_deps = [version_map]
-        if is_ms_linker
-            def_file = custom_target(libname + '_def',
-                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                    input: version_map,
-                    output: '@0@_exports.def'.format(libname))
-            lk_deps += [def_file]
-
-            if is_ms_compiler
-                lk_args = ['/def:' + def_file.full_path()]
-            else
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
-            endif
-        else
-            if is_windows
-                mingw_map = custom_target(libname + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(libname))
-                lk_deps += [mingw_map]
+        link_mode = 'gnu'
+    endif
+    version_map = custom_target(libname + '_map',
+            command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+            input: sources,
+            output: '_'.join(name, 'exports.map'))
+    lk_deps = [version_map]
 
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
-        endif
+    if is_ms_linker and is_ms_compiler
+        lk_args = ['/def:' + version_map.full_path()]
+    elif is_ms_linker
+        lk_args = ['-Wl,/def:' + version_map.full_path()]
+    else
+        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
     endif
 
     if developer_mode and not is_windows
@@ -315,11 +280,11 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols, version_map_path, '@INPUT@'],
+                command: [check_symbols, version_map.full_path(), '@INPUT@'],
                 capture: true,
                 input: static_lib,
                 output: name + '.sym_chk',
-                depends: version_map_dep)
+                depends: [version_map])
     endif
 
     if not use_function_versioning or is_windows
-- 
2.48.1


^ permalink raw reply	[relevance 16%]

* Re: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
                     ` (2 preceding siblings ...)
  2025-03-17 15:43 16%   ` [RFC v4 7/8] build: use dynamically generated version maps David Marchand
@ 2025-03-18  8:19  0%   ` David Marchand
  2025-03-26 12:02  0%   ` David Marchand
  4 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-18  8:19 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

On Mon, Mar 17, 2025 at 4:43 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
>
>
> Comments please.
>
>
> --
> David Marchand
>
> Depends-on: series-34869 ("remove driver-specific logic for AVX builds")

Erm, looks like the CI did not catch this dependency (maybe I should
have not put it after the signature.. ?).

Anyway... this is 25.07 material, I'll repost once dependencies (Bruce
series) have been merged in next release.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v3] rust: support raw DPDK API
  @ 2025-03-18  8:51  2%   ` Dariusz Sosnowski
  0 siblings, 0 replies; 153+ results
From: Dariusz Sosnowski @ 2025-03-18  8:51 UTC (permalink / raw)
  To: Gregory Etelson
  Cc: bruce.richardson, dev, mkashani, thomas, harry.van.haaren,
	igootorov, stephen

Hi Gregory,

I have been playing around with the patch and I have a few suggestions
which I hope would improve the experience with using the generated bindings.
Please let me know what you think.

First of all, during compilation, there is a lot of warnings regarding naming style.
Basically there is a difference between naming style in C and Rust.
For example:

	warning: type `rte_mempool_mem_cb_t` should have an upper camel case name
	    --> /home/ds/obj/dpdk/lib/rust/src/raw/rte_mempool.rs:6292:10
	     |
	6292 | pub type rte_mempool_mem_cb_t = ::std::option::Option<
	     |          ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to upper camel case: `RteMempoolMemCbT`

In the end, I don't think these are important for bindings and they can be safely ignored.
I think the following should be added to "src/raw/lib.rs":

	#[allow(non_camel_case_types)]
	#[allow(non_upper_case_globals)]
	#[allow(non_snake_case)]

There are also some warnings regarding the use of u128:

	warning: `extern` block uses type `u128`, which is not FFI-safe
	    --> /home/ds/obj/dpdk/lib/rust/src/raw/rte_mempool.rs:4170:18
	     |
	4170 |         __value: u128,
	     |                  ^^^^ not FFI-safe
	     |
	     = note: 128-bit integers don't currently have a known stable ABI

There's an open issue for that with more information and discussion:
https://github.com/rust-lang/rust/issues/78473

These warnings are caused by declaration of the following functions in stdlib.h
(which is transitively included by DPDK headers):

	qecvt_r
	qfcvt_r
	qecvt
	qfcvt
	qgcvt
	strtold

These are not part of DPDK public API and removing their bindings
should not be an issue in my opinion.
I think we can just remove them from bindings with '--blocklist-function' argument.
What do you think?

On Fri, Mar 14, 2025 at 08:38:15PM +0200, Gregory Etelson wrote:
> The patch converts include files with DPDK API to RUST and binds new
> RUST API files into raw module under dpdk crate.
> 
> The RUST files and DPDK libraries build from C sources
> allow creation of DPDK application in RUST.
> 
> RUST DPDK application must specify the `dpdk` crate as
> dependency in Cargo.toml file.
> 
> RUST `dpdk` crate is installed into
> $MESON_INSTALL_DESTDIR_PREFIX/$libdir/rust directory.
> 
> Software requirements:
> - clang
> - RUST installation
> - bindgen-cli crate
> 
> RUST dpdk installation instructions:
> 1. Configure DPDK with `-Deanble_rust=true`
> 2. Build and install DPDK. The installation procedure will create
>    $MESON_INSTALL_DESTDIR_PREFIX/$libdir/rust crate.
> 3. Update PKG_CONFIG_PATH to point to DPDK installation.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---
> v2:
> Change rust crate name from dpdklib to dpdk.
> Add raw module for to link with C API.
> Add "cargo:rerun-if-changed=build.rs".
> v3:
> Move init_port_config() to Port.
> Move start_port() to Port.
> Remove Cargo.lock from git repository
> Reformat code.
> ---
>  buildtools/meson.build               |   4 +
>  buildtools/rust-env.sh               |  81 ++++++++++
>  examples/rust/helloworld/Cargo.toml  |   7 +
>  examples/rust/helloworld/build.rs    |  24 +++
>  examples/rust/helloworld/src/main.rs | 219 +++++++++++++++++++++++++++
>  meson_options.txt                    |   2 +
>  6 files changed, 337 insertions(+)
>  create mode 100755 buildtools/rust-env.sh
>  create mode 100644 examples/rust/helloworld/Cargo.toml
>  create mode 100644 examples/rust/helloworld/build.rs
>  create mode 100644 examples/rust/helloworld/src/main.rs
> 
> diff --git a/buildtools/meson.build b/buildtools/meson.build
> index 4e2c1217a2..b9d0092f07 100644
> --- a/buildtools/meson.build
> +++ b/buildtools/meson.build
> @@ -50,3 +50,7 @@ else
>      pmdinfo += 'ar'
>      pmdinfogen += 'elf'
>  endif
> +
> +if get_option('enable_rust')
> +    meson.add_install_script(['rust-env.sh', get_option('libdir')])
> +endif
> diff --git a/buildtools/rust-env.sh b/buildtools/rust-env.sh
> new file mode 100755
> index 0000000000..fe0877643b
> --- /dev/null
> +++ b/buildtools/rust-env.sh
> @@ -0,0 +1,81 @@
> +#! /bin/sh
> +
> +# Convert DPDK API files into RUST.
> +# DPDK files selection is on demand.
> +#
> +# The coversion is done in 4 stages:
> +# 1. Preparation [Optional]
> +#    Due to the bindgen conversion utility limitations source file may need
> +#    manual adjustment.
> +# 2. Preprocessing [Mandatory]
> +#    Run preprocessor on a source file before conversion.
> +# 3. Conversion [Mandatory]
> +#    Convert preprocessed C file into RUST file
> +# 4. Post translation [Optional]
> +#    Manually fix translation.
> +
> +# DPDK files list
> +files='
> +rte_build_config.h
> +rte_eal.h
> +rte_ethdev.h
> +rte_mbuf.h
> +rte_mbuf_core.h
> +rte_mempool.h
> +'

There is a lot of warnings generated for types redeclaration, for example:

	warning: `rte_mempool_ops_get_info` redeclared with a different signature
	    --> /home/ds/obj/dpdk/lib/rust/src/raw/rte_mbuf.rs:6352:5
	     |
	6352 | /     pub fn rte_mempool_ops_get_info(
	6353 | |         mp: *const rte_mempool,
	6354 | |         info: *mut rte_mempool_info,
	6355 | |     ) -> ::std::os::raw::c_int;
	     | |_______________________________^ this signature doesn't match the previous declaration
	     |
	    ::: /home/ds/obj/dpdk/lib/rust/src/raw/rte_ethdev.rs:8957:5
	     |
	8957 | /     pub fn rte_mempool_ops_get_info(
	8958 | |         mp: *const rte_mempool,
	8959 | |         info: *mut rte_mempool_info,
	8960 | |     ) -> ::std::os::raw::c_int;
	     | |_______________________________- `rte_mempool_ops_get_info` previously declared here
	     |
	     = note: expected `unsafe extern "C" fn(*const rte_ethdev::rte_mempool, *mut rte_ethdev::rte_mempool_info) -> i32`
			found `unsafe extern "C" fn(*const raw::rte_mbuf::rte_mempool, *mut raw::rte_mbuf::rte_mempool_info) -> i32`

These come from the fact that bindings for each header file are generated separately,
but certain types and functions are defined "transitively" in more than one.
rte_mempool for example is defined in rte_mempool.h which is included by both
rte_ethdev.h and rte_mbuf.h
It's ok in C, but in Rust, resulting bindings these types are different
(dpdk::raw::rte_ethdev::rte_mempool != dpdk::raw::rte_mbuf::rte_mempool).
They could be transmuted between one another, but it is an additional obstacle in using the bindings.

What do you think about using a wrapper header?
This is a solution used in Rust for Linux
(https://github.com/torvalds/linux/blob/master/rust/bindings/bindings_helper.h).
Basically, a new header file is added which includes all headers we want to translate.
bindgen is only run on this wrapper header.
This removes the duplication of all definitions.

This comes at a price of having a flat namespace in dpdk::raw::*,
but I'm not sure it's that much important for raw bindings.
Proper namespacing can be introduced by higher level wrappers.

> +libdir="$1"
> +rust_dir="${MESON_INSTALL_DESTDIR_PREFIX}/$libdir/rust"
> +include_dir="${MESON_INSTALL_DESTDIR_PREFIX}/include"
> +
> +if test -d "$rust_dir"; then
> +  rm -rf "$rust_dir"
> +fi
> +
> +mkdir -p "$rust_dir/src/raw"
> +if ! test -d "$rust_dir"; then
> +  echo "failed to create Rust library $rust_dir"
> +  exit 255
> +fi
> +
> +bindgen_opt='--no-layout-tests --no-derive-debug'
> +bindgen_clang_opt='-Wno-unused-command-line-argument'
> +
> +create_rust_lib ()
> +{
> +  base=$1
> +
> +  cp $include_dir/${base}.h /tmp/${base}.h
> +
> +# bindgen cannot process complex macro definitions
> +# manually simplify macros before conversion
> +  sed -i -e 's/RTE_BIT64(\([0-9]*\))/(1UL << \1)/g' /tmp/${base}.h
> +  sed -i -e 's/RTE_BIT32(\([0-9]*\))/(1U << \1)/g' /tmp/${base}.h
> +  sed -i -e 's/UINT64_C(\([0-9]*\))/\1/g' /tmp/${base}.h
> +
> +  # clang output has better integration with bindgen than GCC
> +  clang -E -dD -I$include_dir /tmp/${base}.h > /tmp/$base.i
> +  bindgen $bindgen_opt --output $rust_dir/src/raw/$base.rs /tmp/$base.i -- $bindgen_clang_opt
> +  rm -f /tmp/$base.i /tmp/$base.h
> +}
> +
> +echo 'pub mod raw;' > "$rust_dir/src/lib.rs"
> +
> +touch "$rust_dir/src/raw/mod.rs"
> +for file in $files; do
> +  base=$(basename $file | cut -d. -f 1)
> +  create_rust_lib $base
> +  echo "pub mod $base;" >> "$rust_dir/src/raw/mod.rs"
> +done
> +
> +cat > "$rust_dir/Cargo.toml" <<EOF
> +[package]
> +name = "dpdk"
> +version = "$(cat ${MESON_SOURCE_ROOT}/VERSION | sed 's/\.0\([1-9]\)/\.\1/')"
> +EOF
> +
> +# post conversion updates
> +# RUST does not accept aligned structures into packed structure.
> +# TODO: fix DPDK definitions.
> +sed -i 's/repr(align(2))/repr(packed(2))/g'  "$rust_dir/src/raw/rte_ethdev.rs"

I have taken a look at specific types with that issue and there is only a few of them.
The following are affected:

	rte_l2tpv2_combined_msg_hdr
	rte_flow_item_l2tpv2
	rte_arp_ipv4
	rte_arp_hdr
	rte_flow_item_arp_eth_ipv4

Before changing DPDK definitions (if possible), we could make bindings for them opaque.
This would basically generate, for each type, a struct with byte array inside,
which would be "naturally" packed (no need for 'repr(packed)').
Reading/modifying specific fields could be written as a higher level getter/setter.

What do you think about making these types opaque through '--opaque-type' bindgen parameter?
This would:

- remove the need for manually replacing align/packed
  (which removes required alignment on rte_ether_addr),
- remove the need to disable layout tests
  (which are very good to have),
- "document" the list of problematic types.

> +
> +echo "Install RUST DPDK crate in $rust_dir"
> diff --git a/examples/rust/helloworld/Cargo.toml b/examples/rust/helloworld/Cargo.toml
> new file mode 100644
> index 0000000000..ceeecf958d
> --- /dev/null
> +++ b/examples/rust/helloworld/Cargo.toml
> @@ -0,0 +1,7 @@
> +[package]
> +name = "helloworld"
> +version = "0.1.0"
> +edition = "2024"
> +
> +[dependencies]
> +dpdk = {path = $MESON_INSTALL_DESTDIR_PREFIX/$libdir/rust }
> diff --git a/examples/rust/helloworld/build.rs b/examples/rust/helloworld/build.rs
> new file mode 100644
> index 0000000000..bd5737d209
> --- /dev/null
> +++ b/examples/rust/helloworld/build.rs
> @@ -0,0 +1,24 @@
> +use std::process::Command;
> +
> +pub fn main() {
> +    let mut pkgconfig = Command::new("pkg-config");
> +
> +    match pkgconfig.args(["--libs", "libdpdk"]).output() {
> +        Ok(output) => {
> +            let stdout = String::from_utf8_lossy(&output.stdout)
> +                .trim_end()
> +                .to_string();
> +            for token in stdout.split_ascii_whitespace().filter(|s| !s.is_empty()) {
> +                if token.starts_with("-L") {
> +                    println!("cargo::rustc-link-search=native={}", &token[2..]);
> +                } else if token.starts_with("-l") {
> +                    println!("cargo::rustc-link-lib={}", &token[2..]);
> +                }
> +            }
> +            println!("cargo:rerun-if-changed=build.rs");
> +        }
> +        Err(error) => {
> +            panic!("failed to read libdpdk package: {:?}", error);
> +        }
> +    }
> +}


What do you think about using pkg_config crate in build scripts?
(https://crates.io/crates/pkg-config)

This would allow to remove manual command execution and output parsing with the following:

	fn main() {
	  pkg_config::probe_library("libdpdk").expect("Unable to find libdpdk");
	}

Also, what do you think about moving this logic to dpdk crate?
This way dependency resolution would be shared by all examples/applications
which use the dpdk crate.

> diff --git a/examples/rust/helloworld/src/main.rs b/examples/rust/helloworld/src/main.rs
> new file mode 100644
> index 0000000000..0c057cb39f
> --- /dev/null
> +++ b/examples/rust/helloworld/src/main.rs
> @@ -0,0 +1,219 @@
> +/// Usage: helloworld -a <port 1 params> -a <port 2 params> ...
> +use std::env;
> +use std::ffi::CStr;
> +use std::ffi::CString;
> +use std::os::raw::{c_char, c_int};
> +
> +use dpdk::raw::rte_eal::{
> +    rte_eal_cleanup,
> +    // Functions
> +    rte_eal_init,
> +};
> +
> +use dpdk::raw::rte_ethdev::{
> +    RTE_ETH_DEV_NO_OWNER,
> +    RTE_ETH_NAME_MAX_LEN,
> +    RTE_ETH_RSS_IP,
> +    rte_eth_conf,
> +    rte_eth_dev_configure,
> +    // Functions
> +    rte_eth_dev_get_name_by_port,
> +    // Structures
> +    rte_eth_dev_info,
> +    rte_eth_dev_info_get,
> +    rte_eth_dev_start,
> +    rte_eth_find_next_owned_by,
> +    rte_eth_rx_mq_mode_RTE_ETH_MQ_RX_RSS,
> +    rte_eth_rx_mq_mode_RTE_ETH_MQ_RX_VMDQ_DCB_RSS,
> +
> +    rte_eth_rx_queue_setup,
> +    rte_eth_rxconf,
> +
> +    rte_eth_tx_queue_setup,
> +    rte_eth_txconf,
> +};
> +
> +use dpdk::raw::rte_build_config::RTE_MAX_ETHPORTS;
> +
> +use dpdk::raw::rte_mbuf::rte_pktmbuf_pool_create;
> +
> +use dpdk::raw::rte_mbuf_core::RTE_MBUF_DEFAULT_BUF_SIZE;
> +
> +pub type DpdkPort = u16;
> +pub struct Port {
> +    pub port_id: DpdkPort,
> +    pub dev_info: rte_eth_dev_info,
> +    pub dev_conf: rte_eth_conf,
> +    pub rxq_num: u16,
> +    pub txq_num: u16,
> +}
> +
> +impl Port {
> +    unsafe fn new(id: DpdkPort) -> Self {
> +        Port {
> +            port_id: id,
> +            dev_info: unsafe {
> +                let uninit: ::std::mem::MaybeUninit<rte_eth_dev_info> =
> +                    ::std::mem::MaybeUninit::zeroed().assume_init();
> +                *uninit.as_ptr()
> +            },
> +            dev_conf: unsafe {
> +                let uninit: ::std::mem::MaybeUninit<rte_eth_conf> =
> +                    ::std::mem::MaybeUninit::zeroed().assume_init();
> +                *uninit.as_ptr()
> +            },
> +            rxq_num: 1,
> +            txq_num: 1,
> +        }
> +    }
> +
> +    pub unsafe fn init_port_config(&mut self) {
> +        let ret = unsafe {
> +            rte_eth_dev_info_get(self.port_id, &mut self.dev_info as *mut rte_eth_dev_info)
> +        };
> +        if ret != 0 {
> +            panic!("self-{}: failed to get dev info {ret}", self.port_id);
> +        }
> +
> +        self.dev_conf.rx_adv_conf.rss_conf.rss_key = std::ptr::null_mut();
> +        self.dev_conf.rx_adv_conf.rss_conf.rss_hf = if self.rxq_num > 1 {
> +            RTE_ETH_RSS_IP as u64 & self.dev_info.flow_type_rss_offloads
> +        } else {
> +            0
> +        };
> +
> +        if self.dev_conf.rx_adv_conf.rss_conf.rss_hf != 0 {
> +            self.dev_conf.rxmode.mq_mode = rte_eth_rx_mq_mode_RTE_ETH_MQ_RX_VMDQ_DCB_RSS
> +                & rte_eth_rx_mq_mode_RTE_ETH_MQ_RX_RSS;
> +        }
> +    }
> +
> +    unsafe fn start_port(&mut self) {
> +        let mut rc = unsafe {
> +            rte_eth_dev_configure(
> +                self.port_id,
> +                self.rxq_num,
> +                self.txq_num,
> +                &self.dev_conf as *const rte_eth_conf,
> +            )
> +        };
> +        if rc != 0 {
> +            panic!("failed to configure self-{}: {rc}", self.port_id)
> +        }
> +        println!("self-{} configured", self.port_id);
> +
> +        rc = unsafe { rte_eth_tx_queue_setup(self.port_id, 0, 64, 0, 0 as *const rte_eth_txconf) };
> +        if rc != 0 {
> +            panic!("self-{}: failed to configure TX queue 0 {rc}", self.port_id)
> +        }
> +        println!("self-{} configured TX queue 0", self.port_id);
> +
> +        let mbuf_pool_name = CString::new(format!("mbuf pool self-{}", self.port_id)).unwrap();
> +        let mbuf_pool: *mut dpdk::raw::rte_mbuf::rte_mempool = unsafe {
> +            rte_pktmbuf_pool_create(
> +                mbuf_pool_name.as_ptr(),
> +                1024,
> +                0,
> +                0,
> +                RTE_MBUF_DEFAULT_BUF_SIZE as u16,
> +                0,
> +            )
> +        };
> +        if mbuf_pool == 0 as *mut dpdk::raw::rte_mbuf::rte_mempool {
> +            panic!("self-{}: failed to allocate mempool {rc}", self.port_id)
> +        }
> +        println!("self-{} mempool ready", self.port_id);
> +
> +        let mut rxq_conf: rte_eth_rxconf = self.dev_info.default_rxconf.clone();
> +        rxq_conf.offloads = 0;
> +        rc = unsafe {
> +            rte_eth_rx_queue_setup(
> +                self.port_id,
> +                0,
> +                64,
> +                0,
> +                &mut rxq_conf as *mut rte_eth_rxconf,
> +                mbuf_pool as *mut dpdk::raw::rte_ethdev::rte_mempool,
> +            )
> +        };
> +        if rc != 0 {
> +            panic!("self-{}: failed to configure RX queue 0 {rc}", self.port_id)
> +        }
> +        println!("self-{} configured RX queue 0", self.port_id);
> +        rc = unsafe { rte_eth_dev_start(self.port_id) };
> +        if rc != 0 {
> +            panic!("failed to start self-{}: {rc}", self.port_id)
> +        }
> +        println!("self-{} started", self.port_id);
> +    }
> +}
> +
> +pub unsafe fn iter_rte_eth_dev_owned_by(owner_id: u64) -> impl Iterator<Item = DpdkPort> {
> +    let mut port_id: DpdkPort = 0 as DpdkPort;
> +    std::iter::from_fn(move || {
> +        let cur = port_id;
> +        port_id = unsafe { rte_eth_find_next_owned_by(cur, owner_id) as DpdkPort };
> +        if port_id == RTE_MAX_ETHPORTS as DpdkPort {
> +            return None;
> +        }
> +        if cur == port_id {
> +            port_id += 1
> +        }
> +        Some(cur)
> +    })
> +}
> +
> +pub unsafe fn iter_rte_eth_dev() -> impl Iterator<Item = DpdkPort> {
> +    unsafe { iter_rte_eth_dev_owned_by(RTE_ETH_DEV_NO_OWNER as u64) }
> +}
> +
> +pub unsafe fn show_ports_summary(ports: &Vec<Port>) {
> +    let mut name_buf: [c_char; RTE_ETH_NAME_MAX_LEN as usize] =
> +        [0 as c_char; RTE_ETH_NAME_MAX_LEN as usize];
> +    let title = format!("{:<4}    {:<32} {:<14}", "Port", "Name", "Driver");
> +    println!("{title}");
> +    ports.iter().for_each(|p| unsafe {
> +        let _rc = rte_eth_dev_get_name_by_port(p.port_id, name_buf.as_mut_ptr());
> +        let name = CStr::from_ptr(name_buf.as_ptr());
> +        let drv = CStr::from_ptr(p.dev_info.driver_name);
> +        let summary = format!(
> +            "{:<4}    {:<32} {:<14}",
> +            p.port_id,
> +            name.to_str().unwrap(),
> +            drv.to_str().unwrap()
> +        );
> +        println!("{summary}");
> +    });
> +}
> +
> +fn main() {
> +    let mut argv: Vec<*mut c_char> = env::args()
> +        .map(|arg| CString::new(arg).unwrap().into_raw())
> +        .collect();
> +
> +    let rc = unsafe { rte_eal_init(env::args().len() as c_int, argv.as_mut_ptr()) };
> +    if rc == -1 {
> +        unsafe {
> +            rte_eal_cleanup();
> +        }
> +    }
> +
> +    let mut ports: Vec<Port> = vec![];
> +    unsafe {
> +        for port_id in
> +            iter_rte_eth_dev().take(dpdk::raw::rte_build_config::RTE_MAX_ETHPORTS as usize)
> +        {
> +            let mut port = Port::new(port_id);
> +            port.init_port_config();
> +            println!("init port {port_id}");
> +            port.start_port();
> +            ports.push(port);
> +        }
> +    }
> +
> +    unsafe {
> +        show_ports_summary(&ports);
> +    }
> +
> +    println!("Hello, world!");
> +}
> diff --git a/meson_options.txt b/meson_options.txt
> index e49b2fc089..d37b9ba1dc 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -60,3 +60,5 @@ option('tests', type: 'boolean', value: true, description:
>         'build unit tests')
>  option('use_hpet', type: 'boolean', value: false, description:
>         'use HPET timer in EAL')
> +option('enable_rust', type: 'boolean', value: false, description:
> +       'enable RUST')
> -- 
> 2.45.2
> 

Best regards,
Dariusz Sosnowski

^ permalink raw reply	[relevance 2%]

* [PATCH v2] raw/cnxk_gpio: switch to character based GPIO interface
  2025-03-14 12:57  1% [PATCH] raw/cnxk_gpio: switch to character based GPIO interface Tomasz Duszynski
@ 2025-03-24  8:28  1% ` Tomasz Duszynski
  2025-03-25  4:14  1%   ` [PATCH v3] " Tomasz Duszynski
  0 siblings, 1 reply; 153+ results
From: Tomasz Duszynski @ 2025-03-24  8:28 UTC (permalink / raw)
  To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj

The direct passthrough interrupt mechanism, which allowed bypassing the
kernel, was obscure and is no longer supported. So this driver won't
work with latest SDK kernels. Additionally, the sysfs GPIO control
interface has been deprecated by Linux kernel itself.

That said, this change updates the PMD to use the current GPIO interface
ensuring compatibility with current kernel standards while improving
maintainability and security.

Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
---
v2:
- compile conditionally based on GPIO_V2_PRESENT

 doc/guides/rawdevs/cnxk_gpio.rst           |  37 +-
 drivers/raw/cnxk_gpio/cnxk_gpio.c          | 525 ++++++++++++---------
 drivers/raw/cnxk_gpio/cnxk_gpio.h          |  17 +-
 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c      | 216 ---------
 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 235 ++-------
 drivers/raw/cnxk_gpio/meson.build          |   1 -
 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h  |  64 ++-
 7 files changed, 441 insertions(+), 654 deletions(-)
 delete mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c

diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst
index 954d3b8905..8084dd4adb 100644
--- a/doc/guides/rawdevs/cnxk_gpio.rst
+++ b/doc/guides/rawdevs/cnxk_gpio.rst
@@ -6,31 +6,20 @@ Marvell CNXK GPIO Driver

 CNXK GPIO PMD configures and manages GPIOs available on the system using
 standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies
-both on standard sysfs GPIO interface provided by the Linux kernel and GPIO
-kernel driver custom interface allowing one to install userspace interrupt
-handlers.
+on standard kernel GPIO character device interface.

 Features
 --------

 Following features are available:

-- export/unexport a GPIO
-- read/write specific value from/to exported GPIO
+- read/write specific value from/to GPIO
 - set GPIO direction
 - set GPIO edge that triggers interrupt
 - set GPIO active low
 - register interrupt handler for specific GPIO
 - multiprocess aware

-Requirements
-------------
-
-PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface
-for installing interrupt handlers for low latency signal processing.
-
-Driver is shipped with Marvell SDK.
-
 Limitations
 -----------

@@ -43,20 +32,20 @@ Device Setup
 CNXK GPIO PMD binds to virtual device which gets created by passing
 `--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter
 tells PMD which GPIO controller should be used. Available controllers are
-available under `/sys/class/gpio`. For further details on how Linux represents
-GPIOs in userspace please refer to
-`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_.
+`/dev/gpiochipN` character devices. For further details on
+how Linux represents GPIOs in userspace please refer to
+`gpio-cdev <https://www.kernel.org/doc/Documentation/ABI/testing/gpio-cdev>`_.

 If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically
 sort list of available gpiochips is used.

 .. code-block:: console

-   $ ls /sys/class/gpio
-   export gpiochip448 unexport
+   $ ls /dev/gpiochip*
+   /dev/gpiochip0

 In above scenario only one GPIO controller is present hence
-`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL.
+`--vdev=cnxk_gpio,gpiochip=0` should be passed to EAL.

 Before performing actual data transfer one needs to call
 ``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The
@@ -65,7 +54,7 @@ being controllable or not. Thus it is user responsibility to pick the proper
 ones. The latter call simply returns queue capacity.

 In order to allow using only subset of available GPIOs `allowlist` PMD param may
-be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]`
+be used. For example passing `--vdev=cnxk_gpio,gpiochip=0,allowlist=[0,1,2,3]`
 to EAL will deny using all GPIOs except those specified explicitly in the
 `allowlist`.

@@ -179,12 +168,12 @@ Request interrupt

 Message is used to install custom interrupt handler.

-Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``.
+Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2``.

-Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt
+Payload needs to be set to ``struct cnxk_gpio_irq2`` which describes interrupt
 being requested.

-Consider using ``rte_pmd_gpio_register_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_register_irq2()`` wrapper.

 Free interrupt
 ~~~~~~~~~~~~~~
@@ -193,7 +182,7 @@ Message is used to remove installed interrupt handler.

 Message must have type set to ``CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ``.

-Consider using ``rte_pmd_gpio_unregister_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_unregister_irq()`` wrapper.

 Self test
 ---------
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c
index 329ac28a27..08384a8b09 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c
@@ -3,11 +3,19 @@
  */

 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+#include <regex.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/types.h>

 #include <bus_vdev_driver.h>
 #include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_interrupts.h>
 #include <rte_kvargs.h>
 #include <rte_lcore.h>
 #include <rte_rawdev_pmd.h>
@@ -17,9 +25,12 @@
 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"

-#define CNXK_GPIO_BUFSZ 128
-#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio"
 #define CNXK_GPIO_PARAMS_MZ_NAME "cnxk_gpio_params_mz"
+#define CNXK_GPIO_INVALID_FD (-1)
+
+RTE_LOG_REGISTER_SUFFIX(cnxk_logtype_gpio, gpio, INFO);
+
+#ifdef GPIO_V2_PRESENT

 struct cnxk_gpio_params {
 	unsigned int num;
@@ -40,6 +51,31 @@ cnxk_gpio_format_name(char *name, size_t len)
 	snprintf(name, len, "cnxk_gpio");
 }

+static int
+cnxk_gpio_ioctl(struct cnxk_gpio *gpio, unsigned long cmd, void *arg)
+{
+	return ioctl(gpio->fd, cmd, arg) ? -errno : 0;
+}
+
+static int
+cnxk_gpio_gpiochip_ioctl(struct cnxk_gpiochip *gpiochip, unsigned long cmd, void *arg)
+{
+	char path[PATH_MAX];
+	int ret = 0, fd;
+
+	snprintf(path, sizeof(path), "/dev/gpiochip%d", gpiochip->num);
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -errno;
+
+	if (ioctl(fd, cmd, arg))
+		ret = -errno;
+
+	close(fd);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_filter_gpiochip(const struct dirent *dirent)
 {
@@ -54,8 +90,7 @@ cnxk_gpio_set_defaults(struct cnxk_gpio_params *params)
 	struct dirent **namelist;
 	int ret = 0, n;

-	n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip,
-		    alphasort);
+	n = scandir("/dev", &namelist, cnxk_gpio_filter_gpiochip, alphasort);
 	if (n < 0 || n == 0)
 		return -ENODEV;

@@ -143,7 +178,7 @@ cnxk_gpio_parse_arg(struct rte_kvargs *kvlist, const char *arg, arg_handler_t ha
 static int
 cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 {
-	size_t len = sizeof(**params);
+	size_t len = sizeof(**params) + 1;
 	const char *allowlist = NULL;
 	struct rte_kvargs *kvlist;
 	int ret;
@@ -163,11 +198,13 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)

 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_ALLOWLIST, cnxk_gpio_parse_arg_allowlist,
 				  &allowlist);
-	if (ret < 0)
+	if (ret < 0) {
+		ret = -EINVAL;
 		goto out;
+	}

 	if (allowlist)
-		len += strlen(allowlist) + 1;
+		len += strlen(allowlist);

 	*params = cnxk_gpio_params_reserve(len);
 	if (!(*params)) {
@@ -175,7 +212,8 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 		goto out;
 	}

-	strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);
+	if (allowlist)
+		strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);

 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_GPIOCHIP, cnxk_gpio_parse_arg_gpiochip,
 				  &(*params)->num);
@@ -188,6 +226,24 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 	return ret;
 }

+static bool
+cnxk_gpio_allowlist_valid(const char *allowlist)
+{
+	bool ret = false;
+	regex_t regex;
+
+	/* [gpio0<,gpio1,...,gpioN>], where '<...>' is optional part */
+	if (regcomp(&regex, "^\\[[0-9]+(,[0-9]+)*\\]$", REG_EXTENDED))
+		return ret;
+
+	if (!regexec(&regex, allowlist, 0, NULL, 0))
+		ret = true;
+
+	regfree(&regex);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 {
@@ -199,6 +255,17 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	if (!list)
 		return -ENOMEM;

+	/* no gpios provided so allow all */
+	if (!*allowlist) {
+		for (i = 0; i < gpiochip->num_gpios; i++)
+			list[queue++] = i;
+
+		goto out_done;
+	}
+
+	if (!cnxk_gpio_allowlist_valid(allowlist))
+		return -EINVAL;
+
 	allowlist = strdup(allowlist);
 	if (!allowlist) {
 		ret = -ENOMEM;
@@ -239,6 +306,7 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	} while ((token = strtok(NULL, ",")));

 	free(allowlist);
+out_done:
 	gpiochip->allowlist = list;
 	gpiochip->num_queues = queue;

@@ -250,88 +318,6 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	return ret;
 }

-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-static int
-cnxk_gpio_read_attr_int(char *attr, int *val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret;
-
-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	ret = sscanf(buf, "%d", val);
-	if (ret < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr(const char *attr, const char *val)
-{
-	FILE *fp;
-	int ret;
-
-	if (!val)
-		return -EINVAL;
-
-	fp = fopen(attr, "w");
-	if (!fp)
-		return -errno;
-
-	ret = fprintf(fp, "%s", val);
-	if (ret < 0) {
-		fclose(fp);
-		return ret;
-	}
-
-	ret = fclose(fp);
-	if (ret)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr_int(const char *attr, int val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-
-	snprintf(buf, sizeof(buf), "%d", val);
-
-	return cnxk_gpio_write_attr(attr, buf);
-}
-
 static bool
 cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 {
@@ -353,14 +339,16 @@ cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 }

 static bool
-cnxk_gpio_exists(int num)
+cnxk_gpio_available(struct cnxk_gpio *gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	struct stat st;
+	struct gpio_v2_line_info info = { .offset = gpio->num };
+	int ret;

-	snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, num);
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return false;

-	return !stat(buf, &st);
+	return !(info.flags & GPIO_V2_LINE_FLAG_USED);
 }

 static int
@@ -368,9 +356,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 		      rte_rawdev_obj_t queue_conf, size_t queue_conf_size)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
+	struct gpio_v2_line_request req = {0};
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int ret;

 	RTE_SET_USED(queue_conf);
 	RTE_SET_USED(queue_conf_size);
@@ -386,33 +374,87 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 	if (!gpio)
 		return -ENOMEM;

-	num = cnxk_queue_to_gpio(gpiochip, queue_id);
-	gpio->num = num + gpiochip->base;
+	gpio->num = cnxk_queue_to_gpio(gpiochip, queue_id);
+	gpio->fd = CNXK_GPIO_INVALID_FD;
 	gpio->gpiochip = gpiochip;

-	if (!cnxk_gpio_exists(gpio->num)) {
-		snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH);
-		ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-		if (ret) {
-			rte_free(gpio);
-			return ret;
-		}
-	} else {
-		CNXK_GPIO_LOG(WARNING, "using existing gpio%d", gpio->num);
+	if (!cnxk_gpio_available(gpio)) {
+		rte_free(gpio);
+		return -EBUSY;
+	}
+
+	cnxk_gpio_format_name(req.consumer, sizeof(req.consumer));
+	req.offsets[req.num_lines] = gpio->num;
+	req.num_lines = 1;
+
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINE_IOCTL, &req);
+	if (ret) {
+		rte_free(gpio);
+		return ret;
 	}

-	gpiochip->gpios[num] = gpio;
+	gpio->fd = req.fd;
+	gpiochip->gpios[gpio->num] = gpio;

 	return 0;
 }

+static void
+cnxk_gpio_intr_handler(void *data)
+{
+	struct gpio_v2_line_event event;
+	struct cnxk_gpio *gpio = data;
+	int ret;
+
+	ret = read(gpio->fd, &event, sizeof(event));
+	if (ret != sizeof(event)) {
+		CNXK_GPIO_LOG(ERR, "failed to read gpio%d event data", gpio->num);
+		goto out;
+	}
+	if ((unsigned int)gpio->num != event.offset) {
+		CNXK_GPIO_LOG(ERR, "expected event from gpio%d, received from gpio%d",
+			      gpio->num, event.offset);
+		goto out;
+	}
+
+	if (gpio->intr.handler2)
+		(gpio->intr.handler2)(&event, gpio->intr.data);
+	else if (gpio->intr.handler)
+		(gpio->intr.handler)(gpio->num, gpio->intr.data);
+out:
+	rte_intr_ack(gpio->intr.intr_handle);
+}
+
+static int
+cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+{
+	int ret;
+
+	if (!gpio->intr.intr_handle)
+		return 0;
+
+	ret = rte_intr_disable(gpio->intr.intr_handle);
+	if (ret)
+		return ret;
+
+	ret = rte_intr_callback_unregister_sync(gpio->intr.intr_handle, cnxk_gpio_intr_handler,
+						(void *)-1);
+	if (ret)
+		return ret;
+
+	rte_intr_instance_free(gpio->intr.intr_handle);
+	gpio->intr.intr_handle = NULL;
+
+	return 0;
+}
+
+
 static int
 cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int num;

 	if (!cnxk_gpio_queue_valid(gpiochip, queue_id))
 		return -EINVAL;
@@ -421,10 +463,11 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 	if (!gpio)
 		return -ENODEV;

-	snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH);
-	ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-	if (ret)
-		return ret;
+	if (gpio->intr.intr_handle)
+		cnxk_gpio_unregister_irq(gpio);
+
+	if (gpio->fd != CNXK_GPIO_INVALID_FD)
+		close(gpio->fd);

 	num = cnxk_queue_to_gpio(gpiochip, queue_id);
 	gpiochip->gpios[num] = NULL;
@@ -462,134 +505,218 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev)

 static const struct {
 	enum cnxk_gpio_pin_edge edge;
-	const char *name;
-} cnxk_gpio_edge_name[] = {
-	{ CNXK_GPIO_PIN_EDGE_NONE, "none" },
-	{ CNXK_GPIO_PIN_EDGE_FALLING, "falling" },
-	{ CNXK_GPIO_PIN_EDGE_RISING, "rising" },
-	{ CNXK_GPIO_PIN_EDGE_BOTH, "both" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_edge_flag[] = {
+	{ CNXK_GPIO_PIN_EDGE_NONE, 0 },
+	{ CNXK_GPIO_PIN_EDGE_FALLING, GPIO_V2_LINE_FLAG_EDGE_FALLING },
+	{ CNXK_GPIO_PIN_EDGE_RISING, GPIO_V2_LINE_FLAG_EDGE_RISING },
+	{ CNXK_GPIO_PIN_EDGE_BOTH, GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING },
 };

-static const char *
-cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge)
+static enum gpio_v2_line_flag
+cnxk_gpio_edge_to_flag(enum cnxk_gpio_pin_edge edge)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (cnxk_gpio_edge_name[i].edge == edge)
-			return cnxk_gpio_edge_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if (cnxk_gpio_edge_flag[i].edge == edge)
+			break;
 	}

-	return NULL;
+	return cnxk_gpio_edge_flag[i].flag;
 }

 static enum cnxk_gpio_pin_edge
-cnxk_gpio_name_to_edge(const char *name)
+cnxk_gpio_flag_to_edge(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (!strcmp(cnxk_gpio_edge_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if ((cnxk_gpio_edge_flag[i].flag & flag) == cnxk_gpio_edge_flag[i].flag)
 			break;
 	}

-	return cnxk_gpio_edge_name[i].edge;
+	return cnxk_gpio_edge_flag[i].edge;
 }

 static const struct {
 	enum cnxk_gpio_pin_dir dir;
-	const char *name;
-} cnxk_gpio_dir_name[] = {
-	{ CNXK_GPIO_PIN_DIR_IN, "in" },
-	{ CNXK_GPIO_PIN_DIR_OUT, "out" },
-	{ CNXK_GPIO_PIN_DIR_HIGH, "high" },
-	{ CNXK_GPIO_PIN_DIR_LOW, "low" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_dir_flag[] = {
+	{ CNXK_GPIO_PIN_DIR_IN, GPIO_V2_LINE_FLAG_INPUT },
+	{ CNXK_GPIO_PIN_DIR_OUT, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_HIGH, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_LOW, GPIO_V2_LINE_FLAG_OUTPUT },
 };

-static const char *
-cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir)
+static enum gpio_v2_line_flag
+cnxk_gpio_dir_to_flag(enum cnxk_gpio_pin_dir dir)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (cnxk_gpio_dir_name[i].dir == dir)
-			return cnxk_gpio_dir_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if (cnxk_gpio_dir_flag[i].dir == dir)
+			break;
 	}

-	return NULL;
+	return cnxk_gpio_dir_flag[i].flag;
 }

 static enum cnxk_gpio_pin_dir
-cnxk_gpio_name_to_dir(const char *name)
+cnxk_gpio_flag_to_dir(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (!strcmp(cnxk_gpio_dir_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if ((cnxk_gpio_dir_flag[i].flag & flag) == cnxk_gpio_dir_flag[i].flag)
 			break;
 	}

-	return cnxk_gpio_dir_name[i].dir;
+	return cnxk_gpio_dir_flag[i].dir;
 }

 static int
-cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
+cnxk_gpio_register_irq_compat(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq,
+			      struct cnxk_gpio_irq2 *irq2)
 {
+	struct rte_intr_handle *intr_handle;
 	int ret;

-	ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu);
-	if (ret)
-		return ret;
+	if (!irq && !irq2)
+		return -EINVAL;
+
+	if ((irq && !irq->handler) || (irq2 && !irq2->handler))
+		return -EINVAL;
+
+	if (gpio->intr.intr_handle)
+		return -EEXIST;
+
+	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+	if (!intr_handle)
+		return -ENOMEM;
+
+	if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_VDEV)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_fd_set(intr_handle, gpio->fd)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_callback_register(intr_handle, cnxk_gpio_intr_handler, gpio)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	gpio->intr.intr_handle = intr_handle;

-	gpio->handler = irq->handler;
-	gpio->data = irq->data;
-	gpio->cpu = irq->cpu;
+	if (irq) {
+		gpio->intr.data = irq->data;
+		gpio->intr.handler = irq->handler;
+	} else {
+		gpio->intr.data = irq2->data;
+		gpio->intr.handler2 = irq2->handler;
+	}
+
+	if (rte_intr_enable(gpio->intr.intr_handle)) {
+		ret = -EINVAL;
+		goto out;
+	}

 	return 0;
+out:
+	rte_intr_instance_free(intr_handle);
+
+	return ret;
 }

 static int
-cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
 {
-	return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base);
+	CNXK_GPIO_LOG(WARNING, "using deprecated interrupt registration api");
+
+	return cnxk_gpio_register_irq_compat(gpio, irq, NULL);
+}
+
+static int
+cnxk_gpio_register_irq2(struct cnxk_gpio *gpio, struct cnxk_gpio_irq2 *irq)
+{
+	return cnxk_gpio_register_irq_compat(gpio, NULL, irq);
 }

 static int
 cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 {
 	struct cnxk_gpio_msg *msg = rbuf->buf_addr;
+	struct gpio_v2_line_values values = {0};
+	struct gpio_v2_line_config config = {0};
+	struct gpio_v2_line_info info = {0};
 	enum cnxk_gpio_pin_edge edge;
 	enum cnxk_gpio_pin_dir dir;
-	char buf[CNXK_GPIO_BUFSZ];
 	void *rsp = NULL;
-	int ret, val, n;
+	int ret;

-	n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH,
-		     gpio->num);
+	info.offset = gpio->num;
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return ret;
+
+	info.flags &= ~GPIO_V2_LINE_FLAG_USED;

 	switch (msg->type) {
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data);
+		values.bits = *(int *)msg->data ?  RTE_BIT64(gpio->num) : 0;
+		values.mask = RTE_BIT64(gpio->num);
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
 		edge = *(enum cnxk_gpio_pin_edge *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge));
+		info.flags &= ~(GPIO_V2_LINE_FLAG_EDGE_RISING | GPIO_V2_LINE_FLAG_EDGE_FALLING);
+		info.flags |= cnxk_gpio_edge_to_flag(edge);
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
 		dir = *(enum cnxk_gpio_pin_dir *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir));
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = cnxk_gpio_dir_to_flag(dir);
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		if (dir == CNXK_GPIO_PIN_DIR_HIGH || dir == CNXK_GPIO_PIN_DIR_LOW) {
+			config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
+			config.attrs[config.num_attrs].attr.values = dir == CNXK_GPIO_PIN_DIR_HIGH ?
+								     RTE_BIT64(gpio->num) : 0;
+			config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+			config.num_attrs++;
+		}
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		val = *(int *)msg->data;
-		ret = cnxk_gpio_write_attr_int(buf, val);
+		if (*(int *)msg->data)
+			info.flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+		else
+			info.flags &= ~GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
+		values.mask = RTE_BIT64(gpio->num);
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
 		if (ret)
 			break;

@@ -597,47 +724,35 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 		if (!rsp)
 			return -ENOMEM;

-		*(int *)rsp = val;
+		*(int *)rsp = !!(values.bits & RTE_BIT64(gpio->num));
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf);
+		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_flag_to_edge(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
-		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0);
+		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf);
+		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_flag_to_dir(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(int), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(int *)rsp = val;
+		*(int *)rsp = !!(info.flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
 		break;
 	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ:
 		ret = cnxk_gpio_register_irq(gpio, (struct cnxk_gpio_irq *)msg->data);
 		break;
+	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2:
+		ret = cnxk_gpio_register_irq2(gpio, (struct cnxk_gpio_irq2 *)msg->data);
+		break;
 	case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ:
 		ret = cnxk_gpio_unregister_irq(gpio);
 		break;
@@ -731,11 +846,11 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = {
 static int
 cnxk_gpio_probe(struct rte_vdev_device *dev)
 {
+	struct gpiochip_info gpiochip_info;
 	char name[RTE_RAWDEV_NAME_MAX_LEN];
 	struct cnxk_gpio_params *params;
 	struct cnxk_gpiochip *gpiochip;
 	struct rte_rawdev *rawdev;
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

 	cnxk_gpio_format_name(name, sizeof(name));
@@ -762,25 +877,14 @@ cnxk_gpio_probe(struct rte_vdev_device *dev)

 	gpiochip->num = params->num;

-	ret = cnxk_gpio_irq_init(gpiochip);
-	if (ret)
-		goto out;
-
-	/* read gpio base */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base);
-	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
-		goto out;
-	}
-
 	/* read number of available gpios */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios);
+	ret = cnxk_gpio_gpiochip_ioctl(gpiochip, GPIO_GET_CHIPINFO_IOCTL, &gpiochip_info);
 	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
+		CNXK_GPIO_LOG(ERR, "failed to read /dev/gpiochip%d info", gpiochip->num);
 		goto out;
 	}
+
+	gpiochip->num_gpios = gpiochip_info.lines;
 	gpiochip->num_queues = gpiochip->num_gpios;

 	ret = cnxk_gpio_parse_allowlist(gpiochip, params->allowlist);
@@ -827,15 +931,11 @@ cnxk_gpio_remove(struct rte_vdev_device *dev)
 		if (!gpio)
 			continue;

-		if (gpio->handler)
-			cnxk_gpio_unregister_irq(gpio);
-
 		cnxk_gpio_queue_release(rawdev, gpio->num);
 	}

 	rte_free(gpiochip->allowlist);
 	rte_free(gpiochip->gpios);
-	cnxk_gpio_irq_fini();
 	cnxk_gpio_params_release();
 	rte_rawdev_pmd_release(rawdev);

@@ -851,4 +951,5 @@ RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv);
 RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio,
 		"gpiochip=<int> "
 		"allowlist=<list>");
-RTE_LOG_REGISTER_SUFFIX(cnxk_logtype_gpio, gpio, INFO);
+
+#endif /* GPIO_V2_PRESENT */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h
index 94c8e36977..adc7f90936 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h
@@ -11,20 +11,24 @@ extern int cnxk_logtype_gpio;
 #define CNXK_GPIO_LOG(level, ...) \
 	RTE_LOG_LINE(level, CNXK_GPIO, __VA_ARGS__)

+struct gpio_v2_line_event;
 struct cnxk_gpiochip;

 struct cnxk_gpio {
 	struct cnxk_gpiochip *gpiochip;
+	struct {
+		struct rte_intr_handle *intr_handle;
+		void (*handler)(int gpio, void *data);
+		void (*handler2)(struct gpio_v2_line_event *event, void *data);
+		void *data;
+	} intr;
 	void *rsp;
 	int num;
-	void (*handler)(int gpio, void *data);
-	void *data;
-	int cpu;
+	int fd;
 };

 struct cnxk_gpiochip {
 	int num;
-	int base;
 	int num_gpios;
 	int num_queues;
 	struct cnxk_gpio **gpios;
@@ -33,9 +37,4 @@ struct cnxk_gpiochip {

 int cnxk_gpio_selftest(uint16_t dev_id);

-int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip);
-void cnxk_gpio_irq_fini(void);
-int cnxk_gpio_irq_request(int gpio, int cpu);
-int cnxk_gpio_irq_free(int gpio);
-
 #endif /* _CNXK_GPIO_H_ */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
deleted file mode 100644
index 2fa8e69899..0000000000
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(C) 2021 Marvell.
- */
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/queue.h>
-#include <unistd.h>
-
-#include <rte_rawdev_pmd.h>
-
-#include <roc_api.h>
-
-#include "cnxk_gpio.h"
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_SET_GPIO_HANDLER                                               \
-	_IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data)
-#define OTX_IOC_CLR_GPIO_HANDLER                                               \
-	_IO(OTX_IOC_MAGIC, 2)
-
-struct otx_gpio_usr_data {
-	uint64_t isr_base;
-	uint64_t sp;
-	uint64_t cpu;
-	uint64_t gpio_num;
-};
-
-struct cnxk_gpio_irq_stack {
-	LIST_ENTRY(cnxk_gpio_irq_stack) next;
-	void *sp_buffer;
-	int cpu;
-	int inuse;
-};
-
-struct cnxk_gpio_irqchip {
-	int fd;
-	/* serialize access to this struct */
-	pthread_mutex_t lock;
-	LIST_HEAD(, cnxk_gpio_irq_stack) stacks;
-
-	struct cnxk_gpiochip *gpiochip;
-};
-
-static struct cnxk_gpio_irqchip *irqchip;
-
-static void
-cnxk_gpio_irq_stack_free(int cpu)
-{
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (!stack)
-		return;
-
-	if (stack->inuse)
-		stack->inuse--;
-
-	if (stack->inuse == 0) {
-		LIST_REMOVE(stack, next);
-		rte_free(stack->sp_buffer);
-		rte_free(stack);
-	}
-}
-
-static void *
-cnxk_gpio_irq_stack_alloc(int cpu)
-{
-#define ARM_STACK_ALIGNMENT (2 * sizeof(void *))
-#define IRQ_STACK_SIZE 0x200000
-
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (stack) {
-		stack->inuse++;
-		return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-	}
-
-	stack = rte_malloc(NULL, sizeof(*stack), 0);
-	if (!stack)
-		return NULL;
-
-	stack->sp_buffer =
-		rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT);
-	if (!stack->sp_buffer) {
-		rte_free(stack);
-		return NULL;
-	}
-
-	stack->cpu = cpu;
-	stack->inuse = 1;
-	LIST_INSERT_HEAD(&irqchip->stacks, stack, next);
-
-	return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-}
-
-static void
-cnxk_gpio_irq_handler(int gpio_num)
-{
-	struct cnxk_gpiochip *gpiochip = irqchip->gpiochip;
-	struct cnxk_gpio *gpio;
-
-	if (gpio_num >= gpiochip->num_gpios)
-		goto out;
-
-	gpio = gpiochip->gpios[gpio_num];
-	if (likely(gpio->handler))
-		gpio->handler(gpio_num, gpio->data);
-
-out:
-	roc_atf_ret();
-}
-
-int
-cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip)
-{
-	if (irqchip)
-		return 0;
-
-	irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0);
-	if (!irqchip)
-		return -ENOMEM;
-
-	irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (irqchip->fd < 0) {
-		rte_free(irqchip);
-		return -errno;
-	}
-
-	pthread_mutex_init(&irqchip->lock, NULL);
-	LIST_INIT(&irqchip->stacks);
-	irqchip->gpiochip = gpiochip;
-
-	return 0;
-}
-
-void
-cnxk_gpio_irq_fini(void)
-{
-	if (!irqchip)
-		return;
-
-	close(irqchip->fd);
-	rte_free(irqchip);
-	irqchip = NULL;
-}
-
-int
-cnxk_gpio_irq_request(int gpio, int cpu)
-{
-	struct otx_gpio_usr_data data;
-	void *sp;
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	sp = cnxk_gpio_irq_stack_alloc(cpu);
-	if (!sp) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
-
-	data.isr_base = (uint64_t)cnxk_gpio_irq_handler;
-	data.sp = (uint64_t)sp;
-	data.cpu = (uint64_t)cpu;
-	data.gpio_num = (uint64_t)gpio;
-
-	mlockall(MCL_CURRENT | MCL_FUTURE);
-	ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data);
-	if (ret) {
-		ret = -errno;
-		goto out_free_stack;
-	}
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-
-out_free_stack:
-	cnxk_gpio_irq_stack_free(cpu);
-out_unlock:
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return ret;
-}
-
-int
-cnxk_gpio_irq_free(int gpio)
-{
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio);
-	if (ret) {
-		pthread_mutex_unlock(&irqchip->lock);
-		return -errno;
-	}
-
-	cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu);
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-}
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
index a0d9942f20..e82d6ccf6b 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
@@ -3,102 +3,33 @@
  */

 #include <fcntl.h>
+#include <linux/gpio.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>

 #include <rte_cycles.h>
+#include <rte_io.h>
 #include <rte_rawdev.h>
 #include <rte_rawdev_pmd.h>
-#include <rte_service.h>

 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"

-#define CNXK_GPIO_BUFSZ 128
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_TRIGGER_GPIO_HANDLER                                           \
-	_IO(OTX_IOC_MAGIC, 3)
-
-static int fd;
-
-static int
-cnxk_gpio_attr_exists(const char *attr)
-{
-	struct stat st;
-
-	return !stat(attr, &st);
-}
-
-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                  \
-	if (err) {                                                             \
-		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, \
-			##__VA_ARGS__, err);                                   \
-		goto out;                                                      \
-	}                                                                      \
+#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                                      \
+	if (err) {                                                                                 \
+		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, ##__VA_ARGS__, err); \
+		goto out;                                                                          \
+	}                                                                                          \
 } while (0)

 static int
-cnxk_gpio_validate_attr(char *attr, const char *expected)
+cnxk_gpio_test_input(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	if (strncmp(buf, expected, sizeof(buf)))
-		return -EIO;
-
-	return 0;
-}
-
-#define CNXK_GPIO_PATH_FMT "/sys/class/gpio/gpio%d"
-
-static int
-cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, n;
-
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
-
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
-	ret = cnxk_gpio_validate_attr(buf, "in");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1) |
 	      rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
@@ -107,29 +38,17 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 		CNXK_GPIO_ERR_STR(ret, "input pin overwritten");
 	}

-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "falling");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "rising");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "both");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	/*
 	 * calling this makes sure kernel driver switches off inverted
@@ -141,44 +60,36 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 	return ret;
 }

-static int
-cnxk_gpio_trigger_irq(int gpio)
-{
-	int ret;
-
-	ret = ioctl(fd, OTX_IOC_TRIGGER_GPIO_HANDLER, gpio);
-
-	return ret == -1 ? -errno : 0;
-}
+static uint32_t triggered;

 static void
-cnxk_gpio_irq_handler(int gpio, void *data)
+cnxk_gpio_irq_handler(struct gpio_v2_line_event *event, void *data)
 {
-	*(int *)data = gpio;
+	int gpio = (int)(size_t)data;
+
+	if ((int)event->offset != gpio)
+		CNXK_GPIO_LOG(ERR, "event from gpio%d instead of gpio%d", event->offset, gpio);
+
+	rte_write32(1, &triggered);
 }

 static int
 cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 {
-	int irq_data, ret;
+	int ret;

 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");

-	irq_data = 0;
-	ret = rte_pmd_gpio_register_irq(dev_id, gpio, rte_lcore_id(),
-					cnxk_gpio_irq_handler, &irq_data);
+	ret = rte_pmd_gpio_register_irq2(dev_id, gpio, cnxk_gpio_irq_handler, (int *)(size_t)gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to register irq handler");

-	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio,
-					    CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to enable interrupt");

-	ret = cnxk_gpio_trigger_irq(gpio);
-	CNXK_GPIO_ERR_STR(ret, "failed to trigger irq");
-	rte_delay_ms(1);
-	ret = *(volatile int *)&irq_data == gpio ? 0 : -EIO;
-	CNXK_GPIO_ERR_STR(ret, "failed to test irq");
+	rte_delay_ms(2);
+	rte_read32(&triggered);
+	CNXK_GPIO_ERR_STR(!triggered, "failed to trigger irq");

 	ret = rte_pmd_gpio_disable_interrupt(dev_id, gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to disable interrupt");
@@ -193,24 +104,15 @@ cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 }

 static int
-cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
+cnxk_gpio_test_output(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, val, n;
+	int ret, val;

-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
-
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_OUT);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to out");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val)
@@ -219,64 +121,41 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val != 1)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_LOW);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to low");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_HIGH);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to high");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
+
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	/* this one should succeed */
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_get_pin_active_low(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read active_low");
@@ -284,23 +163,24 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 0)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 0", val);

-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
 out:
 	return ret;
 }
@@ -309,17 +189,13 @@ int
 cnxk_gpio_selftest(uint16_t dev_id)
 {
 	struct cnxk_gpio_queue_conf conf;
-	struct cnxk_gpiochip *gpiochip;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct rte_rawdev *rawdev;
 	unsigned int queues, i;
-	struct cnxk_gpio *gpio;
 	int ret, ret2;

 	rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio");
 	if (!rawdev)
 		return -ENODEV;
-	gpiochip = rawdev->dev_private;

 	queues = rte_rawdev_queue_count(dev_id);
 	if (queues == 0)
@@ -329,10 +205,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	if (ret)
 		return ret;

-	fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (fd < 0)
-		return -errno;
-
 	for (i = 0; i < queues; i++) {
 		ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf));
 		if (ret) {
@@ -355,15 +227,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			goto out;
 		}

-		gpio = gpiochip->gpios[conf.gpio];
-		snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, gpio->num);
-		if (!cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s does not exist", buf);
-			ret = -ENOENT;
-			goto release;
-		}
-
-		ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_input(dev_id, conf.gpio);
 		if (ret)
 			goto release;

@@ -371,7 +235,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 		if (ret)
 			goto release;

-		ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_output(dev_id, conf.gpio);
 release:
 		ret2 = ret;
 		ret = rte_rawdev_queue_release(dev_id, i);
@@ -381,12 +245,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			break;
 		}

-		if (cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s still exists", buf);
-			ret = -EIO;
-			break;
-		}
-
 		if (ret2) {
 			ret = ret2;
 			break;
@@ -394,7 +252,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	}

 out:
-	close(fd);
 	rte_rawdev_stop(dev_id);

 	return ret;
diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build
index 9d9a527392..372f3d9f46 100644
--- a/drivers/raw/cnxk_gpio/meson.build
+++ b/drivers/raw/cnxk_gpio/meson.build
@@ -5,7 +5,6 @@
 deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs']
 sources = files(
         'cnxk_gpio.c',
-        'cnxk_gpio_irq.c',
         'cnxk_gpio_selftest.c',
 )
 headers = files('rte_pmd_cnxk_gpio.h')
diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
index 80a37be9c7..2e787d2f70 100644
--- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
@@ -5,6 +5,15 @@
 #ifndef _RTE_PMD_CNXK_GPIO_H_
 #define _RTE_PMD_CNXK_GPIO_H_

+#include <linux/gpio.h>
+#include <linux/version.h>
+
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#define GPIO_V2_PRESENT
+#else
+struct gpio_v2_line_event {};
+#endif
+
 #include <rte_malloc.h>
 #include <rte_memcpy.h>
 #include <rte_rawdev.h>
@@ -48,8 +57,10 @@ enum cnxk_gpio_msg_type {
 	CNXK_GPIO_MSG_TYPE_GET_PIN_DIR,
 	/** Type used to read inverted logic state */
 	CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW,
-	/** Type used to register interrupt handler */
+	/** Type used to register interrupt handler (deprecated) */
 	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ,
+	/** Type used to register interrupt handler */
+	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
 	/** Type used to remove interrupt handler */
 	CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ,
 };
@@ -79,7 +90,7 @@ enum cnxk_gpio_pin_dir {
 };

 /**
- * GPIO interrupt handler
+ * GPIO interrupt handler (deprecated)
  *
  * @param gpio
  *   Zero-based GPIO number
@@ -97,6 +108,23 @@ struct cnxk_gpio_irq {
 	int cpu;
 };

+/**
+ * GPIO interrupt handler
+ *
+ * @param event
+ *   Pointer to gpio event data
+ * @param data
+ *   Cookie passed to interrupt handler
+ */
+typedef void (*cnxk_gpio_irq_handler2_t)(struct gpio_v2_line_event *event, void *data);
+
+struct cnxk_gpio_irq2 {
+	/** Interrupt handler */
+	cnxk_gpio_irq_handler2_t handler;
+	/** User data passed to irq handler */
+	void *data;
+};
+
 struct cnxk_gpio_msg {
 	/** Message type */
 	enum cnxk_gpio_msg_type type;
@@ -338,7 +366,7 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val)
 }

 /**
- * Attach interrupt handler to GPIO
+ * Attach interrupt handler to GPIO (deprecated)
  *
  * @param dev_id
  *   The identifier of the device
@@ -371,6 +399,36 @@ rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu,
 	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
 }

+/**
+ * Attach interrupt handler to GPIO
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @param gpio
+ *   Zero-based GPIO number
+ * @param handler
+ *   Interrupt handler to be executed
+ * @param data
+ *   Data to be passed to interrupt handler
+ *
+ * @return
+ *   Returns 0 on success, negative error code otherwise
+ */
+static __rte_always_inline int
+rte_pmd_gpio_register_irq2(uint16_t dev_id, int gpio, cnxk_gpio_irq_handler2_t handler, void *data)
+{
+	struct cnxk_gpio_irq2 irq = {
+		.handler = handler,
+		.data = data,
+	};
+	struct cnxk_gpio_msg msg = {
+		.type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
+		.data = &irq,
+	};
+
+	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
+}
+
 /**
  * Detach interrupt handler from GPIO
  *
--
2.34.1


^ permalink raw reply	[relevance 1%]

* DPDK 25.03 released
@ 2025-03-24 23:26  3% Thomas Monjalon
  0 siblings, 0 replies; 153+ results
From: Thomas Monjalon @ 2025-03-24 23:26 UTC (permalink / raw)
  To: announce

A new major release is available:
	https://fast.dpdk.org/rel/dpdk-25.03.tar.xz

It was a small release cycle:
	812 commits from 137 authors
	1601 files changed, 85099 insertions(+), 38808 deletions(-)

It is not planned to start a maintenance branch for 25.03.
This version is ABI-compatible with 24.11.

Highlights of 25.03:
	- Staged-Ordered ring (SORING)
	- mbuf raw bulk functions
	- some Intel drivers merged together
	- Yunsilicon xsc networking driver
	- ZTE Storage Data Accelerator (ZSDA) driver
	- RSA in vhost/virtio
	- Intel compiler icc support replaced by icx
	- more function annotations for analyzers
	- more MSVC compatibility

More details in the release notes:
	https://doc.dpdk.org/guides/rel_notes/release_25_03.html


There are 25 new contributors (including authors, reviewers and testers).
Welcome to Amir Avivi, Ariel Otilibili, Changqi Dingluo, Dima Ruinskiy,
Dongwei Xu, Eimear Morrissey, Frode Nordahl, Jakub Buchocki,
Jianping Zhao, Menachem Fogel, Monendra Singh Kushwaha, Nawal Kishor,
Nicolas Planel, Nithinsen Kaithakadan, Piotr Krzewinski,
Rajesh Mudimadugula, Renyong Wan, Rong Qian, Shaokai Zhang,
Tamar Mashiah, Xiaoxiong Zhang, Yang Ming, Yosef Raisman, Zaiyu Wang,
and Zhigang Hu.

Below is the number of commits per employer (with authors count):
	154     Intel (39)
	 79     Marvell (18)
	 77     NVIDIA (20)
	 71     Napatech (3)
	 63     stephen@networkplumber.org (1)
	 62     Microsoft (4)
	 47     Arm (4)
	 44     ZTE (3)
	 42     Red Hat (4)
	 33     Huawei (7)
	 29     Yunsilicon (1)
	 24     UNH (4)
	        ...

A big thank to all courageous people who took on the non rewarding task
of reviewing other's job.
Based on Reviewed-by and Acked-by tags, the top non-PMD reviewers are:
	 48     Paul Szczepanek <paul.szczepanek@arm.com>
	 27     Bruce Richardson <bruce.richardson@intel.com>
	 23     Dean Marx <dmarx@iol.unh.edu>
	 20     Morten Brørup <mb@smartsharesystems.com>
	 19     Maxime Coquelin <maxime.coquelin@redhat.com>
	 18     Akhil Goyal <gakhil@marvell.com>
	 13     Stephen Hemminger <stephen@networkplumber.org>
	 13     Chengwen Feng <fengchengwen@huawei.com>


The next version will be 25.07 in July.
The new features for 25.07 can be submitted until mid-April:
	http://core.dpdk.org/roadmap/
Please share your roadmap.


In the meantime, please do not forget to register for the DPDK Summit in Prague:
	https://www.dpdk.org/event/dpdk-summit-2025-prague/


Thanks everyone



^ permalink raw reply	[relevance 3%]

* [PATCH v3] raw/cnxk_gpio: switch to character based GPIO interface
  2025-03-24  8:28  1% ` [PATCH v2] " Tomasz Duszynski
@ 2025-03-25  4:14  1%   ` Tomasz Duszynski
  2025-03-27  9:12  1%     ` [PATCH v4] " Tomasz Duszynski
  0 siblings, 1 reply; 153+ results
From: Tomasz Duszynski @ 2025-03-25  4:14 UTC (permalink / raw)
  To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj

The direct passthrough interrupt mechanism, which allowed bypassing the
kernel, was obscure and is no longer supported. So this driver won't
work with latest SDK kernels. Additionally, the sysfs GPIO control
interface has been deprecated by Linux kernel itself.

That said, this change updates the PMD to use the current GPIO interface
ensuring compatibility with current kernel standards while improving
maintainability and security.

Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
---
v2:
- compile conditionally based on GPIO_V2_PRESENT
v3:
- fix compilation issues due to missing structure member
- quiesce compiler warnings about maybe unused parameters

 doc/guides/rawdevs/cnxk_gpio.rst           |  37 +-
 drivers/raw/cnxk_gpio/cnxk_gpio.c          | 525 ++++++++++++---------
 drivers/raw/cnxk_gpio/cnxk_gpio.h          |  17 +-
 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c      | 216 ---------
 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 238 ++--------
 drivers/raw/cnxk_gpio/meson.build          |   1 -
 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h  |  64 ++-
 7 files changed, 445 insertions(+), 653 deletions(-)
 delete mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c

diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst
index 954d3b8905..8084dd4adb 100644
--- a/doc/guides/rawdevs/cnxk_gpio.rst
+++ b/doc/guides/rawdevs/cnxk_gpio.rst
@@ -6,31 +6,20 @@ Marvell CNXK GPIO Driver

 CNXK GPIO PMD configures and manages GPIOs available on the system using
 standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies
-both on standard sysfs GPIO interface provided by the Linux kernel and GPIO
-kernel driver custom interface allowing one to install userspace interrupt
-handlers.
+on standard kernel GPIO character device interface.

 Features
 --------

 Following features are available:

-- export/unexport a GPIO
-- read/write specific value from/to exported GPIO
+- read/write specific value from/to GPIO
 - set GPIO direction
 - set GPIO edge that triggers interrupt
 - set GPIO active low
 - register interrupt handler for specific GPIO
 - multiprocess aware

-Requirements
-------------
-
-PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface
-for installing interrupt handlers for low latency signal processing.
-
-Driver is shipped with Marvell SDK.
-
 Limitations
 -----------

@@ -43,20 +32,20 @@ Device Setup
 CNXK GPIO PMD binds to virtual device which gets created by passing
 `--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter
 tells PMD which GPIO controller should be used. Available controllers are
-available under `/sys/class/gpio`. For further details on how Linux represents
-GPIOs in userspace please refer to
-`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_.
+`/dev/gpiochipN` character devices. For further details on
+how Linux represents GPIOs in userspace please refer to
+`gpio-cdev <https://www.kernel.org/doc/Documentation/ABI/testing/gpio-cdev>`_.

 If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically
 sort list of available gpiochips is used.

 .. code-block:: console

-   $ ls /sys/class/gpio
-   export gpiochip448 unexport
+   $ ls /dev/gpiochip*
+   /dev/gpiochip0

 In above scenario only one GPIO controller is present hence
-`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL.
+`--vdev=cnxk_gpio,gpiochip=0` should be passed to EAL.

 Before performing actual data transfer one needs to call
 ``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The
@@ -65,7 +54,7 @@ being controllable or not. Thus it is user responsibility to pick the proper
 ones. The latter call simply returns queue capacity.

 In order to allow using only subset of available GPIOs `allowlist` PMD param may
-be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]`
+be used. For example passing `--vdev=cnxk_gpio,gpiochip=0,allowlist=[0,1,2,3]`
 to EAL will deny using all GPIOs except those specified explicitly in the
 `allowlist`.

@@ -179,12 +168,12 @@ Request interrupt

 Message is used to install custom interrupt handler.

-Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``.
+Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2``.

-Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt
+Payload needs to be set to ``struct cnxk_gpio_irq2`` which describes interrupt
 being requested.

-Consider using ``rte_pmd_gpio_register_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_register_irq2()`` wrapper.

 Free interrupt
 ~~~~~~~~~~~~~~
@@ -193,7 +182,7 @@ Message is used to remove installed interrupt handler.

 Message must have type set to ``CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ``.

-Consider using ``rte_pmd_gpio_unregister_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_unregister_irq()`` wrapper.

 Self test
 ---------
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c
index 329ac28a27..08384a8b09 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c
@@ -3,11 +3,19 @@
  */

 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+#include <regex.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/types.h>

 #include <bus_vdev_driver.h>
 #include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_interrupts.h>
 #include <rte_kvargs.h>
 #include <rte_lcore.h>
 #include <rte_rawdev_pmd.h>
@@ -17,9 +25,12 @@
 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"

-#define CNXK_GPIO_BUFSZ 128
-#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio"
 #define CNXK_GPIO_PARAMS_MZ_NAME "cnxk_gpio_params_mz"
+#define CNXK_GPIO_INVALID_FD (-1)
+
+RTE_LOG_REGISTER_SUFFIX(cnxk_logtype_gpio, gpio, INFO);
+
+#ifdef GPIO_V2_PRESENT

 struct cnxk_gpio_params {
 	unsigned int num;
@@ -40,6 +51,31 @@ cnxk_gpio_format_name(char *name, size_t len)
 	snprintf(name, len, "cnxk_gpio");
 }

+static int
+cnxk_gpio_ioctl(struct cnxk_gpio *gpio, unsigned long cmd, void *arg)
+{
+	return ioctl(gpio->fd, cmd, arg) ? -errno : 0;
+}
+
+static int
+cnxk_gpio_gpiochip_ioctl(struct cnxk_gpiochip *gpiochip, unsigned long cmd, void *arg)
+{
+	char path[PATH_MAX];
+	int ret = 0, fd;
+
+	snprintf(path, sizeof(path), "/dev/gpiochip%d", gpiochip->num);
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -errno;
+
+	if (ioctl(fd, cmd, arg))
+		ret = -errno;
+
+	close(fd);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_filter_gpiochip(const struct dirent *dirent)
 {
@@ -54,8 +90,7 @@ cnxk_gpio_set_defaults(struct cnxk_gpio_params *params)
 	struct dirent **namelist;
 	int ret = 0, n;

-	n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip,
-		    alphasort);
+	n = scandir("/dev", &namelist, cnxk_gpio_filter_gpiochip, alphasort);
 	if (n < 0 || n == 0)
 		return -ENODEV;

@@ -143,7 +178,7 @@ cnxk_gpio_parse_arg(struct rte_kvargs *kvlist, const char *arg, arg_handler_t ha
 static int
 cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 {
-	size_t len = sizeof(**params);
+	size_t len = sizeof(**params) + 1;
 	const char *allowlist = NULL;
 	struct rte_kvargs *kvlist;
 	int ret;
@@ -163,11 +198,13 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)

 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_ALLOWLIST, cnxk_gpio_parse_arg_allowlist,
 				  &allowlist);
-	if (ret < 0)
+	if (ret < 0) {
+		ret = -EINVAL;
 		goto out;
+	}

 	if (allowlist)
-		len += strlen(allowlist) + 1;
+		len += strlen(allowlist);

 	*params = cnxk_gpio_params_reserve(len);
 	if (!(*params)) {
@@ -175,7 +212,8 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 		goto out;
 	}

-	strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);
+	if (allowlist)
+		strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);

 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_GPIOCHIP, cnxk_gpio_parse_arg_gpiochip,
 				  &(*params)->num);
@@ -188,6 +226,24 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 	return ret;
 }

+static bool
+cnxk_gpio_allowlist_valid(const char *allowlist)
+{
+	bool ret = false;
+	regex_t regex;
+
+	/* [gpio0<,gpio1,...,gpioN>], where '<...>' is optional part */
+	if (regcomp(&regex, "^\\[[0-9]+(,[0-9]+)*\\]$", REG_EXTENDED))
+		return ret;
+
+	if (!regexec(&regex, allowlist, 0, NULL, 0))
+		ret = true;
+
+	regfree(&regex);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 {
@@ -199,6 +255,17 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	if (!list)
 		return -ENOMEM;

+	/* no gpios provided so allow all */
+	if (!*allowlist) {
+		for (i = 0; i < gpiochip->num_gpios; i++)
+			list[queue++] = i;
+
+		goto out_done;
+	}
+
+	if (!cnxk_gpio_allowlist_valid(allowlist))
+		return -EINVAL;
+
 	allowlist = strdup(allowlist);
 	if (!allowlist) {
 		ret = -ENOMEM;
@@ -239,6 +306,7 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	} while ((token = strtok(NULL, ",")));

 	free(allowlist);
+out_done:
 	gpiochip->allowlist = list;
 	gpiochip->num_queues = queue;

@@ -250,88 +318,6 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	return ret;
 }

-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-static int
-cnxk_gpio_read_attr_int(char *attr, int *val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret;
-
-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	ret = sscanf(buf, "%d", val);
-	if (ret < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr(const char *attr, const char *val)
-{
-	FILE *fp;
-	int ret;
-
-	if (!val)
-		return -EINVAL;
-
-	fp = fopen(attr, "w");
-	if (!fp)
-		return -errno;
-
-	ret = fprintf(fp, "%s", val);
-	if (ret < 0) {
-		fclose(fp);
-		return ret;
-	}
-
-	ret = fclose(fp);
-	if (ret)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr_int(const char *attr, int val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-
-	snprintf(buf, sizeof(buf), "%d", val);
-
-	return cnxk_gpio_write_attr(attr, buf);
-}
-
 static bool
 cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 {
@@ -353,14 +339,16 @@ cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 }

 static bool
-cnxk_gpio_exists(int num)
+cnxk_gpio_available(struct cnxk_gpio *gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	struct stat st;
+	struct gpio_v2_line_info info = { .offset = gpio->num };
+	int ret;

-	snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, num);
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return false;

-	return !stat(buf, &st);
+	return !(info.flags & GPIO_V2_LINE_FLAG_USED);
 }

 static int
@@ -368,9 +356,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 		      rte_rawdev_obj_t queue_conf, size_t queue_conf_size)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
+	struct gpio_v2_line_request req = {0};
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int ret;

 	RTE_SET_USED(queue_conf);
 	RTE_SET_USED(queue_conf_size);
@@ -386,33 +374,87 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 	if (!gpio)
 		return -ENOMEM;

-	num = cnxk_queue_to_gpio(gpiochip, queue_id);
-	gpio->num = num + gpiochip->base;
+	gpio->num = cnxk_queue_to_gpio(gpiochip, queue_id);
+	gpio->fd = CNXK_GPIO_INVALID_FD;
 	gpio->gpiochip = gpiochip;

-	if (!cnxk_gpio_exists(gpio->num)) {
-		snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH);
-		ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-		if (ret) {
-			rte_free(gpio);
-			return ret;
-		}
-	} else {
-		CNXK_GPIO_LOG(WARNING, "using existing gpio%d", gpio->num);
+	if (!cnxk_gpio_available(gpio)) {
+		rte_free(gpio);
+		return -EBUSY;
+	}
+
+	cnxk_gpio_format_name(req.consumer, sizeof(req.consumer));
+	req.offsets[req.num_lines] = gpio->num;
+	req.num_lines = 1;
+
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINE_IOCTL, &req);
+	if (ret) {
+		rte_free(gpio);
+		return ret;
 	}

-	gpiochip->gpios[num] = gpio;
+	gpio->fd = req.fd;
+	gpiochip->gpios[gpio->num] = gpio;

 	return 0;
 }

+static void
+cnxk_gpio_intr_handler(void *data)
+{
+	struct gpio_v2_line_event event;
+	struct cnxk_gpio *gpio = data;
+	int ret;
+
+	ret = read(gpio->fd, &event, sizeof(event));
+	if (ret != sizeof(event)) {
+		CNXK_GPIO_LOG(ERR, "failed to read gpio%d event data", gpio->num);
+		goto out;
+	}
+	if ((unsigned int)gpio->num != event.offset) {
+		CNXK_GPIO_LOG(ERR, "expected event from gpio%d, received from gpio%d",
+			      gpio->num, event.offset);
+		goto out;
+	}
+
+	if (gpio->intr.handler2)
+		(gpio->intr.handler2)(&event, gpio->intr.data);
+	else if (gpio->intr.handler)
+		(gpio->intr.handler)(gpio->num, gpio->intr.data);
+out:
+	rte_intr_ack(gpio->intr.intr_handle);
+}
+
+static int
+cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+{
+	int ret;
+
+	if (!gpio->intr.intr_handle)
+		return 0;
+
+	ret = rte_intr_disable(gpio->intr.intr_handle);
+	if (ret)
+		return ret;
+
+	ret = rte_intr_callback_unregister_sync(gpio->intr.intr_handle, cnxk_gpio_intr_handler,
+						(void *)-1);
+	if (ret)
+		return ret;
+
+	rte_intr_instance_free(gpio->intr.intr_handle);
+	gpio->intr.intr_handle = NULL;
+
+	return 0;
+}
+
+
 static int
 cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int num;

 	if (!cnxk_gpio_queue_valid(gpiochip, queue_id))
 		return -EINVAL;
@@ -421,10 +463,11 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 	if (!gpio)
 		return -ENODEV;

-	snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH);
-	ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-	if (ret)
-		return ret;
+	if (gpio->intr.intr_handle)
+		cnxk_gpio_unregister_irq(gpio);
+
+	if (gpio->fd != CNXK_GPIO_INVALID_FD)
+		close(gpio->fd);

 	num = cnxk_queue_to_gpio(gpiochip, queue_id);
 	gpiochip->gpios[num] = NULL;
@@ -462,134 +505,218 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev)

 static const struct {
 	enum cnxk_gpio_pin_edge edge;
-	const char *name;
-} cnxk_gpio_edge_name[] = {
-	{ CNXK_GPIO_PIN_EDGE_NONE, "none" },
-	{ CNXK_GPIO_PIN_EDGE_FALLING, "falling" },
-	{ CNXK_GPIO_PIN_EDGE_RISING, "rising" },
-	{ CNXK_GPIO_PIN_EDGE_BOTH, "both" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_edge_flag[] = {
+	{ CNXK_GPIO_PIN_EDGE_NONE, 0 },
+	{ CNXK_GPIO_PIN_EDGE_FALLING, GPIO_V2_LINE_FLAG_EDGE_FALLING },
+	{ CNXK_GPIO_PIN_EDGE_RISING, GPIO_V2_LINE_FLAG_EDGE_RISING },
+	{ CNXK_GPIO_PIN_EDGE_BOTH, GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING },
 };

-static const char *
-cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge)
+static enum gpio_v2_line_flag
+cnxk_gpio_edge_to_flag(enum cnxk_gpio_pin_edge edge)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (cnxk_gpio_edge_name[i].edge == edge)
-			return cnxk_gpio_edge_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if (cnxk_gpio_edge_flag[i].edge == edge)
+			break;
 	}

-	return NULL;
+	return cnxk_gpio_edge_flag[i].flag;
 }

 static enum cnxk_gpio_pin_edge
-cnxk_gpio_name_to_edge(const char *name)
+cnxk_gpio_flag_to_edge(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (!strcmp(cnxk_gpio_edge_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if ((cnxk_gpio_edge_flag[i].flag & flag) == cnxk_gpio_edge_flag[i].flag)
 			break;
 	}

-	return cnxk_gpio_edge_name[i].edge;
+	return cnxk_gpio_edge_flag[i].edge;
 }

 static const struct {
 	enum cnxk_gpio_pin_dir dir;
-	const char *name;
-} cnxk_gpio_dir_name[] = {
-	{ CNXK_GPIO_PIN_DIR_IN, "in" },
-	{ CNXK_GPIO_PIN_DIR_OUT, "out" },
-	{ CNXK_GPIO_PIN_DIR_HIGH, "high" },
-	{ CNXK_GPIO_PIN_DIR_LOW, "low" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_dir_flag[] = {
+	{ CNXK_GPIO_PIN_DIR_IN, GPIO_V2_LINE_FLAG_INPUT },
+	{ CNXK_GPIO_PIN_DIR_OUT, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_HIGH, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_LOW, GPIO_V2_LINE_FLAG_OUTPUT },
 };

-static const char *
-cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir)
+static enum gpio_v2_line_flag
+cnxk_gpio_dir_to_flag(enum cnxk_gpio_pin_dir dir)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (cnxk_gpio_dir_name[i].dir == dir)
-			return cnxk_gpio_dir_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if (cnxk_gpio_dir_flag[i].dir == dir)
+			break;
 	}

-	return NULL;
+	return cnxk_gpio_dir_flag[i].flag;
 }

 static enum cnxk_gpio_pin_dir
-cnxk_gpio_name_to_dir(const char *name)
+cnxk_gpio_flag_to_dir(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (!strcmp(cnxk_gpio_dir_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if ((cnxk_gpio_dir_flag[i].flag & flag) == cnxk_gpio_dir_flag[i].flag)
 			break;
 	}

-	return cnxk_gpio_dir_name[i].dir;
+	return cnxk_gpio_dir_flag[i].dir;
 }

 static int
-cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
+cnxk_gpio_register_irq_compat(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq,
+			      struct cnxk_gpio_irq2 *irq2)
 {
+	struct rte_intr_handle *intr_handle;
 	int ret;

-	ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu);
-	if (ret)
-		return ret;
+	if (!irq && !irq2)
+		return -EINVAL;
+
+	if ((irq && !irq->handler) || (irq2 && !irq2->handler))
+		return -EINVAL;
+
+	if (gpio->intr.intr_handle)
+		return -EEXIST;
+
+	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+	if (!intr_handle)
+		return -ENOMEM;
+
+	if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_VDEV)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_fd_set(intr_handle, gpio->fd)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_callback_register(intr_handle, cnxk_gpio_intr_handler, gpio)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	gpio->intr.intr_handle = intr_handle;

-	gpio->handler = irq->handler;
-	gpio->data = irq->data;
-	gpio->cpu = irq->cpu;
+	if (irq) {
+		gpio->intr.data = irq->data;
+		gpio->intr.handler = irq->handler;
+	} else {
+		gpio->intr.data = irq2->data;
+		gpio->intr.handler2 = irq2->handler;
+	}
+
+	if (rte_intr_enable(gpio->intr.intr_handle)) {
+		ret = -EINVAL;
+		goto out;
+	}

 	return 0;
+out:
+	rte_intr_instance_free(intr_handle);
+
+	return ret;
 }

 static int
-cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
 {
-	return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base);
+	CNXK_GPIO_LOG(WARNING, "using deprecated interrupt registration api");
+
+	return cnxk_gpio_register_irq_compat(gpio, irq, NULL);
+}
+
+static int
+cnxk_gpio_register_irq2(struct cnxk_gpio *gpio, struct cnxk_gpio_irq2 *irq)
+{
+	return cnxk_gpio_register_irq_compat(gpio, NULL, irq);
 }

 static int
 cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 {
 	struct cnxk_gpio_msg *msg = rbuf->buf_addr;
+	struct gpio_v2_line_values values = {0};
+	struct gpio_v2_line_config config = {0};
+	struct gpio_v2_line_info info = {0};
 	enum cnxk_gpio_pin_edge edge;
 	enum cnxk_gpio_pin_dir dir;
-	char buf[CNXK_GPIO_BUFSZ];
 	void *rsp = NULL;
-	int ret, val, n;
+	int ret;

-	n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH,
-		     gpio->num);
+	info.offset = gpio->num;
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return ret;
+
+	info.flags &= ~GPIO_V2_LINE_FLAG_USED;

 	switch (msg->type) {
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data);
+		values.bits = *(int *)msg->data ?  RTE_BIT64(gpio->num) : 0;
+		values.mask = RTE_BIT64(gpio->num);
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
 		edge = *(enum cnxk_gpio_pin_edge *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge));
+		info.flags &= ~(GPIO_V2_LINE_FLAG_EDGE_RISING | GPIO_V2_LINE_FLAG_EDGE_FALLING);
+		info.flags |= cnxk_gpio_edge_to_flag(edge);
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
 		dir = *(enum cnxk_gpio_pin_dir *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir));
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = cnxk_gpio_dir_to_flag(dir);
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		if (dir == CNXK_GPIO_PIN_DIR_HIGH || dir == CNXK_GPIO_PIN_DIR_LOW) {
+			config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
+			config.attrs[config.num_attrs].attr.values = dir == CNXK_GPIO_PIN_DIR_HIGH ?
+								     RTE_BIT64(gpio->num) : 0;
+			config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+			config.num_attrs++;
+		}
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		val = *(int *)msg->data;
-		ret = cnxk_gpio_write_attr_int(buf, val);
+		if (*(int *)msg->data)
+			info.flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+		else
+			info.flags &= ~GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
+		values.mask = RTE_BIT64(gpio->num);
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
 		if (ret)
 			break;

@@ -597,47 +724,35 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 		if (!rsp)
 			return -ENOMEM;

-		*(int *)rsp = val;
+		*(int *)rsp = !!(values.bits & RTE_BIT64(gpio->num));
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf);
+		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_flag_to_edge(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
-		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0);
+		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf);
+		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_flag_to_dir(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(int), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(int *)rsp = val;
+		*(int *)rsp = !!(info.flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
 		break;
 	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ:
 		ret = cnxk_gpio_register_irq(gpio, (struct cnxk_gpio_irq *)msg->data);
 		break;
+	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2:
+		ret = cnxk_gpio_register_irq2(gpio, (struct cnxk_gpio_irq2 *)msg->data);
+		break;
 	case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ:
 		ret = cnxk_gpio_unregister_irq(gpio);
 		break;
@@ -731,11 +846,11 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = {
 static int
 cnxk_gpio_probe(struct rte_vdev_device *dev)
 {
+	struct gpiochip_info gpiochip_info;
 	char name[RTE_RAWDEV_NAME_MAX_LEN];
 	struct cnxk_gpio_params *params;
 	struct cnxk_gpiochip *gpiochip;
 	struct rte_rawdev *rawdev;
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

 	cnxk_gpio_format_name(name, sizeof(name));
@@ -762,25 +877,14 @@ cnxk_gpio_probe(struct rte_vdev_device *dev)

 	gpiochip->num = params->num;

-	ret = cnxk_gpio_irq_init(gpiochip);
-	if (ret)
-		goto out;
-
-	/* read gpio base */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base);
-	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
-		goto out;
-	}
-
 	/* read number of available gpios */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios);
+	ret = cnxk_gpio_gpiochip_ioctl(gpiochip, GPIO_GET_CHIPINFO_IOCTL, &gpiochip_info);
 	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
+		CNXK_GPIO_LOG(ERR, "failed to read /dev/gpiochip%d info", gpiochip->num);
 		goto out;
 	}
+
+	gpiochip->num_gpios = gpiochip_info.lines;
 	gpiochip->num_queues = gpiochip->num_gpios;

 	ret = cnxk_gpio_parse_allowlist(gpiochip, params->allowlist);
@@ -827,15 +931,11 @@ cnxk_gpio_remove(struct rte_vdev_device *dev)
 		if (!gpio)
 			continue;

-		if (gpio->handler)
-			cnxk_gpio_unregister_irq(gpio);
-
 		cnxk_gpio_queue_release(rawdev, gpio->num);
 	}

 	rte_free(gpiochip->allowlist);
 	rte_free(gpiochip->gpios);
-	cnxk_gpio_irq_fini();
 	cnxk_gpio_params_release();
 	rte_rawdev_pmd_release(rawdev);

@@ -851,4 +951,5 @@ RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv);
 RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio,
 		"gpiochip=<int> "
 		"allowlist=<list>");
-RTE_LOG_REGISTER_SUFFIX(cnxk_logtype_gpio, gpio, INFO);
+
+#endif /* GPIO_V2_PRESENT */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h
index 94c8e36977..adc7f90936 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h
@@ -11,20 +11,24 @@ extern int cnxk_logtype_gpio;
 #define CNXK_GPIO_LOG(level, ...) \
 	RTE_LOG_LINE(level, CNXK_GPIO, __VA_ARGS__)

+struct gpio_v2_line_event;
 struct cnxk_gpiochip;

 struct cnxk_gpio {
 	struct cnxk_gpiochip *gpiochip;
+	struct {
+		struct rte_intr_handle *intr_handle;
+		void (*handler)(int gpio, void *data);
+		void (*handler2)(struct gpio_v2_line_event *event, void *data);
+		void *data;
+	} intr;
 	void *rsp;
 	int num;
-	void (*handler)(int gpio, void *data);
-	void *data;
-	int cpu;
+	int fd;
 };

 struct cnxk_gpiochip {
 	int num;
-	int base;
 	int num_gpios;
 	int num_queues;
 	struct cnxk_gpio **gpios;
@@ -33,9 +37,4 @@ struct cnxk_gpiochip {

 int cnxk_gpio_selftest(uint16_t dev_id);

-int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip);
-void cnxk_gpio_irq_fini(void);
-int cnxk_gpio_irq_request(int gpio, int cpu);
-int cnxk_gpio_irq_free(int gpio);
-
 #endif /* _CNXK_GPIO_H_ */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
deleted file mode 100644
index 2fa8e69899..0000000000
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(C) 2021 Marvell.
- */
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/queue.h>
-#include <unistd.h>
-
-#include <rte_rawdev_pmd.h>
-
-#include <roc_api.h>
-
-#include "cnxk_gpio.h"
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_SET_GPIO_HANDLER                                               \
-	_IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data)
-#define OTX_IOC_CLR_GPIO_HANDLER                                               \
-	_IO(OTX_IOC_MAGIC, 2)
-
-struct otx_gpio_usr_data {
-	uint64_t isr_base;
-	uint64_t sp;
-	uint64_t cpu;
-	uint64_t gpio_num;
-};
-
-struct cnxk_gpio_irq_stack {
-	LIST_ENTRY(cnxk_gpio_irq_stack) next;
-	void *sp_buffer;
-	int cpu;
-	int inuse;
-};
-
-struct cnxk_gpio_irqchip {
-	int fd;
-	/* serialize access to this struct */
-	pthread_mutex_t lock;
-	LIST_HEAD(, cnxk_gpio_irq_stack) stacks;
-
-	struct cnxk_gpiochip *gpiochip;
-};
-
-static struct cnxk_gpio_irqchip *irqchip;
-
-static void
-cnxk_gpio_irq_stack_free(int cpu)
-{
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (!stack)
-		return;
-
-	if (stack->inuse)
-		stack->inuse--;
-
-	if (stack->inuse == 0) {
-		LIST_REMOVE(stack, next);
-		rte_free(stack->sp_buffer);
-		rte_free(stack);
-	}
-}
-
-static void *
-cnxk_gpio_irq_stack_alloc(int cpu)
-{
-#define ARM_STACK_ALIGNMENT (2 * sizeof(void *))
-#define IRQ_STACK_SIZE 0x200000
-
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (stack) {
-		stack->inuse++;
-		return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-	}
-
-	stack = rte_malloc(NULL, sizeof(*stack), 0);
-	if (!stack)
-		return NULL;
-
-	stack->sp_buffer =
-		rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT);
-	if (!stack->sp_buffer) {
-		rte_free(stack);
-		return NULL;
-	}
-
-	stack->cpu = cpu;
-	stack->inuse = 1;
-	LIST_INSERT_HEAD(&irqchip->stacks, stack, next);
-
-	return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-}
-
-static void
-cnxk_gpio_irq_handler(int gpio_num)
-{
-	struct cnxk_gpiochip *gpiochip = irqchip->gpiochip;
-	struct cnxk_gpio *gpio;
-
-	if (gpio_num >= gpiochip->num_gpios)
-		goto out;
-
-	gpio = gpiochip->gpios[gpio_num];
-	if (likely(gpio->handler))
-		gpio->handler(gpio_num, gpio->data);
-
-out:
-	roc_atf_ret();
-}
-
-int
-cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip)
-{
-	if (irqchip)
-		return 0;
-
-	irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0);
-	if (!irqchip)
-		return -ENOMEM;
-
-	irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (irqchip->fd < 0) {
-		rte_free(irqchip);
-		return -errno;
-	}
-
-	pthread_mutex_init(&irqchip->lock, NULL);
-	LIST_INIT(&irqchip->stacks);
-	irqchip->gpiochip = gpiochip;
-
-	return 0;
-}
-
-void
-cnxk_gpio_irq_fini(void)
-{
-	if (!irqchip)
-		return;
-
-	close(irqchip->fd);
-	rte_free(irqchip);
-	irqchip = NULL;
-}
-
-int
-cnxk_gpio_irq_request(int gpio, int cpu)
-{
-	struct otx_gpio_usr_data data;
-	void *sp;
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	sp = cnxk_gpio_irq_stack_alloc(cpu);
-	if (!sp) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
-
-	data.isr_base = (uint64_t)cnxk_gpio_irq_handler;
-	data.sp = (uint64_t)sp;
-	data.cpu = (uint64_t)cpu;
-	data.gpio_num = (uint64_t)gpio;
-
-	mlockall(MCL_CURRENT | MCL_FUTURE);
-	ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data);
-	if (ret) {
-		ret = -errno;
-		goto out_free_stack;
-	}
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-
-out_free_stack:
-	cnxk_gpio_irq_stack_free(cpu);
-out_unlock:
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return ret;
-}
-
-int
-cnxk_gpio_irq_free(int gpio)
-{
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio);
-	if (ret) {
-		pthread_mutex_unlock(&irqchip->lock);
-		return -errno;
-	}
-
-	cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu);
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-}
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
index a0d9942f20..bafd1de4ce 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
@@ -3,102 +3,33 @@
  */

 #include <fcntl.h>
+#include <linux/gpio.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>

 #include <rte_cycles.h>
+#include <rte_io.h>
 #include <rte_rawdev.h>
 #include <rte_rawdev_pmd.h>
-#include <rte_service.h>

 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"

-#define CNXK_GPIO_BUFSZ 128
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_TRIGGER_GPIO_HANDLER                                           \
-	_IO(OTX_IOC_MAGIC, 3)
-
-static int fd;
-
-static int
-cnxk_gpio_attr_exists(const char *attr)
-{
-	struct stat st;
-
-	return !stat(attr, &st);
-}
-
-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                  \
-	if (err) {                                                             \
-		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, \
-			##__VA_ARGS__, err);                                   \
-		goto out;                                                      \
-	}                                                                      \
+#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                                      \
+	if (err) {                                                                                 \
+		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, ##__VA_ARGS__, err); \
+		goto out;                                                                          \
+	}                                                                                          \
 } while (0)

 static int
-cnxk_gpio_validate_attr(char *attr, const char *expected)
+cnxk_gpio_test_input(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	if (strncmp(buf, expected, sizeof(buf)))
-		return -EIO;
-
-	return 0;
-}
-
-#define CNXK_GPIO_PATH_FMT "/sys/class/gpio/gpio%d"
-
-static int
-cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, n;
-
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
-
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
-	ret = cnxk_gpio_validate_attr(buf, "in");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1) |
 	      rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
@@ -107,29 +38,17 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 		CNXK_GPIO_ERR_STR(ret, "input pin overwritten");
 	}

-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "falling");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "rising");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "both");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	/*
 	 * calling this makes sure kernel driver switches off inverted
@@ -141,44 +60,41 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 	return ret;
 }

-static int
-cnxk_gpio_trigger_irq(int gpio)
+static uint32_t triggered;
+
+static void
+cnxk_gpio_irq_handler(struct gpio_v2_line_event *event, void *data)
 {
-	int ret;
+	RTE_SET_USED(event);
+	RTE_SET_USED(data);

-	ret = ioctl(fd, OTX_IOC_TRIGGER_GPIO_HANDLER, gpio);
+#ifdef GPIO_V2_PRESENT
+	int gpio = (int)(size_t)data;

-	return ret == -1 ? -errno : 0;
-}
+	if ((int)event->offset != gpio)
+		CNXK_GPIO_LOG(ERR, "event from gpio%d instead of gpio%d", event->offset, gpio);
+#endif

-static void
-cnxk_gpio_irq_handler(int gpio, void *data)
-{
-	*(int *)data = gpio;
+	rte_write32(1, &triggered);
 }

 static int
 cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 {
-	int irq_data, ret;
+	int ret;

 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");

-	irq_data = 0;
-	ret = rte_pmd_gpio_register_irq(dev_id, gpio, rte_lcore_id(),
-					cnxk_gpio_irq_handler, &irq_data);
+	ret = rte_pmd_gpio_register_irq2(dev_id, gpio, cnxk_gpio_irq_handler, (int *)(size_t)gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to register irq handler");

-	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio,
-					    CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to enable interrupt");

-	ret = cnxk_gpio_trigger_irq(gpio);
-	CNXK_GPIO_ERR_STR(ret, "failed to trigger irq");
-	rte_delay_ms(1);
-	ret = *(volatile int *)&irq_data == gpio ? 0 : -EIO;
-	CNXK_GPIO_ERR_STR(ret, "failed to test irq");
+	rte_delay_ms(2);
+	rte_read32(&triggered);
+	CNXK_GPIO_ERR_STR(!triggered, "failed to trigger irq");

 	ret = rte_pmd_gpio_disable_interrupt(dev_id, gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to disable interrupt");
@@ -193,24 +109,15 @@ cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 }

 static int
-cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
+cnxk_gpio_test_output(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, val, n;
-
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
+	int ret, val;

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_OUT);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to out");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val)
@@ -219,64 +126,41 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val != 1)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_LOW);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to low");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_HIGH);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to high");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
+
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	/* this one should succeed */
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_get_pin_active_low(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read active_low");
@@ -284,23 +168,24 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 0)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 0", val);

-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
 out:
 	return ret;
 }
@@ -309,17 +194,13 @@ int
 cnxk_gpio_selftest(uint16_t dev_id)
 {
 	struct cnxk_gpio_queue_conf conf;
-	struct cnxk_gpiochip *gpiochip;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct rte_rawdev *rawdev;
 	unsigned int queues, i;
-	struct cnxk_gpio *gpio;
 	int ret, ret2;

 	rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio");
 	if (!rawdev)
 		return -ENODEV;
-	gpiochip = rawdev->dev_private;

 	queues = rte_rawdev_queue_count(dev_id);
 	if (queues == 0)
@@ -329,10 +210,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	if (ret)
 		return ret;

-	fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (fd < 0)
-		return -errno;
-
 	for (i = 0; i < queues; i++) {
 		ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf));
 		if (ret) {
@@ -355,15 +232,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			goto out;
 		}

-		gpio = gpiochip->gpios[conf.gpio];
-		snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, gpio->num);
-		if (!cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s does not exist", buf);
-			ret = -ENOENT;
-			goto release;
-		}
-
-		ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_input(dev_id, conf.gpio);
 		if (ret)
 			goto release;

@@ -371,7 +240,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 		if (ret)
 			goto release;

-		ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_output(dev_id, conf.gpio);
 release:
 		ret2 = ret;
 		ret = rte_rawdev_queue_release(dev_id, i);
@@ -381,12 +250,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			break;
 		}

-		if (cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s still exists", buf);
-			ret = -EIO;
-			break;
-		}
-
 		if (ret2) {
 			ret = ret2;
 			break;
@@ -394,7 +257,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	}

 out:
-	close(fd);
 	rte_rawdev_stop(dev_id);

 	return ret;
diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build
index 9d9a527392..372f3d9f46 100644
--- a/drivers/raw/cnxk_gpio/meson.build
+++ b/drivers/raw/cnxk_gpio/meson.build
@@ -5,7 +5,6 @@
 deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs']
 sources = files(
         'cnxk_gpio.c',
-        'cnxk_gpio_irq.c',
         'cnxk_gpio_selftest.c',
 )
 headers = files('rte_pmd_cnxk_gpio.h')
diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
index 80a37be9c7..2e787d2f70 100644
--- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
@@ -5,6 +5,15 @@
 #ifndef _RTE_PMD_CNXK_GPIO_H_
 #define _RTE_PMD_CNXK_GPIO_H_

+#include <linux/gpio.h>
+#include <linux/version.h>
+
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#define GPIO_V2_PRESENT
+#else
+struct gpio_v2_line_event {};
+#endif
+
 #include <rte_malloc.h>
 #include <rte_memcpy.h>
 #include <rte_rawdev.h>
@@ -48,8 +57,10 @@ enum cnxk_gpio_msg_type {
 	CNXK_GPIO_MSG_TYPE_GET_PIN_DIR,
 	/** Type used to read inverted logic state */
 	CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW,
-	/** Type used to register interrupt handler */
+	/** Type used to register interrupt handler (deprecated) */
 	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ,
+	/** Type used to register interrupt handler */
+	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
 	/** Type used to remove interrupt handler */
 	CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ,
 };
@@ -79,7 +90,7 @@ enum cnxk_gpio_pin_dir {
 };

 /**
- * GPIO interrupt handler
+ * GPIO interrupt handler (deprecated)
  *
  * @param gpio
  *   Zero-based GPIO number
@@ -97,6 +108,23 @@ struct cnxk_gpio_irq {
 	int cpu;
 };

+/**
+ * GPIO interrupt handler
+ *
+ * @param event
+ *   Pointer to gpio event data
+ * @param data
+ *   Cookie passed to interrupt handler
+ */
+typedef void (*cnxk_gpio_irq_handler2_t)(struct gpio_v2_line_event *event, void *data);
+
+struct cnxk_gpio_irq2 {
+	/** Interrupt handler */
+	cnxk_gpio_irq_handler2_t handler;
+	/** User data passed to irq handler */
+	void *data;
+};
+
 struct cnxk_gpio_msg {
 	/** Message type */
 	enum cnxk_gpio_msg_type type;
@@ -338,7 +366,7 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val)
 }

 /**
- * Attach interrupt handler to GPIO
+ * Attach interrupt handler to GPIO (deprecated)
  *
  * @param dev_id
  *   The identifier of the device
@@ -371,6 +399,36 @@ rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu,
 	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
 }

+/**
+ * Attach interrupt handler to GPIO
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @param gpio
+ *   Zero-based GPIO number
+ * @param handler
+ *   Interrupt handler to be executed
+ * @param data
+ *   Data to be passed to interrupt handler
+ *
+ * @return
+ *   Returns 0 on success, negative error code otherwise
+ */
+static __rte_always_inline int
+rte_pmd_gpio_register_irq2(uint16_t dev_id, int gpio, cnxk_gpio_irq_handler2_t handler, void *data)
+{
+	struct cnxk_gpio_irq2 irq = {
+		.handler = handler,
+		.data = data,
+	};
+	struct cnxk_gpio_msg msg = {
+		.type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
+		.data = &irq,
+	};
+
+	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
+}
+
 /**
  * Detach interrupt handler from GPIO
  *
--
2.34.1


^ permalink raw reply	[relevance 1%]

* [PATCH] version: 25.07-rc0
@ 2025-03-26  9:00 10% David Marchand
  2025-03-26  9:10  3% ` Bruce Richardson
  2025-03-27  7:39  0% ` David Marchand
  0 siblings, 2 replies; 153+ results
From: David Marchand @ 2025-03-26  9:00 UTC (permalink / raw)
  To: dev; +Cc: thomas

Start a new release cycle with empty release notes.
Bump version and ABI minor.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 .github/workflows/build.yml            |   2 +-
 ABI_VERSION                            |   2 +-
 VERSION                                |   2 +-
 doc/guides/rel_notes/index.rst         |   1 +
 doc/guides/rel_notes/release_25_07.rst | 138 +++++++++++++++++++++++++
 5 files changed, 142 insertions(+), 3 deletions(-)
 create mode 100644 doc/guides/rel_notes/release_25_07.rst

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fba46b920f..0cc4d12b0b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,7 +12,7 @@ defaults:
 env:
   REF_GIT_BRANCH: main
   REF_GIT_REPO: https://github.com/DPDK/dpdk
-  REF_GIT_TAG: v24.11
+  REF_GIT_TAG: v25.03
 
 jobs:
   checkpatch:
diff --git a/ABI_VERSION b/ABI_VERSION
index 8b9bee5b58..a5615e1308 100644
--- a/ABI_VERSION
+++ b/ABI_VERSION
@@ -1 +1 @@
-25.1
+25.2
diff --git a/VERSION b/VERSION
index 16164a3867..fedf12952a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-25.03.0
+25.07.0-rc0
diff --git a/doc/guides/rel_notes/index.rst b/doc/guides/rel_notes/index.rst
index fc0309113e..6462f01966 100644
--- a/doc/guides/rel_notes/index.rst
+++ b/doc/guides/rel_notes/index.rst
@@ -8,6 +8,7 @@ Release Notes
     :maxdepth: 1
     :numbered:
 
+    release_25_07
     release_25_03
     release_24_11
     release_24_07
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
new file mode 100644
index 0000000000..cd1025aac0
--- /dev/null
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -0,0 +1,138 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+   Copyright 2025 The DPDK contributors
+
+.. include:: <isonum.txt>
+
+DPDK Release 25.07
+==================
+
+.. **Read this first.**
+
+   The text in the sections below explains how to update the release notes.
+
+   Use proper spelling, capitalization and punctuation in all sections.
+
+   Variable and config names should be quoted as fixed width text:
+   ``LIKE_THIS``.
+
+   Build the docs and view the output file to ensure the changes are correct::
+
+      ninja -C build doc
+      xdg-open build/doc/guides/html/rel_notes/release_25_07.html
+
+
+New Features
+------------
+
+.. This section should contain new features added in this release.
+   Sample format:
+
+   * **Add a title in the past tense with a full stop.**
+
+     Add a short 1-2 sentence description in the past tense.
+     The description should be enough to allow someone scanning
+     the release notes to understand the new feature.
+
+     If the feature adds a lot of sub-features you can use a bullet list
+     like this:
+
+     * Added feature foo to do something.
+     * Enhanced feature bar to do something else.
+
+     Refer to the previous release notes for examples.
+
+     Suggested order in release notes items:
+     * Core libs (EAL, mempool, ring, mbuf, buses)
+     * Device abstraction libs and PMDs (ordered alphabetically by vendor name)
+       - ethdev (lib, PMDs)
+       - cryptodev (lib, PMDs)
+       - eventdev (lib, PMDs)
+       - etc
+     * Other libs
+     * Apps, Examples, Tools (if significant)
+
+     This section is a comment. Do not overwrite or remove it.
+     Also, make sure to start the actual text at the margin.
+     =======================================================
+
+
+Removed Items
+-------------
+
+.. This section should contain removed items in this release. Sample format:
+
+   * Add a short 1-2 sentence description of the removed item
+     in the past tense.
+
+   This section is a comment. Do not overwrite or remove it.
+   Also, make sure to start the actual text at the margin.
+   =======================================================
+
+
+API Changes
+-----------
+
+.. This section should contain API changes. Sample format:
+
+   * sample: Add a short 1-2 sentence description of the API change
+     which was announced in the previous releases and made in this release.
+     Start with a scope label like "ethdev:".
+     Use fixed width quotes for ``function_names`` or ``struct_names``.
+     Use the past tense.
+
+   This section is a comment. Do not overwrite or remove it.
+   Also, make sure to start the actual text at the margin.
+   =======================================================
+
+
+ABI Changes
+-----------
+
+.. This section should contain ABI changes. Sample format:
+
+   * sample: Add a short 1-2 sentence description of the ABI change
+     which was announced in the previous releases and made in this release.
+     Start with a scope label like "ethdev:".
+     Use fixed width quotes for ``function_names`` or ``struct_names``.
+     Use the past tense.
+
+   This section is a comment. Do not overwrite or remove it.
+   Also, make sure to start the actual text at the margin.
+   =======================================================
+
+* No ABI change that would break compatibility with 24.11.
+
+
+Known Issues
+------------
+
+.. This section should contain new known issues in this release. Sample format:
+
+   * **Add title in present tense with full stop.**
+
+     Add a short 1-2 sentence description of the known issue
+     in the present tense. Add information on any known workarounds.
+
+   This section is a comment. Do not overwrite or remove it.
+   Also, make sure to start the actual text at the margin.
+   =======================================================
+
+
+Tested Platforms
+----------------
+
+.. This section should contain a list of platforms that were tested
+   with this release.
+
+   The format is:
+
+   * <vendor> platform with <vendor> <type of devices> combinations
+
+     * List of CPU
+     * List of OS
+     * List of devices
+     * Other relevant details...
+
+   This section is a comment. Do not overwrite or remove it.
+   Also, make sure to start the actual text at the margin.
+   =======================================================
-- 
2.48.1


^ permalink raw reply	[relevance 10%]

* Re: [PATCH] version: 25.07-rc0
  2025-03-26  9:00 10% [PATCH] version: 25.07-rc0 David Marchand
@ 2025-03-26  9:10  3% ` Bruce Richardson
  2025-03-26 10:09  3%   ` David Marchand
  2025-03-27  7:39  0% ` David Marchand
  1 sibling, 1 reply; 153+ results
From: Bruce Richardson @ 2025-03-26  9:10 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas

On Wed, Mar 26, 2025 at 10:00:03AM +0100, David Marchand wrote:
> Start a new release cycle with empty release notes.
> Bump version and ABI minor.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---

One query below, otherwise LGTM

Acked-by: Bruce Richardson <bruce.richardson@intel.com>


>  .github/workflows/build.yml            |   2 +-
>  ABI_VERSION                            |   2 +-
>  VERSION                                |   2 +-
>  doc/guides/rel_notes/index.rst         |   1 +
>  doc/guides/rel_notes/release_25_07.rst | 138 +++++++++++++++++++++++++
>  5 files changed, 142 insertions(+), 3 deletions(-)
>  create mode 100644 doc/guides/rel_notes/release_25_07.rst
> 
> diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
> index fba46b920f..0cc4d12b0b 100644
> --- a/.github/workflows/build.yml
> +++ b/.github/workflows/build.yml
> @@ -12,7 +12,7 @@ defaults:
>  env:
>    REF_GIT_BRANCH: main
>    REF_GIT_REPO: https://github.com/DPDK/dpdk
> -  REF_GIT_TAG: v24.11
> +  REF_GIT_TAG: v25.03
>  
Do we not keep comparing against 24.11 for the whole of the 25-ABI cycle?

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] version: 25.07-rc0
  2025-03-26  9:10  3% ` Bruce Richardson
@ 2025-03-26 10:09  3%   ` David Marchand
  2025-03-26 10:37  0%     ` Bruce Richardson
  0 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-26 10:09 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: dev, thomas

On Wed, Mar 26, 2025 at 10:11 AM Bruce Richardson
<bruce.richardson@intel.com> wrote:
> > diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
> > index fba46b920f..0cc4d12b0b 100644
> > --- a/.github/workflows/build.yml
> > +++ b/.github/workflows/build.yml
> > @@ -12,7 +12,7 @@ defaults:
> >  env:
> >    REF_GIT_BRANCH: main
> >    REF_GIT_REPO: https://github.com/DPDK/dpdk
> > -  REF_GIT_TAG: v24.11
> > +  REF_GIT_TAG: v25.03
> >
> Do we not keep comparing against 24.11 for the whole of the 25-ABI cycle?

Comparing the new release against v24.11 would not detect breakage of
symbols introduced for the next ABI, during v25.03.
Like for example the changes in 52633e3a3fa8 ("net: add thread-safe CRC API").


-- 
David Marchand


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] version: 25.07-rc0
  2025-03-26 10:09  3%   ` David Marchand
@ 2025-03-26 10:37  0%     ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-03-26 10:37 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas

On Wed, Mar 26, 2025 at 11:09:50AM +0100, David Marchand wrote:
> On Wed, Mar 26, 2025 at 10:11 AM Bruce Richardson
> <bruce.richardson@intel.com> wrote:
> > > diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
> > > index fba46b920f..0cc4d12b0b 100644
> > > --- a/.github/workflows/build.yml
> > > +++ b/.github/workflows/build.yml
> > > @@ -12,7 +12,7 @@ defaults:
> > >  env:
> > >    REF_GIT_BRANCH: main
> > >    REF_GIT_REPO: https://github.com/DPDK/dpdk
> > > -  REF_GIT_TAG: v24.11
> > > +  REF_GIT_TAG: v25.03
> > >
> > Do we not keep comparing against 24.11 for the whole of the 25-ABI cycle?
> 
> Comparing the new release against v24.11 would not detect breakage of
> symbols introduced for the next ABI, during v25.03.
> Like for example the changes in 52633e3a3fa8 ("net: add thread-safe CRC API").
> 
Ah, yes, thanks for the explanation.

/Bruce

^ permalink raw reply	[relevance 0%]

* Re: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
                     ` (3 preceding siblings ...)
  2025-03-18  8:19  0%   ` [RFC v4 0/8] Symbol versioning and export rework David Marchand
@ 2025-03-26 12:02  0%   ` David Marchand
  2025-03-26 12:26  3%     ` Morten Brørup
                       ` (2 more replies)
  4 siblings, 3 replies; 153+ results
From: David Marchand @ 2025-03-26 12:02 UTC (permalink / raw)
  To: thomas, bruce.richardson; +Cc: dev, andremue, dpdk-techboard

On Mon, Mar 17, 2025 at 4:43 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
>
>
> Comments please.

- I am considering making rte_function_versioning.h a non exported
header (precisely, moving it to buildtools/ and maybe renaming it).

This header contains macros not prefixed with RTE_.
Using it requires some build trick (see use_function_versioning).
And I don't see symbol versioning as a MUST infrastructure that DPDK
needs to provide to datapath applications.

Yet technically, this change would be an API breakage if some
applications indeed relied on it.

Cc: techboard for info.


- On a similar note, this RFC series adds the rte_exports.h header
(defining RTE_EXPORT*_SYMBOL()) in config/, though its job is for
extracting a symbol list during the build.
So a better location is probably buildtools/.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* RE: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-26 12:02  0%   ` David Marchand
@ 2025-03-26 12:26  3%     ` Morten Brørup
  2025-03-26 13:07  0%     ` Bruce Richardson
  2025-03-26 13:36  0%     ` Bruce Richardson
  2 siblings, 0 replies; 153+ results
From: Morten Brørup @ 2025-03-26 12:26 UTC (permalink / raw)
  To: David Marchand, thomas, bruce.richardson; +Cc: dev, andremue, dpdk-techboard

> From: David Marchand [mailto:david.marchand@redhat.com]
> Sent: Wednesday, 26 March 2025 13.03
> 

[...]

> And I don't see symbol versioning as a MUST infrastructure that DPDK
> needs to provide to datapath applications.

Agree.
Getting rid of the exotic stuff like symbol versioning would make the DPDK API easier understandable for DPDK application developers.
Also, it's my impression that symbol versioning does not really play a major role in keeping or breaking ABI compatibility.
Conclusion: Not worth the complexity.


^ permalink raw reply	[relevance 3%]

* Re: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-26 12:02  0%   ` David Marchand
  2025-03-26 12:26  3%     ` Morten Brørup
@ 2025-03-26 13:07  0%     ` Bruce Richardson
  2025-03-26 13:36  0%     ` Bruce Richardson
  2 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-03-26 13:07 UTC (permalink / raw)
  To: David Marchand; +Cc: thomas, dev, andremue, dpdk-techboard

On Wed, Mar 26, 2025 at 01:02:32PM +0100, David Marchand wrote:
> On Mon, Mar 17, 2025 at 4:43 PM David Marchand
> <david.marchand@redhat.com> wrote:
> >
> > So far, each DPDK library (or driver) exposing symbols in an ABI had to
> > maintain a version.map and use some macros for symbol versioning,
> > specially crafted with the GNU linker in mind.
> >
> > This series proposes to rework the whole principle, and instead rely on
> > marking the symbol exports in the source code itself, then let it to the
> > build framework to produce a version script adapted to the linker in use
> > (think GNU linker vs MSVC linker).
> >
> > This greatly simplifies versioning symbols: a developer does not need to
> > know anything about version.map, or that a versioned symbol must be
> > renamed with _v26, annotated with __vsym, exported in a header etc...
> >
> > Checking symbol maps becomes unnecessary since generated by the build
> > framework.
> >
> > Updating to a new ABI is just a matter of bumping the value in
> > ABI_VERSION.
> >
> >
> > Comments please.
> 
> - I am considering making rte_function_versioning.h a non exported
> header (precisely, moving it to buildtools/ and maybe renaming it).
> 

+1 for not exporting it.
-1 for moving to buildtools. I don't see the need to introduce yet another
header path in DPDK. Let's just keep it where it is, or moved slightly in
the EAL folder, and then not export it.

> This header contains macros not prefixed with RTE_.
> Using it requires some build trick (see use_function_versioning).
> And I don't see symbol versioning as a MUST infrastructure that DPDK
> needs to provide to datapath applications.
> 
> Yet technically, this change would be an API breakage if some
> applications indeed relied on it.
> 
> Cc: techboard for info.
> 
> 
> - On a similar note, this RFC series adds the rte_exports.h header
> (defining RTE_EXPORT*_SYMBOL()) in config/, though its job is for
> extracting a symbol list during the build.
> So a better location is probably buildtools/.
> 

Again, don't particularly like buildtools as a path, as it's not really a
tool, just a header file. I'd rather keep the tools folders for scripts and
the like.

/Bruce

^ permalink raw reply	[relevance 0%]

* [RFC 1/2] eventdev: introduce event vector adapter
  @ 2025-03-26 13:14  1% ` pbhagavatula
    0 siblings, 1 reply; 153+ results
From: pbhagavatula @ 2025-03-26 13:14 UTC (permalink / raw)
  To: jerinj, Bruce Richardson; +Cc: dev, Pavan Nikhilesh

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

The event vector adapter supports offloading creation of
event vectors by vectorizing objects (mbufs/ptrs/u64s).
Applications can create a vector adapter associated with
an event queue and enqueue objects to be vectorized.
When the vector reaches the configured size or when the timeout
is reached, the vector adapter will enqueue the vector to the
event queue.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 config/rte_config.h                     |   1 +
 lib/eventdev/event_vector_adapter_pmd.h |  87 +++++
 lib/eventdev/eventdev_pmd.h             |  36 ++
 lib/eventdev/meson.build                |   3 +
 lib/eventdev/rte_event_vector_adapter.c | 444 ++++++++++++++++++++++
 lib/eventdev/rte_event_vector_adapter.h | 469 ++++++++++++++++++++++++
 lib/eventdev/rte_eventdev.c             |  21 ++
 lib/eventdev/rte_eventdev.h             |   8 +
 lib/eventdev/version.map                |  13 +
 9 files changed, 1082 insertions(+)
 create mode 100644 lib/eventdev/event_vector_adapter_pmd.h
 create mode 100644 lib/eventdev/rte_event_vector_adapter.c
 create mode 100644 lib/eventdev/rte_event_vector_adapter.h

diff --git a/config/rte_config.h b/config/rte_config.h
index 86897de75e..9535c48d81 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -92,6 +92,7 @@
 #define RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_DMA_ADAPTER_MAX_INSTANCE 32
+#define RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE 32
 
 /* rawdev defines */
 #define RTE_RAWDEV_MAX_DEVS 64
diff --git a/lib/eventdev/event_vector_adapter_pmd.h b/lib/eventdev/event_vector_adapter_pmd.h
new file mode 100644
index 0000000000..dab0350564
--- /dev/null
+++ b/lib/eventdev/event_vector_adapter_pmd.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+#ifndef __EVENT_VECTOR_ADAPTER_PMD_H__
+#define __EVENT_VECTOR_ADAPTER_PMD_H__
+/**
+ * @file
+ * RTE Event Vector Adapter API (PMD Side)
+ *
+ * @note
+ * This file provides implementation helpers for internal use by PMDs.  They
+ * are not intended to be exposed to applications and are not subject to ABI
+ * versioning.
+ */
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+typedef int (*rte_event_vector_adapter_create_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation setup */
+typedef int (*rte_event_vector_adapter_destroy_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation teardown */
+typedef int (*rte_event_vector_adapter_caps_get_t)(struct rte_eventdev *dev);
+/**< @internal Get capabilities for event vector adapter */
+typedef int (*rte_event_vector_adapter_stats_get_t)(const struct rte_event_vector_adapter *adapter,
+						    struct rte_event_vector_adapter_stats *stats);
+/**< @internal Get statistics for event vector adapter */
+typedef int (*rte_event_vector_adapter_stats_reset_t)(
+	const struct rte_event_vector_adapter *adapter);
+/**< @internal Reset statistics for event vector adapter */
+
+/**
+ * @internal Structure containing the functions exported by an event vector
+ * adapter implementation.
+ */
+struct event_vector_adapter_ops {
+	rte_event_vector_adapter_create_t create;
+	/**< Set up adapter */
+	rte_event_vector_adapter_destroy_t destroy;
+	/**< Tear down adapter */
+	rte_event_vector_adapter_caps_get_t caps_get;
+	/**< Get capabilities from driver */
+	rte_event_vector_adapter_stats_get_t stats_get;
+	/**< Get adapter statistics */
+	rte_event_vector_adapter_stats_reset_t stats_reset;
+	/**< Reset adapter statistics */
+
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Enqueue ptrs into the event vector adapter */
+};
+/**
+ * @internal Adapter data; structure to be placed in shared memory to be
+ * accessible by various processes in a multi-process configuration.
+ */
+struct __rte_cache_aligned rte_event_vector_adapter_data {
+	uint32_t id;
+	/**< Event vector adapter ID */
+	uint8_t event_dev_id;
+	/**< Event device ID */
+	uint32_t socket_id;
+	/**< Socket ID where memory is allocated */
+	uint8_t event_port_id;
+	/**< Optional: event port ID used when the inbuilt port is absent */
+	const struct rte_memzone *mz;
+	/**< Event vector adapter memzone pointer */
+	struct rte_event_vector_adapter_conf conf;
+	/**< Configuration used to configure the adapter. */
+	uint32_t caps;
+	/**< Adapter capabilities */
+	void *adapter_priv;
+	/**< Vector adapter private data*/
+	uint32_t unified_service_id;
+	/**< Unified Service ID*/
+};
+
+static int
+dummy_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uintptr_t ptrs[],
+			     uint16_t num_events, uint64_t flags)
+{
+	RTE_SET_USED(adapter);
+	RTE_SET_USED(ptrs);
+	RTE_SET_USED(num_events);
+	RTE_SET_USED(flags);
+	return 0;
+}
+
+#endif /* __EVENT_VECTOR_ADAPTER_PMD_H__ */
diff --git a/lib/eventdev/eventdev_pmd.h b/lib/eventdev/eventdev_pmd.h
index ad13ba5b03..d03461316b 100644
--- a/lib/eventdev/eventdev_pmd.h
+++ b/lib/eventdev/eventdev_pmd.h
@@ -26,6 +26,7 @@
 
 #include "event_timer_adapter_pmd.h"
 #include "rte_event_eth_rx_adapter.h"
+#include "rte_event_vector_adapter.h"
 #include "rte_eventdev.h"
 
 #ifdef __cplusplus
@@ -1555,6 +1556,36 @@ typedef int (*eventdev_dma_adapter_stats_get)(const struct rte_eventdev *dev,
 typedef int (*eventdev_dma_adapter_stats_reset)(const struct rte_eventdev *dev,
 						const int16_t dma_dev_id);
 
+/**
+ * Event device vector adapter capabilities.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param caps
+ *   Vector adapter capabilities
+ * @param ops
+ *   Vector adapter ops
+ *
+ * @return
+ *   Return 0 on success.
+ *
+ */
+typedef int (*eventdev_vector_adapter_caps_get_t)(const struct rte_eventdev *dev, uint32_t *caps,
+						  const struct event_vector_adapter_ops **ops);
+
+/**
+ * Event device vector adapter info.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param info
+ *   Vector adapter info
+ *
+ * @return
+ *   Return 0 on success.
+ */
+typedef int (*eventdev_vector_adapter_info_get_t)(const struct rte_eventdev *dev,
+						  struct rte_event_vector_adapter_info *info);
 
 /** Event device operations function pointer table */
 struct eventdev_ops {
@@ -1697,6 +1728,11 @@ struct eventdev_ops {
 	eventdev_dma_adapter_stats_reset dma_adapter_stats_reset;
 	/**< Reset DMA stats */
 
+	eventdev_vector_adapter_caps_get_t vector_adapter_caps_get;
+	/**< Get vector adapter capabilities */
+	eventdev_vector_adapter_info_get_t vector_adapter_info_get;
+	/**< Get vector adapter info */
+
 	eventdev_selftest dev_selftest;
 	/**< Start eventdev Selftest */
 
diff --git a/lib/eventdev/meson.build b/lib/eventdev/meson.build
index 71dea91727..0797c145e7 100644
--- a/lib/eventdev/meson.build
+++ b/lib/eventdev/meson.build
@@ -18,6 +18,7 @@ sources = files(
         'rte_event_eth_tx_adapter.c',
         'rte_event_ring.c',
         'rte_event_timer_adapter.c',
+        'rte_event_vector_adapter.c',
         'rte_eventdev.c',
 )
 headers = files(
@@ -27,6 +28,7 @@ headers = files(
         'rte_event_eth_tx_adapter.h',
         'rte_event_ring.h',
         'rte_event_timer_adapter.h',
+        'rte_event_vector_adapter.h',
         'rte_eventdev.h',
         'rte_eventdev_trace_fp.h',
 )
@@ -38,6 +40,7 @@ driver_sdk_headers += files(
         'eventdev_pmd_pci.h',
         'eventdev_pmd_vdev.h',
         'event_timer_adapter_pmd.h',
+        'event_vector_adapter_pmd.h',
 )
 
 deps += ['ring', 'ethdev', 'hash', 'mempool', 'mbuf', 'timer', 'cryptodev', 'dmadev']
diff --git a/lib/eventdev/rte_event_vector_adapter.c b/lib/eventdev/rte_event_vector_adapter.c
new file mode 100644
index 0000000000..5f38a9a40b
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.c
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mcslock.h>
+#include <rte_service_component.h>
+#include <rte_tailq.h>
+
+#include "event_vector_adapter_pmd.h"
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+#define ADAPTER_ID(dev_id, queue_id, adapter_id)                                                   \
+	((uint32_t)dev_id << 16 | (uint32_t)queue_id << 8 | (uint32_t)adapter_id)
+#define DEV_ID_FROM_ADAPTER_ID(adapter_id)     ((adapter_id >> 16) & 0xFF)
+#define QUEUE_ID_FROM_ADAPTER_ID(adapter_id)   ((adapter_id >> 8) & 0xFF)
+#define ADAPTER_ID_FROM_ADAPTER_ID(adapter_id) (adapter_id & 0xFF)
+
+#define MZ_NAME_MAX_LEN	    64
+#define DATA_MZ_NAME_FORMAT "rte_event_vector_adapter_data_%d_%d_%d"
+
+RTE_LOG_REGISTER_SUFFIX(ev_vector_logtype, adapter.vector, NOTICE);
+#define RTE_LOGTYPE_EVVEC ev_vector_logtype
+
+struct rte_event_vector_adapter *adapters[RTE_EVENT_MAX_DEVS][RTE_EVENT_MAX_QUEUES_PER_DEV];
+
+#define EVVEC_LOG(level, logtype, ...)                                                             \
+	RTE_LOG_LINE_PREFIX(level, logtype,                                                        \
+			    "EVVEC: %s() line %u: ", __func__ RTE_LOG_COMMA __LINE__, __VA_ARGS__)
+#define EVVEC_LOG_ERR(...) EVVEC_LOG(ERR, EVVEC, __VA_ARGS__)
+
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+#define EVVEC_LOG_DBG(...) EVVEC_LOG(DEBUG, EVVEC, __VA_ARGS__)
+#else
+#define EVVEC_LOG_DBG(...) /* No debug logging */
+#endif
+
+#define PTR_VALID_OR_ERR_RET(ptr, retval)                                                          \
+	do {                                                                                       \
+		if (ptr == NULL) {                                                                 \
+			rte_errno = EINVAL;                                                        \
+			return retval;                                                             \
+		}                                                                                  \
+	} while (0)
+
+static int
+validate_conf(const struct rte_event_vector_adapter_conf *conf,
+	      struct rte_event_vector_adapter_info *info)
+{
+	int rc = -EINVAL;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, rc);
+
+	if (conf->vector_sz < info->min_vector_sz || conf->vector_sz > info->max_vector_sz) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be between %u and %u",
+			      conf->vector_sz, info->min_vector_sz, info->max_vector_sz);
+		return rc;
+	}
+
+	if (conf->vector_timeout_ns < info->min_vector_timeout_ns ||
+	    conf->vector_timeout_ns > info->max_vector_timeout_ns) {
+		EVVEC_LOG_DBG("invalid vector timeout %u, should be between %u and %u",
+			      conf->vector_timeout_ns, info->min_vector_timeout_ns,
+			      info->max_vector_timeout_ns);
+		return rc;
+	}
+
+	if (conf->vector_mp == NULL) {
+		EVVEC_LOG_DBG("invalid mempool for vector adapter");
+		return rc;
+	}
+
+	if (info->log2_sz && rte_is_power_of_2(conf->vector_sz) != 0) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be a power of 2", conf->vector_sz);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+default_port_conf_cb(uint8_t event_dev_id, uint8_t *event_port_id, void *conf_arg)
+{
+	struct rte_event_port_conf *port_conf, def_port_conf = {0};
+	struct rte_event_dev_config dev_conf;
+	struct rte_eventdev *dev;
+	uint8_t port_id;
+	uint8_t dev_id;
+	int started;
+	int ret;
+
+	dev = &rte_eventdevs[event_dev_id];
+	dev_id = dev->data->dev_id;
+	dev_conf = dev->data->dev_conf;
+
+	started = dev->data->dev_started;
+	if (started)
+		rte_event_dev_stop(dev_id);
+
+	port_id = dev_conf.nb_event_ports;
+	if (conf_arg != NULL)
+		port_conf = conf_arg;
+	else {
+		port_conf = &def_port_conf;
+		ret = rte_event_port_default_conf_get(dev_id, (port_id - 1), port_conf);
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_conf.nb_event_ports += 1;
+	if (port_conf->event_port_cfg & RTE_EVENT_PORT_CFG_SINGLE_LINK)
+		dev_conf.nb_single_link_event_port_queues += 1;
+
+	ret = rte_event_dev_configure(dev_id, &dev_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to configure event dev %u", dev_id);
+		if (started)
+			if (rte_event_dev_start(dev_id))
+				return -EIO;
+
+		return ret;
+	}
+
+	ret = rte_event_port_setup(dev_id, port_id, port_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to setup event port %u on event dev %u", port_id, dev_id);
+		return ret;
+	}
+
+	*event_port_id = port_id;
+
+	if (started)
+		ret = rte_event_dev_start(dev_id);
+
+	return ret;
+}
+
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf)
+{
+	return rte_event_vector_adapter_create_ext(conf, default_port_conf_cb, NULL);
+}
+
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb, void *conf_arg)
+{
+	struct rte_event_vector_adapter *adapter = NULL;
+	struct rte_event_vector_adapter_info info;
+	char mz_name[MZ_NAME_MAX_LEN];
+	const struct rte_memzone *mz;
+	struct rte_eventdev *dev;
+	uint32_t caps;
+	int i, n, rc;
+
+	PTR_VALID_OR_ERR_RET(conf, NULL);
+
+	if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+		adapters[conf->event_dev_id][conf->ev.queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[conf->event_dev_id][conf->ev.queue_id][i].used == false) {
+			adapter = &adapters[conf->event_dev_id][conf->ev.queue_id][i];
+			adapter->adapter_id = ADAPTER_ID(conf->event_dev_id, conf->ev.queue_id, i);
+			adapter->used = true;
+			break;
+		}
+	}
+
+	if (adapter == NULL) {
+		EVVEC_LOG_DBG("no available vector adapters");
+		rte_errno = ENODEV;
+		return NULL;
+	}
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, NULL);
+
+	dev = &rte_eventdevs[conf->event_dev_id];
+	if (dev->dev_ops->vector_adapter_caps_get != NULL &&
+	    dev->dev_ops->vector_adapter_info_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &caps, &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+
+		rc = dev->dev_ops->vector_adapter_info_get(dev, &info);
+		if (rc < 0) {
+			adapter->ops = NULL;
+			EVVEC_LOG_DBG("failed to get vector adapter info rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+	}
+
+	if (conf->ev.sched_type != dev->data->queues_cfg[conf->ev.queue_id].schedule_type &&
+	    !(dev->data->event_dev_cap & RTE_EVENT_DEV_CAP_QUEUE_ALL_TYPES)) {
+		EVVEC_LOG_DBG("invalid event schedule type, eventdev doesn't support all types");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	if (!(caps & RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT)) {
+		if (conf_cb == NULL) {
+			EVVEC_LOG_DBG("port config callback is NULL");
+			rte_errno = EINVAL;
+			goto error;
+		}
+
+		rc = conf_cb(conf->event_dev_id, &adapter->data->event_port_id, conf_arg);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to create port for vector adapter");
+			rte_errno = EINVAL;
+			goto error;
+		}
+	}
+
+	rc = validate_conf(conf, &info);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	n = snprintf(mz_name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, conf->event_dev_id,
+		     conf->ev.queue_id, adapter->adapter_id);
+	if (n >= (int)sizeof(mz_name)) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create memzone name");
+		rte_errno = EINVAL;
+		goto error;
+	}
+	mz = rte_memzone_reserve(mz_name, sizeof(struct rte_event_vector_adapter_data),
+				 conf->socket_id, 0);
+	if (mz == NULL) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to reserve memzone for vector adapter");
+		rte_errno = ENOMEM;
+		goto error;
+	}
+
+	adapter->data = mz->addr;
+	memset(adapter->data, 0, sizeof(struct rte_event_vector_adapter_data));
+
+	adapter->data->mz = mz;
+	adapter->data->event_dev_id = conf->event_dev_id;
+	adapter->data->id = adapter->adapter_id;
+	adapter->data->socket_id = conf->socket_id;
+	adapter->data->conf = *conf;
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->create, NULL);
+
+	rc = adapter->ops->create(adapter);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create vector adapter");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+
+	return adapter;
+
+error:
+	adapter->used = false;
+	return NULL;
+}
+
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id)
+{
+	uint8_t adapter_idx = ADAPTER_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t queue_id = QUEUE_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t dev_id = DEV_ID_FROM_ADAPTER_ID(adapter_id);
+	struct rte_event_vector_adapter *adapter;
+	const struct rte_memzone *mz;
+	char name[MZ_NAME_MAX_LEN];
+	struct rte_eventdev *dev;
+	int rc;
+
+	if (dev_id >= RTE_EVENT_MAX_DEVS || queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV ||
+	    adapter_idx >= RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE) {
+		EVVEC_LOG_ERR("invalid adapter id %u", adapter_id);
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (adapters[dev_id][queue_id] == NULL) {
+		adapters[dev_id][queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[dev_id][queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	if (adapters[dev_id][queue_id][adapter_idx].used == true)
+		return &adapters[dev_id][queue_id][adapter_idx];
+
+	adapter = &adapters[dev_id][queue_id][adapter_idx];
+
+	snprintf(name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, dev_id, queue_id, adapter_idx);
+	mz = rte_memzone_lookup(name);
+	if (mz == NULL) {
+		EVVEC_LOG_DBG("failed to lookup memzone for vector adapter");
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	adapter->data = mz->addr;
+	dev = &rte_eventdevs[dev_id];
+
+	if (dev->dev_ops->vector_adapter_caps_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &adapter->data->caps,
+							   &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities");
+			rte_errno = ENOTSUP;
+			return NULL;
+		}
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+	adapter->adapter_id = adapter_id;
+	adapter->used = true;
+
+	return adapter;
+}
+
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter)
+{
+	int rc;
+
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	if (adapter->used == false) {
+		EVVEC_LOG_ERR("event vector adapter is not allocated");
+		return -EINVAL;
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->destroy, -ENOTSUP);
+
+	rc = adapter->ops->destroy(adapter);
+	if (rc < 0) {
+		EVVEC_LOG_DBG("failed to destroy vector adapter");
+		return rc;
+	}
+
+	rte_memzone_free(adapter->data->mz);
+	adapter->ops = NULL;
+	adapter->enqueue = dummy_vector_adapter_enqueue;
+	adapter->data = NULL;
+	adapter->used = false;
+
+	return 0;
+}
+
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id, struct rte_event_vector_adapter_info *info)
+{
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, -EINVAL);
+	PTR_VALID_OR_ERR_RET(info, -EINVAL);
+
+	struct rte_eventdev *dev = &rte_eventdevs[event_dev_id];
+	if (dev->dev_ops->vector_adapter_info_get != NULL)
+		return dev->dev_ops->vector_adapter_info_get(dev, info);
+
+	return 0;
+}
+
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(conf, -EINVAL);
+
+	*conf = adapter->data->conf;
+	return 0;
+}
+
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id)
+{
+	uint8_t remaining = 0;
+	int i;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, 0);
+
+	if (event_queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV)
+		return 0;
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[event_dev_id][event_queue_id][i].used == false)
+			remaining++;
+	}
+
+	return remaining;
+}
+
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(stats, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_get, -ENOTSUP);
+
+	adapter->ops->stats_get(adapter, stats);
+
+	return 0;
+}
+
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_reset, -ENOTSUP);
+
+	adapter->ops->stats_reset(adapter);
+
+	return 0;
+}
diff --git a/lib/eventdev/rte_event_vector_adapter.h b/lib/eventdev/rte_event_vector_adapter.h
new file mode 100644
index 0000000000..e7ecc26cdf
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.h
@@ -0,0 +1,469 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __RTE_EVENT_VECTOR_ADAPTER_H__
+#define __RTE_EVENT_VECTOR_ADAPTER_H__
+
+/**
+ * @file rte_event_vector_adapter.h
+ *
+ * @warning
+ * @b EXPERIMENTAL:
+ * All functions in this file may be changed or removed without prior notice.
+ *
+ * Event vector adapter API.
+ *
+ * An event vector adapter has the following working model:
+ *
+ *         ┌──────────┐
+ *         │  Vector  ├─┐
+ *         │ adapter0 │ │
+ *         └──────────┘ │
+ *         ┌──────────┐ │   ┌──────────┐
+ *         │  Vector  ├─┼──►│  Event   │
+ *         │ adapter1 │ │   │  Queue0  │
+ *         └──────────┘ │   └──────────┘
+ *         ┌──────────┐ │
+ *         │  Vector  ├─┘
+ *         │ adapter2 │
+ *         └──────────┘
+ *
+ *         ┌──────────┐
+ *         │  Vector  ├─┐
+ *         │ adapter0 │ │   ┌──────────┐
+ *         └──────────┘ ├──►│  Event   │
+ *         ┌──────────┐ │   │  Queue1  │
+ *         │  Vector  ├─┘   └──────────┘
+ *         │ adapter1 │
+ *         └──────────┘
+ *
+ * - A vector adapter can be seen as an extension to event queue. It helps in
+ *   aggregating ptrs and generating a vector event which is enqueued to the
+ *   event queue.
+ *
+ * - Multiple vector adapters can be created on an event queue, each with its
+ *   own unique properties such as event properties, vector size, and timeout.
+ *   Note: If the target event queue doesn't support RTE_EVENT_QUEUE_CFG_ALL_TYPES,
+ *         then the vector adapter should use the same schedule type as the event
+ *         queue.
+ *
+ * - Each vector adapter aggregates ptrs, generates a vector event and
+ *   enqueues it to the event queue with the event properties mentioned in
+ *   rte_event_vector_adapter_conf::ev.
+ *
+ * - After configuring the vector adapter, Application needs to use the
+ *   rte_event_vector_adapter_enqueue() function to enqueue ptrs i.e.,
+ *   mbufs/ptrs/u64s to the vector adapter.
+ *   On reaching the configured vector size or timeout, the vector adapter
+ *   enqueues the event vector to the event queue.
+ *   Note: Application should use the event_type and sub_event_type properly
+ *         identifying the contents of vector event on dequeue.
+ *
+ * - If the vector adapter advertises the RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ *  capability, application can use the RTE_EVENT_VECTOR_ENQ_[S|E]OV flags
+ *  to indicate the start and end of a vector event.
+ *  * When RTE_EVENT_VECTOR_ENQ_SOV is set, the vector adapter will flush any
+ *    aggregation in progress as a vector event and start aggregating a new
+ *    vector event with the enqueued ptr.
+ *  * When RTE_EVENT_VECTOR_ENQ_EOV is set, the vector adapter will add the
+ *    current ptr enqueued to the aggregated event and enqueue the vector event
+ *    to the event queue.
+ *  * If both flags are set, the vector adapter will flush the current aggregation
+ *    as a vector event and enqueue the current ptr as a single event to the event
+ *    queue.
+ *
+ * - If the vector adapter reaches the configured vector size, it will enqueue
+ *   the aggregated vector event to the event queue.
+ *
+ * - If the vector adapter reaches the configured vector timeout, it will flush
+ *   the current aggregation as a vector event if the minimum vector size is
+ *   reached, if not it will enqueue the ptrs as single events to the event
+ *   queue.
+ *
+ * - If the vector adapter is unable to aggregate the ptrs into a vector event,
+ *   it will enqueue the ptrs as single events to the event queue with the event
+ *   properties mentioned in rte_event_vector_adapter_conf::ev_fallback.
+ *
+ * Before using the vector adapter, the application has to create and configure
+ * an event device and based on the event device capability it might require
+ * creating an additional event port.
+ *
+ * When the application creates the vector adapter using the
+ * ``rte_event_vector_adapter_create()`` function, the event device driver
+ * capabilities are checked. If an in-built port is absent, the application
+ * uses the default function to create a new event port.
+ * For finer control over event port creation, the application should use
+ * the ``rte_event_vector_adapter_create_ext()`` function.
+ *
+ * The application can enqueue one or more ptrs to the vector adapter using the
+ * ``rte_event_vector_adapter_enqueue()`` function and control the aggregation
+ * using the flags.
+ *
+ * Vector adapters report stats using the ``rte_event_vector_adapter_stats_get()``
+ * function and reset the stats using the ``rte_event_vector_adapter_stats_reset()``.
+ *
+ * The application can destroy the vector adapter using the
+ * ``rte_event_vector_adapter_destroy()`` function.
+ *
+ */
+
+#include <rte_eventdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV (1ULL << 0)
+/**< Vector adapter supports Start of Vector (SOV) and End of Vector (EOV) flags
+ *  in the enqueue flags.
+ *
+ * @see RTE_EVENT_VECTOR_ENQ_SOV
+ * @see RTE_EVENT_VECTOR_ENQ_EOV
+ */
+
+#define RTE_EVENT_VECTOR_ENQ_SOV   (1ULL << 0)
+/**< Indicates the start of a vector event. When enqueue is called with
+ *  RTE_EVENT_VECTOR_ENQ_SOV, the vector adapter will flush any vector
+ *  aggregation in progress and start aggregating a new vector event with
+ *  the enqueued ptr.
+ */
+#define RTE_EVENT_VECTOR_ENQ_EOV   (1ULL << 1)
+/**< Indicates the end of a vector event. When enqueue is called with
+ *  RTE_EVENT_VECTOR_ENQ_EOV, the vector adapter will add the current ptr
+ *  to the aggregated event and flush the event vector.
+ */
+#define RTE_EVENT_VECTOR_ENQ_FLUSH (1ULL << 2)
+/**< Flush any in-progress vector aggregation. */
+
+/**
+ * Vector adapter configuration structure
+ */
+struct rte_event_vector_adapter_conf {
+	uint8_t event_dev_id;
+	/**< Event device identifier */
+	uint32_t socket_id;
+	/**< Identifier of socket from which to allocate memory for adapter */
+	struct rte_event ev;
+	/**<
+	 *  The values from the following event fields will be used when
+	 *  queuing work:
+	 *   - queue_id: Targeted event queue ID for vector event.
+	 *   - event_priority: Event priority of the vector event in
+	 *                     the event queue relative to other events.
+	 *   - sched_type: Scheduling type for events from this vector adapter.
+	 *   - event_type: Event type for the vector event.
+	 *   - sub_event_type: Sub event type for the vector event.
+	 *   - flow_id: Flow ID for the vectors enqueued to the event queue by
+	 *              the vector adapter.
+	 */
+	struct rte_event ev_fallback;
+	/**<
+	 * The values from the following event fields will be used when
+	 * aggregation fails and single event is enqueued:
+	 *   - event_type: Event type for the single event.
+	 *   - sub_event_type: Sub event type for the single event.
+	 *   - flow_id: Flow ID for the single event.
+	 *
+	 * Other fields are taken from rte_event_vector_adapter_conf::ev.
+	 */
+	uint16_t vector_sz;
+	/**<
+	 * Indicates the maximum number for enqueued work to combine and form a vector.
+	 * Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_sz
+	 * @see rte_event_vector_adapter_info::max_vector_sz
+	 */
+	uint64_t vector_timeout_ns;
+	/**<
+	 * Indicates the maximum number of nanoseconds to wait for receiving
+	 * work. Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_ns
+	 * @see rte_event_vector_adapter_info::max_vector_ns
+	 */
+	struct rte_mempool *vector_mp;
+	/**<
+	 * Indicates the mempool that should be used for allocating
+	 * rte_event_vector container.
+	 * @see rte_event_vector_pool_create
+	 */
+};
+
+/**
+ * Vector adapter vector info structure
+ */
+struct rte_event_vector_adapter_info {
+	uint8_t max_vector_adapters_per_event_queue;
+	/**< Maximum number of vector adapters configurable */
+	uint16_t min_vector_sz;
+	/**< Minimum vector size configurable */
+	uint16_t max_vector_sz;
+	/**< Maximum vector size configurable */
+	uint64_t min_vector_timeout_ns;
+	/**< Minimum vector timeout configurable */
+	uint64_t max_vector_timeout_ns;
+	/**< Maximum vector timeout configurable */
+	uint8_t log2_sz;
+	/**< True if the size configured should be in log2. */
+};
+
+/**
+ * Vector adapter statistics structure
+ */
+struct rte_event_vector_adapter_stats {
+	uint64_t vectorized;
+	/**< Number of events vectorized */
+	uint64_t vectors_timedout;
+	/**< Number of timeouts occurred */
+	uint64_t vectors_flushed;
+	/**< Number of vectors flushed */
+	uint64_t alloc_failures;
+	/**< Number of vector allocation failures */
+};
+
+struct rte_event_vector_adapter;
+
+typedef int (*rte_event_vector_adapter_enqueue_t)(struct rte_event_vector_adapter *adapter,
+						  uintptr_t ptrs[], uint16_t num_elem,
+						  uint64_t flags);
+/**< @internal Enqueue ptrs into the event vector adapter. */
+
+struct __rte_cache_aligned rte_event_vector_adapter {
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Pointer to driver enqueue function. */
+	struct rte_event_vector_adapter_data *data;
+	/**< Pointer to the adapter data */
+	const struct event_vector_adapter_ops *ops;
+	/**< Functions exported by adapter driver */
+
+	uint32_t adapter_id;
+	/**< Identifier of the adapter instance. */
+	uint8_t used : 1;
+	/**< Flag to indicate that this adapter is being used. */
+};
+
+/**
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_vector_adapter_port_conf_cb_t)(uint8_t event_dev_id, uint8_t *event_port_id,
+						       void *conf_arg);
+
+/**
+ * Create an event vector adapter.
+ *
+ * This function creates an event vector adapter based on the provided
+ * configuration. The adapter can be used to combine multiple mbufs/ptrs/u64s
+ * into a single vector event, i.e., rte_event_vector, which is then enqueued
+ * to the event queue provided.
+ * @see rte_event_vector_adapter_conf::ev::event_queue_id.
+ *
+ * @param conf
+ *   Configuration for the event vector adapter.
+ * @return
+ *   - Pointer to the created event vector adapter on success.
+ *   - NULL on failure with rte_errno set to the error code.
+ *     Possible rte_errno values include:
+ *    - EINVAL: Invalid event device identifier specified in config.
+ *    - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *    - ENOSPC: Maximum number of adapters already created.
+ */
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Create an event vector adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the event
+ * vector adapter creation. If a built-in port is absent, then the function uses
+ * the callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ *   The event vector adapter configuration structure.
+ * @param conf_cb
+ *   The port config callback function.
+ * @param conf_arg
+ *   Opaque pointer to the argument for the callback function.
+ * @return
+ *   - Pointer to the new allocated event vector adapter on success.
+ *   - NULL on error with rte_errno set appropriately.
+ *   Possible rte_errno values include:
+ *   - ERANGE: vector_timeout_ns is not in supported range.
+ *   - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *   - EINVAL: Invalid event device identifier specified in config.
+ *   - ENOSPC: Maximum number of adapters already created.
+ */
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb,
+				    void *conf_arg);
+
+/**
+ * Lookup an event vector adapter using its identifier.
+ *
+ * This function returns the event vector adapter based on the adapter_id.
+ * This is useful when the adapter is created in another process and the
+ * application wants to use the adapter in the current process.
+ *
+ * @param adapter_id
+ *   Identifier of the event vector adapter to look up.
+ * @return
+ *   - Pointer to the event vector adapter on success.
+ *   - NULL if the adapter is not found.
+ */
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id);
+
+/**
+ * Destroy an event vector adapter.
+ *
+ * This function releases the resources associated with the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter to be destroyed.
+ * @return
+ *   - 0 on success.
+ *   - Negative value on failure with rte_errno set to the error code.
+ */
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Get the vector info of an event vector adapter.
+ *
+ * This function retrieves the vector info of the event vector adapter.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param info
+ *   Pointer to the structure where the vector info will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ *   - EINVAL if the event device identifier is invalid.
+ *   - ENOTSUP if the event device does not support vector adapters.
+ */
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id,
+				  struct rte_event_vector_adapter_info *info);
+
+/**
+ * Get the configuration of an event vector adapter.
+ *
+ * This function retrieves the configuration of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param conf
+ *   Pointer to the structure where the configuration will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Get the remaining event vector adapters.
+ *
+ * This function retrieves the number of remaining event vector adapters
+ * available for a given event device and event queue.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param event_queue_id
+ *   Event queue identifier.
+ * @return
+ *   Number of remaining slots available for enqueuing events.
+ */
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id);
+
+/**
+ * Get the event vector adapter statistics.
+ *
+ * This function retrieves the statistics of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param stats
+ *   Pointer to the structure where the statistics will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats);
+
+/**
+ * @brief Reset the event vector adapter statistics.
+ *
+ * This function resets the statistics of the event vector adapter to their default values.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter whose statistics are to be reset.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Retrieve the service ID of the event vector adapter. If the adapter doesn't
+ * use an rte_service function, this function returns -ESRCH.
+ *
+ * @param adapter
+ *   A pointer to an event vector adapter.
+ * @param [out] service_id
+ *   A pointer to a uint32_t, to be filled in with the service id.
+ *
+ * @return
+ *   - 0: Success
+ *   - <0: Error code on failure
+ *   - -ESRCH: the adapter does not require a service to operate
+ */
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id);
+
+/**
+ * Enqueue ptrs into the event vector adapter.
+ *
+ * This function enqueues a specified number of ptrs into the event vector adapter.
+ * The ptrs are combined into a single vector event, i.e., rte_event_vector, which
+ * is then enqueued to the event queue configured in the adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param ptrs
+ *   Array of ptrs to be enqueued.
+ * @param num_elem
+ *   Number of ptrs to be enqueued.
+ * @param flags
+ *   Flags to be used for the enqueue operation.
+ * @return
+ *   Number of ptrs enqueued on success.
+ */
+static inline int
+rte_event_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uintptr_t ptrs[],
+				 uint16_t num_elem, uint64_t flags)
+{
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+	if (adapter == NULL) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+
+	if (adapter->used == false) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+#endif
+	return adapter->enqueue(adapter, ptrs, num_elem, flags);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTE_EVENT_VECTOR_ADAPTER_H__ */
diff --git a/lib/eventdev/rte_eventdev.c b/lib/eventdev/rte_eventdev.c
index 43cd95d765..0be1c0ba31 100644
--- a/lib/eventdev/rte_eventdev.c
+++ b/lib/eventdev/rte_eventdev.c
@@ -244,6 +244,27 @@ rte_event_dma_adapter_caps_get(uint8_t dev_id, uint8_t dma_dev_id, uint32_t *cap
 	return 0;
 }
 
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps)
+{
+	const struct event_vector_adapter_ops *ops;
+	struct rte_eventdev *dev;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(dev_id, -EINVAL);
+
+	dev = &rte_eventdevs[dev_id];
+
+	if (caps == NULL)
+		return -EINVAL;
+
+	if (dev->dev_ops->vector_adapter_caps_get == NULL)
+		*caps = 0;
+
+	return dev->dev_ops->vector_adapter_caps_get ?
+		       dev->dev_ops->vector_adapter_caps_get(dev, caps, &ops) :
+		       0;
+}
+
 static inline int
 event_dev_queue_config(struct rte_eventdev *dev, uint8_t nb_queues)
 {
diff --git a/lib/eventdev/rte_eventdev.h b/lib/eventdev/rte_eventdev.h
index 6400d6109f..41c459feff 100644
--- a/lib/eventdev/rte_eventdev.h
+++ b/lib/eventdev/rte_eventdev.h
@@ -1985,6 +1985,14 @@ int
 rte_event_eth_tx_adapter_caps_get(uint8_t dev_id, uint16_t eth_port_id,
 				uint32_t *caps);
 
+/* Vector adapter capability bitmap flags */
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT	0x1
+/**< This flag is set when the vector adapter is capable of generating events
+ * using an internal event port.
+ */
+
+int rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps);
+
 /**
  * Converts nanoseconds to *timeout_ticks* value for rte_event_dequeue_burst()
  *
diff --git a/lib/eventdev/version.map b/lib/eventdev/version.map
index 44687255cb..083e6c0d18 100644
--- a/lib/eventdev/version.map
+++ b/lib/eventdev/version.map
@@ -156,6 +156,19 @@ EXPERIMENTAL {
 
 	# added in 25.03
 	rte_event_eth_rx_adapter_queues_add;
+
+	#added in 25.05
+	rte_event_vector_adapter_create;
+	rte_event_vector_adapter_create_ext;
+	rte_event_vector_adapter_lookup;
+	rte_event_vector_adapter_destroy;
+	rte_event_vector_adapter_info_get;
+	rte_event_vector_adapter_conf_get;
+	rte_event_vector_adapter_remaining;
+	rte_event_vector_adapter_stats_get;
+	rte_event_vector_adapter_stats_reset;
+	rte_event_vector_adapter_service_id_get;
+	rte_event_vector_adapter_enqueue;
 };
 
 INTERNAL {
-- 
2.43.0


^ permalink raw reply	[relevance 1%]

* Re: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-26 12:02  0%   ` David Marchand
  2025-03-26 12:26  3%     ` Morten Brørup
  2025-03-26 13:07  0%     ` Bruce Richardson
@ 2025-03-26 13:36  0%     ` Bruce Richardson
  2025-03-26 13:54  3%       ` David Marchand
  2 siblings, 1 reply; 153+ results
From: Bruce Richardson @ 2025-03-26 13:36 UTC (permalink / raw)
  To: David Marchand; +Cc: thomas, dev, andremue, dpdk-techboard

On Wed, Mar 26, 2025 at 01:02:32PM +0100, David Marchand wrote:
> On Mon, Mar 17, 2025 at 4:43 PM David Marchand
> <david.marchand@redhat.com> wrote:
> >
> > So far, each DPDK library (or driver) exposing symbols in an ABI had to
> > maintain a version.map and use some macros for symbol versioning,
> > specially crafted with the GNU linker in mind.
> >
> > This series proposes to rework the whole principle, and instead rely on
> > marking the symbol exports in the source code itself, then let it to the
> > build framework to produce a version script adapted to the linker in use
> > (think GNU linker vs MSVC linker).
> >
> > This greatly simplifies versioning symbols: a developer does not need to
> > know anything about version.map, or that a versioned symbol must be
> > renamed with _v26, annotated with __vsym, exported in a header etc...
> >
> > Checking symbol maps becomes unnecessary since generated by the build
> > framework.
> >
> > Updating to a new ABI is just a matter of bumping the value in
> > ABI_VERSION.
> >
> >
> > Comments please.
> 
> - I am considering making rte_function_versioning.h a non exported
> header (precisely, moving it to buildtools/ and maybe renaming it).
> 
> This header contains macros not prefixed with RTE_.
> Using it requires some build trick (see use_function_versioning).
> And I don't see symbol versioning as a MUST infrastructure that DPDK
> needs to provide to datapath applications.
> 
> Yet technically, this change would be an API breakage if some
> applications indeed relied on it.
> 

Is it not needed for exporting if an exported library header had versioned
symbols is in?

/Bruce

^ permalink raw reply	[relevance 0%]

* Re: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-26 13:36  0%     ` Bruce Richardson
@ 2025-03-26 13:54  3%       ` David Marchand
  2025-03-26 14:16  0%         ` Bruce Richardson
  0 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-26 13:54 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: thomas, dev, andremue, dpdk-techboard

On Wed, Mar 26, 2025 2:37 PM Bruce Richardson
<bruce.richardson@intel.com> wrote:
> > - I am considering making rte_function_versioning.h a non exported
> > header (precisely, moving it to buildtools/ and maybe renaming it).
> >
> > This header contains macros not prefixed with RTE_.
> > Using it requires some build trick (see use_function_versioning).
> > And I don't see symbol versioning as a MUST infrastructure that DPDK
> > needs to provide to datapath applications.
> >
> > Yet technically, this change would be an API breakage if some
> > applications indeed relied on it.
> >
>
> Is it not needed for exporting if an exported library header had versioned
> symbols is in?

That could be, though we don't expose such versionned symbols atm (and
I don't think we ever did in DPDK).
The only public symbol is always the symbol implemented for the latest
ABI for newly compiled applications.

Wrt to your other comment on the header(s) location, I don't mind
moving to lib/eal/common.


-- 
David Marchand


^ permalink raw reply	[relevance 3%]

* Re: [RFC v4 0/8] Symbol versioning and export rework
  2025-03-26 13:54  3%       ` David Marchand
@ 2025-03-26 14:16  0%         ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-03-26 14:16 UTC (permalink / raw)
  To: David Marchand; +Cc: thomas, dev, andremue, dpdk-techboard

On Wed, Mar 26, 2025 at 02:54:32PM +0100, David Marchand wrote:
> On Wed, Mar 26, 2025 2:37 PM Bruce Richardson
> <bruce.richardson@intel.com> wrote:
> > > - I am considering making rte_function_versioning.h a non exported
> > > header (precisely, moving it to buildtools/ and maybe renaming it).
> > >
> > > This header contains macros not prefixed with RTE_.
> > > Using it requires some build trick (see use_function_versioning).
> > > And I don't see symbol versioning as a MUST infrastructure that DPDK
> > > needs to provide to datapath applications.
> > >
> > > Yet technically, this change would be an API breakage if some
> > > applications indeed relied on it.
> > >
> >
> > Is it not needed for exporting if an exported library header had versioned
> > symbols is in?
> 
> That could be, though we don't expose such versionned symbols atm (and
> I don't think we ever did in DPDK).
> The only public symbol is always the symbol implemented for the latest
> ABI for newly compiled applications.
>

Ok, that's good. Looking through code and code history I do indeed see that
the use of function versioning in the past seems to have all been done
through the C file. Therefore, no issues with making the header
internal-only and not exported.

/Bruce
 

^ permalink raw reply	[relevance 0%]

* RE: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels without monitoring enabled
  2025-03-12 15:36  0%     ` Stephen Hemminger
@ 2025-03-26 21:53  0%       ` Long Li
  0 siblings, 0 replies; 153+ results
From: Long Li @ 2025-03-26 21:53 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: longli, Wei Hu, dev

> Subject: Re: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels without
> monitoring enabled
> 
> On Wed, 12 Mar 2025 00:33:52 +0000
> Long Li <longli@microsoft.com> wrote:
> 
> > > Subject: [EXTERNAL] Re: [patch v2 0/6] Support VMBUS channels
> > > without monitoring enabled
> > >
> > > On Mon, 10 Mar 2025 14:42:51 -0700
> > > longli@linuxonhyperv.com wrote:
> > >
> > > > From: Long Li <longli@microsoft.com>
> > > >
> > > > Hyperv may expose VMBUS channels without monitoring enabled. In
> > > > this case, it programs almost all the data traffic to VF.
> > > >
> > > > This patchset enabled vmbus/netvsc to use channels without
> > > > monitoring enabled.
> > >
> > >
> > > CI still reports a build issue
> >
> > There are ABI changes to rte_vmbus_* calls. This patch added
> rte_vmbus_device* as the 1st parameter to those calls.
> >
> > This will be a breaking change, and it only affects hn_netvsc as it's the only PMD
> using the vmbus.
> >
> > Reading ./doc/guides/contributing/abi_policy.rst, I think the best option is to
> use RTE_NEXT_ABI. But I can't find its definition in the code base.
> >
> > Please advise on how to proceed with making those breaking ABI changes.
> >
> > Thanks,
> > Long
> 
> Can't take it as is, here are some options:
> 
> 1. Version the API even though should only be used internally. Use API versioning
>    as transistion until 25.11.
> 2. Wait for 25.11 and just fix it now, and do deprecation notice now.
> 
> 3. Mark the API's as internal (in 25.11) and do deprecation notice now.
> 
> 4. Make new functions with different names, and mark old ones as deprecated,
> then remove in 25.11

Hi Stephen,

I have sent deprecation notice and it has been accepted:
https://patchwork.dpdk.org/project/dpdk/patch/1742242184-19600-1-git-send-email-longli@linuxonhyperv.com/

Can you take this patch series?

Thanks,
Long

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] version: 25.07-rc0
  2025-03-26  9:00 10% [PATCH] version: 25.07-rc0 David Marchand
  2025-03-26  9:10  3% ` Bruce Richardson
@ 2025-03-27  7:39  0% ` David Marchand
  1 sibling, 0 replies; 153+ results
From: David Marchand @ 2025-03-27  7:39 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas, Bruce Richardson

On Wed, Mar 26, 2025 at 10:00 AM David Marchand
<david.marchand@redhat.com> wrote:
>
> Start a new release cycle with empty release notes.
> Bump version and ABI minor.
>
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> Acked-by: Bruce Richardson <bruce.richardson@intel.com>

Applied, thanks.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* [PATCH v4] raw/cnxk_gpio: switch to character based GPIO interface
  2025-03-25  4:14  1%   ` [PATCH v3] " Tomasz Duszynski
@ 2025-03-27  9:12  1%     ` Tomasz Duszynski
  0 siblings, 0 replies; 153+ results
From: Tomasz Duszynski @ 2025-03-27  9:12 UTC (permalink / raw)
  To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj

The direct passthrough interrupt mechanism, which allowed bypassing the
kernel, was obscure and is no longer supported. So this driver won't
work with latest SDK kernels. Additionally, the sysfs GPIO control
interface has been deprecated by Linux kernel itself.

That said, this change updates the PMD to use the current GPIO interface
ensuring compatibility with current kernel standards while improving
maintainability and security.

Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
---
v2:
- compile conditionally based on GPIO_V2_PRESENT
v3:
- fix compilation issues due to missing structure member
- quiesce compiler warnings about maybe unused parameters
v4:
- rename GPIO_V2_PRESENT to CNXK_GPIO_V2_PRESENT to indicate
  it's PMD specific

 doc/guides/rawdevs/cnxk_gpio.rst           |  37 +-
 drivers/raw/cnxk_gpio/cnxk_gpio.c          | 525 ++++++++++++---------
 drivers/raw/cnxk_gpio/cnxk_gpio.h          |  17 +-
 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c      | 216 ---------
 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 238 ++--------
 drivers/raw/cnxk_gpio/meson.build          |   1 -
 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h  |  64 ++-
 7 files changed, 445 insertions(+), 653 deletions(-)
 delete mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c

diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst
index 954d3b8905..8084dd4adb 100644
--- a/doc/guides/rawdevs/cnxk_gpio.rst
+++ b/doc/guides/rawdevs/cnxk_gpio.rst
@@ -6,31 +6,20 @@ Marvell CNXK GPIO Driver

 CNXK GPIO PMD configures and manages GPIOs available on the system using
 standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies
-both on standard sysfs GPIO interface provided by the Linux kernel and GPIO
-kernel driver custom interface allowing one to install userspace interrupt
-handlers.
+on standard kernel GPIO character device interface.

 Features
 --------

 Following features are available:

-- export/unexport a GPIO
-- read/write specific value from/to exported GPIO
+- read/write specific value from/to GPIO
 - set GPIO direction
 - set GPIO edge that triggers interrupt
 - set GPIO active low
 - register interrupt handler for specific GPIO
 - multiprocess aware

-Requirements
-------------
-
-PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface
-for installing interrupt handlers for low latency signal processing.
-
-Driver is shipped with Marvell SDK.
-
 Limitations
 -----------

@@ -43,20 +32,20 @@ Device Setup
 CNXK GPIO PMD binds to virtual device which gets created by passing
 `--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter
 tells PMD which GPIO controller should be used. Available controllers are
-available under `/sys/class/gpio`. For further details on how Linux represents
-GPIOs in userspace please refer to
-`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_.
+`/dev/gpiochipN` character devices. For further details on
+how Linux represents GPIOs in userspace please refer to
+`gpio-cdev <https://www.kernel.org/doc/Documentation/ABI/testing/gpio-cdev>`_.

 If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically
 sort list of available gpiochips is used.

 .. code-block:: console

-   $ ls /sys/class/gpio
-   export gpiochip448 unexport
+   $ ls /dev/gpiochip*
+   /dev/gpiochip0

 In above scenario only one GPIO controller is present hence
-`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL.
+`--vdev=cnxk_gpio,gpiochip=0` should be passed to EAL.

 Before performing actual data transfer one needs to call
 ``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The
@@ -65,7 +54,7 @@ being controllable or not. Thus it is user responsibility to pick the proper
 ones. The latter call simply returns queue capacity.

 In order to allow using only subset of available GPIOs `allowlist` PMD param may
-be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]`
+be used. For example passing `--vdev=cnxk_gpio,gpiochip=0,allowlist=[0,1,2,3]`
 to EAL will deny using all GPIOs except those specified explicitly in the
 `allowlist`.

@@ -179,12 +168,12 @@ Request interrupt

 Message is used to install custom interrupt handler.

-Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``.
+Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2``.

-Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt
+Payload needs to be set to ``struct cnxk_gpio_irq2`` which describes interrupt
 being requested.

-Consider using ``rte_pmd_gpio_register_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_register_irq2()`` wrapper.

 Free interrupt
 ~~~~~~~~~~~~~~
@@ -193,7 +182,7 @@ Message is used to remove installed interrupt handler.

 Message must have type set to ``CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ``.

-Consider using ``rte_pmd_gpio_unregister_gpio()`` wrapper.
+Consider using ``rte_pmd_gpio_unregister_irq()`` wrapper.

 Self test
 ---------
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c
index 329ac28a27..bb2dca5441 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c
@@ -3,11 +3,19 @@
  */

 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+#include <regex.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/types.h>

 #include <bus_vdev_driver.h>
 #include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_interrupts.h>
 #include <rte_kvargs.h>
 #include <rte_lcore.h>
 #include <rte_rawdev_pmd.h>
@@ -17,9 +25,12 @@
 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"

-#define CNXK_GPIO_BUFSZ 128
-#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio"
 #define CNXK_GPIO_PARAMS_MZ_NAME "cnxk_gpio_params_mz"
+#define CNXK_GPIO_INVALID_FD (-1)
+
+RTE_LOG_REGISTER_SUFFIX(cnxk_logtype_gpio, gpio, INFO);
+
+#ifdef CNXK_GPIO_V2_PRESENT

 struct cnxk_gpio_params {
 	unsigned int num;
@@ -40,6 +51,31 @@ cnxk_gpio_format_name(char *name, size_t len)
 	snprintf(name, len, "cnxk_gpio");
 }

+static int
+cnxk_gpio_ioctl(struct cnxk_gpio *gpio, unsigned long cmd, void *arg)
+{
+	return ioctl(gpio->fd, cmd, arg) ? -errno : 0;
+}
+
+static int
+cnxk_gpio_gpiochip_ioctl(struct cnxk_gpiochip *gpiochip, unsigned long cmd, void *arg)
+{
+	char path[PATH_MAX];
+	int ret = 0, fd;
+
+	snprintf(path, sizeof(path), "/dev/gpiochip%d", gpiochip->num);
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -errno;
+
+	if (ioctl(fd, cmd, arg))
+		ret = -errno;
+
+	close(fd);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_filter_gpiochip(const struct dirent *dirent)
 {
@@ -54,8 +90,7 @@ cnxk_gpio_set_defaults(struct cnxk_gpio_params *params)
 	struct dirent **namelist;
 	int ret = 0, n;

-	n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip,
-		    alphasort);
+	n = scandir("/dev", &namelist, cnxk_gpio_filter_gpiochip, alphasort);
 	if (n < 0 || n == 0)
 		return -ENODEV;

@@ -143,7 +178,7 @@ cnxk_gpio_parse_arg(struct rte_kvargs *kvlist, const char *arg, arg_handler_t ha
 static int
 cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 {
-	size_t len = sizeof(**params);
+	size_t len = sizeof(**params) + 1;
 	const char *allowlist = NULL;
 	struct rte_kvargs *kvlist;
 	int ret;
@@ -163,11 +198,13 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)

 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_ALLOWLIST, cnxk_gpio_parse_arg_allowlist,
 				  &allowlist);
-	if (ret < 0)
+	if (ret < 0) {
+		ret = -EINVAL;
 		goto out;
+	}

 	if (allowlist)
-		len += strlen(allowlist) + 1;
+		len += strlen(allowlist);

 	*params = cnxk_gpio_params_reserve(len);
 	if (!(*params)) {
@@ -175,7 +212,8 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 		goto out;
 	}

-	strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);
+	if (allowlist)
+		strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);

 	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_GPIOCHIP, cnxk_gpio_parse_arg_gpiochip,
 				  &(*params)->num);
@@ -188,6 +226,24 @@ cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
 	return ret;
 }

+static bool
+cnxk_gpio_allowlist_valid(const char *allowlist)
+{
+	bool ret = false;
+	regex_t regex;
+
+	/* [gpio0<,gpio1,...,gpioN>], where '<...>' is optional part */
+	if (regcomp(&regex, "^\\[[0-9]+(,[0-9]+)*\\]$", REG_EXTENDED))
+		return ret;
+
+	if (!regexec(&regex, allowlist, 0, NULL, 0))
+		ret = true;
+
+	regfree(&regex);
+
+	return ret;
+}
+
 static int
 cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 {
@@ -199,6 +255,17 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	if (!list)
 		return -ENOMEM;

+	/* no gpios provided so allow all */
+	if (!*allowlist) {
+		for (i = 0; i < gpiochip->num_gpios; i++)
+			list[queue++] = i;
+
+		goto out_done;
+	}
+
+	if (!cnxk_gpio_allowlist_valid(allowlist))
+		return -EINVAL;
+
 	allowlist = strdup(allowlist);
 	if (!allowlist) {
 		ret = -ENOMEM;
@@ -239,6 +306,7 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	} while ((token = strtok(NULL, ",")));

 	free(allowlist);
+out_done:
 	gpiochip->allowlist = list;
 	gpiochip->num_queues = queue;

@@ -250,88 +318,6 @@ cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 	return ret;
 }

-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-static int
-cnxk_gpio_read_attr_int(char *attr, int *val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret;
-
-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	ret = sscanf(buf, "%d", val);
-	if (ret < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr(const char *attr, const char *val)
-{
-	FILE *fp;
-	int ret;
-
-	if (!val)
-		return -EINVAL;
-
-	fp = fopen(attr, "w");
-	if (!fp)
-		return -errno;
-
-	ret = fprintf(fp, "%s", val);
-	if (ret < 0) {
-		fclose(fp);
-		return ret;
-	}
-
-	ret = fclose(fp);
-	if (ret)
-		return -errno;
-
-	return 0;
-}
-
-static int
-cnxk_gpio_write_attr_int(const char *attr, int val)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-
-	snprintf(buf, sizeof(buf), "%d", val);
-
-	return cnxk_gpio_write_attr(attr, buf);
-}
-
 static bool
 cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 {
@@ -353,14 +339,16 @@ cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue)
 }

 static bool
-cnxk_gpio_exists(int num)
+cnxk_gpio_available(struct cnxk_gpio *gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	struct stat st;
+	struct gpio_v2_line_info info = { .offset = gpio->num };
+	int ret;

-	snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, num);
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return false;

-	return !stat(buf, &st);
+	return !(info.flags & GPIO_V2_LINE_FLAG_USED);
 }

 static int
@@ -368,9 +356,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 		      rte_rawdev_obj_t queue_conf, size_t queue_conf_size)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
+	struct gpio_v2_line_request req = {0};
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int ret;

 	RTE_SET_USED(queue_conf);
 	RTE_SET_USED(queue_conf_size);
@@ -386,33 +374,87 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id,
 	if (!gpio)
 		return -ENOMEM;

-	num = cnxk_queue_to_gpio(gpiochip, queue_id);
-	gpio->num = num + gpiochip->base;
+	gpio->num = cnxk_queue_to_gpio(gpiochip, queue_id);
+	gpio->fd = CNXK_GPIO_INVALID_FD;
 	gpio->gpiochip = gpiochip;

-	if (!cnxk_gpio_exists(gpio->num)) {
-		snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH);
-		ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-		if (ret) {
-			rte_free(gpio);
-			return ret;
-		}
-	} else {
-		CNXK_GPIO_LOG(WARNING, "using existing gpio%d", gpio->num);
+	if (!cnxk_gpio_available(gpio)) {
+		rte_free(gpio);
+		return -EBUSY;
+	}
+
+	cnxk_gpio_format_name(req.consumer, sizeof(req.consumer));
+	req.offsets[req.num_lines] = gpio->num;
+	req.num_lines = 1;
+
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINE_IOCTL, &req);
+	if (ret) {
+		rte_free(gpio);
+		return ret;
 	}

-	gpiochip->gpios[num] = gpio;
+	gpio->fd = req.fd;
+	gpiochip->gpios[gpio->num] = gpio;

 	return 0;
 }

+static void
+cnxk_gpio_intr_handler(void *data)
+{
+	struct gpio_v2_line_event event;
+	struct cnxk_gpio *gpio = data;
+	int ret;
+
+	ret = read(gpio->fd, &event, sizeof(event));
+	if (ret != sizeof(event)) {
+		CNXK_GPIO_LOG(ERR, "failed to read gpio%d event data", gpio->num);
+		goto out;
+	}
+	if ((unsigned int)gpio->num != event.offset) {
+		CNXK_GPIO_LOG(ERR, "expected event from gpio%d, received from gpio%d",
+			      gpio->num, event.offset);
+		goto out;
+	}
+
+	if (gpio->intr.handler2)
+		(gpio->intr.handler2)(&event, gpio->intr.data);
+	else if (gpio->intr.handler)
+		(gpio->intr.handler)(gpio->num, gpio->intr.data);
+out:
+	rte_intr_ack(gpio->intr.intr_handle);
+}
+
+static int
+cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+{
+	int ret;
+
+	if (!gpio->intr.intr_handle)
+		return 0;
+
+	ret = rte_intr_disable(gpio->intr.intr_handle);
+	if (ret)
+		return ret;
+
+	ret = rte_intr_callback_unregister_sync(gpio->intr.intr_handle, cnxk_gpio_intr_handler,
+						(void *)-1);
+	if (ret)
+		return ret;
+
+	rte_intr_instance_free(gpio->intr.intr_handle);
+	gpio->intr.intr_handle = NULL;
+
+	return 0;
+}
+
+
 static int
 cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 {
 	struct cnxk_gpiochip *gpiochip = dev->dev_private;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct cnxk_gpio *gpio;
-	int num, ret;
+	int num;

 	if (!cnxk_gpio_queue_valid(gpiochip, queue_id))
 		return -EINVAL;
@@ -421,10 +463,11 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id)
 	if (!gpio)
 		return -ENODEV;

-	snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH);
-	ret = cnxk_gpio_write_attr_int(buf, gpio->num);
-	if (ret)
-		return ret;
+	if (gpio->intr.intr_handle)
+		cnxk_gpio_unregister_irq(gpio);
+
+	if (gpio->fd != CNXK_GPIO_INVALID_FD)
+		close(gpio->fd);

 	num = cnxk_queue_to_gpio(gpiochip, queue_id);
 	gpiochip->gpios[num] = NULL;
@@ -462,134 +505,218 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev)

 static const struct {
 	enum cnxk_gpio_pin_edge edge;
-	const char *name;
-} cnxk_gpio_edge_name[] = {
-	{ CNXK_GPIO_PIN_EDGE_NONE, "none" },
-	{ CNXK_GPIO_PIN_EDGE_FALLING, "falling" },
-	{ CNXK_GPIO_PIN_EDGE_RISING, "rising" },
-	{ CNXK_GPIO_PIN_EDGE_BOTH, "both" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_edge_flag[] = {
+	{ CNXK_GPIO_PIN_EDGE_NONE, 0 },
+	{ CNXK_GPIO_PIN_EDGE_FALLING, GPIO_V2_LINE_FLAG_EDGE_FALLING },
+	{ CNXK_GPIO_PIN_EDGE_RISING, GPIO_V2_LINE_FLAG_EDGE_RISING },
+	{ CNXK_GPIO_PIN_EDGE_BOTH, GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING },
 };

-static const char *
-cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge)
+static enum gpio_v2_line_flag
+cnxk_gpio_edge_to_flag(enum cnxk_gpio_pin_edge edge)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (cnxk_gpio_edge_name[i].edge == edge)
-			return cnxk_gpio_edge_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if (cnxk_gpio_edge_flag[i].edge == edge)
+			break;
 	}

-	return NULL;
+	return cnxk_gpio_edge_flag[i].flag;
 }

 static enum cnxk_gpio_pin_edge
-cnxk_gpio_name_to_edge(const char *name)
+cnxk_gpio_flag_to_edge(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) {
-		if (!strcmp(cnxk_gpio_edge_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_edge_flag); i++) {
+		if ((cnxk_gpio_edge_flag[i].flag & flag) == cnxk_gpio_edge_flag[i].flag)
 			break;
 	}

-	return cnxk_gpio_edge_name[i].edge;
+	return cnxk_gpio_edge_flag[i].edge;
 }

 static const struct {
 	enum cnxk_gpio_pin_dir dir;
-	const char *name;
-} cnxk_gpio_dir_name[] = {
-	{ CNXK_GPIO_PIN_DIR_IN, "in" },
-	{ CNXK_GPIO_PIN_DIR_OUT, "out" },
-	{ CNXK_GPIO_PIN_DIR_HIGH, "high" },
-	{ CNXK_GPIO_PIN_DIR_LOW, "low" },
+	enum gpio_v2_line_flag flag;
+} cnxk_gpio_dir_flag[] = {
+	{ CNXK_GPIO_PIN_DIR_IN, GPIO_V2_LINE_FLAG_INPUT },
+	{ CNXK_GPIO_PIN_DIR_OUT, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_HIGH, GPIO_V2_LINE_FLAG_OUTPUT },
+	{ CNXK_GPIO_PIN_DIR_LOW, GPIO_V2_LINE_FLAG_OUTPUT },
 };

-static const char *
-cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir)
+static enum gpio_v2_line_flag
+cnxk_gpio_dir_to_flag(enum cnxk_gpio_pin_dir dir)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (cnxk_gpio_dir_name[i].dir == dir)
-			return cnxk_gpio_dir_name[i].name;
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if (cnxk_gpio_dir_flag[i].dir == dir)
+			break;
 	}

-	return NULL;
+	return cnxk_gpio_dir_flag[i].flag;
 }

 static enum cnxk_gpio_pin_dir
-cnxk_gpio_name_to_dir(const char *name)
+cnxk_gpio_flag_to_dir(enum gpio_v2_line_flag flag)
 {
 	unsigned int i;

-	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) {
-		if (!strcmp(cnxk_gpio_dir_name[i].name, name))
+	for (i = 0; i < RTE_DIM(cnxk_gpio_dir_flag); i++) {
+		if ((cnxk_gpio_dir_flag[i].flag & flag) == cnxk_gpio_dir_flag[i].flag)
 			break;
 	}

-	return cnxk_gpio_dir_name[i].dir;
+	return cnxk_gpio_dir_flag[i].dir;
 }

 static int
-cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
+cnxk_gpio_register_irq_compat(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq,
+			      struct cnxk_gpio_irq2 *irq2)
 {
+	struct rte_intr_handle *intr_handle;
 	int ret;

-	ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu);
-	if (ret)
-		return ret;
+	if (!irq && !irq2)
+		return -EINVAL;
+
+	if ((irq && !irq->handler) || (irq2 && !irq2->handler))
+		return -EINVAL;
+
+	if (gpio->intr.intr_handle)
+		return -EEXIST;
+
+	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+	if (!intr_handle)
+		return -ENOMEM;
+
+	if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_VDEV)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_fd_set(intr_handle, gpio->fd)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (rte_intr_callback_register(intr_handle, cnxk_gpio_intr_handler, gpio)) {
+		ret = -rte_errno;
+		goto out;
+	}
+
+	gpio->intr.intr_handle = intr_handle;

-	gpio->handler = irq->handler;
-	gpio->data = irq->data;
-	gpio->cpu = irq->cpu;
+	if (irq) {
+		gpio->intr.data = irq->data;
+		gpio->intr.handler = irq->handler;
+	} else {
+		gpio->intr.data = irq2->data;
+		gpio->intr.handler2 = irq2->handler;
+	}
+
+	if (rte_intr_enable(gpio->intr.intr_handle)) {
+		ret = -EINVAL;
+		goto out;
+	}

 	return 0;
+out:
+	rte_intr_instance_free(intr_handle);
+
+	return ret;
 }

 static int
-cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
+cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq)
 {
-	return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base);
+	CNXK_GPIO_LOG(WARNING, "using deprecated interrupt registration api");
+
+	return cnxk_gpio_register_irq_compat(gpio, irq, NULL);
+}
+
+static int
+cnxk_gpio_register_irq2(struct cnxk_gpio *gpio, struct cnxk_gpio_irq2 *irq)
+{
+	return cnxk_gpio_register_irq_compat(gpio, NULL, irq);
 }

 static int
 cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 {
 	struct cnxk_gpio_msg *msg = rbuf->buf_addr;
+	struct gpio_v2_line_values values = {0};
+	struct gpio_v2_line_config config = {0};
+	struct gpio_v2_line_info info = {0};
 	enum cnxk_gpio_pin_edge edge;
 	enum cnxk_gpio_pin_dir dir;
-	char buf[CNXK_GPIO_BUFSZ];
 	void *rsp = NULL;
-	int ret, val, n;
+	int ret;

-	n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH,
-		     gpio->num);
+	info.offset = gpio->num;
+	ret = cnxk_gpio_gpiochip_ioctl(gpio->gpiochip, GPIO_V2_GET_LINEINFO_IOCTL, &info);
+	if (ret)
+		return ret;
+
+	info.flags &= ~GPIO_V2_LINE_FLAG_USED;

 	switch (msg->type) {
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data);
+		values.bits = *(int *)msg->data ?  RTE_BIT64(gpio->num) : 0;
+		values.mask = RTE_BIT64(gpio->num);
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
 		edge = *(enum cnxk_gpio_pin_edge *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge));
+		info.flags &= ~(GPIO_V2_LINE_FLAG_EDGE_RISING | GPIO_V2_LINE_FLAG_EDGE_FALLING);
+		info.flags |= cnxk_gpio_edge_to_flag(edge);
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
 		dir = *(enum cnxk_gpio_pin_dir *)msg->data;
-		ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir));
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = cnxk_gpio_dir_to_flag(dir);
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		if (dir == CNXK_GPIO_PIN_DIR_HIGH || dir == CNXK_GPIO_PIN_DIR_LOW) {
+			config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
+			config.attrs[config.num_attrs].attr.values = dir == CNXK_GPIO_PIN_DIR_HIGH ?
+								     RTE_BIT64(gpio->num) : 0;
+			config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+			config.num_attrs++;
+		}
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		val = *(int *)msg->data;
-		ret = cnxk_gpio_write_attr_int(buf, val);
+		if (*(int *)msg->data)
+			info.flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+		else
+			info.flags &= ~GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+
+		config.attrs[config.num_attrs].attr.id = GPIO_V2_LINE_ATTR_ID_FLAGS;
+		config.attrs[config.num_attrs].attr.flags = info.flags;
+		config.attrs[config.num_attrs].mask = RTE_BIT64(gpio->num);
+		config.num_attrs++;
+
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_SET_CONFIG_IOCTL, &config);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE:
-		snprintf(buf + n, sizeof(buf) - n, "/value");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
+		values.mask = RTE_BIT64(gpio->num);
+		ret = cnxk_gpio_ioctl(gpio, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
 		if (ret)
 			break;

@@ -597,47 +724,35 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 		if (!rsp)
 			return -ENOMEM;

-		*(int *)rsp = val;
+		*(int *)rsp = !!(values.bits & RTE_BIT64(gpio->num));
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE:
-		snprintf(buf + n, sizeof(buf) - n, "/edge");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf);
+		*(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_flag_to_edge(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR:
-		snprintf(buf + n, sizeof(buf) - n, "/direction");
-		ret = cnxk_gpio_read_attr(buf, buf);
-		if (ret)
-			break;
-
-		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0);
+		rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf);
+		*(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_flag_to_dir(info.flags);
 		break;
 	case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW:
-		snprintf(buf + n, sizeof(buf) - n, "/active_low");
-		ret = cnxk_gpio_read_attr_int(buf, &val);
-		if (ret)
-			break;
-
 		rsp = rte_zmalloc(NULL, sizeof(int), 0);
 		if (!rsp)
 			return -ENOMEM;

-		*(int *)rsp = val;
+		*(int *)rsp = !!(info.flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
 		break;
 	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ:
 		ret = cnxk_gpio_register_irq(gpio, (struct cnxk_gpio_irq *)msg->data);
 		break;
+	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2:
+		ret = cnxk_gpio_register_irq2(gpio, (struct cnxk_gpio_irq2 *)msg->data);
+		break;
 	case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ:
 		ret = cnxk_gpio_unregister_irq(gpio);
 		break;
@@ -731,11 +846,11 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = {
 static int
 cnxk_gpio_probe(struct rte_vdev_device *dev)
 {
+	struct gpiochip_info gpiochip_info;
 	char name[RTE_RAWDEV_NAME_MAX_LEN];
 	struct cnxk_gpio_params *params;
 	struct cnxk_gpiochip *gpiochip;
 	struct rte_rawdev *rawdev;
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

 	cnxk_gpio_format_name(name, sizeof(name));
@@ -762,25 +877,14 @@ cnxk_gpio_probe(struct rte_vdev_device *dev)

 	gpiochip->num = params->num;

-	ret = cnxk_gpio_irq_init(gpiochip);
-	if (ret)
-		goto out;
-
-	/* read gpio base */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base);
-	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
-		goto out;
-	}
-
 	/* read number of available gpios */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, gpiochip->num);
-	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios);
+	ret = cnxk_gpio_gpiochip_ioctl(gpiochip, GPIO_GET_CHIPINFO_IOCTL, &gpiochip_info);
 	if (ret) {
-		CNXK_GPIO_LOG(ERR, "failed to read %s", buf);
+		CNXK_GPIO_LOG(ERR, "failed to read /dev/gpiochip%d info", gpiochip->num);
 		goto out;
 	}
+
+	gpiochip->num_gpios = gpiochip_info.lines;
 	gpiochip->num_queues = gpiochip->num_gpios;

 	ret = cnxk_gpio_parse_allowlist(gpiochip, params->allowlist);
@@ -827,15 +931,11 @@ cnxk_gpio_remove(struct rte_vdev_device *dev)
 		if (!gpio)
 			continue;

-		if (gpio->handler)
-			cnxk_gpio_unregister_irq(gpio);
-
 		cnxk_gpio_queue_release(rawdev, gpio->num);
 	}

 	rte_free(gpiochip->allowlist);
 	rte_free(gpiochip->gpios);
-	cnxk_gpio_irq_fini();
 	cnxk_gpio_params_release();
 	rte_rawdev_pmd_release(rawdev);

@@ -851,4 +951,5 @@ RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv);
 RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio,
 		"gpiochip=<int> "
 		"allowlist=<list>");
-RTE_LOG_REGISTER_SUFFIX(cnxk_logtype_gpio, gpio, INFO);
+
+#endif /* CNXK_GPIO_V2_PRESENT */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h
index 94c8e36977..adc7f90936 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h
@@ -11,20 +11,24 @@ extern int cnxk_logtype_gpio;
 #define CNXK_GPIO_LOG(level, ...) \
 	RTE_LOG_LINE(level, CNXK_GPIO, __VA_ARGS__)

+struct gpio_v2_line_event;
 struct cnxk_gpiochip;

 struct cnxk_gpio {
 	struct cnxk_gpiochip *gpiochip;
+	struct {
+		struct rte_intr_handle *intr_handle;
+		void (*handler)(int gpio, void *data);
+		void (*handler2)(struct gpio_v2_line_event *event, void *data);
+		void *data;
+	} intr;
 	void *rsp;
 	int num;
-	void (*handler)(int gpio, void *data);
-	void *data;
-	int cpu;
+	int fd;
 };

 struct cnxk_gpiochip {
 	int num;
-	int base;
 	int num_gpios;
 	int num_queues;
 	struct cnxk_gpio **gpios;
@@ -33,9 +37,4 @@ struct cnxk_gpiochip {

 int cnxk_gpio_selftest(uint16_t dev_id);

-int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip);
-void cnxk_gpio_irq_fini(void);
-int cnxk_gpio_irq_request(int gpio, int cpu);
-int cnxk_gpio_irq_free(int gpio);
-
 #endif /* _CNXK_GPIO_H_ */
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
deleted file mode 100644
index 2fa8e69899..0000000000
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(C) 2021 Marvell.
- */
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/queue.h>
-#include <unistd.h>
-
-#include <rte_rawdev_pmd.h>
-
-#include <roc_api.h>
-
-#include "cnxk_gpio.h"
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_SET_GPIO_HANDLER                                               \
-	_IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data)
-#define OTX_IOC_CLR_GPIO_HANDLER                                               \
-	_IO(OTX_IOC_MAGIC, 2)
-
-struct otx_gpio_usr_data {
-	uint64_t isr_base;
-	uint64_t sp;
-	uint64_t cpu;
-	uint64_t gpio_num;
-};
-
-struct cnxk_gpio_irq_stack {
-	LIST_ENTRY(cnxk_gpio_irq_stack) next;
-	void *sp_buffer;
-	int cpu;
-	int inuse;
-};
-
-struct cnxk_gpio_irqchip {
-	int fd;
-	/* serialize access to this struct */
-	pthread_mutex_t lock;
-	LIST_HEAD(, cnxk_gpio_irq_stack) stacks;
-
-	struct cnxk_gpiochip *gpiochip;
-};
-
-static struct cnxk_gpio_irqchip *irqchip;
-
-static void
-cnxk_gpio_irq_stack_free(int cpu)
-{
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (!stack)
-		return;
-
-	if (stack->inuse)
-		stack->inuse--;
-
-	if (stack->inuse == 0) {
-		LIST_REMOVE(stack, next);
-		rte_free(stack->sp_buffer);
-		rte_free(stack);
-	}
-}
-
-static void *
-cnxk_gpio_irq_stack_alloc(int cpu)
-{
-#define ARM_STACK_ALIGNMENT (2 * sizeof(void *))
-#define IRQ_STACK_SIZE 0x200000
-
-	struct cnxk_gpio_irq_stack *stack;
-
-	LIST_FOREACH(stack, &irqchip->stacks, next) {
-		if (stack->cpu == cpu)
-			break;
-	}
-
-	if (stack) {
-		stack->inuse++;
-		return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-	}
-
-	stack = rte_malloc(NULL, sizeof(*stack), 0);
-	if (!stack)
-		return NULL;
-
-	stack->sp_buffer =
-		rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT);
-	if (!stack->sp_buffer) {
-		rte_free(stack);
-		return NULL;
-	}
-
-	stack->cpu = cpu;
-	stack->inuse = 1;
-	LIST_INSERT_HEAD(&irqchip->stacks, stack, next);
-
-	return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
-}
-
-static void
-cnxk_gpio_irq_handler(int gpio_num)
-{
-	struct cnxk_gpiochip *gpiochip = irqchip->gpiochip;
-	struct cnxk_gpio *gpio;
-
-	if (gpio_num >= gpiochip->num_gpios)
-		goto out;
-
-	gpio = gpiochip->gpios[gpio_num];
-	if (likely(gpio->handler))
-		gpio->handler(gpio_num, gpio->data);
-
-out:
-	roc_atf_ret();
-}
-
-int
-cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip)
-{
-	if (irqchip)
-		return 0;
-
-	irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0);
-	if (!irqchip)
-		return -ENOMEM;
-
-	irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (irqchip->fd < 0) {
-		rte_free(irqchip);
-		return -errno;
-	}
-
-	pthread_mutex_init(&irqchip->lock, NULL);
-	LIST_INIT(&irqchip->stacks);
-	irqchip->gpiochip = gpiochip;
-
-	return 0;
-}
-
-void
-cnxk_gpio_irq_fini(void)
-{
-	if (!irqchip)
-		return;
-
-	close(irqchip->fd);
-	rte_free(irqchip);
-	irqchip = NULL;
-}
-
-int
-cnxk_gpio_irq_request(int gpio, int cpu)
-{
-	struct otx_gpio_usr_data data;
-	void *sp;
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	sp = cnxk_gpio_irq_stack_alloc(cpu);
-	if (!sp) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
-
-	data.isr_base = (uint64_t)cnxk_gpio_irq_handler;
-	data.sp = (uint64_t)sp;
-	data.cpu = (uint64_t)cpu;
-	data.gpio_num = (uint64_t)gpio;
-
-	mlockall(MCL_CURRENT | MCL_FUTURE);
-	ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data);
-	if (ret) {
-		ret = -errno;
-		goto out_free_stack;
-	}
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-
-out_free_stack:
-	cnxk_gpio_irq_stack_free(cpu);
-out_unlock:
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return ret;
-}
-
-int
-cnxk_gpio_irq_free(int gpio)
-{
-	int ret;
-
-	pthread_mutex_lock(&irqchip->lock);
-
-	ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio);
-	if (ret) {
-		pthread_mutex_unlock(&irqchip->lock);
-		return -errno;
-	}
-
-	cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu);
-
-	pthread_mutex_unlock(&irqchip->lock);
-
-	return 0;
-}
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
index a0d9942f20..778740985a 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
@@ -3,102 +3,33 @@
  */

 #include <fcntl.h>
+#include <linux/gpio.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>

 #include <rte_cycles.h>
+#include <rte_io.h>
 #include <rte_rawdev.h>
 #include <rte_rawdev_pmd.h>
-#include <rte_service.h>

 #include "cnxk_gpio.h"
 #include "rte_pmd_cnxk_gpio.h"

-#define CNXK_GPIO_BUFSZ 128
-
-#define OTX_IOC_MAGIC 0xF2
-#define OTX_IOC_TRIGGER_GPIO_HANDLER                                           \
-	_IO(OTX_IOC_MAGIC, 3)
-
-static int fd;
-
-static int
-cnxk_gpio_attr_exists(const char *attr)
-{
-	struct stat st;
-
-	return !stat(attr, &st);
-}
-
-static int
-cnxk_gpio_read_attr(char *attr, char *val)
-{
-	int ret, ret2;
-	FILE *fp;
-
-	fp = fopen(attr, "r");
-	if (!fp)
-		return -errno;
-
-	ret = fscanf(fp, "%s", val);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-	if (ret != 1) {
-		ret = -EIO;
-		goto out;
-	}
-
-	ret = 0;
-out:
-	ret2 = fclose(fp);
-	if (!ret)
-		ret = ret2;
-
-	return ret;
-}
-
-#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                  \
-	if (err) {                                                             \
-		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, \
-			##__VA_ARGS__, err);                                   \
-		goto out;                                                      \
-	}                                                                      \
+#define CNXK_GPIO_ERR_STR(err, str, ...) do {                                                      \
+	if (err) {                                                                                 \
+		CNXK_GPIO_LOG(ERR, "%s:%d: " str " (%d)", __func__, __LINE__, ##__VA_ARGS__, err); \
+		goto out;                                                                          \
+	}                                                                                          \
 } while (0)

 static int
-cnxk_gpio_validate_attr(char *attr, const char *expected)
+cnxk_gpio_test_input(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

-	ret = cnxk_gpio_read_attr(attr, buf);
-	if (ret)
-		return ret;
-
-	if (strncmp(buf, expected, sizeof(buf)))
-		return -EIO;
-
-	return 0;
-}
-
-#define CNXK_GPIO_PATH_FMT "/sys/class/gpio/gpio%d"
-
-static int
-cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
-{
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, n;
-
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
-	snprintf(buf + n, sizeof(buf) - n, "/direction");
-
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
-	ret = cnxk_gpio_validate_attr(buf, "in");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1) |
 	      rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
@@ -107,29 +38,17 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 		CNXK_GPIO_ERR_STR(ret, "input pin overwritten");
 	}

-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "falling");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "rising");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "both");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to set edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	/*
 	 * calling this makes sure kernel driver switches off inverted
@@ -141,44 +60,41 @@ cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
 	return ret;
 }

-static int
-cnxk_gpio_trigger_irq(int gpio)
+static uint32_t triggered;
+
+static void
+cnxk_gpio_irq_handler(struct gpio_v2_line_event *event, void *data)
 {
-	int ret;
+	RTE_SET_USED(event);
+	RTE_SET_USED(data);

-	ret = ioctl(fd, OTX_IOC_TRIGGER_GPIO_HANDLER, gpio);
+#ifdef CNXK_GPIO_V2_PRESENT
+	int gpio = (int)(size_t)data;

-	return ret == -1 ? -errno : 0;
-}
+	if ((int)event->offset != gpio)
+		CNXK_GPIO_LOG(ERR, "event from gpio%d instead of gpio%d", event->offset, gpio);
+#endif

-static void
-cnxk_gpio_irq_handler(int gpio, void *data)
-{
-	*(int *)data = gpio;
+	rte_write32(1, &triggered);
 }

 static int
 cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 {
-	int irq_data, ret;
+	int ret;

 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");

-	irq_data = 0;
-	ret = rte_pmd_gpio_register_irq(dev_id, gpio, rte_lcore_id(),
-					cnxk_gpio_irq_handler, &irq_data);
+	ret = rte_pmd_gpio_register_irq2(dev_id, gpio, cnxk_gpio_irq_handler, (int *)(size_t)gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to register irq handler");

-	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio,
-					    CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	CNXK_GPIO_ERR_STR(ret, "failed to enable interrupt");

-	ret = cnxk_gpio_trigger_irq(gpio);
-	CNXK_GPIO_ERR_STR(ret, "failed to trigger irq");
-	rte_delay_ms(1);
-	ret = *(volatile int *)&irq_data == gpio ? 0 : -EIO;
-	CNXK_GPIO_ERR_STR(ret, "failed to test irq");
+	rte_delay_ms(2);
+	rte_read32(&triggered);
+	CNXK_GPIO_ERR_STR(!triggered, "failed to trigger irq");

 	ret = rte_pmd_gpio_disable_interrupt(dev_id, gpio);
 	CNXK_GPIO_ERR_STR(ret, "failed to disable interrupt");
@@ -193,24 +109,15 @@ cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
 }

 static int
-cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
+cnxk_gpio_test_output(uint16_t dev_id, int gpio)
 {
-	char buf[CNXK_GPIO_BUFSZ];
-	int ret, val, n;
-
-	n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
+	int ret, val;

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_OUT);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to out");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val)
@@ -219,64 +126,41 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
 	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read value");
 	if (val != 1)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_LOW);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to low");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/direction");
 	ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_HIGH);
 	CNXK_GPIO_ERR_STR(ret, "failed to set dir to high");
-	ret = cnxk_gpio_validate_attr(buf, "out");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-	snprintf(buf + n, sizeof(buf) - n, "/value");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
-	snprintf(buf + n, sizeof(buf) - n, "/edge");
-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_FALLING);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
+
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_FALLING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to falling");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
-					CNXK_GPIO_PIN_EDGE_RISING);
+	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_RISING);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to rising");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
 	ret = ret == 0 ? -EIO : 0;
 	CNXK_GPIO_ERR_STR(ret, "changed edge to both");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	/* this one should succeed */
 	ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
 	CNXK_GPIO_ERR_STR(ret, "failed to change edge to none");
-	ret = cnxk_gpio_validate_attr(buf, "none");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);

 	ret = rte_pmd_gpio_get_pin_active_low(dev_id, gpio, &val);
 	CNXK_GPIO_ERR_STR(ret, "failed to read active_low");
@@ -284,23 +168,24 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
 		ret = -EIO;
 	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

-	snprintf(buf + n, sizeof(buf) - n, "/value");
 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
-	ret = cnxk_gpio_validate_attr(buf, "1");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 1)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);

 	ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
+	ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
+	CNXK_GPIO_ERR_STR(ret, "failed to read value");
+	if (val != 0)
+		ret = -EIO;
+	CNXK_GPIO_ERR_STR(ret, "read %d instead of 0", val);

-	snprintf(buf + n, sizeof(buf) - n, "/active_low");
 	ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 0);
 	CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 0");
-	ret = cnxk_gpio_validate_attr(buf, "0");
-	CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
-
 out:
 	return ret;
 }
@@ -309,17 +194,13 @@ int
 cnxk_gpio_selftest(uint16_t dev_id)
 {
 	struct cnxk_gpio_queue_conf conf;
-	struct cnxk_gpiochip *gpiochip;
-	char buf[CNXK_GPIO_BUFSZ];
 	struct rte_rawdev *rawdev;
 	unsigned int queues, i;
-	struct cnxk_gpio *gpio;
 	int ret, ret2;

 	rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio");
 	if (!rawdev)
 		return -ENODEV;
-	gpiochip = rawdev->dev_private;

 	queues = rte_rawdev_queue_count(dev_id);
 	if (queues == 0)
@@ -329,10 +210,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	if (ret)
 		return ret;

-	fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
-	if (fd < 0)
-		return -errno;
-
 	for (i = 0; i < queues; i++) {
 		ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf));
 		if (ret) {
@@ -355,15 +232,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			goto out;
 		}

-		gpio = gpiochip->gpios[conf.gpio];
-		snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, gpio->num);
-		if (!cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s does not exist", buf);
-			ret = -ENOENT;
-			goto release;
-		}
-
-		ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_input(dev_id, conf.gpio);
 		if (ret)
 			goto release;

@@ -371,7 +240,7 @@ cnxk_gpio_selftest(uint16_t dev_id)
 		if (ret)
 			goto release;

-		ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio);
+		ret = cnxk_gpio_test_output(dev_id, conf.gpio);
 release:
 		ret2 = ret;
 		ret = rte_rawdev_queue_release(dev_id, i);
@@ -381,12 +250,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 			break;
 		}

-		if (cnxk_gpio_attr_exists(buf)) {
-			CNXK_GPIO_LOG(ERR, "%s still exists", buf);
-			ret = -EIO;
-			break;
-		}
-
 		if (ret2) {
 			ret = ret2;
 			break;
@@ -394,7 +257,6 @@ cnxk_gpio_selftest(uint16_t dev_id)
 	}

 out:
-	close(fd);
 	rte_rawdev_stop(dev_id);

 	return ret;
diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build
index 9d9a527392..372f3d9f46 100644
--- a/drivers/raw/cnxk_gpio/meson.build
+++ b/drivers/raw/cnxk_gpio/meson.build
@@ -5,7 +5,6 @@
 deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs']
 sources = files(
         'cnxk_gpio.c',
-        'cnxk_gpio_irq.c',
         'cnxk_gpio_selftest.c',
 )
 headers = files('rte_pmd_cnxk_gpio.h')
diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
index 80a37be9c7..1fbb542c26 100644
--- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
+++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h
@@ -5,6 +5,15 @@
 #ifndef _RTE_PMD_CNXK_GPIO_H_
 #define _RTE_PMD_CNXK_GPIO_H_

+#include <linux/gpio.h>
+#include <linux/version.h>
+
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#define CNXK_GPIO_V2_PRESENT
+#else
+struct gpio_v2_line_event {};
+#endif
+
 #include <rte_malloc.h>
 #include <rte_memcpy.h>
 #include <rte_rawdev.h>
@@ -48,8 +57,10 @@ enum cnxk_gpio_msg_type {
 	CNXK_GPIO_MSG_TYPE_GET_PIN_DIR,
 	/** Type used to read inverted logic state */
 	CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW,
-	/** Type used to register interrupt handler */
+	/** Type used to register interrupt handler (deprecated) */
 	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ,
+	/** Type used to register interrupt handler */
+	CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
 	/** Type used to remove interrupt handler */
 	CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ,
 };
@@ -79,7 +90,7 @@ enum cnxk_gpio_pin_dir {
 };

 /**
- * GPIO interrupt handler
+ * GPIO interrupt handler (deprecated)
  *
  * @param gpio
  *   Zero-based GPIO number
@@ -97,6 +108,23 @@ struct cnxk_gpio_irq {
 	int cpu;
 };

+/**
+ * GPIO interrupt handler
+ *
+ * @param event
+ *   Pointer to gpio event data
+ * @param data
+ *   Cookie passed to interrupt handler
+ */
+typedef void (*cnxk_gpio_irq_handler2_t)(struct gpio_v2_line_event *event, void *data);
+
+struct cnxk_gpio_irq2 {
+	/** Interrupt handler */
+	cnxk_gpio_irq_handler2_t handler;
+	/** User data passed to irq handler */
+	void *data;
+};
+
 struct cnxk_gpio_msg {
 	/** Message type */
 	enum cnxk_gpio_msg_type type;
@@ -338,7 +366,7 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val)
 }

 /**
- * Attach interrupt handler to GPIO
+ * Attach interrupt handler to GPIO (deprecated)
  *
  * @param dev_id
  *   The identifier of the device
@@ -371,6 +399,36 @@ rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu,
 	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
 }

+/**
+ * Attach interrupt handler to GPIO
+ *
+ * @param dev_id
+ *   The identifier of the device
+ * @param gpio
+ *   Zero-based GPIO number
+ * @param handler
+ *   Interrupt handler to be executed
+ * @param data
+ *   Data to be passed to interrupt handler
+ *
+ * @return
+ *   Returns 0 on success, negative error code otherwise
+ */
+static __rte_always_inline int
+rte_pmd_gpio_register_irq2(uint16_t dev_id, int gpio, cnxk_gpio_irq_handler2_t handler, void *data)
+{
+	struct cnxk_gpio_irq2 irq = {
+		.handler = handler,
+		.data = data,
+	};
+	struct cnxk_gpio_msg msg = {
+		.type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ2,
+		.data = &irq,
+	};
+
+	return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0);
+}
+
 /**
  * Detach interrupt handler from GPIO
  *
--
2.34.1


^ permalink raw reply	[relevance 1%]

* [PATCH v5 0/8] Symbol versioning and export rework
  2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
                   ` (2 preceding siblings ...)
  2025-03-17 15:42  3% ` [RFC v4 " David Marchand
@ 2025-03-27 13:36  3% ` David Marchand
  2025-03-27 13:36 17%   ` [PATCH v5 4/8] build: generate symbol maps David Marchand
                     ` (3 more replies)
  2025-03-28 10:52  3% ` [PATCH v6 " David Marchand
  2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
  5 siblings, 4 replies; 153+ results
From: David Marchand @ 2025-03-27 13:36 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

So far, each DPDK library (or driver) exposing symbols in an ABI had to
maintain a version.map and use some macros for symbol versioning,
specially crafted with the GNU linker in mind.

This series proposes to rework the whole principle, and instead rely on
marking the symbol exports in the source code itself, then let it to the
build framework to produce a version script adapted to the linker in use
(think GNU linker vs MSVC linker).

This greatly simplifies versioning symbols: a developer does not need to
know anything about version.map, or that a versioned symbol must be
renamed with _v26, annotated with __vsym, exported in a header etc...

Checking symbol maps becomes unnecessary since generated by the build
framework.

Updating to a new ABI is just a matter of bumping the value in
ABI_VERSION.



-- 
David Marchand

Changes since RFC v4:
- rebased on main, now that Bruce series is merged,
- the export macros header has been moved to lib/eal/common/
  and its inclusion is now mandatory (rather than an implicit -include),
- reordered patches: symbol versioning is touched last and merged
  in the export header (replacing the legacy rte_function_versioning.h),

Changes since RFC v3:
- fixed/simplified documentation,
- rebased on top of Bruce series for common handling of AVX sources,

Changes since RFC v2:
- updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
  signature is enclosed in the macro,
- dropped invalid exports for some dead symbols or inline helpers,
- updated documentation and tooling,
- converted the whole tree (via a local script of mine),

David Marchand (8):
  lib: remove incorrect exported symbols
  drivers: remove incorrect exported symbols
  buildtools: display version when listing symbols
  build: generate symbol maps
  build: mark exported symbols
  build: use dynamically generated version maps
  build: remove static version maps
  eal: rework function versioning macros

 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   9 +-
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/gen-version-map.py                 | 105 ++++
 buildtools/map-list-symbol.sh                 |  15 +-
 buildtools/map_to_win.py                      |  41 --
 buildtools/meson.build                        |   2 +-
 devtools/check-spdx-tag.sh                    |   2 +-
 devtools/check-symbol-change.py               |  90 +++
 devtools/check-symbol-change.sh               | 186 ------
 devtools/check-symbol-maps.sh                 | 115 ----
 devtools/checkpatches.sh                      |   4 +-
 devtools/update-abi.sh                        |  46 --
 devtools/update_version_map_abi.py            | 210 -------
 doc/api/doxy-api-index.md                     |   1 -
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/abi_versioning.rst    | 415 +++----------
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 +++++----
 doc/guides/contributing/patches.rst           |   6 +-
 doc/guides/rel_notes/release_25_07.rst        |   2 +
 drivers/baseband/acc/rte_acc100_pmd.c         |   2 +
 drivers/baseband/acc/version.map              |  10 -
 .../fpga_5gnr_fec/rte_fpga_5gnr_fec.c         |   2 +
 drivers/baseband/fpga_5gnr_fec/version.map    |  11 -
 drivers/baseband/fpga_lte_fec/fpga_lte_fec.c  |   2 +
 drivers/baseband/fpga_lte_fec/version.map     |  10 -
 drivers/bus/auxiliary/auxiliary_common.c      |   3 +
 drivers/bus/auxiliary/version.map             |   8 -
 drivers/bus/cdx/cdx.c                         |   5 +
 drivers/bus/cdx/cdx_vfio.c                    |   5 +
 drivers/bus/cdx/version.map                   |  14 -
 drivers/bus/dpaa/dpaa_bus.c                   |  10 +
 drivers/bus/dpaa/dpaa_bus_symbols.c           |  99 +++
 drivers/bus/dpaa/meson.build                  |   1 +
 drivers/bus/dpaa/version.map                  | 109 ----
 drivers/bus/fslmc/fslmc_bus.c                 |   5 +
 drivers/bus/fslmc/fslmc_vfio.c                |  13 +
 drivers/bus/fslmc/mc/dpbp.c                   |   8 +
 drivers/bus/fslmc/mc/dpci.c                   |   5 +
 drivers/bus/fslmc/mc/dpcon.c                  |   8 +
 drivers/bus/fslmc/mc/dpdmai.c                 |  10 +
 drivers/bus/fslmc/mc/dpio.c                   |  15 +
 drivers/bus/fslmc/mc/dpmng.c                  |   4 +
 drivers/bus/fslmc/mc/mc_sys.c                 |   2 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpbp.c      |   4 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpci.c      |   3 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpio.c      |  12 +
 drivers/bus/fslmc/qbman/qbman_debug.c         |   4 +
 drivers/bus/fslmc/qbman/qbman_portal.c        |  43 ++
 drivers/bus/fslmc/version.map                 | 129 ----
 drivers/bus/ifpga/ifpga_bus.c                 |   4 +
 drivers/bus/ifpga/version.map                 |   9 -
 drivers/bus/pci/bsd/pci.c                     |  11 +
 drivers/bus/pci/linux/pci.c                   |  11 +
 drivers/bus/pci/pci_common.c                  |  11 +
 drivers/bus/pci/version.map                   |  43 --
 drivers/bus/pci/windows/pci.c                 |  11 +
 drivers/bus/platform/platform.c               |   3 +
 drivers/bus/platform/version.map              |  10 -
 drivers/bus/uacce/uacce.c                     |  10 +
 drivers/bus/uacce/version.map                 |  15 -
 drivers/bus/vdev/vdev.c                       |   7 +
 drivers/bus/vdev/version.map                  |  17 -
 drivers/bus/vmbus/linux/vmbus_bus.c           |   7 +
 drivers/bus/vmbus/version.map                 |  33 -
 drivers/bus/vmbus/vmbus_channel.c             |  14 +
 drivers/bus/vmbus/vmbus_common.c              |   4 +
 drivers/common/cnxk/cnxk_security.c           |  13 +
 drivers/common/cnxk/cnxk_utils.c              |   2 +
 drivers/common/cnxk/meson.build               |   1 +
 drivers/common/cnxk/roc_platform.c            |  19 +
 drivers/common/cnxk/roc_platform_symbols.c    | 545 +++++++++++++++++
 drivers/common/cnxk/roc_se.h                  |   1 -
 drivers/common/cnxk/version.map               | 578 ------------------
 drivers/common/cpt/cpt_fpm_tables.c           |   3 +
 drivers/common/cpt/cpt_pmd_ops_helper.c       |   4 +
 drivers/common/cpt/version.map                |  11 -
 drivers/common/dpaax/caamflib.c               |   3 +
 drivers/common/dpaax/dpaa_of.c                |  13 +
 drivers/common/dpaax/dpaax_iova_table.c       |   7 +
 drivers/common/dpaax/version.map              |  25 -
 drivers/common/ionic/ionic_common_uio.c       |   5 +
 drivers/common/ionic/version.map              |  10 -
 .../common/mlx5/linux/mlx5_common_auxiliary.c |   2 +
 drivers/common/mlx5/linux/mlx5_common_os.c    |  11 +
 drivers/common/mlx5/linux/mlx5_common_verbs.c |   4 +
 drivers/common/mlx5/linux/mlx5_glue.c         |   3 +
 drivers/common/mlx5/linux/mlx5_nl.c           |  22 +
 drivers/common/mlx5/mlx5_common.c             |  10 +
 drivers/common/mlx5/mlx5_common_devx.c        |  10 +
 drivers/common/mlx5/mlx5_common_mp.c          |   9 +
 drivers/common/mlx5/mlx5_common_mr.c          |  12 +
 drivers/common/mlx5/mlx5_common_pci.c         |   3 +
 drivers/common/mlx5/mlx5_common_utils.c       |  12 +
 drivers/common/mlx5/mlx5_devx_cmds.c          |  52 ++
 drivers/common/mlx5/mlx5_malloc.c             |   5 +
 drivers/common/mlx5/version.map               | 175 ------
 drivers/common/mlx5/windows/mlx5_common_os.c  |   6 +
 drivers/common/mlx5/windows/mlx5_glue.c       |   4 +-
 drivers/common/mvep/mvep_common.c             |   3 +
 drivers/common/mvep/version.map               |   8 -
 drivers/common/nfp/nfp_common.c               |   9 +
 drivers/common/nfp/nfp_common_pci.c           |   2 +
 drivers/common/nfp/nfp_dev.c                  |   2 +
 drivers/common/nfp/version.map                |  16 -
 drivers/common/nitrox/nitrox_device.c         |   2 +
 drivers/common/nitrox/nitrox_logs.c           |   2 +
 drivers/common/nitrox/nitrox_qp.c             |   3 +
 drivers/common/nitrox/version.map             |  10 -
 drivers/common/octeontx/octeontx_mbox.c       |   7 +
 drivers/common/octeontx/version.map           |  12 -
 drivers/common/sfc_efx/meson.build            |   1 +
 drivers/common/sfc_efx/sfc_efx.c              |   3 +
 drivers/common/sfc_efx/sfc_efx_mcdi.c         |   3 +
 drivers/common/sfc_efx/sfc_symbols.c          | 275 +++++++++
 drivers/common/sfc_efx/version.map            | 302 ---------
 drivers/crypto/cnxk/cn10k_cryptodev_ops.c     |   8 +
 drivers/crypto/cnxk/cn9k_cryptodev_ops.c      |   3 +
 drivers/crypto/cnxk/cnxk_cryptodev_ops.c      |   8 +
 drivers/crypto/cnxk/version.map               |  30 -
 drivers/crypto/dpaa2_sec/dpaa2_sec_dpseci.c   |   3 +
 drivers/crypto/dpaa2_sec/version.map          |   8 -
 drivers/crypto/dpaa_sec/dpaa_sec.c            |   3 +
 drivers/crypto/dpaa_sec/version.map           |   8 -
 drivers/crypto/octeontx/otx_cryptodev_ops.c   |   3 +
 drivers/crypto/octeontx/version.map           |  12 -
 .../scheduler/rte_cryptodev_scheduler.c       |  11 +
 drivers/crypto/scheduler/version.map          |  16 -
 drivers/dma/cnxk/cnxk_dmadev_fp.c             |   5 +
 drivers/dma/cnxk/version.map                  |  10 -
 drivers/event/cnxk/cnxk_worker.c              |   3 +
 drivers/event/cnxk/version.map                |  11 -
 drivers/event/dlb2/rte_pmd_dlb2.c             |   2 +
 drivers/event/dlb2/version.map                |  10 -
 drivers/mempool/cnxk/cn10k_hwpool_ops.c       |   4 +
 drivers/mempool/cnxk/version.map              |  12 -
 drivers/mempool/dpaa/dpaa_mempool.c           |   3 +
 drivers/mempool/dpaa/version.map              |   8 -
 drivers/mempool/dpaa2/dpaa2_hw_mempool.c      |   6 +
 drivers/mempool/dpaa2/version.map             |  16 -
 drivers/meson.build                           |  74 +--
 drivers/net/atlantic/rte_pmd_atlantic.c       |   7 +
 drivers/net/atlantic/version.map              |  15 -
 drivers/net/bnxt/rte_pmd_bnxt.c               |  17 +
 drivers/net/bnxt/version.map                  |  22 -
 drivers/net/bonding/rte_eth_bond_8023ad.c     |  13 +
 drivers/net/bonding/rte_eth_bond_api.c        |  16 +
 drivers/net/bonding/version.map               |  33 -
 drivers/net/cnxk/cnxk_ethdev.c                |   4 +
 drivers/net/cnxk/cnxk_ethdev_sec.c            |  10 +
 drivers/net/cnxk/version.map                  |  27 -
 drivers/net/dpaa/dpaa_ethdev.c                |   4 +
 drivers/net/dpaa/version.map                  |  14 -
 drivers/net/dpaa2/dpaa2_ethdev.c              |   8 +
 drivers/net/dpaa2/dpaa2_mux.c                 |   4 +
 drivers/net/dpaa2/dpaa2_rxtx.c                |   2 +
 drivers/net/dpaa2/dpaa2_symbols.c             |   8 +
 drivers/net/dpaa2/meson.build                 |   1 +
 drivers/net/dpaa2/version.map                 |  35 --
 drivers/net/intel/i40e/rte_pmd_i40e.c         |  40 ++
 drivers/net/intel/i40e/version.map            |  55 --
 drivers/net/intel/iavf/iavf_rxtx.c            |   9 +
 drivers/net/intel/iavf/iavf_symbols.c         |  13 +
 drivers/net/intel/iavf/meson.build            |   1 +
 drivers/net/intel/iavf/version.map            |  33 -
 drivers/net/intel/ice/ice_diagnose.c          |   4 +
 drivers/net/intel/ice/version.map             |  16 -
 drivers/net/intel/idpf/idpf_common_device.c   |  11 +
 drivers/net/intel/idpf/idpf_common_rxtx.c     |  25 +
 .../net/intel/idpf/idpf_common_rxtx_avx2.c    |   3 +
 .../net/intel/idpf/idpf_common_rxtx_avx512.c  |   6 +
 drivers/net/intel/idpf/idpf_common_virtchnl.c |  31 +
 drivers/net/intel/idpf/version.map            |  80 ---
 drivers/net/intel/ipn3ke/ipn3ke_ethdev.c      |   2 +
 drivers/net/intel/ipn3ke/version.map          |   9 -
 drivers/net/intel/ixgbe/rte_pmd_ixgbe.c       |  38 ++
 drivers/net/intel/ixgbe/version.map           |  49 --
 drivers/net/mlx5/mlx5.c                       |   2 +
 drivers/net/mlx5/mlx5_flow.c                  |   5 +
 drivers/net/mlx5/mlx5_rx.c                    |   3 +
 drivers/net/mlx5/mlx5_rxq.c                   |   3 +
 drivers/net/mlx5/mlx5_tx.c                    |   2 +
 drivers/net/mlx5/mlx5_txq.c                   |   4 +
 drivers/net/mlx5/version.map                  |  28 -
 drivers/net/octeontx/octeontx_ethdev.c        |   2 +
 drivers/net/octeontx/version.map              |   7 -
 drivers/net/ring/rte_eth_ring.c               |   3 +
 drivers/net/ring/version.map                  |   8 -
 drivers/net/softnic/rte_eth_softnic.c         |   2 +
 drivers/net/softnic/rte_eth_softnic_thread.c  |   2 +
 drivers/net/softnic/version.map               |   8 -
 drivers/net/vhost/rte_eth_vhost.c             |   3 +
 drivers/net/vhost/version.map                 |   8 -
 drivers/power/kvm_vm/guest_channel.c          |   3 +
 drivers/power/kvm_vm/version.map              |   8 -
 drivers/raw/cnxk_rvu_lf/cnxk_rvu_lf.c         |  11 +
 drivers/raw/cnxk_rvu_lf/version.map           |  16 -
 drivers/raw/ifpga/rte_pmd_ifpga.c             |  12 +
 drivers/raw/ifpga/version.map                 |  17 -
 drivers/version.map                           |   3 -
 lib/acl/acl_bld.c                             |   2 +
 lib/acl/acl_run_scalar.c                      |   3 +
 lib/acl/rte_acl.c                             |  12 +
 lib/acl/version.map                           |  19 -
 lib/argparse/rte_argparse.c                   |   3 +
 lib/argparse/version.map                      |   9 -
 lib/bbdev/bbdev_trace_points.c                |   3 +
 lib/bbdev/rte_bbdev.c                         |  32 +
 lib/bbdev/version.map                         |  47 --
 lib/bitratestats/rte_bitrate.c                |   5 +
 lib/bitratestats/version.map                  |  10 -
 lib/bpf/bpf.c                                 |   3 +
 lib/bpf/bpf_convert.c                         |   2 +
 lib/bpf/bpf_dump.c                            |   2 +
 lib/bpf/bpf_exec.c                            |   3 +
 lib/bpf/bpf_load.c                            |   2 +
 lib/bpf/bpf_load_elf.c                        |   2 +
 lib/bpf/bpf_pkt.c                             |   5 +
 lib/bpf/bpf_stub.c                            |   3 +
 lib/bpf/version.map                           |  18 -
 lib/cfgfile/rte_cfgfile.c                     |  18 +
 lib/cfgfile/version.map                       |  23 -
 lib/cmdline/cmdline.c                         |  10 +
 lib/cmdline/cmdline_cirbuf.c                  |  21 +
 lib/cmdline/cmdline_parse.c                   |   5 +
 lib/cmdline/cmdline_parse_bool.c              |   2 +
 lib/cmdline/cmdline_parse_etheraddr.c         |   4 +
 lib/cmdline/cmdline_parse_ipaddr.c            |   4 +
 lib/cmdline/cmdline_parse_num.c               |   4 +
 lib/cmdline/cmdline_parse_portlist.c          |   4 +
 lib/cmdline/cmdline_parse_string.c            |   6 +
 lib/cmdline/cmdline_rdline.c                  |  17 +
 lib/cmdline/cmdline_socket.c                  |   5 +
 lib/cmdline/cmdline_vt100.c                   |   4 +
 lib/cmdline/version.map                       |  82 ---
 lib/compressdev/rte_comp.c                    |   7 +
 lib/compressdev/rte_compressdev.c             |  26 +
 lib/compressdev/rte_compressdev_pmd.c         |   4 +
 lib/compressdev/version.map                   |  40 --
 lib/cryptodev/cryptodev_pmd.c                 |   8 +
 lib/cryptodev/cryptodev_trace_points.c        |   4 +
 lib/cryptodev/rte_cryptodev.c                 |  84 +++
 lib/cryptodev/version.map                     | 114 ----
 lib/dispatcher/rte_dispatcher.c               |  14 +
 lib/dispatcher/version.map                    |  20 -
 lib/distributor/rte_distributor.c             |  10 +
 lib/distributor/version.map                   |  15 -
 lib/dmadev/rte_dmadev.c                       |  20 +
 lib/dmadev/rte_dmadev_trace_points.c          |   8 +
 lib/dmadev/version.map                        |  47 --
 lib/eal/arm/rte_cpuflags.c                    |   4 +
 lib/eal/arm/rte_hypervisor.c                  |   2 +
 lib/eal/arm/rte_power_intrinsics.c            |   5 +
 lib/eal/common/eal_common_bus.c               |  11 +
 lib/eal/common/eal_common_class.c             |   5 +
 lib/eal/common/eal_common_config.c            |   8 +
 lib/eal/common/eal_common_cpuflags.c          |   2 +
 lib/eal/common/eal_common_debug.c             |   3 +
 lib/eal/common/eal_common_dev.c               |  20 +
 lib/eal/common/eal_common_devargs.c           |  10 +
 lib/eal/common/eal_common_errno.c             |   3 +
 lib/eal/common/eal_common_fbarray.c           |  27 +
 lib/eal/common/eal_common_hexdump.c           |   3 +
 lib/eal/common/eal_common_hypervisor.c        |   2 +
 lib/eal/common/eal_common_interrupts.c        |  28 +
 lib/eal/common/eal_common_launch.c            |   6 +
 lib/eal/common/eal_common_lcore.c             |  18 +
 lib/eal/common/eal_common_lcore_var.c         |   2 +
 lib/eal/common/eal_common_mcfg.c              |  21 +
 lib/eal/common/eal_common_memory.c            |  30 +
 lib/eal/common/eal_common_memzone.c           |  10 +
 lib/eal/common/eal_common_options.c           |   5 +
 lib/eal/common/eal_common_proc.c              |   9 +
 lib/eal/common/eal_common_string_fns.c        |   4 +
 lib/eal/common/eal_common_tailqs.c            |   4 +
 lib/eal/common/eal_common_thread.c            |  15 +
 lib/eal/common/eal_common_timer.c             |   5 +
 lib/eal/common/eal_common_trace.c             |  16 +
 lib/eal/common/eal_common_trace_ctf.c         |   2 +
 lib/eal/common/eal_common_trace_points.c      |  19 +
 lib/eal/common/eal_common_trace_utils.c       |   2 +
 lib/eal/common/eal_common_uuid.c              |   5 +
 lib/eal/common/eal_symbol_exports.h           |  82 +++
 lib/eal/common/rte_bitset.c                   |   2 +
 lib/eal/common/rte_keepalive.c                |   7 +
 lib/eal/common/rte_malloc.c                   |  23 +
 lib/eal/common/rte_random.c                   |   5 +
 lib/eal/common/rte_reciprocal.c               |   3 +
 lib/eal/common/rte_service.c                  |  32 +
 lib/eal/common/rte_version.c                  |   8 +
 lib/eal/freebsd/eal.c                         |  23 +
 lib/eal/freebsd/eal_alarm.c                   |   3 +
 lib/eal/freebsd/eal_dev.c                     |   5 +
 lib/eal/freebsd/eal_interrupts.c              |  20 +
 lib/eal/freebsd/eal_memory.c                  |   4 +
 lib/eal/freebsd/eal_thread.c                  |   3 +
 lib/eal/freebsd/eal_timer.c                   |   2 +
 lib/eal/include/rte_function_versioning.h     |  99 ---
 lib/eal/linux/eal.c                           |   8 +
 lib/eal/linux/eal_alarm.c                     |   3 +
 lib/eal/linux/eal_dev.c                       |   5 +
 lib/eal/linux/eal_interrupts.c                |  20 +
 lib/eal/linux/eal_memory.c                    |   4 +
 lib/eal/linux/eal_thread.c                    |   3 +
 lib/eal/linux/eal_timer.c                     |   5 +
 lib/eal/linux/eal_vfio.c                      |  17 +
 lib/eal/loongarch/rte_cpuflags.c              |   4 +
 lib/eal/loongarch/rte_hypervisor.c            |   2 +
 lib/eal/loongarch/rte_power_intrinsics.c      |   5 +
 lib/eal/ppc/rte_cpuflags.c                    |   4 +
 lib/eal/ppc/rte_hypervisor.c                  |   2 +
 lib/eal/ppc/rte_power_intrinsics.c            |   5 +
 lib/eal/riscv/rte_cpuflags.c                  |   4 +
 lib/eal/riscv/rte_hypervisor.c                |   2 +
 lib/eal/riscv/rte_power_intrinsics.c          |   5 +
 lib/eal/unix/eal_debug.c                      |   3 +
 lib/eal/unix/eal_filesystem.c                 |   2 +
 lib/eal/unix/eal_firmware.c                   |   2 +
 lib/eal/unix/eal_unix_memory.c                |   5 +
 lib/eal/unix/eal_unix_timer.c                 |   2 +
 lib/eal/unix/rte_thread.c                     |  14 +
 lib/eal/version.map                           | 451 --------------
 lib/eal/windows/eal.c                         |  12 +
 lib/eal/windows/eal_alarm.c                   |   3 +
 lib/eal/windows/eal_debug.c                   |   2 +
 lib/eal/windows/eal_dev.c                     |   5 +
 lib/eal/windows/eal_interrupts.c              |  20 +
 lib/eal/windows/eal_memory.c                  |   8 +
 lib/eal/windows/eal_mp.c                      |   7 +
 lib/eal/windows/eal_thread.c                  |   2 +
 lib/eal/windows/eal_timer.c                   |   2 +
 lib/eal/windows/rte_thread.c                  |  15 +
 lib/eal/x86/rte_cpuflags.c                    |   4 +
 lib/eal/x86/rte_hypervisor.c                  |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |   5 +
 lib/eal/x86/rte_spinlock.c                    |   2 +
 lib/efd/rte_efd.c                             |   8 +
 lib/efd/version.map                           |  13 -
 lib/ethdev/ethdev_driver.c                    |  25 +
 lib/ethdev/ethdev_linux_ethtool.c             |   4 +
 lib/ethdev/ethdev_private.c                   |   3 +
 lib/ethdev/ethdev_trace_points.c              |   7 +
 lib/ethdev/rte_ethdev.c                       | 169 +++++
 lib/ethdev/rte_ethdev_cman.c                  |   5 +
 lib/ethdev/rte_flow.c                         |  65 ++
 lib/ethdev/rte_mtr.c                          |  22 +
 lib/ethdev/rte_tm.c                           |  32 +
 lib/ethdev/version.map                        | 378 ------------
 lib/eventdev/eventdev_private.c               |   3 +
 lib/eventdev/eventdev_trace_points.c          |  12 +
 lib/eventdev/rte_event_crypto_adapter.c       |  16 +
 lib/eventdev/rte_event_dma_adapter.c          |  16 +
 lib/eventdev/rte_event_eth_rx_adapter.c       |  24 +
 lib/eventdev/rte_event_eth_tx_adapter.c       |  18 +
 lib/eventdev/rte_event_ring.c                 |   5 +
 lib/eventdev/rte_event_timer_adapter.c        |  12 +
 lib/eventdev/rte_eventdev.c                   |  47 ++
 lib/eventdev/version.map                      | 179 ------
 lib/fib/rte_fib.c                             |  11 +
 lib/fib/rte_fib6.c                            |  10 +
 lib/fib/version.map                           |  31 -
 lib/gpudev/gpudev.c                           |  33 +
 lib/gpudev/version.map                        |  44 --
 lib/graph/graph.c                             |  17 +
 lib/graph/graph_debug.c                       |   3 +
 lib/graph/graph_stats.c                       |   5 +
 lib/graph/node.c                              |  12 +
 lib/graph/rte_graph_model_mcore_dispatch.c    |   4 +
 lib/graph/rte_graph_worker.c                  |   4 +
 lib/graph/version.map                         |  61 --
 lib/gro/rte_gro.c                             |   7 +
 lib/gro/version.map                           |  12 -
 lib/gso/rte_gso.c                             |   2 +
 lib/gso/version.map                           |   7 -
 lib/hash/rte_cuckoo_hash.c                    |  28 +
 lib/hash/rte_fbk_hash.c                       |   4 +
 lib/hash/rte_hash_crc.c                       |   3 +
 lib/hash/rte_thash.c                          |  13 +
 lib/hash/rte_thash_gf2_poly_math.c            |   2 +
 lib/hash/rte_thash_gfni.c                     |   3 +
 lib/hash/version.map                          |  66 --
 lib/ip_frag/rte_ip_frag_common.c              |   6 +
 lib/ip_frag/rte_ipv4_fragmentation.c          |   3 +
 lib/ip_frag/rte_ipv4_reassembly.c             |   2 +
 lib/ip_frag/rte_ipv6_fragmentation.c          |   2 +
 lib/ip_frag/rte_ipv6_reassembly.c             |   2 +
 lib/ip_frag/version.map                       |  16 -
 lib/ipsec/ipsec_sad.c                         |   7 +
 lib/ipsec/ipsec_telemetry.c                   |   3 +
 lib/ipsec/sa.c                                |   5 +
 lib/ipsec/ses.c                               |   2 +
 lib/ipsec/version.map                         |  23 -
 lib/jobstats/rte_jobstats.c                   |  15 +
 lib/jobstats/version.map                      |  20 -
 lib/kvargs/rte_kvargs.c                       |   9 +
 lib/kvargs/version.map                        |  14 -
 lib/latencystats/rte_latencystats.c           |   6 +
 lib/latencystats/version.map                  |  11 -
 lib/log/log.c                                 |  23 +
 lib/log/log_color.c                           |   2 +
 lib/log/log_internal.h                        |   3 -
 lib/log/log_syslog.c                          |   2 +
 lib/log/log_timestamp.c                       |   2 +
 lib/log/version.map                           |  37 --
 lib/lpm/rte_lpm.c                             |   9 +
 lib/lpm/rte_lpm6.c                            |  11 +
 lib/lpm/version.map                           |  24 -
 lib/mbuf/rte_mbuf.c                           |  18 +
 lib/mbuf/rte_mbuf_dyn.c                       |  10 +
 lib/mbuf/rte_mbuf_pool_ops.c                  |   6 +
 lib/mbuf/rte_mbuf_ptype.c                     |   9 +
 lib/mbuf/version.map                          |  45 --
 lib/member/rte_member.c                       |  14 +
 lib/member/version.map                        |  19 -
 lib/mempool/mempool_trace_points.c            |  11 +
 lib/mempool/rte_mempool.c                     |  28 +
 lib/mempool/rte_mempool_ops.c                 |   5 +
 lib/mempool/rte_mempool_ops_default.c         |   5 +
 lib/mempool/version.map                       |  65 --
 lib/meson.build                               |  67 +-
 lib/meter/rte_meter.c                         |   7 +
 lib/meter/version.map                         |  12 -
 lib/metrics/rte_metrics.c                     |   9 +
 lib/metrics/rte_metrics_telemetry.c           |  12 +
 lib/metrics/version.map                       |  26 -
 lib/mldev/mldev_utils.c                       |   3 +
 lib/mldev/mldev_utils_neon.c                  |  20 +
 lib/mldev/mldev_utils_neon_bfloat16.c         |   4 +
 lib/mldev/mldev_utils_scalar.c                |  20 +
 lib/mldev/mldev_utils_scalar_bfloat16.c       |   4 +
 lib/mldev/rte_mldev.c                         |  38 ++
 lib/mldev/rte_mldev_pmd.c                     |   3 +
 lib/mldev/version.map                         |  74 ---
 lib/net/net_crc.h                             |  15 -
 lib/net/rte_arp.c                             |   2 +
 lib/net/rte_ether.c                           |   4 +
 lib/net/rte_net.c                             |   3 +
 lib/net/rte_net_crc.c                         |  31 +-
 lib/net/version.map                           |  23 -
 lib/node/ethdev_ctrl.c                        |   3 +
 lib/node/ip4_lookup.c                         |   2 +
 lib/node/ip4_reassembly.c                     |   2 +
 lib/node/ip4_rewrite.c                        |   2 +
 lib/node/ip6_lookup.c                         |   2 +
 lib/node/ip6_rewrite.c                        |   2 +
 lib/node/udp4_input.c                         |   3 +
 lib/node/version.map                          |  25 -
 lib/pcapng/rte_pcapng.c                       |   8 +
 lib/pcapng/version.map                        |  13 -
 lib/pci/rte_pci.c                             |   4 +
 lib/pci/version.map                           |   9 -
 lib/pdcp/rte_pdcp.c                           |   6 +
 lib/pdcp/version.map                          |  16 -
 lib/pdump/rte_pdump.c                         |  10 +
 lib/pdump/version.map                         |  15 -
 lib/pipeline/rte_pipeline.c                   |  24 +
 lib/pipeline/rte_port_in_action.c             |   9 +
 lib/pipeline/rte_swx_ctl.c                    |  18 +
 lib/pipeline/rte_swx_ipsec.c                  |   8 +
 lib/pipeline/rte_swx_pipeline.c               |  74 +++
 lib/pipeline/rte_table_action.c               |  17 +
 lib/pipeline/version.map                      | 172 ------
 lib/port/rte_port_ethdev.c                    |   4 +
 lib/port/rte_port_eventdev.c                  |   4 +
 lib/port/rte_port_fd.c                        |   4 +
 lib/port/rte_port_frag.c                      |   3 +
 lib/port/rte_port_ras.c                       |   3 +
 lib/port/rte_port_ring.c                      |   7 +
 lib/port/rte_port_sched.c                     |   3 +
 lib/port/rte_port_source_sink.c               |   3 +
 lib/port/rte_port_sym_crypto.c                |   4 +
 lib/port/rte_swx_port_ethdev.c                |   3 +
 lib/port/rte_swx_port_fd.c                    |   3 +
 lib/port/rte_swx_port_ring.c                  |   3 +
 lib/port/rte_swx_port_source_sink.c           |   4 +
 lib/port/version.map                          |  50 --
 lib/power/power_common.c                      |   9 +
 lib/power/rte_power_cpufreq.c                 |  19 +
 lib/power/rte_power_pmd_mgmt.c                |  11 +
 lib/power/rte_power_qos.c                     |   3 +
 lib/power/rte_power_uncore.c                  |  15 +
 lib/power/version.map                         |  71 ---
 lib/rawdev/rte_rawdev.c                       |  31 +
 lib/rawdev/version.map                        |  36 --
 lib/rcu/rte_rcu_qsbr.c                        |  12 +
 lib/rcu/version.map                           |  17 -
 lib/regexdev/rte_regexdev.c                   |  27 +
 lib/regexdev/version.map                      |  40 --
 lib/reorder/rte_reorder.c                     |  12 +
 lib/reorder/version.map                       |  27 -
 lib/rib/rte_rib.c                             |  15 +
 lib/rib/rte_rib6.c                            |  15 +
 lib/rib/version.map                           |  34 --
 lib/ring/rte_ring.c                           |  12 +
 lib/ring/rte_soring.c                         |   4 +
 lib/ring/soring.c                             |  18 +
 lib/ring/version.map                          |  42 --
 lib/sched/rte_approx.c                        |   2 +
 lib/sched/rte_pie.c                           |   3 +
 lib/sched/rte_red.c                           |   7 +
 lib/sched/rte_sched.c                         |  16 +
 lib/sched/version.map                         |  30 -
 lib/security/rte_security.c                   |  21 +
 lib/security/version.map                      |  37 --
 lib/stack/rte_stack.c                         |   4 +
 lib/stack/version.map                         |   9 -
 lib/table/rte_swx_table_em.c                  |   3 +
 lib/table/rte_swx_table_learner.c             |  11 +
 lib/table/rte_swx_table_selector.c            |   7 +
 lib/table/rte_swx_table_wm.c                  |   2 +
 lib/table/rte_table_acl.c                     |   2 +
 lib/table/rte_table_array.c                   |   2 +
 lib/table/rte_table_hash_cuckoo.c             |   2 +
 lib/table/rte_table_hash_ext.c                |   2 +
 lib/table/rte_table_hash_key16.c              |   3 +
 lib/table/rte_table_hash_key32.c              |   3 +
 lib/table/rte_table_hash_key8.c               |   3 +
 lib/table/rte_table_hash_lru.c                |   2 +
 lib/table/rte_table_lpm.c                     |   2 +
 lib/table/rte_table_lpm_ipv6.c                |   2 +
 lib/table/rte_table_stub.c                    |   2 +
 lib/table/version.map                         |  53 --
 lib/telemetry/telemetry.c                     |   4 +
 lib/telemetry/telemetry_data.c                |  18 +
 lib/telemetry/telemetry_legacy.c              |   2 +
 lib/telemetry/version.map                     |  40 --
 lib/timer/rte_timer.c                         |  19 +
 lib/timer/version.map                         |  24 -
 lib/vhost/socket.c                            |  17 +
 lib/vhost/vdpa.c                              |  12 +
 lib/vhost/version.map                         | 111 ----
 lib/vhost/vhost.c                             |  42 ++
 lib/vhost/vhost_crypto.c                      |   7 +
 lib/vhost/vhost_user.c                        |   3 +
 lib/vhost/virtio_net.c                        |   8 +
 536 files changed, 5132 insertions(+), 6573 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 delete mode 100644 buildtools/map_to_win.py
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py
 delete mode 100644 drivers/baseband/acc/version.map
 delete mode 100644 drivers/baseband/fpga_5gnr_fec/version.map
 delete mode 100644 drivers/baseband/fpga_lte_fec/version.map
 delete mode 100644 drivers/bus/auxiliary/version.map
 delete mode 100644 drivers/bus/cdx/version.map
 create mode 100644 drivers/bus/dpaa/dpaa_bus_symbols.c
 delete mode 100644 drivers/bus/dpaa/version.map
 delete mode 100644 drivers/bus/fslmc/version.map
 delete mode 100644 drivers/bus/ifpga/version.map
 delete mode 100644 drivers/bus/pci/version.map
 delete mode 100644 drivers/bus/platform/version.map
 delete mode 100644 drivers/bus/uacce/version.map
 delete mode 100644 drivers/bus/vdev/version.map
 delete mode 100644 drivers/bus/vmbus/version.map
 create mode 100644 drivers/common/cnxk/roc_platform_symbols.c
 delete mode 100644 drivers/common/cnxk/version.map
 delete mode 100644 drivers/common/cpt/version.map
 delete mode 100644 drivers/common/dpaax/version.map
 delete mode 100644 drivers/common/ionic/version.map
 delete mode 100644 drivers/common/mlx5/version.map
 delete mode 100644 drivers/common/mvep/version.map
 delete mode 100644 drivers/common/nfp/version.map
 delete mode 100644 drivers/common/nitrox/version.map
 delete mode 100644 drivers/common/octeontx/version.map
 create mode 100644 drivers/common/sfc_efx/sfc_symbols.c
 delete mode 100644 drivers/common/sfc_efx/version.map
 delete mode 100644 drivers/crypto/cnxk/version.map
 delete mode 100644 drivers/crypto/dpaa2_sec/version.map
 delete mode 100644 drivers/crypto/dpaa_sec/version.map
 delete mode 100644 drivers/crypto/octeontx/version.map
 delete mode 100644 drivers/crypto/scheduler/version.map
 delete mode 100644 drivers/dma/cnxk/version.map
 delete mode 100644 drivers/event/cnxk/version.map
 delete mode 100644 drivers/event/dlb2/version.map
 delete mode 100644 drivers/mempool/cnxk/version.map
 delete mode 100644 drivers/mempool/dpaa/version.map
 delete mode 100644 drivers/mempool/dpaa2/version.map
 delete mode 100644 drivers/net/atlantic/version.map
 delete mode 100644 drivers/net/bnxt/version.map
 delete mode 100644 drivers/net/bonding/version.map
 delete mode 100644 drivers/net/cnxk/version.map
 delete mode 100644 drivers/net/dpaa/version.map
 create mode 100644 drivers/net/dpaa2/dpaa2_symbols.c
 delete mode 100644 drivers/net/dpaa2/version.map
 delete mode 100644 drivers/net/intel/i40e/version.map
 create mode 100644 drivers/net/intel/iavf/iavf_symbols.c
 delete mode 100644 drivers/net/intel/iavf/version.map
 delete mode 100644 drivers/net/intel/ice/version.map
 delete mode 100644 drivers/net/intel/idpf/version.map
 delete mode 100644 drivers/net/intel/ipn3ke/version.map
 delete mode 100644 drivers/net/intel/ixgbe/version.map
 delete mode 100644 drivers/net/mlx5/version.map
 delete mode 100644 drivers/net/octeontx/version.map
 delete mode 100644 drivers/net/ring/version.map
 delete mode 100644 drivers/net/softnic/version.map
 delete mode 100644 drivers/net/vhost/version.map
 delete mode 100644 drivers/power/kvm_vm/version.map
 delete mode 100644 drivers/raw/cnxk_rvu_lf/version.map
 delete mode 100644 drivers/raw/ifpga/version.map
 delete mode 100644 drivers/version.map
 delete mode 100644 lib/acl/version.map
 delete mode 100644 lib/argparse/version.map
 delete mode 100644 lib/bbdev/version.map
 delete mode 100644 lib/bitratestats/version.map
 delete mode 100644 lib/bpf/version.map
 delete mode 100644 lib/cfgfile/version.map
 delete mode 100644 lib/cmdline/version.map
 delete mode 100644 lib/compressdev/version.map
 delete mode 100644 lib/cryptodev/version.map
 delete mode 100644 lib/dispatcher/version.map
 delete mode 100644 lib/distributor/version.map
 delete mode 100644 lib/dmadev/version.map
 create mode 100644 lib/eal/common/eal_symbol_exports.h
 delete mode 100644 lib/eal/include/rte_function_versioning.h
 delete mode 100644 lib/eal/version.map
 delete mode 100644 lib/efd/version.map
 delete mode 100644 lib/ethdev/version.map
 delete mode 100644 lib/eventdev/version.map
 delete mode 100644 lib/fib/version.map
 delete mode 100644 lib/gpudev/version.map
 delete mode 100644 lib/graph/version.map
 delete mode 100644 lib/gro/version.map
 delete mode 100644 lib/gso/version.map
 delete mode 100644 lib/hash/version.map
 delete mode 100644 lib/ip_frag/version.map
 delete mode 100644 lib/ipsec/version.map
 delete mode 100644 lib/jobstats/version.map
 delete mode 100644 lib/kvargs/version.map
 delete mode 100644 lib/latencystats/version.map
 delete mode 100644 lib/log/version.map
 delete mode 100644 lib/lpm/version.map
 delete mode 100644 lib/mbuf/version.map
 delete mode 100644 lib/member/version.map
 delete mode 100644 lib/mempool/version.map
 delete mode 100644 lib/meter/version.map
 delete mode 100644 lib/metrics/version.map
 delete mode 100644 lib/mldev/version.map
 delete mode 100644 lib/net/version.map
 delete mode 100644 lib/node/version.map
 delete mode 100644 lib/pcapng/version.map
 delete mode 100644 lib/pci/version.map
 delete mode 100644 lib/pdcp/version.map
 delete mode 100644 lib/pdump/version.map
 delete mode 100644 lib/pipeline/version.map
 delete mode 100644 lib/port/version.map
 delete mode 100644 lib/power/version.map
 delete mode 100644 lib/rawdev/version.map
 delete mode 100644 lib/rcu/version.map
 delete mode 100644 lib/regexdev/version.map
 delete mode 100644 lib/reorder/version.map
 delete mode 100644 lib/rib/version.map
 delete mode 100644 lib/ring/version.map
 delete mode 100644 lib/sched/version.map
 delete mode 100644 lib/security/version.map
 delete mode 100644 lib/stack/version.map
 delete mode 100644 lib/table/version.map
 delete mode 100644 lib/telemetry/version.map
 delete mode 100644 lib/timer/version.map
 delete mode 100644 lib/vhost/version.map

-- 
2.48.1


^ permalink raw reply	[relevance 3%]

* [PATCH v5 4/8] build: generate symbol maps
  2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
@ 2025-03-27 13:36 17%   ` David Marchand
  2025-03-27 13:36 16%   ` [PATCH v5 6/8] build: use dynamically generated version maps David Marchand
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-27 13:36 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff

Rather than maintain a file in parallel of the code, symbols to be
exported can be marked with a token RTE_EXPORT_*SYMBOL.

From those marks, the build framework generates map files only for
symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
becomes unnecessary).

The build framework directly creates a map file in the format that the
linker expects (rather than converting from GNU linker to MSVC linker).

Empty maps are allowed again as a replacement for drivers/version.map.

The symbol check is updated to only support the new format.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v4:
- fixed MSVC export map (a msvc->mslinker update was missing),
- fixed join() error (with older meson? I don't see this on Fedora),
- explicit inclusion of header is now required,
- header has been renamed and moved to lib/eal/common/,
- because lib/log does not depend on EAL, added explicit
  include_directories,
- symbol versioning update has been moved later in the series,
  so updated gen-version-map.py accordingly,
- fixed bug when checking symbol removal,

Changes since RFC v3:
- polished python,
- fixed doc updates not belonging to this patch,
- renamed map files,
- changed msvc->mslinker as link mode,
- added parsing of AVX sources,

Changes since RFC v2:
- because of MSVC limitations wrt macro passed via cmdline,
  used an internal header for defining RTE_EXPORT_* macros,
- updated documentation and tooling,

---
 MAINTAINERS                                |   2 +
 buildtools/gen-version-map.py              | 106 ++++++++++
 buildtools/map-list-symbol.sh              |  10 +-
 buildtools/meson.build                     |   1 +
 devtools/check-symbol-change.py            |  90 ++++++++
 devtools/check-symbol-maps.sh              |  14 --
 devtools/checkpatches.sh                   |   2 +-
 doc/guides/contributing/abi_versioning.rst | 227 ++-------------------
 drivers/meson.build                        |  96 +++++----
 drivers/version.map                        |   3 -
 lib/eal/common/eal_symbol_exports.h        |  16 ++
 lib/meson.build                            |  94 ++++++---
 12 files changed, 365 insertions(+), 296 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100644 drivers/version.map
 create mode 100644 lib/eal/common/eal_symbol_exports.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4b01103f8e..42ea07854b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
 F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
+F: devtools/check-symbol-change.py
 F: devtools/check-symbol-change.sh
 F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
@@ -127,6 +128,7 @@ F: config/
 F: buildtools/check-symbols.sh
 F: buildtools/chkincs/
 F: buildtools/call-sphinx-build.py
+F: buildtools/gen-version-map.py
 F: buildtools/get-cpu-count.py
 F: buildtools/get-numa-count.py
 F: buildtools/list-dir-globs.py
diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
new file mode 100755
index 0000000000..c7dfc9b8c2
--- /dev/null
+++ b/buildtools/gen-version-map.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Generate a version map file used by GNU or MSVC linker."""
+
+import re
+import sys
+
+scriptname, link_mode, abi_version_file, output, *files = sys.argv
+
+# From eal_symbol_exports.h
+export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
+export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# From rte_function_versioning.h
+ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
+default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+
+with open(abi_version_file) as f:
+    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
+
+symbols = {}
+
+for file in files:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            node = None
+            symbol = None
+            comment = ''
+            if export_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                node = abi
+                symbol = export_sym_regexp.match(ln).group(1)
+            elif ver_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(2))
+                symbol = ver_sym_regexp.match(ln).group(1)
+            elif ver_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = ver_exp_sym_regexp.match(ln).group(1)
+            elif default_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(2))
+                symbol = default_sym_regexp.match(ln).group(1)
+
+            if not symbol:
+                continue
+
+            if node not in symbols:
+                symbols[node] = {}
+            symbols[node][symbol] = comment
+
+if link_mode == 'mslinker':
+    with open(output, "w") as outfile:
+        print(f"EXPORTS", file=outfile)
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            for symbol in sorted(symbols[key].keys()):
+                print(f"\t{symbol}", file=outfile)
+            del symbols[key]
+else:
+    with open(output, "w") as outfile:
+        local_token = False
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            print(f"{key} {{\n\tglobal:\n", file=outfile)
+            for symbol in sorted(symbols[key].keys()):
+                if link_mode == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                comment = symbols[key][symbol]
+                print(f"\t{prefix}{symbol};{comment}", file=outfile)
+            if not local_token:
+                print("\n\tlocal: *;", file=outfile)
+                local_token = True
+            print("};", file=outfile)
+            del symbols[key]
+        for key in sorted(symbols.keys()):
+            print(f"{key} {{\n\tglobal:\n", file=outfile)
+            for symbol in sorted(symbols[key].keys()):
+                if link_mode == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                comment = symbols[key][symbol]
+                print(f"\t{prefix}{symbol};{comment}", file=outfile)
+            print(f"}} {abi};", file=outfile)
+            if not local_token:
+                print("\n\tlocal: *;", file=outfile)
+                local_token = True
+            del symbols[key]
+        # No exported symbol, add a catch all
+        if not local_token:
+            print(f"{abi} {{", file=outfile)
+            print("\n\tlocal: *;", file=outfile)
+            local_token = True
+            print("};", file=outfile)
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index eb98451d8e..0829df4be5 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -62,10 +62,14 @@ for file in $@; do
 		if (current_section == "") {
 			next;
 		}
+		symbol_version = current_version
+		if (/^[^}].*[^:*]; # added in /) {
+			symbol_version = $5
+		}
 		if ("'$version'" != "") {
-			if ("'$version'" == "unset" && current_version != "") {
+			if ("'$version'" == "unset" && symbol_version != "") {
 				next;
-			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
+			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
 				next;
 			}
 		}
@@ -73,7 +77,7 @@ for file in $@; do
 		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
 			ret = 0;
 			if ("'$quiet'" == "") {
-				print "'$file' "current_section" "$1" "current_version;
+				print "'$file' "current_section" "$1" "symbol_version;
 			}
 			if ("'$symbol'" != "all") {
 				exit 0;
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 4e2c1217a2..b745e9afa4 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -16,6 +16,7 @@ else
     py3 = ['meson', 'runpython']
 endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
+gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
new file mode 100755
index 0000000000..d522fbb1ec
--- /dev/null
+++ b/devtools/check-symbol-change.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Check exported symbols change in a patch."""
+
+import re
+import sys
+
+file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
+# From eal_symbol_exports.h
+export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
+export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# TODO, handle versioned symbols from rte_function_versioning.h
+# ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+# ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
+# default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+
+symbols = {}
+
+for file in sys.argv[1:]:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            if file_header_regexp.match(ln):
+                if file_header_regexp.match(ln).group(2) == "lib":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+                elif file_header_regexp.match(ln).group(3) == "intel":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
+                else:
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+
+                if lib not in symbols:
+                    symbols[lib] = {}
+                continue
+
+            if export_exp_sym_regexp.match(ln):
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                node = 'EXPERIMENTAL'
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                symbol = export_sym_regexp.match(ln).group(1)
+                node = 'stable'
+            else:
+                continue
+
+            if symbol not in symbols[lib]:
+                symbols[lib][symbol] = {}
+            added = ln[0] == '+'
+            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if added:
+                symbols[lib][symbol]['added'] = node
+            else:
+                symbols[lib][symbol]['removed'] = node
+
+    for lib in sorted(symbols.keys()):
+        error = False
+        for symbol in sorted(symbols[lib].keys()):
+            if 'removed' not in symbols[lib][symbol]:
+                # Symbol addition
+                node = symbols[lib][symbol]['added']
+                if node == 'stable':
+                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
+                    error = True
+                else:
+                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
+                continue
+
+            if 'added' not in symbols[lib][symbol]:
+                # Symbol removal
+                node = symbols[lib][symbol]['removed']
+                if node == 'stable':
+                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
+                    print(f"Please check it has gone though the deprecation process.")
+                continue
+
+            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
+                # Symbol was moved around
+                continue
+
+            # Symbol modifications
+            added = symbols[lib][symbol]['added']
+            removed = symbols[lib][symbol]['removed']
+            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
+            print(f"Please check it has gone though the deprecation process.")
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
index 6121f78ec6..fcd3931e5d 100755
--- a/devtools/check-symbol-maps.sh
+++ b/devtools/check-symbol-maps.sh
@@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
     ret=1
 fi
 
-find_empty_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
-    done
-}
-
-empty_maps=$(find_empty_maps $@)
-if [ -n "$empty_maps" ] ; then
-    echo "Found empty maps:"
-    echo "$empty_maps"
-    ret=1
-fi
-
 find_bad_format_maps ()
 {
     abi_version=$(cut -d'.' -f 1 ABI_VERSION)
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index c9088bb403..9180c2b070 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
 PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
 SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
 LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
-NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
+NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
 options="$options $DPDK_CHECKPATCH_OPTIONS"
 
 print_usage () {
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..b6b6bf64d7 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -58,12 +58,12 @@ persists over multiple releases.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -77,7 +77,7 @@ that library.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
@@ -88,7 +88,7 @@ that library.
  } DPDK_21;
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -100,12 +100,12 @@ how this may be done.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_22 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_22 {
         global:
  ...
@@ -134,8 +134,7 @@ linked to the DPDK.
 
 To support backward compatibility the ``rte_function_versioning.h``
 header file provides macros to use when updating exported functions. These
-macros are used in conjunction with the ``version.map`` file for
-a given library to allow multiple versions of a symbol to exist in a shared
+macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
 The macros exported are:
@@ -176,6 +175,7 @@ Assume we have a function as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param)
  {
@@ -194,6 +194,7 @@ private, is safe), but it also requires modifying the code as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param, int debug)
  {
@@ -210,78 +211,16 @@ The addition of a parameter to the function is ABI breaking as the function is
 public, and existing application may use it in its current form. However, the
 compatibility macros in DPDK allow a developer to use symbol versioning so that
 multiple functions can be mapped to the same public symbol based on when an
-application was linked to it. To see how this is done, we start with the
-requisite libraries version map file. Initially the version map file for the acl
-library looks like this
+application was linked to it.
 
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-This file needs to be modified as follows
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-
-   } DPDK_21;
-
-The addition of the new block tells the linker that a new version node
-``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
-the symbols from the DPDK_21 node. This list is directly translated into a
-list of exported symbols when DPDK is compiled as a shared library.
-
-Next, we need to specify in the code which function maps to the rte_acl_create
+We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
 we need to update the function so that it is uniquely named, and not in conflict
 with the public symbol name
 
 .. code-block:: c
 
+ -RTE_EXPORT_SYMBOL(rte_acl_create)
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
  +struct rte_acl_ctx * __vsym
@@ -358,9 +297,9 @@ such that it contains both versions of the symbol and the public API.
    rte_acl_create_v22(const struct rte_acl_param *param, int debug);
 
 
-And that's it, on the next shared library rebuild, there will be two versions of
-rte_acl_create, an old DPDK_21 version, used by previously built applications,
-and a new DPDK_22 version, used by future built applications.
+And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
+an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
+used by newly built applications.
 
 .. note::
 
@@ -443,6 +382,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
@@ -450,27 +390,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
    ...
    }
 
-In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL``
-version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 When we promote the symbol to the stable ABI, we simply strip the
-``__rte_experimental`` annotation from the function and move the symbol from the
-``EXPERIMENTAL`` node, to the node of the next major ABI version as follow.
+``__rte_experimental`` annotation from the function.
 
 .. code-block:: c
 
@@ -478,31 +399,13 @@ When we promote the symbol to the stable ABI, we simply strip the
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
           ...
    }
 
-We then update the map file, adding the symbol ``rte_acl_create``
-to the ``DPDK_22`` version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-
 Although there are strictly no guarantees or commitments associated with
 :ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer
 an alias to experimental. The process to add an alias to experimental,
@@ -519,6 +422,7 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
@@ -540,37 +444,6 @@ and ``DPDK_22`` version nodes.
    }
    BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
-In the map file, we map the symbol to both the ``EXPERIMENTAL``
-and ``DPDK_22`` version nodes.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -579,38 +452,7 @@ ________________________________
 Lets assume that you've done the above updates, and in preparation for the next
 major ABI version you decide you would like to retire the old version of the
 function. After having gone through the ABI deprecation announcement process,
-removal is easy. Start by removing the symbol from the requisite version map
-file:
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
- -      rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-   } DPDK_21;
-
+removal is easy.
 
 Next remove the corresponding versioned export.
 
@@ -634,36 +476,9 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
-To do this, start by modifying the version map file, such that all symbols from
-the node to be removed are merged into the next node in the map.
-
-In the case of our map above, it would transform to look as follows
-
-.. code-block:: none
-
-   DPDK_22 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
-        rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
  };
 
-Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index c15319dc24..5368d38363 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -275,14 +275,14 @@ foreach subpath:subdirs
                 dependencies: static_deps,
                 c_args: cflags)
         objs += tmp_lib.extract_all_objects(recursive: true)
-        sources = custom_target(out_filename,
+        sources_pmd_info = custom_target(out_filename,
                 command: [pmdinfo, tmp_lib.full_path(), '@OUTPUT@', pmdinfogen],
                 output: out_filename,
                 depends: [tmp_lib])
 
         # now build the static driver
         static_lib = static_library(lib_name,
-                sources,
+                sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: static_deps,
@@ -292,48 +292,70 @@ foreach subpath:subdirs
         # now build the shared driver
         version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
 
-        lk_deps = []
-        lk_args = []
         if not fs.is_file(version_map)
-            version_map = '@0@/version.map'.format(meson.current_source_dir())
-            lk_deps += [version_map]
-        else
-            lk_deps += [version_map]
-            if not is_windows and developer_mode
-                # on unix systems check the output of the
-                # check-symbols.sh script, using it as a
-                # dependency of the .so build
-                lk_deps += custom_target(lib_name + '.sym_chk',
-                        command: [check_symbols, version_map, '@INPUT@'],
-                        capture: true,
-                        input: static_lib,
-                        output: lib_name + '.sym_chk')
-            endif
-        endif
-
-        if is_windows
             if is_ms_linker
-                def_file = custom_target(lib_name + '_def',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_exports.def'.format(lib_name))
-                lk_deps += [def_file]
-
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
+                link_mode = 'mslinker'
+            elif is_windows
+                link_mode = 'mingw'
             else
-                mingw_map = custom_target(lib_name + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(lib_name))
-                lk_deps += [mingw_map]
-
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                link_mode = 'gnu'
+            endif
+            version_map = custom_target(lib_name + '_map',
+                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                    input: sources + sources_avx2 + sources_avx512,
+                    output: '_'.join([class, name, 'exports.map']))
+            version_map_path = version_map.full_path()
+            version_map_dep = [version_map]
+            lk_deps = [version_map]
+
+            if is_ms_linker and is_ms_compiler
+                lk_args = ['/def:' + version_map.full_path()]
+            elif is_ms_linker
+                lk_args = ['-Wl,/def:' + version_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
             endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            version_map_path = version_map
+            version_map_dep = []
+            lk_deps = [version_map]
+
+            if is_windows
+                if is_ms_linker
+                    def_file = custom_target(lib_name + '_def',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_exports.def'.format(lib_name))
+                    lk_deps += [def_file]
+
+                    lk_args = ['-Wl,/def:' + def_file.full_path()]
+                else
+                    mingw_map = custom_target(lib_name + '_mingw',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_mingw.map'.format(lib_name))
+                    lk_deps += [mingw_map]
+
+                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                endif
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
+        endif
+
+        if not is_windows and developer_mode
+            # on unix systems check the output of the
+            # check-symbols.sh script, using it as a
+            # dependency of the .so build
+            lk_deps += custom_target(lib_name + '.sym_chk',
+                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    capture: true,
+                    input: static_lib,
+                    output: lib_name + '.sym_chk',
+                    depends: version_map_dep)
         endif
 
-        shared_lib = shared_library(lib_name, sources,
+        shared_lib = shared_library(lib_name, sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: shared_deps,
diff --git a/drivers/version.map b/drivers/version.map
deleted file mode 100644
index 17cc97bda6..0000000000
--- a/drivers/version.map
+++ /dev/null
@@ -1,3 +0,0 @@
-DPDK_25 {
-	local: *;
-};
diff --git a/lib/eal/common/eal_symbol_exports.h b/lib/eal/common/eal_symbol_exports.h
new file mode 100644
index 0000000000..b3033dd336
--- /dev/null
+++ b/lib/eal/common/eal_symbol_exports.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef EAL_SYMBOL_EXPORTS_H
+#define EAL_SYMBOL_EXPORTS_H
+
+/* Internal macros for exporting symbols, used by the build system.
+ * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
+ * version this symbol was introduced in.
+ */
+#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
+#define RTE_EXPORT_INTERNAL_SYMBOL(a)
+#define RTE_EXPORT_SYMBOL(a)
+
+#endif /* EAL_SYMBOL_EXPORTS_H */
diff --git a/lib/meson.build b/lib/meson.build
index 91efc65c4b..a9cbea5fea 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
+fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -137,9 +138,12 @@ foreach l:libraries
     # external package/library requirements
     ext_deps = []
     deps = []
-    # eal is standard dependency once built
     if dpdk_conf.has('RTE_LIB_EAL')
+        # eal is standard dependency once built
         deps += ['eal']
+    else
+        # otherwise, make private headers available (like eal_symbol_exports.h)
+        includes += include_directories('eal/common')
     endif
 
     if dpdk_libs_deprecated.contains(l)
@@ -285,42 +289,58 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not use_function_versioning or is_windows
-        # use pre-build objects to build shared lib
-        sources = []
-        objs += static_lib.extract_all_objects(recursive: false)
-    else
-        # for compat we need to rebuild with
-        # RTE_BUILD_SHARED_LIB defined
-        cflags += '-DRTE_BUILD_SHARED_LIB'
-    endif
-
-    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-    lk_deps = [version_map]
-
-    if is_ms_linker
-        def_file = custom_target(libname + '_def',
-                command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                input: version_map,
-                output: '@0@_exports.def'.format(libname))
-        lk_deps += [def_file]
+    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
+        else
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(libname + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources,
+                output: '_'.join([name, 'exports.map']))
+        version_map_path = version_map.full_path()
+        version_map_dep = [version_map]
+        lk_deps = [version_map]
 
-        if is_ms_compiler
-            lk_args = ['/def:' + def_file.full_path()]
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
         else
-            lk_args = ['-Wl,/def:' + def_file.full_path()]
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
     else
-        if is_windows
-            mingw_map = custom_target(libname + '_mingw',
+        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
+        version_map_path = version_map
+        version_map_dep = []
+        lk_deps = [version_map]
+        if is_ms_linker
+            def_file = custom_target(libname + '_def',
                     command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
                     input: version_map,
-                    output: '@0@_mingw.map'.format(libname))
-            lk_deps += [mingw_map]
+                    output: '@0@_exports.def'.format(libname))
+            lk_deps += [def_file]
 
-            lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            if is_ms_compiler
+                lk_args = ['/def:' + def_file.full_path()]
+            else
+                lk_args = ['-Wl,/def:' + def_file.full_path()]
+            endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            if is_windows
+                mingw_map = custom_target(libname + '_mingw',
+                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                        input: version_map,
+                        output: '@0@_mingw.map'.format(libname))
+                lk_deps += [mingw_map]
+
+                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
         endif
     endif
 
@@ -329,11 +349,21 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols,
-                    version_map, '@INPUT@'],
+                command: [check_symbols, version_map_path, '@INPUT@'],
                 capture: true,
                 input: static_lib,
-                output: name + '.sym_chk')
+                output: name + '.sym_chk',
+                depends: version_map_dep)
+    endif
+
+    if not use_function_versioning or is_windows
+        # use pre-build objects to build shared lib
+        sources = []
+        objs += static_lib.extract_all_objects(recursive: false)
+    else
+        # for compat we need to rebuild with
+        # RTE_BUILD_SHARED_LIB defined
+        cflags += '-DRTE_BUILD_SHARED_LIB'
     endif
 
     shared_lib = shared_library(libname,
-- 
2.48.1


^ permalink raw reply	[relevance 17%]

* [PATCH v5 6/8] build: use dynamically generated version maps
  2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
  2025-03-27 13:36 17%   ` [PATCH v5 4/8] build: generate symbol maps David Marchand
@ 2025-03-27 13:36 16%   ` David Marchand
  2025-03-28 13:19  0%     ` Aaron Conole
  2025-03-27 13:36 20%   ` [PATCH v5 8/8] eal: rework function versioning macros David Marchand
  2025-03-27 18:22  0%   ` [PATCH v5 0/8] Symbol versioning and export rework David Marchand
  3 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-27 13:36 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andremue, Aaron Conole,
	Michael Santana, Dmitry Kozlyuk, Tyler Retzlaff

Switch to dynamically generated version maps.

As the map files get generated, tooling around checking, converting,
updating etc.. static version maps can be removed.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   7 -
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/map-list-symbol.sh                 |   7 +-
 buildtools/map_to_win.py                      |  41 ---
 buildtools/meson.build                        |   1 -
 devtools/check-symbol-change.sh               | 186 -----------
 devtools/check-symbol-maps.sh                 | 101 ------
 devtools/checkpatches.sh                      |   2 +-
 devtools/update-abi.sh                        |  46 ---
 devtools/update_version_map_abi.py            | 210 ------------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/meson.build                           |  74 ++---
 lib/meson.build                               |  73 ++---
 17 files changed, 188 insertions(+), 931 deletions(-)
 delete mode 100644 buildtools/map_to_win.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0cc4d12b0b..7a6b679fe5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,6 @@ jobs:
         failed=
         devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
         devtools/check-meson.py || failed=true
-        devtools/check-symbol-maps.sh || failed=true
         [ -z "$failed" ]
   ubuntu-vm-builds:
     name: ${{ join(matrix.config.*, '-') }}
diff --git a/MAINTAINERS b/MAINTAINERS
index 42ea07854b..480972ef1e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
 F: MAINTAINERS
 F: devtools/build-dict.sh
 F: devtools/check-abi.sh
-F: devtools/check-abi-version.sh
 F: devtools/check-doc-vs-code.sh
 F: devtools/check-dup-includes.sh
 F: devtools/check-maintainers.sh
@@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
 F: devtools/check-symbol-change.py
-F: devtools/check-symbol-change.sh
-F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
 F: devtools/get-maintainer.sh
 F: devtools/git-log-fixes.sh
 F: devtools/load-devel-config
 F: devtools/parse-flow-support.sh
 F: devtools/process-iwyu.py
-F: devtools/update-abi.sh
 F: devtools/update-patches.py
-F: devtools/update_version_map_abi.py
 F: devtools/libabigail.abignore
 F: devtools/words-case.txt
 F: license/
@@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/common/
 F: lib/eal/unix/
 F: lib/eal/include/
-F: lib/eal/version.map
 F: doc/guides/prog_guide/env_abstraction_layer.rst
 F: app/test/test_alarm.c
 F: app/test/test_atomic.c
@@ -396,7 +390,6 @@ Windows support
 M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
 M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/windows/
-F: buildtools/map_to_win.py
 F: doc/guides/windows_gsg/
 
 Windows memory allocation
diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
index b8ac24391e..0d6745ec14 100755
--- a/buildtools/check-symbols.sh
+++ b/buildtools/check-symbols.sh
@@ -7,29 +7,12 @@ OBJFILE=$2
 
 ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
 LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
-CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
-
-# added check for "make -C test/" usage
-if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
-then
-	exit 0
-fi
-
-if [ -d $MAPFILE ]
-then
-	exit 0
-fi
-
 DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
 trap 'rm -f "$DUMPFILE"' EXIT
 objdump -t $OBJFILE >$DUMPFILE
 
 ret=0
 
-if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
-	ret=1
-fi
-
 for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
 do
 	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
@@ -37,8 +20,7 @@ do
 		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as experimental
-		but is listed in version map
+		$SYM is not flagged as experimental but is exported as an experimental symbol
 		Please add __rte_experimental to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -53,9 +35,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as experimental
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as experimental but is not exported as an experimental symbol
+		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
@@ -67,8 +48,7 @@ do
 		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as internal
-		but is listed in version map
+		$SYM is not flagged as internal but is exported as an internal symbol
 		Please add __rte_internal to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -83,9 +63,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as internal
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as internal but is not exported as an internal symbol
+		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index 0829df4be5..962d5f3271 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -42,7 +42,6 @@ for file in $@; do
 	cat "$file" |awk '
 	BEGIN {
 		current_section = "";
-		current_version = "";
 		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
 			ret = 0;
 		} else {
@@ -54,15 +53,11 @@ for file in $@; do
 			current_section = $1;
 		}
 	}
-	/.*}/ { current_section = ""; current_version = ""; }
-	/^\t# added in / {
-		current_version=$4;
-	}
+	/.*}/ { current_section = ""; }
 	/^[^}].*[^:*];/ {
 		if (current_section == "") {
 			next;
 		}
-		symbol_version = current_version
 		if (/^[^}].*[^:*]; # added in /) {
 			symbol_version = $5
 		}
diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
deleted file mode 100644
index aa1752cacd..0000000000
--- a/buildtools/map_to_win.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-import sys
-
-
-def is_function_line(ln):
-    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
-
-# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
-def create_mingw_map_file(input_map, output_map):
-    with open(input_map) as f_in, open(output_map, 'w') as f_out:
-        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
-
-def main(args):
-    if not args[1].endswith('version.map') or \
-            not args[2].endswith('exports.def') and \
-            not args[2].endswith('mingw.map'):
-        return 1
-
-    if args[2].endswith('mingw.map'):
-        create_mingw_map_file(args[1], args[2])
-        return 0
-
-# generate def file from map file.
-# This works taking indented lines only which end with a ";" and which don't
-# have a colon in them, i.e. the lines defining functions only.
-    else:
-        with open(args[1]) as f_in:
-            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
-                         if is_function_line(ln)]
-            functions = ["EXPORTS\n"] + functions
-
-    with open(args[2], 'w') as f_out:
-        f_out.writelines(functions)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv))
diff --git a/buildtools/meson.build b/buildtools/meson.build
index b745e9afa4..1cd1ce02fd 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -18,7 +18,6 @@ endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
 gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
-map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
 get_cpu_count_cmd = py3 + files('get-cpu-count.py')
 get_numa_count_cmd = py3 + files('get-numa-count.py')
diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
deleted file mode 100755
index 8992214ac8..0000000000
--- a/devtools/check-symbol-change.sh
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
-
-build_map_changes()
-{
-	local fname="$1"
-	local mapdb="$2"
-
-	cat "$fname" | awk '
-		# Initialize our variables
-		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
-
-		# Anything that starts with + or -, followed by an a
-		# and ends in the string .map is the name of our map file
-		# This may appear multiple times in a patch if multiple
-		# map files are altered, and all section/symbol names
-		# appearing between a triggering of this rule and the
-		# next trigger of this rule are associated with this file
-		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
-
-		# The previous rule catches all .map files, anything else
-		# indicates we left the map chunk.
-		/[-+] [ab]\// {in_map=0}
-
-		# Triggering this rule, which starts a line and ends it
-		# with a { identifies a versioned section.  The section name is
-		# the rest of the line with the + and { symbols removed.
-		# Triggering this rule sets in_sec to 1, which actives the
-		# symbol rule below
-		/^.*{/ {
-			gsub("+", "");
-			if (in_map == 1) {
-				sec=$(NF-1); in_sec=1;
-			}
-		}
-
-		# This rule identifies the end of a section, and disables the
-		# symbol rule
-		/.*}/ {in_sec=0}
-
-		# This rule matches on a + followed by any characters except a :
-		# (which denotes a global vs local segment), and ends with a ;.
-		# The semicolon is removed and the symbol is printed with its
-		# association file name and version section, along with an
-		# indicator that the symbol is a new addition.  Note this rule
-		# only works if we have found a version section in the rule
-		# above (hence the in_sec check) And found a map file (the
-		# in_map check).  If we are not in a map chunk, do nothing.  If
-		# we are in a map chunk but not a section chunk, record it as
-		# unknown.
-		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " add"
-				} else {
-					print map " " sym " unknown add"
-				}
-			}
-		}
-
-		# This is the same rule as above, but the rule matches on a
-		# leading - rather than a +, denoting that the symbol is being
-		# removed.
-		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " del"
-				} else {
-					print map " " sym " unknown del"
-				}
-			}
-		}' > "$mapdb"
-
-		sort -u "$mapdb" > "$mapdb.2"
-		mv -f "$mapdb.2" "$mapdb"
-
-}
-
-is_stable_section() {
-	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
-}
-
-check_for_rule_violations()
-{
-	local mapdb="$1"
-	local mname
-	local symname
-	local secname
-	local ar
-	local ret=0
-
-	while read mname symname secname ar
-	do
-		if [ "$ar" = "add" ]
-		then
-
-			if [ "$secname" = "unknown" ]
-			then
-				# Just inform the user of this occurrence, but
-				# don't flag it as an error
-				echo -n "INFO: symbol $symname is added but "
-				echo -n "patch has insufficient context "
-				echo -n "to determine the section name "
-				echo -n "please ensure the version is "
-				echo "EXPERIMENTAL"
-				continue
-			fi
-
-			oldsecname=$(sed -n \
-			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
-
-			# A symbol can not enter a stable section directly
-			if [ -z "$oldsecname" ]
-			then
-				if ! is_stable_section $secname
-				then
-					echo -n "INFO: symbol $symname has "
-					echo -n "been added to the "
-					echo -n "$secname section of the "
-					echo "version map"
-					continue
-				else
-					echo -n "ERROR: symbol $symname "
-					echo -n "is added in the $secname "
-					echo -n "section, but is expected to "
-					echo -n "be added in the EXPERIMENTAL "
-					echo "section of the version map"
-					ret=1
-					continue
-				fi
-			fi
-
-			# This symbol is moving inside a section, nothing to do
-			if [ "$oldsecname" = "$secname" ]
-			then
-				continue
-			fi
-
-			# This symbol is moving between two sections (the
-			# original section is a stable section).
-			# This can be legit, just warn.
-			if is_stable_section $oldsecname
-			then
-				echo -n "INFO: symbol $symname is being "
-				echo -n "moved from $oldsecname to $secname. "
-				echo -n "Ensure that it has gone through the "
-				echo "deprecation process"
-				continue
-			fi
-		else
-
-			if ! grep -q "$mname $symname .* add" "$mapdb" && \
-			   is_stable_section $secname
-			then
-				# Just inform users that stable
-				# symbols need to go through a deprecation
-				# process
-				echo -n "INFO: symbol $symname is being "
-				echo -n "removed, ensure that it has "
-				echo "gone through the deprecation process"
-			fi
-		fi
-	done < "$mapdb"
-
-	return $ret
-}
-
-trap clean_and_exit_on_sig EXIT
-
-mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
-patch=$1
-exit_code=1
-
-clean_and_exit_on_sig()
-{
-	rm -f "$mapfile"
-	exit $exit_code
-}
-
-build_map_changes "$patch" "$mapfile"
-check_for_rule_violations "$mapfile"
-exit_code=$?
-rm -f "$mapfile"
-
-exit $exit_code
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
deleted file mode 100755
index fcd3931e5d..0000000000
--- a/devtools/check-symbol-maps.sh
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright 2018 Mellanox Technologies, Ltd
-
-cd $(dirname $0)/..
-
-# speed up by ignoring Unicode details
-export LC_ALL=C
-
-if [ $# = 0 ] ; then
-    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
-fi
-
-ret=0
-
-find_orphan_symbols ()
-{
-    for map in $@ ; do
-        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
-            if echo $sym | grep -q '^per_lcore_' ; then
-                symsrc=${sym#per_lcore_}
-            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
-                symsrc=${sym#__}
-            else
-                symsrc=$sym
-            fi
-            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
-                echo "$map: $sym"
-            fi
-        done
-    done
-}
-
-orphan_symbols=$(find_orphan_symbols $@)
-if [ -n "$orphan_symbols" ] ; then
-    echo "Found only in symbol map file:"
-    echo "$orphan_symbols" | sed 's,^,\t,'
-    ret=1
-fi
-
-find_duplicate_symbols ()
-{
-    for map in $@ ; do
-        buildtools/map-list-symbol.sh $map | \
-            sort | uniq -c | grep -v " 1 $map" || true
-    done
-}
-
-duplicate_symbols=$(find_duplicate_symbols $@)
-if [ -n "$duplicate_symbols" ] ; then
-    echo "Found duplicates in symbol map file:"
-    echo "$duplicate_symbols"
-    ret=1
-fi
-
-local_miss_maps=$(grep -L 'local: \*;' $@ || true)
-if [ -n "$local_miss_maps" ] ; then
-    echo "Found maps without local catch-all:"
-    echo "$local_miss_maps"
-    ret=1
-fi
-
-find_bad_format_maps ()
-{
-    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
-    next_abi_version=$((abi_version + 1))
-    for map in $@ ; do
-        cat $map | awk '
-            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
-            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
-            /^$/ { next; } # empty line
-            /^\t(global:|local: \*;)$/ { next; } # qualifiers
-            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
-            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
-            { print $0; }' || echo $map
-    done
-}
-
-bad_format_maps=$(find_bad_format_maps $@)
-if [ -n "$bad_format_maps" ] ; then
-    echo "Found badly formatted maps:"
-    echo "$bad_format_maps"
-    ret=1
-fi
-
-find_non_versioned_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
-            echo $map
-    done
-}
-
-non_versioned_maps=$(find_non_versioned_maps $@)
-if [ -n "$non_versioned_maps" ] ; then
-    echo "Found non versioned maps:"
-    echo "$non_versioned_maps"
-    ret=1
-fi
-
-exit $ret
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index 9180c2b070..c111b0fef3 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -9,7 +9,7 @@
 # - DPDK_CHECKPATCH_OPTIONS
 . $(dirname $(readlink -f $0))/load-devel-config
 
-VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
+VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
 
 # Enable codespell by default. This can be overwritten from a config file.
 # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
deleted file mode 100755
index 45437f3c3b..0000000000
--- a/devtools/update-abi.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-abi_version=$1
-abi_version_file="./ABI_VERSION"
-update_path="lib drivers"
-
-# check ABI version format string
-check_abi_version() {
-      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
-}
-
-if [ -z "$1" ]; then
-      # output to stderr
-      >&2 echo "Please provide ABI version"
-      exit 1
-fi
-
-# check version string format
-if ! check_abi_version $abi_version ; then
-      # output to stderr
-      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
-      exit 1
-fi
-
-if [ -n "$2" ]; then
-      abi_version_file=$2
-fi
-
-if [ -n "$3" ]; then
-      # drop $1 and $2
-      shift 2
-      # assign all other arguments as update paths
-      update_path=$@
-fi
-
-echo "New ABI version:" $abi_version
-echo "ABI_VERSION path:" $abi_version_file
-echo "Path to update:" $update_path
-
-echo $abi_version > $abi_version_file
-
-find $update_path -name version.map -exec \
-      devtools/update_version_map_abi.py {} \
-      $abi_version \; -print
diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
deleted file mode 100755
index d17b02a327..0000000000
--- a/devtools/update_version_map_abi.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-"""
-A Python program that updates and merges all available stable ABI versions into
-one ABI version, while leaving experimental ABI exactly as it is. The intended
-ABI version is supplied via command-line parameter. This script is to be called
-from the devtools/update-abi.sh utility.
-"""
-
-import argparse
-import sys
-import re
-
-
-def __parse_map_file(f_in):
-    # match function name, followed by semicolon, followed by EOL or comments,
-    # optionally with whitespace in between each item
-    func_line_regex = re.compile(r"\s*"
-                                 r"(?P<line>"
-                                 r"(?P<func>[a-zA-Z_0-9]+)"
-                                 r"\s*"
-                                 r";"
-                                 r"\s*"
-                                 r"(?P<comment>#.+)?"
-                                 r")"
-                                 r"\s*"
-                                 r"$")
-    # match section name, followed by opening bracked, followed by EOL,
-    # optionally with whitespace in between each item
-    section_begin_regex = re.compile(r"\s*"
-                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
-                                     r"\s*"
-                                     r"{"
-                                     r"\s*"
-                                     r"$")
-    # match closing bracket, optionally followed by section name (for when we
-    # inherit from another ABI version), followed by semicolon, followed by
-    # EOL, optionally with whitespace in between each item
-    section_end_regex = re.compile(r"\s*"
-                                   r"}"
-                                   r"\s*"
-                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
-                                   r"\s*"
-                                   r";"
-                                   r"\s*"
-                                   r"$")
-
-    # for stable ABI, we don't care about which version introduced which
-    # function, we just flatten the list. there are dupes in certain files, so
-    # use a set instead of a list
-    stable_lines = set()
-    # copy experimental section as is
-    experimental_lines = []
-    # copy internal section as is
-    internal_lines = []
-    in_experimental = False
-    in_internal = False
-    has_stable = False
-
-    # gather all functions
-    for line in f_in:
-        # clean up the line
-        line = line.strip('\n').strip()
-
-        # is this an end of section?
-        match = section_end_regex.match(line)
-        if match:
-            # whatever section this was, it's not active any more
-            in_experimental = False
-            in_internal = False
-            continue
-
-        # if we're in the middle of experimental section, we need to copy
-        # the section verbatim, so just add the line
-        if in_experimental:
-            experimental_lines += [line]
-            continue
-
-        # if we're in the middle of internal section, we need to copy
-        # the section verbatim, so just add the line
-        if in_internal:
-            internal_lines += [line]
-            continue
-
-        # skip empty lines
-        if not line:
-            continue
-
-        # is this a beginning of a new section?
-        match = section_begin_regex.match(line)
-        if match:
-            cur_section = match.group("version")
-            # is it experimental?
-            in_experimental = cur_section == "EXPERIMENTAL"
-            # is it internal?
-            in_internal = cur_section == "INTERNAL"
-            if not in_experimental and not in_internal:
-                has_stable = True
-            continue
-
-        # is this a function?
-        match = func_line_regex.match(line)
-        if match:
-            stable_lines.add(match.group("line"))
-
-    return has_stable, stable_lines, experimental_lines, internal_lines
-
-
-def __generate_stable_abi(f_out, abi_major, lines):
-    # print ABI version header
-    print("DPDK_{} {{".format(abi_major), file=f_out)
-
-    # print global section if it exists
-    if lines:
-        print("\tglobal:", file=f_out)
-        # blank line
-        print(file=f_out)
-
-        # print all stable lines, alphabetically sorted
-        for line in sorted(lines):
-            print("\t{}".format(line), file=f_out)
-
-        # another blank line
-        print(file=f_out)
-
-    # print local section
-    print("\tlocal: *;", file=f_out)
-
-    # end stable version
-    print("};", file=f_out)
-
-
-def __generate_experimental_abi(f_out, lines):
-    # start experimental section
-    print("EXPERIMENTAL {", file=f_out)
-
-    # print all experimental lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __generate_internal_abi(f_out, lines):
-    # start internal section
-    print("INTERNAL {", file=f_out)
-
-    # print all internal lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __main():
-    arg_parser = argparse.ArgumentParser(
-        description='Merge versions in linker version script.')
-
-    arg_parser.add_argument("map_file", type=str,
-                            help='path to linker version script file '
-                                 '(pattern: version.map)')
-    arg_parser.add_argument("abi_version", type=str,
-                            help='target ABI version (pattern: MAJOR.MINOR)')
-
-    parsed = arg_parser.parse_args()
-
-    if not parsed.map_file.endswith('version.map'):
-        print("Invalid input file: {}".format(parsed.map_file),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-
-    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
-        print("Invalid ABI version: {}".format(parsed.abi_version),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-    abi_major = parsed.abi_version.split('.')[0]
-
-    with open(parsed.map_file) as f_in:
-        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
-
-    with open(parsed.map_file, 'w') as f_out:
-        need_newline = has_stable and experimental_lines
-        if has_stable:
-            __generate_stable_abi(f_out, abi_major, stable_lines)
-        if need_newline:
-            # separate sections with a newline
-            print(file=f_out)
-        if experimental_lines:
-            __generate_experimental_abi(f_out, experimental_lines)
-        if internal_lines:
-            if has_stable or experimental_lines:
-              # separate sections with a newline
-              print(file=f_out)
-            __generate_internal_abi(f_out, internal_lines)
-
-
-if __name__ == "__main__":
-    __main()
diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
index d96153c6b2..f03a7467ac 100644
--- a/doc/guides/contributing/abi_policy.rst
+++ b/doc/guides/contributing/abi_policy.rst
@@ -330,31 +330,14 @@ become part of a tracked ABI version.
 
 Note that marking an API as experimental is a multi step process.
 To mark an API as experimental, the symbols which are desired to be exported
-must be placed in an EXPERIMENTAL version block in the corresponding libraries'
-version map script.
+must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
+sources.
 Experimental symbols must be commented so that it is clear in which DPDK
 version they were introduced.
 
-.. code-block:: none
-
-   EXPERIMENTAL {
-           global:
-
-           # added in 20.11
-           rte_foo_init;
-           rte_foo_configure;
-
-           # added in 21.02
-           rte_foo_cleanup;
-   ...
-
 Secondly, the corresponding prototypes of those exported functions (in the
 development header files), must be marked with the ``__rte_experimental`` tag
 (see ``rte_compat.h``).
-The DPDK build makefiles perform a check to ensure that the map file and the
-C code reflect the same list of symbols.
-This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
-during compilation in the corresponding library Makefile.
 
 In addition to tagging the code with ``__rte_experimental``,
 the doxygen markup must also contain the EXPERIMENTAL string,
diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
index 1ebc79ca3c..43e27bbd0a 100644
--- a/doc/guides/contributing/coding_style.rst
+++ b/doc/guides/contributing/coding_style.rst
@@ -1018,13 +1018,6 @@ name
 	sources are stored in a directory ``lib/xyz``, this value should
 	never be needed for new libraries.
 
-.. note::
-
-	The name value also provides the name used to find the function version
-	map file, as part of the build process, so if the directory name and
-	library names differ, the ``version.map`` file should be named
-	consistently with the library, not the directory
-
 objs
 	**Default Value = []**.
 	This variable can be used to pass to the library build some pre-built
diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
index 4debb07b98..a06d8a2a3b 100644
--- a/doc/guides/contributing/img/patch_cheatsheet.svg
+++ b/doc/guides/contributing/img/patch_cheatsheet.svg
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    version="1.1"
    width="210mm"
    height="297mm"
    id="svg2985"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="patch_cheatsheet.svg">
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="patch_cheatsheet.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
   <sodipodi:namedview
      pagecolor="#ffffff"
      bordercolor="#666666"
@@ -23,18 +23,22 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:window-width="1920"
-     inkscape:window-height="1017"
+     inkscape:window-height="975"
      id="namedview274"
      showgrid="false"
      inkscape:zoom="0.89702958"
-     inkscape:cx="246.07409"
-     inkscape:cy="416.76022"
-     inkscape:window-x="1072"
-     inkscape:window-y="-8"
+     inkscape:cx="546.24732"
+     inkscape:cy="385.71749"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
      inkscape:window-maximized="1"
      inkscape:current-layer="layer1"
      inkscape:document-rotation="0"
-     inkscape:snap-grids="false" />
+     inkscape:snap-grids="false"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm" />
   <defs
      id="defs3">
     <linearGradient
@@ -906,7 +910,7 @@
              style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
              id="tspan4092-8-7-6-9-7"
              y="855.79816"
-             x="460.18405">****</tspan></text>
+             x="460.18405">***</tspan></text>
       </g>
     </g>
     <text
@@ -1132,161 +1136,126 @@
            id="tspan4092-8-6-3-1-8-4-4-55-7"
            style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
     </g>
+    <text
+       x="424.10629"
+       y="363.21423"
+       id="text4090-8"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="363.21423"
+         id="tspan4092-8"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
+    <text
+       x="424.10629"
+       y="393.60123"
+       id="text4090-8-5"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="393.60123"
+         id="tspan4092-8-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
+    <text
+       x="424.10629"
+       y="424.20575"
+       id="text4090-8-5-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="424.20575"
+         id="tspan4092-8-5-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
+    <text
+       x="424.10629"
+       y="453.10339"
+       id="text4090-8-5-6-9-4"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="453.10339"
+         id="tspan4092-8-5-5-3-4"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
+    <text
+       x="424.10629"
+       y="514.09497"
+       id="text4090-8-5-6-9-4-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="514.09497"
+         id="tspan4092-8-5-5-3-4-0"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
+    <text
+       x="425.12708"
+       y="544.91718"
+       id="text4090-8-5-6-9-4-6-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="425.12708"
+         y="544.91718"
+         id="tspan4092-8-5-5-3-4-0-6"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
-       id="g3605">
-      <text
-         x="42.176418"
-         y="1020.4383"
-         id="text4090-8-7-8-7-6-3-8-4"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="42.176418"
-           y="1020.4383"
-           id="tspan4092-8-6-3-1-8-4-4-55"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
-      <text
-         x="30.942892"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="30.942892"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-      <text
-         x="25.247679"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="25.247679"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-    </g>
-    <g
-       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
-       id="g3275">
+       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
+       id="g3334">
       <g
-         id="g3341">
+         id="g3267"
+         transform="translate(-13.517932,3.1531035)">
         <text
-           x="394.78601"
-           y="390.17807"
-           id="text4090-8"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="390.17807"
-             id="tspan4092-8"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
-        <text
-           x="394.78601"
-           y="420.24835"
-           id="text4090-8-5"
+           x="660.46729"
+           y="468.01297"
+           id="text4090-8-1-8-9-1-4-1"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="420.24835"
-             id="tspan4092-8-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
-        <text
-           x="394.78601"
-           y="450.53394"
-           id="text4090-8-5-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="450.53394"
-             id="tspan4092-8-5-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
-        <text
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="660.46729"
+             y="468.01297"
+             id="tspan4092-8-7-6-9-7-0-7"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+      </g>
+      <text
+         x="394.78601"
+         y="483.59955"
+         id="text4090-8-5-6-9"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="513.13031"
-           id="text4090-8-5-6-9-4"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="513.13031"
-             id="tspan4092-8-5-5-3-4"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
-        <text
+           y="483.59955"
+           id="tspan4092-8-5-5-3"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+    </g>
+    <g
+       id="g3428"
+       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
+      <text
+         x="394.78601"
+         y="541.38928"
+         id="text4090-8-5-6-9-4-6-1"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="573.48621"
-           id="text4090-8-5-6-9-4-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="573.48621"
-             id="tspan4092-8-5-5-3-4-0"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
+           y="541.38928"
+           id="tspan4092-8-5-5-3-4-0-7"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
+      <g
+         transform="translate(-119.92979,57.949844)"
+         id="g3267-9">
         <text
-           x="395.79617"
-           y="603.98718"
-           id="text4090-8-5-6-9-4-6-6"
+           x="628.93628"
+           y="473.13675"
+           id="text4090-8-1-8-9-1-4-1-4"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="395.79617"
-             y="603.98718"
-             id="tspan4092-8-5-5-3-4-0-6"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
-        <g
-           transform="translate(0,-0.83470152)"
-           id="g3334">
-          <g
-             id="g3267"
-             transform="translate(-13.517932,3.1531035)">
-            <text
-               x="660.46729"
-               y="468.01297"
-               id="text4090-8-1-8-9-1-4-1"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="660.46729"
-                 y="468.01297"
-                 id="tspan4092-8-7-6-9-7-0-7"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
-          </g>
-          <text
-             x="394.78601"
-             y="483.59955"
-             id="text4090-8-5-6-9"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="483.59955"
-               id="tspan4092-8-5-5-3"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
-        </g>
-        <g
-           id="g3428"
-           transform="translate(0,0.88137813)">
-          <text
-             x="394.78601"
-             y="541.38928"
-             id="text4090-8-5-6-9-4-6-1"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="541.38928"
-               id="tspan4092-8-5-5-3-4-0-7"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
-          <g
-             transform="translate(-119.92979,57.949844)"
-             id="g3267-9">
-            <text
-               x="628.93628"
-               y="473.13675"
-               id="text4090-8-1-8-9-1-4-1-4"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="628.93628"
-                 y="473.13675"
-                 id="tspan4092-8-7-6-9-7-0-7-8"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
-          </g>
-        </g>
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="628.93628"
+             y="473.13675"
+             id="tspan4092-8-7-6-9-7-0-7-8"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
       </g>
     </g>
     <text
@@ -1301,7 +1270,7 @@
          id="tspan4092-8-5-5-3-4-0-6-2-11-0"
          style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3595">
       <text
          x="30.942892"
@@ -1332,7 +1301,7 @@
            x="19.552465"
            y="1037.0271"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
       <text
          x="42.830166"
          y="1033.2393"
@@ -1345,7 +1314,7 @@
            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
     </g>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3619">
       <text
          x="42.212418"
@@ -1396,7 +1365,7 @@
            x="14.016749"
            y="1049.8527"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
     </g>
     <rect
        width="196.44218"
diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index d21ee288b2..8ad6b6e715 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
 
   * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
 
-* New external functions should be added to the local ``version.map`` file. See
-  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
-  guides. New external functions should also be added in alphabetical order.
+* New external functions should be exported.
+  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
+  guides.
 
 * Any new API function should be used in ``/app`` test directory.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index 5368d38363..15a4991abf 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -5,8 +5,6 @@ if is_ms_compiler
     subdir_done()
 endif
 
-fs = import('fs')
-
 # Defines the order of dependencies evaluation
 subdirs = [
         'common',
@@ -290,57 +288,25 @@ foreach subpath:subdirs
                 install: true)
 
         # now build the shared driver
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
-
-        if not fs.is_file(version_map)
-            if is_ms_linker
-                link_mode = 'mslinker'
-            elif is_windows
-                link_mode = 'mingw'
-            else
-                link_mode = 'gnu'
-            endif
-            version_map = custom_target(lib_name + '_map',
-                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                    input: sources + sources_avx2 + sources_avx512,
-                    output: '_'.join([class, name, 'exports.map']))
-            version_map_path = version_map.full_path()
-            version_map_dep = [version_map]
-            lk_deps = [version_map]
-
-            if is_ms_linker and is_ms_compiler
-                lk_args = ['/def:' + version_map.full_path()]
-            elif is_ms_linker
-                lk_args = ['-Wl,/def:' + version_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-            endif
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
         else
-            version_map_path = version_map
-            version_map_dep = []
-            lk_deps = [version_map]
-
-            if is_windows
-                if is_ms_linker
-                    def_file = custom_target(lib_name + '_def',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_exports.def'.format(lib_name))
-                    lk_deps += [def_file]
-
-                    lk_args = ['-Wl,/def:' + def_file.full_path()]
-                else
-                    mingw_map = custom_target(lib_name + '_mingw',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_mingw.map'.format(lib_name))
-                    lk_deps += [mingw_map]
-
-                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-                endif
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(lib_name + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources + sources_avx2 + sources_avx512,
+                output: '_'.join([class, name, 'exports.map']))
+        lk_deps = [version_map]
+
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
+        else
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
 
         if not is_windows and developer_mode
@@ -348,11 +314,11 @@ foreach subpath:subdirs
             # check-symbols.sh script, using it as a
             # dependency of the .so build
             lk_deps += custom_target(lib_name + '.sym_chk',
-                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
                     capture: true,
                     input: static_lib,
                     output: lib_name + '.sym_chk',
-                    depends: version_map_dep)
+                    depends: [version_map])
         endif
 
         shared_lib = shared_library(lib_name, sources_pmd_info,
diff --git a/lib/meson.build b/lib/meson.build
index a9cbea5fea..6157d0e13e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
-fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -289,59 +288,25 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
-        if is_ms_linker
-            link_mode = 'mslinker'
-        elif is_windows
-            link_mode = 'mingw'
-        else
-            link_mode = 'gnu'
-        endif
-        version_map = custom_target(libname + '_map',
-                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                input: sources,
-                output: '_'.join([name, 'exports.map']))
-        version_map_path = version_map.full_path()
-        version_map_dep = [version_map]
-        lk_deps = [version_map]
-
-        if is_ms_linker and is_ms_compiler
-            lk_args = ['/def:' + version_map.full_path()]
-        elif is_ms_linker
-            lk_args = ['-Wl,/def:' + version_map.full_path()]
-        else
-            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-        endif
+    if is_ms_linker
+        link_mode = 'mslinker'
+    elif is_windows
+        link_mode = 'mingw'
     else
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-        version_map_path = version_map
-        version_map_dep = []
-        lk_deps = [version_map]
-        if is_ms_linker
-            def_file = custom_target(libname + '_def',
-                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                    input: version_map,
-                    output: '@0@_exports.def'.format(libname))
-            lk_deps += [def_file]
-
-            if is_ms_compiler
-                lk_args = ['/def:' + def_file.full_path()]
-            else
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
-            endif
-        else
-            if is_windows
-                mingw_map = custom_target(libname + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(libname))
-                lk_deps += [mingw_map]
+        link_mode = 'gnu'
+    endif
+    version_map = custom_target(libname + '_map',
+            command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+            input: sources,
+            output: '_'.join([name, 'exports.map']))
+    lk_deps = [version_map]
 
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
-        endif
+    if is_ms_linker and is_ms_compiler
+        lk_args = ['/def:' + version_map.full_path()]
+    elif is_ms_linker
+        lk_args = ['-Wl,/def:' + version_map.full_path()]
+    else
+        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
     endif
 
     if developer_mode and not is_windows
@@ -349,11 +314,11 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols, version_map_path, '@INPUT@'],
+                command: [check_symbols, version_map.full_path(), '@INPUT@'],
                 capture: true,
                 input: static_lib,
                 output: name + '.sym_chk',
-                depends: version_map_dep)
+                depends: [version_map])
     endif
 
     if not use_function_versioning or is_windows
-- 
2.48.1


^ permalink raw reply	[relevance 16%]

* [PATCH v5 8/8] eal: rework function versioning macros
  2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
  2025-03-27 13:36 17%   ` [PATCH v5 4/8] build: generate symbol maps David Marchand
  2025-03-27 13:36 16%   ` [PATCH v5 6/8] build: use dynamically generated version maps David Marchand
@ 2025-03-27 13:36 20%   ` David Marchand
  2025-03-27 18:22  0%   ` [PATCH v5 0/8] Symbol versioning and export rework David Marchand
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-27 13:36 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
exporting and versioning symbols that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a symbol is just a call to a single macro:
- RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
  keeping an old implementation code under a versioned function (resp.
  experimental function),
- RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

This replaces the macros from rte_function_versioning.h that were
previously publicly exported.

Update lib/net accordingly.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v4:
- moved new macros to eal_symbol_exports.h and simply dropped legacy
  macros/header,
- added release notes update,

Changes since RFC v3:
- fixed documentation and simplified examples,

Changes since RFC v1:
- renamed and prefixed macros,
- reindented in prevision of second patch,

---
 buildtools/gen-version-map.py              |  15 +-
 devtools/check-symbol-change.py            |   8 +-
 doc/api/doxy-api-index.md                  |   1 -
 doc/guides/contributing/abi_versioning.rst | 192 ++++++---------------
 doc/guides/rel_notes/release_25_07.rst     |   2 +
 lib/eal/common/eal_symbol_exports.h        |  66 +++++++
 lib/eal/include/rte_function_versioning.h  |  99 -----------
 lib/net/net_crc.h                          |  15 --
 lib/net/rte_net_crc.c                      |  29 +---
 9 files changed, 135 insertions(+), 292 deletions(-)
 delete mode 100644 lib/eal/include/rte_function_versioning.h

diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
index c7dfc9b8c2..7ee80ec640 100755
--- a/buildtools/gen-version-map.py
+++ b/buildtools/gen-version-map.py
@@ -13,10 +13,9 @@
 export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
 export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
 export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
-# From rte_function_versioning.h
-ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
-ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
-default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
 
 with open(abi_version_file) as f:
     abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
@@ -40,14 +39,14 @@
                 node = abi
                 symbol = export_sym_regexp.match(ln).group(1)
             elif ver_sym_regexp.match(ln):
-                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(2))
-                symbol = ver_sym_regexp.match(ln).group(1)
+                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
+                symbol = ver_sym_regexp.match(ln).group(2)
             elif ver_exp_sym_regexp.match(ln):
                 node = 'EXPERIMENTAL'
                 symbol = ver_exp_sym_regexp.match(ln).group(1)
             elif default_sym_regexp.match(ln):
-                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(2))
-                symbol = default_sym_regexp.match(ln).group(1)
+                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
+                symbol = default_sym_regexp.match(ln).group(2)
 
             if not symbol:
                 continue
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
index d522fbb1ec..d59ecaddd7 100755
--- a/devtools/check-symbol-change.py
+++ b/devtools/check-symbol-change.py
@@ -12,10 +12,10 @@
 export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
 export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
 export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
-# TODO, handle versioned symbols from rte_function_versioning.h
-# ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
-# ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
-# default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+# TODO, handle versioned symbols
+# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
 
 symbols = {}
 
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..5c425a2cb9 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -248,7 +248,6 @@ The public API headers are grouped by topics:
   [EAL config](@ref rte_eal.h),
   [common](@ref rte_common.h),
   [experimental APIs](@ref rte_compat.h),
-  [ABI versioning](@ref rte_function_versioning.h),
   [version](@ref rte_version.h)
 
 - **tests**:
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index b6b6bf64d7..a0ce0ce1c9 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -132,32 +132,25 @@ functionality or behavior. When that occurs, it is may be required to allow for
 backward compatibility for a time with older binaries that are dynamically
 linked to the DPDK.
 
-To support backward compatibility the ``rte_function_versioning.h``
+To support backward compatibility the ``eal_symbol_exports.h``
 header file provides macros to use when updating exported functions. These
 macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
-The macros exported are:
+The macros are:
 
-* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
-  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
+* ``RTE_VERSION_SYMBOL(ver, type, name, args)``: Creates a symbol version table
+  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
+  ``<name>_v<ver>``.
 
-* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
-  the linker to bind references to symbol ``b`` to the internal symbol
-  ``be``.
+* ``RTE_DEFAULT_SYMBOL(ver, type, name, args)``: Creates a symbol version entry
+  instructing the linker to bind references to symbol ``<name>`` to the internal
+  symbol ``<name>_v<ver>``.
 
-* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
-  fully qualified function ``p``, so that if a symbol becomes versioned, it
-  can still be mapped back to the public symbol name.
-
-* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
-  ``be`` to signal that it is being used as an implementation of a particular
-  version of symbol ``b``.
-
-* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
-  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
-  The macro is used when a symbol matures to become part of the stable ABI, to
-  provide an alias to experimental until the next major ABI version.
+* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
+  but for experimental API symbols. The macro is used when a symbol matures
+  to become part of the stable ABI, to provide an alias to experimental
+  until the next major ABI version.
 
 .. _example_abi_macro_usage:
 
@@ -176,8 +169,8 @@ Assume we have a function as follows
   * manipulate
   */
  RTE_EXPORT_SYMBOL(rte_acl_create)
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param)
+ int
+ rte_acl_create(struct rte_acl_param *param)
  {
         ...
  }
@@ -195,8 +188,8 @@ private, is safe), but it also requires modifying the code as follows
   * manipulate
   */
  RTE_EXPORT_SYMBOL(rte_acl_create)
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param, int debug)
+ int
+ rte_acl_create(struct rte_acl_param *param, int debug)
  {
         ...
  }
@@ -215,87 +208,48 @@ application was linked to it.
 
 We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
+the function return type, the function name and its arguments.
 
 .. code-block:: c
 
  -RTE_EXPORT_SYMBOL(rte_acl_create)
- -struct rte_acl_ctx *
- -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ -int
+ -rte_acl_create(struct rte_acl_param *param)
+ +RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
 
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
-
-Remembering to also add the rte_function_versioning.h header to the requisite c
-file where these changes are being made. The macro instructs the linker to
-create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+The macro instructs the linker to create a new symbol ``rte_acl_create@DPDK_21``,
+which matches the symbol created in older builds,
+but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
    {
-        struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
+        int ret = rte_acl_create_v21(param);
 
-        ctx->debug = debug;
+        if (debug) {
+        ...
+        }
 
-        return ctx;
+        return ret;
    }
 
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
-
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
 an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
@@ -304,43 +258,10 @@ used by newly built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -384,8 +305,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     */
    RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
    ...
    }
@@ -400,8 +321,8 @@ When we promote the symbol to the stable ABI, we simply strip the
     * manipulate
     */
    RTE_EXPORT_SYMBOL(rte_acl_create)
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
           ...
    }
@@ -416,33 +337,20 @@ and ``DPDK_22`` version nodes.
 .. code-block:: c
 
    #include <rte_compat.h>;
-   #include <rte_function_versioning.h>
 
    /*
     * Create an acl context object for apps to
     * manipulate
     */
-   RTE_EXPORT_SYMBOL(rte_acl_create)
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param))
    {
    ...
    }
 
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
-
-   struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_acl_create, (struct rte_acl_param *param))
    {
       return rte_acl_create(param);
    }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
 .. _abi_deprecation:
 
@@ -458,10 +366,10 @@ Next remove the corresponding versioned export.
 
 .. code-block:: c
 
- -VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
 
 
-Note that the internal function definition could also be removed, but its used
+Note that the internal function definition must also be removed, but it is used
 in our example by the newer version ``v22``, so we leave it in place and declare
 it as static. This is a coding style choice.
 
@@ -476,18 +384,16 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
- };
-
-Any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
 .. code-block:: c
 
- -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21);
- +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+ -RTE_DEFAULT_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param, int debug))
+ +RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
 
-Lastly, any VERSION_SYMBOL macros that point to the old version nodes
+Lastly, any RTE_VERSION_SYMBOL macros that point to the old version nodes
 should be removed, taking care to preserve any code that is shared
 with the new version node.
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..093b85d206 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -68,6 +68,8 @@ Removed Items
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* eal: Removed the ``rte_function_versioning.h`` header from the exported headers.
+
 
 API Changes
 -----------
diff --git a/lib/eal/common/eal_symbol_exports.h b/lib/eal/common/eal_symbol_exports.h
index b3033dd336..b66ca8fa68 100644
--- a/lib/eal/common/eal_symbol_exports.h
+++ b/lib/eal/common/eal_symbol_exports.h
@@ -5,6 +5,8 @@
 #ifndef EAL_SYMBOL_EXPORTS_H
 #define EAL_SYMBOL_EXPORTS_H
 
+#include <rte_common.h>
+
 /* Internal macros for exporting symbols, used by the build system.
  * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
  * version this symbol was introduced in.
@@ -13,4 +15,68 @@
 #define RTE_EXPORT_INTERNAL_SYMBOL(a)
 #define RTE_EXPORT_SYMBOL(a)
 
+#if !defined(RTE_USE_FUNCTION_VERSIONING) && (defined(RTE_CC_GCC) || defined(RTE_CC_CLANG))
+#define VERSIONING_WARN RTE_PRAGMA_WARNING(Use of function versioning disabled. \
+	Is "use_function_versioning=true" in meson.build?)
+#else
+#define VERSIONING_WARN
+#endif
+
+/*
+ * Provides backwards compatibility when updating exported functions.
+ * When a symbol is exported from a library to provide an API, it also provides a
+ * calling convention (ABI) that is embodied in its name, return type,
+ * arguments, etc.  On occasion that function may need to change to accommodate
+ * new functionality, behavior, etc.  When that occurs, it is desirable to
+ * allow for backwards compatibility for a time with older binaries that are
+ * dynamically linked to the dpdk.
+ */
+
+#ifdef RTE_BUILD_SHARED_LIB
+
+/*
+ * RTE_VERSION_SYMBOL
+ * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
+ * function name <name>_v<ver>.
+ */
+#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+/*
+ * RTE_VERSION_EXPERIMENTAL_SYMBOL
+ * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
+ * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
+ */
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
+__rte_used type name ## _exp args; \
+type name ## _exp args
+
+/*
+ * RTE_DEFAULT_SYMBOL
+ * Creates a symbol version entry instructing the linker to bind references to
+ * symbol <name> to the internal symbol <name>_v<ver>.
+ */
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+#else /* !RTE_BUILD_SHARED_LIB */
+
+#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
+type name ## _exp args; \
+type name ## _exp args
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+type name args
+
+#endif /* RTE_BUILD_SHARED_LIB */
+
 #endif /* EAL_SYMBOL_EXPORTS_H */
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
deleted file mode 100644
index eb6dd2bc17..0000000000
--- a/lib/eal/include/rte_function_versioning.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2015 Neil Horman <nhorman@tuxdriver.com>.
- * All rights reserved.
- */
-
-#ifndef _RTE_FUNCTION_VERSIONING_H_
-#define _RTE_FUNCTION_VERSIONING_H_
-#include <rte_common.h>
-
-#ifndef RTE_USE_FUNCTION_VERSIONING
-#error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
-#endif
-
-#ifdef RTE_BUILD_SHARED_LIB
-
-/*
- * Provides backwards compatibility when updating exported functions.
- * When a symbol is exported from a library to provide an API, it also provides a
- * calling convention (ABI) that is embodied in its name, return type,
- * arguments, etc.  On occasion that function may need to change to accommodate
- * new functionality, behavior, etc.  When that occurs, it is desirable to
- * allow for backwards compatibility for a time with older binaries that are
- * dynamically linked to the dpdk.  To support that, the __vsym and
- * VERSION_SYMBOL macros are created.  They, in conjunction with the
- * version.map file for a given library allow for multiple versions of
- * a symbol to exist in a shared library so that older binaries need not be
- * immediately recompiled.
- *
- * Refer to the guidelines document in the docs subdirectory for details on the
- * use of these macros
- */
-
-/*
- * Macro Parameters:
- * b - function base name
- * e - function version extension, to be concatenated with base name
- * n - function symbol version string to be applied
- * f - function prototype
- * p - full function symbol name
- */
-
-/*
- * VERSION_SYMBOL
- * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
- * function name <b><e>
- */
-#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
-
-/*
- * VERSION_SYMBOL_EXPERIMENTAL
- * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
- * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
- * to provide an alias to experimental for some time.
- */
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
-
-/*
- * BIND_DEFAULT_SYMBOL
- * Creates a symbol version entry instructing the linker to bind references to
- * symbol <b> to the internal symbol <b><e>
- */
-#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
-
-/*
- * __vsym
- * Annotation to be used in declaration of the internal symbol <b><e> to signal
- * that it is being used as an implementation of a particular version of symbol
- * <b>.
- */
-#define __vsym __rte_used
-
-/*
- * MAP_STATIC_SYMBOL
- * If a function has been bifurcated into multiple versions, none of which
- * are defined as the exported symbol name in the map file, this macro can be
- * used to alias a specific version of the symbol to its exported name.  For
- * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
- * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
- * building a shared library, this macro can be used to map either foo_v1 or
- * foo_v2 to the symbol foo when building a static library, e.g.:
- * MAP_STATIC_SYMBOL(void foo(), foo_v2);
- */
-#define MAP_STATIC_SYMBOL(f, p)
-
-#else
-/*
- * No symbol versioning in use
- */
-#define VERSION_SYMBOL(b, e, n)
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
-#define __vsym
-#define BIND_DEFAULT_SYMBOL(b, e, n)
-#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
-/*
- * RTE_BUILD_SHARED_LIB=n
- */
-#endif
-
-#endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 4930e2f0b3..320b0edca8 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -7,21 +7,6 @@
 
 #include "rte_net_crc.h"
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
-
-struct rte_net_crc *
-rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len, enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len);
-
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 4efb095bc4..e769c7e22e 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -11,7 +11,6 @@
 #include <rte_net_crc.h>
 #include <rte_log.h>
 #include <rte_vect.h>
-#include <rte_function_versioning.h>
 #include <rte_malloc.h>
 
 #include "net_crc.h"
@@ -346,8 +345,7 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+RTE_VERSION_SYMBOL(25, void, rte_net_crc_set_alg, (enum rte_net_crc_alg alg))
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -374,10 +372,9 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+RTE_DEFAULT_SYMBOL(26, struct rte_net_crc *, rte_net_crc_set_alg, (enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type))
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -415,10 +412,6 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 	}
 	return crc;
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
 
 RTE_EXPORT_SYMBOL(rte_net_crc_free)
 void rte_net_crc_free(struct rte_net_crc *crc)
@@ -426,10 +419,8 @@ void rte_net_crc_free(struct rte_net_crc *crc)
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
+	enum rte_net_crc_type type))
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -439,18 +430,12 @@ rte_net_crc_calc_v25(const void *data,
 
 	return ret;
 }
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+RTE_DEFAULT_SYMBOL(26, uint32_t, rte_net_crc_calc, (const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len))
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.48.1


^ permalink raw reply	[relevance 20%]

* Re: [PATCH v5 0/8] Symbol versioning and export rework
  2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
                     ` (2 preceding siblings ...)
  2025-03-27 13:36 20%   ` [PATCH v5 8/8] eal: rework function versioning macros David Marchand
@ 2025-03-27 18:22  0%   ` David Marchand
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-03-27 18:22 UTC (permalink / raw)
  To: bruce.richardson, Mcnamara, John; +Cc: thomas, andremue, dev

On Thu, Mar 27, 2025 at 2:37 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.

Intel CI keeps on spewing non sense failures on doc generation, as it
filters out doc/ updates from patches...


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* [PATCH v6 0/8] Symbol versioning and export rework
  2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
                   ` (3 preceding siblings ...)
  2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
@ 2025-03-28 10:52  3% ` David Marchand
  2025-03-28 10:52 17%   ` [PATCH v6 4/8] build: generate symbol maps David Marchand
                     ` (2 more replies)
  2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
  5 siblings, 3 replies; 153+ results
From: David Marchand @ 2025-03-28 10:52 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

So far, each DPDK library (or driver) exposing symbols in an ABI had to
maintain a version.map and use some macros for symbol versioning,
specially crafted with the GNU linker in mind.

This series proposes to rework the whole principle, and instead rely on
marking the symbol exports in the source code itself, then let it to the
build framework to produce a version script adapted to the linker in use
(think GNU linker vs MSVC linker).

This greatly simplifies versioning symbols: a developer does not need to
know anything about version.map, or that a versioned symbol must be
renamed with _v26, annotated with __vsym, exported in a header etc...

Checking symbol maps becomes unnecessary since generated by the build
framework.

Updating to a new ABI is just a matter of bumping the value in
ABI_VERSION.



-- 
David Marchand

Changes since v5:
- fixed Windows symbol exports for net/mlx5,

Changes since RFC v4:
- rebased on main, now that Bruce series is merged,
- the export macros header has been moved to lib/eal/common/
  and its inclusion is now mandatory (rather than an implicit -include),
- reordered patches: symbol versioning is touched last and merged
  in the export header (replacing the legacy rte_function_versioning.h),

Changes since RFC v3:
- fixed/simplified documentation,
- rebased on top of Bruce series for common handling of AVX sources,

Changes since RFC v2:
- updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
  signature is enclosed in the macro,
- dropped invalid exports for some dead symbols or inline helpers,
- updated documentation and tooling,
- converted the whole tree (via a local script of mine),

David Marchand (8):
  lib: remove incorrect exported symbols
  drivers: remove incorrect exported symbols
  buildtools: display version when listing symbols
  build: generate symbol maps
  build: mark exported symbols
  build: use dynamically generated version maps
  build: remove static version maps
  eal: rework function versioning macros

 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   9 +-
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/gen-version-map.py                 | 105 ++++
 buildtools/map-list-symbol.sh                 |  15 +-
 buildtools/map_to_win.py                      |  41 --
 buildtools/meson.build                        |   2 +-
 devtools/check-spdx-tag.sh                    |   2 +-
 devtools/check-symbol-change.py               |  90 +++
 devtools/check-symbol-change.sh               | 186 ------
 devtools/check-symbol-maps.sh                 | 115 ----
 devtools/checkpatches.sh                      |   4 +-
 devtools/update-abi.sh                        |  46 --
 devtools/update_version_map_abi.py            | 210 -------
 doc/api/doxy-api-index.md                     |   1 -
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/abi_versioning.rst    | 415 +++----------
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 +++++----
 doc/guides/contributing/patches.rst           |   6 +-
 doc/guides/rel_notes/release_25_07.rst        |   2 +
 drivers/baseband/acc/rte_acc100_pmd.c         |   2 +
 drivers/baseband/acc/version.map              |  10 -
 .../fpga_5gnr_fec/rte_fpga_5gnr_fec.c         |   2 +
 drivers/baseband/fpga_5gnr_fec/version.map    |  11 -
 drivers/baseband/fpga_lte_fec/fpga_lte_fec.c  |   2 +
 drivers/baseband/fpga_lte_fec/version.map     |  10 -
 drivers/bus/auxiliary/auxiliary_common.c      |   3 +
 drivers/bus/auxiliary/version.map             |   8 -
 drivers/bus/cdx/cdx.c                         |   5 +
 drivers/bus/cdx/cdx_vfio.c                    |   5 +
 drivers/bus/cdx/version.map                   |  14 -
 drivers/bus/dpaa/dpaa_bus.c                   |  10 +
 drivers/bus/dpaa/dpaa_bus_symbols.c           |  99 +++
 drivers/bus/dpaa/meson.build                  |   1 +
 drivers/bus/dpaa/version.map                  | 109 ----
 drivers/bus/fslmc/fslmc_bus.c                 |   5 +
 drivers/bus/fslmc/fslmc_vfio.c                |  13 +
 drivers/bus/fslmc/mc/dpbp.c                   |   8 +
 drivers/bus/fslmc/mc/dpci.c                   |   5 +
 drivers/bus/fslmc/mc/dpcon.c                  |   8 +
 drivers/bus/fslmc/mc/dpdmai.c                 |  10 +
 drivers/bus/fslmc/mc/dpio.c                   |  15 +
 drivers/bus/fslmc/mc/dpmng.c                  |   4 +
 drivers/bus/fslmc/mc/mc_sys.c                 |   2 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpbp.c      |   4 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpci.c      |   3 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpio.c      |  12 +
 drivers/bus/fslmc/qbman/qbman_debug.c         |   4 +
 drivers/bus/fslmc/qbman/qbman_portal.c        |  43 ++
 drivers/bus/fslmc/version.map                 | 129 ----
 drivers/bus/ifpga/ifpga_bus.c                 |   4 +
 drivers/bus/ifpga/version.map                 |   9 -
 drivers/bus/pci/bsd/pci.c                     |  11 +
 drivers/bus/pci/linux/pci.c                   |  11 +
 drivers/bus/pci/pci_common.c                  |  11 +
 drivers/bus/pci/version.map                   |  43 --
 drivers/bus/pci/windows/pci.c                 |  11 +
 drivers/bus/platform/platform.c               |   3 +
 drivers/bus/platform/version.map              |  10 -
 drivers/bus/uacce/uacce.c                     |  10 +
 drivers/bus/uacce/version.map                 |  15 -
 drivers/bus/vdev/vdev.c                       |   7 +
 drivers/bus/vdev/version.map                  |  17 -
 drivers/bus/vmbus/linux/vmbus_bus.c           |   7 +
 drivers/bus/vmbus/version.map                 |  33 -
 drivers/bus/vmbus/vmbus_channel.c             |  14 +
 drivers/bus/vmbus/vmbus_common.c              |   4 +
 drivers/common/cnxk/cnxk_security.c           |  13 +
 drivers/common/cnxk/cnxk_utils.c              |   2 +
 drivers/common/cnxk/meson.build               |   1 +
 drivers/common/cnxk/roc_platform.c            |  19 +
 drivers/common/cnxk/roc_platform_symbols.c    | 545 +++++++++++++++++
 drivers/common/cnxk/roc_se.h                  |   1 -
 drivers/common/cnxk/version.map               | 578 ------------------
 drivers/common/cpt/cpt_fpm_tables.c           |   3 +
 drivers/common/cpt/cpt_pmd_ops_helper.c       |   4 +
 drivers/common/cpt/version.map                |  11 -
 drivers/common/dpaax/caamflib.c               |   3 +
 drivers/common/dpaax/dpaa_of.c                |  13 +
 drivers/common/dpaax/dpaax_iova_table.c       |   7 +
 drivers/common/dpaax/version.map              |  25 -
 drivers/common/ionic/ionic_common_uio.c       |   5 +
 drivers/common/ionic/version.map              |  10 -
 .../common/mlx5/linux/mlx5_common_auxiliary.c |   2 +
 drivers/common/mlx5/linux/mlx5_common_os.c    |  11 +
 drivers/common/mlx5/linux/mlx5_common_verbs.c |   4 +
 drivers/common/mlx5/linux/mlx5_glue.c         |   3 +
 drivers/common/mlx5/linux/mlx5_nl.c           |  22 +
 drivers/common/mlx5/mlx5_common.c             |  10 +
 drivers/common/mlx5/mlx5_common_devx.c        |  10 +
 drivers/common/mlx5/mlx5_common_mp.c          |   9 +
 drivers/common/mlx5/mlx5_common_mr.c          |  12 +
 drivers/common/mlx5/mlx5_common_pci.c         |   3 +
 drivers/common/mlx5/mlx5_common_utils.c       |  12 +
 drivers/common/mlx5/mlx5_devx_cmds.c          |  52 ++
 drivers/common/mlx5/mlx5_malloc.c             |   5 +
 drivers/common/mlx5/version.map               | 175 ------
 drivers/common/mlx5/windows/mlx5_common_os.c  |   7 +
 drivers/common/mlx5/windows/mlx5_glue.c       |   4 +-
 drivers/common/mvep/mvep_common.c             |   3 +
 drivers/common/mvep/version.map               |   8 -
 drivers/common/nfp/nfp_common.c               |   9 +
 drivers/common/nfp/nfp_common_pci.c           |   2 +
 drivers/common/nfp/nfp_dev.c                  |   2 +
 drivers/common/nfp/version.map                |  16 -
 drivers/common/nitrox/nitrox_device.c         |   2 +
 drivers/common/nitrox/nitrox_logs.c           |   2 +
 drivers/common/nitrox/nitrox_qp.c             |   3 +
 drivers/common/nitrox/version.map             |  10 -
 drivers/common/octeontx/octeontx_mbox.c       |   7 +
 drivers/common/octeontx/version.map           |  12 -
 drivers/common/sfc_efx/meson.build            |   1 +
 drivers/common/sfc_efx/sfc_efx.c              |   3 +
 drivers/common/sfc_efx/sfc_efx_mcdi.c         |   3 +
 drivers/common/sfc_efx/sfc_symbols.c          | 275 +++++++++
 drivers/common/sfc_efx/version.map            | 302 ---------
 drivers/crypto/cnxk/cn10k_cryptodev_ops.c     |   8 +
 drivers/crypto/cnxk/cn9k_cryptodev_ops.c      |   3 +
 drivers/crypto/cnxk/cnxk_cryptodev_ops.c      |   8 +
 drivers/crypto/cnxk/version.map               |  30 -
 drivers/crypto/dpaa2_sec/dpaa2_sec_dpseci.c   |   3 +
 drivers/crypto/dpaa2_sec/version.map          |   8 -
 drivers/crypto/dpaa_sec/dpaa_sec.c            |   3 +
 drivers/crypto/dpaa_sec/version.map           |   8 -
 drivers/crypto/octeontx/otx_cryptodev_ops.c   |   3 +
 drivers/crypto/octeontx/version.map           |  12 -
 .../scheduler/rte_cryptodev_scheduler.c       |  11 +
 drivers/crypto/scheduler/version.map          |  16 -
 drivers/dma/cnxk/cnxk_dmadev_fp.c             |   5 +
 drivers/dma/cnxk/version.map                  |  10 -
 drivers/event/cnxk/cnxk_worker.c              |   3 +
 drivers/event/cnxk/version.map                |  11 -
 drivers/event/dlb2/rte_pmd_dlb2.c             |   2 +
 drivers/event/dlb2/version.map                |  10 -
 drivers/mempool/cnxk/cn10k_hwpool_ops.c       |   4 +
 drivers/mempool/cnxk/version.map              |  12 -
 drivers/mempool/dpaa/dpaa_mempool.c           |   3 +
 drivers/mempool/dpaa/version.map              |   8 -
 drivers/mempool/dpaa2/dpaa2_hw_mempool.c      |   6 +
 drivers/mempool/dpaa2/version.map             |  16 -
 drivers/meson.build                           |  74 +--
 drivers/net/atlantic/rte_pmd_atlantic.c       |   7 +
 drivers/net/atlantic/version.map              |  15 -
 drivers/net/bnxt/rte_pmd_bnxt.c               |  17 +
 drivers/net/bnxt/version.map                  |  22 -
 drivers/net/bonding/rte_eth_bond_8023ad.c     |  13 +
 drivers/net/bonding/rte_eth_bond_api.c        |  16 +
 drivers/net/bonding/version.map               |  33 -
 drivers/net/cnxk/cnxk_ethdev.c                |   4 +
 drivers/net/cnxk/cnxk_ethdev_sec.c            |  10 +
 drivers/net/cnxk/version.map                  |  27 -
 drivers/net/dpaa/dpaa_ethdev.c                |   4 +
 drivers/net/dpaa/version.map                  |  14 -
 drivers/net/dpaa2/dpaa2_ethdev.c              |   8 +
 drivers/net/dpaa2/dpaa2_mux.c                 |   4 +
 drivers/net/dpaa2/dpaa2_rxtx.c                |   2 +
 drivers/net/dpaa2/dpaa2_symbols.c             |   8 +
 drivers/net/dpaa2/meson.build                 |   1 +
 drivers/net/dpaa2/version.map                 |  35 --
 drivers/net/intel/i40e/rte_pmd_i40e.c         |  40 ++
 drivers/net/intel/i40e/version.map            |  55 --
 drivers/net/intel/iavf/iavf_rxtx.c            |   9 +
 drivers/net/intel/iavf/iavf_symbols.c         |  13 +
 drivers/net/intel/iavf/meson.build            |   1 +
 drivers/net/intel/iavf/version.map            |  33 -
 drivers/net/intel/ice/ice_diagnose.c          |   4 +
 drivers/net/intel/ice/version.map             |  16 -
 drivers/net/intel/idpf/idpf_common_device.c   |  11 +
 drivers/net/intel/idpf/idpf_common_rxtx.c     |  25 +
 .../net/intel/idpf/idpf_common_rxtx_avx2.c    |   3 +
 .../net/intel/idpf/idpf_common_rxtx_avx512.c  |   6 +
 drivers/net/intel/idpf/idpf_common_virtchnl.c |  31 +
 drivers/net/intel/idpf/version.map            |  80 ---
 drivers/net/intel/ipn3ke/ipn3ke_ethdev.c      |   2 +
 drivers/net/intel/ipn3ke/version.map          |   9 -
 drivers/net/intel/ixgbe/rte_pmd_ixgbe.c       |  38 ++
 drivers/net/intel/ixgbe/version.map           |  49 --
 drivers/net/mlx5/mlx5.c                       |   2 +
 drivers/net/mlx5/mlx5_flow.c                  |   5 +
 drivers/net/mlx5/mlx5_rx.c                    |   3 +
 drivers/net/mlx5/mlx5_rxq.c                   |   3 +
 drivers/net/mlx5/mlx5_tx.c                    |   2 +
 drivers/net/mlx5/mlx5_txq.c                   |   4 +
 drivers/net/mlx5/version.map                  |  28 -
 drivers/net/octeontx/octeontx_ethdev.c        |   2 +
 drivers/net/octeontx/version.map              |   7 -
 drivers/net/ring/rte_eth_ring.c               |   3 +
 drivers/net/ring/version.map                  |   8 -
 drivers/net/softnic/rte_eth_softnic.c         |   2 +
 drivers/net/softnic/rte_eth_softnic_thread.c  |   2 +
 drivers/net/softnic/version.map               |   8 -
 drivers/net/vhost/rte_eth_vhost.c             |   3 +
 drivers/net/vhost/version.map                 |   8 -
 drivers/power/kvm_vm/guest_channel.c          |   3 +
 drivers/power/kvm_vm/version.map              |   8 -
 drivers/raw/cnxk_rvu_lf/cnxk_rvu_lf.c         |  11 +
 drivers/raw/cnxk_rvu_lf/version.map           |  16 -
 drivers/raw/ifpga/rte_pmd_ifpga.c             |  12 +
 drivers/raw/ifpga/version.map                 |  17 -
 drivers/version.map                           |   3 -
 lib/acl/acl_bld.c                             |   2 +
 lib/acl/acl_run_scalar.c                      |   3 +
 lib/acl/rte_acl.c                             |  12 +
 lib/acl/version.map                           |  19 -
 lib/argparse/rte_argparse.c                   |   3 +
 lib/argparse/version.map                      |   9 -
 lib/bbdev/bbdev_trace_points.c                |   3 +
 lib/bbdev/rte_bbdev.c                         |  32 +
 lib/bbdev/version.map                         |  47 --
 lib/bitratestats/rte_bitrate.c                |   5 +
 lib/bitratestats/version.map                  |  10 -
 lib/bpf/bpf.c                                 |   3 +
 lib/bpf/bpf_convert.c                         |   2 +
 lib/bpf/bpf_dump.c                            |   2 +
 lib/bpf/bpf_exec.c                            |   3 +
 lib/bpf/bpf_load.c                            |   2 +
 lib/bpf/bpf_load_elf.c                        |   2 +
 lib/bpf/bpf_pkt.c                             |   5 +
 lib/bpf/bpf_stub.c                            |   3 +
 lib/bpf/version.map                           |  18 -
 lib/cfgfile/rte_cfgfile.c                     |  18 +
 lib/cfgfile/version.map                       |  23 -
 lib/cmdline/cmdline.c                         |  10 +
 lib/cmdline/cmdline_cirbuf.c                  |  21 +
 lib/cmdline/cmdline_parse.c                   |   5 +
 lib/cmdline/cmdline_parse_bool.c              |   2 +
 lib/cmdline/cmdline_parse_etheraddr.c         |   4 +
 lib/cmdline/cmdline_parse_ipaddr.c            |   4 +
 lib/cmdline/cmdline_parse_num.c               |   4 +
 lib/cmdline/cmdline_parse_portlist.c          |   4 +
 lib/cmdline/cmdline_parse_string.c            |   6 +
 lib/cmdline/cmdline_rdline.c                  |  17 +
 lib/cmdline/cmdline_socket.c                  |   5 +
 lib/cmdline/cmdline_vt100.c                   |   4 +
 lib/cmdline/version.map                       |  82 ---
 lib/compressdev/rte_comp.c                    |   7 +
 lib/compressdev/rte_compressdev.c             |  26 +
 lib/compressdev/rte_compressdev_pmd.c         |   4 +
 lib/compressdev/version.map                   |  40 --
 lib/cryptodev/cryptodev_pmd.c                 |   8 +
 lib/cryptodev/cryptodev_trace_points.c        |   4 +
 lib/cryptodev/rte_cryptodev.c                 |  84 +++
 lib/cryptodev/version.map                     | 114 ----
 lib/dispatcher/rte_dispatcher.c               |  14 +
 lib/dispatcher/version.map                    |  20 -
 lib/distributor/rte_distributor.c             |  10 +
 lib/distributor/version.map                   |  15 -
 lib/dmadev/rte_dmadev.c                       |  20 +
 lib/dmadev/rte_dmadev_trace_points.c          |   8 +
 lib/dmadev/version.map                        |  47 --
 lib/eal/arm/rte_cpuflags.c                    |   4 +
 lib/eal/arm/rte_hypervisor.c                  |   2 +
 lib/eal/arm/rte_power_intrinsics.c            |   5 +
 lib/eal/common/eal_common_bus.c               |  11 +
 lib/eal/common/eal_common_class.c             |   5 +
 lib/eal/common/eal_common_config.c            |   8 +
 lib/eal/common/eal_common_cpuflags.c          |   2 +
 lib/eal/common/eal_common_debug.c             |   3 +
 lib/eal/common/eal_common_dev.c               |  20 +
 lib/eal/common/eal_common_devargs.c           |  10 +
 lib/eal/common/eal_common_errno.c             |   3 +
 lib/eal/common/eal_common_fbarray.c           |  27 +
 lib/eal/common/eal_common_hexdump.c           |   3 +
 lib/eal/common/eal_common_hypervisor.c        |   2 +
 lib/eal/common/eal_common_interrupts.c        |  28 +
 lib/eal/common/eal_common_launch.c            |   6 +
 lib/eal/common/eal_common_lcore.c             |  18 +
 lib/eal/common/eal_common_lcore_var.c         |   2 +
 lib/eal/common/eal_common_mcfg.c              |  21 +
 lib/eal/common/eal_common_memory.c            |  30 +
 lib/eal/common/eal_common_memzone.c           |  10 +
 lib/eal/common/eal_common_options.c           |   5 +
 lib/eal/common/eal_common_proc.c              |   9 +
 lib/eal/common/eal_common_string_fns.c        |   4 +
 lib/eal/common/eal_common_tailqs.c            |   4 +
 lib/eal/common/eal_common_thread.c            |  15 +
 lib/eal/common/eal_common_timer.c             |   5 +
 lib/eal/common/eal_common_trace.c             |  16 +
 lib/eal/common/eal_common_trace_ctf.c         |   2 +
 lib/eal/common/eal_common_trace_points.c      |  19 +
 lib/eal/common/eal_common_trace_utils.c       |   2 +
 lib/eal/common/eal_common_uuid.c              |   5 +
 lib/eal/common/eal_symbol_exports.h           |  82 +++
 lib/eal/common/rte_bitset.c                   |   2 +
 lib/eal/common/rte_keepalive.c                |   7 +
 lib/eal/common/rte_malloc.c                   |  23 +
 lib/eal/common/rte_random.c                   |   5 +
 lib/eal/common/rte_reciprocal.c               |   3 +
 lib/eal/common/rte_service.c                  |  32 +
 lib/eal/common/rte_version.c                  |   8 +
 lib/eal/freebsd/eal.c                         |  23 +
 lib/eal/freebsd/eal_alarm.c                   |   3 +
 lib/eal/freebsd/eal_dev.c                     |   5 +
 lib/eal/freebsd/eal_interrupts.c              |  20 +
 lib/eal/freebsd/eal_memory.c                  |   4 +
 lib/eal/freebsd/eal_thread.c                  |   3 +
 lib/eal/freebsd/eal_timer.c                   |   2 +
 lib/eal/include/rte_function_versioning.h     |  99 ---
 lib/eal/linux/eal.c                           |   8 +
 lib/eal/linux/eal_alarm.c                     |   3 +
 lib/eal/linux/eal_dev.c                       |   5 +
 lib/eal/linux/eal_interrupts.c                |  20 +
 lib/eal/linux/eal_memory.c                    |   4 +
 lib/eal/linux/eal_thread.c                    |   3 +
 lib/eal/linux/eal_timer.c                     |   5 +
 lib/eal/linux/eal_vfio.c                      |  17 +
 lib/eal/loongarch/rte_cpuflags.c              |   4 +
 lib/eal/loongarch/rte_hypervisor.c            |   2 +
 lib/eal/loongarch/rte_power_intrinsics.c      |   5 +
 lib/eal/ppc/rte_cpuflags.c                    |   4 +
 lib/eal/ppc/rte_hypervisor.c                  |   2 +
 lib/eal/ppc/rte_power_intrinsics.c            |   5 +
 lib/eal/riscv/rte_cpuflags.c                  |   4 +
 lib/eal/riscv/rte_hypervisor.c                |   2 +
 lib/eal/riscv/rte_power_intrinsics.c          |   5 +
 lib/eal/unix/eal_debug.c                      |   3 +
 lib/eal/unix/eal_filesystem.c                 |   2 +
 lib/eal/unix/eal_firmware.c                   |   2 +
 lib/eal/unix/eal_unix_memory.c                |   5 +
 lib/eal/unix/eal_unix_timer.c                 |   2 +
 lib/eal/unix/rte_thread.c                     |  14 +
 lib/eal/version.map                           | 451 --------------
 lib/eal/windows/eal.c                         |  12 +
 lib/eal/windows/eal_alarm.c                   |   3 +
 lib/eal/windows/eal_debug.c                   |   2 +
 lib/eal/windows/eal_dev.c                     |   5 +
 lib/eal/windows/eal_interrupts.c              |  20 +
 lib/eal/windows/eal_memory.c                  |   8 +
 lib/eal/windows/eal_mp.c                      |   7 +
 lib/eal/windows/eal_thread.c                  |   2 +
 lib/eal/windows/eal_timer.c                   |   2 +
 lib/eal/windows/rte_thread.c                  |  15 +
 lib/eal/x86/rte_cpuflags.c                    |   4 +
 lib/eal/x86/rte_hypervisor.c                  |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |   5 +
 lib/eal/x86/rte_spinlock.c                    |   2 +
 lib/efd/rte_efd.c                             |   8 +
 lib/efd/version.map                           |  13 -
 lib/ethdev/ethdev_driver.c                    |  25 +
 lib/ethdev/ethdev_linux_ethtool.c             |   4 +
 lib/ethdev/ethdev_private.c                   |   3 +
 lib/ethdev/ethdev_trace_points.c              |   7 +
 lib/ethdev/rte_ethdev.c                       | 169 +++++
 lib/ethdev/rte_ethdev_cman.c                  |   5 +
 lib/ethdev/rte_flow.c                         |  65 ++
 lib/ethdev/rte_mtr.c                          |  22 +
 lib/ethdev/rte_tm.c                           |  32 +
 lib/ethdev/version.map                        | 378 ------------
 lib/eventdev/eventdev_private.c               |   3 +
 lib/eventdev/eventdev_trace_points.c          |  12 +
 lib/eventdev/rte_event_crypto_adapter.c       |  16 +
 lib/eventdev/rte_event_dma_adapter.c          |  16 +
 lib/eventdev/rte_event_eth_rx_adapter.c       |  24 +
 lib/eventdev/rte_event_eth_tx_adapter.c       |  18 +
 lib/eventdev/rte_event_ring.c                 |   5 +
 lib/eventdev/rte_event_timer_adapter.c        |  12 +
 lib/eventdev/rte_eventdev.c                   |  47 ++
 lib/eventdev/version.map                      | 179 ------
 lib/fib/rte_fib.c                             |  11 +
 lib/fib/rte_fib6.c                            |  10 +
 lib/fib/version.map                           |  31 -
 lib/gpudev/gpudev.c                           |  33 +
 lib/gpudev/version.map                        |  44 --
 lib/graph/graph.c                             |  17 +
 lib/graph/graph_debug.c                       |   3 +
 lib/graph/graph_stats.c                       |   5 +
 lib/graph/node.c                              |  12 +
 lib/graph/rte_graph_model_mcore_dispatch.c    |   4 +
 lib/graph/rte_graph_worker.c                  |   4 +
 lib/graph/version.map                         |  61 --
 lib/gro/rte_gro.c                             |   7 +
 lib/gro/version.map                           |  12 -
 lib/gso/rte_gso.c                             |   2 +
 lib/gso/version.map                           |   7 -
 lib/hash/rte_cuckoo_hash.c                    |  28 +
 lib/hash/rte_fbk_hash.c                       |   4 +
 lib/hash/rte_hash_crc.c                       |   3 +
 lib/hash/rte_thash.c                          |  13 +
 lib/hash/rte_thash_gf2_poly_math.c            |   2 +
 lib/hash/rte_thash_gfni.c                     |   3 +
 lib/hash/version.map                          |  66 --
 lib/ip_frag/rte_ip_frag_common.c              |   6 +
 lib/ip_frag/rte_ipv4_fragmentation.c          |   3 +
 lib/ip_frag/rte_ipv4_reassembly.c             |   2 +
 lib/ip_frag/rte_ipv6_fragmentation.c          |   2 +
 lib/ip_frag/rte_ipv6_reassembly.c             |   2 +
 lib/ip_frag/version.map                       |  16 -
 lib/ipsec/ipsec_sad.c                         |   7 +
 lib/ipsec/ipsec_telemetry.c                   |   3 +
 lib/ipsec/sa.c                                |   5 +
 lib/ipsec/ses.c                               |   2 +
 lib/ipsec/version.map                         |  23 -
 lib/jobstats/rte_jobstats.c                   |  15 +
 lib/jobstats/version.map                      |  20 -
 lib/kvargs/rte_kvargs.c                       |   9 +
 lib/kvargs/version.map                        |  14 -
 lib/latencystats/rte_latencystats.c           |   6 +
 lib/latencystats/version.map                  |  11 -
 lib/log/log.c                                 |  23 +
 lib/log/log_color.c                           |   2 +
 lib/log/log_internal.h                        |   3 -
 lib/log/log_syslog.c                          |   2 +
 lib/log/log_timestamp.c                       |   2 +
 lib/log/version.map                           |  37 --
 lib/lpm/rte_lpm.c                             |   9 +
 lib/lpm/rte_lpm6.c                            |  11 +
 lib/lpm/version.map                           |  24 -
 lib/mbuf/rte_mbuf.c                           |  18 +
 lib/mbuf/rte_mbuf_dyn.c                       |  10 +
 lib/mbuf/rte_mbuf_pool_ops.c                  |   6 +
 lib/mbuf/rte_mbuf_ptype.c                     |   9 +
 lib/mbuf/version.map                          |  45 --
 lib/member/rte_member.c                       |  14 +
 lib/member/version.map                        |  19 -
 lib/mempool/mempool_trace_points.c            |  11 +
 lib/mempool/rte_mempool.c                     |  28 +
 lib/mempool/rte_mempool_ops.c                 |   5 +
 lib/mempool/rte_mempool_ops_default.c         |   5 +
 lib/mempool/version.map                       |  65 --
 lib/meson.build                               |  67 +-
 lib/meter/rte_meter.c                         |   7 +
 lib/meter/version.map                         |  12 -
 lib/metrics/rte_metrics.c                     |   9 +
 lib/metrics/rte_metrics_telemetry.c           |  12 +
 lib/metrics/version.map                       |  26 -
 lib/mldev/mldev_utils.c                       |   3 +
 lib/mldev/mldev_utils_neon.c                  |  20 +
 lib/mldev/mldev_utils_neon_bfloat16.c         |   4 +
 lib/mldev/mldev_utils_scalar.c                |  20 +
 lib/mldev/mldev_utils_scalar_bfloat16.c       |   4 +
 lib/mldev/rte_mldev.c                         |  38 ++
 lib/mldev/rte_mldev_pmd.c                     |   3 +
 lib/mldev/version.map                         |  74 ---
 lib/net/net_crc.h                             |  15 -
 lib/net/rte_arp.c                             |   2 +
 lib/net/rte_ether.c                           |   4 +
 lib/net/rte_net.c                             |   3 +
 lib/net/rte_net_crc.c                         |  31 +-
 lib/net/version.map                           |  23 -
 lib/node/ethdev_ctrl.c                        |   3 +
 lib/node/ip4_lookup.c                         |   2 +
 lib/node/ip4_reassembly.c                     |   2 +
 lib/node/ip4_rewrite.c                        |   2 +
 lib/node/ip6_lookup.c                         |   2 +
 lib/node/ip6_rewrite.c                        |   2 +
 lib/node/udp4_input.c                         |   3 +
 lib/node/version.map                          |  25 -
 lib/pcapng/rte_pcapng.c                       |   8 +
 lib/pcapng/version.map                        |  13 -
 lib/pci/rte_pci.c                             |   4 +
 lib/pci/version.map                           |   9 -
 lib/pdcp/rte_pdcp.c                           |   6 +
 lib/pdcp/version.map                          |  16 -
 lib/pdump/rte_pdump.c                         |  10 +
 lib/pdump/version.map                         |  15 -
 lib/pipeline/rte_pipeline.c                   |  24 +
 lib/pipeline/rte_port_in_action.c             |   9 +
 lib/pipeline/rte_swx_ctl.c                    |  18 +
 lib/pipeline/rte_swx_ipsec.c                  |   8 +
 lib/pipeline/rte_swx_pipeline.c               |  74 +++
 lib/pipeline/rte_table_action.c               |  17 +
 lib/pipeline/version.map                      | 172 ------
 lib/port/rte_port_ethdev.c                    |   4 +
 lib/port/rte_port_eventdev.c                  |   4 +
 lib/port/rte_port_fd.c                        |   4 +
 lib/port/rte_port_frag.c                      |   3 +
 lib/port/rte_port_ras.c                       |   3 +
 lib/port/rte_port_ring.c                      |   7 +
 lib/port/rte_port_sched.c                     |   3 +
 lib/port/rte_port_source_sink.c               |   3 +
 lib/port/rte_port_sym_crypto.c                |   4 +
 lib/port/rte_swx_port_ethdev.c                |   3 +
 lib/port/rte_swx_port_fd.c                    |   3 +
 lib/port/rte_swx_port_ring.c                  |   3 +
 lib/port/rte_swx_port_source_sink.c           |   4 +
 lib/port/version.map                          |  50 --
 lib/power/power_common.c                      |   9 +
 lib/power/rte_power_cpufreq.c                 |  19 +
 lib/power/rte_power_pmd_mgmt.c                |  11 +
 lib/power/rte_power_qos.c                     |   3 +
 lib/power/rte_power_uncore.c                  |  15 +
 lib/power/version.map                         |  71 ---
 lib/rawdev/rte_rawdev.c                       |  31 +
 lib/rawdev/version.map                        |  36 --
 lib/rcu/rte_rcu_qsbr.c                        |  12 +
 lib/rcu/version.map                           |  17 -
 lib/regexdev/rte_regexdev.c                   |  27 +
 lib/regexdev/version.map                      |  40 --
 lib/reorder/rte_reorder.c                     |  12 +
 lib/reorder/version.map                       |  27 -
 lib/rib/rte_rib.c                             |  15 +
 lib/rib/rte_rib6.c                            |  15 +
 lib/rib/version.map                           |  34 --
 lib/ring/rte_ring.c                           |  12 +
 lib/ring/rte_soring.c                         |   4 +
 lib/ring/soring.c                             |  18 +
 lib/ring/version.map                          |  42 --
 lib/sched/rte_approx.c                        |   2 +
 lib/sched/rte_pie.c                           |   3 +
 lib/sched/rte_red.c                           |   7 +
 lib/sched/rte_sched.c                         |  16 +
 lib/sched/version.map                         |  30 -
 lib/security/rte_security.c                   |  21 +
 lib/security/version.map                      |  37 --
 lib/stack/rte_stack.c                         |   4 +
 lib/stack/version.map                         |   9 -
 lib/table/rte_swx_table_em.c                  |   3 +
 lib/table/rte_swx_table_learner.c             |  11 +
 lib/table/rte_swx_table_selector.c            |   7 +
 lib/table/rte_swx_table_wm.c                  |   2 +
 lib/table/rte_table_acl.c                     |   2 +
 lib/table/rte_table_array.c                   |   2 +
 lib/table/rte_table_hash_cuckoo.c             |   2 +
 lib/table/rte_table_hash_ext.c                |   2 +
 lib/table/rte_table_hash_key16.c              |   3 +
 lib/table/rte_table_hash_key32.c              |   3 +
 lib/table/rte_table_hash_key8.c               |   3 +
 lib/table/rte_table_hash_lru.c                |   2 +
 lib/table/rte_table_lpm.c                     |   2 +
 lib/table/rte_table_lpm_ipv6.c                |   2 +
 lib/table/rte_table_stub.c                    |   2 +
 lib/table/version.map                         |  53 --
 lib/telemetry/telemetry.c                     |   4 +
 lib/telemetry/telemetry_data.c                |  18 +
 lib/telemetry/telemetry_legacy.c              |   2 +
 lib/telemetry/version.map                     |  40 --
 lib/timer/rte_timer.c                         |  19 +
 lib/timer/version.map                         |  24 -
 lib/vhost/socket.c                            |  17 +
 lib/vhost/vdpa.c                              |  12 +
 lib/vhost/version.map                         | 111 ----
 lib/vhost/vhost.c                             |  42 ++
 lib/vhost/vhost_crypto.c                      |   7 +
 lib/vhost/vhost_user.c                        |   3 +
 lib/vhost/virtio_net.c                        |   8 +
 536 files changed, 5133 insertions(+), 6573 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 delete mode 100644 buildtools/map_to_win.py
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py
 delete mode 100644 drivers/baseband/acc/version.map
 delete mode 100644 drivers/baseband/fpga_5gnr_fec/version.map
 delete mode 100644 drivers/baseband/fpga_lte_fec/version.map
 delete mode 100644 drivers/bus/auxiliary/version.map
 delete mode 100644 drivers/bus/cdx/version.map
 create mode 100644 drivers/bus/dpaa/dpaa_bus_symbols.c
 delete mode 100644 drivers/bus/dpaa/version.map
 delete mode 100644 drivers/bus/fslmc/version.map
 delete mode 100644 drivers/bus/ifpga/version.map
 delete mode 100644 drivers/bus/pci/version.map
 delete mode 100644 drivers/bus/platform/version.map
 delete mode 100644 drivers/bus/uacce/version.map
 delete mode 100644 drivers/bus/vdev/version.map
 delete mode 100644 drivers/bus/vmbus/version.map
 create mode 100644 drivers/common/cnxk/roc_platform_symbols.c
 delete mode 100644 drivers/common/cnxk/version.map
 delete mode 100644 drivers/common/cpt/version.map
 delete mode 100644 drivers/common/dpaax/version.map
 delete mode 100644 drivers/common/ionic/version.map
 delete mode 100644 drivers/common/mlx5/version.map
 delete mode 100644 drivers/common/mvep/version.map
 delete mode 100644 drivers/common/nfp/version.map
 delete mode 100644 drivers/common/nitrox/version.map
 delete mode 100644 drivers/common/octeontx/version.map
 create mode 100644 drivers/common/sfc_efx/sfc_symbols.c
 delete mode 100644 drivers/common/sfc_efx/version.map
 delete mode 100644 drivers/crypto/cnxk/version.map
 delete mode 100644 drivers/crypto/dpaa2_sec/version.map
 delete mode 100644 drivers/crypto/dpaa_sec/version.map
 delete mode 100644 drivers/crypto/octeontx/version.map
 delete mode 100644 drivers/crypto/scheduler/version.map
 delete mode 100644 drivers/dma/cnxk/version.map
 delete mode 100644 drivers/event/cnxk/version.map
 delete mode 100644 drivers/event/dlb2/version.map
 delete mode 100644 drivers/mempool/cnxk/version.map
 delete mode 100644 drivers/mempool/dpaa/version.map
 delete mode 100644 drivers/mempool/dpaa2/version.map
 delete mode 100644 drivers/net/atlantic/version.map
 delete mode 100644 drivers/net/bnxt/version.map
 delete mode 100644 drivers/net/bonding/version.map
 delete mode 100644 drivers/net/cnxk/version.map
 delete mode 100644 drivers/net/dpaa/version.map
 create mode 100644 drivers/net/dpaa2/dpaa2_symbols.c
 delete mode 100644 drivers/net/dpaa2/version.map
 delete mode 100644 drivers/net/intel/i40e/version.map
 create mode 100644 drivers/net/intel/iavf/iavf_symbols.c
 delete mode 100644 drivers/net/intel/iavf/version.map
 delete mode 100644 drivers/net/intel/ice/version.map
 delete mode 100644 drivers/net/intel/idpf/version.map
 delete mode 100644 drivers/net/intel/ipn3ke/version.map
 delete mode 100644 drivers/net/intel/ixgbe/version.map
 delete mode 100644 drivers/net/mlx5/version.map
 delete mode 100644 drivers/net/octeontx/version.map
 delete mode 100644 drivers/net/ring/version.map
 delete mode 100644 drivers/net/softnic/version.map
 delete mode 100644 drivers/net/vhost/version.map
 delete mode 100644 drivers/power/kvm_vm/version.map
 delete mode 100644 drivers/raw/cnxk_rvu_lf/version.map
 delete mode 100644 drivers/raw/ifpga/version.map
 delete mode 100644 drivers/version.map
 delete mode 100644 lib/acl/version.map
 delete mode 100644 lib/argparse/version.map
 delete mode 100644 lib/bbdev/version.map
 delete mode 100644 lib/bitratestats/version.map
 delete mode 100644 lib/bpf/version.map
 delete mode 100644 lib/cfgfile/version.map
 delete mode 100644 lib/cmdline/version.map
 delete mode 100644 lib/compressdev/version.map
 delete mode 100644 lib/cryptodev/version.map
 delete mode 100644 lib/dispatcher/version.map
 delete mode 100644 lib/distributor/version.map
 delete mode 100644 lib/dmadev/version.map
 create mode 100644 lib/eal/common/eal_symbol_exports.h
 delete mode 100644 lib/eal/include/rte_function_versioning.h
 delete mode 100644 lib/eal/version.map
 delete mode 100644 lib/efd/version.map
 delete mode 100644 lib/ethdev/version.map
 delete mode 100644 lib/eventdev/version.map
 delete mode 100644 lib/fib/version.map
 delete mode 100644 lib/gpudev/version.map
 delete mode 100644 lib/graph/version.map
 delete mode 100644 lib/gro/version.map
 delete mode 100644 lib/gso/version.map
 delete mode 100644 lib/hash/version.map
 delete mode 100644 lib/ip_frag/version.map
 delete mode 100644 lib/ipsec/version.map
 delete mode 100644 lib/jobstats/version.map
 delete mode 100644 lib/kvargs/version.map
 delete mode 100644 lib/latencystats/version.map
 delete mode 100644 lib/log/version.map
 delete mode 100644 lib/lpm/version.map
 delete mode 100644 lib/mbuf/version.map
 delete mode 100644 lib/member/version.map
 delete mode 100644 lib/mempool/version.map
 delete mode 100644 lib/meter/version.map
 delete mode 100644 lib/metrics/version.map
 delete mode 100644 lib/mldev/version.map
 delete mode 100644 lib/net/version.map
 delete mode 100644 lib/node/version.map
 delete mode 100644 lib/pcapng/version.map
 delete mode 100644 lib/pci/version.map
 delete mode 100644 lib/pdcp/version.map
 delete mode 100644 lib/pdump/version.map
 delete mode 100644 lib/pipeline/version.map
 delete mode 100644 lib/port/version.map
 delete mode 100644 lib/power/version.map
 delete mode 100644 lib/rawdev/version.map
 delete mode 100644 lib/rcu/version.map
 delete mode 100644 lib/regexdev/version.map
 delete mode 100644 lib/reorder/version.map
 delete mode 100644 lib/rib/version.map
 delete mode 100644 lib/ring/version.map
 delete mode 100644 lib/sched/version.map
 delete mode 100644 lib/security/version.map
 delete mode 100644 lib/stack/version.map
 delete mode 100644 lib/table/version.map
 delete mode 100644 lib/telemetry/version.map
 delete mode 100644 lib/timer/version.map
 delete mode 100644 lib/vhost/version.map

-- 
2.48.1


^ permalink raw reply	[relevance 3%]

* [PATCH v6 6/8] build: use dynamically generated version maps
  2025-03-28 10:52  3% ` [PATCH v6 " David Marchand
  2025-03-28 10:52 17%   ` [PATCH v6 4/8] build: generate symbol maps David Marchand
@ 2025-03-28 10:52 16%   ` David Marchand
  2025-03-28 13:20  0%     ` Aaron Conole
  2025-03-28 10:52 20%   ` [PATCH v6 8/8] eal: rework function versioning macros David Marchand
  2 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-28 10:52 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andremue, Aaron Conole,
	Michael Santana, Dmitry Kozlyuk, Tyler Retzlaff

Switch to dynamically generated version maps.

As the map files get generated, tooling around checking, converting,
updating etc.. static version maps can be removed.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   7 -
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/map-list-symbol.sh                 |   7 +-
 buildtools/map_to_win.py                      |  41 ---
 buildtools/meson.build                        |   1 -
 devtools/check-symbol-change.sh               | 186 -----------
 devtools/check-symbol-maps.sh                 | 101 ------
 devtools/checkpatches.sh                      |   2 +-
 devtools/update-abi.sh                        |  46 ---
 devtools/update_version_map_abi.py            | 210 ------------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/meson.build                           |  74 ++---
 lib/meson.build                               |  73 ++---
 17 files changed, 188 insertions(+), 931 deletions(-)
 delete mode 100644 buildtools/map_to_win.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0cc4d12b0b..7a6b679fe5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,6 @@ jobs:
         failed=
         devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
         devtools/check-meson.py || failed=true
-        devtools/check-symbol-maps.sh || failed=true
         [ -z "$failed" ]
   ubuntu-vm-builds:
     name: ${{ join(matrix.config.*, '-') }}
diff --git a/MAINTAINERS b/MAINTAINERS
index 42ea07854b..480972ef1e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
 F: MAINTAINERS
 F: devtools/build-dict.sh
 F: devtools/check-abi.sh
-F: devtools/check-abi-version.sh
 F: devtools/check-doc-vs-code.sh
 F: devtools/check-dup-includes.sh
 F: devtools/check-maintainers.sh
@@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
 F: devtools/check-symbol-change.py
-F: devtools/check-symbol-change.sh
-F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
 F: devtools/get-maintainer.sh
 F: devtools/git-log-fixes.sh
 F: devtools/load-devel-config
 F: devtools/parse-flow-support.sh
 F: devtools/process-iwyu.py
-F: devtools/update-abi.sh
 F: devtools/update-patches.py
-F: devtools/update_version_map_abi.py
 F: devtools/libabigail.abignore
 F: devtools/words-case.txt
 F: license/
@@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/common/
 F: lib/eal/unix/
 F: lib/eal/include/
-F: lib/eal/version.map
 F: doc/guides/prog_guide/env_abstraction_layer.rst
 F: app/test/test_alarm.c
 F: app/test/test_atomic.c
@@ -396,7 +390,6 @@ Windows support
 M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
 M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/windows/
-F: buildtools/map_to_win.py
 F: doc/guides/windows_gsg/
 
 Windows memory allocation
diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
index b8ac24391e..0d6745ec14 100755
--- a/buildtools/check-symbols.sh
+++ b/buildtools/check-symbols.sh
@@ -7,29 +7,12 @@ OBJFILE=$2
 
 ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
 LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
-CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
-
-# added check for "make -C test/" usage
-if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
-then
-	exit 0
-fi
-
-if [ -d $MAPFILE ]
-then
-	exit 0
-fi
-
 DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
 trap 'rm -f "$DUMPFILE"' EXIT
 objdump -t $OBJFILE >$DUMPFILE
 
 ret=0
 
-if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
-	ret=1
-fi
-
 for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
 do
 	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
@@ -37,8 +20,7 @@ do
 		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as experimental
-		but is listed in version map
+		$SYM is not flagged as experimental but is exported as an experimental symbol
 		Please add __rte_experimental to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -53,9 +35,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as experimental
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as experimental but is not exported as an experimental symbol
+		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
@@ -67,8 +48,7 @@ do
 		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as internal
-		but is listed in version map
+		$SYM is not flagged as internal but is exported as an internal symbol
 		Please add __rte_internal to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -83,9 +63,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as internal
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as internal but is not exported as an internal symbol
+		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index 0829df4be5..962d5f3271 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -42,7 +42,6 @@ for file in $@; do
 	cat "$file" |awk '
 	BEGIN {
 		current_section = "";
-		current_version = "";
 		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
 			ret = 0;
 		} else {
@@ -54,15 +53,11 @@ for file in $@; do
 			current_section = $1;
 		}
 	}
-	/.*}/ { current_section = ""; current_version = ""; }
-	/^\t# added in / {
-		current_version=$4;
-	}
+	/.*}/ { current_section = ""; }
 	/^[^}].*[^:*];/ {
 		if (current_section == "") {
 			next;
 		}
-		symbol_version = current_version
 		if (/^[^}].*[^:*]; # added in /) {
 			symbol_version = $5
 		}
diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
deleted file mode 100644
index aa1752cacd..0000000000
--- a/buildtools/map_to_win.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-import sys
-
-
-def is_function_line(ln):
-    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
-
-# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
-def create_mingw_map_file(input_map, output_map):
-    with open(input_map) as f_in, open(output_map, 'w') as f_out:
-        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
-
-def main(args):
-    if not args[1].endswith('version.map') or \
-            not args[2].endswith('exports.def') and \
-            not args[2].endswith('mingw.map'):
-        return 1
-
-    if args[2].endswith('mingw.map'):
-        create_mingw_map_file(args[1], args[2])
-        return 0
-
-# generate def file from map file.
-# This works taking indented lines only which end with a ";" and which don't
-# have a colon in them, i.e. the lines defining functions only.
-    else:
-        with open(args[1]) as f_in:
-            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
-                         if is_function_line(ln)]
-            functions = ["EXPORTS\n"] + functions
-
-    with open(args[2], 'w') as f_out:
-        f_out.writelines(functions)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv))
diff --git a/buildtools/meson.build b/buildtools/meson.build
index b745e9afa4..1cd1ce02fd 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -18,7 +18,6 @@ endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
 gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
-map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
 get_cpu_count_cmd = py3 + files('get-cpu-count.py')
 get_numa_count_cmd = py3 + files('get-numa-count.py')
diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
deleted file mode 100755
index 8992214ac8..0000000000
--- a/devtools/check-symbol-change.sh
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
-
-build_map_changes()
-{
-	local fname="$1"
-	local mapdb="$2"
-
-	cat "$fname" | awk '
-		# Initialize our variables
-		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
-
-		# Anything that starts with + or -, followed by an a
-		# and ends in the string .map is the name of our map file
-		# This may appear multiple times in a patch if multiple
-		# map files are altered, and all section/symbol names
-		# appearing between a triggering of this rule and the
-		# next trigger of this rule are associated with this file
-		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
-
-		# The previous rule catches all .map files, anything else
-		# indicates we left the map chunk.
-		/[-+] [ab]\// {in_map=0}
-
-		# Triggering this rule, which starts a line and ends it
-		# with a { identifies a versioned section.  The section name is
-		# the rest of the line with the + and { symbols removed.
-		# Triggering this rule sets in_sec to 1, which actives the
-		# symbol rule below
-		/^.*{/ {
-			gsub("+", "");
-			if (in_map == 1) {
-				sec=$(NF-1); in_sec=1;
-			}
-		}
-
-		# This rule identifies the end of a section, and disables the
-		# symbol rule
-		/.*}/ {in_sec=0}
-
-		# This rule matches on a + followed by any characters except a :
-		# (which denotes a global vs local segment), and ends with a ;.
-		# The semicolon is removed and the symbol is printed with its
-		# association file name and version section, along with an
-		# indicator that the symbol is a new addition.  Note this rule
-		# only works if we have found a version section in the rule
-		# above (hence the in_sec check) And found a map file (the
-		# in_map check).  If we are not in a map chunk, do nothing.  If
-		# we are in a map chunk but not a section chunk, record it as
-		# unknown.
-		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " add"
-				} else {
-					print map " " sym " unknown add"
-				}
-			}
-		}
-
-		# This is the same rule as above, but the rule matches on a
-		# leading - rather than a +, denoting that the symbol is being
-		# removed.
-		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " del"
-				} else {
-					print map " " sym " unknown del"
-				}
-			}
-		}' > "$mapdb"
-
-		sort -u "$mapdb" > "$mapdb.2"
-		mv -f "$mapdb.2" "$mapdb"
-
-}
-
-is_stable_section() {
-	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
-}
-
-check_for_rule_violations()
-{
-	local mapdb="$1"
-	local mname
-	local symname
-	local secname
-	local ar
-	local ret=0
-
-	while read mname symname secname ar
-	do
-		if [ "$ar" = "add" ]
-		then
-
-			if [ "$secname" = "unknown" ]
-			then
-				# Just inform the user of this occurrence, but
-				# don't flag it as an error
-				echo -n "INFO: symbol $symname is added but "
-				echo -n "patch has insufficient context "
-				echo -n "to determine the section name "
-				echo -n "please ensure the version is "
-				echo "EXPERIMENTAL"
-				continue
-			fi
-
-			oldsecname=$(sed -n \
-			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
-
-			# A symbol can not enter a stable section directly
-			if [ -z "$oldsecname" ]
-			then
-				if ! is_stable_section $secname
-				then
-					echo -n "INFO: symbol $symname has "
-					echo -n "been added to the "
-					echo -n "$secname section of the "
-					echo "version map"
-					continue
-				else
-					echo -n "ERROR: symbol $symname "
-					echo -n "is added in the $secname "
-					echo -n "section, but is expected to "
-					echo -n "be added in the EXPERIMENTAL "
-					echo "section of the version map"
-					ret=1
-					continue
-				fi
-			fi
-
-			# This symbol is moving inside a section, nothing to do
-			if [ "$oldsecname" = "$secname" ]
-			then
-				continue
-			fi
-
-			# This symbol is moving between two sections (the
-			# original section is a stable section).
-			# This can be legit, just warn.
-			if is_stable_section $oldsecname
-			then
-				echo -n "INFO: symbol $symname is being "
-				echo -n "moved from $oldsecname to $secname. "
-				echo -n "Ensure that it has gone through the "
-				echo "deprecation process"
-				continue
-			fi
-		else
-
-			if ! grep -q "$mname $symname .* add" "$mapdb" && \
-			   is_stable_section $secname
-			then
-				# Just inform users that stable
-				# symbols need to go through a deprecation
-				# process
-				echo -n "INFO: symbol $symname is being "
-				echo -n "removed, ensure that it has "
-				echo "gone through the deprecation process"
-			fi
-		fi
-	done < "$mapdb"
-
-	return $ret
-}
-
-trap clean_and_exit_on_sig EXIT
-
-mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
-patch=$1
-exit_code=1
-
-clean_and_exit_on_sig()
-{
-	rm -f "$mapfile"
-	exit $exit_code
-}
-
-build_map_changes "$patch" "$mapfile"
-check_for_rule_violations "$mapfile"
-exit_code=$?
-rm -f "$mapfile"
-
-exit $exit_code
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
deleted file mode 100755
index fcd3931e5d..0000000000
--- a/devtools/check-symbol-maps.sh
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright 2018 Mellanox Technologies, Ltd
-
-cd $(dirname $0)/..
-
-# speed up by ignoring Unicode details
-export LC_ALL=C
-
-if [ $# = 0 ] ; then
-    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
-fi
-
-ret=0
-
-find_orphan_symbols ()
-{
-    for map in $@ ; do
-        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
-            if echo $sym | grep -q '^per_lcore_' ; then
-                symsrc=${sym#per_lcore_}
-            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
-                symsrc=${sym#__}
-            else
-                symsrc=$sym
-            fi
-            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
-                echo "$map: $sym"
-            fi
-        done
-    done
-}
-
-orphan_symbols=$(find_orphan_symbols $@)
-if [ -n "$orphan_symbols" ] ; then
-    echo "Found only in symbol map file:"
-    echo "$orphan_symbols" | sed 's,^,\t,'
-    ret=1
-fi
-
-find_duplicate_symbols ()
-{
-    for map in $@ ; do
-        buildtools/map-list-symbol.sh $map | \
-            sort | uniq -c | grep -v " 1 $map" || true
-    done
-}
-
-duplicate_symbols=$(find_duplicate_symbols $@)
-if [ -n "$duplicate_symbols" ] ; then
-    echo "Found duplicates in symbol map file:"
-    echo "$duplicate_symbols"
-    ret=1
-fi
-
-local_miss_maps=$(grep -L 'local: \*;' $@ || true)
-if [ -n "$local_miss_maps" ] ; then
-    echo "Found maps without local catch-all:"
-    echo "$local_miss_maps"
-    ret=1
-fi
-
-find_bad_format_maps ()
-{
-    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
-    next_abi_version=$((abi_version + 1))
-    for map in $@ ; do
-        cat $map | awk '
-            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
-            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
-            /^$/ { next; } # empty line
-            /^\t(global:|local: \*;)$/ { next; } # qualifiers
-            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
-            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
-            { print $0; }' || echo $map
-    done
-}
-
-bad_format_maps=$(find_bad_format_maps $@)
-if [ -n "$bad_format_maps" ] ; then
-    echo "Found badly formatted maps:"
-    echo "$bad_format_maps"
-    ret=1
-fi
-
-find_non_versioned_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
-            echo $map
-    done
-}
-
-non_versioned_maps=$(find_non_versioned_maps $@)
-if [ -n "$non_versioned_maps" ] ; then
-    echo "Found non versioned maps:"
-    echo "$non_versioned_maps"
-    ret=1
-fi
-
-exit $ret
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index 9180c2b070..c111b0fef3 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -9,7 +9,7 @@
 # - DPDK_CHECKPATCH_OPTIONS
 . $(dirname $(readlink -f $0))/load-devel-config
 
-VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
+VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
 
 # Enable codespell by default. This can be overwritten from a config file.
 # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
deleted file mode 100755
index 45437f3c3b..0000000000
--- a/devtools/update-abi.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-abi_version=$1
-abi_version_file="./ABI_VERSION"
-update_path="lib drivers"
-
-# check ABI version format string
-check_abi_version() {
-      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
-}
-
-if [ -z "$1" ]; then
-      # output to stderr
-      >&2 echo "Please provide ABI version"
-      exit 1
-fi
-
-# check version string format
-if ! check_abi_version $abi_version ; then
-      # output to stderr
-      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
-      exit 1
-fi
-
-if [ -n "$2" ]; then
-      abi_version_file=$2
-fi
-
-if [ -n "$3" ]; then
-      # drop $1 and $2
-      shift 2
-      # assign all other arguments as update paths
-      update_path=$@
-fi
-
-echo "New ABI version:" $abi_version
-echo "ABI_VERSION path:" $abi_version_file
-echo "Path to update:" $update_path
-
-echo $abi_version > $abi_version_file
-
-find $update_path -name version.map -exec \
-      devtools/update_version_map_abi.py {} \
-      $abi_version \; -print
diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
deleted file mode 100755
index d17b02a327..0000000000
--- a/devtools/update_version_map_abi.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-"""
-A Python program that updates and merges all available stable ABI versions into
-one ABI version, while leaving experimental ABI exactly as it is. The intended
-ABI version is supplied via command-line parameter. This script is to be called
-from the devtools/update-abi.sh utility.
-"""
-
-import argparse
-import sys
-import re
-
-
-def __parse_map_file(f_in):
-    # match function name, followed by semicolon, followed by EOL or comments,
-    # optionally with whitespace in between each item
-    func_line_regex = re.compile(r"\s*"
-                                 r"(?P<line>"
-                                 r"(?P<func>[a-zA-Z_0-9]+)"
-                                 r"\s*"
-                                 r";"
-                                 r"\s*"
-                                 r"(?P<comment>#.+)?"
-                                 r")"
-                                 r"\s*"
-                                 r"$")
-    # match section name, followed by opening bracked, followed by EOL,
-    # optionally with whitespace in between each item
-    section_begin_regex = re.compile(r"\s*"
-                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
-                                     r"\s*"
-                                     r"{"
-                                     r"\s*"
-                                     r"$")
-    # match closing bracket, optionally followed by section name (for when we
-    # inherit from another ABI version), followed by semicolon, followed by
-    # EOL, optionally with whitespace in between each item
-    section_end_regex = re.compile(r"\s*"
-                                   r"}"
-                                   r"\s*"
-                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
-                                   r"\s*"
-                                   r";"
-                                   r"\s*"
-                                   r"$")
-
-    # for stable ABI, we don't care about which version introduced which
-    # function, we just flatten the list. there are dupes in certain files, so
-    # use a set instead of a list
-    stable_lines = set()
-    # copy experimental section as is
-    experimental_lines = []
-    # copy internal section as is
-    internal_lines = []
-    in_experimental = False
-    in_internal = False
-    has_stable = False
-
-    # gather all functions
-    for line in f_in:
-        # clean up the line
-        line = line.strip('\n').strip()
-
-        # is this an end of section?
-        match = section_end_regex.match(line)
-        if match:
-            # whatever section this was, it's not active any more
-            in_experimental = False
-            in_internal = False
-            continue
-
-        # if we're in the middle of experimental section, we need to copy
-        # the section verbatim, so just add the line
-        if in_experimental:
-            experimental_lines += [line]
-            continue
-
-        # if we're in the middle of internal section, we need to copy
-        # the section verbatim, so just add the line
-        if in_internal:
-            internal_lines += [line]
-            continue
-
-        # skip empty lines
-        if not line:
-            continue
-
-        # is this a beginning of a new section?
-        match = section_begin_regex.match(line)
-        if match:
-            cur_section = match.group("version")
-            # is it experimental?
-            in_experimental = cur_section == "EXPERIMENTAL"
-            # is it internal?
-            in_internal = cur_section == "INTERNAL"
-            if not in_experimental and not in_internal:
-                has_stable = True
-            continue
-
-        # is this a function?
-        match = func_line_regex.match(line)
-        if match:
-            stable_lines.add(match.group("line"))
-
-    return has_stable, stable_lines, experimental_lines, internal_lines
-
-
-def __generate_stable_abi(f_out, abi_major, lines):
-    # print ABI version header
-    print("DPDK_{} {{".format(abi_major), file=f_out)
-
-    # print global section if it exists
-    if lines:
-        print("\tglobal:", file=f_out)
-        # blank line
-        print(file=f_out)
-
-        # print all stable lines, alphabetically sorted
-        for line in sorted(lines):
-            print("\t{}".format(line), file=f_out)
-
-        # another blank line
-        print(file=f_out)
-
-    # print local section
-    print("\tlocal: *;", file=f_out)
-
-    # end stable version
-    print("};", file=f_out)
-
-
-def __generate_experimental_abi(f_out, lines):
-    # start experimental section
-    print("EXPERIMENTAL {", file=f_out)
-
-    # print all experimental lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __generate_internal_abi(f_out, lines):
-    # start internal section
-    print("INTERNAL {", file=f_out)
-
-    # print all internal lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __main():
-    arg_parser = argparse.ArgumentParser(
-        description='Merge versions in linker version script.')
-
-    arg_parser.add_argument("map_file", type=str,
-                            help='path to linker version script file '
-                                 '(pattern: version.map)')
-    arg_parser.add_argument("abi_version", type=str,
-                            help='target ABI version (pattern: MAJOR.MINOR)')
-
-    parsed = arg_parser.parse_args()
-
-    if not parsed.map_file.endswith('version.map'):
-        print("Invalid input file: {}".format(parsed.map_file),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-
-    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
-        print("Invalid ABI version: {}".format(parsed.abi_version),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-    abi_major = parsed.abi_version.split('.')[0]
-
-    with open(parsed.map_file) as f_in:
-        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
-
-    with open(parsed.map_file, 'w') as f_out:
-        need_newline = has_stable and experimental_lines
-        if has_stable:
-            __generate_stable_abi(f_out, abi_major, stable_lines)
-        if need_newline:
-            # separate sections with a newline
-            print(file=f_out)
-        if experimental_lines:
-            __generate_experimental_abi(f_out, experimental_lines)
-        if internal_lines:
-            if has_stable or experimental_lines:
-              # separate sections with a newline
-              print(file=f_out)
-            __generate_internal_abi(f_out, internal_lines)
-
-
-if __name__ == "__main__":
-    __main()
diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
index d96153c6b2..f03a7467ac 100644
--- a/doc/guides/contributing/abi_policy.rst
+++ b/doc/guides/contributing/abi_policy.rst
@@ -330,31 +330,14 @@ become part of a tracked ABI version.
 
 Note that marking an API as experimental is a multi step process.
 To mark an API as experimental, the symbols which are desired to be exported
-must be placed in an EXPERIMENTAL version block in the corresponding libraries'
-version map script.
+must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
+sources.
 Experimental symbols must be commented so that it is clear in which DPDK
 version they were introduced.
 
-.. code-block:: none
-
-   EXPERIMENTAL {
-           global:
-
-           # added in 20.11
-           rte_foo_init;
-           rte_foo_configure;
-
-           # added in 21.02
-           rte_foo_cleanup;
-   ...
-
 Secondly, the corresponding prototypes of those exported functions (in the
 development header files), must be marked with the ``__rte_experimental`` tag
 (see ``rte_compat.h``).
-The DPDK build makefiles perform a check to ensure that the map file and the
-C code reflect the same list of symbols.
-This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
-during compilation in the corresponding library Makefile.
 
 In addition to tagging the code with ``__rte_experimental``,
 the doxygen markup must also contain the EXPERIMENTAL string,
diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
index 1ebc79ca3c..43e27bbd0a 100644
--- a/doc/guides/contributing/coding_style.rst
+++ b/doc/guides/contributing/coding_style.rst
@@ -1018,13 +1018,6 @@ name
 	sources are stored in a directory ``lib/xyz``, this value should
 	never be needed for new libraries.
 
-.. note::
-
-	The name value also provides the name used to find the function version
-	map file, as part of the build process, so if the directory name and
-	library names differ, the ``version.map`` file should be named
-	consistently with the library, not the directory
-
 objs
 	**Default Value = []**.
 	This variable can be used to pass to the library build some pre-built
diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
index 4debb07b98..a06d8a2a3b 100644
--- a/doc/guides/contributing/img/patch_cheatsheet.svg
+++ b/doc/guides/contributing/img/patch_cheatsheet.svg
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    version="1.1"
    width="210mm"
    height="297mm"
    id="svg2985"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="patch_cheatsheet.svg">
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="patch_cheatsheet.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
   <sodipodi:namedview
      pagecolor="#ffffff"
      bordercolor="#666666"
@@ -23,18 +23,22 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:window-width="1920"
-     inkscape:window-height="1017"
+     inkscape:window-height="975"
      id="namedview274"
      showgrid="false"
      inkscape:zoom="0.89702958"
-     inkscape:cx="246.07409"
-     inkscape:cy="416.76022"
-     inkscape:window-x="1072"
-     inkscape:window-y="-8"
+     inkscape:cx="546.24732"
+     inkscape:cy="385.71749"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
      inkscape:window-maximized="1"
      inkscape:current-layer="layer1"
      inkscape:document-rotation="0"
-     inkscape:snap-grids="false" />
+     inkscape:snap-grids="false"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm" />
   <defs
      id="defs3">
     <linearGradient
@@ -906,7 +910,7 @@
              style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
              id="tspan4092-8-7-6-9-7"
              y="855.79816"
-             x="460.18405">****</tspan></text>
+             x="460.18405">***</tspan></text>
       </g>
     </g>
     <text
@@ -1132,161 +1136,126 @@
            id="tspan4092-8-6-3-1-8-4-4-55-7"
            style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
     </g>
+    <text
+       x="424.10629"
+       y="363.21423"
+       id="text4090-8"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="363.21423"
+         id="tspan4092-8"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
+    <text
+       x="424.10629"
+       y="393.60123"
+       id="text4090-8-5"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="393.60123"
+         id="tspan4092-8-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
+    <text
+       x="424.10629"
+       y="424.20575"
+       id="text4090-8-5-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="424.20575"
+         id="tspan4092-8-5-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
+    <text
+       x="424.10629"
+       y="453.10339"
+       id="text4090-8-5-6-9-4"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="453.10339"
+         id="tspan4092-8-5-5-3-4"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
+    <text
+       x="424.10629"
+       y="514.09497"
+       id="text4090-8-5-6-9-4-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="514.09497"
+         id="tspan4092-8-5-5-3-4-0"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
+    <text
+       x="425.12708"
+       y="544.91718"
+       id="text4090-8-5-6-9-4-6-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="425.12708"
+         y="544.91718"
+         id="tspan4092-8-5-5-3-4-0-6"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
-       id="g3605">
-      <text
-         x="42.176418"
-         y="1020.4383"
-         id="text4090-8-7-8-7-6-3-8-4"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="42.176418"
-           y="1020.4383"
-           id="tspan4092-8-6-3-1-8-4-4-55"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
-      <text
-         x="30.942892"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="30.942892"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-      <text
-         x="25.247679"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="25.247679"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-    </g>
-    <g
-       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
-       id="g3275">
+       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
+       id="g3334">
       <g
-         id="g3341">
+         id="g3267"
+         transform="translate(-13.517932,3.1531035)">
         <text
-           x="394.78601"
-           y="390.17807"
-           id="text4090-8"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="390.17807"
-             id="tspan4092-8"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
-        <text
-           x="394.78601"
-           y="420.24835"
-           id="text4090-8-5"
+           x="660.46729"
+           y="468.01297"
+           id="text4090-8-1-8-9-1-4-1"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="420.24835"
-             id="tspan4092-8-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
-        <text
-           x="394.78601"
-           y="450.53394"
-           id="text4090-8-5-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="450.53394"
-             id="tspan4092-8-5-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
-        <text
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="660.46729"
+             y="468.01297"
+             id="tspan4092-8-7-6-9-7-0-7"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+      </g>
+      <text
+         x="394.78601"
+         y="483.59955"
+         id="text4090-8-5-6-9"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="513.13031"
-           id="text4090-8-5-6-9-4"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="513.13031"
-             id="tspan4092-8-5-5-3-4"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
-        <text
+           y="483.59955"
+           id="tspan4092-8-5-5-3"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+    </g>
+    <g
+       id="g3428"
+       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
+      <text
+         x="394.78601"
+         y="541.38928"
+         id="text4090-8-5-6-9-4-6-1"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="573.48621"
-           id="text4090-8-5-6-9-4-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="573.48621"
-             id="tspan4092-8-5-5-3-4-0"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
+           y="541.38928"
+           id="tspan4092-8-5-5-3-4-0-7"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
+      <g
+         transform="translate(-119.92979,57.949844)"
+         id="g3267-9">
         <text
-           x="395.79617"
-           y="603.98718"
-           id="text4090-8-5-6-9-4-6-6"
+           x="628.93628"
+           y="473.13675"
+           id="text4090-8-1-8-9-1-4-1-4"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="395.79617"
-             y="603.98718"
-             id="tspan4092-8-5-5-3-4-0-6"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
-        <g
-           transform="translate(0,-0.83470152)"
-           id="g3334">
-          <g
-             id="g3267"
-             transform="translate(-13.517932,3.1531035)">
-            <text
-               x="660.46729"
-               y="468.01297"
-               id="text4090-8-1-8-9-1-4-1"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="660.46729"
-                 y="468.01297"
-                 id="tspan4092-8-7-6-9-7-0-7"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
-          </g>
-          <text
-             x="394.78601"
-             y="483.59955"
-             id="text4090-8-5-6-9"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="483.59955"
-               id="tspan4092-8-5-5-3"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
-        </g>
-        <g
-           id="g3428"
-           transform="translate(0,0.88137813)">
-          <text
-             x="394.78601"
-             y="541.38928"
-             id="text4090-8-5-6-9-4-6-1"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="541.38928"
-               id="tspan4092-8-5-5-3-4-0-7"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
-          <g
-             transform="translate(-119.92979,57.949844)"
-             id="g3267-9">
-            <text
-               x="628.93628"
-               y="473.13675"
-               id="text4090-8-1-8-9-1-4-1-4"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="628.93628"
-                 y="473.13675"
-                 id="tspan4092-8-7-6-9-7-0-7-8"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
-          </g>
-        </g>
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="628.93628"
+             y="473.13675"
+             id="tspan4092-8-7-6-9-7-0-7-8"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
       </g>
     </g>
     <text
@@ -1301,7 +1270,7 @@
          id="tspan4092-8-5-5-3-4-0-6-2-11-0"
          style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3595">
       <text
          x="30.942892"
@@ -1332,7 +1301,7 @@
            x="19.552465"
            y="1037.0271"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
       <text
          x="42.830166"
          y="1033.2393"
@@ -1345,7 +1314,7 @@
            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
     </g>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3619">
       <text
          x="42.212418"
@@ -1396,7 +1365,7 @@
            x="14.016749"
            y="1049.8527"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
     </g>
     <rect
        width="196.44218"
diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index d21ee288b2..8ad6b6e715 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
 
   * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
 
-* New external functions should be added to the local ``version.map`` file. See
-  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
-  guides. New external functions should also be added in alphabetical order.
+* New external functions should be exported.
+  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
+  guides.
 
 * Any new API function should be used in ``/app`` test directory.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index 5368d38363..15a4991abf 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -5,8 +5,6 @@ if is_ms_compiler
     subdir_done()
 endif
 
-fs = import('fs')
-
 # Defines the order of dependencies evaluation
 subdirs = [
         'common',
@@ -290,57 +288,25 @@ foreach subpath:subdirs
                 install: true)
 
         # now build the shared driver
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
-
-        if not fs.is_file(version_map)
-            if is_ms_linker
-                link_mode = 'mslinker'
-            elif is_windows
-                link_mode = 'mingw'
-            else
-                link_mode = 'gnu'
-            endif
-            version_map = custom_target(lib_name + '_map',
-                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                    input: sources + sources_avx2 + sources_avx512,
-                    output: '_'.join([class, name, 'exports.map']))
-            version_map_path = version_map.full_path()
-            version_map_dep = [version_map]
-            lk_deps = [version_map]
-
-            if is_ms_linker and is_ms_compiler
-                lk_args = ['/def:' + version_map.full_path()]
-            elif is_ms_linker
-                lk_args = ['-Wl,/def:' + version_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-            endif
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
         else
-            version_map_path = version_map
-            version_map_dep = []
-            lk_deps = [version_map]
-
-            if is_windows
-                if is_ms_linker
-                    def_file = custom_target(lib_name + '_def',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_exports.def'.format(lib_name))
-                    lk_deps += [def_file]
-
-                    lk_args = ['-Wl,/def:' + def_file.full_path()]
-                else
-                    mingw_map = custom_target(lib_name + '_mingw',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_mingw.map'.format(lib_name))
-                    lk_deps += [mingw_map]
-
-                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-                endif
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(lib_name + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources + sources_avx2 + sources_avx512,
+                output: '_'.join([class, name, 'exports.map']))
+        lk_deps = [version_map]
+
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
+        else
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
 
         if not is_windows and developer_mode
@@ -348,11 +314,11 @@ foreach subpath:subdirs
             # check-symbols.sh script, using it as a
             # dependency of the .so build
             lk_deps += custom_target(lib_name + '.sym_chk',
-                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
                     capture: true,
                     input: static_lib,
                     output: lib_name + '.sym_chk',
-                    depends: version_map_dep)
+                    depends: [version_map])
         endif
 
         shared_lib = shared_library(lib_name, sources_pmd_info,
diff --git a/lib/meson.build b/lib/meson.build
index a9cbea5fea..6157d0e13e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
-fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -289,59 +288,25 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
-        if is_ms_linker
-            link_mode = 'mslinker'
-        elif is_windows
-            link_mode = 'mingw'
-        else
-            link_mode = 'gnu'
-        endif
-        version_map = custom_target(libname + '_map',
-                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
-                input: sources,
-                output: '_'.join([name, 'exports.map']))
-        version_map_path = version_map.full_path()
-        version_map_dep = [version_map]
-        lk_deps = [version_map]
-
-        if is_ms_linker and is_ms_compiler
-            lk_args = ['/def:' + version_map.full_path()]
-        elif is_ms_linker
-            lk_args = ['-Wl,/def:' + version_map.full_path()]
-        else
-            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-        endif
+    if is_ms_linker
+        link_mode = 'mslinker'
+    elif is_windows
+        link_mode = 'mingw'
     else
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-        version_map_path = version_map
-        version_map_dep = []
-        lk_deps = [version_map]
-        if is_ms_linker
-            def_file = custom_target(libname + '_def',
-                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                    input: version_map,
-                    output: '@0@_exports.def'.format(libname))
-            lk_deps += [def_file]
-
-            if is_ms_compiler
-                lk_args = ['/def:' + def_file.full_path()]
-            else
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
-            endif
-        else
-            if is_windows
-                mingw_map = custom_target(libname + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(libname))
-                lk_deps += [mingw_map]
+        link_mode = 'gnu'
+    endif
+    version_map = custom_target(libname + '_map',
+            command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+            input: sources,
+            output: '_'.join([name, 'exports.map']))
+    lk_deps = [version_map]
 
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
-        endif
+    if is_ms_linker and is_ms_compiler
+        lk_args = ['/def:' + version_map.full_path()]
+    elif is_ms_linker
+        lk_args = ['-Wl,/def:' + version_map.full_path()]
+    else
+        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
     endif
 
     if developer_mode and not is_windows
@@ -349,11 +314,11 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols, version_map_path, '@INPUT@'],
+                command: [check_symbols, version_map.full_path(), '@INPUT@'],
                 capture: true,
                 input: static_lib,
                 output: name + '.sym_chk',
-                depends: version_map_dep)
+                depends: [version_map])
     endif
 
     if not use_function_versioning or is_windows
-- 
2.48.1


^ permalink raw reply	[relevance 16%]

* [PATCH v6 8/8] eal: rework function versioning macros
  2025-03-28 10:52  3% ` [PATCH v6 " David Marchand
  2025-03-28 10:52 17%   ` [PATCH v6 4/8] build: generate symbol maps David Marchand
  2025-03-28 10:52 16%   ` [PATCH v6 6/8] build: use dynamically generated version maps David Marchand
@ 2025-03-28 10:52 20%   ` David Marchand
  2025-04-02 11:53  0%     ` Thomas Monjalon
  2 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-28 10:52 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
exporting and versioning symbols that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a symbol is just a call to a single macro:
- RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
  keeping an old implementation code under a versioned function (resp.
  experimental function),
- RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

This replaces the macros from rte_function_versioning.h that were
previously publicly exported.

Update lib/net accordingly.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v4:
- moved new macros to eal_symbol_exports.h and simply dropped legacy
  macros/header,
- added release notes update,

Changes since RFC v3:
- fixed documentation and simplified examples,

Changes since RFC v1:
- renamed and prefixed macros,
- reindented in prevision of second patch,

---
 buildtools/gen-version-map.py              |  15 +-
 devtools/check-symbol-change.py            |   8 +-
 doc/api/doxy-api-index.md                  |   1 -
 doc/guides/contributing/abi_versioning.rst | 192 ++++++---------------
 doc/guides/rel_notes/release_25_07.rst     |   2 +
 lib/eal/common/eal_symbol_exports.h        |  66 +++++++
 lib/eal/include/rte_function_versioning.h  |  99 -----------
 lib/net/net_crc.h                          |  15 --
 lib/net/rte_net_crc.c                      |  29 +---
 9 files changed, 135 insertions(+), 292 deletions(-)
 delete mode 100644 lib/eal/include/rte_function_versioning.h

diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
index c7dfc9b8c2..7ee80ec640 100755
--- a/buildtools/gen-version-map.py
+++ b/buildtools/gen-version-map.py
@@ -13,10 +13,9 @@
 export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
 export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
 export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
-# From rte_function_versioning.h
-ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
-ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
-default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
 
 with open(abi_version_file) as f:
     abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
@@ -40,14 +39,14 @@
                 node = abi
                 symbol = export_sym_regexp.match(ln).group(1)
             elif ver_sym_regexp.match(ln):
-                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(2))
-                symbol = ver_sym_regexp.match(ln).group(1)
+                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(1))
+                symbol = ver_sym_regexp.match(ln).group(2)
             elif ver_exp_sym_regexp.match(ln):
                 node = 'EXPERIMENTAL'
                 symbol = ver_exp_sym_regexp.match(ln).group(1)
             elif default_sym_regexp.match(ln):
-                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(2))
-                symbol = default_sym_regexp.match(ln).group(1)
+                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(1))
+                symbol = default_sym_regexp.match(ln).group(2)
 
             if not symbol:
                 continue
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
index d522fbb1ec..d59ecaddd7 100755
--- a/devtools/check-symbol-change.py
+++ b/devtools/check-symbol-change.py
@@ -12,10 +12,10 @@
 export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
 export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
 export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
-# TODO, handle versioned symbols from rte_function_versioning.h
-# ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
-# ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
-# default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+# TODO, handle versioned symbols
+# ver_sym_regexp = re.compile(r"^.RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+# ver_exp_sym_regexp = re.compile(r"^.RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+# default_sym_regexp = re.compile(r"^.RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
 
 symbols = {}
 
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..5c425a2cb9 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -248,7 +248,6 @@ The public API headers are grouped by topics:
   [EAL config](@ref rte_eal.h),
   [common](@ref rte_common.h),
   [experimental APIs](@ref rte_compat.h),
-  [ABI versioning](@ref rte_function_versioning.h),
   [version](@ref rte_version.h)
 
 - **tests**:
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index b6b6bf64d7..a0ce0ce1c9 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -132,32 +132,25 @@ functionality or behavior. When that occurs, it is may be required to allow for
 backward compatibility for a time with older binaries that are dynamically
 linked to the DPDK.
 
-To support backward compatibility the ``rte_function_versioning.h``
+To support backward compatibility the ``eal_symbol_exports.h``
 header file provides macros to use when updating exported functions. These
 macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
-The macros exported are:
+The macros are:
 
-* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
-  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
+* ``RTE_VERSION_SYMBOL(ver, type, name, args)``: Creates a symbol version table
+  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
+  ``<name>_v<ver>``.
 
-* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
-  the linker to bind references to symbol ``b`` to the internal symbol
-  ``be``.
+* ``RTE_DEFAULT_SYMBOL(ver, type, name, args)``: Creates a symbol version entry
+  instructing the linker to bind references to symbol ``<name>`` to the internal
+  symbol ``<name>_v<ver>``.
 
-* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
-  fully qualified function ``p``, so that if a symbol becomes versioned, it
-  can still be mapped back to the public symbol name.
-
-* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
-  ``be`` to signal that it is being used as an implementation of a particular
-  version of symbol ``b``.
-
-* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
-  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
-  The macro is used when a symbol matures to become part of the stable ABI, to
-  provide an alias to experimental until the next major ABI version.
+* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
+  but for experimental API symbols. The macro is used when a symbol matures
+  to become part of the stable ABI, to provide an alias to experimental
+  until the next major ABI version.
 
 .. _example_abi_macro_usage:
 
@@ -176,8 +169,8 @@ Assume we have a function as follows
   * manipulate
   */
  RTE_EXPORT_SYMBOL(rte_acl_create)
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param)
+ int
+ rte_acl_create(struct rte_acl_param *param)
  {
         ...
  }
@@ -195,8 +188,8 @@ private, is safe), but it also requires modifying the code as follows
   * manipulate
   */
  RTE_EXPORT_SYMBOL(rte_acl_create)
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param, int debug)
+ int
+ rte_acl_create(struct rte_acl_param *param, int debug)
  {
         ...
  }
@@ -215,87 +208,48 @@ application was linked to it.
 
 We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
+the function return type, the function name and its arguments.
 
 .. code-block:: c
 
  -RTE_EXPORT_SYMBOL(rte_acl_create)
- -struct rte_acl_ctx *
- -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ -int
+ -rte_acl_create(struct rte_acl_param *param)
+ +RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
 
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
-
-Remembering to also add the rte_function_versioning.h header to the requisite c
-file where these changes are being made. The macro instructs the linker to
-create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+The macro instructs the linker to create a new symbol ``rte_acl_create@DPDK_21``,
+which matches the symbol created in older builds,
+but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
    {
-        struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
+        int ret = rte_acl_create_v21(param);
 
-        ctx->debug = debug;
+        if (debug) {
+        ...
+        }
 
-        return ctx;
+        return ret;
    }
 
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
-
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
 an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
@@ -304,43 +258,10 @@ used by newly built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -384,8 +305,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     */
    RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
    ...
    }
@@ -400,8 +321,8 @@ When we promote the symbol to the stable ABI, we simply strip the
     * manipulate
     */
    RTE_EXPORT_SYMBOL(rte_acl_create)
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
           ...
    }
@@ -416,33 +337,20 @@ and ``DPDK_22`` version nodes.
 .. code-block:: c
 
    #include <rte_compat.h>;
-   #include <rte_function_versioning.h>
 
    /*
     * Create an acl context object for apps to
     * manipulate
     */
-   RTE_EXPORT_SYMBOL(rte_acl_create)
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param))
    {
    ...
    }
 
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
-
-   struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_acl_create, (struct rte_acl_param *param))
    {
       return rte_acl_create(param);
    }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
 .. _abi_deprecation:
 
@@ -458,10 +366,10 @@ Next remove the corresponding versioned export.
 
 .. code-block:: c
 
- -VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
 
 
-Note that the internal function definition could also be removed, but its used
+Note that the internal function definition must also be removed, but it is used
 in our example by the newer version ``v22``, so we leave it in place and declare
 it as static. This is a coding style choice.
 
@@ -476,18 +384,16 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
- };
-
-Any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
 .. code-block:: c
 
- -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21);
- +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+ -RTE_DEFAULT_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param, int debug))
+ +RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
 
-Lastly, any VERSION_SYMBOL macros that point to the old version nodes
+Lastly, any RTE_VERSION_SYMBOL macros that point to the old version nodes
 should be removed, taking care to preserve any code that is shared
 with the new version node.
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..093b85d206 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -68,6 +68,8 @@ Removed Items
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* eal: Removed the ``rte_function_versioning.h`` header from the exported headers.
+
 
 API Changes
 -----------
diff --git a/lib/eal/common/eal_symbol_exports.h b/lib/eal/common/eal_symbol_exports.h
index b3033dd336..b66ca8fa68 100644
--- a/lib/eal/common/eal_symbol_exports.h
+++ b/lib/eal/common/eal_symbol_exports.h
@@ -5,6 +5,8 @@
 #ifndef EAL_SYMBOL_EXPORTS_H
 #define EAL_SYMBOL_EXPORTS_H
 
+#include <rte_common.h>
+
 /* Internal macros for exporting symbols, used by the build system.
  * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
  * version this symbol was introduced in.
@@ -13,4 +15,68 @@
 #define RTE_EXPORT_INTERNAL_SYMBOL(a)
 #define RTE_EXPORT_SYMBOL(a)
 
+#if !defined(RTE_USE_FUNCTION_VERSIONING) && (defined(RTE_CC_GCC) || defined(RTE_CC_CLANG))
+#define VERSIONING_WARN RTE_PRAGMA_WARNING(Use of function versioning disabled. \
+	Is "use_function_versioning=true" in meson.build?)
+#else
+#define VERSIONING_WARN
+#endif
+
+/*
+ * Provides backwards compatibility when updating exported functions.
+ * When a symbol is exported from a library to provide an API, it also provides a
+ * calling convention (ABI) that is embodied in its name, return type,
+ * arguments, etc.  On occasion that function may need to change to accommodate
+ * new functionality, behavior, etc.  When that occurs, it is desirable to
+ * allow for backwards compatibility for a time with older binaries that are
+ * dynamically linked to the dpdk.
+ */
+
+#ifdef RTE_BUILD_SHARED_LIB
+
+/*
+ * RTE_VERSION_SYMBOL
+ * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
+ * function name <name>_v<ver>.
+ */
+#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+/*
+ * RTE_VERSION_EXPERIMENTAL_SYMBOL
+ * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
+ * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
+ */
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
+__rte_used type name ## _exp args; \
+type name ## _exp args
+
+/*
+ * RTE_DEFAULT_SYMBOL
+ * Creates a symbol version entry instructing the linker to bind references to
+ * symbol <name> to the internal symbol <name>_v<ver>.
+ */
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+#else /* !RTE_BUILD_SHARED_LIB */
+
+#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
+type name ## _exp args; \
+type name ## _exp args
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+type name args
+
+#endif /* RTE_BUILD_SHARED_LIB */
+
 #endif /* EAL_SYMBOL_EXPORTS_H */
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
deleted file mode 100644
index eb6dd2bc17..0000000000
--- a/lib/eal/include/rte_function_versioning.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2015 Neil Horman <nhorman@tuxdriver.com>.
- * All rights reserved.
- */
-
-#ifndef _RTE_FUNCTION_VERSIONING_H_
-#define _RTE_FUNCTION_VERSIONING_H_
-#include <rte_common.h>
-
-#ifndef RTE_USE_FUNCTION_VERSIONING
-#error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
-#endif
-
-#ifdef RTE_BUILD_SHARED_LIB
-
-/*
- * Provides backwards compatibility when updating exported functions.
- * When a symbol is exported from a library to provide an API, it also provides a
- * calling convention (ABI) that is embodied in its name, return type,
- * arguments, etc.  On occasion that function may need to change to accommodate
- * new functionality, behavior, etc.  When that occurs, it is desirable to
- * allow for backwards compatibility for a time with older binaries that are
- * dynamically linked to the dpdk.  To support that, the __vsym and
- * VERSION_SYMBOL macros are created.  They, in conjunction with the
- * version.map file for a given library allow for multiple versions of
- * a symbol to exist in a shared library so that older binaries need not be
- * immediately recompiled.
- *
- * Refer to the guidelines document in the docs subdirectory for details on the
- * use of these macros
- */
-
-/*
- * Macro Parameters:
- * b - function base name
- * e - function version extension, to be concatenated with base name
- * n - function symbol version string to be applied
- * f - function prototype
- * p - full function symbol name
- */
-
-/*
- * VERSION_SYMBOL
- * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
- * function name <b><e>
- */
-#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
-
-/*
- * VERSION_SYMBOL_EXPERIMENTAL
- * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
- * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
- * to provide an alias to experimental for some time.
- */
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
-
-/*
- * BIND_DEFAULT_SYMBOL
- * Creates a symbol version entry instructing the linker to bind references to
- * symbol <b> to the internal symbol <b><e>
- */
-#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
-
-/*
- * __vsym
- * Annotation to be used in declaration of the internal symbol <b><e> to signal
- * that it is being used as an implementation of a particular version of symbol
- * <b>.
- */
-#define __vsym __rte_used
-
-/*
- * MAP_STATIC_SYMBOL
- * If a function has been bifurcated into multiple versions, none of which
- * are defined as the exported symbol name in the map file, this macro can be
- * used to alias a specific version of the symbol to its exported name.  For
- * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
- * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
- * building a shared library, this macro can be used to map either foo_v1 or
- * foo_v2 to the symbol foo when building a static library, e.g.:
- * MAP_STATIC_SYMBOL(void foo(), foo_v2);
- */
-#define MAP_STATIC_SYMBOL(f, p)
-
-#else
-/*
- * No symbol versioning in use
- */
-#define VERSION_SYMBOL(b, e, n)
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
-#define __vsym
-#define BIND_DEFAULT_SYMBOL(b, e, n)
-#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
-/*
- * RTE_BUILD_SHARED_LIB=n
- */
-#endif
-
-#endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 4930e2f0b3..320b0edca8 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -7,21 +7,6 @@
 
 #include "rte_net_crc.h"
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
-
-struct rte_net_crc *
-rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len, enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len);
-
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index 4efb095bc4..e769c7e22e 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -11,7 +11,6 @@
 #include <rte_net_crc.h>
 #include <rte_log.h>
 #include <rte_vect.h>
-#include <rte_function_versioning.h>
 #include <rte_malloc.h>
 
 #include "net_crc.h"
@@ -346,8 +345,7 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+RTE_VERSION_SYMBOL(25, void, rte_net_crc_set_alg, (enum rte_net_crc_alg alg))
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -374,10 +372,9 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+RTE_DEFAULT_SYMBOL(26, struct rte_net_crc *, rte_net_crc_set_alg, (enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type))
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -415,10 +412,6 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 	}
 	return crc;
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
 
 RTE_EXPORT_SYMBOL(rte_net_crc_free)
 void rte_net_crc_free(struct rte_net_crc *crc)
@@ -426,10 +419,8 @@ void rte_net_crc_free(struct rte_net_crc *crc)
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
+	enum rte_net_crc_type type))
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -439,18 +430,12 @@ rte_net_crc_calc_v25(const void *data,
 
 	return ret;
 }
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+RTE_DEFAULT_SYMBOL(26, uint32_t, rte_net_crc_calc, (const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len))
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.48.1


^ permalink raw reply	[relevance 20%]

* [PATCH v6 4/8] build: generate symbol maps
  2025-03-28 10:52  3% ` [PATCH v6 " David Marchand
@ 2025-03-28 10:52 17%   ` David Marchand
  2025-04-01 20:33  0%     ` Thomas Monjalon
  2025-03-28 10:52 16%   ` [PATCH v6 6/8] build: use dynamically generated version maps David Marchand
  2025-03-28 10:52 20%   ` [PATCH v6 8/8] eal: rework function versioning macros David Marchand
  2 siblings, 1 reply; 153+ results
From: David Marchand @ 2025-03-28 10:52 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff

Rather than maintain a file in parallel of the code, symbols to be
exported can be marked with a token RTE_EXPORT_*SYMBOL.

From those marks, the build framework generates map files only for
symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
becomes unnecessary).

The build framework directly creates a map file in the format that the
linker expects (rather than converting from GNU linker to MSVC linker).

Empty maps are allowed again as a replacement for drivers/version.map.

The symbol check is updated to only support the new format.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v4:
- fixed MSVC export map (a msvc->mslinker update was missing),
- fixed join() error (with older meson? I don't see this on Fedora),
- explicit inclusion of header is now required,
- header has been renamed and moved to lib/eal/common/,
- because lib/log does not depend on EAL, added explicit
  include_directories,
- symbol versioning update has been moved later in the series,
  so updated gen-version-map.py accordingly,
- fixed bug when checking symbol removal,

Changes since RFC v3:
- polished python,
- fixed doc updates not belonging to this patch,
- renamed map files,
- changed msvc->mslinker as link mode,
- added parsing of AVX sources,

Changes since RFC v2:
- because of MSVC limitations wrt macro passed via cmdline,
  used an internal header for defining RTE_EXPORT_* macros,
- updated documentation and tooling,

---
 MAINTAINERS                                |   2 +
 buildtools/gen-version-map.py              | 106 ++++++++++
 buildtools/map-list-symbol.sh              |  10 +-
 buildtools/meson.build                     |   1 +
 devtools/check-symbol-change.py            |  90 ++++++++
 devtools/check-symbol-maps.sh              |  14 --
 devtools/checkpatches.sh                   |   2 +-
 doc/guides/contributing/abi_versioning.rst | 227 ++-------------------
 drivers/meson.build                        |  96 +++++----
 drivers/version.map                        |   3 -
 lib/eal/common/eal_symbol_exports.h        |  16 ++
 lib/meson.build                            |  94 ++++++---
 12 files changed, 365 insertions(+), 296 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100644 drivers/version.map
 create mode 100644 lib/eal/common/eal_symbol_exports.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4b01103f8e..42ea07854b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
 F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
+F: devtools/check-symbol-change.py
 F: devtools/check-symbol-change.sh
 F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
@@ -127,6 +128,7 @@ F: config/
 F: buildtools/check-symbols.sh
 F: buildtools/chkincs/
 F: buildtools/call-sphinx-build.py
+F: buildtools/gen-version-map.py
 F: buildtools/get-cpu-count.py
 F: buildtools/get-numa-count.py
 F: buildtools/list-dir-globs.py
diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
new file mode 100755
index 0000000000..c7dfc9b8c2
--- /dev/null
+++ b/buildtools/gen-version-map.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Generate a version map file used by GNU or MSVC linker."""
+
+import re
+import sys
+
+scriptname, link_mode, abi_version_file, output, *files = sys.argv
+
+# From eal_symbol_exports.h
+export_exp_sym_regexp = re.compile(r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)")
+export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# From rte_function_versioning.h
+ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
+default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+
+with open(abi_version_file) as f:
+    abi = 'DPDK_{}'.format(re.match("([0-9]+).[0-9]", f.readline()).group(1))
+
+symbols = {}
+
+for file in files:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            node = None
+            symbol = None
+            comment = ''
+            if export_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                comment = ' # added in {}'.format(export_exp_sym_regexp.match(ln).group(2))
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                node = abi
+                symbol = export_sym_regexp.match(ln).group(1)
+            elif ver_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(ver_sym_regexp.match(ln).group(2))
+                symbol = ver_sym_regexp.match(ln).group(1)
+            elif ver_exp_sym_regexp.match(ln):
+                node = 'EXPERIMENTAL'
+                symbol = ver_exp_sym_regexp.match(ln).group(1)
+            elif default_sym_regexp.match(ln):
+                node = 'DPDK_{}'.format(default_sym_regexp.match(ln).group(2))
+                symbol = default_sym_regexp.match(ln).group(1)
+
+            if not symbol:
+                continue
+
+            if node not in symbols:
+                symbols[node] = {}
+            symbols[node][symbol] = comment
+
+if link_mode == 'mslinker':
+    with open(output, "w") as outfile:
+        print(f"EXPORTS", file=outfile)
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            for symbol in sorted(symbols[key].keys()):
+                print(f"\t{symbol}", file=outfile)
+            del symbols[key]
+else:
+    with open(output, "w") as outfile:
+        local_token = False
+        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
+            if key not in symbols:
+                continue
+            print(f"{key} {{\n\tglobal:\n", file=outfile)
+            for symbol in sorted(symbols[key].keys()):
+                if link_mode == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                comment = symbols[key][symbol]
+                print(f"\t{prefix}{symbol};{comment}", file=outfile)
+            if not local_token:
+                print("\n\tlocal: *;", file=outfile)
+                local_token = True
+            print("};", file=outfile)
+            del symbols[key]
+        for key in sorted(symbols.keys()):
+            print(f"{key} {{\n\tglobal:\n", file=outfile)
+            for symbol in sorted(symbols[key].keys()):
+                if link_mode == 'mingw' and symbol.startswith('per_lcore'):
+                    prefix = '__emutls_v.'
+                else:
+                    prefix = ''
+                comment = symbols[key][symbol]
+                print(f"\t{prefix}{symbol};{comment}", file=outfile)
+            print(f"}} {abi};", file=outfile)
+            if not local_token:
+                print("\n\tlocal: *;", file=outfile)
+                local_token = True
+            del symbols[key]
+        # No exported symbol, add a catch all
+        if not local_token:
+            print(f"{abi} {{", file=outfile)
+            print("\n\tlocal: *;", file=outfile)
+            local_token = True
+            print("};", file=outfile)
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index eb98451d8e..0829df4be5 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -62,10 +62,14 @@ for file in $@; do
 		if (current_section == "") {
 			next;
 		}
+		symbol_version = current_version
+		if (/^[^}].*[^:*]; # added in /) {
+			symbol_version = $5
+		}
 		if ("'$version'" != "") {
-			if ("'$version'" == "unset" && current_version != "") {
+			if ("'$version'" == "unset" && symbol_version != "") {
 				next;
-			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
+			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
 				next;
 			}
 		}
@@ -73,7 +77,7 @@ for file in $@; do
 		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
 			ret = 0;
 			if ("'$quiet'" == "") {
-				print "'$file' "current_section" "$1" "current_version;
+				print "'$file' "current_section" "$1" "symbol_version;
 			}
 			if ("'$symbol'" != "all") {
 				exit 0;
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 4e2c1217a2..b745e9afa4 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -16,6 +16,7 @@ else
     py3 = ['meson', 'runpython']
 endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
+gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
new file mode 100755
index 0000000000..d522fbb1ec
--- /dev/null
+++ b/devtools/check-symbol-change.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Check exported symbols change in a patch."""
+
+import re
+import sys
+
+file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
+# From eal_symbol_exports.h
+export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
+export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# TODO, handle versioned symbols from rte_function_versioning.h
+# ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+# ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
+# default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+
+symbols = {}
+
+for file in sys.argv[1:]:
+    with open(file, encoding="utf-8") as f:
+        for ln in f.readlines():
+            if file_header_regexp.match(ln):
+                if file_header_regexp.match(ln).group(2) == "lib":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+                elif file_header_regexp.match(ln).group(3) == "intel":
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
+                else:
+                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
+
+                if lib not in symbols:
+                    symbols[lib] = {}
+                continue
+
+            if export_exp_sym_regexp.match(ln):
+                symbol = export_exp_sym_regexp.match(ln).group(1)
+                node = 'EXPERIMENTAL'
+            elif export_int_sym_regexp.match(ln):
+                node = 'INTERNAL'
+                symbol = export_int_sym_regexp.match(ln).group(1)
+            elif export_sym_regexp.match(ln):
+                symbol = export_sym_regexp.match(ln).group(1)
+                node = 'stable'
+            else:
+                continue
+
+            if symbol not in symbols[lib]:
+                symbols[lib][symbol] = {}
+            added = ln[0] == '+'
+            if added and 'added' in symbols[lib][symbol] and node != symbols[lib][symbol]['added']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if not added and 'removed' in symbols[lib][symbol] and node != symbols[lib][symbol]['removed']:
+                print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+            if added:
+                symbols[lib][symbol]['added'] = node
+            else:
+                symbols[lib][symbol]['removed'] = node
+
+    for lib in sorted(symbols.keys()):
+        error = False
+        for symbol in sorted(symbols[lib].keys()):
+            if 'removed' not in symbols[lib][symbol]:
+                # Symbol addition
+                node = symbols[lib][symbol]['added']
+                if node == 'stable':
+                    print(f"ERROR: {symbol} in {lib} has been added directly to stable ABI.")
+                    error = True
+                else:
+                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
+                continue
+
+            if 'added' not in symbols[lib][symbol]:
+                # Symbol removal
+                node = symbols[lib][symbol]['removed']
+                if node == 'stable':
+                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
+                    print(f"Please check it has gone though the deprecation process.")
+                continue
+
+            if symbols[lib][symbol]['added'] == symbols[lib][symbol]['removed']:
+                # Symbol was moved around
+                continue
+
+            # Symbol modifications
+            added = symbols[lib][symbol]['added']
+            removed = symbols[lib][symbol]['removed']
+            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
+            print(f"Please check it has gone though the deprecation process.")
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
index 6121f78ec6..fcd3931e5d 100755
--- a/devtools/check-symbol-maps.sh
+++ b/devtools/check-symbol-maps.sh
@@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
     ret=1
 fi
 
-find_empty_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
-    done
-}
-
-empty_maps=$(find_empty_maps $@)
-if [ -n "$empty_maps" ] ; then
-    echo "Found empty maps:"
-    echo "$empty_maps"
-    ret=1
-fi
-
 find_bad_format_maps ()
 {
     abi_version=$(cut -d'.' -f 1 ABI_VERSION)
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index c9088bb403..9180c2b070 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
 PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
 SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
 LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
-NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
+NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
 options="$options $DPDK_CHECKPATCH_OPTIONS"
 
 print_usage () {
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..b6b6bf64d7 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -58,12 +58,12 @@ persists over multiple releases.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -77,7 +77,7 @@ that library.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
@@ -88,7 +88,7 @@ that library.
  } DPDK_21;
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -100,12 +100,12 @@ how this may be done.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_22 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_22 {
         global:
  ...
@@ -134,8 +134,7 @@ linked to the DPDK.
 
 To support backward compatibility the ``rte_function_versioning.h``
 header file provides macros to use when updating exported functions. These
-macros are used in conjunction with the ``version.map`` file for
-a given library to allow multiple versions of a symbol to exist in a shared
+macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
 The macros exported are:
@@ -176,6 +175,7 @@ Assume we have a function as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param)
  {
@@ -194,6 +194,7 @@ private, is safe), but it also requires modifying the code as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param, int debug)
  {
@@ -210,78 +211,16 @@ The addition of a parameter to the function is ABI breaking as the function is
 public, and existing application may use it in its current form. However, the
 compatibility macros in DPDK allow a developer to use symbol versioning so that
 multiple functions can be mapped to the same public symbol based on when an
-application was linked to it. To see how this is done, we start with the
-requisite libraries version map file. Initially the version map file for the acl
-library looks like this
+application was linked to it.
 
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-This file needs to be modified as follows
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-
-   } DPDK_21;
-
-The addition of the new block tells the linker that a new version node
-``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
-the symbols from the DPDK_21 node. This list is directly translated into a
-list of exported symbols when DPDK is compiled as a shared library.
-
-Next, we need to specify in the code which function maps to the rte_acl_create
+We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
 we need to update the function so that it is uniquely named, and not in conflict
 with the public symbol name
 
 .. code-block:: c
 
+ -RTE_EXPORT_SYMBOL(rte_acl_create)
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
  +struct rte_acl_ctx * __vsym
@@ -358,9 +297,9 @@ such that it contains both versions of the symbol and the public API.
    rte_acl_create_v22(const struct rte_acl_param *param, int debug);
 
 
-And that's it, on the next shared library rebuild, there will be two versions of
-rte_acl_create, an old DPDK_21 version, used by previously built applications,
-and a new DPDK_22 version, used by future built applications.
+And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
+an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
+used by newly built applications.
 
 .. note::
 
@@ -443,6 +382,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
@@ -450,27 +390,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
    ...
    }
 
-In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL``
-version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 When we promote the symbol to the stable ABI, we simply strip the
-``__rte_experimental`` annotation from the function and move the symbol from the
-``EXPERIMENTAL`` node, to the node of the next major ABI version as follow.
+``__rte_experimental`` annotation from the function.
 
 .. code-block:: c
 
@@ -478,31 +399,13 @@ When we promote the symbol to the stable ABI, we simply strip the
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
           ...
    }
 
-We then update the map file, adding the symbol ``rte_acl_create``
-to the ``DPDK_22`` version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-
 Although there are strictly no guarantees or commitments associated with
 :ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer
 an alias to experimental. The process to add an alias to experimental,
@@ -519,6 +422,7 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
@@ -540,37 +444,6 @@ and ``DPDK_22`` version nodes.
    }
    BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
-In the map file, we map the symbol to both the ``EXPERIMENTAL``
-and ``DPDK_22`` version nodes.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -579,38 +452,7 @@ ________________________________
 Lets assume that you've done the above updates, and in preparation for the next
 major ABI version you decide you would like to retire the old version of the
 function. After having gone through the ABI deprecation announcement process,
-removal is easy. Start by removing the symbol from the requisite version map
-file:
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
- -      rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-   } DPDK_21;
-
+removal is easy.
 
 Next remove the corresponding versioned export.
 
@@ -634,36 +476,9 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
-To do this, start by modifying the version map file, such that all symbols from
-the node to be removed are merged into the next node in the map.
-
-In the case of our map above, it would transform to look as follows
-
-.. code-block:: none
-
-   DPDK_22 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
-        rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
  };
 
-Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index c15319dc24..5368d38363 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -275,14 +275,14 @@ foreach subpath:subdirs
                 dependencies: static_deps,
                 c_args: cflags)
         objs += tmp_lib.extract_all_objects(recursive: true)
-        sources = custom_target(out_filename,
+        sources_pmd_info = custom_target(out_filename,
                 command: [pmdinfo, tmp_lib.full_path(), '@OUTPUT@', pmdinfogen],
                 output: out_filename,
                 depends: [tmp_lib])
 
         # now build the static driver
         static_lib = static_library(lib_name,
-                sources,
+                sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: static_deps,
@@ -292,48 +292,70 @@ foreach subpath:subdirs
         # now build the shared driver
         version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
 
-        lk_deps = []
-        lk_args = []
         if not fs.is_file(version_map)
-            version_map = '@0@/version.map'.format(meson.current_source_dir())
-            lk_deps += [version_map]
-        else
-            lk_deps += [version_map]
-            if not is_windows and developer_mode
-                # on unix systems check the output of the
-                # check-symbols.sh script, using it as a
-                # dependency of the .so build
-                lk_deps += custom_target(lib_name + '.sym_chk',
-                        command: [check_symbols, version_map, '@INPUT@'],
-                        capture: true,
-                        input: static_lib,
-                        output: lib_name + '.sym_chk')
-            endif
-        endif
-
-        if is_windows
             if is_ms_linker
-                def_file = custom_target(lib_name + '_def',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_exports.def'.format(lib_name))
-                lk_deps += [def_file]
-
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
+                link_mode = 'mslinker'
+            elif is_windows
+                link_mode = 'mingw'
             else
-                mingw_map = custom_target(lib_name + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(lib_name))
-                lk_deps += [mingw_map]
-
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                link_mode = 'gnu'
+            endif
+            version_map = custom_target(lib_name + '_map',
+                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                    input: sources + sources_avx2 + sources_avx512,
+                    output: '_'.join([class, name, 'exports.map']))
+            version_map_path = version_map.full_path()
+            version_map_dep = [version_map]
+            lk_deps = [version_map]
+
+            if is_ms_linker and is_ms_compiler
+                lk_args = ['/def:' + version_map.full_path()]
+            elif is_ms_linker
+                lk_args = ['-Wl,/def:' + version_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
             endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            version_map_path = version_map
+            version_map_dep = []
+            lk_deps = [version_map]
+
+            if is_windows
+                if is_ms_linker
+                    def_file = custom_target(lib_name + '_def',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_exports.def'.format(lib_name))
+                    lk_deps += [def_file]
+
+                    lk_args = ['-Wl,/def:' + def_file.full_path()]
+                else
+                    mingw_map = custom_target(lib_name + '_mingw',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_mingw.map'.format(lib_name))
+                    lk_deps += [mingw_map]
+
+                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                endif
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
+        endif
+
+        if not is_windows and developer_mode
+            # on unix systems check the output of the
+            # check-symbols.sh script, using it as a
+            # dependency of the .so build
+            lk_deps += custom_target(lib_name + '.sym_chk',
+                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    capture: true,
+                    input: static_lib,
+                    output: lib_name + '.sym_chk',
+                    depends: version_map_dep)
         endif
 
-        shared_lib = shared_library(lib_name, sources,
+        shared_lib = shared_library(lib_name, sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: shared_deps,
diff --git a/drivers/version.map b/drivers/version.map
deleted file mode 100644
index 17cc97bda6..0000000000
--- a/drivers/version.map
+++ /dev/null
@@ -1,3 +0,0 @@
-DPDK_25 {
-	local: *;
-};
diff --git a/lib/eal/common/eal_symbol_exports.h b/lib/eal/common/eal_symbol_exports.h
new file mode 100644
index 0000000000..b3033dd336
--- /dev/null
+++ b/lib/eal/common/eal_symbol_exports.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef EAL_SYMBOL_EXPORTS_H
+#define EAL_SYMBOL_EXPORTS_H
+
+/* Internal macros for exporting symbols, used by the build system.
+ * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
+ * version this symbol was introduced in.
+ */
+#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
+#define RTE_EXPORT_INTERNAL_SYMBOL(a)
+#define RTE_EXPORT_SYMBOL(a)
+
+#endif /* EAL_SYMBOL_EXPORTS_H */
diff --git a/lib/meson.build b/lib/meson.build
index 91efc65c4b..a9cbea5fea 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
+fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -137,9 +138,12 @@ foreach l:libraries
     # external package/library requirements
     ext_deps = []
     deps = []
-    # eal is standard dependency once built
     if dpdk_conf.has('RTE_LIB_EAL')
+        # eal is standard dependency once built
         deps += ['eal']
+    else
+        # otherwise, make private headers available (like eal_symbol_exports.h)
+        includes += include_directories('eal/common')
     endif
 
     if dpdk_libs_deprecated.contains(l)
@@ -285,42 +289,58 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not use_function_versioning or is_windows
-        # use pre-build objects to build shared lib
-        sources = []
-        objs += static_lib.extract_all_objects(recursive: false)
-    else
-        # for compat we need to rebuild with
-        # RTE_BUILD_SHARED_LIB defined
-        cflags += '-DRTE_BUILD_SHARED_LIB'
-    endif
-
-    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-    lk_deps = [version_map]
-
-    if is_ms_linker
-        def_file = custom_target(libname + '_def',
-                command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                input: version_map,
-                output: '@0@_exports.def'.format(libname))
-        lk_deps += [def_file]
+    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
+        else
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(libname + '_map',
+                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
+                input: sources,
+                output: '_'.join([name, 'exports.map']))
+        version_map_path = version_map.full_path()
+        version_map_dep = [version_map]
+        lk_deps = [version_map]
 
-        if is_ms_compiler
-            lk_args = ['/def:' + def_file.full_path()]
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
         else
-            lk_args = ['-Wl,/def:' + def_file.full_path()]
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
     else
-        if is_windows
-            mingw_map = custom_target(libname + '_mingw',
+        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
+        version_map_path = version_map
+        version_map_dep = []
+        lk_deps = [version_map]
+        if is_ms_linker
+            def_file = custom_target(libname + '_def',
                     command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
                     input: version_map,
-                    output: '@0@_mingw.map'.format(libname))
-            lk_deps += [mingw_map]
+                    output: '@0@_exports.def'.format(libname))
+            lk_deps += [def_file]
 
-            lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            if is_ms_compiler
+                lk_args = ['/def:' + def_file.full_path()]
+            else
+                lk_args = ['-Wl,/def:' + def_file.full_path()]
+            endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            if is_windows
+                mingw_map = custom_target(libname + '_mingw',
+                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                        input: version_map,
+                        output: '@0@_mingw.map'.format(libname))
+                lk_deps += [mingw_map]
+
+                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
         endif
     endif
 
@@ -329,11 +349,21 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols,
-                    version_map, '@INPUT@'],
+                command: [check_symbols, version_map_path, '@INPUT@'],
                 capture: true,
                 input: static_lib,
-                output: name + '.sym_chk')
+                output: name + '.sym_chk',
+                depends: version_map_dep)
+    endif
+
+    if not use_function_versioning or is_windows
+        # use pre-build objects to build shared lib
+        sources = []
+        objs += static_lib.extract_all_objects(recursive: false)
+    else
+        # for compat we need to rebuild with
+        # RTE_BUILD_SHARED_LIB defined
+        cflags += '-DRTE_BUILD_SHARED_LIB'
     endif
 
     shared_lib = shared_library(libname,
-- 
2.48.1


^ permalink raw reply	[relevance 17%]

* Re: [PATCH v5 6/8] build: use dynamically generated version maps
  2025-03-27 13:36 16%   ` [PATCH v5 6/8] build: use dynamically generated version maps David Marchand
@ 2025-03-28 13:19  0%     ` Aaron Conole
  0 siblings, 0 replies; 153+ results
From: Aaron Conole @ 2025-03-28 13:19 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, thomas, bruce.richardson, andremue, Michael Santana,
	Dmitry Kozlyuk, Tyler Retzlaff

David Marchand <david.marchand@redhat.com> writes:

> Switch to dynamically generated version maps.
>
> As the map files get generated, tooling around checking, converting,
> updating etc.. static version maps can be removed.
>
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---

Just a minor (non-blocking) nit below.

Otherwise,
Acked-by: Aaron Conole <aconole@redhat.com>

>  .github/workflows/build.yml                   |   1 -
>  MAINTAINERS                                   |   7 -
>  buildtools/check-symbols.sh                   |  33 +-
>  buildtools/map-list-symbol.sh                 |   7 +-
>  buildtools/map_to_win.py                      |  41 ---
>  buildtools/meson.build                        |   1 -
>  devtools/check-symbol-change.sh               | 186 -----------
>  devtools/check-symbol-maps.sh                 | 101 ------
>  devtools/checkpatches.sh                      |   2 +-
>  devtools/update-abi.sh                        |  46 ---
>  devtools/update_version_map_abi.py            | 210 ------------
>  doc/guides/contributing/abi_policy.rst        |  21 +-
>  doc/guides/contributing/coding_style.rst      |   7 -
>  .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
>  doc/guides/contributing/patches.rst           |   6 +-
>  drivers/meson.build                           |  74 ++---
>  lib/meson.build                               |  73 ++---
>  17 files changed, 188 insertions(+), 931 deletions(-)
>  delete mode 100644 buildtools/map_to_win.py
>  delete mode 100755 devtools/check-symbol-change.sh
>  delete mode 100755 devtools/check-symbol-maps.sh
>  delete mode 100755 devtools/update-abi.sh
>  delete mode 100755 devtools/update_version_map_abi.py
>
> diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
> index 0cc4d12b0b..7a6b679fe5 100644
> --- a/.github/workflows/build.yml
> +++ b/.github/workflows/build.yml
> @@ -31,7 +31,6 @@ jobs:
>          failed=
>          devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
>          devtools/check-meson.py || failed=true
> -        devtools/check-symbol-maps.sh || failed=true
>          [ -z "$failed" ]
>    ubuntu-vm-builds:
>      name: ${{ join(matrix.config.*, '-') }}
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 42ea07854b..480972ef1e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
>  F: MAINTAINERS
>  F: devtools/build-dict.sh
>  F: devtools/check-abi.sh
> -F: devtools/check-abi-version.sh
>  F: devtools/check-doc-vs-code.sh
>  F: devtools/check-dup-includes.sh
>  F: devtools/check-maintainers.sh
> @@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
>  F: devtools/check-git-log.sh
>  F: devtools/check-spdx-tag.sh
>  F: devtools/check-symbol-change.py
> -F: devtools/check-symbol-change.sh
> -F: devtools/check-symbol-maps.sh
>  F: devtools/checkpatches.sh
>  F: devtools/get-maintainer.sh
>  F: devtools/git-log-fixes.sh
>  F: devtools/load-devel-config
>  F: devtools/parse-flow-support.sh
>  F: devtools/process-iwyu.py
> -F: devtools/update-abi.sh
>  F: devtools/update-patches.py
> -F: devtools/update_version_map_abi.py
>  F: devtools/libabigail.abignore
>  F: devtools/words-case.txt
>  F: license/
> @@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
>  F: lib/eal/common/
>  F: lib/eal/unix/
>  F: lib/eal/include/
> -F: lib/eal/version.map
>  F: doc/guides/prog_guide/env_abstraction_layer.rst
>  F: app/test/test_alarm.c
>  F: app/test/test_atomic.c
> @@ -396,7 +390,6 @@ Windows support
>  M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
>  M: Tyler Retzlaff <roretzla@linux.microsoft.com>
>  F: lib/eal/windows/
> -F: buildtools/map_to_win.py
>  F: doc/guides/windows_gsg/
>  
>  Windows memory allocation
> diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
> index b8ac24391e..0d6745ec14 100755
> --- a/buildtools/check-symbols.sh
> +++ b/buildtools/check-symbols.sh
> @@ -7,29 +7,12 @@ OBJFILE=$2
>  
>  ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
>  LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
> -CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
> -
> -# added check for "make -C test/" usage
> -if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
> -then
> -	exit 0
> -fi
> -
> -if [ -d $MAPFILE ]
> -then
> -	exit 0
> -fi
> -
>  DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
>  trap 'rm -f "$DUMPFILE"' EXIT
>  objdump -t $OBJFILE >$DUMPFILE
>  
>  ret=0
>  
> -if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
> -	ret=1
> -fi
> -
>  for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
>  do
>  	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
> @@ -37,8 +20,7 @@ do
>  		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
>  	then
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is not flagged as experimental
> -		but is listed in version map
> +		$SYM is not flagged as experimental but is exported as an experimental symbol
>  		Please add __rte_experimental to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
> @@ -53,9 +35,8 @@ for SYM in `awk '{
>  do
>  	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is flagged as experimental
> -		but is not listed in version map
> -		Please add $SYM to the version map
> +		$SYM is flagged as experimental but is not exported as an experimental symbol
> +		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
>  	}
> @@ -67,8 +48,7 @@ do
>  		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
>  	then
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is not flagged as internal
> -		but is listed in version map
> +		$SYM is not flagged as internal but is exported as an internal symbol
>  		Please add __rte_internal to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
> @@ -83,9 +63,8 @@ for SYM in `awk '{
>  do
>  	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is flagged as internal
> -		but is not listed in version map
> -		Please add $SYM to the version map
> +		$SYM is flagged as internal but is not exported as an internal symbol
> +		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
>  	}
> diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
> index 0829df4be5..962d5f3271 100755
> --- a/buildtools/map-list-symbol.sh
> +++ b/buildtools/map-list-symbol.sh
> @@ -42,7 +42,6 @@ for file in $@; do
>  	cat "$file" |awk '
>  	BEGIN {
>  		current_section = "";
> -		current_version = "";
>  		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
>  			ret = 0;
>  		} else {
> @@ -54,15 +53,11 @@ for file in $@; do
>  			current_section = $1;
>  		}
>  	}
> -	/.*}/ { current_section = ""; current_version = ""; }
> -	/^\t# added in / {
> -		current_version=$4;
> -	}
> +	/.*}/ { current_section = ""; }
>  	/^[^}].*[^:*];/ {
>  		if (current_section == "") {
>  			next;
>  		}
> -		symbol_version = current_version
>  		if (/^[^}].*[^:*]; # added in /) {
>  			symbol_version = $5
>  		}
> diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
> deleted file mode 100644
> index aa1752cacd..0000000000
> --- a/buildtools/map_to_win.py
> +++ /dev/null
> @@ -1,41 +0,0 @@
> -#!/usr/bin/env python3
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2019 Intel Corporation
> -
> -import sys
> -
> -
> -def is_function_line(ln):
> -    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
> -
> -# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
> -def create_mingw_map_file(input_map, output_map):
> -    with open(input_map) as f_in, open(output_map, 'w') as f_out:
> -        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
> -
> -def main(args):
> -    if not args[1].endswith('version.map') or \
> -            not args[2].endswith('exports.def') and \
> -            not args[2].endswith('mingw.map'):
> -        return 1
> -
> -    if args[2].endswith('mingw.map'):
> -        create_mingw_map_file(args[1], args[2])
> -        return 0
> -
> -# generate def file from map file.
> -# This works taking indented lines only which end with a ";" and which don't
> -# have a colon in them, i.e. the lines defining functions only.
> -    else:
> -        with open(args[1]) as f_in:
> -            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
> -                         if is_function_line(ln)]
> -            functions = ["EXPORTS\n"] + functions
> -
> -    with open(args[2], 'w') as f_out:
> -        f_out.writelines(functions)
> -    return 0
> -
> -
> -if __name__ == "__main__":
> -    sys.exit(main(sys.argv))
> diff --git a/buildtools/meson.build b/buildtools/meson.build
> index b745e9afa4..1cd1ce02fd 100644
> --- a/buildtools/meson.build
> +++ b/buildtools/meson.build
> @@ -18,7 +18,6 @@ endif
>  echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
>  gen_version_map = py3 + files('gen-version-map.py')
>  list_dir_globs = py3 + files('list-dir-globs.py')
> -map_to_win_cmd = py3 + files('map_to_win.py')
>  sphinx_wrapper = py3 + files('call-sphinx-build.py')
>  get_cpu_count_cmd = py3 + files('get-cpu-count.py')
>  get_numa_count_cmd = py3 + files('get-numa-count.py')
> diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
> deleted file mode 100755
> index 8992214ac8..0000000000
> --- a/devtools/check-symbol-change.sh
> +++ /dev/null
> @@ -1,186 +0,0 @@
> -#!/bin/sh
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
> -
> -build_map_changes()
> -{
> -	local fname="$1"
> -	local mapdb="$2"
> -
> -	cat "$fname" | awk '
> -		# Initialize our variables
> -		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
> -
> -		# Anything that starts with + or -, followed by an a
> -		# and ends in the string .map is the name of our map file
> -		# This may appear multiple times in a patch if multiple
> -		# map files are altered, and all section/symbol names
> -		# appearing between a triggering of this rule and the
> -		# next trigger of this rule are associated with this file
> -		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
> -
> -		# The previous rule catches all .map files, anything else
> -		# indicates we left the map chunk.
> -		/[-+] [ab]\// {in_map=0}
> -
> -		# Triggering this rule, which starts a line and ends it
> -		# with a { identifies a versioned section.  The section name is
> -		# the rest of the line with the + and { symbols removed.
> -		# Triggering this rule sets in_sec to 1, which actives the
> -		# symbol rule below
> -		/^.*{/ {
> -			gsub("+", "");
> -			if (in_map == 1) {
> -				sec=$(NF-1); in_sec=1;
> -			}
> -		}
> -
> -		# This rule identifies the end of a section, and disables the
> -		# symbol rule
> -		/.*}/ {in_sec=0}
> -
> -		# This rule matches on a + followed by any characters except a :
> -		# (which denotes a global vs local segment), and ends with a ;.
> -		# The semicolon is removed and the symbol is printed with its
> -		# association file name and version section, along with an
> -		# indicator that the symbol is a new addition.  Note this rule
> -		# only works if we have found a version section in the rule
> -		# above (hence the in_sec check) And found a map file (the
> -		# in_map check).  If we are not in a map chunk, do nothing.  If
> -		# we are in a map chunk but not a section chunk, record it as
> -		# unknown.
> -		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
> -			if (in_map == 1) {
> -				if (in_sec == 1) {
> -					print map " " sym " " sec " add"
> -				} else {
> -					print map " " sym " unknown add"
> -				}
> -			}
> -		}
> -
> -		# This is the same rule as above, but the rule matches on a
> -		# leading - rather than a +, denoting that the symbol is being
> -		# removed.
> -		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
> -			if (in_map == 1) {
> -				if (in_sec == 1) {
> -					print map " " sym " " sec " del"
> -				} else {
> -					print map " " sym " unknown del"
> -				}
> -			}
> -		}' > "$mapdb"
> -
> -		sort -u "$mapdb" > "$mapdb.2"
> -		mv -f "$mapdb.2" "$mapdb"
> -
> -}
> -
> -is_stable_section() {
> -	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
> -}
> -
> -check_for_rule_violations()
> -{
> -	local mapdb="$1"
> -	local mname
> -	local symname
> -	local secname
> -	local ar
> -	local ret=0
> -
> -	while read mname symname secname ar
> -	do
> -		if [ "$ar" = "add" ]
> -		then
> -
> -			if [ "$secname" = "unknown" ]
> -			then
> -				# Just inform the user of this occurrence, but
> -				# don't flag it as an error
> -				echo -n "INFO: symbol $symname is added but "
> -				echo -n "patch has insufficient context "
> -				echo -n "to determine the section name "
> -				echo -n "please ensure the version is "
> -				echo "EXPERIMENTAL"
> -				continue
> -			fi
> -
> -			oldsecname=$(sed -n \
> -			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
> -
> -			# A symbol can not enter a stable section directly
> -			if [ -z "$oldsecname" ]
> -			then
> -				if ! is_stable_section $secname
> -				then
> -					echo -n "INFO: symbol $symname has "
> -					echo -n "been added to the "
> -					echo -n "$secname section of the "
> -					echo "version map"
> -					continue
> -				else
> -					echo -n "ERROR: symbol $symname "
> -					echo -n "is added in the $secname "
> -					echo -n "section, but is expected to "
> -					echo -n "be added in the EXPERIMENTAL "
> -					echo "section of the version map"
> -					ret=1
> -					continue
> -				fi
> -			fi
> -
> -			# This symbol is moving inside a section, nothing to do
> -			if [ "$oldsecname" = "$secname" ]
> -			then
> -				continue
> -			fi
> -
> -			# This symbol is moving between two sections (the
> -			# original section is a stable section).
> -			# This can be legit, just warn.
> -			if is_stable_section $oldsecname
> -			then
> -				echo -n "INFO: symbol $symname is being "
> -				echo -n "moved from $oldsecname to $secname. "
> -				echo -n "Ensure that it has gone through the "
> -				echo "deprecation process"
> -				continue
> -			fi
> -		else
> -
> -			if ! grep -q "$mname $symname .* add" "$mapdb" && \
> -			   is_stable_section $secname
> -			then
> -				# Just inform users that stable
> -				# symbols need to go through a deprecation
> -				# process
> -				echo -n "INFO: symbol $symname is being "
> -				echo -n "removed, ensure that it has "
> -				echo "gone through the deprecation process"
> -			fi
> -		fi
> -	done < "$mapdb"
> -
> -	return $ret
> -}
> -
> -trap clean_and_exit_on_sig EXIT
> -
> -mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
> -patch=$1
> -exit_code=1
> -
> -clean_and_exit_on_sig()
> -{
> -	rm -f "$mapfile"
> -	exit $exit_code
> -}
> -
> -build_map_changes "$patch" "$mapfile"
> -check_for_rule_violations "$mapfile"
> -exit_code=$?
> -rm -f "$mapfile"
> -
> -exit $exit_code
> diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
> deleted file mode 100755
> index fcd3931e5d..0000000000
> --- a/devtools/check-symbol-maps.sh
> +++ /dev/null
> @@ -1,101 +0,0 @@
> -#! /bin/sh -e
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright 2018 Mellanox Technologies, Ltd
> -
> -cd $(dirname $0)/..
> -
> -# speed up by ignoring Unicode details
> -export LC_ALL=C
> -
> -if [ $# = 0 ] ; then
> -    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
> -fi
> -
> -ret=0
> -
> -find_orphan_symbols ()
> -{
> -    for map in $@ ; do
> -        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
> -            if echo $sym | grep -q '^per_lcore_' ; then
> -                symsrc=${sym#per_lcore_}
> -            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
> -                symsrc=${sym#__}
> -            else
> -                symsrc=$sym
> -            fi
> -            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
> -                echo "$map: $sym"
> -            fi
> -        done
> -    done
> -}
> -
> -orphan_symbols=$(find_orphan_symbols $@)
> -if [ -n "$orphan_symbols" ] ; then
> -    echo "Found only in symbol map file:"
> -    echo "$orphan_symbols" | sed 's,^,\t,'
> -    ret=1
> -fi
> -
> -find_duplicate_symbols ()
> -{
> -    for map in $@ ; do
> -        buildtools/map-list-symbol.sh $map | \
> -            sort | uniq -c | grep -v " 1 $map" || true
> -    done
> -}
> -
> -duplicate_symbols=$(find_duplicate_symbols $@)
> -if [ -n "$duplicate_symbols" ] ; then
> -    echo "Found duplicates in symbol map file:"
> -    echo "$duplicate_symbols"
> -    ret=1
> -fi
> -
> -local_miss_maps=$(grep -L 'local: \*;' $@ || true)
> -if [ -n "$local_miss_maps" ] ; then
> -    echo "Found maps without local catch-all:"
> -    echo "$local_miss_maps"
> -    ret=1
> -fi
> -
> -find_bad_format_maps ()
> -{
> -    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
> -    next_abi_version=$((abi_version + 1))
> -    for map in $@ ; do
> -        cat $map | awk '
> -            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
> -            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
> -            /^$/ { next; } # empty line
> -            /^\t(global:|local: \*;)$/ { next; } # qualifiers
> -            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
> -            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
> -            { print $0; }' || echo $map
> -    done
> -}
> -
> -bad_format_maps=$(find_bad_format_maps $@)
> -if [ -n "$bad_format_maps" ] ; then
> -    echo "Found badly formatted maps:"
> -    echo "$bad_format_maps"
> -    ret=1
> -fi
> -
> -find_non_versioned_maps ()
> -{
> -    for map in $@ ; do
> -        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
> -            echo $map
> -    done
> -}
> -
> -non_versioned_maps=$(find_non_versioned_maps $@)
> -if [ -n "$non_versioned_maps" ] ; then
> -    echo "Found non versioned maps:"
> -    echo "$non_versioned_maps"
> -    ret=1
> -fi
> -
> -exit $ret
> diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
> index 9180c2b070..c111b0fef3 100755
> --- a/devtools/checkpatches.sh
> +++ b/devtools/checkpatches.sh
> @@ -9,7 +9,7 @@
>  # - DPDK_CHECKPATCH_OPTIONS
>  . $(dirname $(readlink -f $0))/load-devel-config
>  
> -VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
> +VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
>  
>  # Enable codespell by default. This can be overwritten from a config file.
>  # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
> diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
> deleted file mode 100755
> index 45437f3c3b..0000000000
> --- a/devtools/update-abi.sh
> +++ /dev/null
> @@ -1,46 +0,0 @@
> -#!/bin/sh -e
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2019 Intel Corporation
> -
> -abi_version=$1
> -abi_version_file="./ABI_VERSION"
> -update_path="lib drivers"
> -
> -# check ABI version format string
> -check_abi_version() {
> -      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
> -}
> -
> -if [ -z "$1" ]; then
> -      # output to stderr
> -      >&2 echo "Please provide ABI version"
> -      exit 1
> -fi
> -
> -# check version string format
> -if ! check_abi_version $abi_version ; then
> -      # output to stderr
> -      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
> -      exit 1
> -fi
> -
> -if [ -n "$2" ]; then
> -      abi_version_file=$2
> -fi
> -
> -if [ -n "$3" ]; then
> -      # drop $1 and $2
> -      shift 2
> -      # assign all other arguments as update paths
> -      update_path=$@
> -fi
> -
> -echo "New ABI version:" $abi_version
> -echo "ABI_VERSION path:" $abi_version_file
> -echo "Path to update:" $update_path
> -
> -echo $abi_version > $abi_version_file
> -
> -find $update_path -name version.map -exec \
> -      devtools/update_version_map_abi.py {} \
> -      $abi_version \; -print
> diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
> deleted file mode 100755
> index d17b02a327..0000000000
> --- a/devtools/update_version_map_abi.py
> +++ /dev/null
> @@ -1,210 +0,0 @@
> -#!/usr/bin/env python3
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2019 Intel Corporation
> -
> -"""
> -A Python program that updates and merges all available stable ABI versions into
> -one ABI version, while leaving experimental ABI exactly as it is. The intended
> -ABI version is supplied via command-line parameter. This script is to be called
> -from the devtools/update-abi.sh utility.
> -"""
> -
> -import argparse
> -import sys
> -import re
> -
> -
> -def __parse_map_file(f_in):
> -    # match function name, followed by semicolon, followed by EOL or comments,
> -    # optionally with whitespace in between each item
> -    func_line_regex = re.compile(r"\s*"
> -                                 r"(?P<line>"
> -                                 r"(?P<func>[a-zA-Z_0-9]+)"
> -                                 r"\s*"
> -                                 r";"
> -                                 r"\s*"
> -                                 r"(?P<comment>#.+)?"
> -                                 r")"
> -                                 r"\s*"
> -                                 r"$")
> -    # match section name, followed by opening bracked, followed by EOL,
> -    # optionally with whitespace in between each item
> -    section_begin_regex = re.compile(r"\s*"
> -                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
> -                                     r"\s*"
> -                                     r"{"
> -                                     r"\s*"
> -                                     r"$")
> -    # match closing bracket, optionally followed by section name (for when we
> -    # inherit from another ABI version), followed by semicolon, followed by
> -    # EOL, optionally with whitespace in between each item
> -    section_end_regex = re.compile(r"\s*"
> -                                   r"}"
> -                                   r"\s*"
> -                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
> -                                   r"\s*"
> -                                   r";"
> -                                   r"\s*"
> -                                   r"$")
> -
> -    # for stable ABI, we don't care about which version introduced which
> -    # function, we just flatten the list. there are dupes in certain files, so
> -    # use a set instead of a list
> -    stable_lines = set()
> -    # copy experimental section as is
> -    experimental_lines = []
> -    # copy internal section as is
> -    internal_lines = []
> -    in_experimental = False
> -    in_internal = False
> -    has_stable = False
> -
> -    # gather all functions
> -    for line in f_in:
> -        # clean up the line
> -        line = line.strip('\n').strip()
> -
> -        # is this an end of section?
> -        match = section_end_regex.match(line)
> -        if match:
> -            # whatever section this was, it's not active any more
> -            in_experimental = False
> -            in_internal = False
> -            continue
> -
> -        # if we're in the middle of experimental section, we need to copy
> -        # the section verbatim, so just add the line
> -        if in_experimental:
> -            experimental_lines += [line]
> -            continue
> -
> -        # if we're in the middle of internal section, we need to copy
> -        # the section verbatim, so just add the line
> -        if in_internal:
> -            internal_lines += [line]
> -            continue
> -
> -        # skip empty lines
> -        if not line:
> -            continue
> -
> -        # is this a beginning of a new section?
> -        match = section_begin_regex.match(line)
> -        if match:
> -            cur_section = match.group("version")
> -            # is it experimental?
> -            in_experimental = cur_section == "EXPERIMENTAL"
> -            # is it internal?
> -            in_internal = cur_section == "INTERNAL"
> -            if not in_experimental and not in_internal:
> -                has_stable = True
> -            continue
> -
> -        # is this a function?
> -        match = func_line_regex.match(line)
> -        if match:
> -            stable_lines.add(match.group("line"))
> -
> -    return has_stable, stable_lines, experimental_lines, internal_lines
> -
> -
> -def __generate_stable_abi(f_out, abi_major, lines):
> -    # print ABI version header
> -    print("DPDK_{} {{".format(abi_major), file=f_out)
> -
> -    # print global section if it exists
> -    if lines:
> -        print("\tglobal:", file=f_out)
> -        # blank line
> -        print(file=f_out)
> -
> -        # print all stable lines, alphabetically sorted
> -        for line in sorted(lines):
> -            print("\t{}".format(line), file=f_out)
> -
> -        # another blank line
> -        print(file=f_out)
> -
> -    # print local section
> -    print("\tlocal: *;", file=f_out)
> -
> -    # end stable version
> -    print("};", file=f_out)
> -
> -
> -def __generate_experimental_abi(f_out, lines):
> -    # start experimental section
> -    print("EXPERIMENTAL {", file=f_out)
> -
> -    # print all experimental lines as they were
> -    for line in lines:
> -        # don't print empty whitespace
> -        if not line:
> -            print("", file=f_out)
> -        else:
> -            print("\t{}".format(line), file=f_out)
> -
> -    # end section
> -    print("};", file=f_out)
> -
> -def __generate_internal_abi(f_out, lines):
> -    # start internal section
> -    print("INTERNAL {", file=f_out)
> -
> -    # print all internal lines as they were
> -    for line in lines:
> -        # don't print empty whitespace
> -        if not line:
> -            print("", file=f_out)
> -        else:
> -            print("\t{}".format(line), file=f_out)
> -
> -    # end section
> -    print("};", file=f_out)
> -
> -def __main():
> -    arg_parser = argparse.ArgumentParser(
> -        description='Merge versions in linker version script.')
> -
> -    arg_parser.add_argument("map_file", type=str,
> -                            help='path to linker version script file '
> -                                 '(pattern: version.map)')
> -    arg_parser.add_argument("abi_version", type=str,
> -                            help='target ABI version (pattern: MAJOR.MINOR)')
> -
> -    parsed = arg_parser.parse_args()
> -
> -    if not parsed.map_file.endswith('version.map'):
> -        print("Invalid input file: {}".format(parsed.map_file),
> -              file=sys.stderr)
> -        arg_parser.print_help()
> -        sys.exit(1)
> -
> -    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
> -        print("Invalid ABI version: {}".format(parsed.abi_version),
> -              file=sys.stderr)
> -        arg_parser.print_help()
> -        sys.exit(1)
> -    abi_major = parsed.abi_version.split('.')[0]
> -
> -    with open(parsed.map_file) as f_in:
> -        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
> -
> -    with open(parsed.map_file, 'w') as f_out:
> -        need_newline = has_stable and experimental_lines
> -        if has_stable:
> -            __generate_stable_abi(f_out, abi_major, stable_lines)
> -        if need_newline:
> -            # separate sections with a newline
> -            print(file=f_out)
> -        if experimental_lines:
> -            __generate_experimental_abi(f_out, experimental_lines)
> -        if internal_lines:
> -            if has_stable or experimental_lines:
> -              # separate sections with a newline
> -              print(file=f_out)
> -            __generate_internal_abi(f_out, internal_lines)
> -
> -
> -if __name__ == "__main__":
> -    __main()
> diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
> index d96153c6b2..f03a7467ac 100644
> --- a/doc/guides/contributing/abi_policy.rst
> +++ b/doc/guides/contributing/abi_policy.rst
> @@ -330,31 +330,14 @@ become part of a tracked ABI version.
>  
>  Note that marking an API as experimental is a multi step process.
>  To mark an API as experimental, the symbols which are desired to be exported
> -must be placed in an EXPERIMENTAL version block in the corresponding libraries'
> -version map script.
> +must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
> +sources.

When reading through, this line stuck out as getting very long (92
characters).  I know it's .rst file so generally more like a markup
language, but maybe consider breaking ~79 characters region.  If you
agree, it's okay for this to be on apply.

>  Experimental symbols must be commented so that it is clear in which DPDK
>  version they were introduced.
>  
> -.. code-block:: none
> -
> -   EXPERIMENTAL {
> -           global:
> -
> -           # added in 20.11
> -           rte_foo_init;
> -           rte_foo_configure;
> -
> -           # added in 21.02
> -           rte_foo_cleanup;
> -   ...
> -
>  Secondly, the corresponding prototypes of those exported functions (in the
>  development header files), must be marked with the ``__rte_experimental`` tag
>  (see ``rte_compat.h``).
> -The DPDK build makefiles perform a check to ensure that the map file and the
> -C code reflect the same list of symbols.
> -This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
> -during compilation in the corresponding library Makefile.
>  
>  In addition to tagging the code with ``__rte_experimental``,
>  the doxygen markup must also contain the EXPERIMENTAL string,
> diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
> index 1ebc79ca3c..43e27bbd0a 100644
> --- a/doc/guides/contributing/coding_style.rst
> +++ b/doc/guides/contributing/coding_style.rst
> @@ -1018,13 +1018,6 @@ name
>  	sources are stored in a directory ``lib/xyz``, this value should
>  	never be needed for new libraries.
>  
> -.. note::
> -
> -	The name value also provides the name used to find the function version
> -	map file, as part of the build process, so if the directory name and
> -	library names differ, the ``version.map`` file should be named
> -	consistently with the library, not the directory
> -
>  objs
>  	**Default Value = []**.
>  	This variable can be used to pass to the library build some pre-built
> diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
> index 4debb07b98..a06d8a2a3b 100644
> --- a/doc/guides/contributing/img/patch_cheatsheet.svg
> +++ b/doc/guides/contributing/img/patch_cheatsheet.svg
> @@ -1,18 +1,18 @@
>  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
>  <svg
> -   xmlns:dc="http://purl.org/dc/elements/1.1/"
> -   xmlns:cc="http://creativecommons.org/ns#"
> -   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
> -   xmlns:svg="http://www.w3.org/2000/svg"
> -   xmlns="http://www.w3.org/2000/svg"
> -   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
> -   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
>     version="1.1"
>     width="210mm"
>     height="297mm"
>     id="svg2985"
> -   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
> -   sodipodi:docname="patch_cheatsheet.svg">
> +   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
> +   sodipodi:docname="patch_cheatsheet.svg"
> +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
> +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
> +   xmlns="http://www.w3.org/2000/svg"
> +   xmlns:svg="http://www.w3.org/2000/svg"
> +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
> +   xmlns:cc="http://creativecommons.org/ns#"
> +   xmlns:dc="http://purl.org/dc/elements/1.1/">
>    <sodipodi:namedview
>       pagecolor="#ffffff"
>       bordercolor="#666666"
> @@ -23,18 +23,22 @@
>       inkscape:pageopacity="0"
>       inkscape:pageshadow="2"
>       inkscape:window-width="1920"
> -     inkscape:window-height="1017"
> +     inkscape:window-height="975"
>       id="namedview274"
>       showgrid="false"
>       inkscape:zoom="0.89702958"
> -     inkscape:cx="246.07409"
> -     inkscape:cy="416.76022"
> -     inkscape:window-x="1072"
> -     inkscape:window-y="-8"
> +     inkscape:cx="546.24732"
> +     inkscape:cy="385.71749"
> +     inkscape:window-x="0"
> +     inkscape:window-y="0"
>       inkscape:window-maximized="1"
>       inkscape:current-layer="layer1"
>       inkscape:document-rotation="0"
> -     inkscape:snap-grids="false" />
> +     inkscape:snap-grids="false"
> +     inkscape:showpageshadow="2"
> +     inkscape:pagecheckerboard="0"
> +     inkscape:deskcolor="#d1d1d1"
> +     inkscape:document-units="mm" />
>    <defs
>       id="defs3">
>      <linearGradient
> @@ -906,7 +910,7 @@
>               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
>               id="tspan4092-8-7-6-9-7"
>               y="855.79816"
> -             x="460.18405">****</tspan></text>
> +             x="460.18405">***</tspan></text>
>        </g>
>      </g>
>      <text
> @@ -1132,161 +1136,126 @@
>             id="tspan4092-8-6-3-1-8-4-4-55-7"
>             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
>      </g>
> +    <text
> +       x="424.10629"
> +       y="363.21423"
> +       id="text4090-8"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="363.21423"
> +         id="tspan4092-8"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
> +    <text
> +       x="424.10629"
> +       y="393.60123"
> +       id="text4090-8-5"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="393.60123"
> +         id="tspan4092-8-5"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
> +    <text
> +       x="424.10629"
> +       y="424.20575"
> +       id="text4090-8-5-6"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="424.20575"
> +         id="tspan4092-8-5-5"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
> +    <text
> +       x="424.10629"
> +       y="453.10339"
> +       id="text4090-8-5-6-9-4"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="453.10339"
> +         id="tspan4092-8-5-5-3-4"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
> +    <text
> +       x="424.10629"
> +       y="514.09497"
> +       id="text4090-8-5-6-9-4-6"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="514.09497"
> +         id="tspan4092-8-5-5-3-4-0"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
> +    <text
> +       x="425.12708"
> +       y="544.91718"
> +       id="text4090-8-5-6-9-4-6-6"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="425.12708"
> +         y="544.91718"
> +         id="tspan4092-8-5-5-3-4-0-6"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
>      <g
> -       transform="translate(1.0962334,-2.7492248)"
> -       id="g3605">
> -      <text
> -         x="42.176418"
> -         y="1020.4383"
> -         id="text4090-8-7-8-7-6-3-8-4"
> -         xml:space="preserve"
> -         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -           x="42.176418"
> -           y="1020.4383"
> -           id="tspan4092-8-6-3-1-8-4-4-55"
> -           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
> -      <text
> -         x="30.942892"
> -         y="1024.2014"
> -         id="text4090-8-7-8-7-6-3-8-4-1-5"
> -         xml:space="preserve"
> -         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -           x="30.942892"
> -           y="1024.2014"
> -           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> -      <text
> -         x="25.247679"
> -         y="1024.2014"
> -         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
> -         xml:space="preserve"
> -         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -           x="25.247679"
> -           y="1024.2014"
> -           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> -    </g>
> -    <g
> -       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
> -       id="g3275">
> +       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
> +       id="g3334">
>        <g
> -         id="g3341">
> +         id="g3267"
> +         transform="translate(-13.517932,3.1531035)">
>          <text
> -           x="394.78601"
> -           y="390.17807"
> -           id="text4090-8"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="390.17807"
> -             id="tspan4092-8"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
> -        <text
> -           x="394.78601"
> -           y="420.24835"
> -           id="text4090-8-5"
> +           x="660.46729"
> +           y="468.01297"
> +           id="text4090-8-1-8-9-1-4-1"
>             xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="420.24835"
> -             id="tspan4092-8-5"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
> -        <text
> -           x="394.78601"
> -           y="450.53394"
> -           id="text4090-8-5-6"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="450.53394"
> -             id="tspan4092-8-5-5"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
> -        <text
> +           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> +             x="660.46729"
> +             y="468.01297"
> +             id="tspan4092-8-7-6-9-7-0-7"
> +             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
> +      </g>
> +      <text
> +         x="394.78601"
> +         y="483.59955"
> +         id="text4090-8-5-6-9"
> +         xml:space="preserve"
> +         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
>             x="394.78601"
> -           y="513.13031"
> -           id="text4090-8-5-6-9-4"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="513.13031"
> -             id="tspan4092-8-5-5-3-4"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
> -        <text
> +           y="483.59955"
> +           id="tspan4092-8-5-5-3"
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
> +    </g>
> +    <g
> +       id="g3428"
> +       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
> +      <text
> +         x="394.78601"
> +         y="541.38928"
> +         id="text4090-8-5-6-9-4-6-1"
> +         xml:space="preserve"
> +         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
>             x="394.78601"
> -           y="573.48621"
> -           id="text4090-8-5-6-9-4-6"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="573.48621"
> -             id="tspan4092-8-5-5-3-4-0"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
> +           y="541.38928"
> +           id="tspan4092-8-5-5-3-4-0-7"
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
> +      <g
> +         transform="translate(-119.92979,57.949844)"
> +         id="g3267-9">
>          <text
> -           x="395.79617"
> -           y="603.98718"
> -           id="text4090-8-5-6-9-4-6-6"
> +           x="628.93628"
> +           y="473.13675"
> +           id="text4090-8-1-8-9-1-4-1-4"
>             xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="395.79617"
> -             y="603.98718"
> -             id="tspan4092-8-5-5-3-4-0-6"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
> -        <g
> -           transform="translate(0,-0.83470152)"
> -           id="g3334">
> -          <g
> -             id="g3267"
> -             transform="translate(-13.517932,3.1531035)">
> -            <text
> -               x="660.46729"
> -               y="468.01297"
> -               id="text4090-8-1-8-9-1-4-1"
> -               xml:space="preserve"
> -               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -                 x="660.46729"
> -                 y="468.01297"
> -                 id="tspan4092-8-7-6-9-7-0-7"
> -                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
> -          </g>
> -          <text
> -             x="394.78601"
> -             y="483.59955"
> -             id="text4090-8-5-6-9"
> -             xml:space="preserve"
> -             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -               x="394.78601"
> -               y="483.59955"
> -               id="tspan4092-8-5-5-3"
> -               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
> -        </g>
> -        <g
> -           id="g3428"
> -           transform="translate(0,0.88137813)">
> -          <text
> -             x="394.78601"
> -             y="541.38928"
> -             id="text4090-8-5-6-9-4-6-1"
> -             xml:space="preserve"
> -             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -               x="394.78601"
> -               y="541.38928"
> -               id="tspan4092-8-5-5-3-4-0-7"
> -               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
> -          <g
> -             transform="translate(-119.92979,57.949844)"
> -             id="g3267-9">
> -            <text
> -               x="628.93628"
> -               y="473.13675"
> -               id="text4090-8-1-8-9-1-4-1-4"
> -               xml:space="preserve"
> -               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -                 x="628.93628"
> -                 y="473.13675"
> -                 id="tspan4092-8-7-6-9-7-0-7-8"
> -                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
> -          </g>
> -        </g>
> +           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> +             x="628.93628"
> +             y="473.13675"
> +             id="tspan4092-8-7-6-9-7-0-7-8"
> +             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
>        </g>
>      </g>
>      <text
> @@ -1301,7 +1270,7 @@
>           id="tspan4092-8-5-5-3-4-0-6-2-11-0"
>           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
>      <g
> -       transform="translate(1.0962334,-2.7492248)"
> +       transform="translate(1.0962334,-14.749225)"
>         id="g3595">
>        <text
>           x="30.942892"
> @@ -1332,7 +1301,7 @@
>             x="19.552465"
>             y="1037.0271"
>             id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
>        <text
>           x="42.830166"
>           y="1033.2393"
> @@ -1345,7 +1314,7 @@
>             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
>      </g>
>      <g
> -       transform="translate(1.0962334,-2.7492248)"
> +       transform="translate(1.0962334,-14.749225)"
>         id="g3619">
>        <text
>           x="42.212418"
> @@ -1396,7 +1365,7 @@
>             x="14.016749"
>             y="1049.8527"
>             id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
>      </g>
>      <rect
>         width="196.44218"
> diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
> index d21ee288b2..8ad6b6e715 100644
> --- a/doc/guides/contributing/patches.rst
> +++ b/doc/guides/contributing/patches.rst
> @@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
>  
>    * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
>  
> -* New external functions should be added to the local ``version.map`` file. See
> -  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
> -  guides. New external functions should also be added in alphabetical order.
> +* New external functions should be exported.
> +  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
> +  guides.
>  
>  * Any new API function should be used in ``/app`` test directory.
>  
> diff --git a/drivers/meson.build b/drivers/meson.build
> index 5368d38363..15a4991abf 100644
> --- a/drivers/meson.build
> +++ b/drivers/meson.build
> @@ -5,8 +5,6 @@ if is_ms_compiler
>      subdir_done()
>  endif
>  
> -fs = import('fs')
> -
>  # Defines the order of dependencies evaluation
>  subdirs = [
>          'common',
> @@ -290,57 +288,25 @@ foreach subpath:subdirs
>                  install: true)
>  
>          # now build the shared driver
> -        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
> -
> -        if not fs.is_file(version_map)
> -            if is_ms_linker
> -                link_mode = 'mslinker'
> -            elif is_windows
> -                link_mode = 'mingw'
> -            else
> -                link_mode = 'gnu'
> -            endif
> -            version_map = custom_target(lib_name + '_map',
> -                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> -                    input: sources + sources_avx2 + sources_avx512,
> -                    output: '_'.join([class, name, 'exports.map']))
> -            version_map_path = version_map.full_path()
> -            version_map_dep = [version_map]
> -            lk_deps = [version_map]
> -
> -            if is_ms_linker and is_ms_compiler
> -                lk_args = ['/def:' + version_map.full_path()]
> -            elif is_ms_linker
> -                lk_args = ['-Wl,/def:' + version_map.full_path()]
> -            else
> -                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
> -            endif
> +        if is_ms_linker
> +            link_mode = 'mslinker'
> +        elif is_windows
> +            link_mode = 'mingw'
>          else
> -            version_map_path = version_map
> -            version_map_dep = []
> -            lk_deps = [version_map]
> -
> -            if is_windows
> -                if is_ms_linker
> -                    def_file = custom_target(lib_name + '_def',
> -                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                            input: version_map,
> -                            output: '@0@_exports.def'.format(lib_name))
> -                    lk_deps += [def_file]
> -
> -                    lk_args = ['-Wl,/def:' + def_file.full_path()]
> -                else
> -                    mingw_map = custom_target(lib_name + '_mingw',
> -                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                            input: version_map,
> -                            output: '@0@_mingw.map'.format(lib_name))
> -                    lk_deps += [mingw_map]
> -
> -                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> -                endif
> -            else
> -                lk_args = ['-Wl,--version-script=' + version_map]
> -            endif
> +            link_mode = 'gnu'
> +        endif
> +        version_map = custom_target(lib_name + '_map',
> +                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> +                input: sources + sources_avx2 + sources_avx512,
> +                output: '_'.join([class, name, 'exports.map']))
> +        lk_deps = [version_map]
> +
> +        if is_ms_linker and is_ms_compiler
> +            lk_args = ['/def:' + version_map.full_path()]
> +        elif is_ms_linker
> +            lk_args = ['-Wl,/def:' + version_map.full_path()]
> +        else
> +            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
>          endif
>  
>          if not is_windows and developer_mode
> @@ -348,11 +314,11 @@ foreach subpath:subdirs
>              # check-symbols.sh script, using it as a
>              # dependency of the .so build
>              lk_deps += custom_target(lib_name + '.sym_chk',
> -                    command: [check_symbols, version_map_path, '@INPUT@'],
> +                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
>                      capture: true,
>                      input: static_lib,
>                      output: lib_name + '.sym_chk',
> -                    depends: version_map_dep)
> +                    depends: [version_map])
>          endif
>  
>          shared_lib = shared_library(lib_name, sources_pmd_info,
> diff --git a/lib/meson.build b/lib/meson.build
> index a9cbea5fea..6157d0e13e 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -1,7 +1,6 @@
>  # SPDX-License-Identifier: BSD-3-Clause
>  # Copyright(c) 2017-2019 Intel Corporation
>  
> -fs = import('fs')
>  
>  # process all libraries equally, as far as possible
>  # "core" libs first, then others alphabetically as far as possible
> @@ -289,59 +288,25 @@ foreach l:libraries
>              include_directories: includes,
>              dependencies: static_deps)
>  
> -    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
> -        if is_ms_linker
> -            link_mode = 'mslinker'
> -        elif is_windows
> -            link_mode = 'mingw'
> -        else
> -            link_mode = 'gnu'
> -        endif
> -        version_map = custom_target(libname + '_map',
> -                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> -                input: sources,
> -                output: '_'.join([name, 'exports.map']))
> -        version_map_path = version_map.full_path()
> -        version_map_dep = [version_map]
> -        lk_deps = [version_map]
> -
> -        if is_ms_linker and is_ms_compiler
> -            lk_args = ['/def:' + version_map.full_path()]
> -        elif is_ms_linker
> -            lk_args = ['-Wl,/def:' + version_map.full_path()]
> -        else
> -            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
> -        endif
> +    if is_ms_linker
> +        link_mode = 'mslinker'
> +    elif is_windows
> +        link_mode = 'mingw'
>      else
> -        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
> -        version_map_path = version_map
> -        version_map_dep = []
> -        lk_deps = [version_map]
> -        if is_ms_linker
> -            def_file = custom_target(libname + '_def',
> -                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                    input: version_map,
> -                    output: '@0@_exports.def'.format(libname))
> -            lk_deps += [def_file]
> -
> -            if is_ms_compiler
> -                lk_args = ['/def:' + def_file.full_path()]
> -            else
> -                lk_args = ['-Wl,/def:' + def_file.full_path()]
> -            endif
> -        else
> -            if is_windows
> -                mingw_map = custom_target(libname + '_mingw',
> -                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                        input: version_map,
> -                        output: '@0@_mingw.map'.format(libname))
> -                lk_deps += [mingw_map]
> +        link_mode = 'gnu'
> +    endif
> +    version_map = custom_target(libname + '_map',
> +            command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> +            input: sources,
> +            output: '_'.join([name, 'exports.map']))
> +    lk_deps = [version_map]
>  
> -                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> -            else
> -                lk_args = ['-Wl,--version-script=' + version_map]
> -            endif
> -        endif
> +    if is_ms_linker and is_ms_compiler
> +        lk_args = ['/def:' + version_map.full_path()]
> +    elif is_ms_linker
> +        lk_args = ['-Wl,/def:' + version_map.full_path()]
> +    else
> +        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
>      endif
>  
>      if developer_mode and not is_windows
> @@ -349,11 +314,11 @@ foreach l:libraries
>          # check-symbols.sh script, using it as a
>          # dependency of the .so build
>          lk_deps += custom_target(name + '.sym_chk',
> -                command: [check_symbols, version_map_path, '@INPUT@'],
> +                command: [check_symbols, version_map.full_path(), '@INPUT@'],
>                  capture: true,
>                  input: static_lib,
>                  output: name + '.sym_chk',
> -                depends: version_map_dep)
> +                depends: [version_map])
>      endif
>  
>      if not use_function_versioning or is_windows


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v6 6/8] build: use dynamically generated version maps
  2025-03-28 10:52 16%   ` [PATCH v6 6/8] build: use dynamically generated version maps David Marchand
@ 2025-03-28 13:20  0%     ` Aaron Conole
  0 siblings, 0 replies; 153+ results
From: Aaron Conole @ 2025-03-28 13:20 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, thomas, bruce.richardson, andremue, Michael Santana,
	Dmitry Kozlyuk, Tyler Retzlaff

David Marchand <david.marchand@redhat.com> writes:

> Switch to dynamically generated version maps.
>
> As the map files get generated, tooling around checking, converting,
> updating etc.. static version maps can be removed.
>
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---

Ugh. replied to the wrong one.  Anyway, my previous note still applies.

Acked-by: Aaron Conole <aconole@redhat.com>

>  .github/workflows/build.yml                   |   1 -
>  MAINTAINERS                                   |   7 -
>  buildtools/check-symbols.sh                   |  33 +-
>  buildtools/map-list-symbol.sh                 |   7 +-
>  buildtools/map_to_win.py                      |  41 ---
>  buildtools/meson.build                        |   1 -
>  devtools/check-symbol-change.sh               | 186 -----------
>  devtools/check-symbol-maps.sh                 | 101 ------
>  devtools/checkpatches.sh                      |   2 +-
>  devtools/update-abi.sh                        |  46 ---
>  devtools/update_version_map_abi.py            | 210 ------------
>  doc/guides/contributing/abi_policy.rst        |  21 +-
>  doc/guides/contributing/coding_style.rst      |   7 -
>  .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
>  doc/guides/contributing/patches.rst           |   6 +-
>  drivers/meson.build                           |  74 ++---
>  lib/meson.build                               |  73 ++---
>  17 files changed, 188 insertions(+), 931 deletions(-)
>  delete mode 100644 buildtools/map_to_win.py
>  delete mode 100755 devtools/check-symbol-change.sh
>  delete mode 100755 devtools/check-symbol-maps.sh
>  delete mode 100755 devtools/update-abi.sh
>  delete mode 100755 devtools/update_version_map_abi.py
>
> diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
> index 0cc4d12b0b..7a6b679fe5 100644
> --- a/.github/workflows/build.yml
> +++ b/.github/workflows/build.yml
> @@ -31,7 +31,6 @@ jobs:
>          failed=
>          devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
>          devtools/check-meson.py || failed=true
> -        devtools/check-symbol-maps.sh || failed=true
>          [ -z "$failed" ]
>    ubuntu-vm-builds:
>      name: ${{ join(matrix.config.*, '-') }}
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 42ea07854b..480972ef1e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
>  F: MAINTAINERS
>  F: devtools/build-dict.sh
>  F: devtools/check-abi.sh
> -F: devtools/check-abi-version.sh
>  F: devtools/check-doc-vs-code.sh
>  F: devtools/check-dup-includes.sh
>  F: devtools/check-maintainers.sh
> @@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
>  F: devtools/check-git-log.sh
>  F: devtools/check-spdx-tag.sh
>  F: devtools/check-symbol-change.py
> -F: devtools/check-symbol-change.sh
> -F: devtools/check-symbol-maps.sh
>  F: devtools/checkpatches.sh
>  F: devtools/get-maintainer.sh
>  F: devtools/git-log-fixes.sh
>  F: devtools/load-devel-config
>  F: devtools/parse-flow-support.sh
>  F: devtools/process-iwyu.py
> -F: devtools/update-abi.sh
>  F: devtools/update-patches.py
> -F: devtools/update_version_map_abi.py
>  F: devtools/libabigail.abignore
>  F: devtools/words-case.txt
>  F: license/
> @@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
>  F: lib/eal/common/
>  F: lib/eal/unix/
>  F: lib/eal/include/
> -F: lib/eal/version.map
>  F: doc/guides/prog_guide/env_abstraction_layer.rst
>  F: app/test/test_alarm.c
>  F: app/test/test_atomic.c
> @@ -396,7 +390,6 @@ Windows support
>  M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
>  M: Tyler Retzlaff <roretzla@linux.microsoft.com>
>  F: lib/eal/windows/
> -F: buildtools/map_to_win.py
>  F: doc/guides/windows_gsg/
>  
>  Windows memory allocation
> diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
> index b8ac24391e..0d6745ec14 100755
> --- a/buildtools/check-symbols.sh
> +++ b/buildtools/check-symbols.sh
> @@ -7,29 +7,12 @@ OBJFILE=$2
>  
>  ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
>  LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
> -CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
> -
> -# added check for "make -C test/" usage
> -if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
> -then
> -	exit 0
> -fi
> -
> -if [ -d $MAPFILE ]
> -then
> -	exit 0
> -fi
> -
>  DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
>  trap 'rm -f "$DUMPFILE"' EXIT
>  objdump -t $OBJFILE >$DUMPFILE
>  
>  ret=0
>  
> -if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
> -	ret=1
> -fi
> -
>  for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
>  do
>  	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
> @@ -37,8 +20,7 @@ do
>  		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
>  	then
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is not flagged as experimental
> -		but is listed in version map
> +		$SYM is not flagged as experimental but is exported as an experimental symbol
>  		Please add __rte_experimental to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
> @@ -53,9 +35,8 @@ for SYM in `awk '{
>  do
>  	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is flagged as experimental
> -		but is not listed in version map
> -		Please add $SYM to the version map
> +		$SYM is flagged as experimental but is not exported as an experimental symbol
> +		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
>  	}
> @@ -67,8 +48,7 @@ do
>  		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
>  	then
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is not flagged as internal
> -		but is listed in version map
> +		$SYM is not flagged as internal but is exported as an internal symbol
>  		Please add __rte_internal to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
> @@ -83,9 +63,8 @@ for SYM in `awk '{
>  do
>  	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
>  		cat >&2 <<- END_OF_MESSAGE
> -		$SYM is flagged as internal
> -		but is not listed in version map
> -		Please add $SYM to the version map
> +		$SYM is flagged as internal but is not exported as an internal symbol
> +		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
>  		END_OF_MESSAGE
>  		ret=1
>  	}
> diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
> index 0829df4be5..962d5f3271 100755
> --- a/buildtools/map-list-symbol.sh
> +++ b/buildtools/map-list-symbol.sh
> @@ -42,7 +42,6 @@ for file in $@; do
>  	cat "$file" |awk '
>  	BEGIN {
>  		current_section = "";
> -		current_version = "";
>  		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
>  			ret = 0;
>  		} else {
> @@ -54,15 +53,11 @@ for file in $@; do
>  			current_section = $1;
>  		}
>  	}
> -	/.*}/ { current_section = ""; current_version = ""; }
> -	/^\t# added in / {
> -		current_version=$4;
> -	}
> +	/.*}/ { current_section = ""; }
>  	/^[^}].*[^:*];/ {
>  		if (current_section == "") {
>  			next;
>  		}
> -		symbol_version = current_version
>  		if (/^[^}].*[^:*]; # added in /) {
>  			symbol_version = $5
>  		}
> diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
> deleted file mode 100644
> index aa1752cacd..0000000000
> --- a/buildtools/map_to_win.py
> +++ /dev/null
> @@ -1,41 +0,0 @@
> -#!/usr/bin/env python3
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2019 Intel Corporation
> -
> -import sys
> -
> -
> -def is_function_line(ln):
> -    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
> -
> -# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
> -def create_mingw_map_file(input_map, output_map):
> -    with open(input_map) as f_in, open(output_map, 'w') as f_out:
> -        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
> -
> -def main(args):
> -    if not args[1].endswith('version.map') or \
> -            not args[2].endswith('exports.def') and \
> -            not args[2].endswith('mingw.map'):
> -        return 1
> -
> -    if args[2].endswith('mingw.map'):
> -        create_mingw_map_file(args[1], args[2])
> -        return 0
> -
> -# generate def file from map file.
> -# This works taking indented lines only which end with a ";" and which don't
> -# have a colon in them, i.e. the lines defining functions only.
> -    else:
> -        with open(args[1]) as f_in:
> -            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
> -                         if is_function_line(ln)]
> -            functions = ["EXPORTS\n"] + functions
> -
> -    with open(args[2], 'w') as f_out:
> -        f_out.writelines(functions)
> -    return 0
> -
> -
> -if __name__ == "__main__":
> -    sys.exit(main(sys.argv))
> diff --git a/buildtools/meson.build b/buildtools/meson.build
> index b745e9afa4..1cd1ce02fd 100644
> --- a/buildtools/meson.build
> +++ b/buildtools/meson.build
> @@ -18,7 +18,6 @@ endif
>  echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
>  gen_version_map = py3 + files('gen-version-map.py')
>  list_dir_globs = py3 + files('list-dir-globs.py')
> -map_to_win_cmd = py3 + files('map_to_win.py')
>  sphinx_wrapper = py3 + files('call-sphinx-build.py')
>  get_cpu_count_cmd = py3 + files('get-cpu-count.py')
>  get_numa_count_cmd = py3 + files('get-numa-count.py')
> diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
> deleted file mode 100755
> index 8992214ac8..0000000000
> --- a/devtools/check-symbol-change.sh
> +++ /dev/null
> @@ -1,186 +0,0 @@
> -#!/bin/sh
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
> -
> -build_map_changes()
> -{
> -	local fname="$1"
> -	local mapdb="$2"
> -
> -	cat "$fname" | awk '
> -		# Initialize our variables
> -		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
> -
> -		# Anything that starts with + or -, followed by an a
> -		# and ends in the string .map is the name of our map file
> -		# This may appear multiple times in a patch if multiple
> -		# map files are altered, and all section/symbol names
> -		# appearing between a triggering of this rule and the
> -		# next trigger of this rule are associated with this file
> -		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
> -
> -		# The previous rule catches all .map files, anything else
> -		# indicates we left the map chunk.
> -		/[-+] [ab]\// {in_map=0}
> -
> -		# Triggering this rule, which starts a line and ends it
> -		# with a { identifies a versioned section.  The section name is
> -		# the rest of the line with the + and { symbols removed.
> -		# Triggering this rule sets in_sec to 1, which actives the
> -		# symbol rule below
> -		/^.*{/ {
> -			gsub("+", "");
> -			if (in_map == 1) {
> -				sec=$(NF-1); in_sec=1;
> -			}
> -		}
> -
> -		# This rule identifies the end of a section, and disables the
> -		# symbol rule
> -		/.*}/ {in_sec=0}
> -
> -		# This rule matches on a + followed by any characters except a :
> -		# (which denotes a global vs local segment), and ends with a ;.
> -		# The semicolon is removed and the symbol is printed with its
> -		# association file name and version section, along with an
> -		# indicator that the symbol is a new addition.  Note this rule
> -		# only works if we have found a version section in the rule
> -		# above (hence the in_sec check) And found a map file (the
> -		# in_map check).  If we are not in a map chunk, do nothing.  If
> -		# we are in a map chunk but not a section chunk, record it as
> -		# unknown.
> -		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
> -			if (in_map == 1) {
> -				if (in_sec == 1) {
> -					print map " " sym " " sec " add"
> -				} else {
> -					print map " " sym " unknown add"
> -				}
> -			}
> -		}
> -
> -		# This is the same rule as above, but the rule matches on a
> -		# leading - rather than a +, denoting that the symbol is being
> -		# removed.
> -		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
> -			if (in_map == 1) {
> -				if (in_sec == 1) {
> -					print map " " sym " " sec " del"
> -				} else {
> -					print map " " sym " unknown del"
> -				}
> -			}
> -		}' > "$mapdb"
> -
> -		sort -u "$mapdb" > "$mapdb.2"
> -		mv -f "$mapdb.2" "$mapdb"
> -
> -}
> -
> -is_stable_section() {
> -	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
> -}
> -
> -check_for_rule_violations()
> -{
> -	local mapdb="$1"
> -	local mname
> -	local symname
> -	local secname
> -	local ar
> -	local ret=0
> -
> -	while read mname symname secname ar
> -	do
> -		if [ "$ar" = "add" ]
> -		then
> -
> -			if [ "$secname" = "unknown" ]
> -			then
> -				# Just inform the user of this occurrence, but
> -				# don't flag it as an error
> -				echo -n "INFO: symbol $symname is added but "
> -				echo -n "patch has insufficient context "
> -				echo -n "to determine the section name "
> -				echo -n "please ensure the version is "
> -				echo "EXPERIMENTAL"
> -				continue
> -			fi
> -
> -			oldsecname=$(sed -n \
> -			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
> -
> -			# A symbol can not enter a stable section directly
> -			if [ -z "$oldsecname" ]
> -			then
> -				if ! is_stable_section $secname
> -				then
> -					echo -n "INFO: symbol $symname has "
> -					echo -n "been added to the "
> -					echo -n "$secname section of the "
> -					echo "version map"
> -					continue
> -				else
> -					echo -n "ERROR: symbol $symname "
> -					echo -n "is added in the $secname "
> -					echo -n "section, but is expected to "
> -					echo -n "be added in the EXPERIMENTAL "
> -					echo "section of the version map"
> -					ret=1
> -					continue
> -				fi
> -			fi
> -
> -			# This symbol is moving inside a section, nothing to do
> -			if [ "$oldsecname" = "$secname" ]
> -			then
> -				continue
> -			fi
> -
> -			# This symbol is moving between two sections (the
> -			# original section is a stable section).
> -			# This can be legit, just warn.
> -			if is_stable_section $oldsecname
> -			then
> -				echo -n "INFO: symbol $symname is being "
> -				echo -n "moved from $oldsecname to $secname. "
> -				echo -n "Ensure that it has gone through the "
> -				echo "deprecation process"
> -				continue
> -			fi
> -		else
> -
> -			if ! grep -q "$mname $symname .* add" "$mapdb" && \
> -			   is_stable_section $secname
> -			then
> -				# Just inform users that stable
> -				# symbols need to go through a deprecation
> -				# process
> -				echo -n "INFO: symbol $symname is being "
> -				echo -n "removed, ensure that it has "
> -				echo "gone through the deprecation process"
> -			fi
> -		fi
> -	done < "$mapdb"
> -
> -	return $ret
> -}
> -
> -trap clean_and_exit_on_sig EXIT
> -
> -mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
> -patch=$1
> -exit_code=1
> -
> -clean_and_exit_on_sig()
> -{
> -	rm -f "$mapfile"
> -	exit $exit_code
> -}
> -
> -build_map_changes "$patch" "$mapfile"
> -check_for_rule_violations "$mapfile"
> -exit_code=$?
> -rm -f "$mapfile"
> -
> -exit $exit_code
> diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
> deleted file mode 100755
> index fcd3931e5d..0000000000
> --- a/devtools/check-symbol-maps.sh
> +++ /dev/null
> @@ -1,101 +0,0 @@
> -#! /bin/sh -e
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright 2018 Mellanox Technologies, Ltd
> -
> -cd $(dirname $0)/..
> -
> -# speed up by ignoring Unicode details
> -export LC_ALL=C
> -
> -if [ $# = 0 ] ; then
> -    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
> -fi
> -
> -ret=0
> -
> -find_orphan_symbols ()
> -{
> -    for map in $@ ; do
> -        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
> -            if echo $sym | grep -q '^per_lcore_' ; then
> -                symsrc=${sym#per_lcore_}
> -            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
> -                symsrc=${sym#__}
> -            else
> -                symsrc=$sym
> -            fi
> -            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
> -                echo "$map: $sym"
> -            fi
> -        done
> -    done
> -}
> -
> -orphan_symbols=$(find_orphan_symbols $@)
> -if [ -n "$orphan_symbols" ] ; then
> -    echo "Found only in symbol map file:"
> -    echo "$orphan_symbols" | sed 's,^,\t,'
> -    ret=1
> -fi
> -
> -find_duplicate_symbols ()
> -{
> -    for map in $@ ; do
> -        buildtools/map-list-symbol.sh $map | \
> -            sort | uniq -c | grep -v " 1 $map" || true
> -    done
> -}
> -
> -duplicate_symbols=$(find_duplicate_symbols $@)
> -if [ -n "$duplicate_symbols" ] ; then
> -    echo "Found duplicates in symbol map file:"
> -    echo "$duplicate_symbols"
> -    ret=1
> -fi
> -
> -local_miss_maps=$(grep -L 'local: \*;' $@ || true)
> -if [ -n "$local_miss_maps" ] ; then
> -    echo "Found maps without local catch-all:"
> -    echo "$local_miss_maps"
> -    ret=1
> -fi
> -
> -find_bad_format_maps ()
> -{
> -    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
> -    next_abi_version=$((abi_version + 1))
> -    for map in $@ ; do
> -        cat $map | awk '
> -            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
> -            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
> -            /^$/ { next; } # empty line
> -            /^\t(global:|local: \*;)$/ { next; } # qualifiers
> -            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
> -            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
> -            { print $0; }' || echo $map
> -    done
> -}
> -
> -bad_format_maps=$(find_bad_format_maps $@)
> -if [ -n "$bad_format_maps" ] ; then
> -    echo "Found badly formatted maps:"
> -    echo "$bad_format_maps"
> -    ret=1
> -fi
> -
> -find_non_versioned_maps ()
> -{
> -    for map in $@ ; do
> -        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
> -            echo $map
> -    done
> -}
> -
> -non_versioned_maps=$(find_non_versioned_maps $@)
> -if [ -n "$non_versioned_maps" ] ; then
> -    echo "Found non versioned maps:"
> -    echo "$non_versioned_maps"
> -    ret=1
> -fi
> -
> -exit $ret
> diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
> index 9180c2b070..c111b0fef3 100755
> --- a/devtools/checkpatches.sh
> +++ b/devtools/checkpatches.sh
> @@ -9,7 +9,7 @@
>  # - DPDK_CHECKPATCH_OPTIONS
>  . $(dirname $(readlink -f $0))/load-devel-config
>  
> -VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
> +VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
>  
>  # Enable codespell by default. This can be overwritten from a config file.
>  # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
> diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
> deleted file mode 100755
> index 45437f3c3b..0000000000
> --- a/devtools/update-abi.sh
> +++ /dev/null
> @@ -1,46 +0,0 @@
> -#!/bin/sh -e
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2019 Intel Corporation
> -
> -abi_version=$1
> -abi_version_file="./ABI_VERSION"
> -update_path="lib drivers"
> -
> -# check ABI version format string
> -check_abi_version() {
> -      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
> -}
> -
> -if [ -z "$1" ]; then
> -      # output to stderr
> -      >&2 echo "Please provide ABI version"
> -      exit 1
> -fi
> -
> -# check version string format
> -if ! check_abi_version $abi_version ; then
> -      # output to stderr
> -      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
> -      exit 1
> -fi
> -
> -if [ -n "$2" ]; then
> -      abi_version_file=$2
> -fi
> -
> -if [ -n "$3" ]; then
> -      # drop $1 and $2
> -      shift 2
> -      # assign all other arguments as update paths
> -      update_path=$@
> -fi
> -
> -echo "New ABI version:" $abi_version
> -echo "ABI_VERSION path:" $abi_version_file
> -echo "Path to update:" $update_path
> -
> -echo $abi_version > $abi_version_file
> -
> -find $update_path -name version.map -exec \
> -      devtools/update_version_map_abi.py {} \
> -      $abi_version \; -print
> diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
> deleted file mode 100755
> index d17b02a327..0000000000
> --- a/devtools/update_version_map_abi.py
> +++ /dev/null
> @@ -1,210 +0,0 @@
> -#!/usr/bin/env python3
> -# SPDX-License-Identifier: BSD-3-Clause
> -# Copyright(c) 2019 Intel Corporation
> -
> -"""
> -A Python program that updates and merges all available stable ABI versions into
> -one ABI version, while leaving experimental ABI exactly as it is. The intended
> -ABI version is supplied via command-line parameter. This script is to be called
> -from the devtools/update-abi.sh utility.
> -"""
> -
> -import argparse
> -import sys
> -import re
> -
> -
> -def __parse_map_file(f_in):
> -    # match function name, followed by semicolon, followed by EOL or comments,
> -    # optionally with whitespace in between each item
> -    func_line_regex = re.compile(r"\s*"
> -                                 r"(?P<line>"
> -                                 r"(?P<func>[a-zA-Z_0-9]+)"
> -                                 r"\s*"
> -                                 r";"
> -                                 r"\s*"
> -                                 r"(?P<comment>#.+)?"
> -                                 r")"
> -                                 r"\s*"
> -                                 r"$")
> -    # match section name, followed by opening bracked, followed by EOL,
> -    # optionally with whitespace in between each item
> -    section_begin_regex = re.compile(r"\s*"
> -                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
> -                                     r"\s*"
> -                                     r"{"
> -                                     r"\s*"
> -                                     r"$")
> -    # match closing bracket, optionally followed by section name (for when we
> -    # inherit from another ABI version), followed by semicolon, followed by
> -    # EOL, optionally with whitespace in between each item
> -    section_end_regex = re.compile(r"\s*"
> -                                   r"}"
> -                                   r"\s*"
> -                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
> -                                   r"\s*"
> -                                   r";"
> -                                   r"\s*"
> -                                   r"$")
> -
> -    # for stable ABI, we don't care about which version introduced which
> -    # function, we just flatten the list. there are dupes in certain files, so
> -    # use a set instead of a list
> -    stable_lines = set()
> -    # copy experimental section as is
> -    experimental_lines = []
> -    # copy internal section as is
> -    internal_lines = []
> -    in_experimental = False
> -    in_internal = False
> -    has_stable = False
> -
> -    # gather all functions
> -    for line in f_in:
> -        # clean up the line
> -        line = line.strip('\n').strip()
> -
> -        # is this an end of section?
> -        match = section_end_regex.match(line)
> -        if match:
> -            # whatever section this was, it's not active any more
> -            in_experimental = False
> -            in_internal = False
> -            continue
> -
> -        # if we're in the middle of experimental section, we need to copy
> -        # the section verbatim, so just add the line
> -        if in_experimental:
> -            experimental_lines += [line]
> -            continue
> -
> -        # if we're in the middle of internal section, we need to copy
> -        # the section verbatim, so just add the line
> -        if in_internal:
> -            internal_lines += [line]
> -            continue
> -
> -        # skip empty lines
> -        if not line:
> -            continue
> -
> -        # is this a beginning of a new section?
> -        match = section_begin_regex.match(line)
> -        if match:
> -            cur_section = match.group("version")
> -            # is it experimental?
> -            in_experimental = cur_section == "EXPERIMENTAL"
> -            # is it internal?
> -            in_internal = cur_section == "INTERNAL"
> -            if not in_experimental and not in_internal:
> -                has_stable = True
> -            continue
> -
> -        # is this a function?
> -        match = func_line_regex.match(line)
> -        if match:
> -            stable_lines.add(match.group("line"))
> -
> -    return has_stable, stable_lines, experimental_lines, internal_lines
> -
> -
> -def __generate_stable_abi(f_out, abi_major, lines):
> -    # print ABI version header
> -    print("DPDK_{} {{".format(abi_major), file=f_out)
> -
> -    # print global section if it exists
> -    if lines:
> -        print("\tglobal:", file=f_out)
> -        # blank line
> -        print(file=f_out)
> -
> -        # print all stable lines, alphabetically sorted
> -        for line in sorted(lines):
> -            print("\t{}".format(line), file=f_out)
> -
> -        # another blank line
> -        print(file=f_out)
> -
> -    # print local section
> -    print("\tlocal: *;", file=f_out)
> -
> -    # end stable version
> -    print("};", file=f_out)
> -
> -
> -def __generate_experimental_abi(f_out, lines):
> -    # start experimental section
> -    print("EXPERIMENTAL {", file=f_out)
> -
> -    # print all experimental lines as they were
> -    for line in lines:
> -        # don't print empty whitespace
> -        if not line:
> -            print("", file=f_out)
> -        else:
> -            print("\t{}".format(line), file=f_out)
> -
> -    # end section
> -    print("};", file=f_out)
> -
> -def __generate_internal_abi(f_out, lines):
> -    # start internal section
> -    print("INTERNAL {", file=f_out)
> -
> -    # print all internal lines as they were
> -    for line in lines:
> -        # don't print empty whitespace
> -        if not line:
> -            print("", file=f_out)
> -        else:
> -            print("\t{}".format(line), file=f_out)
> -
> -    # end section
> -    print("};", file=f_out)
> -
> -def __main():
> -    arg_parser = argparse.ArgumentParser(
> -        description='Merge versions in linker version script.')
> -
> -    arg_parser.add_argument("map_file", type=str,
> -                            help='path to linker version script file '
> -                                 '(pattern: version.map)')
> -    arg_parser.add_argument("abi_version", type=str,
> -                            help='target ABI version (pattern: MAJOR.MINOR)')
> -
> -    parsed = arg_parser.parse_args()
> -
> -    if not parsed.map_file.endswith('version.map'):
> -        print("Invalid input file: {}".format(parsed.map_file),
> -              file=sys.stderr)
> -        arg_parser.print_help()
> -        sys.exit(1)
> -
> -    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
> -        print("Invalid ABI version: {}".format(parsed.abi_version),
> -              file=sys.stderr)
> -        arg_parser.print_help()
> -        sys.exit(1)
> -    abi_major = parsed.abi_version.split('.')[0]
> -
> -    with open(parsed.map_file) as f_in:
> -        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
> -
> -    with open(parsed.map_file, 'w') as f_out:
> -        need_newline = has_stable and experimental_lines
> -        if has_stable:
> -            __generate_stable_abi(f_out, abi_major, stable_lines)
> -        if need_newline:
> -            # separate sections with a newline
> -            print(file=f_out)
> -        if experimental_lines:
> -            __generate_experimental_abi(f_out, experimental_lines)
> -        if internal_lines:
> -            if has_stable or experimental_lines:
> -              # separate sections with a newline
> -              print(file=f_out)
> -            __generate_internal_abi(f_out, internal_lines)
> -
> -
> -if __name__ == "__main__":
> -    __main()
> diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
> index d96153c6b2..f03a7467ac 100644
> --- a/doc/guides/contributing/abi_policy.rst
> +++ b/doc/guides/contributing/abi_policy.rst
> @@ -330,31 +330,14 @@ become part of a tracked ABI version.
>  
>  Note that marking an API as experimental is a multi step process.
>  To mark an API as experimental, the symbols which are desired to be exported
> -must be placed in an EXPERIMENTAL version block in the corresponding libraries'
> -version map script.
> +must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
> +sources.
>  Experimental symbols must be commented so that it is clear in which DPDK
>  version they were introduced.
>  
> -.. code-block:: none
> -
> -   EXPERIMENTAL {
> -           global:
> -
> -           # added in 20.11
> -           rte_foo_init;
> -           rte_foo_configure;
> -
> -           # added in 21.02
> -           rte_foo_cleanup;
> -   ...
> -
>  Secondly, the corresponding prototypes of those exported functions (in the
>  development header files), must be marked with the ``__rte_experimental`` tag
>  (see ``rte_compat.h``).
> -The DPDK build makefiles perform a check to ensure that the map file and the
> -C code reflect the same list of symbols.
> -This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
> -during compilation in the corresponding library Makefile.
>  
>  In addition to tagging the code with ``__rte_experimental``,
>  the doxygen markup must also contain the EXPERIMENTAL string,
> diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
> index 1ebc79ca3c..43e27bbd0a 100644
> --- a/doc/guides/contributing/coding_style.rst
> +++ b/doc/guides/contributing/coding_style.rst
> @@ -1018,13 +1018,6 @@ name
>  	sources are stored in a directory ``lib/xyz``, this value should
>  	never be needed for new libraries.
>  
> -.. note::
> -
> -	The name value also provides the name used to find the function version
> -	map file, as part of the build process, so if the directory name and
> -	library names differ, the ``version.map`` file should be named
> -	consistently with the library, not the directory
> -
>  objs
>  	**Default Value = []**.
>  	This variable can be used to pass to the library build some pre-built
> diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
> index 4debb07b98..a06d8a2a3b 100644
> --- a/doc/guides/contributing/img/patch_cheatsheet.svg
> +++ b/doc/guides/contributing/img/patch_cheatsheet.svg
> @@ -1,18 +1,18 @@
>  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
>  <svg
> -   xmlns:dc="http://purl.org/dc/elements/1.1/"
> -   xmlns:cc="http://creativecommons.org/ns#"
> -   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
> -   xmlns:svg="http://www.w3.org/2000/svg"
> -   xmlns="http://www.w3.org/2000/svg"
> -   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
> -   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
>     version="1.1"
>     width="210mm"
>     height="297mm"
>     id="svg2985"
> -   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
> -   sodipodi:docname="patch_cheatsheet.svg">
> +   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
> +   sodipodi:docname="patch_cheatsheet.svg"
> +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
> +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
> +   xmlns="http://www.w3.org/2000/svg"
> +   xmlns:svg="http://www.w3.org/2000/svg"
> +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
> +   xmlns:cc="http://creativecommons.org/ns#"
> +   xmlns:dc="http://purl.org/dc/elements/1.1/">
>    <sodipodi:namedview
>       pagecolor="#ffffff"
>       bordercolor="#666666"
> @@ -23,18 +23,22 @@
>       inkscape:pageopacity="0"
>       inkscape:pageshadow="2"
>       inkscape:window-width="1920"
> -     inkscape:window-height="1017"
> +     inkscape:window-height="975"
>       id="namedview274"
>       showgrid="false"
>       inkscape:zoom="0.89702958"
> -     inkscape:cx="246.07409"
> -     inkscape:cy="416.76022"
> -     inkscape:window-x="1072"
> -     inkscape:window-y="-8"
> +     inkscape:cx="546.24732"
> +     inkscape:cy="385.71749"
> +     inkscape:window-x="0"
> +     inkscape:window-y="0"
>       inkscape:window-maximized="1"
>       inkscape:current-layer="layer1"
>       inkscape:document-rotation="0"
> -     inkscape:snap-grids="false" />
> +     inkscape:snap-grids="false"
> +     inkscape:showpageshadow="2"
> +     inkscape:pagecheckerboard="0"
> +     inkscape:deskcolor="#d1d1d1"
> +     inkscape:document-units="mm" />
>    <defs
>       id="defs3">
>      <linearGradient
> @@ -906,7 +910,7 @@
>               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
>               id="tspan4092-8-7-6-9-7"
>               y="855.79816"
> -             x="460.18405">****</tspan></text>
> +             x="460.18405">***</tspan></text>
>        </g>
>      </g>
>      <text
> @@ -1132,161 +1136,126 @@
>             id="tspan4092-8-6-3-1-8-4-4-55-7"
>             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
>      </g>
> +    <text
> +       x="424.10629"
> +       y="363.21423"
> +       id="text4090-8"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="363.21423"
> +         id="tspan4092-8"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
> +    <text
> +       x="424.10629"
> +       y="393.60123"
> +       id="text4090-8-5"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="393.60123"
> +         id="tspan4092-8-5"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
> +    <text
> +       x="424.10629"
> +       y="424.20575"
> +       id="text4090-8-5-6"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="424.20575"
> +         id="tspan4092-8-5-5"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
> +    <text
> +       x="424.10629"
> +       y="453.10339"
> +       id="text4090-8-5-6-9-4"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="453.10339"
> +         id="tspan4092-8-5-5-3-4"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
> +    <text
> +       x="424.10629"
> +       y="514.09497"
> +       id="text4090-8-5-6-9-4-6"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="424.10629"
> +         y="514.09497"
> +         id="tspan4092-8-5-5-3-4-0"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
> +    <text
> +       x="425.12708"
> +       y="544.91718"
> +       id="text4090-8-5-6-9-4-6-6"
> +       xml:space="preserve"
> +       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
> +       transform="scale(1.0105317,0.98957807)"><tspan
> +         x="425.12708"
> +         y="544.91718"
> +         id="tspan4092-8-5-5-3-4-0-6"
> +         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
>      <g
> -       transform="translate(1.0962334,-2.7492248)"
> -       id="g3605">
> -      <text
> -         x="42.176418"
> -         y="1020.4383"
> -         id="text4090-8-7-8-7-6-3-8-4"
> -         xml:space="preserve"
> -         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -           x="42.176418"
> -           y="1020.4383"
> -           id="tspan4092-8-6-3-1-8-4-4-55"
> -           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
> -      <text
> -         x="30.942892"
> -         y="1024.2014"
> -         id="text4090-8-7-8-7-6-3-8-4-1-5"
> -         xml:space="preserve"
> -         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -           x="30.942892"
> -           y="1024.2014"
> -           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> -      <text
> -         x="25.247679"
> -         y="1024.2014"
> -         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
> -         xml:space="preserve"
> -         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -           x="25.247679"
> -           y="1024.2014"
> -           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> -    </g>
> -    <g
> -       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
> -       id="g3275">
> +       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
> +       id="g3334">
>        <g
> -         id="g3341">
> +         id="g3267"
> +         transform="translate(-13.517932,3.1531035)">
>          <text
> -           x="394.78601"
> -           y="390.17807"
> -           id="text4090-8"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="390.17807"
> -             id="tspan4092-8"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
> -        <text
> -           x="394.78601"
> -           y="420.24835"
> -           id="text4090-8-5"
> +           x="660.46729"
> +           y="468.01297"
> +           id="text4090-8-1-8-9-1-4-1"
>             xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="420.24835"
> -             id="tspan4092-8-5"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
> -        <text
> -           x="394.78601"
> -           y="450.53394"
> -           id="text4090-8-5-6"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="450.53394"
> -             id="tspan4092-8-5-5"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
> -        <text
> +           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> +             x="660.46729"
> +             y="468.01297"
> +             id="tspan4092-8-7-6-9-7-0-7"
> +             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
> +      </g>
> +      <text
> +         x="394.78601"
> +         y="483.59955"
> +         id="text4090-8-5-6-9"
> +         xml:space="preserve"
> +         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
>             x="394.78601"
> -           y="513.13031"
> -           id="text4090-8-5-6-9-4"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="513.13031"
> -             id="tspan4092-8-5-5-3-4"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
> -        <text
> +           y="483.59955"
> +           id="tspan4092-8-5-5-3"
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
> +    </g>
> +    <g
> +       id="g3428"
> +       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
> +      <text
> +         x="394.78601"
> +         y="541.38928"
> +         id="text4090-8-5-6-9-4-6-1"
> +         xml:space="preserve"
> +         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
>             x="394.78601"
> -           y="573.48621"
> -           id="text4090-8-5-6-9-4-6"
> -           xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="394.78601"
> -             y="573.48621"
> -             id="tspan4092-8-5-5-3-4-0"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
> +           y="541.38928"
> +           id="tspan4092-8-5-5-3-4-0-7"
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
> +      <g
> +         transform="translate(-119.92979,57.949844)"
> +         id="g3267-9">
>          <text
> -           x="395.79617"
> -           y="603.98718"
> -           id="text4090-8-5-6-9-4-6-6"
> +           x="628.93628"
> +           y="473.13675"
> +           id="text4090-8-1-8-9-1-4-1-4"
>             xml:space="preserve"
> -           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -             x="395.79617"
> -             y="603.98718"
> -             id="tspan4092-8-5-5-3-4-0-6"
> -             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
> -        <g
> -           transform="translate(0,-0.83470152)"
> -           id="g3334">
> -          <g
> -             id="g3267"
> -             transform="translate(-13.517932,3.1531035)">
> -            <text
> -               x="660.46729"
> -               y="468.01297"
> -               id="text4090-8-1-8-9-1-4-1"
> -               xml:space="preserve"
> -               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -                 x="660.46729"
> -                 y="468.01297"
> -                 id="tspan4092-8-7-6-9-7-0-7"
> -                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
> -          </g>
> -          <text
> -             x="394.78601"
> -             y="483.59955"
> -             id="text4090-8-5-6-9"
> -             xml:space="preserve"
> -             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -               x="394.78601"
> -               y="483.59955"
> -               id="tspan4092-8-5-5-3"
> -               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
> -        </g>
> -        <g
> -           id="g3428"
> -           transform="translate(0,0.88137813)">
> -          <text
> -             x="394.78601"
> -             y="541.38928"
> -             id="text4090-8-5-6-9-4-6-1"
> -             xml:space="preserve"
> -             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -               x="394.78601"
> -               y="541.38928"
> -               id="tspan4092-8-5-5-3-4-0-7"
> -               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
> -          <g
> -             transform="translate(-119.92979,57.949844)"
> -             id="g3267-9">
> -            <text
> -               x="628.93628"
> -               y="473.13675"
> -               id="text4090-8-1-8-9-1-4-1-4"
> -               xml:space="preserve"
> -               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> -                 x="628.93628"
> -                 y="473.13675"
> -                 id="tspan4092-8-7-6-9-7-0-7-8"
> -                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
> -          </g>
> -        </g>
> +           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
> +             x="628.93628"
> +             y="473.13675"
> +             id="tspan4092-8-7-6-9-7-0-7-8"
> +             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
>        </g>
>      </g>
>      <text
> @@ -1301,7 +1270,7 @@
>           id="tspan4092-8-5-5-3-4-0-6-2-11-0"
>           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
>      <g
> -       transform="translate(1.0962334,-2.7492248)"
> +       transform="translate(1.0962334,-14.749225)"
>         id="g3595">
>        <text
>           x="30.942892"
> @@ -1332,7 +1301,7 @@
>             x="19.552465"
>             y="1037.0271"
>             id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
>        <text
>           x="42.830166"
>           y="1033.2393"
> @@ -1345,7 +1314,7 @@
>             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
>      </g>
>      <g
> -       transform="translate(1.0962334,-2.7492248)"
> +       transform="translate(1.0962334,-14.749225)"
>         id="g3619">
>        <text
>           x="42.212418"
> @@ -1396,7 +1365,7 @@
>             x="14.016749"
>             y="1049.8527"
>             id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
> -           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
> +           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
>      </g>
>      <rect
>         width="196.44218"
> diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
> index d21ee288b2..8ad6b6e715 100644
> --- a/doc/guides/contributing/patches.rst
> +++ b/doc/guides/contributing/patches.rst
> @@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
>  
>    * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
>  
> -* New external functions should be added to the local ``version.map`` file. See
> -  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
> -  guides. New external functions should also be added in alphabetical order.
> +* New external functions should be exported.
> +  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
> +  guides.
>  
>  * Any new API function should be used in ``/app`` test directory.
>  
> diff --git a/drivers/meson.build b/drivers/meson.build
> index 5368d38363..15a4991abf 100644
> --- a/drivers/meson.build
> +++ b/drivers/meson.build
> @@ -5,8 +5,6 @@ if is_ms_compiler
>      subdir_done()
>  endif
>  
> -fs = import('fs')
> -
>  # Defines the order of dependencies evaluation
>  subdirs = [
>          'common',
> @@ -290,57 +288,25 @@ foreach subpath:subdirs
>                  install: true)
>  
>          # now build the shared driver
> -        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
> -
> -        if not fs.is_file(version_map)
> -            if is_ms_linker
> -                link_mode = 'mslinker'
> -            elif is_windows
> -                link_mode = 'mingw'
> -            else
> -                link_mode = 'gnu'
> -            endif
> -            version_map = custom_target(lib_name + '_map',
> -                    command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> -                    input: sources + sources_avx2 + sources_avx512,
> -                    output: '_'.join([class, name, 'exports.map']))
> -            version_map_path = version_map.full_path()
> -            version_map_dep = [version_map]
> -            lk_deps = [version_map]
> -
> -            if is_ms_linker and is_ms_compiler
> -                lk_args = ['/def:' + version_map.full_path()]
> -            elif is_ms_linker
> -                lk_args = ['-Wl,/def:' + version_map.full_path()]
> -            else
> -                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
> -            endif
> +        if is_ms_linker
> +            link_mode = 'mslinker'
> +        elif is_windows
> +            link_mode = 'mingw'
>          else
> -            version_map_path = version_map
> -            version_map_dep = []
> -            lk_deps = [version_map]
> -
> -            if is_windows
> -                if is_ms_linker
> -                    def_file = custom_target(lib_name + '_def',
> -                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                            input: version_map,
> -                            output: '@0@_exports.def'.format(lib_name))
> -                    lk_deps += [def_file]
> -
> -                    lk_args = ['-Wl,/def:' + def_file.full_path()]
> -                else
> -                    mingw_map = custom_target(lib_name + '_mingw',
> -                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                            input: version_map,
> -                            output: '@0@_mingw.map'.format(lib_name))
> -                    lk_deps += [mingw_map]
> -
> -                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> -                endif
> -            else
> -                lk_args = ['-Wl,--version-script=' + version_map]
> -            endif
> +            link_mode = 'gnu'
> +        endif
> +        version_map = custom_target(lib_name + '_map',
> +                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> +                input: sources + sources_avx2 + sources_avx512,
> +                output: '_'.join([class, name, 'exports.map']))
> +        lk_deps = [version_map]
> +
> +        if is_ms_linker and is_ms_compiler
> +            lk_args = ['/def:' + version_map.full_path()]
> +        elif is_ms_linker
> +            lk_args = ['-Wl,/def:' + version_map.full_path()]
> +        else
> +            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
>          endif
>  
>          if not is_windows and developer_mode
> @@ -348,11 +314,11 @@ foreach subpath:subdirs
>              # check-symbols.sh script, using it as a
>              # dependency of the .so build
>              lk_deps += custom_target(lib_name + '.sym_chk',
> -                    command: [check_symbols, version_map_path, '@INPUT@'],
> +                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
>                      capture: true,
>                      input: static_lib,
>                      output: lib_name + '.sym_chk',
> -                    depends: version_map_dep)
> +                    depends: [version_map])
>          endif
>  
>          shared_lib = shared_library(lib_name, sources_pmd_info,
> diff --git a/lib/meson.build b/lib/meson.build
> index a9cbea5fea..6157d0e13e 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -1,7 +1,6 @@
>  # SPDX-License-Identifier: BSD-3-Clause
>  # Copyright(c) 2017-2019 Intel Corporation
>  
> -fs = import('fs')
>  
>  # process all libraries equally, as far as possible
>  # "core" libs first, then others alphabetically as far as possible
> @@ -289,59 +288,25 @@ foreach l:libraries
>              include_directories: includes,
>              dependencies: static_deps)
>  
> -    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
> -        if is_ms_linker
> -            link_mode = 'mslinker'
> -        elif is_windows
> -            link_mode = 'mingw'
> -        else
> -            link_mode = 'gnu'
> -        endif
> -        version_map = custom_target(libname + '_map',
> -                command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> -                input: sources,
> -                output: '_'.join([name, 'exports.map']))
> -        version_map_path = version_map.full_path()
> -        version_map_dep = [version_map]
> -        lk_deps = [version_map]
> -
> -        if is_ms_linker and is_ms_compiler
> -            lk_args = ['/def:' + version_map.full_path()]
> -        elif is_ms_linker
> -            lk_args = ['-Wl,/def:' + version_map.full_path()]
> -        else
> -            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
> -        endif
> +    if is_ms_linker
> +        link_mode = 'mslinker'
> +    elif is_windows
> +        link_mode = 'mingw'
>      else
> -        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
> -        version_map_path = version_map
> -        version_map_dep = []
> -        lk_deps = [version_map]
> -        if is_ms_linker
> -            def_file = custom_target(libname + '_def',
> -                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                    input: version_map,
> -                    output: '@0@_exports.def'.format(libname))
> -            lk_deps += [def_file]
> -
> -            if is_ms_compiler
> -                lk_args = ['/def:' + def_file.full_path()]
> -            else
> -                lk_args = ['-Wl,/def:' + def_file.full_path()]
> -            endif
> -        else
> -            if is_windows
> -                mingw_map = custom_target(libname + '_mingw',
> -                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                        input: version_map,
> -                        output: '@0@_mingw.map'.format(libname))
> -                lk_deps += [mingw_map]
> +        link_mode = 'gnu'
> +    endif
> +    version_map = custom_target(libname + '_map',
> +            command: [gen_version_map, link_mode, abi_version_file, '@OUTPUT@', '@INPUT@'],
> +            input: sources,
> +            output: '_'.join([name, 'exports.map']))
> +    lk_deps = [version_map]
>  
> -                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> -            else
> -                lk_args = ['-Wl,--version-script=' + version_map]
> -            endif
> -        endif
> +    if is_ms_linker and is_ms_compiler
> +        lk_args = ['/def:' + version_map.full_path()]
> +    elif is_ms_linker
> +        lk_args = ['-Wl,/def:' + version_map.full_path()]
> +    else
> +        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
>      endif
>  
>      if developer_mode and not is_windows
> @@ -349,11 +314,11 @@ foreach l:libraries
>          # check-symbols.sh script, using it as a
>          # dependency of the .so build
>          lk_deps += custom_target(name + '.sym_chk',
> -                command: [check_symbols, version_map_path, '@INPUT@'],
> +                command: [check_symbols, version_map.full_path(), '@INPUT@'],
>                  capture: true,
>                  input: static_lib,
>                  output: name + '.sym_chk',
> -                depends: version_map_dep)
> +                depends: [version_map])
>      endif
>  
>      if not use_function_versioning or is_windows


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v6 4/8] build: generate symbol maps
  2025-03-28 10:52 17%   ` [PATCH v6 4/8] build: generate symbol maps David Marchand
@ 2025-04-01 20:33  0%     ` Thomas Monjalon
  2025-04-02  7:01  0%       ` David Marchand
  0 siblings, 1 reply; 153+ results
From: Thomas Monjalon @ 2025-04-01 20:33 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, bruce.richardson, andremue, Tyler Retzlaff

28/03/2025 11:52, David Marchand:
> +scriptname, link_mode, abi_version_file, output, *files = sys.argv

You should add a comment to show the usage of the script.

[...]
> +if link_mode == 'mslinker':
> +    with open(output, "w") as outfile:
> +        print(f"EXPORTS", file=outfile)
> +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> +            if key not in symbols:
> +                continue
> +            for symbol in sorted(symbols[key].keys()):
> +                print(f"\t{symbol}", file=outfile)
> +            del symbols[key]
> +else:

Maybe add a comment after the else to make explicit it is GNU format.

[...]
> +for file in sys.argv[1:]:

This script requires a comment with the usage as well
to make clear what we expect as arguments.

> +    with open(file, encoding="utf-8") as f:
> +        for ln in f.readlines():
> +            if file_header_regexp.match(ln):
> +                if file_header_regexp.match(ln).group(2) == "lib":
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> +                elif file_header_regexp.match(ln).group(3) == "intel":
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
> +                else:
> +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))

[...]
> +#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
> +#define RTE_EXPORT_INTERNAL_SYMBOL(a)
> +#define RTE_EXPORT_SYMBOL(a)

I would prefer an explicit variable name instead of "a".



^ permalink raw reply	[relevance 0%]

* Re: [PATCH v6 4/8] build: generate symbol maps
  2025-04-01 20:33  0%     ` Thomas Monjalon
@ 2025-04-02  7:01  0%       ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-04-02  7:01 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev, bruce.richardson, andremue, Tyler Retzlaff

On Tue, Apr 1, 2025 at 10:34 PM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 28/03/2025 11:52, David Marchand:
> > +scriptname, link_mode, abi_version_file, output, *files = sys.argv
>
> You should add a comment to show the usage of the script.
>
> [...]
> > +if link_mode == 'mslinker':
> > +    with open(output, "w") as outfile:
> > +        print(f"EXPORTS", file=outfile)
> > +        for key in (abi, 'EXPERIMENTAL', 'INTERNAL'):
> > +            if key not in symbols:
> > +                continue
> > +            for symbol in sorted(symbols[key].keys()):
> > +                print(f"\t{symbol}", file=outfile)
> > +            del symbols[key]
> > +else:
>
> Maybe add a comment after the else to make explicit it is GNU format.
>
> [...]
> > +for file in sys.argv[1:]:
>
> This script requires a comment with the usage as well
> to make clear what we expect as arguments.

Ack.

>
> > +    with open(file, encoding="utf-8") as f:
> > +        for ln in f.readlines():
> > +            if file_header_regexp.match(ln):
> > +                if file_header_regexp.match(ln).group(2) == "lib":
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
> > +                elif file_header_regexp.match(ln).group(3) == "intel":
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3, 4))
> > +                else:
> > +                    lib = '/'.join(file_header_regexp.match(ln).group(2, 3))
>
> [...]
> > +#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(a, ver)
> > +#define RTE_EXPORT_INTERNAL_SYMBOL(a)
> > +#define RTE_EXPORT_SYMBOL(a)
>
> I would prefer an explicit variable name instead of "a".

Ack.

Thanks for the review.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v6 8/8] eal: rework function versioning macros
  2025-03-28 10:52 20%   ` [PATCH v6 8/8] eal: rework function versioning macros David Marchand
@ 2025-04-02 11:53  0%     ` Thomas Monjalon
  2025-04-02 12:16  0%       ` David Marchand
  0 siblings, 1 reply; 153+ results
From: Thomas Monjalon @ 2025-04-02 11:53 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

28/03/2025 11:52, David Marchand:
> --- a/lib/eal/common/eal_symbol_exports.h
> +++ b/lib/eal/common/eal_symbol_exports.h
> @@ -5,6 +5,8 @@
>  #ifndef EAL_SYMBOL_EXPORTS_H
>  #define EAL_SYMBOL_EXPORTS_H
>  
> +#include <rte_common.h>
> +
>  /* Internal macros for exporting symbols, used by the build system.
>   * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
>   * version this symbol was introduced in.
> @@ -13,4 +15,68 @@
>  #define RTE_EXPORT_INTERNAL_SYMBOL(a)
>  #define RTE_EXPORT_SYMBOL(a)
>  
> +#if !defined(RTE_USE_FUNCTION_VERSIONING) && (defined(RTE_CC_GCC) || defined(RTE_CC_CLANG))
> +#define VERSIONING_WARN RTE_PRAGMA_WARNING(Use of function versioning disabled. \
> +       Is "use_function_versioning=true" in meson.build?)
> +#else
> +#define VERSIONING_WARN
> +#endif

Why no warning for other compilers?
Why not warn at Meson level?

> +
> +/*
> + * Provides backwards compatibility when updating exported functions.

I feel a word is missing. What "provides" it?

> + * When a symbol is exported from a library to provide an API, it also provides a
> + * calling convention (ABI) that is embodied in its name, return type,
> + * arguments, etc.  On occasion that function may need to change to accommodate
> + * new functionality, behavior, etc.  When that occurs, it is desirable to
> + * allow for backwards compatibility for a time with older binaries that are
> + * dynamically linked to the dpdk.
> + */
> +
> +#ifdef RTE_BUILD_SHARED_LIB
> +
> +/*

Why not Doxygen style?

> + * RTE_VERSION_SYMBOL

No need to repeat the macro name here.

> + * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal

Imperative form is more usual.

> + * function name <name>_v<ver>.
> + */
> +#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
> +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
> +__rte_used type name ## _v ## ver args; \
> +type name ## _v ## ver args

This is only GNU style. It should be highlighted both in this file and the commit log.

> +
> +/*
> + * RTE_VERSION_EXPERIMENTAL_SYMBOL
> + * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
> + * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
> + */
> +#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
> +__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
> +__rte_used type name ## _exp args; \
> +type name ## _exp args
> +
> +/*
> + * RTE_DEFAULT_SYMBOL
> + * Creates a symbol version entry instructing the linker to bind references to
> + * symbol <name> to the internal symbol <name>_v<ver>.
> + */
> +#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
> +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
> +__rte_used type name ## _v ## ver args; \
> +type name ## _v ## ver args

Only 3 macros, that's simple and clean, thank you.



^ permalink raw reply	[relevance 0%]

* Re: [PATCH v6 8/8] eal: rework function versioning macros
  2025-04-02 11:53  0%     ` Thomas Monjalon
@ 2025-04-02 12:16  0%       ` David Marchand
  0 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-04-02 12:16 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: dev, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

On Wed, Apr 2, 2025 at 1:53 PM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 28/03/2025 11:52, David Marchand:
> > --- a/lib/eal/common/eal_symbol_exports.h
> > +++ b/lib/eal/common/eal_symbol_exports.h
> > @@ -5,6 +5,8 @@
> >  #ifndef EAL_SYMBOL_EXPORTS_H
> >  #define EAL_SYMBOL_EXPORTS_H
> >
> > +#include <rte_common.h>
> > +
> >  /* Internal macros for exporting symbols, used by the build system.
> >   * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
> >   * version this symbol was introduced in.
> > @@ -13,4 +15,68 @@
> >  #define RTE_EXPORT_INTERNAL_SYMBOL(a)
> >  #define RTE_EXPORT_SYMBOL(a)
> >
> > +#if !defined(RTE_USE_FUNCTION_VERSIONING) && (defined(RTE_CC_GCC) || defined(RTE_CC_CLANG))
> > +#define VERSIONING_WARN RTE_PRAGMA_WARNING(Use of function versioning disabled. \
> > +       Is "use_function_versioning=true" in meson.build?)
> > +#else
> > +#define VERSIONING_WARN
> > +#endif
>
> Why no warning for other compilers?
> Why not warn at Meson level?

Symbol versioning is not functional with Windows compiler / linker atm.

When it is ready, the right warning mechanism can be implemented here.

>
> > +
> > +/*
> > + * Provides backwards compatibility when updating exported functions.
>
> I feel a word is missing. What "provides" it?

I'll reword.


>
> > + * When a symbol is exported from a library to provide an API, it also provides a
> > + * calling convention (ABI) that is embodied in its name, return type,
> > + * arguments, etc.  On occasion that function may need to change to accommodate
> > + * new functionality, behavior, etc.  When that occurs, it is desirable to
> > + * allow for backwards compatibility for a time with older binaries that are
> > + * dynamically linked to the dpdk.
> > + */
> > +
> > +#ifdef RTE_BUILD_SHARED_LIB
> > +
> > +/*
>
> Why not Doxygen style?

Because this header is internal, and not parsed by Doxygen?


>
> > + * RTE_VERSION_SYMBOL
>
> No need to repeat the macro name here.
>
> > + * Creates a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
>
> Imperative form is more usual.

Ack.

>
> > + * function name <name>_v<ver>.
> > + */
> > +#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
> > +__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
> > +__rte_used type name ## _v ## ver args; \
> > +type name ## _v ## ver args
>
> This is only GNU style. It should be highlighted both in this file and the commit log.

Yes, I'll insist on this point in the commitlog.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* [PATCH v12 01/10] mbuf: replace term sanity check
  @ 2025-04-02 23:23  3%   ` Stephen Hemminger
  0 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-04-02 23:23 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Andrew Rybchenko, Morten Brørup,
	Akhil Goyal, Fan Zhang

Replace rte_mbuf_sanity_check() with rte_mbuf_verify()
to match the similar macro RTE_VERIFY() in rte_debug.h

The term sanity check is on the Tier 2 list of words
that should be replaced.

For this release keep old API functions but mark them
as deprecated.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_cryptodev.c            |  2 +-
 app/test/test_mbuf.c                 | 28 +++++-----
 doc/guides/prog_guide/mbuf_lib.rst   |  4 +-
 doc/guides/rel_notes/deprecation.rst |  3 ++
 lib/mbuf/rte_mbuf.c                  | 23 +++++---
 lib/mbuf/rte_mbuf.h                  | 79 +++++++++++++++-------------
 lib/mbuf/version.map                 |  1 +
 7 files changed, 79 insertions(+), 61 deletions(-)

diff --git a/app/test/test_cryptodev.c b/app/test/test_cryptodev.c
index 31a4905a97..d5f3843daf 100644
--- a/app/test/test_cryptodev.c
+++ b/app/test/test_cryptodev.c
@@ -264,7 +264,7 @@ create_mbuf_from_heap(int pkt_len, uint8_t pattern)
 	m->port = RTE_MBUF_PORT_INVALID;
 	m->buf_len = MBUF_SIZE - sizeof(struct rte_mbuf) - RTE_PKTMBUF_HEADROOM;
 	rte_pktmbuf_reset_headroom(m);
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	m->buf_addr = (char *)m + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM;
 
diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
index 17be977f31..3fbb5dea8b 100644
--- a/app/test/test_mbuf.c
+++ b/app/test/test_mbuf.c
@@ -262,8 +262,8 @@ test_one_pktmbuf(struct rte_mempool *pktmbuf_pool)
 		GOTO_FAIL("Buffer should be continuous");
 	memset(hdr, 0x55, MBUF_TEST_HDR2_LEN);
 
-	rte_mbuf_sanity_check(m, 1);
-	rte_mbuf_sanity_check(m, 0);
+	rte_mbuf_verify(m, 1);
+	rte_mbuf_verify(m, 0);
 	rte_pktmbuf_dump(stdout, m, 0);
 
 	/* this prepend should fail */
@@ -1162,7 +1162,7 @@ test_refcnt_mbuf(void)
 
 #ifdef RTE_EXEC_ENV_WINDOWS
 static int
-test_failing_mbuf_sanity_check(struct rte_mempool *pktmbuf_pool)
+test_failing_mbuf_verify(struct rte_mempool *pktmbuf_pool)
 {
 	RTE_SET_USED(pktmbuf_pool);
 	return TEST_SKIPPED;
@@ -1181,12 +1181,12 @@ mbuf_check_pass(struct rte_mbuf *buf)
 }
 
 static int
-test_failing_mbuf_sanity_check(struct rte_mempool *pktmbuf_pool)
+test_failing_mbuf_verify(struct rte_mempool *pktmbuf_pool)
 {
 	struct rte_mbuf *buf;
 	struct rte_mbuf badbuf;
 
-	printf("Checking rte_mbuf_sanity_check for failure conditions\n");
+	printf("Checking rte_mbuf_verify for failure conditions\n");
 
 	/* get a good mbuf to use to make copies */
 	buf = rte_pktmbuf_alloc(pktmbuf_pool);
@@ -1708,7 +1708,7 @@ test_mbuf_validate_tx_offload(const char *test_name,
 		GOTO_FAIL("%s: mbuf allocation failed!\n", __func__);
 	if (rte_pktmbuf_pkt_len(m) != 0)
 		GOTO_FAIL("%s: Bad packet length\n", __func__);
-	rte_mbuf_sanity_check(m, 0);
+	rte_mbuf_verify(m, 0);
 	m->ol_flags = ol_flags;
 	m->tso_segsz = segsize;
 	ret = rte_validate_tx_offload(m);
@@ -1915,7 +1915,7 @@ test_pktmbuf_read(struct rte_mempool *pktmbuf_pool)
 		GOTO_FAIL("%s: mbuf allocation failed!\n", __func__);
 	if (rte_pktmbuf_pkt_len(m) != 0)
 		GOTO_FAIL("%s: Bad packet length\n", __func__);
-	rte_mbuf_sanity_check(m, 0);
+	rte_mbuf_verify(m, 0);
 
 	data = rte_pktmbuf_append(m, MBUF_TEST_DATA_LEN2);
 	if (data == NULL)
@@ -1964,7 +1964,7 @@ test_pktmbuf_read_from_offset(struct rte_mempool *pktmbuf_pool)
 
 	if (rte_pktmbuf_pkt_len(m) != 0)
 		GOTO_FAIL("%s: Bad packet length\n", __func__);
-	rte_mbuf_sanity_check(m, 0);
+	rte_mbuf_verify(m, 0);
 
 	/* prepend an ethernet header */
 	hdr = (struct ether_hdr *)rte_pktmbuf_prepend(m, hdr_len);
@@ -2109,7 +2109,7 @@ create_packet(struct rte_mempool *pktmbuf_pool,
 			GOTO_FAIL("%s: mbuf allocation failed!\n", __func__);
 		if (rte_pktmbuf_pkt_len(pkt_seg) != 0)
 			GOTO_FAIL("%s: Bad packet length\n", __func__);
-		rte_mbuf_sanity_check(pkt_seg, 0);
+		rte_mbuf_verify(pkt_seg, 0);
 		/* Add header only for the first segment */
 		if (test_data->flags == MBUF_HEADER && seg == 0) {
 			hdr_len = sizeof(struct rte_ether_hdr);
@@ -2321,7 +2321,7 @@ test_pktmbuf_ext_shinfo_init_helper(struct rte_mempool *pktmbuf_pool)
 		GOTO_FAIL("%s: mbuf allocation failed!\n", __func__);
 	if (rte_pktmbuf_pkt_len(m) != 0)
 		GOTO_FAIL("%s: Bad packet length\n", __func__);
-	rte_mbuf_sanity_check(m, 0);
+	rte_mbuf_verify(m, 0);
 
 	ext_buf_addr = rte_malloc("External buffer", buf_len,
 			RTE_CACHE_LINE_SIZE);
@@ -2482,8 +2482,8 @@ test_pktmbuf_ext_pinned_buffer(struct rte_mempool *std_pool)
 		GOTO_FAIL("%s: test_pktmbuf_copy(pinned) failed\n",
 			  __func__);
 
-	if (test_failing_mbuf_sanity_check(pinned_pool) < 0)
-		GOTO_FAIL("%s: test_failing_mbuf_sanity_check(pinned)"
+	if (test_failing_mbuf_verify(pinned_pool) < 0)
+		GOTO_FAIL("%s: test_failing_mbuf_verify(pinned)"
 			  " failed\n", __func__);
 
 	if (test_mbuf_linearize_check(pinned_pool) < 0)
@@ -2857,8 +2857,8 @@ test_mbuf(void)
 		goto err;
 	}
 
-	if (test_failing_mbuf_sanity_check(pktmbuf_pool) < 0) {
-		printf("test_failing_mbuf_sanity_check() failed\n");
+	if (test_failing_mbuf_verify(pktmbuf_pool) < 0) {
+		printf("test_failing_mbuf_verify() failed\n");
 		goto err;
 	}
 
diff --git a/doc/guides/prog_guide/mbuf_lib.rst b/doc/guides/prog_guide/mbuf_lib.rst
index 4ad2a21f3f..6c96931f8c 100644
--- a/doc/guides/prog_guide/mbuf_lib.rst
+++ b/doc/guides/prog_guide/mbuf_lib.rst
@@ -266,8 +266,8 @@ can be found in several of the sample applications, for example, the IPv4 Multic
 Debug
 -----
 
-In debug mode, the functions of the mbuf library perform sanity checks before any operation (such as, buffer corruption,
-bad type, and so on).
+In debug mode, the functions of the mbuf library perform consistency checks
+before any operation (such as, buffer corruption, bad type, and so on).
 
 Use Cases
 ---------
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 36489f6e68..10bb08a634 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -142,3 +142,6 @@ Deprecation Notices
 * bus/vmbus: Starting DPDK 25.11, all the vmbus API defined in
   ``drivers/bus/vmbus/rte_bus_vmbus.h`` will become internal to DPDK.
   Those API functions are used internally by DPDK core and netvsc PMD.
+
+* mbuf: The function ``rte_mbuf_sanity_check`` is deprecated.
+  Use the new function ``rte_mbuf_verify`` instead.
diff --git a/lib/mbuf/rte_mbuf.c b/lib/mbuf/rte_mbuf.c
index 559d5ad8a7..fc5d4ba29d 100644
--- a/lib/mbuf/rte_mbuf.c
+++ b/lib/mbuf/rte_mbuf.c
@@ -367,9 +367,9 @@ rte_pktmbuf_pool_create_extbuf(const char *name, unsigned int n,
 	return mp;
 }
 
-/* do some sanity checks on a mbuf: panic if it fails */
+/* do some checks on a mbuf: panic if it fails */
 void
-rte_mbuf_sanity_check(const struct rte_mbuf *m, int is_header)
+rte_mbuf_verify(const struct rte_mbuf *m, int is_header)
 {
 	const char *reason;
 
@@ -377,6 +377,13 @@ rte_mbuf_sanity_check(const struct rte_mbuf *m, int is_header)
 		rte_panic("%s\n", reason);
 }
 
+/* For ABI compatibility, to be removed in next release */
+void
+rte_mbuf_sanity_check(const struct rte_mbuf *m, int is_header)
+{
+	rte_mbuf_verify(m, is_header);
+}
+
 int rte_mbuf_check(const struct rte_mbuf *m, int is_header,
 		   const char **reason)
 {
@@ -496,7 +503,7 @@ void rte_pktmbuf_free_bulk(struct rte_mbuf **mbufs, unsigned int count)
 		if (unlikely(m == NULL))
 			continue;
 
-		__rte_mbuf_sanity_check(m, 1);
+		__rte_mbuf_verify(m, 1);
 
 		do {
 			m_next = m->next;
@@ -546,7 +553,7 @@ rte_pktmbuf_clone(struct rte_mbuf *md, struct rte_mempool *mp)
 		return NULL;
 	}
 
-	__rte_mbuf_sanity_check(mc, 1);
+	__rte_mbuf_verify(mc, 1);
 	return mc;
 }
 
@@ -596,7 +603,7 @@ rte_pktmbuf_copy(const struct rte_mbuf *m, struct rte_mempool *mp,
 	struct rte_mbuf *mc, *m_last, **prev;
 
 	/* garbage in check */
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	/* check for request to copy at offset past end of mbuf */
 	if (unlikely(off >= m->pkt_len))
@@ -660,7 +667,7 @@ rte_pktmbuf_copy(const struct rte_mbuf *m, struct rte_mempool *mp,
 	}
 
 	/* garbage out check */
-	__rte_mbuf_sanity_check(mc, 1);
+	__rte_mbuf_verify(mc, 1);
 	return mc;
 }
 
@@ -671,7 +678,7 @@ rte_pktmbuf_dump(FILE *f, const struct rte_mbuf *m, unsigned dump_len)
 	unsigned int len;
 	unsigned int nb_segs;
 
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	fprintf(f, "dump mbuf at %p, iova=%#" PRIx64 ", buf_len=%u\n", m, rte_mbuf_iova_get(m),
 		m->buf_len);
@@ -689,7 +696,7 @@ rte_pktmbuf_dump(FILE *f, const struct rte_mbuf *m, unsigned dump_len)
 	nb_segs = m->nb_segs;
 
 	while (m && nb_segs != 0) {
-		__rte_mbuf_sanity_check(m, 0);
+		__rte_mbuf_verify(m, 0);
 
 		fprintf(f, "  segment at %p, data=%p, len=%u, off=%u, refcnt=%u\n",
 			m, rte_pktmbuf_mtod(m, void *),
diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
index 06ab7502a5..53a837e4d5 100644
--- a/lib/mbuf/rte_mbuf.h
+++ b/lib/mbuf/rte_mbuf.h
@@ -339,16 +339,20 @@ rte_pktmbuf_priv_flags(struct rte_mempool *mp)
 
 #ifdef RTE_LIBRTE_MBUF_DEBUG
 
-/**  check mbuf type in debug mode */
-#define __rte_mbuf_sanity_check(m, is_h) rte_mbuf_sanity_check(m, is_h)
+/**  do mbuf type in debug mode */
+#define __rte_mbuf_verify(m, is_h) rte_mbuf_verify(m, is_h)
 
 #else /*  RTE_LIBRTE_MBUF_DEBUG */
 
-/**  check mbuf type in debug mode */
-#define __rte_mbuf_sanity_check(m, is_h) do { } while (0)
+/**  ignore mbuf checks if not in debug mode */
+#define __rte_mbuf_verify(m, is_h) do { } while (0)
 
 #endif /*  RTE_LIBRTE_MBUF_DEBUG */
 
+/* deprecated version of the macro */
+#define __rte_mbuf_sanity_check(m, is_h) RTE_DEPRECATED(__rte_mbuf_sanity_check) \
+		__rte_mbuf_verify(m, is_h)
+
 #ifdef RTE_MBUF_REFCNT_ATOMIC
 
 /**
@@ -514,10 +518,9 @@ rte_mbuf_ext_refcnt_update(struct rte_mbuf_ext_shared_info *shinfo,
 
 
 /**
- * Sanity checks on an mbuf.
+ * Check that the mbuf is valid and panic if corrupted.
  *
- * Check the consistency of the given mbuf. The function will cause a
- * panic if corruption is detected.
+ * Acts assertion that mbuf is consistent. If not it calls rte_panic().
  *
  * @param m
  *   The mbuf to be checked.
@@ -526,13 +529,17 @@ rte_mbuf_ext_refcnt_update(struct rte_mbuf_ext_shared_info *shinfo,
  *   of a packet (in this case, some fields like nb_segs are not checked)
  */
 void
+rte_mbuf_verify(const struct rte_mbuf *m, int is_header);
+
+__rte_deprecated
+void
 rte_mbuf_sanity_check(const struct rte_mbuf *m, int is_header);
 
 /**
- * Sanity checks on a mbuf.
+ * Do consistency checks on a mbuf.
  *
- * Almost like rte_mbuf_sanity_check(), but this function gives the reason
- * if corruption is detected rather than panic.
+ * Check the consistency of the given mbuf and if not valid
+ * return the reason.
  *
  * @param m
  *   The mbuf to be checked.
@@ -551,7 +558,7 @@ int rte_mbuf_check(const struct rte_mbuf *m, int is_header,
 		   const char **reason);
 
 /**
- * Sanity checks on a reinitialized mbuf in debug mode.
+ * Do checks on a reinitialized mbuf in debug mode.
  *
  * Check the consistency of the given reinitialized mbuf.
  * The function will cause a panic if corruption is detected.
@@ -563,7 +570,7 @@ int rte_mbuf_check(const struct rte_mbuf *m, int is_header,
  *   The mbuf to be checked.
  */
 static __rte_always_inline void
-__rte_mbuf_raw_sanity_check(__rte_unused const struct rte_mbuf *m)
+__rte_mbuf_raw_verify(__rte_unused const struct rte_mbuf *m)
 {
 	RTE_ASSERT(rte_mbuf_refcnt_read(m) == 1);
 	RTE_ASSERT(m->next == NULL);
@@ -572,11 +579,11 @@ __rte_mbuf_raw_sanity_check(__rte_unused const struct rte_mbuf *m)
 	RTE_ASSERT(!RTE_MBUF_HAS_EXTBUF(m) ||
 			(RTE_MBUF_HAS_PINNED_EXTBUF(m) &&
 			rte_mbuf_ext_refcnt_read(m->shinfo) == 1));
-	__rte_mbuf_sanity_check(m, 0);
+	__rte_mbuf_verify(m, 0);
 }
 
 /** For backwards compatibility. */
-#define MBUF_RAW_ALLOC_CHECK(m) __rte_mbuf_raw_sanity_check(m)
+#define MBUF_RAW_ALLOC_CHECK(m) __rte_mbuf_raw_verify(m)
 
 /**
  * Allocate an uninitialized mbuf from mempool *mp*.
@@ -606,7 +613,7 @@ static inline struct rte_mbuf *rte_mbuf_raw_alloc(struct rte_mempool *mp)
 
 	if (rte_mempool_get(mp, &ret.ptr) < 0)
 		return NULL;
-	__rte_mbuf_raw_sanity_check(ret.m);
+	__rte_mbuf_raw_verify(ret.m);
 	return ret.m;
 }
 
@@ -644,7 +651,7 @@ rte_mbuf_raw_alloc_bulk(struct rte_mempool *mp, struct rte_mbuf **mbufs, unsigne
 	int rc = rte_mempool_get_bulk(mp, (void **)mbufs, count);
 	if (likely(rc == 0))
 		for (unsigned int idx = 0; idx < count; idx++)
-			__rte_mbuf_raw_sanity_check(mbufs[idx]);
+			__rte_mbuf_raw_verify(mbufs[idx]);
 	return rc;
 }
 
@@ -665,7 +672,7 @@ rte_mbuf_raw_alloc_bulk(struct rte_mempool *mp, struct rte_mbuf **mbufs, unsigne
 static __rte_always_inline void
 rte_mbuf_raw_free(struct rte_mbuf *m)
 {
-	__rte_mbuf_raw_sanity_check(m);
+	__rte_mbuf_raw_verify(m);
 	rte_mempool_put(m->pool, m);
 }
 
@@ -700,7 +707,7 @@ rte_mbuf_raw_free_bulk(struct rte_mempool *mp, struct rte_mbuf **mbufs, unsigned
 		const struct rte_mbuf *m = mbufs[idx];
 		RTE_ASSERT(m != NULL);
 		RTE_ASSERT(m->pool == mp);
-		__rte_mbuf_raw_sanity_check(m);
+		__rte_mbuf_raw_verify(m);
 	}
 
 	rte_mempool_put_bulk(mp, (void **)mbufs, count);
@@ -965,7 +972,7 @@ static inline void rte_pktmbuf_reset(struct rte_mbuf *m)
 	rte_pktmbuf_reset_headroom(m);
 
 	m->data_len = 0;
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 }
 
 /**
@@ -1021,22 +1028,22 @@ static inline int rte_pktmbuf_alloc_bulk(struct rte_mempool *pool,
 	switch (count % 4) {
 	case 0:
 		while (idx != count) {
-			__rte_mbuf_raw_sanity_check(mbufs[idx]);
+			__rte_mbuf_raw_verify(mbufs[idx]);
 			rte_pktmbuf_reset(mbufs[idx]);
 			idx++;
 			/* fall-through */
 	case 3:
-			__rte_mbuf_raw_sanity_check(mbufs[idx]);
+			__rte_mbuf_raw_verify(mbufs[idx]);
 			rte_pktmbuf_reset(mbufs[idx]);
 			idx++;
 			/* fall-through */
 	case 2:
-			__rte_mbuf_raw_sanity_check(mbufs[idx]);
+			__rte_mbuf_raw_verify(mbufs[idx]);
 			rte_pktmbuf_reset(mbufs[idx]);
 			idx++;
 			/* fall-through */
 	case 1:
-			__rte_mbuf_raw_sanity_check(mbufs[idx]);
+			__rte_mbuf_raw_verify(mbufs[idx]);
 			rte_pktmbuf_reset(mbufs[idx]);
 			idx++;
 			/* fall-through */
@@ -1267,8 +1274,8 @@ static inline void rte_pktmbuf_attach(struct rte_mbuf *mi, struct rte_mbuf *m)
 	mi->pkt_len = mi->data_len;
 	mi->nb_segs = 1;
 
-	__rte_mbuf_sanity_check(mi, 1);
-	__rte_mbuf_sanity_check(m, 0);
+	__rte_mbuf_verify(mi, 1);
+	__rte_mbuf_verify(m, 0);
 }
 
 /**
@@ -1423,7 +1430,7 @@ static inline int __rte_pktmbuf_pinned_extbuf_decref(struct rte_mbuf *m)
 static __rte_always_inline struct rte_mbuf *
 rte_pktmbuf_prefree_seg(struct rte_mbuf *m)
 {
-	__rte_mbuf_sanity_check(m, 0);
+	__rte_mbuf_verify(m, 0);
 
 	if (likely(rte_mbuf_refcnt_read(m) == 1)) {
 
@@ -1494,7 +1501,7 @@ static inline void rte_pktmbuf_free(struct rte_mbuf *m)
 	struct rte_mbuf *m_next;
 
 	if (m != NULL)
-		__rte_mbuf_sanity_check(m, 1);
+		__rte_mbuf_verify(m, 1);
 
 	while (m != NULL) {
 		m_next = m->next;
@@ -1575,7 +1582,7 @@ rte_pktmbuf_copy(const struct rte_mbuf *m, struct rte_mempool *mp,
  */
 static inline void rte_pktmbuf_refcnt_update(struct rte_mbuf *m, int16_t v)
 {
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	do {
 		rte_mbuf_refcnt_update(m, v);
@@ -1592,7 +1599,7 @@ static inline void rte_pktmbuf_refcnt_update(struct rte_mbuf *m, int16_t v)
  */
 static inline uint16_t rte_pktmbuf_headroom(const struct rte_mbuf *m)
 {
-	__rte_mbuf_sanity_check(m, 0);
+	__rte_mbuf_verify(m, 0);
 	return m->data_off;
 }
 
@@ -1606,7 +1613,7 @@ static inline uint16_t rte_pktmbuf_headroom(const struct rte_mbuf *m)
  */
 static inline uint16_t rte_pktmbuf_tailroom(const struct rte_mbuf *m)
 {
-	__rte_mbuf_sanity_check(m, 0);
+	__rte_mbuf_verify(m, 0);
 	return (uint16_t)(m->buf_len - rte_pktmbuf_headroom(m) -
 			  m->data_len);
 }
@@ -1621,7 +1628,7 @@ static inline uint16_t rte_pktmbuf_tailroom(const struct rte_mbuf *m)
  */
 static inline struct rte_mbuf *rte_pktmbuf_lastseg(struct rte_mbuf *m)
 {
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 	while (m->next != NULL)
 		m = m->next;
 	return m;
@@ -1665,7 +1672,7 @@ static inline struct rte_mbuf *rte_pktmbuf_lastseg(struct rte_mbuf *m)
 static inline char *rte_pktmbuf_prepend(struct rte_mbuf *m,
 					uint16_t len)
 {
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	if (unlikely(len > rte_pktmbuf_headroom(m)))
 		return NULL;
@@ -1700,7 +1707,7 @@ static inline char *rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len)
 	void *tail;
 	struct rte_mbuf *m_last;
 
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	m_last = rte_pktmbuf_lastseg(m);
 	if (unlikely(len > rte_pktmbuf_tailroom(m_last)))
@@ -1728,7 +1735,7 @@ static inline char *rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len)
  */
 static inline char *rte_pktmbuf_adj(struct rte_mbuf *m, uint16_t len)
 {
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	if (unlikely(len > m->data_len))
 		return NULL;
@@ -1760,7 +1767,7 @@ static inline int rte_pktmbuf_trim(struct rte_mbuf *m, uint16_t len)
 {
 	struct rte_mbuf *m_last;
 
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 
 	m_last = rte_pktmbuf_lastseg(m);
 	if (unlikely(len > m_last->data_len))
@@ -1782,7 +1789,7 @@ static inline int rte_pktmbuf_trim(struct rte_mbuf *m, uint16_t len)
  */
 static inline int rte_pktmbuf_is_contiguous(const struct rte_mbuf *m)
 {
-	__rte_mbuf_sanity_check(m, 1);
+	__rte_mbuf_verify(m, 1);
 	return m->nb_segs == 1;
 }
 
diff --git a/lib/mbuf/version.map b/lib/mbuf/version.map
index 76f1832924..2950f24caa 100644
--- a/lib/mbuf/version.map
+++ b/lib/mbuf/version.map
@@ -31,6 +31,7 @@ DPDK_25 {
 	rte_mbuf_set_platform_mempool_ops;
 	rte_mbuf_set_user_mempool_ops;
 	rte_mbuf_user_mempool_ops;
+	rte_mbuf_verify;
 	rte_pktmbuf_clone;
 	rte_pktmbuf_copy;
 	rte_pktmbuf_dump;
-- 
2.47.2


^ permalink raw reply	[relevance 3%]

* [PATCH v7 0/8] Symbol versioning and export rework
  2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
                   ` (4 preceding siblings ...)
  2025-03-28 10:52  3% ` [PATCH v6 " David Marchand
@ 2025-04-03 16:58  3% ` David Marchand
  2025-04-03 16:58 18%   ` [PATCH v7 4/8] build: generate symbol maps David Marchand
                     ` (3 more replies)
  5 siblings, 4 replies; 153+ results
From: David Marchand @ 2025-04-03 16:58 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue

So far, each DPDK library (or driver) exposing symbols in an ABI had to
maintain a version.map and use some macros for symbol versioning,
specially crafted with the GNU linker in mind.

This series proposes to rework the whole principle, and instead rely on
marking the symbol exports in the source code itself, then let it to the
build framework to produce a version script adapted to the linker in use
(think GNU linker vs MSVC linker).

This greatly simplifies versioning symbols: a developer does not need to
know anything about version.map, or that a versioned symbol must be
renamed with _v26, annotated with __vsym, exported in a header etc...

Checking symbol maps becomes unnecessary since generated by the build
framework.

Updating to a new ABI is just a matter of bumping the value in
ABI_VERSION.



-- 
David Marchand

Changes since v6:
- used argparse in python scripts,
- renamed eal_symbol_exports.h as eal_export.h,
- renamed some base symbols export files,

Changes since v5:
- fixed Windows symbol exports for net/mlx5,

Changes since RFC v4:
- rebased on main, now that Bruce series is merged,
- the export macros header has been moved to lib/eal/common/
  and its inclusion is now mandatory (rather than an implicit -include),
- reordered patches: symbol versioning is touched last and merged
  in the export header (replacing the legacy rte_function_versioning.h),

Changes since RFC v3:
- fixed/simplified documentation,
- rebased on top of Bruce series for common handling of AVX sources,

Changes since RFC v2:
- updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
  signature is enclosed in the macro,
- dropped invalid exports for some dead symbols or inline helpers,
- updated documentation and tooling,
- converted the whole tree (via a local script of mine),

David Marchand (8):
  lib: remove incorrect exported symbols
  drivers: remove incorrect exported symbols
  buildtools: display symbols version from map
  build: generate symbol maps
  build: mark exported symbols
  build: use dynamically generated version maps
  build: remove static version maps
  eal: rework function versioning macros

 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   9 +-
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/gen-version-map.py                 | 133 ++++
 buildtools/map-list-symbol.sh                 |  15 +-
 buildtools/map_to_win.py                      |  41 --
 buildtools/meson.build                        |   2 +-
 devtools/check-spdx-tag.sh                    |   2 +-
 devtools/check-symbol-change.py               | 108 ++++
 devtools/check-symbol-change.sh               | 186 ------
 devtools/check-symbol-maps.sh                 | 115 ----
 devtools/checkpatches.sh                      |   6 +-
 devtools/update-abi.sh                        |  46 --
 devtools/update_version_map_abi.py            | 210 -------
 doc/api/doxy-api-index.md                     |   1 -
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/abi_versioning.rst    | 415 +++----------
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 +++++----
 doc/guides/contributing/patches.rst           |   6 +-
 doc/guides/rel_notes/release_25_07.rst        |   2 +
 drivers/baseband/acc/rte_acc100_pmd.c         |   2 +
 drivers/baseband/acc/version.map              |  10 -
 .../fpga_5gnr_fec/rte_fpga_5gnr_fec.c         |   2 +
 drivers/baseband/fpga_5gnr_fec/version.map    |  11 -
 drivers/baseband/fpga_lte_fec/fpga_lte_fec.c  |   2 +
 drivers/baseband/fpga_lte_fec/version.map     |  10 -
 drivers/bus/auxiliary/auxiliary_common.c      |   3 +
 drivers/bus/auxiliary/version.map             |   8 -
 drivers/bus/cdx/cdx.c                         |   5 +
 drivers/bus/cdx/cdx_vfio.c                    |   5 +
 drivers/bus/cdx/version.map                   |  14 -
 drivers/bus/dpaa/dpaa_bus.c                   |  10 +
 drivers/bus/dpaa/dpaa_bus_base_symbols.c      | 100 +++
 drivers/bus/dpaa/meson.build                  |   1 +
 drivers/bus/dpaa/version.map                  | 109 ----
 drivers/bus/fslmc/fslmc_bus.c                 |   5 +
 drivers/bus/fslmc/fslmc_vfio.c                |  13 +
 drivers/bus/fslmc/mc/dpbp.c                   |   8 +
 drivers/bus/fslmc/mc/dpci.c                   |   5 +
 drivers/bus/fslmc/mc/dpcon.c                  |   8 +
 drivers/bus/fslmc/mc/dpdmai.c                 |  10 +
 drivers/bus/fslmc/mc/dpio.c                   |  15 +
 drivers/bus/fslmc/mc/dpmng.c                  |   4 +
 drivers/bus/fslmc/mc/mc_sys.c                 |   2 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpbp.c      |   4 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpci.c      |   3 +
 drivers/bus/fslmc/portal/dpaa2_hw_dpio.c      |  12 +
 drivers/bus/fslmc/qbman/qbman_debug.c         |   4 +
 drivers/bus/fslmc/qbman/qbman_portal.c        |  43 ++
 drivers/bus/fslmc/version.map                 | 129 ----
 drivers/bus/ifpga/ifpga_bus.c                 |   4 +
 drivers/bus/ifpga/version.map                 |   9 -
 drivers/bus/pci/bsd/pci.c                     |  11 +
 drivers/bus/pci/linux/pci.c                   |  11 +
 drivers/bus/pci/pci_common.c                  |  11 +
 drivers/bus/pci/version.map                   |  43 --
 drivers/bus/pci/windows/pci.c                 |  11 +
 drivers/bus/platform/platform.c               |   3 +
 drivers/bus/platform/version.map              |  10 -
 drivers/bus/uacce/uacce.c                     |  10 +
 drivers/bus/uacce/version.map                 |  15 -
 drivers/bus/vdev/vdev.c                       |   7 +
 drivers/bus/vdev/version.map                  |  17 -
 drivers/bus/vmbus/linux/vmbus_bus.c           |   7 +
 drivers/bus/vmbus/version.map                 |  33 -
 drivers/bus/vmbus/vmbus_channel.c             |  14 +
 drivers/bus/vmbus/vmbus_common.c              |   4 +
 drivers/common/cnxk/cnxk_security.c           |  13 +
 drivers/common/cnxk/cnxk_utils.c              |   2 +
 drivers/common/cnxk/meson.build               |   1 +
 drivers/common/cnxk/roc_platform.c            |  19 +
 .../common/cnxk/roc_platform_base_symbols.c   | 546 +++++++++++++++++
 drivers/common/cnxk/roc_se.h                  |   1 -
 drivers/common/cnxk/version.map               | 578 ------------------
 drivers/common/cpt/cpt_fpm_tables.c           |   3 +
 drivers/common/cpt/cpt_pmd_ops_helper.c       |   4 +
 drivers/common/cpt/version.map                |  11 -
 drivers/common/dpaax/caamflib.c               |   3 +
 drivers/common/dpaax/dpaa_of.c                |  13 +
 drivers/common/dpaax/dpaax_iova_table.c       |   7 +
 drivers/common/dpaax/version.map              |  25 -
 drivers/common/ionic/ionic_common_uio.c       |   5 +
 drivers/common/ionic/version.map              |  10 -
 .../common/mlx5/linux/mlx5_common_auxiliary.c |   2 +
 drivers/common/mlx5/linux/mlx5_common_os.c    |  11 +
 drivers/common/mlx5/linux/mlx5_common_verbs.c |   4 +
 drivers/common/mlx5/linux/mlx5_glue.c         |   3 +
 drivers/common/mlx5/linux/mlx5_nl.c           |  22 +
 drivers/common/mlx5/mlx5_common.c             |  10 +
 drivers/common/mlx5/mlx5_common_devx.c        |  10 +
 drivers/common/mlx5/mlx5_common_mp.c          |   9 +
 drivers/common/mlx5/mlx5_common_mr.c          |  12 +
 drivers/common/mlx5/mlx5_common_pci.c         |   3 +
 drivers/common/mlx5/mlx5_common_utils.c       |  12 +
 drivers/common/mlx5/mlx5_devx_cmds.c          |  52 ++
 drivers/common/mlx5/mlx5_malloc.c             |   5 +
 drivers/common/mlx5/version.map               | 175 ------
 drivers/common/mlx5/windows/mlx5_common_os.c  |   7 +
 drivers/common/mlx5/windows/mlx5_glue.c       |   4 +-
 drivers/common/mvep/mvep_common.c             |   3 +
 drivers/common/mvep/version.map               |   8 -
 drivers/common/nfp/nfp_common.c               |   9 +
 drivers/common/nfp/nfp_common_pci.c           |   2 +
 drivers/common/nfp/nfp_dev.c                  |   2 +
 drivers/common/nfp/version.map                |  16 -
 drivers/common/nitrox/nitrox_device.c         |   2 +
 drivers/common/nitrox/nitrox_logs.c           |   2 +
 drivers/common/nitrox/nitrox_qp.c             |   3 +
 drivers/common/nitrox/version.map             |  10 -
 drivers/common/octeontx/octeontx_mbox.c       |   7 +
 drivers/common/octeontx/version.map           |  12 -
 drivers/common/sfc_efx/meson.build            |   1 +
 drivers/common/sfc_efx/sfc_base_symbols.c     | 276 +++++++++
 drivers/common/sfc_efx/sfc_efx.c              |   3 +
 drivers/common/sfc_efx/sfc_efx_mcdi.c         |   3 +
 drivers/common/sfc_efx/version.map            | 302 ---------
 drivers/crypto/cnxk/cn10k_cryptodev_ops.c     |   8 +
 drivers/crypto/cnxk/cn9k_cryptodev_ops.c      |   3 +
 drivers/crypto/cnxk/cnxk_cryptodev_ops.c      |   8 +
 drivers/crypto/cnxk/version.map               |  30 -
 drivers/crypto/dpaa2_sec/dpaa2_sec_dpseci.c   |   3 +
 drivers/crypto/dpaa2_sec/version.map          |   8 -
 drivers/crypto/dpaa_sec/dpaa_sec.c            |   3 +
 drivers/crypto/dpaa_sec/version.map           |   8 -
 drivers/crypto/octeontx/otx_cryptodev_ops.c   |   3 +
 drivers/crypto/octeontx/version.map           |  12 -
 .../scheduler/rte_cryptodev_scheduler.c       |  11 +
 drivers/crypto/scheduler/version.map          |  16 -
 drivers/dma/cnxk/cnxk_dmadev_fp.c             |   5 +
 drivers/dma/cnxk/version.map                  |  10 -
 drivers/event/cnxk/cnxk_worker.c              |   3 +
 drivers/event/cnxk/version.map                |  11 -
 drivers/event/dlb2/rte_pmd_dlb2.c             |   2 +
 drivers/event/dlb2/version.map                |  10 -
 drivers/mempool/cnxk/cn10k_hwpool_ops.c       |   4 +
 drivers/mempool/cnxk/version.map              |  12 -
 drivers/mempool/dpaa/dpaa_mempool.c           |   3 +
 drivers/mempool/dpaa/version.map              |   8 -
 drivers/mempool/dpaa2/dpaa2_hw_mempool.c      |   6 +
 drivers/mempool/dpaa2/version.map             |  16 -
 drivers/meson.build                           |  76 +--
 drivers/net/atlantic/rte_pmd_atlantic.c       |   7 +
 drivers/net/atlantic/version.map              |  15 -
 drivers/net/bnxt/rte_pmd_bnxt.c               |  17 +
 drivers/net/bnxt/version.map                  |  22 -
 drivers/net/bonding/rte_eth_bond_8023ad.c     |  13 +
 drivers/net/bonding/rte_eth_bond_api.c        |  16 +
 drivers/net/bonding/version.map               |  33 -
 drivers/net/cnxk/cnxk_ethdev.c                |   4 +
 drivers/net/cnxk/cnxk_ethdev_sec.c            |  10 +
 drivers/net/cnxk/version.map                  |  27 -
 drivers/net/dpaa/dpaa_ethdev.c                |   4 +
 drivers/net/dpaa/version.map                  |  14 -
 drivers/net/dpaa2/base/dpaa2_hw_dpni.c        |   2 +
 drivers/net/dpaa2/base/dpaa2_tlu_hash.c       |   3 +
 drivers/net/dpaa2/dpaa2_ethdev.c              |   8 +
 drivers/net/dpaa2/dpaa2_mux.c                 |   4 +
 drivers/net/dpaa2/dpaa2_rxtx.c                |   2 +
 drivers/net/dpaa2/version.map                 |  35 --
 drivers/net/intel/i40e/rte_pmd_i40e.c         |  40 ++
 drivers/net/intel/i40e/version.map            |  55 --
 drivers/net/intel/iavf/iavf_base_symbols.c    |  14 +
 drivers/net/intel/iavf/iavf_rxtx.c            |   9 +
 drivers/net/intel/iavf/meson.build            |   1 +
 drivers/net/intel/iavf/version.map            |  33 -
 drivers/net/intel/ice/ice_diagnose.c          |   4 +
 drivers/net/intel/ice/version.map             |  16 -
 drivers/net/intel/idpf/idpf_common_device.c   |  11 +
 drivers/net/intel/idpf/idpf_common_rxtx.c     |  25 +
 .../net/intel/idpf/idpf_common_rxtx_avx2.c    |   3 +
 .../net/intel/idpf/idpf_common_rxtx_avx512.c  |   6 +
 drivers/net/intel/idpf/idpf_common_virtchnl.c |  31 +
 drivers/net/intel/idpf/version.map            |  80 ---
 drivers/net/intel/ipn3ke/ipn3ke_ethdev.c      |   2 +
 drivers/net/intel/ipn3ke/version.map          |   9 -
 drivers/net/intel/ixgbe/rte_pmd_ixgbe.c       |  38 ++
 drivers/net/intel/ixgbe/version.map           |  49 --
 drivers/net/mlx5/mlx5.c                       |   2 +
 drivers/net/mlx5/mlx5_flow.c                  |   5 +
 drivers/net/mlx5/mlx5_rx.c                    |   3 +
 drivers/net/mlx5/mlx5_rxq.c                   |   3 +
 drivers/net/mlx5/mlx5_tx.c                    |   2 +
 drivers/net/mlx5/mlx5_txq.c                   |   4 +
 drivers/net/mlx5/version.map                  |  28 -
 drivers/net/octeontx/octeontx_ethdev.c        |   2 +
 drivers/net/octeontx/version.map              |   7 -
 drivers/net/ring/rte_eth_ring.c               |   3 +
 drivers/net/ring/version.map                  |   8 -
 drivers/net/softnic/rte_eth_softnic.c         |   2 +
 drivers/net/softnic/rte_eth_softnic_thread.c  |   2 +
 drivers/net/softnic/version.map               |   8 -
 drivers/net/vhost/rte_eth_vhost.c             |   3 +
 drivers/net/vhost/version.map                 |   8 -
 drivers/power/kvm_vm/guest_channel.c          |   3 +
 drivers/power/kvm_vm/version.map              |   8 -
 drivers/raw/cnxk_rvu_lf/cnxk_rvu_lf.c         |  11 +
 drivers/raw/cnxk_rvu_lf/version.map           |  16 -
 drivers/raw/ifpga/rte_pmd_ifpga.c             |  12 +
 drivers/raw/ifpga/version.map                 |  17 -
 drivers/version.map                           |   3 -
 lib/acl/acl_bld.c                             |   2 +
 lib/acl/acl_run_scalar.c                      |   3 +
 lib/acl/rte_acl.c                             |  12 +
 lib/acl/version.map                           |  19 -
 lib/argparse/rte_argparse.c                   |   3 +
 lib/argparse/version.map                      |   9 -
 lib/bbdev/bbdev_trace_points.c                |   3 +
 lib/bbdev/rte_bbdev.c                         |  32 +
 lib/bbdev/version.map                         |  47 --
 lib/bitratestats/rte_bitrate.c                |   5 +
 lib/bitratestats/version.map                  |  10 -
 lib/bpf/bpf.c                                 |   3 +
 lib/bpf/bpf_convert.c                         |   2 +
 lib/bpf/bpf_dump.c                            |   2 +
 lib/bpf/bpf_exec.c                            |   3 +
 lib/bpf/bpf_load.c                            |   2 +
 lib/bpf/bpf_load_elf.c                        |   2 +
 lib/bpf/bpf_pkt.c                             |   5 +
 lib/bpf/bpf_stub.c                            |   3 +
 lib/bpf/version.map                           |  18 -
 lib/cfgfile/rte_cfgfile.c                     |  18 +
 lib/cfgfile/version.map                       |  23 -
 lib/cmdline/cmdline.c                         |  10 +
 lib/cmdline/cmdline_cirbuf.c                  |  21 +
 lib/cmdline/cmdline_parse.c                   |   5 +
 lib/cmdline/cmdline_parse_bool.c              |   2 +
 lib/cmdline/cmdline_parse_etheraddr.c         |   4 +
 lib/cmdline/cmdline_parse_ipaddr.c            |   4 +
 lib/cmdline/cmdline_parse_num.c               |   4 +
 lib/cmdline/cmdline_parse_portlist.c          |   4 +
 lib/cmdline/cmdline_parse_string.c            |   6 +
 lib/cmdline/cmdline_rdline.c                  |  17 +
 lib/cmdline/cmdline_socket.c                  |   5 +
 lib/cmdline/cmdline_vt100.c                   |   4 +
 lib/cmdline/version.map                       |  82 ---
 lib/compressdev/rte_comp.c                    |   7 +
 lib/compressdev/rte_compressdev.c             |  26 +
 lib/compressdev/rte_compressdev_pmd.c         |   4 +
 lib/compressdev/version.map                   |  40 --
 lib/cryptodev/cryptodev_pmd.c                 |   8 +
 lib/cryptodev/cryptodev_trace_points.c        |   4 +
 lib/cryptodev/rte_cryptodev.c                 |  84 +++
 lib/cryptodev/version.map                     | 114 ----
 lib/dispatcher/rte_dispatcher.c               |  14 +
 lib/dispatcher/version.map                    |  20 -
 lib/distributor/rte_distributor.c             |  10 +
 lib/distributor/version.map                   |  15 -
 lib/dmadev/rte_dmadev.c                       |  20 +
 lib/dmadev/rte_dmadev_trace_points.c          |   8 +
 lib/dmadev/version.map                        |  47 --
 lib/eal/arm/rte_cpuflags.c                    |   4 +
 lib/eal/arm/rte_hypervisor.c                  |   2 +
 lib/eal/arm/rte_power_intrinsics.c            |   5 +
 lib/eal/common/eal_common_bus.c               |  11 +
 lib/eal/common/eal_common_class.c             |   5 +
 lib/eal/common/eal_common_config.c            |   8 +
 lib/eal/common/eal_common_cpuflags.c          |   2 +
 lib/eal/common/eal_common_debug.c             |   3 +
 lib/eal/common/eal_common_dev.c               |  20 +
 lib/eal/common/eal_common_devargs.c           |  10 +
 lib/eal/common/eal_common_errno.c             |   3 +
 lib/eal/common/eal_common_fbarray.c           |  27 +
 lib/eal/common/eal_common_hexdump.c           |   3 +
 lib/eal/common/eal_common_hypervisor.c        |   2 +
 lib/eal/common/eal_common_interrupts.c        |  28 +
 lib/eal/common/eal_common_launch.c            |   6 +
 lib/eal/common/eal_common_lcore.c             |  18 +
 lib/eal/common/eal_common_lcore_var.c         |   2 +
 lib/eal/common/eal_common_mcfg.c              |  21 +
 lib/eal/common/eal_common_memory.c            |  30 +
 lib/eal/common/eal_common_memzone.c           |  10 +
 lib/eal/common/eal_common_options.c           |   5 +
 lib/eal/common/eal_common_proc.c              |   9 +
 lib/eal/common/eal_common_string_fns.c        |   4 +
 lib/eal/common/eal_common_tailqs.c            |   4 +
 lib/eal/common/eal_common_thread.c            |  15 +
 lib/eal/common/eal_common_timer.c             |   5 +
 lib/eal/common/eal_common_trace.c             |  16 +
 lib/eal/common/eal_common_trace_ctf.c         |   2 +
 lib/eal/common/eal_common_trace_points.c      |  19 +
 lib/eal/common/eal_common_trace_utils.c       |   2 +
 lib/eal/common/eal_common_uuid.c              |   5 +
 lib/eal/common/eal_export.h                   |  79 +++
 lib/eal/common/rte_bitset.c                   |   2 +
 lib/eal/common/rte_keepalive.c                |   7 +
 lib/eal/common/rte_malloc.c                   |  23 +
 lib/eal/common/rte_random.c                   |   5 +
 lib/eal/common/rte_reciprocal.c               |   3 +
 lib/eal/common/rte_service.c                  |  32 +
 lib/eal/common/rte_version.c                  |   8 +
 lib/eal/freebsd/eal.c                         |  23 +
 lib/eal/freebsd/eal_alarm.c                   |   3 +
 lib/eal/freebsd/eal_dev.c                     |   5 +
 lib/eal/freebsd/eal_interrupts.c              |  20 +
 lib/eal/freebsd/eal_memory.c                  |   4 +
 lib/eal/freebsd/eal_thread.c                  |   3 +
 lib/eal/freebsd/eal_timer.c                   |   2 +
 lib/eal/include/rte_function_versioning.h     |  99 ---
 lib/eal/linux/eal.c                           |   8 +
 lib/eal/linux/eal_alarm.c                     |   3 +
 lib/eal/linux/eal_dev.c                       |   5 +
 lib/eal/linux/eal_interrupts.c                |  20 +
 lib/eal/linux/eal_memory.c                    |   4 +
 lib/eal/linux/eal_thread.c                    |   3 +
 lib/eal/linux/eal_timer.c                     |   5 +
 lib/eal/linux/eal_vfio.c                      |  17 +
 lib/eal/loongarch/rte_cpuflags.c              |   4 +
 lib/eal/loongarch/rte_hypervisor.c            |   2 +
 lib/eal/loongarch/rte_power_intrinsics.c      |   5 +
 lib/eal/ppc/rte_cpuflags.c                    |   4 +
 lib/eal/ppc/rte_hypervisor.c                  |   2 +
 lib/eal/ppc/rte_power_intrinsics.c            |   5 +
 lib/eal/riscv/rte_cpuflags.c                  |   4 +
 lib/eal/riscv/rte_hypervisor.c                |   2 +
 lib/eal/riscv/rte_power_intrinsics.c          |   5 +
 lib/eal/unix/eal_debug.c                      |   3 +
 lib/eal/unix/eal_filesystem.c                 |   2 +
 lib/eal/unix/eal_firmware.c                   |   2 +
 lib/eal/unix/eal_unix_memory.c                |   5 +
 lib/eal/unix/eal_unix_timer.c                 |   2 +
 lib/eal/unix/rte_thread.c                     |  14 +
 lib/eal/version.map                           | 451 --------------
 lib/eal/windows/eal.c                         |  12 +
 lib/eal/windows/eal_alarm.c                   |   3 +
 lib/eal/windows/eal_debug.c                   |   2 +
 lib/eal/windows/eal_dev.c                     |   5 +
 lib/eal/windows/eal_interrupts.c              |  20 +
 lib/eal/windows/eal_memory.c                  |   8 +
 lib/eal/windows/eal_mp.c                      |   7 +
 lib/eal/windows/eal_thread.c                  |   2 +
 lib/eal/windows/eal_timer.c                   |   2 +
 lib/eal/windows/rte_thread.c                  |  15 +
 lib/eal/x86/rte_cpuflags.c                    |   4 +
 lib/eal/x86/rte_hypervisor.c                  |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |   5 +
 lib/eal/x86/rte_spinlock.c                    |   2 +
 lib/efd/rte_efd.c                             |   8 +
 lib/efd/version.map                           |  13 -
 lib/ethdev/ethdev_driver.c                    |  25 +
 lib/ethdev/ethdev_linux_ethtool.c             |   4 +
 lib/ethdev/ethdev_private.c                   |   3 +
 lib/ethdev/ethdev_trace_points.c              |   7 +
 lib/ethdev/rte_ethdev.c                       | 169 +++++
 lib/ethdev/rte_ethdev_cman.c                  |   5 +
 lib/ethdev/rte_flow.c                         |  65 ++
 lib/ethdev/rte_mtr.c                          |  22 +
 lib/ethdev/rte_tm.c                           |  32 +
 lib/ethdev/version.map                        | 378 ------------
 lib/eventdev/eventdev_private.c               |   3 +
 lib/eventdev/eventdev_trace_points.c          |  12 +
 lib/eventdev/rte_event_crypto_adapter.c       |  16 +
 lib/eventdev/rte_event_dma_adapter.c          |  16 +
 lib/eventdev/rte_event_eth_rx_adapter.c       |  24 +
 lib/eventdev/rte_event_eth_tx_adapter.c       |  18 +
 lib/eventdev/rte_event_ring.c                 |   5 +
 lib/eventdev/rte_event_timer_adapter.c        |  12 +
 lib/eventdev/rte_eventdev.c                   |  47 ++
 lib/eventdev/version.map                      | 179 ------
 lib/fib/rte_fib.c                             |  11 +
 lib/fib/rte_fib6.c                            |  10 +
 lib/fib/version.map                           |  31 -
 lib/gpudev/gpudev.c                           |  33 +
 lib/gpudev/version.map                        |  44 --
 lib/graph/graph.c                             |  17 +
 lib/graph/graph_debug.c                       |   3 +
 lib/graph/graph_stats.c                       |   5 +
 lib/graph/node.c                              |  12 +
 lib/graph/rte_graph_model_mcore_dispatch.c    |   4 +
 lib/graph/rte_graph_worker.c                  |   4 +
 lib/graph/version.map                         |  61 --
 lib/gro/rte_gro.c                             |   7 +
 lib/gro/version.map                           |  12 -
 lib/gso/rte_gso.c                             |   2 +
 lib/gso/version.map                           |   7 -
 lib/hash/rte_cuckoo_hash.c                    |  28 +
 lib/hash/rte_fbk_hash.c                       |   4 +
 lib/hash/rte_hash_crc.c                       |   3 +
 lib/hash/rte_thash.c                          |  13 +
 lib/hash/rte_thash_gf2_poly_math.c            |   2 +
 lib/hash/rte_thash_gfni.c                     |   3 +
 lib/hash/version.map                          |  66 --
 lib/ip_frag/rte_ip_frag_common.c              |   6 +
 lib/ip_frag/rte_ipv4_fragmentation.c          |   3 +
 lib/ip_frag/rte_ipv4_reassembly.c             |   2 +
 lib/ip_frag/rte_ipv6_fragmentation.c          |   2 +
 lib/ip_frag/rte_ipv6_reassembly.c             |   2 +
 lib/ip_frag/version.map                       |  16 -
 lib/ipsec/ipsec_sad.c                         |   7 +
 lib/ipsec/ipsec_telemetry.c                   |   3 +
 lib/ipsec/sa.c                                |   5 +
 lib/ipsec/ses.c                               |   2 +
 lib/ipsec/version.map                         |  23 -
 lib/jobstats/rte_jobstats.c                   |  15 +
 lib/jobstats/version.map                      |  20 -
 lib/kvargs/rte_kvargs.c                       |   9 +
 lib/kvargs/version.map                        |  14 -
 lib/latencystats/rte_latencystats.c           |   6 +
 lib/latencystats/version.map                  |  11 -
 lib/log/log.c                                 |  23 +
 lib/log/log_color.c                           |   2 +
 lib/log/log_internal.h                        |   3 -
 lib/log/log_syslog.c                          |   2 +
 lib/log/log_timestamp.c                       |   2 +
 lib/log/version.map                           |  37 --
 lib/lpm/rte_lpm.c                             |   9 +
 lib/lpm/rte_lpm6.c                            |  11 +
 lib/lpm/version.map                           |  24 -
 lib/mbuf/rte_mbuf.c                           |  18 +
 lib/mbuf/rte_mbuf_dyn.c                       |  10 +
 lib/mbuf/rte_mbuf_pool_ops.c                  |   6 +
 lib/mbuf/rte_mbuf_ptype.c                     |   9 +
 lib/mbuf/version.map                          |  45 --
 lib/member/rte_member.c                       |  14 +
 lib/member/version.map                        |  19 -
 lib/mempool/mempool_trace_points.c            |  11 +
 lib/mempool/rte_mempool.c                     |  28 +
 lib/mempool/rte_mempool_ops.c                 |   5 +
 lib/mempool/rte_mempool_ops_default.c         |   5 +
 lib/mempool/version.map                       |  65 --
 lib/meson.build                               |  69 +--
 lib/meter/rte_meter.c                         |   7 +
 lib/meter/version.map                         |  12 -
 lib/metrics/rte_metrics.c                     |   9 +
 lib/metrics/rte_metrics_telemetry.c           |  12 +
 lib/metrics/version.map                       |  26 -
 lib/mldev/mldev_utils.c                       |   3 +
 lib/mldev/mldev_utils_neon.c                  |  20 +
 lib/mldev/mldev_utils_neon_bfloat16.c         |   4 +
 lib/mldev/mldev_utils_scalar.c                |  20 +
 lib/mldev/mldev_utils_scalar_bfloat16.c       |   4 +
 lib/mldev/rte_mldev.c                         |  38 ++
 lib/mldev/rte_mldev_pmd.c                     |   3 +
 lib/mldev/version.map                         |  74 ---
 lib/net/net_crc.h                             |  15 -
 lib/net/rte_arp.c                             |   2 +
 lib/net/rte_ether.c                           |   4 +
 lib/net/rte_net.c                             |   3 +
 lib/net/rte_net_crc.c                         |  31 +-
 lib/net/version.map                           |  23 -
 lib/node/ethdev_ctrl.c                        |   3 +
 lib/node/ip4_lookup.c                         |   2 +
 lib/node/ip4_reassembly.c                     |   2 +
 lib/node/ip4_rewrite.c                        |   2 +
 lib/node/ip6_lookup.c                         |   2 +
 lib/node/ip6_rewrite.c                        |   2 +
 lib/node/udp4_input.c                         |   3 +
 lib/node/version.map                          |  25 -
 lib/pcapng/rte_pcapng.c                       |   8 +
 lib/pcapng/version.map                        |  13 -
 lib/pci/rte_pci.c                             |   4 +
 lib/pci/version.map                           |   9 -
 lib/pdcp/rte_pdcp.c                           |   6 +
 lib/pdcp/version.map                          |  16 -
 lib/pdump/rte_pdump.c                         |  10 +
 lib/pdump/version.map                         |  15 -
 lib/pipeline/rte_pipeline.c                   |  24 +
 lib/pipeline/rte_port_in_action.c             |   9 +
 lib/pipeline/rte_swx_ctl.c                    |  18 +
 lib/pipeline/rte_swx_ipsec.c                  |   8 +
 lib/pipeline/rte_swx_pipeline.c               |  74 +++
 lib/pipeline/rte_table_action.c               |  17 +
 lib/pipeline/version.map                      | 172 ------
 lib/port/rte_port_ethdev.c                    |   4 +
 lib/port/rte_port_eventdev.c                  |   4 +
 lib/port/rte_port_fd.c                        |   4 +
 lib/port/rte_port_frag.c                      |   3 +
 lib/port/rte_port_ras.c                       |   3 +
 lib/port/rte_port_ring.c                      |   7 +
 lib/port/rte_port_sched.c                     |   3 +
 lib/port/rte_port_source_sink.c               |   3 +
 lib/port/rte_port_sym_crypto.c                |   4 +
 lib/port/rte_swx_port_ethdev.c                |   3 +
 lib/port/rte_swx_port_fd.c                    |   3 +
 lib/port/rte_swx_port_ring.c                  |   3 +
 lib/port/rte_swx_port_source_sink.c           |   4 +
 lib/port/version.map                          |  50 --
 lib/power/power_common.c                      |   9 +
 lib/power/rte_power_cpufreq.c                 |  19 +
 lib/power/rte_power_pmd_mgmt.c                |  11 +
 lib/power/rte_power_qos.c                     |   3 +
 lib/power/rte_power_uncore.c                  |  15 +
 lib/power/version.map                         |  71 ---
 lib/rawdev/rte_rawdev.c                       |  31 +
 lib/rawdev/version.map                        |  36 --
 lib/rcu/rte_rcu_qsbr.c                        |  12 +
 lib/rcu/version.map                           |  17 -
 lib/regexdev/rte_regexdev.c                   |  27 +
 lib/regexdev/version.map                      |  40 --
 lib/reorder/rte_reorder.c                     |  12 +
 lib/reorder/version.map                       |  27 -
 lib/rib/rte_rib.c                             |  15 +
 lib/rib/rte_rib6.c                            |  15 +
 lib/rib/version.map                           |  34 --
 lib/ring/rte_ring.c                           |  12 +
 lib/ring/rte_soring.c                         |   4 +
 lib/ring/soring.c                             |  18 +
 lib/ring/version.map                          |  42 --
 lib/sched/rte_approx.c                        |   2 +
 lib/sched/rte_pie.c                           |   3 +
 lib/sched/rte_red.c                           |   7 +
 lib/sched/rte_sched.c                         |  16 +
 lib/sched/version.map                         |  30 -
 lib/security/rte_security.c                   |  21 +
 lib/security/version.map                      |  37 --
 lib/stack/rte_stack.c                         |   4 +
 lib/stack/version.map                         |   9 -
 lib/table/rte_swx_table_em.c                  |   3 +
 lib/table/rte_swx_table_learner.c             |  11 +
 lib/table/rte_swx_table_selector.c            |   7 +
 lib/table/rte_swx_table_wm.c                  |   2 +
 lib/table/rte_table_acl.c                     |   2 +
 lib/table/rte_table_array.c                   |   2 +
 lib/table/rte_table_hash_cuckoo.c             |   2 +
 lib/table/rte_table_hash_ext.c                |   2 +
 lib/table/rte_table_hash_key16.c              |   3 +
 lib/table/rte_table_hash_key32.c              |   3 +
 lib/table/rte_table_hash_key8.c               |   3 +
 lib/table/rte_table_hash_lru.c                |   2 +
 lib/table/rte_table_lpm.c                     |   2 +
 lib/table/rte_table_lpm_ipv6.c                |   2 +
 lib/table/rte_table_stub.c                    |   2 +
 lib/table/version.map                         |  53 --
 lib/telemetry/telemetry.c                     |   4 +
 lib/telemetry/telemetry_data.c                |  18 +
 lib/telemetry/telemetry_legacy.c              |   2 +
 lib/telemetry/version.map                     |  40 --
 lib/timer/rte_timer.c                         |  19 +
 lib/timer/version.map                         |  24 -
 lib/vhost/socket.c                            |  17 +
 lib/vhost/vdpa.c                              |  12 +
 lib/vhost/version.map                         | 111 ----
 lib/vhost/vhost.c                             |  42 ++
 lib/vhost/vhost_crypto.c                      |   7 +
 lib/vhost/vhost_user.c                        |   3 +
 lib/vhost/virtio_net.c                        |   8 +
 536 files changed, 5181 insertions(+), 6574 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 delete mode 100644 buildtools/map_to_win.py
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py
 delete mode 100644 drivers/baseband/acc/version.map
 delete mode 100644 drivers/baseband/fpga_5gnr_fec/version.map
 delete mode 100644 drivers/baseband/fpga_lte_fec/version.map
 delete mode 100644 drivers/bus/auxiliary/version.map
 delete mode 100644 drivers/bus/cdx/version.map
 create mode 100644 drivers/bus/dpaa/dpaa_bus_base_symbols.c
 delete mode 100644 drivers/bus/dpaa/version.map
 delete mode 100644 drivers/bus/fslmc/version.map
 delete mode 100644 drivers/bus/ifpga/version.map
 delete mode 100644 drivers/bus/pci/version.map
 delete mode 100644 drivers/bus/platform/version.map
 delete mode 100644 drivers/bus/uacce/version.map
 delete mode 100644 drivers/bus/vdev/version.map
 delete mode 100644 drivers/bus/vmbus/version.map
 create mode 100644 drivers/common/cnxk/roc_platform_base_symbols.c
 delete mode 100644 drivers/common/cnxk/version.map
 delete mode 100644 drivers/common/cpt/version.map
 delete mode 100644 drivers/common/dpaax/version.map
 delete mode 100644 drivers/common/ionic/version.map
 delete mode 100644 drivers/common/mlx5/version.map
 delete mode 100644 drivers/common/mvep/version.map
 delete mode 100644 drivers/common/nfp/version.map
 delete mode 100644 drivers/common/nitrox/version.map
 delete mode 100644 drivers/common/octeontx/version.map
 create mode 100644 drivers/common/sfc_efx/sfc_base_symbols.c
 delete mode 100644 drivers/common/sfc_efx/version.map
 delete mode 100644 drivers/crypto/cnxk/version.map
 delete mode 100644 drivers/crypto/dpaa2_sec/version.map
 delete mode 100644 drivers/crypto/dpaa_sec/version.map
 delete mode 100644 drivers/crypto/octeontx/version.map
 delete mode 100644 drivers/crypto/scheduler/version.map
 delete mode 100644 drivers/dma/cnxk/version.map
 delete mode 100644 drivers/event/cnxk/version.map
 delete mode 100644 drivers/event/dlb2/version.map
 delete mode 100644 drivers/mempool/cnxk/version.map
 delete mode 100644 drivers/mempool/dpaa/version.map
 delete mode 100644 drivers/mempool/dpaa2/version.map
 delete mode 100644 drivers/net/atlantic/version.map
 delete mode 100644 drivers/net/bnxt/version.map
 delete mode 100644 drivers/net/bonding/version.map
 delete mode 100644 drivers/net/cnxk/version.map
 delete mode 100644 drivers/net/dpaa/version.map
 delete mode 100644 drivers/net/dpaa2/version.map
 delete mode 100644 drivers/net/intel/i40e/version.map
 create mode 100644 drivers/net/intel/iavf/iavf_base_symbols.c
 delete mode 100644 drivers/net/intel/iavf/version.map
 delete mode 100644 drivers/net/intel/ice/version.map
 delete mode 100644 drivers/net/intel/idpf/version.map
 delete mode 100644 drivers/net/intel/ipn3ke/version.map
 delete mode 100644 drivers/net/intel/ixgbe/version.map
 delete mode 100644 drivers/net/mlx5/version.map
 delete mode 100644 drivers/net/octeontx/version.map
 delete mode 100644 drivers/net/ring/version.map
 delete mode 100644 drivers/net/softnic/version.map
 delete mode 100644 drivers/net/vhost/version.map
 delete mode 100644 drivers/power/kvm_vm/version.map
 delete mode 100644 drivers/raw/cnxk_rvu_lf/version.map
 delete mode 100644 drivers/raw/ifpga/version.map
 delete mode 100644 drivers/version.map
 delete mode 100644 lib/acl/version.map
 delete mode 100644 lib/argparse/version.map
 delete mode 100644 lib/bbdev/version.map
 delete mode 100644 lib/bitratestats/version.map
 delete mode 100644 lib/bpf/version.map
 delete mode 100644 lib/cfgfile/version.map
 delete mode 100644 lib/cmdline/version.map
 delete mode 100644 lib/compressdev/version.map
 delete mode 100644 lib/cryptodev/version.map
 delete mode 100644 lib/dispatcher/version.map
 delete mode 100644 lib/distributor/version.map
 delete mode 100644 lib/dmadev/version.map
 create mode 100644 lib/eal/common/eal_export.h
 delete mode 100644 lib/eal/include/rte_function_versioning.h
 delete mode 100644 lib/eal/version.map
 delete mode 100644 lib/efd/version.map
 delete mode 100644 lib/ethdev/version.map
 delete mode 100644 lib/eventdev/version.map
 delete mode 100644 lib/fib/version.map
 delete mode 100644 lib/gpudev/version.map
 delete mode 100644 lib/graph/version.map
 delete mode 100644 lib/gro/version.map
 delete mode 100644 lib/gso/version.map
 delete mode 100644 lib/hash/version.map
 delete mode 100644 lib/ip_frag/version.map
 delete mode 100644 lib/ipsec/version.map
 delete mode 100644 lib/jobstats/version.map
 delete mode 100644 lib/kvargs/version.map
 delete mode 100644 lib/latencystats/version.map
 delete mode 100644 lib/log/version.map
 delete mode 100644 lib/lpm/version.map
 delete mode 100644 lib/mbuf/version.map
 delete mode 100644 lib/member/version.map
 delete mode 100644 lib/mempool/version.map
 delete mode 100644 lib/meter/version.map
 delete mode 100644 lib/metrics/version.map
 delete mode 100644 lib/mldev/version.map
 delete mode 100644 lib/net/version.map
 delete mode 100644 lib/node/version.map
 delete mode 100644 lib/pcapng/version.map
 delete mode 100644 lib/pci/version.map
 delete mode 100644 lib/pdcp/version.map
 delete mode 100644 lib/pdump/version.map
 delete mode 100644 lib/pipeline/version.map
 delete mode 100644 lib/port/version.map
 delete mode 100644 lib/power/version.map
 delete mode 100644 lib/rawdev/version.map
 delete mode 100644 lib/rcu/version.map
 delete mode 100644 lib/regexdev/version.map
 delete mode 100644 lib/reorder/version.map
 delete mode 100644 lib/rib/version.map
 delete mode 100644 lib/ring/version.map
 delete mode 100644 lib/sched/version.map
 delete mode 100644 lib/security/version.map
 delete mode 100644 lib/stack/version.map
 delete mode 100644 lib/table/version.map
 delete mode 100644 lib/telemetry/version.map
 delete mode 100644 lib/timer/version.map
 delete mode 100644 lib/vhost/version.map

-- 
2.49.0


^ permalink raw reply	[relevance 3%]

* [PATCH v7 4/8] build: generate symbol maps
  2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
@ 2025-04-03 16:58 18%   ` David Marchand
  2025-04-03 16:58 18%   ` [PATCH v7 6/8] build: use dynamically generated version maps David Marchand
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-04-03 16:58 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff

Rather than maintain a file in parallel of the code, symbols to be
exported can be marked with a token RTE_EXPORT_*SYMBOL.

From those marks, the build framework generates map files only for
symbols actually compiled (which means that the WINDOWS_NO_EXPORT hack
becomes unnecessary).

The build framework directly creates a map file in the format that the
linker expects (rather than converting from GNU linker to MSVC linker).

Empty maps are allowed again as a replacement for drivers/version.map.

The symbol check is updated to only support the new format.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since v6:
- rewrote parameter parsing for python scripts,
- fixed most pylint complaints,
- dropped TODO comment in symbol check,
- renamed header to eal_export.h,
- updated macros definition,

Changes since RFC v4:
- fixed MSVC export map (a msvc->mslinker update was missing),
- fixed join() error (with older meson? I don't see this on Fedora),
- explicit inclusion of header is now required,
- header has been renamed and moved to lib/eal/common/,
- because lib/log does not depend on EAL, added explicit
  include_directories,
- symbol versioning update has been moved later in the series,
  so updated gen-version-map.py accordingly,
- fixed bug when checking symbol removal,

Changes since RFC v3:
- polished python,
- fixed doc updates not belonging to this patch,
- renamed map files,
- changed msvc->mslinker as link mode,
- added parsing of AVX sources,

Changes since RFC v2:
- because of MSVC limitations wrt macro passed via cmdline,
  used an internal header for defining RTE_EXPORT_* macros,
- updated documentation and tooling,

---
 MAINTAINERS                                |   2 +
 buildtools/gen-version-map.py              | 134 ++++++++++++
 buildtools/map-list-symbol.sh              |  10 +-
 buildtools/meson.build                     |   1 +
 devtools/check-symbol-change.py            | 108 ++++++++++
 devtools/check-symbol-maps.sh              |  14 --
 devtools/checkpatches.sh                   |   2 +-
 doc/guides/contributing/abi_versioning.rst | 227 ++-------------------
 drivers/meson.build                        |  98 +++++----
 drivers/version.map                        |   3 -
 lib/eal/common/eal_export.h                |  16 ++
 lib/meson.build                            |  96 ++++++---
 12 files changed, 415 insertions(+), 296 deletions(-)
 create mode 100755 buildtools/gen-version-map.py
 create mode 100755 devtools/check-symbol-change.py
 delete mode 100644 drivers/version.map
 create mode 100644 lib/eal/common/eal_export.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4b01103f8e..42ea07854b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -95,6 +95,7 @@ F: devtools/check-maintainers.sh
 F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
+F: devtools/check-symbol-change.py
 F: devtools/check-symbol-change.sh
 F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
@@ -127,6 +128,7 @@ F: config/
 F: buildtools/check-symbols.sh
 F: buildtools/chkincs/
 F: buildtools/call-sphinx-build.py
+F: buildtools/gen-version-map.py
 F: buildtools/get-cpu-count.py
 F: buildtools/get-numa-count.py
 F: buildtools/list-dir-globs.py
diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
new file mode 100755
index 0000000000..c5ea10def0
--- /dev/null
+++ b/buildtools/gen-version-map.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Generate a version map file used by GNU or MSVC linker."""
+
+import argparse
+import re
+
+# From eal_export.h
+export_exp_sym_regexp = re.compile(
+    r"^RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+), ([0-9]+.[0-9]+)\)"
+)
+export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
+# From rte_function_versioning.h
+ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
+default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter,
+)
+parser.add_argument(
+    "--linker", choices=["gnu", "mingw", "mslinker", ], required=True, help="Linker type"
+)
+parser.add_argument(
+    "--abi-version",
+    required=True,
+    type=argparse.FileType("r", encoding="utf-8"),
+    help="ABI version file",
+)
+parser.add_argument(
+    "--output",
+    required=True,
+    type=argparse.FileType("w", encoding="utf-8"),
+    help="Output file",
+)
+parser.add_argument(
+    "--source",
+    nargs="+",
+    required=True,
+    type=argparse.FileType("r", encoding="utf-8"),
+    help="Input source to parse",
+)
+args = parser.parse_args()
+
+ABI_MAJOR = re.match("([0-9]+).[0-9]", args.abi_version.readline()).group(1)
+ABI = f"DPDK_{ABI_MAJOR}"
+
+symbols = {}
+
+for file in args.source:
+    for ln in file.readlines():
+        node = None
+        symbol = None
+        comment = ""
+        if export_exp_sym_regexp.match(ln):
+            node = "EXPERIMENTAL"
+            symbol = export_exp_sym_regexp.match(ln).group(1)
+            version = export_exp_sym_regexp.match(ln).group(2)
+            comment = f" # added in {version}"
+        elif export_int_sym_regexp.match(ln):
+            node = "INTERNAL"
+            symbol = export_int_sym_regexp.match(ln).group(1)
+        elif export_sym_regexp.match(ln):
+            node = ABI
+            symbol = export_sym_regexp.match(ln).group(1)
+        elif ver_sym_regexp.match(ln):
+            abi = ver_sym_regexp.match(ln).group(2)
+            node = f"DPDK_{abi}"
+            symbol = ver_sym_regexp.match(ln).group(1)
+        elif ver_exp_sym_regexp.match(ln):
+            node = "EXPERIMENTAL"
+            symbol = ver_exp_sym_regexp.match(ln).group(1)
+        elif default_sym_regexp.match(ln):
+            abi = default_sym_regexp.match(ln).group(2)
+            node = f"DPDK_{abi}"
+            symbol = default_sym_regexp.match(ln).group(1)
+
+        if not symbol:
+            continue
+
+        if node not in symbols:
+            symbols[node] = {}
+        symbols[node][symbol] = comment
+
+if args.linker == "mslinker":
+    print("EXPORTS", file=args.output)
+    for key in (ABI, "EXPERIMENTAL", "INTERNAL"):
+        if key not in symbols:
+            continue
+        for symbol in sorted(symbols[key].keys()):
+            print(f"\t{symbol}", file=args.output)
+        del symbols[key]
+else:  # GNU format
+    local_token = False
+    for key in (ABI, "EXPERIMENTAL", "INTERNAL"):
+        if key not in symbols:
+            continue
+        print(f"{key} {{\n\tglobal:\n", file=args.output)
+        for symbol in sorted(symbols[key].keys()):
+            if args.linker == "mingw" and symbol.startswith("per_lcore"):
+                prefix = "__emutls_v."
+            else:
+                prefix = ""
+            comment = symbols[key][symbol]
+            print(f"\t{prefix}{symbol};{comment}", file=args.output)
+        if not local_token:
+            print("\n\tlocal: *;", file=args.output)
+            local_token = True
+        print("};", file=args.output)
+        del symbols[key]
+    for key in sorted(symbols.keys()):
+        print(f"{key} {{\n\tglobal:\n", file=args.output)
+        for symbol in sorted(symbols[key].keys()):
+            if args.linker == "mingw" and symbol.startswith("per_lcore"):
+                prefix = "__emutls_v."
+            else:
+                prefix = ""
+            comment = symbols[key][symbol]
+            print(f"\t{prefix}{symbol};{comment}", file=args.output)
+        print(f"}} {ABI};", file=args.output)
+        if not local_token:
+            print("\n\tlocal: *;", file=args.output)
+            local_token = True
+        del symbols[key]
+    # No exported symbol, add a catch all
+    if not local_token:
+        print(f"{ABI} {{", file=args.output)
+        print("\n\tlocal: *;", file=args.output)
+        local_token = True
+        print("};", file=args.output)
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index eb98451d8e..0829df4be5 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -62,10 +62,14 @@ for file in $@; do
 		if (current_section == "") {
 			next;
 		}
+		symbol_version = current_version
+		if (/^[^}].*[^:*]; # added in /) {
+			symbol_version = $5
+		}
 		if ("'$version'" != "") {
-			if ("'$version'" == "unset" && current_version != "") {
+			if ("'$version'" == "unset" && symbol_version != "") {
 				next;
-			} else if ("'$version'" != "unset" && "'$version'" != current_version) {
+			} else if ("'$version'" != "unset" && "'$version'" != symbol_version) {
 				next;
 			}
 		}
@@ -73,7 +77,7 @@ for file in $@; do
 		if ("'$symbol'" == "all" || $1 == "'$symbol'") {
 			ret = 0;
 			if ("'$quiet'" == "") {
-				print "'$file' "current_section" "$1" "current_version;
+				print "'$file' "current_section" "$1" "symbol_version;
 			}
 			if ("'$symbol'" != "all") {
 				exit 0;
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 4e2c1217a2..b745e9afa4 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -16,6 +16,7 @@ else
     py3 = ['meson', 'runpython']
 endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
+gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
diff --git a/devtools/check-symbol-change.py b/devtools/check-symbol-change.py
new file mode 100755
index 0000000000..1efeb82fcd
--- /dev/null
+++ b/devtools/check-symbol-change.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2025 Red Hat, Inc.
+
+"""Check exported symbols change in a patch."""
+
+import argparse
+import re
+
+file_header_regexp = re.compile(r"^(\-\-\-|\+\+\+) [ab]/(lib|drivers)/([^/]+)/([^/]+)")
+# From eal_exports.h
+export_exp_sym_regexp = re.compile(r"^.RTE_EXPORT_EXPERIMENTAL_SYMBOL\(([^,]+),")
+export_int_sym_regexp = re.compile(r"^.RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
+export_sym_regexp = re.compile(r"^.RTE_EXPORT_SYMBOL\(([^)]+)\)")
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter,
+)
+parser.add_argument(
+    "--patch",
+    nargs="+",
+    required=True,
+    type=argparse.FileType("r", encoding="utf-8"),
+    help="Input patch to parse",
+)
+args = parser.parse_args()
+
+symbols = {}
+
+for file in args.patch:
+    for ln in file.readlines():
+        if file_header_regexp.match(ln):
+            if file_header_regexp.match(ln).group(2) == "lib":
+                lib = "/".join(file_header_regexp.match(ln).group(2, 3))
+            elif file_header_regexp.match(ln).group(3) == "intel":
+                lib = "/".join(file_header_regexp.match(ln).group(2, 3, 4))
+            else:
+                lib = "/".join(file_header_regexp.match(ln).group(2, 3))
+
+            if lib not in symbols:
+                symbols[lib] = {}
+            continue
+
+        if export_exp_sym_regexp.match(ln):
+            symbol = export_exp_sym_regexp.match(ln).group(1)
+            node = "EXPERIMENTAL"
+        elif export_int_sym_regexp.match(ln):
+            node = "INTERNAL"
+            symbol = export_int_sym_regexp.match(ln).group(1)
+        elif export_sym_regexp.match(ln):
+            symbol = export_sym_regexp.match(ln).group(1)
+            node = "stable"
+        else:
+            continue
+
+        if symbol not in symbols[lib]:
+            symbols[lib][symbol] = {}
+        added = ln[0] == "+"
+        if (
+            added
+            and "added" in symbols[lib][symbol]
+            and node != symbols[lib][symbol]["added"]
+        ):
+            print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+        if (
+            not added
+            and "removed" in symbols[lib][symbol]
+            and node != symbols[lib][symbol]["removed"]
+        ):
+            print(f"{symbol} in {lib} was found in multiple ABI, please check.")
+        if added:
+            symbols[lib][symbol]["added"] = node
+        else:
+            symbols[lib][symbol]["removed"] = node
+
+    for lib in sorted(symbols.keys()):
+        error = False
+        for symbol in sorted(symbols[lib].keys()):
+            if "removed" not in symbols[lib][symbol]:
+                # Symbol addition
+                node = symbols[lib][symbol]["added"]
+                if node == "stable":
+                    print(
+                        f"ERROR: {symbol} in {lib} has been added directly to stable ABI."
+                    )
+                    error = True
+                else:
+                    print(f"INFO: {symbol} in {lib} has been added to {node} ABI.")
+                continue
+
+            if "added" not in symbols[lib][symbol]:
+                # Symbol removal
+                node = symbols[lib][symbol]["removed"]
+                if node == "stable":
+                    print(f"INFO: {symbol} in {lib} has been removed from stable ABI.")
+                    print("Please check it has gone though the deprecation process.")
+                continue
+
+            if symbols[lib][symbol]["added"] == symbols[lib][symbol]["removed"]:
+                # Symbol was moved around
+                continue
+
+            # Symbol modifications
+            added = symbols[lib][symbol]["added"]
+            removed = symbols[lib][symbol]["removed"]
+            print(f"INFO: {symbol} in {lib} is moving from {removed} to {added}")
+            print("Please check it has gone though the deprecation process.")
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
index 6121f78ec6..fcd3931e5d 100755
--- a/devtools/check-symbol-maps.sh
+++ b/devtools/check-symbol-maps.sh
@@ -60,20 +60,6 @@ if [ -n "$local_miss_maps" ] ; then
     ret=1
 fi
 
-find_empty_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh $map | wc -l) != '0' ] || echo $map
-    done
-}
-
-empty_maps=$(find_empty_maps $@)
-if [ -n "$empty_maps" ] ; then
-    echo "Found empty maps:"
-    echo "$empty_maps"
-    ret=1
-fi
-
 find_bad_format_maps ()
 {
     abi_version=$(cut -d'.' -f 1 ABI_VERSION)
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index c9088bb403..9180c2b070 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -33,7 +33,7 @@ VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\
 PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
 SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
 LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
-NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG"
+NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG,EXPORT_SYMBOL"
 options="$options $DPDK_CHECKPATCH_OPTIONS"
 
 print_usage () {
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 7afd1c1886..b6b6bf64d7 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -58,12 +58,12 @@ persists over multiple releases.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -77,7 +77,7 @@ that library.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_21 {
         global:
  ...
@@ -88,7 +88,7 @@ that library.
  } DPDK_21;
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_21 {
         global:
  ...
@@ -100,12 +100,12 @@ how this may be done.
 
 .. code-block:: none
 
- $ head ./lib/acl/version.map
+ $ head ./build/lib/acl_exports.map
  DPDK_22 {
         global:
  ...
 
- $ head ./lib/eal/version.map
+ $ head ./build/lib/eal_exports.map
  DPDK_22 {
         global:
  ...
@@ -134,8 +134,7 @@ linked to the DPDK.
 
 To support backward compatibility the ``rte_function_versioning.h``
 header file provides macros to use when updating exported functions. These
-macros are used in conjunction with the ``version.map`` file for
-a given library to allow multiple versions of a symbol to exist in a shared
+macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
 The macros exported are:
@@ -176,6 +175,7 @@ Assume we have a function as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param)
  {
@@ -194,6 +194,7 @@ private, is safe), but it also requires modifying the code as follows
   * Create an acl context object for apps to
   * manipulate
   */
+ RTE_EXPORT_SYMBOL(rte_acl_create)
  struct rte_acl_ctx *
  rte_acl_create(const struct rte_acl_param *param, int debug)
  {
@@ -210,78 +211,16 @@ The addition of a parameter to the function is ABI breaking as the function is
 public, and existing application may use it in its current form. However, the
 compatibility macros in DPDK allow a developer to use symbol versioning so that
 multiple functions can be mapped to the same public symbol based on when an
-application was linked to it. To see how this is done, we start with the
-requisite libraries version map file. Initially the version map file for the acl
-library looks like this
+application was linked to it.
 
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-This file needs to be modified as follows
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_create;
-        rte_acl_dump;
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-
-   } DPDK_21;
-
-The addition of the new block tells the linker that a new version node
-``DPDK_22`` is available, which contains the symbol rte_acl_create, and inherits
-the symbols from the DPDK_21 node. This list is directly translated into a
-list of exported symbols when DPDK is compiled as a shared library.
-
-Next, we need to specify in the code which function maps to the rte_acl_create
+We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
 we need to update the function so that it is uniquely named, and not in conflict
 with the public symbol name
 
 .. code-block:: c
 
+ -RTE_EXPORT_SYMBOL(rte_acl_create)
  -struct rte_acl_ctx *
  -rte_acl_create(const struct rte_acl_param *param)
  +struct rte_acl_ctx * __vsym
@@ -358,9 +297,9 @@ such that it contains both versions of the symbol and the public API.
    rte_acl_create_v22(const struct rte_acl_param *param, int debug);
 
 
-And that's it, on the next shared library rebuild, there will be two versions of
-rte_acl_create, an old DPDK_21 version, used by previously built applications,
-and a new DPDK_22 version, used by future built applications.
+And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
+an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
+used by newly built applications.
 
 .. note::
 
@@ -443,6 +382,7 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
@@ -450,27 +390,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
    ...
    }
 
-In the map file, experimental symbols are listed as part of the ``EXPERIMENTAL``
-version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
 When we promote the symbol to the stable ABI, we simply strip the
-``__rte_experimental`` annotation from the function and move the symbol from the
-``EXPERIMENTAL`` node, to the node of the next major ABI version as follow.
+``__rte_experimental`` annotation from the function.
 
 .. code-block:: c
 
@@ -478,31 +399,13 @@ When we promote the symbol to the stable ABI, we simply strip the
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
           ...
    }
 
-We then update the map file, adding the symbol ``rte_acl_create``
-to the ``DPDK_22`` version node.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-
 Although there are strictly no guarantees or commitments associated with
 :ref:`experimental symbols <experimental_apis>`, a maintainer may wish to offer
 an alias to experimental. The process to add an alias to experimental,
@@ -519,6 +422,7 @@ and ``DPDK_22`` version nodes.
     * Create an acl context object for apps to
     * manipulate
     */
+   RTE_EXPORT_SYMBOL(rte_acl_create)
    struct rte_acl_ctx *
    rte_acl_create(const struct rte_acl_param *param)
    {
@@ -540,37 +444,6 @@ and ``DPDK_22`` version nodes.
    }
    BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
-In the map file, we map the symbol to both the ``EXPERIMENTAL``
-and ``DPDK_22`` version nodes.
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-        ...
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-
-        rte_acl_create;
-   } DPDK_21;
-
-   EXPERIMENTAL {
-        global:
-
-        rte_acl_create;
-   };
-
-.. note::
-
-   Please note, similar to :ref:`symbol versioning <example_abi_macro_usage>`,
-   when aliasing to experimental you will also need to take care of
-   :ref:`mapping static symbols <mapping_static_symbols>`.
-
-
 .. _abi_deprecation:
 
 Deprecating part of a public API
@@ -579,38 +452,7 @@ ________________________________
 Lets assume that you've done the above updates, and in preparation for the next
 major ABI version you decide you would like to retire the old version of the
 function. After having gone through the ABI deprecation announcement process,
-removal is easy. Start by removing the symbol from the requisite version map
-file:
-
-.. code-block:: none
-
-   DPDK_21 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
- -      rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
-   };
-
-   DPDK_22 {
-        global:
-        rte_acl_create;
-   } DPDK_21;
-
+removal is easy.
 
 Next remove the corresponding versioned export.
 
@@ -634,36 +476,9 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
-To do this, start by modifying the version map file, such that all symbols from
-the node to be removed are merged into the next node in the map.
-
-In the case of our map above, it would transform to look as follows
-
-.. code-block:: none
-
-   DPDK_22 {
-        global:
-
-        rte_acl_add_rules;
-        rte_acl_build;
-        rte_acl_classify;
-        rte_acl_classify_alg;
-        rte_acl_classify_scalar;
-        rte_acl_dump;
-        rte_acl_create
-        rte_acl_find_existing;
-        rte_acl_free;
-        rte_acl_ipv4vlan_add_rules;
-        rte_acl_ipv4vlan_build;
-        rte_acl_list_dump;
-        rte_acl_reset;
-        rte_acl_reset_rules;
-        rte_acl_set_ctx_classify;
-
-        local: *;
  };
 
-Then any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index c15319dc24..ff0cb2e02d 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -275,14 +275,14 @@ foreach subpath:subdirs
                 dependencies: static_deps,
                 c_args: cflags)
         objs += tmp_lib.extract_all_objects(recursive: true)
-        sources = custom_target(out_filename,
+        sources_pmd_info = custom_target(out_filename,
                 command: [pmdinfo, tmp_lib.full_path(), '@OUTPUT@', pmdinfogen],
                 output: out_filename,
                 depends: [tmp_lib])
 
         # now build the static driver
         static_lib = static_library(lib_name,
-                sources,
+                sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: static_deps,
@@ -292,48 +292,72 @@ foreach subpath:subdirs
         # now build the shared driver
         version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
 
-        lk_deps = []
-        lk_args = []
         if not fs.is_file(version_map)
-            version_map = '@0@/version.map'.format(meson.current_source_dir())
-            lk_deps += [version_map]
-        else
-            lk_deps += [version_map]
-            if not is_windows and developer_mode
-                # on unix systems check the output of the
-                # check-symbols.sh script, using it as a
-                # dependency of the .so build
-                lk_deps += custom_target(lib_name + '.sym_chk',
-                        command: [check_symbols, version_map, '@INPUT@'],
-                        capture: true,
-                        input: static_lib,
-                        output: lib_name + '.sym_chk')
-            endif
-        endif
-
-        if is_windows
             if is_ms_linker
-                def_file = custom_target(lib_name + '_def',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_exports.def'.format(lib_name))
-                lk_deps += [def_file]
-
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
+                link_mode = 'mslinker'
+            elif is_windows
+                link_mode = 'mingw'
             else
-                mingw_map = custom_target(lib_name + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(lib_name))
-                lk_deps += [mingw_map]
-
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                link_mode = 'gnu'
+            endif
+            version_map = custom_target(lib_name + '_map',
+                    command: [gen_version_map, '--linker', link_mode,
+                              '--abi-version', abi_version_file, '--output', '@OUTPUT@',
+                              '--source', '@INPUT@'],
+                    input: sources + sources_avx2 + sources_avx512,
+                    output: '_'.join([class, name, 'exports.map']))
+            version_map_path = version_map.full_path()
+            version_map_dep = [version_map]
+            lk_deps = [version_map]
+
+            if is_ms_linker and is_ms_compiler
+                lk_args = ['/def:' + version_map.full_path()]
+            elif is_ms_linker
+                lk_args = ['-Wl,/def:' + version_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
             endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            version_map_path = version_map
+            version_map_dep = []
+            lk_deps = [version_map]
+
+            if is_windows
+                if is_ms_linker
+                    def_file = custom_target(lib_name + '_def',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_exports.def'.format(lib_name))
+                    lk_deps += [def_file]
+
+                    lk_args = ['-Wl,/def:' + def_file.full_path()]
+                else
+                    mingw_map = custom_target(lib_name + '_mingw',
+                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                            input: version_map,
+                            output: '@0@_mingw.map'.format(lib_name))
+                    lk_deps += [mingw_map]
+
+                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+                endif
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
+        endif
+
+        if not is_windows and developer_mode
+            # on unix systems check the output of the
+            # check-symbols.sh script, using it as a
+            # dependency of the .so build
+            lk_deps += custom_target(lib_name + '.sym_chk',
+                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    capture: true,
+                    input: static_lib,
+                    output: lib_name + '.sym_chk',
+                    depends: version_map_dep)
         endif
 
-        shared_lib = shared_library(lib_name, sources,
+        shared_lib = shared_library(lib_name, sources_pmd_info,
                 objects: objs,
                 include_directories: includes,
                 dependencies: shared_deps,
diff --git a/drivers/version.map b/drivers/version.map
deleted file mode 100644
index 17cc97bda6..0000000000
--- a/drivers/version.map
+++ /dev/null
@@ -1,3 +0,0 @@
-DPDK_25 {
-	local: *;
-};
diff --git a/lib/eal/common/eal_export.h b/lib/eal/common/eal_export.h
new file mode 100644
index 0000000000..7904ed851c
--- /dev/null
+++ b/lib/eal/common/eal_export.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef EAL_EXPORT_H
+#define EAL_EXPORT_H
+
+/* Internal macros for exporting symbols, used by the build system.
+ * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
+ * version this symbol was introduced in.
+ */
+#define RTE_EXPORT_EXPERIMENTAL_SYMBOL(symbol, ver)
+#define RTE_EXPORT_INTERNAL_SYMBOL(symbol)
+#define RTE_EXPORT_SYMBOL(symbol)
+
+#endif /* EAL_EXPORT_H */
diff --git a/lib/meson.build b/lib/meson.build
index 91efc65c4b..51d44bfd41 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
+fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -137,9 +138,12 @@ foreach l:libraries
     # external package/library requirements
     ext_deps = []
     deps = []
-    # eal is standard dependency once built
     if dpdk_conf.has('RTE_LIB_EAL')
+        # eal is standard dependency once built
         deps += ['eal']
+    else
+        # otherwise, make private headers available (like eal_export.h)
+        includes += include_directories('eal/common')
     endif
 
     if dpdk_libs_deprecated.contains(l)
@@ -285,42 +289,60 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not use_function_versioning or is_windows
-        # use pre-build objects to build shared lib
-        sources = []
-        objs += static_lib.extract_all_objects(recursive: false)
-    else
-        # for compat we need to rebuild with
-        # RTE_BUILD_SHARED_LIB defined
-        cflags += '-DRTE_BUILD_SHARED_LIB'
-    endif
-
-    version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-    lk_deps = [version_map]
-
-    if is_ms_linker
-        def_file = custom_target(libname + '_def',
-                command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                input: version_map,
-                output: '@0@_exports.def'.format(libname))
-        lk_deps += [def_file]
+    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
+        else
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(libname + '_map',
+                command: [gen_version_map, '--linker', link_mode,
+                          '--abi-version', abi_version_file, '--output', '@OUTPUT@',
+                          '--source', '@INPUT@'],
+                input: sources,
+                output: '_'.join([name, 'exports.map']))
+        version_map_path = version_map.full_path()
+        version_map_dep = [version_map]
+        lk_deps = [version_map]
 
-        if is_ms_compiler
-            lk_args = ['/def:' + def_file.full_path()]
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
         else
-            lk_args = ['-Wl,/def:' + def_file.full_path()]
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
     else
-        if is_windows
-            mingw_map = custom_target(libname + '_mingw',
+        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
+        version_map_path = version_map
+        version_map_dep = []
+        lk_deps = [version_map]
+        if is_ms_linker
+            def_file = custom_target(libname + '_def',
                     command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
                     input: version_map,
-                    output: '@0@_mingw.map'.format(libname))
-            lk_deps += [mingw_map]
+                    output: '@0@_exports.def'.format(libname))
+            lk_deps += [def_file]
 
-            lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            if is_ms_compiler
+                lk_args = ['/def:' + def_file.full_path()]
+            else
+                lk_args = ['-Wl,/def:' + def_file.full_path()]
+            endif
         else
-            lk_args = ['-Wl,--version-script=' + version_map]
+            if is_windows
+                mingw_map = custom_target(libname + '_mingw',
+                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
+                        input: version_map,
+                        output: '@0@_mingw.map'.format(libname))
+                lk_deps += [mingw_map]
+
+                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
+            else
+                lk_args = ['-Wl,--version-script=' + version_map]
+            endif
         endif
     endif
 
@@ -329,11 +351,21 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols,
-                    version_map, '@INPUT@'],
+                command: [check_symbols, version_map_path, '@INPUT@'],
                 capture: true,
                 input: static_lib,
-                output: name + '.sym_chk')
+                output: name + '.sym_chk',
+                depends: version_map_dep)
+    endif
+
+    if not use_function_versioning or is_windows
+        # use pre-build objects to build shared lib
+        sources = []
+        objs += static_lib.extract_all_objects(recursive: false)
+    else
+        # for compat we need to rebuild with
+        # RTE_BUILD_SHARED_LIB defined
+        cflags += '-DRTE_BUILD_SHARED_LIB'
     endif
 
     shared_lib = shared_library(libname,
-- 
2.49.0


^ permalink raw reply	[relevance 18%]

* [PATCH v7 6/8] build: use dynamically generated version maps
  2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
  2025-04-03 16:58 18%   ` [PATCH v7 4/8] build: generate symbol maps David Marchand
@ 2025-04-03 16:58 18%   ` David Marchand
  2025-04-03 16:58 23%   ` [PATCH v7 8/8] eal: rework function versioning macros David Marchand
  2025-04-07 13:46  0%   ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-04-03 16:58 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Aaron Conole, Michael Santana

Switch to dynamically generated version maps.

As the map files get generated, tooling around checking, converting,
updating etc.. static version maps can be removed.

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Aaron Conole <aconole@redhat.com>
---
 .github/workflows/build.yml                   |   1 -
 MAINTAINERS                                   |   7 -
 buildtools/check-symbols.sh                   |  33 +-
 buildtools/map-list-symbol.sh                 |   7 +-
 buildtools/map_to_win.py                      |  41 ---
 buildtools/meson.build                        |   1 -
 devtools/check-symbol-change.sh               | 186 -----------
 devtools/check-symbol-maps.sh                 | 101 ------
 devtools/checkpatches.sh                      |   4 +-
 devtools/update-abi.sh                        |  46 ---
 devtools/update_version_map_abi.py            | 210 ------------
 doc/guides/contributing/abi_policy.rst        |  21 +-
 doc/guides/contributing/coding_style.rst      |   7 -
 .../contributing/img/patch_cheatsheet.svg     | 303 ++++++++----------
 doc/guides/contributing/patches.rst           |   6 +-
 drivers/meson.build                           |  78 ++---
 lib/meson.build                               |  77 ++---
 17 files changed, 193 insertions(+), 936 deletions(-)
 delete mode 100644 buildtools/map_to_win.py
 delete mode 100755 devtools/check-symbol-change.sh
 delete mode 100755 devtools/check-symbol-maps.sh
 delete mode 100755 devtools/update-abi.sh
 delete mode 100755 devtools/update_version_map_abi.py

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0cc4d12b0b..7a6b679fe5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,6 @@ jobs:
         failed=
         devtools/check-doc-vs-code.sh upstream/${{ env.REF_GIT_BRANCH }} || failed=true
         devtools/check-meson.py || failed=true
-        devtools/check-symbol-maps.sh || failed=true
         [ -z "$failed" ]
   ubuntu-vm-builds:
     name: ${{ join(matrix.config.*, '-') }}
diff --git a/MAINTAINERS b/MAINTAINERS
index 42ea07854b..480972ef1e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -88,7 +88,6 @@ M: Thomas Monjalon <thomas@monjalon.net>
 F: MAINTAINERS
 F: devtools/build-dict.sh
 F: devtools/check-abi.sh
-F: devtools/check-abi-version.sh
 F: devtools/check-doc-vs-code.sh
 F: devtools/check-dup-includes.sh
 F: devtools/check-maintainers.sh
@@ -96,17 +95,13 @@ F: devtools/check-forbidden-tokens.awk
 F: devtools/check-git-log.sh
 F: devtools/check-spdx-tag.sh
 F: devtools/check-symbol-change.py
-F: devtools/check-symbol-change.sh
-F: devtools/check-symbol-maps.sh
 F: devtools/checkpatches.sh
 F: devtools/get-maintainer.sh
 F: devtools/git-log-fixes.sh
 F: devtools/load-devel-config
 F: devtools/parse-flow-support.sh
 F: devtools/process-iwyu.py
-F: devtools/update-abi.sh
 F: devtools/update-patches.py
-F: devtools/update_version_map_abi.py
 F: devtools/libabigail.abignore
 F: devtools/words-case.txt
 F: license/
@@ -166,7 +161,6 @@ M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/common/
 F: lib/eal/unix/
 F: lib/eal/include/
-F: lib/eal/version.map
 F: doc/guides/prog_guide/env_abstraction_layer.rst
 F: app/test/test_alarm.c
 F: app/test/test_atomic.c
@@ -396,7 +390,6 @@ Windows support
 M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
 M: Tyler Retzlaff <roretzla@linux.microsoft.com>
 F: lib/eal/windows/
-F: buildtools/map_to_win.py
 F: doc/guides/windows_gsg/
 
 Windows memory allocation
diff --git a/buildtools/check-symbols.sh b/buildtools/check-symbols.sh
index b8ac24391e..0d6745ec14 100755
--- a/buildtools/check-symbols.sh
+++ b/buildtools/check-symbols.sh
@@ -7,29 +7,12 @@ OBJFILE=$2
 
 ROOTDIR=$(readlink -f $(dirname $(readlink -f $0))/..)
 LIST_SYMBOL=$ROOTDIR/buildtools/map-list-symbol.sh
-CHECK_SYMBOL_MAPS=$ROOTDIR/devtools/check-symbol-maps.sh
-
-# added check for "make -C test/" usage
-if [ ! -e $MAPFILE ] || [ ! -f $OBJFILE ]
-then
-	exit 0
-fi
-
-if [ -d $MAPFILE ]
-then
-	exit 0
-fi
-
 DUMPFILE=$(mktemp -t dpdk.${0##*/}.objdump.XXXXXX)
 trap 'rm -f "$DUMPFILE"' EXIT
 objdump -t $OBJFILE >$DUMPFILE
 
 ret=0
 
-if ! $CHECK_SYMBOL_MAPS $MAPFILE; then
-	ret=1
-fi
-
 for SYM in `$LIST_SYMBOL -S EXPERIMENTAL $MAPFILE |cut -d ' ' -f 3`
 do
 	if grep -q "\.text.*[[:space:]]$SYM$" $DUMPFILE &&
@@ -37,8 +20,7 @@ do
 		$LIST_SYMBOL -s $SYM $MAPFILE | grep -q EXPERIMENTAL
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as experimental
-		but is listed in version map
+		$SYM is not flagged as experimental but is exported as an experimental symbol
 		Please add __rte_experimental to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -53,9 +35,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S EXPERIMENTAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as experimental
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as experimental but is not exported as an experimental symbol
+		Please add RTE_EXPORT_EXPERIMENTAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
@@ -67,8 +48,7 @@ do
 		! grep -q "\.text\.internal.*[[:space:]]$SYM$" $DUMPFILE
 	then
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is not flagged as internal
-		but is listed in version map
+		$SYM is not flagged as internal but is exported as an internal symbol
 		Please add __rte_internal to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
@@ -83,9 +63,8 @@ for SYM in `awk '{
 do
 	$LIST_SYMBOL -S INTERNAL -s $SYM -q $MAPFILE || {
 		cat >&2 <<- END_OF_MESSAGE
-		$SYM is flagged as internal
-		but is not listed in version map
-		Please add $SYM to the version map
+		$SYM is flagged as internal but is not exported as an internal symbol
+		Please add RTE_EXPORT_INTERNAL_SYMBOL to the definition of $SYM
 		END_OF_MESSAGE
 		ret=1
 	}
diff --git a/buildtools/map-list-symbol.sh b/buildtools/map-list-symbol.sh
index 0829df4be5..962d5f3271 100755
--- a/buildtools/map-list-symbol.sh
+++ b/buildtools/map-list-symbol.sh
@@ -42,7 +42,6 @@ for file in $@; do
 	cat "$file" |awk '
 	BEGIN {
 		current_section = "";
-		current_version = "";
 		if ("'$section'" == "all" && "'$symbol'" == "all" && "'$version'" == "") {
 			ret = 0;
 		} else {
@@ -54,15 +53,11 @@ for file in $@; do
 			current_section = $1;
 		}
 	}
-	/.*}/ { current_section = ""; current_version = ""; }
-	/^\t# added in / {
-		current_version=$4;
-	}
+	/.*}/ { current_section = ""; }
 	/^[^}].*[^:*];/ {
 		if (current_section == "") {
 			next;
 		}
-		symbol_version = current_version
 		if (/^[^}].*[^:*]; # added in /) {
 			symbol_version = $5
 		}
diff --git a/buildtools/map_to_win.py b/buildtools/map_to_win.py
deleted file mode 100644
index aa1752cacd..0000000000
--- a/buildtools/map_to_win.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-import sys
-
-
-def is_function_line(ln):
-    return ln.startswith('\t') and ln.endswith(';\n') and ":" not in ln and "# WINDOWS_NO_EXPORT" not in ln
-
-# MinGW keeps the original .map file but replaces per_lcore* to __emutls_v.per_lcore*
-def create_mingw_map_file(input_map, output_map):
-    with open(input_map) as f_in, open(output_map, 'w') as f_out:
-        f_out.writelines([lines.replace('per_lcore', '__emutls_v.per_lcore') for lines in f_in.readlines()])
-
-def main(args):
-    if not args[1].endswith('version.map') or \
-            not args[2].endswith('exports.def') and \
-            not args[2].endswith('mingw.map'):
-        return 1
-
-    if args[2].endswith('mingw.map'):
-        create_mingw_map_file(args[1], args[2])
-        return 0
-
-# generate def file from map file.
-# This works taking indented lines only which end with a ";" and which don't
-# have a colon in them, i.e. the lines defining functions only.
-    else:
-        with open(args[1]) as f_in:
-            functions = [ln[:-2] + '\n' for ln in sorted(f_in.readlines())
-                         if is_function_line(ln)]
-            functions = ["EXPORTS\n"] + functions
-
-    with open(args[2], 'w') as f_out:
-        f_out.writelines(functions)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv))
diff --git a/buildtools/meson.build b/buildtools/meson.build
index b745e9afa4..1cd1ce02fd 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -18,7 +18,6 @@ endif
 echo = py3 + ['-c', 'import sys; print(*sys.argv[1:])']
 gen_version_map = py3 + files('gen-version-map.py')
 list_dir_globs = py3 + files('list-dir-globs.py')
-map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
 get_cpu_count_cmd = py3 + files('get-cpu-count.py')
 get_numa_count_cmd = py3 + files('get-numa-count.py')
diff --git a/devtools/check-symbol-change.sh b/devtools/check-symbol-change.sh
deleted file mode 100755
index 8992214ac8..0000000000
--- a/devtools/check-symbol-change.sh
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2018 Neil Horman <nhorman@tuxdriver.com>
-
-build_map_changes()
-{
-	local fname="$1"
-	local mapdb="$2"
-
-	cat "$fname" | awk '
-		# Initialize our variables
-		BEGIN {map="";sym="";ar="";sec=""; in_sec=0; in_map=0}
-
-		# Anything that starts with + or -, followed by an a
-		# and ends in the string .map is the name of our map file
-		# This may appear multiple times in a patch if multiple
-		# map files are altered, and all section/symbol names
-		# appearing between a triggering of this rule and the
-		# next trigger of this rule are associated with this file
-		/[-+] [ab]\/.*\.map/ {map=$2; in_map=1; next}
-
-		# The previous rule catches all .map files, anything else
-		# indicates we left the map chunk.
-		/[-+] [ab]\// {in_map=0}
-
-		# Triggering this rule, which starts a line and ends it
-		# with a { identifies a versioned section.  The section name is
-		# the rest of the line with the + and { symbols removed.
-		# Triggering this rule sets in_sec to 1, which actives the
-		# symbol rule below
-		/^.*{/ {
-			gsub("+", "");
-			if (in_map == 1) {
-				sec=$(NF-1); in_sec=1;
-			}
-		}
-
-		# This rule identifies the end of a section, and disables the
-		# symbol rule
-		/.*}/ {in_sec=0}
-
-		# This rule matches on a + followed by any characters except a :
-		# (which denotes a global vs local segment), and ends with a ;.
-		# The semicolon is removed and the symbol is printed with its
-		# association file name and version section, along with an
-		# indicator that the symbol is a new addition.  Note this rule
-		# only works if we have found a version section in the rule
-		# above (hence the in_sec check) And found a map file (the
-		# in_map check).  If we are not in a map chunk, do nothing.  If
-		# we are in a map chunk but not a section chunk, record it as
-		# unknown.
-		/^+[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " add"
-				} else {
-					print map " " sym " unknown add"
-				}
-			}
-		}
-
-		# This is the same rule as above, but the rule matches on a
-		# leading - rather than a +, denoting that the symbol is being
-		# removed.
-		/^-[^}].*[^:*];/ {gsub(";","");sym=$2;
-			if (in_map == 1) {
-				if (in_sec == 1) {
-					print map " " sym " " sec " del"
-				} else {
-					print map " " sym " unknown del"
-				}
-			}
-		}' > "$mapdb"
-
-		sort -u "$mapdb" > "$mapdb.2"
-		mv -f "$mapdb.2" "$mapdb"
-
-}
-
-is_stable_section() {
-	[ "$1" != 'EXPERIMENTAL' ] && [ "$1" != 'INTERNAL' ]
-}
-
-check_for_rule_violations()
-{
-	local mapdb="$1"
-	local mname
-	local symname
-	local secname
-	local ar
-	local ret=0
-
-	while read mname symname secname ar
-	do
-		if [ "$ar" = "add" ]
-		then
-
-			if [ "$secname" = "unknown" ]
-			then
-				# Just inform the user of this occurrence, but
-				# don't flag it as an error
-				echo -n "INFO: symbol $symname is added but "
-				echo -n "patch has insufficient context "
-				echo -n "to determine the section name "
-				echo -n "please ensure the version is "
-				echo "EXPERIMENTAL"
-				continue
-			fi
-
-			oldsecname=$(sed -n \
-			"s#$mname $symname \(.*\) del#\1#p" "$mapdb")
-
-			# A symbol can not enter a stable section directly
-			if [ -z "$oldsecname" ]
-			then
-				if ! is_stable_section $secname
-				then
-					echo -n "INFO: symbol $symname has "
-					echo -n "been added to the "
-					echo -n "$secname section of the "
-					echo "version map"
-					continue
-				else
-					echo -n "ERROR: symbol $symname "
-					echo -n "is added in the $secname "
-					echo -n "section, but is expected to "
-					echo -n "be added in the EXPERIMENTAL "
-					echo "section of the version map"
-					ret=1
-					continue
-				fi
-			fi
-
-			# This symbol is moving inside a section, nothing to do
-			if [ "$oldsecname" = "$secname" ]
-			then
-				continue
-			fi
-
-			# This symbol is moving between two sections (the
-			# original section is a stable section).
-			# This can be legit, just warn.
-			if is_stable_section $oldsecname
-			then
-				echo -n "INFO: symbol $symname is being "
-				echo -n "moved from $oldsecname to $secname. "
-				echo -n "Ensure that it has gone through the "
-				echo "deprecation process"
-				continue
-			fi
-		else
-
-			if ! grep -q "$mname $symname .* add" "$mapdb" && \
-			   is_stable_section $secname
-			then
-				# Just inform users that stable
-				# symbols need to go through a deprecation
-				# process
-				echo -n "INFO: symbol $symname is being "
-				echo -n "removed, ensure that it has "
-				echo "gone through the deprecation process"
-			fi
-		fi
-	done < "$mapdb"
-
-	return $ret
-}
-
-trap clean_and_exit_on_sig EXIT
-
-mapfile=`mktemp -t dpdk.mapdb.XXXXXX`
-patch=$1
-exit_code=1
-
-clean_and_exit_on_sig()
-{
-	rm -f "$mapfile"
-	exit $exit_code
-}
-
-build_map_changes "$patch" "$mapfile"
-check_for_rule_violations "$mapfile"
-exit_code=$?
-rm -f "$mapfile"
-
-exit $exit_code
diff --git a/devtools/check-symbol-maps.sh b/devtools/check-symbol-maps.sh
deleted file mode 100755
index fcd3931e5d..0000000000
--- a/devtools/check-symbol-maps.sh
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright 2018 Mellanox Technologies, Ltd
-
-cd $(dirname $0)/..
-
-# speed up by ignoring Unicode details
-export LC_ALL=C
-
-if [ $# = 0 ] ; then
-    set -- $(find lib drivers -name '*.map' -a ! -path drivers/version.map)
-fi
-
-ret=0
-
-find_orphan_symbols ()
-{
-    for map in $@ ; do
-        for sym in $(sed -rn 's,^([^}]*_.*);.*$,\1,p' $map) ; do
-            if echo $sym | grep -q '^per_lcore_' ; then
-                symsrc=${sym#per_lcore_}
-            elif echo $sym | grep -q '^__rte_.*_trace_' ; then
-                symsrc=${sym#__}
-            else
-                symsrc=$sym
-            fi
-            if [ -z "$(grep -rlw $symsrc $(dirname $map) | grep -v $map)" ] ; then
-                echo "$map: $sym"
-            fi
-        done
-    done
-}
-
-orphan_symbols=$(find_orphan_symbols $@)
-if [ -n "$orphan_symbols" ] ; then
-    echo "Found only in symbol map file:"
-    echo "$orphan_symbols" | sed 's,^,\t,'
-    ret=1
-fi
-
-find_duplicate_symbols ()
-{
-    for map in $@ ; do
-        buildtools/map-list-symbol.sh $map | \
-            sort | uniq -c | grep -v " 1 $map" || true
-    done
-}
-
-duplicate_symbols=$(find_duplicate_symbols $@)
-if [ -n "$duplicate_symbols" ] ; then
-    echo "Found duplicates in symbol map file:"
-    echo "$duplicate_symbols"
-    ret=1
-fi
-
-local_miss_maps=$(grep -L 'local: \*;' $@ || true)
-if [ -n "$local_miss_maps" ] ; then
-    echo "Found maps without local catch-all:"
-    echo "$local_miss_maps"
-    ret=1
-fi
-
-find_bad_format_maps ()
-{
-    abi_version=$(cut -d'.' -f 1 ABI_VERSION)
-    next_abi_version=$((abi_version + 1))
-    for map in $@ ; do
-        cat $map | awk '
-            /^(DPDK_('$abi_version'|'$next_abi_version')|EXPERIMENTAL|INTERNAL) \{$/ { next; } # start of a section
-            /^}( DPDK_'$abi_version')?;$/ { next; } # end of a section
-            /^$/ { next; } # empty line
-            /^\t(global:|local: \*;)$/ { next; } # qualifiers
-            /^\t[a-zA-Z_0-9]*;( # WINDOWS_NO_EXPORT)?$/ { next; } # symbols
-            /^\t# added in [0-9]*\.[0-9]*$/ { next; } # version comments
-            { print $0; }' || echo $map
-    done
-}
-
-bad_format_maps=$(find_bad_format_maps $@)
-if [ -n "$bad_format_maps" ] ; then
-    echo "Found badly formatted maps:"
-    echo "$bad_format_maps"
-    ret=1
-fi
-
-find_non_versioned_maps ()
-{
-    for map in $@ ; do
-        [ $(buildtools/map-list-symbol.sh -S EXPERIMENTAL -V unset $map | wc -l) = '0' ] ||
-            echo $map
-    done
-}
-
-non_versioned_maps=$(find_non_versioned_maps $@)
-if [ -n "$non_versioned_maps" ] ; then
-    echo "Found non versioned maps:"
-    echo "$non_versioned_maps"
-    ret=1
-fi
-
-exit $ret
diff --git a/devtools/checkpatches.sh b/devtools/checkpatches.sh
index 9180c2b070..f6079d513b 100755
--- a/devtools/checkpatches.sh
+++ b/devtools/checkpatches.sh
@@ -9,7 +9,7 @@
 # - DPDK_CHECKPATCH_OPTIONS
 . $(dirname $(readlink -f $0))/load-devel-config
 
-VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
+VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.py
 
 # Enable codespell by default. This can be overwritten from a config file.
 # Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
@@ -472,7 +472,7 @@ check () { # <patch-file> <commit>
 	fi
 
 	! $verbose || printf '\nChecking API additions/removals:\n'
-	report=$($VALIDATE_NEW_API "$tmpinput")
+	report=$($VALIDATE_NEW_API --patch "$tmpinput")
 	if [ $? -ne 0 ] ; then
 		$headline_printed || print_headline "$subject"
 		printf '%s\n' "$report"
diff --git a/devtools/update-abi.sh b/devtools/update-abi.sh
deleted file mode 100755
index 45437f3c3b..0000000000
--- a/devtools/update-abi.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh -e
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-abi_version=$1
-abi_version_file="./ABI_VERSION"
-update_path="lib drivers"
-
-# check ABI version format string
-check_abi_version() {
-      echo $1 | grep -q -e "^[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}$"
-}
-
-if [ -z "$1" ]; then
-      # output to stderr
-      >&2 echo "Please provide ABI version"
-      exit 1
-fi
-
-# check version string format
-if ! check_abi_version $abi_version ; then
-      # output to stderr
-      >&2 echo "ABI version must be formatted as MAJOR.MINOR version"
-      exit 1
-fi
-
-if [ -n "$2" ]; then
-      abi_version_file=$2
-fi
-
-if [ -n "$3" ]; then
-      # drop $1 and $2
-      shift 2
-      # assign all other arguments as update paths
-      update_path=$@
-fi
-
-echo "New ABI version:" $abi_version
-echo "ABI_VERSION path:" $abi_version_file
-echo "Path to update:" $update_path
-
-echo $abi_version > $abi_version_file
-
-find $update_path -name version.map -exec \
-      devtools/update_version_map_abi.py {} \
-      $abi_version \; -print
diff --git a/devtools/update_version_map_abi.py b/devtools/update_version_map_abi.py
deleted file mode 100755
index d17b02a327..0000000000
--- a/devtools/update_version_map_abi.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2019 Intel Corporation
-
-"""
-A Python program that updates and merges all available stable ABI versions into
-one ABI version, while leaving experimental ABI exactly as it is. The intended
-ABI version is supplied via command-line parameter. This script is to be called
-from the devtools/update-abi.sh utility.
-"""
-
-import argparse
-import sys
-import re
-
-
-def __parse_map_file(f_in):
-    # match function name, followed by semicolon, followed by EOL or comments,
-    # optionally with whitespace in between each item
-    func_line_regex = re.compile(r"\s*"
-                                 r"(?P<line>"
-                                 r"(?P<func>[a-zA-Z_0-9]+)"
-                                 r"\s*"
-                                 r";"
-                                 r"\s*"
-                                 r"(?P<comment>#.+)?"
-                                 r")"
-                                 r"\s*"
-                                 r"$")
-    # match section name, followed by opening bracked, followed by EOL,
-    # optionally with whitespace in between each item
-    section_begin_regex = re.compile(r"\s*"
-                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
-                                     r"\s*"
-                                     r"{"
-                                     r"\s*"
-                                     r"$")
-    # match closing bracket, optionally followed by section name (for when we
-    # inherit from another ABI version), followed by semicolon, followed by
-    # EOL, optionally with whitespace in between each item
-    section_end_regex = re.compile(r"\s*"
-                                   r"}"
-                                   r"\s*"
-                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
-                                   r"\s*"
-                                   r";"
-                                   r"\s*"
-                                   r"$")
-
-    # for stable ABI, we don't care about which version introduced which
-    # function, we just flatten the list. there are dupes in certain files, so
-    # use a set instead of a list
-    stable_lines = set()
-    # copy experimental section as is
-    experimental_lines = []
-    # copy internal section as is
-    internal_lines = []
-    in_experimental = False
-    in_internal = False
-    has_stable = False
-
-    # gather all functions
-    for line in f_in:
-        # clean up the line
-        line = line.strip('\n').strip()
-
-        # is this an end of section?
-        match = section_end_regex.match(line)
-        if match:
-            # whatever section this was, it's not active any more
-            in_experimental = False
-            in_internal = False
-            continue
-
-        # if we're in the middle of experimental section, we need to copy
-        # the section verbatim, so just add the line
-        if in_experimental:
-            experimental_lines += [line]
-            continue
-
-        # if we're in the middle of internal section, we need to copy
-        # the section verbatim, so just add the line
-        if in_internal:
-            internal_lines += [line]
-            continue
-
-        # skip empty lines
-        if not line:
-            continue
-
-        # is this a beginning of a new section?
-        match = section_begin_regex.match(line)
-        if match:
-            cur_section = match.group("version")
-            # is it experimental?
-            in_experimental = cur_section == "EXPERIMENTAL"
-            # is it internal?
-            in_internal = cur_section == "INTERNAL"
-            if not in_experimental and not in_internal:
-                has_stable = True
-            continue
-
-        # is this a function?
-        match = func_line_regex.match(line)
-        if match:
-            stable_lines.add(match.group("line"))
-
-    return has_stable, stable_lines, experimental_lines, internal_lines
-
-
-def __generate_stable_abi(f_out, abi_major, lines):
-    # print ABI version header
-    print("DPDK_{} {{".format(abi_major), file=f_out)
-
-    # print global section if it exists
-    if lines:
-        print("\tglobal:", file=f_out)
-        # blank line
-        print(file=f_out)
-
-        # print all stable lines, alphabetically sorted
-        for line in sorted(lines):
-            print("\t{}".format(line), file=f_out)
-
-        # another blank line
-        print(file=f_out)
-
-    # print local section
-    print("\tlocal: *;", file=f_out)
-
-    # end stable version
-    print("};", file=f_out)
-
-
-def __generate_experimental_abi(f_out, lines):
-    # start experimental section
-    print("EXPERIMENTAL {", file=f_out)
-
-    # print all experimental lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __generate_internal_abi(f_out, lines):
-    # start internal section
-    print("INTERNAL {", file=f_out)
-
-    # print all internal lines as they were
-    for line in lines:
-        # don't print empty whitespace
-        if not line:
-            print("", file=f_out)
-        else:
-            print("\t{}".format(line), file=f_out)
-
-    # end section
-    print("};", file=f_out)
-
-def __main():
-    arg_parser = argparse.ArgumentParser(
-        description='Merge versions in linker version script.')
-
-    arg_parser.add_argument("map_file", type=str,
-                            help='path to linker version script file '
-                                 '(pattern: version.map)')
-    arg_parser.add_argument("abi_version", type=str,
-                            help='target ABI version (pattern: MAJOR.MINOR)')
-
-    parsed = arg_parser.parse_args()
-
-    if not parsed.map_file.endswith('version.map'):
-        print("Invalid input file: {}".format(parsed.map_file),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-
-    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
-        print("Invalid ABI version: {}".format(parsed.abi_version),
-              file=sys.stderr)
-        arg_parser.print_help()
-        sys.exit(1)
-    abi_major = parsed.abi_version.split('.')[0]
-
-    with open(parsed.map_file) as f_in:
-        has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in)
-
-    with open(parsed.map_file, 'w') as f_out:
-        need_newline = has_stable and experimental_lines
-        if has_stable:
-            __generate_stable_abi(f_out, abi_major, stable_lines)
-        if need_newline:
-            # separate sections with a newline
-            print(file=f_out)
-        if experimental_lines:
-            __generate_experimental_abi(f_out, experimental_lines)
-        if internal_lines:
-            if has_stable or experimental_lines:
-              # separate sections with a newline
-              print(file=f_out)
-            __generate_internal_abi(f_out, internal_lines)
-
-
-if __name__ == "__main__":
-    __main()
diff --git a/doc/guides/contributing/abi_policy.rst b/doc/guides/contributing/abi_policy.rst
index d96153c6b2..f03a7467ac 100644
--- a/doc/guides/contributing/abi_policy.rst
+++ b/doc/guides/contributing/abi_policy.rst
@@ -330,31 +330,14 @@ become part of a tracked ABI version.
 
 Note that marking an API as experimental is a multi step process.
 To mark an API as experimental, the symbols which are desired to be exported
-must be placed in an EXPERIMENTAL version block in the corresponding libraries'
-version map script.
+must be annotated with a RTE_EXPORT_EXPERIMENTAL_SYMBOL call in the corresponding libraries'
+sources.
 Experimental symbols must be commented so that it is clear in which DPDK
 version they were introduced.
 
-.. code-block:: none
-
-   EXPERIMENTAL {
-           global:
-
-           # added in 20.11
-           rte_foo_init;
-           rte_foo_configure;
-
-           # added in 21.02
-           rte_foo_cleanup;
-   ...
-
 Secondly, the corresponding prototypes of those exported functions (in the
 development header files), must be marked with the ``__rte_experimental`` tag
 (see ``rte_compat.h``).
-The DPDK build makefiles perform a check to ensure that the map file and the
-C code reflect the same list of symbols.
-This check can be circumvented by defining ``ALLOW_EXPERIMENTAL_API``
-during compilation in the corresponding library Makefile.
 
 In addition to tagging the code with ``__rte_experimental``,
 the doxygen markup must also contain the EXPERIMENTAL string,
diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
index 1ebc79ca3c..43e27bbd0a 100644
--- a/doc/guides/contributing/coding_style.rst
+++ b/doc/guides/contributing/coding_style.rst
@@ -1018,13 +1018,6 @@ name
 	sources are stored in a directory ``lib/xyz``, this value should
 	never be needed for new libraries.
 
-.. note::
-
-	The name value also provides the name used to find the function version
-	map file, as part of the build process, so if the directory name and
-	library names differ, the ``version.map`` file should be named
-	consistently with the library, not the directory
-
 objs
 	**Default Value = []**.
 	This variable can be used to pass to the library build some pre-built
diff --git a/doc/guides/contributing/img/patch_cheatsheet.svg b/doc/guides/contributing/img/patch_cheatsheet.svg
index 4debb07b98..a06d8a2a3b 100644
--- a/doc/guides/contributing/img/patch_cheatsheet.svg
+++ b/doc/guides/contributing/img/patch_cheatsheet.svg
@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    version="1.1"
    width="210mm"
    height="297mm"
    id="svg2985"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="patch_cheatsheet.svg">
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="patch_cheatsheet.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
   <sodipodi:namedview
      pagecolor="#ffffff"
      bordercolor="#666666"
@@ -23,18 +23,22 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:window-width="1920"
-     inkscape:window-height="1017"
+     inkscape:window-height="975"
      id="namedview274"
      showgrid="false"
      inkscape:zoom="0.89702958"
-     inkscape:cx="246.07409"
-     inkscape:cy="416.76022"
-     inkscape:window-x="1072"
-     inkscape:window-y="-8"
+     inkscape:cx="546.24732"
+     inkscape:cy="385.71749"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
      inkscape:window-maximized="1"
      inkscape:current-layer="layer1"
      inkscape:document-rotation="0"
-     inkscape:snap-grids="false" />
+     inkscape:snap-grids="false"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm" />
   <defs
      id="defs3">
     <linearGradient
@@ -906,7 +910,7 @@
              style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
              id="tspan4092-8-7-6-9-7"
              y="855.79816"
-             x="460.18405">****</tspan></text>
+             x="460.18405">***</tspan></text>
       </g>
     </g>
     <text
@@ -1132,161 +1136,126 @@
            id="tspan4092-8-6-3-1-8-4-4-55-7"
            style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
     </g>
+    <text
+       x="424.10629"
+       y="363.21423"
+       id="text4090-8"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="363.21423"
+         id="tspan4092-8"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Rebase to git  </tspan></text>
+    <text
+       x="424.10629"
+       y="393.60123"
+       id="text4090-8-5"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="393.60123"
+         id="tspan4092-8-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Checkpatch </tspan></text>
+    <text
+       x="424.10629"
+       y="424.20575"
+       id="text4090-8-5-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="424.20575"
+         id="tspan4092-8-5-5"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ ABI breakage </tspan></text>
+    <text
+       x="424.10629"
+       y="453.10339"
+       id="text4090-8-5-6-9-4"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="453.10339"
+         id="tspan4092-8-5-5-3-4"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Maintainers file</tspan></text>
+    <text
+       x="424.10629"
+       y="514.09497"
+       id="text4090-8-5-6-9-4-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="424.10629"
+         y="514.09497"
+         id="tspan4092-8-5-5-3-4-0"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Release notes</tspan></text>
+    <text
+       x="425.12708"
+       y="544.91718"
+       id="text4090-8-5-6-9-4-6-6"
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40.4213px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01053"
+       transform="scale(1.0105317,0.98957807)"><tspan
+         x="425.12708"
+         y="544.91718"
+         id="tspan4092-8-5-5-3-4-0-6"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21.2212px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.01053">+ Documentation</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
-       id="g3605">
-      <text
-         x="42.176418"
-         y="1020.4383"
-         id="text4090-8-7-8-7-6-3-8-4"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="42.176418"
-           y="1020.4383"
-           id="tspan4092-8-6-3-1-8-4-4-55"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">The version.map function names must be in alphabetical order.</tspan></text>
-      <text
-         x="30.942892"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="30.942892"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-      <text
-         x="25.247679"
-         y="1024.2014"
-         id="text4090-8-7-8-7-6-3-8-4-1-5-6"
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:0%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"><tspan
-           x="25.247679"
-           y="1024.2014"
-           id="tspan4092-8-6-3-1-8-4-4-55-7-2-8"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
-    </g>
-    <g
-       transform="matrix(1.0211743,0,0,1,25.427515,-30.749225)"
-       id="g3275">
+       transform="matrix(1.0211743,0,0,1,25.427515,-31.583927)"
+       id="g3334">
       <g
-         id="g3341">
+         id="g3267"
+         transform="translate(-13.517932,3.1531035)">
         <text
-           x="394.78601"
-           y="390.17807"
-           id="text4090-8"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="390.17807"
-             id="tspan4092-8"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Rebase to git  </tspan></text>
-        <text
-           x="394.78601"
-           y="420.24835"
-           id="text4090-8-5"
+           x="660.46729"
+           y="468.01297"
+           id="text4090-8-1-8-9-1-4-1"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="420.24835"
-             id="tspan4092-8-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Checkpatch </tspan></text>
-        <text
-           x="394.78601"
-           y="450.53394"
-           id="text4090-8-5-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="450.53394"
-             id="tspan4092-8-5-5"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ ABI breakage </tspan></text>
-        <text
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="660.46729"
+             y="468.01297"
+             id="tspan4092-8-7-6-9-7-0-7"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+      </g>
+      <text
+         x="394.78601"
+         y="483.59955"
+         id="text4090-8-5-6-9"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="513.13031"
-           id="text4090-8-5-6-9-4"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="513.13031"
-             id="tspan4092-8-5-5-3-4"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Maintainers file</tspan></text>
-        <text
+           y="483.59955"
+           id="tspan4092-8-5-5-3"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
+    </g>
+    <g
+       id="g3428"
+       transform="matrix(1.0211743,0,0,1,25.427515,-63.867847)">
+      <text
+         x="394.78601"
+         y="541.38928"
+         id="text4090-8-5-6-9-4-6-1"
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
            x="394.78601"
-           y="573.48621"
-           id="text4090-8-5-6-9-4-6"
-           xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="394.78601"
-             y="573.48621"
-             id="tspan4092-8-5-5-3-4-0"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Release notes</tspan></text>
+           y="541.38928"
+           id="tspan4092-8-5-5-3-4-0-7"
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
+      <g
+         transform="translate(-119.92979,57.949844)"
+         id="g3267-9">
         <text
-           x="395.79617"
-           y="603.98718"
-           id="text4090-8-5-6-9-4-6-6"
+           x="628.93628"
+           y="473.13675"
+           id="text4090-8-1-8-9-1-4-1-4"
            xml:space="preserve"
-           style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-             x="395.79617"
-             y="603.98718"
-             id="tspan4092-8-5-5-3-4-0-6"
-             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Documentation</tspan></text>
-        <g
-           transform="translate(0,-0.83470152)"
-           id="g3334">
-          <g
-             id="g3267"
-             transform="translate(-13.517932,3.1531035)">
-            <text
-               x="660.46729"
-               y="468.01297"
-               id="text4090-8-1-8-9-1-4-1"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="660.46729"
-                 y="468.01297"
-                 id="tspan4092-8-7-6-9-7-0-7"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
-          </g>
-          <text
-             x="394.78601"
-             y="483.59955"
-             id="text4090-8-5-6-9"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="483.59955"
-               id="tspan4092-8-5-5-3"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Update version.map</tspan></text>
-        </g>
-        <g
-           id="g3428"
-           transform="translate(0,0.88137813)">
-          <text
-             x="394.78601"
-             y="541.38928"
-             id="text4090-8-5-6-9-4-6-1"
-             xml:space="preserve"
-             style="font-style:normal;font-weight:normal;font-size:40px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-               x="394.78601"
-               y="541.38928"
-               id="tspan4092-8-5-5-3-4-0-7"
-               style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+ Doxygen</tspan></text>
-          <g
-             transform="translate(-119.92979,57.949844)"
-             id="g3267-9">
-            <text
-               x="628.93628"
-               y="473.13675"
-               id="text4090-8-1-8-9-1-4-1-4"
-               xml:space="preserve"
-               style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
-                 x="628.93628"
-                 y="473.13675"
-                 id="tspan4092-8-7-6-9-7-0-7-8"
-                 style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">***</tspan></text>
-          </g>
-        </g>
+           style="font-style:normal;font-weight:normal;font-size:25.6917px;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><tspan
+             x="628.93628"
+             y="473.13675"
+             id="tspan4092-8-7-6-9-7-0-7-8"
+             style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:11.5613px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">**</tspan></text>
       </g>
     </g>
     <text
@@ -1301,7 +1270,7 @@
          id="tspan4092-8-5-5-3-4-0-6-2-11-0"
          style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:21px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">+</tspan></text>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3595">
       <text
          x="30.942892"
@@ -1332,7 +1301,7 @@
            x="19.552465"
            y="1037.0271"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
       <text
          x="42.830166"
          y="1033.2393"
@@ -1345,7 +1314,7 @@
            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11px;line-height:125%;font-family:monospace;-inkscape-font-specification:Monospace;text-align:start;writing-mode:lr-tb;text-anchor:start">New header files must get a new page in the API docs.</tspan></text>
     </g>
     <g
-       transform="translate(1.0962334,-2.7492248)"
+       transform="translate(1.0962334,-14.749225)"
        id="g3619">
       <text
          x="42.212418"
@@ -1396,7 +1365,7 @@
            x="14.016749"
            y="1049.8527"
            id="tspan4092-8-6-3-1-8-4-4-55-7-3-9-6-5"
-           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">*</tspan></text>
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13px;line-height:125%;font-family:monospace;-inkscape-font-specification:'Monospace Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" /></text>
     </g>
     <rect
        width="196.44218"
diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index d21ee288b2..8ad6b6e715 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -160,9 +160,9 @@ Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines
 
   * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
 
-* New external functions should be added to the local ``version.map`` file. See
-  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
-  guides. New external functions should also be added in alphabetical order.
+* New external functions should be exported.
+  See the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
+  guides.
 
 * Any new API function should be used in ``/app`` test directory.
 
diff --git a/drivers/meson.build b/drivers/meson.build
index ff0cb2e02d..caee1e3b79 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -5,8 +5,6 @@ if is_ms_compiler
     subdir_done()
 endif
 
-fs = import('fs')
-
 # Defines the order of dependencies evaluation
 subdirs = [
         'common',
@@ -290,59 +288,27 @@ foreach subpath:subdirs
                 install: true)
 
         # now build the shared driver
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
-
-        if not fs.is_file(version_map)
-            if is_ms_linker
-                link_mode = 'mslinker'
-            elif is_windows
-                link_mode = 'mingw'
-            else
-                link_mode = 'gnu'
-            endif
-            version_map = custom_target(lib_name + '_map',
-                    command: [gen_version_map, '--linker', link_mode,
-                              '--abi-version', abi_version_file, '--output', '@OUTPUT@',
-                              '--source', '@INPUT@'],
-                    input: sources + sources_avx2 + sources_avx512,
-                    output: '_'.join([class, name, 'exports.map']))
-            version_map_path = version_map.full_path()
-            version_map_dep = [version_map]
-            lk_deps = [version_map]
-
-            if is_ms_linker and is_ms_compiler
-                lk_args = ['/def:' + version_map.full_path()]
-            elif is_ms_linker
-                lk_args = ['-Wl,/def:' + version_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-            endif
+        if is_ms_linker
+            link_mode = 'mslinker'
+        elif is_windows
+            link_mode = 'mingw'
         else
-            version_map_path = version_map
-            version_map_dep = []
-            lk_deps = [version_map]
-
-            if is_windows
-                if is_ms_linker
-                    def_file = custom_target(lib_name + '_def',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_exports.def'.format(lib_name))
-                    lk_deps += [def_file]
-
-                    lk_args = ['-Wl,/def:' + def_file.full_path()]
-                else
-                    mingw_map = custom_target(lib_name + '_mingw',
-                            command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                            input: version_map,
-                            output: '@0@_mingw.map'.format(lib_name))
-                    lk_deps += [mingw_map]
-
-                    lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-                endif
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
+            link_mode = 'gnu'
+        endif
+        version_map = custom_target(lib_name + '_map',
+                command: [gen_version_map, '--linker', link_mode,
+                          '--abi-version', abi_version_file, '--output', '@OUTPUT@',
+                          '--source', '@INPUT@'],
+                input: sources + sources_avx2 + sources_avx512,
+                output: '_'.join([class, name, 'exports.map']))
+        lk_deps = [version_map]
+
+        if is_ms_linker and is_ms_compiler
+            lk_args = ['/def:' + version_map.full_path()]
+        elif is_ms_linker
+            lk_args = ['-Wl,/def:' + version_map.full_path()]
+        else
+            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
         endif
 
         if not is_windows and developer_mode
@@ -350,11 +316,11 @@ foreach subpath:subdirs
             # check-symbols.sh script, using it as a
             # dependency of the .so build
             lk_deps += custom_target(lib_name + '.sym_chk',
-                    command: [check_symbols, version_map_path, '@INPUT@'],
+                    command: [check_symbols, version_map.full_path(), '@INPUT@'],
                     capture: true,
                     input: static_lib,
                     output: lib_name + '.sym_chk',
-                    depends: version_map_dep)
+                    depends: [version_map])
         endif
 
         shared_lib = shared_library(lib_name, sources_pmd_info,
diff --git a/lib/meson.build b/lib/meson.build
index 51d44bfd41..41fd98f4e9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2017-2019 Intel Corporation
 
-fs = import('fs')
 
 # process all libraries equally, as far as possible
 # "core" libs first, then others alphabetically as far as possible
@@ -289,61 +288,27 @@ foreach l:libraries
             include_directories: includes,
             dependencies: static_deps)
 
-    if not fs.is_file('@0@/@1@/version.map'.format(meson.current_source_dir(), l))
-        if is_ms_linker
-            link_mode = 'mslinker'
-        elif is_windows
-            link_mode = 'mingw'
-        else
-            link_mode = 'gnu'
-        endif
-        version_map = custom_target(libname + '_map',
-                command: [gen_version_map, '--linker', link_mode,
-                          '--abi-version', abi_version_file, '--output', '@OUTPUT@',
-                          '--source', '@INPUT@'],
-                input: sources,
-                output: '_'.join([name, 'exports.map']))
-        version_map_path = version_map.full_path()
-        version_map_dep = [version_map]
-        lk_deps = [version_map]
-
-        if is_ms_linker and is_ms_compiler
-            lk_args = ['/def:' + version_map.full_path()]
-        elif is_ms_linker
-            lk_args = ['-Wl,/def:' + version_map.full_path()]
-        else
-            lk_args = ['-Wl,--version-script=' + version_map.full_path()]
-        endif
+    if is_ms_linker
+        link_mode = 'mslinker'
+    elif is_windows
+        link_mode = 'mingw'
     else
-        version_map = '@0@/@1@/version.map'.format(meson.current_source_dir(), l)
-        version_map_path = version_map
-        version_map_dep = []
-        lk_deps = [version_map]
-        if is_ms_linker
-            def_file = custom_target(libname + '_def',
-                    command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                    input: version_map,
-                    output: '@0@_exports.def'.format(libname))
-            lk_deps += [def_file]
-
-            if is_ms_compiler
-                lk_args = ['/def:' + def_file.full_path()]
-            else
-                lk_args = ['-Wl,/def:' + def_file.full_path()]
-            endif
-        else
-            if is_windows
-                mingw_map = custom_target(libname + '_mingw',
-                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
-                        input: version_map,
-                        output: '@0@_mingw.map'.format(libname))
-                lk_deps += [mingw_map]
+        link_mode = 'gnu'
+    endif
+    version_map = custom_target(libname + '_map',
+            command: [gen_version_map, '--linker', link_mode,
+                      '--abi-version', abi_version_file, '--output', '@OUTPUT@',
+                      '--source', '@INPUT@'],
+            input: sources,
+            output: '_'.join([name, 'exports.map']))
+    lk_deps = [version_map]
 
-                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
-            else
-                lk_args = ['-Wl,--version-script=' + version_map]
-            endif
-        endif
+    if is_ms_linker and is_ms_compiler
+        lk_args = ['/def:' + version_map.full_path()]
+    elif is_ms_linker
+        lk_args = ['-Wl,/def:' + version_map.full_path()]
+    else
+        lk_args = ['-Wl,--version-script=' + version_map.full_path()]
     endif
 
     if developer_mode and not is_windows
@@ -351,11 +316,11 @@ foreach l:libraries
         # check-symbols.sh script, using it as a
         # dependency of the .so build
         lk_deps += custom_target(name + '.sym_chk',
-                command: [check_symbols, version_map_path, '@INPUT@'],
+                command: [check_symbols, version_map.full_path(), '@INPUT@'],
                 capture: true,
                 input: static_lib,
                 output: name + '.sym_chk',
-                depends: version_map_dep)
+                depends: [version_map])
     endif
 
     if not use_function_versioning or is_windows
-- 
2.49.0


^ permalink raw reply	[relevance 18%]

* [PATCH v7 8/8] eal: rework function versioning macros
  2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
  2025-04-03 16:58 18%   ` [PATCH v7 4/8] build: generate symbol maps David Marchand
  2025-04-03 16:58 18%   ` [PATCH v7 6/8] build: use dynamically generated version maps David Marchand
@ 2025-04-03 16:58 23%   ` David Marchand
  2025-04-07 13:46  0%   ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-04-03 16:58 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andremue, Tyler Retzlaff, Jasvinder Singh

For versioning symbols:
- MSVC uses pragmas on the symbol,
- GNU linker uses special asm directives,

To accommodate both GNU linker and MSVC linker, introduce new macros for
exporting and versioning symbols that will surround the whole function.

This has the advantage of hiding all the ugly details in the macros.
Now versioning a symbol is just a call to a single macro:
- RTE_VERSION_SYMBOL (resp. RTE_VERSION_EXPERIMENTAL_SYMBOL), for
  keeping an old implementation code under a versioned function (resp.
  experimental function),
- RTE_DEFAULT_SYMBOL, for declaring the new default versioned function,
  and handling the static link special case, instead of
  BIND_DEFAULT_SYMBOL + MAP_STATIC_SYMBOL,

Those macros replaces the legacy ones from rte_function_versioning.h
that were previously publicly exported.

It is important to note that, for now, function versioning is
implemented for GCC and CLANG only.
No warning will be generated for MSVC builds.

Update lib/net accordingly.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since v6:
- updated macros description in header,
- updated commitlog,

Changes since RFC v4:
- moved new macros to eal_symbol_exports.h and simply dropped legacy
  macros/header,
- added release notes update,

Changes since RFC v3:
- fixed documentation and simplified examples,

Changes since RFC v1:
- renamed and prefixed macros,
- reindented in prevision of second patch,

---
 buildtools/gen-version-map.py              |  15 +-
 doc/api/doxy-api-index.md                  |   1 -
 doc/guides/contributing/abi_versioning.rst | 192 ++++++---------------
 doc/guides/rel_notes/release_25_07.rst     |   2 +
 lib/eal/common/eal_export.h                |  63 +++++++
 lib/eal/include/rte_function_versioning.h  |  99 -----------
 lib/net/net_crc.h                          |  15 --
 lib/net/rte_net_crc.c                      |  29 +---
 8 files changed, 128 insertions(+), 288 deletions(-)
 delete mode 100644 lib/eal/include/rte_function_versioning.h

diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
index c5ea10def0..57e08a8c0f 100755
--- a/buildtools/gen-version-map.py
+++ b/buildtools/gen-version-map.py
@@ -13,10 +13,9 @@
 )
 export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
 export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
-# From rte_function_versioning.h
-ver_sym_regexp = re.compile(r"^VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
-ver_exp_sym_regexp = re.compile(r"^VERSION_SYMBOL_EXPERIMENTAL\([^,]+, ([^,]+)\)")
-default_sym_regexp = re.compile(r"^BIND_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+)\)")
+ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
 
 parser = argparse.ArgumentParser(
     description=__doc__,
@@ -68,16 +67,16 @@
             node = ABI
             symbol = export_sym_regexp.match(ln).group(1)
         elif ver_sym_regexp.match(ln):
-            abi = ver_sym_regexp.match(ln).group(2)
+            abi = ver_sym_regexp.match(ln).group(1)
             node = f"DPDK_{abi}"
-            symbol = ver_sym_regexp.match(ln).group(1)
+            symbol = ver_sym_regexp.match(ln).group(2)
         elif ver_exp_sym_regexp.match(ln):
             node = "EXPERIMENTAL"
             symbol = ver_exp_sym_regexp.match(ln).group(1)
         elif default_sym_regexp.match(ln):
-            abi = default_sym_regexp.match(ln).group(2)
+            abi = default_sym_regexp.match(ln).group(1)
             node = f"DPDK_{abi}"
-            symbol = default_sym_regexp.match(ln).group(1)
+            symbol = default_sym_regexp.match(ln).group(2)
 
         if not symbol:
             continue
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..5c425a2cb9 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -248,7 +248,6 @@ The public API headers are grouped by topics:
   [EAL config](@ref rte_eal.h),
   [common](@ref rte_common.h),
   [experimental APIs](@ref rte_compat.h),
-  [ABI versioning](@ref rte_function_versioning.h),
   [version](@ref rte_version.h)
 
 - **tests**:
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index b6b6bf64d7..2fa2b15edc 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -132,32 +132,25 @@ functionality or behavior. When that occurs, it is may be required to allow for
 backward compatibility for a time with older binaries that are dynamically
 linked to the DPDK.
 
-To support backward compatibility the ``rte_function_versioning.h``
+To support backward compatibility the ``eal_export.h``
 header file provides macros to use when updating exported functions. These
 macros allow multiple versions of a symbol to exist in a shared
 library so that older binaries need not be immediately recompiled.
 
-The macros exported are:
+The macros are:
 
-* ``VERSION_SYMBOL(b, e, n)``: Creates a symbol version table entry binding
-  versioned symbol ``b@DPDK_n`` to the internal function ``be``.
+* ``RTE_VERSION_SYMBOL(ver, type, name, args)``: Creates a symbol version table
+  entry binding symbol ``<name>@DPDK_<ver>`` to the internal function name
+  ``<name>_v<ver>``.
 
-* ``BIND_DEFAULT_SYMBOL(b, e, n)``: Creates a symbol version entry instructing
-  the linker to bind references to symbol ``b`` to the internal symbol
-  ``be``.
+* ``RTE_DEFAULT_SYMBOL(ver, type, name, args)``: Creates a symbol version entry
+  instructing the linker to bind references to symbol ``<name>`` to the internal
+  symbol ``<name>_v<ver>``.
 
-* ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
-  fully qualified function ``p``, so that if a symbol becomes versioned, it
-  can still be mapped back to the public symbol name.
-
-* ``__vsym``:  Annotation to be used in a declaration of the internal symbol
-  ``be`` to signal that it is being used as an implementation of a particular
-  version of symbol ``b``.
-
-* ``VERSION_SYMBOL_EXPERIMENTAL(b, e)``: Creates a symbol version table entry
-  binding versioned symbol ``b@EXPERIMENTAL`` to the internal function ``be``.
-  The macro is used when a symbol matures to become part of the stable ABI, to
-  provide an alias to experimental until the next major ABI version.
+* ``RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args)``:  Similar to RTE_VERSION_SYMBOL
+  but for experimental API symbols. The macro is used when a symbol matures
+  to become part of the stable ABI, to provide an alias to experimental
+  until the next major ABI version.
 
 .. _example_abi_macro_usage:
 
@@ -176,8 +169,8 @@ Assume we have a function as follows
   * manipulate
   */
  RTE_EXPORT_SYMBOL(rte_acl_create)
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param)
+ int
+ rte_acl_create(struct rte_acl_param *param)
  {
         ...
  }
@@ -195,8 +188,8 @@ private, is safe), but it also requires modifying the code as follows
   * manipulate
   */
  RTE_EXPORT_SYMBOL(rte_acl_create)
- struct rte_acl_ctx *
- rte_acl_create(const struct rte_acl_param *param, int debug)
+ int
+ rte_acl_create(struct rte_acl_param *param, int debug)
  {
         ...
  }
@@ -215,87 +208,48 @@ application was linked to it.
 
 We need to specify in the code which function maps to the rte_acl_create
 symbol at which versions.  First, at the site of the initial symbol definition,
-we need to update the function so that it is uniquely named, and not in conflict
-with the public symbol name
+we wrap the function with ``RTE_VERSION_SYMBOL``, passing the current ABI version,
+the function return type, the function name and its arguments.
 
 .. code-block:: c
 
  -RTE_EXPORT_SYMBOL(rte_acl_create)
- -struct rte_acl_ctx *
- -rte_acl_create(const struct rte_acl_param *param)
- +struct rte_acl_ctx * __vsym
- +rte_acl_create_v21(const struct rte_acl_param *param)
+ -int
+ -rte_acl_create(struct rte_acl_param *param)
+ +RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
  {
         size_t sz;
         struct rte_acl_ctx *ctx;
         ...
 
-Note that the base name of the symbol was kept intact, as this is conducive to
-the macros used for versioning symbols and we have annotated the function as
-``__vsym``, an implementation of a versioned symbol . That is our next step,
-mapping this new symbol name to the initial symbol name at version node 21.
-Immediately after the function, we add the VERSION_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   VERSION_SYMBOL(rte_acl_create, _v21, 21);
-
-Remembering to also add the rte_function_versioning.h header to the requisite c
-file where these changes are being made. The macro instructs the linker to
-create a new symbol ``rte_acl_create@DPDK_21``, which matches the symbol created
-in older builds, but now points to the above newly named function. We have now
-mapped the original rte_acl_create symbol to the original function (but with a
-new name).
+The macro instructs the linker to create a new symbol ``rte_acl_create@DPDK_21``,
+which matches the symbol created in older builds,
+but now points to the above newly named function ``rte_acl_create_v21``.
+We have now mapped the original rte_acl_create symbol to the original function
+(but with a new name).
 
 Please see the section :ref:`Enabling versioning macros
 <enabling_versioning_macros>` to enable this macro in the meson/ninja build.
-Next, we need to create the new ``v22`` version of the symbol. We create a new
-function name, with the ``v22`` suffix, and implement it appropriately.
+
+Next, we need to create the new version of the symbol. We create a new
+function name and implement it appropriately, then wrap it in a call to ``RTE_DEFAULT_SYMBOL``.
 
 .. code-block:: c
 
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
    {
-        struct rte_acl_ctx *ctx = rte_acl_create_v21(param);
+        int ret = rte_acl_create_v21(param);
 
-        ctx->debug = debug;
+        if (debug) {
+        ...
+        }
 
-        return ctx;
+        return ret;
    }
 
-This code serves as our new API call. Its the same as our old call, but adds the
-new parameter in place. Next we need to map this function to the new default
-symbol ``rte_acl_create@DPDK_22``. To do this, immediately after the function,
-we add the BIND_DEFAULT_SYMBOL macro.
-
-.. code-block:: c
-
-   #include <rte_function_versioning.h>
-
-   ...
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
-
 The macro instructs the linker to create the new default symbol
-``rte_acl_create@DPDK_22``, which points to the above newly named function.
-
-We finally modify the prototype of the call in the public header file,
-such that it contains both versions of the symbol and the public API.
-
-.. code-block:: c
-
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v21(const struct rte_acl_param *param);
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug);
-
+``rte_acl_create@DPDK_22``, which points to the function named ``rte_acl_create_v22``
+(declared by the macro).
 
 And that's it. On the next shared library rebuild, there will be two versions of rte_acl_create,
 an old DPDK_21 version, used by previously built applications, and a new DPDK_22 version,
@@ -304,43 +258,10 @@ used by newly built applications.
 .. note::
 
    **Before you leave**, please take care reviewing the sections on
-   :ref:`mapping static symbols <mapping_static_symbols>`,
    :ref:`enabling versioning macros <enabling_versioning_macros>`,
    and :ref:`ABI deprecation <abi_deprecation>`.
 
 
-.. _mapping_static_symbols:
-
-Mapping static symbols
-______________________
-
-Now we've taken what was a public symbol, and duplicated it into two uniquely
-and differently named symbols. We've then mapped each of those back to the
-public symbol ``rte_acl_create`` with different version tags. This only applies
-to dynamic linking, as static linking has no notion of versioning. That leaves
-this code in a position of no longer having a symbol simply named
-``rte_acl_create`` and a static build will fail on that missing symbol.
-
-To correct this, we can simply map a function of our choosing back to the public
-symbol in the static build with the ``MAP_STATIC_SYMBOL`` macro.  Generally the
-assumption is that the most recent version of the symbol is the one you want to
-map.  So, back in the C file where, immediately after ``rte_acl_create_v22`` is
-defined, we add this
-
-
-.. code-block:: c
-
-   struct rte_acl_ctx * __vsym
-   rte_acl_create_v22(const struct rte_acl_param *param, int debug)
-   {
-        ...
-   }
-   MAP_STATIC_SYMBOL(struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param, int debug), rte_acl_create_v22);
-
-That tells the compiler that, when building a static library, any calls to the
-symbol ``rte_acl_create`` should be linked to ``rte_acl_create_v22``
-
-
 .. _enabling_versioning_macros:
 
 Enabling versioning macros
@@ -384,8 +305,8 @@ Assume we have an experimental function ``rte_acl_create`` as follows:
     */
    RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_acl_create)
    __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
    ...
    }
@@ -400,8 +321,8 @@ When we promote the symbol to the stable ABI, we simply strip the
     * manipulate
     */
    RTE_EXPORT_SYMBOL(rte_acl_create)
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   int
+   rte_acl_create(struct rte_acl_param *param)
    {
           ...
    }
@@ -416,33 +337,20 @@ and ``DPDK_22`` version nodes.
 .. code-block:: c
 
    #include <rte_compat.h>;
-   #include <rte_function_versioning.h>
 
    /*
     * Create an acl context object for apps to
     * manipulate
     */
-   RTE_EXPORT_SYMBOL(rte_acl_create)
-   struct rte_acl_ctx *
-   rte_acl_create(const struct rte_acl_param *param)
+   RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param))
    {
    ...
    }
 
-   __rte_experimental
-   struct rte_acl_ctx *
-   rte_acl_create_e(const struct rte_acl_param *param)
-   {
-      return rte_acl_create(param);
-   }
-   VERSION_SYMBOL_EXPERIMENTAL(rte_acl_create, _e);
-
-   struct rte_acl_ctx *
-   rte_acl_create_v22(const struct rte_acl_param *param)
+   RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_acl_create, (struct rte_acl_param *param))
    {
       return rte_acl_create(param);
    }
-   BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
 
 .. _abi_deprecation:
 
@@ -458,10 +366,10 @@ Next remove the corresponding versioned export.
 
 .. code-block:: c
 
- -VERSION_SYMBOL(rte_acl_create, _v21, 21);
+ -RTE_VERSION_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param))
 
 
-Note that the internal function definition could also be removed, but its used
+Note that the internal function definition must also be removed, but it is used
 in our example by the newer version ``v22``, so we leave it in place and declare
 it as static. This is a coding style choice.
 
@@ -476,18 +384,16 @@ of a major ABI version. If a version node completely specifies an API, then
 removing part of it, typically makes it incomplete. In those cases it is better
 to remove the entire node.
 
- };
-
-Any uses of BIND_DEFAULT_SYMBOL that pointed to the old node should be
+Any uses of RTE_DEFAULT_SYMBOL that pointed to the old node should be
 updated to point to the new version node in any header files for all affected
 symbols.
 
 .. code-block:: c
 
- -BIND_DEFAULT_SYMBOL(rte_acl_create, _v21, 21);
- +BIND_DEFAULT_SYMBOL(rte_acl_create, _v22, 22);
+ -RTE_DEFAULT_SYMBOL(21, int, rte_acl_create, (struct rte_acl_param *param, int debug))
+ +RTE_DEFAULT_SYMBOL(22, int, rte_acl_create, (struct rte_acl_param *param, int debug))
 
-Lastly, any VERSION_SYMBOL macros that point to the old version nodes
+Lastly, any RTE_VERSION_SYMBOL macros that point to the old version nodes
 should be removed, taking care to preserve any code that is shared
 with the new version node.
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..093b85d206 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -68,6 +68,8 @@ Removed Items
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* eal: Removed the ``rte_function_versioning.h`` header from the exported headers.
+
 
 API Changes
 -----------
diff --git a/lib/eal/common/eal_export.h b/lib/eal/common/eal_export.h
index 7904ed851c..7e47d34064 100644
--- a/lib/eal/common/eal_export.h
+++ b/lib/eal/common/eal_export.h
@@ -5,6 +5,8 @@
 #ifndef EAL_EXPORT_H
 #define EAL_EXPORT_H
 
+#include <rte_common.h>
+
 /* Internal macros for exporting symbols, used by the build system.
  * For RTE_EXPORT_EXPERIMENTAL_SYMBOL, ver indicates the
  * version this symbol was introduced in.
@@ -13,4 +15,65 @@
 #define RTE_EXPORT_INTERNAL_SYMBOL(symbol)
 #define RTE_EXPORT_SYMBOL(symbol)
 
+#if !defined(RTE_USE_FUNCTION_VERSIONING) && (defined(RTE_CC_GCC) || defined(RTE_CC_CLANG))
+#define VERSIONING_WARN RTE_PRAGMA_WARNING(Use of function versioning disabled. \
+	Is "use_function_versioning=true" in meson.build?)
+#else
+#define VERSIONING_WARN
+#endif
+
+/*
+ * The macros below provide backwards compatibility when updating exported functions.
+ * When a symbol is exported from a library to provide an API, it also provides a
+ * calling convention (ABI) that is embodied in its name, return type,
+ * arguments, etc.  On occasion that function may need to change to accommodate
+ * new functionality, behavior, etc.  When that occurs, it is desirable to
+ * allow for backwards compatibility for a time with older binaries that are
+ * dynamically linked to the dpdk.
+ */
+
+#ifdef RTE_BUILD_SHARED_LIB
+
+/*
+ * Create a symbol version table entry binding symbol <name>@DPDK_<ver> to the internal
+ * function name <name>_v<ver>.
+ */
+#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+/*
+ * Similar to RTE_VERSION_SYMBOL but for experimental API symbols.
+ * This is mainly used for keeping compatibility for symbols that get promoted to stable ABI.
+ */
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
+__rte_used type name ## _exp args; \
+type name ## _exp args
+
+/*
+ * Create a symbol version entry instructing the linker to bind references to
+ * symbol <name> to the internal symbol <name>_v<ver>.
+ */
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver)); \
+__rte_used type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+#else /* !RTE_BUILD_SHARED_LIB */
+
+#define RTE_VERSION_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+type name ## _v ## ver args; \
+type name ## _v ## ver args
+
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
+type name ## _exp args; \
+type name ## _exp args
+
+#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
+type name args
+
+#endif /* RTE_BUILD_SHARED_LIB */
+
 #endif /* EAL_EXPORT_H */
diff --git a/lib/eal/include/rte_function_versioning.h b/lib/eal/include/rte_function_versioning.h
deleted file mode 100644
index eb6dd2bc17..0000000000
--- a/lib/eal/include/rte_function_versioning.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2015 Neil Horman <nhorman@tuxdriver.com>.
- * All rights reserved.
- */
-
-#ifndef _RTE_FUNCTION_VERSIONING_H_
-#define _RTE_FUNCTION_VERSIONING_H_
-#include <rte_common.h>
-
-#ifndef RTE_USE_FUNCTION_VERSIONING
-#error Use of function versioning disabled, is "use_function_versioning=true" in meson.build?
-#endif
-
-#ifdef RTE_BUILD_SHARED_LIB
-
-/*
- * Provides backwards compatibility when updating exported functions.
- * When a symbol is exported from a library to provide an API, it also provides a
- * calling convention (ABI) that is embodied in its name, return type,
- * arguments, etc.  On occasion that function may need to change to accommodate
- * new functionality, behavior, etc.  When that occurs, it is desirable to
- * allow for backwards compatibility for a time with older binaries that are
- * dynamically linked to the dpdk.  To support that, the __vsym and
- * VERSION_SYMBOL macros are created.  They, in conjunction with the
- * version.map file for a given library allow for multiple versions of
- * a symbol to exist in a shared library so that older binaries need not be
- * immediately recompiled.
- *
- * Refer to the guidelines document in the docs subdirectory for details on the
- * use of these macros
- */
-
-/*
- * Macro Parameters:
- * b - function base name
- * e - function version extension, to be concatenated with base name
- * n - function symbol version string to be applied
- * f - function prototype
- * p - full function symbol name
- */
-
-/*
- * VERSION_SYMBOL
- * Creates a symbol version table entry binding symbol <b>@DPDK_<n> to the internal
- * function name <b><e>
- */
-#define VERSION_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
-
-/*
- * VERSION_SYMBOL_EXPERIMENTAL
- * Creates a symbol version table entry binding the symbol <b>@EXPERIMENTAL to the internal
- * function name <b><e>. The macro is used when a symbol matures to become part of the stable ABI,
- * to provide an alias to experimental for some time.
- */
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@EXPERIMENTAL")
-
-/*
- * BIND_DEFAULT_SYMBOL
- * Creates a symbol version entry instructing the linker to bind references to
- * symbol <b> to the internal symbol <b><e>
- */
-#define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
-
-/*
- * __vsym
- * Annotation to be used in declaration of the internal symbol <b><e> to signal
- * that it is being used as an implementation of a particular version of symbol
- * <b>.
- */
-#define __vsym __rte_used
-
-/*
- * MAP_STATIC_SYMBOL
- * If a function has been bifurcated into multiple versions, none of which
- * are defined as the exported symbol name in the map file, this macro can be
- * used to alias a specific version of the symbol to its exported name.  For
- * example, if you have 2 versions of a function foo_v1 and foo_v2, where the
- * former is mapped to foo@DPDK_1 and the latter is mapped to foo@DPDK_2 when
- * building a shared library, this macro can be used to map either foo_v1 or
- * foo_v2 to the symbol foo when building a static library, e.g.:
- * MAP_STATIC_SYMBOL(void foo(), foo_v2);
- */
-#define MAP_STATIC_SYMBOL(f, p)
-
-#else
-/*
- * No symbol versioning in use
- */
-#define VERSION_SYMBOL(b, e, n)
-#define VERSION_SYMBOL_EXPERIMENTAL(b, e)
-#define __vsym
-#define BIND_DEFAULT_SYMBOL(b, e, n)
-#define MAP_STATIC_SYMBOL(f, p) f __attribute__((alias(RTE_STR(p))))
-/*
- * RTE_BUILD_SHARED_LIB=n
- */
-#endif
-
-#endif /* _RTE_FUNCTION_VERSIONING_H_ */
diff --git a/lib/net/net_crc.h b/lib/net/net_crc.h
index 4930e2f0b3..320b0edca8 100644
--- a/lib/net/net_crc.h
+++ b/lib/net/net_crc.h
@@ -7,21 +7,6 @@
 
 #include "rte_net_crc.h"
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg);
-
-struct rte_net_crc *
-rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len, enum rte_net_crc_type type);
-
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len);
-
 /*
  * Different implementations of CRC
  */
diff --git a/lib/net/rte_net_crc.c b/lib/net/rte_net_crc.c
index a762c8eef0..b8b0ba5127 100644
--- a/lib/net/rte_net_crc.c
+++ b/lib/net/rte_net_crc.c
@@ -11,7 +11,6 @@
 #include <rte_net_crc.h>
 #include <rte_log.h>
 #include <rte_vect.h>
-#include <rte_function_versioning.h>
 #include <rte_malloc.h>
 
 #include "net_crc.h"
@@ -346,8 +345,7 @@ handlers_init(enum rte_net_crc_alg alg)
 
 /* Public API */
 
-void
-rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
+RTE_VERSION_SYMBOL(25, void, rte_net_crc_set_alg, (enum rte_net_crc_alg alg))
 {
 	handlers = NULL;
 	if (max_simd_bitwidth == 0)
@@ -374,10 +372,9 @@ rte_net_crc_set_alg_v25(enum rte_net_crc_alg alg)
 	if (handlers == NULL)
 		handlers = handlers_scalar;
 }
-VERSION_SYMBOL(rte_net_crc_set_alg, _v25, 25);
 
-struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
-	enum rte_net_crc_type type)
+RTE_DEFAULT_SYMBOL(26, struct rte_net_crc *, rte_net_crc_set_alg, (enum rte_net_crc_alg alg,
+	enum rte_net_crc_type type))
 {
 	uint16_t max_simd_bitwidth;
 	struct rte_net_crc *crc;
@@ -415,10 +412,6 @@ struct rte_net_crc *rte_net_crc_set_alg_v26(enum rte_net_crc_alg alg,
 	}
 	return crc;
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_set_alg, _v26, 26);
-MAP_STATIC_SYMBOL(struct rte_net_crc *rte_net_crc_set_alg(
-	enum rte_net_crc_alg alg, enum rte_net_crc_type type),
-	rte_net_crc_set_alg_v26);
 
 RTE_EXPORT_SYMBOL(rte_net_crc_free)
 void rte_net_crc_free(struct rte_net_crc *crc)
@@ -426,10 +419,8 @@ void rte_net_crc_free(struct rte_net_crc *crc)
 	rte_free(crc);
 }
 
-uint32_t
-rte_net_crc_calc_v25(const void *data,
-	uint32_t data_len,
-	enum rte_net_crc_type type)
+RTE_VERSION_SYMBOL(25, uint32_t, rte_net_crc_calc, (const void *data, uint32_t data_len,
+	enum rte_net_crc_type type))
 {
 	uint32_t ret;
 	rte_net_crc_handler f_handle;
@@ -439,18 +430,12 @@ rte_net_crc_calc_v25(const void *data,
 
 	return ret;
 }
-VERSION_SYMBOL(rte_net_crc_calc, _v25, 25);
 
-uint32_t
-rte_net_crc_calc_v26(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len)
+RTE_DEFAULT_SYMBOL(26, uint32_t, rte_net_crc_calc, (const struct rte_net_crc *ctx,
+	const void *data, const uint32_t data_len))
 {
 	return handlers_dpdk26[ctx->alg].f[ctx->type](data, data_len);
 }
-BIND_DEFAULT_SYMBOL(rte_net_crc_calc, _v26, 26);
-MAP_STATIC_SYMBOL(uint32_t rte_net_crc_calc(const struct rte_net_crc *ctx,
-	const void *data, const uint32_t data_len),
-	rte_net_crc_calc_v26);
 
 /* Call initialisation helpers for all crc algorithm handlers */
 RTE_INIT(rte_net_crc_init)
-- 
2.49.0


^ permalink raw reply	[relevance 23%]

* Re: [RFC PATCH 1/4] ethdev: add support to provide link type
  @ 2025-04-04  0:46  3% ` Stephen Hemminger
  2025-04-15  7:08  3%   ` Nithin Dabilpuram
  0 siblings, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-04-04  0:46 UTC (permalink / raw)
  To: Nithin Dabilpuram; +Cc: Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev

On Thu, 3 Apr 2025 12:38:34 +0530
Nithin Dabilpuram <ndabilpuram@marvell.com> wrote:

>  /**
>   * A structure used to retrieve link-level information of an Ethernet port.
>   */
> @@ -341,6 +354,7 @@ struct rte_eth_link {
>  			uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
>  			uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
>  			uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
> +			uint16_t link_type    : 5;  /**< RTE_ETH_LINK_TYPE_* */
>  		};
>  	};
>  };

Seems like an ABI break, and not sure that all drivers will fill those bits with zero now.

^ permalink raw reply	[relevance 3%]

* Re: [PATCH v7 0/8] Symbol versioning and export rework
  2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
                     ` (2 preceding siblings ...)
  2025-04-03 16:58 23%   ` [PATCH v7 8/8] eal: rework function versioning macros David Marchand
@ 2025-04-07 13:46  0%   ` David Marchand
  3 siblings, 0 replies; 153+ results
From: David Marchand @ 2025-04-07 13:46 UTC (permalink / raw)
  To: David Marchand, Stephen Hemminger, Ajit Khaparde,
	bruce.richardson, Jerin Jacob Kollanukkaran, Raslan Darawsheh,
	Maxime Coquelin, Akhil Goyal
  Cc: dev, thomas, andremue

On Thu, Apr 3, 2025 at 6:59 PM David Marchand <david.marchand@redhat.com> wrote:
>
> So far, each DPDK library (or driver) exposing symbols in an ABI had to
> maintain a version.map and use some macros for symbol versioning,
> specially crafted with the GNU linker in mind.
>
> This series proposes to rework the whole principle, and instead rely on
> marking the symbol exports in the source code itself, then let it to the
> build framework to produce a version script adapted to the linker in use
> (think GNU linker vs MSVC linker).
>
> This greatly simplifies versioning symbols: a developer does not need to
> know anything about version.map, or that a versioned symbol must be
> renamed with _v26, annotated with __vsym, exported in a header etc...
>
> Checking symbol maps becomes unnecessary since generated by the build
> framework.
>
> Updating to a new ABI is just a matter of bumping the value in
> ABI_VERSION.
>
>
>
> --
> David Marchand
>
> Changes since v6:
> - used argparse in python scripts,
> - renamed eal_symbol_exports.h as eal_export.h,
> - renamed some base symbols export files,
>
> Changes since v5:
> - fixed Windows symbol exports for net/mlx5,
>
> Changes since RFC v4:
> - rebased on main, now that Bruce series is merged,
> - the export macros header has been moved to lib/eal/common/
>   and its inclusion is now mandatory (rather than an implicit -include),
> - reordered patches: symbol versioning is touched last and merged
>   in the export header (replacing the legacy rte_function_versioning.h),
>
> Changes since RFC v3:
> - fixed/simplified documentation,
> - rebased on top of Bruce series for common handling of AVX sources,
>
> Changes since RFC v2:
> - updated RTE_VERSION_SYMBOL() (and friends) so that only the fonction
>   signature is enclosed in the macro,
> - dropped invalid exports for some dead symbols or inline helpers,
> - updated documentation and tooling,
> - converted the whole tree (via a local script of mine),
>
> David Marchand (8):
>   lib: remove incorrect exported symbols
>   drivers: remove incorrect exported symbols
>   buildtools: display symbols version from map
>   build: generate symbol maps
>   build: mark exported symbols
>   build: use dynamically generated version maps
>   build: remove static version maps
>   eal: rework function versioning macros

Series applied.

Heads up to subtree maintainers.
TL;DR: please rebase your trees.

If you had changes on version.map in your trees, you'll need to
convert the new symbols and use RTE_EXPORT_SYMBOL* macros.
If you have some doubt, ping me and I'll help.


-- 
David Marchand


^ permalink raw reply	[relevance 0%]

* [PATCH 1/3] eventdev: introduce event vector adapter
  @ 2025-04-10 18:00  1%     ` pbhagavatula
  2025-05-28 16:58  0%       ` Jerin Jacob
    1 sibling, 1 reply; 153+ results
From: pbhagavatula @ 2025-04-10 18:00 UTC (permalink / raw)
  To: jerinj, pravin.pathak, hemant.agrawal, sachin.saxena,
	mattias.ronnblom, liangma, peter.mccarthy, harry.van.haaren,
	erik.g.carrillo, abhinandan.gujjar, amitprakashs,
	s.v.naga.harish.k, anatoly.burakov, Bruce Richardson
  Cc: dev, Pavan Nikhilesh

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 53145 bytes --]

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

The event vector adapter supports offloading creation of
event vectors by vectorizing objects (mbufs/ptrs/u64s).
Applications can create a vector adapter associated with
an event queue and enqueue objects to be vectorized.
When the vector reaches the configured size or when the timeout
is reached, the vector adapter will enqueue the vector to the
event queue.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 config/rte_config.h                           |   1 +
 doc/api/doxy-api-index.md                     |   1 +
 doc/guides/eventdevs/features/default.ini     |   7 +
 .../eventdev/event_vector_adapter.rst         | 208 ++++++++
 doc/guides/prog_guide/eventdev/eventdev.rst   |  10 +-
 doc/guides/prog_guide/eventdev/index.rst      |   1 +
 doc/guides/rel_notes/release_25_07.rst        |   6 +
 lib/eventdev/event_vector_adapter_pmd.h       |  85 ++++
 lib/eventdev/eventdev_pmd.h                   |  36 ++
 lib/eventdev/meson.build                      |   3 +
 lib/eventdev/rte_event_vector_adapter.c       | 472 +++++++++++++++++
 lib/eventdev/rte_event_vector_adapter.h       | 481 ++++++++++++++++++
 lib/eventdev/rte_eventdev.c                   |  22 +
 lib/eventdev/rte_eventdev.h                   |  10 +
 14 files changed, 1338 insertions(+), 5 deletions(-)
 create mode 100644 doc/guides/prog_guide/eventdev/event_vector_adapter.rst
 create mode 100644 lib/eventdev/event_vector_adapter_pmd.h
 create mode 100644 lib/eventdev/rte_event_vector_adapter.c
 create mode 100644 lib/eventdev/rte_event_vector_adapter.h

diff --git a/config/rte_config.h b/config/rte_config.h
index 86897de75e..9535c48d81 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -92,6 +92,7 @@
 #define RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_DMA_ADAPTER_MAX_INSTANCE 32
+#define RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE 32
 
 /* rawdev defines */
 #define RTE_RAWDEV_MAX_DEVS 64
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb9..a11bd59526 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -30,6 +30,7 @@ The public API headers are grouped by topics:
   [event_timer_adapter](@ref rte_event_timer_adapter.h),
   [event_crypto_adapter](@ref rte_event_crypto_adapter.h),
   [event_dma_adapter](@ref rte_event_dma_adapter.h),
+  [event_vector_adapter](@ref rte_event_vector_adapter.h),
   [rawdev](@ref rte_rawdev.h),
   [metrics](@ref rte_metrics.h),
   [bitrate](@ref rte_bitrate.h),
diff --git a/doc/guides/eventdevs/features/default.ini b/doc/guides/eventdevs/features/default.ini
index fa24ba38b4..9fb68f946e 100644
--- a/doc/guides/eventdevs/features/default.ini
+++ b/doc/guides/eventdevs/features/default.ini
@@ -64,3 +64,10 @@ internal_port_vchan_ev_bind =
 [Timer adapter Features]
 internal_port              =
 periodic                   =
+
+;
+; Features of a default Vector adapter
+;
+[Vector adapter Features]
+internal_port              =
+sov_eov                    =
diff --git a/doc/guides/prog_guide/eventdev/event_vector_adapter.rst b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
new file mode 100644
index 0000000000..e257552d22
--- /dev/null
+++ b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
@@ -0,0 +1,208 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2025 Marvell International Ltd.
+
+Event Vector Adapter Library
+============================
+
+The Event Vector Adapter library extends the event-driven model by introducing
+a mechanism to aggregate multiple 8B objects (e.g., mbufs, u64s) into a single
+vector event and enqueue it to an event queue. It provides an API to create,
+configure, and manage vector adapters.
+
+The Event Vector Adapter library is designed to interface with hardware or
+software implementations of vector aggregation. It queries an eventdev PMD
+to determine the appropriate implementation.
+
+Examples of using the API are presented in the `API Overview`_ and
+`Processing Vector Events`_ sections.
+
+.. _vector_event:
+
+Vector Event
+~~~~~~~~~~~~
+
+A vector event is enqueued in the event device when the vector adapter
+reaches the configured vector size or timeout. The event device uses the
+attributes configured by the application when scheduling it.
+
+Fallback Behavior
+~~~~~~~~~~~~~~~~~
+
+If the vector adapter cannot aggregate objects into a vector event, it
+enqueues the objects as single events with fallback event properties configured
+by the application.
+
+Timeout and Size
+~~~~~~~~~~~~~~~~
+
+The vector adapter aggregates objects until the configured vector size or
+timeout is reached. If the timeout is reached before the minimum vector size
+is met, the adapter enqueues the objects as single events with fallback event
+properties configured by the application.
+
+API Overview
+------------
+
+This section introduces the Event Vector Adapter API, showing how to create
+and configure a vector adapter and use it to manage vector events.
+
+From a high level, the setup steps are:
+
+* rte_event_vector_adapter_create()
+
+And to enqueue and manage vectors:
+
+* rte_event_vector_adapter_enqueue()
+* rte_event_vector_adapter_stats_get()
+
+Create and Configure a Vector Adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To create a vector adapter instance, initialize an ``rte_event_vector_adapter_conf``
+struct with the desired values, and pass it to ``rte_event_vector_adapter_create()``.
+
+.. code-block:: c
+
+	const struct rte_event_vector_adapter_conf adapter_config = {
+		.event_dev_id = event_dev_id,
+		.socket_id = rte_socket_id(),
+		.ev = {
+			.queue_id = event_queue_id,
+			.sched_type = RTE_SCHED_TYPE_ATOMIC,
+			.priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
+			.event_type = RTE_EVENT_TYPE_VECTOR | RTE_EVENT_TYPE_CPU,
+		},
+		.ev_fallback = {
+			.event_type = RTE_EVENT_TYPE_CPU,
+		},
+		.vector_sz = 64,
+		.vector_timeout_ns = 1000000, // 1ms
+		.vector_mp = vector_mempool,
+	};
+
+	struct rte_event_vector_adapter *adapter;
+	adapter = rte_event_vector_adapter_create(&adapter_config);
+
+	if (adapter == NULL) { ... }
+
+Before creating an instance of a vector adapter, the application should create
+and configure an event device along with its event ports. Based on the event
+device's capability, it might require creating an additional event port to be
+used by the vector adapter. If required, the ``rte_event_vector_adapter_create()``
+function will use a default method to configure an event port.
+
+If the application desires finer control of event port allocation and setup,
+it can use the ``rte_event_vector_adapter_create_ext()`` function. This function
+is passed a callback function that will be invoked if the adapter needs to
+create an event port, giving the application the opportunity to control how
+it is done.
+
+Retrieve Vector Adapter Contextual Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The vector adapter implementation may have constraints on vector size or
+timeout based on the given event device or system. The application can retrieve
+these constraints using ``rte_event_vector_adapter_info_get()``. This function
+returns an ``rte_event_vector_adapter_info`` struct, which contains the following
+members:
+
+* ``max_vector_adapters_per_event_queue`` - Maximum number of vector adapters
+  configurable per event queue.
+* ``min_vector_sz`` - Minimum vector size configurable.
+* ``max_vector_sz`` - Maximum vector size configurable.
+* ``min_vector_timeout_ns`` - Minimum vector timeout configurable.
+* ``max_vector_timeout_ns`` - Maximum vector timeout configurable.
+* ``log2_sz`` - Vector size should be a power of 2.
+
+Enqueuing Objects to the Vector Adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once a vector adapter has been created, the application can enqueue objects
+to it using ``rte_event_vector_adapter_enqueue()``. The adapter will aggregate
+the objects into a vector event based on the configured size and timeout.
+
+.. code-block:: c
+
+	uint64_t objs[32];
+	uint16_t num_elem = 32;
+	uint64_t flags = 0;
+
+	int ret = rte_event_vector_adapter_enqueue(adapter, objs, num_elem, flags);
+	if (ret < 0) { ... }
+
+The application can use the ``RTE_EVENT_VECTOR_ENQ_SOV`` and ``RTE_EVENT_VECTOR_ENQ_EOV``
+flags to control the start and end of vector aggregation.
+
+The ``RTE_EVENT_VECTOR_ENQ_SOV`` flag marks the beginning of a vector and applies
+to the first pointer in the enqueue operation. Any incomplete vectors will be
+enqueued to the event device.
+
+The ``RTE_EVENT_VECTOR_ENQ_EOV`` flag marks the end of a vector and applies to
+the last pointer in the enqueue operation. The vector is enqueued to the event
+device even if the configured vector size is not reached.
+
+If both flags are set, the adapter will form a new vector event with the given
+objects and enqueue it to the event device.
+
+The ``RTE_EVENT_VECTOR_ENQ_FLUSH`` flag can be used to flush any remaining
+objects in the vector adapter. This is useful when the application needs to
+ensure that all objects are processed, even if the configured vector size or
+timeout is not reached. An enqueue call with this flag set will not handle any
+objects and will return 0.
+
+Processing Vector Events
+------------------------
+
+Once a vector event has been enqueued in the event device, the application will
+subsequently dequeue it from the event device. The application can process the
+vector event and its aggregated objects as needed:
+
+.. code-block:: c
+
+	void
+	event_processing_loop(...)
+	{
+		while (...) {
+			/* Receive events from the configured event port. */
+			rte_event_dequeue_burst(event_dev_id, event_port, &ev, 1, 0);
+			...
+			switch(ev.event_type) {
+				...
+				case RTE_EVENT_TYPE_VECTOR:
+					process_vector_event(ev);
+					...
+					break;
+			}
+		}
+	}
+
+	void
+	process_vector_event(struct rte_event ev)
+	{
+		struct rte_event_vector *vector = ev.event_ptr;
+		for (uint16_t i = 0; i < vector->nb_elem; i++) {
+			uint64_t obj = vector->u64s[i];
+			/* Process each object in the vector. */
+			...
+		}
+	}
+
+Statistics and Cleanup
+----------------------
+
+The application can retrieve statistics for the vector adapter using
+``rte_event_vector_adapter_stats_get()``:
+
+.. code-block:: c
+
+	struct rte_event_vector_adapter_stats stats;
+	rte_event_vector_adapter_stats_get(adapter, &stats);
+
+	printf("Vectors created: %" PRIu64 "\n", stats.vectorized);
+	printf("Timeouts occurred: %" PRIu64 "\n", stats.vectors_timedout);
+
+To reset the statistics, use ``rte_event_vector_adapter_stats_reset()``.
+
+To destroy the vector adapter and release its resources, use
+``rte_event_vector_adapter_destroy()``. The destroy function will
+flush any remaining events in the vector adapter before destroying it.
diff --git a/doc/guides/prog_guide/eventdev/eventdev.rst b/doc/guides/prog_guide/eventdev/eventdev.rst
index 8bb72da908..5e49db8983 100644
--- a/doc/guides/prog_guide/eventdev/eventdev.rst
+++ b/doc/guides/prog_guide/eventdev/eventdev.rst
@@ -424,8 +424,8 @@ eventdev.
 .. Note::
 
          EventDev needs to be started before starting the event producers such
-         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter and
-         event_dma_adapter.
+         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter,
+         event_dma_adapter and event_vector_adapter.
 
 Ingress of New Events
 ~~~~~~~~~~~~~~~~~~~~~
@@ -561,9 +561,9 @@ using ``rte_event_dev_stop_flush_callback_register()`` function.
 .. Note::
 
         The event producers such as ``event_eth_rx_adapter``,
-        ``event_timer_adapter``, ``event_crypto_adapter`` and
-        ``event_dma_adapter`` need to be stopped before stopping
-        the event device.
+        ``event_timer_adapter``, ``event_crypto_adapter``,
+        ``event_dma_adapter`` and ``event_vector_adapter``
+        need to be stopped before stopping the event device.
 
 Summary
 -------
diff --git a/doc/guides/prog_guide/eventdev/index.rst b/doc/guides/prog_guide/eventdev/index.rst
index 2e1940ce76..af11a57e71 100644
--- a/doc/guides/prog_guide/eventdev/index.rst
+++ b/doc/guides/prog_guide/eventdev/index.rst
@@ -14,3 +14,4 @@ Event Device Library
     event_crypto_adapter
     event_dma_adapter
     dispatcher_lib
+    event_vector_adapter
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..e6e84eeec6 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added eventdev vector adapter.**
+
+  * Added the Event vector Adapter Library. This library extends the event-based
+    model by introducing APIs that allow applications to offload creation of
+    event vectors.
+
 
 Removed Items
 -------------
diff --git a/lib/eventdev/event_vector_adapter_pmd.h b/lib/eventdev/event_vector_adapter_pmd.h
new file mode 100644
index 0000000000..667363c496
--- /dev/null
+++ b/lib/eventdev/event_vector_adapter_pmd.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+#ifndef __EVENT_VECTOR_ADAPTER_PMD_H__
+#define __EVENT_VECTOR_ADAPTER_PMD_H__
+/**
+ * @file
+ * RTE Event Vector Adapter API (PMD Side)
+ *
+ * @note
+ * This file provides implementation helpers for internal use by PMDs.  They
+ * are not intended to be exposed to applications and are not subject to ABI
+ * versioning.
+ */
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+typedef int (*rte_event_vector_adapter_create_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation setup */
+typedef int (*rte_event_vector_adapter_destroy_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation teardown */
+typedef int (*rte_event_vector_adapter_stats_get_t)(const struct rte_event_vector_adapter *adapter,
+						    struct rte_event_vector_adapter_stats *stats);
+/**< @internal Get statistics for event vector adapter */
+typedef int (*rte_event_vector_adapter_stats_reset_t)(
+	const struct rte_event_vector_adapter *adapter);
+/**< @internal Reset statistics for event vector adapter */
+
+/**
+ * @internal Structure containing the functions exported by an event vector
+ * adapter implementation.
+ */
+struct event_vector_adapter_ops {
+	rte_event_vector_adapter_create_t create;
+	/**< Set up adapter */
+	rte_event_vector_adapter_destroy_t destroy;
+	/**< Tear down adapter */
+	rte_event_vector_adapter_stats_get_t stats_get;
+	/**< Get adapter statistics */
+	rte_event_vector_adapter_stats_reset_t stats_reset;
+	/**< Reset adapter statistics */
+
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Enqueue objects into the event vector adapter */
+};
+/**
+ * @internal Adapter data; structure to be placed in shared memory to be
+ * accessible by various processes in a multi-process configuration.
+ */
+struct __rte_cache_aligned rte_event_vector_adapter_data {
+	uint32_t id;
+	/**< Event vector adapter ID */
+	uint8_t event_dev_id;
+	/**< Event device ID */
+	uint32_t socket_id;
+	/**< Socket ID where memory is allocated */
+	uint8_t event_port_id;
+	/**< Optional: event port ID used when the inbuilt port is absent */
+	const struct rte_memzone *mz;
+	/**< Event vector adapter memzone pointer */
+	struct rte_event_vector_adapter_conf conf;
+	/**< Configuration used to configure the adapter. */
+	uint32_t caps;
+	/**< Adapter capabilities */
+	void *adapter_priv;
+	/**< Vector adapter private data*/
+	uint8_t service_inited;
+	/**< Service initialization state */
+	uint32_t unified_service_id;
+	/**< Unified Service ID*/
+};
+
+static inline int
+dummy_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
+			     uint16_t num_events, uint64_t flags)
+{
+	RTE_SET_USED(adapter);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(num_events);
+	RTE_SET_USED(flags);
+	return 0;
+}
+
+#endif /* __EVENT_VECTOR_ADAPTER_PMD_H__ */
diff --git a/lib/eventdev/eventdev_pmd.h b/lib/eventdev/eventdev_pmd.h
index ad13ba5b03..d03461316b 100644
--- a/lib/eventdev/eventdev_pmd.h
+++ b/lib/eventdev/eventdev_pmd.h
@@ -26,6 +26,7 @@
 
 #include "event_timer_adapter_pmd.h"
 #include "rte_event_eth_rx_adapter.h"
+#include "rte_event_vector_adapter.h"
 #include "rte_eventdev.h"
 
 #ifdef __cplusplus
@@ -1555,6 +1556,36 @@ typedef int (*eventdev_dma_adapter_stats_get)(const struct rte_eventdev *dev,
 typedef int (*eventdev_dma_adapter_stats_reset)(const struct rte_eventdev *dev,
 						const int16_t dma_dev_id);
 
+/**
+ * Event device vector adapter capabilities.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param caps
+ *   Vector adapter capabilities
+ * @param ops
+ *   Vector adapter ops
+ *
+ * @return
+ *   Return 0 on success.
+ *
+ */
+typedef int (*eventdev_vector_adapter_caps_get_t)(const struct rte_eventdev *dev, uint32_t *caps,
+						  const struct event_vector_adapter_ops **ops);
+
+/**
+ * Event device vector adapter info.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param info
+ *   Vector adapter info
+ *
+ * @return
+ *   Return 0 on success.
+ */
+typedef int (*eventdev_vector_adapter_info_get_t)(const struct rte_eventdev *dev,
+						  struct rte_event_vector_adapter_info *info);
 
 /** Event device operations function pointer table */
 struct eventdev_ops {
@@ -1697,6 +1728,11 @@ struct eventdev_ops {
 	eventdev_dma_adapter_stats_reset dma_adapter_stats_reset;
 	/**< Reset DMA stats */
 
+	eventdev_vector_adapter_caps_get_t vector_adapter_caps_get;
+	/**< Get vector adapter capabilities */
+	eventdev_vector_adapter_info_get_t vector_adapter_info_get;
+	/**< Get vector adapter info */
+
 	eventdev_selftest dev_selftest;
 	/**< Start eventdev Selftest */
 
diff --git a/lib/eventdev/meson.build b/lib/eventdev/meson.build
index 71dea91727..0797c145e7 100644
--- a/lib/eventdev/meson.build
+++ b/lib/eventdev/meson.build
@@ -18,6 +18,7 @@ sources = files(
         'rte_event_eth_tx_adapter.c',
         'rte_event_ring.c',
         'rte_event_timer_adapter.c',
+        'rte_event_vector_adapter.c',
         'rte_eventdev.c',
 )
 headers = files(
@@ -27,6 +28,7 @@ headers = files(
         'rte_event_eth_tx_adapter.h',
         'rte_event_ring.h',
         'rte_event_timer_adapter.h',
+        'rte_event_vector_adapter.h',
         'rte_eventdev.h',
         'rte_eventdev_trace_fp.h',
 )
@@ -38,6 +40,7 @@ driver_sdk_headers += files(
         'eventdev_pmd_pci.h',
         'eventdev_pmd_vdev.h',
         'event_timer_adapter_pmd.h',
+        'event_vector_adapter_pmd.h',
 )
 
 deps += ['ring', 'ethdev', 'hash', 'mempool', 'mbuf', 'timer', 'cryptodev', 'dmadev']
diff --git a/lib/eventdev/rte_event_vector_adapter.c b/lib/eventdev/rte_event_vector_adapter.c
new file mode 100644
index 0000000000..ff6bc43b17
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.c
@@ -0,0 +1,472 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mcslock.h>
+#include <rte_service_component.h>
+#include <rte_tailq.h>
+
+#include <eal_export.h>
+
+#include "event_vector_adapter_pmd.h"
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+#define ADAPTER_ID(dev_id, queue_id, adapter_id)                                                   \
+	((uint32_t)dev_id << 16 | (uint32_t)queue_id << 8 | (uint32_t)adapter_id)
+#define DEV_ID_FROM_ADAPTER_ID(adapter_id)     ((adapter_id >> 16) & 0xFF)
+#define QUEUE_ID_FROM_ADAPTER_ID(adapter_id)   ((adapter_id >> 8) & 0xFF)
+#define ADAPTER_ID_FROM_ADAPTER_ID(adapter_id) (adapter_id & 0xFF)
+
+#define MZ_NAME_MAX_LEN	    64
+#define DATA_MZ_NAME_FORMAT "vector_adapter_data_%d_%d_%d"
+
+RTE_LOG_REGISTER_SUFFIX(ev_vector_logtype, adapter.vector, NOTICE);
+#define RTE_LOGTYPE_EVVEC ev_vector_logtype
+
+struct rte_event_vector_adapter *adapters[RTE_EVENT_MAX_DEVS][RTE_EVENT_MAX_QUEUES_PER_DEV];
+
+#define EVVEC_LOG(level, logtype, ...)                                                             \
+	RTE_LOG_LINE_PREFIX(level, logtype,                                                        \
+			    "EVVEC: %s() line %u: ", __func__ RTE_LOG_COMMA __LINE__, __VA_ARGS__)
+#define EVVEC_LOG_ERR(...) EVVEC_LOG(ERR, EVVEC, __VA_ARGS__)
+
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+#define EVVEC_LOG_DBG(...) EVVEC_LOG(DEBUG, EVVEC, __VA_ARGS__)
+#else
+#define EVVEC_LOG_DBG(...) /* No debug logging */
+#endif
+
+#define PTR_VALID_OR_ERR_RET(ptr, retval)                                                          \
+	do {                                                                                       \
+		if (ptr == NULL) {                                                                 \
+			rte_errno = EINVAL;                                                        \
+			return retval;                                                             \
+		}                                                                                  \
+	} while (0)
+
+static int
+validate_conf(const struct rte_event_vector_adapter_conf *conf,
+	      struct rte_event_vector_adapter_info *info)
+{
+	int rc = -EINVAL;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, rc);
+
+	if (conf->vector_sz < info->min_vector_sz || conf->vector_sz > info->max_vector_sz) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be between %u and %u",
+			      conf->vector_sz, info->min_vector_sz, info->max_vector_sz);
+		return rc;
+	}
+
+	if (conf->vector_timeout_ns < info->min_vector_timeout_ns ||
+	    conf->vector_timeout_ns > info->max_vector_timeout_ns) {
+		EVVEC_LOG_DBG("invalid vector timeout %" PRIu64 ", should be between %" PRIu64
+			      " and %" PRIu64,
+			      conf->vector_timeout_ns, info->min_vector_timeout_ns,
+			      info->max_vector_timeout_ns);
+		return rc;
+	}
+
+	if (conf->vector_mp == NULL) {
+		EVVEC_LOG_DBG("invalid mempool for vector adapter");
+		return rc;
+	}
+
+	if (info->log2_sz && rte_is_power_of_2(conf->vector_sz) != 0) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be a power of 2", conf->vector_sz);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+default_port_conf_cb(uint8_t event_dev_id, uint8_t *event_port_id, void *conf_arg)
+{
+	struct rte_event_port_conf *port_conf, def_port_conf = {0};
+	struct rte_event_dev_config dev_conf;
+	struct rte_eventdev *dev;
+	uint8_t port_id;
+	uint8_t dev_id;
+	int started;
+	int ret;
+
+	dev = &rte_eventdevs[event_dev_id];
+	dev_id = dev->data->dev_id;
+	dev_conf = dev->data->dev_conf;
+
+	started = dev->data->dev_started;
+	if (started)
+		rte_event_dev_stop(dev_id);
+
+	port_id = dev_conf.nb_event_ports;
+	if (conf_arg != NULL)
+		port_conf = conf_arg;
+	else {
+		port_conf = &def_port_conf;
+		ret = rte_event_port_default_conf_get(dev_id, (port_id - 1), port_conf);
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_conf.nb_event_ports += 1;
+	if (port_conf->event_port_cfg & RTE_EVENT_PORT_CFG_SINGLE_LINK)
+		dev_conf.nb_single_link_event_port_queues += 1;
+
+	ret = rte_event_dev_configure(dev_id, &dev_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to configure event dev %u", dev_id);
+		if (started)
+			if (rte_event_dev_start(dev_id))
+				return -EIO;
+
+		return ret;
+	}
+
+	ret = rte_event_port_setup(dev_id, port_id, port_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to setup event port %u on event dev %u", port_id, dev_id);
+		return ret;
+	}
+
+	*event_port_id = port_id;
+
+	if (started)
+		ret = rte_event_dev_start(dev_id);
+
+	return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf)
+{
+	return rte_event_vector_adapter_create_ext(conf, default_port_conf_cb, NULL);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create_ext, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb, void *conf_arg)
+{
+	struct rte_event_vector_adapter *adapter = NULL;
+	struct rte_event_vector_adapter_info info;
+	char mz_name[MZ_NAME_MAX_LEN];
+	const struct rte_memzone *mz;
+	struct rte_eventdev *dev;
+	uint32_t caps;
+	int i, n, rc;
+
+	PTR_VALID_OR_ERR_RET(conf, NULL);
+
+	if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+		adapters[conf->event_dev_id][conf->ev.queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[conf->event_dev_id][conf->ev.queue_id][i].used == false) {
+			adapter = &adapters[conf->event_dev_id][conf->ev.queue_id][i];
+			adapter->adapter_id = ADAPTER_ID(conf->event_dev_id, conf->ev.queue_id, i);
+			adapter->used = true;
+			break;
+		}
+		EVVEC_LOG_DBG("adapter %u is already in use", i);
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	if (adapter == NULL) {
+		EVVEC_LOG_DBG("no available vector adapters");
+		rte_errno = ENODEV;
+		return NULL;
+	}
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, NULL);
+
+	dev = &rte_eventdevs[conf->event_dev_id];
+	if (dev->dev_ops->vector_adapter_caps_get != NULL &&
+	    dev->dev_ops->vector_adapter_info_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &caps, &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+
+		rc = dev->dev_ops->vector_adapter_info_get(dev, &info);
+		if (rc < 0) {
+			adapter->ops = NULL;
+			EVVEC_LOG_DBG("failed to get vector adapter info rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+	}
+
+	if (conf->ev.sched_type != dev->data->queues_cfg[conf->ev.queue_id].schedule_type &&
+	    !(dev->data->event_dev_cap & RTE_EVENT_DEV_CAP_QUEUE_ALL_TYPES)) {
+		EVVEC_LOG_DBG("invalid event schedule type, eventdev doesn't support all types");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	rc = validate_conf(conf, &info);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	n = snprintf(mz_name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, conf->event_dev_id,
+		     conf->ev.queue_id, adapter->adapter_id);
+	if (n >= (int)sizeof(mz_name)) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create memzone name");
+		rte_errno = EINVAL;
+		goto error;
+	}
+	mz = rte_memzone_reserve(mz_name, sizeof(struct rte_event_vector_adapter_data),
+				 conf->socket_id, 0);
+	if (mz == NULL) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to reserve memzone for vector adapter");
+		rte_errno = ENOMEM;
+		goto error;
+	}
+
+	adapter->data = mz->addr;
+	memset(adapter->data, 0, sizeof(struct rte_event_vector_adapter_data));
+
+	adapter->data->mz = mz;
+	adapter->data->event_dev_id = conf->event_dev_id;
+	adapter->data->id = adapter->adapter_id;
+	adapter->data->socket_id = conf->socket_id;
+	adapter->data->conf = *conf;
+
+	if (!(caps & RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT)) {
+		if (conf_cb == NULL) {
+			EVVEC_LOG_DBG("port config callback is NULL");
+			rte_errno = EINVAL;
+			goto error;
+		}
+
+		rc = conf_cb(conf->event_dev_id, &adapter->data->event_port_id, conf_arg);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to create port for vector adapter");
+			rte_errno = EINVAL;
+			goto error;
+		}
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->create, NULL);
+
+	rc = adapter->ops->create(adapter);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create vector adapter");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+
+	return adapter;
+
+error:
+	adapter->used = false;
+	return NULL;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_lookup, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id)
+{
+	uint8_t adapter_idx = ADAPTER_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t queue_id = QUEUE_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t dev_id = DEV_ID_FROM_ADAPTER_ID(adapter_id);
+	struct rte_event_vector_adapter *adapter;
+	const struct rte_memzone *mz;
+	char name[MZ_NAME_MAX_LEN];
+	struct rte_eventdev *dev;
+	int rc;
+
+	if (dev_id >= RTE_EVENT_MAX_DEVS || queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV ||
+	    adapter_idx >= RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE) {
+		EVVEC_LOG_ERR("invalid adapter id %u", adapter_id);
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (adapters[dev_id][queue_id] == NULL) {
+		adapters[dev_id][queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[dev_id][queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	if (adapters[dev_id][queue_id][adapter_idx].used == true)
+		return &adapters[dev_id][queue_id][adapter_idx];
+
+	adapter = &adapters[dev_id][queue_id][adapter_idx];
+
+	snprintf(name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, dev_id, queue_id, adapter_idx);
+	mz = rte_memzone_lookup(name);
+	if (mz == NULL) {
+		EVVEC_LOG_DBG("failed to lookup memzone for vector adapter");
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	adapter->data = mz->addr;
+	dev = &rte_eventdevs[dev_id];
+
+	if (dev->dev_ops->vector_adapter_caps_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &adapter->data->caps,
+							   &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities");
+			rte_errno = ENOTSUP;
+			return NULL;
+		}
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+	adapter->adapter_id = adapter_id;
+	adapter->used = true;
+
+	return adapter;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_service_id_get, 25.07)
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	if (adapter->data->service_inited && service_id != NULL)
+		*service_id = adapter->data->unified_service_id;
+
+	return adapter->data->service_inited ? 0 : -ESRCH;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_destroy, 25.07)
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter)
+{
+	int rc;
+
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	if (adapter->used == false) {
+		EVVEC_LOG_ERR("event vector adapter is not allocated");
+		return -EINVAL;
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->destroy, -ENOTSUP);
+
+	rc = adapter->ops->destroy(adapter);
+	if (rc < 0) {
+		EVVEC_LOG_DBG("failed to destroy vector adapter");
+		return rc;
+	}
+
+	rte_memzone_free(adapter->data->mz);
+	adapter->ops = NULL;
+	adapter->enqueue = dummy_vector_adapter_enqueue;
+	adapter->data = NULL;
+	adapter->used = false;
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_info_get, 25.07)
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id, struct rte_event_vector_adapter_info *info)
+{
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, -EINVAL);
+	PTR_VALID_OR_ERR_RET(info, -EINVAL);
+
+	struct rte_eventdev *dev = &rte_eventdevs[event_dev_id];
+	if (dev->dev_ops->vector_adapter_info_get != NULL)
+		return dev->dev_ops->vector_adapter_info_get(dev, info);
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_conf_get, 25.07)
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(conf, -EINVAL);
+
+	*conf = adapter->data->conf;
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_remaining, 25.07)
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id)
+{
+	uint8_t remaining = 0;
+	int i;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, 0);
+
+	if (event_queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV)
+		return 0;
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[event_dev_id][event_queue_id][i].used == false)
+			remaining++;
+	}
+
+	return remaining;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_get, 25.07)
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(stats, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_get, -ENOTSUP);
+
+	adapter->ops->stats_get(adapter, stats);
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_reset, 25.07)
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_reset, -ENOTSUP);
+
+	adapter->ops->stats_reset(adapter);
+
+	return 0;
+}
diff --git a/lib/eventdev/rte_event_vector_adapter.h b/lib/eventdev/rte_event_vector_adapter.h
new file mode 100644
index 0000000000..61680ec307
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.h
@@ -0,0 +1,481 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __RTE_EVENT_VECTOR_ADAPTER_H__
+#define __RTE_EVENT_VECTOR_ADAPTER_H__
+
+/**
+ * @file rte_event_vector_adapter.h
+ *
+ * @warning
+ * @b EXPERIMENTAL:
+ * All functions in this file may be changed or removed without prior notice.
+ *
+ * Event vector adapter API.
+ *
+ * An event vector adapter has the following working model:
+ *
+ *         ┌──────────┐
+ *         │  Vector  ├─┐
+ *         │ adapter0 │ │
+ *         └──────────┘ │
+ *         ┌──────────┐ │   ┌──────────┐
+ *         │  Vector  ├─┼──►│  Event   │
+ *         │ adapter1 │ │   │  Queue0  │
+ *         └──────────┘ │   └──────────┘
+ *         ┌──────────┐ │
+ *         │  Vector  ├─┘
+ *         │ adapter2 │
+ *         └──────────┘
+ *
+ *         ┌──────────┐
+ *         │  Vector  ├─┐
+ *         │ adapter0 │ │   ┌──────────┐
+ *         └──────────┘ ├──►│  Event   │
+ *         ┌──────────┐ │   │  Queue1  │
+ *         │  Vector  ├─┘   └──────────┘
+ *         │ adapter1 │
+ *         └──────────┘
+ *
+ * - A vector adapter can be seen as an extension to event queue. It helps in
+ *   aggregating objects and generating a vector event which is enqueued to the
+ *   event queue.
+ *
+ * - Multiple vector adapters can be created on an event queue, each with its
+ *   own unique properties such as event properties, vector size, and timeout.
+ *   Note: If the target event queue doesn't support RTE_EVENT_QUEUE_CFG_ALL_TYPES,
+ *         then the vector adapter should use the same schedule type as the event
+ *         queue.
+ *
+ * - Each vector adapter aggregates 8B objects, generates a vector event and
+ *   enqueues it to the event queue with the event properties mentioned in
+ *   rte_event_vector_adapter_conf::ev.
+ *
+ * - After configuring the vector adapter, Application needs to use the
+ *   rte_event_vector_adapter_enqueue() function to enqueue objects i.e.,
+ *   mbufs/ptrs/u64s to the vector adapter.
+ *   On reaching the configured vector size or timeout, the vector adapter
+ *   enqueues the event vector to the event queue.
+ *   Note: Application should use the event_type and sub_event_type properly
+ *         identifying the contents of vector event on dequeue.
+ *
+ * - If the vector adapter advertises the RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ *   capability, application can use the RTE_EVENT_VECTOR_ENQ_[S|E]OV flags
+ *   to indicate the start and end of a vector event.
+ *   * When RTE_EVENT_VECTOR_ENQ_SOV is set, the vector adapter will flush any
+ *     aggregation in-progress and start aggregating a new vector event with
+ *     the enqueued objects.
+ *   * When RTE_EVENT_VECTOR_ENQ_EOV is set, the vector adapter will add the
+ *     objects enqueued to the in-progress aggregation and enqueue the vector
+ *     event to the event queue, even if configured vector size or timeout is
+ *     not reached.
+ *   * If both flags are set, the vector adapter will flush any aggregation in
+ *     progress and enqueue the objects as a new vector event to the event
+ *     queue.
+ *
+ * - If the vector adapter reaches the configured vector size, it will enqueue
+ *   the aggregated vector event to the event queue.
+ *
+ * - If the vector adapter reaches the configured vector timeout, it will flush
+ *   the aggregated objects as a vector event if the minimum vector size is
+ *   reached, if not it will enqueue the objs as single events to the event
+ *   queue.
+ *
+ * - If the vector adapter is unable to aggregate the objs into a vector event,
+ *   it will enqueue the objs as single events to the event queue with the event
+ *   properties mentioned in rte_event_vector_adapter_conf::ev_fallback.
+ *
+ * Before using the vector adapter, the application has to create and configure
+ * an event device and based on the event device capability it might require
+ * creating an additional event port.
+ *
+ * When the application creates the vector adapter using the
+ * ``rte_event_vector_adapter_create()`` function, the event device driver
+ * capabilities are checked. If an in-built port is absent, the application
+ * uses the default function to create a new event port.
+ * For finer control over event port creation, the application should use
+ * the ``rte_event_vector_adapter_create_ext()`` function.
+ *
+ * The application can enqueue one or more objs to the vector adapter using the
+ * ``rte_event_vector_adapter_enqueue()`` function and control the aggregation
+ * using the flags.
+ *
+ * Vector adapters report stats using the ``rte_event_vector_adapter_stats_get()``
+ * function and reset the stats using the ``rte_event_vector_adapter_stats_reset()``.
+ *
+ * The application can destroy the vector adapter using the
+ * ``rte_event_vector_adapter_destroy()`` function.
+ *
+ */
+
+#include <rte_eventdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV (1ULL << 0)
+/**< Vector adapter supports Start of Vector (SOV) and End of Vector (EOV) flags
+ *  in the enqueue flags.
+ *
+ * @see RTE_EVENT_VECTOR_ENQ_SOV
+ * @see RTE_EVENT_VECTOR_ENQ_EOV
+ */
+
+#define RTE_EVENT_VECTOR_ENQ_SOV   (1ULL << 0)
+/**< Indicates the start of a vector event. When enqueue is called with
+ *  RTE_EVENT_VECTOR_ENQ_SOV, the vector adapter will flush any vector
+ *  aggregation in progress and start aggregating a new vector event with
+ *  the enqueued objects.
+ */
+#define RTE_EVENT_VECTOR_ENQ_EOV   (1ULL << 1)
+/**< Indicates the end of a vector event. When enqueue is called with
+ *  RTE_EVENT_VECTOR_ENQ_EOV, the vector adapter will add the objects
+ *  to any inprogress aggregation and flush the event vector.
+ */
+#define RTE_EVENT_VECTOR_ENQ_FLUSH (1ULL << 2)
+/**< Flush any in-progress vector aggregation. */
+
+/**
+ * Vector adapter configuration structure
+ */
+struct rte_event_vector_adapter_conf {
+	uint8_t event_dev_id;
+	/**< Event device identifier */
+	uint32_t socket_id;
+	/**< Identifier of socket from which to allocate memory for adapter */
+	struct rte_event ev;
+	/**<
+	 *  The values from the following event fields will be used when
+	 *  queuing work:
+	 *   - queue_id: Targeted event queue ID for vector event.
+	 *   - event_priority: Event priority of the vector event in
+	 *                     the event queue relative to other events.
+	 *   - sched_type: Scheduling type for events from this vector adapter.
+	 *   - event_type: Event type for the vector event.
+	 *   - sub_event_type: Sub event type for the vector event.
+	 *   - flow_id: Flow ID for the vectors enqueued to the event queue by
+	 *              the vector adapter.
+	 */
+	struct rte_event ev_fallback;
+	/**<
+	 * The values from the following event fields will be used when
+	 * aggregation fails and single event is enqueued:
+	 *   - event_type: Event type for the single event.
+	 *   - sub_event_type: Sub event type for the single event.
+	 *   - flow_id: Flow ID for the single event.
+	 *
+	 * Other fields are taken from rte_event_vector_adapter_conf::ev.
+	 */
+	uint16_t vector_sz;
+	/**<
+	 * Indicates the maximum number for enqueued work to combine and form a vector.
+	 * Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_sz
+	 * @see rte_event_vector_adapter_info::max_vector_sz
+	 */
+	uint64_t vector_timeout_ns;
+	/**<
+	 * Indicates the maximum number of nanoseconds to wait for receiving
+	 * work. Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_ns
+	 * @see rte_event_vector_adapter_info::max_vector_ns
+	 */
+	struct rte_mempool *vector_mp;
+	/**<
+	 * Indicates the mempool that should be used for allocating
+	 * rte_event_vector container.
+	 * @see rte_event_vector_pool_create
+	 */
+};
+
+/**
+ * Vector adapter vector info structure
+ */
+struct rte_event_vector_adapter_info {
+	uint8_t max_vector_adapters_per_event_queue;
+	/**< Maximum number of vector adapters configurable */
+	uint16_t min_vector_sz;
+	/**< Minimum vector size configurable */
+	uint16_t max_vector_sz;
+	/**< Maximum vector size configurable */
+	uint64_t min_vector_timeout_ns;
+	/**< Minimum vector timeout configurable */
+	uint64_t max_vector_timeout_ns;
+	/**< Maximum vector timeout configurable */
+	uint8_t log2_sz;
+	/**< True if the size configured should be in log2. */
+};
+
+/**
+ * Vector adapter statistics structure
+ */
+struct rte_event_vector_adapter_stats {
+	uint64_t vectorized;
+	/**< Number of events vectorized */
+	uint64_t vectors_timedout;
+	/**< Number of timeouts occurred */
+	uint64_t vectors_flushed;
+	/**< Number of vectors flushed */
+	uint64_t alloc_failures;
+	/**< Number of vector allocation failures */
+};
+
+struct rte_event_vector_adapter;
+
+typedef int (*rte_event_vector_adapter_enqueue_t)(struct rte_event_vector_adapter *adapter,
+						  uint64_t objs[], uint16_t num_elem,
+						  uint64_t flags);
+/**< @internal Enqueue objs into the event vector adapter. */
+
+struct __rte_cache_aligned rte_event_vector_adapter {
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Pointer to driver enqueue function. */
+	struct rte_event_vector_adapter_data *data;
+	/**< Pointer to the adapter data */
+	const struct event_vector_adapter_ops *ops;
+	/**< Functions exported by adapter driver */
+
+	uint32_t adapter_id;
+	/**< Identifier of the adapter instance. */
+	uint8_t used : 1;
+	/**< Flag to indicate that this adapter is being used. */
+};
+
+/**
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_vector_adapter_port_conf_cb_t)(uint8_t event_dev_id, uint8_t *event_port_id,
+						       void *conf_arg);
+
+/**
+ * Create an event vector adapter.
+ *
+ * This function creates an event vector adapter based on the provided
+ * configuration. The adapter can be used to combine multiple mbufs/ptrs/u64s
+ * into a single vector event, i.e., rte_event_vector, which is then enqueued
+ * to the event queue provided.
+ * @see rte_event_vector_adapter_conf::ev::event_queue_id.
+ *
+ * @param conf
+ *   Configuration for the event vector adapter.
+ * @return
+ *   - Pointer to the created event vector adapter on success.
+ *   - NULL on failure with rte_errno set to the error code.
+ *     Possible rte_errno values include:
+ *    - EINVAL: Invalid event device identifier specified in config.
+ *    - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *    - ENOSPC: Maximum number of adapters already created.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Create an event vector adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the event
+ * vector adapter creation. If a built-in port is absent, then the function uses
+ * the callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ *   The event vector adapter configuration structure.
+ * @param conf_cb
+ *   The port config callback function.
+ * @param conf_arg
+ *   Opaque pointer to the argument for the callback function.
+ * @return
+ *   - Pointer to the new allocated event vector adapter on success.
+ *   - NULL on error with rte_errno set appropriately.
+ *   Possible rte_errno values include:
+ *   - ERANGE: vector_timeout_ns is not in supported range.
+ *   - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *   - EINVAL: Invalid event device identifier specified in config.
+ *   - ENOSPC: Maximum number of adapters already created.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb,
+				    void *conf_arg);
+
+/**
+ * Lookup an event vector adapter using its identifier.
+ *
+ * This function returns the event vector adapter based on the adapter_id.
+ * This is useful when the adapter is created in another process and the
+ * application wants to use the adapter in the current process.
+ *
+ * @param adapter_id
+ *   Identifier of the event vector adapter to look up.
+ * @return
+ *   - Pointer to the event vector adapter on success.
+ *   - NULL if the adapter is not found.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id);
+
+/**
+ * Destroy an event vector adapter.
+ *
+ * This function releases the resources associated with the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter to be destroyed.
+ * @return
+ *   - 0 on success.
+ *   - Negative value on failure with rte_errno set to the error code.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Get the vector info of an event vector adapter.
+ *
+ * This function retrieves the vector info of the event vector adapter.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param info
+ *   Pointer to the structure where the vector info will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ *   - EINVAL if the event device identifier is invalid.
+ *   - ENOTSUP if the event device does not support vector adapters.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id,
+				  struct rte_event_vector_adapter_info *info);
+
+/**
+ * Get the configuration of an event vector adapter.
+ *
+ * This function retrieves the configuration of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param conf
+ *   Pointer to the structure where the configuration will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Get the remaining event vector adapters.
+ *
+ * This function retrieves the number of remaining event vector adapters
+ * available for a given event device and event queue.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param event_queue_id
+ *   Event queue identifier.
+ * @return
+ *   Number of remaining slots available for enqueuing events.
+ */
+__rte_experimental
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id);
+
+/**
+ * Get the event vector adapter statistics.
+ *
+ * This function retrieves the statistics of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param stats
+ *   Pointer to the structure where the statistics will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats);
+
+/**
+ * @brief Reset the event vector adapter statistics.
+ *
+ * This function resets the statistics of the event vector adapter to their default values.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter whose statistics are to be reset.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Retrieve the service ID of the event vector adapter. If the adapter doesn't
+ * use an rte_service function, this function returns -ESRCH.
+ *
+ * @param adapter
+ *   A pointer to an event vector adapter.
+ * @param [out] service_id
+ *   A pointer to a uint32_t, to be filled in with the service id.
+ *
+ * @return
+ *   - 0: Success
+ *   - <0: Error code on failure
+ *   - -ESRCH: the adapter does not require a service to operate
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id);
+
+/**
+ * Enqueue objs into the event vector adapter.
+ *
+ * This function enqueues a specified number of objs into the event vector adapter.
+ * The objs are combined into a single vector event, i.e., rte_event_vector, which
+ * is then enqueued to the event queue configured in the adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param objs
+ *   Array of objs to be enqueued.
+ * @param num_elem
+ *   Number of objs to be enqueued.
+ * @param flags
+ *   Flags to be used for the enqueue operation.
+ * @return
+ *   Number of objs enqueued on success.
+ */
+__rte_experimental
+static inline int
+rte_event_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
+				 uint16_t num_elem, uint64_t flags)
+{
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+	if (adapter == NULL) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+
+	if (adapter->used == false) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+#endif
+	return adapter->enqueue(adapter, objs, num_elem, flags);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTE_EVENT_VECTOR_ADAPTER_H__ */
diff --git a/lib/eventdev/rte_eventdev.c b/lib/eventdev/rte_eventdev.c
index b66cbb4676..916bad6c2c 100644
--- a/lib/eventdev/rte_eventdev.c
+++ b/lib/eventdev/rte_eventdev.c
@@ -257,6 +257,28 @@ rte_event_dma_adapter_caps_get(uint8_t dev_id, uint8_t dma_dev_id, uint32_t *cap
 	return 0;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_caps_get, 25.07)
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps)
+{
+	const struct event_vector_adapter_ops *ops;
+	struct rte_eventdev *dev;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(dev_id, -EINVAL);
+
+	dev = &rte_eventdevs[dev_id];
+
+	if (caps == NULL)
+		return -EINVAL;
+
+	if (dev->dev_ops->vector_adapter_caps_get == NULL)
+		*caps = 0;
+
+	return dev->dev_ops->vector_adapter_caps_get ?
+		       dev->dev_ops->vector_adapter_caps_get(dev, caps, &ops) :
+		       0;
+}
+
 static inline int
 event_dev_queue_config(struct rte_eventdev *dev, uint8_t nb_queues)
 {
diff --git a/lib/eventdev/rte_eventdev.h b/lib/eventdev/rte_eventdev.h
index 6400d6109f..3c7fcbf0be 100644
--- a/lib/eventdev/rte_eventdev.h
+++ b/lib/eventdev/rte_eventdev.h
@@ -1985,6 +1985,16 @@ int
 rte_event_eth_tx_adapter_caps_get(uint8_t dev_id, uint16_t eth_port_id,
 				uint32_t *caps);
 
+/* Vector adapter capability bitmap flags */
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT	0x1
+/**< This flag is set when the vector adapter is capable of generating events
+ * using an internal event port.
+ */
+
+__rte_experimental
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps);
+
 /**
  * Converts nanoseconds to *timeout_ticks* value for rte_event_dequeue_burst()
  *
-- 
2.43.0


^ permalink raw reply	[relevance 1%]

* Re: [RFC PATCH] add rust binding support to DPDK
  @ 2025-04-11 13:13  4%     ` Van Haaren, Harry
  2025-04-11 15:39  0%       ` Etelson, Gregory
  0 siblings, 1 reply; 153+ results
From: Van Haaren, Harry @ 2025-04-11 13:13 UTC (permalink / raw)
  To: Etelson, Gregory, Richardson, Bruce; +Cc: dev

> From: Etelson, Gregory
> Sent: Thursday, April 10, 2025 6:28 AM
> To: Richardson, Bruce
> Cc: dev@dpdk.org
> Subject: Re: [RFC PATCH] add rust binding support to DPDK
> 
> Hello Bruce,

Hi Bruce & Gregory,

> > Add a Cargo.toml file in the root folder and a number of other scripts
> > and rust-related files into buildtools/rust, which then enables DPDK to
> > be cloned and built as a rust crate - all-be-it one with only two
> > functions: rte_eal_init and rte_eal_cleanup.
> >
> > Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> > ---
> >
> > This RFC is proposed as an alternative approach to enabling rust support
> > in DPDK. The key difference vs previous is that we are taking the whole
> > DPDK project here as a rust "crate", which can then be used by other
> > higher-level crates as a dependency. Building the crate does a
> > (minimal) build of DPDK and statically links it in, so there is no
> > "install" step or anything needed - the Rust app just adds DPDK to their
> > Cargo.toml file and then should have everything they need.
> >
> 
> Having a shared source directory for both for C and Rust DPDK infrastructure is
> the correct approach.

It seems so, with the various approaches so far, agreed.

I've tested Bruce's repo, and Cargo.toml. The initial build takes some time.. but it works!
The experience of "Adding DPDK to a Rust project" is very similar to adding any other Rust crate.
I like this - it makes the "strangeness" of depending on DPDK near-zero :)


> My concern is how to properly maintain Rust crate once DPDK starts to implement
> it's own API.

I'm not really sure what is meant here. I don't understand what "own" word refers to?

I see it like this:
- DPDK has the public C API exported (and that stays the same as today, with ABI rules, version.map files, etc)
- The Rust crate consumes the public C API (via bindgen, as done in this patch. More detail about bindgen below.)

So DPDK changing APIs is API/ABI breaking for *all* consumers of DPDK, not just the Rust bindings.
No special treatment required, just identify API/ABI changes when they occur (as per ABI policy) and fix.

Some approaches to how CI could help, and when to identify/fix Rust bindings took place on the last
TechBoard call (minutes not yet available on https://core.dpdk.org/techboard/minutes/).
No decision made on how - we're still at prototyping phase anyway.


> Rust files may need a separate directories to host libraries, applications and PMD.

I'm not sure this is strictly required - perhaps "nice to have". Bruce's approach
has been to minimize changes in the "DPDK C repo", doing just enough to enable
building "Safe Rust" wrappers on top in a seperate "DPDK Rust" repo.

I like this approach - use what bindgen gives access to - and incrementally export/expose
more DPDK APIs *as they are consumed*. This ensures we don't accidentally over-export,
or have mistakes go unnoticed because nobody tested/used the APIs yet. More on this below
in the context of bindgen usage.

<snip>

> > diff --git a/buildtools/rust/build.rs b/buildtools/rust/build.rs
> > new file mode 100644
> > index 0000000000..1ffdf03d2f
> > --- /dev/null
> > +++ b/buildtools/rust/build.rs

<snip>

> > +    let bindings = bindings.header("buildtools/rust/wrapper.h")
> > +        .derive_default(true)
> > +        .allowlist_function("rte_eal_init")
> > +        .allowlist_function("rte_eal_cleanup")
> 
> Calling the `allowlist_function()` method generates well-maintained target
> bindings.
> That approach requires to specify each DPDK symbol for Rust
> library API explicitly. There are way too many symbols to bind even for
> a simple network application.

The "allowlist" approach ensures that DPDK C functions, bindgen-converted in Rust are used, and reviewed.

If all DPDK C APIs are exported, there is additional and unknown risk of bugs, that the users
of the DPDK-rust-bindings will hit: providing bad user-experience. I do not believe it is a good
idea to export all API structs & symbols without any tests, reviews, and quality control.

If it is really desired by a end user to export all C API as "unsafe Rust" bindings, there are many
crates existing already doing this (and your patch Gregory also provides that method?). I do not feel
this adds value for DPDK community to maintain: the end-goal must be an ergonomic and safe Rust API.

My vote is to incrementally "allowlist" new APIs, and having a "Safe Rust" (or "idiomatic Rust APIs")
wrapping them to export public functions in the "end-user facing" DPDK Rust crate. This ensures that
any public function in the "DPDK Rust" crate has been built, reviewed and tested.

This will cause the initial versions of the "DPDK Rust" to be very minimal, and to gradually
export more functionality and APIs in a safe way. From my perspective, this is the only way
to give a consistent high-quality user-experience that is the norm for the Rust ecosystem.


> Bindings file or directory for RTE library API is not enough.
> Each PMD has it's own symbols set and will need a separate bindings library.

As per above, I recommend we focus on minimal functionality, exported and exposed in an ergonomic way.
That means initial focus on EAL, Lcore management, Mempools, Ethdev, and Mbufs. Or from a "use-case"
perspective, enough to get L2-macswap to work using "Safe Rust" bindings.

This means that things like "PMD specific symbols" are not going to be high on the priority list initially.
Once the above L2 forward use-case is satisfied, I'm happy to provide input on designing an API which is
"PMD specific" in a generic way (initial technical thoughts, a "dyn Trait" with downcast to a PMD specific trait).


<snip>

> > +#[cfg(test)]
> > +mod tests {
> > +    use super::*;
> > +
> > +    #[test]
> > +    fn test_helloworld() {
> > +       let appname = std::ffi::CString::new("test-rs").unwrap();
> > +       let mut argv = [appname.into_raw()];
> > +       let ret = unsafe {
> > +               rte_eal_init(argv.len().try_into().unwrap(), argv.as_mut_ptr())
> 
> Activating rte_eal_init() proves that Rust crate has properly linked with DPDK
> libraries.
> That is more like infrastructure test.
> An active network port with IO capabilities is a real Rust-DPDK POC.

Agreed - as you can see, this is a #[test]. It is intended only to show that eal_init() and cleanup() work.

Next steps are to "allowlist" more DPDK public API functions, and start building "Safe Rust" APIs over
them, in order to expose an ergonomic and misuse-resistant API. As you note, this is where a network ports,
lcores, mempools, and ethdev configuration are all required. First goal something like "Safe L2 macswap"?

I will make some Safe-Rust API suggestions for EAL, and send a patch sometime next week to discuss.

Thanks for the inputs! Regards, -Harry


^ permalink raw reply	[relevance 4%]

* Re: [RFC PATCH] add rust binding support to DPDK
  2025-04-11 13:13  4%     ` Van Haaren, Harry
@ 2025-04-11 15:39  0%       ` Etelson, Gregory
  2025-04-11 16:22  4%         ` Van Haaren, Harry
  0 siblings, 1 reply; 153+ results
From: Etelson, Gregory @ 2025-04-11 15:39 UTC (permalink / raw)
  To: Van Haaren, Harry; +Cc: Richardson, Bruce, dev

Hello Bruce & Harry,

<snip>

>
>> My concern is how to properly maintain Rust crate once DPDK starts to implement
>> it's own API.
>
> I'm not really sure what is meant here. I don't understand what "own" word refers to?
>
> I see it like this:
> - DPDK has the public C API exported (and that stays the same as today, with ABI rules, version.map files, etc)
> - The Rust crate consumes the public C API (via bindgen, as done in this patch. More detail about bindgen below.)
>

Bindgen cannot provide access to all DPDK public API.

A good example here is rte_eth_rx_burst().
That function is defined as inline and bindgen does not translate it.
Also, the function definition references rte_eth_fp_ops array that is not part of the 
public DPDK API. That means Rust cannot duplicate rte_eth_rx_burst() "as-is" and 
the solution can require extensions to existing DPDK API.

I added a new public API that exports rte_eth_fp_ops for a given port Id.

Rust implementation of rte_eth_rx_burst() does not have to follow the original 
approach.
Usage of rte_eth_fp_ops is good for C, but Rust has different methods.

For conclusion, Rust DPDK infrastructure cannot relay on bindgen only and needs 
to provide native implementation for some public DPDK API.
It can be easier to maintain Rust files separately.

You can check out my Rust DPDK version here: https://github.com/getelson-at-mellanox/rdpdk

<snip>

>
> Next steps are to "allowlist" more DPDK public API functions, and start building "Safe Rust" APIs over
> them, in order to expose an ergonomic and misuse-resistant API. As you note, this is where a network ports,
> lcores, mempools, and ethdev configuration are all required. First goal something like "Safe L2 macswap"?

I suggest to set the first goal to initiate a port and run simple Rx/Tx functions.

>
> I will make some Safe-Rust API suggestions for EAL, and send a patch sometime next week to discuss.
>
> Thanks for the inputs! Regards, -Harry

Regards,
Gregory

^ permalink raw reply	[relevance 0%]

* Re: [RFC PATCH] add rust binding support to DPDK
  2025-04-11 15:39  0%       ` Etelson, Gregory
@ 2025-04-11 16:22  4%         ` Van Haaren, Harry
  2025-04-13  8:07  0%           ` Etelson, Gregory
  0 siblings, 1 reply; 153+ results
From: Van Haaren, Harry @ 2025-04-11 16:22 UTC (permalink / raw)
  To: Etelson, Gregory; +Cc: Richardson, Bruce, dev

> From: Etelson, Gregory
> Sent: Friday, April 11, 2025 4:39 PM
> To: Van Haaren, Harry
> Cc: Richardson, Bruce; dev@dpdk.org
> Subject: Re: [RFC PATCH] add rust binding support to DPDK
> 
> Hello Bruce & Harry,
> 
> <snip>
> 
> >
> >> My concern is how to properly maintain Rust crate once DPDK starts to implement
> >> it's own API.
> >
> > I'm not really sure what is meant here. I don't understand what "own" word refers to?
> >
> > I see it like this:
> > - DPDK has the public C API exported (and that stays the same as today, with ABI rules, version.map files, etc)
> > - The Rust crate consumes the public C API (via bindgen, as done in this patch. More detail about bindgen below.)
> >
> 
> Bindgen cannot provide access to all DPDK public API.

Ah - you're referring to C static inline functions, declared in header files, which bindgen doesn't wrap.
Correct - thanks - I understand your point now.

> A good example here is rte_eth_rx_burst().
> That function is defined as inline and bindgen does not translate it.
> Also, the function definition references rte_eth_fp_ops array that is not part of the
> public DPDK API. That means Rust cannot duplicate rte_eth_rx_burst() "as-is" and
> the solution can require extensions to existing DPDK API.
> 
> I added a new public API that exports rte_eth_fp_ops for a given port Id.
> 
> Rust implementation of rte_eth_rx_burst() does not have to follow the original
> approach.
> Usage of rte_eth_fp_ops is good for C, but Rust has different methods.

Agreed there is a certain "mismatch" sometimes, if functions aren't in the
actually "C ABI" then they can't be called via bindgen.

Agree that elegant solutions (clean, maintainable, and high performance) will have
to be found here. Many existing solutions just wrap the "static inline" function into
a "non-static" function, and export it as a public symbol. That allows calling into it
from Rust (via bindgen-generated header) however causes an actual function call..
(LTO _might_ fix/inline it, but not everybody compiles with LTO.. link times!)

As DPDK uses static-inline functions primarily for "packet-at-a-time" performance reasons,
it is unfortunate to give-up (some small amounts of..?) performance by having a C->Rust FFI call.
We have work to do to find/propose the best solution.

> For conclusion, Rust DPDK infrastructure cannot relay on bindgen only and needs
> to provide native implementation for some public DPDK API.
> It can be easier to maintain Rust files separately.

OK, now I understand your "kind-of-C-DPDK, kind of Rust-DPDK" code, or the "own" code reference above.
I'm not sure right now where that would be best implemented/maintained, I'll have to think about it and do a POC.


> You can check out my Rust DPDK version here: https://github.com/getelson-at-mellanox/rdpdk

Thanks for the link - I see you've been pushing code actively! Good to see opportunities,
and compare approaches and concepts. Have you investigated wrapping the various pointers
into structs, and providing safer APIs? For example, the [*mut rte_mbuf; 64] array for RX causes
raw pointers to be handled for all packet-processing - resulting in "unsafe{ /* work here */ }" blocks.

The code in the above repo feels like "DPDK C code written in Rust". It is a great step towards better
understanding, and having something that works is very valuable; thanks for sharing it.

A "top down" application view might help to brainstorm idiomatic/Safe Rust APIs, and then we can
discuss/understand how to map these high level APIs onto the "DPDK C in Rust" or even "DPDK C API/ABI" layers.

Does that seem like a good method to you, to achieve an ergonomic Safe Rust API as the end result?


> > Next steps are to "allowlist" more DPDK public API functions, and start building "Safe Rust" APIs over
> > them, in order to expose an ergonomic and misuse-resistant API. As you note, this is where a network ports,
> > lcores, mempools, and ethdev configuration are all required. First goal something like "Safe L2 macswap"?
> 
> I suggest to set the first goal to initiate a port and run simple Rx/Tx functions.

Agreed, we need some API to "work towards" (Top down) as well as "work from" (DPDK C APIs, and your repo above).

> > I will make some Safe-Rust API suggestions for EAL, and send a patch sometime next week to discuss.

Hopefully this Safe-Rust API proposal will be a candidate for "Top Down" API design approach.

> > Thanks for the inputs! Regards, -Harry
> 
> Regards,
> Gregory

Thanks! -Harry

^ permalink raw reply	[relevance 4%]

* Re: [RFC PATCH] add rust binding support to DPDK
  2025-04-11 16:22  4%         ` Van Haaren, Harry
@ 2025-04-13  8:07  0%           ` Etelson, Gregory
  2025-04-13 17:09  3%             ` Owen Hilyard
  0 siblings, 1 reply; 153+ results
From: Etelson, Gregory @ 2025-04-13  8:07 UTC (permalink / raw)
  To: Van Haaren, Harry; +Cc: Etelson, Gregory, Richardson, Bruce, dev

Hello Harry,

>>>> My concern is how to properly maintain Rust crate once DPDK starts to implement
>>>> it's own API.
>>>
>>> I'm not really sure what is meant here. I don't understand what "own" word refers to?
>>>
>>> I see it like this:
>>> - DPDK has the public C API exported (and that stays the same as today, with ABI rules, version.map files, etc)
>>> - The Rust crate consumes the public C API (via bindgen, as done in this patch. More detail about bindgen below.)
>>>
>>
>> Bindgen cannot provide access to all DPDK public API.
>
> Ah - you're referring to C static inline functions, declared in header files, which bindgen doesn't wrap.
> Correct - thanks - I understand your point now.
>
>> A good example here is rte_eth_rx_burst().
>> That function is defined as inline and bindgen does not translate it.
>> Also, the function definition references rte_eth_fp_ops array that is not part of the
>> public DPDK API. That means Rust cannot duplicate rte_eth_rx_burst() "as-is" and
>> the solution can require extensions to existing DPDK API.
>>
>> I added a new public API that exports rte_eth_fp_ops for a given port Id.
>>
>> Rust implementation of rte_eth_rx_burst() does not have to follow the original
>> approach.
>> Usage of rte_eth_fp_ops is good for C, but Rust has different methods.
>
> Agreed there is a certain "mismatch" sometimes, if functions aren't in the
> actually "C ABI" then they can't be called via bindgen.
>
> Agree that elegant solutions (clean, maintainable, and high performance) will have
> to be found here. Many existing solutions just wrap the "static inline" function into
> a "non-static" function, and export it as a public symbol. That allows calling into it
> from Rust (via bindgen-generated header) however causes an actual function call..
> (LTO _might_ fix/inline it, but not everybody compiles with LTO.. link times!)
>

The core DPDK code is maintained as a self-contained pure C project.
What about extending that model and provide direct access to DPDK resources that 
are needed for Rust API ?
That can be arranged as part of "native" DPDK API or as extension for 
Rust-enabled DPDK only.
I'm currently experimenting with the latter in 
https://github.com/getelson-at-mellanox/rdpdk/tree/main/dpdk-patches

> As DPDK uses static-inline functions primarily for "packet-at-a-time" performance reasons,
> it is unfortunate to give-up (some small amounts of..?) performance by having a C->Rust FFI call.
> We have work to do to find/propose the best solution.

What exactly is a small amount of performance degradation ?
For some existing performance oriented projects loosing performance to 
eliminate the `unsafe {}` construct is not an option.

>
>> For conclusion, Rust DPDK infrastructure cannot relay on bindgen only and needs
>> to provide native implementation for some public DPDK API.
>> It can be easier to maintain Rust files separately.
>
> OK, now I understand your "kind-of-C-DPDK, kind of Rust-DPDK" code, or the "own" code reference above.
> I'm not sure right now where that would be best implemented/maintained, I'll have to think about it and do a POC.
>
>
>> You can check out my Rust DPDK version here: https://github.com/getelson-at-mellanox/rdpdk
>
> Thanks for the link - I see you've been pushing code actively! Good to see opportunities,
> and compare approaches and concepts. Have you investigated wrapping the various pointers
> into structs, and providing safer APIs? For example, the [*mut rte_mbuf; 64] array for RX causes
> raw pointers to be handled for all packet-processing - resulting in "unsafe{ /* work here */ }" blocks.
>

This construct can work:

struct PktsBuf<const SIZE: usize> {
 	buffer: [*mut rte_mbuf; SIZE]
}

> The code in the above repo feels like "DPDK C code written in Rust".

Agree.
At this stage there is no native Rust DPDK API.

> It is a great step towards better
> understanding, and having something that works is very valuable; thanks for sharing it.
>
> A "top down" application view might help to brainstorm idiomatic/Safe Rust APIs, and then we can
> discuss/understand how to map these high level APIs onto the "DPDK C in Rust" or even "DPDK C API/ABI" layers.
>
> Does that seem like a good method to you, to achieve an ergonomic Safe Rust API as the end result?
>

I think we need to conclude what the safe API means in terms of DPDK project.
Because it's possible to provide native Rust API for DPDK what will use FFI.
Specially, if Rust PMD is not in plans and performance is one of the main goals.

>
>>> Next steps are to "allowlist" more DPDK public API functions, and start building "Safe Rust" APIs over
>>> them, in order to expose an ergonomic and misuse-resistant API. As you note, this is where a network ports,
>>> lcores, mempools, and ethdev configuration are all required. First goal something like "Safe L2 macswap"?

check out for Rx, L2 addr swap, Tx sequence here:
https://github.com/getelson-at-mellanox/rdpdk/blob/253fde7c16a5514bf99d3823725f825262e244c8/app/runpmd/runpmd.rs#L156

Regards,
Gregory

^ permalink raw reply	[relevance 0%]

* Re: [RFC PATCH] add rust binding support to DPDK
  2025-04-13  8:07  0%           ` Etelson, Gregory
@ 2025-04-13 17:09  3%             ` Owen Hilyard
  0 siblings, 0 replies; 153+ results
From: Owen Hilyard @ 2025-04-13 17:09 UTC (permalink / raw)
  To: Etelson, Gregory, Van Haaren, Harry; +Cc: Richardson, Bruce, dev

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

Hello all,

I wanted to chime in as someone who uses DPDK from Rust in quite a few projects.

No snips so the conversation can continue past me for things I don't comment on.
Hello Harry,

>>>> My concern is how to properly maintain Rust crate once DPDK starts to implement
>>>> it's own API.
>>>
>>> I'm not really sure what is meant here. I don't understand what "own" word refers to?
>>>
>>> I see it like this:
>>> - DPDK has the public C API exported (and that stays the same as today, with ABI rules, version.map files, etc)
>>> - The Rust crate consumes the public C API (via bindgen, as done in this patch. More detail about bindgen below.)
>>>
>>
>> Bindgen cannot provide access to all DPDK public API.
>
> Ah - you're referring to C static inline functions, declared in header files, which bindgen doesn't wrap.
> Correct - thanks - I understand your point now.
>
>> A good example here is rte_eth_rx_burst().
>> That function is defined as inline and bindgen does not translate it.
>> Also, the function definition references rte_eth_fp_ops array that is not part of the
>> public DPDK API. That means Rust cannot duplicate rte_eth_rx_burst() "as-is" and
>> the solution can require extensions to existing DPDK API.
>>
>> I added a new public API that exports rte_eth_fp_ops for a given port Id.
>>
>> Rust implementation of rte_eth_rx_burst() does not have to follow the original
>> approach.
>> Usage of rte_eth_fp_ops is good for C, but Rust has different methods.
>
> Agreed there is a certain "mismatch" sometimes, if functions aren't in the
> actually "C ABI" then they can't be called via bindgen.
>
> Agree that elegant solutions (clean, maintainable, and high performance) will have
> to be found here. Many existing solutions just wrap the "static inline" function into
> a "non-static" function, and export it as a public symbol. That allows calling into it
> from Rust (via bindgen-generated header) however causes an actual function call..
> (LTO _might_ fix/inline it, but not everybody compiles with LTO.. link times!)
>

The core DPDK code is maintained as a self-contained pure C project.
What about extending that model and provide direct access to DPDK resources that
are needed for Rust API ?
That can be arranged as part of "native" DPDK API or as extension for
Rust-enabled DPDK only.
I'm currently experimenting with the latter in
https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fgetelson-at-mellanox%2Frdpdk%2Ftree%2Fmain%2Fdpdk-patches&data=05%7C02%7Cowen.hilyard%40unh.edu%7C59c29799f1544a59f44108dd7a62352a%7Cd6241893512d46dc8d2bbe47e25f5666%7C0%7C0%7C638801284429834568%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=3YrOkklWRWlwKyfg9BblBnLsNqnCEf7RdAxdUW3JGgM%3D&reserved=0<https://github.com/getelson-at-mellanox/rdpdk/tree/main/dpdk-patches>
There are some performance wins to be found along this path. It's not a lot, but you can "devirtualize" a lot of DPDK if you use Rust's generics to specialize on particular hardware combinations, at the cost of multiplying code size by the number of supported NICs. I can do this in my bindings since I tend to only need to test a few software PMDs and the hardware I have, with a fallback to "dyn dpdk::EthDev" which pulls data from info structs all of the time, instead of only pulling runtime-only information (ex: mac addr, rss key). This is paired with a top-level function which does dispatch for different hardware to "lift" the runtime information about what NIC is used to compile time. I think the main win here is from inlining driver functions, but I haven't done a detailed analysis of the "why". These are marginal improvements, around a half percent for nic_single_core_perf on my hardware, but there may be more wins for less heavily optimized paths through DPDK where the compiler can do more heavy lifting.
> As DPDK uses static-inline functions primarily for "packet-at-a-time" performance reasons,
> it is unfortunate to give-up (some small amounts of..?) performance by having a C->Rust FFI call.
> We have work to do to find/propose the best solution.

What exactly is a small amount of performance degradation ?
For some existing performance oriented projects loosing performance to
eliminate the `unsafe {}` construct is not an option.
If you are willing to compile with clang and use LTO, both rustc and clang will emit LLVM IR, which gets inlined at link time. I haven't been able to measure a performance difference vs C code even when calling "static inline" hot path function wrappers across the FFI boundary. This does increase compile times by quite a bit, more than LTO with pure C would increase them due to the amount of LLVM IR that rustc emits, but it means that the performance difference barely exists if it does at all.
>
>> For conclusion, Rust DPDK infrastructure cannot relay on bindgen only and needs
>> to provide native implementation for some public DPDK API.
>> It can be easier to maintain Rust files separately.
>
> OK, now I understand your "kind-of-C-DPDK, kind of Rust-DPDK" code, or the "own" code reference above.
> I'm not sure right now where that would be best implemented/maintained, I'll have to think about it and do a POC.
>
>
>> You can check out my Rust DPDK version here: https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fgetelson-at-mellanox%2Frdpdk&data=05%7C02%7Cowen.hilyard%40unh.edu%7C59c29799f1544a59f44108dd7a62352a%7Cd6241893512d46dc8d2bbe47e25f5666%7C0%7C0%7C638801284429852869%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=Qxiozd%2B2%2BnlqsKeuzzVdsU1egsfukfgNEyQyZASpLr8%3D&reserved=0<https://github.com/getelson-at-mellanox/rdpdk>
>
> Thanks for the link - I see you've been pushing code actively! Good to see opportunities,
> and compare approaches and concepts. Have you investigated wrapping the various pointers
> into structs, and providing safer APIs? For example, the [*mut rte_mbuf; 64] array for RX causes
> raw pointers to be handled for all packet-processing - resulting in "unsafe{ /* work here */ }" blocks.
>

This construct can work:

struct PktsBuf<const SIZE: usize> {
        buffer: [*mut rte_mbuf; SIZE]
}

I think that they mean a construct that wraps a *mut rte_mbuf and provides a safe interface. Another option, depending on how invasive the bindings are allowed to be, is to make them references with a lifetime tied to the mempool they are allocated from. For many usecases, mempools exist for the entire lifetime of the program and have static lifetimes, meaning that the references have static lifetimes which greatly simplifies a lot of the code. This also gets rid of unsafe accesses. However, it would require a wrapper around the buffer with the valid length, something like a fixed-capacity vector. Another option is to make the pointers Option<NonNull<rte_mbuf>>, which has the same ABI as a C *rte_mbuf but forces null checks. This will be annoying to program against, but for "one packet at a time to completion" processing it's equivalent to "if (buffer[i] != null) { ... }" at the top of the loop. For the pipeline or graph libraries, this latter option will incur a lot of potentially extra null checks, but the upside is that it's ABI compatible with the burst functions from the C API.
> The code in the above repo feels like "DPDK C code written in Rust".

Agree.
At this stage there is no native Rust DPDK API.

> It is a great step towards better
> understanding, and having something that works is very valuable; thanks for sharing it.
>
> A "top down" application view might help to brainstorm idiomatic/Safe Rust APIs, and then we can
> discuss/understand how to map these high level APIs onto the "DPDK C in Rust" or even "DPDK C API/ABI" layers.
>
> Does that seem like a good method to you, to achieve an ergonomic Safe Rust API as the end result?
>

I think we need to conclude what the safe API means in terms of DPDK project.
Because it's possible to provide native Rust API for DPDK what will use FFI.
Specially, if Rust PMD is not in plans and performance is one of the main goals.
I think most of the value of Rust can be gotten by making a good Rust API available to consumers of DPDK. There is value in having Rust for PMDs, but there is a lot more code written which consumes DPDK than code in new PMDs. It might be better to hold that conversation until someone wants to write a Rust PMD. I think that there is also value in a version which is "maximum safety" that takes performance hits where necessary. DPDK is far enough ahead of the performance of most other options that "DPDK at 80%" is still going to be fast enough for many purposes and the extra safety is valuable there from an ease-of-use perspective. There are, of course, applications which need everything DPDK has to offer from a performance perspective, and those should be served as well, but I think DPDK can offer a spectrum where users get steadily closer to "zero overhead abstraction over the DPDK C API" the more performance they need, possibly sacrificing some safety along the way.

>
>>> Next steps are to "allowlist" more DPDK public API functions, and start building "Safe Rust" APIs over
>>> them, in order to expose an ergonomic and misuse-resistant API. As you note, this is where a network ports,
>>> lcores, mempools, and ethdev configuration are all required. First goal something like "Safe L2 macswap"?

check out for Rx, L2 addr swap, Tx sequence here:
https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fgetelson-at-mellanox%2Frdpdk%2Fblob%2F253fde7c16a5514bf99d3823725f825262e244c8%2Fapp%2Frunpmd%2Frunpmd.rs%23L156&data=05%7C02%7Cowen.hilyard%40unh.edu%7C59c29799f1544a59f44108dd7a62352a%7Cd6241893512d46dc8d2bbe47e25f5666%7C0%7C0%7C638801284429865170%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=DKvEoYTKjKEx75AKr16Ge7wN7vigNrv75O1p%2BwTzdH0%3D&reserved=0<https://github.com/getelson-at-mellanox/rdpdk/blob/253fde7c16a5514bf99d3823725f825262e244c8/app/runpmd/runpmd.rs#L156>

Regards,
Gregory

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

^ permalink raw reply	[relevance 3%]

* Re: [RFC PATCH 1/4] ethdev: add support to provide link type
  2025-04-04  0:46  3% ` Stephen Hemminger
@ 2025-04-15  7:08  3%   ` Nithin Dabilpuram
  2025-04-16  0:03  0%     ` Stephen Hemminger
  0 siblings, 1 reply; 153+ results
From: Nithin Dabilpuram @ 2025-04-15  7:08 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Nithin Dabilpuram, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev

On Fri, Apr 4, 2025 at 6:16 AM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Thu, 3 Apr 2025 12:38:34 +0530
> Nithin Dabilpuram <ndabilpuram@marvell.com> wrote:
>
> >  /**
> >   * A structure used to retrieve link-level information of an Ethernet port.
> >   */
> > @@ -341,6 +354,7 @@ struct rte_eth_link {
> >                       uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
> >                       uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
> >                       uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
> > +                     uint16_t link_type    : 5;  /**< RTE_ETH_LINK_TYPE_* */
> >               };
> >       };
> >  };
>
> Seems like an ABI break, and not sure that all drivers will fill those bits with zero now.

Generally ABI is between APP and all DPDK libraries/PMD and not
between DPDK libraries and PMD ?
For example:

#1 App build with DPDK 24.11, DPDK libraries and PMD's based on 24.11
#2 App built with DPDK 24.11, DPDK libraries and PMD's based on 25.07
#3 App build with DPDK 24.11, DPDK libraries based on 25.07 and PMD's
based on 24.11
#4 App build with DPDK 24.11, DPDK libraries based on 24.11 and PMD's
based on 25.07


For scenario #2, there is no issue if my change can includes memset in
ethdev library ?
Scenario #3 and Scenario #4 are not really valid right ?






Scenario #2 is not possible right ?

^ permalink raw reply	[relevance 3%]

* Re: [RFC PATCH 1/4] ethdev: add support to provide link type
  2025-04-15  7:08  3%   ` Nithin Dabilpuram
@ 2025-04-16  0:03  0%     ` Stephen Hemminger
  2025-04-16  9:08  0%       ` Nithin Dabilpuram
  0 siblings, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-04-16  0:03 UTC (permalink / raw)
  To: Nithin Dabilpuram
  Cc: Nithin Dabilpuram, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev

On Tue, 15 Apr 2025 12:38:18 +0530
Nithin Dabilpuram <nithind1988@gmail.com> wrote:

> On Fri, Apr 4, 2025 at 6:16 AM Stephen Hemminger
> <stephen@networkplumber.org> wrote:
> >
> > On Thu, 3 Apr 2025 12:38:34 +0530
> > Nithin Dabilpuram <ndabilpuram@marvell.com> wrote:
> >  
> > >  /**
> > >   * A structure used to retrieve link-level information of an Ethernet port.
> > >   */
> > > @@ -341,6 +354,7 @@ struct rte_eth_link {
> > >                       uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
> > >                       uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
> > >                       uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
> > > +                     uint16_t link_type    : 5;  /**< RTE_ETH_LINK_TYPE_* */
> > >               };
> > >       };
> > >  };  
> >
> > Seems like an ABI break, and not sure that all drivers will fill those bits with zero now.  
> 
> Generally ABI is between APP and all DPDK libraries/PMD and not
> between DPDK libraries and PMD ?

The problem is rte_eth_link is returne by rte_eth_link_get which is exposed
to the application.

^ permalink raw reply	[relevance 0%]

* Re: [RFC PATCH 1/4] ethdev: add support to provide link type
  2025-04-16  0:03  0%     ` Stephen Hemminger
@ 2025-04-16  9:08  0%       ` Nithin Dabilpuram
  0 siblings, 0 replies; 153+ results
From: Nithin Dabilpuram @ 2025-04-16  9:08 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Nithin Dabilpuram, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev

On Wed, Apr 16, 2025 at 5:33 AM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Tue, 15 Apr 2025 12:38:18 +0530
> Nithin Dabilpuram <nithind1988@gmail.com> wrote:
>
> > On Fri, Apr 4, 2025 at 6:16 AM Stephen Hemminger
> > <stephen@networkplumber.org> wrote:
> > >
> > > On Thu, 3 Apr 2025 12:38:34 +0530
> > > Nithin Dabilpuram <ndabilpuram@marvell.com> wrote:
> > >
> > > >  /**
> > > >   * A structure used to retrieve link-level information of an Ethernet port.
> > > >   */
> > > > @@ -341,6 +354,7 @@ struct rte_eth_link {
> > > >                       uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
> > > >                       uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
> > > >                       uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
> > > > +                     uint16_t link_type    : 5;  /**< RTE_ETH_LINK_TYPE_* */
> > > >               };
> > > >       };
> > > >  };
> > >
> > > Seems like an ABI break, and not sure that all drivers will fill those bits with zero now.
> >
> > Generally ABI is between APP and all DPDK libraries/PMD and not
> > between DPDK libraries and PMD ?
>
> The problem is rte_eth_link is returne by rte_eth_link_get which is exposed
> to the application.

Which scenario you are talking about here ?

^ permalink raw reply	[relevance 0%]

* Re: [PATCH V2] ethdev_trace.h: Update the trace point function when _TIME_BITS=64
  @ 2025-04-23 18:00  4% ` Stephen Hemminger
  2025-04-27  2:06  0%   ` Changqing Li
  0 siblings, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-04-23 18:00 UTC (permalink / raw)
  To: changqing.li; +Cc: dev

On Tue, 22 Apr 2025 20:29:56 +0800
<changqing.li@windriver.com> wrote:

> +#if defined(_TIME_BITS) && _TIME_BITS == 64
>  RTE_TRACE_POINT(
>  	rte_eth_trace_timesync_write_time,
>  	RTE_TRACE_POINT_ARGS(uint16_t port_id, const struct timespec *time,
>  		int ret),
>  	rte_trace_point_emit_u16(port_id);
> +	rte_trace_point_emit_u64(time->tv_sec);
> +	rte_trace_point_emit_long(time->tv_nsec);
> +	rte_trace_point_emit_int(ret);
> +)
> +#else
> +RTE_TRACE_POINT(
> +	rte_eth_trace_timesync_write_time,
> +	RTE_TRACE_POINT_ARGS(uint16_t port_id, const struct timespec *time,
> +	  int ret),
> +	rte_trace_point_emit_u16(port_id);
>  	rte_trace_point_emit_size_t(time->tv_sec);
>  	rte_trace_point_emit_long(time->tv_nsec);
>  	rte_trace_point_emit_int(ret);
>  )
> +#endif

No. Do not start adding #ifdef to trace points.
Instead, add new hook rte_trace_point_emit_time_t and that can handle
any ABI changes like this.

Best to wait until 25.11 release since could be ABI change.

^ permalink raw reply	[relevance 4%]

* Re: [PATCH V2] ethdev_trace.h: Update the trace point function when _TIME_BITS=64
  2025-04-23 18:00  4% ` Stephen Hemminger
@ 2025-04-27  2:06  0%   ` Changqing Li
  0 siblings, 0 replies; 153+ results
From: Changqing Li @ 2025-04-27  2:06 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev

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


On 4/24/25 02:00, Stephen Hemminger wrote:
> CAUTION: This email comes from a non Wind River email account!
> Do not click links or open attachments unless you recognize the sender and know the content is safe.
>
> On Tue, 22 Apr 2025 20:29:56 +0800
> <changqing.li@windriver.com>  wrote:
>
>> +#if defined(_TIME_BITS) && _TIME_BITS == 64
>>   RTE_TRACE_POINT(
>>        rte_eth_trace_timesync_write_time,
>>        RTE_TRACE_POINT_ARGS(uint16_t port_id, const struct timespec *time,
>>                int ret),
>>        rte_trace_point_emit_u16(port_id);
>> +     rte_trace_point_emit_u64(time->tv_sec);
>> +     rte_trace_point_emit_long(time->tv_nsec);
>> +     rte_trace_point_emit_int(ret);
>> +)
>> +#else
>> +RTE_TRACE_POINT(
>> +     rte_eth_trace_timesync_write_time,
>> +     RTE_TRACE_POINT_ARGS(uint16_t port_id, const struct timespec *time,
>> +       int ret),
>> +     rte_trace_point_emit_u16(port_id);
>>        rte_trace_point_emit_size_t(time->tv_sec);
>>        rte_trace_point_emit_long(time->tv_nsec);
>>        rte_trace_point_emit_int(ret);
>>   )
>> +#endif
> No. Do not start adding #ifdef to trace points.
> Instead, add new hook rte_trace_point_emit_time_t and that can handle
> any ABI changes like this.

Hi, Stephen

Thanks,  I will try to add this.

>
> Best to wait until 25.11 release since could be ABI change.

Do you mean I should not send V3 patch? Send the patch after 25.11 is 
release?

Regards

Changqing

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

^ permalink raw reply	[relevance 0%]

* Re: [PATCH v3 1/1] bus/pci: introduce get_iova_mode for pci dev
  @ 2025-04-28 15:50  3%   ` Stephen Hemminger
  0 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-04-28 15:50 UTC (permalink / raw)
  To: Kyo Liu; +Cc: Thomas Monjalon, Chenbo Xia, Nipun Gupta, dev

On Sun, 27 Apr 2025 02:27:34 +0000
Kyo Liu <kyo.liu@nebula-matrix.com> wrote:

> I propose this patch for DPDK to enable coexistence between
> DPDK and kernel drivers for regular NICs.This solution requires
> adding a new pci_ops in rte_pci_driver, through which DPDK will
> retrieve the required IOVA mode from the vendor driver.
> This mechanism is necessary to handle different IOMMU
> configurations and operating modes. Below is a detailed
> analysis of various scenarios:
> 
> 1. When IOMMU is enabled:
> 1.1 With PT (Pass-Through) enabled:
> In this case, the domain type is IOMMU_DOMAIN_IDENTITY,
> which prevents vendor drivers from setting IOVA->PA mapping tables.
> Therefore, DPDK must use PA mode. To achieve this:
> The vendor kernel driver will register a character device (cdev) to
> communicate with DPDK. This cdev handles device operations
> (open, mmap, etc.) and ultimately
> programs the hardware registers.
> 
> 1.2 With PT disabled:
> Here, the vendor driver doesn't enforce specific IOVA mode requirements.
> Our implementation will:
> Integrate a mediated device (mdev) in the vendor driver.
> This mdev interacts with DPDK and manages IOVA->PA mapping configurations.
> 
> 2. When IOMMU is disabled:
> The vendor driver mandates PA mode (consistent with DPDK's PA mode
> requirement in this scenario).
> A character device (cdev) will similarly be registered for DPDK
> communication.
> 
> Summary:
> The solution leverages multiple technologies:
> mdev for IOVA management when IOMMU is partially enabled.
> VFIO for device passthrough operations.
> cdev for register programming coordination.
> A new pci_ops interface in DPDK to dynamically determine IOVA modes.
> This architecture enables clean coexistence by establishing standardized
> communication channels between DPDK and vendor drivers across different
> IOMMU configurations.
> 
> Motivation for the Patch:
> This patch is introduced to prepare for the upcoming open-source
> contribution of our NebulaMatrix SNIC driver to DPDK. We aim to
> ensure that our SNIC can seamlessly coexist with kernel drivers
> using this mechanism. By adopting the proposed
> architecture—leveraging dynamic IOVA mode negotiation via pci_ops,
> mediated devices (mdev), and character device (cdev)
> interactions—we enable our SNIC to operate in hybrid environments
> here both DPDK and kernel drivers may manage the same hardware.
> This design aligns with DPDK’s scalability goals and ensures
> compatibility across diverse IOMMU configurations, which is critical
> for real-world deployment scenarios.
> 
> Signed-off-by: Kyo Liu <kyo.liu@nebula-matrix.com>

Looks like this might also end up being ABI change so may have
to wait for 25.11

^ permalink raw reply	[relevance 3%]

* Re: |FAILURE| pw153190 [PATCH V3] Add new tracepoint function for type time_t
       [not found]     ` <20250430024403.2690306-1-robot@bytheb.org>
@ 2025-04-30  5:21  0%   ` Changqing Li
  0 siblings, 0 replies; 153+ results
From: Changqing Li @ 2025-04-30  5:21 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, thomas

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

Hi,

I'm new to this project, and have no clue about the failure, could 
experts at this project provide

some help about the following failure?

+ sudo babeltrace 
/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21 


Error: at line 2819: token "time_t": syntax error, unexpected IDENTIFIER
Error: Error creating AST


Thanks

Changqing

On 4/30/25 10:44, 0-day Robot wrote:
> From:robot@bytheb.org
>
> Test-Label: github-robot: build
> Test-Status: FAILURE
> http://patchwork.dpdk.org/patch/153190/
>
> _github build: failed_
> Build URL:https://github.com/ovsrobot/dpdk/actions/runs/14744930390
> Build Logs:
> -----------------------Summary of failed steps-----------------------
> "ubuntu-22.04-clang-asan+doc+tests" failed at step Build and test
> "ubuntu-22.04-gcc-abi+debug+doc+examples+tests" failed at step Build and test
> ----------------------End summary of failed steps--------------------
>
> -------------------------------BEGIN LOGS----------------------------
> ####################################################################################
> #### [Begin job log] "ubuntu-22.04-clang-asan+doc+tests" at step Build and test
> ####################################################################################
>
> Full log written to /home/runner/work/dpdk/dpdk/build/meson-logs/testlog.txt
> + catch_coredump
> + ls /tmp/dpdk-core.*.*
> + return 0
> + check_traces
> + which babeltrace
> + sudo find /home/runner -name metadata
> + dirname /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46/metadata
> + sudo babeltrace /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46
> + dirname /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46/metadata
> + sudo babeltrace /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46
> [error] at line 2819: token "time_t": syntax error, unexpected IDENTIFIER
>
> [error] Error creating AST
> [warning] Unable to open trace metadata for path "/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46".
> [warning] [Context] Cannot open_trace of format ctf at path /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46.
> [warning] [Context] cannot open trace "/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46" from /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46 for reading.
> [error] Cannot open any trace for reading.
>
> [error] opening trace "/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-20-46" for reading.
>
> [error] none of the specified trace paths could be opened.
>
> ##[error]Process completed with exit code 1.
> ####################################################################################
> #### [End job log] "ubuntu-22.04-clang-asan+doc+tests" at step Build and test
> ####################################################################################
>
>
>
>
> ####################################################################################
> #### [Begin job log] "ubuntu-22.04-gcc-abi+debug+doc+examples+tests" at step Build and test
> ####################################################################################
>
> Full log written to /home/runner/work/dpdk/dpdk/build/meson-logs/testlog.txt
> + catch_coredump
> + ls /tmp/dpdk-core.*.*
> + return 0
> + check_traces
> + which babeltrace
> + sudo find /home/runner -name metadata
> + dirname /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21/metadata
> + sudo babeltrace /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21
> + dirname /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21/metadata
> + sudo babeltrace /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21
> [error] at line 2819: token "time_t": syntax error, unexpected IDENTIFIER
>
> [error] Error creating AST
> [warning] Unable to open trace metadata for path "/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21".
> [warning] [Context] Cannot open_trace of format ctf at path /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21.
> [warning] [Context] cannot open trace "/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21" from /home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21 for reading.
> [error] Cannot open any trace for reading.
>
> [error] opening trace "/home/runner/work/dpdk/dpdk/build/app/test/suites/rte-2025-04-30-AM-02-25-21" for reading.
>
> [error] none of the specified trace paths could be opened.
>
> ##[error]Process completed with exit code 1.
> ####################################################################################
> #### [End job log] "ubuntu-22.04-gcc-abi+debug+doc+examples+tests" at step Build and test
> ####################################################################################
> --------------------------------END LOGS-----------------------------

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

^ permalink raw reply	[relevance 0%]

* [PATCH v3 1/4] eal: deprecate old coremask-based EAL parameters
  @ 2025-05-02 15:11  2%   ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-05-02 15:11 UTC (permalink / raw)
  To: dev; +Cc: david.marchand, mb, stephen, Bruce Richardson

As the number of cores/cpus on platforms has increased over the years,
the use of coremasks rather than core-lists for identifying DPDK cores
has become more and more unwieldy. At this point, let's deprecate the
coremask-based EAL parameters for future removal, and point users to the
core-list based versions instead.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 doc/guides/eventdevs/dlb2.rst                |  6 +++---
 doc/guides/faq/faq.rst                       |  8 +++-----
 doc/guides/linux_gsg/build_sample_apps.rst   |  7 +++----
 doc/guides/linux_gsg/eal_args.include.rst    |  8 ++------
 doc/guides/prog_guide/meson_ut.rst           |  2 +-
 doc/guides/prog_guide/multi_proc_support.rst |  2 +-
 doc/guides/prog_guide/service_cores.rst      |  8 ++++----
 doc/guides/rel_notes/deprecation.rst         | 10 ++++++++++
 doc/guides/sample_app_ug/ip_frag.rst         |  7 +------
 doc/guides/sample_app_ug/ip_reassembly.rst   |  7 +------
 doc/guides/sample_app_ug/multi_process.rst   | 14 +++++---------
 doc/guides/sample_app_ug/qos_scheduler.rst   |  2 +-
 doc/guides/sample_app_ug/test_pipeline.rst   |  2 +-
 doc/guides/tools/testbbdev.rst               |  2 +-
 lib/eal/common/eal_common_options.c          |  6 ++++++
 15 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/doc/guides/eventdevs/dlb2.rst b/doc/guides/eventdevs/dlb2.rst
index 2532d92888..d1b736830d 100644
--- a/doc/guides/eventdevs/dlb2.rst
+++ b/doc/guides/eventdevs/dlb2.rst
@@ -408,9 +408,9 @@ the DLB device locally available on the same tile along with other
 resources. To allocate optimal resources, probing is done for each
 producer port (PP) for a given CPU and the best performing ports are
 allocated to producers. The cpu used for probing is either the first
-core of producer coremask (if present) or the second core of EAL
-coremask. This will be extended later to probe for all CPUs in the
-producer coremask or EAL coremask. Producer coremask can be passed
+core of producer coremask DLB2 device parameter (if present) or the second core of EAL
+core list. This will be extended later to probe for all CPUs in the
+producer coremask or EAL core list. Producer coremask can be passed
 along with the BDF of the DLB devices.
 
     .. code-block:: console
diff --git a/doc/guides/faq/faq.rst b/doc/guides/faq/faq.rst
index 297cb5119e..ddcb018b3a 100644
--- a/doc/guides/faq/faq.rst
+++ b/doc/guides/faq/faq.rst
@@ -47,11 +47,9 @@ therefore all the hugepages are allocated on the wrong socket.
 To avoid this scenario, either lower the amount of hugepage memory available to 1 GB size (or less), or run the application with taskset
 affinitizing the application to a would-be main core.
 
-For example, if your EAL coremask is 0xff0, the main core will usually be the first core in the coremask (0x10); this is what you have to supply to taskset::
+For example, if your EAL core list is '4-11', the main core will usually be the first core in the list (core 4); this is what you have to supply to taskset::
 
-   taskset 0x10 ./l2fwd -l 4-11 -n 2
-
-.. Note: Instead of '-c 0xff0' use the '-l 4-11' as a cleaner way to define lcores.
+   taskset -c 4 ./l2fwd -l 4-11 -n 2
 
 In this way, the hugepages have a greater chance of being allocated to the correct socket.
 Additionally, a ``--socket-mem`` option could be used to ensure the availability of memory for each socket, so that if hugepages were allocated on
@@ -167,7 +165,7 @@ Can I split packet RX to use DPDK and have an application's higher order functio
 ----------------------------------------------------------------------------------------------------------------
 
 The DPDK's lcore threads are Linux pthreads bound onto specific cores. Configure the DPDK to do work on the same
-cores and run the application's other work on other cores using the DPDK's "coremask" setting to specify which
+cores and run the application's other work on other cores using the DPDK's "core list" (-l/--lcores) setting to specify which
 cores it should launch itself on.
 
 
diff --git a/doc/guides/linux_gsg/build_sample_apps.rst b/doc/guides/linux_gsg/build_sample_apps.rst
index 433839ecc7..c568549f26 100644
--- a/doc/guides/linux_gsg/build_sample_apps.rst
+++ b/doc/guides/linux_gsg/build_sample_apps.rst
@@ -126,10 +126,9 @@ and that cores 0-3 are present and are to be used for running the application)::
 Logical Core Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The coremask (-c 0x0f) or corelist (-l 0-3) parameter is always mandatory for DPDK applications.
-Each bit of the mask corresponds to the equivalent logical core number as reported by Linux. The preferred corelist option is a cleaner method to define cores to be used.
+The corelist (-l/--lcores 0-3) parameter is always mandatory for DPDK applications.
 Since these logical core numbers, and their mapping to specific cores on specific NUMA sockets, can vary from platform to platform,
-it is recommended that the core layout for each platform be considered when choosing the coremask/corelist to use in each case.
+it is recommended that the core layout for each platform be considered when choosing the corelist to use in each case.
 
 On initialization of the EAL layer by a DPDK application, the logical cores to be used and their socket location are displayed.
 This information can also be determined for all cores on the system by examining the ``/proc/cpuinfo`` file, for example, by running cat ``/proc/cpuinfo``.
@@ -151,7 +150,7 @@ This can be useful when using other processors to understand the mapping of the
 
 .. warning::
 
-    The logical core layout can change between different board layouts and should be checked before selecting an application coremask/corelist.
+    The logical core layout can change between different board layouts and should be checked before selecting an application corelist.
 
 Hugepage Memory Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/linux_gsg/eal_args.include.rst b/doc/guides/linux_gsg/eal_args.include.rst
index 9cfbf7de84..7ffd2e2535 100644
--- a/doc/guides/linux_gsg/eal_args.include.rst
+++ b/doc/guides/linux_gsg/eal_args.include.rst
@@ -4,10 +4,6 @@
 Lcore-related options
 ~~~~~~~~~~~~~~~~~~~~~
 
-*   ``-c <core mask>``
-
-    Set the hexadecimal bitmask of the cores to run on.
-
 *   ``-l <core list>``
 
     List of cores to run on
@@ -37,9 +33,9 @@ Lcore-related options
 
     Core ID that is used as main.
 
-*   ``-s <service core mask>``
+*   ``-S <service core list>``
 
-    Hexadecimal bitmask of cores to be used as service cores.
+    List of cores to be used as service cores.
 
 Device-related options
 ~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/prog_guide/meson_ut.rst b/doc/guides/prog_guide/meson_ut.rst
index 78cf3f845c..9bc52a30fc 100644
--- a/doc/guides/prog_guide/meson_ut.rst
+++ b/doc/guides/prog_guide/meson_ut.rst
@@ -60,7 +60,7 @@ Arguments of ``test()`` that can be provided in meson.build are as below:
 
 Note: the content of meson ``--test-args`` option and the content of ``args``
 are appended when invoking the DPDK test binary.
-Because of this, it is recommended not to set any default coremask or memory
+Because of this, it is recommended not to set any default corelist or memory
 configuration in per test ``args`` and rather let users select what best fits
 their environment. If a test can't run, then it should be skipped, as described
 below.
diff --git a/doc/guides/prog_guide/multi_proc_support.rst b/doc/guides/prog_guide/multi_proc_support.rst
index 0c57145470..dd57b6f8c1 100644
--- a/doc/guides/prog_guide/multi_proc_support.rst
+++ b/doc/guides/prog_guide/multi_proc_support.rst
@@ -166,7 +166,7 @@ Some of these are documented below:
     so it is recommended that it be disabled only when absolutely necessary,
     and only when the implications of this change have been understood.
 
-*   All DPDK processes running as a single application and using shared memory must have distinct coremask/corelist arguments.
+*   All DPDK processes running as a single application and using shared memory must have distinct corelist arguments.
     It is not possible to have a primary and secondary instance, or two secondary instances,
     using any of the same logical cores.
     Attempting to do so can cause corruption of memory pool caches, among other issues.
diff --git a/doc/guides/prog_guide/service_cores.rst b/doc/guides/prog_guide/service_cores.rst
index d4e6c3d6e6..5284eeb96a 100644
--- a/doc/guides/prog_guide/service_cores.rst
+++ b/doc/guides/prog_guide/service_cores.rst
@@ -26,10 +26,10 @@ Service Core Initialization
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 There are two methods to having service cores in a DPDK application, either by
-using the service coremask, or by dynamically adding cores using the API.
-The simpler of the two is to pass the `-s` coremask argument to EAL, which will
-take any cores available in the main DPDK coremask, and if the bits are also set
-in the service coremask the cores become service-cores instead of DPDK
+using the service corelist, or by dynamically adding cores using the API.
+The simpler of the two is to pass the `-S` corelist argument to EAL, which will
+take any cores available in the main DPDK corelist, and if also set
+in the service corelist the cores become service-cores instead of DPDK
 application lcores.
 
 Enabling Services on Cores
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 36489f6e68..2ea898ff8a 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -17,6 +17,16 @@ Other API and ABI deprecation notices are to be posted below.
 Deprecation Notices
 -------------------
 
+* EAL: The ``-c <coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-l <corelist>`` or ``--lcores=<corelist>`` parameters instead
+  to specify the cores to be used when running a DPDK application.
+
+* EAL: The ``-s <service-coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-S <service-corelist>`` parameter instead
+  to specify the cores to be used for background services in DPDK.
+
 * build: The ``enable_kmods`` option is deprecated and will be removed in a future release.
   Setting/clearing the option has no impact on the build.
   Instead, kernel modules will be always built for OS's where out-of-tree kernel modules
diff --git a/doc/guides/sample_app_ug/ip_frag.rst b/doc/guides/sample_app_ug/ip_frag.rst
index 4b2071cbae..3f0b5ce5e7 100644
--- a/doc/guides/sample_app_ug/ip_frag.rst
+++ b/doc/guides/sample_app_ug/ip_frag.rst
@@ -67,12 +67,7 @@ To run the example in linux environment with 2 lcores (2,4) over 2 ports(0,2) wi
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-ip_fragmentation -l 2,4 -n 3 -- -p 5
-    EAL: coremask set to 14
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 1
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 1
-    EAL: Detected lcore 4 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     Initializing port 0 on lcore 2... Address:00:1B:21:76:FA:2C, rxq=0 txq=2,0 txq=4,1
diff --git a/doc/guides/sample_app_ug/ip_reassembly.rst b/doc/guides/sample_app_ug/ip_reassembly.rst
index b8800dd9e7..e55ee7eb79 100644
--- a/doc/guides/sample_app_ug/ip_reassembly.rst
+++ b/doc/guides/sample_app_ug/ip_reassembly.rst
@@ -63,12 +63,7 @@ with 1 Rx queue per lcore:
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-ip_reassembly -l 2,4 -n 3 -- -p 5
-    EAL: coremask set to 14
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 1
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 1
-    EAL: Detected lcore 4 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     Initializing port 0 on lcore 2... Address:00:1B:21:76:FA:2C, rxq=0 txq=2,0 txq=4,1
diff --git a/doc/guides/sample_app_ug/multi_process.rst b/doc/guides/sample_app_ug/multi_process.rst
index e2b1b16c84..29bca806e1 100644
--- a/doc/guides/sample_app_ug/multi_process.rst
+++ b/doc/guides/sample_app_ug/multi_process.rst
@@ -36,7 +36,7 @@ Running the Application
 ^^^^^^^^^^^^^^^^^^^^^^^
 
 To run the application, start ``simple_mp`` binary in one terminal,
-passing at least two cores in the coremask/corelist:
+passing at least two cores in the corelist:
 
 .. code-block:: console
 
@@ -50,11 +50,7 @@ The process should start successfully and display a command prompt as follows:
 .. code-block:: console
 
     $ ./<build_dir>/examples/dpdk-simple_mp -l 0-1 -n 4 --proc-type=primary
-    EAL: coremask set to 3
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 0
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     EAL: Requesting 2 pages of size 1073741824
@@ -72,7 +68,7 @@ The process should start successfully and display a command prompt as follows:
     simple_mp >
 
 To run the secondary process to communicate with the primary process,
-again run the same binary setting at least two cores in the coremask/corelist:
+again run the same binary setting at least two cores in the corelist:
 
 .. code-block:: console
 
@@ -237,8 +233,8 @@ In addition to the EAL parameters, the application-specific parameters are:
 .. note::
 
    In the server process, has a single thread using the lowest numbered lcore
-   in the coremask/corelist, performs all packet I/O.
-   If coremask/corelist parameter specifies with more than a single lcore bit set,
+   in the corelist, performs all packet I/O.
+   If corelist parameter specifies with more than a single lcore,
    an additional lcore will be used for a thread to print packet count periodically.
 
 The server application stores configuration data in shared memory,
diff --git a/doc/guides/sample_app_ug/qos_scheduler.rst b/doc/guides/sample_app_ug/qos_scheduler.rst
index 9936b99172..36ada4902c 100644
--- a/doc/guides/sample_app_ug/qos_scheduler.rst
+++ b/doc/guides/sample_app_ug/qos_scheduler.rst
@@ -194,7 +194,7 @@ Another example with 2 packet flow configurations using different ports but shar
 Note that independent cores for the packet flow configurations for each of the RX, WT and TX thread are also supported,
 providing flexibility to balance the work.
 
-The EAL coremask/corelist is constrained to contain the default main core 1 and the RX, WT and TX cores only.
+The EAL corelist is constrained to contain the default main core 1 and the RX, WT and TX cores only.
 
 Explanation
 -----------
diff --git a/doc/guides/sample_app_ug/test_pipeline.rst b/doc/guides/sample_app_ug/test_pipeline.rst
index d57d08fb2c..818be93cd6 100644
--- a/doc/guides/sample_app_ug/test_pipeline.rst
+++ b/doc/guides/sample_app_ug/test_pipeline.rst
@@ -47,7 +47,7 @@ The application execution command line is:
 
     ./dpdk-test-pipeline [EAL options] -- -p PORTMASK --TABLE_TYPE
 
-The -c or -l EAL CPU coremask/corelist option has to contain exactly 3 CPU cores.
+The ``-l/--lcores`` EAL CPU corelist option has to contain exactly 3 CPU cores.
 The first CPU core in the core mask is assigned for core A, the second for core B and the third for core C.
 
 The PORTMASK parameter must contain 2 or 4 ports.
diff --git a/doc/guides/tools/testbbdev.rst b/doc/guides/tools/testbbdev.rst
index ddb8d787be..8677cd2c43 100644
--- a/doc/guides/tools/testbbdev.rst
+++ b/doc/guides/tools/testbbdev.rst
@@ -78,7 +78,7 @@ The following are the command-line options:
 
 ``-l NUM_LCORES, --num_lcores NUM_LCORES``
  Specifies number of lcores to run. If not specified num_lcores is set
- according to value from RTE configuration (EAL coremask)
+ according to value from RTE configuration (EAL corelist)
 
 ``-b BURST_SIZE [BURST_SIZE ...], --burst-size BURST_SIZE [BURST_SIZE ...]``
  Specifies operations enqueue/dequeue burst size. If not specified burst_size is
diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index b6fff7ec05..19c5997c7c 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -614,6 +614,9 @@ eal_parse_service_coremask(const char *coremask)
 	int val;
 	uint32_t taken_lcore_count = 0;
 
+	EAL_LOG(WARNING, "'-s <service-coremask>' is deprecated, and will be removed in a future release.");
+	EAL_LOG(WARNING, "\tUse '-S <service-corelist>' option instead.");
+
 	if (coremask == NULL)
 		return -1;
 	/* Remove all blank characters ahead and after .
@@ -777,6 +780,9 @@ rte_eal_parse_coremask(const char *coremask, int *cores)
 		cores[idx] = -1;
 	idx = 0;
 
+	EAL_LOG(WARNING, "'-c <coremask>' option is deprecated, and will be removed in a future release");
+	EAL_LOG(WARNING, "\tUse '-l <corelist>' or '--lcores=<corelist>' option instead");
+
 	/* Remove all blank characters ahead and after .
 	 * Remove 0x/0X if exists.
 	 */
-- 
2.45.2


^ permalink raw reply	[relevance 2%]

* [PATCH] doc: reword contributor's guidelines
@ 2025-05-12 14:48 12% Nandini Persad
  0 siblings, 0 replies; 153+ results
From: Nandini Persad @ 2025-05-12 14:48 UTC (permalink / raw)
  To: Maxime Coquelin; +Cc: dev

I have revised sections 9-12 for grammar and clarity.

Signed-off-by: Nandini Persad <nandinipersad361@gmail.com>
---
 doc/guides/contributing/linux_uapi.rst    |  32 ++--
 doc/guides/contributing/patches.rst       | 177 +++++++++++-----------
 doc/guides/contributing/stable.rst        | 163 ++++++++------------
 doc/guides/contributing/vulnerability.rst |  59 ++++----
 4 files changed, 193 insertions(+), 238 deletions(-)

diff --git a/doc/guides/contributing/linux_uapi.rst b/doc/guides/contributing/linux_uapi.rst
index 79bedb478e..72abef2133 100644
--- a/doc/guides/contributing/linux_uapi.rst
+++ b/doc/guides/contributing/linux_uapi.rst
@@ -7,14 +7,14 @@ Linux uAPI header files
 Rationale
 ---------
 
-The system a DPDK library or driver is built on is not necessarily running the
-same Kernel version than the system that will run it.
-Importing Linux Kernel uAPI headers enable to build features that are not
-supported yet by the build system.
+The system used to build a DPDK library or driver may not be running the same kernel version
+as the target system where it will be deployed. To support features that are not yet available
+in the build system’s kernel, Linux kernel uAPI headers can be imported.
 
-For example, the build system runs upstream Kernel v5.19 and we would like to
-build a VDUSE application that will use VDUSE_IOTLB_GET_INFO ioctl() introduced
-in Linux Kernel v6.0.
+For example, if the build system runs upstream Kernel v5.19, but you need to build a VDUSE application
+that uses the VDUSE_IOTLB_GET_INFO ioctl introduced in Kernel v6.0, importing the relevant uAPI headers allows this.
+
+The Linux kernel's syscall license exception permits the inclusion of unmodified uAPI header files in such cases.
 
 `Linux Kernel licence exception regarding syscalls
 <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/LICENSES/exceptions/Linux-syscall-note>`_
@@ -23,20 +23,19 @@ enable importing unmodified Linux Kernel uAPI header files.
 Importing or updating an uAPI header file
 -----------------------------------------
 
-In order to ensure the imported uAPI headers are both unmodified and from a
-released version of the linux Kernel, a helper script is made available and
-MUST be used.
+To ensure that imported uAPI headers are unmodified and sourced from an official Linux
+kernel release, a helper script is provided and must be used.
 Below is an example to import ``linux/vduse.h`` file from Linux ``v6.10``:
 
 .. code-block:: console
 
    devtools/linux-uapi.sh -i linux/vduse.h -u v6.10
 
-Once imported, the header files should be committed without any other change.
-Note that all the imported headers will be updated to the requested version.
+Once imported, header files must be committed without any modifications. Note that all imported
+headers will be updated to match the specified kernel version.
 
-Updating imported headers to a newer released version should only be done on
-a need basis, it can be achieved using the same script:
+Updates to a newer released version should be performed only when necessary, and can be done
+using the same helper script.
 
 .. code-block:: console
 
@@ -60,8 +59,9 @@ Note that all the linux-uapi.sh script options can be combined. For example:
 Header inclusion into library or driver
 ---------------------------------------
 
-The library or driver willing to make use of imported uAPI headers needs to
-explicitly include the header file with ``uapi/`` prefix in C files.
+Libraries or drivers that rely on imported uAPI headers must explicitly include
+the relevant header using the ``uapi/`` prefix in their C source files.
+
 For example to include VDUSE uAPI:
 
 .. code-block:: c
diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index d21ee288b2..88945b8f5d 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -5,56 +5,53 @@
 
 Contributing Code to DPDK
 =========================
+his document provides guidelines for submitting code to DPDK.
 
-This document outlines the guidelines for submitting code to DPDK.
-
-The DPDK development process is modeled (loosely) on the Linux Kernel development model so it is worth reading the
-Linux kernel guide on submitting patches:
+The DPDK development process is loosely based on the Linux Kernel development
+model. It is recommended to review the Linux kernel's guide:
 `How to Get Your Change Into the Linux Kernel <https://www.kernel.org/doc/html/latest/process/submitting-patches.html>`_.
-The rationale for many of the DPDK guidelines is explained in greater detail in the kernel guidelines.
+Many of DPDK's submissions guidelines draw from the kernel process,
+and the rationale behind them is often explained in greater depth there.
 
 
 The DPDK Development Process
 ----------------------------
 
-The DPDK development process has the following features:
+The DPDK development process includes the following key elements:
 
-* The code is hosted in a public git repository.
-* There is a mailing list where developers submit patches.
-* There are maintainers for hierarchical components.
+* Code is maintained in a public Git repository.
+* Developers submit patches via a public mailing list.
+* Maintainers are assigned to different components in a hierarchical structure.
 * Patches are reviewed publicly on the mailing list.
-* Successfully reviewed patches are merged to the repository.
-* Patches should be sent to the target repository or sub-tree, see below.
+* Once reviewed, patches are merged into the repository.
+* Patches should be sent to the target repository or sub-tree (see below for details).
 
 The mailing list for DPDK development is `dev@dpdk.org <https://mails.dpdk.org/archives/dev/>`_.
 Contributors will need to `register for the mailing list <https://mails.dpdk.org/listinfo/dev>`_ in order to submit patches.
-It is also worth registering for the DPDK `Patchwork <https://patches.dpdk.org/project/dpdk/list/>`_
+You should also register for DPDK `Patchwork <https://patches.dpdk.org/project/dpdk/list/>`_
 
-If you are using the GitHub service, pushing to a branch will trigger GitHub
-Actions to automatically build your changes and run unit tests and ABI checks.
+If you're using GitHub, pushing to a branch will trigger GitHub Actions
+which will build your changes and run unit tests and ABI checks.
 
-The development process requires some familiarity with the ``git`` version control system.
-Refer to the `Pro Git Book <http://www.git-scm.com/book/>`_ for further information.
+Contributing to DPDK requires a basic understanding of the Git control system.
+For more information, refer to the `Pro Git Book <http://www.git-scm.com/book/>`_.
 
 Source License
 --------------
 
-The DPDK uses the Open Source BSD-3-Clause license for the core libraries and
-drivers. The kernel components are GPL-2.0 licensed. DPDK uses single line
-reference to Unique License Identifiers in source files as defined by the Linux
-Foundation's `SPDX project <http://spdx.org/>`_.
+DPDK uses the Open Source BSD-3-Clause license for its core libraries and
+drivers. Kernel components are GPL-2.0 licensed.
+To identify licenses  in source files, DPDK follows the SPDX standard
+developed by the Linux Foundation `SPDX project <http://spdx.org/>`_.
 
-DPDK uses first line of the file to be SPDX tag. In case of *#!* scripts, SPDX
-tag can be placed in 2nd line of the file.
+The SPDX tag should by used on the first line of the file.
+In case of *#!* scripts, the SPDX tag should be placed on 2nd line.
 
-For example, to label a file as subject to the BSD-3-Clause license,
-the following text would be used:
+For BSD-3-Clause:
 
 ``SPDX-License-Identifier: BSD-3-Clause``
 
-To label a file as dual-licensed with BSD-3-Clause and GPL-2.0 (e.g., for code
-that is shared between the kernel and userspace), the following text would be
-used:
+For dual-licensing with BSD-3-Clause and GPL-2.0 (e.g.,shared kernel/user-space code):
 
 ``SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0)``
 
@@ -87,37 +84,41 @@ Where:
 
 Additional details are given in the ``MAINTAINERS`` file.
 
-The role of the component maintainers is to:
+Component maintainers are responsible for the following:
 
-* Review patches for the component or delegate the review.
+* Reviewing patches related to their component or delegating the review to others.
   The review should be done, ideally, within 1 week of submission to the mailing list.
-* Add an ``acked-by`` to patches, or patchsets, that are ready for committing to a tree.
-* Reply to questions asked about the component.
+* Acknowledging patches by adding an ``acked-by`` tag to those deemed ready for merging.
+* Responding to questions regarding the component and offering guidance when needed.
+
+Maintainers can be added or removed by submitting a patch to the ``MAINTAINERS`` file.
+New maintainers should have demonstrated consistent contributions or reviews to the component area.
+Their addition should be confirmed by an ``ack`` from an established contributor.
+Multiple maintainers may be assigned to a single component, if needed.
+
+Tree Maintainer Responsibilities:
+
+Tree maintainers are responsible for ensuring the overall quality and integrity of their tree.
+Their duties include:
+
+* Conducting additional reviews, compilation checks, or other tests as needed.
+* Committing patches that have been sufficiently reviewed by component maintainers or other contributors.
+* Ensuring timely review of submitted patches.
+* Preparing the tree for integration.
+* Appointing a designated backup maintainer and coordinating handovers when unavailable.
 
-Component maintainers can be added or removed by submitting a patch to the ``MAINTAINERS`` file.
-Maintainers should have demonstrated a reasonable level of contributions or reviews to the component area.
-The maintainer should be confirmed by an ``ack`` from an established contributor.
-There can be more than one component maintainer if desired.
+Maintainer Changes:
 
-The role of the tree maintainers is to:
+Tree maintainers can be added or removed by submitting a patch to the MAINTAINERS file.
+Proposals must justify the creation of a new sub-tree and demonstrate a significant contribution
+to the relevant area. An ack from an existing tree maintainer is required.
 
-* Maintain the overall quality of their tree.
-  This can entail additional review, compilation checks or other tests deemed necessary by the maintainer.
-* Commit patches that have been reviewed by component maintainers and/or other contributors.
-  The tree maintainer should determine if patches have been reviewed sufficiently.
-* Ensure that patches are reviewed in a timely manner.
-* Prepare the tree for integration.
-* Ensure that there is a designated back-up maintainer and coordinate a handover for periods where the
-  tree maintainer can't perform their role.
+In case of disputes regarding trees or maintainers, issues may be escalated to the Technical Board.
 
-Tree maintainers can be added or removed by submitting a patch to the ``MAINTAINERS`` file.
-The proposer should justify the need for a new sub-tree and should have demonstrated a sufficient level of contributions in the area or to a similar area.
-The maintainer should be confirmed by an ``ack`` from an existing tree maintainer.
-Disagreements on trees or maintainers can be brought to the Technical Board.
+Backup Maintainers:
 
-The backup maintainer for the main tree should be selected
-from the existing sub-tree maintainers of the project.
-The backup maintainer for a sub-tree should be selected from among the component maintainers within that sub-tree.
+* The main tree's backup maintainer should be chosen from among existing sub-tree maintainers.
+* A sub-tree's backup should come from the component maintainers within that sub-tree.
 
 
 Getting the Source Code
@@ -135,63 +136,57 @@ sub-repositories (`list <https://git.dpdk.org/next>`_)::
     git clone git://dpdk.org/next/dpdk-next-*
     git clone https://dpdk.org/git/next/dpdk-next-*
 
-Make your Changes
------------------
 
-Make your planned changes in the cloned ``dpdk`` repo. Here are some guidelines and requirements:
 
-* Follow the :ref:`coding_style` guidelines.
+Make Changes
+------------
+
+Once you've cloned the DPDK repository, make your planned changes while following
+these key guidelines and requirements:
 
-* If you are a new contributor, or if your mail address changed,
-  you may update the ``.mailmap`` file.
-  Otherwise the new name or address will be added by a maintainer.
-  Keeping this file up-to-date will help when someone wants to contact you
-  about the changes you contributed to.
+* Follow the :ref:`coding_style` guidelines.
+* Update ``.mailmap`` if you’re a new contributor or have changed your email address.
+  Maintainers may also handle this, but keeping it current ensures others can contact you about your contributions.
+* Update ``MAINTAINERS`` if you add new files or directories, by adding your name under the appropriate section.
 
-* If you add new files or directories you should add your name to the ``MAINTAINERS`` file.
+PMD Submissions
+~~~~~~~~~~~~~~~
 
-* Initial submission of new PMDs should be prepared against a corresponding repo.
+Submit new PMDs to the appropriate next repo:
 
-  * Thus, for example, initial submission of a new network PMD should be
-    prepared against dpdk-next-net repo.
+* New network PMDs → dpdk-next-net
 
-  * Likewise, initial submission of a new crypto or compression PMD should be
-    prepared against dpdk-next-crypto repo.
+* New crypto or compression PMDs → dpdk-next-crypto
 
-  * For other PMDs and more info, refer to the ``MAINTAINERS`` file.
+* For other PMD types, refer to the MAINTAINERS file.
 
-* New external functions should be added to the local ``version.map`` file. See
-  the :doc:`ABI policy <abi_policy>` and :ref:`ABI versioning <abi_versioning>`
-  guides. New external functions should also be added in alphabetical order.
+API and ABI Guidelines
+~~~~~~~~~~~~~~~~~~~~~~
 
-* Any new API function should be used in ``/app`` test directory.
+* Add any new external functions to the relevant ``version.map`` file in alphabetical order.
+  Refer to the ABI policy and ABI versioning documentation.
 
-* When introducing a new device API, at least one driver should implement it.
+* New API functions should be accompanied by a corresponding test in the /app/test directory.
 
-* Important changes will require an addition to the release notes in ``doc/guides/rel_notes/``.
-  See the :ref:`Release Notes section of the Documentation Guidelines <doc_guidelines>` for details.
+* When adding a new device API, ensure that at least one driver implements it.
 
-* Test the compilation works with different targets, compilers and options, see :ref:`contrib_check_compilation`.
+Documentation and Release Notes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* Don't break compilation between commits with forward dependencies in a patchset.
-  Each commit should compile on its own to allow for ``git bisect`` and continuous integration testing.
+* Document new APIs using Doxygen-style comments.
 
-* Add tests to the ``app/test`` unit test framework where possible.
+* If needed, provide user-facing documentation in reStructuredText (RST) format, following the Documentation Guidelines.
 
-* Add documentation, if relevant, in the form of Doxygen comments or a User Guide in RST format.
-  See the :ref:`Documentation Guidelines <doc_guidelines>`.
+* For major changes, add an entry to the release notes under doc/guides/rel_notes/.
 
-* Code and related documentation must be updated atomically in the same patch.
+Compilation and Testing
+~~~~~~~~~~~~~~~~~~~~~~~
 
-Once the changes have been made you should commit them to your local repo.
+* Test compilation across different targets, compilers, and build options (see Checking Compilation :ref:`contrib_check_compilation`.).
 
-For small changes, that do not require specific explanations, it is better to keep things together in the
-same patch.
-Larger changes that require different explanations should be separated into logical patches in a patchset.
-A good way of thinking about whether a patch should be split is to consider whether the change could be
-applied without dependencies as a backport.
+* Each commit in a patchset must compile independently to support git bisect and CI testing.
 
-As a guide to how patches should be structured run ``git log`` on similar files.
+* Include unit tests in the app/test framework wherever applicable.
 
 
 Commit Messages: Subject Line
@@ -229,7 +224,7 @@ It is intended as a way of getting early feedback.
 Commit Messages: Body
 ---------------------
 
-Here are some guidelines for the body of a commit message:
+Here are guidelines for the body of a commit message:
 
 * The body of the message should describe the issue being fixed or the feature being added.
   It is important to provide enough information to allow a reviewer to understand the purpose of the patch.
@@ -566,6 +561,10 @@ Script ``get-maintainer.sh`` can be used to select maintainers automatically::
 
   git send-email --to-cmd ./devtools/get-maintainer.sh --cc dev@dpdk.org 000*.patch
 
+New additions can be sent without a maintainer::
+
+   git send-email --to dev@dpdk.org 000*.patch
+
 You can test the emails by sending it to yourself or with the ``--dry-run`` option.
 
 If the patch is in relation to a previous email thread you can add it to the same thread using the Message ID::
diff --git a/doc/guides/contributing/stable.rst b/doc/guides/contributing/stable.rst
index 808e7fa779..9e8b6d4f62 100644
--- a/doc/guides/contributing/stable.rst
+++ b/doc/guides/contributing/stable.rst
@@ -6,154 +6,117 @@
 DPDK Stable Releases and Long Term Support
 ==========================================
 
-This section sets out the guidelines for the DPDK Stable Releases and the DPDK
-Long Term Support releases (LTS).
 
+This section outlines the guidelines for DPDK Stable Releases and Long Term Support (LTS) Releases.
 
 Introduction
 ------------
 
-The purpose of the DPDK Stable Releases is to maintain releases of DPDK with
-backported fixes over an extended period of time. This provides downstream
-consumers of DPDK with a stable target on which to base applications or
-packages.
+The purpose of DPDK Stable Releases is to maintain DPDK versions with backported
+fixes over an extended period. This allows downstream users to base applications
+or packages on a stable, well-maintained version of DPDK.
 
-The primary characteristics of stable releases is that they attempt to
-fix issues and not introduce any new regressions while keeping backwards
-compatibility with the initial release of the stable version.
-
-The Long Term Support release (LTS) is a designation applied to a Stable
-Release to indicate longer term support.
+The primary goal of stable releases is to fix issues without introducing regressions,
+while preserving backward compatibility with the original version.
 
+LTS (Long Term Support) is a designation given to specific stable releases to indicate
+extended support duration.
 
 Stable Releases
 ---------------
 
-Any release of DPDK can be designated as a Stable Release if a
-maintainer volunteers to maintain it and there is a commitment from major
-contributors to validate it before releases.
-If a version is to be a "Stable Release", it should be designated as such
-within one month of that version being initially released.
-
-A Stable Release is used to backport fixes from an ``N`` release back to an
-``N-1`` release, for example, from 16.11 to 16.07.
-
-The duration of a stable is one complete release cycle (4 months). It can be
-longer, up to 1 year, if a maintainer continues to support the stable branch,
-or if users supply backported fixes, however the explicit commitment should be
-for one release cycle.
-
-The release cadence is determined by the maintainer based on the number of
-bugfixes and the criticality of the bugs. Releases should be coordinated with
-the validation engineers to ensure that a tagged release has been tested.
+Any DPDK release may become a Stable Release if a maintainer volunteers to support it
+and major contributors commit to validating it before each release. This designation
+should ideally be made within one month of the version’s initial release.
 
+Stable Releases are typically used to backport fixes from an N release to an N-1 release.
+For example: from version 16.11 to 16.07.
 
-LTS Release
------------
+- The standard support duration is one full release cycle (4 months).
+- This may extend up to one year if the maintainer continues support or if users provide backported fixes.
+- The release cadence is determined by the maintainer based on the volume and criticality of fixes.
+- Releases must be coordinated with validation engineers to ensure proper testing before tagging.
 
-A stable release can be designated as an LTS release based on community
-agreement and a commitment from a maintainer. The current policy is that each
-year's November (X.11) release will be maintained as an LTS for 3 years,
-however that is dependent on continued community support for validation.
+LTS Releases
+------------
 
-After the X.11 release, an LTS branch will be created for it at
-https://git.dpdk.org/dpdk-stable where bugfixes will be backported to.
+A Stable Release can be promoted to an LTS Release through community agreement and a maintainer’s commitment.
 
-A LTS release may align with the declaration of a new major ABI version,
-please read the :doc:`abi_policy` for more information.
+- The current policy designates each November release (X.11) as an LTS and maintains it for 3 years,
+  contingent on community validation support.
+- After release, an LTS branch is created at: https://git.dpdk.org/dpdk-stable
+- Fixes are backported to this branch.
+- LTS Releases may coincide with the declaration of a new major ABI version. Refer to the :ref:`abi_policy` for details.
 
-It is anticipated that there will be at least 3 releases per year of the LTS
-or approximately 1 every 4 months. This is done to align with the DPDK main
-branch releases so that fixes have already gone through validation as part of
-the DPDK main branch release validation. However, the cadence can be shorter or
-longer depending on the number and criticality of the backported
-fixes. Releases should be coordinated with the validation engineers to ensure
-that a tagged release has been tested.
+Release Cadence:
 
-For a list of the currently maintained stable/LTS branches please see
-the latest `stable roadmap <https://core.dpdk.org/roadmap/#stable>`_.
+- At least three LTS updates per year (roughly one every four months).
+- Aligned with main DPDK releases to leverage shared validation.
+- Frequency may vary depending on the urgency and volume of fixes.
+- As with stable releases, validation must be coordinated with test engineers.
 
-At the end of the 3 years, a final X.11.N release will be made and at that
-point the LTS branch will no longer be maintained with no further releases.
+For the latest list of supported Stable/LTS branches, refer to the current stable roadmap.
 
+At the end of the 3-year period, a final `X.11.N` release will be made, after which the LTS branch will no longer be maintained.
 
-What changes should be backported
+What Changes Should Be Backported
 ---------------------------------
 
-Backporting should be limited to bug fixes. All patches accepted on the main
-branch with a Fixes: tag should be backported to the relevant stable/LTS
-branches, unless the submitter indicates otherwise. If there are exceptions,
-they will be discussed on the mailing lists.
-
-Fixes suitable for backport should have a ``Cc: stable@dpdk.org`` tag in the
-commit message body as follows::
-
-     doc: fix some parameter description
+Backports should be limited to **bug fixes**.
 
-     Update the docs, fixing description of some parameter.
+All main-branch patches with a ``Fixes:`` tag should be considered for backport unless the
+submitter indicates otherwise. These patches must include a ``Cc: stable@dpdk.org`` tag in the commit message:
 
-     Fixes: abcdefgh1234 ("doc: add some parameter")
-     Cc: stable@dpdk.org
+.. code-block:: none
 
-     Signed-off-by: Alex Smith <alex.smith@example.com>
+   doc: fix parameter description
 
+   Update the docs to fix the description of a parameter.
 
-Fixes not suitable for backport should not include the ``Cc: stable@dpdk.org`` tag.
+   Fixes: abcdefgh1234 ("doc: add some parameter")
+   Cc: stable@dpdk.org
 
-To support the goal of stability and not introducing regressions,
-new code being introduced is limited to bug fixes.
-New features should not be backported to stable releases.
+   Signed-off-by: Alex Smith <alex.smith@example.com>
 
-In some limited cases, it may be acceptable to backport a new feature
-to a stable release. Some of the factors which impact the decision by
-stable maintainers are as follows:
+Patches **not** suitable for backport should omit the ``Cc: stable@dpdk.org`` tag.
 
-* Does the feature break API/ABI?
-* Does the feature break backwards compatibility?
-* Is it for the latest LTS release (to avoid LTS upgrade issues)?
-* Is there a commitment from the proposer or affiliation to validate the feature
-  and check for regressions in related functionality?
-* Is there a track record of the proposer or affiliation validating stable releases?
-* Is it obvious that the feature will not impact existing functionality?
-* How intrusive is the code change?
-* What is the scope of the code change?
-* Does it impact common components or vendor specific?
-* Is there a justifiable use case (a clear user need)?
-* Is there a community consensus about the backport?
+Backports must avoid introducing new features. However, in limited cases, a new feature may be allowed if:
 
-Performance improvements are generally not considered to be fixes,
-but may be considered in some cases where:
+- It does **not** break API or ABI.
+- It preserves **backward compatibility**.
+- It targets the latest LTS release (to help with upgrade paths).
+- The proposer commits to testing the feature and monitoring regressions.
+- The proposer or their organization has a strong track record of validating stable releases.
+- It clearly does not impact existing functionality.
+- The change is minimally invasive and scoped.
+- It affects vendor-specific components rather than common ones.
+- There is a compelling use case.
+- There is broad community support.
 
-* It is fixing a performance regression that occurred previously.
-* An existing feature in LTS is not usable as intended without it.
+Performance enhancements are generally not considered bug fixes, unless:
 
-APIs marked as ``experimental`` are not considered part of the ABI version
-and can be changed without prior notice. This is necessary for the API to be
-improved and stabilized and become part of the ABI version in the future.
+- They resolve a performance regression.
+- They are necessary to make an existing feature usable.
 
-However, in LTS releases ``experimental`` API should not be changed as there
-will not be a future ABI version on the branch and compatibility with previous
-release of an LTS version is of the highest importance.
+**Experimental APIs** are excluded from the ABI versioning and may be changed in mainline development.
+However, **experimental APIs must not be modified in LTS branches**, where stability and compatibility are paramount.
 
 The Stable Mailing List
 -----------------------
 
-The Stable and LTS release are coordinated on the stable@dpdk.org mailing
-list.
+Coordination for all Stable and LTS releases occurs on the ``stable@dpdk.org`` mailing list.
 
-All fix patches to the main branch that are candidates for backporting
-should also be CCed to the `stable@dpdk.org <https://mails.dpdk.org/listinfo/stable>`_
-mailing list.
+All fix patches intended for backporting should be CCed to this list when sent to the main branch.
 
 
 Releasing
 ---------
 
-A Stable Release will be released by:
+A Stable Release will be occur by:
 
 * Tagging the release with YY.MM.n (year, month, number).
 * Uploading a tarball of the release to dpdk.org.
 * Sending an announcement to the `announce@dpdk.org <https://mails.dpdk.org/listinfo/announce>`_
   list.
 
-Stable releases are available on the `dpdk.org download page <https://core.dpdk.org/download/>`_.
+Stable releases are available on the `dpdk.org download page <https://core.dpdk.org/download/>`_.
\ No newline at end of file
diff --git a/doc/guides/contributing/vulnerability.rst b/doc/guides/contributing/vulnerability.rst
index fc60e02e37..aee618f6ec 100644
--- a/doc/guides/contributing/vulnerability.rst
+++ b/doc/guides/contributing/vulnerability.rst
@@ -23,41 +23,33 @@ If in doubt, please consider the vulnerability as security sensitive.
 At worst, the response will be to report the bug through the usual channels.
 
 
-Finding
--------
+Finding Security Issues
+-----------------------
 
 There is no pro-active security engineering effort at the moment.
 
 Please report any security issue you find in DPDK as described below.
 
 
-Report
-------
+Reporting Security Issues
+-------------------------
 
-Do not use Bugzilla (unsecured).
-Instead, send GPG-encrypted emails
-to `security@dpdk.org <https://core.dpdk.org/security#contact>`_.
-Anyone can post to this list.
-In order to reduce the disclosure of a vulnerability in the early stages,
-membership of this list is intentionally limited to a `small number of people
-<https://mails.dpdk.org/roster/security>`_.
+Do not use Bugzilla to report security vulnerabilities, as it is not secured for such communication.
+Instead, send a GPG-encrypted email to `security@dpdk.org <https://core.dpdk.org/security#contact>`_.
+This address is open to all, but access to its inbox is intentionally limited to a small group
+to minimize the risk of early disclosure.
 
-It is additionally encouraged to GPG-sign one-on-one conversations
-as part of the security process.
+It is strongly recommended to GPG-sign any one-on-one correspondence related to the vulnerability
+report as part of maintaining a secure communication process.
 
-As it is with any bug, the more information provided,
-the easier it will be to diagnose and fix.
-If you already have a fix, please include it with your report,
-as that can speed up the process considerably.
+As with any bug report, detailed information greatly aids in diagnosing and resolving the issue.
+If you have already developed a fix, please include it in your submission to help accelerate resolution.
 
-In the report, please note how you would like to be credited
-for discovering the issue
-and the details of any embargo you would like to impose.
+In your report, specify how you would like to be credited for the discovery and mention any embargo
+period you wish to impose.
 
-If the vulnerability is not public yet,
-no patch or information should be disclosed publicly.
-If a fix is already published,
-the reporting process must be followed anyway, as described below.
+If the vulnerability has not yet been made public, do not disclose patches or related information
+publicly. Even if a fix has already been published, the proper reporting process outlined here must still be followed.
 
 
 Confirmation
@@ -91,7 +83,7 @@ using the standard patch process, once a CVE number has been assigned.
 
 The confirmation mail should be sent within **3 business days**.
 
-Following information must be included in the mail:
+The following information must be included in the mail:
 
 * Confirmation
 * CVSS severity and score
@@ -275,17 +267,18 @@ Private Disclosure Mail Template
 Public Disclosure
 -----------------
 
-On embargo expiration, following tasks will be done simultaneously:
+When the embargo expires, the following actions will be carried out simultaneously:
 
-* The assigned bug is filled by a member of the security team,
-  with all relevant information, and it is made public.
-* The patches are pushed to the appropriate branches.
-* For long and short term stable branches fixed,
-  new versions should be released.
+* A member of the security team files the assigned bug with all relevant details and makes it publicly accessible.
 
-Releases on Monday to Wednesday are preferred, so that system administrators
-do not have to deal with security updates over the weekend.
+* The associated patches are pushed to the appropriate branches.
 
+* Updated versions are released for all affected stable branches, both short-term and long-term.
+
+To ease adoption by system administrators, security releases are preferably
+scheduled between Monday and Wednesday, avoiding weekends.
+
+A security advisory is sent to announce@dpdk.org and the public OSS Security mailing list as soon as the patches are published.
 The security advisory is posted
 to `announce@dpdk.org <mailto:announce@dpdk.org>`_ and to `the public OSS-security
 mailing list <mailto:oss-security@lists.openwall.com>` as soon as the patches
-- 
2.34.1


^ permalink raw reply	[relevance 12%]

* [PATCH v4 1/3] eal: deprecate old coremask-based EAL parameters
  @ 2025-05-13 16:17  2%   ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-05-13 16:17 UTC (permalink / raw)
  To: dev; +Cc: david.marchand, Bruce Richardson

As the number of cores/cpus on platforms has increased over the years,
the use of coremasks rather than core-lists for identifying DPDK cores
has become more and more unwieldy. At this point, let's deprecate the
coremask-based EAL parameters for future removal, and point users to the
core-list based versions instead.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 doc/guides/eventdevs/dlb2.rst                |  6 +++---
 doc/guides/faq/faq.rst                       |  8 +++-----
 doc/guides/linux_gsg/build_sample_apps.rst   |  7 +++----
 doc/guides/linux_gsg/eal_args.include.rst    |  8 ++------
 doc/guides/prog_guide/meson_ut.rst           |  2 +-
 doc/guides/prog_guide/multi_proc_support.rst |  2 +-
 doc/guides/prog_guide/service_cores.rst      |  8 ++++----
 doc/guides/rel_notes/deprecation.rst         | 10 ++++++++++
 doc/guides/sample_app_ug/ip_frag.rst         |  7 +------
 doc/guides/sample_app_ug/ip_reassembly.rst   |  7 +------
 doc/guides/sample_app_ug/multi_process.rst   | 14 +++++---------
 doc/guides/sample_app_ug/qos_scheduler.rst   |  2 +-
 doc/guides/sample_app_ug/test_pipeline.rst   |  2 +-
 doc/guides/tools/testbbdev.rst               |  2 +-
 lib/eal/common/eal_common_options.c          |  6 ++++++
 15 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/doc/guides/eventdevs/dlb2.rst b/doc/guides/eventdevs/dlb2.rst
index 2532d92888..d1b736830d 100644
--- a/doc/guides/eventdevs/dlb2.rst
+++ b/doc/guides/eventdevs/dlb2.rst
@@ -408,9 +408,9 @@ the DLB device locally available on the same tile along with other
 resources. To allocate optimal resources, probing is done for each
 producer port (PP) for a given CPU and the best performing ports are
 allocated to producers. The cpu used for probing is either the first
-core of producer coremask (if present) or the second core of EAL
-coremask. This will be extended later to probe for all CPUs in the
-producer coremask or EAL coremask. Producer coremask can be passed
+core of producer coremask DLB2 device parameter (if present) or the second core of EAL
+core list. This will be extended later to probe for all CPUs in the
+producer coremask or EAL core list. Producer coremask can be passed
 along with the BDF of the DLB devices.
 
     .. code-block:: console
diff --git a/doc/guides/faq/faq.rst b/doc/guides/faq/faq.rst
index 297cb5119e..ddcb018b3a 100644
--- a/doc/guides/faq/faq.rst
+++ b/doc/guides/faq/faq.rst
@@ -47,11 +47,9 @@ therefore all the hugepages are allocated on the wrong socket.
 To avoid this scenario, either lower the amount of hugepage memory available to 1 GB size (or less), or run the application with taskset
 affinitizing the application to a would-be main core.
 
-For example, if your EAL coremask is 0xff0, the main core will usually be the first core in the coremask (0x10); this is what you have to supply to taskset::
+For example, if your EAL core list is '4-11', the main core will usually be the first core in the list (core 4); this is what you have to supply to taskset::
 
-   taskset 0x10 ./l2fwd -l 4-11 -n 2
-
-.. Note: Instead of '-c 0xff0' use the '-l 4-11' as a cleaner way to define lcores.
+   taskset -c 4 ./l2fwd -l 4-11 -n 2
 
 In this way, the hugepages have a greater chance of being allocated to the correct socket.
 Additionally, a ``--socket-mem`` option could be used to ensure the availability of memory for each socket, so that if hugepages were allocated on
@@ -167,7 +165,7 @@ Can I split packet RX to use DPDK and have an application's higher order functio
 ----------------------------------------------------------------------------------------------------------------
 
 The DPDK's lcore threads are Linux pthreads bound onto specific cores. Configure the DPDK to do work on the same
-cores and run the application's other work on other cores using the DPDK's "coremask" setting to specify which
+cores and run the application's other work on other cores using the DPDK's "core list" (-l/--lcores) setting to specify which
 cores it should launch itself on.
 
 
diff --git a/doc/guides/linux_gsg/build_sample_apps.rst b/doc/guides/linux_gsg/build_sample_apps.rst
index 433839ecc7..c568549f26 100644
--- a/doc/guides/linux_gsg/build_sample_apps.rst
+++ b/doc/guides/linux_gsg/build_sample_apps.rst
@@ -126,10 +126,9 @@ and that cores 0-3 are present and are to be used for running the application)::
 Logical Core Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The coremask (-c 0x0f) or corelist (-l 0-3) parameter is always mandatory for DPDK applications.
-Each bit of the mask corresponds to the equivalent logical core number as reported by Linux. The preferred corelist option is a cleaner method to define cores to be used.
+The corelist (-l/--lcores 0-3) parameter is always mandatory for DPDK applications.
 Since these logical core numbers, and their mapping to specific cores on specific NUMA sockets, can vary from platform to platform,
-it is recommended that the core layout for each platform be considered when choosing the coremask/corelist to use in each case.
+it is recommended that the core layout for each platform be considered when choosing the corelist to use in each case.
 
 On initialization of the EAL layer by a DPDK application, the logical cores to be used and their socket location are displayed.
 This information can also be determined for all cores on the system by examining the ``/proc/cpuinfo`` file, for example, by running cat ``/proc/cpuinfo``.
@@ -151,7 +150,7 @@ This can be useful when using other processors to understand the mapping of the
 
 .. warning::
 
-    The logical core layout can change between different board layouts and should be checked before selecting an application coremask/corelist.
+    The logical core layout can change between different board layouts and should be checked before selecting an application corelist.
 
 Hugepage Memory Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/linux_gsg/eal_args.include.rst b/doc/guides/linux_gsg/eal_args.include.rst
index 9cfbf7de84..7ffd2e2535 100644
--- a/doc/guides/linux_gsg/eal_args.include.rst
+++ b/doc/guides/linux_gsg/eal_args.include.rst
@@ -4,10 +4,6 @@
 Lcore-related options
 ~~~~~~~~~~~~~~~~~~~~~
 
-*   ``-c <core mask>``
-
-    Set the hexadecimal bitmask of the cores to run on.
-
 *   ``-l <core list>``
 
     List of cores to run on
@@ -37,9 +33,9 @@ Lcore-related options
 
     Core ID that is used as main.
 
-*   ``-s <service core mask>``
+*   ``-S <service core list>``
 
-    Hexadecimal bitmask of cores to be used as service cores.
+    List of cores to be used as service cores.
 
 Device-related options
 ~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/prog_guide/meson_ut.rst b/doc/guides/prog_guide/meson_ut.rst
index 78cf3f845c..9bc52a30fc 100644
--- a/doc/guides/prog_guide/meson_ut.rst
+++ b/doc/guides/prog_guide/meson_ut.rst
@@ -60,7 +60,7 @@ Arguments of ``test()`` that can be provided in meson.build are as below:
 
 Note: the content of meson ``--test-args`` option and the content of ``args``
 are appended when invoking the DPDK test binary.
-Because of this, it is recommended not to set any default coremask or memory
+Because of this, it is recommended not to set any default corelist or memory
 configuration in per test ``args`` and rather let users select what best fits
 their environment. If a test can't run, then it should be skipped, as described
 below.
diff --git a/doc/guides/prog_guide/multi_proc_support.rst b/doc/guides/prog_guide/multi_proc_support.rst
index 0c57145470..dd57b6f8c1 100644
--- a/doc/guides/prog_guide/multi_proc_support.rst
+++ b/doc/guides/prog_guide/multi_proc_support.rst
@@ -166,7 +166,7 @@ Some of these are documented below:
     so it is recommended that it be disabled only when absolutely necessary,
     and only when the implications of this change have been understood.
 
-*   All DPDK processes running as a single application and using shared memory must have distinct coremask/corelist arguments.
+*   All DPDK processes running as a single application and using shared memory must have distinct corelist arguments.
     It is not possible to have a primary and secondary instance, or two secondary instances,
     using any of the same logical cores.
     Attempting to do so can cause corruption of memory pool caches, among other issues.
diff --git a/doc/guides/prog_guide/service_cores.rst b/doc/guides/prog_guide/service_cores.rst
index d4e6c3d6e6..5284eeb96a 100644
--- a/doc/guides/prog_guide/service_cores.rst
+++ b/doc/guides/prog_guide/service_cores.rst
@@ -26,10 +26,10 @@ Service Core Initialization
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 There are two methods to having service cores in a DPDK application, either by
-using the service coremask, or by dynamically adding cores using the API.
-The simpler of the two is to pass the `-s` coremask argument to EAL, which will
-take any cores available in the main DPDK coremask, and if the bits are also set
-in the service coremask the cores become service-cores instead of DPDK
+using the service corelist, or by dynamically adding cores using the API.
+The simpler of the two is to pass the `-S` corelist argument to EAL, which will
+take any cores available in the main DPDK corelist, and if also set
+in the service corelist the cores become service-cores instead of DPDK
 application lcores.
 
 Enabling Services on Cores
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 36489f6e68..2ea898ff8a 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -17,6 +17,16 @@ Other API and ABI deprecation notices are to be posted below.
 Deprecation Notices
 -------------------
 
+* EAL: The ``-c <coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-l <corelist>`` or ``--lcores=<corelist>`` parameters instead
+  to specify the cores to be used when running a DPDK application.
+
+* EAL: The ``-s <service-coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-S <service-corelist>`` parameter instead
+  to specify the cores to be used for background services in DPDK.
+
 * build: The ``enable_kmods`` option is deprecated and will be removed in a future release.
   Setting/clearing the option has no impact on the build.
   Instead, kernel modules will be always built for OS's where out-of-tree kernel modules
diff --git a/doc/guides/sample_app_ug/ip_frag.rst b/doc/guides/sample_app_ug/ip_frag.rst
index 4b2071cbae..3f0b5ce5e7 100644
--- a/doc/guides/sample_app_ug/ip_frag.rst
+++ b/doc/guides/sample_app_ug/ip_frag.rst
@@ -67,12 +67,7 @@ To run the example in linux environment with 2 lcores (2,4) over 2 ports(0,2) wi
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-ip_fragmentation -l 2,4 -n 3 -- -p 5
-    EAL: coremask set to 14
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 1
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 1
-    EAL: Detected lcore 4 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     Initializing port 0 on lcore 2... Address:00:1B:21:76:FA:2C, rxq=0 txq=2,0 txq=4,1
diff --git a/doc/guides/sample_app_ug/ip_reassembly.rst b/doc/guides/sample_app_ug/ip_reassembly.rst
index b8800dd9e7..e55ee7eb79 100644
--- a/doc/guides/sample_app_ug/ip_reassembly.rst
+++ b/doc/guides/sample_app_ug/ip_reassembly.rst
@@ -63,12 +63,7 @@ with 1 Rx queue per lcore:
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-ip_reassembly -l 2,4 -n 3 -- -p 5
-    EAL: coremask set to 14
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 1
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 1
-    EAL: Detected lcore 4 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     Initializing port 0 on lcore 2... Address:00:1B:21:76:FA:2C, rxq=0 txq=2,0 txq=4,1
diff --git a/doc/guides/sample_app_ug/multi_process.rst b/doc/guides/sample_app_ug/multi_process.rst
index e2b1b16c84..29bca806e1 100644
--- a/doc/guides/sample_app_ug/multi_process.rst
+++ b/doc/guides/sample_app_ug/multi_process.rst
@@ -36,7 +36,7 @@ Running the Application
 ^^^^^^^^^^^^^^^^^^^^^^^
 
 To run the application, start ``simple_mp`` binary in one terminal,
-passing at least two cores in the coremask/corelist:
+passing at least two cores in the corelist:
 
 .. code-block:: console
 
@@ -50,11 +50,7 @@ The process should start successfully and display a command prompt as follows:
 .. code-block:: console
 
     $ ./<build_dir>/examples/dpdk-simple_mp -l 0-1 -n 4 --proc-type=primary
-    EAL: coremask set to 3
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 0
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     EAL: Requesting 2 pages of size 1073741824
@@ -72,7 +68,7 @@ The process should start successfully and display a command prompt as follows:
     simple_mp >
 
 To run the secondary process to communicate with the primary process,
-again run the same binary setting at least two cores in the coremask/corelist:
+again run the same binary setting at least two cores in the corelist:
 
 .. code-block:: console
 
@@ -237,8 +233,8 @@ In addition to the EAL parameters, the application-specific parameters are:
 .. note::
 
    In the server process, has a single thread using the lowest numbered lcore
-   in the coremask/corelist, performs all packet I/O.
-   If coremask/corelist parameter specifies with more than a single lcore bit set,
+   in the corelist, performs all packet I/O.
+   If corelist parameter specifies with more than a single lcore,
    an additional lcore will be used for a thread to print packet count periodically.
 
 The server application stores configuration data in shared memory,
diff --git a/doc/guides/sample_app_ug/qos_scheduler.rst b/doc/guides/sample_app_ug/qos_scheduler.rst
index 9936b99172..36ada4902c 100644
--- a/doc/guides/sample_app_ug/qos_scheduler.rst
+++ b/doc/guides/sample_app_ug/qos_scheduler.rst
@@ -194,7 +194,7 @@ Another example with 2 packet flow configurations using different ports but shar
 Note that independent cores for the packet flow configurations for each of the RX, WT and TX thread are also supported,
 providing flexibility to balance the work.
 
-The EAL coremask/corelist is constrained to contain the default main core 1 and the RX, WT and TX cores only.
+The EAL corelist is constrained to contain the default main core 1 and the RX, WT and TX cores only.
 
 Explanation
 -----------
diff --git a/doc/guides/sample_app_ug/test_pipeline.rst b/doc/guides/sample_app_ug/test_pipeline.rst
index d57d08fb2c..818be93cd6 100644
--- a/doc/guides/sample_app_ug/test_pipeline.rst
+++ b/doc/guides/sample_app_ug/test_pipeline.rst
@@ -47,7 +47,7 @@ The application execution command line is:
 
     ./dpdk-test-pipeline [EAL options] -- -p PORTMASK --TABLE_TYPE
 
-The -c or -l EAL CPU coremask/corelist option has to contain exactly 3 CPU cores.
+The ``-l/--lcores`` EAL CPU corelist option has to contain exactly 3 CPU cores.
 The first CPU core in the core mask is assigned for core A, the second for core B and the third for core C.
 
 The PORTMASK parameter must contain 2 or 4 ports.
diff --git a/doc/guides/tools/testbbdev.rst b/doc/guides/tools/testbbdev.rst
index ddb8d787be..8677cd2c43 100644
--- a/doc/guides/tools/testbbdev.rst
+++ b/doc/guides/tools/testbbdev.rst
@@ -78,7 +78,7 @@ The following are the command-line options:
 
 ``-l NUM_LCORES, --num_lcores NUM_LCORES``
  Specifies number of lcores to run. If not specified num_lcores is set
- according to value from RTE configuration (EAL coremask)
+ according to value from RTE configuration (EAL corelist)
 
 ``-b BURST_SIZE [BURST_SIZE ...], --burst-size BURST_SIZE [BURST_SIZE ...]``
  Specifies operations enqueue/dequeue burst size. If not specified burst_size is
diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index b6fff7ec05..19c5997c7c 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -614,6 +614,9 @@ eal_parse_service_coremask(const char *coremask)
 	int val;
 	uint32_t taken_lcore_count = 0;
 
+	EAL_LOG(WARNING, "'-s <service-coremask>' is deprecated, and will be removed in a future release.");
+	EAL_LOG(WARNING, "\tUse '-S <service-corelist>' option instead.");
+
 	if (coremask == NULL)
 		return -1;
 	/* Remove all blank characters ahead and after .
@@ -777,6 +780,9 @@ rte_eal_parse_coremask(const char *coremask, int *cores)
 		cores[idx] = -1;
 	idx = 0;
 
+	EAL_LOG(WARNING, "'-c <coremask>' option is deprecated, and will be removed in a future release");
+	EAL_LOG(WARNING, "\tUse '-l <corelist>' or '--lcores=<corelist>' option instead");
+
 	/* Remove all blank characters ahead and after .
 	 * Remove 0x/0X if exists.
 	 */
-- 
2.45.2


^ permalink raw reply	[relevance 2%]

* [RFC PATCH 3/7] argparse: make argparse EAL-args compatible
  @ 2025-05-20 16:40  3% ` Bruce Richardson
  2025-05-22 10:44  0%   ` Bruce Richardson
  0 siblings, 1 reply; 153+ results
From: Bruce Richardson @ 2025-05-20 16:40 UTC (permalink / raw)
  To: dev; +Cc: david.marchand, Bruce Richardson, Chengwen Feng

The argparse library was missing two key features which made it
unsuitable for use by EAL or any program wanting similar behaviour.

1. It didn't stop parsing arguments when it hit a "--" character
2. It never returned the number of arguments parsed

Fix both these issues - the latter is a change to the ABI, since we now
return >= 0 rather than == 0 on success. However, the ABI is still
experimental so we can make exactly these sorts of tweaks to it.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test/test_argparse.c    | 46 ++++++++++++++++++-------------------
 lib/argparse/rte_argparse.c | 12 +++++++---
 lib/argparse/rte_argparse.h |  3 ++-
 3 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index fcea620501..77bace5a39 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -393,14 +393,14 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
@@ -434,13 +434,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -448,13 +448,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
@@ -503,14 +503,14 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -555,14 +555,14 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
@@ -618,14 +618,14 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -633,13 +633,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
@@ -671,7 +671,7 @@ test_argparse_pos_autosave_parse_int(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
@@ -738,7 +738,7 @@ test_argparse_pos_callback_parse_int(void)
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
@@ -774,7 +774,7 @@ test_argparse_parse_type(void)
 	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");
 
 	/* test for u8 parsing */
@@ -786,7 +786,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u8 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");
 
 	/* test for u16 parsing */
@@ -798,7 +798,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u16 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");
 
 	/* test for u32 parsing */
@@ -810,7 +810,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u32 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");
 
 	/* test for u64 parsing */
@@ -820,7 +820,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u64 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");
 
 	return 0;
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 1cc06b550b..d8f964dc3c 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -605,6 +605,12 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 
 	for (i = 1; i < argc; i++) {
 		curr_argv = argv[i];
+
+		if (strcmp(argv[i], "--") == 0) {
+			i++;
+			break;
+		}
+
 		if (curr_argv[0] != '-') {
 			/* process positional parameters. */
 			position_index++;
@@ -668,7 +674,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		arg->flags |= ARG_ATTR_FLAG_PARSED_MASK;
 	}
 
-	return 0;
+	return i;
 }
 
 static uint32_t
@@ -784,7 +790,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		goto error;
 
 	ret = parse_args(obj, argc, argv, &show_help);
-	if (ret != 0)
+	if (ret < 0)
 		goto error;
 
 	if (show_help) {
@@ -792,7 +798,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		exit(0);
 	}
 
-	return 0;
+	return ret;
 
 error:
 	if (obj->exit_on_error)
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index 332184302e..8cdb3195cb 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -183,7 +183,8 @@ struct rte_argparse {
  *   Array of parameters points.
  *
  * @return
- *   0 on success. Otherwise negative value is returned.
+ *   number of arguments parsed (>= 0) on success.
+ *   Otherwise negative error code is returned.
  */
 __rte_experimental
 int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
-- 
2.48.1


^ permalink raw reply	[relevance 3%]

* [DPDK/ethdev Bug 1536] net/tap: crash in tap pmd when using more than RTE_MP_MAX_FD_NUM rx queues
  @ 2025-05-21 16:44  3% ` bugzilla
  0 siblings, 0 replies; 153+ results
From: bugzilla @ 2025-05-21 16:44 UTC (permalink / raw)
  To: dev

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

https://bugs.dpdk.org/show_bug.cgi?id=1536

Stephen Hemminger (stephen@networkplumber.org) changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |stephen@networkplumber.org
         Resolution|---                         |FIXED
             Status|UNCONFIRMED                 |RESOLVED

--- Comment #1 from Stephen Hemminger (stephen@networkplumber.org) ---
This was addressed in 24.11 by increasing the size of RTE_MP_MAX_FD_NUM to the
kernel limit (253)

See:


commit 5ff00bbc04d8338108241b083b7a6238208cfbc6
Author: Stephen Hemminger <stephen@networkplumber.org>
Date:   Thu Sep 5 09:20:18 2024 -0700

    ipc: increase file descriptor maximum number

    The TAP and XDP driver both are limited to only 8 queues because of the
    small limit imposed by EAL.
    Increase the limit now, since this release allows changing ABI.

    Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
    Acked-by: Chengwen Feng <fengchengwen@huawei.com>
    Acked-by: Morten Brørup <mb@smartsharesystems.com>
    Acked-by: Ferruh Yigit <ferruh.yigit@amd.com>

-- 
You are receiving this mail because:
You are the assignee for the bug.

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

^ permalink raw reply	[relevance 3%]

* Re: [RFC PATCH 3/7] argparse: make argparse EAL-args compatible
  2025-05-20 16:40  3% ` [RFC PATCH 3/7] argparse: make argparse EAL-args compatible Bruce Richardson
@ 2025-05-22 10:44  0%   ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-05-22 10:44 UTC (permalink / raw)
  To: dev; +Cc: david.marchand, Chengwen Feng

On Tue, May 20, 2025 at 05:40:20PM +0100, Bruce Richardson wrote:
> The argparse library was missing two key features which made it
> unsuitable for use by EAL or any program wanting similar behaviour.
> 
> 1. It didn't stop parsing arguments when it hit a "--" character
> 2. It never returned the number of arguments parsed
> 
> Fix both these issues - the latter is a change to the ABI, since we now
> return >= 0 rather than == 0 on success. However, the ABI is still
> experimental so we can make exactly these sorts of tweaks to it.
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---

Thinking about it further, for EAL we can actually do without these changes
to the argparse library*. However, this is also functionality that may be
useful in other cases, so looking for feedback on whether to continue with
this patch, or drop it?

Question:
* should argparse library stop processing args at "--"?
* should argparse library return number of args parsed, or zero on success?

/Bruce

*The reason we don't need these is because we clone the argv data on
eal_init so we can return it via telemetry library. This splits the args
into eal and non-eal args, so we can use just the "eal" arg array to pass to
arg-parse, if we don't include this patch.

^ permalink raw reply	[relevance 0%]

* [PATCH] rcu: add deprecation notice about limit on defer queue element size
@ 2025-05-22 23:37  5% Andre Muezerie
  0 siblings, 0 replies; 153+ results
From: Andre Muezerie @ 2025-05-22 23:37 UTC (permalink / raw)
  Cc: dev, Andre Muezerie

The functions rte_rcu_qsbr_dq_create and rte_rcu_qsbr_dq_reclaim establish
no limit on the size of each element in the defer queue. With DPDK 25.11 a
hard limit will be set (``RTE_QSBR_ESIZE_MAX``). This will allow fixed
C arrays to be used in the functions' implementations, avoiding VLAs and
use of alloca().

Signed-off-by: Andre Muezerie <andremue@linux.microsoft.com>
---
 doc/guides/rel_notes/deprecation.rst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 36489f6e68..78b2a27b52 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -17,6 +17,12 @@ Other API and ABI deprecation notices are to be posted below.
 Deprecation Notices
 -------------------
 
+* rcu: The functions ``rte_rcu_qsbr_dq_create`` and ``rte_rcu_qsbr_dq_reclaim``
+  establish no limit on the size of each element in the defer queue. With
+  DPDK 25.11 a hard limit will be set (``RTE_QSBR_ESIZE_MAX``). This will
+  allow fixed C arrays to be used in the functions' implementations,
+  avoiding VLAs and use of alloca().
+
 * build: The ``enable_kmods`` option is deprecated and will be removed in a future release.
   Setting/clearing the option has no impact on the build.
   Instead, kernel modules will be always built for OS's where out-of-tree kernel modules
-- 
2.49.0.vfs.0.3


^ permalink raw reply	[relevance 5%]

* [PATCH 0/3] argparse additions and rework
@ 2025-05-27  9:21  4% Bruce Richardson
  2025-05-27  9:21 10% ` [PATCH 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
                   ` (5 more replies)
  0 siblings, 6 replies; 153+ results
From: Bruce Richardson @ 2025-05-27  9:21 UTC (permalink / raw)
  To: dev; +Cc: Chengwen Feng, Bruce Richardson

This patchset is based off the work to adjust how we do argument parsing
inside EAL. To enable argparse to be effectively used for EAL, we have
new features and some changes in the first two patches, which are 
relatively small - though are ABI/API affecting.

These add support for saving off strings and boolean values, have argparse
stop parsing at a "--", and finally have argparse return the number of
arguments actually parsed on success.

The third patch is a bigger change. It was inspired by the fact that
when adding the boolean and string support we had to update some
"MAX" value defines used in the code. This is obviously not good from
an ABI/API perspective, once the library becomes part of the stable ABI.
In order to remove these MAX values, patch 3 looks to replace the
#define values with enums - which means some rework splitting the
various flags into separate categories, and similarly splitting the
single "flags" field with separate fields specifying if an argument
value is required, what type that value should have, and then a
final smaller field for any additional modifiers.

Bruce Richardson (3):
  argparse: add support for string and boolean args
  argparse: make argparse EAL-args compatible
  argparse: use enums to remove max-value defines in lists

 app/test/test_argparse.c               | 235 ++++++++++++++-----------
 doc/guides/rel_notes/release_25_07.rst |  49 ++++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 184 ++++++++++++-------
 lib/argparse/rte_argparse.h            |  91 +++++-----
 6 files changed, 352 insertions(+), 231 deletions(-)

-- 
2.48.1


^ permalink raw reply	[relevance 4%]

* [PATCH 2/3] argparse: make argparse EAL-args compatible
  2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
@ 2025-05-27  9:21 10% ` Bruce Richardson
  2025-06-03  8:44  0%   ` fengchengwen
  2025-05-27  9:21  6% ` [PATCH 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 153+ results
From: Bruce Richardson @ 2025-05-27  9:21 UTC (permalink / raw)
  To: dev; +Cc: Chengwen Feng, Bruce Richardson

The argparse library was missing two key features which made it
unsuitable for use by EAL or any program wanting similar behaviour.

1. It didn't stop parsing arguments when it hit a "--" character
2. It never returned the number of arguments parsed

Fix both these issues - the latter is a change to the ABI, since we now
return >= 0 rather than == 0 on success. However, the ABI is still
experimental so we can make exactly these sorts of tweaks to it.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test/test_argparse.c               | 46 +++++++++++++-------------
 doc/guides/rel_notes/release_25_07.rst |  9 +++++
 lib/argparse/rte_argparse.c            | 12 +++++--
 lib/argparse/rte_argparse.h            |  3 +-
 4 files changed, 43 insertions(+), 27 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 6b0d1524b5..a907fbe53f 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -393,14 +393,14 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
@@ -434,13 +434,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -448,13 +448,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
@@ -503,14 +503,14 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -555,14 +555,14 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
@@ -618,14 +618,14 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -633,13 +633,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
@@ -671,7 +671,7 @@ test_argparse_pos_autosave_parse_int(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
@@ -738,7 +738,7 @@ test_argparse_pos_callback_parse_int(void)
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
@@ -780,7 +780,7 @@ test_argparse_parse_type(void)
 	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");
 
 	/* test for u8 parsing */
@@ -792,7 +792,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u8 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");
 
 	/* test for u16 parsing */
@@ -804,7 +804,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u16 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");
 
 	/* test for u32 parsing */
@@ -816,7 +816,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u32 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");
 
 	/* test for u64 parsing */
@@ -826,7 +826,7 @@ test_argparse_parse_type(void)
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u64 = 0;
 	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
-	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
+	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");
 
 	/* test for string parsing - all it does is save string, so all are valid */
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 523d69c854..608526f021 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -109,6 +109,15 @@ ABI Changes
 
 * No ABI change that would break compatibility with 24.11.
 
+* argparse: The experimental "argparse" library has had the following updates:
+   * The main parsing function, ``rte_argparse_parse()``,
+     now returns the number of arguments parsed on success, rather than zero.
+     It still returns a negative value on error.
+   * When parsing a list of arguments,
+     ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
+     This behaviour mirrors the behaviour of the ``getopt()`` function,
+     as well as the behaviour of ``rte_eal_init()`` function.
+
 
 Known Issues
 ------------
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index af2ca7e455..bb03cdc4af 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -605,6 +605,12 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 
 	for (i = 1; i < argc; i++) {
 		curr_argv = argv[i];
+
+		if (strcmp(argv[i], "--") == 0) {
+			i++;
+			break;
+		}
+
 		if (curr_argv[0] != '-') {
 			/* process positional parameters. */
 			position_index++;
@@ -668,7 +674,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		arg->flags |= ARG_ATTR_FLAG_PARSED_MASK;
 	}
 
-	return 0;
+	return i;
 }
 
 static uint32_t
@@ -784,7 +790,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		goto error;
 
 	ret = parse_args(obj, argc, argv, &show_help);
-	if (ret != 0)
+	if (ret < 0)
 		goto error;
 
 	if (show_help) {
@@ -792,7 +798,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		exit(0);
 	}
 
-	return 0;
+	return ret;
 
 error:
 	if (obj->exit_on_error)
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index 332184302e..8cdb3195cb 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -183,7 +183,8 @@ struct rte_argparse {
  *   Array of parameters points.
  *
  * @return
- *   0 on success. Otherwise negative value is returned.
+ *   number of arguments parsed (>= 0) on success.
+ *   Otherwise negative error code is returned.
  */
 __rte_experimental
 int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
-- 
2.48.1


^ permalink raw reply	[relevance 10%]

* [PATCH 3/3] argparse: use enums to remove max-value defines in lists
  2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
  2025-05-27  9:21 10% ` [PATCH 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
@ 2025-05-27  9:21  6% ` Bruce Richardson
  2025-06-04  1:22  0%   ` fengchengwen
  2025-05-29 15:09  0% ` [PATCH 0/3] argparse additions and rework Bruce Richardson
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 153+ results
From: Bruce Richardson @ 2025-05-27  9:21 UTC (permalink / raw)
  To: dev; +Cc: Chengwen Feng, Bruce Richardson

The use of lists of #defines with _MAX entries at the end causes issues
for ABI compatibility as those MAX values often leak through to
applications and can cause issues when changed.

We can rework the code to increase type safety by splitting the flags
field and using enums for each set of values explicitly:

* One enum for whether an argument takes a parameter or not (or
  optionally takes one)
* One enum for the type of the argument.
* A separate flags field for any additional info - right now this is
  only to indicate an argument can appear more than once.

The use of the MAX field is no longer necessary using the enums as we
can use an inline function with a switch statement to check for valid
values. Compilers like GCC will warn when an enum value is missing from
the switch statement, easing future maintenance if enum values are
added.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test/test_argparse.c               | 178 ++++++++++++-------------
 doc/guides/rel_notes/release_25_07.rst |  35 +++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 144 ++++++++++----------
 lib/argparse/rte_argparse.h            |  92 +++++++------
 6 files changed, 255 insertions(+), 218 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index a907fbe53f..0459fa5c0d 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -79,8 +79,8 @@ test_argparse_callback(uint32_t index, const char *value, void *opaque)
 	.exit_on_error = false, \
 	.callback = test_argparse_callback, \
 	.args = { \
-		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
-		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
+		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
+		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
 		ARGPARSE_ARG_END(), \
 	}, \
 }
@@ -188,27 +188,20 @@ test_argparse_invalid_arg_help(void)
 static int
 test_argparse_invalid_has_val(void)
 {
-	uint64_t set_mask[] = { 0,
-				RTE_ARGPARSE_ARG_NO_VALUE,
-				RTE_ARGPARSE_ARG_OPTIONAL_VALUE
-			      };
+	uint64_t invalid_values[] = {
+			RTE_ARGPARSE_VALUE_NONE,
+			RTE_ARGPARSE_VALUE_OPTIONAL,
+	};
 	struct rte_argparse *obj;
 	uint32_t index;
 	int ret;
 
-	/* test optional arg don't config has-value. */
-	obj = test_argparse_init_obj();
-	obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
 	/* test positional arg don't config required-value. */
-	for (index = 0; index < RTE_DIM(set_mask); index++) {
+	for (index = 0; index < RTE_DIM(invalid_values); index++) {
 		obj = test_argparse_init_obj();
 		obj->args[0].name_long = "abc";
 		obj->args[0].name_short = NULL;
-		obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-		obj->args[0].flags |= set_mask[index];
+		obj->args[0].value_required = invalid_values[index];
 		ret = rte_argparse_parse(obj, default_argc, default_argv);
 		TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 	}
@@ -225,14 +218,15 @@ test_argparse_invalid_arg_saver(void)
 	/* test saver == NULL with val-type != 0. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test saver == NULL with callback is NULL. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->callback = NULL;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -241,15 +235,7 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
-	/* test saver != NULL with val-type is max. */
-	obj = test_argparse_init_obj();
-	obj->args[0].val_saver = (void *)1;
-	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -257,7 +243,8 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -272,9 +259,7 @@ test_argparse_invalid_arg_flags(void)
 
 	/* test set unused bits. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= ~(RTE_ARGPARSE_HAS_VAL_BITMASK |
-				RTE_ARGPARSE_VAL_TYPE_BITMASK |
-				RTE_ARGPARSE_ARG_SUPPORT_MULTI);
+	obj->args[0].flags |= ~(RTE_ARGPARSE_FLAG_SUPPORT_MULTI);
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -284,14 +269,15 @@ test_argparse_invalid_arg_flags(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT |
-			     RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test optional arg enabled multiple but prased by autosave. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -344,7 +330,6 @@ test_argparse_invalid_option(void)
 static int
 test_argparse_opt_autosave_parse_int_of_no_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -355,7 +340,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -363,7 +349,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -376,7 +363,6 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_required_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -387,7 +373,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -396,7 +383,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -404,7 +392,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	argv[2] = test_strdup("100a");
@@ -417,7 +406,6 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_optional_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -429,14 +417,16 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -444,13 +434,15 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -458,12 +450,14 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -498,7 +492,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -506,7 +500,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -549,7 +543,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -558,7 +552,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -566,12 +560,12 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -613,7 +607,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -621,7 +615,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -629,13 +623,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -643,7 +637,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	argv[1] = test_strdup("-t=100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -654,7 +648,6 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 static int
 test_argparse_pos_autosave_parse_int(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -666,7 +659,8 @@ test_argparse_pos_autosave_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -675,14 +669,16 @@ test_argparse_pos_autosave_parse_int(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test too much position parameters. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -727,12 +723,12 @@ test_argparse_pos_callback_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = "test-long2";
 	obj->args[1].name_short = NULL;
 	obj->args[1].val_saver = NULL;
 	obj->args[1].val_set = (void *)2;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[2].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -743,8 +739,8 @@ test_argparse_pos_callback_parse_int(void)
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
 	/* test positional callback parse failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("200a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -775,78 +771,78 @@ test_argparse_parse_type(void)
 	int ret;
 
 	/* test for int parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");
 
 	/* test for u8 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u8 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");
 
 	/* test for u16 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u16 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");
 
 	/* test for u32 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u32 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");
 
 	/* test for u64 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u64 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");
 
 	/* test for string parsing - all it does is save string, so all are valid */
 	const char *val_str;
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_STR, &val_str);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_STR, &val_str);
 	TEST_ASSERT(ret == 0, "Argparse parse a string failed unexpectedly!");
 
 	/* test for boolean parsing */
 	bool val_bool = false;
-	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (true) failed!");
-	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (false) failed!");
-	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (invalid) passed unexpectedly!");
-	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (numeric true) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (numeric false) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (numeric invalid) passed unexpectedly!");
 	return 0;
 }
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 608526f021..0720dd9cea 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -91,6 +91,10 @@ API Changes
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* argparse: The ``rte_argparse_arg`` structure used for defining arguments has been updated.
+    See next section, :ref:`ABI-Changes` for details.
+
+.. _ABI-Changes:
 
 ABI Changes
 -----------
@@ -117,6 +121,37 @@ ABI Changes
      ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
      This behaviour mirrors the behaviour of the ``getopt()`` function,
      as well as the behaviour of ``rte_eal_init()`` function.
+   * The ``rte_argparse_arg`` structure used for defining arguments has been updated
+     to separate out into separate fields the options for:
+
+     #. Whether the argument is required or optional.
+     #. What the type of the argument is (in case of saving the parameters automatically).
+     #. Any other flags - of which there is only one, ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI``, at this time.
+
+   * With the splitting of the flags into separate enums for categories,
+     the names of the flags have been changed to better reflect their purpose.
+     The flags/enum values are:
+
+     * For the ``value_required`` field:
+
+        * ``RTE_ARGPARSE_VALUE_NONE``
+        * ``RTE_ARGPARSE_VALUE_REQUIRED``
+        * ``RTE_ARGPARSE_VALUE_OPTIONAL``
+
+     * For the ``value_type`` field:
+
+        * ``RTE_ARGPARSE_VALUE_TYPE_NONE`` (No argument value type is specified, callback is to be used for processing.)
+        * ``RTE_ARGPARSE_VALUE_TYPE_INT``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U8``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U16``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U32``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U64``
+        * ``RTE_ARGPARSE_VALUE_TYPE_STR``
+        * ``RTE_ARGPARSE_VALUE_TYPE_BOOL``
+
+     * Other flags:
+
+        * ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI`` (Allows the argument to be specified multiple times.)
 
 
 Known Issues
diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c
index acceae6b7b..5ba0aaa40b 100644
--- a/examples/dma/dmafwd.c
+++ b/examples/dma/dmafwd.c
@@ -644,43 +644,43 @@ dma_parse_args(int argc, char **argv, unsigned int nb_ports)
 		.args = {
 			{ "--mac-updating", NULL, "Enable MAC addresses updating",
 			  &mac_updating, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--no-mac-updating", NULL, "Disable MAC addresses updating",
 			  &mac_updating, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--portmask", "-p", "hexadecimal bitmask of ports to configure",
 			  NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--nb-queue", "-q", "number of RX queues per port (default is 1)",
 			  &nb_queues, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--copy-type", "-c", "type of copy: sw|hw",
 			  NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--ring-size", "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode",
 			  &ring_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--dma-batch-size", "-b", "number of requests per DMA batch",
 			  &dma_batch_sz, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--max-frame-size", "-f", "max frame size",
 			  &max_frame_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--force-min-copy-size", "-m", "force a minimum copy length, even for smaller packets",
 			  &force_min_copy_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--stats-interval", "-i", "interval, in seconds, between stats prints (default is 1)",
 			  &stats_interval, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/examples/flow_filtering/main.c b/examples/flow_filtering/main.c
index c0bc1938ce..9c429a8335 100644
--- a/examples/flow_filtering/main.c
+++ b/examples/flow_filtering/main.c
@@ -274,11 +274,11 @@ flow_filtering_parse_args(int argc, char **argv)
 		.args = {
 			{ "--template", NULL, "Enable template API flow",
 			  &use_template_api, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--non-template", NULL, "Enable non template API flow",
 			  &use_template_api, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index bb03cdc4af..d7fee101d4 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -30,30 +30,48 @@ is_arg_positional(const struct rte_argparse_arg *arg)
 	return arg->name_long[0] != '-';
 }
 
-static inline uint32_t
-arg_attr_has_val(const struct rte_argparse_arg *arg)
+static inline bool
+is_valid_has_value_field(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_HAS_VAL_BITMASK, arg->flags);
+	switch (arg->value_required) {
+		case RTE_ARGPARSE_VALUE_NONE:
+		case RTE_ARGPARSE_VALUE_OPTIONAL:
+		case RTE_ARGPARSE_VALUE_REQUIRED:
+			return true;
+		/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }
 
-static inline uint32_t
-arg_attr_val_type(const struct rte_argparse_arg *arg)
-{
-	return RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, arg->flags);
+static inline bool
+is_valid_value_type_field(const struct rte_argparse_arg *arg)
+{
+	switch (arg->value_type) {
+		case RTE_ARGPARSE_VALUE_TYPE_NONE:
+		case RTE_ARGPARSE_VALUE_TYPE_INT:
+		case RTE_ARGPARSE_VALUE_TYPE_U8:
+		case RTE_ARGPARSE_VALUE_TYPE_U16:
+		case RTE_ARGPARSE_VALUE_TYPE_U32:
+		case RTE_ARGPARSE_VALUE_TYPE_U64:
+		case RTE_ARGPARSE_VALUE_TYPE_STR:
+		case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+			return true;
+		/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }
 
+
 static inline bool
 arg_attr_flag_multi(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_ARG_SUPPORT_MULTI, arg->flags);
+	return (arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI) != 0;
 }
 
 static inline uint64_t
 arg_attr_unused_bits(const struct rte_argparse_arg *arg)
 {
-#define USED_BIT_MASK	(RTE_ARGPARSE_HAS_VAL_BITMASK | \
-			 RTE_ARGPARSE_VAL_TYPE_BITMASK | \
-			 RTE_ARGPARSE_ARG_SUPPORT_MULTI)
+#define USED_BIT_MASK	(RTE_ARGPARSE_FLAG_SUPPORT_MULTI)
 	return arg->flags & ~USED_BIT_MASK;
 }
 
@@ -110,56 +128,51 @@ verify_arg_help(const struct rte_argparse_arg *arg)
 static int
 verify_arg_has_val(const struct rte_argparse_arg *arg)
 {
-	uint32_t has_val = arg_attr_has_val(arg);
-
+	if (!is_valid_has_value_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value field!", arg->name_long);
+		return -EINVAL;
+	}
 	if (is_arg_positional(arg)) {
-		if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE)
+		if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED)
 			return 0;
 		ARGPARSE_LOG(ERR, "argument %s is positional, must config required-val!",
 			     arg->name_long);
 		return -EINVAL;
 	}
 
-	if (has_val == 0) {
-		ARGPARSE_LOG(ERR, "argument %s is optional, has-value config wrong!",
-			     arg->name_long);
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
 static int
 verify_arg_saver(const struct rte_argparse *obj, uint32_t index)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	const struct rte_argparse_arg *arg = &obj->args[index];
-	uint32_t val_type = arg_attr_val_type(arg);
-	uint32_t has_val = arg_attr_has_val(arg);
 
 	if (arg->val_saver == NULL) {
-		if (val_type != 0) {
+		if (arg->value_type != RTE_ARGPARSE_VALUE_TYPE_NONE) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, value-type should not be set!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		if (obj->callback == NULL) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, but callback is NULL!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		return 0;
 	}
 
-	if (val_type == 0 || val_type >= cmp_max) {
-		ARGPARSE_LOG(ERR, "argument %s value-type config wrong!", arg->name_long);
+	/* check value_type field */
+	if (!is_valid_value_type_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value-type field!", arg->name_long);
+		return -EINVAL;
+	}
+	if (arg->value_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "missing value-type for argument %s!", arg->name_long);
 		return -EINVAL;
 	}
 
-	if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) {
+	if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED && arg->val_set != NULL) {
 		ARGPARSE_LOG(ERR, "argument %s has required value, value-set should be NULL!",
 			     arg->name_long);
 		return -EINVAL;
@@ -180,7 +193,7 @@ verify_arg_flags(const struct rte_argparse *obj, uint32_t index)
 		return -EINVAL;
 	}
 
-	if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI))
+	if (!(arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI))
 		return 0;
 
 	if (is_arg_positional(arg)) {
@@ -543,26 +556,27 @@ parse_arg_bool(struct rte_argparse_arg *arg, const char *value)
 static int
 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value)
 {
-	static struct {
-		int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value);
-	} map[] = {
-		/* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */
-		{ NULL          },
-		{ parse_arg_int },
-		{ parse_arg_u8  },
-		{ parse_arg_u16 },
-		{ parse_arg_u32 },
-		{ parse_arg_u64 },
-		{ parse_arg_str},
-		{ parse_arg_bool },
-	};
-	uint32_t index = arg_attr_val_type(arg);
-	int ret = -EINVAL;
-
-	if (index > 0 && index < RTE_DIM(map))
-		ret = map[index].f_parse_type(arg, value);
-
-	return ret;
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+		ARGPARSE_LOG(ERR, "argument %s doesn't specify a value-type!", arg->name_long);
+		return -EINVAL;
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+		return parse_arg_int(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+		return parse_arg_u8(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+		return parse_arg_u16(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+		return parse_arg_u32(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+		return parse_arg_u64(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+		return parse_arg_str(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return parse_arg_bool(arg, value);
+	/* omit default case so compiler warns on missing enum values */
+	}
+	return -EINVAL;
 }
 
 /* arg_parse indicates the name entered by the user, which can be long-name or short-name. */
@@ -615,7 +629,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 			/* process positional parameters. */
 			position_index++;
 			if (position_index > position_count) {
-				ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv);
+				ARGPARSE_LOG(ERR, "too many positional arguments %s!", curr_argv);
 				return -EINVAL;
 			}
 			arg = find_position_arg(obj, position_index);
@@ -640,23 +654,20 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		}
 
 		if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) {
-			ARGPARSE_LOG(ERR, "argument %s should not occur multiple!",
-				     arg_name);
+			ARGPARSE_LOG(ERR, "argument %s should not occur multiple times!", arg_name);
 			return -EINVAL;
 		}
 
 		value = (has_equal != NULL ? has_equal + 1 : NULL);
-		if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) {
+		if (arg->value_required == RTE_ARGPARSE_VALUE_NONE) {
 			if (value != NULL) {
-				ARGPARSE_LOG(ERR, "argument %s should not take value!",
-					     arg_name);
+				ARGPARSE_LOG(ERR, "argument %s should not take value!", arg_name);
 				return -EINVAL;
 			}
-		} else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) {
+		} else if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED) {
 			if (value == NULL) {
 				if (i >= argc - 1) {
-					ARGPARSE_LOG(ERR, "argument %s doesn't have value!",
-						     arg_name);
+					ARGPARSE_LOG(ERR, "argument %s doesn't have value!", arg_name);
 					return -EINVAL;
 				}
 				/* Set value and make i move next. */
@@ -808,21 +819,18 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 
 RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_argparse_parse_type, 24.03)
 int
-rte_argparse_parse_type(const char *str, uint64_t val_type, void *val)
+rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	struct rte_argparse_arg arg = {
 		.name_long = str,
 		.name_short = NULL,
 		.val_saver = val,
 		.val_set = NULL,
-		.flags = val_type,
+		.value_type = val_type,
 	};
-	uint32_t value_type = arg_attr_val_type(&arg);
-
-	if (value_type == 0 || value_type >= cmp_max)
+	if (val_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "argument %s doesn't have value-type!", str);
 		return -EINVAL;
-
+	}
 	return parse_arg_autosave(&arg, str);
 }
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index 8cdb3195cb..8a78d53bd3 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -37,49 +37,44 @@
 extern "C" {
 #endif
 
-/**@{@name Flag definition (in bitmask form) for an argument
- *
- * @note Bits[0~1] represent the argument whether has value,
- * bits[2~9] represent the value type which used when autosave.
- *
- * @see struct rte_argparse_arg::flags
- */
-/** The argument has no value. */
-#define RTE_ARGPARSE_ARG_NO_VALUE       RTE_SHIFT_VAL64(1, 0)
-/** The argument must have a value. */
-#define RTE_ARGPARSE_ARG_REQUIRED_VALUE RTE_SHIFT_VAL64(2, 0)
-/** The argument has optional value. */
-#define RTE_ARGPARSE_ARG_OPTIONAL_VALUE RTE_SHIFT_VAL64(3, 0)
-/** The argument's value is int type. */
-#define RTE_ARGPARSE_ARG_VALUE_INT      RTE_SHIFT_VAL64(1, 2)
-/** The argument's value is uint8 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U8       RTE_SHIFT_VAL64(2, 2)
-/** The argument's value is uint16 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U16      RTE_SHIFT_VAL64(3, 2)
-/** The argument's value is uint32 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U32      RTE_SHIFT_VAL64(4, 2)
-/** The argument's value is uint64 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U64      RTE_SHIFT_VAL64(5, 2)
-/** The argument's value is string type. */
-#define RTE_ARGPARSE_ARG_VALUE_STR      RTE_SHIFT_VAL64(6, 2)
-/** The argument's value is boolean flag type. */
-#define RTE_ARGPARSE_ARG_VALUE_BOOL     RTE_SHIFT_VAL64(7, 2)
-/** Max value type. */
-#define RTE_ARGPARSE_ARG_VALUE_MAX      RTE_SHIFT_VAL64(8, 2)
 /**
- * Flag for that argument support occur multiple times.
- * This flag can be set only when the argument is optional.
- * When this flag is set, the callback type must be used for parsing.
+ * enum defining whether an argument takes a value or not.
  */
-#define RTE_ARGPARSE_ARG_SUPPORT_MULTI  RTE_BIT64(10)
-/** Reserved for this library implementation usage. */
-#define RTE_ARGPARSE_ARG_RESERVED_FIELD RTE_GENMASK64(63, 48)
-/**@}*/
+enum rte_argparse_value_required {
+	/** The argument takes no value. */
+	RTE_ARGPARSE_VALUE_NONE,
+	/** The argument must have a value. */
+	RTE_ARGPARSE_VALUE_REQUIRED,
+	/** The argument has optional value. */
+	RTE_ARGPARSE_VALUE_OPTIONAL,
+};
 
-/** Bitmask used to get the argument whether has value. */
-#define RTE_ARGPARSE_HAS_VAL_BITMASK	RTE_GENMASK64(1, 0)
-/** Bitmask used to get the argument's value type. */
-#define RTE_ARGPARSE_VAL_TYPE_BITMASK	RTE_GENMASK64(9, 2)
+/** enum defining the type of the argument, integer, boolean or just string */
+enum rte_argparse_value_type {
+	/** Argument takes no value, or value type not specified.
+	 * Should be used when argument is to be handled via callback.
+	 */
+	RTE_ARGPARSE_VALUE_TYPE_NONE = 0,
+	/** The argument's value is int type. */
+	RTE_ARGPARSE_VALUE_TYPE_INT,
+	/** The argument's value is uint8 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U8,
+	/** The argument's value is uint16 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U16,
+	/** The argument's value is uint32 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U32,
+	/** The argument's value is uint64 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U64,
+	/** The argument's value is string type. */
+	RTE_ARGPARSE_VALUE_TYPE_STR,
+	/** The argument's value is boolean flag type. */
+	RTE_ARGPARSE_VALUE_TYPE_BOOL,
+};
+
+/** Additional flags which may be specified for each argument */
+enum rte_argparse_arg_flags {
+	RTE_ARGPARSE_FLAG_SUPPORT_MULTI = RTE_BIT32(0),
+};
 
 /**
  * A structure used to hold argument's configuration.
@@ -105,10 +100,8 @@ struct rte_argparse_arg {
 
 	/**
 	 * Saver for the argument's value.
-	 * 1) If the filed is NULL, the callback way is used for parsing
-	 *    argument.
-	 * 2) If the field is not NULL, the autosave way is used for parsing
-	 *    argument.
+	 * 1) If this field is NULL, the callback is used for parsing argument.
+	 * 2) If this field is not NULL, the argument's value will be automatically saved.
 	 */
 	void *val_saver;
 	/**
@@ -123,8 +116,13 @@ struct rte_argparse_arg {
 	 */
 	void *val_set;
 
-	/** Flag definition (RTE_ARGPARSE_ARG_*) for the argument. */
-	uint64_t flags;
+	/** Specify if the argument takes a value, @see enum rte_argparse_value_required. */
+	enum rte_argparse_value_required value_required;
+	/** The type of the argument, @see enum rte_argparse_value_type. */
+	enum rte_argparse_value_type value_type;
+
+	/** any additional flags for this argument */
+	uint32_t flags;
 };
 
 /**
@@ -206,7 +204,7 @@ int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
  *   0 on success. Otherwise negative value is returned.
  */
 __rte_experimental
-int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val);
+int rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val);
 
 #ifdef __cplusplus
 }
-- 
2.48.1


^ permalink raw reply	[relevance 6%]

* Re: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  @ 2025-05-27 15:07  3%   ` Stephen Hemminger
  2025-05-27 16:24  3%     ` Morten Brørup
  0 siblings, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-05-27 15:07 UTC (permalink / raw)
  To: skori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Jie Hai, Ian Stokes,
	Bruce Richardson, Vladimir Medvedkin, Anatoly Burakov,
	Dariusz Sosnowski, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
	Suanming Mou, Matan Azrad, Long Li, Wei Hu, Chaoyong He,
	Jiawen Wu, Andrew Rybchenko, Jerin Jacob, Maciej Czekaj,
	Jian Wang, Maxime Coquelin, Chenbo Xia, Jochen Behrens,
	Thomas Monjalon, Ferruh Yigit, dev

On Mon, 12 May 2025 20:37:19 +0530
<skori@marvell.com> wrote:

>  /**@{@name Rx hardware descriptor states
> diff --git a/lib/ethdev/rte_ethdev_core.h b/lib/ethdev/rte_ethdev_core.h
> index e55fb42996..4ffae4921a 100644
> --- a/lib/ethdev/rte_ethdev_core.h
> +++ b/lib/ethdev/rte_ethdev_core.h
> @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void *txq,
>  
>  
>  /** @internal Get number of used descriptors on a receive queue. */
> -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> +typedef int (*eth_rx_queue_count_t)(void *rxq);
>  
>  /** @internal Check the status of a Rx descriptor */
>  typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t offset);


This gets reported as ABI breakage. The change will have to wait until next LTS (25.11)


  [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at rte_ethdev.c:47:1:
    type of variable changed:
      array element type 'struct rte_eth_fp_ops' changed:
        type size hasn't changed
        1 data member change:
          type of 'eth_rx_queue_count_t rx_queue_count' changed:
            underlying type 'uint32_t (*)(void*)' changed:
              in pointed to type 'function type uint32_t (void*)':
                return type changed:
                  entity changed from 'typedef uint32_t' to 'int'
                  type size hasn't changed
      type size hasn't changed

^ permalink raw reply	[relevance 3%]

* [PATCH v5 1/3] eal: deprecate old coremask-based EAL parameters
  @ 2025-05-27 15:29  2%   ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-05-27 15:29 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson

As the number of cores/cpus on platforms has increased over the years,
the use of coremasks rather than core-lists for identifying DPDK cores
has become more and more unwieldy. At this point, let's deprecate the
coremask-based EAL parameters for future removal, and point users to the
core-list based versions instead.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 doc/guides/eventdevs/dlb2.rst                |  6 +++---
 doc/guides/faq/faq.rst                       |  8 +++-----
 doc/guides/linux_gsg/build_sample_apps.rst   |  7 +++----
 doc/guides/linux_gsg/eal_args.include.rst    |  8 ++------
 doc/guides/prog_guide/meson_ut.rst           |  2 +-
 doc/guides/prog_guide/multi_proc_support.rst |  2 +-
 doc/guides/prog_guide/service_cores.rst      |  8 ++++----
 doc/guides/rel_notes/deprecation.rst         | 10 ++++++++++
 doc/guides/sample_app_ug/ip_frag.rst         |  7 +------
 doc/guides/sample_app_ug/ip_reassembly.rst   |  7 +------
 doc/guides/sample_app_ug/multi_process.rst   | 14 +++++---------
 doc/guides/sample_app_ug/qos_scheduler.rst   |  2 +-
 doc/guides/sample_app_ug/test_pipeline.rst   |  2 +-
 doc/guides/tools/testbbdev.rst               |  2 +-
 lib/eal/common/eal_common_options.c          |  6 ++++++
 15 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/doc/guides/eventdevs/dlb2.rst b/doc/guides/eventdevs/dlb2.rst
index 8ec7168f20..daa8bf27fb 100644
--- a/doc/guides/eventdevs/dlb2.rst
+++ b/doc/guides/eventdevs/dlb2.rst
@@ -408,9 +408,9 @@ the DLB device locally available on the same tile along with other
 resources. To allocate optimal resources, probing is done for each
 producer port (PP) for a given CPU and the best performing ports are
 allocated to producers. The cpu used for probing is either the first
-core of producer coremask (if present) or the second core of EAL
-coremask. This will be extended later to probe for all CPUs in the
-producer coremask or EAL coremask. Producer coremask can be passed
+core of producer coremask DLB2 device parameter (if present) or the second core of EAL
+core list. This will be extended later to probe for all CPUs in the
+producer coremask or EAL core list. Producer coremask can be passed
 along with the BDF of the DLB devices.
 
     .. code-block:: console
diff --git a/doc/guides/faq/faq.rst b/doc/guides/faq/faq.rst
index 297cb5119e..ddcb018b3a 100644
--- a/doc/guides/faq/faq.rst
+++ b/doc/guides/faq/faq.rst
@@ -47,11 +47,9 @@ therefore all the hugepages are allocated on the wrong socket.
 To avoid this scenario, either lower the amount of hugepage memory available to 1 GB size (or less), or run the application with taskset
 affinitizing the application to a would-be main core.
 
-For example, if your EAL coremask is 0xff0, the main core will usually be the first core in the coremask (0x10); this is what you have to supply to taskset::
+For example, if your EAL core list is '4-11', the main core will usually be the first core in the list (core 4); this is what you have to supply to taskset::
 
-   taskset 0x10 ./l2fwd -l 4-11 -n 2
-
-.. Note: Instead of '-c 0xff0' use the '-l 4-11' as a cleaner way to define lcores.
+   taskset -c 4 ./l2fwd -l 4-11 -n 2
 
 In this way, the hugepages have a greater chance of being allocated to the correct socket.
 Additionally, a ``--socket-mem`` option could be used to ensure the availability of memory for each socket, so that if hugepages were allocated on
@@ -167,7 +165,7 @@ Can I split packet RX to use DPDK and have an application's higher order functio
 ----------------------------------------------------------------------------------------------------------------
 
 The DPDK's lcore threads are Linux pthreads bound onto specific cores. Configure the DPDK to do work on the same
-cores and run the application's other work on other cores using the DPDK's "coremask" setting to specify which
+cores and run the application's other work on other cores using the DPDK's "core list" (-l/--lcores) setting to specify which
 cores it should launch itself on.
 
 
diff --git a/doc/guides/linux_gsg/build_sample_apps.rst b/doc/guides/linux_gsg/build_sample_apps.rst
index 433839ecc7..c568549f26 100644
--- a/doc/guides/linux_gsg/build_sample_apps.rst
+++ b/doc/guides/linux_gsg/build_sample_apps.rst
@@ -126,10 +126,9 @@ and that cores 0-3 are present and are to be used for running the application)::
 Logical Core Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The coremask (-c 0x0f) or corelist (-l 0-3) parameter is always mandatory for DPDK applications.
-Each bit of the mask corresponds to the equivalent logical core number as reported by Linux. The preferred corelist option is a cleaner method to define cores to be used.
+The corelist (-l/--lcores 0-3) parameter is always mandatory for DPDK applications.
 Since these logical core numbers, and their mapping to specific cores on specific NUMA sockets, can vary from platform to platform,
-it is recommended that the core layout for each platform be considered when choosing the coremask/corelist to use in each case.
+it is recommended that the core layout for each platform be considered when choosing the corelist to use in each case.
 
 On initialization of the EAL layer by a DPDK application, the logical cores to be used and their socket location are displayed.
 This information can also be determined for all cores on the system by examining the ``/proc/cpuinfo`` file, for example, by running cat ``/proc/cpuinfo``.
@@ -151,7 +150,7 @@ This can be useful when using other processors to understand the mapping of the
 
 .. warning::
 
-    The logical core layout can change between different board layouts and should be checked before selecting an application coremask/corelist.
+    The logical core layout can change between different board layouts and should be checked before selecting an application corelist.
 
 Hugepage Memory Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/linux_gsg/eal_args.include.rst b/doc/guides/linux_gsg/eal_args.include.rst
index 9cfbf7de84..7ffd2e2535 100644
--- a/doc/guides/linux_gsg/eal_args.include.rst
+++ b/doc/guides/linux_gsg/eal_args.include.rst
@@ -4,10 +4,6 @@
 Lcore-related options
 ~~~~~~~~~~~~~~~~~~~~~
 
-*   ``-c <core mask>``
-
-    Set the hexadecimal bitmask of the cores to run on.
-
 *   ``-l <core list>``
 
     List of cores to run on
@@ -37,9 +33,9 @@ Lcore-related options
 
     Core ID that is used as main.
 
-*   ``-s <service core mask>``
+*   ``-S <service core list>``
 
-    Hexadecimal bitmask of cores to be used as service cores.
+    List of cores to be used as service cores.
 
 Device-related options
 ~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/prog_guide/meson_ut.rst b/doc/guides/prog_guide/meson_ut.rst
index 78cf3f845c..9bc52a30fc 100644
--- a/doc/guides/prog_guide/meson_ut.rst
+++ b/doc/guides/prog_guide/meson_ut.rst
@@ -60,7 +60,7 @@ Arguments of ``test()`` that can be provided in meson.build are as below:
 
 Note: the content of meson ``--test-args`` option and the content of ``args``
 are appended when invoking the DPDK test binary.
-Because of this, it is recommended not to set any default coremask or memory
+Because of this, it is recommended not to set any default corelist or memory
 configuration in per test ``args`` and rather let users select what best fits
 their environment. If a test can't run, then it should be skipped, as described
 below.
diff --git a/doc/guides/prog_guide/multi_proc_support.rst b/doc/guides/prog_guide/multi_proc_support.rst
index 0c57145470..dd57b6f8c1 100644
--- a/doc/guides/prog_guide/multi_proc_support.rst
+++ b/doc/guides/prog_guide/multi_proc_support.rst
@@ -166,7 +166,7 @@ Some of these are documented below:
     so it is recommended that it be disabled only when absolutely necessary,
     and only when the implications of this change have been understood.
 
-*   All DPDK processes running as a single application and using shared memory must have distinct coremask/corelist arguments.
+*   All DPDK processes running as a single application and using shared memory must have distinct corelist arguments.
     It is not possible to have a primary and secondary instance, or two secondary instances,
     using any of the same logical cores.
     Attempting to do so can cause corruption of memory pool caches, among other issues.
diff --git a/doc/guides/prog_guide/service_cores.rst b/doc/guides/prog_guide/service_cores.rst
index d4e6c3d6e6..5284eeb96a 100644
--- a/doc/guides/prog_guide/service_cores.rst
+++ b/doc/guides/prog_guide/service_cores.rst
@@ -26,10 +26,10 @@ Service Core Initialization
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 There are two methods to having service cores in a DPDK application, either by
-using the service coremask, or by dynamically adding cores using the API.
-The simpler of the two is to pass the `-s` coremask argument to EAL, which will
-take any cores available in the main DPDK coremask, and if the bits are also set
-in the service coremask the cores become service-cores instead of DPDK
+using the service corelist, or by dynamically adding cores using the API.
+The simpler of the two is to pass the `-S` corelist argument to EAL, which will
+take any cores available in the main DPDK corelist, and if also set
+in the service corelist the cores become service-cores instead of DPDK
 application lcores.
 
 Enabling Services on Cores
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 36489f6e68..2ea898ff8a 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -17,6 +17,16 @@ Other API and ABI deprecation notices are to be posted below.
 Deprecation Notices
 -------------------
 
+* EAL: The ``-c <coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-l <corelist>`` or ``--lcores=<corelist>`` parameters instead
+  to specify the cores to be used when running a DPDK application.
+
+* EAL: The ``-s <service-coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-S <service-corelist>`` parameter instead
+  to specify the cores to be used for background services in DPDK.
+
 * build: The ``enable_kmods`` option is deprecated and will be removed in a future release.
   Setting/clearing the option has no impact on the build.
   Instead, kernel modules will be always built for OS's where out-of-tree kernel modules
diff --git a/doc/guides/sample_app_ug/ip_frag.rst b/doc/guides/sample_app_ug/ip_frag.rst
index 4b2071cbae..3f0b5ce5e7 100644
--- a/doc/guides/sample_app_ug/ip_frag.rst
+++ b/doc/guides/sample_app_ug/ip_frag.rst
@@ -67,12 +67,7 @@ To run the example in linux environment with 2 lcores (2,4) over 2 ports(0,2) wi
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-ip_fragmentation -l 2,4 -n 3 -- -p 5
-    EAL: coremask set to 14
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 1
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 1
-    EAL: Detected lcore 4 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     Initializing port 0 on lcore 2... Address:00:1B:21:76:FA:2C, rxq=0 txq=2,0 txq=4,1
diff --git a/doc/guides/sample_app_ug/ip_reassembly.rst b/doc/guides/sample_app_ug/ip_reassembly.rst
index b8800dd9e7..e55ee7eb79 100644
--- a/doc/guides/sample_app_ug/ip_reassembly.rst
+++ b/doc/guides/sample_app_ug/ip_reassembly.rst
@@ -63,12 +63,7 @@ with 1 Rx queue per lcore:
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-ip_reassembly -l 2,4 -n 3 -- -p 5
-    EAL: coremask set to 14
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 1
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 1
-    EAL: Detected lcore 4 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     Initializing port 0 on lcore 2... Address:00:1B:21:76:FA:2C, rxq=0 txq=2,0 txq=4,1
diff --git a/doc/guides/sample_app_ug/multi_process.rst b/doc/guides/sample_app_ug/multi_process.rst
index e2b1b16c84..29bca806e1 100644
--- a/doc/guides/sample_app_ug/multi_process.rst
+++ b/doc/guides/sample_app_ug/multi_process.rst
@@ -36,7 +36,7 @@ Running the Application
 ^^^^^^^^^^^^^^^^^^^^^^^
 
 To run the application, start ``simple_mp`` binary in one terminal,
-passing at least two cores in the coremask/corelist:
+passing at least two cores in the corelist:
 
 .. code-block:: console
 
@@ -50,11 +50,7 @@ The process should start successfully and display a command prompt as follows:
 .. code-block:: console
 
     $ ./<build_dir>/examples/dpdk-simple_mp -l 0-1 -n 4 --proc-type=primary
-    EAL: coremask set to 3
-    EAL: Detected lcore 0 on socket 0
-    EAL: Detected lcore 1 on socket 0
-    EAL: Detected lcore 2 on socket 0
-    EAL: Detected lcore 3 on socket 0
+    EAL: Detected CPU lcores: ...
     ...
 
     EAL: Requesting 2 pages of size 1073741824
@@ -72,7 +68,7 @@ The process should start successfully and display a command prompt as follows:
     simple_mp >
 
 To run the secondary process to communicate with the primary process,
-again run the same binary setting at least two cores in the coremask/corelist:
+again run the same binary setting at least two cores in the corelist:
 
 .. code-block:: console
 
@@ -237,8 +233,8 @@ In addition to the EAL parameters, the application-specific parameters are:
 .. note::
 
    In the server process, has a single thread using the lowest numbered lcore
-   in the coremask/corelist, performs all packet I/O.
-   If coremask/corelist parameter specifies with more than a single lcore bit set,
+   in the corelist, performs all packet I/O.
+   If corelist parameter specifies with more than a single lcore,
    an additional lcore will be used for a thread to print packet count periodically.
 
 The server application stores configuration data in shared memory,
diff --git a/doc/guides/sample_app_ug/qos_scheduler.rst b/doc/guides/sample_app_ug/qos_scheduler.rst
index 9936b99172..36ada4902c 100644
--- a/doc/guides/sample_app_ug/qos_scheduler.rst
+++ b/doc/guides/sample_app_ug/qos_scheduler.rst
@@ -194,7 +194,7 @@ Another example with 2 packet flow configurations using different ports but shar
 Note that independent cores for the packet flow configurations for each of the RX, WT and TX thread are also supported,
 providing flexibility to balance the work.
 
-The EAL coremask/corelist is constrained to contain the default main core 1 and the RX, WT and TX cores only.
+The EAL corelist is constrained to contain the default main core 1 and the RX, WT and TX cores only.
 
 Explanation
 -----------
diff --git a/doc/guides/sample_app_ug/test_pipeline.rst b/doc/guides/sample_app_ug/test_pipeline.rst
index d57d08fb2c..818be93cd6 100644
--- a/doc/guides/sample_app_ug/test_pipeline.rst
+++ b/doc/guides/sample_app_ug/test_pipeline.rst
@@ -47,7 +47,7 @@ The application execution command line is:
 
     ./dpdk-test-pipeline [EAL options] -- -p PORTMASK --TABLE_TYPE
 
-The -c or -l EAL CPU coremask/corelist option has to contain exactly 3 CPU cores.
+The ``-l/--lcores`` EAL CPU corelist option has to contain exactly 3 CPU cores.
 The first CPU core in the core mask is assigned for core A, the second for core B and the third for core C.
 
 The PORTMASK parameter must contain 2 or 4 ports.
diff --git a/doc/guides/tools/testbbdev.rst b/doc/guides/tools/testbbdev.rst
index ddb8d787be..8677cd2c43 100644
--- a/doc/guides/tools/testbbdev.rst
+++ b/doc/guides/tools/testbbdev.rst
@@ -78,7 +78,7 @@ The following are the command-line options:
 
 ``-l NUM_LCORES, --num_lcores NUM_LCORES``
  Specifies number of lcores to run. If not specified num_lcores is set
- according to value from RTE configuration (EAL coremask)
+ according to value from RTE configuration (EAL corelist)
 
 ``-b BURST_SIZE [BURST_SIZE ...], --burst-size BURST_SIZE [BURST_SIZE ...]``
  Specifies operations enqueue/dequeue burst size. If not specified burst_size is
diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index b6fff7ec05..19c5997c7c 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -614,6 +614,9 @@ eal_parse_service_coremask(const char *coremask)
 	int val;
 	uint32_t taken_lcore_count = 0;
 
+	EAL_LOG(WARNING, "'-s <service-coremask>' is deprecated, and will be removed in a future release.");
+	EAL_LOG(WARNING, "\tUse '-S <service-corelist>' option instead.");
+
 	if (coremask == NULL)
 		return -1;
 	/* Remove all blank characters ahead and after .
@@ -777,6 +780,9 @@ rte_eal_parse_coremask(const char *coremask, int *cores)
 		cores[idx] = -1;
 	idx = 0;
 
+	EAL_LOG(WARNING, "'-c <coremask>' option is deprecated, and will be removed in a future release");
+	EAL_LOG(WARNING, "\tUse '-l <corelist>' or '--lcores=<corelist>' option instead");
+
 	/* Remove all blank characters ahead and after .
 	 * Remove 0x/0X if exists.
 	 */
-- 
2.48.1


^ permalink raw reply	[relevance 2%]

* RE: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  2025-05-27 15:07  3%   ` Stephen Hemminger
@ 2025-05-27 16:24  3%     ` Morten Brørup
  2025-05-28  8:17  0%       ` Konstantin Ananyev
  0 siblings, 1 reply; 153+ results
From: Morten Brørup @ 2025-05-27 16:24 UTC (permalink / raw)
  To: Stephen Hemminger, skori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Jie Hai, Ian Stokes,
	Bruce Richardson, Vladimir Medvedkin, Anatoly Burakov,
	Dariusz Sosnowski, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
	Suanming Mou, Matan Azrad, Long Li, Wei Hu, Chaoyong He,
	Jiawen Wu, Andrew Rybchenko, Jerin Jacob, Maciej Czekaj,
	Jian Wang, Maxime Coquelin, Chenbo Xia, Jochen Behrens,
	Thomas Monjalon, Ferruh Yigit, dev

> From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> Sent: Tuesday, 27 May 2025 17.07
> 
> On Mon, 12 May 2025 20:37:19 +0530
> <skori@marvell.com> wrote:
> 
> >  /**@{@name Rx hardware descriptor states
> > diff --git a/lib/ethdev/rte_ethdev_core.h
> b/lib/ethdev/rte_ethdev_core.h
> > index e55fb42996..4ffae4921a 100644
> > --- a/lib/ethdev/rte_ethdev_core.h
> > +++ b/lib/ethdev/rte_ethdev_core.h
> > @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void *txq,
> >
> >
> >  /** @internal Get number of used descriptors on a receive queue. */
> > -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > +typedef int (*eth_rx_queue_count_t)(void *rxq);
> >
> >  /** @internal Check the status of a Rx descriptor */
> >  typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t
> offset);
> 
> 
> This gets reported as ABI breakage. The change will have to wait until
> next LTS (25.11)

The return type was weird (wrong) to begin with.
When used, it gets cast to int:
https://elixir.bootlin.com/dpdk/v25.03/source/lib/ethdev/rte_ethdev.h#L6404

You are right that it formally changes the ABI, and we should go through the LTS motions.
But, for this change, I'd favor an exception.

PS: As a consequence of this change, a patch to update the return type of the callback in all the ethdev drivers should be provided.

> 
> 
>   [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at
> rte_ethdev.c:47:1:
>     type of variable changed:
>       array element type 'struct rte_eth_fp_ops' changed:
>         type size hasn't changed
>         1 data member change:
>           type of 'eth_rx_queue_count_t rx_queue_count' changed:
>             underlying type 'uint32_t (*)(void*)' changed:
>               in pointed to type 'function type uint32_t (void*)':
>                 return type changed:
>                   entity changed from 'typedef uint32_t' to 'int'
>                   type size hasn't changed
>       type size hasn't changed

^ permalink raw reply	[relevance 3%]

* RE: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  2025-05-27 16:24  3%     ` Morten Brørup
@ 2025-05-28  8:17  0%       ` Konstantin Ananyev
  2025-05-28  8:23  0%         ` Konstantin Ananyev
  0 siblings, 1 reply; 153+ results
From: Konstantin Ananyev @ 2025-05-28  8:17 UTC (permalink / raw)
  To: Morten Brørup, Stephen Hemminger, skori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Jie Hai, Ian Stokes,
	Bruce Richardson, Vladimir Medvedkin, Anatoly Burakov,
	Dariusz Sosnowski, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
	Suanming Mou, Matan Azrad, Long Li, Wei Hu, Chaoyong He,
	Jiawen Wu, Andrew Rybchenko, Jerin Jacob, Maciej Czekaj,
	Jian Wang, Maxime Coquelin, Chenbo Xia, Jochen Behrens,
	Thomas Monjalon, Ferruh Yigit, dev




> 
> > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > Sent: Tuesday, 27 May 2025 17.07
> >
> > On Mon, 12 May 2025 20:37:19 +0530
> > <skori@marvell.com> wrote:
> >
> > >  /**@{@name Rx hardware descriptor states
> > > diff --git a/lib/ethdev/rte_ethdev_core.h
> > b/lib/ethdev/rte_ethdev_core.h
> > > index e55fb42996..4ffae4921a 100644
> > > --- a/lib/ethdev/rte_ethdev_core.h
> > > +++ b/lib/ethdev/rte_ethdev_core.h
> > > @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void *txq,
> > >
> > >
> > >  /** @internal Get number of used descriptors on a receive queue. */
> > > -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > > +typedef int (*eth_rx_queue_count_t)(void *rxq);
> > >
> > >  /** @internal Check the status of a Rx descriptor */
> > >  typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t
> > offset);
> >
> >
> > This gets reported as ABI breakage. The change will have to wait until
> > next LTS (25.11)
> 
> The return type was weird (wrong) to begin with.
> When used, it gets cast to int:
> https://elixir.bootlin.com/dpdk/v25.03/source/lib/ethdev/rte_ethdev.h#L6404

Personally, I don't see anything strange here.
devops rx_queue_count() supposed to return uint, because it should never failed for
valid queue. 
While rte_eth_rx_queue_count() itself can fail - wrong port/queue id, etc. 

> 
> You are right that it formally changes the ABI, and we should go through the LTS motions.
> But, for this change, I'd favor an exception.

Again, from my opinion, there is nothing that urgent/important why such changes (if needed)
can't wait till next LTS.
For now, we can simply do type conversion explicitly at rte_eth_rx_queue_count().

> PS: As a consequence of this change, a patch to update the return type of the callback in all the ethdev drivers should be provided.
> 
> >
> >
> >   [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at
> > rte_ethdev.c:47:1:
> >     type of variable changed:
> >       array element type 'struct rte_eth_fp_ops' changed:
> >         type size hasn't changed
> >         1 data member change:
> >           type of 'eth_rx_queue_count_t rx_queue_count' changed:
> >             underlying type 'uint32_t (*)(void*)' changed:
> >               in pointed to type 'function type uint32_t (void*)':
> >                 return type changed:
> >                   entity changed from 'typedef uint32_t' to 'int'
> >                   type size hasn't changed
> >       type size hasn't changed

^ permalink raw reply	[relevance 0%]

* RE: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  2025-05-28  8:17  0%       ` Konstantin Ananyev
@ 2025-05-28  8:23  0%         ` Konstantin Ananyev
  2025-05-28  8:48  0%           ` Morten Brørup
  0 siblings, 1 reply; 153+ results
From: Konstantin Ananyev @ 2025-05-28  8:23 UTC (permalink / raw)
  To: Konstantin Ananyev, Morten Brørup, Stephen Hemminger, skori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Ian Stokes, Bruce Richardson,
	Vladimir Medvedkin, Anatoly Burakov, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad, Long Li, Wei Hu, Chaoyong He, Jiawen Wu,
	Andrew Rybchenko, Jerin Jacob, Maciej Czekaj, Jian Wang,
	Maxime Coquelin, Chenbo Xia, Jochen Behrens, Thomas Monjalon,
	Ferruh Yigit, dev



> >
> > > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > > Sent: Tuesday, 27 May 2025 17.07
> > >
> > > On Mon, 12 May 2025 20:37:19 +0530
> > > <skori@marvell.com> wrote:
> > >
> > > >  /**@{@name Rx hardware descriptor states
> > > > diff --git a/lib/ethdev/rte_ethdev_core.h
> > > b/lib/ethdev/rte_ethdev_core.h
> > > > index e55fb42996..4ffae4921a 100644
> > > > --- a/lib/ethdev/rte_ethdev_core.h
> > > > +++ b/lib/ethdev/rte_ethdev_core.h
> > > > @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void *txq,
> > > >
> > > >
> > > >  /** @internal Get number of used descriptors on a receive queue. */
> > > > -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > > > +typedef int (*eth_rx_queue_count_t)(void *rxq);
> > > >
> > > >  /** @internal Check the status of a Rx descriptor */
> > > >  typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t
> > > offset);
> > >
> > >
> > > This gets reported as ABI breakage. The change will have to wait until
> > > next LTS (25.11)
> >
> > The return type was weird (wrong) to begin with.
> > When used, it gets cast to int:
> > https://elixir.bootlin.com/dpdk/v25.03/source/lib/ethdev/rte_ethdev.h#L6404
> 
> Personally, I don't see anything strange here.
> devops rx_queue_count() supposed to return uint, because it should never failed for
> valid queue.
> While rte_eth_rx_queue_count() itself can fail - wrong port/queue id, etc.

BTW, rx_queue_setup() accepts only uint16_t for number of rx descritoirs:
int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
                uint16_t nb_rx_desc, unsigned int socket_id,
                const struct rte_eth_rxconf *rx_conf,
                struct rte_mempool *mb_pool);

shouldn't dev->rx_queue_count() then also return uitn16_t (for consistency)?

> >
> > You are right that it formally changes the ABI, and we should go through the LTS motions.
> > But, for this change, I'd favor an exception.
> 
> Again, from my opinion, there is nothing that urgent/important why such changes (if needed)
> can't wait till next LTS.
> For now, we can simply do type conversion explicitly at rte_eth_rx_queue_count().
> 
> > PS: As a consequence of this change, a patch to update the return type of the callback in all the ethdev drivers should be provided.
> >
> > >
> > >
> > >   [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at
> > > rte_ethdev.c:47:1:
> > >     type of variable changed:
> > >       array element type 'struct rte_eth_fp_ops' changed:
> > >         type size hasn't changed
> > >         1 data member change:
> > >           type of 'eth_rx_queue_count_t rx_queue_count' changed:
> > >             underlying type 'uint32_t (*)(void*)' changed:
> > >               in pointed to type 'function type uint32_t (void*)':
> > >                 return type changed:
> > >                   entity changed from 'typedef uint32_t' to 'int'
> > >                   type size hasn't changed
> > >       type size hasn't changed

^ permalink raw reply	[relevance 0%]

* RE: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  2025-05-28  8:23  0%         ` Konstantin Ananyev
@ 2025-05-28  8:48  0%           ` Morten Brørup
  2025-05-28  9:14  0%             ` Konstantin Ananyev
  0 siblings, 1 reply; 153+ results
From: Morten Brørup @ 2025-05-28  8:48 UTC (permalink / raw)
  To: Konstantin Ananyev, Stephen Hemminger, skori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Ian Stokes, Bruce Richardson,
	Vladimir Medvedkin, Anatoly Burakov, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad, Long Li, Wei Hu, Chaoyong He, Jiawen Wu,
	Andrew Rybchenko, Jerin Jacob, Maciej Czekaj, Jian Wang,
	Maxime Coquelin, Chenbo Xia, Jochen Behrens, Thomas Monjalon,
	Ferruh Yigit, dev

> From: Konstantin Ananyev [mailto:konstantin.ananyev@huawei.com]
> Sent: Wednesday, 28 May 2025 10.24
> 
> > >
> > > > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > > > Sent: Tuesday, 27 May 2025 17.07
> > > >
> > > > On Mon, 12 May 2025 20:37:19 +0530
> > > > <skori@marvell.com> wrote:
> > > >
> > > > >  /**@{@name Rx hardware descriptor states
> > > > > diff --git a/lib/ethdev/rte_ethdev_core.h
> > > > b/lib/ethdev/rte_ethdev_core.h
> > > > > index e55fb42996..4ffae4921a 100644
> > > > > --- a/lib/ethdev/rte_ethdev_core.h
> > > > > +++ b/lib/ethdev/rte_ethdev_core.h
> > > > > @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void *txq,
> > > > >
> > > > >
> > > > >  /** @internal Get number of used descriptors on a receive
> queue. */
> > > > > -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > > > > +typedef int (*eth_rx_queue_count_t)(void *rxq);
> > > > >
> > > > >  /** @internal Check the status of a Rx descriptor */
> > > > >  typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t
> > > > offset);
> > > >
> > > >
> > > > This gets reported as ABI breakage. The change will have to wait
> until
> > > > next LTS (25.11)
> > >
> > > The return type was weird (wrong) to begin with.
> > > When used, it gets cast to int:
> > >
> https://elixir.bootlin.com/dpdk/v25.03/source/lib/ethdev/rte_ethdev.h#L
> 6404
> >
> > Personally, I don't see anything strange here.
> > devops rx_queue_count() supposed to return uint, because it should
> never failed for
> > valid queue.

The main thing wrong is inconsistency with its sibling API for TX queue count:
/** @internal Get number of used descriptors on a receive queue. */
typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
/** @internal Get number of used descriptors on a transmit queue. */
typedef int (*eth_tx_queue_count_t)(void *txq);

> > While rte_eth_rx_queue_count() itself can fail - wrong port/queue id,
> etc.
> 
> BTW, rx_queue_setup() accepts only uint16_t for number of rx
> descritoirs:
> int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
>                 uint16_t nb_rx_desc, unsigned int socket_id,
>                 const struct rte_eth_rxconf *rx_conf,
>                 struct rte_mempool *mb_pool);
> 
> shouldn't dev->rx_queue_count() then also return uitn16_t (for
> consistency)?

If neither the RX or TX queue count callbacks can fail, then yes, both APIs could be updated to return uint16_t.
But it would be more future proof to allow these callbacks to fail, even on a valid queue.
The public APIs rte_eth_rx/tx_queue_count() can fail, so passing on failures from the callbacks would not be a change of the public API. (Assuming no new error codes are allowed.)

And the "future" already arrived:
The return type must be int (or have the same size as int), for the performance patch [1] replacing the ops->callback==NULL check with dummy callbacks returning -ENOTSUP.

[1]: https://inbox.dpdk.org/dev/20250512150732.65743-2-skori@marvell.com/

> 
> > >
> > > You are right that it formally changes the ABI, and we should go
> through the LTS motions.
> > > But, for this change, I'd favor an exception.
> >
> > Again, from my opinion, there is nothing that urgent/important why
> such changes (if needed)
> > can't wait till next LTS.
> > For now, we can simply do type conversion explicitly at
> rte_eth_rx_queue_count().

OK. No objections from me. Just trying to accelerate some cleanup work.

> >
> > > PS: As a consequence of this change, a patch to update the return
> type of the callback in all the ethdev drivers should be provided.
> > >
> > > >
> > > >
> > > >   [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at
> > > > rte_ethdev.c:47:1:
> > > >     type of variable changed:
> > > >       array element type 'struct rte_eth_fp_ops' changed:
> > > >         type size hasn't changed
> > > >         1 data member change:
> > > >           type of 'eth_rx_queue_count_t rx_queue_count' changed:
> > > >             underlying type 'uint32_t (*)(void*)' changed:
> > > >               in pointed to type 'function type uint32_t
> (void*)':
> > > >                 return type changed:
> > > >                   entity changed from 'typedef uint32_t' to 'int'
> > > >                   type size hasn't changed
> > > >       type size hasn't changed

^ permalink raw reply	[relevance 0%]

* RE: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  2025-05-28  8:48  0%           ` Morten Brørup
@ 2025-05-28  9:14  0%             ` Konstantin Ananyev
  2025-05-28  9:39  0%               ` Morten Brørup
  0 siblings, 1 reply; 153+ results
From: Konstantin Ananyev @ 2025-05-28  9:14 UTC (permalink / raw)
  To: Morten Brørup, Stephen Hemminger, skori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Ian Stokes, Bruce Richardson,
	Vladimir Medvedkin, Anatoly Burakov, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad, Long Li, Wei Hu, Chaoyong He, Jiawen Wu,
	Andrew Rybchenko, Jerin Jacob, Maciej Czekaj, Jian Wang,
	Maxime Coquelin, Chenbo Xia, Jochen Behrens, Thomas Monjalon,
	Ferruh Yigit, dev



> 
> > From: Konstantin Ananyev [mailto:konstantin.ananyev@huawei.com]
> > Sent: Wednesday, 28 May 2025 10.24
> >
> > > >
> > > > > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > > > > Sent: Tuesday, 27 May 2025 17.07
> > > > >
> > > > > On Mon, 12 May 2025 20:37:19 +0530
> > > > > <skori@marvell.com> wrote:
> > > > >
> > > > > >  /**@{@name Rx hardware descriptor states
> > > > > > diff --git a/lib/ethdev/rte_ethdev_core.h
> > > > > b/lib/ethdev/rte_ethdev_core.h
> > > > > > index e55fb42996..4ffae4921a 100644
> > > > > > --- a/lib/ethdev/rte_ethdev_core.h
> > > > > > +++ b/lib/ethdev/rte_ethdev_core.h
> > > > > > @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void *txq,
> > > > > >
> > > > > >
> > > > > >  /** @internal Get number of used descriptors on a receive
> > queue. */
> > > > > > -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > > > > > +typedef int (*eth_rx_queue_count_t)(void *rxq);
> > > > > >
> > > > > >  /** @internal Check the status of a Rx descriptor */
> > > > > >  typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t
> > > > > offset);
> > > > >
> > > > >
> > > > > This gets reported as ABI breakage. The change will have to wait
> > until
> > > > > next LTS (25.11)
> > > >
> > > > The return type was weird (wrong) to begin with.
> > > > When used, it gets cast to int:
> > > >
> > https://elixir.bootlin.com/dpdk/v25.03/source/lib/ethdev/rte_ethdev.h#L
> > 6404
> > >
> > > Personally, I don't see anything strange here.
> > > devops rx_queue_count() supposed to return uint, because it should
> > never failed for
> > > valid queue.
> 
> The main thing wrong is inconsistency with its sibling API for TX queue count:
> /** @internal Get number of used descriptors on a receive queue. */
> typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> /** @internal Get number of used descriptors on a transmit queue. */
> typedef int (*eth_tx_queue_count_t)(void *txq);
> 
> > > While rte_eth_rx_queue_count() itself can fail - wrong port/queue id,
> > etc.
> >
> > BTW, rx_queue_setup() accepts only uint16_t for number of rx
> > descritoirs:
> > int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
> >                 uint16_t nb_rx_desc, unsigned int socket_id,
> >                 const struct rte_eth_rxconf *rx_conf,
> >                 struct rte_mempool *mb_pool);
> >
> > shouldn't dev->rx_queue_count() then also return uitn16_t (for
> > consistency)?
> 
> If neither the RX or TX queue count callbacks can fail, then yes, both APIs could be updated to return uint16_t.
> But it would be more future proof to allow these callbacks to fail, even on a valid queue.
> The public APIs rte_eth_rx/tx_queue_count() can fail, so passing on failures from the callbacks would not be a change of the public
> API. (Assuming no new error codes are allowed.)
> 
> And the "future" already arrived:
> The return type must be int (or have the same size as int), for the performance patch [1] replacing the ops->callback==NULL check
> with dummy callbacks returning -ENOTSUP.
> 
> [1]: https://inbox.dpdk.org/dev/20250512150732.65743-2-skori@marvell.com/

So what we are saving with that patch: one cmp and one un-taken branch:
@@ -6399,8 +6399,6 @@ rte_eth_rx_queue_count(uint16_t port_id, uint16_t queue_id)
 		return -EINVAL;
 #endif
 
-	if (p->rx_queue_count == NULL)
-		return -ENOTSUP;
 	return p->rx_queue_count(qd);
 }

I wonder is how realistic (and measurable) is the gain?

>   
> >
> > > >
> > > > You are right that it formally changes the ABI, and we should go
> > through the LTS motions.
> > > > But, for this change, I'd favor an exception.
> > >
> > > Again, from my opinion, there is nothing that urgent/important why
> > such changes (if needed)
> > > can't wait till next LTS.
> > > For now, we can simply do type conversion explicitly at
> > rte_eth_rx_queue_count().
> 
> OK. No objections from me. Just trying to accelerate some cleanup work.
> 
> > >
> > > > PS: As a consequence of this change, a patch to update the return
> > type of the callback in all the ethdev drivers should be provided.
> > > >
> > > > >
> > > > >
> > > > >   [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at
> > > > > rte_ethdev.c:47:1:
> > > > >     type of variable changed:
> > > > >       array element type 'struct rte_eth_fp_ops' changed:
> > > > >         type size hasn't changed
> > > > >         1 data member change:
> > > > >           type of 'eth_rx_queue_count_t rx_queue_count' changed:
> > > > >             underlying type 'uint32_t (*)(void*)' changed:
> > > > >               in pointed to type 'function type uint32_t
> > (void*)':
> > > > >                 return type changed:
> > > > >                   entity changed from 'typedef uint32_t' to 'int'
> > > > >                   type size hasn't changed
> > > > >       type size hasn't changed


^ permalink raw reply	[relevance 0%]

* RE: [PATCH v2 1/2] ethdev: remove unnecessary type conversion
  2025-05-28  9:14  0%             ` Konstantin Ananyev
@ 2025-05-28  9:39  0%               ` Morten Brørup
  0 siblings, 0 replies; 153+ results
From: Morten Brørup @ 2025-05-28  9:39 UTC (permalink / raw)
  To: Konstantin Ananyev, Stephen Hemminger, Sunil Kumar Kori
  Cc: Shepard Siegel, Ed Czeck, John Miller, Igor Russkikh,
	Ajit Khaparde, Somnath Kotur, Nithin Dabilpuram, Kiran Kumar K,
	Satha Rao, Harman Kalra, Hemant Agrawal, Sachin Saxena,
	John Daley, Hyong Youb Kim, Ian Stokes, Bruce Richardson,
	Vladimir Medvedkin, Anatoly Burakov, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad, Long Li, Wei Hu, Chaoyong He, Jiawen Wu,
	Andrew Rybchenko, Jerin Jacob, Maciej Czekaj, Jian Wang,
	Maxime Coquelin, Chenbo Xia, Jochen Behrens, Thomas Monjalon,
	Ferruh Yigit, dev

> From: Konstantin Ananyev [mailto:konstantin.ananyev@huawei.com]
> Sent: Wednesday, 28 May 2025 11.14
> 
> >
> > > From: Konstantin Ananyev [mailto:konstantin.ananyev@huawei.com]
> > > Sent: Wednesday, 28 May 2025 10.24
> > >
> > > > >
> > > > > > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > > > > > Sent: Tuesday, 27 May 2025 17.07
> > > > > >
> > > > > > On Mon, 12 May 2025 20:37:19 +0530
> > > > > > <skori@marvell.com> wrote:
> > > > > >
> > > > > > >  /**@{@name Rx hardware descriptor states
> > > > > > > diff --git a/lib/ethdev/rte_ethdev_core.h
> > > > > > b/lib/ethdev/rte_ethdev_core.h
> > > > > > > index e55fb42996..4ffae4921a 100644
> > > > > > > --- a/lib/ethdev/rte_ethdev_core.h
> > > > > > > +++ b/lib/ethdev/rte_ethdev_core.h
> > > > > > > @@ -45,7 +45,7 @@ typedef uint16_t (*eth_tx_prep_t)(void
> *txq,
> > > > > > >
> > > > > > >
> > > > > > >  /** @internal Get number of used descriptors on a receive
> > > queue. */
> > > > > > > -typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > > > > > > +typedef int (*eth_rx_queue_count_t)(void *rxq);
> > > > > > >
> > > > > > >  /** @internal Check the status of a Rx descriptor */
> > > > > > >  typedef int (*eth_rx_descriptor_status_t)(void *rxq,
> uint16_t
> > > > > > offset);
> > > > > >
> > > > > >
> > > > > > This gets reported as ABI breakage. The change will have to
> wait
> > > until
> > > > > > next LTS (25.11)
> > > > >
> > > > > The return type was weird (wrong) to begin with.
> > > > > When used, it gets cast to int:
> > > > >
> > >
> https://elixir.bootlin.com/dpdk/v25.03/source/lib/ethdev/rte_ethdev.h#L
> > > 6404
> > > >
> > > > Personally, I don't see anything strange here.
> > > > devops rx_queue_count() supposed to return uint, because it
> should
> > > never failed for
> > > > valid queue.
> >
> > The main thing wrong is inconsistency with its sibling API for TX
> queue count:
> > /** @internal Get number of used descriptors on a receive queue. */
> > typedef uint32_t (*eth_rx_queue_count_t)(void *rxq);
> > /** @internal Get number of used descriptors on a transmit queue. */
> > typedef int (*eth_tx_queue_count_t)(void *txq);
> >
> > > > While rte_eth_rx_queue_count() itself can fail - wrong port/queue
> id,
> > > etc.
> > >
> > > BTW, rx_queue_setup() accepts only uint16_t for number of rx
> > > descritoirs:
> > > int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
> > >                 uint16_t nb_rx_desc, unsigned int socket_id,
> > >                 const struct rte_eth_rxconf *rx_conf,
> > >                 struct rte_mempool *mb_pool);
> > >
> > > shouldn't dev->rx_queue_count() then also return uitn16_t (for
> > > consistency)?
> >
> > If neither the RX or TX queue count callbacks can fail, then yes,
> both APIs could be updated to return uint16_t.
> > But it would be more future proof to allow these callbacks to fail,
> even on a valid queue.
> > The public APIs rte_eth_rx/tx_queue_count() can fail, so passing on
> failures from the callbacks would not be a change of the public
> > API. (Assuming no new error codes are allowed.)
> >
> > And the "future" already arrived:
> > The return type must be int (or have the same size as int), for the
> performance patch [1] replacing the ops->callback==NULL check
> > with dummy callbacks returning -ENOTSUP.
> >
> > [1]: https://inbox.dpdk.org/dev/20250512150732.65743-2-
> skori@marvell.com/
> 
> So what we are saving with that patch: one cmp and one un-taken branch:
> @@ -6399,8 +6399,6 @@ rte_eth_rx_queue_count(uint16_t port_id, uint16_t
> queue_id)
>  		return -EINVAL;
>  #endif
> 
> -	if (p->rx_queue_count == NULL)
> -		return -ENOTSUP;
>  	return p->rx_queue_count(qd);
>  }
> 
> I wonder is how realistic (and measurable) is the gain?

Moving the performance discussion to the other thread [2].
[2]: https://inbox.dpdk.org/dev/98CBD80474FA8B44BF855DF32C47DC35E9FC9E@smartserver.smartshare.dk/

> 
> >
> > >
> > > > >
> > > > > You are right that it formally changes the ABI, and we should
> go
> > > through the LTS motions.
> > > > > But, for this change, I'd favor an exception.
> > > >
> > > > Again, from my opinion, there is nothing that urgent/important
> why
> > > such changes (if needed)
> > > > can't wait till next LTS.
> > > > For now, we can simply do type conversion explicitly at
> > > rte_eth_rx_queue_count().
> >
> > OK. No objections from me. Just trying to accelerate some cleanup
> work.
> >
> > > >
> > > > > PS: As a consequence of this change, a patch to update the
> return
> > > type of the callback in all the ethdev drivers should be provided.
> > > > >
> > > > > >
> > > > > >
> > > > > >   [C] 'rte_eth_fp_ops rte_eth_fp_ops[32]' was changed at
> > > > > > rte_ethdev.c:47:1:
> > > > > >     type of variable changed:
> > > > > >       array element type 'struct rte_eth_fp_ops' changed:
> > > > > >         type size hasn't changed
> > > > > >         1 data member change:
> > > > > >           type of 'eth_rx_queue_count_t rx_queue_count'
> changed:
> > > > > >             underlying type 'uint32_t (*)(void*)' changed:
> > > > > >               in pointed to type 'function type uint32_t
> > > (void*)':
> > > > > >                 return type changed:
> > > > > >                   entity changed from 'typedef uint32_t' to
> 'int'
> > > > > >                   type size hasn't changed
> > > > > >       type size hasn't changed


^ permalink raw reply	[relevance 0%]

* Re: [PATCH 1/3] eventdev: introduce event vector adapter
  2025-04-10 18:00  1%     ` [PATCH 1/3] eventdev: " pbhagavatula
@ 2025-05-28 16:58  0%       ` Jerin Jacob
  0 siblings, 0 replies; 153+ results
From: Jerin Jacob @ 2025-05-28 16:58 UTC (permalink / raw)
  To: pbhagavatula
  Cc: jerinj, pravin.pathak, hemant.agrawal, sachin.saxena,
	mattias.ronnblom, liangma, peter.mccarthy, harry.van.haaren,
	erik.g.carrillo, abhinandan.gujjar, amitprakashs,
	s.v.naga.harish.k, anatoly.burakov, Bruce Richardson, dev

On Fri, Apr 11, 2025 at 12:10 AM <pbhagavatula@marvell.com> wrote:
>
> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>
> The event vector adapter supports offloading creation of
> event vectors by vectorizing objects (mbufs/ptrs/u64s).
> Applications can create a vector adapter associated with
> an event queue and enqueue objects to be vectorized.
> When the vector reaches the configured size or when the timeout
> is reached, the vector adapter will enqueue the vector to the
> event queue.
>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> ---
>  config/rte_config.h                           |   1 +
>  doc/api/doxy-api-index.md                     |   1 +
>  doc/guides/eventdevs/features/default.ini     |   7 +
>  .../eventdev/event_vector_adapter.rst         | 208 ++++++++
>  doc/guides/prog_guide/eventdev/eventdev.rst   |  10 +-
>  doc/guides/prog_guide/eventdev/index.rst      |   1 +
>  doc/guides/rel_notes/release_25_07.rst        |   6 +
>  lib/eventdev/event_vector_adapter_pmd.h       |  85 ++++
>  lib/eventdev/eventdev_pmd.h                   |  36 ++
>  lib/eventdev/meson.build                      |   3 +
>  lib/eventdev/rte_event_vector_adapter.c       | 472 +++++++++++++++++
>  lib/eventdev/rte_event_vector_adapter.h       | 481 ++++++++++++++++++


Update MAINTAINER file for new file additions


>  lib/eventdev/rte_eventdev.c                   |  22 +
>  lib/eventdev/rte_eventdev.h                   |  10 +
>  14 files changed, 1338 insertions(+), 5 deletions(-)
>  create mode 100644 doc/guides/prog_guide/eventdev/event_vector_adapter.rst
>  create mode 100644 lib/eventdev/event_vector_adapter_pmd.h
>  create mode 100644 lib/eventdev/rte_event_vector_adapter.c
>  create mode 100644 lib/eventdev/rte_event_vector_adapter.h
>
> diff --git a/config/rte_config.h b/config/rte_config.h
> index 86897de75e..9535c48d81 100644
> --- a/config/rte_config.h
> +++ b/config/rte_config.h
> @@ -92,6 +92,7 @@
>  #define RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE 32
>  #define RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE 32
>  #define RTE_EVENT_DMA_ADAPTER_MAX_INSTANCE 32
> +#define RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE 32
>
>  /* rawdev defines */
>  #define RTE_RAWDEV_MAX_DEVS 64
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index 5c425a2cb9..a11bd59526 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -30,6 +30,7 @@ The public API headers are grouped by topics:
>    [event_timer_adapter](@ref rte_event_timer_adapter.h),
>    [event_crypto_adapter](@ref rte_event_crypto_adapter.h),
>    [event_dma_adapter](@ref rte_event_dma_adapter.h),
> +  [event_vector_adapter](@ref rte_event_vector_adapter.h),
>    [rawdev](@ref rte_rawdev.h),
>    [metrics](@ref rte_metrics.h),
>    [bitrate](@ref rte_bitrate.h),
> diff --git a/doc/guides/eventdevs/features/default.ini b/doc/guides/eventdevs/features/default.ini
> index fa24ba38b4..9fb68f946e 100644
> --- a/doc/guides/eventdevs/features/default.ini
> +++ b/doc/guides/eventdevs/features/default.ini
> @@ -64,3 +64,10 @@ internal_port_vchan_ev_bind =
>  [Timer adapter Features]
>  internal_port              =
>  periodic                   =
> +
> +;
> +; Features of a default Vector adapter
> +;
> +[Vector adapter Features]
> +internal_port              =
> +sov_eov                    =
> diff --git a/doc/guides/prog_guide/eventdev/event_vector_adapter.rst b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
> new file mode 100644
> index 0000000000..e257552d22
> --- /dev/null
> +++ b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
> @@ -0,0 +1,208 @@
> +..  SPDX-License-Identifier: BSD-3-Clause
> +    Copyright(c) 2025 Marvell International Ltd.
> +
> +Event Vector Adapter Library
> +============================
> +
> +The Event Vector Adapter library extends the event-driven model by introducing
> +a mechanism to aggregate multiple 8B objects (e.g., mbufs, u64s) into a single

Add link to 8B object structure.

Also, tell the use case for this i.e when and why to use this

> +vector event and enqueue it to an event queue. It provides an API to create,
> +configure, and manage vector adapters.
> +
> +The Event Vector Adapter library is designed to interface with hardware or
> +software implementations of vector aggregation. It queries an eventdev PMD
> +to determine the appropriate implementation.
> +
> +Examples of using the API are presented in the `API Overview`_ and
> +`Processing Vector Events`_ sections.
> +
> +.. _vector_event:
> +
> +Vector Event
> +~~~~~~~~~~~~
> +
> +A vector event is enqueued in the event device when the vector adapter
> +reaches the configured vector size or timeout. The event device uses the
> +attributes configured by the application when scheduling it.
> +
> +Fallback Behavior
> +~~~~~~~~~~~~~~~~~
> +
> +If the vector adapter cannot aggregate objects into a vector event, it
> +enqueues the objects as single events with fallback event properties configured
> +by the application.
> +
> +Timeout and Size
> +~~~~~~~~~~~~~~~~
> +
> +The vector adapter aggregates objects until the configured vector size or
> +timeout is reached. If the timeout is reached before the minimum vector size
> +is met, the adapter enqueues the objects as single events with fallback event
> +properties configured by the application.
> +
> +API Overview
> +------------
> +
> +This section introduces the Event Vector Adapter API, showing how to create
> +and configure a vector adapter and use it to manage vector events.
> +
> +From a high level, the setup steps are:
> +
> +* rte_event_vector_adapter_create()
> +
> +And to enqueue and manage vectors:
> +
> +* rte_event_vector_adapter_enqueue()
> +* rte_event_vector_adapter_stats_get()
> +
> +Create and Configure a Vector Adapter
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +To create a vector adapter instance, initialize an ``rte_event_vector_adapter_conf``
> +struct with the desired values, and pass it to ``rte_event_vector_adapter_create()``.
> +
> +.. code-block:: c
> +
> +       const struct rte_event_vector_adapter_conf adapter_config = {
> +               .event_dev_id = event_dev_id,
> +               .socket_id = rte_socket_id(),
> +               .ev = {
> +                       .queue_id = event_queue_id,
> +                       .sched_type = RTE_SCHED_TYPE_ATOMIC,
> +                       .priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
> +                       .event_type = RTE_EVENT_TYPE_VECTOR | RTE_EVENT_TYPE_CPU,
> +               },
> +               .ev_fallback = {
> +                       .event_type = RTE_EVENT_TYPE_CPU,
> +               },
> +               .vector_sz = 64,
> +               .vector_timeout_ns = 1000000, // 1ms
> +               .vector_mp = vector_mempool,
> +       };
> +
> +       struct rte_event_vector_adapter *adapter;
> +       adapter = rte_event_vector_adapter_create(&adapter_config);
> +
> +       if (adapter == NULL) { ... }
> +
> +Before creating an instance of a vector adapter, the application should create
> +and configure an event device along with its event ports. Based on the event
> +device's capability, it might require creating an additional event port to be
> +used by the vector adapter. If required, the ``rte_event_vector_adapter_create()``
> +function will use a default method to configure an event port.
> +
> +If the application desires finer control of event port allocation and setup,
> +it can use the ``rte_event_vector_adapter_create_ext()`` function. This function
> +is passed a callback function that will be invoked if the adapter needs to
> +create an event port, giving the application the opportunity to control how
> +it is done.
> +
> +Retrieve Vector Adapter Contextual Information
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The vector adapter implementation may have constraints on vector size or
> +timeout based on the given event device or system. The application can retrieve
> +these constraints using ``rte_event_vector_adapter_info_get()``. This function
> +returns an ``rte_event_vector_adapter_info`` struct, which contains the following
> +members:
> +
> +* ``max_vector_adapters_per_event_queue`` - Maximum number of vector adapters
> +  configurable per event queue.
> +* ``min_vector_sz`` - Minimum vector size configurable.
> +* ``max_vector_sz`` - Maximum vector size configurable.
> +* ``min_vector_timeout_ns`` - Minimum vector timeout configurable.
> +* ``max_vector_timeout_ns`` - Maximum vector timeout configurable.
> +* ``log2_sz`` - Vector size should be a power of 2.
> +
> +Enqueuing Objects to the Vector Adapter
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Once a vector adapter has been created, the application can enqueue objects
> +to it using ``rte_event_vector_adapter_enqueue()``. The adapter will aggregate
> +the objects into a vector event based on the configured size and timeout.
> +
> +.. code-block:: c
> +
> +       uint64_t objs[32];
> +       uint16_t num_elem = 32;
> +       uint64_t flags = 0;
> +
> +       int ret = rte_event_vector_adapter_enqueue(adapter, objs, num_elem, flags);
> +       if (ret < 0) { ... }
> +
> +The application can use the ``RTE_EVENT_VECTOR_ENQ_SOV`` and ``RTE_EVENT_VECTOR_ENQ_EOV``
> +flags to control the start and end of vector aggregation.

Reference the relevant capability flag here. Also, if the capability
is not available mention this as NOP


> +
> +The ``RTE_EVENT_VECTOR_ENQ_SOV`` flag marks the beginning of a vector and applies
> +to the first pointer in the enqueue operation. Any incomplete vectors will be
> +enqueued to the event device.
> +
> +The ``RTE_EVENT_VECTOR_ENQ_EOV`` flag marks the end of a vector and applies to
> +the last pointer in the enqueue operation. The vector is enqueued to the event
> +device even if the configured vector size is not reached.
> +
> +If both flags are set, the adapter will form a new vector event with the given
> +objects and enqueue it to the event device.
> +
> +The ``RTE_EVENT_VECTOR_ENQ_FLUSH`` flag can be used to flush any remaining
> +objects in the vector adapter. This is useful when the application needs to
> +ensure that all objects are processed, even if the configured vector size or
> +timeout is not reached. An enqueue call with this flag set will not handle any
> +objects and will return 0.
> +
> +Processing Vector Events
> +------------------------
> +
> +Once a vector event has been enqueued in the event device, the application will
> +subsequently dequeue it from the event device. The application can process the
> +vector event and its aggregated objects as needed:
> +
> +.. code-block:: c
> +
> +       void
> +       event_processing_loop(...)
> +       {
> +               while (...) {
> +                       /* Receive events from the configured event port. */
> +                       rte_event_dequeue_burst(event_dev_id, event_port, &ev, 1, 0);
> +                       ...
> +                       switch(ev.event_type) {
> +                               ...
> +                               case RTE_EVENT_TYPE_VECTOR:
> +                                       process_vector_event(ev);
> +                                       ...
> +                                       break;
> +                       }
> +               }
> +       }
> +
> +       void
> +       process_vector_event(struct rte_event ev)
> +       {
> +               struct rte_event_vector *vector = ev.event_ptr;
> +               for (uint16_t i = 0; i < vector->nb_elem; i++) {
> +                       uint64_t obj = vector->u64s[i];
> +                       /* Process each object in the vector. */
> +                       ...
> +               }
> +       }
> +
> +Statistics and Cleanup
> +----------------------
> +
> +The application can retrieve statistics for the vector adapter using
> +``rte_event_vector_adapter_stats_get()``:
> +
> +.. code-block:: c
> +
> +       struct rte_event_vector_adapter_stats stats;
> +       rte_event_vector_adapter_stats_get(adapter, &stats);
> +
> +       printf("Vectors created: %" PRIu64 "\n", stats.vectorized);
> +       printf("Timeouts occurred: %" PRIu64 "\n", stats.vectors_timedout);
> +
> +To reset the statistics, use ``rte_event_vector_adapter_stats_reset()``.
> +
> +To destroy the vector adapter and release its resources, use
> +``rte_event_vector_adapter_destroy()``. The destroy function will
> +flush any remaining events in the vector adapter before destroying it.
> diff --git a/doc/guides/prog_guide/eventdev/eventdev.rst b/doc/guides/prog_guide/eventdev/eventdev.rst
> index 8bb72da908..5e49db8983 100644
> --- a/doc/guides/prog_guide/eventdev/eventdev.rst
> +++ b/doc/guides/prog_guide/eventdev/eventdev.rst
> @@ -424,8 +424,8 @@ eventdev.
>  .. Note::
>
>           EventDev needs to be started before starting the event producers such
> -         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter and
> -         event_dma_adapter.
> +         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter,
> +         event_dma_adapter and event_vector_adapter.
>
>  Ingress of New Events
>  ~~~~~~~~~~~~~~~~~~~~~
> @@ -561,9 +561,9 @@ using ``rte_event_dev_stop_flush_callback_register()`` function.
>  .. Note::
>
>          The event producers such as ``event_eth_rx_adapter``,
> -        ``event_timer_adapter``, ``event_crypto_adapter`` and
> -        ``event_dma_adapter`` need to be stopped before stopping
> -        the event device.
> +        ``event_timer_adapter``, ``event_crypto_adapter``,
> +        ``event_dma_adapter`` and ``event_vector_adapter``
> +        need to be stopped before stopping the event device.
>
>  Summary
>  -------
> diff --git a/doc/guides/prog_guide/eventdev/index.rst b/doc/guides/prog_guide/eventdev/index.rst
> index 2e1940ce76..af11a57e71 100644
> --- a/doc/guides/prog_guide/eventdev/index.rst
> +++ b/doc/guides/prog_guide/eventdev/index.rst
> @@ -14,3 +14,4 @@ Event Device Library
>      event_crypto_adapter
>      event_dma_adapter
>      dispatcher_lib
> +    event_vector_adapter
> diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
> index 093b85d206..e6e84eeec6 100644
> --- a/doc/guides/rel_notes/release_25_07.rst
> +++ b/doc/guides/rel_notes/release_25_07.rst
> @@ -55,6 +55,12 @@ New Features
>       Also, make sure to start the actual text at the margin.
>       =======================================================
>
> +* **Added eventdev vector adapter.**
> +
> +  * Added the Event vector Adapter Library. This library extends the event-based
> +    model by introducing APIs that allow applications to offload creation of
> +    event vectors.


Change the description in such a way the end user understands when to
and why to use this library.


> +
>
>  Removed Items
>  -------------
> diff --git a/lib/eventdev/event_vector_adapter_pmd.h b/lib/eventdev/event_vector_adapter_pmd.h
> new file mode 100644
> index 0000000000..667363c496
> --- /dev/null
> +++ b/lib/eventdev/event_vector_adapter_pmd.h
> @@ -0,0 +1,85 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Marvell International Ltd.
> + * All rights reserved.
> + */
> +#ifndef __EVENT_VECTOR_ADAPTER_PMD_H__
> +#define __EVENT_VECTOR_ADAPTER_PMD_H__
> +/**
> + * @file
> + * RTE Event Vector Adapter API (PMD Side)
> + *
> + * @note
> + * This file provides implementation helpers for internal use by PMDs.  They
> + * are not intended to be exposed to applications and are not subject to ABI
> + * versioning.
> + */
> +#include "eventdev_pmd.h"
> +#include "rte_event_vector_adapter.h"
> +
> +typedef int (*rte_event_vector_adapter_create_t)(struct rte_event_vector_adapter *adapter);
> +/**< @internal Event vector adapter implementation setup */
> +typedef int (*rte_event_vector_adapter_destroy_t)(struct rte_event_vector_adapter *adapter);
> +/**< @internal Event vector adapter implementation teardown */
> +typedef int (*rte_event_vector_adapter_stats_get_t)(const struct rte_event_vector_adapter *adapter,
> +                                                   struct rte_event_vector_adapter_stats *stats);
> +/**< @internal Get statistics for event vector adapter */
> +typedef int (*rte_event_vector_adapter_stats_reset_t)(
> +       const struct rte_event_vector_adapter *adapter);
> +/**< @internal Reset statistics for event vector adapter */
> +
> +/**
> + * @internal Structure containing the functions exported by an event vector
> + * adapter implementation.
> + */
> +struct event_vector_adapter_ops {
> +       rte_event_vector_adapter_create_t create;
> +       /**< Set up adapter */
> +       rte_event_vector_adapter_destroy_t destroy;
> +       /**< Tear down adapter */
> +       rte_event_vector_adapter_stats_get_t stats_get;
> +       /**< Get adapter statistics */
> +       rte_event_vector_adapter_stats_reset_t stats_reset;
> +       /**< Reset adapter statistics */
> +
> +       rte_event_vector_adapter_enqueue_t enqueue;
> +       /**< Enqueue objects into the event vector adapter */
> +};
> +/**
> + * @internal Adapter data; structure to be placed in shared memory to be
> + * accessible by various processes in a multi-process configuration.
> + */
> +struct __rte_cache_aligned rte_event_vector_adapter_data {
> +       uint32_t id;
> +       /**< Event vector adapter ID */
> +       uint8_t event_dev_id;
> +       /**< Event device ID */
> +       uint32_t socket_id;
> +       /**< Socket ID where memory is allocated */
> +       uint8_t event_port_id;
> +       /**< Optional: event port ID used when the inbuilt port is absent */
> +       const struct rte_memzone *mz;
> +       /**< Event vector adapter memzone pointer */
> +       struct rte_event_vector_adapter_conf conf;
> +       /**< Configuration used to configure the adapter. */
> +       uint32_t caps;
> +       /**< Adapter capabilities */
> +       void *adapter_priv;
> +       /**< Vector adapter private data*/
> +       uint8_t service_inited;
> +       /**< Service initialization state */
> +       uint32_t unified_service_id;
> +       /**< Unified Service ID*/
> +};
> +
> +static inline int
> +dummy_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
> +                            uint16_t num_events, uint64_t flags)
> +{
> +       RTE_SET_USED(adapter);
> +       RTE_SET_USED(objs);
> +       RTE_SET_USED(num_events);
> +       RTE_SET_USED(flags);
> +       return 0;
> +}
> +
> +#endif /* __EVENT_VECTOR_ADAPTER_PMD_H__ */
> diff --git a/lib/eventdev/eventdev_pmd.h b/lib/eventdev/eventdev_pmd.h
> index ad13ba5b03..d03461316b 100644
> --- a/lib/eventdev/eventdev_pmd.h
> +++ b/lib/eventdev/eventdev_pmd.h
> @@ -26,6 +26,7 @@
>
>  #include "event_timer_adapter_pmd.h"
>  #include "rte_event_eth_rx_adapter.h"
> +#include "rte_event_vector_adapter.h"
>  #include "rte_eventdev.h"
>
>  #ifdef __cplusplus
> @@ -1555,6 +1556,36 @@ typedef int (*eventdev_dma_adapter_stats_get)(const struct rte_eventdev *dev,
>  typedef int (*eventdev_dma_adapter_stats_reset)(const struct rte_eventdev *dev,
>                                                 const int16_t dma_dev_id);
>
> +/**
> + * Event device vector adapter capabilities.
> + *
> + * @param dev
> + *   Event device pointer
> + * @param caps
> + *   Vector adapter capabilities
> + * @param ops
> + *   Vector adapter ops
> + *
> + * @return
> + *   Return 0 on success.
> + *
> + */
> +typedef int (*eventdev_vector_adapter_caps_get_t)(const struct rte_eventdev *dev, uint32_t *caps,
> +                                                 const struct event_vector_adapter_ops **ops);
> +
> +/**
> + * Event device vector adapter info.
> + *
> + * @param dev
> + *   Event device pointer
> + * @param info
> + *   Vector adapter info
> + *
> + * @return
> + *   Return 0 on success.
> + */
> +typedef int (*eventdev_vector_adapter_info_get_t)(const struct rte_eventdev *dev,
> +                                                 struct rte_event_vector_adapter_info *info);
>
>  /** Event device operations function pointer table */
>  struct eventdev_ops {
> @@ -1697,6 +1728,11 @@ struct eventdev_ops {
>         eventdev_dma_adapter_stats_reset dma_adapter_stats_reset;
>         /**< Reset DMA stats */
>
> +       eventdev_vector_adapter_caps_get_t vector_adapter_caps_get;
> +       /**< Get vector adapter capabilities */
> +       eventdev_vector_adapter_info_get_t vector_adapter_info_get;
> +       /**< Get vector adapter info */
> +
>         eventdev_selftest dev_selftest;
>         /**< Start eventdev Selftest */
>
> diff --git a/lib/eventdev/meson.build b/lib/eventdev/meson.build
> index 71dea91727..0797c145e7 100644
> --- a/lib/eventdev/meson.build
> +++ b/lib/eventdev/meson.build
> @@ -18,6 +18,7 @@ sources = files(
>          'rte_event_eth_tx_adapter.c',
>          'rte_event_ring.c',
>          'rte_event_timer_adapter.c',
> +        'rte_event_vector_adapter.c',
>          'rte_eventdev.c',
>  )
>  headers = files(
> @@ -27,6 +28,7 @@ headers = files(
>          'rte_event_eth_tx_adapter.h',
>          'rte_event_ring.h',
>          'rte_event_timer_adapter.h',
> +        'rte_event_vector_adapter.h',
>          'rte_eventdev.h',
>          'rte_eventdev_trace_fp.h',
>  )
> @@ -38,6 +40,7 @@ driver_sdk_headers += files(
>          'eventdev_pmd_pci.h',
>          'eventdev_pmd_vdev.h',
>          'event_timer_adapter_pmd.h',
> +        'event_vector_adapter_pmd.h',
>  )
>
>  deps += ['ring', 'ethdev', 'hash', 'mempool', 'mbuf', 'timer', 'cryptodev', 'dmadev']
> diff --git a/lib/eventdev/rte_event_vector_adapter.c b/lib/eventdev/rte_event_vector_adapter.c
> new file mode 100644
> index 0000000000..ff6bc43b17
> --- /dev/null
> +++ b/lib/eventdev/rte_event_vector_adapter.c
> @@ -0,0 +1,472 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Marvell International Ltd.
> + * All rights reserved.
> + */
> +
> +#include <rte_errno.h>
> +#include <rte_malloc.h>
> +#include <rte_mcslock.h>
> +#include <rte_service_component.h>
> +#include <rte_tailq.h>
> +
> +#include <eal_export.h>
> +
> +#include "event_vector_adapter_pmd.h"
> +#include "eventdev_pmd.h"
> +#include "rte_event_vector_adapter.h"
> +
> +#define ADAPTER_ID(dev_id, queue_id, adapter_id)                                                   \
> +       ((uint32_t)dev_id << 16 | (uint32_t)queue_id << 8 | (uint32_t)adapter_id)
> +#define DEV_ID_FROM_ADAPTER_ID(adapter_id)     ((adapter_id >> 16) & 0xFF)
> +#define QUEUE_ID_FROM_ADAPTER_ID(adapter_id)   ((adapter_id >> 8) & 0xFF)
> +#define ADAPTER_ID_FROM_ADAPTER_ID(adapter_id) (adapter_id & 0xFF)
> +
> +#define MZ_NAME_MAX_LEN            64
> +#define DATA_MZ_NAME_FORMAT "vector_adapter_data_%d_%d_%d"
> +
> +RTE_LOG_REGISTER_SUFFIX(ev_vector_logtype, adapter.vector, NOTICE);
> +#define RTE_LOGTYPE_EVVEC ev_vector_logtype
> +
> +struct rte_event_vector_adapter *adapters[RTE_EVENT_MAX_DEVS][RTE_EVENT_MAX_QUEUES_PER_DEV];
> +
> +#define EVVEC_LOG(level, logtype, ...)                                                             \
> +       RTE_LOG_LINE_PREFIX(level, logtype,                                                        \
> +                           "EVVEC: %s() line %u: ", __func__ RTE_LOG_COMMA __LINE__, __VA_ARGS__)
> +#define EVVEC_LOG_ERR(...) EVVEC_LOG(ERR, EVVEC, __VA_ARGS__)
> +
> +#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
> +#define EVVEC_LOG_DBG(...) EVVEC_LOG(DEBUG, EVVEC, __VA_ARGS__)
> +#else
> +#define EVVEC_LOG_DBG(...) /* No debug logging */
> +#endif
> +
> +#define PTR_VALID_OR_ERR_RET(ptr, retval)                                                          \
> +       do {                                                                                       \
> +               if (ptr == NULL) {                                                                 \
> +                       rte_errno = EINVAL;                                                        \
> +                       return retval;                                                             \
> +               }                                                                                  \
> +       } while (0)
> +
> +static int
> +validate_conf(const struct rte_event_vector_adapter_conf *conf,
> +             struct rte_event_vector_adapter_info *info)
> +{
> +       int rc = -EINVAL;
> +
> +       RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, rc);
> +
> +       if (conf->vector_sz < info->min_vector_sz || conf->vector_sz > info->max_vector_sz) {
> +               EVVEC_LOG_DBG("invalid vector size %u, should be between %u and %u",
> +                             conf->vector_sz, info->min_vector_sz, info->max_vector_sz);
> +               return rc;
> +       }
> +
> +       if (conf->vector_timeout_ns < info->min_vector_timeout_ns ||
> +           conf->vector_timeout_ns > info->max_vector_timeout_ns) {
> +               EVVEC_LOG_DBG("invalid vector timeout %" PRIu64 ", should be between %" PRIu64
> +                             " and %" PRIu64,
> +                             conf->vector_timeout_ns, info->min_vector_timeout_ns,
> +                             info->max_vector_timeout_ns);
> +               return rc;
> +       }
> +
> +       if (conf->vector_mp == NULL) {
> +               EVVEC_LOG_DBG("invalid mempool for vector adapter");
> +               return rc;
> +       }
> +
> +       if (info->log2_sz && rte_is_power_of_2(conf->vector_sz) != 0) {
> +               EVVEC_LOG_DBG("invalid vector size %u, should be a power of 2", conf->vector_sz);
> +               return rc;
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +default_port_conf_cb(uint8_t event_dev_id, uint8_t *event_port_id, void *conf_arg)
> +{
> +       struct rte_event_port_conf *port_conf, def_port_conf = {0};
> +       struct rte_event_dev_config dev_conf;
> +       struct rte_eventdev *dev;
> +       uint8_t port_id;
> +       uint8_t dev_id;
> +       int started;
> +       int ret;
> +
> +       dev = &rte_eventdevs[event_dev_id];
> +       dev_id = dev->data->dev_id;
> +       dev_conf = dev->data->dev_conf;
> +
> +       started = dev->data->dev_started;
> +       if (started)
> +               rte_event_dev_stop(dev_id);
> +
> +       port_id = dev_conf.nb_event_ports;
> +       if (conf_arg != NULL)
> +               port_conf = conf_arg;
> +       else {
> +               port_conf = &def_port_conf;
> +               ret = rte_event_port_default_conf_get(dev_id, (port_id - 1), port_conf);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       dev_conf.nb_event_ports += 1;
> +       if (port_conf->event_port_cfg & RTE_EVENT_PORT_CFG_SINGLE_LINK)
> +               dev_conf.nb_single_link_event_port_queues += 1;
> +
> +       ret = rte_event_dev_configure(dev_id, &dev_conf);
> +       if (ret < 0) {
> +               EVVEC_LOG_ERR("failed to configure event dev %u", dev_id);
> +               if (started)
> +                       if (rte_event_dev_start(dev_id))
> +                               return -EIO;
> +
> +               return ret;
> +       }
> +
> +       ret = rte_event_port_setup(dev_id, port_id, port_conf);
> +       if (ret < 0) {
> +               EVVEC_LOG_ERR("failed to setup event port %u on event dev %u", port_id, dev_id);
> +               return ret;
> +       }
> +
> +       *event_port_id = port_id;
> +
> +       if (started)
> +               ret = rte_event_dev_start(dev_id);
> +
> +       return ret;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create, 25.07)
> +struct rte_event_vector_adapter *
> +rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf)
> +{
> +       return rte_event_vector_adapter_create_ext(conf, default_port_conf_cb, NULL);
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create_ext, 25.07)
> +struct rte_event_vector_adapter *
> +rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
> +                                   rte_event_vector_adapter_port_conf_cb_t conf_cb, void *conf_arg)
> +{
> +       struct rte_event_vector_adapter *adapter = NULL;
> +       struct rte_event_vector_adapter_info info;
> +       char mz_name[MZ_NAME_MAX_LEN];
> +       const struct rte_memzone *mz;
> +       struct rte_eventdev *dev;
> +       uint32_t caps;
> +       int i, n, rc;
> +
> +       PTR_VALID_OR_ERR_RET(conf, NULL);
> +
> +       if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
> +               adapters[conf->event_dev_id][conf->ev.queue_id] =
> +                       rte_zmalloc("rte_event_vector_adapter",
> +                                   sizeof(struct rte_event_vector_adapter) *
> +                                           RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
> +                                   RTE_CACHE_LINE_SIZE);
> +               if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
> +                       EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
> +                       rte_errno = ENOMEM;
> +                       return NULL;
> +               }
> +       }
> +
> +       for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
> +               if (adapters[conf->event_dev_id][conf->ev.queue_id][i].used == false) {
> +                       adapter = &adapters[conf->event_dev_id][conf->ev.queue_id][i];
> +                       adapter->adapter_id = ADAPTER_ID(conf->event_dev_id, conf->ev.queue_id, i);
> +                       adapter->used = true;
> +                       break;
> +               }
> +               EVVEC_LOG_DBG("adapter %u is already in use", i);
> +               rte_errno = EEXIST;
> +               return NULL;
> +       }
> +
> +       if (adapter == NULL) {
> +               EVVEC_LOG_DBG("no available vector adapters");
> +               rte_errno = ENODEV;
> +               return NULL;
> +       }
> +
> +       RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, NULL);
> +
> +       dev = &rte_eventdevs[conf->event_dev_id];
> +       if (dev->dev_ops->vector_adapter_caps_get != NULL &&
> +           dev->dev_ops->vector_adapter_info_get != NULL) {
> +               rc = dev->dev_ops->vector_adapter_caps_get(dev, &caps, &adapter->ops);
> +               if (rc < 0) {
> +                       EVVEC_LOG_DBG("failed to get vector adapter capabilities rc = %d", rc);
> +                       rte_errno = ENOTSUP;
> +                       goto error;
> +               }
> +
> +               rc = dev->dev_ops->vector_adapter_info_get(dev, &info);
> +               if (rc < 0) {
> +                       adapter->ops = NULL;
> +                       EVVEC_LOG_DBG("failed to get vector adapter info rc = %d", rc);
> +                       rte_errno = ENOTSUP;
> +                       goto error;
> +               }
> +       }
> +
> +       if (conf->ev.sched_type != dev->data->queues_cfg[conf->ev.queue_id].schedule_type &&
> +           !(dev->data->event_dev_cap & RTE_EVENT_DEV_CAP_QUEUE_ALL_TYPES)) {
> +               EVVEC_LOG_DBG("invalid event schedule type, eventdev doesn't support all types");
> +               rte_errno = EINVAL;
> +               goto error;
> +       }
> +
> +       rc = validate_conf(conf, &info);
> +       if (rc < 0) {
> +               adapter->ops = NULL;
> +               rte_errno = EINVAL;
> +               goto error;
> +       }
> +
> +       n = snprintf(mz_name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, conf->event_dev_id,
> +                    conf->ev.queue_id, adapter->adapter_id);
> +       if (n >= (int)sizeof(mz_name)) {
> +               adapter->ops = NULL;
> +               EVVEC_LOG_DBG("failed to create memzone name");
> +               rte_errno = EINVAL;
> +               goto error;
> +       }
> +       mz = rte_memzone_reserve(mz_name, sizeof(struct rte_event_vector_adapter_data),
> +                                conf->socket_id, 0);
> +       if (mz == NULL) {
> +               adapter->ops = NULL;
> +               EVVEC_LOG_DBG("failed to reserve memzone for vector adapter");
> +               rte_errno = ENOMEM;
> +               goto error;
> +       }
> +
> +       adapter->data = mz->addr;
> +       memset(adapter->data, 0, sizeof(struct rte_event_vector_adapter_data));
> +
> +       adapter->data->mz = mz;
> +       adapter->data->event_dev_id = conf->event_dev_id;
> +       adapter->data->id = adapter->adapter_id;
> +       adapter->data->socket_id = conf->socket_id;
> +       adapter->data->conf = *conf;
> +
> +       if (!(caps & RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT)) {
> +               if (conf_cb == NULL) {
> +                       EVVEC_LOG_DBG("port config callback is NULL");
> +                       rte_errno = EINVAL;
> +                       goto error;
> +               }
> +
> +               rc = conf_cb(conf->event_dev_id, &adapter->data->event_port_id, conf_arg);
> +               if (rc < 0) {
> +                       EVVEC_LOG_DBG("failed to create port for vector adapter");
> +                       rte_errno = EINVAL;
> +                       goto error;
> +               }
> +       }
> +
> +       FUNC_PTR_OR_ERR_RET(adapter->ops->create, NULL);
> +
> +       rc = adapter->ops->create(adapter);
> +       if (rc < 0) {
> +               adapter->ops = NULL;
> +               EVVEC_LOG_DBG("failed to create vector adapter");
> +               rte_errno = EINVAL;
> +               goto error;
> +       }
> +
> +       adapter->enqueue = adapter->ops->enqueue;
> +
> +       return adapter;
> +
> +error:
> +       adapter->used = false;
> +       return NULL;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_lookup, 25.07)
> +struct rte_event_vector_adapter *
> +rte_event_vector_adapter_lookup(uint32_t adapter_id)
> +{
> +       uint8_t adapter_idx = ADAPTER_ID_FROM_ADAPTER_ID(adapter_id);
> +       uint8_t queue_id = QUEUE_ID_FROM_ADAPTER_ID(adapter_id);
> +       uint8_t dev_id = DEV_ID_FROM_ADAPTER_ID(adapter_id);
> +       struct rte_event_vector_adapter *adapter;
> +       const struct rte_memzone *mz;
> +       char name[MZ_NAME_MAX_LEN];
> +       struct rte_eventdev *dev;
> +       int rc;
> +
> +       if (dev_id >= RTE_EVENT_MAX_DEVS || queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV ||
> +           adapter_idx >= RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE) {
> +               EVVEC_LOG_ERR("invalid adapter id %u", adapter_id);
> +               rte_errno = EINVAL;
> +               return NULL;
> +       }
> +
> +       if (adapters[dev_id][queue_id] == NULL) {
> +               adapters[dev_id][queue_id] =
> +                       rte_zmalloc("rte_event_vector_adapter",
> +                                   sizeof(struct rte_event_vector_adapter) *
> +                                           RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
> +                                   RTE_CACHE_LINE_SIZE);
> +               if (adapters[dev_id][queue_id] == NULL) {
> +                       EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
> +                       rte_errno = ENOMEM;
> +                       return NULL;
> +               }
> +       }
> +
> +       if (adapters[dev_id][queue_id][adapter_idx].used == true)
> +               return &adapters[dev_id][queue_id][adapter_idx];
> +
> +       adapter = &adapters[dev_id][queue_id][adapter_idx];
> +
> +       snprintf(name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, dev_id, queue_id, adapter_idx);
> +       mz = rte_memzone_lookup(name);
> +       if (mz == NULL) {
> +               EVVEC_LOG_DBG("failed to lookup memzone for vector adapter");
> +               rte_errno = ENOENT;
> +               return NULL;
> +       }
> +
> +       adapter->data = mz->addr;
> +       dev = &rte_eventdevs[dev_id];
> +
> +       if (dev->dev_ops->vector_adapter_caps_get != NULL) {
> +               rc = dev->dev_ops->vector_adapter_caps_get(dev, &adapter->data->caps,
> +                                                          &adapter->ops);
> +               if (rc < 0) {
> +                       EVVEC_LOG_DBG("failed to get vector adapter capabilities");
> +                       rte_errno = ENOTSUP;
> +                       return NULL;
> +               }
> +       }
> +
> +       adapter->enqueue = adapter->ops->enqueue;
> +       adapter->adapter_id = adapter_id;
> +       adapter->used = true;
> +
> +       return adapter;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_service_id_get, 25.07)
> +int
> +rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
> +                                       uint32_t *service_id)
> +{
> +       PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
> +
> +       if (adapter->data->service_inited && service_id != NULL)
> +               *service_id = adapter->data->unified_service_id;
> +
> +       return adapter->data->service_inited ? 0 : -ESRCH;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_destroy, 25.07)
> +int
> +rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter)
> +{
> +       int rc;
> +
> +       PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
> +       if (adapter->used == false) {
> +               EVVEC_LOG_ERR("event vector adapter is not allocated");
> +               return -EINVAL;
> +       }
> +
> +       FUNC_PTR_OR_ERR_RET(adapter->ops->destroy, -ENOTSUP);
> +
> +       rc = adapter->ops->destroy(adapter);
> +       if (rc < 0) {
> +               EVVEC_LOG_DBG("failed to destroy vector adapter");
> +               return rc;
> +       }
> +
> +       rte_memzone_free(adapter->data->mz);
> +       adapter->ops = NULL;
> +       adapter->enqueue = dummy_vector_adapter_enqueue;
> +       adapter->data = NULL;
> +       adapter->used = false;
> +
> +       return 0;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_info_get, 25.07)
> +int
> +rte_event_vector_adapter_info_get(uint8_t event_dev_id, struct rte_event_vector_adapter_info *info)
> +{
> +       RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, -EINVAL);
> +       PTR_VALID_OR_ERR_RET(info, -EINVAL);
> +
> +       struct rte_eventdev *dev = &rte_eventdevs[event_dev_id];
> +       if (dev->dev_ops->vector_adapter_info_get != NULL)
> +               return dev->dev_ops->vector_adapter_info_get(dev, info);
> +
> +       return 0;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_conf_get, 25.07)
> +int
> +rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
> +                                 struct rte_event_vector_adapter_conf *conf)
> +{
> +       PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
> +       PTR_VALID_OR_ERR_RET(conf, -EINVAL);
> +
> +       *conf = adapter->data->conf;
> +       return 0;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_remaining, 25.07)
> +uint8_t
> +rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id)
> +{
> +       uint8_t remaining = 0;
> +       int i;
> +
> +       RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, 0);
> +
> +       if (event_queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV)
> +               return 0;
> +
> +       for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
> +               if (adapters[event_dev_id][event_queue_id][i].used == false)
> +                       remaining++;
> +       }
> +
> +       return remaining;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_get, 25.07)
> +int
> +rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
> +                                  struct rte_event_vector_adapter_stats *stats)
> +{
> +       PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
> +       PTR_VALID_OR_ERR_RET(stats, -EINVAL);
> +
> +       FUNC_PTR_OR_ERR_RET(adapter->ops->stats_get, -ENOTSUP);
> +
> +       adapter->ops->stats_get(adapter, stats);
> +
> +       return 0;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_reset, 25.07)
> +int
> +rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter)
> +{
> +       PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
> +
> +       FUNC_PTR_OR_ERR_RET(adapter->ops->stats_reset, -ENOTSUP);
> +
> +       adapter->ops->stats_reset(adapter);
> +
> +       return 0;
> +}
> diff --git a/lib/eventdev/rte_event_vector_adapter.h b/lib/eventdev/rte_event_vector_adapter.h
> new file mode 100644
> index 0000000000..61680ec307
> --- /dev/null
> +++ b/lib/eventdev/rte_event_vector_adapter.h
> @@ -0,0 +1,481 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Marvell International Ltd.
> + * All rights reserved.
> + */
> +
> +#ifndef __RTE_EVENT_VECTOR_ADAPTER_H__
> +#define __RTE_EVENT_VECTOR_ADAPTER_H__
> +
> +/**
> + * @file rte_event_vector_adapter.h
> + *
> + * @warning
> + * @b EXPERIMENTAL:
> + * All functions in this file may be changed or removed without prior notice.
> + *
> + * Event vector adapter API.
> + *
> + * An event vector adapter has the following working model:
> + *
> + *         ┌──────────┐
> + *         │  Vector  ├─┐
> + *         │ adapter0 │ │
> + *         └──────────┘ │
> + *         ┌──────────┐ │   ┌──────────┐
> + *         │  Vector  ├─┼──►│  Event   │
> + *         │ adapter1 │ │   │  Queue0  │
> + *         └──────────┘ │   └──────────┘
> + *         ┌──────────┐ │
> + *         │  Vector  ├─┘
> + *         │ adapter2 │
> + *         └──────────┘
> + *
> + *         ┌──────────┐
> + *         │  Vector  ├─┐
> + *         │ adapter0 │ │   ┌──────────┐
> + *         └──────────┘ ├──►│  Event   │
> + *         ┌──────────┐ │   │  Queue1  │
> + *         │  Vector  ├─┘   └──────────┘
> + *         │ adapter1 │
> + *         └──────────┘
> + *
> + * - A vector adapter can be seen as an extension to event queue. It helps in
> + *   aggregating objects and generating a vector event which is enqueued to the
> + *   event queue.
> + *
> + * - Multiple vector adapters can be created on an event queue, each with its
> + *   own unique properties such as event properties, vector size, and timeout.
> + *   Note: If the target event queue doesn't support RTE_EVENT_QUEUE_CFG_ALL_TYPES,
> + *         then the vector adapter should use the same schedule type as the event
> + *         queue.
> + *
> + * - Each vector adapter aggregates 8B objects, generates a vector event and
> + *   enqueues it to the event queue with the event properties mentioned in
> + *   rte_event_vector_adapter_conf::ev.
> + *
> + * - After configuring the vector adapter, Application needs to use the
> + *   rte_event_vector_adapter_enqueue() function to enqueue objects i.e.,
> + *   mbufs/ptrs/u64s to the vector adapter.
> + *   On reaching the configured vector size or timeout, the vector adapter
> + *   enqueues the event vector to the event queue.
> + *   Note: Application should use the event_type and sub_event_type properly
> + *         identifying the contents of vector event on dequeue.
> + *
> + * - If the vector adapter advertises the RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
> + *   capability, application can use the RTE_EVENT_VECTOR_ENQ_[S|E]OV flags
> + *   to indicate the start and end of a vector event.
> + *   * When RTE_EVENT_VECTOR_ENQ_SOV is set, the vector adapter will flush any
> + *     aggregation in-progress and start aggregating a new vector event with
> + *     the enqueued objects.
> + *   * When RTE_EVENT_VECTOR_ENQ_EOV is set, the vector adapter will add the
> + *     objects enqueued to the in-progress aggregation and enqueue the vector
> + *     event to the event queue, even if configured vector size or timeout is
> + *     not reached.
> + *   * If both flags are set, the vector adapter will flush any aggregation in
> + *     progress and enqueue the objects as a new vector event to the event
> + *     queue.
> + *
> + * - If the vector adapter reaches the configured vector size, it will enqueue
> + *   the aggregated vector event to the event queue.
> + *
> + * - If the vector adapter reaches the configured vector timeout, it will flush
> + *   the aggregated objects as a vector event if the minimum vector size is
> + *   reached, if not it will enqueue the objs as single events to the event
> + *   queue.
> + *
> + * - If the vector adapter is unable to aggregate the objs into a vector event,
> + *   it will enqueue the objs as single events to the event queue with the event
> + *   properties mentioned in rte_event_vector_adapter_conf::ev_fallback.
> + *
> + * Before using the vector adapter, the application has to create and configure
> + * an event device and based on the event device capability it might require
> + * creating an additional event port.
> + *
> + * When the application creates the vector adapter using the
> + * ``rte_event_vector_adapter_create()`` function, the event device driver
> + * capabilities are checked. If an in-built port is absent, the application
> + * uses the default function to create a new event port.
> + * For finer control over event port creation, the application should use
> + * the ``rte_event_vector_adapter_create_ext()`` function.
> + *
> + * The application can enqueue one or more objs to the vector adapter using the
> + * ``rte_event_vector_adapter_enqueue()`` function and control the aggregation
> + * using the flags.
> + *
> + * Vector adapters report stats using the ``rte_event_vector_adapter_stats_get()``
> + * function and reset the stats using the ``rte_event_vector_adapter_stats_reset()``.
> + *
> + * The application can destroy the vector adapter using the
> + * ``rte_event_vector_adapter_destroy()`` function.
> + *
> + */
> +
> +#include <rte_eventdev.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#define RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV (1ULL << 0)


Use RTE_BIT_UL flavour everywhere in the new code.



> +/**< Vector adapter supports Start of Vector (SOV) and End of Vector (EOV) flags
> + *  in the enqueue flags.
> + *
> + * @see RTE_EVENT_VECTOR_ENQ_SOV
> + * @see RTE_EVENT_VECTOR_ENQ_EOV
> + */
> +
> +#define RTE_EVENT_VECTOR_ENQ_SOV   (1ULL << 0)
> +/**< Indicates the start of a vector event. When enqueue is called with
> + *  RTE_EVENT_VECTOR_ENQ_SOV, the vector adapter will flush any vector
> + *  aggregation in progress and start aggregating a new vector event with
> + *  the enqueued objects.


reference the capablity here

> + */
> +#define RTE_EVENT_VECTOR_ENQ_EOV   (1ULL << 1)
> +/**< Indicates the end of a vector event. When enqueue is called with
> + *  RTE_EVENT_VECTOR_ENQ_EOV, the vector adapter will add the objects
> + *  to any inprogress aggregation and flush the event vector.

reference the capablity here


> + */
> +#define RTE_EVENT_VECTOR_ENQ_FLUSH (1ULL << 2)
> +/**< Flush any in-progress vector aggregation. */
> +
> +/**
> + * Vector adapter configuration structure
> + */
> +struct rte_event_vector_adapter_conf {
> +       uint8_t event_dev_id;
> +       /**< Event device identifier */
> +       uint32_t socket_id;
> +       /**< Identifier of socket from which to allocate memory for adapter */
> +       struct rte_event ev;
> +       /**<
> +        *  The values from the following event fields will be used when
> +        *  queuing work:
> +        *   - queue_id: Targeted event queue ID for vector event.
> +        *   - event_priority: Event priority of the vector event in
> +        *                     the event queue relative to other events.
> +        *   - sched_type: Scheduling type for events from this vector adapter.
> +        *   - event_type: Event type for the vector event.
> +        *   - sub_event_type: Sub event type for the vector event.
> +        *   - flow_id: Flow ID for the vectors enqueued to the event queue by
> +        *              the vector adapter.
> +        */
> +       struct rte_event ev_fallback;
> +       /**<
> +        * The values from the following event fields will be used when
> +        * aggregation fails and single event is enqueued:
> +        *   - event_type: Event type for the single event.
> +        *   - sub_event_type: Sub event type for the single event.
> +        *   - flow_id: Flow ID for the single event.
> +        *
> +        * Other fields are taken from rte_event_vector_adapter_conf::ev.
> +        */
> +       uint16_t vector_sz;
> +       /**<
> +        * Indicates the maximum number for enqueued work to combine and form a vector.
> +        * Should be within vectorization limits of the adapter.
> +        * @see rte_event_vector_adapter_info::min_vector_sz
> +        * @see rte_event_vector_adapter_info::max_vector_sz
> +        */
> +       uint64_t vector_timeout_ns;
> +       /**<
> +        * Indicates the maximum number of nanoseconds to wait for receiving
> +        * work. Should be within vectorization limits of the adapter.
> +        * @see rte_event_vector_adapter_info::min_vector_ns
> +        * @see rte_event_vector_adapter_info::max_vector_ns
> +        */
> +       struct rte_mempool *vector_mp;
> +       /**<
> +        * Indicates the mempool that should be used for allocating
> +        * rte_event_vector container.
> +        * @see rte_event_vector_pool_create
> +        */
> +};
> +
> +/**
> + * Vector adapter vector info structure
> + */
> +struct rte_event_vector_adapter_info {
> +       uint8_t max_vector_adapters_per_event_queue;
> +       /**< Maximum number of vector adapters configurable */
> +       uint16_t min_vector_sz;
> +       /**< Minimum vector size configurable */
> +       uint16_t max_vector_sz;
> +       /**< Maximum vector size configurable */
> +       uint64_t min_vector_timeout_ns;
> +       /**< Minimum vector timeout configurable */
> +       uint64_t max_vector_timeout_ns;
> +       /**< Maximum vector timeout configurable */
> +       uint8_t log2_sz;
> +       /**< True if the size configured should be in log2. */
> +};
> +
> +/**
> + * Vector adapter statistics structure
> + */
> +struct rte_event_vector_adapter_stats {
> +       uint64_t vectorized;
> +       /**< Number of events vectorized */
> +       uint64_t vectors_timedout;
> +       /**< Number of timeouts occurred */
> +       uint64_t vectors_flushed;
> +       /**< Number of vectors flushed */
> +       uint64_t alloc_failures;
> +       /**< Number of vector allocation failures */
> +};
> +
> +struct rte_event_vector_adapter;
> +
> +typedef int (*rte_event_vector_adapter_enqueue_t)(struct rte_event_vector_adapter *adapter,
> +                                                 uint64_t objs[], uint16_t num_elem,
> +                                                 uint64_t flags);
> +/**< @internal Enqueue objs into the event vector adapter. */
> +
> +struct __rte_cache_aligned rte_event_vector_adapter {
> +       rte_event_vector_adapter_enqueue_t enqueue;
> +       /**< Pointer to driver enqueue function. */
> +       struct rte_event_vector_adapter_data *data;
> +       /**< Pointer to the adapter data */
> +       const struct event_vector_adapter_ops *ops;
> +       /**< Functions exported by adapter driver */
> +
> +       uint32_t adapter_id;
> +       /**< Identifier of the adapter instance. */
> +       uint8_t used : 1;
> +       /**< Flag to indicate that this adapter is being used. */
> +};
> +
> +/**
> + * Callback function type for producer port creation.
> + */
> +typedef int (*rte_event_vector_adapter_port_conf_cb_t)(uint8_t event_dev_id, uint8_t *event_port_id,
> +                                                      void *conf_arg);
> +
> +/**
> + * Create an event vector adapter.
> + *
> + * This function creates an event vector adapter based on the provided
> + * configuration. The adapter can be used to combine multiple mbufs/ptrs/u64s
> + * into a single vector event, i.e., rte_event_vector, which is then enqueued
> + * to the event queue provided.
> + * @see rte_event_vector_adapter_conf::ev::event_queue_id.
> + *
> + * @param conf
> + *   Configuration for the event vector adapter.
> + * @return
> + *   - Pointer to the created event vector adapter on success.
> + *   - NULL on failure with rte_errno set to the error code.
> + *     Possible rte_errno values include:
> + *    - EINVAL: Invalid event device identifier specified in config.
> + *    - ENOMEM: Unable to allocate sufficient memory for adapter instances.
> + *    - ENOSPC: Maximum number of adapters already created.
> + */
> +__rte_experimental
> +struct rte_event_vector_adapter *
> +rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf);
> +
> +/**
> + * Create an event vector adapter with the supplied callback.
> + *
> + * This function can be used to have a more granular control over the event
> + * vector adapter creation. If a built-in port is absent, then the function uses
> + * the callback provided to create and get the port id to be used as a producer
> + * port.
> + *
> + * @param conf
> + *   The event vector adapter configuration structure.
> + * @param conf_cb
> + *   The port config callback function.
> + * @param conf_arg
> + *   Opaque pointer to the argument for the callback function.
> + * @return
> + *   - Pointer to the new allocated event vector adapter on success.
> + *   - NULL on error with rte_errno set appropriately.
> + *   Possible rte_errno values include:
> + *   - ERANGE: vector_timeout_ns is not in supported range.
> + *   - ENOMEM: Unable to allocate sufficient memory for adapter instances.
> + *   - EINVAL: Invalid event device identifier specified in config.
> + *   - ENOSPC: Maximum number of adapters already created.
> + */
> +__rte_experimental
> +struct rte_event_vector_adapter *
> +rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
> +                                   rte_event_vector_adapter_port_conf_cb_t conf_cb,
> +                                   void *conf_arg);
> +
> +/**
> + * Lookup an event vector adapter using its identifier.
> + *
> + * This function returns the event vector adapter based on the adapter_id.
> + * This is useful when the adapter is created in another process and the
> + * application wants to use the adapter in the current process.
> + *
> + * @param adapter_id
> + *   Identifier of the event vector adapter to look up.
> + * @return
> + *   - Pointer to the event vector adapter on success.
> + *   - NULL if the adapter is not found.
> + */
> +__rte_experimental
> +struct rte_event_vector_adapter *
> +rte_event_vector_adapter_lookup(uint32_t adapter_id);
> +
> +/**
> + * Destroy an event vector adapter.
> + *
> + * This function releases the resources associated with the event vector adapter.
> + *
> + * @param adapter
> + *   Pointer to the event vector adapter to be destroyed.
> + * @return
> + *   - 0 on success.
> + *   - Negative value on failure with rte_errno set to the error code.
> + */
> +__rte_experimental
> +int
> +rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter);
> +
> +/**
> + * Get the vector info of an event vector adapter.
> + *
> + * This function retrieves the vector info of the event vector adapter.
> + *
> + * @param event_dev_id
> + *   Event device identifier.
> + * @param info
> + *   Pointer to the structure where the vector info will be stored.
> + * @return
> + *   0 on success, negative value on failure.
> + *   - EINVAL if the event device identifier is invalid.
> + *   - ENOTSUP if the event device does not support vector adapters.
> + */
> +__rte_experimental
> +int
> +rte_event_vector_adapter_info_get(uint8_t event_dev_id,
> +                                 struct rte_event_vector_adapter_info *info);
> +
> +/**
> + * Get the configuration of an event vector adapter.
> + *
> + * This function retrieves the configuration of the event vector adapter.
> + *
> + * @param adapter
> + *   Pointer to the event vector adapter.
> + * @param conf
> + *   Pointer to the structure where the configuration will be stored.
> + * @return
> + *   0 on success, negative value on failure.
> + */
> +__rte_experimental
> +int
> +rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
> +                                 struct rte_event_vector_adapter_conf *conf);
> +
> +/**
> + * Get the remaining event vector adapters.
> + *
> + * This function retrieves the number of remaining event vector adapters
> + * available for a given event device and event queue.
> + *
> + * @param event_dev_id
> + *   Event device identifier.
> + * @param event_queue_id
> + *   Event queue identifier.
> + * @return
> + *   Number of remaining slots available for enqueuing events.
> + */
> +__rte_experimental
> +uint8_t
> +rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id);
> +
> +/**
> + * Get the event vector adapter statistics.
> + *
> + * This function retrieves the statistics of the event vector adapter.
> + *
> + * @param adapter
> + *   Pointer to the event vector adapter.
> + * @param stats
> + *   Pointer to the structure where the statistics will be stored.
> + * @return
> + *   0 on success, negative value on failure.
> + */
> +__rte_experimental
> +int
> +rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
> +                                  struct rte_event_vector_adapter_stats *stats);
> +
> +/**
> + * @brief Reset the event vector adapter statistics.
> + *
> + * This function resets the statistics of the event vector adapter to their default values.
> + *
> + * @param adapter
> + *   Pointer to the event vector adapter whose statistics are to be reset.
> + * @return
> + *   0 on success, negative value on failure.
> + */
> +__rte_experimental
> +int
> +rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter);
> +
> +/**
> + * Retrieve the service ID of the event vector adapter. If the adapter doesn't
> + * use an rte_service function, this function returns -ESRCH.
> + *
> + * @param adapter
> + *   A pointer to an event vector adapter.
> + * @param [out] service_id
> + *   A pointer to a uint32_t, to be filled in with the service id.
> + *
> + * @return
> + *   - 0: Success
> + *   - <0: Error code on failure
> + *   - -ESRCH: the adapter does not require a service to operate
> + */
> +__rte_experimental
> +int
> +rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
> +                                       uint32_t *service_id);
> +
> +/**
> + * Enqueue objs into the event vector adapter.
> + *
> + * This function enqueues a specified number of objs into the event vector adapter.
> + * The objs are combined into a single vector event, i.e., rte_event_vector, which
> + * is then enqueued to the event queue configured in the adapter.
> + *
> + * @param adapter
> + *   Pointer to the event vector adapter.
> + * @param objs
> + *   Array of objs to be enqueued.
> + * @param num_elem
> + *   Number of objs to be enqueued.
> + * @param flags
> + *   Flags to be used for the enqueue operation.
> + * @return
> + *   Number of objs enqueued on success.
> + */
> +__rte_experimental
> +static inline int
> +rte_event_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
> +                                uint16_t num_elem, uint64_t flags)
> +{
> +#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
> +       if (adapter == NULL) {
> +               rte_errno = EINVAL;
> +               return 0;
> +       }
> +
> +       if (adapter->used == false) {
> +               rte_errno = EINVAL;
> +               return 0;
> +       }
> +#endif
> +       return adapter->enqueue(adapter, objs, num_elem, flags);
> +}
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* __RTE_EVENT_VECTOR_ADAPTER_H__ */
> diff --git a/lib/eventdev/rte_eventdev.c b/lib/eventdev/rte_eventdev.c
> index b66cbb4676..916bad6c2c 100644
> --- a/lib/eventdev/rte_eventdev.c
> +++ b/lib/eventdev/rte_eventdev.c
> @@ -257,6 +257,28 @@ rte_event_dma_adapter_caps_get(uint8_t dev_id, uint8_t dma_dev_id, uint32_t *cap
>         return 0;
>  }
>
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_caps_get, 25.07)
> +int
> +rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps)
> +{
> +       const struct event_vector_adapter_ops *ops;
> +       struct rte_eventdev *dev;
> +
> +       RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(dev_id, -EINVAL);
> +
> +       dev = &rte_eventdevs[dev_id];
> +
> +       if (caps == NULL)
> +               return -EINVAL;
> +
> +       if (dev->dev_ops->vector_adapter_caps_get == NULL)
> +               *caps = 0;
> +
> +       return dev->dev_ops->vector_adapter_caps_get ?
> +                      dev->dev_ops->vector_adapter_caps_get(dev, caps, &ops) :
> +                      0;
> +}
> +
>  static inline int
>  event_dev_queue_config(struct rte_eventdev *dev, uint8_t nb_queues)
>  {
> diff --git a/lib/eventdev/rte_eventdev.h b/lib/eventdev/rte_eventdev.h
> index 6400d6109f..3c7fcbf0be 100644
> --- a/lib/eventdev/rte_eventdev.h
> +++ b/lib/eventdev/rte_eventdev.h
> @@ -1985,6 +1985,16 @@ int
>  rte_event_eth_tx_adapter_caps_get(uint8_t dev_id, uint16_t eth_port_id,
>                                 uint32_t *caps);
>
> +/* Vector adapter capability bitmap flags */
> +#define RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT     0x1
> +/**< This flag is set when the vector adapter is capable of generating events
> + * using an internal event port.
> + */
> +
> +__rte_experimental
> +int
> +rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps);
> +
>  /**
>   * Converts nanoseconds to *timeout_ticks* value for rte_event_dequeue_burst()
>   *
> --
> 2.43.0
>

^ permalink raw reply	[relevance 0%]

* [PATCH] doc: update ABI reference version in example
@ 2025-05-28 23:30  7% Stephen Hemminger
  0 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-05-28 23:30 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger

The example should list current ABI reference version.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 doc/guides/contributing/patches.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/guides/contributing/patches.rst b/doc/guides/contributing/patches.rst
index 8ad6b6e715..fc9844242e 100644
--- a/doc/guides/contributing/patches.rst
+++ b/doc/guides/contributing/patches.rst
@@ -539,7 +539,7 @@ to a different location.
 
 Sample::
 
-   DPDK_ABI_REF_VERSION=v19.11 DPDK_ABI_REF_DIR=/tmp ./devtools/test-meson-builds.sh
+   DPDK_ABI_REF_VERSION=v24.11 DPDK_ABI_REF_DIR=/tmp ./devtools/test-meson-builds.sh
 
 
 Sending Patches
-- 
2.47.2


^ permalink raw reply	[relevance 7%]

* Re: [PATCH 0/3] argparse additions and rework
  2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
  2025-05-27  9:21 10% ` [PATCH 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
  2025-05-27  9:21  6% ` [PATCH 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
@ 2025-05-29 15:09  0% ` Bruce Richardson
  2025-06-03 15:32  4% ` [PATCH v2 " Bruce Richardson
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-05-29 15:09 UTC (permalink / raw)
  To: dev; +Cc: Chengwen Feng, david.marchand

On Tue, May 27, 2025 at 10:21:10AM +0100, Bruce Richardson wrote:
> This patchset is based off the work to adjust how we do argument parsing
> inside EAL. To enable argparse to be effectively used for EAL, we have
> new features and some changes in the first two patches, which are 
> relatively small - though are ABI/API affecting.
> 
> These add support for saving off strings and boolean values, have argparse
> stop parsing at a "--", and finally have argparse return the number of
> arguments actually parsed on success.
> 
> The third patch is a bigger change. It was inspired by the fact that
> when adding the boolean and string support we had to update some
> "MAX" value defines used in the code. This is obviously not good from
> an ABI/API perspective, once the library becomes part of the stable ABI.
> In order to remove these MAX values, patch 3 looks to replace the
> #define values with enums - which means some rework splitting the
> various flags into separate categories, and similarly splitting the
> single "flags" field with separate fields specifying if an argument
> value is required, what type that value should have, and then a
> final smaller field for any additional modifiers.
> 
> Bruce Richardson (3):
>   argparse: add support for string and boolean args
>   argparse: make argparse EAL-args compatible
>   argparse: use enums to remove max-value defines in lists
>

Ping for review.

Chengwen, as maintainer of argparse, can you perhaps take a look at this
set and give your feedback? I'm hoping to do some rework of EAL argument
parsing using argparse (see [1]), which requires some of the changes in this
patchset.

Thanks,
/Bruce 

[1] https://patches.dpdk.org/project/dpdk/list/?series=35256&state=*

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] pcapng: allow any protocol link type for the interface block
  @ 2025-05-29 19:31  3% ` Stephen Hemminger
  2025-06-05 23:02  3% ` [PATCH v2] " Schneide
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-05-29 19:31 UTC (permalink / raw)
  To: Schneide
  Cc: Thomas Monjalon, Reshma Pattan, Jerin Jacob, Kiran Kumar K,
	Nithin Dabilpuram, Zhirun Yan, dev

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

What other packet types would a DPDK app use. The possible type fields are
quite limited see the spec.

Plus it would be an ABI breakage would require versioning.

On Thu, May 29, 2025, 10:16 Schneide <schneide@qti.qualcomm.com> wrote:

> From: Dylan Schneider <schneide@qti.qualcomm.com>
>
> Allow the user to specify protocol link type when creating pcapng files.
> This change is needed to specify the protocol type in the pcapng file,
> DLT_EN10MB specifies ethernet packets only. This will allow dissectors
> for other protocols to be used on files generated by pcapng.
>
> Includes a breaking change to rte_pcapng_add_interface to add link_type
> parameter. Existing calls to the function have been updated to pass
> DLT_EN10MB for the link type argument.
>
> Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
> Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
> Cc: stephen@networkplumber.org
> ---
>
> diff --git a/.mailmap b/.mailmap
> index d9423aa..7ef0964 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -388,6 +388,7 @@
>  Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
>  Duncan Bellamy <dunk@denkimushi.com>
>  Dustin Lundquist <dustin@null-ptr.net>
> +Dylan Schneider <schneide@qti.qualcomm.com>
>  Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
>  Ed Czeck <ed.czeck@atomicrules.com>
>  Eduard Serra <eserra@vmware.com>
> diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
> index 3d3c0db..e0e2b26 100644
> --- a/app/dumpcap/main.c
> +++ b/app/dumpcap/main.c
> @@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
>                 free(os);
>
>                 TAILQ_FOREACH(intf, &interfaces, next) {
> -                       if (rte_pcapng_add_interface(ret.pcapng,
> intf->port, intf->ifname,
> -                                                    intf->ifdescr,
> intf->opts.filter) < 0)
> +                       if (rte_pcapng_add_interface(ret.pcapng,
> intf->port, DLT_EN10MB,
> +                               intf->ifname, intf->ifdescr,
> intf->opts.filter) < 0)
>                                 rte_exit(EXIT_FAILURE,
> "rte_pcapng_add_interface %u failed\n",
>                                         intf->port);
>                 }
> diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
> index 8f2cff3..bcf9972 100644
> --- a/app/test/test_pcapng.c
> +++ b/app/test/test_pcapng.c
> @@ -345,7 +345,7 @@ test_add_interface(void)
>         }
>
>         /* Add interface to the file */
> -       ret = rte_pcapng_add_interface(pcapng, port_id,
> +       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                        NULL, NULL, NULL);
>         if (ret < 0) {
>                 fprintf(stderr, "can not add port %u\n", port_id);
> @@ -353,7 +353,7 @@ test_add_interface(void)
>         }
>
>         /* Add interface with ifname and ifdescr */
> -       ret = rte_pcapng_add_interface(pcapng, port_id,
> +       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                        "myeth", "Some long description",
> NULL);
>         if (ret < 0) {
>                 fprintf(stderr, "can not add port %u with ifname\n",
> port_id);
> @@ -361,7 +361,7 @@ test_add_interface(void)
>         }
>
>         /* Add interface with filter */
> -       ret = rte_pcapng_add_interface(pcapng, port_id,
> +       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                        NULL, NULL, "tcp port 8080");
>         if (ret < 0) {
>                 fprintf(stderr, "can not add port %u with filter\n",
> port_id);
> @@ -406,7 +406,7 @@ test_write_packets(void)
>         }
>
>         /* Add interface to the file */
> -       ret = rte_pcapng_add_interface(pcapng, port_id,
> +       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                        NULL, NULL, NULL);
>         if (ret < 0) {
>                 fprintf(stderr, "can not add port %u\n", port_id);
> diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
> index 89525f1..46fab8c 100644
> --- a/lib/graph/graph_pcap.c
> +++ b/lib/graph/graph_pcap.c
> @@ -117,7 +117,7 @@ graph_pcap_file_open(const char *filename)
>
>         /* Add the configured interfaces as possible capture ports */
>         RTE_ETH_FOREACH_DEV(portid) {
> -               ret = rte_pcapng_add_interface(pcapng_fd, portid,
> +               ret = rte_pcapng_add_interface(pcapng_fd, portid,
> DLT_EN10MB
>                                                NULL, NULL, NULL);
>                 if (ret < 0) {
>                         graph_err("Graph rte_pcapng_add_interface port %u
> failed: %d",
> diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
> index 16485b2..bed1e14 100644
> --- a/lib/pcapng/rte_pcapng.c
> +++ b/lib/pcapng/rte_pcapng.c
> @@ -205,7 +205,7 @@ pcapng_section_block(rte_pcapng_t *self,
>  /* Write an interface block for a DPDK port */
>  RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
>  int
> -rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
> +rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t
> link_type,
>                          const char *ifname, const char *ifdescr,
>                          const char *filter)
>  {
> @@ -277,7 +277,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t
> port,
>         hdr = (struct pcapng_interface_block *)buf;
>         *hdr = (struct pcapng_interface_block) {
>                 .block_type = PCAPNG_INTERFACE_BLOCK,
> -               .link_type = 1,         /* DLT_EN10MB - Ethernet */
> +               .link_type = link_type,
>                 .block_length = len,
>         };
>
> diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
> index 48f2b57..0a35ec9 100644
> --- a/lib/pcapng/rte_pcapng.h
> +++ b/lib/pcapng/rte_pcapng.h
> @@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
>   *  The handle to the packet capture file
>   * @param port
>   *  The Ethernet port to report stats on.
> + * @param link_type
> + *  The protocol link type of the packets
>   * @param ifname (optional)
>   *  Interface name to record in the file.
>   *  If not specified, name will be constructed from port
> @@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
>   * must be added.
>   */
>  int
> -rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
> +rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t
> link_type,
>                          const char *ifname, const char *ifdescr,
>                          const char *filter);
>
>

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

^ permalink raw reply	[relevance 3%]

* [PATCH v2 1/3] eventdev: introduce event vector adapter
  @ 2025-05-29 22:24  1%       ` pbhagavatula
    1 sibling, 0 replies; 153+ results
From: pbhagavatula @ 2025-05-29 22:24 UTC (permalink / raw)
  To: jerinj, pbhagavatula, abhinandan.gujjar, amitprakashs,
	s.v.naga.harish.k, sthotton, pravin.pathak, hemant.agrawal,
	sachin.saxena, mattias.ronnblom, liangma, peter.mccarthy,
	harry.van.haaren, anatoly.burakov, erik.g.carrillo,
	Thomas Monjalon, Bruce Richardson
  Cc: dev

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

The event vector adapter supports offloading creation of
event vectors by vectorizing objects (mbufs/ptrs/u64s).
Applications can create a vector adapter associated with
an event queue and enqueue objects to be vectorized.
When the vector reaches the configured size or when the timeout
is reached, the vector adapter will enqueue the vector to the
event queue.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 MAINTAINERS                                   |   7 +
 config/rte_config.h                           |   1 +
 doc/api/doxy-api-index.md                     |   1 +
 doc/guides/eventdevs/features/default.ini     |   7 +
 .../eventdev/event_vector_adapter.rst         | 221 ++++++++
 doc/guides/prog_guide/eventdev/eventdev.rst   |  10 +-
 doc/guides/prog_guide/eventdev/index.rst      |   1 +
 doc/guides/rel_notes/release_25_07.rst        |  13 +
 lib/eventdev/event_vector_adapter_pmd.h       |  85 +++
 lib/eventdev/eventdev_pmd.h                   |  36 ++
 lib/eventdev/meson.build                      |   3 +
 lib/eventdev/rte_event_vector_adapter.c       | 472 +++++++++++++++++
 lib/eventdev/rte_event_vector_adapter.h       | 483 ++++++++++++++++++
 lib/eventdev/rte_eventdev.c                   |  22 +
 lib/eventdev/rte_eventdev.h                   |  10 +
 15 files changed, 1367 insertions(+), 5 deletions(-)
 create mode 100644 doc/guides/prog_guide/eventdev/event_vector_adapter.rst
 create mode 100644 lib/eventdev/event_vector_adapter_pmd.h
 create mode 100644 lib/eventdev/rte_event_vector_adapter.c
 create mode 100644 lib/eventdev/rte_event_vector_adapter.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 3e16789250d4..4c7564f5d8b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -578,6 +578,13 @@ F: lib/eventdev/*dma_adapter*
 F: app/test/test_event_dma_adapter.c
 F: doc/guides/prog_guide/eventdev/event_dma_adapter.rst
 
+Eventdev Vector Adapter API - EXPERIMENTAL
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+T: git://dpdk.org/next/dpdk-next-eventdev
+F: lib/eventdev/*vector_adapter*
+F: app/test/test_event_vector_adapter.c
+F: doc/guides/prog_guide/eventdev/event_vector_adapter.rst
+
 Raw device API
 M: Sachin Saxena <sachin.saxena@oss.nxp.com>
 M: Hemant Agrawal <hemant.agrawal@nxp.com>
diff --git a/config/rte_config.h b/config/rte_config.h
index 86897de75e75..9535c48d8188 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -92,6 +92,7 @@
 #define RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_DMA_ADAPTER_MAX_INSTANCE 32
+#define RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE 32
 
 /* rawdev defines */
 #define RTE_RAWDEV_MAX_DEVS 64
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb945..a11bd59526e7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -30,6 +30,7 @@ The public API headers are grouped by topics:
   [event_timer_adapter](@ref rte_event_timer_adapter.h),
   [event_crypto_adapter](@ref rte_event_crypto_adapter.h),
   [event_dma_adapter](@ref rte_event_dma_adapter.h),
+  [event_vector_adapter](@ref rte_event_vector_adapter.h),
   [rawdev](@ref rte_rawdev.h),
   [metrics](@ref rte_metrics.h),
   [bitrate](@ref rte_bitrate.h),
diff --git a/doc/guides/eventdevs/features/default.ini b/doc/guides/eventdevs/features/default.ini
index fa24ba38b4f4..9fb68f946ebc 100644
--- a/doc/guides/eventdevs/features/default.ini
+++ b/doc/guides/eventdevs/features/default.ini
@@ -64,3 +64,10 @@ internal_port_vchan_ev_bind =
 [Timer adapter Features]
 internal_port              =
 periodic                   =
+
+;
+; Features of a default Vector adapter
+;
+[Vector adapter Features]
+internal_port              =
+sov_eov                    =
diff --git a/doc/guides/prog_guide/eventdev/event_vector_adapter.rst b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
new file mode 100644
index 000000000000..3a3624526631
--- /dev/null
+++ b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
@@ -0,0 +1,221 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2025 Marvell International Ltd.
+
+Event Vector Adapter Library
+============================
+
+The Event Vector Adapter library extends the event-driven model by introducing
+a mechanism to aggregate multiple 8B objects (e.g., mbufs, u64s) into a single
+vector event and enqueue it to an event queue, there by reducing the scheduling
+latency while preserving the ingress order and offloading vector allocation and
+aggregation to the vector adapter.
+
+@see struct rte_event_vector::mbufs
+@see struct rte_event_vector::u64s
+@see struct rte_event_vector::ptrs
+
+The Event Vector Adapter library is designed to interface with hardware or
+software implementations of vector aggregation. It queries an eventdev PMD
+to determine the appropriate implementation and provides APIs to create,
+configure, and manage vector adapters.
+
+Use cases for the Event Vector Adapter include:
+  * Offload CPU event vector creation overhead i.e., vector allocation and
+    aggregation.
+  * Splitting existing vectors to classify objects into different event queues
+    based on their properties.
+
+Examples of using the API are presented in the `API Overview`_ and
+`Processing Vector Events`_ sections.
+
+.. _vector_event:
+
+Vector Event
+~~~~~~~~~~~~
+
+A vector event is enqueued in the event device when the vector adapter
+reaches the configured vector size or timeout. The event device uses the
+attributes configured by the application when scheduling it.
+
+Fallback Behavior
+~~~~~~~~~~~~~~~~~
+
+If the vector adapter cannot aggregate objects into a vector event, it
+enqueues the objects as single events with fallback event properties configured
+by the application.
+
+Timeout and Size
+~~~~~~~~~~~~~~~~
+
+The vector adapter aggregates objects until the configured vector size or
+timeout is reached. If the timeout is reached before the minimum vector size
+is met, the adapter enqueues the objects as single events with fallback event
+properties configured by the application.
+
+API Overview
+------------
+
+This section introduces the Event Vector Adapter API, showing how to create
+and configure a vector adapter and use it to manage vector events.
+
+From a high level, the setup steps are:
+
+* rte_event_vector_adapter_create()
+
+And to enqueue and manage vectors:
+
+* rte_event_vector_adapter_enqueue()
+* rte_event_vector_adapter_stats_get()
+
+Create and Configure a Vector Adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To create a vector adapter instance, initialize an ``rte_event_vector_adapter_conf``
+struct with the desired values, and pass it to ``rte_event_vector_adapter_create()``.
+
+.. code-block:: c
+
+	const struct rte_event_vector_adapter_conf adapter_config = {
+		.event_dev_id = event_dev_id,
+		.socket_id = rte_socket_id(),
+		.ev = {
+			.queue_id = event_queue_id,
+			.sched_type = RTE_SCHED_TYPE_ATOMIC,
+			.priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
+			.event_type = RTE_EVENT_TYPE_VECTOR | RTE_EVENT_TYPE_CPU,
+		},
+		.ev_fallback = {
+			.event_type = RTE_EVENT_TYPE_CPU,
+		},
+		.vector_sz = 64,
+		.vector_timeout_ns = 1000000, // 1ms
+		.vector_mp = vector_mempool,
+	};
+
+	struct rte_event_vector_adapter *adapter;
+	adapter = rte_event_vector_adapter_create(&adapter_config);
+
+	if (adapter == NULL) { ... }
+
+Before creating an instance of a vector adapter, the application should create
+and configure an event device along with its event ports. Based on the event
+device's capability, it might require creating an additional event port to be
+used by the vector adapter. If required, the ``rte_event_vector_adapter_create()``
+function will use a default method to configure an event port.
+
+If the application desires finer control of event port allocation and setup,
+it can use the ``rte_event_vector_adapter_create_ext()`` function. This function
+is passed a callback function that will be invoked if the adapter needs to
+create an event port, giving the application the opportunity to control how
+it is done.
+
+Retrieve Vector Adapter Contextual Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The vector adapter implementation may have constraints on vector size or
+timeout based on the given event device or system. The application can retrieve
+these constraints using ``rte_event_vector_adapter_info_get()``. This function
+returns an ``rte_event_vector_adapter_info`` struct, which contains the following
+members:
+
+* ``max_vector_adapters_per_event_queue`` - Maximum number of vector adapters
+  configurable per event queue.
+* ``min_vector_sz`` - Minimum vector size configurable.
+* ``max_vector_sz`` - Maximum vector size configurable.
+* ``min_vector_timeout_ns`` - Minimum vector timeout configurable.
+* ``max_vector_timeout_ns`` - Maximum vector timeout configurable.
+* ``log2_sz`` - Vector size should be a power of 2.
+
+Enqueuing Objects to the Vector Adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once a vector adapter has been created, the application can enqueue objects
+to it using ``rte_event_vector_adapter_enqueue()``. The adapter will aggregate
+the objects into a vector event based on the configured size and timeout.
+
+.. code-block:: c
+
+	uint64_t objs[32];
+	uint16_t num_elem = 32;
+	uint64_t flags = 0;
+
+	int ret = rte_event_vector_adapter_enqueue(adapter, objs, num_elem, flags);
+	if (ret < 0) { ... }
+
+The application can use the ``RTE_EVENT_VECTOR_ENQ_SOV`` and ``RTE_EVENT_VECTOR_ENQ_EOV``
+flags to control the start and end of vector aggregation if the vector adapter supports
+``RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV`` capability if not, then the flags will be ignored.
+
+The ``RTE_EVENT_VECTOR_ENQ_SOV`` flag marks the beginning of a vector and applies
+to the first pointer in the enqueue operation. Any incomplete vectors will be
+enqueued to the event device.
+
+The ``RTE_EVENT_VECTOR_ENQ_EOV`` flag marks the end of a vector and applies to
+the last pointer in the enqueue operation. The vector is enqueued to the event
+device even if the configured vector size is not reached.
+
+If both flags are set, the adapter will form a new vector event with the given
+objects and enqueue it to the event device.
+
+The ``RTE_EVENT_VECTOR_ENQ_FLUSH`` flag can be used to flush any remaining
+objects in the vector adapter. This is useful when the application needs to
+ensure that all objects are processed, even if the configured vector size or
+timeout is not reached. An enqueue call with this flag set will not handle any
+objects and will return 0.
+
+Processing Vector Events
+------------------------
+
+Once a vector event has been enqueued in the event device, the application will
+subsequently dequeue it from the event device. The application can process the
+vector event and its aggregated objects as needed:
+
+.. code-block:: c
+
+	void
+	event_processing_loop(...)
+	{
+		while (...) {
+			/* Receive events from the configured event port. */
+			rte_event_dequeue_burst(event_dev_id, event_port, &ev, 1, 0);
+			...
+			switch(ev.event_type) {
+				...
+				case RTE_EVENT_TYPE_VECTOR:
+					process_vector_event(ev);
+					...
+					break;
+			}
+		}
+	}
+
+	void
+	process_vector_event(struct rte_event ev)
+	{
+		struct rte_event_vector *vector = ev.event_ptr;
+		for (uint16_t i = 0; i < vector->nb_elem; i++) {
+			uint64_t obj = vector->u64s[i];
+			/* Process each object in the vector. */
+			...
+		}
+	}
+
+Statistics and Cleanup
+----------------------
+
+The application can retrieve statistics for the vector adapter using
+``rte_event_vector_adapter_stats_get()``:
+
+.. code-block:: c
+
+	struct rte_event_vector_adapter_stats stats;
+	rte_event_vector_adapter_stats_get(adapter, &stats);
+
+	printf("Vectors created: %" PRIu64 "\n", stats.vectorized);
+	printf("Timeouts occurred: %" PRIu64 "\n", stats.vectors_timedout);
+
+To reset the statistics, use ``rte_event_vector_adapter_stats_reset()``.
+
+To destroy the vector adapter and release its resources, use
+``rte_event_vector_adapter_destroy()``. The destroy function will
+flush any remaining events in the vector adapter before destroying it.
diff --git a/doc/guides/prog_guide/eventdev/eventdev.rst b/doc/guides/prog_guide/eventdev/eventdev.rst
index 8bb72da908ba..5e49db89830e 100644
--- a/doc/guides/prog_guide/eventdev/eventdev.rst
+++ b/doc/guides/prog_guide/eventdev/eventdev.rst
@@ -424,8 +424,8 @@ eventdev.
 .. Note::
 
          EventDev needs to be started before starting the event producers such
-         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter and
-         event_dma_adapter.
+         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter,
+         event_dma_adapter and event_vector_adapter.
 
 Ingress of New Events
 ~~~~~~~~~~~~~~~~~~~~~
@@ -561,9 +561,9 @@ using ``rte_event_dev_stop_flush_callback_register()`` function.
 .. Note::
 
         The event producers such as ``event_eth_rx_adapter``,
-        ``event_timer_adapter``, ``event_crypto_adapter`` and
-        ``event_dma_adapter`` need to be stopped before stopping
-        the event device.
+        ``event_timer_adapter``, ``event_crypto_adapter``,
+        ``event_dma_adapter`` and ``event_vector_adapter``
+        need to be stopped before stopping the event device.
 
 Summary
 -------
diff --git a/doc/guides/prog_guide/eventdev/index.rst b/doc/guides/prog_guide/eventdev/index.rst
index 2e1940ce760a..af11a57e71c8 100644
--- a/doc/guides/prog_guide/eventdev/index.rst
+++ b/doc/guides/prog_guide/eventdev/index.rst
@@ -14,3 +14,4 @@ Event Device Library
     event_crypto_adapter
     event_dma_adapter
     dispatcher_lib
+    event_vector_adapter
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de0f..5bd299e7a3c7 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -78,6 +78,19 @@ New Features
 
   See the :doc:`../cryptodevs/zsda` guide for more details on the new driver.
 
+* **Added eventdev vector adapter.**
+
+  Added the Event vector Adapter Library. This library extends the event-based
+  model by introducing APIs that allow applications to offload creation of
+  event vectors, thereby reducing the scheduling latency.
+
+  Use cases for the Event Vector Adapter include:
+    * Offload CPU event vector creation overhead i.e., vector allocation and
+      aggregation.
+    * Splitting existing vectors to classify objects into different event queues
+      based on their properties.
+
+
 
 Removed Items
 -------------
diff --git a/lib/eventdev/event_vector_adapter_pmd.h b/lib/eventdev/event_vector_adapter_pmd.h
new file mode 100644
index 000000000000..667363c496ff
--- /dev/null
+++ b/lib/eventdev/event_vector_adapter_pmd.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+#ifndef __EVENT_VECTOR_ADAPTER_PMD_H__
+#define __EVENT_VECTOR_ADAPTER_PMD_H__
+/**
+ * @file
+ * RTE Event Vector Adapter API (PMD Side)
+ *
+ * @note
+ * This file provides implementation helpers for internal use by PMDs.  They
+ * are not intended to be exposed to applications and are not subject to ABI
+ * versioning.
+ */
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+typedef int (*rte_event_vector_adapter_create_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation setup */
+typedef int (*rte_event_vector_adapter_destroy_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation teardown */
+typedef int (*rte_event_vector_adapter_stats_get_t)(const struct rte_event_vector_adapter *adapter,
+						    struct rte_event_vector_adapter_stats *stats);
+/**< @internal Get statistics for event vector adapter */
+typedef int (*rte_event_vector_adapter_stats_reset_t)(
+	const struct rte_event_vector_adapter *adapter);
+/**< @internal Reset statistics for event vector adapter */
+
+/**
+ * @internal Structure containing the functions exported by an event vector
+ * adapter implementation.
+ */
+struct event_vector_adapter_ops {
+	rte_event_vector_adapter_create_t create;
+	/**< Set up adapter */
+	rte_event_vector_adapter_destroy_t destroy;
+	/**< Tear down adapter */
+	rte_event_vector_adapter_stats_get_t stats_get;
+	/**< Get adapter statistics */
+	rte_event_vector_adapter_stats_reset_t stats_reset;
+	/**< Reset adapter statistics */
+
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Enqueue objects into the event vector adapter */
+};
+/**
+ * @internal Adapter data; structure to be placed in shared memory to be
+ * accessible by various processes in a multi-process configuration.
+ */
+struct __rte_cache_aligned rte_event_vector_adapter_data {
+	uint32_t id;
+	/**< Event vector adapter ID */
+	uint8_t event_dev_id;
+	/**< Event device ID */
+	uint32_t socket_id;
+	/**< Socket ID where memory is allocated */
+	uint8_t event_port_id;
+	/**< Optional: event port ID used when the inbuilt port is absent */
+	const struct rte_memzone *mz;
+	/**< Event vector adapter memzone pointer */
+	struct rte_event_vector_adapter_conf conf;
+	/**< Configuration used to configure the adapter. */
+	uint32_t caps;
+	/**< Adapter capabilities */
+	void *adapter_priv;
+	/**< Vector adapter private data*/
+	uint8_t service_inited;
+	/**< Service initialization state */
+	uint32_t unified_service_id;
+	/**< Unified Service ID*/
+};
+
+static inline int
+dummy_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
+			     uint16_t num_events, uint64_t flags)
+{
+	RTE_SET_USED(adapter);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(num_events);
+	RTE_SET_USED(flags);
+	return 0;
+}
+
+#endif /* __EVENT_VECTOR_ADAPTER_PMD_H__ */
diff --git a/lib/eventdev/eventdev_pmd.h b/lib/eventdev/eventdev_pmd.h
index ad13ba5b030a..d03461316b8a 100644
--- a/lib/eventdev/eventdev_pmd.h
+++ b/lib/eventdev/eventdev_pmd.h
@@ -26,6 +26,7 @@
 
 #include "event_timer_adapter_pmd.h"
 #include "rte_event_eth_rx_adapter.h"
+#include "rte_event_vector_adapter.h"
 #include "rte_eventdev.h"
 
 #ifdef __cplusplus
@@ -1555,6 +1556,36 @@ typedef int (*eventdev_dma_adapter_stats_get)(const struct rte_eventdev *dev,
 typedef int (*eventdev_dma_adapter_stats_reset)(const struct rte_eventdev *dev,
 						const int16_t dma_dev_id);
 
+/**
+ * Event device vector adapter capabilities.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param caps
+ *   Vector adapter capabilities
+ * @param ops
+ *   Vector adapter ops
+ *
+ * @return
+ *   Return 0 on success.
+ *
+ */
+typedef int (*eventdev_vector_adapter_caps_get_t)(const struct rte_eventdev *dev, uint32_t *caps,
+						  const struct event_vector_adapter_ops **ops);
+
+/**
+ * Event device vector adapter info.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param info
+ *   Vector adapter info
+ *
+ * @return
+ *   Return 0 on success.
+ */
+typedef int (*eventdev_vector_adapter_info_get_t)(const struct rte_eventdev *dev,
+						  struct rte_event_vector_adapter_info *info);
 
 /** Event device operations function pointer table */
 struct eventdev_ops {
@@ -1697,6 +1728,11 @@ struct eventdev_ops {
 	eventdev_dma_adapter_stats_reset dma_adapter_stats_reset;
 	/**< Reset DMA stats */
 
+	eventdev_vector_adapter_caps_get_t vector_adapter_caps_get;
+	/**< Get vector adapter capabilities */
+	eventdev_vector_adapter_info_get_t vector_adapter_info_get;
+	/**< Get vector adapter info */
+
 	eventdev_selftest dev_selftest;
 	/**< Start eventdev Selftest */
 
diff --git a/lib/eventdev/meson.build b/lib/eventdev/meson.build
index 71dea91727c1..0797c145e70c 100644
--- a/lib/eventdev/meson.build
+++ b/lib/eventdev/meson.build
@@ -18,6 +18,7 @@ sources = files(
         'rte_event_eth_tx_adapter.c',
         'rte_event_ring.c',
         'rte_event_timer_adapter.c',
+        'rte_event_vector_adapter.c',
         'rte_eventdev.c',
 )
 headers = files(
@@ -27,6 +28,7 @@ headers = files(
         'rte_event_eth_tx_adapter.h',
         'rte_event_ring.h',
         'rte_event_timer_adapter.h',
+        'rte_event_vector_adapter.h',
         'rte_eventdev.h',
         'rte_eventdev_trace_fp.h',
 )
@@ -38,6 +40,7 @@ driver_sdk_headers += files(
         'eventdev_pmd_pci.h',
         'eventdev_pmd_vdev.h',
         'event_timer_adapter_pmd.h',
+        'event_vector_adapter_pmd.h',
 )
 
 deps += ['ring', 'ethdev', 'hash', 'mempool', 'mbuf', 'timer', 'cryptodev', 'dmadev']
diff --git a/lib/eventdev/rte_event_vector_adapter.c b/lib/eventdev/rte_event_vector_adapter.c
new file mode 100644
index 000000000000..ff6bc43b1773
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.c
@@ -0,0 +1,472 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mcslock.h>
+#include <rte_service_component.h>
+#include <rte_tailq.h>
+
+#include <eal_export.h>
+
+#include "event_vector_adapter_pmd.h"
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+#define ADAPTER_ID(dev_id, queue_id, adapter_id)                                                   \
+	((uint32_t)dev_id << 16 | (uint32_t)queue_id << 8 | (uint32_t)adapter_id)
+#define DEV_ID_FROM_ADAPTER_ID(adapter_id)     ((adapter_id >> 16) & 0xFF)
+#define QUEUE_ID_FROM_ADAPTER_ID(adapter_id)   ((adapter_id >> 8) & 0xFF)
+#define ADAPTER_ID_FROM_ADAPTER_ID(adapter_id) (adapter_id & 0xFF)
+
+#define MZ_NAME_MAX_LEN	    64
+#define DATA_MZ_NAME_FORMAT "vector_adapter_data_%d_%d_%d"
+
+RTE_LOG_REGISTER_SUFFIX(ev_vector_logtype, adapter.vector, NOTICE);
+#define RTE_LOGTYPE_EVVEC ev_vector_logtype
+
+struct rte_event_vector_adapter *adapters[RTE_EVENT_MAX_DEVS][RTE_EVENT_MAX_QUEUES_PER_DEV];
+
+#define EVVEC_LOG(level, logtype, ...)                                                             \
+	RTE_LOG_LINE_PREFIX(level, logtype,                                                        \
+			    "EVVEC: %s() line %u: ", __func__ RTE_LOG_COMMA __LINE__, __VA_ARGS__)
+#define EVVEC_LOG_ERR(...) EVVEC_LOG(ERR, EVVEC, __VA_ARGS__)
+
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+#define EVVEC_LOG_DBG(...) EVVEC_LOG(DEBUG, EVVEC, __VA_ARGS__)
+#else
+#define EVVEC_LOG_DBG(...) /* No debug logging */
+#endif
+
+#define PTR_VALID_OR_ERR_RET(ptr, retval)                                                          \
+	do {                                                                                       \
+		if (ptr == NULL) {                                                                 \
+			rte_errno = EINVAL;                                                        \
+			return retval;                                                             \
+		}                                                                                  \
+	} while (0)
+
+static int
+validate_conf(const struct rte_event_vector_adapter_conf *conf,
+	      struct rte_event_vector_adapter_info *info)
+{
+	int rc = -EINVAL;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, rc);
+
+	if (conf->vector_sz < info->min_vector_sz || conf->vector_sz > info->max_vector_sz) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be between %u and %u",
+			      conf->vector_sz, info->min_vector_sz, info->max_vector_sz);
+		return rc;
+	}
+
+	if (conf->vector_timeout_ns < info->min_vector_timeout_ns ||
+	    conf->vector_timeout_ns > info->max_vector_timeout_ns) {
+		EVVEC_LOG_DBG("invalid vector timeout %" PRIu64 ", should be between %" PRIu64
+			      " and %" PRIu64,
+			      conf->vector_timeout_ns, info->min_vector_timeout_ns,
+			      info->max_vector_timeout_ns);
+		return rc;
+	}
+
+	if (conf->vector_mp == NULL) {
+		EVVEC_LOG_DBG("invalid mempool for vector adapter");
+		return rc;
+	}
+
+	if (info->log2_sz && rte_is_power_of_2(conf->vector_sz) != 0) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be a power of 2", conf->vector_sz);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+default_port_conf_cb(uint8_t event_dev_id, uint8_t *event_port_id, void *conf_arg)
+{
+	struct rte_event_port_conf *port_conf, def_port_conf = {0};
+	struct rte_event_dev_config dev_conf;
+	struct rte_eventdev *dev;
+	uint8_t port_id;
+	uint8_t dev_id;
+	int started;
+	int ret;
+
+	dev = &rte_eventdevs[event_dev_id];
+	dev_id = dev->data->dev_id;
+	dev_conf = dev->data->dev_conf;
+
+	started = dev->data->dev_started;
+	if (started)
+		rte_event_dev_stop(dev_id);
+
+	port_id = dev_conf.nb_event_ports;
+	if (conf_arg != NULL)
+		port_conf = conf_arg;
+	else {
+		port_conf = &def_port_conf;
+		ret = rte_event_port_default_conf_get(dev_id, (port_id - 1), port_conf);
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_conf.nb_event_ports += 1;
+	if (port_conf->event_port_cfg & RTE_EVENT_PORT_CFG_SINGLE_LINK)
+		dev_conf.nb_single_link_event_port_queues += 1;
+
+	ret = rte_event_dev_configure(dev_id, &dev_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to configure event dev %u", dev_id);
+		if (started)
+			if (rte_event_dev_start(dev_id))
+				return -EIO;
+
+		return ret;
+	}
+
+	ret = rte_event_port_setup(dev_id, port_id, port_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to setup event port %u on event dev %u", port_id, dev_id);
+		return ret;
+	}
+
+	*event_port_id = port_id;
+
+	if (started)
+		ret = rte_event_dev_start(dev_id);
+
+	return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf)
+{
+	return rte_event_vector_adapter_create_ext(conf, default_port_conf_cb, NULL);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create_ext, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb, void *conf_arg)
+{
+	struct rte_event_vector_adapter *adapter = NULL;
+	struct rte_event_vector_adapter_info info;
+	char mz_name[MZ_NAME_MAX_LEN];
+	const struct rte_memzone *mz;
+	struct rte_eventdev *dev;
+	uint32_t caps;
+	int i, n, rc;
+
+	PTR_VALID_OR_ERR_RET(conf, NULL);
+
+	if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+		adapters[conf->event_dev_id][conf->ev.queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[conf->event_dev_id][conf->ev.queue_id][i].used == false) {
+			adapter = &adapters[conf->event_dev_id][conf->ev.queue_id][i];
+			adapter->adapter_id = ADAPTER_ID(conf->event_dev_id, conf->ev.queue_id, i);
+			adapter->used = true;
+			break;
+		}
+		EVVEC_LOG_DBG("adapter %u is already in use", i);
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	if (adapter == NULL) {
+		EVVEC_LOG_DBG("no available vector adapters");
+		rte_errno = ENODEV;
+		return NULL;
+	}
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, NULL);
+
+	dev = &rte_eventdevs[conf->event_dev_id];
+	if (dev->dev_ops->vector_adapter_caps_get != NULL &&
+	    dev->dev_ops->vector_adapter_info_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &caps, &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+
+		rc = dev->dev_ops->vector_adapter_info_get(dev, &info);
+		if (rc < 0) {
+			adapter->ops = NULL;
+			EVVEC_LOG_DBG("failed to get vector adapter info rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+	}
+
+	if (conf->ev.sched_type != dev->data->queues_cfg[conf->ev.queue_id].schedule_type &&
+	    !(dev->data->event_dev_cap & RTE_EVENT_DEV_CAP_QUEUE_ALL_TYPES)) {
+		EVVEC_LOG_DBG("invalid event schedule type, eventdev doesn't support all types");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	rc = validate_conf(conf, &info);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	n = snprintf(mz_name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, conf->event_dev_id,
+		     conf->ev.queue_id, adapter->adapter_id);
+	if (n >= (int)sizeof(mz_name)) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create memzone name");
+		rte_errno = EINVAL;
+		goto error;
+	}
+	mz = rte_memzone_reserve(mz_name, sizeof(struct rte_event_vector_adapter_data),
+				 conf->socket_id, 0);
+	if (mz == NULL) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to reserve memzone for vector adapter");
+		rte_errno = ENOMEM;
+		goto error;
+	}
+
+	adapter->data = mz->addr;
+	memset(adapter->data, 0, sizeof(struct rte_event_vector_adapter_data));
+
+	adapter->data->mz = mz;
+	adapter->data->event_dev_id = conf->event_dev_id;
+	adapter->data->id = adapter->adapter_id;
+	adapter->data->socket_id = conf->socket_id;
+	adapter->data->conf = *conf;
+
+	if (!(caps & RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT)) {
+		if (conf_cb == NULL) {
+			EVVEC_LOG_DBG("port config callback is NULL");
+			rte_errno = EINVAL;
+			goto error;
+		}
+
+		rc = conf_cb(conf->event_dev_id, &adapter->data->event_port_id, conf_arg);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to create port for vector adapter");
+			rte_errno = EINVAL;
+			goto error;
+		}
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->create, NULL);
+
+	rc = adapter->ops->create(adapter);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create vector adapter");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+
+	return adapter;
+
+error:
+	adapter->used = false;
+	return NULL;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_lookup, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id)
+{
+	uint8_t adapter_idx = ADAPTER_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t queue_id = QUEUE_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t dev_id = DEV_ID_FROM_ADAPTER_ID(adapter_id);
+	struct rte_event_vector_adapter *adapter;
+	const struct rte_memzone *mz;
+	char name[MZ_NAME_MAX_LEN];
+	struct rte_eventdev *dev;
+	int rc;
+
+	if (dev_id >= RTE_EVENT_MAX_DEVS || queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV ||
+	    adapter_idx >= RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE) {
+		EVVEC_LOG_ERR("invalid adapter id %u", adapter_id);
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (adapters[dev_id][queue_id] == NULL) {
+		adapters[dev_id][queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[dev_id][queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	if (adapters[dev_id][queue_id][adapter_idx].used == true)
+		return &adapters[dev_id][queue_id][adapter_idx];
+
+	adapter = &adapters[dev_id][queue_id][adapter_idx];
+
+	snprintf(name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, dev_id, queue_id, adapter_idx);
+	mz = rte_memzone_lookup(name);
+	if (mz == NULL) {
+		EVVEC_LOG_DBG("failed to lookup memzone for vector adapter");
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	adapter->data = mz->addr;
+	dev = &rte_eventdevs[dev_id];
+
+	if (dev->dev_ops->vector_adapter_caps_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &adapter->data->caps,
+							   &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities");
+			rte_errno = ENOTSUP;
+			return NULL;
+		}
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+	adapter->adapter_id = adapter_id;
+	adapter->used = true;
+
+	return adapter;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_service_id_get, 25.07)
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	if (adapter->data->service_inited && service_id != NULL)
+		*service_id = adapter->data->unified_service_id;
+
+	return adapter->data->service_inited ? 0 : -ESRCH;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_destroy, 25.07)
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter)
+{
+	int rc;
+
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	if (adapter->used == false) {
+		EVVEC_LOG_ERR("event vector adapter is not allocated");
+		return -EINVAL;
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->destroy, -ENOTSUP);
+
+	rc = adapter->ops->destroy(adapter);
+	if (rc < 0) {
+		EVVEC_LOG_DBG("failed to destroy vector adapter");
+		return rc;
+	}
+
+	rte_memzone_free(adapter->data->mz);
+	adapter->ops = NULL;
+	adapter->enqueue = dummy_vector_adapter_enqueue;
+	adapter->data = NULL;
+	adapter->used = false;
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_info_get, 25.07)
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id, struct rte_event_vector_adapter_info *info)
+{
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, -EINVAL);
+	PTR_VALID_OR_ERR_RET(info, -EINVAL);
+
+	struct rte_eventdev *dev = &rte_eventdevs[event_dev_id];
+	if (dev->dev_ops->vector_adapter_info_get != NULL)
+		return dev->dev_ops->vector_adapter_info_get(dev, info);
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_conf_get, 25.07)
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(conf, -EINVAL);
+
+	*conf = adapter->data->conf;
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_remaining, 25.07)
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id)
+{
+	uint8_t remaining = 0;
+	int i;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, 0);
+
+	if (event_queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV)
+		return 0;
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[event_dev_id][event_queue_id][i].used == false)
+			remaining++;
+	}
+
+	return remaining;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_get, 25.07)
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(stats, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_get, -ENOTSUP);
+
+	adapter->ops->stats_get(adapter, stats);
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_reset, 25.07)
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_reset, -ENOTSUP);
+
+	adapter->ops->stats_reset(adapter);
+
+	return 0;
+}
diff --git a/lib/eventdev/rte_event_vector_adapter.h b/lib/eventdev/rte_event_vector_adapter.h
new file mode 100644
index 000000000000..6b69f7ba3e48
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.h
@@ -0,0 +1,483 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __RTE_EVENT_VECTOR_ADAPTER_H__
+#define __RTE_EVENT_VECTOR_ADAPTER_H__
+
+/**
+ * @file rte_event_vector_adapter.h
+ *
+ * @warning
+ * @b EXPERIMENTAL:
+ * All functions in this file may be changed or removed without prior notice.
+ *
+ * Event vector adapter API.
+ *
+ * An event vector adapter has the following working model:
+ *
+ *         ┌──────────┐
+ *         │  Vector  ├─┐
+ *         │ adapter0 │ │
+ *         └──────────┘ │
+ *         ┌──────────┐ │   ┌──────────┐
+ *         │  Vector  ├─┼──►│  Event   │
+ *         │ adapter1 │ │   │  Queue0  │
+ *         └──────────┘ │   └──────────┘
+ *         ┌──────────┐ │
+ *         │  Vector  ├─┘
+ *         │ adapter2 │
+ *         └──────────┘
+ *
+ *         ┌──────────┐
+ *         │  Vector  ├─┐
+ *         │ adapter0 │ │   ┌──────────┐
+ *         └──────────┘ ├──►│  Event   │
+ *         ┌──────────┐ │   │  Queue1  │
+ *         │  Vector  ├─┘   └──────────┘
+ *         │ adapter1 │
+ *         └──────────┘
+ *
+ * - A vector adapter can be seen as an extension to event queue. It helps in
+ *   aggregating objects and generating a vector event which is enqueued to the
+ *   event queue.
+ *
+ * - Multiple vector adapters can be created on an event queue, each with its
+ *   own unique properties such as event properties, vector size, and timeout.
+ *   Note: If the target event queue doesn't support RTE_EVENT_QUEUE_CFG_ALL_TYPES,
+ *         then the vector adapter should use the same schedule type as the event
+ *         queue.
+ *
+ * - Each vector adapter aggregates 8B objects, generates a vector event and
+ *   enqueues it to the event queue with the event properties mentioned in
+ *   rte_event_vector_adapter_conf::ev.
+ *
+ * - After configuring the vector adapter, Application needs to use the
+ *   rte_event_vector_adapter_enqueue() function to enqueue objects i.e.,
+ *   mbufs/ptrs/u64s to the vector adapter.
+ *   On reaching the configured vector size or timeout, the vector adapter
+ *   enqueues the event vector to the event queue.
+ *   Note: Application should use the event_type and sub_event_type properly
+ *         identifying the contents of vector event on dequeue.
+ *
+ * - If the vector adapter advertises the RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ *   capability, application can use the RTE_EVENT_VECTOR_ENQ_[S|E]OV flags
+ *   to indicate the start and end of a vector event.
+ *   * When RTE_EVENT_VECTOR_ENQ_SOV is set, the vector adapter will flush any
+ *     aggregation in-progress and start aggregating a new vector event with
+ *     the enqueued objects.
+ *   * When RTE_EVENT_VECTOR_ENQ_EOV is set, the vector adapter will add the
+ *     objects enqueued to the in-progress aggregation and enqueue the vector
+ *     event to the event queue, even if configured vector size or timeout is
+ *     not reached.
+ *   * If both flags are set, the vector adapter will flush any aggregation in
+ *     progress and enqueue the objects as a new vector event to the event
+ *     queue.
+ *
+ * - If the vector adapter reaches the configured vector size, it will enqueue
+ *   the aggregated vector event to the event queue.
+ *
+ * - If the vector adapter reaches the configured vector timeout, it will flush
+ *   the aggregated objects as a vector event if the minimum vector size is
+ *   reached, if not it will enqueue the objs as single events to the event
+ *   queue.
+ *
+ * - If the vector adapter is unable to aggregate the objs into a vector event,
+ *   it will enqueue the objs as single events to the event queue with the event
+ *   properties mentioned in rte_event_vector_adapter_conf::ev_fallback.
+ *
+ * Before using the vector adapter, the application has to create and configure
+ * an event device and based on the event device capability it might require
+ * creating an additional event port.
+ *
+ * When the application creates the vector adapter using the
+ * ``rte_event_vector_adapter_create()`` function, the event device driver
+ * capabilities are checked. If an in-built port is absent, the application
+ * uses the default function to create a new event port.
+ * For finer control over event port creation, the application should use
+ * the ``rte_event_vector_adapter_create_ext()`` function.
+ *
+ * The application can enqueue one or more objs to the vector adapter using the
+ * ``rte_event_vector_adapter_enqueue()`` function and control the aggregation
+ * using the flags.
+ *
+ * Vector adapters report stats using the ``rte_event_vector_adapter_stats_get()``
+ * function and reset the stats using the ``rte_event_vector_adapter_stats_reset()``.
+ *
+ * The application can destroy the vector adapter using the
+ * ``rte_event_vector_adapter_destroy()`` function.
+ *
+ */
+
+#include <rte_eventdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV RTE_BIT64(0)
+/**< Vector adapter supports Start of Vector (SOV) and End of Vector (EOV) flags
+ *  in the enqueue flags.
+ *
+ * @see RTE_EVENT_VECTOR_ENQ_SOV
+ * @see RTE_EVENT_VECTOR_ENQ_EOV
+ */
+
+#define RTE_EVENT_VECTOR_ENQ_SOV RTE_BIT64(0)
+/**< Indicates the start of a vector event. When enqueue is called with
+ *  RTE_EVENT_VECTOR_ENQ_SOV, the vector adapter will flush any vector
+ *  aggregation in progress and start aggregating a new vector event with
+ *  the enqueued objects.
+ *  @see RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ */
+#define RTE_EVENT_VECTOR_ENQ_EOV RTE_BIT64(1)
+/**< Indicates the end of a vector event. When enqueue is called with
+ *  RTE_EVENT_VECTOR_ENQ_EOV, the vector adapter will add the objects
+ *  to any inprogress aggregation and flush the event vector.
+ *  @see RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ */
+#define RTE_EVENT_VECTOR_ENQ_FLUSH RTE_BIT64(2)
+/**< Flush any in-progress vector aggregation. */
+
+/**
+ * Vector adapter configuration structure
+ */
+struct rte_event_vector_adapter_conf {
+	uint8_t event_dev_id;
+	/**< Event device identifier */
+	uint32_t socket_id;
+	/**< Identifier of socket from which to allocate memory for adapter */
+	struct rte_event ev;
+	/**<
+	 *  The values from the following event fields will be used when
+	 *  queuing work:
+	 *   - queue_id: Targeted event queue ID for vector event.
+	 *   - event_priority: Event priority of the vector event in
+	 *                     the event queue relative to other events.
+	 *   - sched_type: Scheduling type for events from this vector adapter.
+	 *   - event_type: Event type for the vector event.
+	 *   - sub_event_type: Sub event type for the vector event.
+	 *   - flow_id: Flow ID for the vectors enqueued to the event queue by
+	 *              the vector adapter.
+	 */
+	struct rte_event ev_fallback;
+	/**<
+	 * The values from the following event fields will be used when
+	 * aggregation fails and single event is enqueued:
+	 *   - event_type: Event type for the single event.
+	 *   - sub_event_type: Sub event type for the single event.
+	 *   - flow_id: Flow ID for the single event.
+	 *
+	 * Other fields are taken from rte_event_vector_adapter_conf::ev.
+	 */
+	uint16_t vector_sz;
+	/**<
+	 * Indicates the maximum number for enqueued work to combine and form a vector.
+	 * Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_sz
+	 * @see rte_event_vector_adapter_info::max_vector_sz
+	 */
+	uint64_t vector_timeout_ns;
+	/**<
+	 * Indicates the maximum number of nanoseconds to wait for receiving
+	 * work. Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_ns
+	 * @see rte_event_vector_adapter_info::max_vector_ns
+	 */
+	struct rte_mempool *vector_mp;
+	/**<
+	 * Indicates the mempool that should be used for allocating
+	 * rte_event_vector container.
+	 * @see rte_event_vector_pool_create
+	 */
+};
+
+/**
+ * Vector adapter vector info structure
+ */
+struct rte_event_vector_adapter_info {
+	uint8_t max_vector_adapters_per_event_queue;
+	/**< Maximum number of vector adapters configurable */
+	uint16_t min_vector_sz;
+	/**< Minimum vector size configurable */
+	uint16_t max_vector_sz;
+	/**< Maximum vector size configurable */
+	uint64_t min_vector_timeout_ns;
+	/**< Minimum vector timeout configurable */
+	uint64_t max_vector_timeout_ns;
+	/**< Maximum vector timeout configurable */
+	uint8_t log2_sz;
+	/**< True if the size configured should be in log2. */
+};
+
+/**
+ * Vector adapter statistics structure
+ */
+struct rte_event_vector_adapter_stats {
+	uint64_t vectorized;
+	/**< Number of events vectorized */
+	uint64_t vectors_timedout;
+	/**< Number of timeouts occurred */
+	uint64_t vectors_flushed;
+	/**< Number of vectors flushed */
+	uint64_t alloc_failures;
+	/**< Number of vector allocation failures */
+};
+
+struct rte_event_vector_adapter;
+
+typedef int (*rte_event_vector_adapter_enqueue_t)(struct rte_event_vector_adapter *adapter,
+						  uint64_t objs[], uint16_t num_elem,
+						  uint64_t flags);
+/**< @internal Enqueue objs into the event vector adapter. */
+
+struct __rte_cache_aligned rte_event_vector_adapter {
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Pointer to driver enqueue function. */
+	struct rte_event_vector_adapter_data *data;
+	/**< Pointer to the adapter data */
+	const struct event_vector_adapter_ops *ops;
+	/**< Functions exported by adapter driver */
+
+	uint32_t adapter_id;
+	/**< Identifier of the adapter instance. */
+	uint8_t used : 1;
+	/**< Flag to indicate that this adapter is being used. */
+};
+
+/**
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_vector_adapter_port_conf_cb_t)(uint8_t event_dev_id, uint8_t *event_port_id,
+						       void *conf_arg);
+
+/**
+ * Create an event vector adapter.
+ *
+ * This function creates an event vector adapter based on the provided
+ * configuration. The adapter can be used to combine multiple mbufs/ptrs/u64s
+ * into a single vector event, i.e., rte_event_vector, which is then enqueued
+ * to the event queue provided.
+ * @see rte_event_vector_adapter_conf::ev::event_queue_id.
+ *
+ * @param conf
+ *   Configuration for the event vector adapter.
+ * @return
+ *   - Pointer to the created event vector adapter on success.
+ *   - NULL on failure with rte_errno set to the error code.
+ *     Possible rte_errno values include:
+ *    - EINVAL: Invalid event device identifier specified in config.
+ *    - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *    - ENOSPC: Maximum number of adapters already created.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Create an event vector adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the event
+ * vector adapter creation. If a built-in port is absent, then the function uses
+ * the callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ *   The event vector adapter configuration structure.
+ * @param conf_cb
+ *   The port config callback function.
+ * @param conf_arg
+ *   Opaque pointer to the argument for the callback function.
+ * @return
+ *   - Pointer to the new allocated event vector adapter on success.
+ *   - NULL on error with rte_errno set appropriately.
+ *   Possible rte_errno values include:
+ *   - ERANGE: vector_timeout_ns is not in supported range.
+ *   - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *   - EINVAL: Invalid event device identifier specified in config.
+ *   - ENOSPC: Maximum number of adapters already created.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb,
+				    void *conf_arg);
+
+/**
+ * Lookup an event vector adapter using its identifier.
+ *
+ * This function returns the event vector adapter based on the adapter_id.
+ * This is useful when the adapter is created in another process and the
+ * application wants to use the adapter in the current process.
+ *
+ * @param adapter_id
+ *   Identifier of the event vector adapter to look up.
+ * @return
+ *   - Pointer to the event vector adapter on success.
+ *   - NULL if the adapter is not found.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id);
+
+/**
+ * Destroy an event vector adapter.
+ *
+ * This function releases the resources associated with the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter to be destroyed.
+ * @return
+ *   - 0 on success.
+ *   - Negative value on failure with rte_errno set to the error code.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Get the vector info of an event vector adapter.
+ *
+ * This function retrieves the vector info of the event vector adapter.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param info
+ *   Pointer to the structure where the vector info will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ *   - EINVAL if the event device identifier is invalid.
+ *   - ENOTSUP if the event device does not support vector adapters.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id,
+				  struct rte_event_vector_adapter_info *info);
+
+/**
+ * Get the configuration of an event vector adapter.
+ *
+ * This function retrieves the configuration of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param conf
+ *   Pointer to the structure where the configuration will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Get the remaining event vector adapters.
+ *
+ * This function retrieves the number of remaining event vector adapters
+ * available for a given event device and event queue.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param event_queue_id
+ *   Event queue identifier.
+ * @return
+ *   Number of remaining slots available for enqueuing events.
+ */
+__rte_experimental
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id);
+
+/**
+ * Get the event vector adapter statistics.
+ *
+ * This function retrieves the statistics of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param stats
+ *   Pointer to the structure where the statistics will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats);
+
+/**
+ * @brief Reset the event vector adapter statistics.
+ *
+ * This function resets the statistics of the event vector adapter to their default values.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter whose statistics are to be reset.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Retrieve the service ID of the event vector adapter. If the adapter doesn't
+ * use an rte_service function, this function returns -ESRCH.
+ *
+ * @param adapter
+ *   A pointer to an event vector adapter.
+ * @param [out] service_id
+ *   A pointer to a uint32_t, to be filled in with the service id.
+ *
+ * @return
+ *   - 0: Success
+ *   - <0: Error code on failure
+ *   - -ESRCH: the adapter does not require a service to operate
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id);
+
+/**
+ * Enqueue objs into the event vector adapter.
+ *
+ * This function enqueues a specified number of objs into the event vector adapter.
+ * The objs are combined into a single vector event, i.e., rte_event_vector, which
+ * is then enqueued to the event queue configured in the adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param objs
+ *   Array of objs to be enqueued.
+ * @param num_elem
+ *   Number of objs to be enqueued.
+ * @param flags
+ *   Flags to be used for the enqueue operation.
+ * @return
+ *   Number of objs enqueued on success.
+ */
+__rte_experimental
+static inline int
+rte_event_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
+				 uint16_t num_elem, uint64_t flags)
+{
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+	if (adapter == NULL) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+
+	if (adapter->used == false) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+#endif
+	return adapter->enqueue(adapter, objs, num_elem, flags);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTE_EVENT_VECTOR_ADAPTER_H__ */
diff --git a/lib/eventdev/rte_eventdev.c b/lib/eventdev/rte_eventdev.c
index b66cbb4676c6..916bad6c2c4e 100644
--- a/lib/eventdev/rte_eventdev.c
+++ b/lib/eventdev/rte_eventdev.c
@@ -257,6 +257,28 @@ rte_event_dma_adapter_caps_get(uint8_t dev_id, uint8_t dma_dev_id, uint32_t *cap
 	return 0;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_caps_get, 25.07)
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps)
+{
+	const struct event_vector_adapter_ops *ops;
+	struct rte_eventdev *dev;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(dev_id, -EINVAL);
+
+	dev = &rte_eventdevs[dev_id];
+
+	if (caps == NULL)
+		return -EINVAL;
+
+	if (dev->dev_ops->vector_adapter_caps_get == NULL)
+		*caps = 0;
+
+	return dev->dev_ops->vector_adapter_caps_get ?
+		       dev->dev_ops->vector_adapter_caps_get(dev, caps, &ops) :
+		       0;
+}
+
 static inline int
 event_dev_queue_config(struct rte_eventdev *dev, uint8_t nb_queues)
 {
diff --git a/lib/eventdev/rte_eventdev.h b/lib/eventdev/rte_eventdev.h
index 6400d6109f79..3c7fcbf0be02 100644
--- a/lib/eventdev/rte_eventdev.h
+++ b/lib/eventdev/rte_eventdev.h
@@ -1985,6 +1985,16 @@ int
 rte_event_eth_tx_adapter_caps_get(uint8_t dev_id, uint16_t eth_port_id,
 				uint32_t *caps);
 
+/* Vector adapter capability bitmap flags */
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT	0x1
+/**< This flag is set when the vector adapter is capable of generating events
+ * using an internal event port.
+ */
+
+__rte_experimental
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps);
+
 /**
  * Converts nanoseconds to *timeout_ticks* value for rte_event_dequeue_burst()
  *
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply	[relevance 1%]

* [PATCH v3 1/3] eventdev: introduce event vector adapter
  @ 2025-05-30 16:25  1%         ` pbhagavatula
  0 siblings, 0 replies; 153+ results
From: pbhagavatula @ 2025-05-30 16:25 UTC (permalink / raw)
  To: jerinj, pbhagavatula, abhinandan.gujjar, amitprakashs,
	s.v.naga.harish.k, sthotton, pravin.pathak, hemant.agrawal,
	sachin.saxena, mattias.ronnblom, liangma, peter.mccarthy,
	harry.van.haaren, anatoly.burakov, erik.g.carrillo,
	Thomas Monjalon, Bruce Richardson
  Cc: dev

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

The event vector adapter offloads creation of event vectors
i.e., aggregation of 8B objects (mbufs/ptrs/u64s) into a
rte_event_vector from the CPU thereby reducing the overhead
of allocating the vector and maintaining vector state.

Applications can create a vector adapter associated with
an event queue and enqueue objects to be vectorized.
When the vector reaches the configured size or when the timeout
is reached, the vector adapter will enqueue the vector to the
event queue.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Series-acked-by: Jerin Jacob <jerinj@marvell.com>
---
 MAINTAINERS                                   |   7 +
 config/rte_config.h                           |   1 +
 doc/api/doxy-api-index.md                     |   1 +
 doc/guides/eventdevs/features/default.ini     |   7 +
 .../eventdev/event_vector_adapter.rst         | 216 ++++++++
 doc/guides/prog_guide/eventdev/eventdev.rst   |  10 +-
 doc/guides/prog_guide/eventdev/index.rst      |   1 +
 doc/guides/rel_notes/release_25_07.rst        |  10 +
 lib/eventdev/event_vector_adapter_pmd.h       |  85 ++++
 lib/eventdev/eventdev_pmd.h                   |  36 ++
 lib/eventdev/meson.build                      |   3 +
 lib/eventdev/rte_event_vector_adapter.c       | 472 +++++++++++++++++
 lib/eventdev/rte_event_vector_adapter.h       | 477 ++++++++++++++++++
 lib/eventdev/rte_eventdev.c                   |  22 +
 lib/eventdev/rte_eventdev.h                   |  10 +
 15 files changed, 1353 insertions(+), 5 deletions(-)
 create mode 100644 doc/guides/prog_guide/eventdev/event_vector_adapter.rst
 create mode 100644 lib/eventdev/event_vector_adapter_pmd.h
 create mode 100644 lib/eventdev/rte_event_vector_adapter.c
 create mode 100644 lib/eventdev/rte_event_vector_adapter.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 3e16789250d4..4c7564f5d8b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -578,6 +578,13 @@ F: lib/eventdev/*dma_adapter*
 F: app/test/test_event_dma_adapter.c
 F: doc/guides/prog_guide/eventdev/event_dma_adapter.rst

+Eventdev Vector Adapter API - EXPERIMENTAL
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+T: git://dpdk.org/next/dpdk-next-eventdev
+F: lib/eventdev/*vector_adapter*
+F: app/test/test_event_vector_adapter.c
+F: doc/guides/prog_guide/eventdev/event_vector_adapter.rst
+
 Raw device API
 M: Sachin Saxena <sachin.saxena@oss.nxp.com>
 M: Hemant Agrawal <hemant.agrawal@nxp.com>
diff --git a/config/rte_config.h b/config/rte_config.h
index 86897de75e75..9535c48d8188 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -92,6 +92,7 @@
 #define RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE 32
 #define RTE_EVENT_DMA_ADAPTER_MAX_INSTANCE 32
+#define RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE 32

 /* rawdev defines */
 #define RTE_RAWDEV_MAX_DEVS 64
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb945..a11bd59526e7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -30,6 +30,7 @@ The public API headers are grouped by topics:
   [event_timer_adapter](@ref rte_event_timer_adapter.h),
   [event_crypto_adapter](@ref rte_event_crypto_adapter.h),
   [event_dma_adapter](@ref rte_event_dma_adapter.h),
+  [event_vector_adapter](@ref rte_event_vector_adapter.h),
   [rawdev](@ref rte_rawdev.h),
   [metrics](@ref rte_metrics.h),
   [bitrate](@ref rte_bitrate.h),
diff --git a/doc/guides/eventdevs/features/default.ini b/doc/guides/eventdevs/features/default.ini
index fa24ba38b4f4..9fb68f946ebc 100644
--- a/doc/guides/eventdevs/features/default.ini
+++ b/doc/guides/eventdevs/features/default.ini
@@ -64,3 +64,10 @@ internal_port_vchan_ev_bind =
 [Timer adapter Features]
 internal_port              =
 periodic                   =
+
+;
+; Features of a default Vector adapter
+;
+[Vector adapter Features]
+internal_port              =
+sov_eov                    =
diff --git a/doc/guides/prog_guide/eventdev/event_vector_adapter.rst b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
new file mode 100644
index 000000000000..e98fa6b77206
--- /dev/null
+++ b/doc/guides/prog_guide/eventdev/event_vector_adapter.rst
@@ -0,0 +1,216 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2025 Marvell International Ltd.
+
+Event Vector Adapter Library
+============================
+
+The Event Vector Adapter library enhances the event-driven model by enabling
+the CPU to offload the creation, aggregation and queuing of event vectors
+(to a configurable event queue) thereby reducing the CPU's workload related to
+vector allocation and aggregation.
+
+Event vectors are created by aggregating multiple 8B objects
+(e.g. ``rte_event_vector::mbufs``, ``rte_event_vector::u64s``) into a
+``rte_event_vector``. Currently, the Rx adapter and Crypto adapter support
+offloading vector creation and aggregation to the event device.
+
+The Event Vector Adapter library is designed to allow CPU to enqueue objects
+that have to be aggregated to underlying hardware or software implementation
+of vector aggregator. The library queries an eventdev PMD to determine the
+appropriate implementation and provides APIs to create, configure, and manage
+vector adapters.
+
+Examples of using the API are presented in the `API Overview`_ and
+`Processing Vector Events`_ sections.
+
+.. _vector_event:
+
+Vector Event
+~~~~~~~~~~~~
+
+A vector event is enqueued in the event device when the vector adapter
+reaches the configured vector size or timeout. The event device uses the
+attributes configured by the application when scheduling it.
+
+Fallback Behavior
+~~~~~~~~~~~~~~~~~
+
+If the vector adapter cannot aggregate objects into a vector event, it
+enqueues the objects as single events with fallback event properties configured
+by the application.
+
+Timeout and Size
+~~~~~~~~~~~~~~~~
+
+The vector adapter aggregates objects until the configured vector size or
+timeout is reached. If the timeout is reached before the minimum vector size
+is met, the adapter enqueues the objects as single events with fallback event
+properties configured by the application.
+
+API Overview
+------------
+
+This section introduces the Event Vector Adapter API, showing how to create
+and configure a vector adapter and use it to manage vector events.
+
+From a high level, the setup steps are:
+
+* ``rte_event_vector_adapter_create()``
+
+And to enqueue and manage vectors:
+
+* ``rte_event_vector_adapter_enqueue()``
+* ``rte_event_vector_adapter_stats_get()``
+
+Create and Configure a Vector Adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To create a vector adapter instance, initialize an ``rte_event_vector_adapter_conf``
+struct with the desired values, and pass it to ``rte_event_vector_adapter_create()``.
+
+.. code-block:: c
+
+	const struct rte_event_vector_adapter_conf adapter_config = {
+		.event_dev_id = event_dev_id,
+		.socket_id = rte_socket_id(),
+		.ev = {
+			.queue_id = event_queue_id,
+			.sched_type = RTE_SCHED_TYPE_ATOMIC,
+			.priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
+			.event_type = RTE_EVENT_TYPE_VECTOR | RTE_EVENT_TYPE_CPU,
+		},
+		.ev_fallback = {
+			.event_type = RTE_EVENT_TYPE_CPU,
+		},
+		.vector_sz = 64,
+		.vector_timeout_ns = 1000000, // 1ms
+		.vector_mp = vector_mempool,
+	};
+
+	struct rte_event_vector_adapter *adapter;
+	adapter = rte_event_vector_adapter_create(&adapter_config);
+
+	if (adapter == NULL) { ... }
+
+Before creating an instance of a vector adapter, the application should create
+and configure an event device along with its event ports. Based on the event
+device's capability, it might require creating an additional event port to be
+used by the vector adapter. If required, the ``rte_event_vector_adapter_create()``
+function will use a default method to configure an event port.
+
+If the application desires finer control of event port allocation and setup,
+it can use the ``rte_event_vector_adapter_create_ext()`` function. This function
+is passed a callback function that will be invoked if the adapter needs to
+create an event port, giving the application the opportunity to control how
+it is done.
+
+Retrieve Vector Adapter Contextual Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The vector adapter implementation may have constraints on vector size or
+timeout based on the given event device or system. The application can retrieve
+these constraints using ``rte_event_vector_adapter_info_get()``. This function
+returns an ``rte_event_vector_adapter_info`` struct, which contains the following
+members:
+
+* ``max_vector_adapters_per_event_queue`` - Maximum number of vector adapters
+  configurable per event queue.
+* ``min_vector_sz`` - Minimum vector size configurable.
+* ``max_vector_sz`` - Maximum vector size configurable.
+* ``min_vector_timeout_ns`` - Minimum vector timeout configurable.
+* ``max_vector_timeout_ns`` - Maximum vector timeout configurable.
+* ``log2_sz`` - Vector size should be a power of 2.
+
+Enqueuing Objects to the Vector Adapter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once a vector adapter has been created, the application can enqueue objects
+to it using ``rte_event_vector_adapter_enqueue()``. The adapter will aggregate
+the objects into a vector event based on the configured size and timeout.
+
+.. code-block:: c
+
+	uint64_t objs[32];
+	uint16_t num_elem = 32;
+	uint64_t flags = 0;
+
+	int ret = rte_event_vector_adapter_enqueue(adapter, objs, num_elem, flags);
+	if (ret < 0) { ... }
+
+The application can use the ``RTE_EVENT_VECTOR_ENQ_SOV`` and ``RTE_EVENT_VECTOR_ENQ_EOV``
+flags to control the start and end of vector aggregation if the vector adapter supports
+``RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV`` capability if not, then the flags will be ignored.
+
+The ``RTE_EVENT_VECTOR_ENQ_SOV`` flag marks the beginning of a vector and applies
+to the first pointer in the enqueue operation. Any incomplete vectors will be
+enqueued to the event device.
+
+The ``RTE_EVENT_VECTOR_ENQ_EOV`` flag marks the end of a vector and applies to
+the last pointer in the enqueue operation. The vector is enqueued to the event
+device even if the configured vector size is not reached.
+
+If both flags are set, the adapter will form a new vector event with the given
+objects and enqueue it to the event device.
+
+The ``RTE_EVENT_VECTOR_ENQ_FLUSH`` flag can be used to flush any remaining
+objects in the vector adapter. This is useful when the application needs to
+ensure that all objects are processed, even if the configured vector size or
+timeout is not reached. An enqueue call with this flag set will not handle any
+objects and will return 0.
+
+Processing Vector Events
+------------------------
+
+Once a vector event has been enqueued in the event device, the application will
+subsequently dequeue it from the event device. The application can process the
+vector event and its aggregated objects as needed:
+
+.. code-block:: c
+
+	void
+	event_processing_loop(...)
+	{
+		while (...) {
+			/* Receive events from the configured event port. */
+			rte_event_dequeue_burst(event_dev_id, event_port, &ev, 1, 0);
+			...
+			switch(ev.event_type) {
+				...
+				case RTE_EVENT_TYPE_VECTOR:
+					process_vector_event(ev);
+					...
+					break;
+			}
+		}
+	}
+
+	void
+	process_vector_event(struct rte_event ev)
+	{
+		struct rte_event_vector *vector = ev.event_ptr;
+		for (uint16_t i = 0; i < vector->nb_elem; i++) {
+			uint64_t obj = vector->u64s[i];
+			/* Process each object in the vector. */
+			...
+		}
+	}
+
+Statistics and Cleanup
+----------------------
+
+The application can retrieve statistics for the vector adapter using
+``rte_event_vector_adapter_stats_get()``
+
+.. code-block:: c
+
+	struct rte_event_vector_adapter_stats stats;
+	rte_event_vector_adapter_stats_get(adapter, &stats);
+
+	printf("Vectors created: %" PRIu64 "\n", stats.vectorized);
+	printf("Timeouts occurred: %" PRIu64 "\n", stats.vectors_timedout);
+
+To reset the statistics, use ``rte_event_vector_adapter_stats_reset()``.
+
+To destroy the vector adapter and release its resources, use
+``rte_event_vector_adapter_destroy()``. The destroy function will
+flush any remaining events in the vector adapter before destroying it.
diff --git a/doc/guides/prog_guide/eventdev/eventdev.rst b/doc/guides/prog_guide/eventdev/eventdev.rst
index 8bb72da908ba..5e49db89830e 100644
--- a/doc/guides/prog_guide/eventdev/eventdev.rst
+++ b/doc/guides/prog_guide/eventdev/eventdev.rst
@@ -424,8 +424,8 @@ eventdev.
 .. Note::

          EventDev needs to be started before starting the event producers such
-         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter and
-         event_dma_adapter.
+         as event_eth_rx_adapter, event_timer_adapter, event_crypto_adapter,
+         event_dma_adapter and event_vector_adapter.

 Ingress of New Events
 ~~~~~~~~~~~~~~~~~~~~~
@@ -561,9 +561,9 @@ using ``rte_event_dev_stop_flush_callback_register()`` function.
 .. Note::

         The event producers such as ``event_eth_rx_adapter``,
-        ``event_timer_adapter``, ``event_crypto_adapter`` and
-        ``event_dma_adapter`` need to be stopped before stopping
-        the event device.
+        ``event_timer_adapter``, ``event_crypto_adapter``,
+        ``event_dma_adapter`` and ``event_vector_adapter``
+        need to be stopped before stopping the event device.

 Summary
 -------
diff --git a/doc/guides/prog_guide/eventdev/index.rst b/doc/guides/prog_guide/eventdev/index.rst
index 2e1940ce760a..af11a57e71c8 100644
--- a/doc/guides/prog_guide/eventdev/index.rst
+++ b/doc/guides/prog_guide/eventdev/index.rst
@@ -14,3 +14,4 @@ Event Device Library
     event_crypto_adapter
     event_dma_adapter
     dispatcher_lib
+    event_vector_adapter
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de0f..f9e14d12698d 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -78,6 +78,16 @@ New Features

   See the :doc:`../cryptodevs/zsda` guide for more details on the new driver.

+* **Added eventdev vector adapter.**
+
+  Added the Event vector Adapter Library. This library extends the event-based
+  model by introducing APIs that allow applications to offload creation of
+  event vectors, thereby reducing the scheduling latency.
+
+  See the :doc:`../prog_guide/eventdev/event_vector_adapter` guide for more
+  details on the new library.
+
+

 Removed Items
 -------------
diff --git a/lib/eventdev/event_vector_adapter_pmd.h b/lib/eventdev/event_vector_adapter_pmd.h
new file mode 100644
index 000000000000..667363c496ff
--- /dev/null
+++ b/lib/eventdev/event_vector_adapter_pmd.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+#ifndef __EVENT_VECTOR_ADAPTER_PMD_H__
+#define __EVENT_VECTOR_ADAPTER_PMD_H__
+/**
+ * @file
+ * RTE Event Vector Adapter API (PMD Side)
+ *
+ * @note
+ * This file provides implementation helpers for internal use by PMDs.  They
+ * are not intended to be exposed to applications and are not subject to ABI
+ * versioning.
+ */
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+typedef int (*rte_event_vector_adapter_create_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation setup */
+typedef int (*rte_event_vector_adapter_destroy_t)(struct rte_event_vector_adapter *adapter);
+/**< @internal Event vector adapter implementation teardown */
+typedef int (*rte_event_vector_adapter_stats_get_t)(const struct rte_event_vector_adapter *adapter,
+						    struct rte_event_vector_adapter_stats *stats);
+/**< @internal Get statistics for event vector adapter */
+typedef int (*rte_event_vector_adapter_stats_reset_t)(
+	const struct rte_event_vector_adapter *adapter);
+/**< @internal Reset statistics for event vector adapter */
+
+/**
+ * @internal Structure containing the functions exported by an event vector
+ * adapter implementation.
+ */
+struct event_vector_adapter_ops {
+	rte_event_vector_adapter_create_t create;
+	/**< Set up adapter */
+	rte_event_vector_adapter_destroy_t destroy;
+	/**< Tear down adapter */
+	rte_event_vector_adapter_stats_get_t stats_get;
+	/**< Get adapter statistics */
+	rte_event_vector_adapter_stats_reset_t stats_reset;
+	/**< Reset adapter statistics */
+
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Enqueue objects into the event vector adapter */
+};
+/**
+ * @internal Adapter data; structure to be placed in shared memory to be
+ * accessible by various processes in a multi-process configuration.
+ */
+struct __rte_cache_aligned rte_event_vector_adapter_data {
+	uint32_t id;
+	/**< Event vector adapter ID */
+	uint8_t event_dev_id;
+	/**< Event device ID */
+	uint32_t socket_id;
+	/**< Socket ID where memory is allocated */
+	uint8_t event_port_id;
+	/**< Optional: event port ID used when the inbuilt port is absent */
+	const struct rte_memzone *mz;
+	/**< Event vector adapter memzone pointer */
+	struct rte_event_vector_adapter_conf conf;
+	/**< Configuration used to configure the adapter. */
+	uint32_t caps;
+	/**< Adapter capabilities */
+	void *adapter_priv;
+	/**< Vector adapter private data*/
+	uint8_t service_inited;
+	/**< Service initialization state */
+	uint32_t unified_service_id;
+	/**< Unified Service ID*/
+};
+
+static inline int
+dummy_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
+			     uint16_t num_events, uint64_t flags)
+{
+	RTE_SET_USED(adapter);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(num_events);
+	RTE_SET_USED(flags);
+	return 0;
+}
+
+#endif /* __EVENT_VECTOR_ADAPTER_PMD_H__ */
diff --git a/lib/eventdev/eventdev_pmd.h b/lib/eventdev/eventdev_pmd.h
index ad13ba5b030a..d03461316b8a 100644
--- a/lib/eventdev/eventdev_pmd.h
+++ b/lib/eventdev/eventdev_pmd.h
@@ -26,6 +26,7 @@

 #include "event_timer_adapter_pmd.h"
 #include "rte_event_eth_rx_adapter.h"
+#include "rte_event_vector_adapter.h"
 #include "rte_eventdev.h"

 #ifdef __cplusplus
@@ -1555,6 +1556,36 @@ typedef int (*eventdev_dma_adapter_stats_get)(const struct rte_eventdev *dev,
 typedef int (*eventdev_dma_adapter_stats_reset)(const struct rte_eventdev *dev,
 						const int16_t dma_dev_id);

+/**
+ * Event device vector adapter capabilities.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param caps
+ *   Vector adapter capabilities
+ * @param ops
+ *   Vector adapter ops
+ *
+ * @return
+ *   Return 0 on success.
+ *
+ */
+typedef int (*eventdev_vector_adapter_caps_get_t)(const struct rte_eventdev *dev, uint32_t *caps,
+						  const struct event_vector_adapter_ops **ops);
+
+/**
+ * Event device vector adapter info.
+ *
+ * @param dev
+ *   Event device pointer
+ * @param info
+ *   Vector adapter info
+ *
+ * @return
+ *   Return 0 on success.
+ */
+typedef int (*eventdev_vector_adapter_info_get_t)(const struct rte_eventdev *dev,
+						  struct rte_event_vector_adapter_info *info);

 /** Event device operations function pointer table */
 struct eventdev_ops {
@@ -1697,6 +1728,11 @@ struct eventdev_ops {
 	eventdev_dma_adapter_stats_reset dma_adapter_stats_reset;
 	/**< Reset DMA stats */

+	eventdev_vector_adapter_caps_get_t vector_adapter_caps_get;
+	/**< Get vector adapter capabilities */
+	eventdev_vector_adapter_info_get_t vector_adapter_info_get;
+	/**< Get vector adapter info */
+
 	eventdev_selftest dev_selftest;
 	/**< Start eventdev Selftest */

diff --git a/lib/eventdev/meson.build b/lib/eventdev/meson.build
index 71dea91727c1..0797c145e70c 100644
--- a/lib/eventdev/meson.build
+++ b/lib/eventdev/meson.build
@@ -18,6 +18,7 @@ sources = files(
         'rte_event_eth_tx_adapter.c',
         'rte_event_ring.c',
         'rte_event_timer_adapter.c',
+        'rte_event_vector_adapter.c',
         'rte_eventdev.c',
 )
 headers = files(
@@ -27,6 +28,7 @@ headers = files(
         'rte_event_eth_tx_adapter.h',
         'rte_event_ring.h',
         'rte_event_timer_adapter.h',
+        'rte_event_vector_adapter.h',
         'rte_eventdev.h',
         'rte_eventdev_trace_fp.h',
 )
@@ -38,6 +40,7 @@ driver_sdk_headers += files(
         'eventdev_pmd_pci.h',
         'eventdev_pmd_vdev.h',
         'event_timer_adapter_pmd.h',
+        'event_vector_adapter_pmd.h',
 )

 deps += ['ring', 'ethdev', 'hash', 'mempool', 'mbuf', 'timer', 'cryptodev', 'dmadev']
diff --git a/lib/eventdev/rte_event_vector_adapter.c b/lib/eventdev/rte_event_vector_adapter.c
new file mode 100644
index 000000000000..ff6bc43b1773
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.c
@@ -0,0 +1,472 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mcslock.h>
+#include <rte_service_component.h>
+#include <rte_tailq.h>
+
+#include <eal_export.h>
+
+#include "event_vector_adapter_pmd.h"
+#include "eventdev_pmd.h"
+#include "rte_event_vector_adapter.h"
+
+#define ADAPTER_ID(dev_id, queue_id, adapter_id)                                                   \
+	((uint32_t)dev_id << 16 | (uint32_t)queue_id << 8 | (uint32_t)adapter_id)
+#define DEV_ID_FROM_ADAPTER_ID(adapter_id)     ((adapter_id >> 16) & 0xFF)
+#define QUEUE_ID_FROM_ADAPTER_ID(adapter_id)   ((adapter_id >> 8) & 0xFF)
+#define ADAPTER_ID_FROM_ADAPTER_ID(adapter_id) (adapter_id & 0xFF)
+
+#define MZ_NAME_MAX_LEN	    64
+#define DATA_MZ_NAME_FORMAT "vector_adapter_data_%d_%d_%d"
+
+RTE_LOG_REGISTER_SUFFIX(ev_vector_logtype, adapter.vector, NOTICE);
+#define RTE_LOGTYPE_EVVEC ev_vector_logtype
+
+struct rte_event_vector_adapter *adapters[RTE_EVENT_MAX_DEVS][RTE_EVENT_MAX_QUEUES_PER_DEV];
+
+#define EVVEC_LOG(level, logtype, ...)                                                             \
+	RTE_LOG_LINE_PREFIX(level, logtype,                                                        \
+			    "EVVEC: %s() line %u: ", __func__ RTE_LOG_COMMA __LINE__, __VA_ARGS__)
+#define EVVEC_LOG_ERR(...) EVVEC_LOG(ERR, EVVEC, __VA_ARGS__)
+
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+#define EVVEC_LOG_DBG(...) EVVEC_LOG(DEBUG, EVVEC, __VA_ARGS__)
+#else
+#define EVVEC_LOG_DBG(...) /* No debug logging */
+#endif
+
+#define PTR_VALID_OR_ERR_RET(ptr, retval)                                                          \
+	do {                                                                                       \
+		if (ptr == NULL) {                                                                 \
+			rte_errno = EINVAL;                                                        \
+			return retval;                                                             \
+		}                                                                                  \
+	} while (0)
+
+static int
+validate_conf(const struct rte_event_vector_adapter_conf *conf,
+	      struct rte_event_vector_adapter_info *info)
+{
+	int rc = -EINVAL;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, rc);
+
+	if (conf->vector_sz < info->min_vector_sz || conf->vector_sz > info->max_vector_sz) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be between %u and %u",
+			      conf->vector_sz, info->min_vector_sz, info->max_vector_sz);
+		return rc;
+	}
+
+	if (conf->vector_timeout_ns < info->min_vector_timeout_ns ||
+	    conf->vector_timeout_ns > info->max_vector_timeout_ns) {
+		EVVEC_LOG_DBG("invalid vector timeout %" PRIu64 ", should be between %" PRIu64
+			      " and %" PRIu64,
+			      conf->vector_timeout_ns, info->min_vector_timeout_ns,
+			      info->max_vector_timeout_ns);
+		return rc;
+	}
+
+	if (conf->vector_mp == NULL) {
+		EVVEC_LOG_DBG("invalid mempool for vector adapter");
+		return rc;
+	}
+
+	if (info->log2_sz && rte_is_power_of_2(conf->vector_sz) != 0) {
+		EVVEC_LOG_DBG("invalid vector size %u, should be a power of 2", conf->vector_sz);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+default_port_conf_cb(uint8_t event_dev_id, uint8_t *event_port_id, void *conf_arg)
+{
+	struct rte_event_port_conf *port_conf, def_port_conf = {0};
+	struct rte_event_dev_config dev_conf;
+	struct rte_eventdev *dev;
+	uint8_t port_id;
+	uint8_t dev_id;
+	int started;
+	int ret;
+
+	dev = &rte_eventdevs[event_dev_id];
+	dev_id = dev->data->dev_id;
+	dev_conf = dev->data->dev_conf;
+
+	started = dev->data->dev_started;
+	if (started)
+		rte_event_dev_stop(dev_id);
+
+	port_id = dev_conf.nb_event_ports;
+	if (conf_arg != NULL)
+		port_conf = conf_arg;
+	else {
+		port_conf = &def_port_conf;
+		ret = rte_event_port_default_conf_get(dev_id, (port_id - 1), port_conf);
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_conf.nb_event_ports += 1;
+	if (port_conf->event_port_cfg & RTE_EVENT_PORT_CFG_SINGLE_LINK)
+		dev_conf.nb_single_link_event_port_queues += 1;
+
+	ret = rte_event_dev_configure(dev_id, &dev_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to configure event dev %u", dev_id);
+		if (started)
+			if (rte_event_dev_start(dev_id))
+				return -EIO;
+
+		return ret;
+	}
+
+	ret = rte_event_port_setup(dev_id, port_id, port_conf);
+	if (ret < 0) {
+		EVVEC_LOG_ERR("failed to setup event port %u on event dev %u", port_id, dev_id);
+		return ret;
+	}
+
+	*event_port_id = port_id;
+
+	if (started)
+		ret = rte_event_dev_start(dev_id);
+
+	return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf)
+{
+	return rte_event_vector_adapter_create_ext(conf, default_port_conf_cb, NULL);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_create_ext, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb, void *conf_arg)
+{
+	struct rte_event_vector_adapter *adapter = NULL;
+	struct rte_event_vector_adapter_info info;
+	char mz_name[MZ_NAME_MAX_LEN];
+	const struct rte_memzone *mz;
+	struct rte_eventdev *dev;
+	uint32_t caps;
+	int i, n, rc;
+
+	PTR_VALID_OR_ERR_RET(conf, NULL);
+
+	if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+		adapters[conf->event_dev_id][conf->ev.queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[conf->event_dev_id][conf->ev.queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[conf->event_dev_id][conf->ev.queue_id][i].used == false) {
+			adapter = &adapters[conf->event_dev_id][conf->ev.queue_id][i];
+			adapter->adapter_id = ADAPTER_ID(conf->event_dev_id, conf->ev.queue_id, i);
+			adapter->used = true;
+			break;
+		}
+		EVVEC_LOG_DBG("adapter %u is already in use", i);
+		rte_errno = EEXIST;
+		return NULL;
+	}
+
+	if (adapter == NULL) {
+		EVVEC_LOG_DBG("no available vector adapters");
+		rte_errno = ENODEV;
+		return NULL;
+	}
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(conf->event_dev_id, NULL);
+
+	dev = &rte_eventdevs[conf->event_dev_id];
+	if (dev->dev_ops->vector_adapter_caps_get != NULL &&
+	    dev->dev_ops->vector_adapter_info_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &caps, &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+
+		rc = dev->dev_ops->vector_adapter_info_get(dev, &info);
+		if (rc < 0) {
+			adapter->ops = NULL;
+			EVVEC_LOG_DBG("failed to get vector adapter info rc = %d", rc);
+			rte_errno = ENOTSUP;
+			goto error;
+		}
+	}
+
+	if (conf->ev.sched_type != dev->data->queues_cfg[conf->ev.queue_id].schedule_type &&
+	    !(dev->data->event_dev_cap & RTE_EVENT_DEV_CAP_QUEUE_ALL_TYPES)) {
+		EVVEC_LOG_DBG("invalid event schedule type, eventdev doesn't support all types");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	rc = validate_conf(conf, &info);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	n = snprintf(mz_name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, conf->event_dev_id,
+		     conf->ev.queue_id, adapter->adapter_id);
+	if (n >= (int)sizeof(mz_name)) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create memzone name");
+		rte_errno = EINVAL;
+		goto error;
+	}
+	mz = rte_memzone_reserve(mz_name, sizeof(struct rte_event_vector_adapter_data),
+				 conf->socket_id, 0);
+	if (mz == NULL) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to reserve memzone for vector adapter");
+		rte_errno = ENOMEM;
+		goto error;
+	}
+
+	adapter->data = mz->addr;
+	memset(adapter->data, 0, sizeof(struct rte_event_vector_adapter_data));
+
+	adapter->data->mz = mz;
+	adapter->data->event_dev_id = conf->event_dev_id;
+	adapter->data->id = adapter->adapter_id;
+	adapter->data->socket_id = conf->socket_id;
+	adapter->data->conf = *conf;
+
+	if (!(caps & RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT)) {
+		if (conf_cb == NULL) {
+			EVVEC_LOG_DBG("port config callback is NULL");
+			rte_errno = EINVAL;
+			goto error;
+		}
+
+		rc = conf_cb(conf->event_dev_id, &adapter->data->event_port_id, conf_arg);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to create port for vector adapter");
+			rte_errno = EINVAL;
+			goto error;
+		}
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->create, NULL);
+
+	rc = adapter->ops->create(adapter);
+	if (rc < 0) {
+		adapter->ops = NULL;
+		EVVEC_LOG_DBG("failed to create vector adapter");
+		rte_errno = EINVAL;
+		goto error;
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+
+	return adapter;
+
+error:
+	adapter->used = false;
+	return NULL;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_lookup, 25.07)
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id)
+{
+	uint8_t adapter_idx = ADAPTER_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t queue_id = QUEUE_ID_FROM_ADAPTER_ID(adapter_id);
+	uint8_t dev_id = DEV_ID_FROM_ADAPTER_ID(adapter_id);
+	struct rte_event_vector_adapter *adapter;
+	const struct rte_memzone *mz;
+	char name[MZ_NAME_MAX_LEN];
+	struct rte_eventdev *dev;
+	int rc;
+
+	if (dev_id >= RTE_EVENT_MAX_DEVS || queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV ||
+	    adapter_idx >= RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE) {
+		EVVEC_LOG_ERR("invalid adapter id %u", adapter_id);
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (adapters[dev_id][queue_id] == NULL) {
+		adapters[dev_id][queue_id] =
+			rte_zmalloc("rte_event_vector_adapter",
+				    sizeof(struct rte_event_vector_adapter) *
+					    RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE,
+				    RTE_CACHE_LINE_SIZE);
+		if (adapters[dev_id][queue_id] == NULL) {
+			EVVEC_LOG_DBG("failed to allocate memory for vector adapters");
+			rte_errno = ENOMEM;
+			return NULL;
+		}
+	}
+
+	if (adapters[dev_id][queue_id][adapter_idx].used == true)
+		return &adapters[dev_id][queue_id][adapter_idx];
+
+	adapter = &adapters[dev_id][queue_id][adapter_idx];
+
+	snprintf(name, MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, dev_id, queue_id, adapter_idx);
+	mz = rte_memzone_lookup(name);
+	if (mz == NULL) {
+		EVVEC_LOG_DBG("failed to lookup memzone for vector adapter");
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	adapter->data = mz->addr;
+	dev = &rte_eventdevs[dev_id];
+
+	if (dev->dev_ops->vector_adapter_caps_get != NULL) {
+		rc = dev->dev_ops->vector_adapter_caps_get(dev, &adapter->data->caps,
+							   &adapter->ops);
+		if (rc < 0) {
+			EVVEC_LOG_DBG("failed to get vector adapter capabilities");
+			rte_errno = ENOTSUP;
+			return NULL;
+		}
+	}
+
+	adapter->enqueue = adapter->ops->enqueue;
+	adapter->adapter_id = adapter_id;
+	adapter->used = true;
+
+	return adapter;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_service_id_get, 25.07)
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	if (adapter->data->service_inited && service_id != NULL)
+		*service_id = adapter->data->unified_service_id;
+
+	return adapter->data->service_inited ? 0 : -ESRCH;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_destroy, 25.07)
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter)
+{
+	int rc;
+
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	if (adapter->used == false) {
+		EVVEC_LOG_ERR("event vector adapter is not allocated");
+		return -EINVAL;
+	}
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->destroy, -ENOTSUP);
+
+	rc = adapter->ops->destroy(adapter);
+	if (rc < 0) {
+		EVVEC_LOG_DBG("failed to destroy vector adapter");
+		return rc;
+	}
+
+	rte_memzone_free(adapter->data->mz);
+	adapter->ops = NULL;
+	adapter->enqueue = dummy_vector_adapter_enqueue;
+	adapter->data = NULL;
+	adapter->used = false;
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_info_get, 25.07)
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id, struct rte_event_vector_adapter_info *info)
+{
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, -EINVAL);
+	PTR_VALID_OR_ERR_RET(info, -EINVAL);
+
+	struct rte_eventdev *dev = &rte_eventdevs[event_dev_id];
+	if (dev->dev_ops->vector_adapter_info_get != NULL)
+		return dev->dev_ops->vector_adapter_info_get(dev, info);
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_conf_get, 25.07)
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(conf, -EINVAL);
+
+	*conf = adapter->data->conf;
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_remaining, 25.07)
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id)
+{
+	uint8_t remaining = 0;
+	int i;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(event_dev_id, 0);
+
+	if (event_queue_id >= RTE_EVENT_MAX_QUEUES_PER_DEV)
+		return 0;
+
+	for (i = 0; i < RTE_EVENT_VECTOR_ADAPTER_MAX_INSTANCE_PER_QUEUE; i++) {
+		if (adapters[event_dev_id][event_queue_id][i].used == false)
+			remaining++;
+	}
+
+	return remaining;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_get, 25.07)
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+	PTR_VALID_OR_ERR_RET(stats, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_get, -ENOTSUP);
+
+	adapter->ops->stats_get(adapter, stats);
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_stats_reset, 25.07)
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter)
+{
+	PTR_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+	FUNC_PTR_OR_ERR_RET(adapter->ops->stats_reset, -ENOTSUP);
+
+	adapter->ops->stats_reset(adapter);
+
+	return 0;
+}
diff --git a/lib/eventdev/rte_event_vector_adapter.h b/lib/eventdev/rte_event_vector_adapter.h
new file mode 100644
index 000000000000..22869c3d95f5
--- /dev/null
+++ b/lib/eventdev/rte_event_vector_adapter.h
@@ -0,0 +1,477 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Marvell International Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __RTE_EVENT_VECTOR_ADAPTER_H__
+#define __RTE_EVENT_VECTOR_ADAPTER_H__
+
+/**
+ * @file rte_event_vector_adapter.h
+ *
+ * @warning
+ * @b EXPERIMENTAL:
+ * All functions in this file may be changed or removed without prior notice.
+ *
+ * Event vector adapter API.
+ *
+ * An event vector adapter has the following working model:
+ *
+ *          ┌──────────┐
+ *   Enqueue│  Vector  ├─┐
+ *      ───►│ adapter 0│ │
+ *          └──────────┘ │
+ *          ┌──────────┐ │   ┌──────────┐
+ *   Enqueue│  Vector  ├─┼──►│  Event   │
+ *      ───►│ adapter 1│ │   │  Queue 0 │
+ *          └──────────┘ │   └──────────┘
+ *          ┌──────────┐ │
+ *   Enqueue│  Vector  ├─┘
+ *      ───►│ adapter n│
+ *          └──────────┘
+ *
+ * - A vector adapter can be seen as an extension to event queue. It helps in
+ *   aggregating objects and generating a vector event which is enqueued to the
+ *   event queue.
+ *
+ * - Multiple vector adapters can be created on an event queue, each with its
+ *   own unique properties such as event properties, vector size, and timeout.
+ *   Note: If the target event queue doesn't support #RTE_EVENT_QUEUE_CFG_ALL_TYPES,
+ *         then the vector adapter should use the same schedule type as the event
+ *         queue.
+ *
+ * - Each vector adapter aggregates 8B objects, generates a vector event and
+ *   enqueues it to the event queue with the event properties mentioned in
+ *   rte_event_vector_adapter_conf::ev.
+ *
+ * - After configuring the vector adapter, Application needs to use the
+ *   rte_event_vector_adapter_enqueue() function to enqueue objects i.e.,
+ *   mbufs/ptrs/u64s to the vector adapter.
+ *   On reaching the configured vector size or timeout, the vector adapter
+ *   enqueues the event vector to the event queue.
+ *   Note: Application should use the event_type and sub_event_type properly
+ *         identifying the contents of vector event on dequeue.
+ *
+ * - If the vector adapter advertises the #RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ *   capability, application can use the RTE_EVENT_VECTOR_ENQ_[S|E]OV flags
+ *   to indicate the start and end of a vector event.
+ *   * When #RTE_EVENT_VECTOR_ENQ_SOV is set, the vector adapter will flush any
+ *     aggregation in-progress and start aggregating a new vector event with
+ *     the enqueued objects.
+ *   * When #RTE_EVENT_VECTOR_ENQ_EOV is set, the vector adapter will add the
+ *     objects enqueued to the in-progress aggregation and enqueue the vector
+ *     event to the event queue, even if configured vector size or timeout is
+ *     not reached.
+ *   * If both flags are set, the vector adapter will flush any aggregation in
+ *     progress and enqueue the objects as a new vector event to the event
+ *     queue.
+ *
+ * - If the vector adapter reaches the configured vector size, it will enqueue
+ *   the aggregated vector event to the event queue.
+ *
+ * - If the vector adapter reaches the configured vector timeout, it will flush
+ *   the aggregated objects as a vector event if the minimum vector size is
+ *   reached, if not it will enqueue the objs as single events to the event
+ *   queue.
+ *
+ * - If the vector adapter is unable to aggregate the objs into a vector event,
+ *   it will enqueue the objs as single events to the event queue with the event
+ *   properties mentioned in rte_event_vector_adapter_conf::ev_fallback.
+ *
+ * Before using the vector adapter, the application has to create and configure
+ * an event device and based on the event device capability it might require
+ * creating an additional event port.
+ *
+ * When the application creates the vector adapter using the
+ * ``rte_event_vector_adapter_create()`` function, the event device driver
+ * capabilities are checked. If an in-built port is absent, the application
+ * uses the default function to create a new event port.
+ * For finer control over event port creation, the application should use
+ * the ``rte_event_vector_adapter_create_ext()`` function.
+ *
+ * The application can enqueue one or more objs to the vector adapter using the
+ * ``rte_event_vector_adapter_enqueue()`` function and control the aggregation
+ * using the flags.
+ *
+ * Vector adapters report stats using the ``rte_event_vector_adapter_stats_get()``
+ * function and reset the stats using the ``rte_event_vector_adapter_stats_reset()``.
+ *
+ * The application can destroy the vector adapter using the
+ * ``rte_event_vector_adapter_destroy()`` function.
+ *
+ */
+
+#include <rte_eventdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV RTE_BIT64(0)
+/**< Vector adapter supports Start of Vector (SOV) and End of Vector (EOV) flags
+ *  in the enqueue flags.
+ *
+ * @see RTE_EVENT_VECTOR_ENQ_SOV
+ * @see RTE_EVENT_VECTOR_ENQ_EOV
+ */
+
+#define RTE_EVENT_VECTOR_ENQ_SOV RTE_BIT64(0)
+/**< Indicates the start of a vector event. When enqueue is called with
+ *  #RTE_EVENT_VECTOR_ENQ_SOV, the vector adapter will flush any vector
+ *  aggregation in progress and start aggregating a new vector event with
+ *  the enqueued objects.
+ *  @see RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ */
+#define RTE_EVENT_VECTOR_ENQ_EOV RTE_BIT64(1)
+/**< Indicates the end of a vector event. When enqueue is called with
+ *  #RTE_EVENT_VECTOR_ENQ_EOV, the vector adapter will add the objects
+ *  to any inprogress aggregation and flush the event vector.
+ *  @see RTE_EVENT_VECTOR_ADAPTER_CAP_SOV_EOV
+ */
+#define RTE_EVENT_VECTOR_ENQ_FLUSH RTE_BIT64(2)
+/**< Flush any in-progress vector aggregation. */
+
+/**
+ * Vector adapter configuration structure
+ */
+struct rte_event_vector_adapter_conf {
+	uint8_t event_dev_id;
+	/**< Event device identifier */
+	uint32_t socket_id;
+	/**< Identifier of socket from which to allocate memory for adapter */
+	struct rte_event ev;
+	/**<
+	 *  The values from the following event fields will be used when
+	 *  queuing work:
+	 *   - queue_id: Targeted event queue ID for vector event.
+	 *   - event_priority: Event priority of the vector event in
+	 *                     the event queue relative to other events.
+	 *   - sched_type: Scheduling type for events from this vector adapter.
+	 *   - event_type: Event type for the vector event.
+	 *   - sub_event_type: Sub event type for the vector event.
+	 *   - flow_id: Flow ID for the vectors enqueued to the event queue by
+	 *              the vector adapter.
+	 */
+	struct rte_event ev_fallback;
+	/**<
+	 * The values from the following event fields will be used when
+	 * aggregation fails and single event is enqueued:
+	 *   - event_type: Event type for the single event.
+	 *   - sub_event_type: Sub event type for the single event.
+	 *   - flow_id: Flow ID for the single event.
+	 *
+	 * Other fields are taken from rte_event_vector_adapter_conf::ev.
+	 */
+	uint16_t vector_sz;
+	/**<
+	 * Indicates the maximum number for enqueued work to combine and form a vector.
+	 * Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_sz
+	 * @see rte_event_vector_adapter_info::max_vector_sz
+	 */
+	uint64_t vector_timeout_ns;
+	/**<
+	 * Indicates the maximum number of nanoseconds to wait for receiving
+	 * work. Should be within vectorization limits of the adapter.
+	 * @see rte_event_vector_adapter_info::min_vector_ns
+	 * @see rte_event_vector_adapter_info::max_vector_ns
+	 */
+	struct rte_mempool *vector_mp;
+	/**<
+	 * Indicates the mempool that should be used for allocating
+	 * rte_event_vector container.
+	 * @see rte_event_vector_pool_create
+	 */
+};
+
+/**
+ * Vector adapter info structure
+ */
+struct rte_event_vector_adapter_info {
+	uint8_t max_vector_adapters_per_event_queue;
+	/**< Maximum number of vector adapters configurable */
+	uint16_t min_vector_sz;
+	/**< Minimum vector size configurable */
+	uint16_t max_vector_sz;
+	/**< Maximum vector size configurable */
+	uint64_t min_vector_timeout_ns;
+	/**< Minimum vector timeout configurable */
+	uint64_t max_vector_timeout_ns;
+	/**< Maximum vector timeout configurable */
+	uint8_t log2_sz;
+	/**< True if the size configured should be in log2. */
+};
+
+/**
+ * Vector adapter statistics structure
+ */
+struct rte_event_vector_adapter_stats {
+	uint64_t vectorized;
+	/**< Number of events vectorized */
+	uint64_t vectors_timedout;
+	/**< Number of timeouts occurred */
+	uint64_t vectors_flushed;
+	/**< Number of vectors flushed */
+	uint64_t alloc_failures;
+	/**< Number of vector allocation failures */
+};
+
+struct rte_event_vector_adapter;
+
+typedef int (*rte_event_vector_adapter_enqueue_t)(struct rte_event_vector_adapter *adapter,
+						  uint64_t objs[], uint16_t num_elem,
+						  uint64_t flags);
+/**< @internal Enqueue objs into the event vector adapter. */
+
+/**
+ * Vector adapter structure
+ */
+struct __rte_cache_aligned rte_event_vector_adapter {
+	rte_event_vector_adapter_enqueue_t enqueue;
+	/**< Pointer to driver enqueue function. */
+	struct rte_event_vector_adapter_data *data;
+	/**< Pointer to the adapter data */
+	const struct event_vector_adapter_ops *ops;
+	/**< Functions exported by adapter driver */
+
+	uint32_t adapter_id;
+	/**< Identifier of the adapter instance. */
+	uint8_t used : 1;
+	/**< Flag to indicate that this adapter is being used. */
+};
+
+/**
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_vector_adapter_port_conf_cb_t)(uint8_t event_dev_id, uint8_t *event_port_id,
+						       void *conf_arg);
+
+/**
+ * Create an event vector adapter.
+ *
+ * This function creates an event vector adapter based on the provided
+ * configuration. The adapter can be used to combine multiple mbufs/ptrs/u64s
+ * into a single vector event, i.e., rte_event_vector, which is then enqueued
+ * to the event queue provided.
+ * @see rte_event_vector_adapter_conf::ev::queue_id
+ *
+ * @param conf
+ *   Configuration for the event vector adapter.
+ * @return
+ *   - Pointer to the created event vector adapter on success.
+ *   - NULL on failure with rte_errno set to the error code.
+ * Possible rte_errno values include:
+ *    - EINVAL: Invalid event device identifier specified in config.
+ *    - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *    - ENOSPC: Maximum number of adapters already created.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create(const struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Create an event vector adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the event
+ * vector adapter creation. If a built-in port is absent, then the function uses
+ * the callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ *   The event vector adapter configuration structure.
+ * @param conf_cb
+ *   The port config callback function.
+ * @param conf_arg
+ *   Opaque pointer to the argument for the callback function.
+ * @return
+ *   - Pointer to the new allocated event vector adapter on success.
+ *   - NULL on error with rte_errno set appropriately
+ * Possible rte_errno values include:
+ *   - ERANGE: vector_timeout_ns is not in supported range.
+ *   - ENOMEM: Unable to allocate sufficient memory for adapter instances.
+ *   - EINVAL: Invalid event device identifier specified in config.
+ *   - ENOSPC: Maximum number of adapters already created.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_create_ext(const struct rte_event_vector_adapter_conf *conf,
+				    rte_event_vector_adapter_port_conf_cb_t conf_cb,
+				    void *conf_arg);
+
+/**
+ * Lookup an event vector adapter using its identifier.
+ *
+ * This function returns the event vector adapter based on the adapter_id.
+ * This is useful when the adapter is created in another process and the
+ * application wants to use the adapter in the current process.
+ *
+ * @param adapter_id
+ *   Identifier of the event vector adapter to look up.
+ * @return
+ *   - Pointer to the event vector adapter on success.
+ *   - NULL if the adapter is not found.
+ */
+__rte_experimental
+struct rte_event_vector_adapter *
+rte_event_vector_adapter_lookup(uint32_t adapter_id);
+
+/**
+ * Destroy an event vector adapter.
+ *
+ * This function releases the resources associated with the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter to be destroyed.
+ * @return
+ *   - 0 on success.
+ *   - Negative value on failure with rte_errno set to the error code.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_destroy(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Get the vector info of an event vector adapter.
+ *
+ * This function retrieves the vector info of the event vector adapter.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param info
+ *   Pointer to the structure where the vector info will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ *   - EINVAL if the event device identifier is invalid.
+ *   - ENOTSUP if the event device does not support vector adapters.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_info_get(uint8_t event_dev_id,
+				  struct rte_event_vector_adapter_info *info);
+
+/**
+ * Get the configuration of an event vector adapter.
+ *
+ * This function retrieves the configuration of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param conf
+ *   Pointer to the structure where the configuration will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_conf_get(struct rte_event_vector_adapter *adapter,
+				  struct rte_event_vector_adapter_conf *conf);
+
+/**
+ * Get the remaining event vector adapters.
+ *
+ * This function retrieves the number of remaining event vector adapters
+ * available for a given event device and event queue.
+ *
+ * @param event_dev_id
+ *   Event device identifier.
+ * @param event_queue_id
+ *   Event queue identifier.
+ * @return
+ *   Number of remaining slots available for enqueuing events.
+ */
+__rte_experimental
+uint8_t
+rte_event_vector_adapter_remaining(uint8_t event_dev_id, uint8_t event_queue_id);
+
+/**
+ * Get the event vector adapter statistics.
+ *
+ * This function retrieves the statistics of the event vector adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param stats
+ *   Pointer to the structure where the statistics will be stored.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_stats_get(struct rte_event_vector_adapter *adapter,
+				   struct rte_event_vector_adapter_stats *stats);
+
+/**
+ * Reset the event vector adapter statistics.
+ *
+ * This function resets the statistics of the event vector adapter to their default values.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter whose statistics are to be reset.
+ * @return
+ *   0 on success, negative value on failure.
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_stats_reset(struct rte_event_vector_adapter *adapter);
+
+/**
+ * Retrieve the service ID of the event vector adapter. If the adapter doesn't
+ * use an rte_service function, this function returns -ESRCH.
+ *
+ * @param adapter
+ *   A pointer to an event vector adapter.
+ * @param [out] service_id
+ *   A pointer to a uint32_t, to be filled in with the service id.
+ *
+ * @return
+ *   - 0: Success
+ *   - <0: Error code on failure
+ *   - -ESRCH: the adapter does not require a service to operate
+ */
+__rte_experimental
+int
+rte_event_vector_adapter_service_id_get(struct rte_event_vector_adapter *adapter,
+					uint32_t *service_id);
+
+/**
+ * Enqueue objs into the event vector adapter.
+ *
+ * This function enqueues a specified number of objs into the event vector adapter.
+ * The objs are combined into a single vector event, i.e., rte_event_vector, which
+ * is then enqueued to the event queue configured in the adapter.
+ *
+ * @param adapter
+ *   Pointer to the event vector adapter.
+ * @param objs
+ *   Array of objs to be enqueued.
+ * @param num_elem
+ *   Number of objs to be enqueued.
+ * @param flags
+ *   Flags to be used for the enqueue operation.
+ * @return
+ *   Number of objs enqueued on success.
+ */
+__rte_experimental
+static inline int
+rte_event_vector_adapter_enqueue(struct rte_event_vector_adapter *adapter, uint64_t objs[],
+				 uint16_t num_elem, uint64_t flags)
+{
+#ifdef RTE_LIBRTE_EVENTDEV_DEBUG
+	if (adapter == NULL) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+
+	if (adapter->used == false) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+#endif
+	return adapter->enqueue(adapter, objs, num_elem, flags);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTE_EVENT_VECTOR_ADAPTER_H__ */
diff --git a/lib/eventdev/rte_eventdev.c b/lib/eventdev/rte_eventdev.c
index b66cbb4676c6..916bad6c2c4e 100644
--- a/lib/eventdev/rte_eventdev.c
+++ b/lib/eventdev/rte_eventdev.c
@@ -257,6 +257,28 @@ rte_event_dma_adapter_caps_get(uint8_t dev_id, uint8_t dma_dev_id, uint32_t *cap
 	return 0;
 }

+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_event_vector_adapter_caps_get, 25.07)
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps)
+{
+	const struct event_vector_adapter_ops *ops;
+	struct rte_eventdev *dev;
+
+	RTE_EVENTDEV_VALID_DEVID_OR_ERR_RET(dev_id, -EINVAL);
+
+	dev = &rte_eventdevs[dev_id];
+
+	if (caps == NULL)
+		return -EINVAL;
+
+	if (dev->dev_ops->vector_adapter_caps_get == NULL)
+		*caps = 0;
+
+	return dev->dev_ops->vector_adapter_caps_get ?
+		       dev->dev_ops->vector_adapter_caps_get(dev, caps, &ops) :
+		       0;
+}
+
 static inline int
 event_dev_queue_config(struct rte_eventdev *dev, uint8_t nb_queues)
 {
diff --git a/lib/eventdev/rte_eventdev.h b/lib/eventdev/rte_eventdev.h
index 6400d6109f79..3c7fcbf0be02 100644
--- a/lib/eventdev/rte_eventdev.h
+++ b/lib/eventdev/rte_eventdev.h
@@ -1985,6 +1985,16 @@ int
 rte_event_eth_tx_adapter_caps_get(uint8_t dev_id, uint16_t eth_port_id,
 				uint32_t *caps);

+/* Vector adapter capability bitmap flags */
+#define RTE_EVENT_VECTOR_ADAPTER_CAP_INTERNAL_PORT	0x1
+/**< This flag is set when the vector adapter is capable of generating events
+ * using an internal event port.
+ */
+
+__rte_experimental
+int
+rte_event_vector_adapter_caps_get(uint8_t dev_id, uint32_t *caps);
+
 /**
  * Converts nanoseconds to *timeout_ticks* value for rte_event_dequeue_burst()
  *
--
2.39.5 (Apple Git-154)


^ permalink raw reply	[relevance 1%]

* [PATCH] doc: remove the public interfaces from bus/vmbus
@ 2025-06-03  0:31 11% longli
  0 siblings, 0 replies; 153+ results
From: longli @ 2025-06-03  0:31 UTC (permalink / raw)
  To: Stephen Hemminger, Wei Hu, Thomas Monjalon, David Marchand; +Cc: dev, Long Li

From: Long Li <longli@microsoft.com>

The deprecation notice has been sent in
7642e3b0e0 ("doc: announce vmbus API deprecation")

Remove those interfaces in this release.

Signed-off-by: Long Li <longli@microsoft.com>
---
 doc/guides/rel_notes/release_25_07.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de..672307da6c 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -125,7 +125,8 @@ ABI Changes
    Also, make sure to start the actual text at the margin.
    =======================================================
 
-* No ABI change that would break compatibility with 24.11.
+* All the vmbus interfaces defined in ``drivers/bus/vmbus/rte_bus_vmbus.h``
+  become internal to DPDK.
 
 
 Known Issues
-- 
2.25.1


^ permalink raw reply	[relevance 11%]

* Re: [PATCH 2/3] argparse: make argparse EAL-args compatible
  2025-05-27  9:21 10% ` [PATCH 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
@ 2025-06-03  8:44  0%   ` fengchengwen
  2025-06-03 15:33  0%     ` Bruce Richardson
  0 siblings, 1 reply; 153+ results
From: fengchengwen @ 2025-06-03  8:44 UTC (permalink / raw)
  To: Bruce Richardson, dev

On 2025/5/27 17:21, Bruce Richardson wrote:
> The argparse library was missing two key features which made it
> unsuitable for use by EAL or any program wanting similar behaviour.
> 
> 1. It didn't stop parsing arguments when it hit a "--" character
> 2. It never returned the number of arguments parsed
> 
> Fix both these issues - the latter is a change to the ABI, since we now
> return >= 0 rather than == 0 on success. However, the ABI is still
> experimental so we can make exactly these sorts of tweaks to it.
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  app/test/test_argparse.c               | 46 +++++++++++++-------------
>  doc/guides/rel_notes/release_25_07.rst |  9 +++++
>  lib/argparse/rte_argparse.c            | 12 +++++--
>  lib/argparse/rte_argparse.h            |  3 +-
>  4 files changed, 43 insertions(+), 27 deletions(-)
> 
> diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
> index 6b0d1524b5..a907fbe53f 100644
> --- a/app/test/test_argparse.c
> +++ b/app/test/test_argparse.c
> @@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
>  	argv[0] = test_strdup(obj->prog_name);
>  	argv[1] = test_strdup("--test-long");
>  	ret = rte_argparse_parse(obj, 2, argv);
> -	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
> +	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");

Please compared with specific number, eg. TEST_ASSERT(ret == 2, "xxx");

...

>  
> @@ -780,7 +780,7 @@ test_argparse_parse_type(void)
>  	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
>  	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
>  	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
> -	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
> +	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");

No need for rte_argparse_parse_type() API, this API still return 0 if success.

...

> diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
> index 332184302e..8cdb3195cb 100644
> --- a/lib/argparse/rte_argparse.h
> +++ b/lib/argparse/rte_argparse.h
> @@ -183,7 +183,8 @@ struct rte_argparse {
>   *   Array of parameters points.
>   *
>   * @return
> - *   0 on success. Otherwise negative value is returned.
> + *   number of arguments parsed (>= 0) on success.
> + *   Otherwise negative error code is returned.

Please add note for "stops processing arguments when a ``--`` argument is encountered".

>   */
>  __rte_experimental
>  int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);


^ permalink raw reply	[relevance 0%]

* [PATCH v2 0/3] argparse additions and rework
  2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
                   ` (2 preceding siblings ...)
  2025-05-29 15:09  0% ` [PATCH 0/3] argparse additions and rework Bruce Richardson
@ 2025-06-03 15:32  4% ` Bruce Richardson
  2025-06-03 15:32 10%   ` [PATCH v2 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
                     ` (2 more replies)
  2025-06-10 16:29  4% ` [PATCH v3 " Bruce Richardson
  2025-06-11 12:36  4% ` [PATCH v4 0/3] argparse additions and rework Bruce Richardson
  5 siblings, 3 replies; 153+ results
From: Bruce Richardson @ 2025-06-03 15:32 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson

This patchset is based off the work to adjust how we do argument parsing
inside EAL. To enable argparse to be effectively used for EAL, we have
new features and some changes in the first two patches, which are
relatively small - though are ABI/API affecting.

These add support for saving off strings and boolean values, have argparse
stop parsing at a "--", and finally have argparse return the number of
arguments actually parsed on success.

The third patch is a bigger change. It was inspired by the fact that
when adding the boolean and string support we had to update some
"MAX" value defines used in the code. This is obviously not good from
an ABI/API perspective, once the library becomes part of the stable ABI.
In order to remove these MAX values, patch 3 looks to replace the
#define values with enums - which means some rework splitting the
various flags into separate categories, and similarly splitting the
single "flags" field with separate fields specifying if an argument
value is required, what type that value should have, and then a
final smaller field for any additional modifiers.

v2: minor changes to patch 2 (see log at end of that patch)

Bruce Richardson (3):
  argparse: add support for string and boolean args
  argparse: make argparse EAL-args compatible
  argparse: use enums to remove max-value defines in lists

 app/test/test_argparse.c               | 229 ++++++++++++++-----------
 doc/guides/rel_notes/release_25_07.rst |  50 ++++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 184 ++++++++++++--------
 lib/argparse/rte_argparse.h            |  94 +++++-----
 6 files changed, 355 insertions(+), 226 deletions(-)

--
2.48.1


^ permalink raw reply	[relevance 4%]

* [PATCH v2 2/3] argparse: make argparse EAL-args compatible
  2025-06-03 15:32  4% ` [PATCH v2 " Bruce Richardson
@ 2025-06-03 15:32 10%   ` Bruce Richardson
  2025-06-03 15:32  6%   ` [PATCH v2 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
  2025-06-04  1:37  0%   ` [PATCH v2 0/3] argparse additions and rework fengchengwen
  2 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-03 15:32 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson, Chengwen Feng

The argparse library was missing two key features which made it
unsuitable for use by EAL or any program wanting similar behaviour.

1. It didn't stop parsing arguments when it hit a "--" character
2. It never returned the number of arguments parsed

Fix both these issues - the latter is a change to the ABI, since we now
return >= 0 rather than == 0 on success. However, the ABI is still
experimental so we can make exactly these sorts of tweaks to it.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---

v2:
* change tests to check for correct number of parameters parsed, not
  just >= 0
* revert unnecessary changes to the parse_type() function tests
* added doxygen comment that the parse() function stops on "--"
---
 app/test/test_argparse.c               | 36 +++++++++++++-------------
 doc/guides/rel_notes/release_25_07.rst |  9 +++++++
 lib/argparse/rte_argparse.c            | 12 ++++++---
 lib/argparse/rte_argparse.h            |  6 +++--
 4 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 6b0d1524b5..382b6250b3 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -393,14 +393,14 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
@@ -434,13 +434,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -448,13 +448,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
@@ -503,14 +503,14 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -555,14 +555,14 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
@@ -618,14 +618,14 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -633,13 +633,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
@@ -671,7 +671,7 @@ test_argparse_pos_autosave_parse_int(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
@@ -738,7 +738,7 @@ test_argparse_pos_callback_parse_int(void)
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 26fc9fca4d..6398b5a954 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -138,6 +138,15 @@ ABI Changes
 
 * No ABI change that would break compatibility with 24.11.
 
+* argparse: The experimental "argparse" library has had the following updates:
+   * The main parsing function, ``rte_argparse_parse()``,
+     now returns the number of arguments parsed on success, rather than zero.
+     It still returns a negative value on error.
+   * When parsing a list of arguments,
+     ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
+     This behaviour mirrors the behaviour of the ``getopt()`` function,
+     as well as the behaviour of ``rte_eal_init()`` function.
+
 
 Known Issues
 ------------
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 49485861e3..13a3f4d9eb 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -606,6 +606,12 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 
 	for (i = 1; i < argc; i++) {
 		curr_argv = argv[i];
+
+		if (strcmp(argv[i], "--") == 0) {
+			i++;
+			break;
+		}
+
 		if (curr_argv[0] != '-') {
 			/* process positional parameters. */
 			position_index++;
@@ -669,7 +675,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		arg->flags |= ARG_ATTR_FLAG_PARSED_MASK;
 	}
 
-	return 0;
+	return i;
 }
 
 static uint32_t
@@ -785,7 +791,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		goto error;
 
 	ret = parse_args(obj, argc, argv, &show_help);
-	if (ret != 0)
+	if (ret < 0)
 		goto error;
 
 	if (show_help) {
@@ -793,7 +799,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		exit(0);
 	}
 
-	return 0;
+	return ret;
 
 error:
 	if (obj->exit_on_error)
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index 332184302e..fd5c33b319 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -173,7 +173,8 @@ struct rte_argparse {
  * @warning
  * @b EXPERIMENTAL: this API may change without prior notice.
  *
- * Parse parameters.
+ * Parse parameters passed until all are processed,
+ * or until a "--" parameter is encountered.
  *
  * @param obj
  *   Parser object.
@@ -183,7 +184,8 @@ struct rte_argparse {
  *   Array of parameters points.
  *
  * @return
- *   0 on success. Otherwise negative value is returned.
+ *   number of arguments parsed (>= 0) on success.
+ *   Otherwise negative error code is returned.
  */
 __rte_experimental
 int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
-- 
2.48.1


^ permalink raw reply	[relevance 10%]

* [PATCH v2 3/3] argparse: use enums to remove max-value defines in lists
  2025-06-03 15:32  4% ` [PATCH v2 " Bruce Richardson
  2025-06-03 15:32 10%   ` [PATCH v2 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
@ 2025-06-03 15:32  6%   ` Bruce Richardson
  2025-06-04  1:37  0%   ` [PATCH v2 0/3] argparse additions and rework fengchengwen
  2 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-03 15:32 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson, Chengwen Feng, Kevin Laatz, Ori Kam

The use of lists of #defines with _MAX entries at the end causes issues
for ABI compatibility as those MAX values often leak through to
applications and can cause issues when changed.

We can rework the code to increase type safety by splitting the flags
field and using enums for each set of values explicitly:

* One enum for whether an argument takes a parameter or not (or
  optionally takes one)
* One enum for the type of the argument.
* A separate flags field for any additional info - right now this is
  only to indicate an argument can appear more than once.

The use of the MAX field is no longer necessary using the enums as we
can use an inline function with a switch statement to check for valid
values. Compilers like GCC will warn when an enum value is missing from
the switch statement, easing future maintenance if enum values are
added.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test/test_argparse.c               | 182 ++++++++++++-------------
 doc/guides/rel_notes/release_25_07.rst |  35 +++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 143 ++++++++++---------
 lib/argparse/rte_argparse.h            |  92 ++++++-------
 6 files changed, 259 insertions(+), 217 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 382b6250b3..57b85c4bf0 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -79,8 +79,10 @@ test_argparse_callback(uint32_t index, const char *value, void *opaque)
 	.exit_on_error = false, \
 	.callback = test_argparse_callback, \
 	.args = { \
-		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
-		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
+		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, \
+			RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
+		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, \
+			RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
 		ARGPARSE_ARG_END(), \
 	}, \
 }
@@ -188,27 +190,20 @@ test_argparse_invalid_arg_help(void)
 static int
 test_argparse_invalid_has_val(void)
 {
-	uint64_t set_mask[] = { 0,
-				RTE_ARGPARSE_ARG_NO_VALUE,
-				RTE_ARGPARSE_ARG_OPTIONAL_VALUE
-			      };
+	uint64_t invalid_values[] = {
+			RTE_ARGPARSE_VALUE_NONE,
+			RTE_ARGPARSE_VALUE_OPTIONAL,
+	};
 	struct rte_argparse *obj;
 	uint32_t index;
 	int ret;
 
-	/* test optional arg don't config has-value. */
-	obj = test_argparse_init_obj();
-	obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
 	/* test positional arg don't config required-value. */
-	for (index = 0; index < RTE_DIM(set_mask); index++) {
+	for (index = 0; index < RTE_DIM(invalid_values); index++) {
 		obj = test_argparse_init_obj();
 		obj->args[0].name_long = "abc";
 		obj->args[0].name_short = NULL;
-		obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-		obj->args[0].flags |= set_mask[index];
+		obj->args[0].value_required = invalid_values[index];
 		ret = rte_argparse_parse(obj, default_argc, default_argv);
 		TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 	}
@@ -225,14 +220,15 @@ test_argparse_invalid_arg_saver(void)
 	/* test saver == NULL with val-type != 0. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test saver == NULL with callback is NULL. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->callback = NULL;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -241,15 +237,7 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
-	/* test saver != NULL with val-type is max. */
-	obj = test_argparse_init_obj();
-	obj->args[0].val_saver = (void *)1;
-	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -257,7 +245,8 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -272,9 +261,7 @@ test_argparse_invalid_arg_flags(void)
 
 	/* test set unused bits. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= ~(RTE_ARGPARSE_HAS_VAL_BITMASK |
-				RTE_ARGPARSE_VAL_TYPE_BITMASK |
-				RTE_ARGPARSE_ARG_SUPPORT_MULTI);
+	obj->args[0].flags |= ~(RTE_ARGPARSE_FLAG_SUPPORT_MULTI);
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -284,14 +271,15 @@ test_argparse_invalid_arg_flags(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT |
-			     RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test optional arg enabled multiple but prased by autosave. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -344,7 +332,6 @@ test_argparse_invalid_option(void)
 static int
 test_argparse_opt_autosave_parse_int_of_no_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -355,7 +342,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -363,7 +351,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -376,7 +365,6 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_required_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -387,7 +375,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -396,7 +385,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -404,7 +394,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	argv[2] = test_strdup("100a");
@@ -417,7 +408,6 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_optional_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -429,14 +419,16 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -444,13 +436,15 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -458,12 +452,14 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -498,7 +494,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -506,7 +502,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -549,7 +545,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -558,7 +554,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -566,12 +562,12 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -613,7 +609,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -621,7 +617,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -629,13 +625,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -643,7 +639,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	argv[1] = test_strdup("-t=100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -654,7 +650,6 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 static int
 test_argparse_pos_autosave_parse_int(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -666,7 +661,8 @@ test_argparse_pos_autosave_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -675,14 +671,16 @@ test_argparse_pos_autosave_parse_int(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test too much position parameters. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -727,12 +725,12 @@ test_argparse_pos_callback_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = "test-long2";
 	obj->args[1].name_short = NULL;
 	obj->args[1].val_saver = NULL;
 	obj->args[1].val_set = (void *)2;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[2].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -743,8 +741,8 @@ test_argparse_pos_callback_parse_int(void)
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
 	/* test positional callback parse failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("200a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -775,78 +773,80 @@ test_argparse_parse_type(void)
 	int ret;
 
 	/* test for int parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");
 
 	/* test for u8 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u8 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");
 
 	/* test for u16 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u16 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");
 
 	/* test for u32 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u32 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");
 
 	/* test for u64 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u64 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");
 
 	/* test for string parsing - all it does is save string, so all are valid */
 	const char *val_str;
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_STR, &val_str);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_STR, &val_str);
 	TEST_ASSERT(ret == 0, "Argparse parse a string failed unexpectedly!");
 
 	/* test for boolean parsing */
 	bool val_bool = false;
-	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (true) failed!");
-	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (false) failed!");
-	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (invalid) passed unexpectedly!");
-	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (numeric true) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			&val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (numeric false) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			&val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (numeric invalid) passed unexpectedly!");
 	return 0;
 }
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6398b5a954..ef2d7076c5 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -120,6 +120,10 @@ API Changes
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* argparse: The ``rte_argparse_arg`` structure used for defining arguments has been updated.
+    See next section, :ref:`ABI-Changes` for details.
+
+.. _ABI-Changes:
 
 ABI Changes
 -----------
@@ -146,6 +150,37 @@ ABI Changes
      ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
      This behaviour mirrors the behaviour of the ``getopt()`` function,
      as well as the behaviour of ``rte_eal_init()`` function.
+   * The ``rte_argparse_arg`` structure used for defining arguments has been updated
+     to separate out into separate fields the options for:
+
+     #. Whether the argument is required or optional.
+     #. What the type of the argument is (in case of saving the parameters automatically).
+     #. Any other flags - of which there is only one, ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI``, at this time.
+
+   * With the splitting of the flags into separate enums for categories,
+     the names of the flags have been changed to better reflect their purpose.
+     The flags/enum values are:
+
+     * For the ``value_required`` field:
+
+        * ``RTE_ARGPARSE_VALUE_NONE``
+        * ``RTE_ARGPARSE_VALUE_REQUIRED``
+        * ``RTE_ARGPARSE_VALUE_OPTIONAL``
+
+     * For the ``value_type`` field:
+
+        * ``RTE_ARGPARSE_VALUE_TYPE_NONE`` (No argument value type is specified, callback is to be used for processing.)
+        * ``RTE_ARGPARSE_VALUE_TYPE_INT``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U8``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U16``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U32``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U64``
+        * ``RTE_ARGPARSE_VALUE_TYPE_STR``
+        * ``RTE_ARGPARSE_VALUE_TYPE_BOOL``
+
+     * Other flags:
+
+        * ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI`` (Allows the argument to be specified multiple times.)
 
 
 Known Issues
diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c
index acceae6b7b..5ba0aaa40b 100644
--- a/examples/dma/dmafwd.c
+++ b/examples/dma/dmafwd.c
@@ -644,43 +644,43 @@ dma_parse_args(int argc, char **argv, unsigned int nb_ports)
 		.args = {
 			{ "--mac-updating", NULL, "Enable MAC addresses updating",
 			  &mac_updating, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--no-mac-updating", NULL, "Disable MAC addresses updating",
 			  &mac_updating, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--portmask", "-p", "hexadecimal bitmask of ports to configure",
 			  NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--nb-queue", "-q", "number of RX queues per port (default is 1)",
 			  &nb_queues, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--copy-type", "-c", "type of copy: sw|hw",
 			  NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--ring-size", "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode",
 			  &ring_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--dma-batch-size", "-b", "number of requests per DMA batch",
 			  &dma_batch_sz, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--max-frame-size", "-f", "max frame size",
 			  &max_frame_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--force-min-copy-size", "-m", "force a minimum copy length, even for smaller packets",
 			  &force_min_copy_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--stats-interval", "-i", "interval, in seconds, between stats prints (default is 1)",
 			  &stats_interval, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/examples/flow_filtering/main.c b/examples/flow_filtering/main.c
index c0bc1938ce..9c429a8335 100644
--- a/examples/flow_filtering/main.c
+++ b/examples/flow_filtering/main.c
@@ -274,11 +274,11 @@ flow_filtering_parse_args(int argc, char **argv)
 		.args = {
 			{ "--template", NULL, "Enable template API flow",
 			  &use_template_api, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--non-template", NULL, "Enable non template API flow",
 			  &use_template_api, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 13a3f4d9eb..b5b8467dea 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -30,30 +30,48 @@ is_arg_positional(const struct rte_argparse_arg *arg)
 	return arg->name_long[0] != '-';
 }
 
-static inline uint32_t
-arg_attr_has_val(const struct rte_argparse_arg *arg)
+static inline bool
+is_valid_has_value_field(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_HAS_VAL_BITMASK, arg->flags);
+	switch (arg->value_required) {
+	case RTE_ARGPARSE_VALUE_NONE:
+	case RTE_ARGPARSE_VALUE_OPTIONAL:
+	case RTE_ARGPARSE_VALUE_REQUIRED:
+		return true;
+	/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }
 
-static inline uint32_t
-arg_attr_val_type(const struct rte_argparse_arg *arg)
-{
-	return RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, arg->flags);
+static inline bool
+is_valid_value_type_field(const struct rte_argparse_arg *arg)
+{
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return true;
+	/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }
 
+
 static inline bool
 arg_attr_flag_multi(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_ARG_SUPPORT_MULTI, arg->flags);
+	return (arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI) != 0;
 }
 
 static inline uint64_t
 arg_attr_unused_bits(const struct rte_argparse_arg *arg)
 {
-#define USED_BIT_MASK	(RTE_ARGPARSE_HAS_VAL_BITMASK | \
-			 RTE_ARGPARSE_VAL_TYPE_BITMASK | \
-			 RTE_ARGPARSE_ARG_SUPPORT_MULTI)
+#define USED_BIT_MASK	(RTE_ARGPARSE_FLAG_SUPPORT_MULTI)
 	return arg->flags & ~USED_BIT_MASK;
 }
 
@@ -110,56 +128,51 @@ verify_arg_help(const struct rte_argparse_arg *arg)
 static int
 verify_arg_has_val(const struct rte_argparse_arg *arg)
 {
-	uint32_t has_val = arg_attr_has_val(arg);
-
+	if (!is_valid_has_value_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value field!", arg->name_long);
+		return -EINVAL;
+	}
 	if (is_arg_positional(arg)) {
-		if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE)
+		if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED)
 			return 0;
 		ARGPARSE_LOG(ERR, "argument %s is positional, must config required-val!",
 			     arg->name_long);
 		return -EINVAL;
 	}
 
-	if (has_val == 0) {
-		ARGPARSE_LOG(ERR, "argument %s is optional, has-value config wrong!",
-			     arg->name_long);
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
 static int
 verify_arg_saver(const struct rte_argparse *obj, uint32_t index)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	const struct rte_argparse_arg *arg = &obj->args[index];
-	uint32_t val_type = arg_attr_val_type(arg);
-	uint32_t has_val = arg_attr_has_val(arg);
 
 	if (arg->val_saver == NULL) {
-		if (val_type != 0) {
+		if (arg->value_type != RTE_ARGPARSE_VALUE_TYPE_NONE) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, value-type should not be set!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		if (obj->callback == NULL) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, but callback is NULL!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		return 0;
 	}
 
-	if (val_type == 0 || val_type >= cmp_max) {
-		ARGPARSE_LOG(ERR, "argument %s value-type config wrong!", arg->name_long);
+	/* check value_type field */
+	if (!is_valid_value_type_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value-type field!", arg->name_long);
+		return -EINVAL;
+	}
+	if (arg->value_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "missing value-type for argument %s!", arg->name_long);
 		return -EINVAL;
 	}
 
-	if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) {
+	if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED && arg->val_set != NULL) {
 		ARGPARSE_LOG(ERR, "argument %s has required value, value-set should be NULL!",
 			     arg->name_long);
 		return -EINVAL;
@@ -180,7 +193,7 @@ verify_arg_flags(const struct rte_argparse *obj, uint32_t index)
 		return -EINVAL;
 	}
 
-	if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI))
+	if (!(arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI))
 		return 0;
 
 	if (is_arg_positional(arg)) {
@@ -544,26 +557,27 @@ parse_arg_bool(struct rte_argparse_arg *arg, const char *value)
 static int
 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value)
 {
-	static struct {
-		int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value);
-	} map[] = {
-		/* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */
-		{ NULL          },
-		{ parse_arg_int },
-		{ parse_arg_u8  },
-		{ parse_arg_u16 },
-		{ parse_arg_u32 },
-		{ parse_arg_u64 },
-		{ parse_arg_str},
-		{ parse_arg_bool },
-	};
-	uint32_t index = arg_attr_val_type(arg);
-	int ret = -EINVAL;
-
-	if (index > 0 && index < RTE_DIM(map))
-		ret = map[index].f_parse_type(arg, value);
-
-	return ret;
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+		ARGPARSE_LOG(ERR, "argument %s doesn't specify a value-type!", arg->name_long);
+		return -EINVAL;
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+		return parse_arg_int(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+		return parse_arg_u8(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+		return parse_arg_u16(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+		return parse_arg_u32(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+		return parse_arg_u64(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+		return parse_arg_str(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return parse_arg_bool(arg, value);
+	/* omit default case so compiler warns on missing enum values */
+	}
+	return -EINVAL;
 }
 
 /* arg_parse indicates the name entered by the user, which can be long-name or short-name. */
@@ -616,7 +630,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 			/* process positional parameters. */
 			position_index++;
 			if (position_index > position_count) {
-				ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv);
+				ARGPARSE_LOG(ERR, "too many positional arguments %s!", curr_argv);
 				return -EINVAL;
 			}
 			arg = find_position_arg(obj, position_index);
@@ -641,23 +655,21 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		}
 
 		if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) {
-			ARGPARSE_LOG(ERR, "argument %s should not occur multiple!",
-				     arg_name);
+			ARGPARSE_LOG(ERR, "argument %s should not occur multiple times!", arg_name);
 			return -EINVAL;
 		}
 
 		value = (has_equal != NULL ? has_equal + 1 : NULL);
-		if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) {
+		if (arg->value_required == RTE_ARGPARSE_VALUE_NONE) {
 			if (value != NULL) {
-				ARGPARSE_LOG(ERR, "argument %s should not take value!",
-					     arg_name);
+				ARGPARSE_LOG(ERR, "argument %s should not take value!", arg_name);
 				return -EINVAL;
 			}
-		} else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) {
+		} else if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED) {
 			if (value == NULL) {
 				if (i >= argc - 1) {
 					ARGPARSE_LOG(ERR, "argument %s doesn't have value!",
-						     arg_name);
+							arg_name);
 					return -EINVAL;
 				}
 				/* Set value and make i move next. */
@@ -809,21 +821,18 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 
 RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_argparse_parse_type, 24.03)
 int
-rte_argparse_parse_type(const char *str, uint64_t val_type, void *val)
+rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	struct rte_argparse_arg arg = {
 		.name_long = str,
 		.name_short = NULL,
 		.val_saver = val,
 		.val_set = NULL,
-		.flags = val_type,
+		.value_type = val_type,
 	};
-	uint32_t value_type = arg_attr_val_type(&arg);
-
-	if (value_type == 0 || value_type >= cmp_max)
+	if (val_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "argument %s doesn't have value-type!", str);
 		return -EINVAL;
-
+	}
 	return parse_arg_autosave(&arg, str);
 }
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index fd5c33b319..4b650ea282 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -37,49 +37,44 @@
 extern "C" {
 #endif
 
-/**@{@name Flag definition (in bitmask form) for an argument
- *
- * @note Bits[0~1] represent the argument whether has value,
- * bits[2~9] represent the value type which used when autosave.
- *
- * @see struct rte_argparse_arg::flags
- */
-/** The argument has no value. */
-#define RTE_ARGPARSE_ARG_NO_VALUE       RTE_SHIFT_VAL64(1, 0)
-/** The argument must have a value. */
-#define RTE_ARGPARSE_ARG_REQUIRED_VALUE RTE_SHIFT_VAL64(2, 0)
-/** The argument has optional value. */
-#define RTE_ARGPARSE_ARG_OPTIONAL_VALUE RTE_SHIFT_VAL64(3, 0)
-/** The argument's value is int type. */
-#define RTE_ARGPARSE_ARG_VALUE_INT      RTE_SHIFT_VAL64(1, 2)
-/** The argument's value is uint8 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U8       RTE_SHIFT_VAL64(2, 2)
-/** The argument's value is uint16 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U16      RTE_SHIFT_VAL64(3, 2)
-/** The argument's value is uint32 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U32      RTE_SHIFT_VAL64(4, 2)
-/** The argument's value is uint64 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U64      RTE_SHIFT_VAL64(5, 2)
-/** The argument's value is string type. */
-#define RTE_ARGPARSE_ARG_VALUE_STR      RTE_SHIFT_VAL64(6, 2)
-/** The argument's value is boolean flag type. */
-#define RTE_ARGPARSE_ARG_VALUE_BOOL     RTE_SHIFT_VAL64(7, 2)
-/** Max value type. */
-#define RTE_ARGPARSE_ARG_VALUE_MAX      RTE_SHIFT_VAL64(8, 2)
 /**
- * Flag for that argument support occur multiple times.
- * This flag can be set only when the argument is optional.
- * When this flag is set, the callback type must be used for parsing.
+ * enum defining whether an argument takes a value or not.
  */
-#define RTE_ARGPARSE_ARG_SUPPORT_MULTI  RTE_BIT64(10)
-/** Reserved for this library implementation usage. */
-#define RTE_ARGPARSE_ARG_RESERVED_FIELD RTE_GENMASK64(63, 48)
-/**@}*/
+enum rte_argparse_value_required {
+	/** The argument takes no value. */
+	RTE_ARGPARSE_VALUE_NONE,
+	/** The argument must have a value. */
+	RTE_ARGPARSE_VALUE_REQUIRED,
+	/** The argument has optional value. */
+	RTE_ARGPARSE_VALUE_OPTIONAL,
+};
 
-/** Bitmask used to get the argument whether has value. */
-#define RTE_ARGPARSE_HAS_VAL_BITMASK	RTE_GENMASK64(1, 0)
-/** Bitmask used to get the argument's value type. */
-#define RTE_ARGPARSE_VAL_TYPE_BITMASK	RTE_GENMASK64(9, 2)
+/** enum defining the type of the argument, integer, boolean or just string */
+enum rte_argparse_value_type {
+	/** Argument takes no value, or value type not specified.
+	 * Should be used when argument is to be handled via callback.
+	 */
+	RTE_ARGPARSE_VALUE_TYPE_NONE = 0,
+	/** The argument's value is int type. */
+	RTE_ARGPARSE_VALUE_TYPE_INT,
+	/** The argument's value is uint8 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U8,
+	/** The argument's value is uint16 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U16,
+	/** The argument's value is uint32 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U32,
+	/** The argument's value is uint64 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U64,
+	/** The argument's value is string type. */
+	RTE_ARGPARSE_VALUE_TYPE_STR,
+	/** The argument's value is boolean flag type. */
+	RTE_ARGPARSE_VALUE_TYPE_BOOL,
+};
+
+/** Additional flags which may be specified for each argument */
+enum rte_argparse_arg_flags {
+	RTE_ARGPARSE_FLAG_SUPPORT_MULTI = RTE_BIT32(0),
+};
 
 /**
  * A structure used to hold argument's configuration.
@@ -105,10 +100,8 @@ struct rte_argparse_arg {
 
 	/**
 	 * Saver for the argument's value.
-	 * 1) If the filed is NULL, the callback way is used for parsing
-	 *    argument.
-	 * 2) If the field is not NULL, the autosave way is used for parsing
-	 *    argument.
+	 * 1) If this field is NULL, the callback is used for parsing argument.
+	 * 2) If this field is not NULL, the argument's value will be automatically saved.
 	 */
 	void *val_saver;
 	/**
@@ -123,8 +116,13 @@ struct rte_argparse_arg {
 	 */
 	void *val_set;
 
-	/** Flag definition (RTE_ARGPARSE_ARG_*) for the argument. */
-	uint64_t flags;
+	/** Specify if the argument takes a value, @see enum rte_argparse_value_required. */
+	enum rte_argparse_value_required value_required;
+	/** The type of the argument, @see enum rte_argparse_value_type. */
+	enum rte_argparse_value_type value_type;
+
+	/** any additional flags for this argument */
+	uint32_t flags;
 };
 
 /**
@@ -207,7 +205,7 @@ int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
  *   0 on success. Otherwise negative value is returned.
  */
 __rte_experimental
-int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val);
+int rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val);
 
 #ifdef __cplusplus
 }
-- 
2.48.1


^ permalink raw reply	[relevance 6%]

* Re: [PATCH 2/3] argparse: make argparse EAL-args compatible
  2025-06-03  8:44  0%   ` fengchengwen
@ 2025-06-03 15:33  0%     ` Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-03 15:33 UTC (permalink / raw)
  To: fengchengwen; +Cc: dev

On Tue, Jun 03, 2025 at 04:44:42PM +0800, fengchengwen wrote:
> On 2025/5/27 17:21, Bruce Richardson wrote:
> > The argparse library was missing two key features which made it
> > unsuitable for use by EAL or any program wanting similar behaviour.
> > 
> > 1. It didn't stop parsing arguments when it hit a "--" character
> > 2. It never returned the number of arguments parsed
> > 
> > Fix both these issues - the latter is a change to the ABI, since we now
> > return >= 0 rather than == 0 on success. However, the ABI is still
> > experimental so we can make exactly these sorts of tweaks to it.
> > 
> > Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> > ---
> >  app/test/test_argparse.c               | 46 +++++++++++++-------------
> >  doc/guides/rel_notes/release_25_07.rst |  9 +++++
> >  lib/argparse/rte_argparse.c            | 12 +++++--
> >  lib/argparse/rte_argparse.h            |  3 +-
> >  4 files changed, 43 insertions(+), 27 deletions(-)
> > 
> > diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
> > index 6b0d1524b5..a907fbe53f 100644
> > --- a/app/test/test_argparse.c
> > +++ b/app/test/test_argparse.c
> > @@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
> >  	argv[0] = test_strdup(obj->prog_name);
> >  	argv[1] = test_strdup("--test-long");
> >  	ret = rte_argparse_parse(obj, 2, argv);
> > -	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
> > +	TEST_ASSERT(ret >= 0, "Argparse parse expect success!");
> 
> Please compared with specific number, eg. TEST_ASSERT(ret == 2, "xxx");
> 
> ...
> 
> >  
> > @@ -780,7 +780,7 @@ test_argparse_parse_type(void)
> >  	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
> >  	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
> >  	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
> > -	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
> > +	TEST_ASSERT(ret >= 0, "Argparse parse type expect failed!");
> 
> No need for rte_argparse_parse_type() API, this API still return 0 if success.
> 
> ...
> 
> > diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
> > index 332184302e..8cdb3195cb 100644
> > --- a/lib/argparse/rte_argparse.h
> > +++ b/lib/argparse/rte_argparse.h
> > @@ -183,7 +183,8 @@ struct rte_argparse {
> >   *   Array of parameters points.
> >   *
> >   * @return
> > - *   0 on success. Otherwise negative value is returned.
> > + *   number of arguments parsed (>= 0) on success.
> > + *   Otherwise negative error code is returned.
> 
> Please add note for "stops processing arguments when a ``--`` argument is encountered".
> 

Thanks for the review. All 3 comments fixed in v2 patchset.

^ permalink raw reply	[relevance 0%]

* Re: [PATCH 3/3] argparse: use enums to remove max-value defines in lists
  2025-05-27  9:21  6% ` [PATCH 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
@ 2025-06-04  1:22  0%   ` fengchengwen
  0 siblings, 0 replies; 153+ results
From: fengchengwen @ 2025-06-04  1:22 UTC (permalink / raw)
  To: Bruce Richardson, dev

It looks more clear and user-friendly for splitting the flag.
Acked-by: Chengwen Feng <fengchengwen@huawei.com>

On 2025/5/27 17:21, Bruce Richardson wrote:
> The use of lists of #defines with _MAX entries at the end causes issues
> for ABI compatibility as those MAX values often leak through to
> applications and can cause issues when changed.
> 
> We can rework the code to increase type safety by splitting the flags
> field and using enums for each set of values explicitly:
> 
> * One enum for whether an argument takes a parameter or not (or
>   optionally takes one)
> * One enum for the type of the argument.
> * A separate flags field for any additional info - right now this is
>   only to indicate an argument can appear more than once.
> 
> The use of the MAX field is no longer necessary using the enums as we
> can use an inline function with a switch statement to check for valid
> values. Compilers like GCC will warn when an enum value is missing from
> the switch statement, easing future maintenance if enum values are
> added.
> 
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply	[relevance 0%]

* Re: [PATCH v2 0/3] argparse additions and rework
  2025-06-03 15:32  4% ` [PATCH v2 " Bruce Richardson
  2025-06-03 15:32 10%   ` [PATCH v2 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
  2025-06-03 15:32  6%   ` [PATCH v2 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
@ 2025-06-04  1:37  0%   ` fengchengwen
  2 siblings, 0 replies; 153+ results
From: fengchengwen @ 2025-06-04  1:37 UTC (permalink / raw)
  To: Bruce Richardson, dev

Thanks for this patchset, Bruce.
Series-acked-by: Chengwen Feng <fengchengwen@huawei.com>

On 2025/6/3 23:32, Bruce Richardson wrote:
> This patchset is based off the work to adjust how we do argument parsing
> inside EAL. To enable argparse to be effectively used for EAL, we have
> new features and some changes in the first two patches, which are
> relatively small - though are ABI/API affecting.
> 
> These add support for saving off strings and boolean values, have argparse
> stop parsing at a "--", and finally have argparse return the number of
> arguments actually parsed on success.
> 
> The third patch is a bigger change. It was inspired by the fact that
> when adding the boolean and string support we had to update some
> "MAX" value defines used in the code. This is obviously not good from
> an ABI/API perspective, once the library becomes part of the stable ABI.
> In order to remove these MAX values, patch 3 looks to replace the
> #define values with enums - which means some rework splitting the
> various flags into separate categories, and similarly splitting the
> single "flags" field with separate fields specifying if an argument
> value is required, what type that value should have, and then a
> final smaller field for any additional modifiers.
> 
> v2: minor changes to patch 2 (see log at end of that patch)
> 
> Bruce Richardson (3):
>   argparse: add support for string and boolean args
>   argparse: make argparse EAL-args compatible
>   argparse: use enums to remove max-value defines in lists
> 
>  app/test/test_argparse.c               | 229 ++++++++++++++-----------
>  doc/guides/rel_notes/release_25_07.rst |  50 ++++++
>  examples/dma/dmafwd.c                  |  20 +--
>  examples/flow_filtering/main.c         |   4 +-
>  lib/argparse/rte_argparse.c            | 184 ++++++++++++--------
>  lib/argparse/rte_argparse.h            |  94 +++++-----
>  6 files changed, 355 insertions(+), 226 deletions(-)
> 
> --
> 2.48.1
> 


^ permalink raw reply	[relevance 0%]

* Re: [PATCH 1/7] ethdev: add support to provide link type
  @ 2025-06-05 15:22  3% ` Stephen Hemminger
  0 siblings, 0 replies; 153+ results
From: Stephen Hemminger @ 2025-06-05 15:22 UTC (permalink / raw)
  To: skori
  Cc: Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev, Nithin Dabilpuram

On Thu, 5 Jun 2025 14:36:36 +0530
<skori@marvell.com> wrote:

> @@ -341,6 +354,7 @@ struct rte_eth_link {
>  			uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
>  			uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
>  			uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
> +			uint16_t link_type    : 5;  /**< RTE_ETH_LINK_TYPE_XXX */
>  		};
>  	};
>  };

ABI change will have to wait for 25.11

^ permalink raw reply	[relevance 3%]

* [PATCH v2] pcapng: allow any protocol link type for the interface block
    2025-05-29 19:31  3% ` Stephen Hemminger
@ 2025-06-05 23:02  3% ` Schneide
  2025-06-06 21:52  3% ` Schneide
  2025-06-09 21:19  3% ` Schneide
  3 siblings, 0 replies; 153+ results
From: Schneide @ 2025-06-05 23:02 UTC (permalink / raw)
  To: dev, Thomas Monjalon, Reshma Pattan, Stephen Hemminger,
	Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram, Zhirun Yan
  Cc: Dylan Schneider

From: Dylan Schneider <schneide@qti.qualcomm.com>

Allow the user to specify protocol link type when creating pcapng files.
This change is needed to specify the protocol type in the pcapng file,
DLT_EN10MB specifies ethernet packets only. This will allow dissectors
for other protocols to be used on files generated by pcapng.

Includes a breaking change to rte_pcapng_add_interface to add link_type
parameter. Existing calls to the function have been updated to pass
DLT_EN10MB for the link type argument.

Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
Cc: stephen@networkplumber.org
---
 .mailmap                               |  1 +
 app/dumpcap/main.c                     |  4 ++--
 app/test/test_pcapng.c                 |  8 ++++----
 doc/guides/rel_notes/release_25_07.rst |  5 ++++-
 lib/graph/graph_pcap.c                 |  4 +++-
 lib/pcapng/rte_pcapng.c                | 21 +++++++++++++++------
 lib/pcapng/rte_pcapng.h                |  4 +++-
 7 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/.mailmap b/.mailmap
index 91e08f4a1f..a585124832 100644
--- a/.mailmap
+++ b/.mailmap
@@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
 Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
 Duncan Bellamy <dunk@denkimushi.com>
 Dustin Lundquist <dustin@null-ptr.net>
+Dylan Schneider <schneide@qti.qualcomm.com>
 Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
 Ed Czeck <ed.czeck@atomicrules.com>
 Eduard Serra <eserra@vmware.com>
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..e0e2b26269 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
 		free(os);
 
 		TAILQ_FOREACH(intf, &interfaces, next) {
-			if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
-						     intf->ifdescr, intf->opts.filter) < 0)
+			if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
+				intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
 				rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
 					intf->port);
 		}
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 8f2cff36c3..bcf99724fa 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -345,7 +345,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface to the file */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u\n", port_id);
@@ -353,7 +353,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface with ifname and ifdescr */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       "myeth", "Some long description", NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u with ifname\n", port_id);
@@ -361,7 +361,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface with filter */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, "tcp port 8080");
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u with filter\n", port_id);
@@ -406,7 +406,7 @@ test_write_packets(void)
 	}
 
 	/* Add interface to the file */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u\n", port_id);
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de..2396c7b014 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -108,7 +108,10 @@ API Changes
    This section is a comment. Do not overwrite or remove it.
    Also, make sure to start the actual text at the margin.
    =======================================================
-
+* pcapng: Changed the API for adding interfaces to include a link type argument.
+  The link type was previously hardcoded to the ethernet link type in the API.
+  This argument is added to ``rte_pcapng_add_interface``.
+  These functions are versioned to retain binary compatibility until the next LTS release.
 
 ABI Changes
 -----------
diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
index 89525f1220..13d86b7a18 100644
--- a/lib/graph/graph_pcap.c
+++ b/lib/graph/graph_pcap.c
@@ -11,6 +11,8 @@
 #include <rte_mbuf.h>
 #include <rte_pcapng.h>
 
+#include <pcap/pcap.h>
+
 #include "rte_graph_worker.h"
 
 #include "graph_pcap_private.h"
@@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)
 
 	/* Add the configured interfaces as possible capture ports */
 	RTE_ETH_FOREACH_DEV(portid) {
-		ret = rte_pcapng_add_interface(pcapng_fd, portid,
+		ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
 					       NULL, NULL, NULL);
 		if (ret < 0) {
 			graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index cacbefdc50..95c1a4d635 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -200,11 +200,10 @@ pcapng_section_block(rte_pcapng_t *self,
 }
 
 /* Write an interface block for a DPDK port */
-RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
-int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
-			 const char *ifname, const char *ifdescr,
-			 const char *filter)
+RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
+			(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
+			const char *ifname, const char *ifdescr,
+			const char *filter))
 {
 	struct pcapng_interface_block *hdr;
 	struct rte_eth_dev_info dev_info;
@@ -274,7 +273,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
 	hdr = (struct pcapng_interface_block *)buf;
 	*hdr = (struct pcapng_interface_block) {
 		.block_type = PCAPNG_INTERFACE_BLOCK,
-		.link_type = 1,		/* DLT_EN10MB - Ethernet */
+		.link_type = link_type,
 		.block_length = len,
 	};
 
@@ -319,6 +318,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
 	return write(self->outfd, buf, len);
 }
 
+RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
+				  (rte_pcapng_t *self, uint16_t port,
+				   const char *ifname, const char *ifdescr,
+				   const char *filter))
+{
+	/* Call the new version with a default link_type (Ethernet) */
+	return rte_pcapng_add_interface(self, port, 1 /* DLT_EN10MB */,
+									ifname, ifdescr, filter);
+}
+
 /*
  * Write an Interface statistics block at the end of capture.
  */
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index 48f2b57564..9880d415c4 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
  *  The handle to the packet capture file
  * @param port
  *  The Ethernet port to report stats on.
+ * @param link_type
+ *   The link type (e.g., DLT_EN10MB).
  * @param ifname (optional)
  *  Interface name to record in the file.
  *  If not specified, name will be constructed from port
@@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
  * must be added.
  */
 int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
+rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
 			 const char *ifname, const char *ifdescr,
 			 const char *filter);
 
-- 
2.27.0


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2 1/1] ethdev: add support to provide link type
  @ 2025-06-06 15:23  3%     ` Stephen Hemminger
  2025-06-10  5:02  0%       ` [EXTERNAL] " Sunil Kumar Kori
  0 siblings, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-06-06 15:23 UTC (permalink / raw)
  To: Morten Brørup
  Cc: skori, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev,
	Nithin Dabilpuram, jerinj

On Fri, 6 Jun 2025 11:54:52 +0200
Morten Brørup <mb@smartsharesystems.com> wrote:

> > From: skori@marvell.com [mailto:skori@marvell.com]
> > Sent: Friday, 6 June 2025 11.28
> > 
> > From: Sunil Kumar Kori <skori@marvell.com>
> > 
> > Adding link type parameter to provide the type
> > of port like twisted pair, fibre etc.
> > 
> > Also added an API to convert the RTE_ETH_LINK_TYPE_XXX
> > to a readable string.
> > 
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > Signed-off-by: Sunil Kumar Kori <skori@marvell.com>
> > ---
> > +/**@{@name PORT type
> > + * Ethernet port type
> > + */
> > +#define RTE_ETH_LINK_TYPE_NONE  0x00 /**< Not defined */
> > +#define RTE_ETH_LINK_TYPE_TP    0x01 /**< Twisted Pair */
> > +#define RTE_ETH_LINK_TYPE_AUI   0x02 /**< Attachment Unit Interface */
> > +#define RTE_ETH_LINK_TYPE_MII   0x03 /**< Media Independent Interface
> > */
> > +#define RTE_ETH_LINK_TYPE_FIBRE 0x04 /**< Fibre */
> > +#define RTE_ETH_LINK_TYPE_BNC   0x05 /**< BNC */
> > +#define RTE_ETH_LINK_TYPE_DA    0x06 /**< Direct Attach copper */
> > +#define RTE_ETH_LINK_TYPE_OTHER 0x1F /**< Other type */
> > +/**@}*/  
> 
> I don't see the use case for this patch, when only ancient interface types are defined.
> How about RMII, GMII, RGMII, XGMII, XAUI, XAUI4, CAUI4, SFP, SFP+, SFP28, etc.
> 
> Please don't blindly port stuff from Linux to DPDK.
> 
> 

Agree with Morten here.
Plus, it isn't really much use to user unless all drivers report it.
Also, it breaks ABI (see build failures).

^ permalink raw reply	[relevance 3%]

* [PATCH v2] pcapng: allow any protocol link type for the interface block
    2025-05-29 19:31  3% ` Stephen Hemminger
  2025-06-05 23:02  3% ` [PATCH v2] " Schneide
@ 2025-06-06 21:52  3% ` Schneide
  2025-06-08 22:34  0%   ` Stephen Hemminger
  2025-06-16 14:29  2%   ` Dylan Schneider
  2025-06-09 21:19  3% ` Schneide
  3 siblings, 2 replies; 153+ results
From: Schneide @ 2025-06-06 21:52 UTC (permalink / raw)
  To: dev, Thomas Monjalon, Reshma Pattan, Stephen Hemminger,
	Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram, Zhirun Yan
  Cc: Dylan Schneider

From: Dylan Schneider <schneide@qti.qualcomm.com>

Allow the user to specify protocol link type when creating pcapng files.
This change is needed to specify the protocol type in the pcapng file,
DLT_EN10MB specifies ethernet packets only. This will allow dissectors
for other protocols to be used on files generated by pcapng.

Includes a breaking change to rte_pcapng_add_interface to add link_type
parameter. Existing calls to the function have been updated to pass
DLT_EN10MB for the link type argument.

Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
Cc: stephen@networkplumber.org
---
 .mailmap                               |  1 +
 app/dumpcap/main.c                     |  4 ++--
 app/test/test_pcapng.c                 |  8 ++++----
 doc/guides/rel_notes/release_25_07.rst |  5 ++++-
 lib/graph/graph_pcap.c                 |  4 +++-
 lib/pcapng/meson.build                 |  2 ++
 lib/pcapng/rte_pcapng.c                | 21 +++++++++++++++------
 lib/pcapng/rte_pcapng.h                |  4 +++-
 8 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/.mailmap b/.mailmap
index 91e08f4a1f..a585124832 100644
--- a/.mailmap
+++ b/.mailmap
@@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
 Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
 Duncan Bellamy <dunk@denkimushi.com>
 Dustin Lundquist <dustin@null-ptr.net>
+Dylan Schneider <schneide@qti.qualcomm.com>
 Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
 Ed Czeck <ed.czeck@atomicrules.com>
 Eduard Serra <eserra@vmware.com>
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..e0e2b26269 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
 		free(os);
 
 		TAILQ_FOREACH(intf, &interfaces, next) {
-			if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
-						     intf->ifdescr, intf->opts.filter) < 0)
+			if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
+				intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
 				rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
 					intf->port);
 		}
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 8f2cff36c3..bcf99724fa 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -345,7 +345,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface to the file */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u\n", port_id);
@@ -353,7 +353,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface with ifname and ifdescr */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       "myeth", "Some long description", NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u with ifname\n", port_id);
@@ -361,7 +361,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface with filter */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, "tcp port 8080");
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u with filter\n", port_id);
@@ -406,7 +406,7 @@ test_write_packets(void)
 	}
 
 	/* Add interface to the file */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u\n", port_id);
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de..2396c7b014 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -108,7 +108,10 @@ API Changes
    This section is a comment. Do not overwrite or remove it.
    Also, make sure to start the actual text at the margin.
    =======================================================
-
+* pcapng: Changed the API for adding interfaces to include a link type argument.
+  The link type was previously hardcoded to the ethernet link type in the API.
+  This argument is added to ``rte_pcapng_add_interface``.
+  These functions are versioned to retain binary compatibility until the next LTS release.
 
 ABI Changes
 -----------
diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
index 89525f1220..13d86b7a18 100644
--- a/lib/graph/graph_pcap.c
+++ b/lib/graph/graph_pcap.c
@@ -11,6 +11,8 @@
 #include <rte_mbuf.h>
 #include <rte_pcapng.h>
 
+#include <pcap/pcap.h>
+
 #include "rte_graph_worker.h"
 
 #include "graph_pcap_private.h"
@@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)
 
 	/* Add the configured interfaces as possible capture ports */
 	RTE_ETH_FOREACH_DEV(portid) {
-		ret = rte_pcapng_add_interface(pcapng_fd, portid,
+		ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
 					       NULL, NULL, NULL);
 		if (ret < 0) {
 			graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
index 4549925d41..3aa7ba5155 100644
--- a/lib/pcapng/meson.build
+++ b/lib/pcapng/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_pcapng.c')
 headers = files('rte_pcapng.h')
 
 deps += ['ethdev']
+
+use_function_versioning = true
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index cacbefdc50..f18af25983 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -200,11 +200,10 @@ pcapng_section_block(rte_pcapng_t *self,
 }
 
 /* Write an interface block for a DPDK port */
-RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
-int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
-			 const char *ifname, const char *ifdescr,
-			 const char *filter)
+RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
+			(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
+			const char *ifname, const char *ifdescr,
+			const char *filter))
 {
 	struct pcapng_interface_block *hdr;
 	struct rte_eth_dev_info dev_info;
@@ -274,7 +273,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
 	hdr = (struct pcapng_interface_block *)buf;
 	*hdr = (struct pcapng_interface_block) {
 		.block_type = PCAPNG_INTERFACE_BLOCK,
-		.link_type = 1,		/* DLT_EN10MB - Ethernet */
+		.link_type = link_type,
 		.block_length = len,
 	};
 
@@ -319,6 +318,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
 	return write(self->outfd, buf, len);
 }
 
+RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
+				  (rte_pcapng_t *self, uint16_t port,
+				   const char *ifname, const char *ifdescr,
+				   const char *filter))
+{
+	/* Call the new version with a default link_type (Ethernet) */
+	return rte_pcapng_add_interface(self, port, DLT_EN10MB,
+									ifname, ifdescr, filter);
+}
+
 /*
  * Write an Interface statistics block at the end of capture.
  */
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index 48f2b57564..9880d415c4 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
  *  The handle to the packet capture file
  * @param port
  *  The Ethernet port to report stats on.
+ * @param link_type
+ *   The link type (e.g., DLT_EN10MB).
  * @param ifname (optional)
  *  Interface name to record in the file.
  *  If not specified, name will be constructed from port
@@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
  * must be added.
  */
 int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
+rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
 			 const char *ifname, const char *ifdescr,
 			 const char *filter);
 
-- 
2.27.0


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2] pcapng: allow any protocol link type for the interface block
  2025-06-06 21:52  3% ` Schneide
@ 2025-06-08 22:34  0%   ` Stephen Hemminger
  2025-06-09 15:51  0%     ` Dylan Schneider
  2025-06-16 14:29  2%   ` Dylan Schneider
  1 sibling, 1 reply; 153+ results
From: Stephen Hemminger @ 2025-06-08 22:34 UTC (permalink / raw)
  To: Schneide
  Cc: dev, Thomas Monjalon, Reshma Pattan, Jerin Jacob, Kiran Kumar K,
	Nithin Dabilpuram, Zhirun Yan

On Fri,  6 Jun 2025 15:52:08 -0600
Schneide <schneide@qti.qualcomm.com> wrote:

> From: Dylan Schneider <schneide@qti.qualcomm.com>
> 
> Allow the user to specify protocol link type when creating pcapng files.
> This change is needed to specify the protocol type in the pcapng file,
> DLT_EN10MB specifies ethernet packets only. This will allow dissectors
> for other protocols to be used on files generated by pcapng.
> 
> Includes a breaking change to rte_pcapng_add_interface to add link_type
> parameter. Existing calls to the function have been updated to pass
> DLT_EN10MB for the link type argument.
> 
> Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
> Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
> Cc: stephen@networkplumber.org
> ---
>  .mailmap                               |  1 +
>  app/dumpcap/main.c                     |  4 ++--
>  app/test/test_pcapng.c                 |  8 ++++----
>  doc/guides/rel_notes/release_25_07.rst |  5 ++++-
>  lib/graph/graph_pcap.c                 |  4 +++-
>  lib/pcapng/meson.build                 |  2 ++
>  lib/pcapng/rte_pcapng.c                | 21 +++++++++++++++------
>  lib/pcapng/rte_pcapng.h                |  4 +++-
>  8 files changed, 34 insertions(+), 15 deletions(-)
> 
> diff --git a/.mailmap b/.mailmap
> index 91e08f4a1f..a585124832 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
>  Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
>  Duncan Bellamy <dunk@denkimushi.com>
>  Dustin Lundquist <dustin@null-ptr.net>
> +Dylan Schneider <schneide@qti.qualcomm.com>
>  Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
>  Ed Czeck <ed.czeck@atomicrules.com>
>  Eduard Serra <eserra@vmware.com>
> diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
> index 3d3c0dbc66..e0e2b26269 100644
> --- a/app/dumpcap/main.c
> +++ b/app/dumpcap/main.c
> @@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
>  		free(os);
>  
>  		TAILQ_FOREACH(intf, &interfaces, next) {
> -			if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
> -						     intf->ifdescr, intf->opts.filter) < 0)
> +			if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
> +				intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
>  				rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
>  					intf->port);
>  		}
> diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
> index 8f2cff36c3..bcf99724fa 100644
> --- a/app/test/test_pcapng.c
> +++ b/app/test/test_pcapng.c
> @@ -345,7 +345,7 @@ test_add_interface(void)
>  	}
>  
>  	/* Add interface to the file */
> -	ret = rte_pcapng_add_interface(pcapng, port_id,
> +	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>  				       NULL, NULL, NULL);
>  	if (ret < 0) {
>  		fprintf(stderr, "can not add port %u\n", port_id);
> @@ -353,7 +353,7 @@ test_add_interface(void)
>  	}
>  
>  	/* Add interface with ifname and ifdescr */
> -	ret = rte_pcapng_add_interface(pcapng, port_id,
> +	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>  				       "myeth", "Some long description", NULL);
>  	if (ret < 0) {
>  		fprintf(stderr, "can not add port %u with ifname\n", port_id);
> @@ -361,7 +361,7 @@ test_add_interface(void)
>  	}
>  
>  	/* Add interface with filter */
> -	ret = rte_pcapng_add_interface(pcapng, port_id,
> +	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>  				       NULL, NULL, "tcp port 8080");
>  	if (ret < 0) {
>  		fprintf(stderr, "can not add port %u with filter\n", port_id);
> @@ -406,7 +406,7 @@ test_write_packets(void)
>  	}
>  
>  	/* Add interface to the file */
> -	ret = rte_pcapng_add_interface(pcapng, port_id,
> +	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>  				       NULL, NULL, NULL);
>  	if (ret < 0) {
>  		fprintf(stderr, "can not add port %u\n", port_id);
> diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
> index 6b070801de..2396c7b014 100644
> --- a/doc/guides/rel_notes/release_25_07.rst
> +++ b/doc/guides/rel_notes/release_25_07.rst
> @@ -108,7 +108,10 @@ API Changes
>     This section is a comment. Do not overwrite or remove it.
>     Also, make sure to start the actual text at the margin.
>     =======================================================
> -
> +* pcapng: Changed the API for adding interfaces to include a link type argument.
> +  The link type was previously hardcoded to the ethernet link type in the API.
> +  This argument is added to ``rte_pcapng_add_interface``.
> +  These functions are versioned to retain binary compatibility until the next LTS release.
>  
>  ABI Changes
>  -----------
> diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
> index 89525f1220..13d86b7a18 100644
> --- a/lib/graph/graph_pcap.c
> +++ b/lib/graph/graph_pcap.c
> @@ -11,6 +11,8 @@
>  #include <rte_mbuf.h>
>  #include <rte_pcapng.h>
>  
> +#include <pcap/pcap.h>
> +
>  #include "rte_graph_worker.h"
>  
>  #include "graph_pcap_private.h"
> @@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)
>  
>  	/* Add the configured interfaces as possible capture ports */
>  	RTE_ETH_FOREACH_DEV(portid) {
> -		ret = rte_pcapng_add_interface(pcapng_fd, portid,
> +		ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
>  					       NULL, NULL, NULL);
>  		if (ret < 0) {
>  			graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
> diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
> index 4549925d41..3aa7ba5155 100644
> --- a/lib/pcapng/meson.build
> +++ b/lib/pcapng/meson.build
> @@ -5,3 +5,5 @@ sources = files('rte_pcapng.c')
>  headers = files('rte_pcapng.h')
>  
>  deps += ['ethdev']
> +
> +use_function_versioning = true
> diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
> index cacbefdc50..f18af25983 100644
> --- a/lib/pcapng/rte_pcapng.c
> +++ b/lib/pcapng/rte_pcapng.c
> @@ -200,11 +200,10 @@ pcapng_section_block(rte_pcapng_t *self,
>  }
>  
>  /* Write an interface block for a DPDK port */
> -RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
> -int
> -rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
> -			 const char *ifname, const char *ifdescr,
> -			 const char *filter)
> +RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
> +			(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
> +			const char *ifname, const char *ifdescr,
> +			const char *filter))
>  {
>  	struct pcapng_interface_block *hdr;
>  	struct rte_eth_dev_info dev_info;
> @@ -274,7 +273,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
>  	hdr = (struct pcapng_interface_block *)buf;
>  	*hdr = (struct pcapng_interface_block) {
>  		.block_type = PCAPNG_INTERFACE_BLOCK,
> -		.link_type = 1,		/* DLT_EN10MB - Ethernet */
> +		.link_type = link_type,
>  		.block_length = len,
>  	};
>  
> @@ -319,6 +318,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
>  	return write(self->outfd, buf, len);
>  }
>  
> +RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
> +				  (rte_pcapng_t *self, uint16_t port,
> +				   const char *ifname, const char *ifdescr,
> +				   const char *filter))
> +{
> +	/* Call the new version with a default link_type (Ethernet) */
> +	return rte_pcapng_add_interface(self, port, DLT_EN10MB,
> +									ifname, ifdescr, filter);
> +}
> +
>  /*
>   * Write an Interface statistics block at the end of capture.
>   */
> diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
> index 48f2b57564..9880d415c4 100644
> --- a/lib/pcapng/rte_pcapng.h
> +++ b/lib/pcapng/rte_pcapng.h
> @@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
>   *  The handle to the packet capture file
>   * @param port
>   *  The Ethernet port to report stats on.
> + * @param link_type
> + *   The link type (e.g., DLT_EN10MB).
>   * @param ifname (optional)
>   *  Interface name to record in the file.
>   *  If not specified, name will be constructed from port
> @@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
>   * must be added.
>   */
>  int
> -rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
> +rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
>  			 const char *ifname, const char *ifdescr,
>  			 const char *filter);
>  

Build is failing, DLT_EN10MB is not available in all configs.

^ permalink raw reply	[relevance 0%]

* Re: [PATCH v2] pcapng: allow any protocol link type for the interface block
  2025-06-08 22:34  0%   ` Stephen Hemminger
@ 2025-06-09 15:51  0%     ` Dylan Schneider
  0 siblings, 0 replies; 153+ results
From: Dylan Schneider @ 2025-06-09 15:51 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: dev, Thomas Monjalon, Reshma Pattan, Jerin Jacob, Kiran Kumar K,
	Nithin Dabilpuram, Zhirun Yan

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

Hey Stephen,
In this case, is it best to just pass 1 with a comment saying that is DLT_EN10MB? Or is there a way we can get it defined in all configs?
________________________________
From: Stephen Hemminger <stephen@networkplumber.org>
Sent: Sunday, June 8, 2025 4:34 PM
To: Dylan Schneider <schneide@qti.qualcomm.com>
Cc: dev@dpdk.org <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Reshma Pattan <reshma.pattan@intel.com>; Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K <kirankumark@marvell.com>; Nithin Dabilpuram <ndabilpuram@marvell.com>; Zhirun Yan <yanzhirun_163@163.com>
Subject: Re: [PATCH v2] pcapng: allow any protocol link type for the interface block

WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.

On Fri,  6 Jun 2025 15:52:08 -0600
Schneide <schneide@qti.qualcomm.com> wrote:

> From: Dylan Schneider <schneide@qti.qualcomm.com>
>
> Allow the user to specify protocol link type when creating pcapng files.
> This change is needed to specify the protocol type in the pcapng file,
> DLT_EN10MB specifies ethernet packets only. This will allow dissectors
> for other protocols to be used on files generated by pcapng.
>
> Includes a breaking change to rte_pcapng_add_interface to add link_type
> parameter. Existing calls to the function have been updated to pass
> DLT_EN10MB for the link type argument.
>
> Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
> Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
> Cc: stephen@networkplumber.org
> ---
>  .mailmap                               |  1 +
>  app/dumpcap/main.c                     |  4 ++--
>  app/test/test_pcapng.c                 |  8 ++++----
>  doc/guides/rel_notes/release_25_07.rst |  5 ++++-
>  lib/graph/graph_pcap.c                 |  4 +++-
>  lib/pcapng/meson.build                 |  2 ++
>  lib/pcapng/rte_pcapng.c                | 21 +++++++++++++++------
>  lib/pcapng/rte_pcapng.h                |  4 +++-
>  8 files changed, 34 insertions(+), 15 deletions(-)
>
> diff --git a/.mailmap b/.mailmap
> index 91e08f4a1f..a585124832 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
>  Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
>  Duncan Bellamy <dunk@denkimushi.com>
>  Dustin Lundquist <dustin@null-ptr.net>
> +Dylan Schneider <schneide@qti.qualcomm.com>
>  Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
>  Ed Czeck <ed.czeck@atomicrules.com>
>  Eduard Serra <eserra@vmware.com>
> diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
> index 3d3c0dbc66..e0e2b26269 100644
> --- a/app/dumpcap/main.c
> +++ b/app/dumpcap/main.c
> @@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
>               free(os);
>
>               TAILQ_FOREACH(intf, &interfaces, next) {
> -                     if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
> -                                                  intf->ifdescr, intf->opts.filter) < 0)
> +                     if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
> +                             intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
>                               rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
>                                       intf->port);
>               }
> diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
> index 8f2cff36c3..bcf99724fa 100644
> --- a/app/test/test_pcapng.c
> +++ b/app/test/test_pcapng.c
> @@ -345,7 +345,7 @@ test_add_interface(void)
>       }
>
>       /* Add interface to the file */
> -     ret = rte_pcapng_add_interface(pcapng, port_id,
> +     ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                      NULL, NULL, NULL);
>       if (ret < 0) {
>               fprintf(stderr, "can not add port %u\n", port_id);
> @@ -353,7 +353,7 @@ test_add_interface(void)
>       }
>
>       /* Add interface with ifname and ifdescr */
> -     ret = rte_pcapng_add_interface(pcapng, port_id,
> +     ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                      "myeth", "Some long description", NULL);
>       if (ret < 0) {
>               fprintf(stderr, "can not add port %u with ifname\n", port_id);
> @@ -361,7 +361,7 @@ test_add_interface(void)
>       }
>
>       /* Add interface with filter */
> -     ret = rte_pcapng_add_interface(pcapng, port_id,
> +     ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                      NULL, NULL, "tcp port 8080");
>       if (ret < 0) {
>               fprintf(stderr, "can not add port %u with filter\n", port_id);
> @@ -406,7 +406,7 @@ test_write_packets(void)
>       }
>
>       /* Add interface to the file */
> -     ret = rte_pcapng_add_interface(pcapng, port_id,
> +     ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
>                                      NULL, NULL, NULL);
>       if (ret < 0) {
>               fprintf(stderr, "can not add port %u\n", port_id);
> diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
> index 6b070801de..2396c7b014 100644
> --- a/doc/guides/rel_notes/release_25_07.rst
> +++ b/doc/guides/rel_notes/release_25_07.rst
> @@ -108,7 +108,10 @@ API Changes
>     This section is a comment. Do not overwrite or remove it.
>     Also, make sure to start the actual text at the margin.
>     =======================================================
> -
> +* pcapng: Changed the API for adding interfaces to include a link type argument.
> +  The link type was previously hardcoded to the ethernet link type in the API.
> +  This argument is added to ``rte_pcapng_add_interface``.
> +  These functions are versioned to retain binary compatibility until the next LTS release.
>
>  ABI Changes
>  -----------
> diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
> index 89525f1220..13d86b7a18 100644
> --- a/lib/graph/graph_pcap.c
> +++ b/lib/graph/graph_pcap.c
> @@ -11,6 +11,8 @@
>  #include <rte_mbuf.h>
>  #include <rte_pcapng.h>
>
> +#include <pcap/pcap.h>
> +
>  #include "rte_graph_worker.h"
>
>  #include "graph_pcap_private.h"
> @@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)
>
>       /* Add the configured interfaces as possible capture ports */
>       RTE_ETH_FOREACH_DEV(portid) {
> -             ret = rte_pcapng_add_interface(pcapng_fd, portid,
> +             ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
>                                              NULL, NULL, NULL);
>               if (ret < 0) {
>                       graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
> diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
> index 4549925d41..3aa7ba5155 100644
> --- a/lib/pcapng/meson.build
> +++ b/lib/pcapng/meson.build
> @@ -5,3 +5,5 @@ sources = files('rte_pcapng.c')
>  headers = files('rte_pcapng.h')
>
>  deps += ['ethdev']
> +
> +use_function_versioning = true
> diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
> index cacbefdc50..f18af25983 100644
> --- a/lib/pcapng/rte_pcapng.c
> +++ b/lib/pcapng/rte_pcapng.c
> @@ -200,11 +200,10 @@ pcapng_section_block(rte_pcapng_t *self,
>  }
>
>  /* Write an interface block for a DPDK port */
> -RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
> -int
> -rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
> -                      const char *ifname, const char *ifdescr,
> -                      const char *filter)
> +RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
> +                     (rte_pcapng_t *self, uint16_t port, uint16_t link_type,
> +                     const char *ifname, const char *ifdescr,
> +                     const char *filter))
>  {
>       struct pcapng_interface_block *hdr;
>       struct rte_eth_dev_info dev_info;
> @@ -274,7 +273,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
>       hdr = (struct pcapng_interface_block *)buf;
>       *hdr = (struct pcapng_interface_block) {
>               .block_type = PCAPNG_INTERFACE_BLOCK,
> -             .link_type = 1,         /* DLT_EN10MB - Ethernet */
> +             .link_type = link_type,
>               .block_length = len,
>       };
>
> @@ -319,6 +318,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
>       return write(self->outfd, buf, len);
>  }
>
> +RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
> +                               (rte_pcapng_t *self, uint16_t port,
> +                                const char *ifname, const char *ifdescr,
> +                                const char *filter))
> +{
> +     /* Call the new version with a default link_type (Ethernet) */
> +     return rte_pcapng_add_interface(self, port, DLT_EN10MB,
> +                                                                     ifname, ifdescr, filter);
> +}
> +
>  /*
>   * Write an Interface statistics block at the end of capture.
>   */
> diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
> index 48f2b57564..9880d415c4 100644
> --- a/lib/pcapng/rte_pcapng.h
> +++ b/lib/pcapng/rte_pcapng.h
> @@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
>   *  The handle to the packet capture file
>   * @param port
>   *  The Ethernet port to report stats on.
> + * @param link_type
> + *   The link type (e.g., DLT_EN10MB).
>   * @param ifname (optional)
>   *  Interface name to record in the file.
>   *  If not specified, name will be constructed from port
> @@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
>   * must be added.
>   */
>  int
> -rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
> +rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
>                        const char *ifname, const char *ifdescr,
>                        const char *filter);
>

Build is failing, DLT_EN10MB is not available in all configs.

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

^ permalink raw reply	[relevance 0%]

* [PATCH v2] pcapng: allow any protocol link type for the interface block
                     ` (2 preceding siblings ...)
  2025-06-06 21:52  3% ` Schneide
@ 2025-06-09 21:19  3% ` Schneide
  3 siblings, 0 replies; 153+ results
From: Schneide @ 2025-06-09 21:19 UTC (permalink / raw)
  To: dev, Thomas Monjalon, Reshma Pattan, Stephen Hemminger,
	Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram, Zhirun Yan
  Cc: Dylan Schneider

From: Dylan Schneider <schneide@qti.qualcomm.com>

Allow the user to specify protocol link type when creating pcapng files.
This change is needed to specify the protocol type in the pcapng file,
DLT_EN10MB specifies ethernet packets only. This will allow dissectors
for other protocols to be used on files generated by pcapng.

Includes a breaking change to rte_pcapng_add_interface to add link_type
parameter. Existing calls to the function have been updated to pass
DLT_EN10MB for the link type argument.

Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
Cc: stephen@networkplumber.org
---
 .mailmap                               |  1 +
 app/dumpcap/main.c                     |  4 ++--
 app/test/test_pcapng.c                 |  8 ++++----
 doc/guides/rel_notes/release_25_07.rst |  4 ++++
 lib/graph/graph_pcap.c                 |  4 +++-
 lib/pcapng/meson.build                 |  7 +++++++
 lib/pcapng/rte_pcapng.c                | 22 ++++++++++++++++------
 lib/pcapng/rte_pcapng.h                |  4 +++-
 8 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/.mailmap b/.mailmap
index 91e08f4a1f..a585124832 100644
--- a/.mailmap
+++ b/.mailmap
@@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
 Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
 Duncan Bellamy <dunk@denkimushi.com>
 Dustin Lundquist <dustin@null-ptr.net>
+Dylan Schneider <schneide@qti.qualcomm.com>
 Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
 Ed Czeck <ed.czeck@atomicrules.com>
 Eduard Serra <eserra@vmware.com>
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..e0e2b26269 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
 		free(os);
 
 		TAILQ_FOREACH(intf, &interfaces, next) {
-			if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
-						     intf->ifdescr, intf->opts.filter) < 0)
+			if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
+				intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
 				rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
 					intf->port);
 		}
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 8f2cff36c3..bcf99724fa 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -345,7 +345,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface to the file */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u\n", port_id);
@@ -353,7 +353,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface with ifname and ifdescr */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       "myeth", "Some long description", NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u with ifname\n", port_id);
@@ -361,7 +361,7 @@ test_add_interface(void)
 	}
 
 	/* Add interface with filter */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, "tcp port 8080");
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u with filter\n", port_id);
@@ -406,7 +406,7 @@ test_write_packets(void)
 	}
 
 	/* Add interface to the file */
-	ret = rte_pcapng_add_interface(pcapng, port_id,
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
 				       NULL, NULL, NULL);
 	if (ret < 0) {
 		fprintf(stderr, "can not add port %u\n", port_id);
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de..5f8a472928 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -109,6 +109,10 @@ API Changes
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* pcapng: Changed the API for adding interfaces to include a link type argument.
+  The link type was previously hardcoded to the ethernet link type in the API.
+  This argument is added to ``rte_pcapng_add_interface``.
+  These functions are versioned to retain binary compatibility until the next LTS release.
 
 ABI Changes
 -----------
diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
index 89525f1220..13d86b7a18 100644
--- a/lib/graph/graph_pcap.c
+++ b/lib/graph/graph_pcap.c
@@ -11,6 +11,8 @@
 #include <rte_mbuf.h>
 #include <rte_pcapng.h>
 
+#include <pcap/pcap.h>
+
 #include "rte_graph_worker.h"
 
 #include "graph_pcap_private.h"
@@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)
 
 	/* Add the configured interfaces as possible capture ports */
 	RTE_ETH_FOREACH_DEV(portid) {
-		ret = rte_pcapng_add_interface(pcapng_fd, portid,
+		ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
 					       NULL, NULL, NULL);
 		if (ret < 0) {
 			graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
index 4549925d41..d1ace87feb 100644
--- a/lib/pcapng/meson.build
+++ b/lib/pcapng/meson.build
@@ -1,7 +1,14 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2019 Microsoft Corporation
 
+if not dpdk_conf.has('RTE_HAS_LIBPCAP')
+    build = false
+    reason = 'missing dependency, "libpcap"'
+endif
+
 sources = files('rte_pcapng.c')
 headers = files('rte_pcapng.h')
 
 deps += ['ethdev']
+
+use_function_versioning = true
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index cacbefdc50..9db8813696 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -29,6 +29,7 @@
 #include <rte_reciprocal.h>
 #include <rte_time.h>
 
+#include "pcap/dlt.h"
 #include "pcapng_proto.h"
 
 /* conversion from DPDK speed to PCAPNG */
@@ -200,11 +201,10 @@ pcapng_section_block(rte_pcapng_t *self,
 }
 
 /* Write an interface block for a DPDK port */
-RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
-int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
-			 const char *ifname, const char *ifdescr,
-			 const char *filter)
+RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
+		   (rte_pcapng_t *self, uint16_t port, uint16_t link_type,
+		   const char *ifname, const char *ifdescr,
+		   const char *filter))
 {
 	struct pcapng_interface_block *hdr;
 	struct rte_eth_dev_info dev_info;
@@ -274,7 +274,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
 	hdr = (struct pcapng_interface_block *)buf;
 	*hdr = (struct pcapng_interface_block) {
 		.block_type = PCAPNG_INTERFACE_BLOCK,
-		.link_type = 1,		/* DLT_EN10MB - Ethernet */
+		.link_type = link_type,
 		.block_length = len,
 	};
 
@@ -319,6 +319,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
 	return write(self->outfd, buf, len);
 }
 
+RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
+		   (rte_pcapng_t *self, uint16_t port,
+		   const char *ifname, const char *ifdescr,
+		   const char *filter))
+{
+	/* Call the new version with a default link_type (Ethernet) */
+	return rte_pcapng_add_interface(self, port, DLT_EN10MB,
+					ifname, ifdescr, filter);
+}
+
 /*
  * Write an Interface statistics block at the end of capture.
  */
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index 48f2b57564..cce141e3f4 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
  *  The handle to the packet capture file
  * @param port
  *  The Ethernet port to report stats on.
+ * @param link_type
+ *  The link type (e.g., DLT_EN10MB).
  * @param ifname (optional)
  *  Interface name to record in the file.
  *  If not specified, name will be constructed from port
@@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
  * must be added.
  */
 int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
+rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
 			 const char *ifname, const char *ifdescr,
 			 const char *filter);
 
-- 
2.27.0


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v1 0/4] Adjust wording for NUMA vs. socket ID in DPDK
  @ 2025-06-09 21:44  0% ` Thomas Monjalon
  0 siblings, 0 replies; 153+ results
From: Thomas Monjalon @ 2025-06-09 21:44 UTC (permalink / raw)
  To: Anatoly Burakov; +Cc: dev

26/11/2024 14:14, Anatoly Burakov:
> While initially, DPDK has used the term "socket ID" to refer to physical package
> ID, the last time DPDK read "physical_package_id" for socket ID was ~9 years
> ago, so it's been a while since we've actually switched over to using the term
> "socket" to mean "NUMA node".
> 
> This wasn't a problem before, as most systems had one NUMA node per physical
> socket. However, in the last few years, more and more systems have multiple NUMA
> nodes per physical CPU socket. Since DPDK used NUMA nodes already, the
> transition was pretty seamless, however now we're faced with a situation when
> most of our documentation still uses outdated terms, and our API is ripe with
> references to "sockets" when in actuality we mean "NUMA nodes". This could be a
> source of confusion.
> 
> While completely renaming all of our API's would be a huge effort, will take a
> long time and arguably wouldn't even be worth the API breakages (given that this
> mismatch between terminology and reality is implicitly understood by most people
> working on DPDK, and so this isn't so much of a problem in practice), we can do
> some tweaks around the edges and at least document this unfortunate reality.
> 
> This patchset suggests the following changes:
> 
> - Update rte_socket/rte_lcore documentation to refer to NUMA nodes rather than
> sockets
> - Rename internal structures' fields to better reflect this intention
> - Rename --socket-mem/--socket-limit flags to refer to NUMA rather than sockets
> 
> The documentation is updated to refer to new EAL flags, but is otherwise left
> untouched, and instead the entry in "glossary" is amended to indicate that when
> DPDK documentation refers to "sockets", it actually means "NUMA ID's". As next
> steps, we could rename all API parameters to refer to NUMA ID rather than socket
> ID - this would not break neither API nor ABI, and instead would be a
> documentation change in practice.

I agree with this direction.
Less confusion is always better.
Please continue.

Nobody complained about this path in 6 months,
so it is applied with removal of some old useless messages in docs:
	EAL: Detected lcore 0 on socket 0

Thanks.




^ permalink raw reply	[relevance 0%]

* RE: [EXTERNAL] Re: [PATCH v2 1/1] ethdev: add support to provide link type
  2025-06-06 15:23  3%     ` Stephen Hemminger
@ 2025-06-10  5:02  0%       ` Sunil Kumar Kori
  2025-06-10  6:45  0%         ` Morten Brørup
  0 siblings, 1 reply; 153+ results
From: Sunil Kumar Kori @ 2025-06-10  5:02 UTC (permalink / raw)
  To: Stephen Hemminger, Morten Brørup
  Cc: Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev,
	Nithin Kumar Dabilpuram, Jerin Jacob

> On Fri, 6 Jun 2025 11:54:52 +0200
> Morten Brørup <mb@smartsharesystems.com> wrote:
> 
> > > From: skori@marvell.com [mailto:skori@marvell.com]
> > > Sent: Friday, 6 June 2025 11.28
> > >
> > > From: Sunil Kumar Kori <skori@marvell.com>
> > >
> > > Adding link type parameter to provide the type of port like twisted
> > > pair, fibre etc.
> > >
> > > Also added an API to convert the RTE_ETH_LINK_TYPE_XXX to a readable
> > > string.
> > >
> > > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > > Signed-off-by: Sunil Kumar Kori <skori@marvell.com>
> > > ---
> > > +/**@{@name PORT type
> > > + * Ethernet port type
> > > + */
> > > +#define RTE_ETH_LINK_TYPE_NONE  0x00 /**< Not defined */
> > > +#define RTE_ETH_LINK_TYPE_TP    0x01 /**< Twisted Pair */
> > > +#define RTE_ETH_LINK_TYPE_AUI   0x02 /**< Attachment Unit Interface */
> > > +#define RTE_ETH_LINK_TYPE_MII   0x03 /**< Media Independent Interface
> > > */
> > > +#define RTE_ETH_LINK_TYPE_FIBRE 0x04 /**< Fibre */
> > > +#define RTE_ETH_LINK_TYPE_BNC   0x05 /**< BNC */
> > > +#define RTE_ETH_LINK_TYPE_DA    0x06 /**< Direct Attach copper */
> > > +#define RTE_ETH_LINK_TYPE_OTHER 0x1F /**< Other type */ /**@}*/
> >
> > I don't see the use case for this patch, when only ancient interface types are
> defined.
> > How about RMII, GMII, RGMII, XGMII, XAUI, XAUI4, CAUI4, SFP, SFP+, SFP28, etc.
> >
> > Please don't blindly port stuff from Linux to DPDK.
> >
> >
> 
> Agree with Morten here.
> Plus, it isn't really much use to user unless all drivers report it.
> Also, it breaks ABI (see build failures).

Agreed. I will try to make this implementation more useful and will share next version after handling the suggestions.
Thanks.

^ permalink raw reply	[relevance 0%]

* RE: [EXTERNAL] Re: [PATCH v2 1/1] ethdev: add support to provide link type
  2025-06-10  5:02  0%       ` [EXTERNAL] " Sunil Kumar Kori
@ 2025-06-10  6:45  0%         ` Morten Brørup
  0 siblings, 0 replies; 153+ results
From: Morten Brørup @ 2025-06-10  6:45 UTC (permalink / raw)
  To: Sunil Kumar Kori, Stephen Hemminger
  Cc: Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko, dev,
	Nithin Kumar Dabilpuram, Jerin Jacob

> From: Sunil Kumar Kori [mailto:skori@marvell.com]
> Sent: Tuesday, 10 June 2025 07.02
> 
> > On Fri, 6 Jun 2025 11:54:52 +0200
> > Morten Brørup <mb@smartsharesystems.com> wrote:
> >
> > > > From: skori@marvell.com [mailto:skori@marvell.com]
> > > > Sent: Friday, 6 June 2025 11.28
> > > >
> > > > From: Sunil Kumar Kori <skori@marvell.com>
> > > >
> > > > Adding link type parameter to provide the type of port like twisted
> > > > pair, fibre etc.
> > > >
> > > > Also added an API to convert the RTE_ETH_LINK_TYPE_XXX to a readable
> > > > string.
> > > >
> > > > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > > > Signed-off-by: Sunil Kumar Kori <skori@marvell.com>
> > > > ---
> > > > +/**@{@name PORT type
> > > > + * Ethernet port type
> > > > + */
> > > > +#define RTE_ETH_LINK_TYPE_NONE  0x00 /**< Not defined */
> > > > +#define RTE_ETH_LINK_TYPE_TP    0x01 /**< Twisted Pair */
> > > > +#define RTE_ETH_LINK_TYPE_AUI   0x02 /**< Attachment Unit Interface */
> > > > +#define RTE_ETH_LINK_TYPE_MII   0x03 /**< Media Independent Interface
> > > > */
> > > > +#define RTE_ETH_LINK_TYPE_FIBRE 0x04 /**< Fibre */
> > > > +#define RTE_ETH_LINK_TYPE_BNC   0x05 /**< BNC */
> > > > +#define RTE_ETH_LINK_TYPE_DA    0x06 /**< Direct Attach copper */
> > > > +#define RTE_ETH_LINK_TYPE_OTHER 0x1F /**< Other type */ /**@}*/
> > >
> > > I don't see the use case for this patch, when only ancient interface types
> are
> > defined.
> > > How about RMII, GMII, RGMII, XGMII, XAUI, XAUI4, CAUI4, SFP, SFP+, SFP28,
> etc.
> > >
> > > Please don't blindly port stuff from Linux to DPDK.
> > >
> > >
> >
> > Agree with Morten here.
> > Plus, it isn't really much use to user unless all drivers report it.
> > Also, it breaks ABI (see build failures).
> 
> Agreed. I will try to make this implementation more useful and will share next
> version after handling the suggestions.
> Thanks.

If you choose to proceed with this, please note that APIs for managing PHYs and pluggable modules (SFP, etc.) would be more useful than simply indicating if dual-personality port is connected by Twisted Pair or SFP (which is the only case I can imagine for this API).
A PHY API could also be used for EEE ("Green Ethernet") and POE management, cable test and similar features.

Marvell makes PHYs, so please ask to your PHY people about relevant features for a PHY API.
This should be driven by use cases, not by what is available in Linux APIs.


^ permalink raw reply	[relevance 0%]

* [PATCH v3 0/3] argparse additions and rework
  2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
                   ` (3 preceding siblings ...)
  2025-06-03 15:32  4% ` [PATCH v2 " Bruce Richardson
@ 2025-06-10 16:29  4% ` Bruce Richardson
  2025-06-10 16:29 10%   ` [PATCH v3 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
  2025-06-10 16:29  6%   ` [PATCH v3 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
  2025-06-11 12:36  4% ` [PATCH v4 0/3] argparse additions and rework Bruce Richardson
  5 siblings, 2 replies; 153+ results
From: Bruce Richardson @ 2025-06-10 16:29 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson

This patchset is based off the work to adjust how we do argument parsing
inside EAL. To enable argparse to be effectively used for EAL, we have
new features and some changes in the first two patches, which are
relatively small - though are ABI/API affecting.

These add support for saving off strings and boolean values, have argparse
stop parsing at a "--", and finally have argparse return the number of
arguments actually parsed on success.

The third patch is a bigger change. It was inspired by the fact that
when adding the boolean and string support we had to update some
"MAX" value defines used in the code. This is obviously not good from
an ABI/API perspective, once the library becomes part of the stable ABI.
In order to remove these MAX values, patch 3 looks to replace the
#define values with enums - which means some rework splitting the
various flags into separate categories, and similarly splitting the
single "flags" field with separate fields specifying if an argument
value is required, what type that value should have, and then a
final smaller field for any additional modifiers.

v3: additional doc updates to patch 3

v2: minor changes to patch 2 (see log at end of that patch)


Bruce Richardson (3):
  argparse: add support for string and boolean args
  argparse: make argparse EAL-args compatible
  argparse: use enums to remove max-value defines in lists

 app/test/test_argparse.c               | 229 ++++++++++++++-----------
 doc/guides/prog_guide/argparse_lib.rst |  36 ++--
 doc/guides/rel_notes/release_25_07.rst |  50 ++++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 184 ++++++++++++--------
 lib/argparse/rte_argparse.h            |  97 ++++++-----
 7 files changed, 379 insertions(+), 241 deletions(-)

--
2.48.1


^ permalink raw reply	[relevance 4%]

* [PATCH v3 2/3] argparse: make argparse EAL-args compatible
  2025-06-10 16:29  4% ` [PATCH v3 " Bruce Richardson
@ 2025-06-10 16:29 10%   ` Bruce Richardson
  2025-06-10 16:29  6%   ` [PATCH v3 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
  1 sibling, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-10 16:29 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson, Chengwen Feng

The argparse library was missing two key features which made it
unsuitable for use by EAL or any program wanting similar behaviour.

1. It didn't stop parsing arguments when it hit a "--" character
2. It never returned the number of arguments parsed

Fix both these issues - the latter is a change to the ABI, since we now
return >= 0 rather than == 0 on success. However, the ABI is still
experimental so we can make exactly these sorts of tweaks to it.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_argparse.c               | 36 +++++++++++++-------------
 doc/guides/rel_notes/release_25_07.rst |  9 +++++++
 lib/argparse/rte_argparse.c            | 12 ++++++---
 lib/argparse/rte_argparse.h            |  6 +++--
 4 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 6b0d1524b5..382b6250b3 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -393,14 +393,14 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
@@ -434,13 +434,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -448,13 +448,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
@@ -503,14 +503,14 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -555,14 +555,14 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
@@ -618,14 +618,14 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -633,13 +633,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
@@ -671,7 +671,7 @@ test_argparse_pos_autosave_parse_int(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
@@ -738,7 +738,7 @@ test_argparse_pos_callback_parse_int(void)
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 68f56347da..a420cef248 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -170,6 +170,15 @@ ABI Changes
 
 * No ABI change that would break compatibility with 24.11.
 
+* argparse: The experimental "argparse" library has had the following updates:
+   * The main parsing function, ``rte_argparse_parse()``,
+     now returns the number of arguments parsed on success, rather than zero.
+     It still returns a negative value on error.
+   * When parsing a list of arguments,
+     ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
+     This behaviour mirrors the behaviour of the ``getopt()`` function,
+     as well as the behaviour of ``rte_eal_init()`` function.
+
 
 Known Issues
 ------------
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 49485861e3..13a3f4d9eb 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -606,6 +606,12 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 
 	for (i = 1; i < argc; i++) {
 		curr_argv = argv[i];
+
+		if (strcmp(argv[i], "--") == 0) {
+			i++;
+			break;
+		}
+
 		if (curr_argv[0] != '-') {
 			/* process positional parameters. */
 			position_index++;
@@ -669,7 +675,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		arg->flags |= ARG_ATTR_FLAG_PARSED_MASK;
 	}
 
-	return 0;
+	return i;
 }
 
 static uint32_t
@@ -785,7 +791,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		goto error;
 
 	ret = parse_args(obj, argc, argv, &show_help);
-	if (ret != 0)
+	if (ret < 0)
 		goto error;
 
 	if (show_help) {
@@ -793,7 +799,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		exit(0);
 	}
 
-	return 0;
+	return ret;
 
 error:
 	if (obj->exit_on_error)
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index 332184302e..fd5c33b319 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -173,7 +173,8 @@ struct rte_argparse {
  * @warning
  * @b EXPERIMENTAL: this API may change without prior notice.
  *
- * Parse parameters.
+ * Parse parameters passed until all are processed,
+ * or until a "--" parameter is encountered.
  *
  * @param obj
  *   Parser object.
@@ -183,7 +184,8 @@ struct rte_argparse {
  *   Array of parameters points.
  *
  * @return
- *   0 on success. Otherwise negative value is returned.
+ *   number of arguments parsed (>= 0) on success.
+ *   Otherwise negative error code is returned.
  */
 __rte_experimental
 int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
-- 
2.48.1


^ permalink raw reply	[relevance 10%]

* [PATCH v3 3/3] argparse: use enums to remove max-value defines in lists
  2025-06-10 16:29  4% ` [PATCH v3 " Bruce Richardson
  2025-06-10 16:29 10%   ` [PATCH v3 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
@ 2025-06-10 16:29  6%   ` Bruce Richardson
  1 sibling, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-10 16:29 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson, Chengwen Feng, Kevin Laatz, Ori Kam

The use of lists of #defines with _MAX entries at the end causes issues
for ABI compatibility as those MAX values often leak through to
applications and can cause issues when changed.

We can rework the code to increase type safety by splitting the flags
field and using enums for each set of values explicitly:

* One enum for whether an argument takes a parameter or not (or
  optionally takes one)
* One enum for the type of the argument.
* A separate flags field for any additional info - right now this is
  only to indicate an argument can appear more than once.

The use of the MAX field is no longer necessary using the enums as we
can use an inline function with a switch statement to check for valid
values. Compilers like GCC will warn when an enum value is missing from
the switch statement, easing future maintenance if enum values are
added.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Chengwen Feng <fengchengwen@huawei.com>
---
v3:
  * Update the argparse programmers guide doc.
  * Document RTE_ARGPARSE_FLAG_SUPPORT_MULTI for doxygen
  * Fix old doxygen cross reference
---
 app/test/test_argparse.c               | 182 ++++++++++++-------------
 doc/guides/prog_guide/argparse_lib.rst |  36 +++--
 doc/guides/rel_notes/release_25_07.rst |  35 +++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 143 ++++++++++---------
 lib/argparse/rte_argparse.h            |  95 +++++++------
 7 files changed, 283 insertions(+), 232 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 382b6250b3..57b85c4bf0 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -79,8 +79,10 @@ test_argparse_callback(uint32_t index, const char *value, void *opaque)
 	.exit_on_error = false, \
 	.callback = test_argparse_callback, \
 	.args = { \
-		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
-		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
+		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, \
+			RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
+		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, \
+			RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
 		ARGPARSE_ARG_END(), \
 	}, \
 }
@@ -188,27 +190,20 @@ test_argparse_invalid_arg_help(void)
 static int
 test_argparse_invalid_has_val(void)
 {
-	uint64_t set_mask[] = { 0,
-				RTE_ARGPARSE_ARG_NO_VALUE,
-				RTE_ARGPARSE_ARG_OPTIONAL_VALUE
-			      };
+	uint64_t invalid_values[] = {
+			RTE_ARGPARSE_VALUE_NONE,
+			RTE_ARGPARSE_VALUE_OPTIONAL,
+	};
 	struct rte_argparse *obj;
 	uint32_t index;
 	int ret;

-	/* test optional arg don't config has-value. */
-	obj = test_argparse_init_obj();
-	obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
 	/* test positional arg don't config required-value. */
-	for (index = 0; index < RTE_DIM(set_mask); index++) {
+	for (index = 0; index < RTE_DIM(invalid_values); index++) {
 		obj = test_argparse_init_obj();
 		obj->args[0].name_long = "abc";
 		obj->args[0].name_short = NULL;
-		obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-		obj->args[0].flags |= set_mask[index];
+		obj->args[0].value_required = invalid_values[index];
 		ret = rte_argparse_parse(obj, default_argc, default_argv);
 		TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 	}
@@ -225,14 +220,15 @@ test_argparse_invalid_arg_saver(void)
 	/* test saver == NULL with val-type != 0. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

 	/* test saver == NULL with callback is NULL. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->callback = NULL;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -241,15 +237,7 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
-	/* test saver != NULL with val-type is max. */
-	obj = test_argparse_init_obj();
-	obj->args[0].val_saver = (void *)1;
-	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

@@ -257,7 +245,8 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

@@ -272,9 +261,7 @@ test_argparse_invalid_arg_flags(void)

 	/* test set unused bits. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= ~(RTE_ARGPARSE_HAS_VAL_BITMASK |
-				RTE_ARGPARSE_VAL_TYPE_BITMASK |
-				RTE_ARGPARSE_ARG_SUPPORT_MULTI);
+	obj->args[0].flags |= ~(RTE_ARGPARSE_FLAG_SUPPORT_MULTI);
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

@@ -284,14 +271,15 @@ test_argparse_invalid_arg_flags(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT |
-			     RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

 	/* test optional arg enabled multiple but prased by autosave. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

@@ -344,7 +332,6 @@ test_argparse_invalid_option(void)
 static int
 test_argparse_opt_autosave_parse_int_of_no_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -355,7 +342,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -363,7 +351,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -376,7 +365,6 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_required_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -387,7 +375,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -396,7 +385,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -404,7 +394,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

 	/* test invalid value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	argv[2] = test_strdup("100a");
@@ -417,7 +408,6 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_optional_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -429,14 +419,16 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -444,13 +436,15 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

 	/* test with value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -458,12 +452,14 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");

 	/* test with option value, but with wrong value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -498,7 +494,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -506,7 +502,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -549,7 +545,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -558,7 +554,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -566,12 +562,12 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

 	/* test no more parameters. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -613,7 +609,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -621,7 +617,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");

-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -629,13 +625,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");

 	/* test with value. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -643,7 +639,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	argv[1] = test_strdup("-t=100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -654,7 +650,6 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 static int
 test_argparse_pos_autosave_parse_int(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -666,7 +661,8 @@ test_argparse_pos_autosave_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -675,14 +671,16 @@ test_argparse_pos_autosave_parse_int(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

 	/* test positional autosave parse failed. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

 	/* test too much position parameters. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -727,12 +725,12 @@ test_argparse_pos_callback_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = "test-long2";
 	obj->args[1].name_short = NULL;
 	obj->args[1].val_saver = NULL;
 	obj->args[1].val_set = (void *)2;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[2].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -743,8 +741,8 @@ test_argparse_pos_callback_parse_int(void)
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");

 	/* test positional callback parse failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("200a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -775,78 +773,80 @@ test_argparse_parse_type(void)
 	int ret;

 	/* test for int parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");

 	/* test for u8 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u8 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");

 	/* test for u16 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u16 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");

 	/* test for u32 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u32 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");

 	/* test for u64 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u64 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");

 	/* test for string parsing - all it does is save string, so all are valid */
 	const char *val_str;
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_STR, &val_str);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_STR, &val_str);
 	TEST_ASSERT(ret == 0, "Argparse parse a string failed unexpectedly!");

 	/* test for boolean parsing */
 	bool val_bool = false;
-	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (true) failed!");
-	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (false) failed!");
-	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (invalid) passed unexpectedly!");
-	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (numeric true) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			&val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (numeric false) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			&val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (numeric invalid) passed unexpectedly!");
 	return 0;
 }
diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst
index f827312daa..9f11714890 100644
--- a/doc/guides/prog_guide/argparse_lib.rst
+++ b/doc/guides/prog_guide/argparse_lib.rst
@@ -70,14 +70,14 @@ The following code demonstrates how to use:
       .exit_on_error = true,
       .callback = argparse_user_callback,
       .args = {
-         { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE       | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "--bbb", "-b", "bbb argument", &bbb_val, NULL,        RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "--ddd", "-d", "ddd argument", NULL,     (void *)1,   RTE_ARGPARSE_ARG_NO_VALUE       },
-         { "--eee", "-e", "eee argument", NULL,     (void *)2,   RTE_ARGPARSE_ARG_REQUIRED_VALUE },
-         { "--fff", "-f", "fff argument", NULL,     (void *)3,   RTE_ARGPARSE_ARG_OPTIONAL_VALUE },
-         { "ooo",   NULL, "ooo argument", &ooo_val, NULL,        RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "ppp",   NULL, "ppp argument", NULL,     (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE },
+         { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_VALUE_NONE,     RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "--bbb", "-b", "bbb argument", &bbb_val, NULL,        RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_VALUE_OPTIONAL, RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "--ddd", "-d", "ddd argument", NULL,     (void *)1,   RTE_ARGPARSE_VALUE_NONE },
+         { "--eee", "-e", "eee argument", NULL,     (void *)2,   RTE_ARGPARSE_VALUE_REQUIRED },
+         { "--fff", "-f", "fff argument", NULL,     (void *)3,   RTE_ARGPARSE_VALUE_OPTIONAL },
+         { "ooo",   NULL, "ooo argument", &ooo_val, NULL,        RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "ppp",   NULL, "ppp argument", NULL,     (void *)300, RTE_ARGPARSE_VALUE_REQUIRED },
       },
    };

@@ -95,12 +95,12 @@ and the arguments which don't start with a hyphen (-) are positional arguments
 (they're ``ooo``/``ppp``).

 Every argument must be set whether to carry a value (one of
-``RTE_ARGPARSE_ARG_NO_VALUE``, ``RTE_ARGPARSE_ARG_REQUIRED_VALUE`` and
-``RTE_ARGPARSE_ARG_OPTIONAL_VALUE``).
+``RTE_ARGPARSE_VALUE_NONE``, ``RTE_ARGPARSE_VALUE_REQUIRED`` and
+``RTE_ARGPARSE_VALUE_OPTIONAL``).

 .. note::

-   Positional argument must set ``RTE_ARGPARSE_ARG_REQUIRED_VALUE``.
+   Positional argument must set ``RTE_ARGPARSE_VALUE_REQUIRED``.

 User Input Requirements
 ~~~~~~~~~~~~~~~~~~~~~~~
@@ -135,7 +135,7 @@ their values are parsing in the order defined.
 Parsing by autosave way
 ~~~~~~~~~~~~~~~~~~~~~~~

-Argument of known value type (e.g. ``RTE_ARGPARSE_ARG_VALUE_INT``)
+Argument of known value type (e.g. ``RTE_ARGPARSE_VALUE_TYPE_INT``)
 could be parsed using this autosave way,
 and its result will save in the ``val_saver`` field.

@@ -172,12 +172,20 @@ Multiple times argument
 ~~~~~~~~~~~~~~~~~~~~~~~

 If want to support the ability to enter the same argument multiple times,
-then should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in the ``flags`` field.
+then should mark ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI`` in the ``flags`` field.
 For example:

 .. code-block:: C

-   { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI },
+   {
+      .long_name = "--xyz",
+      .short_name = "-x",
+      .desc = "xyz argument",
+      .val_set = (void *)10,
+      .val_mode = RTE_ARGPARSE_VALUE_REQUIRED,
+      // val_type is implicitly RTE_ARGPARSE_VALUE_TYPE_NONE,
+      .flags = RTE_ARGPARSE_FLAG_SUPPORT_MULTI,
+   },

 Then the user input could contain multiple ``--xyz`` arguments.

diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index a420cef248..b203488c68 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -152,6 +152,10 @@ API Changes

 * graph: Added ``graph`` field to the ``rte_node.dispatch`` structure.

+* argparse: The ``rte_argparse_arg`` structure used for defining arguments has been updated.
+    See next section, :ref:`ABI-Changes` for details.
+
+.. _ABI-Changes:

 ABI Changes
 -----------
@@ -178,6 +182,37 @@ ABI Changes
      ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
      This behaviour mirrors the behaviour of the ``getopt()`` function,
      as well as the behaviour of ``rte_eal_init()`` function.
+   * The ``rte_argparse_arg`` structure used for defining arguments has been updated
+     to separate out into separate fields the options for:
+
+     #. Whether the argument is required or optional.
+     #. What the type of the argument is (in case of saving the parameters automatically).
+     #. Any other flags - of which there is only one, ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI``, at this time.
+
+   * With the splitting of the flags into separate enums for categories,
+     the names of the flags have been changed to better reflect their purpose.
+     The flags/enum values are:
+
+     * For the ``value_required`` field:
+
+        * ``RTE_ARGPARSE_VALUE_NONE``
+        * ``RTE_ARGPARSE_VALUE_REQUIRED``
+        * ``RTE_ARGPARSE_VALUE_OPTIONAL``
+
+     * For the ``value_type`` field:
+
+        * ``RTE_ARGPARSE_VALUE_TYPE_NONE`` (No argument value type is specified, callback is to be used for processing.)
+        * ``RTE_ARGPARSE_VALUE_TYPE_INT``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U8``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U16``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U32``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U64``
+        * ``RTE_ARGPARSE_VALUE_TYPE_STR``
+        * ``RTE_ARGPARSE_VALUE_TYPE_BOOL``
+
+     * Other flags:
+
+        * ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI`` (Allows the argument to be specified multiple times.)


 Known Issues
diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c
index acceae6b7b..5ba0aaa40b 100644
--- a/examples/dma/dmafwd.c
+++ b/examples/dma/dmafwd.c
@@ -644,43 +644,43 @@ dma_parse_args(int argc, char **argv, unsigned int nb_ports)
 		.args = {
 			{ "--mac-updating", NULL, "Enable MAC addresses updating",
 			  &mac_updating, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--no-mac-updating", NULL, "Disable MAC addresses updating",
 			  &mac_updating, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--portmask", "-p", "hexadecimal bitmask of ports to configure",
 			  NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--nb-queue", "-q", "number of RX queues per port (default is 1)",
 			  &nb_queues, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--copy-type", "-c", "type of copy: sw|hw",
 			  NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--ring-size", "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode",
 			  &ring_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--dma-batch-size", "-b", "number of requests per DMA batch",
 			  &dma_batch_sz, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--max-frame-size", "-f", "max frame size",
 			  &max_frame_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--force-min-copy-size", "-m", "force a minimum copy length, even for smaller packets",
 			  &force_min_copy_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--stats-interval", "-i", "interval, in seconds, between stats prints (default is 1)",
 			  &stats_interval, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/examples/flow_filtering/main.c b/examples/flow_filtering/main.c
index c0bc1938ce..9c429a8335 100644
--- a/examples/flow_filtering/main.c
+++ b/examples/flow_filtering/main.c
@@ -274,11 +274,11 @@ flow_filtering_parse_args(int argc, char **argv)
 		.args = {
 			{ "--template", NULL, "Enable template API flow",
 			  &use_template_api, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--non-template", NULL, "Enable non template API flow",
 			  &use_template_api, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 13a3f4d9eb..b5b8467dea 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -30,30 +30,48 @@ is_arg_positional(const struct rte_argparse_arg *arg)
 	return arg->name_long[0] != '-';
 }

-static inline uint32_t
-arg_attr_has_val(const struct rte_argparse_arg *arg)
+static inline bool
+is_valid_has_value_field(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_HAS_VAL_BITMASK, arg->flags);
+	switch (arg->value_required) {
+	case RTE_ARGPARSE_VALUE_NONE:
+	case RTE_ARGPARSE_VALUE_OPTIONAL:
+	case RTE_ARGPARSE_VALUE_REQUIRED:
+		return true;
+	/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }

-static inline uint32_t
-arg_attr_val_type(const struct rte_argparse_arg *arg)
-{
-	return RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, arg->flags);
+static inline bool
+is_valid_value_type_field(const struct rte_argparse_arg *arg)
+{
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return true;
+	/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }

+
 static inline bool
 arg_attr_flag_multi(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_ARG_SUPPORT_MULTI, arg->flags);
+	return (arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI) != 0;
 }

 static inline uint64_t
 arg_attr_unused_bits(const struct rte_argparse_arg *arg)
 {
-#define USED_BIT_MASK	(RTE_ARGPARSE_HAS_VAL_BITMASK | \
-			 RTE_ARGPARSE_VAL_TYPE_BITMASK | \
-			 RTE_ARGPARSE_ARG_SUPPORT_MULTI)
+#define USED_BIT_MASK	(RTE_ARGPARSE_FLAG_SUPPORT_MULTI)
 	return arg->flags & ~USED_BIT_MASK;
 }

@@ -110,56 +128,51 @@ verify_arg_help(const struct rte_argparse_arg *arg)
 static int
 verify_arg_has_val(const struct rte_argparse_arg *arg)
 {
-	uint32_t has_val = arg_attr_has_val(arg);
-
+	if (!is_valid_has_value_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value field!", arg->name_long);
+		return -EINVAL;
+	}
 	if (is_arg_positional(arg)) {
-		if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE)
+		if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED)
 			return 0;
 		ARGPARSE_LOG(ERR, "argument %s is positional, must config required-val!",
 			     arg->name_long);
 		return -EINVAL;
 	}

-	if (has_val == 0) {
-		ARGPARSE_LOG(ERR, "argument %s is optional, has-value config wrong!",
-			     arg->name_long);
-		return -EINVAL;
-	}
-
 	return 0;
 }

 static int
 verify_arg_saver(const struct rte_argparse *obj, uint32_t index)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	const struct rte_argparse_arg *arg = &obj->args[index];
-	uint32_t val_type = arg_attr_val_type(arg);
-	uint32_t has_val = arg_attr_has_val(arg);

 	if (arg->val_saver == NULL) {
-		if (val_type != 0) {
+		if (arg->value_type != RTE_ARGPARSE_VALUE_TYPE_NONE) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, value-type should not be set!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		if (obj->callback == NULL) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, but callback is NULL!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		return 0;
 	}

-	if (val_type == 0 || val_type >= cmp_max) {
-		ARGPARSE_LOG(ERR, "argument %s value-type config wrong!", arg->name_long);
+	/* check value_type field */
+	if (!is_valid_value_type_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value-type field!", arg->name_long);
+		return -EINVAL;
+	}
+	if (arg->value_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "missing value-type for argument %s!", arg->name_long);
 		return -EINVAL;
 	}

-	if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) {
+	if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED && arg->val_set != NULL) {
 		ARGPARSE_LOG(ERR, "argument %s has required value, value-set should be NULL!",
 			     arg->name_long);
 		return -EINVAL;
@@ -180,7 +193,7 @@ verify_arg_flags(const struct rte_argparse *obj, uint32_t index)
 		return -EINVAL;
 	}

-	if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI))
+	if (!(arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI))
 		return 0;

 	if (is_arg_positional(arg)) {
@@ -544,26 +557,27 @@ parse_arg_bool(struct rte_argparse_arg *arg, const char *value)
 static int
 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value)
 {
-	static struct {
-		int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value);
-	} map[] = {
-		/* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */
-		{ NULL          },
-		{ parse_arg_int },
-		{ parse_arg_u8  },
-		{ parse_arg_u16 },
-		{ parse_arg_u32 },
-		{ parse_arg_u64 },
-		{ parse_arg_str},
-		{ parse_arg_bool },
-	};
-	uint32_t index = arg_attr_val_type(arg);
-	int ret = -EINVAL;
-
-	if (index > 0 && index < RTE_DIM(map))
-		ret = map[index].f_parse_type(arg, value);
-
-	return ret;
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+		ARGPARSE_LOG(ERR, "argument %s doesn't specify a value-type!", arg->name_long);
+		return -EINVAL;
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+		return parse_arg_int(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+		return parse_arg_u8(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+		return parse_arg_u16(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+		return parse_arg_u32(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+		return parse_arg_u64(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+		return parse_arg_str(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return parse_arg_bool(arg, value);
+	/* omit default case so compiler warns on missing enum values */
+	}
+	return -EINVAL;
 }

 /* arg_parse indicates the name entered by the user, which can be long-name or short-name. */
@@ -616,7 +630,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 			/* process positional parameters. */
 			position_index++;
 			if (position_index > position_count) {
-				ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv);
+				ARGPARSE_LOG(ERR, "too many positional arguments %s!", curr_argv);
 				return -EINVAL;
 			}
 			arg = find_position_arg(obj, position_index);
@@ -641,23 +655,21 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		}

 		if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) {
-			ARGPARSE_LOG(ERR, "argument %s should not occur multiple!",
-				     arg_name);
+			ARGPARSE_LOG(ERR, "argument %s should not occur multiple times!", arg_name);
 			return -EINVAL;
 		}

 		value = (has_equal != NULL ? has_equal + 1 : NULL);
-		if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) {
+		if (arg->value_required == RTE_ARGPARSE_VALUE_NONE) {
 			if (value != NULL) {
-				ARGPARSE_LOG(ERR, "argument %s should not take value!",
-					     arg_name);
+				ARGPARSE_LOG(ERR, "argument %s should not take value!", arg_name);
 				return -EINVAL;
 			}
-		} else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) {
+		} else if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED) {
 			if (value == NULL) {
 				if (i >= argc - 1) {
 					ARGPARSE_LOG(ERR, "argument %s doesn't have value!",
-						     arg_name);
+							arg_name);
 					return -EINVAL;
 				}
 				/* Set value and make i move next. */
@@ -809,21 +821,18 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)

 RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_argparse_parse_type, 24.03)
 int
-rte_argparse_parse_type(const char *str, uint64_t val_type, void *val)
+rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	struct rte_argparse_arg arg = {
 		.name_long = str,
 		.name_short = NULL,
 		.val_saver = val,
 		.val_set = NULL,
-		.flags = val_type,
+		.value_type = val_type,
 	};
-	uint32_t value_type = arg_attr_val_type(&arg);
-
-	if (value_type == 0 || value_type >= cmp_max)
+	if (val_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "argument %s doesn't have value-type!", str);
 		return -EINVAL;
-
+	}
 	return parse_arg_autosave(&arg, str);
 }
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index fd5c33b319..3d04d47771 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -37,49 +37,45 @@
 extern "C" {
 #endif

-/**@{@name Flag definition (in bitmask form) for an argument
- *
- * @note Bits[0~1] represent the argument whether has value,
- * bits[2~9] represent the value type which used when autosave.
- *
- * @see struct rte_argparse_arg::flags
- */
-/** The argument has no value. */
-#define RTE_ARGPARSE_ARG_NO_VALUE       RTE_SHIFT_VAL64(1, 0)
-/** The argument must have a value. */
-#define RTE_ARGPARSE_ARG_REQUIRED_VALUE RTE_SHIFT_VAL64(2, 0)
-/** The argument has optional value. */
-#define RTE_ARGPARSE_ARG_OPTIONAL_VALUE RTE_SHIFT_VAL64(3, 0)
-/** The argument's value is int type. */
-#define RTE_ARGPARSE_ARG_VALUE_INT      RTE_SHIFT_VAL64(1, 2)
-/** The argument's value is uint8 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U8       RTE_SHIFT_VAL64(2, 2)
-/** The argument's value is uint16 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U16      RTE_SHIFT_VAL64(3, 2)
-/** The argument's value is uint32 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U32      RTE_SHIFT_VAL64(4, 2)
-/** The argument's value is uint64 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U64      RTE_SHIFT_VAL64(5, 2)
-/** The argument's value is string type. */
-#define RTE_ARGPARSE_ARG_VALUE_STR      RTE_SHIFT_VAL64(6, 2)
-/** The argument's value is boolean flag type. */
-#define RTE_ARGPARSE_ARG_VALUE_BOOL     RTE_SHIFT_VAL64(7, 2)
-/** Max value type. */
-#define RTE_ARGPARSE_ARG_VALUE_MAX      RTE_SHIFT_VAL64(8, 2)
 /**
- * Flag for that argument support occur multiple times.
- * This flag can be set only when the argument is optional.
- * When this flag is set, the callback type must be used for parsing.
+ * enum defining whether an argument takes a value or not.
  */
-#define RTE_ARGPARSE_ARG_SUPPORT_MULTI  RTE_BIT64(10)
-/** Reserved for this library implementation usage. */
-#define RTE_ARGPARSE_ARG_RESERVED_FIELD RTE_GENMASK64(63, 48)
-/**@}*/
+enum rte_argparse_value_required {
+	/** The argument takes no value. */
+	RTE_ARGPARSE_VALUE_NONE,
+	/** The argument must have a value. */
+	RTE_ARGPARSE_VALUE_REQUIRED,
+	/** The argument has optional value. */
+	RTE_ARGPARSE_VALUE_OPTIONAL,
+};

-/** Bitmask used to get the argument whether has value. */
-#define RTE_ARGPARSE_HAS_VAL_BITMASK	RTE_GENMASK64(1, 0)
-/** Bitmask used to get the argument's value type. */
-#define RTE_ARGPARSE_VAL_TYPE_BITMASK	RTE_GENMASK64(9, 2)
+/** enum defining the type of the argument, integer, boolean or just string */
+enum rte_argparse_value_type {
+	/** Argument takes no value, or value type not specified.
+	 * Should be used when argument is to be handled via callback.
+	 */
+	RTE_ARGPARSE_VALUE_TYPE_NONE = 0,
+	/** The argument's value is int type. */
+	RTE_ARGPARSE_VALUE_TYPE_INT,
+	/** The argument's value is uint8 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U8,
+	/** The argument's value is uint16 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U16,
+	/** The argument's value is uint32 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U32,
+	/** The argument's value is uint64 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U64,
+	/** The argument's value is string type. */
+	RTE_ARGPARSE_VALUE_TYPE_STR,
+	/** The argument's value is boolean flag type. */
+	RTE_ARGPARSE_VALUE_TYPE_BOOL,
+};
+
+/** Additional flags which may be specified for each argument */
+enum rte_argparse_arg_flags {
+	/** argument may be specified multiple times on the commandline */
+	RTE_ARGPARSE_FLAG_SUPPORT_MULTI = RTE_BIT32(0),
+};

 /**
  * A structure used to hold argument's configuration.
@@ -105,10 +101,8 @@ struct rte_argparse_arg {

 	/**
 	 * Saver for the argument's value.
-	 * 1) If the filed is NULL, the callback way is used for parsing
-	 *    argument.
-	 * 2) If the field is not NULL, the autosave way is used for parsing
-	 *    argument.
+	 * 1) If this field is NULL, the callback is used for parsing argument.
+	 * 2) If this field is not NULL, the argument's value will be automatically saved.
 	 */
 	void *val_saver;
 	/**
@@ -123,8 +117,13 @@ struct rte_argparse_arg {
 	 */
 	void *val_set;

-	/** Flag definition (RTE_ARGPARSE_ARG_*) for the argument. */
-	uint64_t flags;
+	/** Specify if the argument takes a value, @see enum rte_argparse_value_required. */
+	enum rte_argparse_value_required value_required;
+	/** The type of the argument, @see enum rte_argparse_value_type. */
+	enum rte_argparse_value_type value_type;
+
+	/** any additional flags for this argument */
+	uint32_t flags;
 };

 /**
@@ -199,7 +198,7 @@ int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
  * @param str
  *   Input string.
  * @param val_type
- *   The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type.
+ *   The value type, @see rte_argparse_value_type.
  * @param val
  *   Saver for the value.
  *
@@ -207,7 +206,7 @@ int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
  *   0 on success. Otherwise negative value is returned.
  */
 __rte_experimental
-int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val);
+int rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val);

 #ifdef __cplusplus
 }
--
2.48.1


^ permalink raw reply	[relevance 6%]

* [DPDK/meson Bug 1721] failed to compile DPDK
@ 2025-06-11  6:49  3% bugzilla
  0 siblings, 0 replies; 153+ results
From: bugzilla @ 2025-06-11  6:49 UTC (permalink / raw)
  To: dev

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

https://bugs.dpdk.org/show_bug.cgi?id=1721

            Bug ID: 1721
           Summary: failed to compile DPDK
           Product: DPDK
           Version: 24.07
          Hardware: x86
                OS: Windows
            Status: UNCONFIRMED
          Severity: critical
          Priority: Normal
         Component: meson
          Assignee: dev@dpdk.org
          Reporter: pdamouny@nvidia.com
  Target Milestone: ---

commit 57c194d142d927f2f5dd757e36777910b5d77d41
Author: David Marchand <david.marchand@redhat.com>
Date:   Fri Mar 7 20:06:51 2025 +0100

    build: use dynamically generated version maps

    Switch to dynamically generated version maps.

    As the map files get generated, tooling around checking, converting,
    updating etc.. static version maps can be removed.

    Signed-off-by: David Marchand <david.marchand@redhat.com>
    Acked-by: Aaron Conole <aconole@redhat.com>


broke compilation 

FAILED: lib/cmdline_exports.map
"C:\Python38\python.exe" "../buildtools/gen-version-map.py" "--linker"
"mslinker" "--abi-version" "../ABI_VERSION" "--output"
"lib/cmdline_exports.map" "--source" "../lib/cmdline/cmdline.c"
"../lib/cmdline/cmdline_cirbuf.c" "../lib/cmdline/cmdline_parse.c"
"../lib/cmdline/cmdline_parse_etheraddr.c"
"../lib/cmdline/cmdline_parse_ipaddr.c" "../lib/cmdline/cmdline_parse_num.c"
"../lib/cmdline/cmdline_parse_bool.c" "../lib/cmdline/cmdline_parse_portlist.c"
"../lib/cmdline/cmdline_parse_string.c" "../lib/cmdline/cmdline_rdline.c"
"../lib/cmdline/cmdline_socket.c" "../lib/cmdline/cmdline_vt100.c"
"../lib/cmdline/cmdline_os_windows.c"
Traceback (most recent call last):
  File "../buildtools/gen-version-map.py", line 30, in <module>
    type=argparse.FileType("r", encoding="utf-8"),
TypeError: __init__() got an unexpected keyword argument 'encoding'

-- 
You are receiving this mail because:
You are the assignee for the bug.

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

^ permalink raw reply	[relevance 3%]

* [PATCH v4 0/3] argparse additions and rework
  2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
                   ` (4 preceding siblings ...)
  2025-06-10 16:29  4% ` [PATCH v3 " Bruce Richardson
@ 2025-06-11 12:36  4% ` Bruce Richardson
  2025-06-11 12:36 10%   ` [PATCH v4 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
  2025-06-11 12:36  6%   ` [PATCH v4 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
  5 siblings, 2 replies; 153+ results
From: Bruce Richardson @ 2025-06-11 12:36 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson

This patchset is based off the work to adjust how we do argument parsing
inside EAL. To enable argparse to be effectively used for EAL, we have
new features and some changes in the first two patches, which are
relatively small - though are ABI/API affecting.

These add support for saving off strings and boolean values, have argparse
stop parsing at a "--", and finally have argparse return the number of
arguments actually parsed on success.

The third patch is a bigger change. It was inspired by the fact that
when adding the boolean and string support we had to update some
"MAX" value defines used in the code. This is obviously not good from
an ABI/API perspective, once the library becomes part of the stable ABI.
In order to remove these MAX values, patch 3 looks to replace the
#define values with enums - which means some rework splitting the
various flags into separate categories, and similarly splitting the
single "flags" field with separate fields specifying if an argument
value is required, what type that value should have, and then a
final smaller field for any additional modifiers.

v4: rebase on main to avoid CI errors

v3: additional doc updates to patch 3

v2: minor changes to patch 2

Bruce Richardson (3):
  argparse: add support for string and boolean args
  argparse: make argparse EAL-args compatible
  argparse: use enums to remove max-value defines in lists

 app/test/test_argparse.c               | 229 ++++++++++++++-----------
 doc/guides/prog_guide/argparse_lib.rst |  36 ++--
 doc/guides/rel_notes/release_25_07.rst |  50 ++++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 184 ++++++++++++--------
 lib/argparse/rte_argparse.h            |  97 ++++++-----
 7 files changed, 379 insertions(+), 241 deletions(-)

--
2.48.1


^ permalink raw reply	[relevance 4%]

* [PATCH v4 2/3] argparse: make argparse EAL-args compatible
  2025-06-11 12:36  4% ` [PATCH v4 0/3] argparse additions and rework Bruce Richardson
@ 2025-06-11 12:36 10%   ` Bruce Richardson
  2025-06-11 12:36  6%   ` [PATCH v4 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
  1 sibling, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-11 12:36 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson, Chengwen Feng

The argparse library was missing two key features which made it
unsuitable for use by EAL or any program wanting similar behaviour.

1. It didn't stop parsing arguments when it hit a "--" character
2. It never returned the number of arguments parsed

Fix both these issues - the latter is a change to the ABI, since we now
return >= 0 rather than == 0 on success. However, the ABI is still
experimental so we can make exactly these sorts of tweaks to it.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_argparse.c               | 36 +++++++++++++-------------
 doc/guides/rel_notes/release_25_07.rst |  9 +++++++
 lib/argparse/rte_argparse.c            | 12 ++++++---
 lib/argparse/rte_argparse.h            |  6 +++--
 4 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 6b0d1524b5..382b6250b3 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -360,14 +360,14 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -393,14 +393,14 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
@@ -434,13 +434,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -448,13 +448,13 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 	obj->args[0].flags = flags;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
@@ -503,14 +503,14 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	return 0;
@@ -555,14 +555,14 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	argv[1] = test_strdup("--test-long");
 	argv[2] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
@@ -618,14 +618,14 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
@@ -633,13 +633,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
@@ -671,7 +671,7 @@ test_argparse_pos_autosave_parse_int(void)
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
 	ret = rte_argparse_parse(obj, 2, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
@@ -738,7 +738,7 @@ test_argparse_pos_callback_parse_int(void)
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
-	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
+	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index f52675c3d0..b67a504680 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -180,6 +180,15 @@ ABI Changes
 
 * No ABI change that would break compatibility with 24.11.
 
+* argparse: The experimental "argparse" library has had the following updates:
+   * The main parsing function, ``rte_argparse_parse()``,
+     now returns the number of arguments parsed on success, rather than zero.
+     It still returns a negative value on error.
+   * When parsing a list of arguments,
+     ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
+     This behaviour mirrors the behaviour of the ``getopt()`` function,
+     as well as the behaviour of ``rte_eal_init()`` function.
+
 
 Known Issues
 ------------
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 49485861e3..13a3f4d9eb 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -606,6 +606,12 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 
 	for (i = 1; i < argc; i++) {
 		curr_argv = argv[i];
+
+		if (strcmp(argv[i], "--") == 0) {
+			i++;
+			break;
+		}
+
 		if (curr_argv[0] != '-') {
 			/* process positional parameters. */
 			position_index++;
@@ -669,7 +675,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		arg->flags |= ARG_ATTR_FLAG_PARSED_MASK;
 	}
 
-	return 0;
+	return i;
 }
 
 static uint32_t
@@ -785,7 +791,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		goto error;
 
 	ret = parse_args(obj, argc, argv, &show_help);
-	if (ret != 0)
+	if (ret < 0)
 		goto error;
 
 	if (show_help) {
@@ -793,7 +799,7 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 		exit(0);
 	}
 
-	return 0;
+	return ret;
 
 error:
 	if (obj->exit_on_error)
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index 332184302e..fd5c33b319 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -173,7 +173,8 @@ struct rte_argparse {
  * @warning
  * @b EXPERIMENTAL: this API may change without prior notice.
  *
- * Parse parameters.
+ * Parse parameters passed until all are processed,
+ * or until a "--" parameter is encountered.
  *
  * @param obj
  *   Parser object.
@@ -183,7 +184,8 @@ struct rte_argparse {
  *   Array of parameters points.
  *
  * @return
- *   0 on success. Otherwise negative value is returned.
+ *   number of arguments parsed (>= 0) on success.
+ *   Otherwise negative error code is returned.
  */
 __rte_experimental
 int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
-- 
2.48.1


^ permalink raw reply	[relevance 10%]

* [PATCH v4 3/3] argparse: use enums to remove max-value defines in lists
  2025-06-11 12:36  4% ` [PATCH v4 0/3] argparse additions and rework Bruce Richardson
  2025-06-11 12:36 10%   ` [PATCH v4 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
@ 2025-06-11 12:36  6%   ` Bruce Richardson
  1 sibling, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-11 12:36 UTC (permalink / raw)
  To: dev; +Cc: Bruce Richardson, Chengwen Feng

The use of lists of #defines with _MAX entries at the end causes issues
for ABI compatibility as those MAX values often leak through to
applications and can cause issues when changed.

We can rework the code to increase type safety by splitting the flags
field and using enums for each set of values explicitly:

* One enum for whether an argument takes a parameter or not (or
  optionally takes one)
* One enum for the type of the argument.
* A separate flags field for any additional info - right now this is
  only to indicate an argument can appear more than once.

The use of the MAX field is no longer necessary using the enums as we
can use an inline function with a switch statement to check for valid
values. Compilers like GCC will warn when an enum value is missing from
the switch statement, easing future maintenance if enum values are
added.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_argparse.c               | 182 ++++++++++++-------------
 doc/guides/prog_guide/argparse_lib.rst |  36 +++--
 doc/guides/rel_notes/release_25_07.rst |  35 +++++
 examples/dma/dmafwd.c                  |  20 +--
 examples/flow_filtering/main.c         |   4 +-
 lib/argparse/rte_argparse.c            | 143 ++++++++++---------
 lib/argparse/rte_argparse.h            |  95 +++++++------
 7 files changed, 283 insertions(+), 232 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index 382b6250b3..57b85c4bf0 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -79,8 +79,10 @@ test_argparse_callback(uint32_t index, const char *value, void *opaque)
 	.exit_on_error = false, \
 	.callback = test_argparse_callback, \
 	.args = { \
-		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
-		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
+		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, \
+			RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
+		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, \
+			RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_NONE }, \
 		ARGPARSE_ARG_END(), \
 	}, \
 }
@@ -188,27 +190,20 @@ test_argparse_invalid_arg_help(void)
 static int
 test_argparse_invalid_has_val(void)
 {
-	uint64_t set_mask[] = { 0,
-				RTE_ARGPARSE_ARG_NO_VALUE,
-				RTE_ARGPARSE_ARG_OPTIONAL_VALUE
-			      };
+	uint64_t invalid_values[] = {
+			RTE_ARGPARSE_VALUE_NONE,
+			RTE_ARGPARSE_VALUE_OPTIONAL,
+	};
 	struct rte_argparse *obj;
 	uint32_t index;
 	int ret;
 
-	/* test optional arg don't config has-value. */
-	obj = test_argparse_init_obj();
-	obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
 	/* test positional arg don't config required-value. */
-	for (index = 0; index < RTE_DIM(set_mask); index++) {
+	for (index = 0; index < RTE_DIM(invalid_values); index++) {
 		obj = test_argparse_init_obj();
 		obj->args[0].name_long = "abc";
 		obj->args[0].name_short = NULL;
-		obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
-		obj->args[0].flags |= set_mask[index];
+		obj->args[0].value_required = invalid_values[index];
 		ret = rte_argparse_parse(obj, default_argc, default_argv);
 		TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 	}
@@ -225,14 +220,15 @@ test_argparse_invalid_arg_saver(void)
 	/* test saver == NULL with val-type != 0. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test saver == NULL with callback is NULL. */
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->callback = NULL;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -241,15 +237,7 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
-	ret = rte_argparse_parse(obj, default_argc, default_argv);
-	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-
-	/* test saver != NULL with val-type is max. */
-	obj = test_argparse_init_obj();
-	obj->args[0].val_saver = (void *)1;
-	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -257,7 +245,8 @@ test_argparse_invalid_arg_saver(void)
 	obj = test_argparse_init_obj();
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -272,9 +261,7 @@ test_argparse_invalid_arg_flags(void)
 
 	/* test set unused bits. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= ~(RTE_ARGPARSE_HAS_VAL_BITMASK |
-				RTE_ARGPARSE_VAL_TYPE_BITMASK |
-				RTE_ARGPARSE_ARG_SUPPORT_MULTI);
+	obj->args[0].flags |= ~(RTE_ARGPARSE_FLAG_SUPPORT_MULTI);
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -284,14 +271,15 @@ test_argparse_invalid_arg_flags(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)1;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT |
-			     RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test optional arg enabled multiple but prased by autosave. */
 	obj = test_argparse_init_obj();
-	obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI;
+	obj->args[0].flags |= RTE_ARGPARSE_FLAG_SUPPORT_MULTI;
 	ret = rte_argparse_parse(obj, default_argc, default_argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
@@ -344,7 +332,6 @@ test_argparse_invalid_option(void)
 static int
 test_argparse_opt_autosave_parse_int_of_no_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -355,7 +342,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -363,7 +351,8 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -376,7 +365,6 @@ test_argparse_opt_autosave_parse_int_of_no_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_required_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -387,7 +375,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -396,7 +385,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -404,7 +394,8 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test invalid value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	argv[2] = test_strdup("100a");
@@ -417,7 +408,6 @@ test_argparse_opt_autosave_parse_int_of_required_val(void)
 static int
 test_argparse_opt_autosave_parse_int_of_optional_val(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[2];
@@ -429,14 +419,16 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = (void *)100;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -444,13 +436,15 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test with value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -458,12 +452,14 @@ test_argparse_opt_autosave_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
 
 	/* test with option value, but with wrong value. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=200a");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -498,7 +494,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -506,7 +502,7 @@ test_argparse_opt_callback_parse_int_of_no_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_NONE;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -549,7 +545,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -558,7 +554,7 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(ret == 3, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -566,12 +562,12 @@ test_argparse_opt_callback_parse_int_of_required_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test no more parameters. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -613,7 +609,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	obj->args[0].name_short = "-t";
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("--test-long");
@@ -621,7 +617,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -629,13 +625,13 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");
 
 	/* test with value. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("--test-long=100");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == 2, "Argparse parse expect success!");
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	val_saver = 0;
 	argv[1] = test_strdup("-t=100");
 	ret = rte_argparse_parse(obj, 2, argv);
@@ -643,7 +639,7 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test callback return failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_OPTIONAL;
 	argv[1] = test_strdup("-t=100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -654,7 +650,6 @@ test_argparse_opt_callback_parse_int_of_optional_val(void)
 static int
 test_argparse_pos_autosave_parse_int(void)
 {
-	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
 	struct rte_argparse *obj;
 	int val_saver = 0;
 	char *argv[3];
@@ -666,7 +661,8 @@ test_argparse_pos_autosave_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = (void *)&val_saver;
 	obj->args[0].val_set = NULL;
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	obj->args[1].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -675,14 +671,16 @@ test_argparse_pos_autosave_parse_int(void)
 	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
 
 	/* test positional autosave parse failed. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	val_saver = 0;
 	argv[1] = test_strdup("100a");
 	ret = rte_argparse_parse(obj, 2, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
 
 	/* test too much position parameters. */
-	obj->args[0].flags = flags;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[0].value_type = RTE_ARGPARSE_VALUE_TYPE_INT;
 	argv[1] = test_strdup("100");
 	argv[2] = test_strdup("200");
 	ret = rte_argparse_parse(obj, 3, argv);
@@ -727,12 +725,12 @@ test_argparse_pos_callback_parse_int(void)
 	obj->args[0].name_short = NULL;
 	obj->args[0].val_saver = NULL;
 	obj->args[0].val_set = (void *)1;
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[1].name_long = "test-long2";
 	obj->args[1].name_short = NULL;
 	obj->args[1].val_saver = NULL;
 	obj->args[1].val_set = (void *)2;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	obj->args[2].name_long = NULL;
 	argv[0] = test_strdup(obj->prog_name);
 	argv[1] = test_strdup("100");
@@ -743,8 +741,8 @@ test_argparse_pos_callback_parse_int(void)
 	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");
 
 	/* test positional callback parse failed. */
-	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
-	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
+	obj->args[0].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
+	obj->args[1].value_required = RTE_ARGPARSE_VALUE_REQUIRED;
 	argv[2] = test_strdup("200a");
 	ret = rte_argparse_parse(obj, 3, argv);
 	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
@@ -775,78 +773,80 @@ test_argparse_parse_type(void)
 	int ret;
 
 	/* test for int parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_INT, &val_int);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");
 
 	/* test for u8 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u8 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U8, &val_u8);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");
 
 	/* test for u16 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u16 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U16, &val_u16);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");
 
 	/* test for u32 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u32 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U32, &val_u32);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");
 
 	/* test for u64 parsing */
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
-	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
 	val_u64 = 0;
-	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
+	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_VALUE_TYPE_U64, &val_u64);
 	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
 	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");
 
 	/* test for string parsing - all it does is save string, so all are valid */
 	const char *val_str;
-	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_STR, &val_str);
+	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_VALUE_TYPE_STR, &val_str);
 	TEST_ASSERT(ret == 0, "Argparse parse a string failed unexpectedly!");
 
 	/* test for boolean parsing */
 	bool val_bool = false;
-	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (true) failed!");
-	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_false, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (false) failed!");
-	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (invalid) passed unexpectedly!");
-	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_true, RTE_ARGPARSE_VALUE_TYPE_BOOL, &val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == true, "Argparse parse type for bool (numeric true) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_false, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			&val_bool);
 	TEST_ASSERT(ret == 0 && val_bool == false, "Argparse parse type for bool (numeric false) failed!");
-	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_ARG_VALUE_BOOL, &val_bool);
+	ret = rte_argparse_parse_type(bool_numeric_invalid, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			&val_bool);
 	TEST_ASSERT(ret != 0, "Argparse parse type for bool (numeric invalid) passed unexpectedly!");
 	return 0;
 }
diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst
index f827312daa..9f11714890 100644
--- a/doc/guides/prog_guide/argparse_lib.rst
+++ b/doc/guides/prog_guide/argparse_lib.rst
@@ -70,14 +70,14 @@ The following code demonstrates how to use:
       .exit_on_error = true,
       .callback = argparse_user_callback,
       .args = {
-         { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_ARG_NO_VALUE       | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "--bbb", "-b", "bbb argument", &bbb_val, NULL,        RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "--ddd", "-d", "ddd argument", NULL,     (void *)1,   RTE_ARGPARSE_ARG_NO_VALUE       },
-         { "--eee", "-e", "eee argument", NULL,     (void *)2,   RTE_ARGPARSE_ARG_REQUIRED_VALUE },
-         { "--fff", "-f", "fff argument", NULL,     (void *)3,   RTE_ARGPARSE_ARG_OPTIONAL_VALUE },
-         { "ooo",   NULL, "ooo argument", &ooo_val, NULL,        RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT },
-         { "ppp",   NULL, "ppp argument", NULL,     (void *)300, RTE_ARGPARSE_ARG_REQUIRED_VALUE },
+         { "--aaa", "-a", "aaa argument", &aaa_val, (void *)100, RTE_ARGPARSE_VALUE_NONE,     RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "--bbb", "-b", "bbb argument", &bbb_val, NULL,        RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "--ccc", "-c", "ccc argument", &ccc_val, (void *)200, RTE_ARGPARSE_VALUE_OPTIONAL, RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "--ddd", "-d", "ddd argument", NULL,     (void *)1,   RTE_ARGPARSE_VALUE_NONE },
+         { "--eee", "-e", "eee argument", NULL,     (void *)2,   RTE_ARGPARSE_VALUE_REQUIRED },
+         { "--fff", "-f", "fff argument", NULL,     (void *)3,   RTE_ARGPARSE_VALUE_OPTIONAL },
+         { "ooo",   NULL, "ooo argument", &ooo_val, NULL,        RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_INT },
+         { "ppp",   NULL, "ppp argument", NULL,     (void *)300, RTE_ARGPARSE_VALUE_REQUIRED },
       },
    };
 
@@ -95,12 +95,12 @@ and the arguments which don't start with a hyphen (-) are positional arguments
 (they're ``ooo``/``ppp``).
 
 Every argument must be set whether to carry a value (one of
-``RTE_ARGPARSE_ARG_NO_VALUE``, ``RTE_ARGPARSE_ARG_REQUIRED_VALUE`` and
-``RTE_ARGPARSE_ARG_OPTIONAL_VALUE``).
+``RTE_ARGPARSE_VALUE_NONE``, ``RTE_ARGPARSE_VALUE_REQUIRED`` and
+``RTE_ARGPARSE_VALUE_OPTIONAL``).
 
 .. note::
 
-   Positional argument must set ``RTE_ARGPARSE_ARG_REQUIRED_VALUE``.
+   Positional argument must set ``RTE_ARGPARSE_VALUE_REQUIRED``.
 
 User Input Requirements
 ~~~~~~~~~~~~~~~~~~~~~~~
@@ -135,7 +135,7 @@ their values are parsing in the order defined.
 Parsing by autosave way
 ~~~~~~~~~~~~~~~~~~~~~~~
 
-Argument of known value type (e.g. ``RTE_ARGPARSE_ARG_VALUE_INT``)
+Argument of known value type (e.g. ``RTE_ARGPARSE_VALUE_TYPE_INT``)
 could be parsed using this autosave way,
 and its result will save in the ``val_saver`` field.
 
@@ -172,12 +172,20 @@ Multiple times argument
 ~~~~~~~~~~~~~~~~~~~~~~~
 
 If want to support the ability to enter the same argument multiple times,
-then should mark ``RTE_ARGPARSE_ARG_SUPPORT_MULTI`` in the ``flags`` field.
+then should mark ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI`` in the ``flags`` field.
 For example:
 
 .. code-block:: C
 
-   { "--xyz", "-x", "xyz argument", NULL, (void *)10, RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_SUPPORT_MULTI },
+   {
+      .long_name = "--xyz",
+      .short_name = "-x",
+      .desc = "xyz argument",
+      .val_set = (void *)10,
+      .val_mode = RTE_ARGPARSE_VALUE_REQUIRED,
+      // val_type is implicitly RTE_ARGPARSE_VALUE_TYPE_NONE,
+      .flags = RTE_ARGPARSE_FLAG_SUPPORT_MULTI,
+   },
 
 Then the user input could contain multiple ``--xyz`` arguments.
 
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index b67a504680..c5d9ec39ab 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -162,6 +162,10 @@ API Changes
 
 * graph: Added ``graph`` field to the ``rte_node.dispatch`` structure.
 
+* argparse: The ``rte_argparse_arg`` structure used for defining arguments has been updated.
+    See next section, :ref:`ABI-Changes` for details.
+
+.. _ABI-Changes:
 
 ABI Changes
 -----------
@@ -188,6 +192,37 @@ ABI Changes
      ``rte_argparse_parse()`` stops processing arguments when a ``--`` argument is encountered.
      This behaviour mirrors the behaviour of the ``getopt()`` function,
      as well as the behaviour of ``rte_eal_init()`` function.
+   * The ``rte_argparse_arg`` structure used for defining arguments has been updated
+     to separate out into separate fields the options for:
+
+     #. Whether the argument is required or optional.
+     #. What the type of the argument is (in case of saving the parameters automatically).
+     #. Any other flags - of which there is only one, ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI``, at this time.
+
+   * With the splitting of the flags into separate enums for categories,
+     the names of the flags have been changed to better reflect their purpose.
+     The flags/enum values are:
+
+     * For the ``value_required`` field:
+
+        * ``RTE_ARGPARSE_VALUE_NONE``
+        * ``RTE_ARGPARSE_VALUE_REQUIRED``
+        * ``RTE_ARGPARSE_VALUE_OPTIONAL``
+
+     * For the ``value_type`` field:
+
+        * ``RTE_ARGPARSE_VALUE_TYPE_NONE`` (No argument value type is specified, callback is to be used for processing.)
+        * ``RTE_ARGPARSE_VALUE_TYPE_INT``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U8``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U16``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U32``
+        * ``RTE_ARGPARSE_VALUE_TYPE_U64``
+        * ``RTE_ARGPARSE_VALUE_TYPE_STR``
+        * ``RTE_ARGPARSE_VALUE_TYPE_BOOL``
+
+     * Other flags:
+
+        * ``RTE_ARGPARSE_FLAG_SUPPORT_MULTI`` (Allows the argument to be specified multiple times.)
 
 
 Known Issues
diff --git a/examples/dma/dmafwd.c b/examples/dma/dmafwd.c
index acceae6b7b..5ba0aaa40b 100644
--- a/examples/dma/dmafwd.c
+++ b/examples/dma/dmafwd.c
@@ -644,43 +644,43 @@ dma_parse_args(int argc, char **argv, unsigned int nb_ports)
 		.args = {
 			{ "--mac-updating", NULL, "Enable MAC addresses updating",
 			  &mac_updating, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--no-mac-updating", NULL, "Disable MAC addresses updating",
 			  &mac_updating, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--portmask", "-p", "hexadecimal bitmask of ports to configure",
 			  NULL, (void *)CMD_LINE_OPT_PORTMASK_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--nb-queue", "-q", "number of RX queues per port (default is 1)",
 			  &nb_queues, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--copy-type", "-c", "type of copy: sw|hw",
 			  NULL, (void *)CMD_LINE_OPT_COPY_TYPE_INDEX,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE,
+			  RTE_ARGPARSE_VALUE_REQUIRED,
 			},
 			{ "--ring-size", "-s", "size of dmadev descriptor ring for hardware copy mode or rte_ring for software copy mode",
 			  &ring_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			{ "--dma-batch-size", "-b", "number of requests per DMA batch",
 			  &dma_batch_sz, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--max-frame-size", "-f", "max frame size",
 			  &max_frame_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--force-min-copy-size", "-m", "force a minimum copy length, even for smaller packets",
 			  &force_min_copy_size, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U32,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
 			},
 			{ "--stats-interval", "-i", "interval, in seconds, between stats prints (default is 1)",
 			  &stats_interval, NULL,
-			  RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_U16,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U16,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/examples/flow_filtering/main.c b/examples/flow_filtering/main.c
index c0bc1938ce..9c429a8335 100644
--- a/examples/flow_filtering/main.c
+++ b/examples/flow_filtering/main.c
@@ -274,11 +274,11 @@ flow_filtering_parse_args(int argc, char **argv)
 		.args = {
 			{ "--template", NULL, "Enable template API flow",
 			  &use_template_api, (void *)1,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			{ "--non-template", NULL, "Enable non template API flow",
 			  &use_template_api, (void *)0,
-			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_INT,
 			},
 			ARGPARSE_ARG_END(),
 		},
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 13a3f4d9eb..b5b8467dea 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -30,30 +30,48 @@ is_arg_positional(const struct rte_argparse_arg *arg)
 	return arg->name_long[0] != '-';
 }
 
-static inline uint32_t
-arg_attr_has_val(const struct rte_argparse_arg *arg)
+static inline bool
+is_valid_has_value_field(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_HAS_VAL_BITMASK, arg->flags);
+	switch (arg->value_required) {
+	case RTE_ARGPARSE_VALUE_NONE:
+	case RTE_ARGPARSE_VALUE_OPTIONAL:
+	case RTE_ARGPARSE_VALUE_REQUIRED:
+		return true;
+	/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }
 
-static inline uint32_t
-arg_attr_val_type(const struct rte_argparse_arg *arg)
-{
-	return RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, arg->flags);
+static inline bool
+is_valid_value_type_field(const struct rte_argparse_arg *arg)
+{
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return true;
+	/* omit default case so compiler warns on any missing enum values */
+	}
+	return false;
 }
 
+
 static inline bool
 arg_attr_flag_multi(const struct rte_argparse_arg *arg)
 {
-	return RTE_FIELD_GET64(RTE_ARGPARSE_ARG_SUPPORT_MULTI, arg->flags);
+	return (arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI) != 0;
 }
 
 static inline uint64_t
 arg_attr_unused_bits(const struct rte_argparse_arg *arg)
 {
-#define USED_BIT_MASK	(RTE_ARGPARSE_HAS_VAL_BITMASK | \
-			 RTE_ARGPARSE_VAL_TYPE_BITMASK | \
-			 RTE_ARGPARSE_ARG_SUPPORT_MULTI)
+#define USED_BIT_MASK	(RTE_ARGPARSE_FLAG_SUPPORT_MULTI)
 	return arg->flags & ~USED_BIT_MASK;
 }
 
@@ -110,56 +128,51 @@ verify_arg_help(const struct rte_argparse_arg *arg)
 static int
 verify_arg_has_val(const struct rte_argparse_arg *arg)
 {
-	uint32_t has_val = arg_attr_has_val(arg);
-
+	if (!is_valid_has_value_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value field!", arg->name_long);
+		return -EINVAL;
+	}
 	if (is_arg_positional(arg)) {
-		if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE)
+		if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED)
 			return 0;
 		ARGPARSE_LOG(ERR, "argument %s is positional, must config required-val!",
 			     arg->name_long);
 		return -EINVAL;
 	}
 
-	if (has_val == 0) {
-		ARGPARSE_LOG(ERR, "argument %s is optional, has-value config wrong!",
-			     arg->name_long);
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
 static int
 verify_arg_saver(const struct rte_argparse *obj, uint32_t index)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	const struct rte_argparse_arg *arg = &obj->args[index];
-	uint32_t val_type = arg_attr_val_type(arg);
-	uint32_t has_val = arg_attr_has_val(arg);
 
 	if (arg->val_saver == NULL) {
-		if (val_type != 0) {
+		if (arg->value_type != RTE_ARGPARSE_VALUE_TYPE_NONE) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, value-type should not be set!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		if (obj->callback == NULL) {
 			ARGPARSE_LOG(ERR, "argument %s parsed by callback, but callback is NULL!",
 				     arg->name_long);
 			return -EINVAL;
 		}
-
 		return 0;
 	}
 
-	if (val_type == 0 || val_type >= cmp_max) {
-		ARGPARSE_LOG(ERR, "argument %s value-type config wrong!", arg->name_long);
+	/* check value_type field */
+	if (!is_valid_value_type_field(arg)) {
+		ARGPARSE_LOG(ERR, "argument %s has invalid value-type field!", arg->name_long);
+		return -EINVAL;
+	}
+	if (arg->value_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "missing value-type for argument %s!", arg->name_long);
 		return -EINVAL;
 	}
 
-	if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) {
+	if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED && arg->val_set != NULL) {
 		ARGPARSE_LOG(ERR, "argument %s has required value, value-set should be NULL!",
 			     arg->name_long);
 		return -EINVAL;
@@ -180,7 +193,7 @@ verify_arg_flags(const struct rte_argparse *obj, uint32_t index)
 		return -EINVAL;
 	}
 
-	if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI))
+	if (!(arg->flags & RTE_ARGPARSE_FLAG_SUPPORT_MULTI))
 		return 0;
 
 	if (is_arg_positional(arg)) {
@@ -544,26 +557,27 @@ parse_arg_bool(struct rte_argparse_arg *arg, const char *value)
 static int
 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value)
 {
-	static struct {
-		int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value);
-	} map[] = {
-		/* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */
-		{ NULL          },
-		{ parse_arg_int },
-		{ parse_arg_u8  },
-		{ parse_arg_u16 },
-		{ parse_arg_u32 },
-		{ parse_arg_u64 },
-		{ parse_arg_str},
-		{ parse_arg_bool },
-	};
-	uint32_t index = arg_attr_val_type(arg);
-	int ret = -EINVAL;
-
-	if (index > 0 && index < RTE_DIM(map))
-		ret = map[index].f_parse_type(arg, value);
-
-	return ret;
+	switch (arg->value_type) {
+	case RTE_ARGPARSE_VALUE_TYPE_NONE:
+		ARGPARSE_LOG(ERR, "argument %s doesn't specify a value-type!", arg->name_long);
+		return -EINVAL;
+	case RTE_ARGPARSE_VALUE_TYPE_INT:
+		return parse_arg_int(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U8:
+		return parse_arg_u8(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U16:
+		return parse_arg_u16(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U32:
+		return parse_arg_u32(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_U64:
+		return parse_arg_u64(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_STR:
+		return parse_arg_str(arg, value);
+	case RTE_ARGPARSE_VALUE_TYPE_BOOL:
+		return parse_arg_bool(arg, value);
+	/* omit default case so compiler warns on missing enum values */
+	}
+	return -EINVAL;
 }
 
 /* arg_parse indicates the name entered by the user, which can be long-name or short-name. */
@@ -616,7 +630,7 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 			/* process positional parameters. */
 			position_index++;
 			if (position_index > position_count) {
-				ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv);
+				ARGPARSE_LOG(ERR, "too many positional arguments %s!", curr_argv);
 				return -EINVAL;
 			}
 			arg = find_position_arg(obj, position_index);
@@ -641,23 +655,21 @@ parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
 		}
 
 		if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) {
-			ARGPARSE_LOG(ERR, "argument %s should not occur multiple!",
-				     arg_name);
+			ARGPARSE_LOG(ERR, "argument %s should not occur multiple times!", arg_name);
 			return -EINVAL;
 		}
 
 		value = (has_equal != NULL ? has_equal + 1 : NULL);
-		if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) {
+		if (arg->value_required == RTE_ARGPARSE_VALUE_NONE) {
 			if (value != NULL) {
-				ARGPARSE_LOG(ERR, "argument %s should not take value!",
-					     arg_name);
+				ARGPARSE_LOG(ERR, "argument %s should not take value!", arg_name);
 				return -EINVAL;
 			}
-		} else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) {
+		} else if (arg->value_required == RTE_ARGPARSE_VALUE_REQUIRED) {
 			if (value == NULL) {
 				if (i >= argc - 1) {
 					ARGPARSE_LOG(ERR, "argument %s doesn't have value!",
-						     arg_name);
+							arg_name);
 					return -EINVAL;
 				}
 				/* Set value and make i move next. */
@@ -809,21 +821,18 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
 
 RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_argparse_parse_type, 24.03)
 int
-rte_argparse_parse_type(const char *str, uint64_t val_type, void *val)
+rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val)
 {
-	uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK,
-					   RTE_ARGPARSE_ARG_VALUE_MAX);
 	struct rte_argparse_arg arg = {
 		.name_long = str,
 		.name_short = NULL,
 		.val_saver = val,
 		.val_set = NULL,
-		.flags = val_type,
+		.value_type = val_type,
 	};
-	uint32_t value_type = arg_attr_val_type(&arg);
-
-	if (value_type == 0 || value_type >= cmp_max)
+	if (val_type == RTE_ARGPARSE_VALUE_TYPE_NONE) {
+		ARGPARSE_LOG(ERR, "argument %s doesn't have value-type!", str);
 		return -EINVAL;
-
+	}
 	return parse_arg_autosave(&arg, str);
 }
diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h
index fd5c33b319..3d04d47771 100644
--- a/lib/argparse/rte_argparse.h
+++ b/lib/argparse/rte_argparse.h
@@ -37,49 +37,45 @@
 extern "C" {
 #endif
 
-/**@{@name Flag definition (in bitmask form) for an argument
- *
- * @note Bits[0~1] represent the argument whether has value,
- * bits[2~9] represent the value type which used when autosave.
- *
- * @see struct rte_argparse_arg::flags
- */
-/** The argument has no value. */
-#define RTE_ARGPARSE_ARG_NO_VALUE       RTE_SHIFT_VAL64(1, 0)
-/** The argument must have a value. */
-#define RTE_ARGPARSE_ARG_REQUIRED_VALUE RTE_SHIFT_VAL64(2, 0)
-/** The argument has optional value. */
-#define RTE_ARGPARSE_ARG_OPTIONAL_VALUE RTE_SHIFT_VAL64(3, 0)
-/** The argument's value is int type. */
-#define RTE_ARGPARSE_ARG_VALUE_INT      RTE_SHIFT_VAL64(1, 2)
-/** The argument's value is uint8 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U8       RTE_SHIFT_VAL64(2, 2)
-/** The argument's value is uint16 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U16      RTE_SHIFT_VAL64(3, 2)
-/** The argument's value is uint32 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U32      RTE_SHIFT_VAL64(4, 2)
-/** The argument's value is uint64 type. */
-#define RTE_ARGPARSE_ARG_VALUE_U64      RTE_SHIFT_VAL64(5, 2)
-/** The argument's value is string type. */
-#define RTE_ARGPARSE_ARG_VALUE_STR      RTE_SHIFT_VAL64(6, 2)
-/** The argument's value is boolean flag type. */
-#define RTE_ARGPARSE_ARG_VALUE_BOOL     RTE_SHIFT_VAL64(7, 2)
-/** Max value type. */
-#define RTE_ARGPARSE_ARG_VALUE_MAX      RTE_SHIFT_VAL64(8, 2)
 /**
- * Flag for that argument support occur multiple times.
- * This flag can be set only when the argument is optional.
- * When this flag is set, the callback type must be used for parsing.
+ * enum defining whether an argument takes a value or not.
  */
-#define RTE_ARGPARSE_ARG_SUPPORT_MULTI  RTE_BIT64(10)
-/** Reserved for this library implementation usage. */
-#define RTE_ARGPARSE_ARG_RESERVED_FIELD RTE_GENMASK64(63, 48)
-/**@}*/
+enum rte_argparse_value_required {
+	/** The argument takes no value. */
+	RTE_ARGPARSE_VALUE_NONE,
+	/** The argument must have a value. */
+	RTE_ARGPARSE_VALUE_REQUIRED,
+	/** The argument has optional value. */
+	RTE_ARGPARSE_VALUE_OPTIONAL,
+};
 
-/** Bitmask used to get the argument whether has value. */
-#define RTE_ARGPARSE_HAS_VAL_BITMASK	RTE_GENMASK64(1, 0)
-/** Bitmask used to get the argument's value type. */
-#define RTE_ARGPARSE_VAL_TYPE_BITMASK	RTE_GENMASK64(9, 2)
+/** enum defining the type of the argument, integer, boolean or just string */
+enum rte_argparse_value_type {
+	/** Argument takes no value, or value type not specified.
+	 * Should be used when argument is to be handled via callback.
+	 */
+	RTE_ARGPARSE_VALUE_TYPE_NONE = 0,
+	/** The argument's value is int type. */
+	RTE_ARGPARSE_VALUE_TYPE_INT,
+	/** The argument's value is uint8 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U8,
+	/** The argument's value is uint16 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U16,
+	/** The argument's value is uint32 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U32,
+	/** The argument's value is uint64 type. */
+	RTE_ARGPARSE_VALUE_TYPE_U64,
+	/** The argument's value is string type. */
+	RTE_ARGPARSE_VALUE_TYPE_STR,
+	/** The argument's value is boolean flag type. */
+	RTE_ARGPARSE_VALUE_TYPE_BOOL,
+};
+
+/** Additional flags which may be specified for each argument */
+enum rte_argparse_arg_flags {
+	/** argument may be specified multiple times on the commandline */
+	RTE_ARGPARSE_FLAG_SUPPORT_MULTI = RTE_BIT32(0),
+};
 
 /**
  * A structure used to hold argument's configuration.
@@ -105,10 +101,8 @@ struct rte_argparse_arg {
 
 	/**
 	 * Saver for the argument's value.
-	 * 1) If the filed is NULL, the callback way is used for parsing
-	 *    argument.
-	 * 2) If the field is not NULL, the autosave way is used for parsing
-	 *    argument.
+	 * 1) If this field is NULL, the callback is used for parsing argument.
+	 * 2) If this field is not NULL, the argument's value will be automatically saved.
 	 */
 	void *val_saver;
 	/**
@@ -123,8 +117,13 @@ struct rte_argparse_arg {
 	 */
 	void *val_set;
 
-	/** Flag definition (RTE_ARGPARSE_ARG_*) for the argument. */
-	uint64_t flags;
+	/** Specify if the argument takes a value, @see enum rte_argparse_value_required. */
+	enum rte_argparse_value_required value_required;
+	/** The type of the argument, @see enum rte_argparse_value_type. */
+	enum rte_argparse_value_type value_type;
+
+	/** any additional flags for this argument */
+	uint32_t flags;
 };
 
 /**
@@ -199,7 +198,7 @@ int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
  * @param str
  *   Input string.
  * @param val_type
- *   The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type.
+ *   The value type, @see rte_argparse_value_type.
  * @param val
  *   Saver for the value.
  *
@@ -207,7 +206,7 @@ int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv);
  *   0 on success. Otherwise negative value is returned.
  */
 __rte_experimental
-int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val);
+int rte_argparse_parse_type(const char *str, enum rte_argparse_value_type val_type, void *val);
 
 #ifdef __cplusplus
 }
-- 
2.48.1


^ permalink raw reply	[relevance 6%]

* Minutes of Technical Board Meeting, 2025-May-28
@ 2025-06-13  8:29  5% Jerin Jacob
  0 siblings, 0 replies; 153+ results
From: Jerin Jacob @ 2025-06-13  8:29 UTC (permalink / raw)
  To: dev

Minutes of Technical Board Meeting, 2025-May-28
 
Members Attending
-----------------
-Aaron
-Bruce
-Hemant
-Jerin
-Kevin (Next Chair)
-Konstantin
-Maxime
-Morten
-Stephen
-Thomas
      
 
NOTE: The technical board meetings every second Wednesday at
https://zoom-lfx.platform.linuxfoundation.org/meeting/96459488340?password=d808f1f6-0a28-4165-929e-5a5bcae7efebat 3 pm UTC.
Meetings are public, and DPDK community members are welcome to attend.
NOTE: Next meeting will be on Wednesday 2025- June - 11th @3pm UTC, and will be chaired by Kevin
 
 
 
1) License Exception for getline() Function in Windows EAL
a) Stephen to communicate the license exception request to the Governing Board.
b) The Technical Board agreed to grant an exception for the use of getline() in Windows EAL.
c) The Governing Board subsequently approved the exception in a later meeting.
 
2) ABI Stability - Key Considerations
The board had a detailed discussion on ABI stability. No final decisions were made, and the topic will continue to be explored. Key discussion points included:
a) Proposal to extend the ABI compatibility window from 1 year to 2 years.
b) Evaluated the feasibility of making asymmetric crypto device functions non-inline.
c) Encouraged use of initialization functions for public structures, acknowledging that full coverage may not be achievable.
d) Concerns from consumers (e.g., OVS) regarding delays caused by ABI change restrictions; suggestion to consider multiple LTS streams as a potential solution.
e) Observed that product owners prefer LTS, regardless of ABI/API changes.
f) Suggested that ABI changes be limited to non-LTS versions.
g) Agreed to continue discussions at the upcoming U.S. Summit and via the DPDK mailing list on above suggestion.
h) Topic to remain on the agenda for the next Technical Board meeting.
 
3) MACB Driver Enablement
a) Scheduled for discussion in the next Technical Board meeting


^ permalink raw reply	[relevance 5%]

* Minutes of Technical Board Meeting, 2025-June-11
@ 2025-06-13  9:48  4% Kevin Traynor
  0 siblings, 0 replies; 153+ results
From: Kevin Traynor @ 2025-06-13  9:48 UTC (permalink / raw)
  To: dev

Minutes of Technical Board Meeting, 2025-June-11

Members Attending
-----------------
-Aaron
-Bruce
-Jerin
-Kevin
-Morten
-Stephen
-Thomas

NOTE: The technical board meetings every second Wednesday at
https://zoom-lfx.platform.linuxfoundation.org/meeting/96459488340?password=d808f1f6-0a28-4165-929e-5a5bcae7efeb
at 3 pm UTC.
Meetings are public, and DPDK community members are welcome to attend.
NOTE: Next meeting will be on Wednesday 2025- June - 25th @3pm UTC, and
will be chaired by Maxime.

1) MACB Driver Enablement (Stephen)
- Better to take pmd when the kmod is approved
- Do we still want to allow kernel modules ? perhaps if it is strictly
required for a vendor

2) AI meeting notes (Aaron)
- AI companion will be used to summarize techboard meetings
- This will be used to help minute taker compose summary to send out
- AI: Kevin contact meeting owner (Nathan) about this

3) github AI for code reviews (Aaron/Jerin)
- sourcery-ai may be able to help with code reviews
- It will require new branches being created with PRs on github
- Preference not to do that in existing dpdk github repo
- AI: Jerin will propose a workflow

4) ethdev maintainer (Thomas)
- There is need for more help with ethdev
- Other areas also require maintainers
- An email will be sent on announce to advertise
- AI: Kevin to draft email for review

5) ABI stability (Previous meeting)
- Proposal to only guarantee ABI stability on LTS minor releases
- Will need to ensure that ABI breaks are still flagged during reviews
- This will need to be discussed more. Topic for US summit.

6) US summit (Stephen)
- AI: Kevin request Nathan to give an update at next techboard


^ permalink raw reply	[relevance 4%]

* Re: [PATCH v2] pcapng: allow any protocol link type for the interface block
  2025-06-06 21:52  3% ` Schneide
  2025-06-08 22:34  0%   ` Stephen Hemminger
@ 2025-06-16 14:29  2%   ` Dylan Schneider
  2025-06-24 15:15  2%     ` Dylan Schneider
  1 sibling, 1 reply; 153+ results
From: Dylan Schneider @ 2025-06-16 14:29 UTC (permalink / raw)
  To: dev, Thomas Monjalon, Reshma Pattan, Stephen Hemminger,
	Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram, Zhirun Yan

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

Hello Stephen and Thomas,
I uploaded this patch which says it has UT failures, but upon closer inspection, there aren't any signs of any tests failing. Can you please take a look?
________________________________
From: Schneide <schneide@qti.qualcomm.com>
Sent: Friday, June 6, 2025 3:52 PM
To: dev@dpdk.org <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Reshma Pattan <reshma.pattan@intel.com>; Stephen Hemminger <stephen@networkplumber.org>; Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K <kirankumark@marvell.com>; Nithin Dabilpuram <ndabilpuram@marvell.com>; Zhirun Yan <yanzhirun_163@163.com>
Cc: Dylan Schneider <schneide@qti.qualcomm.com>
Subject: [PATCH v2] pcapng: allow any protocol link type for the interface block

From: Dylan Schneider <schneide@qti.qualcomm.com>

Allow the user to specify protocol link type when creating pcapng files.
This change is needed to specify the protocol type in the pcapng file,
DLT_EN10MB specifies ethernet packets only. This will allow dissectors
for other protocols to be used on files generated by pcapng.

Includes a breaking change to rte_pcapng_add_interface to add link_type
parameter. Existing calls to the function have been updated to pass
DLT_EN10MB for the link type argument.

Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
Cc: stephen@networkplumber.org
---
 .mailmap                               |  1 +
 app/dumpcap/main.c                     |  4 ++--
 app/test/test_pcapng.c                 |  8 ++++----
 doc/guides/rel_notes/release_25_07.rst |  5 ++++-
 lib/graph/graph_pcap.c                 |  4 +++-
 lib/pcapng/meson.build                 |  2 ++
 lib/pcapng/rte_pcapng.c                | 21 +++++++++++++++------
 lib/pcapng/rte_pcapng.h                |  4 +++-
 8 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/.mailmap b/.mailmap
index 91e08f4a1f..a585124832 100644
--- a/.mailmap
+++ b/.mailmap
@@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
 Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
 Duncan Bellamy <dunk@denkimushi.com>
 Dustin Lundquist <dustin@null-ptr.net>
+Dylan Schneider <schneide@qti.qualcomm.com>
 Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
 Ed Czeck <ed.czeck@atomicrules.com>
 Eduard Serra <eserra@vmware.com>
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..e0e2b26269 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
                 free(os);

                 TAILQ_FOREACH(intf, &interfaces, next) {
-                       if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
-                                                    intf->ifdescr, intf->opts.filter) < 0)
+                       if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
+                               intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
                                 rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
                                         intf->port);
                 }
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 8f2cff36c3..bcf99724fa 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -345,7 +345,7 @@ test_add_interface(void)
         }

         /* Add interface to the file */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        NULL, NULL, NULL);
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u\n", port_id);
@@ -353,7 +353,7 @@ test_add_interface(void)
         }

         /* Add interface with ifname and ifdescr */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        "myeth", "Some long description", NULL);
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u with ifname\n", port_id);
@@ -361,7 +361,7 @@ test_add_interface(void)
         }

         /* Add interface with filter */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        NULL, NULL, "tcp port 8080");
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u with filter\n", port_id);
@@ -406,7 +406,7 @@ test_write_packets(void)
         }

         /* Add interface to the file */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        NULL, NULL, NULL);
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u\n", port_id);
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de..2396c7b014 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -108,7 +108,10 @@ API Changes
    This section is a comment. Do not overwrite or remove it.
    Also, make sure to start the actual text at the margin.
    =======================================================
-
+* pcapng: Changed the API for adding interfaces to include a link type argument.
+  The link type was previously hardcoded to the ethernet link type in the API.
+  This argument is added to ``rte_pcapng_add_interface``.
+  These functions are versioned to retain binary compatibility until the next LTS release.

 ABI Changes
 -----------
diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
index 89525f1220..13d86b7a18 100644
--- a/lib/graph/graph_pcap.c
+++ b/lib/graph/graph_pcap.c
@@ -11,6 +11,8 @@
 #include <rte_mbuf.h>
 #include <rte_pcapng.h>

+#include <pcap/pcap.h>
+
 #include "rte_graph_worker.h"

 #include "graph_pcap_private.h"
@@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)

         /* Add the configured interfaces as possible capture ports */
         RTE_ETH_FOREACH_DEV(portid) {
-               ret = rte_pcapng_add_interface(pcapng_fd, portid,
+               ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
                                                NULL, NULL, NULL);
                 if (ret < 0) {
                         graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
index 4549925d41..3aa7ba5155 100644
--- a/lib/pcapng/meson.build
+++ b/lib/pcapng/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_pcapng.c')
 headers = files('rte_pcapng.h')

 deps += ['ethdev']
+
+use_function_versioning = true
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index cacbefdc50..f18af25983 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -200,11 +200,10 @@ pcapng_section_block(rte_pcapng_t *self,
 }

 /* Write an interface block for a DPDK port */
-RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
-int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
-                        const char *ifname, const char *ifdescr,
-                        const char *filter)
+RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
+                       (rte_pcapng_t *self, uint16_t port, uint16_t link_type,
+                       const char *ifname, const char *ifdescr,
+                       const char *filter))
 {
         struct pcapng_interface_block *hdr;
         struct rte_eth_dev_info dev_info;
@@ -274,7 +273,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
         hdr = (struct pcapng_interface_block *)buf;
         *hdr = (struct pcapng_interface_block) {
                 .block_type = PCAPNG_INTERFACE_BLOCK,
-               .link_type = 1,         /* DLT_EN10MB - Ethernet */
+               .link_type = link_type,
                 .block_length = len,
         };

@@ -319,6 +318,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
         return write(self->outfd, buf, len);
 }

+RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
+                                 (rte_pcapng_t *self, uint16_t port,
+                                  const char *ifname, const char *ifdescr,
+                                  const char *filter))
+{
+       /* Call the new version with a default link_type (Ethernet) */
+       return rte_pcapng_add_interface(self, port, DLT_EN10MB,
+                                                                       ifname, ifdescr, filter);
+}
+
 /*
  * Write an Interface statistics block at the end of capture.
  */
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index 48f2b57564..9880d415c4 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
  *  The handle to the packet capture file
  * @param port
  *  The Ethernet port to report stats on.
+ * @param link_type
+ *   The link type (e.g., DLT_EN10MB).
  * @param ifname (optional)
  *  Interface name to record in the file.
  *  If not specified, name will be constructed from port
@@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
  * must be added.
  */
 int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
+rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
                          const char *ifname, const char *ifdescr,
                          const char *filter);

--
2.27.0


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

^ permalink raw reply	[relevance 2%]

* Re: [PATCH 2/2] lib/graph: rte_malloc for cache-aligned structs
  @ 2025-06-17 12:46  4%   ` Jerin Jacob
  0 siblings, 0 replies; 153+ results
From: Jerin Jacob @ 2025-06-17 12:46 UTC (permalink / raw)
  To: Marat Khalili, Stephen Hemminger, Thomas Monjalon, David Marchand
  Cc: Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram, Zhirun Yan, dev

On Tue, Jun 17, 2025 at 4:32 PM Marat Khalili <marat.khalili@huawei.com> wrote:
>
> This was flagged by undefined behaviour sanitizer: struct
> rte_graph_cluster_stats is declared as `__rte_cache_aligned` but was
> allocated using stdlib realloc which caused misaligned allocation. More
> than one test needs to be executed in series in order to reproduce the
> problem using graph_autotest, e.g:
>
>     app/dpdk-test --no-huge --no-pci -m128 graph_autotest graph_autotest
>
> First sanitizer message  (similar ones follow):
>
>     lib/graph/graph_stats.c:209:13: runtime error: member access within
>     misaligned address 0x606000008ea0 for type 'struct
>     rte_graph_cluster_stats', which requires 64 byte alignment
>
> To fix the issue replace realloc calls with rte_malloc and rte_realloc
> specifying correct alignment, use rte_free to free the result.
>
> Signed-off-by: Marat Khalili <marat.khalili@huawei.com>


Since this memory is used in slowpath, heap memory is fine.
I think, better fix will be to remove cache alignment from
rte_graph_cluster_stats.
Not sure it will call for ABI change though.Run
devtools/test-meson-builds.sh to validate any ABI breakage.






> ---
>  lib/graph/graph_stats.c | 9 +++++----
>  1 file changed, 5 insertions(+), 4 deletions(-)
>
> diff --git a/lib/graph/graph_stats.c b/lib/graph/graph_stats.c
> index 57cd72e7cc..7ae8ee4987 100644
> --- a/lib/graph/graph_stats.c
> +++ b/lib/graph/graph_stats.c
> @@ -203,7 +203,7 @@ stats_mem_init(struct cluster *cluster,
>         cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
>         cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
>
> -       stats = realloc(NULL, sz);
> +       stats = rte_malloc(NULL, sz, RTE_CACHE_LINE_SIZE);
>         if (stats) {
>                 memset(stats, 0, sz);
>                 stats->fn = fn;
> @@ -248,7 +248,8 @@ stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
>         }
>
>         /* Hey, it is a new node, allocate space for it in the reel */
> -       stats = realloc(stats, stats->sz + stats->cluster_node_size);
> +       stats = rte_realloc(stats, stats->sz + stats->cluster_node_size,
> +               RTE_CACHE_LINE_SIZE);
>         if (stats == NULL)
>                 SET_ERR_JMP(ENOMEM, err, "Realloc failed");
>         *stats_in = NULL;
> @@ -301,7 +302,7 @@ stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
>
>         return 0;
>  free:
> -       free(stats);
> +       rte_free(stats);
>  err:
>         return -rte_errno;
>  }
> @@ -309,7 +310,7 @@ stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
>  static void
>  stats_mem_fini(struct rte_graph_cluster_stats *stats)
>  {
> -       free(stats);
> +       rte_free(stats);
>  }
>
>  static void
> --
> 2.43.0
>

^ permalink raw reply	[relevance 4%]

* RE: [EXTERNAL] [PATCH] app/compress-perf: support dictionary files
  @ 2025-06-17 21:34  4%   ` Akhil Goyal
  0 siblings, 0 replies; 153+ results
From: Akhil Goyal @ 2025-06-17 21:34 UTC (permalink / raw)
  To: Sameer Vaze, Sunila Sahu, Fan Zhang, Ashish Gupta; +Cc: dev

> compress/zlib: support PDCP checksum
> 
> compress/zlib: support zlib dictionary
> 
> compressdev: add PDCP checksum
> 
> compressdev: support zlib dictionary
> 
> Adds support to provide predefined dictionaries to zlib. Handles setting
> and getting of dictionaries using zlib apis. Also includes support to
> read dictionary files
> 
> Adds support for passing in and validationg 3GPP PDCP spec defined
> checksums as defined under the Uplink Data Compression(UDC) feature.
> Changes also include functions that do inflate or deflate specific
> checksum operations.
> 
> Introduces new members to compression api structures to allow setting
> predefined dictionaries
> 
> Signed-off-by: Sameer Vaze <svaze@qti.qualcomm.com>

Seems like multiple patches are squashed into a single patch

I see that this patch has ABI breaks.
We need to defer this patch for next ABI break release.
Please split the patch appropriately.
First patch should define the library changes.
And subsequently logically broken PMD patches
Followed by application patches.
Ensure each patch is compilable.

Since this patch is breaking ABI/API,
Please send a deprecation notice to be merged in this release and
Implementation for next release.

Also avoid unnecessary and irrelevant code changes.


^ permalink raw reply	[relevance 4%]

* [PATCH] eal: deprecate old coremask-based EAL parameters
@ 2025-06-24 13:41  4% Bruce Richardson
  0 siblings, 0 replies; 153+ results
From: Bruce Richardson @ 2025-06-24 13:41 UTC (permalink / raw)
  To: dev
  Cc: david.marchand, thomas, techboard, Bruce Richardson,
	Vipin Varghese, Tyler Retzlaff

As the number of cores/cpus on platforms has increased over the years,
the use of coremasks rather than core-lists for identifying DPDK cores
has become more and more unwieldy. At this point, let's deprecate the
coremask-based EAL parameters for future removal, and point users to the
core-list based versions instead.

Depends-on: series-35540 ("use core lists not masks in documentation")

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Vipin Varghese <vipin.varghese@amd.com>
---
 doc/guides/linux_gsg/build_sample_apps.rst |  7 +++----
 doc/guides/linux_gsg/eal_args.include.rst  |  9 ---------
 doc/guides/rel_notes/deprecation.rst       | 10 ++++++++++
 lib/eal/common/eal_common_options.c        |  6 ++++++
 lib/eal/include/rte_lcore.h                |  1 -
 5 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/doc/guides/linux_gsg/build_sample_apps.rst b/doc/guides/linux_gsg/build_sample_apps.rst
index 8b8026705b..9ce564e881 100644
--- a/doc/guides/linux_gsg/build_sample_apps.rst
+++ b/doc/guides/linux_gsg/build_sample_apps.rst
@@ -126,10 +126,9 @@ and that cores 0-3 are present and are to be used for running the application)::
 Logical Core Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The coremask (-c 0x0f) or corelist (-l 0-3) parameter is always mandatory for DPDK applications.
-Each bit of the mask corresponds to the equivalent logical core number as reported by Linux. The preferred corelist option is a cleaner method to define cores to be used.
+The corelist (-l/--lcores 0-3) parameter is always mandatory for DPDK applications.
 Since these logical core numbers, and their mapping to specific cores on specific NUMA sockets, can vary from platform to platform,
-it is recommended that the core layout for each platform be considered when choosing the coremask/corelist to use in each case.
+it is recommended that the core layout for each platform be considered when choosing the corelist to use in each case.
 
 On initialization of the EAL layer by a DPDK application, the logical cores to be used and their socket location are displayed.
 This information can also be determined for all cores on the system by examining the ``/proc/cpuinfo`` file, for example, by running cat ``/proc/cpuinfo``.
@@ -151,7 +150,7 @@ This can be useful when using other processors to understand the mapping of the
 
 .. warning::
 
-    The logical core layout can change between different board layouts and should be checked before selecting an application coremask/corelist.
+    The logical core layout can change between different board layouts and should be checked before selecting an application corelist.
 
 Hugepage Memory Use by Applications
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/guides/linux_gsg/eal_args.include.rst b/doc/guides/linux_gsg/eal_args.include.rst
index 6ee79877c1..b0cd757569 100644
--- a/doc/guides/linux_gsg/eal_args.include.rst
+++ b/doc/guides/linux_gsg/eal_args.include.rst
@@ -4,10 +4,6 @@
 Lcore-related options
 ~~~~~~~~~~~~~~~~~~~~~
 
-*   ``-c <core mask>``
-
-    Set the hexadecimal bitmask of the cores to run on.
-
 *   ``-l/--lcores <core list>``
 
     List of cores to run on
@@ -75,15 +71,10 @@ Lcore-related options
 
     Core ID that is used as main.
 
-*   ``-s <service core mask>``
-
-    Hexadecimal bitmask of cores to be used as service cores.
-
 *   ``-S <service core list>``
 
     List of cores to be used as service cores.
 
-
 Device-related options
 ~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 36489f6e68..2ea898ff8a 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -17,6 +17,16 @@ Other API and ABI deprecation notices are to be posted below.
 Deprecation Notices
 -------------------
 
+* EAL: The ``-c <coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-l <corelist>`` or ``--lcores=<corelist>`` parameters instead
+  to specify the cores to be used when running a DPDK application.
+
+* EAL: The ``-s <service-coremask>`` commandline parameter is deprecated
+  and will be removed in a future release.
+  Use the ``-S <service-corelist>`` parameter instead
+  to specify the cores to be used for background services in DPDK.
+
 * build: The ``enable_kmods`` option is deprecated and will be removed in a future release.
   Setting/clearing the option has no impact on the build.
   Instead, kernel modules will be always built for OS's where out-of-tree kernel modules
diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index 83b6fc7e89..f0a9ddeeb7 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -616,6 +616,9 @@ eal_parse_service_coremask(const char *coremask)
 	int val;
 	uint32_t taken_lcore_count = 0;
 
+	EAL_LOG(WARNING, "'-s <service-coremask>' is deprecated, and will be removed in a future release.");
+	EAL_LOG(WARNING, "\tUse '-S <service-corelist>' option instead.");
+
 	if (coremask == NULL)
 		return -1;
 	/* Remove all blank characters ahead and after .
@@ -779,6 +782,9 @@ rte_eal_parse_coremask(const char *coremask, int *cores)
 		cores[idx] = -1;
 	idx = 0;
 
+	EAL_LOG(WARNING, "'-c <coremask>' option is deprecated, and will be removed in a future release");
+	EAL_LOG(WARNING, "\tUse '-l <corelist>' or '--lcores=<corelist>' option instead");
+
 	/* Remove all blank characters ahead and after .
 	 * Remove 0x/0X if exists.
 	 */
diff --git a/lib/eal/include/rte_lcore.h b/lib/eal/include/rte_lcore.h
index 44959779a1..10f965b4f0 100644
--- a/lib/eal/include/rte_lcore.h
+++ b/lib/eal/include/rte_lcore.h
@@ -102,7 +102,6 @@ unsigned int rte_lcore_count(void);
  * When option -c or -l is given, the index corresponds
  * to the order in the list.
  * For example:
- * -c 0x30, lcore 4 has index 0, and 5 has index 1.
  * -l 22,18 lcore 22 has index 0, and 18 has index 1.
  *
  * @param lcore_id
-- 
2.48.1


^ permalink raw reply	[relevance 4%]

* Re: [PATCH v2] pcapng: allow any protocol link type for the interface block
  2025-06-16 14:29  2%   ` Dylan Schneider
@ 2025-06-24 15:15  2%     ` Dylan Schneider
  0 siblings, 0 replies; 153+ results
From: Dylan Schneider @ 2025-06-24 15:15 UTC (permalink / raw)
  To: dev, Thomas Monjalon, Reshma Pattan, Stephen Hemminger,
	Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram, Zhirun Yan

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

Hey Stephen, I saw the comment you made to re-run UTs but I cant distinguish if anything actually happened, the report still says everything is passing.
________________________________
From: Dylan Schneider <schneide@qti.qualcomm.com>
Sent: Monday, June 16, 2025 8:29 AM
To: dev@dpdk.org <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Reshma Pattan <reshma.pattan@intel.com>; Stephen Hemminger <stephen@networkplumber.org>; Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K <kirankumark@marvell.com>; Nithin Dabilpuram <ndabilpuram@marvell.com>; Zhirun Yan <yanzhirun_163@163.com>
Subject: Re: [PATCH v2] pcapng: allow any protocol link type for the interface block


WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.

Hello Stephen and Thomas,
I uploaded this patch which says it has UT failures, but upon closer inspection, there aren't any signs of any tests failing. Can you please take a look?
________________________________
From: Schneide <schneide@qti.qualcomm.com>
Sent: Friday, June 6, 2025 3:52 PM
To: dev@dpdk.org <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Reshma Pattan <reshma.pattan@intel.com>; Stephen Hemminger <stephen@networkplumber.org>; Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K <kirankumark@marvell.com>; Nithin Dabilpuram <ndabilpuram@marvell.com>; Zhirun Yan <yanzhirun_163@163.com>
Cc: Dylan Schneider <schneide@qti.qualcomm.com>
Subject: [PATCH v2] pcapng: allow any protocol link type for the interface block

From: Dylan Schneider <schneide@qti.qualcomm.com>

Allow the user to specify protocol link type when creating pcapng files.
This change is needed to specify the protocol type in the pcapng file,
DLT_EN10MB specifies ethernet packets only. This will allow dissectors
for other protocols to be used on files generated by pcapng.

Includes a breaking change to rte_pcapng_add_interface to add link_type
parameter. Existing calls to the function have been updated to pass
DLT_EN10MB for the link type argument.

Fixes: d1da6d0d04c7 ("pcapng: require per-interface information")
Signed-off-by: Dylan Schneider <schneide@qti.qualcomm.com>
Cc: stephen@networkplumber.org
---
 .mailmap                               |  1 +
 app/dumpcap/main.c                     |  4 ++--
 app/test/test_pcapng.c                 |  8 ++++----
 doc/guides/rel_notes/release_25_07.rst |  5 ++++-
 lib/graph/graph_pcap.c                 |  4 +++-
 lib/pcapng/meson.build                 |  2 ++
 lib/pcapng/rte_pcapng.c                | 21 +++++++++++++++------
 lib/pcapng/rte_pcapng.h                |  4 +++-
 8 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/.mailmap b/.mailmap
index 91e08f4a1f..a585124832 100644
--- a/.mailmap
+++ b/.mailmap
@@ -390,6 +390,7 @@ Dukai Yuan <dukaix.yuan@intel.com>
 Dumitru Ceara <dceara@redhat.com> <dumitru.ceara@gmail.com>
 Duncan Bellamy <dunk@denkimushi.com>
 Dustin Lundquist <dustin@null-ptr.net>
+Dylan Schneider <schneide@qti.qualcomm.com>
 Dzmitry Sautsa <dzmitryx.sautsa@intel.com>
 Ed Czeck <ed.czeck@atomicrules.com>
 Eduard Serra <eserra@vmware.com>
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..e0e2b26269 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -800,8 +800,8 @@ static dumpcap_out_t create_output(void)
                 free(os);

                 TAILQ_FOREACH(intf, &interfaces, next) {
-                       if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
-                                                    intf->ifdescr, intf->opts.filter) < 0)
+                       if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
+                               intf->ifname, intf->ifdescr, intf->opts.filter) < 0)
                                 rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
                                         intf->port);
                 }
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 8f2cff36c3..bcf99724fa 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -345,7 +345,7 @@ test_add_interface(void)
         }

         /* Add interface to the file */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        NULL, NULL, NULL);
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u\n", port_id);
@@ -353,7 +353,7 @@ test_add_interface(void)
         }

         /* Add interface with ifname and ifdescr */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        "myeth", "Some long description", NULL);
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u with ifname\n", port_id);
@@ -361,7 +361,7 @@ test_add_interface(void)
         }

         /* Add interface with filter */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        NULL, NULL, "tcp port 8080");
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u with filter\n", port_id);
@@ -406,7 +406,7 @@ test_write_packets(void)
         }

         /* Add interface to the file */
-       ret = rte_pcapng_add_interface(pcapng, port_id,
+       ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
                                        NULL, NULL, NULL);
         if (ret < 0) {
                 fprintf(stderr, "can not add port %u\n", port_id);
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 6b070801de..2396c7b014 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -108,7 +108,10 @@ API Changes
    This section is a comment. Do not overwrite or remove it.
    Also, make sure to start the actual text at the margin.
    =======================================================
-
+* pcapng: Changed the API for adding interfaces to include a link type argument.
+  The link type was previously hardcoded to the ethernet link type in the API.
+  This argument is added to ``rte_pcapng_add_interface``.
+  These functions are versioned to retain binary compatibility until the next LTS release.

 ABI Changes
 -----------
diff --git a/lib/graph/graph_pcap.c b/lib/graph/graph_pcap.c
index 89525f1220..13d86b7a18 100644
--- a/lib/graph/graph_pcap.c
+++ b/lib/graph/graph_pcap.c
@@ -11,6 +11,8 @@
 #include <rte_mbuf.h>
 #include <rte_pcapng.h>

+#include <pcap/pcap.h>
+
 #include "rte_graph_worker.h"

 #include "graph_pcap_private.h"
@@ -117,7 +119,7 @@ graph_pcap_file_open(const char *filename)

         /* Add the configured interfaces as possible capture ports */
         RTE_ETH_FOREACH_DEV(portid) {
-               ret = rte_pcapng_add_interface(pcapng_fd, portid,
+               ret = rte_pcapng_add_interface(pcapng_fd, portid, DLT_EN10MB,
                                                NULL, NULL, NULL);
                 if (ret < 0) {
                         graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
index 4549925d41..3aa7ba5155 100644
--- a/lib/pcapng/meson.build
+++ b/lib/pcapng/meson.build
@@ -5,3 +5,5 @@ sources = files('rte_pcapng.c')
 headers = files('rte_pcapng.h')

 deps += ['ethdev']
+
+use_function_versioning = true
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index cacbefdc50..f18af25983 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -200,11 +200,10 @@ pcapng_section_block(rte_pcapng_t *self,
 }

 /* Write an interface block for a DPDK port */
-RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
-int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
-                        const char *ifname, const char *ifdescr,
-                        const char *filter)
+RTE_DEFAULT_SYMBOL(26, int, rte_pcapng_add_interface,
+                       (rte_pcapng_t *self, uint16_t port, uint16_t link_type,
+                       const char *ifname, const char *ifdescr,
+                       const char *filter))
 {
         struct pcapng_interface_block *hdr;
         struct rte_eth_dev_info dev_info;
@@ -274,7 +273,7 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
         hdr = (struct pcapng_interface_block *)buf;
         *hdr = (struct pcapng_interface_block) {
                 .block_type = PCAPNG_INTERFACE_BLOCK,
-               .link_type = 1,         /* DLT_EN10MB - Ethernet */
+               .link_type = link_type,
                 .block_length = len,
         };

@@ -319,6 +318,16 @@ rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
         return write(self->outfd, buf, len);
 }

+RTE_VERSION_SYMBOL(25, int, rte_pcapng_add_interface,
+                                 (rte_pcapng_t *self, uint16_t port,
+                                  const char *ifname, const char *ifdescr,
+                                  const char *filter))
+{
+       /* Call the new version with a default link_type (Ethernet) */
+       return rte_pcapng_add_interface(self, port, DLT_EN10MB,
+                                                                       ifname, ifdescr, filter);
+}
+
 /*
  * Write an Interface statistics block at the end of capture.
  */
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index 48f2b57564..9880d415c4 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -71,6 +71,8 @@ rte_pcapng_close(rte_pcapng_t *self);
  *  The handle to the packet capture file
  * @param port
  *  The Ethernet port to report stats on.
+ * @param link_type
+ *   The link type (e.g., DLT_EN10MB).
  * @param ifname (optional)
  *  Interface name to record in the file.
  *  If not specified, name will be constructed from port
@@ -84,7 +86,7 @@ rte_pcapng_close(rte_pcapng_t *self);
  * must be added.
  */
 int
-rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
+rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
                          const char *ifname, const char *ifdescr,
                          const char *filter);

--
2.27.0


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

^ permalink raw reply	[relevance 2%]

Results 13801-13953 of 13953	 | reverse | sort options + mbox downloads above
-- links below jump to the message on this page --
2024-04-17 23:41     [PATCH 00/16] remove use of VLAs for Windows built code Tyler Retzlaff
2025-02-06  1:33     ` [PATCH v22 00/27] remove use of VLAs for Windows Andre Muezerie
2025-02-06 20:44       ` David Marchand
2025-02-07 14:23         ` Konstantin Ananyev
2025-02-18 14:22  0%       ` David Marchand
2025-02-19 14:28  0%         ` Konstantin Ananyev
2024-05-29 23:33     [PATCH v10 00/20] Remove use of noninclusive term sanity Stephen Hemminger
2025-04-02 23:23     ` [PATCH v12 00/10] replace use of term sanity check Stephen Hemminger
2025-04-02 23:23  3%   ` [PATCH v12 01/10] mbuf: replace " Stephen Hemminger
2024-09-06 13:57     [DPDK/ethdev Bug 1536] net/tap: crash in tap pmd when using more than RTE_MP_MAX_FD_NUM rx queues bugzilla
2025-05-21 16:44  3% ` bugzilla
2024-09-20 16:32     [RFC PATCH] mempool: obey configured cache size Morten Brørup
2025-02-21 15:13  4% ` [RFC PATCH v18] mempool: fix mempool " Morten Brørup
2025-02-21 19:05  3% ` [RFC PATCH v19] " Morten Brørup
2025-02-21 20:27  3% ` [RFC PATCH v20] " Morten Brørup
2024-11-14  1:10     [PATCH 0/3] introduce rte_memset_sensative Stephen Hemminger
2025-02-11 17:35     ` [PATCH v5 00/11] memset security fixes Stephen Hemminger
2025-02-11 17:35       ` [PATCH v5 02/11] eal: add new secure free function Stephen Hemminger
2025-02-12  2:01         ` fengchengwen
2025-02-12  6:46  3%       ` Stephen Hemminger
2024-11-26 13:14     [PATCH v1 0/4] Adjust wording for NUMA vs. socket ID in DPDK Anatoly Burakov
2025-06-09 21:44  0% ` Thomas Monjalon
2025-01-24 16:14     [PATCH 1/2] trace: support expression for blob length David Marchand
2025-03-04 16:06     ` [PATCH v4 0/5] Trace point framework enhancement for dmadev David Marchand
2025-03-04 16:06  4%   ` [PATCH v4 2/5] dmadev: avoid copies in tracepoints David Marchand
2025-01-28 16:36     [PATCH 0/4] remove common iavf and idpf drivers Bruce Richardson
2025-02-10 16:44     ` [PATCH v5 " Bruce Richardson
2025-02-10 16:44  2%   ` [PATCH v5 3/4] drivers: move iavf common folder to iavf net Bruce Richardson
2025-02-11 14:12  0%     ` Stokes, Ian
2025-02-06  2:53     [PATCH v7 1/1] graph: mcore: optimize graph search Huichao Cai
2025-02-07  1:39     ` [PATCH v8] " Huichao Cai
2025-02-22  6:59  0%   ` [EXTERNAL] " Kiran Kumar Kokkilagadda
2025-02-07 18:24     [PATCH v5] net: add thread-safe crc api Arkadiusz Kusztal
2025-02-10 21:27  4% ` [PATCH v6] " Arkadiusz Kusztal
2025-02-11  6:23  0%   ` Stephen Hemminger
2025-02-11  8:35  0%     ` Kusztal, ArkadiuszX
2025-02-11  9:02  4%   ` [PATCH v7] " Arkadiusz Kusztal
2025-02-18 11:58     [PATCH] ethdev: fix get_reg_info Thierry Herbelot
2025-02-19 18:45     ` Stephen Hemminger
2025-03-07  9:33  3%   ` fengchengwen
2025-02-21 12:47     [PATCH] sched: fix wrr parameter data type Megha Ajmera
2025-02-21 19:14  3% ` Stephen Hemminger
2025-02-21 17:41     [v3 0/6] crypto/virtio: enhancements for RSA and vDPA Gowrishankar Muthukrishnan
2025-02-21 17:41  1% ` [v3 4/6] crypto/virtio: add vDPA backend Gowrishankar Muthukrishnan
2025-02-22  9:16     [v4 0/6] crypto/virtio: enhancements for RSA and vDPA Gowrishankar Muthukrishnan
2025-02-22  9:16  1% ` [v4 4/6] crypto/virtio: add vDPA backend Gowrishankar Muthukrishnan
2025-02-26 18:58     [v5 0/6] crypto/virtio: enhancements for RSA and vDPA Gowrishankar Muthukrishnan
2025-02-26 18:58  1% ` [v5 4/6] crypto/virtio: add vDPA backend Gowrishankar Muthukrishnan
2025-03-05  6:16     [v6 0/6] crypto/virtio: enhancements for RSA and vDPA Gowrishankar Muthukrishnan
2025-03-05  6:16  1% ` [v6 4/6] crypto/virtio: add vDPA backend Gowrishankar Muthukrishnan
2025-03-05 21:23  6% [RFC] eal: add new function versioning macros David Marchand
2025-03-06 12:50  6% ` [RFC v2 1/2] " David Marchand
2025-03-06 12:50  7%   ` [RFC v2 2/2] build: generate symbol maps David Marchand
2025-03-11  9:55  3% ` [RFC v3 0/8] Symbol versioning and export rework David Marchand
2025-03-11  9:56 13%   ` [RFC v3 3/8] eal: rework function versioning macros David Marchand
2025-03-13 16:53  0%     ` Bruce Richardson
2025-03-13 17:09  0%       ` David Marchand
2025-03-11  9:56 18%   ` [RFC v3 5/8] build: generate symbol maps David Marchand
2025-03-13 17:26  0%     ` Bruce Richardson
2025-03-14 15:38  0%       ` David Marchand
2025-03-14 15:27  0%     ` Andre Muezerie
2025-03-14 15:51  4%       ` David Marchand
2025-03-11  9:56 16%   ` [RFC v3 7/8] build: use dynamically generated version maps David Marchand
2025-03-11 10:18  3%   ` [RFC v3 0/8] Symbol versioning and export rework Morten Brørup
2025-03-11 13:43  0%     ` David Marchand
2025-03-17 15:42  3% ` [RFC v4 " David Marchand
2025-03-17 15:42 16%   ` [RFC v4 3/8] eal: rework function versioning macros David Marchand
2025-03-17 15:43 18%   ` [RFC v4 5/8] build: generate symbol maps David Marchand
2025-03-17 15:43 16%   ` [RFC v4 7/8] build: use dynamically generated version maps David Marchand
2025-03-18  8:19  0%   ` [RFC v4 0/8] Symbol versioning and export rework David Marchand
2025-03-26 12:02  0%   ` David Marchand
2025-03-26 12:26  3%     ` Morten Brørup
2025-03-26 13:07  0%     ` Bruce Richardson
2025-03-26 13:36  0%     ` Bruce Richardson
2025-03-26 13:54  3%       ` David Marchand
2025-03-26 14:16  0%         ` Bruce Richardson
2025-03-27 13:36  3% ` [PATCH v5 " David Marchand
2025-03-27 13:36 17%   ` [PATCH v5 4/8] build: generate symbol maps David Marchand
2025-03-27 13:36 16%   ` [PATCH v5 6/8] build: use dynamically generated version maps David Marchand
2025-03-28 13:19  0%     ` Aaron Conole
2025-03-27 13:36 20%   ` [PATCH v5 8/8] eal: rework function versioning macros David Marchand
2025-03-27 18:22  0%   ` [PATCH v5 0/8] Symbol versioning and export rework David Marchand
2025-03-28 10:52  3% ` [PATCH v6 " David Marchand
2025-03-28 10:52 17%   ` [PATCH v6 4/8] build: generate symbol maps David Marchand
2025-04-01 20:33  0%     ` Thomas Monjalon
2025-04-02  7:01  0%       ` David Marchand
2025-03-28 10:52 16%   ` [PATCH v6 6/8] build: use dynamically generated version maps David Marchand
2025-03-28 13:20  0%     ` Aaron Conole
2025-03-28 10:52 20%   ` [PATCH v6 8/8] eal: rework function versioning macros David Marchand
2025-04-02 11:53  0%     ` Thomas Monjalon
2025-04-02 12:16  0%       ` David Marchand
2025-04-03 16:58  3% ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
2025-04-03 16:58 18%   ` [PATCH v7 4/8] build: generate symbol maps David Marchand
2025-04-03 16:58 18%   ` [PATCH v7 6/8] build: use dynamically generated version maps David Marchand
2025-04-03 16:58 23%   ` [PATCH v7 8/8] eal: rework function versioning macros David Marchand
2025-04-07 13:46  0%   ` [PATCH v7 0/8] Symbol versioning and export rework David Marchand
2025-03-06 13:37     [PATCH] rust: support DPDK API Gregory Etelson
2025-03-14 18:38     ` [PATCH v3] rust: support raw " Gregory Etelson
2025-03-18  8:51  2%   ` Dariusz Sosnowski
2025-04-08 14:58     ` [RFC PATCH] add rust binding support to DPDK Bruce Richardson
2025-04-10  5:28       ` Etelson, Gregory
2025-04-11 13:13  4%     ` Van Haaren, Harry
2025-04-11 15:39  0%       ` Etelson, Gregory
2025-04-11 16:22  4%         ` Van Haaren, Harry
2025-04-13  8:07  0%           ` Etelson, Gregory
2025-04-13 17:09  3%             ` Owen Hilyard
2025-03-10 21:42     [patch v2 0/6] Support VMBUS channels without monitoring enabled longli
2025-03-10 23:20     ` Stephen Hemminger
2025-03-12  0:33  4%   ` [EXTERNAL] " Long Li
2025-03-12 15:36  0%     ` Stephen Hemminger
2025-03-26 21:53  0%       ` Long Li
2025-03-13 11:38     [RFC PATCH 0/3] allow easier use of high lcore-ids Bruce Richardson
2025-05-02 15:11     ` [PATCH v3 0/4] rework and expand EAL lcore options Bruce Richardson
2025-05-02 15:11  2%   ` [PATCH v3 1/4] eal: deprecate old coremask-based EAL parameters Bruce Richardson
2025-05-13 16:17     ` [PATCH v4 0/3] lcore options cleanup Bruce Richardson
2025-05-13 16:17  2%   ` [PATCH v4 1/3] eal: deprecate old coremask-based EAL parameters Bruce Richardson
2025-05-27 15:29     ` [PATCH v5 0/3] lcore options cleanup Bruce Richardson
2025-05-27 15:29  2%   ` [PATCH v5 1/3] eal: deprecate old coremask-based EAL parameters Bruce Richardson
2025-03-13 22:54  4% Community CI Meeting Minutes - January 9, 2025 Patrick Robb
2025-03-14 12:57  1% [PATCH] raw/cnxk_gpio: switch to character based GPIO interface Tomasz Duszynski
2025-03-24  8:28  1% ` [PATCH v2] " Tomasz Duszynski
2025-03-25  4:14  1%   ` [PATCH v3] " Tomasz Duszynski
2025-03-27  9:12  1%     ` [PATCH v4] " Tomasz Duszynski
2025-03-24 23:26  3% DPDK 25.03 released Thomas Monjalon
2025-03-26  9:00 10% [PATCH] version: 25.07-rc0 David Marchand
2025-03-26  9:10  3% ` Bruce Richardson
2025-03-26 10:09  3%   ` David Marchand
2025-03-26 10:37  0%     ` Bruce Richardson
2025-03-27  7:39  0% ` David Marchand
2025-03-26 13:14     [RFC 0/2] introduce event vector adapter pbhagavatula
2025-03-26 13:14  1% ` [RFC 1/2] eventdev: " pbhagavatula
2025-04-10 18:00       ` [PATCH 0/3] " pbhagavatula
2025-04-10 18:00  1%     ` [PATCH 1/3] eventdev: " pbhagavatula
2025-05-28 16:58  0%       ` Jerin Jacob
2025-05-29 22:24         ` [PATCH v2 0/3] " pbhagavatula
2025-05-29 22:24  1%       ` [PATCH v2 1/3] eventdev: " pbhagavatula
2025-05-30 16:25           ` [PATCH v3 0/3] " pbhagavatula
2025-05-30 16:25  1%         ` [PATCH v3 1/3] eventdev: " pbhagavatula
2025-04-03  7:08     [RFC PATCH 1/4] ethdev: add support to provide link type Nithin Dabilpuram
2025-04-04  0:46  3% ` Stephen Hemminger
2025-04-15  7:08  3%   ` Nithin Dabilpuram
2025-04-16  0:03  0%     ` Stephen Hemminger
2025-04-16  9:08  0%       ` Nithin Dabilpuram
2025-04-22 12:29     [PATCH V2] ethdev_trace.h: Update the trace point function when _TIME_BITS=64 changqing.li
2025-04-23 18:00  4% ` Stephen Hemminger
2025-04-27  2:06  0%   ` Changqing Li
     [not found]     <20250427013611.150888-1-kyo.liu>
2025-04-27  2:27     ` [PATCH v3 1/1] bus/pci: introduce get_iova_mode for pci dev Kyo Liu
2025-04-28 15:50  3%   ` Stephen Hemminger
2025-04-29 18:11     [PATCH] ethdev: remove callback checks from fast path skori
2025-05-12 15:07     ` [PATCH v2 1/2] ethdev: remove unnecessary type conversion skori
2025-05-27 15:07  3%   ` Stephen Hemminger
2025-05-27 16:24  3%     ` Morten Brørup
2025-05-28  8:17  0%       ` Konstantin Ananyev
2025-05-28  8:23  0%         ` Konstantin Ananyev
2025-05-28  8:48  0%           ` Morten Brørup
2025-05-28  9:14  0%             ` Konstantin Ananyev
2025-05-28  9:39  0%               ` Morten Brørup
2025-04-30  1:34     [PATCH V3] Add new tracepoint function for type time_t changqing.li
     [not found]     ` <20250430024403.2690306-1-robot@bytheb.org>
2025-04-30  5:21  0%   ` |FAILURE| pw153190 " Changqing Li
2025-05-12 14:48 12% [PATCH] doc: reword contributor's guidelines Nandini Persad
2025-05-20 16:40     [RFC PATCH 0/7] rework EAL argument parsing in DPDK Bruce Richardson
2025-05-20 16:40  3% ` [RFC PATCH 3/7] argparse: make argparse EAL-args compatible Bruce Richardson
2025-05-22 10:44  0%   ` Bruce Richardson
2025-05-22 23:37  5% [PATCH] rcu: add deprecation notice about limit on defer queue element size Andre Muezerie
2025-05-27  9:21  4% [PATCH 0/3] argparse additions and rework Bruce Richardson
2025-05-27  9:21 10% ` [PATCH 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
2025-06-03  8:44  0%   ` fengchengwen
2025-06-03 15:33  0%     ` Bruce Richardson
2025-05-27  9:21  6% ` [PATCH 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
2025-06-04  1:22  0%   ` fengchengwen
2025-05-29 15:09  0% ` [PATCH 0/3] argparse additions and rework Bruce Richardson
2025-06-03 15:32  4% ` [PATCH v2 " Bruce Richardson
2025-06-03 15:32 10%   ` [PATCH v2 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
2025-06-03 15:32  6%   ` [PATCH v2 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
2025-06-04  1:37  0%   ` [PATCH v2 0/3] argparse additions and rework fengchengwen
2025-06-10 16:29  4% ` [PATCH v3 " Bruce Richardson
2025-06-10 16:29 10%   ` [PATCH v3 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
2025-06-10 16:29  6%   ` [PATCH v3 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
2025-06-11 12:36  4% ` [PATCH v4 0/3] argparse additions and rework Bruce Richardson
2025-06-11 12:36 10%   ` [PATCH v4 2/3] argparse: make argparse EAL-args compatible Bruce Richardson
2025-06-11 12:36  6%   ` [PATCH v4 3/3] argparse: use enums to remove max-value defines in lists Bruce Richardson
2025-05-28 23:30  7% [PATCH] doc: update ABI reference version in example Stephen Hemminger
2025-05-29 17:16     [PATCH] pcapng: allow any protocol link type for the interface block Schneide
2025-05-29 19:31  3% ` Stephen Hemminger
2025-06-05 23:02  3% ` [PATCH v2] " Schneide
2025-06-06 21:52  3% ` Schneide
2025-06-08 22:34  0%   ` Stephen Hemminger
2025-06-09 15:51  0%     ` Dylan Schneider
2025-06-16 14:29  2%   ` Dylan Schneider
2025-06-24 15:15  2%     ` Dylan Schneider
2025-06-09 21:19  3% ` Schneide
2025-05-30 21:09     [PATCH] app/compress-perf: support dictionary files Sameer Vaze
2025-06-04 16:41     ` Sameer Vaze
2025-06-17 21:34  4%   ` [EXTERNAL] " Akhil Goyal
2025-06-03  0:31 11% [PATCH] doc: remove the public interfaces from bus/vmbus longli
2025-06-05  9:06     [PATCH 1/7] ethdev: add support to provide link type skori
2025-06-05 15:22  3% ` Stephen Hemminger
2025-06-05 11:31     [PATCH] " skori
2025-06-06  9:28     ` [PATCH v2 1/1] " skori
2025-06-06  9:54       ` Morten Brørup
2025-06-06 15:23  3%     ` Stephen Hemminger
2025-06-10  5:02  0%       ` [EXTERNAL] " Sunil Kumar Kori
2025-06-10  6:45  0%         ` Morten Brørup
2025-06-11  6:49  3% [DPDK/meson Bug 1721] failed to compile DPDK bugzilla
2025-06-13  8:29  5% Minutes of Technical Board Meeting, 2025-May-28 Jerin Jacob
2025-06-13  9:48  4% Minutes of Technical Board Meeting, 2025-June-11 Kevin Traynor
2025-06-17 10:52     [PATCH 1/2] lib/graph: avoid memset(NULL, 0, 0) Marat Khalili
2025-06-17 10:52     ` [PATCH 2/2] lib/graph: rte_malloc for cache-aligned structs Marat Khalili
2025-06-17 12:46  4%   ` Jerin Jacob
2025-06-24 13:41  4% [PATCH] eal: deprecate old coremask-based EAL parameters Bruce Richardson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).