* [PATCH v4 02/24] bpf: add format instruction function
From: Marat Khalili @ 2026-06-23 14:31 UTC (permalink / raw)
To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260623143215.95318-1-marat.khalili@huawei.com>
BPF library already contains BPF instruction formatting functions, but
they could only be used via `rte_bpf_dump` to dump result into file. Add
new function `rte_bpf_format` to format instruction in various way
(hexadecimal, disassembly) into a user-provided buffer, as well as a
service function `rte_bpf_insn_is_wide` to detect wide instructions.
Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
lib/bpf/bpf_dump.c | 290 +++++++++++++++++++++++++++------------------
lib/bpf/rte_bpf.h | 51 ++++++++
2 files changed, 226 insertions(+), 115 deletions(-)
diff --git a/lib/bpf/bpf_dump.c b/lib/bpf/bpf_dump.c
index 0abaeef8ae98..4fd67ad5a1df 100644
--- a/lib/bpf/bpf_dump.c
+++ b/lib/bpf/bpf_dump.c
@@ -46,6 +46,38 @@ static const char *const jump_tbl[16] = {
[EBPF_JSLT >> 4] = "jslt", [EBPF_JSLE >> 4] = "jsle",
};
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_insn_is_wide, 26.07)
+bool
+rte_bpf_insn_is_wide(const struct ebpf_insn *ins)
+{
+ return ins->code == (BPF_LD | BPF_IMM | EBPF_DW);
+}
+
+
+/* Format one (possibly wide) eBPF command as hexadecimal in objdump format. */
+static int
+format_hexadecimal(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
+ uint32_t flags)
+{
+ const char *const b = (const char *)ins;
+
+ RTE_ASSERT((flags & RTE_BPF_FORMAT_FLAG_HEXADECIMAL) != 0);
+
+ RTE_BUILD_BUG_ON(sizeof(*ins) != 8);
+
+ if ((flags & RTE_BPF_FORMAT_FLAG_NEVER_WIDE) == 0 && rte_bpf_insn_is_wide(ins))
+ return snprintf(buffer, bufsz,
+ "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
+ b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
+ else
+ return snprintf(buffer, bufsz,
+ "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+/* Return atomic subcommand mnemonic based on BPF_STX immediate. */
static inline const char *
atomic_op(int32_t imm)
{
@@ -59,130 +91,158 @@ atomic_op(int32_t imm)
}
}
-RTE_EXPORT_SYMBOL(rte_bpf_dump)
-void rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len)
+/* Format one (possibly wide) eBPF command as assembler. */
+static int
+format_disassembly(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
+ uint32_t pc, uint32_t flags)
{
- uint32_t i;
+ uint8_t cls = BPF_CLASS(ins->code);
+ const char *op, *postfix = "", *warning = "";
+ char jump[16];
- for (i = 0; i < len; ++i) {
- const struct ebpf_insn *ins = buf + i;
- uint8_t cls = BPF_CLASS(ins->code);
- const char *op, *postfix = "", *warning = "";
+ RTE_ASSERT((flags & RTE_BPF_FORMAT_FLAG_HEXADECIMAL) == 0);
- fprintf(f, " L%u:\t", i);
+ switch (cls) {
+ default:
+ return snprintf(buffer, bufsz, "unimp 0x%x // class: %s",
+ ins->code, class_tbl[cls]);
+ case BPF_ALU:
+ postfix = "32";
+ /* fall through */
+ case EBPF_ALU64:
+ op = alu_op_tbl[BPF_OP_INDEX(ins->code)];
+ if (ins->off != 0)
+ /* Not yet supported variation with non-zero offset. */
+ warning = ", off != 0";
+ if (BPF_SRC(ins->code) == BPF_X)
+ return snprintf(buffer, bufsz, "%s%s r%u, r%u%s", op, postfix, ins->dst_reg,
+ ins->src_reg, warning);
+ else
+ return snprintf(buffer, bufsz, "%s%s r%u, #0x%x%s", op, postfix,
+ ins->dst_reg, ins->imm, warning);
+ case BPF_LD:
+ op = "ld";
+ postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
+ if (ins->code == (BPF_LD | BPF_IMM | EBPF_DW)) {
+ uint64_t val;
- switch (cls) {
- default:
- fprintf(f, "unimp 0x%x // class: %s\n",
- ins->code, class_tbl[cls]);
- break;
- case BPF_ALU:
- postfix = "32";
- /* fall through */
- case EBPF_ALU64:
- op = alu_op_tbl[BPF_OP_INDEX(ins->code)];
- if (ins->off != 0)
- /* Not yet supported variation with non-zero offset. */
- warning = ", off != 0";
- if (BPF_SRC(ins->code) == BPF_X)
- fprintf(f, "%s%s r%u, r%u%s\n", op, postfix, ins->dst_reg,
- ins->src_reg, warning);
- else
- fprintf(f, "%s%s r%u, #0x%x%s\n", op, postfix,
- ins->dst_reg, ins->imm, warning);
- break;
- case BPF_LD:
- op = "ld";
- postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
- if (ins->code == (BPF_LD | BPF_IMM | EBPF_DW)) {
- uint64_t val;
-
- if (ins->src_reg != 0)
- /* Not yet supported variation with non-zero src. */
- warning = ", src != 0";
- val = (uint32_t)ins[0].imm |
- (uint64_t)(uint32_t)ins[1].imm << 32;
- fprintf(f, "%s%s r%d, #0x%"PRIx64"%s\n",
- op, postfix, ins->dst_reg, val, warning);
- i++;
- } else if (BPF_MODE(ins->code) == BPF_IMM)
- fprintf(f, "%s%s r%d, #0x%x\n", op, postfix,
- ins->dst_reg, ins->imm);
- else if (BPF_MODE(ins->code) == BPF_ABS)
- fprintf(f, "%s%s r%d, [%d]\n", op, postfix,
- ins->dst_reg, ins->imm);
- else if (BPF_MODE(ins->code) == BPF_IND)
- fprintf(f, "%s%s r%d, [r%u + %d]\n", op, postfix,
- ins->dst_reg, ins->src_reg, ins->imm);
- else
- fprintf(f, "// BUG: LD opcode 0x%02x in eBPF insns\n",
- ins->code);
- break;
- case BPF_LDX:
- op = "ldx";
- postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
- if (BPF_MODE(ins->code) == BPF_MEM)
- fprintf(f, "%s%s r%d, [r%u + %d]\n", op, postfix, ins->dst_reg,
- ins->src_reg, ins->off);
- else
- fprintf(f, "// BUG: LDX opcode 0x%02x in eBPF insns\n",
- ins->code);
- break;
- case BPF_ST:
- op = "st";
- postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
- if (BPF_MODE(ins->code) == BPF_MEM)
- fprintf(f, "%s%s [r%d + %d], #0x%x\n", op, postfix,
- ins->dst_reg, ins->off, ins->imm);
- else
- fprintf(f, "// BUG: ST opcode 0x%02x in eBPF insns\n",
- ins->code);
- break;
- case BPF_STX:
- if (BPF_MODE(ins->code) == BPF_MEM)
- op = "stx";
- else if (BPF_MODE(ins->code) == EBPF_ATOMIC) {
- op = atomic_op(ins->imm);
- if (op == NULL) {
- fprintf(f, "// BUG: ATOMIC operation 0x%x in eBPF insns\n",
- ins->imm);
- break;
- }
- } else {
- fprintf(f, "// BUG: STX opcode 0x%02x in eBPF insns\n",
- ins->code);
- break;
- }
- postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
- fprintf(f, "%s%s [r%d + %d], r%u\n", op, postfix,
- ins->dst_reg, ins->off, ins->src_reg);
- break;
-#define L(pc, off) ((int)(pc) + 1 + (off))
- case BPF_JMP:
- op = jump_tbl[BPF_OP_INDEX(ins->code)];
if (ins->src_reg != 0)
- /* Not yet supported variation with non-zero src w/o condition. */
+ /* Not yet supported variation with non-zero src. */
warning = ", src != 0";
+ val = (uint32_t)ins[0].imm |
+ (uint64_t)(uint32_t)ins[1].imm << 32;
+ return snprintf(buffer, bufsz, "%s%s r%d, #0x%"PRIx64"%s",
+ op, postfix, ins->dst_reg, val, warning);
+ }
+ switch (BPF_MODE(ins->code)) {
+ case BPF_IMM:
+ return snprintf(buffer, bufsz, "%s%s r%d, #0x%x", op, postfix,
+ ins->dst_reg, ins->imm);
+ case BPF_ABS:
+ return snprintf(buffer, bufsz, "%s%s r%d, [%d]", op, postfix,
+ ins->dst_reg, ins->imm);
+ case BPF_IND:
+ return snprintf(buffer, bufsz, "%s%s r%d, [r%u + %d]", op, postfix,
+ ins->dst_reg, ins->src_reg, ins->imm);
+ default:
+ return snprintf(buffer, bufsz, "// BUG: LD opcode 0x%02x in eBPF insns",
+ ins->code);
+ }
+ case BPF_LDX:
+ op = "ldx";
+ postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
+ if (BPF_MODE(ins->code) == BPF_MEM)
+ return snprintf(buffer, bufsz, "%s%s r%d, [r%u + %d]", op, postfix,
+ ins->dst_reg, ins->src_reg, ins->off);
+ else
+ return snprintf(buffer, bufsz, "// BUG: LDX opcode 0x%02x in eBPF insns",
+ ins->code);
+ case BPF_ST:
+ op = "st";
+ postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
+ if (BPF_MODE(ins->code) == BPF_MEM)
+ return snprintf(buffer, bufsz, "%s%s [r%d + %d], #0x%x", op, postfix,
+ ins->dst_reg, ins->off, ins->imm);
+ else
+ return snprintf(buffer, bufsz, "// BUG: ST opcode 0x%02x in eBPF insns",
+ ins->code);
+ case BPF_STX:
+ switch (BPF_MODE(ins->code)) {
+ case BPF_MEM:
+ op = "stx";
+ break;
+ case EBPF_ATOMIC:
+ op = atomic_op(ins->imm);
if (op == NULL)
- fprintf(f, "invalid jump opcode: %#x\n", ins->code);
- else if (BPF_OP(ins->code) == BPF_JA)
- fprintf(f, "%s L%d%s\n", op, L(i, ins->off), warning);
- else if (BPF_OP(ins->code) == EBPF_CALL)
- /* Call of helper function with index in immediate. */
- fprintf(f, "%s #%u%s\n", op, ins->imm, warning);
- else if (BPF_OP(ins->code) == EBPF_EXIT)
- fprintf(f, "%s%s\n", op, warning);
- else if (BPF_SRC(ins->code) == BPF_X)
- fprintf(f, "%s r%u, r%u, L%d\n", op, ins->dst_reg,
- ins->src_reg, L(i, ins->off));
- else
- fprintf(f, "%s r%u, #0x%x, L%d\n", op, ins->dst_reg,
- ins->imm, L(i, ins->off));
+ return snprintf(buffer, bufsz,
+ "// BUG: ATOMIC operation 0x%x in eBPF insns", ins->imm);
break;
- case BPF_RET:
- fprintf(f, "// BUG: RET opcode 0x%02x in eBPF insns\n",
+ default:
+ return snprintf(buffer, bufsz, "// BUG: STX opcode 0x%02x in eBPF insns",
ins->code);
- break;
}
+ postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
+ return snprintf(buffer, bufsz, "%s%s [r%d + %d], r%u", op, postfix,
+ ins->dst_reg, ins->off, ins->src_reg);
+ case BPF_JMP:
+ op = jump_tbl[BPF_OP_INDEX(ins->code)];
+ if (op == NULL)
+ return snprintf(buffer, bufsz, "invalid jump opcode: %#x", ins->code);
+
+ if ((flags & RTE_BPF_FORMAT_FLAG_ABSOLUTE_JUMPS) != 0)
+ snprintf(jump, sizeof(jump), "L%d", pc + 1 + ins->off);
+ else
+ snprintf(jump, sizeof(jump), "%+d", (int)ins->off);
+
+ if (ins->src_reg != 0)
+ /* Not yet supported variation with non-zero src w/o condition. */
+ warning = ", src != 0";
+ switch (BPF_OP(ins->code)) {
+ case BPF_JA:
+ return snprintf(buffer, bufsz, "%s %s%s", op, jump, warning);
+ case EBPF_CALL:
+ /* Call of helper function with index in immediate. */
+ return snprintf(buffer, bufsz, "%s #%u%s", op, ins->imm, warning);
+ case EBPF_EXIT:
+ return snprintf(buffer, bufsz, "%s%s", op, warning);
+ }
+
+ if (BPF_SRC(ins->code) == BPF_X)
+ return snprintf(buffer, bufsz, "%s r%u, r%u, %s", op, ins->dst_reg,
+ ins->src_reg, jump);
+ else
+ return snprintf(buffer, bufsz, "%s r%u, #0x%x, %s", op, ins->dst_reg,
+ ins->imm, jump);
+ case BPF_RET:
+ return snprintf(buffer, bufsz, "// BUG: RET opcode 0x%02x in eBPF insns",
+ ins->code);
+ }
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_format, 26.07)
+int
+rte_bpf_format(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
+ uint32_t pc, uint32_t flags)
+{
+ if ((flags & RTE_BPF_FORMAT_FLAG_HEXADECIMAL) != 0)
+ return format_hexadecimal(buffer, bufsz, ins, flags);
+ else
+ return format_disassembly(buffer, bufsz, ins, pc, flags);
+}
+
+RTE_EXPORT_SYMBOL(rte_bpf_dump)
+void rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len)
+{
+ uint32_t i;
+ char buffer[256];
+
+ for (i = 0; i < len; ++i) {
+ const struct ebpf_insn *ins = buf + i;
+
+ format_disassembly(buffer, sizeof(buffer), ins, i,
+ RTE_BPF_FORMAT_FLAG_DISASSEMBLY |
+ RTE_BPF_FORMAT_FLAG_ABSOLUTE_JUMPS);
+ fprintf(f, " L%u:\t%s\n", i, buffer);
+ i += rte_bpf_insn_is_wide(ins);
}
}
diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h
index 413ccf049755..b6c232704a56 100644
--- a/lib/bpf/rte_bpf.h
+++ b/lib/bpf/rte_bpf.h
@@ -30,6 +30,23 @@ extern "C" {
/** Mask with all supported `RTE_BPF_EXEC_FLAG_*` flags set. */
#define RTE_BPF_EXEC_FLAG_MASK RTE_BPF_EXEC_FLAG_JIT
+/* Format instructions as assembler. */
+#define RTE_BPF_FORMAT_FLAG_DISASSEMBLY 0
+/* Format instructions as hexadecimal. */
+#define RTE_BPF_FORMAT_FLAG_HEXADECIMAL RTE_BIT32(0)
+
+/* Only valid in disassembly mode. */
+/* Format jump offsets relative to the next instruction. */
+#define RTE_BPF_FORMAT_FLAG_RELATIVE_JUMPS 0
+/* Format jump targets relative to the start of the program. */
+#define RTE_BPF_FORMAT_FLAG_ABSOLUTE_JUMPS RTE_BIT32(1)
+
+/* Only valid in hexadecimal mode. */
+/* Format full hexadecimal representation of wide instructions. */
+#define RTE_BPF_FORMAT_FLAG_AUTO_WIDE 0
+/* Format as hexadecimal only first half of wide instructions. */
+#define RTE_BPF_FORMAT_FLAG_NEVER_WIDE RTE_BIT32(2)
+
/**
* Possible types for function/BPF program arguments.
*/
@@ -391,6 +408,40 @@ __rte_experimental
int
rte_bpf_get_jit_ex(const struct rte_bpf *bpf, struct rte_bpf_jit_ex *jit);
+/**
+ * Determine instruction width.
+ *
+ * @return
+ * True if ins points to a wide (128-bit) instruction.
+ */
+__rte_experimental
+bool
+rte_bpf_insn_is_wide(const struct ebpf_insn *ins);
+
+/**
+ * Print eBPF instruction into a buffer.
+ *
+ * Semantics of handling buffer size repeats those of snprintf.
+ *
+ * @param buffer
+ * Output buffer (may be NULL if bufsz is zero).
+ * @param bufsz
+ * Output buffer size.
+ * @param ins
+ * Narrow or wide (depending on opcode) eBPF instruction. That is, when
+ * `rte_bpf_insn_is_wide` is true `ins[1]` is also accessed.
+ * @param pc
+ * Current instruction number for displaying absolute jump targets.
+ * @param flags
+ * Bitwise-OR combination of `RTE_BPF_FORMAT_FLAG_*` values.
+ * @return
+ * Number of characters to be written excluding terminating zero.
+ */
+__rte_experimental
+int
+rte_bpf_format(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
+ uint32_t pc, uint32_t flags);
+
/**
* Dump epf instructions to a file.
*
--
2.43.0
^ permalink raw reply related
* [PATCH v4 00/24] bpf: test and fix issues in verifier
From: Marat Khalili @ 2026-06-23 14:31 UTC (permalink / raw)
Cc: dev
In-Reply-To: <20260612104743.6465-1-marat.khalili@huawei.com>
This patchset addresses numerous bugs in the BPF verifier's abstract
interpretation logic and introduces a new validation debugger API to
enable precise, robust testing of the verifier itself.
While the existing DPDK eBPF verifier is capable of checking basic
execution graph loops and dead code, the mathematical tracking of
register bounds (both signed and unsigned) contained flaws resulting in
false positives and false negatives, undefined behavior, and hardware
exceptions such as SIGFPE during validation.
To resolve these issues and ensure they do not regress, this patchset
first introduces the "Validation Debugger API"
(`rte_bpf_validate_debug_*`). This gdb-like interface allows setting
breakpoints and catchpoints during the validation process to inspect the
verifier's internal state.
Using this new API, a comprehensive test harness
(`app/test/test_bpf_validate.c`) was created to formally check the
abstract domains of instructions across all their valid branches. The
remainder of the patchset incrementally fixes the math and bounds logic
for individual eBPF instructions, using the new tests to prove the
correctness of the fixes.
This debugger API also lays the foundation for an interactive eBPF
validation debugger to be introduced in the future.
Series-Depends-on: series-38434 ("bpf: introduce extensible load API")
v4:
No code changes (almost).
* Removed compilation error on presence of LIST_FOREACH_SAFE in Linux.
* Rebased on fresh main with prerequisite series merged.
* Moved new API documentation changes to the introducing commit.
* Made new release notes one section instead of two.
* Corrected documentation wording slightly to prevent confusion between
the debugging API and a debugger application (not part of this series).
v3:
* Rebased on v5 of the prerequisite series and updated Depends-on tags.
* Replaced a hardcoded compiler attribute with __rte_format_printf.
v2:
* Addressed AI reviewer comments:
* replaced `false` and `true` with 0 and 1 in some API descriptions
and invocations that multiplex boolean and negative error code;
* made some previously implicit casts explicit;
* moved new enum value to the end of the definition.
* Added Acked-by and Depends-on tags to all individual commits to
align with patchwork requirements.
* Added Reported-by tags to fixes of issues discovered by Claudia Cauli
using a formal methods framework.
Marat Khalili (24):
bpf: format and dump jlt, jle, jslt, and jsle
bpf: add format instruction function
bpf/validate: break on error in evaluate
bpf/validate: expand comments in evaluate cycle
bpf/validate: introduce debugging interface
bpf/validate: fix BPF_ADD of pointer to a scalar
bpf/validate: fix BPF_LDX | EBPF_DW signed range
test/bpf_validate: add setup and basic tests
test/bpf_validate: add harness for pointer tests
bpf/validate: fix EBPF_JSLT | BPF_X evaluation
bpf/validate: fix BPF_NEG of INT64_MIN and 0
bpf/validate: fix BPF_DIV and BPF_MOD signed part
bpf/validate: fix BPF_MUL ranges minimum typo
bpf/validate: fix BPF_MUL signed overflow UB
bpf/validate: fix BPF_JGT/EBPF_JSGT no-jump max
bpf/validate: fix BPF_JMP source range calculation
bpf/validate: fix BPF_JMP empty range handling
bpf/validate: fix BPF_AND min calculations
bpf/validate: fix BPF_LSH shift-out-of-bounds UB
bpf/validate: fix BPF_OR min calculations
bpf/validate: fix BPF_SUB signed max zero case
bpf/validate: fix BPF_XOR signed min calculation
bpf/validate: prevent overflow when building graph
doc: add release notes for BPF validation fixes
app/test/meson.build | 1 +
app/test/test_bpf.c | 99 ++
app/test/test_bpf_validate.c | 2271 ++++++++++++++++++++++++
doc/guides/prog_guide/bpf_lib.rst | 31 +
doc/guides/rel_notes/release_26_07.rst | 14 +-
lib/bpf/bpf_dump.c | 292 +--
lib/bpf/bpf_validate.c | 730 +++++++-
lib/bpf/bpf_validate.h | 60 +
lib/bpf/bpf_validate_debug.c | 659 +++++++
lib/bpf/bpf_validate_debug.h | 86 +
lib/bpf/bpf_value_set.c | 403 +++++
lib/bpf/bpf_value_set.h | 126 ++
lib/bpf/meson.build | 9 +-
lib/bpf/rte_bpf.h | 55 +
lib/bpf/rte_bpf_validate_debug.h | 377 ++++
15 files changed, 5015 insertions(+), 198 deletions(-)
create mode 100644 app/test/test_bpf_validate.c
create mode 100644 lib/bpf/bpf_validate.h
create mode 100644 lib/bpf/bpf_validate_debug.c
create mode 100644 lib/bpf/bpf_validate_debug.h
create mode 100644 lib/bpf/bpf_value_set.c
create mode 100644 lib/bpf/bpf_value_set.h
create mode 100644 lib/bpf/rte_bpf_validate_debug.h
--
2.43.0
^ permalink raw reply
* [PATCH 3/3] vhost: remove use of strncpy
From: Bruce Richardson @ 2026-06-23 14:19 UTC (permalink / raw)
To: dev
Cc: Bruce Richardson, stable, Maxime Coquelin, Chenbo Xia,
Yuanhan Liu, David Marchand, Stephen Hemminger
In-Reply-To: <20260623141930.704771-1-bruce.richardson@intel.com>
The strlcpy is preferred over use of strncpy, which removes the need to
try and explicitly null-terminate some string buffers. We can also
simplify some name length handling as a result of this, as we no longer
need to use strnlen to clamp the length before calling the set_ifname
function.
Fixes: a277c7159876 ("vhost: refactor code structure")
Fixes: 0adb8eccc6a6 ("vhost: add VDUSE device creation and destruction")
Fixes: c171a2d5ff17 ("vhost: use strlcpy instead of strncpy")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/vhost/socket.c | 4 +---
lib/vhost/vduse.c | 2 +-
lib/vhost/vhost.c | 12 +++---------
lib/vhost/vhost.h | 2 +-
4 files changed, 6 insertions(+), 14 deletions(-)
diff --git a/lib/vhost/socket.c b/lib/vhost/socket.c
index 70e582a18d..0943b3e9bb 100644
--- a/lib/vhost/socket.c
+++ b/lib/vhost/socket.c
@@ -207,7 +207,6 @@ static void
vhost_user_add_connection(int fd, struct vhost_user_socket *vsocket)
{
int vid;
- size_t size;
struct vhost_user_connection *conn;
int ret;
struct virtio_net *dev;
@@ -226,8 +225,7 @@ vhost_user_add_connection(int fd, struct vhost_user_socket *vsocket)
goto err;
}
- size = strnlen(vsocket->path, PATH_MAX);
- vhost_set_ifname(vid, vsocket->path, size);
+ vhost_set_ifname(vid, vsocket->path);
vhost_setup_virtio_net(vid, vsocket->use_builtin_virtio_net,
vsocket->net_compliant_ol_flags, vsocket->stats_enabled,
diff --git a/lib/vhost/vduse.c b/lib/vhost/vduse.c
index 0b5d158fee..f8a4a8edcb 100644
--- a/lib/vhost/vduse.c
+++ b/lib/vhost/vduse.c
@@ -796,7 +796,7 @@ vduse_device_create(const char *path, bool compliant_ol_flags, bool extbuf, bool
goto out_dev_destroy;
}
- strncpy(dev->ifname, path, IF_NAME_SZ - 1);
+ strlcpy(dev->ifname, path, sizeof(dev->ifname));
dev->vduse_ctrl_fd = control_fd;
dev->vduse_dev_fd = dev_fd;
diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c
index 7e68b2c3be..fde8acb00c 100644
--- a/lib/vhost/vhost.c
+++ b/lib/vhost/vhost.c
@@ -776,20 +776,15 @@ vhost_attach_vdpa_device(int vid, struct rte_vdpa_device *vdpa_dev)
}
void
-vhost_set_ifname(int vid, const char *if_name, unsigned int if_len)
+vhost_set_ifname(int vid, const char *if_name)
{
struct virtio_net *dev;
- unsigned int len;
dev = get_device(vid);
if (dev == NULL)
return;
- len = if_len > sizeof(dev->ifname) ?
- sizeof(dev->ifname) : if_len;
-
- strncpy(dev->ifname, if_name, len);
- dev->ifname[sizeof(dev->ifname) - 1] = '\0';
+ strlcpy(dev->ifname, if_name, sizeof(dev->ifname));
}
void
@@ -915,8 +910,7 @@ rte_vhost_get_ifname(int vid, char *buf, size_t len)
len = RTE_MIN(len, sizeof(dev->ifname));
- strncpy(buf, dev->ifname, len);
- buf[len - 1] = '\0';
+ strlcpy(buf, dev->ifname, len);
return 0;
}
diff --git a/lib/vhost/vhost.h b/lib/vhost/vhost.h
index ee61f7415e..1c957d2929 100644
--- a/lib/vhost/vhost.h
+++ b/lib/vhost/vhost.h
@@ -877,7 +877,7 @@ int alloc_vring_queue(struct virtio_net *dev, uint32_t vring_idx);
void vhost_attach_vdpa_device(int vid, struct rte_vdpa_device *dev);
-void vhost_set_ifname(int, const char *if_name, unsigned int if_len);
+void vhost_set_ifname(int, const char *if_name);
void vhost_setup_virtio_net(int vid, bool enable, bool legacy_ol_flags, bool stats_enabled,
bool support_iommu);
void vhost_enable_extbuf(int vid);
--
2.53.0
^ permalink raw reply related
* [PATCH 2/3] eventdev: improve bounds checks for names in adapter create
From: Bruce Richardson @ 2026-06-23 14:19 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson, stable, Naga Harish K S V, Jerin Jacob,
Nikhil Rao
In-Reply-To: <20260623141930.704771-1-bruce.richardson@intel.com>
The bounds checks for snprintf and then strncpy used different constant
defines, which happened to resolve to the same value (32). Make this
code more resilient by using sizeof() operator rather than the defines,
and replace use of strncpy with the better strlcpy.
Fixes: a3bbf2e09756 ("eventdev: add eth Tx adapter implementation")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/eventdev/rte_event_eth_tx_adapter.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/eventdev/rte_event_eth_tx_adapter.c b/lib/eventdev/rte_event_eth_tx_adapter.c
index 91c7be55c7..d531da5d69 100644
--- a/lib/eventdev/rte_event_eth_tx_adapter.c
+++ b/lib/eventdev/rte_event_eth_tx_adapter.c
@@ -748,7 +748,7 @@ txa_service_adapter_create_ext(uint8_t id, struct rte_eventdev *dev,
return -EINVAL;
socket_id = dev->data->socket_id;
- snprintf(mem_name, TXA_MEM_NAME_LEN,
+ snprintf(mem_name, sizeof(mem_name),
"rte_event_eth_txa_%d",
id);
@@ -767,7 +767,7 @@ txa_service_adapter_create_ext(uint8_t id, struct rte_eventdev *dev,
txa->id = id;
txa->eventdev_id = dev->data->dev_id;
txa->socket_id = socket_id;
- strncpy(txa->mem_name, mem_name, TXA_SERVICE_NAME_LEN);
+ strlcpy(txa->mem_name, mem_name, sizeof(txa->mem_name));
txa->conf_cb = conf_cb;
txa->conf_arg = conf_arg;
txa->service_id = TXA_INVALID_SERVICE_ID;
--
2.53.0
^ permalink raw reply related
* [PATCH 1/3] ethdev: remove use of strncpy
From: Bruce Richardson @ 2026-06-23 14:19 UTC (permalink / raw)
To: dev
Cc: Bruce Richardson, stable, Thomas Monjalon, Andrew Rybchenko,
Harman Kalra, Ferruh Yigit
In-Reply-To: <20260623141930.704771-1-bruce.richardson@intel.com>
The use of strncpy is not generally recommended, so replace it in code
tokenizing the representor list. Since its use in the function is not
involving null-terminated strings (we know that copied block will
not involve a null value in it), we can replace strncpy with memcpy
rather than a string function. This keeps the original intent of the
code.
For extra safety, also add in an explicit bounds check on the length
value before doing the memcpy.
Fixes: 9a9eb104edf6 ("ethdev: parse multiple representor devargs")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/ethdev/ethdev_driver.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/lib/ethdev/ethdev_driver.c b/lib/ethdev/ethdev_driver.c
index 70ddce5bfc..4043ce898f 100644
--- a/lib/ethdev/ethdev_driver.c
+++ b/lib/ethdev/ethdev_driver.c
@@ -583,10 +583,15 @@ eth_dev_tokenise_representor_list(char *p_val, struct rte_eth_devargs *eth_devar
return devargs;
}
+ /* len - 2 strips the outer '[' and ']'; guard against underflow and overflow */
+ if (len < 2 || (len - 2) >= BUFSIZ) {
+ RTE_ETHDEV_LOG_LINE(ERR, "Representor list too long or malformed: %s", p_val);
+ return -EINVAL;
+ }
memset(str, 0, BUFSIZ);
memset(da_val, 0, BUFSIZ);
/* Remove the exterior [] of the consolidated list */
- strncpy(str, &p_val[1], len - 2);
+ memcpy(str, &p_val[1], len - 2);
while (1) {
if (str[i] == '\0') {
if (da_val[0] != '\0') {
--
2.53.0
^ permalink raw reply related
* [PATCH 0/3] lib: remove use of strncpy
From: Bruce Richardson @ 2026-06-23 14:19 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
Taking a lead from the kernel, which has just finished a multi-year
effort to remove use of strncpy[1], rework DPDK to remove use of the
same function. This series removes all remaining uses of strncpy
in lib directory.
[1] https://www.phoronix.com/news/Linux-7.2-Drops-strncpy
Bruce Richardson (3):
ethdev: remove use of strncpy
eventdev: improve bounds checks for names in adapter create
vhost: remove use of strncpy
lib/ethdev/ethdev_driver.c | 7 ++++++-
lib/eventdev/rte_event_eth_tx_adapter.c | 4 ++--
lib/vhost/socket.c | 4 +---
lib/vhost/vduse.c | 2 +-
lib/vhost/vhost.c | 12 +++---------
lib/vhost/vhost.h | 2 +-
6 files changed, 14 insertions(+), 17 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH v5] graph: add optional profiling stats
From: saeed bishara @ 2026-06-23 14:10 UTC (permalink / raw)
To: Morten Brørup
Cc: Jerin Jacob, dev, Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram,
Zhirun Yan
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F6593A@smartserver.smartshare.dk>
> > also, instead of adding cacheline for this profiling data, can we
> > share with line 1 that used solely for xstats?
>
> This profiling data is 4 indexes * 2 values * 8-byte fields, so one cache line in itself.
make sense.
btw, the default value of RTE_GRAPH_BURST_SIZE is 256, I suspect that
real applications will enforce smaller burst when pulling from input
devices (e.g. 32). Do you expect such cases to change
RTE_GRAPH_BURST_SIZE?
^ permalink raw reply
* DPDK Release Status Meeting 2026-06-23
From: Mcnamara, John @ 2026-06-23 14:02 UTC (permalink / raw)
To: dev@dpdk.org; +Cc: Thomas Monjalon, David Marchand
[-- Attachment #1: Type: text/plain, Size: 2792 bytes --]
Release status meeting minutes 2026-06-23
=========================================
Agenda:
- Release Dates
- Subtrees
- Roadmaps
- LTS
- Defects
- Opens
Participants:
- ARM
- Broadcom
- Debian
- Intel
- Marvell
- Nvidia
- Red Hat
- Stephen Hemminger
Release Dates
-------------
The following are the proposed working dates for 27.03:
| Date | Milestone | Description |
|-----------------|------------------|---------------------------------|
| 30 April 2026 | RFC/v1 patches | Proposal deadline |
| 11 June 2026 | 26.07-rc1 | API freeze |
| 25 June 2026 | 26.07-rc2 | PMD features freeze |
| 02 July 2026 | 26.07-rc3 | Builtin apps features freeze |
| 9 July 2026 | 26.07-rc4 | Documentation ready |
| 16 July 2026 | 26.07.0 | Release |
See https://core.dpdk.org/roadmap/
Subtrees
--------
- next-net
- 76 patches ready for main.
- 26 patches in review/waiting.
- next-net-intel
- 6 patches merged, ready for main
- Small backlog.
- next-net-mlx
- Some patches getting ready for pull.
- next-broadcom
- Most patches merged.
- 1-2 in backlog.
- next-net-mvl
- PR sent.
- next-eventdev
- No change.
- next-baseband
- Nothing pending.
- next-virtio
- Nothing pending.
- next-crypto
- No update.
- next-dts
- No update.
- main
- BUS refactoring.
- DMA driver to review.
- Started pulling trees for RC2.
- RC2 targeting June 29 2026.
Other
-----
- None.
LTS
---
See also: https://core.dpdk.org/roadmap/#stable
LTS versions ongoing/released:
- 25.11.3 - In progress.
- 24.11.7 - In progress.
- 23.11.8 - In progress.
Older releases:
- 20.11.10 - Will only be updated with CVE and critical fixes.
- 19.11.14 - Will only be updated with CVE and critical fixes.
- Distros
- Debian 13 contains DPDK v24.11
- Ubuntu 25.04 contains DPDK v24.11
- Ubuntu 24.04 LTS contains DPDK v23.11
- RHEL 9 contains DPDK 24.11
Defects
-------
- Bugzilla links, 'Bugs', added for hosted projects
- https://www.dpdk.org/hosted-projects/
DPDK Release Status Meetings
----------------------------
The DPDK Release Status Meeting is intended for DPDK Committers to discuss the
status of the main tree and sub-trees, and for project managers to track
progress or milestone dates.
The meeting occurs on every Tuesday at 14:30 DST over Jitsi on https://meet.jit.si/DPDK
You don't need an invite to join the meeting but if you want a calendar reminder just
send an email to "John McNamara <john.mcnamara@intel.com>" for the invite.
[-- Attachment #2: Type: text/html, Size: 35226 bytes --]
^ permalink raw reply
* RE: [PATCH v3 6/6] test/bpf: check that bpf_convert can be JIT'd
From: Marat Khalili @ 2026-06-23 13:57 UTC (permalink / raw)
To: Stephen Hemminger, dev@dpdk.org; +Cc: Konstantin Ananyev
In-Reply-To: <20260621162524.82690-7-stephen@networkplumber.org>
Thank you for working on this, please see some comments inline.
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Sunday 21 June 2026 17:24
> To: dev@dpdk.org
> Cc: Stephen Hemminger <stephen@networkplumber.org>; Konstantin Ananyev <konstantin.ananyev@huawei.com>;
> Marat Khalili <marat.khalili@huawei.com>
> Subject: [PATCH v3 6/6] test/bpf: check that bpf_convert can be JIT'd
>
> Add followup in bpf conversion tests to make sure resulting
> code was also run through JIT and that JIT produces
> same results as non-JIT.
>
> Reduce log output to make it easier to match which
> expression might be causing issues.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> app/test/test_bpf.c | 94 +++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 79 insertions(+), 15 deletions(-)
>
> diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
> index 3a88434c3c..973dd7d659 100644
> --- a/app/test/test_bpf.c
> +++ b/app/test/test_bpf.c
> @@ -8,6 +8,7 @@
> #include <inttypes.h>
> #include <unistd.h>
>
> +#include <rte_byteorder.h>
> #include <rte_memory.h>
> #include <rte_debug.h>
> #include <rte_hexdump.h>
> @@ -32,6 +33,7 @@ test_bpf(void)
> #include <rte_bpf.h>
> #include <rte_ether.h>
> #include <rte_ip.h>
> +#include <rte_udp.h>
>
>
> /* Tests of most simple BPF programs (no instructions, one instruction etc.) */
> @@ -4529,6 +4531,7 @@ test_bpf_match(pcap_t *pcap, const char *str,
> int ret = -1;
> uint64_t rc;
>
> + printf("%s '%s'\n", __func__, str);
> if (pcap_compile(pcap, &fcode, str, 1, PCAP_NETMASK_UNKNOWN)) {
> printf("%s@%d: pcap_compile(\"%s\") failed: %s;\n",
> __func__, __LINE__, str, pcap_geterr(pcap));
> @@ -4550,6 +4553,24 @@ test_bpf_match(pcap_t *pcap, const char *str,
> }
>
> rc = rte_bpf_exec(bpf, mb);
> +#if defined(RTE_ARCH_X86_64) || defined(RTE_ARCH_ARM64)
We are going to have a few of these lines, I think something like
RTE_BPF_JIT_SUPPORTED is warranted.
> + {
> + struct rte_bpf_jit jit;
> +
> + rte_bpf_get_jit(bpf, &jit);
Out of abundance of caution I would also prefill jit with zeroes and check the
return code here.
> + if (jit.func == NULL) {
> + printf("%s@%d: no JIT generated\n", __func__, __LINE__);
> + goto error;
> + }
> +
> + fflush(stdout);
> + uint64_t rc_jit = jit.func(mb);
> + if (rc_jit != rc) {
> + printf("%s@%d: JIT return code does not match\n", __func__, __LINE__);
> + goto error;
> + }
> + }
> +#endif
> /* The return code from bpf capture filter is non-zero if matched */
> ret = (rc == 0);
> error:
> @@ -4560,23 +4581,16 @@ test_bpf_match(pcap_t *pcap, const char *str,
> return ret;
> }
>
> -/* Basic sanity test can we match a IP packet */
> -static int
> -test_bpf_filter_sanity(pcap_t *pcap)
> +/* Setup mbuf for filter test */
> +static void
> +dummy_ip_prep(void *data, uint16_t plen)
> {
> - const uint32_t plen = 100;
> - struct rte_mbuf mb, *m;
> - uint8_t tbuf[RTE_MBUF_DEFAULT_BUF_SIZE];
> struct {
> struct rte_ether_hdr eth_hdr;
> struct rte_ipv4_hdr ip_hdr;
> - } *hdr;
> + struct rte_udp_hdr udp_hdr;
> + } *hdr = data;
>
> - memset(&mb, 0, sizeof(mb));
> - dummy_mbuf_prep(&mb, tbuf, sizeof(tbuf), plen);
> - m = &mb;
> -
> - hdr = rte_pktmbuf_mtod(m, typeof(hdr));
> hdr->eth_hdr = (struct rte_ether_hdr) {
> .dst_addr.addr_bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
> .ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4),
> @@ -4589,13 +4603,32 @@ test_bpf_filter_sanity(pcap_t *pcap)
> .src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK),
> .dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST),
> };
> + hdr->udp_hdr = (struct rte_udp_hdr) {
> + .src_port = rte_rand_max(UINT16_MAX),
> + .dst_port = rte_cpu_to_be_16(9), /* discard port */
> + .dgram_len = rte_cpu_to_be_16(plen - sizeof(struct rte_ipv4_hdr)),
> + .dgram_cksum = 0,
> + };
> +}
> +
> +
> +/* Basic sanity test can we match a IP packet */
> +static int
> +test_bpf_filter_sanity(pcap_t *pcap)
> +{
> + struct rte_mbuf mb = { 0 };
> + uint8_t tbuf[RTE_MBUF_DEFAULT_BUF_SIZE];
> + const uint32_t plen = 100;
> +
> + dummy_mbuf_prep(&mb, tbuf, sizeof(tbuf), plen);
> + dummy_ip_prep(rte_pktmbuf_mtod(&mb, void *), plen);
>
> - if (test_bpf_match(pcap, "ip", m) != 0) {
> + if (test_bpf_match(pcap, "ip", &mb) != 0) {
> printf("%s@%d: filter \"ip\" doesn't match test data\n",
> __func__, __LINE__);
> return -1;
> }
> - if (test_bpf_match(pcap, "not ip", m) == 0) {
> + if (test_bpf_match(pcap, "not ip", &mb) == 0) {
Not a new bug, but this condition should be for non-positive, not just zero.
> printf("%s@%d: filter \"not ip\" does match test data\n",
> __func__, __LINE__);
> return -1;
> @@ -4648,10 +4681,15 @@ static const char * const sample_filters[] = {
> static int
> test_bpf_filter(pcap_t *pcap, const char *s)
> {
> + struct rte_mbuf mb = { 0 };
> + uint8_t tbuf[RTE_MBUF_DEFAULT_BUF_SIZE];
> + const uint32_t plen = 100;
> struct bpf_program fcode;
> struct rte_bpf_prm *prm = NULL;
> struct rte_bpf *bpf = NULL;
> + int ret = -1;
>
> + printf("%s '%s'\n", __func__, s);
> if (pcap_compile(pcap, &fcode, s, 1, PCAP_NETMASK_UNKNOWN)) {
> printf("%s@%d: pcap_compile('%s') failed: %s;\n",
> __func__, __LINE__, s, pcap_geterr(pcap));
> @@ -4665,8 +4703,10 @@ test_bpf_filter(pcap_t *pcap, const char *s)
> goto error;
> }
>
> +#ifdef DEBUG
> printf("bpf convert for \"%s\" produced:\n", s);
> rte_bpf_dump(stdout, prm->ins, prm->nb_ins);
> +#endif
>
> bpf = rte_bpf_load(prm);
> if (bpf == NULL) {
> @@ -4675,6 +4715,30 @@ test_bpf_filter(pcap_t *pcap, const char *s)
> goto error;
> }
>
> + dummy_mbuf_prep(&mb, tbuf, sizeof(tbuf), plen);
> + dummy_ip_prep(rte_pktmbuf_mtod(&mb, void *), plen);
> +
> + uint64_t rc = rte_bpf_exec(bpf, &mb);
Would it be hard to check the result against a known correct answer?
> +#if defined(RTE_ARCH_X86_64) || defined(RTE_ARCH_ARM64)
> + {
> + struct rte_bpf_jit jit;
> +
> + rte_bpf_get_jit(bpf, &jit);
Same suggestion regarding zeroing jit and check return code here.
> + if (jit.func == NULL) {
> + printf("%s@%d: no JIT generated\n", __func__, __LINE__);
> + goto error;
> + }
> +
> + fflush(stdout);
> + uint64_t rc_jit = jit.func(&mb);
> + if (rc_jit != rc) {
> + printf("%s@%d: JIT return code does not match\n", __func__, __LINE__);
> + goto error;
> + }
> + }
> +#endif
> + ret = 0;
> +
Are `test_bpf_filter` and `test_bpf_filter_sanity` substantially different any
more, or could they just be merged?
> error:
> if (bpf)
> rte_bpf_destroy(bpf);
> @@ -4685,7 +4749,7 @@ test_bpf_filter(pcap_t *pcap, const char *s)
>
> rte_free(prm);
> pcap_freecode(&fcode);
> - return (bpf == NULL) ? -1 : 0;
> + return ret;
> }
>
> static int
> --
> 2.53.0
^ permalink raw reply
* Re: [PATCH v4] pcapng: add user-supplied timestamp support
From: Stephen Hemminger @ 2026-06-23 13:53 UTC (permalink / raw)
To: Dawid Wesierski; +Cc: dev, mb, Marek Kasiewicz
In-Reply-To: <20260623141302.486601-1-dawid.wesierski@intel.com>
On Tue, 23 Jun 2026 10:10:11 -0400
Dawid Wesierski <dawid.wesierski@intel.com> wrote:
> +/*
> + * Compatibility wrapper: captures current TSC (converted at write time).
> + * Equivalent to rte_pcapng_copy_ts(..., 0).
> + */
> +RTE_EXPORT_SYMBOL(rte_pcapng_copy)
> +struct rte_mbuf *
> +rte_pcapng_copy(uint16_t port_id, uint32_t queue,
> + const struct rte_mbuf *md,
> + struct rte_mempool *mp,
> + uint32_t length,
> + enum rte_pcapng_direction direction,
> + const char *comment)
> +{
> + return rte_pcapng_copy_ts(port_id, queue, md, mp, length, direction,
> + comment, 0);
> +}
> +
> +/*
> + * Convert a TSC value to nanoseconds since the Unix epoch using the
> + * calibrated clock of the capture file. Uses the same pre-computed
> + * reciprocal multiplier as the internal write path (no integer division).
> + */
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_tsc_to_ns, 26.07)
> +uint64_t
> +rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc)
> +{
> + return tsc_to_ns_epoch(&self->clock, tsc);
> +}
Why not just use function versioning on rte_pcapng_copy() to add new parameter?
Also should add a coverage test app/test/test_pcapng.c
^ permalink raw reply
* Re: [PATCH 0/5] add versioned symbols for recently stabilized APIs
From: David Marchand @ 2026-06-23 13:50 UTC (permalink / raw)
To: Dariusz Sosnowski, Thomas Monjalon, dpdk-techboard
Cc: Bruce Richardson, Andrew Rybchenko, Viacheslav Ovsiienko,
Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad, dev
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
Hello Dariusz,
On Tue, 23 Jun 2026 at 13:38, Dariusz Sosnowski <dsosnowski@nvidia.com> wrote:
>
> Main goal of this patchset is to address https://bugs.dpdk.org/show_bug.cgi?id=1957
It is expected that experimental symbols may disappear overnight, and
this bug could also be closed as NOTABUG.
On the other hand, we do state in the doc that compatibility could be
provided when stabilising an experimental API, so ok.. let's try.
> but it also handles other recently stabilized symbols and has some minor fixes:
>
> - Patch 1 - Fix RTE_VERSION_EXPERIMENTAL_SYMBOL macro on clang.
Ouch... /me hides.
> - Patch 2 - Allow function versioning inside drivers.
> - Patch 3 - Version the function symbols stabilized in
> https://git.dpdk.org/dpdk/commit/?id=e8cab133645f5466ef75e511629add43b68a5027
> - Patch 4 - Introduce versioning macros for global variable symbols.
> - Patch 5 - Version the function and variable symbols stabilized in
> https://git.dpdk.org/dpdk/commit/?id=4ee2f5c1cedf9ee7f39afa667f71b07f4004ba5c
>
> Issue is still not fully fixed for stabilized global variables:
> rte_flow_dynf_metadata_offs and rte_flow_dynf_metadata_mask.
Well, symbol versioning is not something for variables.
Exposing global variables was a mistake from the start...
Those were exported for "performance" reasons as those are accessed
via inline helpers (but I am not sure there were benchmarks showing
the benefits).
I am for forbidding exports of global variables from now, unless some
really good performance benchmark is provided (@techboard for info).
Now, in practice for your issue, rather than reintroducing symbol
aliases (technical solution that I dropped when refactoring the
macros), I think we can do with some middle ground approach:
- leaving the inline helpers as "stable" (not __rte_experimental),
- restoring the EXPERIMENTAL version on the global variables, this
will restore the location of those symbols from the previous ABI pov,
and the checks won't catch this discrepancy anyway,
- during 26.11, drop the EXPERIMENTAL version on those variables,
In other words, stopping at your patch 3 of the series, then adding:
$ git diff
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index ec0fe08355..8bd21ccd31 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -23,11 +23,11 @@
#define FLOW_LOG RTE_ETHDEV_LOG_LINE
/* Mbuf dynamic field name for metadata. */
-RTE_EXPORT_SYMBOL(rte_flow_dynf_metadata_offs)
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_flow_dynf_metadata_offs, 19.11)
int32_t rte_flow_dynf_metadata_offs = -1;
/* Mbuf dynamic field flag bit number for metadata. */
-RTE_EXPORT_SYMBOL(rte_flow_dynf_metadata_mask)
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_flow_dynf_metadata_mask, 19.11)
uint64_t rte_flow_dynf_metadata_mask;
/**
> Patch 4 and 5 address the bug for these global variables,
> by providing a single storage for both EXPERIMENTAL and
> DPDK_26 variable symbol versions.
> This is achieved through symbol aliasing.
> But this solution is limited only to executables compiled with clang.
>
> clang and gcc have a different default behavior regarding relocations
> of global variables exposed by shared libraries.
>
Yeah... not even thinking about adding MSVC in the list...
--
David Marchand
^ permalink raw reply related
* Re: [PATCH 1/5] eal: fix macro for versioned experimental symbol
From: Stephen Hemminger @ 2026-06-23 13:50 UTC (permalink / raw)
To: Dariusz Sosnowski; +Cc: David Marchand, dev, Bruce Richardson
In-Reply-To: <20260623113752.1100072-2-dsosnowski@nvidia.com>
On Tue, 23 Jun 2026 13:37:47 +0200
Dariusz Sosnowski <dsosnowski@nvidia.com> wrote:
> Add a missing semicolon after __asm__ block in
> RTE_VERSION_EXPERIMENTAL_SYMBOL macro.
> It's lack triggers the following compilation error with clang:
>
> ../lib/ethdev/rte_flow.c:320:1: error: expected ';' after top-level asm block
> 320 | RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_flow_dynf_metadata_register, (void))
> | ^
> ../lib/eal/common/eal_export.h:75:74: note: expanded from macro 'RTE_VERSION_EXPERIMENTAL_SYMBOL'
> 75 | __asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
> | ^
> ../lib/eal/include/rte_common.h:237:20: note: expanded from macro '\
> __rte_used'
> 237 | #define __rte_used __attribute__((used))
> | ^
>
> Fixes: e30e194c4d06 ("eal: rework function versioning macros")
> Cc: david.marchand@redhat.com
>
> Signed-
I didn't see this because clang doesn't have symver support.
Which version of clang is this?
^ permalink raw reply
* Re: [PATCH 0/5] add versioned symbols for recently stabilized APIs
From: Stephen Hemminger @ 2026-06-23 13:48 UTC (permalink / raw)
To: Dariusz Sosnowski
Cc: Thomas Monjalon, David Marchand, Bruce Richardson,
Andrew Rybchenko, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
Suanming Mou, Matan Azrad, dev
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
On Tue, 23 Jun 2026 13:37:46 +0200
Dariusz Sosnowski <dsosnowski@nvidia.com> wrote:
> Main goal of this patchset is to address https://bugs.dpdk.org/show_bug.cgi?id=1957
> but it also handles other recently stabilized symbols and has some minor fixes:
>
> - Patch 1 - Fix RTE_VERSION_EXPERIMENTAL_SYMBOL macro on clang.
> - Patch 2 - Allow function versioning inside drivers.
> - Patch 3 - Version the function symbols stabilized in
> https://git.dpdk.org/dpdk/commit/?id=e8cab133645f5466ef75e511629add43b68a5027
> - Patch 4 - Introduce versioning macros for global variable symbols.
> - Patch 5 - Version the function and variable symbols stabilized in
> https://git.dpdk.org/dpdk/commit/?id=4ee2f5c1cedf9ee7f39afa667f71b07f4004ba5c
>
> Issue is still not fully fixed for stabilized global variables:
> rte_flow_dynf_metadata_offs and rte_flow_dynf_metadata_mask.
> Patch 4 and 5 address the bug for these global variables,
> by providing a single storage for both EXPERIMENTAL and
> DPDK_26 variable symbol versions.
> This is achieved through symbol aliasing.
> But this solution is limited only to executables compiled with clang.
>
> clang and gcc have a different default behavior regarding relocations
> of global variables exposed by shared libraries.
>
> With clang, R_X86_64_GLOB_DAT relocations are generated for executables:
>
> $ readelf -sW build-26.07/lib/librte_ethdev.so | grep rte_flow_dynf_metadata_offs
> 113: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
> 116: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
> 970: 00000000000ea4c0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_impl
> 1212: 00000000000ea4c0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_v26
> 1325: 00000000000ea4c0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_exp
> 1415: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
> 1705: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
>
> $ readelf -rW build-26.07/drivers/librte_net_mlx5.so | grep rte_flow_dynf_metadata_offs
> 0000000003ed5f18 0000001600000006 R_X86_64_GLOB_DAT 0000000000000000 rte_flow_dynf_metadata_offs@DPDK_26 + 0
>
> $ readelf -rW build-25.11/app/dpdk-testpmd | grep rte_flow_dynf_metadata_offs
> --> 000000000028ef70 0000011300000006 R_X86_64_GLOB_DAT 0000000000000000 rte_flow_dynf_metadata_offs@EXPERIMENTAL + 0
>
> With gcc, R_X86_64_COPY relocations are generated:
>
> $ readelf -sW build-26.07/lib/librte_ethdev.so | grep rte_flow_dynf_metadata_offs
> 113: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
> 116: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
> 1471: 00000000000e74e0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_impl
> 2134: 00000000000e74e0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_v26
> 2247: 00000000000e74e0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_exp
> 2337: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
> 2627: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
>
> $ readelf -rW build-26.07/drivers/librte_net_mlx5.so | grep rte_flow_dynf_metadata_offs
> 00000000046dbef0 0000001600000006 R_X86_64_GLOB_DAT 0000000000000000 rte_flow_dynf_metadata_offs@DPDK_26 + 0
>
> $ readelf -rW build-25.11/app/dpdk-testpmd | grep rte_flow_dynf_metadata_offs
> --> 000000000029b540 000001d200000005 R_X86_64_COPY 000000000029b540 rte_flow_dynf_metadata_offs@EXPERIMENTAL + 0
>
> With copy relocations (testpmd linked through gcc) the following happens:
>
> - When variable symbol (with EXPERIMENTAL version) gets resolved inside executable,
> global variable gets copied from read-only data to executable's BSS section.
> Executable will access this variable through BSS.
> - When variable symbol (with DPDK_26 version) gets resolved inside a library,
> global variable is accessed indirectly through GOT.
> It is stored inside BSS section of the shared library.
>
> So executable and libraries refer to different storage,
> eventually leading to inconsistent runtime behavior.
> Problems only appears when executable and library require
> different versions of global variable symbol.
> If testpmd from 26.07 is used with libraries from 26.07,
> GOT entry for these variables will point to copied variable.
>
> Without copy relocations (testpmd linked through clang) both
> executable and libraries access the global variable indirectly through GOT.
> Runtime behavior is consistent, regardless of the mix of variable symbol versions.
>
> The only other solution I could find was to use dlsym() inside libraries
> to dynamically resolve the location rte_flow_dynf_metadata_offs and rte_flow_dynf_metadata_mask,
> but this solution sounds like an overkill.
> Essentially this would require moving to getter/setter functions for these variables
> inside the library.
>
> I would appreciate any feedback or suggestions if anybody had encountered a similar issue before.
>
> Dariusz Sosnowski (5):
> eal: fix macro for versioned experimental symbol
> drivers: support function versioning
> net/mlx5: fix stabilized function versions
> eal: support aliases for versioned variable symbols
> ethdev: fix promoted flow metadata symbols
>
> buildtools/gen-version-map.py | 11 ++++++++++
> drivers/meson.build | 8 +++++++
> drivers/net/mlx5/meson.build | 2 ++
> drivers/net/mlx5/mlx5_driver_event.c | 22 ++++++++++++++-----
> drivers/net/mlx5/mlx5_flow.c | 18 ++++++++++-----
> lib/eal/common/eal_export.h | 24 +++++++++++++++++++-
> lib/ethdev/meson.build | 2 ++
> lib/ethdev/rte_flow.c | 33 ++++++++++++++++++----------
> 8 files changed, 96 insertions(+), 24 deletions(-)
>
> --
> 2.47.3
>
The bugfix is good, but not sure the rest is needed right now.
It is getting late to add more stuff for 26.07 and in 26.11 function versioning
will not be needed.
^ permalink raw reply
* [PATCH v4] pcapng: add user-supplied timestamp support
From: Dawid Wesierski @ 2026-06-23 14:10 UTC (permalink / raw)
To: dev; +Cc: dawid.wesierski, stephen, mb, Marek Kasiewicz
In-Reply-To: <20260618143819.310046-1-dawid.wesierski@intel.com>
From: "Wesierski, Dawid" <dawid.wesierski@intel.com>
Introduce rte_pcapng_copy_ts() alongside the existing rte_pcapng_copy()
so that callers with a hardware PTP or pre-captured timestamp can inject
an exact epoch-ns value directly into the packet record.
Timestamp handling in rte_pcapng_copy_ts():
- ts != 0: caller-supplied nanoseconds since the Unix epoch, stored as-is.
- ts == 0: TSC captured at copy time with bit 63 set as a sentinel.
rte_pcapng_write_packets() detects the sentinel and converts the TSC to
epoch ns using the file's calibrated clock. The TSC will not reach
bit 63 for centuries, and epoch-ns values stay below bit 63 until 2554,
so the bit is safe to use as a disambiguation flag.
rte_pcapng_copy() is retained as a real exported function (not an inline
wrapper) so the stable ABI symbol is preserved. It simply calls
rte_pcapng_copy_ts(..., 0) to capture the current TSC.
rte_pcapng_tsc_to_ns() is added as a new experimental helper (addressing
review requests from Stephen Hemminger and Morten Brørup). It exposes the
same calibrated, drift-compensated, divide-free TSC-to-epoch-ns conversion
used internally by rte_pcapng_write_packets(), allowing callers to convert
a TSC captured at packet arrival time before passing it to
rte_pcapng_copy_ts().
Signed-off-by: Marek Kasiewicz <marek.kasiewicz@intel.com>
Signed-off-by: Dawid Wesierski <dawid.wesierski@intel.com>
---
Hi Stephen, Morten,
Thank you very much for your review and comments.
I have prepared a v4 patch.
ABI failure > I have restored rte_pcapng_copy() as a real exported function instead of a static inline wrapper.
This should fix the iol-abi-testing failure. It now simply calls rte_pcapng_copy_ts(..., 0) internally.
As suggested, I've added a new experimental function uint64_t rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc);
I exposed the internal calibrated clock state maintained by the pcapng.
Regards,
Dawid Węsierski.
.mailmap | 2 ++
lib/pcapng/rte_pcapng.c | 71 +++++++++++++++++++++++++++++++++--------
lib/pcapng/rte_pcapng.h | 64 +++++++++++++++++++++++++++++++++++++
3 files changed, 124 insertions(+), 13 deletions(-)
diff --git a/.mailmap b/.mailmap
index 4001e5fb0e..a7d97a631e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -366,6 +366,7 @@ David Zeng <zengxhsh@cn.ibm.com>
Davide Caratti <dcaratti@redhat.com>
Dawid Gorecki <dgr@semihalf.com>
Dawid Jurczak <dawid_jurek@vp.pl>
+Dawid Wesierski <dawid.wesierski@intel.com> Wesierski, Dawid <dawid.wesierski@intel.com>
Dawid Zielinski <dawid.zielinski@intel.com>
Dawid Łukwiński <dawid.lukwinski@intel.com>
Daxue Gao <daxuex.gao@intel.com>
@@ -1014,6 +1015,7 @@ Marcin Wilk <marcin.wilk@caviumnetworks.com>
Marcin Wojtas <mw@semihalf.com>
Marcin Zapolski <marcinx.a.zapolski@intel.com>
Marco Varlese <mvarlese@suse.de>
+Marek Kasiewicz <marek.kasiewicz@intel.com>
Marek Mical <marekx.mical@intel.com>
Marek Zalfresso-jundzillo <marekx.zalfresso-jundzillo@intel.com>
Maria Lingemark <maria.lingemark@ericsson.com>
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index b5d1026891..f583fae995 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -546,14 +546,14 @@ pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
*/
/* Make a copy of original mbuf with pcapng header and options */
-RTE_EXPORT_SYMBOL(rte_pcapng_copy)
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_copy_ts, 26.07)
struct rte_mbuf *
-rte_pcapng_copy(uint16_t port_id, uint32_t queue,
+rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
const struct rte_mbuf *md,
struct rte_mempool *mp,
uint32_t length,
enum rte_pcapng_direction direction,
- const char *comment)
+ const char *comment, uint64_t ts)
{
struct pcapng_enhance_packet_block *epb;
uint32_t orig_len, pkt_len, padding, flags;
@@ -690,8 +690,20 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
/* Interface index is filled in later during write */
mc->port = port_id;
- /* Put timestamp in cycles here - adjust in packet write */
- timestamp = rte_get_tsc_cycles();
+ /*
+ * Timestamp handling:
+ * - If the caller supplied an explicit timestamp (ts != 0), it is
+ * already in nanoseconds since the Unix epoch, so store it as-is.
+ * - If the caller did not (ts == 0), store the current TSC and set
+ * the high bit as a sentinel so rte_pcapng_write_packets() knows
+ * it must convert TSC -> epoch ns at write time. The TSC counter
+ * will not reach bit 63 for centuries, and epoch-ns values stay
+ * below bit 63 until the year 2554, so the bit is safe to use.
+ */
+ if (ts != 0)
+ timestamp = ts;
+ else
+ timestamp = rte_get_tsc_cycles() | (UINT64_C(1) << 63);
epb->timestamp_hi = timestamp >> 32;
epb->timestamp_lo = (uint32_t)timestamp;
epb->capture_length = pkt_len;
@@ -707,6 +719,35 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
return NULL;
}
+/*
+ * Compatibility wrapper: captures current TSC (converted at write time).
+ * Equivalent to rte_pcapng_copy_ts(..., 0).
+ */
+RTE_EXPORT_SYMBOL(rte_pcapng_copy)
+struct rte_mbuf *
+rte_pcapng_copy(uint16_t port_id, uint32_t queue,
+ const struct rte_mbuf *md,
+ struct rte_mempool *mp,
+ uint32_t length,
+ enum rte_pcapng_direction direction,
+ const char *comment)
+{
+ return rte_pcapng_copy_ts(port_id, queue, md, mp, length, direction,
+ comment, 0);
+}
+
+/*
+ * Convert a TSC value to nanoseconds since the Unix epoch using the
+ * calibrated clock of the capture file. Uses the same pre-computed
+ * reciprocal multiplier as the internal write path (no integer division).
+ */
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_tsc_to_ns, 26.07)
+uint64_t
+rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc)
+{
+ return tsc_to_ns_epoch(&self->clock, tsc);
+}
+
/* Write pre-formatted packets to file. */
RTE_EXPORT_SYMBOL(rte_pcapng_write_packets)
ssize_t
@@ -720,7 +761,7 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
for (i = 0; i < nb_pkts; i++) {
struct rte_mbuf *m = pkts[i];
struct pcapng_enhance_packet_block *epb;
- uint64_t cycles, timestamp;
+ uint64_t timestamp;
/* sanity check that is really a pcapng mbuf */
epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
@@ -738,14 +779,18 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
}
/*
- * When data is captured by pcapng_copy the current TSC is stored.
- * Adjust the value recorded in file to PCAP epoch units.
+ * If rte_pcapng_copy[_ts]() stored a TSC value (high bit set
+ * as sentinel), convert it to nanoseconds since the Unix epoch
+ * using the per-file clock. Otherwise the timestamp is already
+ * in epoch ns and is written unchanged.
*/
- cycles = (uint64_t)epb->timestamp_hi << 32;
- cycles += epb->timestamp_lo;
- timestamp = tsc_to_ns_epoch(&self->clock, cycles);
- epb->timestamp_hi = timestamp >> 32;
- epb->timestamp_lo = (uint32_t)timestamp;
+ timestamp = ((uint64_t)epb->timestamp_hi << 32) | epb->timestamp_lo;
+ if (timestamp & (UINT64_C(1) << 63)) {
+ timestamp &= ~(UINT64_C(1) << 63);
+ timestamp = tsc_to_ns_epoch(&self->clock, timestamp);
+ epb->timestamp_hi = timestamp >> 32;
+ epb->timestamp_lo = (uint32_t)timestamp;
+ }
/*
* Handle case of highly fragmented and large burst size
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index d8d328f710..6eeaeada05 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -108,9 +108,50 @@ enum rte_pcapng_direction {
RTE_PCAPNG_DIRECTION_OUT = 2,
};
+/**
+ * Format an mbuf with a caller-supplied timestamp for writing to file.
+ *
+ * @param port_id
+ * The Ethernet port on which packet was received
+ * or is going to be transmitted.
+ * @param queue
+ * The queue on the Ethernet port where packet was received
+ * or is going to be transmitted.
+ * @param mp
+ * The mempool from which the "clone" mbufs are allocated.
+ * @param m
+ * The mbuf to copy
+ * @param length
+ * The upper limit on bytes to copy. Passing UINT32_MAX
+ * means all data (after offset).
+ * @param direction
+ * The direction of the packer: receive, transmit or unknown.
+ * @param comment
+ * Optional per packet comment.
+ * Truncated to UINT16_MAX characters.
+ * @param ts
+ * Packet timestamp in nanoseconds since the Unix epoch. If zero, the
+ * current TSC is captured and converted to epoch ns by
+ * rte_pcapng_write_packets() when the packet is written.
+ *
+ * @return
+ * - The pointer to the new mbuf formatted for pcapng_write
+ * - NULL on error such as invalid port or out of memory.
+ */
+__rte_experimental
+struct rte_mbuf *
+rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
+ const struct rte_mbuf *m, struct rte_mempool *mp,
+ uint32_t length,
+ enum rte_pcapng_direction direction, const char *comment,
+ uint64_t ts);
+
/**
* Format an mbuf for writing to file.
*
+ * Equivalent to rte_pcapng_copy_ts() with ts=0: the current TSC is
+ * captured at copy time and converted to epoch ns at write time.
+ *
* @param port_id
* The Ethernet port on which packet was received
* or is going to be transmitted.
@@ -153,6 +194,29 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
uint32_t
rte_pcapng_mbuf_size(uint32_t length);
+/**
+ * Convert a TSC value to nanoseconds since the Unix epoch.
+ *
+ * Uses the same calibrated clock reference as the capture file so that
+ * the result is consistent with timestamps written by
+ * rte_pcapng_write_packets(). The conversion is drift-compensated and
+ * uses a pre-computed reciprocal multiplier (no integer division).
+ *
+ * Typical use: convert a TSC timestamp captured close to packet arrival
+ * (e.g., from a PMD or hardware register) to an epoch-ns value before
+ * passing it to rte_pcapng_copy_ts().
+ *
+ * @param self
+ * The handle to the packet capture file.
+ * @param tsc
+ * TSC value to convert.
+ * @return
+ * Nanoseconds since the Unix epoch corresponding to @p tsc.
+ */
+__rte_experimental
+uint64_t
+rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc);
+
/**
* Write packets to the capture file.
*
--
2.47.3
---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.
Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.
^ permalink raw reply related
* Re: [PATCH] common/cnxk: fix inline dev null dereference
From: Jerin Jacob @ 2026-06-23 13:35 UTC (permalink / raw)
To: Aarnav JP
Cc: dev, Nithin Dabilpuram, Kiran Kumar K, Sunil Kumar Kori,
Satha Rao, Harman Kalra, Rakesh Kudurumalla, jerinj, rbhansali,
stable
In-Reply-To: <20260623085433.3190541-1-ajp@marvell.com>
On Tue, Jun 23, 2026 at 2:31 PM Aarnav JP <ajp@marvell.com> wrote:
>
> inl_dev is initialized to NULL and only assigned within the
> if (idev && idev->nix_inl_dev) block.
> Move inl_dev->res_addr_offset and inl_dev->cpt_cq_ena
> accesses inside this null-guarded block in
> nix_inl_inb_ipsec_sa_tbl_setup() and nix_inl_reass_inb_sa_tbl_setup()
> to avoid dereferencing a null pointer.
>
> Fixes: 3fdf3e53f3c4 ("common/cnxk: enable CPT CQ for inline IPsec inbound")
> Cc: stable@dpdk.org
>
> Signed-off-by: Aarnav JP <ajp@marvell.com>
Applied to dpdk-next-net-mrvl/for-main. Thanks
^ permalink raw reply
* Re: [PATCH] net/mlx5: fix double free in vectorized Rx recovery
From: Dariusz Sosnowski @ 2026-06-23 12:50 UTC (permalink / raw)
To: Borys Tsyrulnikov
Cc: Thomas Monjalon, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
Suanming Mou, Matan Azrad, Alexander Kozyrev, dev, stable
In-Reply-To: <20260617134301.798213-1-tsyrulnikov.borys@gmail.com>
On Wed, Jun 17, 2026 at 04:43:01PM +0300, Borys Tsyrulnikov wrote:
> During Rx queue error recovery, the vectorized path in
> mlx5_rx_err_handle() reallocates an mbuf for every queue element. When
> rte_mbuf_raw_alloc() fails (for example, the mempool is exhausted), the
> rollback loop frees the mbufs allocated so far, but masks the element
> ring index with "& elts_n" instead of "& (elts_n - 1)".
>
> elts_n is a power-of-two element count, so "x & elts_n" isolates a
> single bit and can only evaluate to 0 or elts_n, regardless of the loop
> counter. The rollback therefore never frees the mbufs just allocated in
> this pass (they are leaked); instead it repeatedly frees elts[0], a live
> mbuf still posted to the NIC (use-after-free / double free), and
> elts[elts_n], the fake_mbuf padding entry used by the vector datapath.
>
> Mask with the existing e_mask (elts_n - 1), as already done in the
> matching forward allocation loop just above.
>
> Fixes: 0f20acbf5eda ("net/mlx5: implement vectorized MPRQ burst")
> Cc: stable@dpdk.org
>
> Signed-off-by: Borys Tsyrulnikov <tsyrulnikov.borys@gmail.com>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
^ permalink raw reply
* Re: [PATCH v3 05/25] bpf/validate: introduce debugging interface
From: Thomas Monjalon @ 2026-06-23 12:29 UTC (permalink / raw)
To: Marat Khalili; +Cc: Konstantin Ananyev, dev@dpdk.org
In-Reply-To: <84ce7f7669404239864c61819267d9b6@huawei.com>
23/06/2026 12:29, Marat Khalili:
> > -----Original Message-----
> > From: Thomas Monjalon <thomas@monjalon.net>
> > Sent: Tuesday 23 June 2026 11:19
> > To: Marat Khalili <marat.khalili@huawei.com>
> > Cc: Konstantin Ananyev <konstantin.ananyev@huawei.com>; dev@dpdk.org
> > Subject: Re: [PATCH v3 05/25] bpf/validate: introduce debugging interface
> >
> > 12/06/2026 12:47, Marat Khalili:
> > > +#ifndef LIST_FOREACH_SAFE
> > > +/* We need this macro which neither Linux nor EAL for Linux include yet. */
> > > +#define LIST_FOREACH_SAFE(var, head, field, tvar) \
> > > + for ((var) = LIST_FIRST((head)); \
> > > + (var) && ((tvar) = LIST_NEXT((var), field), 1); \
> > > + (var) = (tvar))
> > > +#else
> > > +#ifdef RTE_EXEC_ENV_LINUX
> > > +#error "Don't need LIST_FOREACH_SAFE in this version of DPDK anymore, remove it."
> > > +#endif
> > > +#endif
> >
> > It fails on Alpine Linux.
> > Why adding this #error?
> >
>
> This is interesting. My mental model was that Linux is never going to have
> LIST_FOREACH_SAFE, but DPDK will eventually gain its own polyfill. I was
> actually expecting it to happen before my patch is published, so this was a
> reminder to remove my own definition since it clearly belongs to some common
> library. Turns out I was wrong on both accounts: there are Linuxes that define
> LIST_FOREACH_SAFE, and I managed to submit faster. Apart from these
> organizational issues the whole else branch can be safely removed. Do you want
> me to submit an updated version?
Yes would be nice so we will have a full CI run on it
now that the dependency is merged in main.
^ permalink raw reply
* RE: [PATCH v5] graph: add optional profiling stats
From: Morten Brørup @ 2026-06-23 12:04 UTC (permalink / raw)
To: saeed bishara
Cc: Jerin Jacob, dev, Jerin Jacob, Kiran Kumar K, Nithin Dabilpuram,
Zhirun Yan
In-Reply-To: <CAHfVqdWKoDqb0uD_HrF8e=GqadThPhZj0vZnRYDW=KMPei0mXQ@mail.gmail.com>
> From: saeed bishara [mailto:saeed.bishara.os@gmail.com]
> Sent: Tuesday, 23 June 2026 10.34
>
> > > > > + /** Fast path area cache line 3. */
> > > > > +#ifdef RTE_GRAPH_PROFILE
> > > > > + struct {
> > > > > + uint64_t calls; /**< Calls
> processing
> > > > resp. 0 or 1 objects. */
> > > > > + uint64_t cycles; /**< Cycles spent
> > > > processing resp. 0 or 1 objects. */
> > > > > + } usage_stats[2]; /**< Usage when this
> node
> > > > processed 0 or 1 objects. */
> > > > > + uint64_t full_burst_calls; /**< Calls
> processing a
> > > > full burst of objects. */
> > > > > + uint64_t full_burst_cycles; /**< Cycles spent
> > > > processing a full burst of objects. */
> > > > > + uint64_t half_burst_calls; /**< Calls
> processing a
> > > > half burst of objects. */
> > > > > + uint64_t half_burst_cycles; /**< Cycles spent
> > > > processing a half burst of objects. */
> > > > > + /** Fast path area cache line 4. */
> > > > > +#endif
> > > >
> > > > Is it an ABI breakage?
> Can you consider one array for all cases?
Ack.
> also, instead of adding cacheline for this profiling data, can we
> share with line 1 that used solely for xstats?
This profiling data is 4 indexes * 2 values * 8-byte fields, so one cache line in itself.
^ permalink raw reply
* Re: [PATCH v1 0/5] prefix lcore role enum values
From: lihuisong (C) @ 2026-06-23 11:52 UTC (permalink / raw)
To: David Marchand
Cc: Stephen Hemminger, Morten Brørup, thomas, andrew.rybchenko,
dev, zhanjie9
In-Reply-To: <CAJFAV8yNsZ_SLcG-ukzmDTQXRXDsGVtf-9szwSc6T2GM+fhE_Q@mail.gmail.com>
On 6/22/2026 4:18 PM, David Marchand wrote:
> Hello all,
>
> On Mon, 22 Jun 2026 at 03:23, lihuisong (C) <lihuisong@huawei.com> wrote:
>> On 6/19/2026 10:03 AM, Stephen Hemminger wrote:
>>> On Wed, 17 Jun 2026 13:48:37 +0200
>>> Morten Brørup <mb@smartsharesystems.com> wrote:
>>>
>>>>> From: Huisong Li [mailto:lihuisong@huawei.com]
>>>>> Sent: Wednesday, 17 June 2026 12.28
>>>>>
>>>>> Add the RTE_LCORE_ prefix to the lcore role enum values in
>>>>> rte_lcore_role_t
>>>>> to follow DPDK naming conventions.
>>>>>
>>>>> - ROLE_RTE -> RTE_LCORE_ROLE_RTE
>>>>> - ROLE_OFF -> RTE_LCORE_ROLE_OFF
>>>>> - ROLE_SERVICE -> RTE_LCORE_ROLE_SERVICE
>>>>> - ROLE_NON_EAL -> RTE_LCORE_ROLE_NON_EAL
>>>>>
>>>>> Old names are kept as macros aliasing to the new names to preserve
>>>>> backward compatibility.
>>>>>
>>>> Series-Acked-by: Morten Brørup <mb@smartsharesystems.com>
>>>>
>>> The problem with this patch it causes build failures now with abi diff.
>>>
>>> Example build log...
>>>
>>>
>>> 2 functions with some indirect sub-type change:
>>>
>>>
>>>
>>>
>>>
>>> [C] 'function rte_lcore_role_t rte_eal_lcore_role(unsigned int)' at eal_common_lcore.c:74:1 has some indirect sub-type changes:
>>>
>>> return type changed:
>>>
>>> type size hasn't changed
>>>
>>> 4 enumerator deletions:
>>>
>>> 'rte_lcore_role_t::ROLE_RTE' value '0'
>>>
>>> 'rte_lcore_role_t::ROLE_OFF' value '1'
>>>
>>> 'rte_lcore_role_t::ROLE_SERVICE' value '2'
>>>
>>> 'rte_lcore_role_t::ROLE_NON_EAL' value '3'
>>>
>>> 4 enumerator insertions:
>>>
>>> 'rte_lcore_role_t::RTE_LCORE_ROLE_RTE' value '0'
>>>
>>> 'rte_lcore_role_t::RTE_LCORE_ROLE_OFF' value '1'
>>>
>>> 'rte_lcore_role_t::RTE_LCORE_ROLE_SERVICE' value '2'
>>>
>>> 'rte_lcore_role_t::RTE_LCORE_ROLE_NON_EAL' value '3'
>>>
>>>
>>>
>>>
>>>
>>> [C] 'function int rte_lcore_has_role(unsigned int, rte_lcore_role_t)' at eal_common_lcore.c:85:1 has some indirect sub-type changes:
>>>
>>> parameter 2 of type 'enum rte_lcore_role_t' has sub-type changes:
>>>
>>> enum type 'enum rte_lcore_role_t' changed at rte_lcore.h:33:1, as reported earlier
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> Error: ABI issue reported for abidiff --suppr /home/runner/work/dpdk/dpdk/devtools/libabigail.abignore --no-added-syms --headers-dir1 reference/usr/local/include --headers-dir2 install/usr/local/include reference/usr/local/lib/librte_eal.so.26.1 install/usr/local/lib/librte_eal.so.26.2
>> We just came back from the Dragon Boat Festival.
>> I also received this ABI change warning. But I didn't have any good
>> ideas yet.
>> Thanks for helping to handle this.
>> Sorry for the inconvenience.
> There is nothing broken from a ABI pov.
> This is a limitation in earlier versions of libabigail.
> I can't reproduce with libabigail 2.9 (update in progress as I see
> 2.10 is available now).
>
> I think it was solved in libabigail 2.8
> (https://sourceware.org/git/?p=libabigail.git;a=commit;h=6f5f91564bdd).
This seems to solve the problem.
>
> If we want to go with the enum renaming before 26.11, bumping
> libabigail to 2.10 in the CI is an option (latest upstream version,
> and this is the version in f43 and f44).
> I tried it in GHA:
> https://github.com/david-marchand/dpdk/actions/runs/27937595115/job/82662953500
I also tested it based on libabigail 2.9.0 version. No any warning.
-->
abidiff build-ref/lib/librte_eal.so build-new/lib/librte_eal.so
Functions changes summary: 0 Removed, 0 Changed (2 filtered out), 0
Added functions
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
>
>
^ permalink raw reply
* [PATCH 5/5] ethdev: fix promoted flow metadata symbols
From: Dariusz Sosnowski @ 2026-06-23 11:37 UTC (permalink / raw)
To: Thomas Monjalon, Andrew Rybchenko, Ori Kam
Cc: dev, David Marchand, Bruce Richardson, Yu Jiang
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
Offending patch stabilized the following symbols:
- 1 function symbol:
- rte_flow_dynf_metadata_register
- 2 global variable symbols:
- rte_flow_dynf_metadata_offs
- rte_flow_dynf_metadata_mask
Any application using these flow metadata symbols,
which was linked dynamically against 25.11 version of ethdev
library and using current version of ethdev library
would fail on symbol resolution, because EXPERIMENTAL versions
were not exported.
Specifically, on application start up
variable symbol lookup error happens:
/tmp/dpdk-25.11/usr/local/bin/dpdk-testpmd:
symbol lookup error: /tmp/dpdk-25.11/usr/local/bin/dpdk-testpmd:
undefined symbol: rte_flow_dynf_metadata_offs, version EXPERIMENTAL
This error occurss because symbol lookup for global variables
happens on application startup.
This patch addresses that by adding versioned aliases
for the following variable symbols:
- rte_flow_dynf_metadata_offs
- rte_flow_dynf_metadata_mask
Versioned function symbols are also added
for rte_flow_dynf_metadata_register().
Bugzilla ID: 1957
Fixes: 4ee2f5c1cedf ("ethdev: promote flow metadata API to stable")
Reported-by: Yu Jiang <yux.jiang@intel.com>
Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
lib/ethdev/meson.build | 2 ++
lib/ethdev/rte_flow.c | 33 ++++++++++++++++++++++-----------
2 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/lib/ethdev/meson.build b/lib/ethdev/meson.build
index 8ba6c708a2..63fd866af9 100644
--- a/lib/ethdev/meson.build
+++ b/lib/ethdev/meson.build
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2017 Intel Corporation
+use_function_versioning = true
+
sources = files(
'ethdev_driver.c',
'ethdev_private.c',
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index ec0fe08355..a8c01ffe8a 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -23,12 +23,20 @@
#define FLOW_LOG RTE_ETHDEV_LOG_LINE
/* Mbuf dynamic field name for metadata. */
-RTE_EXPORT_SYMBOL(rte_flow_dynf_metadata_offs)
-int32_t rte_flow_dynf_metadata_offs = -1;
+static int32_t rte_flow_dynf_metadata_offs_impl = -1;
+
+RTE_DEFAULT_SYMBOL_ALIAS(26, int32_t, rte_flow_dynf_metadata_offs,
+ rte_flow_dynf_metadata_offs_impl);
+RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS(int32_t, rte_flow_dynf_metadata_offs,
+ rte_flow_dynf_metadata_offs_impl);
/* Mbuf dynamic field flag bit number for metadata. */
-RTE_EXPORT_SYMBOL(rte_flow_dynf_metadata_mask)
-uint64_t rte_flow_dynf_metadata_mask;
+static uint64_t rte_flow_dynf_metadata_mask_impl = 0;
+
+RTE_DEFAULT_SYMBOL_ALIAS(26, uint64_t, rte_flow_dynf_metadata_mask,
+ rte_flow_dynf_metadata_mask_impl);
+RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS(uint64_t, rte_flow_dynf_metadata_mask,
+ rte_flow_dynf_metadata_mask_impl);
/**
* Flow elements description tables.
@@ -281,9 +289,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
MK_FLOW_ACTION(JUMP_TO_TABLE_INDEX, sizeof(struct rte_flow_action_jump_to_table_index)),
};
-RTE_EXPORT_SYMBOL(rte_flow_dynf_metadata_register)
-int
-rte_flow_dynf_metadata_register(void)
+RTE_DEFAULT_SYMBOL(26, int, rte_flow_dynf_metadata_register, (void))
{
int offset;
int flag;
@@ -303,19 +309,24 @@ rte_flow_dynf_metadata_register(void)
flag = rte_mbuf_dynflag_register(&desc_flag);
if (flag < 0)
goto error;
- rte_flow_dynf_metadata_offs = offset;
- rte_flow_dynf_metadata_mask = RTE_BIT64(flag);
+ rte_flow_dynf_metadata_offs_impl = offset;
+ rte_flow_dynf_metadata_mask_impl = RTE_BIT64(flag);
rte_flow_trace_dynf_metadata_register(offset, RTE_BIT64(flag));
return 0;
error:
- rte_flow_dynf_metadata_offs = -1;
- rte_flow_dynf_metadata_mask = UINT64_C(0);
+ rte_flow_dynf_metadata_offs_impl = -1;
+ rte_flow_dynf_metadata_mask_impl = UINT64_C(0);
return -rte_errno;
}
+RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_flow_dynf_metadata_register, (void))
+{
+ return rte_flow_dynf_metadata_register();
+}
+
static inline void
fts_enter(struct rte_eth_dev *dev)
{
--
2.47.3
^ permalink raw reply related
* [PATCH 4/5] eal: support aliases for versioned variable symbols
From: Dariusz Sosnowski @ 2026-06-23 11:37 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, David Marchand
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
Existing symbol versioning macros are not suitable for versioning
exported global variables.
Specifically, if existing macros are used for versioning
global variable symbol promoted from experimental to stable,
result would be multiple variables with separate storage defined.
If an application was linked against older DPDK and had copy
relocations, this would yield an inconsistent behavior:
- Application would use experimental symbol version,
with storage set up in BSS section in application.
- Library would use latest symbol version,
with storage set up in BSS section of shared object.
This patch adds versioning macros which utilize symbol aliasing.
Specifically, a new variable (with version suffix) is defined
as an alias to private (static) variable inside the library.
Variable symbol versions are attached to these alias variables.
Following macros are added:
- RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS
- RTE_DEFAULT_SYMBOL_ALIAS
Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
buildtools/gen-version-map.py | 11 +++++++++++
lib/eal/common/eal_export.h | 22 ++++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/buildtools/gen-version-map.py b/buildtools/gen-version-map.py
index 57e08a8c0f..aa88e69179 100755
--- a/buildtools/gen-version-map.py
+++ b/buildtools/gen-version-map.py
@@ -14,8 +14,12 @@
export_int_sym_regexp = re.compile(r"^RTE_EXPORT_INTERNAL_SYMBOL\(([^)]+)\)")
export_sym_regexp = re.compile(r"^RTE_EXPORT_SYMBOL\(([^)]+)\)")
ver_sym_regexp = re.compile(r"^RTE_VERSION_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+
ver_exp_sym_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL\([^,]+, ([^,]+),")
+ver_exp_sym_alias_regexp = re.compile(r"^RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS\([^,]+, ([^,]+),")
+
default_sym_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL\(([^,]+), [^,]+, ([^,]+),")
+default_sym_alias_regexp = re.compile(r"^RTE_DEFAULT_SYMBOL_ALIAS\(([^,]+), [^,]+, ([^,]+),")
parser = argparse.ArgumentParser(
description=__doc__,
@@ -73,10 +77,17 @@
elif ver_exp_sym_regexp.match(ln):
node = "EXPERIMENTAL"
symbol = ver_exp_sym_regexp.match(ln).group(1)
+ elif ver_exp_sym_alias_regexp.match(ln):
+ node = "EXPERIMENTAL"
+ symbol = ver_exp_sym_alias_regexp.match(ln).group(1)
elif default_sym_regexp.match(ln):
abi = default_sym_regexp.match(ln).group(1)
node = f"DPDK_{abi}"
symbol = default_sym_regexp.match(ln).group(2)
+ elif default_sym_alias_regexp.match(ln):
+ abi = default_sym_alias_regexp.match(ln).group(1)
+ node = f"DPDK_{abi}"
+ symbol = default_sym_alias_regexp.match(ln).group(2)
if not symbol:
continue
diff --git a/lib/eal/common/eal_export.h b/lib/eal/common/eal_export.h
index 7971bf8d7a..5b458f81c6 100644
--- a/lib/eal/common/eal_export.h
+++ b/lib/eal/common/eal_export.h
@@ -63,6 +63,14 @@ __attribute__((__symver__(RTE_STR(name) "@@DPDK_" RTE_STR(ver)))) \
type name ## _v ## ver args; \
type name ## _v ## ver args
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS(type, name, orig) VERSIONING_WARN \
+extern type name ## _exp __attribute((alias(RTE_STR(orig)), \
+ __symver__(RTE_STR(name) "@EXPERIMENTAL")))
+
+#define RTE_DEFAULT_SYMBOL_ALIAS(ver, type, name, orig) VERSIONING_WARN \
+extern type name ## _v ## ver __attribute((alias(RTE_STR(orig)), \
+ __symver__(RTE_STR(name) "@@DPDK_" RTE_STR(ver))))
+
#else /* !__has_attribute(symver) */
/* Use asm tag to create symbol table entry */
@@ -81,6 +89,14 @@ __asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_"
__rte_used type name ## _v ## ver args; \
type name ## _v ## ver args
+#define RTE_DEFAULT_SYMBOL_ALIAS(ver, type, name, orig) VERSIONING_WARN \
+extern type name ## _v ## ver __attribute__((alias(RTE_STR(orig)))); \
+__asm__(".symver " RTE_STR(name) "_v" RTE_STR(ver) ", " RTE_STR(name) "@@DPDK_" RTE_STR(ver));
+
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS(type, name, orig) VERSIONING_WARN \
+extern type name ## _exp __attribute__((alias(RTE_STR(orig)))); \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL");
+
#endif /* __has_attribute(symver) */
#else /* !RTE_BUILD_SHARED_LIB */
@@ -97,6 +113,12 @@ type name ## _exp args
#define RTE_DEFAULT_SYMBOL(ver, type, name, args) VERSIONING_WARN \
type name args
+#define RTE_VERSION_EXPERIMENTAL_SYMBOL_ALIAS(type, name, orig) VERSIONING_WARN \
+extern type name ## _exp __attribute__((alias(RTE_STR(orig))));
+
+#define RTE_DEFAULT_SYMBOL_ALIAS(ver, type, name, orig) VERSIONING_WARN \
+extern type name __attribute__((alias(RTE_STR(orig))));
+
#endif /* RTE_BUILD_SHARED_LIB */
#endif /* EAL_EXPORT_H */
--
2.47.3
^ permalink raw reply related
* [PATCH 3/5] net/mlx5: fix stabilized function versions
From: Dariusz Sosnowski @ 2026-06-23 11:37 UTC (permalink / raw)
To: Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
Matan Azrad
Cc: dev, David Marchand, Bruce Richardson
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
Offending patch stabilized the following function symbols:
- rte_pmd_mlx5_driver_event_cb_register
- rte_pmd_mlx5_driver_event_cb_unregister
- rte_pmd_mlx5_enable_steering
- rte_pmd_mlx5_disable_steering
These function symbols were introduced in 25.11.
Any application using these functions, linked against 25.11 version,
would fail when used with 26.07 libraries, because only DPDK_26 versions
of these symbols were exported.
This patch fixes that by adding proper function symbol versioning
to these symbols.
Fixes: e8cab133645f ("net/mlx5: promote some private API to stable")
Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
drivers/net/mlx5/meson.build | 2 ++
drivers/net/mlx5/mlx5_driver_event.c | 22 ++++++++++++++++------
drivers/net/mlx5/mlx5_flow.c | 18 ++++++++++++------
3 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/drivers/net/mlx5/meson.build b/drivers/net/mlx5/meson.build
index 82a7dfe782..0fa6322779 100644
--- a/drivers/net/mlx5/meson.build
+++ b/drivers/net/mlx5/meson.build
@@ -2,6 +2,8 @@
# Copyright 2018 6WIND S.A.
# Copyright 2018 Mellanox Technologies, Ltd
+use_function_versioning = true
+
if not (is_linux or is_windows)
build = false
reason = 'only supported on Linux and Windows'
diff --git a/drivers/net/mlx5/mlx5_driver_event.c b/drivers/net/mlx5/mlx5_driver_event.c
index 89e49331c8..d0e22d6151 100644
--- a/drivers/net/mlx5/mlx5_driver_event.c
+++ b/drivers/net/mlx5/mlx5_driver_event.c
@@ -236,9 +236,8 @@ notify_existing_devices(rte_pmd_mlx5_driver_event_callback_t cb, void *opaque)
notify_existing_queues(port_id, cb, opaque);
}
-RTE_EXPORT_SYMBOL(rte_pmd_mlx5_driver_event_cb_register)
-int
-rte_pmd_mlx5_driver_event_cb_register(rte_pmd_mlx5_driver_event_callback_t cb, void *opaque)
+RTE_DEFAULT_SYMBOL(26, int, rte_pmd_mlx5_driver_event_cb_register,
+ (rte_pmd_mlx5_driver_event_callback_t cb, void *opaque))
{
struct registered_cb *r;
@@ -264,9 +263,14 @@ rte_pmd_mlx5_driver_event_cb_register(rte_pmd_mlx5_driver_event_callback_t cb, v
return 0;
}
-RTE_EXPORT_SYMBOL(rte_pmd_mlx5_driver_event_cb_unregister)
-int
-rte_pmd_mlx5_driver_event_cb_unregister(rte_pmd_mlx5_driver_event_callback_t cb)
+RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_pmd_mlx5_driver_event_cb_register,
+ (rte_pmd_mlx5_driver_event_callback_t cb, void *opaque))
+{
+ return rte_pmd_mlx5_driver_event_cb_register(cb, opaque);
+}
+
+RTE_DEFAULT_SYMBOL(26, int, rte_pmd_mlx5_driver_event_cb_unregister,
+ (rte_pmd_mlx5_driver_event_callback_t cb))
{
struct registered_cb *r;
bool found = false;
@@ -289,6 +293,12 @@ rte_pmd_mlx5_driver_event_cb_unregister(rte_pmd_mlx5_driver_event_callback_t cb)
return 0;
}
+RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_pmd_mlx5_driver_event_cb_unregister,
+ (rte_pmd_mlx5_driver_event_callback_t cb))
+{
+ return rte_pmd_mlx5_driver_event_cb_unregister(cb);
+}
+
RTE_FINI(rte_pmd_mlx5_driver_event_cb_cleanup) {
struct registered_cb *r;
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index a95dd9dc94..4b984df892 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -12506,9 +12506,7 @@ flow_disable_steering_run_on_related(struct rte_eth_dev *dev,
}
}
-RTE_EXPORT_SYMBOL(rte_pmd_mlx5_disable_steering)
-void
-rte_pmd_mlx5_disable_steering(void)
+RTE_DEFAULT_SYMBOL(26, void, rte_pmd_mlx5_disable_steering, (void))
{
uint16_t port_id;
@@ -12532,9 +12530,12 @@ rte_pmd_mlx5_disable_steering(void)
mlx5_steering_disabled = true;
}
-RTE_EXPORT_SYMBOL(rte_pmd_mlx5_enable_steering)
-int
-rte_pmd_mlx5_enable_steering(void)
+RTE_VERSION_EXPERIMENTAL_SYMBOL(void, rte_pmd_mlx5_disable_steering, (void))
+{
+ rte_pmd_mlx5_disable_steering();
+}
+
+RTE_DEFAULT_SYMBOL(26, int, rte_pmd_mlx5_enable_steering, (void))
{
uint16_t port_id;
@@ -12551,6 +12552,11 @@ rte_pmd_mlx5_enable_steering(void)
return 0;
}
+RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_pmd_mlx5_enable_steering, (void))
+{
+ return rte_pmd_mlx5_enable_steering();
+}
+
bool
mlx5_vport_rx_metadata_passing_enabled(const struct mlx5_dev_ctx_shared *sh)
{
--
2.47.3
^ permalink raw reply related
* [PATCH 2/5] drivers: support function versioning
From: Dariusz Sosnowski @ 2026-06-23 11:37 UTC (permalink / raw)
To: David Marchand, Bruce Richardson; +Cc: dev
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
Add support for enabling function versioning
(through use_function_versioning meson variable) for drivers,
similar to libraries.
Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
drivers/meson.build | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/meson.build b/drivers/meson.build
index 4d95604ecd..a63d93372a 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -171,6 +171,7 @@ foreach subpath:subdirs
pkgconfig_extra_libs = []
testpmd_sources = []
require_iova_in_mbuf = true
+ use_function_versioning = false
# for handling base code files which may need extra cflags
base_sources = []
base_cflags = []
@@ -273,6 +274,13 @@ foreach subpath:subdirs
endif
dpdk_conf.set(lib_name.to_upper(), 1)
+ if developer_mode and is_windows and use_function_versioning
+ message('@0@: Function versioning is not supported by Windows.'.format(name))
+ endif
+ if use_function_versioning
+ cflags += '-DRTE_USE_FUNCTION_VERSIONING'
+ endif
+
dpdk_extra_ldflags += pkgconfig_extra_libs
dpdk_headers += headers
--
2.47.3
^ permalink raw reply related
* [PATCH 0/5] add versioned symbols for recently stabilized APIs
From: Dariusz Sosnowski @ 2026-06-23 11:37 UTC (permalink / raw)
To: Thomas Monjalon, David Marchand, Bruce Richardson,
Andrew Rybchenko, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
Suanming Mou, Matan Azrad
Cc: dev
Main goal of this patchset is to address https://bugs.dpdk.org/show_bug.cgi?id=1957
but it also handles other recently stabilized symbols and has some minor fixes:
- Patch 1 - Fix RTE_VERSION_EXPERIMENTAL_SYMBOL macro on clang.
- Patch 2 - Allow function versioning inside drivers.
- Patch 3 - Version the function symbols stabilized in
https://git.dpdk.org/dpdk/commit/?id=e8cab133645f5466ef75e511629add43b68a5027
- Patch 4 - Introduce versioning macros for global variable symbols.
- Patch 5 - Version the function and variable symbols stabilized in
https://git.dpdk.org/dpdk/commit/?id=4ee2f5c1cedf9ee7f39afa667f71b07f4004ba5c
Issue is still not fully fixed for stabilized global variables:
rte_flow_dynf_metadata_offs and rte_flow_dynf_metadata_mask.
Patch 4 and 5 address the bug for these global variables,
by providing a single storage for both EXPERIMENTAL and
DPDK_26 variable symbol versions.
This is achieved through symbol aliasing.
But this solution is limited only to executables compiled with clang.
clang and gcc have a different default behavior regarding relocations
of global variables exposed by shared libraries.
With clang, R_X86_64_GLOB_DAT relocations are generated for executables:
$ readelf -sW build-26.07/lib/librte_ethdev.so | grep rte_flow_dynf_metadata_offs
113: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
116: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
970: 00000000000ea4c0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_impl
1212: 00000000000ea4c0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_v26
1325: 00000000000ea4c0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_exp
1415: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
1705: 00000000000ea4c0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
$ readelf -rW build-26.07/drivers/librte_net_mlx5.so | grep rte_flow_dynf_metadata_offs
0000000003ed5f18 0000001600000006 R_X86_64_GLOB_DAT 0000000000000000 rte_flow_dynf_metadata_offs@DPDK_26 + 0
$ readelf -rW build-25.11/app/dpdk-testpmd | grep rte_flow_dynf_metadata_offs
--> 000000000028ef70 0000011300000006 R_X86_64_GLOB_DAT 0000000000000000 rte_flow_dynf_metadata_offs@EXPERIMENTAL + 0
With gcc, R_X86_64_COPY relocations are generated:
$ readelf -sW build-26.07/lib/librte_ethdev.so | grep rte_flow_dynf_metadata_offs
113: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
116: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
1471: 00000000000e74e0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_impl
2134: 00000000000e74e0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_v26
2247: 00000000000e74e0 4 OBJECT LOCAL DEFAULT 24 rte_flow_dynf_metadata_offs_exp
2337: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@@DPDK_26
2627: 00000000000e74e0 4 OBJECT GLOBAL DEFAULT 24 rte_flow_dynf_metadata_offs@EXPERIMENTAL
$ readelf -rW build-26.07/drivers/librte_net_mlx5.so | grep rte_flow_dynf_metadata_offs
00000000046dbef0 0000001600000006 R_X86_64_GLOB_DAT 0000000000000000 rte_flow_dynf_metadata_offs@DPDK_26 + 0
$ readelf -rW build-25.11/app/dpdk-testpmd | grep rte_flow_dynf_metadata_offs
--> 000000000029b540 000001d200000005 R_X86_64_COPY 000000000029b540 rte_flow_dynf_metadata_offs@EXPERIMENTAL + 0
With copy relocations (testpmd linked through gcc) the following happens:
- When variable symbol (with EXPERIMENTAL version) gets resolved inside executable,
global variable gets copied from read-only data to executable's BSS section.
Executable will access this variable through BSS.
- When variable symbol (with DPDK_26 version) gets resolved inside a library,
global variable is accessed indirectly through GOT.
It is stored inside BSS section of the shared library.
So executable and libraries refer to different storage,
eventually leading to inconsistent runtime behavior.
Problems only appears when executable and library require
different versions of global variable symbol.
If testpmd from 26.07 is used with libraries from 26.07,
GOT entry for these variables will point to copied variable.
Without copy relocations (testpmd linked through clang) both
executable and libraries access the global variable indirectly through GOT.
Runtime behavior is consistent, regardless of the mix of variable symbol versions.
The only other solution I could find was to use dlsym() inside libraries
to dynamically resolve the location rte_flow_dynf_metadata_offs and rte_flow_dynf_metadata_mask,
but this solution sounds like an overkill.
Essentially this would require moving to getter/setter functions for these variables
inside the library.
I would appreciate any feedback or suggestions if anybody had encountered a similar issue before.
Dariusz Sosnowski (5):
eal: fix macro for versioned experimental symbol
drivers: support function versioning
net/mlx5: fix stabilized function versions
eal: support aliases for versioned variable symbols
ethdev: fix promoted flow metadata symbols
buildtools/gen-version-map.py | 11 ++++++++++
drivers/meson.build | 8 +++++++
drivers/net/mlx5/meson.build | 2 ++
drivers/net/mlx5/mlx5_driver_event.c | 22 ++++++++++++++-----
drivers/net/mlx5/mlx5_flow.c | 18 ++++++++++-----
lib/eal/common/eal_export.h | 24 +++++++++++++++++++-
lib/ethdev/meson.build | 2 ++
lib/ethdev/rte_flow.c | 33 ++++++++++++++++++----------
8 files changed, 96 insertions(+), 24 deletions(-)
--
2.47.3
^ permalink raw reply
* [PATCH 1/5] eal: fix macro for versioned experimental symbol
From: Dariusz Sosnowski @ 2026-06-23 11:37 UTC (permalink / raw)
To: David Marchand; +Cc: dev, Bruce Richardson
In-Reply-To: <20260623113752.1100072-1-dsosnowski@nvidia.com>
Add a missing semicolon after __asm__ block in
RTE_VERSION_EXPERIMENTAL_SYMBOL macro.
It's lack triggers the following compilation error with clang:
../lib/ethdev/rte_flow.c:320:1: error: expected ';' after top-level asm block
320 | RTE_VERSION_EXPERIMENTAL_SYMBOL(int, rte_flow_dynf_metadata_register, (void))
| ^
../lib/eal/common/eal_export.h:75:74: note: expanded from macro 'RTE_VERSION_EXPERIMENTAL_SYMBOL'
75 | __asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
| ^
../lib/eal/include/rte_common.h:237:20: note: expanded from macro '\
__rte_used'
237 | #define __rte_used __attribute__((used))
| ^
Fixes: e30e194c4d06 ("eal: rework function versioning macros")
Cc: david.marchand@redhat.com
Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
lib/eal/common/eal_export.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/eal/common/eal_export.h b/lib/eal/common/eal_export.h
index 888fd9f9ed..7971bf8d7a 100644
--- a/lib/eal/common/eal_export.h
+++ b/lib/eal/common/eal_export.h
@@ -72,7 +72,7 @@ __rte_used type name ## _v ## ver args; \
type name ## _v ## ver args
#define RTE_VERSION_EXPERIMENTAL_SYMBOL(type, name, args) VERSIONING_WARN \
-__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL") \
+__asm__(".symver " RTE_STR(name) "_exp, " RTE_STR(name) "@EXPERIMENTAL"); \
__rte_used type name ## _exp args; \
type name ## _exp args
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox