DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 04/11] bpf: add cBPF origin to rte_bpf_load_ex
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Add cBPF origin to rte_bpf_load_ex to allow loading PCAP filters and
other cBPF code through the unified interface.

Note that for the no-libpcap stub of rte_bpf_convert, the behavior when
called with a NULL program has changed from setting rte_errno to EINVAL
to setting it to ENOTSUP. Since both cases return NULL, callers relying
on pointer checking are unaffected.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf_convert.c | 81 +++++++++++++++++++++++++++++++++++++++++--
 lib/bpf/bpf_impl.h    | 11 ++++++
 lib/bpf/bpf_load.c    | 12 ++++++-
 lib/bpf/bpf_stub.c    | 27 ---------------
 lib/bpf/meson.build   | 11 +++---
 lib/bpf/rte_bpf.h     |  8 ++++-
 6 files changed, 113 insertions(+), 37 deletions(-)
 delete mode 100644 lib/bpf/bpf_stub.c

diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c
index 953ca80670c4..e8074b13d037 100644
--- a/lib/bpf/bpf_convert.c
+++ b/lib/bpf/bpf_convert.c
@@ -9,6 +9,12 @@
  * Copyright (c) 2011 - 2014 PLUMgrid, http://plumgrid.com
  */
 
+#include "bpf_impl.h"
+#include <eal_export.h>
+#include <rte_errno.h>
+
+#ifdef RTE_HAS_LIBPCAP
+
 #include <assert.h>
 #include <errno.h>
 #include <stdbool.h>
@@ -17,17 +23,14 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <eal_export.h>
 #include <rte_common.h>
 #include <rte_bpf.h>
 #include <rte_log.h>
 #include <rte_malloc.h>
-#include <rte_errno.h>
 
 #include <pcap/pcap.h>
 #include <pcap/bpf.h>
 
-#include "bpf_impl.h"
 #include "bpf_def.h"
 
 #ifndef BPF_MAXINSNS
@@ -572,3 +575,75 @@ rte_bpf_convert(const struct bpf_program *prog)
 
 	return prm;
 }
+
+void
+__rte_bpf_convert_cleanup(struct __rte_bpf_load *load)
+{
+	free(load->ins);
+}
+
+int
+__rte_bpf_convert(struct __rte_bpf_load *load)
+{
+	struct rte_bpf_prm_ex *const prm = &load->prm;
+	uint32_t nb_ins = 0;
+	int ret;
+
+	RTE_ASSERT(prm->origin == RTE_BPF_ORIGIN_CBPF);
+
+	if (prm->cbpf.ins == NULL || prm->cbpf.nb_ins == 0)
+		return -EINVAL;
+
+	/* 1st pass: calculate the eBPF program length */
+	ret = bpf_convert_filter(prm->cbpf.ins, prm->cbpf.nb_ins, NULL, &nb_ins);
+	if (ret < 0) {
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cannot get eBPF length");
+		return ret;
+	}
+
+	RTE_ASSERT(load->ins == NULL);
+	load->ins = malloc(nb_ins * sizeof(load->ins[0]));
+	if (load->ins == NULL)
+		return -ENOMEM;
+
+	/* 2nd pass: remap cBPF to eBPF instructions  */
+	ret = bpf_convert_filter(prm->cbpf.ins, prm->cbpf.nb_ins, load->ins, &nb_ins);
+	if (ret < 0) {
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cannot convert cBPF to eBPF");
+		return ret;
+	}
+
+	prm->origin = RTE_BPF_ORIGIN_RAW;
+	prm->raw.ins = load->ins;
+	prm->raw.nb_ins = nb_ins;
+
+	return 0;
+}
+
+#else /* RTE_HAS_LIBPCAP */
+
+RTE_EXPORT_SYMBOL(rte_bpf_convert)
+struct rte_bpf_prm *
+rte_bpf_convert(const struct bpf_program *prog)
+{
+	RTE_SET_USED(prog);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
+	rte_errno = ENOTSUP;
+	return NULL;
+}
+
+void
+__rte_bpf_convert_cleanup(struct __rte_bpf_load *load)
+{
+	RTE_ASSERT(load->ins == NULL);
+}
+
+int
+__rte_bpf_convert(struct __rte_bpf_load *load)
+{
+	RTE_SET_USED(load);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
+	return -ENOTSUP;
+}
+
+#endif /* RTE_HAS_LIBPCAP */
diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h
index 4a98b3373067..92d03583d977 100644
--- a/lib/bpf/bpf_impl.h
+++ b/lib/bpf/bpf_impl.h
@@ -21,6 +21,9 @@ struct rte_bpf {
 struct __rte_bpf_load {
 	struct rte_bpf_prm_ex prm;
 
+	/* Conversion from cBPF. */
+	struct ebpf_insn *ins;
+
 	/* Loading ELF and applying relocations. */
 	int elf_fd;  /* ELF fd, must be negative (not zero) by default. */
 	void *elf;  /* Using void to avoid dependency on libelf. */
@@ -34,6 +37,14 @@ struct __rte_bpf_load {
  * to avoid potential name conflict with other libraries.
  */
 
+/* Free temporary resources created by converting from cBPF to eBPF. */
+void
+__rte_bpf_convert_cleanup(struct __rte_bpf_load *load);
+
+/* Convert program from cBPF to eBPF. */
+int
+__rte_bpf_convert(struct __rte_bpf_load *load);
+
 /* Free temporary resources created by opening ELF. */
 void
 __rte_bpf_load_elf_cleanup(struct __rte_bpf_load *load);
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index f63093b9bc09..e3265e97ff7f 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -240,6 +240,9 @@ load_try(struct __rte_bpf_load *load, const struct rte_bpf_prm_ex *app_prm)
 	switch (load->prm.origin) {
 	case RTE_BPF_ORIGIN_RAW:
 		break;
+	case RTE_BPF_ORIGIN_CBPF:
+		rc = rc < 0 ? rc : __rte_bpf_convert(load);
+		break;
 	case RTE_BPF_ORIGIN_ELF_FILE:
 		rc = rc < 0 ? rc : __rte_bpf_load_elf_file(load);
 		rc = rc < 0 ? rc : __rte_bpf_load_elf_code(load);
@@ -254,6 +257,13 @@ load_try(struct __rte_bpf_load *load, const struct rte_bpf_prm_ex *app_prm)
 	return rc;
 }
 
+static void
+load_cleanup(struct __rte_bpf_load *load)
+{
+	__rte_bpf_convert_cleanup(load);
+	__rte_bpf_load_elf_cleanup(load);
+}
+
 RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_load_ex, 26.11)
 struct rte_bpf *
 rte_bpf_load_ex(const struct rte_bpf_prm_ex *prm)
@@ -262,7 +272,7 @@ rte_bpf_load_ex(const struct rte_bpf_prm_ex *prm)
 
 	const int rc = load_try(&load, prm);
 
-	__rte_bpf_load_elf_cleanup(&load);
+	load_cleanup(&load);
 
 	RTE_ASSERT((rc < 0) == (load.bpf == NULL));
 
diff --git a/lib/bpf/bpf_stub.c b/lib/bpf/bpf_stub.c
deleted file mode 100644
index 4c329832c264..000000000000
--- a/lib/bpf/bpf_stub.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2018-2021 Intel Corporation
- */
-
-#include "bpf_impl.h"
-#include <eal_export.h>
-#include <rte_errno.h>
-
-/**
- * Contains stubs for unimplemented public API functions
- */
-
-#ifndef RTE_HAS_LIBPCAP
-RTE_EXPORT_SYMBOL(rte_bpf_convert)
-struct rte_bpf_prm *
-rte_bpf_convert(const struct bpf_program *prog)
-{
-	if (prog == NULL) {
-		rte_errno = EINVAL;
-		return NULL;
-	}
-
-	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
-	rte_errno = ENOTSUP;
-	return NULL;
-}
-#endif
diff --git a/lib/bpf/meson.build b/lib/bpf/meson.build
index 4901b6ee1463..7e8a300e3f87 100644
--- a/lib/bpf/meson.build
+++ b/lib/bpf/meson.build
@@ -15,14 +15,16 @@ if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_32')
     subdir_done()
 endif
 
-sources = files('bpf.c',
+sources = files(
+        'bpf.c',
+        'bpf_convert.c',
         'bpf_dump.c',
         'bpf_exec.c',
         'bpf_load.c',
         'bpf_load_elf.c',
         'bpf_pkt.c',
-        'bpf_stub.c',
-        'bpf_validate.c')
+        'bpf_validate.c',
+)
 
 if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_64')
     sources += files('bpf_jit_x86.c')
@@ -45,8 +47,7 @@ else
 endif
 
 if dpdk_conf.has('RTE_HAS_LIBPCAP')
-    sources += files('bpf_convert.c')
     ext_deps += pcap_dep
 else
-    warning('libpcap is missing, rte_bpf_convert API will be disabled')
+    warning('libpcap is missing, cBPF API will be disabled')
 endif
diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h
index 0e7eaa3c18eb..da2bdea7e0a8 100644
--- a/lib/bpf/rte_bpf.h
+++ b/lib/bpf/rte_bpf.h
@@ -95,10 +95,12 @@ struct rte_bpf_xsym {
  */
 enum rte_bpf_origin {
 	RTE_BPF_ORIGIN_RAW,		/**< code loaded from raw array */
-	RTE_BPF_ORIGIN_RESERVED,	/**< reserved for cBPF */
+	RTE_BPF_ORIGIN_CBPF,		/**< code converted from cbpf */
 	RTE_BPF_ORIGIN_ELF_FILE,	/**< code loaded from elf_file */
 };
 
+struct bpf_insn;
+
 /**
  * Input parameters for loading eBPF code, extensible version.
  *
@@ -117,6 +119,10 @@ struct rte_bpf_prm_ex {
 			const struct ebpf_insn *ins;  /**< eBPF instructions */
 			uint32_t nb_ins;  /**< number of instructions in ins */
 		} raw;
+		struct {
+			const struct bpf_insn *ins;  /**< cBPF instructions */
+			uint32_t nb_ins;  /**< number of instructions in ins */
+		} cbpf;
 		struct {
 			const char *path;  /**< path to the ELF file */
 			const char *section;  /**< ELF section with the code */
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 03/11] bpf: support up to 5 arguments
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev, Wathsala Vithanage; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

When using rte_bpf_load_ex allow up to 5 arguments for a BPF program.
Particularly useful for call-backs and other internal functions.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf.c           |  32 ++++++++++-
 lib/bpf/bpf_exec.c      | 124 +++++++++++++++++++++++++++++++++++++++-
 lib/bpf/bpf_impl.h      |   2 +-
 lib/bpf/bpf_jit_arm64.c |   2 +-
 lib/bpf/bpf_jit_x86.c   |   2 +-
 lib/bpf/bpf_load.c      |   6 +-
 lib/bpf/bpf_validate.c  |  45 +++++++++++----
 lib/bpf/rte_bpf.h       | 121 +++++++++++++++++++++++++++++++++++++--
 8 files changed, 310 insertions(+), 24 deletions(-)

diff --git a/lib/bpf/bpf.c b/lib/bpf/bpf.c
index 5239b3e11e0e..67dededd9ae8 100644
--- a/lib/bpf/bpf.c
+++ b/lib/bpf/bpf.c
@@ -16,8 +16,8 @@ void
 rte_bpf_destroy(struct rte_bpf *bpf)
 {
 	if (bpf != NULL) {
-		if (bpf->jit.func != NULL)
-			munmap(bpf->jit.func, bpf->jit.sz);
+		if (bpf->jit.raw != NULL)
+			munmap(bpf->jit.raw, bpf->jit.sz);
 		munmap(bpf, bpf->sz);
 	}
 }
@@ -29,7 +29,33 @@ rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit)
 	if (bpf == NULL || jit == NULL)
 		return -EINVAL;
 
-	jit[0] = bpf->jit;
+	if (bpf->prm.nb_prog_arg != 1) {
+		RTE_BPF_LOG_LINE(ERR,
+			"this program takes %d arguments, use rte_bpf_get_jit_ex",
+			bpf->prm.nb_prog_arg);
+		return -EINVAL;
+	}
+
+	*jit = (struct rte_bpf_jit) {
+		.func = bpf->jit.raw,
+		.sz = bpf->jit.sz,
+	};
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_get_jit_ex, 26.11)
+int
+rte_bpf_get_jit_ex(const struct rte_bpf *bpf, struct rte_bpf_jit_ex *jit)
+{
+	if (bpf == NULL || jit == NULL)
+		return -EINVAL;
+
+	if (bpf->jit.raw == NULL) {
+		RTE_BPF_LOG_LINE(ERR, "no JIT-compiled version");
+		return -ENOENT;
+	}
+
+	*jit = bpf->jit;
 	return 0;
 }
 
diff --git a/lib/bpf/bpf_exec.c b/lib/bpf/bpf_exec.c
index e4668ba10b64..20f8d0fa29de 100644
--- a/lib/bpf/bpf_exec.c
+++ b/lib/bpf/bpf_exec.c
@@ -10,6 +10,7 @@
 #include <rte_log.h>
 #include <rte_debug.h>
 #include <rte_byteorder.h>
+#include <rte_errno.h>
 
 #include "bpf_impl.h"
 
@@ -502,6 +503,12 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[],
 	uint64_t reg[EBPF_REG_NUM];
 	uint64_t stack[MAX_BPF_STACK_SIZE / sizeof(uint64_t)];
 
+	if (bpf->prm.nb_prog_arg != 1) {
+		/* Use rte_bpf_exec_burst_ex with this program. */
+		rte_errno = EINVAL;
+		return 0;
+	}
+
 	for (i = 0; i != num; i++) {
 
 		reg[EBPF_REG_1] = (uintptr_t)ctx[i];
@@ -513,12 +520,127 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[],
 	return i;
 }
 
+static uint32_t
+exec_vm_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx,
+	uint64_t rc[], uint32_t num)
+{
+	uint32_t i;
+	uint64_t reg[EBPF_REG_NUM];
+	uint64_t stack[MAX_BPF_STACK_SIZE / sizeof(uint64_t)];
+
+	for (i = 0; i != num; i++) {
+
+		switch (bpf->prm.nb_prog_arg) {
+		case 5:
+			reg[EBPF_REG_5] = ctx[i].arg[4].u64;
+			/* FALLTHROUGH */
+		case 4:
+			reg[EBPF_REG_4] = ctx[i].arg[3].u64;
+			/* FALLTHROUGH */
+		case 3:
+			reg[EBPF_REG_3] = ctx[i].arg[2].u64;
+			/* FALLTHROUGH */
+		case 2:
+			reg[EBPF_REG_2] = ctx[i].arg[1].u64;
+			/* FALLTHROUGH */
+		case 1:
+			reg[EBPF_REG_1] = ctx[i].arg[0].u64;
+			/* FALLTHROUGH */
+		case 0:
+			break;
+		}
+
+		reg[EBPF_REG_10] = (uintptr_t)(stack + RTE_DIM(stack));
+
+		rc[i] = bpf_exec(bpf, reg);
+	}
+
+	return i;
+}
+
+static uint32_t
+exec_jit_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx,
+	uint64_t rc[], uint32_t num)
+{
+	uint32_t i = 0;
+	const struct rte_bpf_jit_ex jit = bpf->jit;
+
+	/*
+	 * Fast path: assumes application pre-validated RTE_BPF_EXEC_FLAG_JIT
+	 * and successful JIT generation. No explicit NULL checks here.
+	 */
+	switch (bpf->prm.nb_prog_arg) {
+	case 0:
+		for (i = 0; i != num; i++)
+			rc[i] = jit.func0();
+		break;
+	case 1:
+		for (i = 0; i != num; i++) {
+			const union rte_bpf_func_arg *const arg = ctx[i].arg;
+			rc[i] = jit.func1(arg[0]);
+		}
+		break;
+	case 2:
+		for (i = 0; i != num; i++) {
+			const union rte_bpf_func_arg *const arg = ctx[i].arg;
+			rc[i] = jit.func2(arg[0], arg[1]);
+		}
+		break;
+	case 3:
+		for (i = 0; i != num; i++) {
+			const union rte_bpf_func_arg *const arg = ctx[i].arg;
+			rc[i] = jit.func3(arg[0], arg[1], arg[2]);
+		}
+		break;
+	case 4:
+		for (i = 0; i != num; i++) {
+			const union rte_bpf_func_arg *const arg = ctx[i].arg;
+			rc[i] = jit.func4(arg[0], arg[1], arg[2], arg[3]);
+		}
+		break;
+	case 5:
+		for (i = 0; i != num; i++) {
+			const union rte_bpf_func_arg *const arg = ctx[i].arg;
+			rc[i] = jit.func5(arg[0], arg[1], arg[2], arg[3], arg[4]);
+		}
+		break;
+	}
+
+	return i;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_exec_burst_ex, 26.11)
+uint32_t
+rte_bpf_exec_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx,
+	uint64_t rc[], uint32_t num, uint64_t flags)
+{
+	if ((flags & ~RTE_BPF_EXEC_FLAG_MASK) != 0) {
+		rte_errno = EINVAL;
+		return 0;
+	}
+
+	return (flags & RTE_BPF_EXEC_FLAG_JIT) != 0 ?
+		exec_jit_burst_ex(bpf, ctx, rc, num) :
+		exec_vm_burst_ex(bpf, ctx, rc, num);
+}
+
 RTE_EXPORT_SYMBOL(rte_bpf_exec)
 uint64_t
 rte_bpf_exec(const struct rte_bpf *bpf, void *ctx)
 {
-	uint64_t rc;
+	uint64_t rc = UINT64_MAX;
 
 	rte_bpf_exec_burst(bpf, &ctx, &rc, 1);
 	return rc;
 }
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_exec_ex, 26.11)
+uint64_t
+rte_bpf_exec_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx,
+		uint64_t flags)
+{
+	uint64_t rc = UINT64_MAX;
+
+	rte_bpf_exec_burst_ex(bpf, ctx, &rc, 1, flags);
+	return rc;
+}
diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h
index 1cee109bc98a..4a98b3373067 100644
--- a/lib/bpf/bpf_impl.h
+++ b/lib/bpf/bpf_impl.h
@@ -12,7 +12,7 @@
 
 struct rte_bpf {
 	struct rte_bpf_prm_ex prm;
-	struct rte_bpf_jit jit;
+	struct rte_bpf_jit_ex jit;
 	size_t sz;
 	uint32_t stack_sz;
 };
diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index 9e5e142c13ba..ba7ae4d680c5 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -1471,7 +1471,7 @@ __rte_bpf_jit_arm64(struct rte_bpf *bpf)
 	/* Flush the icache */
 	__builtin___clear_cache((char *)ctx.ins, (char *)(ctx.ins + ctx.idx));
 
-	bpf->jit.func = (void *)ctx.ins;
+	bpf->jit.raw = ctx.ins;
 	bpf->jit.sz = size;
 
 	goto finish;
diff --git a/lib/bpf/bpf_jit_x86.c b/lib/bpf/bpf_jit_x86.c
index 6f4235d43499..54eb279643b9 100644
--- a/lib/bpf/bpf_jit_x86.c
+++ b/lib/bpf/bpf_jit_x86.c
@@ -1568,7 +1568,7 @@ __rte_bpf_jit_x86(struct rte_bpf *bpf)
 	if (rc != 0)
 		munmap(st.ins, st.sz);
 	else {
-		bpf->jit.func = (void *)st.ins;
+		bpf->jit.raw = st.ins;
 		bpf->jit.sz = st.sz;
 	}
 
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index 2d9e503d1f7e..f63093b9bc09 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -149,7 +149,8 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
 			.raw.nb_ins = prm->nb_ins,
 			.xsym = prm->xsym,
 			.nb_xsym = prm->nb_xsym,
-			.prog_arg = prm->prog_arg,
+			.prog_arg[0] = prm->prog_arg,
+			.nb_prog_arg = 1,
 		});
 }
 
@@ -170,7 +171,8 @@ rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
 			.elf_file.section = sname,
 			.xsym = prm->xsym,
 			.nb_xsym = prm->nb_xsym,
-			.prog_arg = prm->prog_arg,
+			.prog_arg[0] = prm->prog_arg,
+			.nb_prog_arg = 1,
 		});
 }
 
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index 5bfc59296d05..bf8a4abb5a5a 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -2425,10 +2425,14 @@ evaluate(struct bpf_verifier *bvf)
 		.s = {.min = MAX_BPF_STACK_SIZE, .max = MAX_BPF_STACK_SIZE},
 	};
 
-	bvf->evst->rv[EBPF_REG_1].v = bvf->prm->prog_arg;
-	bvf->evst->rv[EBPF_REG_1].mask = UINT64_MAX;
-	if (bvf->prm->prog_arg.type == RTE_BPF_ARG_RAW)
-		eval_max_bound(bvf->evst->rv + EBPF_REG_1, UINT64_MAX);
+	for (uint32_t pai = 0; pai != bvf->prm->nb_prog_arg; ++pai) {
+		struct bpf_reg_val *reg = &bvf->evst->rv[EBPF_REG_1 + pai];
+
+		reg->v = bvf->prm->prog_arg[pai];
+		reg->mask = UINT64_MAX;
+		if (reg->v.type == RTE_BPF_ARG_RAW)
+			eval_max_bound(reg, UINT64_MAX);
+	}
 
 	bvf->evst->rv[EBPF_REG_10] = rvfp;
 
@@ -2521,21 +2525,42 @@ evaluate(struct bpf_verifier *bvf)
 	return rc;
 }
 
+static bool
+prog_arg_is_valid(const struct rte_bpf_arg *prog_arg)
+{
+	/* check input argument type, don't allow mbuf ptr on 32-bit */
+	if (prog_arg->type != RTE_BPF_ARG_RAW &&
+			prog_arg->type != RTE_BPF_ARG_PTR &&
+			(sizeof(uint64_t) != sizeof(uintptr_t) ||
+			prog_arg->type != RTE_BPF_ARG_PTR_MBUF)) {
+		RTE_BPF_LOG_FUNC_LINE(ERR, "unsupported argument type");
+		return false;
+	}
+
+	return true;
+}
+
 int
 __rte_bpf_validate(const struct rte_bpf_prm_ex *prm, uint32_t *stack_sz)
 {
 	int32_t rc;
 	struct bpf_verifier bvf;
 
-	/* check input argument type, don't allow mbuf ptr on 32-bit */
-	if (prm->prog_arg.type != RTE_BPF_ARG_RAW &&
-			prm->prog_arg.type != RTE_BPF_ARG_PTR &&
-			(sizeof(uint64_t) != sizeof(uintptr_t) ||
-			prm->prog_arg.type != RTE_BPF_ARG_PTR_MBUF)) {
-		RTE_BPF_LOG_FUNC_LINE(ERR, "unsupported argument type");
+	if (prm->nb_prog_arg > EBPF_FUNC_MAX_ARGS) {
+		RTE_BPF_LOG_FUNC_LINE(ERR,
+			"support up to %u arguments, found %u",
+			EBPF_FUNC_MAX_ARGS, prm->nb_prog_arg);
 		return -ENOTSUP;
 	}
 
+	for (uint32_t pai = 0; pai != prm->nb_prog_arg; ++pai)
+		if (!prog_arg_is_valid(&prm->prog_arg[pai])) {
+			RTE_BPF_LOG_FUNC_LINE(ERR,
+				"unsupported argument %d (r%d) type",
+				pai, EBPF_REG_1 + pai);
+			return -ENOTSUP;
+		}
+
 	memset(&bvf, 0, sizeof(bvf));
 	bvf.prm = prm;
 	bvf.in = calloc(prm->raw.nb_ins, sizeof(bvf.in[0]));
diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h
index bf58a418191e..0e7eaa3c18eb 100644
--- a/lib/bpf/rte_bpf.h
+++ b/lib/bpf/rte_bpf.h
@@ -25,6 +25,11 @@
 extern "C" {
 #endif
 
+#define RTE_BPF_EXEC_FLAG_JIT	RTE_BIT64(0)	/**< use JIT-compiled version */
+
+/** Mask with all supported `RTE_BPF_EXEC_FLAG_*` flags set. */
+#define RTE_BPF_EXEC_FLAG_MASK  RTE_BPF_EXEC_FLAG_JIT
+
 /**
  * Possible types for function/BPF program arguments.
  */
@@ -122,7 +127,8 @@ struct rte_bpf_prm_ex {
 	/**< array of external symbols that eBPF code is allowed to reference */
 	uint32_t nb_xsym;  /**< number of elements in xsym */
 
-	struct rte_bpf_arg prog_arg;  /**< input arg description */
+	struct rte_bpf_arg prog_arg[EBPF_FUNC_MAX_ARGS];  /**< program arguments */
+	uint32_t nb_prog_arg;  /**< program argument count */
 };
 
 /**
@@ -138,13 +144,49 @@ struct rte_bpf_prm {
 };
 
 /**
- * Information about compiled into native ISA eBPF code.
+ * Information about compiled into native ISA eBPF code accepting 1 argument.
  */
 struct rte_bpf_jit {
 	uint64_t (*func)(void *); /**< JIT-ed native code */
 	size_t sz;                /**< size of JIT-ed code */
 };
 
+union rte_bpf_func_arg {
+	uint64_t u64;
+	void *ptr;
+};
+
+typedef uint64_t (*rte_bpf_jit_func0_t)(void);
+typedef uint64_t (*rte_bpf_jit_func1_t)(union rte_bpf_func_arg);
+typedef uint64_t (*rte_bpf_jit_func2_t)(union rte_bpf_func_arg, union rte_bpf_func_arg);
+typedef uint64_t (*rte_bpf_jit_func3_t)(union rte_bpf_func_arg, union rte_bpf_func_arg,
+	union rte_bpf_func_arg);
+typedef uint64_t (*rte_bpf_jit_func4_t)(union rte_bpf_func_arg, union rte_bpf_func_arg,
+	union rte_bpf_func_arg, union rte_bpf_func_arg);
+typedef uint64_t (*rte_bpf_jit_func5_t)(union rte_bpf_func_arg, union rte_bpf_func_arg,
+	union rte_bpf_func_arg, union rte_bpf_func_arg, union rte_bpf_func_arg);
+
+/**
+ * JIT-ed native code, member depends on number of program arguments.
+ */
+struct rte_bpf_jit_ex {
+	union {
+		void *raw;
+		rte_bpf_jit_func0_t func0;  /* nullary function */
+		rte_bpf_jit_func1_t func1;  /* unary function */
+		rte_bpf_jit_func2_t func2;  /* binary function */
+		rte_bpf_jit_func3_t func3;  /* ternary function */
+		rte_bpf_jit_func4_t func4;  /* quaternary function */
+		rte_bpf_jit_func5_t func5;  /* quinary function */
+	};
+	size_t sz;
+};
+
+/* Tuple of eBPF program arguments. */
+struct rte_bpf_prog_ctx {
+	union rte_bpf_func_arg arg[EBPF_FUNC_MAX_ARGS];
+};
+
 struct rte_bpf;
 
 /**
@@ -224,7 +266,7 @@ rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
 	__rte_malloc __rte_dealloc(rte_bpf_destroy, 1);
 
 /**
- * Execute given BPF bytecode.
+ * Execute given BPF bytecode accepting 1 argument.
  *
  * @param bpf
  *   handle for the BPF code to execute.
@@ -237,7 +279,29 @@ uint64_t
 rte_bpf_exec(const struct rte_bpf *bpf, void *ctx);
 
 /**
- * Execute given BPF bytecode over a set of input contexts.
+ * @warning
+ * @b EXPERIMENTAL: This API may change, or be removed, without prior notice.
+ *
+ * Execute given BPF bytecode accepting any number of arguments.
+ *
+ * @param bpf
+ *   handle for the BPF code to execute.
+ * @param ctx
+ *   program arguments tuple.
+ * @param flags
+ *   bitwise OR of `RTE_BPF_EXEC_FLAG_*` values controlling execution.
+ *   Flag RTE_BPF_EXEC_FLAG_JIT requires presence of JIT version (can be checked
+ *   with rte_bpf_get_jit_ex).
+ * @return
+ *   BPF execution return value.
+ */
+__rte_experimental
+uint64_t
+rte_bpf_exec_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx,
+		uint64_t flags);
+
+/**
+ * Execute given BPF bytecode accepting 1 argument over a set of input contexts.
  *
  * @param bpf
  *   handle for the BPF code to execute.
@@ -255,7 +319,35 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[],
 		uint32_t num);
 
 /**
- * Provide information about natively compiled code for given BPF handle.
+ * @warning
+ * @b EXPERIMENTAL: This API may change, or be removed, without prior notice.
+ *
+ * Execute given BPF program accepting any number of arguments over a set of
+ * input contexts.
+ *
+ * @param bpf
+ *   handle for the BPF code to execute.
+ * @param ctx
+ *   pointer to array of program argument tuples, can be NULL for nullary programs.
+ * @param rc
+ *   array of return values (one per input).
+ * @param num
+ *   number executions, number of elements in arrays ctx and rc[].
+ * @param flags
+ *   bitwise OR of `RTE_BPF_EXEC_FLAG_*` values controlling execution.
+ *   Flag RTE_BPF_EXEC_FLAG_JIT requires presence of JIT version (can be checked
+ *   with rte_bpf_get_jit_ex).
+ * @return
+ *   number of successfully processed inputs.
+ */
+__rte_experimental
+uint32_t
+rte_bpf_exec_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx,
+		uint64_t rc[], uint32_t num, uint64_t flags);
+
+/**
+ * Provide information about natively compiled code for given BPF program
+ * accepting 1 argument.
  *
  * @param bpf
  *   handle for the BPF code.
@@ -268,6 +360,25 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[],
 int
 rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: This API may change, or be removed, without prior notice.
+ *
+ * Get function JIT-compiled from the BPF program.
+ *
+ * @param bpf
+ *   handle for the BPF code.
+ * @param jit
+ *   pointer to the struct rte_bpf_jit_ex.
+ * @return
+ *   - -EINVAL if the parameters are invalid.
+ *   - -ENOENT if there is no JIT-compiled version.
+ *   - Zero if operation completed successfully.
+ */
+__rte_experimental
+int
+rte_bpf_get_jit_ex(const struct rte_bpf *bpf, struct rte_bpf_jit_ex *jit);
+
 /**
  * Dump epf instructions to a file.
  *
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 01/11] bpf: make logging prefixes more consistent
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev, Wathsala Vithanage; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Logging in lib/bpf is inconsistent: some places use `%s()`, other just
`%s` for `__func__`.

Introduce new macro for logging prefixed with function name and use it
everywhere function name without arguments is prefixed to the log line.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf_convert.c   | 18 +++++++++---------
 lib/bpf/bpf_impl.h      |  3 +++
 lib/bpf/bpf_jit_arm64.c |  4 ++--
 lib/bpf/bpf_load.c      |  2 +-
 lib/bpf/bpf_load_elf.c  |  2 +-
 lib/bpf/bpf_stub.c      |  6 ++----
 lib/bpf/bpf_validate.c  | 25 ++++++++++++-------------
 7 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c
index 86e703299d05..953ca80670c4 100644
--- a/lib/bpf/bpf_convert.c
+++ b/lib/bpf/bpf_convert.c
@@ -247,8 +247,8 @@ static int bpf_convert_filter(const struct bpf_insn *prog, size_t len,
 	uint8_t bpf_src;
 
 	if (len > BPF_MAXINSNS) {
-		RTE_BPF_LOG_LINE(ERR, "%s: cBPF program too long (%zu insns)",
-			    __func__, len);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cBPF program too long (%zu insns)",
+			    len);
 		return -EINVAL;
 	}
 
@@ -483,8 +483,8 @@ static int bpf_convert_filter(const struct bpf_insn *prog, size_t len,
 
 			/* Unknown instruction. */
 		default:
-			RTE_BPF_LOG_LINE(ERR, "%s: Unknown instruction!: %#x",
-				    __func__, fp->code);
+			RTE_BPF_LOG_FUNC_LINE(ERR, "Unknown instruction!: %#x",
+				    fp->code);
 			goto err;
 		}
 
@@ -528,7 +528,7 @@ rte_bpf_convert(const struct bpf_program *prog)
 	int ret;
 
 	if (prog == NULL) {
-		RTE_BPF_LOG_LINE(ERR, "%s: NULL program", __func__);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "NULL program");
 		rte_errno = EINVAL;
 		return NULL;
 	}
@@ -536,13 +536,13 @@ rte_bpf_convert(const struct bpf_program *prog)
 	/* 1st pass: calculate the eBPF program length */
 	ret = bpf_convert_filter(prog->bf_insns, prog->bf_len, NULL, &ebpf_len);
 	if (ret < 0) {
-		RTE_BPF_LOG_LINE(ERR, "%s: cannot get eBPF length", __func__);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cannot get eBPF length");
 		rte_errno = -ret;
 		return NULL;
 	}
 
-	RTE_BPF_LOG_LINE(DEBUG, "%s: prog len cBPF=%u -> eBPF=%u",
-		    __func__, prog->bf_len, ebpf_len);
+	RTE_BPF_LOG_FUNC_LINE(DEBUG, "prog len cBPF=%u -> eBPF=%u",
+		    prog->bf_len, ebpf_len);
 
 	prm = rte_zmalloc("bpf_filter",
 			  sizeof(*prm) + ebpf_len * sizeof(*ebpf), 0);
@@ -557,7 +557,7 @@ rte_bpf_convert(const struct bpf_program *prog)
 	/* 2nd pass: remap cBPF to eBPF instructions  */
 	ret = bpf_convert_filter(prog->bf_insns, prog->bf_len, ebpf, &ebpf_len);
 	if (ret < 0) {
-		RTE_BPF_LOG_LINE(ERR, "%s: cannot convert cBPF to eBPF", __func__);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cannot convert cBPF to eBPF");
 		rte_free(prm);
 		rte_errno = -ret;
 		return NULL;
diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h
index f5fa22098489..fb5ec3c4d65f 100644
--- a/lib/bpf/bpf_impl.h
+++ b/lib/bpf/bpf_impl.h
@@ -32,6 +32,9 @@ extern int rte_bpf_logtype;
 #define RTE_BPF_LOG_LINE(lvl, ...) \
 	RTE_LOG_LINE(lvl, BPF, __VA_ARGS__)
 
+#define RTE_BPF_LOG_FUNC_LINE(lvl, fmt, ...) \
+	RTE_LOG_LINE(lvl, BPF, "%s(): " fmt, __func__, ##__VA_ARGS__)
+
 static inline size_t
 bpf_size(uint32_t bpf_op_sz)
 {
diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index a04ef33a9c88..4bbb97da1b89 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -98,8 +98,8 @@ check_invalid_args(struct a64_jit_ctx *ctx, uint32_t limit)
 
 	for (idx = 0; idx < limit; idx++) {
 		if (rte_le_to_cpu_32(ctx->ins[idx]) == A64_INVALID_OP_CODE) {
-			RTE_BPF_LOG_LINE(ERR,
-				"%s: invalid opcode at %u;", __func__, idx);
+			RTE_BPF_LOG_FUNC_LINE(ERR,
+				"invalid opcode at %u;", idx);
 			return -EINVAL;
 		}
 	}
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index 6983c026af0e..b8a0426fe2ed 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -100,7 +100,7 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
 
 	if (rc != 0) {
 		rte_errno = -rc;
-		RTE_BPF_LOG_LINE(ERR, "%s: %d-th xsym is invalid", __func__, i);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "%d-th xsym is invalid", i);
 		return NULL;
 	}
 
diff --git a/lib/bpf/bpf_load_elf.c b/lib/bpf/bpf_load_elf.c
index 1d30ba17e25d..2390823cbf30 100644
--- a/lib/bpf/bpf_load_elf.c
+++ b/lib/bpf/bpf_load_elf.c
@@ -122,7 +122,7 @@ check_elf_header(const Elf64_Ehdr *eh)
 		err = "unexpected machine type";
 
 	if (err != NULL) {
-		RTE_BPF_LOG_LINE(ERR, "%s(): %s", __func__, err);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "%s", err);
 		return -EINVAL;
 	}
 
diff --git a/lib/bpf/bpf_stub.c b/lib/bpf/bpf_stub.c
index dea0d703ca27..e06e820d8327 100644
--- a/lib/bpf/bpf_stub.c
+++ b/lib/bpf/bpf_stub.c
@@ -21,8 +21,7 @@ rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
 		return NULL;
 	}
 
-	RTE_BPF_LOG_LINE(ERR, "%s() is not supported, rebuild with libelf installed",
-		__func__);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libelf installed");
 	rte_errno = ENOTSUP;
 	return NULL;
 }
@@ -38,8 +37,7 @@ rte_bpf_convert(const struct bpf_program *prog)
 		return NULL;
 	}
 
-	RTE_BPF_LOG_LINE(ERR, "%s() is not supported, rebuild with libpcap installed",
-		__func__);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
 	rte_errno = ENOTSUP;
 	return NULL;
 }
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index e8dbec282779..a7f4f576c9d6 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -1838,16 +1838,16 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
 	uint32_t ne;
 
 	if (nidx >= bvf->prm->nb_ins) {
-		RTE_BPF_LOG_LINE(ERR,
-			"%s: program boundary violation at pc: %u, next pc: %u",
-			__func__, get_node_idx(bvf, node), nidx);
+		RTE_BPF_LOG_FUNC_LINE(ERR,
+			"program boundary violation at pc: %u, next pc: %u",
+			get_node_idx(bvf, node), nidx);
 		return -EINVAL;
 	}
 
 	ne = node->nb_edge;
 	if (ne >= RTE_DIM(node->edge_dest)) {
-		RTE_BPF_LOG_LINE(ERR, "%s: internal error at pc: %u",
-			__func__, get_node_idx(bvf, node));
+		RTE_BPF_LOG_FUNC_LINE(ERR, "internal error at pc: %u",
+			get_node_idx(bvf, node));
 		return -EINVAL;
 	}
 
@@ -2005,8 +2005,7 @@ validate(struct bpf_verifier *bvf)
 
 		err = check_syntax(ins);
 		if (err != 0) {
-			RTE_BPF_LOG_LINE(ERR, "%s: %s at pc: %u",
-				__func__, err, i);
+			RTE_BPF_LOG_FUNC_LINE(ERR, "%s at pc: %u", err, i);
 			rc |= -EINVAL;
 		}
 
@@ -2230,9 +2229,9 @@ save_cur_eval_state(struct bpf_verifier *bvf, struct inst_node *node)
 	/* get new eval_state for this node */
 	st = pull_eval_state(&bvf->evst_sr_pool);
 	if (st == NULL) {
-		RTE_BPF_LOG_LINE(ERR,
-			"%s: internal error (out of space) at pc: %u",
-			__func__, get_node_idx(bvf, node));
+		RTE_BPF_LOG_FUNC_LINE(ERR,
+			"internal error (out of space) at pc: %u",
+			get_node_idx(bvf, node));
 		return -ENOMEM;
 	}
 
@@ -2462,8 +2461,8 @@ evaluate(struct bpf_verifier *bvf)
 				err = ins_chk[op].eval(bvf, ins + idx);
 				stats.nb_eval++;
 				if (err != NULL) {
-					RTE_BPF_LOG_LINE(ERR, "%s: %s at pc: %u",
-						__func__, err, idx);
+					RTE_BPF_LOG_FUNC_LINE(ERR,
+						"%s at pc: %u", err, idx);
 					rc = -EINVAL;
 				}
 			}
@@ -2533,7 +2532,7 @@ __rte_bpf_validate(struct rte_bpf *bpf)
 			bpf->prm.prog_arg.type != RTE_BPF_ARG_PTR &&
 			(sizeof(uint64_t) != sizeof(uintptr_t) ||
 			bpf->prm.prog_arg.type != RTE_BPF_ARG_PTR_MBUF)) {
-		RTE_BPF_LOG_LINE(ERR, "%s: unsupported argument type", __func__);
+		RTE_BPF_LOG_FUNC_LINE(ERR, "unsupported argument type");
 		return -ENOTSUP;
 	}
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 00/11] bpf: introduce extensible load API
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  Cc: dev
In-Reply-To: <20260518084912.57006-1-marat.khalili@huawei.com>

This patchset introduces an extensible load API for the BPF library in
DPDK, addressing current limitations regarding ABI stability and feature
constraints.

Currently, `rte_bpf_load` relies on a fixed `struct rte_bpf_prm`, which
makes it difficult to add new loading options or parameters without
breaking the ABI.

To resolve these issues, this series introduces `rte_bpf_load_ex` taking
`struct rte_bpf_prm_ex`. The new parameter structure includes a `sz`
field for backward compatibility, allowing future extensions.

Taking advantage of the new extensible API, this patchset also adds
several new features:
* Support for loading and executing BPF programs with up to 5 arguments.
* Support for loading classic BPF (cBPF) directly.
* Support for loading ELF files directly from memory buffers.
* New API functions (`rte_bpf_eth_rx_install` and `rte_bpf_eth_tx_install`)
  to install an already loaded BPF program as a port callback, decoupling
  the loading phase from the installation phase.


v4:
* Restored missing NULL checks in wrapper functions `rte_bpf_load` and
  `rte_bpf_elf_load`.
* Fixed the burst execution functions (`rte_bpf_exec_burst*`) to return
  `0` and set `rte_errno = EINVAL` on failure, preventing `-EINVAL`
  being reinterpreted as a large `uint32_t` value. Initialized `rc`
  properly in scalar execution wrappers for this case.
* Swapped the Doxygen comments in `rte_bpf_ethdev.h` for RX and TX functions.
* Added diagnostic dump on failure path in `test_bpf_filter`.
* Fixed memory leak of the BPF handle in `bpf_rx_test` upon install failure.
* Added tests for NULL parameter rejection, mismatched execution arguments,
  unsupported execution flags, and the libpcap-less `rte_bpf_convert` stub.

v3:
* Appended Acked-by tags to all individual commits to align with
  patchwork requirements.

v2:
* Fixed a potential segmentation fault in `exec_vm_burst_ex` by deferring
  the dereference of `ctx[i].arg` until it is confirmed that `nb_prog_arg > 0`.
* Clarified documentation and code comments for `RTE_BPF_EXEC_FLAG_JIT`
  requirements and fast-path expectations.


---
To the AI reviewer:
Thank you for the review. Several of your points were very helpful and
have been addressed in v2 (e.g. the potential null pointer dereference
in `exec_vm_burst_ex`, and documenting the `RTE_BPF_EXEC_FLAG_JIT`
expectations). The remaining points were investigated but do not require
code changes for the following reasons:

> Can the macro [RTE_BPF_LOG_FUNC_LINE] be enhanced for type safety?
> Does string literal concatenation support logging format modifiers
> consistently across all call sites?
`RTE_LOG_LINE` uses compiler attributes (`__attribute__((format(printf,
...)))`) to strictly enforce format type safety. Furthermore, standard C
preprocessors inherently support string literal concatenation prior to
format checking, ensuring it works safely across all macro invocations.

> Does `bpf_load()` always set `rte_errno` to `ENOMEM` when returning
> `NULL`, or could it mask other errors by hardcoding `-ENOMEM`?
By design, the internal `bpf_load()` function only returns `NULL` when
its internal memory allocation (`mmap`) fails. Returning `-ENOMEM`
perfectly accurately reflects the nature of this failure.

> Can the loop go out of bounds? When `opts_sz` is larger than
> `type_sz`, does this code read beyond the allocated memory region
> pointed to by `opts`?
The bounds are established by the application-supplied `opts_sz`.
Standard C semantics dictate that if a caller declares a size larger
than the actual allocated memory, it generates undefined behavior on
their end. `opts_valid` iterates exactly within the declared limits.

> Does the `memcpy()` handle the case where `app_prm->sz` is larger than
> `sizeof(load->prm)` correctly? Can partial copies lead to inconsistent
> state if the struct has pointer members?
The call to `opts_valid()` happens prior to `memcpy()` and guarantees
that any extra trailing space is definitively filled with zeroes. This
implies the application relies entirely on default functionality for the
newer unknown fields, making a truncated partial copy completely sound.

> Can `elf_end()` be called with `NULL` safely? Does it cause undefined
> behavior if `load->elf` is `NULL`?
`elf_end()` in `libelf` is explicitly defined to handle `NULL` pointers
as a safe no-op.

> Can this logic handle uninitialized or out-of-range `prog_arg->type`
> values? Does `RTE_BPF_ARG_PTR_MBUF` have a specific numeric value that
> should be validated before use?
The function utilizes strict positive equality checks against valid enum
values (`RTE_BPF_ARG_RAW`, `RTE_BPF_ARG_PTR`, `RTE_BPF_ARG_PTR_MBUF`).
Invalid or uninitialized types simply fall through and cleanly return
`false`.

> Does the `malloc` check for integer overflow in the size calculation
> `nb_ins * sizeof(load->ins[0])`?
`bpf_convert_filter()` acts as a strict gatekeeper and enforces an upper
bound limit (`BPF_MAXINSNS` = 4096) on instruction count. This extremely
small ceiling makes an arithmetic overflow in `malloc` mathematically
impossible.

> Does `bpf_eth_elf_install()` acquire any additional locks? Can this
> lead to a deadlock?
No secondary lock acquisitions exist inside `bpf_eth_elf_install()`. It
executes standard initialization logic and callback registrations
without touching threading constructs, ensuring a deadlock-free flow.

> Are we standardizing on this specific format ["%s(): "]?
Yes, we are standardizing on this specific formatting for BPF logs.

> Does this properly handle the error string in all cases?
Yes, `err` is a statically assigned string literal in this context, so
passing it to `%s` is perfectly safe.

> Does this code dereference app_prm->sz before checking if app_prm is
> NULL?
The code relies on standard C short-circuit evaluation
(`app_prm == NULL || !opts_valid(...)`), which guarantees safety.

> When app_prm->sz is smaller than sizeof(load->prm), does this code
> leave portions of load->prm uninitialized?
No. `load` is zero-initialized upon declaration (`struct __rte_bpf_load
load = { .elf_fd = -1 };`), meaning any tail space is already securely
zeroed.

> If the user-supplied paths are invalid, does this check prevent
> filesystem attacks like path traversal?
In DPDK, path validation and access control are strictly the caller's
responsibility.

> Does this code risk out-of-bounds access if the union or array layout
> changes?
Structural changes to ABI types like `ctx` are major ABI breaks, which
DPDK explicitly manages across releases.

> Does bpf_convert_filter modify nb_ins even on failure? Is checking ret
> sufficient?
The code explicitly checks if `ret < 0` and immediately bails out,
safely ignoring the state of `nb_ins` on error.

> Does this code leak load->ins if called twice without cleanup between
> invocations?
No. `load` is stack-allocated internally for a single `rte_bpf_load_ex`
invocation and never reused.

> When bpf_eth_elf_install returns an error, does this code still
> transfer ownership of the bpf pointer?
On error, ownership is not transferred. The caller retains
responsibility to call `rte_bpf_destroy()`.

> Does elf_memory() actually guarantee not to modify its input buffer?
Yes, `elf_memory` treats the buffer as read-only memory unless explicit
`ELF_C_WRITE` operations are executed, which DPDK does not do here.

> Is the graceful rejection of NULL parameters by the load APIs covered
> by tests?
We have now added `test_bpf_load_null` to verify that `rte_bpf_load`,
`rte_bpf_elf_load`, and `rte_bpf_load_ex` return `NULL` and set
`rte_errno = EINVAL` when called with a `NULL` parameter.

> Does the test suite verify that mismatched arguments or invalid flags
> are correctly rejected during execution?
Yes, we have introduced tests in v4 to explicitly verify that
`rte_bpf_exec_burst` and `rte_bpf_exec_burst_ex` return `0` and set
`rte_errno = EINVAL` under these invalid conditions.

> Is the libpcap-less stub for rte_bpf_convert tested to ensure it
> doesn't crash?
A test has been added to ensure the stub safely returns `NULL` and
doesn't crash, regardless of the exact error code it might return.


Marat Khalili (11):
  bpf: make logging prefixes more consistent
  bpf: introduce extensible load API
  bpf: support up to 5 arguments
  bpf: add cBPF origin to rte_bpf_load_ex
  bpf: support rte_bpf_prm_ex with port callbacks
  bpf: support loading ELF files from memory
  test/bpf: test loading cBPF directly
  test/bpf: test loading ELF file from memory
  doc: add release notes for new extensible BPF API
  doc: add load API to BPF programmer's guide
  test/bpf: add tests for error handling contracts

 app/test/test_bpf.c                    | 455 +++++++++++++++++--------
 doc/guides/prog_guide/bpf_lib.rst      |  75 +++-
 doc/guides/rel_notes/release_26_07.rst |  20 ++
 lib/bpf/bpf.c                          |  32 +-
 lib/bpf/bpf_convert.c                  |  99 +++++-
 lib/bpf/bpf_exec.c                     | 134 +++++++-
 lib/bpf/bpf_impl.h                     |  53 ++-
 lib/bpf/bpf_jit_arm64.c                |  18 +-
 lib/bpf/bpf_jit_x86.c                  |  10 +-
 lib/bpf/bpf_load.c                     | 213 ++++++++++--
 lib/bpf/bpf_load_elf.c                 | 189 ++++++----
 lib/bpf/bpf_pkt.c                      |  65 +++-
 lib/bpf/bpf_stub.c                     |  46 ---
 lib/bpf/bpf_validate.c                 |  94 +++--
 lib/bpf/meson.build                    |  15 +-
 lib/bpf/rte_bpf.h                      | 199 ++++++++++-
 lib/bpf/rte_bpf_ethdev.h               |  54 +++
 17 files changed, 1400 insertions(+), 371 deletions(-)
 delete mode 100644 lib/bpf/bpf_stub.c

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH dpdk v5 2/5] net: support multiple stacked VLAN tags
From: David Marchand @ 2026-05-20 12:42 UTC (permalink / raw)
  To: Robin Jarry; +Cc: dev, Kevin Traynor
In-Reply-To: <DINGHEW3M9WT.3F4XUUTLNKEL3@redhat.com>

On Wed, 20 May 2026 at 13:09, Robin Jarry <rjarry@redhat.com> wrote:
> >> -       } else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
> >> -               const struct rte_vlan_hdr *vh;
> >> -               struct rte_vlan_hdr vh_copy;
> >> +               if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN))
> >> +                       pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
> >> +               else
> >> +                       pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> >> +
> >> +               do {
> >> +                       vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
> >> +                       if (unlikely(vh == NULL))
> >> +                               return pkt_type;
> >
> > Kevin noted that it is weird to report back some packet type when the
> > packet is malformed.
> > Maybe return RTE_PTYPE_UNKNOWN here so that the application is forced
> > to validate the packet? (it should already be doing it, in any
> > case..).
>
> If we do this, we need to fix it in the entire function. There are
> several other places where the "current" value of pkt_type is returned
> on error.

There is this point and the code has been behaving like this for
years, so some applications may have been relying on this behavior.
I don't mind leaving as is.


-- 
David Marchand


^ permalink raw reply

* Re: [PATCH 0/6] net/idpf: fix split queue AVX2 datapath
From: Bruce Richardson @ 2026-05-20 12:36 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-1-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:29PM +0530, Shaiq Wani wrote:
> Fix the split queue AVX2 vectorized path to correctly handle the
> virtchnl2_rx_flex_desc_adv_nic_3 completion descriptor format and
> the flex scheduled Tx descriptor encoding.
> 
> The split queue completion descriptor layout is significantly
> different from the single queue path — the generation bit, DD bit,
> and packet length occupy different positions than the base Rx
> descriptor, and the buffer queue owns the mbuf pool rather than the
> completion queue.
> 
> On Rx, four issues are fixed:
>   - Generation bit extraction order (mask-before-shift)
>   - DD bit byte offset (byte 8 not byte 1)
>   - mbuf initializer sourced from bufq2 instead of the completion queue
>   - Packet type inserted into the correct AVX2 lane
> 
> On Tx, two issues are fixed:
>   - Buffer size shift corrected from 34 to 48 for flex descriptors
>   - Burst clamped to tx_rs_thresh with proper tx_next_rs tracking
> 
> Shaiq Wani (6):
>   net/idpf: fix gen bit extraction in split queue AVX2 Rx
>   net/idpf: fix DD bit byte offset in split queue AVX2 Rx
>   net/idpf: fix mbuf initializer source in split queue AVX2 Rx
>   net/idpf: fix ptype insert position in split queue AVX2 Rx
>   net/idpf: fix split queue AVX2 Tx buffer size shift
>   net/idpf: fix split queue AVX2 Tx burst and completion
> 
Series applied to dpdk-next-net-intel
Thanks,
/Bruce

^ permalink raw reply

* Re: [PATCH 6/6] net/idpf: fix split queue AVX2 Tx burst and completion
From: Bruce Richardson @ 2026-05-20 12:25 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-7-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:35PM +0530, Shaiq Wani wrote:
> Clamp burst size to tx_rs_thresh to prevent crossing completion
> boundaries.  Update tx_next_rs on ring wrap and after each burst
> so that completion tracking in idpf_splitq_scan_cq_ring works
> correctly.  Fix sw_ring pointer to use base-plus-offset.
> 
> Fixes: 57560a92167a ("net/idpf: add AVX2 Tx path for split queue config")
> Signed-off-by: Shaiq Wani <shaiq.wani@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  .../net/intel/idpf/idpf_common_rxtx_avx2.c    | 24 ++++++++++++++-----
>  1 file changed, 18 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> index 7c547b5f09..b6c4fdf20e 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> @@ -884,17 +884,21 @@ idpf_splitq_xmit_fixed_burst_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts,
>  	struct ci_tx_queue *txq = (struct ci_tx_queue *)tx_queue;
>  	struct idpf_flex_tx_sched_desc *txdp;
>  	struct ci_tx_entry_vec *txep;
> -	uint16_t n, nb_commit;
> +	uint16_t n, nb_commit, tx_id;
>  	uint64_t cmd_dtype = IDPF_TXD_FLEX_FLOW_CMD_EOP;
> -	uint16_t tx_id = txq->tx_tail;
>  
> -	nb_commit = (uint16_t)RTE_MIN(txq->nb_tx_free, nb_pkts);
> -	nb_pkts = nb_commit;
> +	/* cross rs_thresh boundary is not allowed */
> +	nb_pkts = RTE_MIN(nb_pkts, txq->tx_rs_thresh);
> +
> +	nb_pkts = (uint16_t)RTE_MIN(txq->nb_tx_free, nb_pkts);
> +	nb_commit = nb_pkts;
>  	if (unlikely(nb_pkts == 0))
>  		return 0;
>  
> -	txdp = (struct idpf_flex_tx_sched_desc *)&txq->desc_ring[tx_id];
> -	txep = &txq->sw_ring_vec[tx_id];
> +	tx_id = txq->tx_tail;
> +	txdp = &txq->desc_ring[tx_id];
> +	txep = (void *)txq->sw_ring;
> +	txep += tx_id;
>  
>  	txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_pkts);
>  
> @@ -909,10 +913,14 @@ idpf_splitq_xmit_fixed_burst_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts,
>  		idpf_splitq_vtx1_avx2(txdp, *tx_pkts++, cmd_dtype);
>  
>  		nb_commit = (uint16_t)(nb_commit - n);
> +
>  		tx_id = 0;
> +		txq->tx_next_rs = (uint16_t)(txq->tx_rs_thresh - 1);
>  
> +		/* avoid reach the end of ring */
>  		txdp = &txq->desc_ring[tx_id];
>  		txep = (void *)txq->sw_ring;
> +		txep += tx_id;
>  	}
>  
>  	ci_tx_backlog_entry_vec(txep, tx_pkts, nb_commit);
> @@ -920,6 +928,10 @@ idpf_splitq_xmit_fixed_burst_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts,
>  	idpf_splitq_vtx_avx2(txdp, tx_pkts, nb_commit, cmd_dtype);
>  
>  	tx_id = (uint16_t)(tx_id + nb_commit);
> +	if (tx_id > txq->tx_next_rs)
> +		txq->tx_next_rs =
> +			(uint16_t)(txq->tx_next_rs + txq->tx_rs_thresh);
> +
>  	txq->tx_tail = tx_id;
>  
>  	IDPF_PCI_REG_WRITE(txq->qtx_tail, txq->tx_tail);
> -- 
> 2.34.1
> 

^ permalink raw reply

* Re: [PATCH 5/6] net/idpf: fix split queue AVX2 Tx buffer size shift
From: Bruce Richardson @ 2026-05-20 12:23 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-6-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:34PM +0530, Shaiq Wani wrote:
> The flex scheduled Tx descriptor (DTYPE 0x0C) used in split queue mode
> places the buffer size at bits 48-63 of QW1, requiring a left-shift
> of 48.  The code incorrectly used IDPF_TXD_QW1_TX_BUF_SZ_S (34),
> which is the shift for base Tx descriptors (DTYPE 0x0) used in single
> queue mode.
> 
> This caused the data_len to be placed in the wrong bit position,
> resulting in hardware reading an incorrect buffer size of zero.
> 
> Define IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S (48) for the flex descriptor
> layout and use it in both vtx1 and vtx_avx2, consistent with the
> AVX512 split queue Tx path.
> 
> Fixes: 57560a92167a ("net/idpf: add AVX2 Tx path for split queue config")
> Signed-off-by: Shaiq Wani <shaiq.wani@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/intel/idpf/idpf_common_rxtx_avx2.c | 12 +++++++-----
>  1 file changed, 7 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> index e66dcc7a14..7c547b5f09 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> @@ -819,6 +819,8 @@ idpf_splitq_scan_cq_ring(struct ci_tx_queue *cq)
>  	cq->tx_tail = cq_qid;
>  }
>  
> +#define IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S  48
> +
>  static __rte_always_inline void
>  idpf_splitq_vtx1_avx2(struct idpf_flex_tx_sched_desc *txdp,
>  				struct rte_mbuf *pkt, uint64_t flags)
> @@ -826,7 +828,7 @@ idpf_splitq_vtx1_avx2(struct idpf_flex_tx_sched_desc *txdp,
>  	uint64_t high_qw =
>  		IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE |
>  		((uint64_t)flags) |
> -		((uint64_t)pkt->data_len << IDPF_TXD_QW1_TX_BUF_SZ_S);
> +		((uint64_t)pkt->data_len << IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S);
>  
>  	__m128i descriptor = _mm_set_epi64x(high_qw,
>  						pkt->buf_iova + pkt->data_off);
> @@ -848,13 +850,13 @@ idpf_splitq_vtx_avx2(struct idpf_flex_tx_sched_desc *txdp,
>  	for (; nb_pkts >= IDPF_VPMD_DESCS_PER_LOOP; txdp += IDPF_VPMD_DESCS_PER_LOOP,
>  			pkt += IDPF_VPMD_DESCS_PER_LOOP, nb_pkts -= IDPF_VPMD_DESCS_PER_LOOP) {
>  		uint64_t hi_qw0 = hi_qw_tmpl |
> -			((uint64_t)pkt[0]->data_len << IDPF_TXD_QW1_TX_BUF_SZ_S);
> +			((uint64_t)pkt[0]->data_len << IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S);
>  		uint64_t hi_qw1 = hi_qw_tmpl |
> -			((uint64_t)pkt[1]->data_len << IDPF_TXD_QW1_TX_BUF_SZ_S);
> +			((uint64_t)pkt[1]->data_len << IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S);
>  		uint64_t hi_qw2 = hi_qw_tmpl |
> -			((uint64_t)pkt[2]->data_len << IDPF_TXD_QW1_TX_BUF_SZ_S);
> +			((uint64_t)pkt[2]->data_len << IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S);
>  		uint64_t hi_qw3 = hi_qw_tmpl |
> -			((uint64_t)pkt[3]->data_len << IDPF_TXD_QW1_TX_BUF_SZ_S);
> +			((uint64_t)pkt[3]->data_len << IDPF_TXD_FLEX_QW1_TX_BUF_SZ_S);
>  
>  		__m256i desc0_1 = _mm256_set_epi64x(hi_qw1,
>  			pkt[1]->buf_iova + pkt[1]->data_off,
> -- 
> 2.34.1
> 

^ permalink raw reply

* Re: [PATCH 4/6] net/idpf: fix ptype insert position in split queue AVX2 Rx
From: Bruce Richardson @ 2026-05-20 12:22 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-5-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:33PM +0530, Shaiq Wani wrote:
> The __m256i register mb10 holds rearm data for two mbufs: mbuf 0 in
> the low 128-bit lane (dwords 0-3) and mbuf 1 in the high 128-bit
> lane (dwords 4-7).  The packet_type field sits at dword 0 within
> each mbuf's rearm_data layout.
> 
> For mbuf 1 (high lane), the packet_type must be inserted at
> _mm256_insert_epi32 index 4 (first dword of the high 128-bit lane).
> Index 2 is the third dword of the low lane, which overwrites the
> wrong mbuf's data.  The same applies to mb32 for mbuf 3.
> 
> Fixes: 1f065f9d75ff ("net/idpf: add AVX2 Rx path for split queue config")
> Signed-off-by: Shaiq Wani <shaiq.wani@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/intel/idpf/idpf_common_rxtx_avx2.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> index d3a8e17778..e66dcc7a14 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> @@ -570,9 +570,9 @@ idpf_dp_splitq_recv_pkts_avx2(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_
>  		ptype2 = (uint16_t)_mm256_extract_epi16(pt32, 1);
>  		ptype3 = (uint16_t)_mm256_extract_epi16(pt32, 9);
>  
> -		mb10 = _mm256_insert_epi32(mb10, (int)ptype_tbl[ptype1], 2);
> +		mb10 = _mm256_insert_epi32(mb10, (int)ptype_tbl[ptype1], 4);
>  		mb10 = _mm256_insert_epi32(mb10, (int)ptype_tbl[ptype0], 0);
> -		mb32 = _mm256_insert_epi32(mb32, (int)ptype_tbl[ptype3], 2);
> +		mb32 = _mm256_insert_epi32(mb32, (int)ptype_tbl[ptype3], 4);
>  		mb32 = _mm256_insert_epi32(mb32, (int)ptype_tbl[ptype2], 0);
>  
>  		/* Build rearm data for each mbuf */
> -- 
> 2.34.1
> 

^ permalink raw reply

* Re: [PATCH 3/6] net/idpf: fix mbuf initializer source in split queue AVX2 Rx
From: Bruce Richardson @ 2026-05-20 12:21 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-4-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:32PM +0530, Shaiq Wani wrote:
> In split queue mode the completion queue (rxq) does not own the mbuf
> pool — the buffer queue (bufq2) does.  The mbuf initializer encodes
> the mempool pointer, refcount and other per-pool mbuf metadata that
> is stamped into every received mbuf during rearm.
> 
> Using queue->mbuf initializer reads an uninitialised or zero value
> from the completion queue, corrupting every mbuf rearm.  Use
> queue->bufq2->mbuf initializer to get the correct value from the
> buffer queue that actually owns the mbufs.
> 
> Fixes: 1f065f9d75ff ("net/idpf: add AVX2 Rx path for split queue config")
> Signed-off-by: Shaiq Wani <shaiq.wani@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/intel/idpf/idpf_common_rxtx_avx2.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> index 28d4246134..d3a8e17778 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> @@ -491,7 +491,7 @@ idpf_dp_splitq_recv_pkts_avx2(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_
>  	struct rte_mbuf **sw_ring = &queue->bufq2->sw_ring[queue->rx_tail];
>  	volatile union virtchnl2_rx_desc *rxdp =
>  		(volatile union virtchnl2_rx_desc *)queue->rx_ring + queue->rx_tail;
> -	const __m256i mbuf_init = _mm256_set_epi64x(0, 0, 0, queue->mbuf_initializer);
> +	const __m256i mbuf_init = _mm256_set_epi64x(0, 0, 0, queue->bufq2->mbuf_initializer);
>  	uint64_t head_gen;
>  	uint16_t received = 0;
>  	int i;
> -- 
> 2.34.1
> 

^ permalink raw reply

* Re: [PATCH 2/6] net/idpf: fix DD bit byte offset in split queue AVX2 Rx
From: Bruce Richardson @ 2026-05-20 12:21 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-3-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:31PM +0530, Shaiq Wani wrote:
> The split queue completion descriptor (virtchnl2_rx_flex_desc_adv_nic_3)
> has two distinct status fields: status_err0_qw0 at byte offset 1 and
> status_err0_qw1 at byte offset 8.  The DD (descriptor done) bit lives
> in status_err0_qw1 (byte 8), not status_err0_qw0 (byte 1).
> 
> Byte 1 (status_err0_qw0) bit 0 is the LPBK (loopback) indicator, so
> reading DD from byte 1 checks the wrong field entirely.
> 
> Fix the _mm_extract_epi8 index from 1 to 8 so the code reads the DD
> bit from its correct location in the writeback descriptor.
> 
> Fixes: 1f065f9d75ff ("net/idpf: add AVX2 Rx path for split queue config")
> Signed-off-by: Shaiq Wani <shaiq.wani@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  drivers/net/intel/idpf/idpf_common_rxtx_avx2.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> index cd10c27a30..28d4246134 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> @@ -587,11 +587,11 @@ idpf_dp_splitq_recv_pkts_avx2(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_
>  		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 2]->rearm_data, rearm2);
>  		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 3]->rearm_data, rearm3);
>  
> -		/* Extract DD and generation bits from the already-loaded descriptor data (d0-d3) */
> -		stat0 = (uint8_t)_mm_extract_epi8(d0, 1);
> -		stat1 = (uint8_t)_mm_extract_epi8(d1, 1);
> -		stat2 = (uint8_t)_mm_extract_epi8(d2, 1);
> -		stat3 = (uint8_t)_mm_extract_epi8(d3, 1);
> +		/* Extract DD bit from status_err0_qw1 (byte 8 of descriptor) */
> +		stat0 = (uint8_t)_mm_extract_epi8(d0, 8);
> +		stat1 = (uint8_t)_mm_extract_epi8(d1, 8);
> +		stat2 = (uint8_t)_mm_extract_epi8(d2, 8);
> +		stat3 = (uint8_t)_mm_extract_epi8(d3, 8);
>  
>  		pktlen_gen0 = (uint16_t)_mm_extract_epi16(d0, 2);
>  		pktlen_gen1 = (uint16_t)_mm_extract_epi16(d1, 2);
> -- 
> 2.34.1
> 

^ permalink raw reply

* Re: [PATCH 1/6] net/idpf: fix gen bit extraction in split queue AVX2 Rx
From: Bruce Richardson @ 2026-05-20 12:20 UTC (permalink / raw)
  To: Shaiq Wani; +Cc: dev, aman.deep.singh
In-Reply-To: <20260511090935.2288837-2-shaiq.wani@intel.com>

On Mon, May 11, 2026 at 02:39:30PM +0530, Shaiq Wani wrote:
> The generation bit in the pktlen_gen_bufq_id field of the split queue
> completion descriptor (virtchnl2_rx_flex_desc_adv_nic_3) must be
> extracted by masking first and then shifting, not the other way around.
> 
> With shift-then-mask, the mask is applied to already-shifted bits,
> which can produce incorrect results when upper bits (packet length,
> buffer queue ID) leak into the extracted value.
> 
> Change to mask-then-shift to correctly isolate the generation bit
> before comparing it with the expected generation ID.
> 
> Fixes: 1f065f9d75ff ("net/idpf: add AVX2 Rx path for split queue config")
> Signed-off-by: Shaiq Wani <shaiq.wani@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
>  .../net/intel/idpf/idpf_common_rxtx_avx2.c    | 20 +++++++++----------
>  1 file changed, 10 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> index db7728afad..cd10c27a30 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx_avx2.c
> @@ -524,8 +524,8 @@ idpf_dp_splitq_recv_pkts_avx2(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_
>  
>  	/* check if there is at least one packet available */
>  	head_gen = rxdp->flex_adv_nic_3_wb.pktlen_gen_bufq_id;
> -	if (((head_gen >> VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) &
> -		 VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) != queue->expected_gen_id)
> +	if (((head_gen & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
> +		 VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) != queue->expected_gen_id)
>  		return 0;
>  
>  	for (i = 0; i < nb_pkts;
> @@ -599,17 +599,17 @@ idpf_dp_splitq_recv_pkts_avx2(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_
>  		pktlen_gen3 = (uint16_t)_mm_extract_epi16(d3, 2);
>  
>  		valid0 = (stat0 & 1) &&
> -			 (((pktlen_gen0 >> VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) &
> -			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) == queue->expected_gen_id);
> +			 (((pktlen_gen0 & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
> +			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) == queue->expected_gen_id);
>  		valid1 = (stat1 & 1) &&
> -			 (((pktlen_gen1 >> VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) &
> -			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) == queue->expected_gen_id);
> +			 (((pktlen_gen1 & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
> +			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) == queue->expected_gen_id);
>  		valid2 = (stat2 & 1) &&
> -			 (((pktlen_gen2 >> VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) &
> -			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) == queue->expected_gen_id);
> +			 (((pktlen_gen2 & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
> +			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) == queue->expected_gen_id);
>  		valid3 = (stat3 & 1) &&
> -			 (((pktlen_gen3 >> VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) &
> -			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) == queue->expected_gen_id);
> +			 (((pktlen_gen3 & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
> +			   VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S) == queue->expected_gen_id);
>  
>  		/* count valid descriptors (holes are impossible because
>  		 * descriptors are read in reverse order while the NIC
> -- 
> 2.34.1
> 

^ permalink raw reply

* Re: [PATCH dpdk v5 2/5] net: support multiple stacked VLAN tags
From: Robin Jarry @ 2026-05-20 11:09 UTC (permalink / raw)
  To: David Marchand; +Cc: dev
In-Reply-To: <CAJFAV8xdkhcmY53VRJf3qbsVsYB-hM3DaxMxr=GS5MNvb1MPGA@mail.gmail.com>

David Marchand, May 20, 2026 at 11:56:
> On Mon, 18 May 2026 at 15:27, Robin Jarry <rjarry@redhat.com> wrote:
>>
>> The VLAN and QinQ code paths in rte_net_get_ptype handle at most two
>> tags with duplicated logic. Replace them with a single loop that
>> consumes all consecutive VLAN/QinQ headers regardless of depth.
>>
>> Bugzilla ID: 1941
>> Suggested-by: David Marchand <david.marchand@redhat.com>
>> Signed-off-by: Robin Jarry <rjarry@redhat.com>
>
> For the record, I am still skeptical about the usecase behind this change.
>
>
>> ---
>>  lib/net/rte_net.c | 35 ++++++++++++++++-------------------
>>  1 file changed, 16 insertions(+), 19 deletions(-)
>>
>> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
>> index c70b57fdc0f8..d3cded961fb5 100644
>> --- a/lib/net/rte_net.c
>> +++ b/lib/net/rte_net.c
>> @@ -349,29 +349,26 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
>>         if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
>>                 goto l3; /* fast path if packet is IPv4 */
>>
>> -       if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
>> +       if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) ||
>> +               (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ))) {
>>                 const struct rte_vlan_hdr *vh;
>>                 struct rte_vlan_hdr vh_copy;
>>
>> -               pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
>> -               vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
>> -               if (unlikely(vh == NULL))
>> -                       return pkt_type;
>> -               off += sizeof(*vh);
>> -               hdr_lens->l2_len += sizeof(*vh);
>> -               proto = vh->eth_proto;
>> -       } else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
>> -               const struct rte_vlan_hdr *vh;
>> -               struct rte_vlan_hdr vh_copy;
>> +               if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN))
>> +                       pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
>> +               else
>> +                       pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
>> +
>> +               do {
>> +                       vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
>> +                       if (unlikely(vh == NULL))
>> +                               return pkt_type;
>
> Kevin noted that it is weird to report back some packet type when the
> packet is malformed.
> Maybe return RTE_PTYPE_UNKNOWN here so that the application is forced
> to validate the packet? (it should already be doing it, in any
> case..).

If we do this, we need to fix it in the entire function. There are
several other places where the "current" value of pkt_type is returned
on error.

>
>
>> +                       off += sizeof(*vh);
>> +                       hdr_lens->l2_len += sizeof(*vh);
>> +                       proto = vh->eth_proto;
>> +               } while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
>> +                       proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
>>
>> -               pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
>> -               vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
>> -                       &vh_copy);
>> -               if (unlikely(vh == NULL))
>> -                       return pkt_type;
>> -               off += 2 * sizeof(*vh);
>> -               hdr_lens->l2_len += 2 * sizeof(*vh);
>> -               proto = vh->eth_proto;
>>         } else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
>>                 (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
>>                 unsigned int i;


-- 
Robin

> Avoid contact with skin.


^ permalink raw reply

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

On 5/18/26 2:27 PM, Robin Jarry wrote:
> This reverts commit 1f250674085aeb4ffd15ac2519a68efc04faf7ac.
> 
> rte_net_get_ptype() now uses |= to set the L2 ptype inside the VLAN
> parsing loop. Since pkt_type is already initialized with
> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN (0x6)
> results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single VLAN frames
> to be misidentified as QinQ.
> 
> This was detected while testing DPDK 25.11.1 in grout. The net/tap
> driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
> L2 header length. With the wrong ptype, l2_len is set to 22 (ether
> + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
> header pointer by 4 bytes. The checksum is then computed on garbage
> data, causing valid packets to be dropped.
> 
> Bugzilla ID: 1941
> Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
> Cc: stable@dpdk.org
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
> ---
>  lib/net/rte_net.c | 29 +++++++++++++++--------------
>  1 file changed, 15 insertions(+), 14 deletions(-)
> 

Acked-by: Kevin Traynor <ktraynor@redhat.com>


^ permalink raw reply

* Re: [PATCH 0/3] i40e base code update
From: Bruce Richardson @ 2026-05-20 10:17 UTC (permalink / raw)
  To: Ciara Loftus; +Cc: dev
In-Reply-To: <20260519145227.308814-1-ciara.loftus@intel.com>

On Tue, May 19, 2026 at 02:52:20PM +0000, Ciara Loftus wrote:
> Update the i40e base code. Both patches address integer type width issues
> that can cause incorrect behaviour and are candidates for stable backport.
> 
> Chinh Cao (1):
>   net/i40e/base: fix integer overflow in NVM timing logic
> 
> Ciara Loftus (1):
>   net/i40e/base: update version info
> 
> Deepthi Kavalur (1):
>   net/i40e/base: fix loop counter width in DCB parsing
> 
Series-Acked-by: Bruce Richardson <bruce.richardson@intel.com>

Applied to dpdk-next-net-intel.
Thanks,
/Bruce
>  .mailmap                                     |  1 +
>  drivers/net/intel/i40e/base/README           |  4 +-
>  drivers/net/intel/i40e/base/i40e_dcb.c       |  6 +-
>  drivers/net/intel/i40e/base/i40e_nvm.c       | 67 +++++++++++++-------
>  drivers/net/intel/i40e/base/i40e_prototype.h |  2 +-
>  drivers/net/intel/i40e/base/i40e_type.h      |  2 +-
>  drivers/net/intel/i40e/i40e_ethdev.c         |  3 +-
>  7 files changed, 55 insertions(+), 30 deletions(-)
> 
> -- 
> 2.43.0
> 

^ permalink raw reply

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

On Mon, 18 May 2026 at 15:27, Robin Jarry <rjarry@redhat.com> wrote:
>
> The VLAN and QinQ code paths in rte_net_get_ptype handle at most two
> tags with duplicated logic. Replace them with a single loop that
> consumes all consecutive VLAN/QinQ headers regardless of depth.
>
> Bugzilla ID: 1941
> Suggested-by: David Marchand <david.marchand@redhat.com>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>

For the record, I am still skeptical about the usecase behind this change.


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

Kevin noted that it is weird to report back some packet type when the
packet is malformed.
Maybe return RTE_PTYPE_UNKNOWN here so that the application is forced
to validate the packet? (it should already be doing it, in any
case..).


> +                       off += sizeof(*vh);
> +                       hdr_lens->l2_len += sizeof(*vh);
> +                       proto = vh->eth_proto;
> +               } while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> +                       proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
>
> -               pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> -               vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
> -                       &vh_copy);
> -               if (unlikely(vh == NULL))
> -                       return pkt_type;
> -               off += 2 * sizeof(*vh);
> -               hdr_lens->l2_len += 2 * sizeof(*vh);
> -               proto = vh->eth_proto;
>         } else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
>                 (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
>                 unsigned int i;


-- 
David Marchand


^ permalink raw reply

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

On Mon, 18 May 2026 at 15:28, Robin Jarry <rjarry@redhat.com> wrote:
>
> This reverts commit 1f250674085aeb4ffd15ac2519a68efc04faf7ac.
>
> rte_net_get_ptype() now uses |= to set the L2 ptype inside the VLAN
> parsing loop. Since pkt_type is already initialized with
> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN (0x6)
> results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single VLAN frames
> to be misidentified as QinQ.
>
> This was detected while testing DPDK 25.11.1 in grout. The net/tap
> driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
> L2 header length. With the wrong ptype, l2_len is set to 22 (ether
> + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
> header pointer by 4 bytes. The checksum is then computed on garbage
> data, causing valid packets to be dropped.
>
> Bugzilla ID: 1941
> Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
> Cc: stable@dpdk.org

> Signed-off-by: Robin Jarry <rjarry@redhat.com>

Acked-by: David Marchand <david.marchand@redhat.com>
Just a note that this commit is already present in all latest LTS releases.


-- 
David Marchand


^ permalink raw reply

* [PATCH v2 2/2] ethdev: add telemetry endpoint for list names
From: Chengwen Feng @ 2026-05-20  9:38 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, andrew.rybchenko, bruce.richardson, mb
In-Reply-To: <20260520093804.29102-1-fengchengwen@huawei.com>

Add /ethdev/list_names telemetry endpoint which returns a dictionary
keyed by port ID with device name as the value, so users can
identify ports by name directly from the telemetry output.

Original /ethdev/list output:
  {"/ethdev/list": [0, 1]}

New /ethdev/list_names output:
  {"/ethdev/list_names": {"0": "0000:7d:00.0",
  "1": "0000:7d:00.1"}}

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/ethdev/rte_ethdev_telemetry.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/lib/ethdev/rte_ethdev_telemetry.c b/lib/ethdev/rte_ethdev_telemetry.c
index a910864bc5..0e71419473 100644
--- a/lib/ethdev/rte_ethdev_telemetry.c
+++ b/lib/ethdev/rte_ethdev_telemetry.c
@@ -61,6 +61,24 @@ eth_dev_handle_port_list(const char *cmd __rte_unused,
 	return 0;
 }
 
+static int
+eth_dev_handle_port_list_names(const char *cmd __rte_unused,
+		const char *params __rte_unused,
+		struct rte_tel_data *d)
+{
+	char id_str[RTE_TEL_MAX_STRING_LEN];
+	struct rte_eth_dev *dev;
+	int port_id;
+
+	rte_tel_data_start_dict(d);
+	RTE_ETH_FOREACH_DEV(port_id) {
+		dev = &rte_eth_devices[port_id];
+		sprintf(id_str, "%d", port_id);
+		rte_tel_data_add_dict_string(d, id_str, dev->data->name);
+	}
+	return 0;
+}
+
 static int
 eth_dev_parse_hide_zero(const char *key, const char *value, void *extra_args)
 {
@@ -1548,6 +1566,9 @@ RTE_INIT(ethdev_init_telemetry)
 	rte_telemetry_register_cmd_arg("/ethdev/list",
 			eth_dev_telemetry_do, eth_dev_handle_port_list,
 			"Returns list of available ethdev ports. Takes no parameters");
+	rte_telemetry_register_cmd_arg("/ethdev/list_names",
+			eth_dev_telemetry_do, eth_dev_handle_port_list_names,
+			"Returns dict of available ethdev ports by ID-NAMEs. Takes no parameters");
 	rte_telemetry_register_cmd_arg("/ethdev/stats",
 			eth_dev_telemetry_do, eth_dev_handle_port_stats,
 			"Returns the common stats for a port. Parameters: int port_id");
-- 
2.17.1


^ permalink raw reply related

* [PATCH v2 1/2] dmadev: add telemetry endpoint for list names
From: Chengwen Feng @ 2026-05-20  9:38 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, andrew.rybchenko, bruce.richardson, mb
In-Reply-To: <20260520093804.29102-1-fengchengwen@huawei.com>

Add /dmadev/list_names telemetry endpoint which returns a dictionary
keyed by device ID with device name as the value, so users can
identify devices by name directly from the telemetry output.

Original /dmadev/list output:
  {"/dmadev/list": [0, 1]}

New /dmadev/list_names output:
  {"/dmadev/list_names": {"0": "hisi_sec2-0-dma0",
  "1": "hisi_sec2-0-dma1"}}

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/dmadev/rte_dmadev.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/lib/dmadev/rte_dmadev.c b/lib/dmadev/rte_dmadev.c
index b75b4f9bd1..98ef7e2b93 100644
--- a/lib/dmadev/rte_dmadev.c
+++ b/lib/dmadev/rte_dmadev.c
@@ -1157,6 +1157,27 @@ dmadev_handle_dev_list(const char *cmd __rte_unused,
 	return 0;
 }
 
+static int
+dmadev_handle_dev_list_names(const char *cmd __rte_unused,
+		const char *params __rte_unused,
+		struct rte_tel_data *d)
+{
+	char id_str[RTE_TEL_MAX_STRING_LEN];
+	struct rte_dma_dev *dev;
+	int dev_id;
+
+	rte_tel_data_start_dict(d);
+	for (dev_id = 0; dev_id < dma_devices_max; dev_id++) {
+		if (!rte_dma_is_valid(dev_id))
+			continue;
+		dev = &rte_dma_devices[dev_id];
+		sprintf(id_str, "%d", dev_id);
+		rte_tel_data_add_dict_string(d, id_str, dev->data->dev_name);
+	}
+
+	return 0;
+}
+
 #define ADD_CAPA(td, dc, c) rte_tel_data_add_dict_int(td, dma_capability_name(c), !!(dc & c))
 
 static int
@@ -1309,6 +1330,8 @@ RTE_INIT(dmadev_init_telemetry)
 {
 	rte_telemetry_register_cmd("/dmadev/list", dmadev_handle_dev_list,
 			"Returns list of available dmadev devices by IDs. No parameters.");
+	rte_telemetry_register_cmd("/dmadev/list_names", dmadev_handle_dev_list_names,
+			"Returns dict of available dmadev devices by ID-NAMEs. No parameters.");
 	rte_telemetry_register_cmd("/dmadev/info", dmadev_handle_dev_info,
 			"Returns information for a dmadev. Parameters: int dev_id");
 	rte_telemetry_register_cmd("/dmadev/stats", dmadev_handle_dev_stats,
-- 
2.17.1


^ permalink raw reply related

* [PATCH v2 0/2] Add list names telemetry endpoint
From: Chengwen Feng @ 2026-05-20  9:38 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, andrew.rybchenko, bruce.richardson, mb
In-Reply-To: <20260520035641.50555-1-fengchengwen@huawei.com>

Currently, the /dmadev/list and /ethdev/list telemetry endpoints return
only integer IDs, making it hard to identify devices. This series add
list_names telemetry endpoint which output both IDs and its device's
name.

---
v2: Add new telemetry endpoint according Bruce's advise.

Chengwen Feng (2):
  dmadev: add telemetry endpoint for list names
  ethdev: add telemetry endpoint for list names

 lib/dmadev/rte_dmadev.c           | 23 +++++++++++++++++++++++
 lib/ethdev/rte_ethdev_telemetry.c | 21 +++++++++++++++++++++
 2 files changed, 44 insertions(+)

-- 
2.17.1


^ permalink raw reply

* [PATCH v2 2/2] doc: update recommended matching versions for cpfl and idpf
From: Soumyadeep Hore @ 2026-05-20 22:03 UTC (permalink / raw)
  To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev
In-Reply-To: <20260520220306.85273-1-soumyadeep.hore@intel.com>

Update the recommended MEV-ts release version corresponding to DPDK
release in the cpfl and idpf drivers documentation.

Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
 doc/guides/nics/cpfl.rst | 21 ++++++++-------------
 doc/guides/nics/idpf.rst | 19 ++++++++-----------
 2 files changed, 16 insertions(+), 24 deletions(-)

diff --git a/doc/guides/nics/cpfl.rst b/doc/guides/nics/cpfl.rst
index bc42c524ea..377397e621 100644
--- a/doc/guides/nics/cpfl.rst
+++ b/doc/guides/nics/cpfl.rst
@@ -28,19 +28,14 @@ It is highly recommended to upgrade the MEV-ts release
 to avoid compatibility issues with the cpfl PMD.
 Here is the suggested matching list which has been tested and verified.
 
-   +------------+------------------+
-   |     DPDK   |  MEV-ts release  |
-   +============+==================+
-   |    23.07   |      0.9.1       |
-   +------------+------------------+
-   |    23.11   |       1.0        |
-   +------------+------------------+
-   |    24.07   |       1.4        |
-   +------------+------------------+
-   |    24.11   |       1.6        |
-   +------------+------------------+
-   |    25.07   |       2.0        |
-   +------------+------------------+
+=====  ==============
+DPDK   MEV-ts release
+=====  ==============
+26.07       2.2
+25.11       2.0
+24.11       1.6
+23.11       1.0
+=====  ==============
 
 
 Configuration
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index c7c76190c8..cc89d22cf0 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -26,17 +26,14 @@ It is highly recommended to upgrade the idpf kernel driver, MEV-ts release
 to avoid compatibility issues with the idpf PMD.
 Here is the suggested matching list which has been tested and verified.
 
-   +------------+---------------+------------------+
-   |    DPDK    | Kernel Driver |  MEV-ts release  |
-   +============+===============+==================+
-   |    23.07   |    0.0.710    |      0.9.1       |
-   +------------+---------------+------------------+
-   |    23.11   |    0.0.720    |       1.0        |
-   +------------+---------------+------------------+
-   |    24.11   |    0.0.754    |       1.6        |
-   +------------+---------------+------------------+
-   |    25.07   |    0.0.772    |       2.0        |
-   +------------+---------------+------------------+
+=====  =============  ==============
+DPDK   Kernel Driver  MEV-ts release
+=====  =============  ==============
+26.07     0.0.780           2.2
+25.11     0.0.772           2.0
+24.11     0.0.754           1.6
+23.11     0.0.720           1.0
+=====  =============  ==============
 
 
 Configuration
-- 
2.47.1


^ permalink raw reply related

* [PATCH v2 1/2] net/idpf/base: add MMG device IDs
From: Soumyadeep Hore @ 2026-05-20 22:03 UTC (permalink / raw)
  To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev
  Cc: Christopher Pau
In-Reply-To: <20260520220306.85273-1-soumyadeep.hore@intel.com>

From: Christopher Pau <christopher.pau@intel.com>

Add MMG device ids for PF and VF.

Signed-off-by: Christopher Pau <christopher.pau@intel.com>
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
 drivers/net/intel/idpf/base/idpf_devids.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/intel/idpf/base/idpf_devids.h b/drivers/net/intel/idpf/base/idpf_devids.h
index 1ae99fcee1..8b10ef9e32 100644
--- a/drivers/net/intel/idpf/base/idpf_devids.h
+++ b/drivers/net/intel/idpf/base/idpf_devids.h
@@ -10,7 +10,10 @@
 
 /* Device IDs */
 #define IDPF_DEV_ID_PF			0x1452
+#define IDPF_DEV_ID_PF1			0x11DF
+#define IDPF_DEV_ID_CPF1		0x11E0
 #define IDPF_DEV_ID_VF			0x145C
+#define IDPF_DEV_ID_VF1			0x0DE2
 #define IDPF_DEV_ID_VF_SIOV		0x0DD5
 
 
-- 
2.47.1


^ permalink raw reply related

* [PATCH v2 0/2] Update IDPF Base Code
From: Soumyadeep Hore @ 2026-05-20 22:03 UTC (permalink / raw)
  To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev
In-Reply-To: <20260509015408.29188-3-soumyadeep.hore@intel.com>

Update idpf base code with compatible configuration on MEV.

---
v2:
- lists only LTS releases
- lists releases in descending order, so that latest is on top
- reworks table to use the new RST table format
---

Christopher Pau (1):
  net/idpf/base: add MMG device IDs

Soumyadeep Hore (1):
  doc: update recommended matching versions for cpfl and idpf

 doc/guides/nics/cpfl.rst                  | 21 ++++++++-------------
 doc/guides/nics/idpf.rst                  | 19 ++++++++-----------
 drivers/net/intel/idpf/base/idpf_devids.h |  3 +++
 3 files changed, 19 insertions(+), 24 deletions(-)

-- 
2.47.1


^ permalink raw reply

* Re: [PATCH v3] ring: fix zero-copy burst API documentation
From: Thomas Monjalon @ 2026-05-20  8:59 UTC (permalink / raw)
  To: jinzhiguang
  Cc: Konstantin Ananyev, Wathsala Vithanage, Dharmik Thakkar,
	Honnappa Nagarahalli, dev, stable
In-Reply-To: <20260519133853.889-1-jinzhiguang@kylinos.cn>

19/05/2026 15:38, jinzhiguang:
> --- a/.mailmap
> +++ b/.mailmap
> @@ -756,6 +756,7 @@ Jing Chen <jing.d.chen@intel.com>
>  Jingguo Fu <jingguox.fu@intel.com>
>  Jingjing Wu <jingjing.wu@intel.com>
>  Jingzhao Ni <jingzhao.ni@arm.com>
> +jinzhiguang <jinzhiguang@kylinos.cn>

Maybe you want to insert a space? What is your first name and family name?
Please use capital letters where appropriate.



^ permalink raw reply

* Re: [PATCH 0/2] enhance telemetry list endpoint with device name
From: Bruce Richardson @ 2026-05-20  7:49 UTC (permalink / raw)
  To: fengchengwen; +Cc: Morten Brørup, thomas, stephen, dev
In-Reply-To: <689a32bf-b6e2-4646-8073-cd1f895b692b@huawei.com>

On Wed, May 20, 2026 at 03:31:57PM +0800, fengchengwen wrote:
> On 5/20/2026 1:40 PM, Morten Brørup wrote:
> >> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> >> Sent: Wednesday, 20 May 2026 05.57
> >>
> >> Currently, the /dmadev/list and /ethdev/list telemetry endpoints return
> >> only integer IDs, making it hard to identify devices. This series
> >> changes
> >> both to output strings in "ID    NAME" format for better usability.
> > 
> > For machine reading of the JSON output, it would be better returning an object with an integer and a string field, {ID, "NAME"}.
> 
> The TEL_DICT could do {"ID", "NAME"}, which like:
>   "/ethdev/list": {
>     "0": "0000:7d:00.0",
>     "1": "0000:7d:00.1"
>   }
> 
> Maybe we could add one TEL_INT_DICT which is int-value pairs, we may get:
>   "/ethdev/list": {
>     0: "0000:7d:00.0",
>     1: "0000:7d:00.1"
>   }
> 
> I prefer the first one, However, the capacity is reduced from 512 (RTE_TEL_MAX_ARRAY_ENTRIES) to 256 (RTE_TEL_MAX_DICT_ENTRIES), but I think it is enough.
> 
> What's your opinion?
> 

I'm not sure about this change at all. This change is only relevant for
those using the script interactively, for any other use, I would expect the
the /ethdev/list call would be followed by the /ethdev/info calls for each
port to get the name. That was the basic design in mind for this, the list
call was purely to provide the ids, any other info you make separate calls
for.

Also, while not officially part of the ABI of DPDK, I think it would be
wrong to go changing the types of the returned data from this /ethdev/list
call. Any user-written interfaces to telemetry will be relying on the
current behaviour to list and query ports. If you really want to have an
easy way to get the names of the ports, I suggest adding instead an
"/ethdev/list_names" API, which can either return the objects above, or
else simply an array of names.

/Bruce

^ permalink raw reply


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