bpf enhancements
cover bpf enhancements
This is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
Stephen Hemminger (5):
bpf: add allocation annotations to functions
bpf: use rte_pktmbuf_free_bulk
bpf: add a test for BPF ELF load
bpf: add test for rx and tx filtering
bpf: remove use of vla
app/test/bpf/meson.build | 53 ++++ app/test/bpf/test_bpf_filter.c | 35 +++ app/test/bpf/test_bpf_load.c | 60 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 426 +++++++++++++++++++++++++++++++++ lib/bpf/bpf_pkt.c | 84 ++++--- lib/bpf/meson.build | 2 - lib/bpf/rte_bpf.h | 11 +- 8 files changed, 632 insertions(+), 41 deletions(-) create mode 100644 app/test/bpf/meson.build create mode 100644 app/test/bpf/test_bpf_filter.c create mode 100644 app/test/bpf/test_bpf_load.c
Signature · 1 line
2.51.0
PATCH 1/5 bpf: add allocation annotations to functions
In commit 80da7efbb4c4 ("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.
--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
lib/bpf/rte_bpf.h +8 -3
@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus
@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF
@@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. *
@@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus }
Signature · 1 line
2.51.0
PATCH 2/5 bpf: use rte_pktmbuf_free_bulk
In reply to Stephen HemmingerWhen rte_pktmbuf_free_bulk is faster because it does single mempool operation rather than per-packet.
--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/bpf/bpf_pkt.c +1 -2
@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,if (drop !=0 ) {/* free filtered out mbufs */ -for (i =0 ; i != k; i++) -rte_pktmbuf_free (dr[i]); +rte_pktmbuf_free_bulk (dr, k); }else {/* copy filtered out mbufs beyond good ones */ for (i =0 ; i != k; i++)
Signature · 1 line
2.51.0
PATCH 3/5 bpf: add a test for BPF ELF load
In reply to Stephen HemmingerCreate an ELF file to load using clang. Repackage the object into an array using xdd. Write a test to see load and run the BPF. If tools are not present, then the step is skipped.
Draft version made with Claude AI, but it didn't work.
--- app/test/bpf/meson.build | 52 +++++++++++++ app/test/bpf/test_bpf_load.c | 60 ++++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 146 +++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 app/test/bpf/meson.build create mode 100644 app/test/bpf/test_bpf_load.c
app/test/bpf/meson.build new file +52 -0
@@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2025 Stephen Hemminger <[email protected]> + +bpf_test_hdrs = [ ] + +# use clang to compile to bpf +clang_supports_bpf = false +clang = find_program('clang', required: false) +if clang.found() + clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', + check: false).returncode() == 0 +endif + +if not clang_supports_bpf + message('app/test_bpf: no BPF load tests missing clang BPF support') + subdir_done() + +endif + +xxd = find_program('xxd', required: false) +if not xxd.found() + message('app/test_bpf: missing xxd required to convert object to array') + subdir_done() +endif + +# BPF compiler flags +bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c'] + +# Enable test in test_bpf.c +cflags += '-DTEST_BPF_ELF_LOAD' + +# BPF sources to compile +test_bpf_progs = [ + 'test_bpf_load' +] + +foreach test_name : test_bpf_progs + # Compile BPF C source to object file + bpf_obj = custom_target(test_name + '_o', + input: test_name + '.c', + output: test_name + '.o', + command: [ clang, bpf_cflags, '@INPUT@', '-o', '@OUTPUT@']) + + # Convert object file to C header using xxd + bpf_test_h = custom_target(test_name + '_h', + input: bpf_obj, + output: test_name + '.h', + command: [ xxd, '-i', '-n', test_name + '_data','@INPUT@', '@OUTPUT@']) + + resources += bpf_test_h + +endforeach
app/test/bpf/test_bpf_load.c new file +60 -0
60 lines not on this page · raw message
app/test/meson.build +2 -0
@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_h
app/test/test_bpf.c +146 -0
164 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH 4/5 bpf: add test for rx and tx filtering
In reply to Stephen HemmingerNew test using null device to test filtering with BPF. Initial not working version build with Claude AI.
--- app/test/bpf/meson.build | 3 +- app/test/bpf/test_bpf_filter.c | 35 +++++ app/test/test_bpf.c | 280 +++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 app/test/bpf/test_bpf_filter.c
app/test/bpf/meson.build +2 -1
@@ -31,7 +31,8 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile test_bpf_progs = [ - 'test_bpf_load' + 'test_bpf_load', + 'test_bpf_filter', ] foreach test_name : test_bpf_progs
app/test/bpf/test_bpf_filter.c new file +35 -0
@@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +#include <stdint.h> +#include <stddef.h> + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +}
app/test/test_bpf.c +280 -0
301 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH 5/5 bpf: remove use of vla
In reply to Stephen HemmingerReplace variable length array with two part loop which applies filter in bursts up to 32 packets.
--- lib/bpf/bpf_pkt.c | 81 ++++++++++++++++++++++++++------------------- lib/bpf/meson.build | 2 -- 2 files changed, 47 insertions(+), 36 deletions(-)
lib/bpf/bpf_pkt.c +47 -34
@@ -157,13 +157,16 @@ bpf_eth_cbh_add(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)/* * BPF packet processing routines. */ +#define BPF_FILTER_BURST 32ustatic inline uint32_t apply_filter (struct rte_mbuf *mb[],const uint64_t rc[],uint32_t num,uint32_t drop) {uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf *dr[BPF_FILTER_BURST ]; + +RTE_ASSERT (num <=BPF_FILTER_BURST );for (i =0 , j =0 , k =0 ; i != num; i++) {
@@ -191,65 +194,75 @@ static inline uint32_t pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint32_t i; -void *dp[num]; -uint64_t rc[num]; + uint32_t matched = 0; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +void *dp[BPF_FILTER_BURST ]; +uint64_t rc[BPF_FILTER_BURST ]; -for (i =0 ; i != num; i++) - dp[i] = rte_pktmbuf_mtod(mb[i], void *); +for (uint32_t j =0 ; j < burst_sz; j++) + dp[j] = rte_pktmbuf_mtod(mb[i + j], void *); -rte_bpf_exec_burst (bpf, dp, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, dp, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -void *dp; -uint64_t rc[num]; - - n =0 ; -for (i =0 ; i != num; i++) { - dp = rte_pktmbuf_mtod(mb[i], void *); - rc[i] = jit->func (dp); - n += (rc[i] ==0 ); - } +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; + +for (uint32_t j =0 ; j < burst_sz; j++) { + void *dp = rte_pktmbuf_mtod(mb[i + j], void *); -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); + rc[j] = jit->func (dp); + } -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_vm (const struct rte_bpf *bpf,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint64_t rc[num]; +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -rte_bpf_exec_burst (bpf, (void **)mb, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, (void **)mb, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -uint64_t rc[num]; +uint32_t matched =0 ; - n =0 ; -for (i =0 ; i != num; i++) { - rc[i] = jit->func (mb[i]); - n += (rc[i] ==0 ); - } +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); +for (uint32_t j =0 ; j < burst_sz; j++) + rc[j] = jit->func (mb[i + j]); -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }/*
lib/bpf/meson.build +0 -2
@@ -7,8 +7,6 @@ if is_windows subdir_done() endif -cflags += no_wvla_cflag - if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_32') build = false reason = 'not supported on 32-bit x86'
Signature · 1 line
2.51.0
Quoted 11 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Thursday 30 October 2025 17:34
To: [email protected]
Cc: Stephen Hemminger <[email protected]>
Subject: [PATCH 0/5] bpf enhancementsThis is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
Thank you for doing this, I will try to have a look next week.
About the overall approach to getting the BPF object file, I understand the clang part, but do we really need to convert object files to headers and then write them back? Could we just pass these object files around as test resources instead? Not that xxd was not present in most systems, but looks like extra work and complexity.
Quoted 7 lines · On Fri, 31 Oct 2025 11:39:54 +0000 Marat Khalili <[email protected]>…
Quoted 7 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Thursday 30 October 2025 17:34
To: [email protected]
Cc: Stephen Hemminger <[email protected]>
Subject: [PATCH 0/5] bpf enhancements
Quoted 8 lines
Quoted 4 lines
This is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
Thank you for doing this, I will try to have a look next week.
About the overall approach to getting the BPF object file, I understand the clang part, but do we really need to convert object files to headers and then write them back? Could we just pass these object files around as test resources instead? Not that xxd was not present in most systems, but looks like extra work and complexity.
The problem is that it is hard to determine file location paths when functional test is run. The test needs to not reference the build directory structure. And installing objects somewhere in build requires more steps in meson. This was the easiest way which is what the cfgfiles test was already doing.
v2 cover BPF enhancements
In reply to Stephen HemmingerThis is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
v2 - add more checks in rx test
fix headers
Stephen Hemminger (5):
bpf: add allocation annotations to functions
bpf: use bulk free on filtered packets
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
bpf: remove use of VLA
app/test/bpf/meson.build | 53 ++++ app/test/bpf/test_bpf_filter.c | 53 ++++ app/test/bpf/test_bpf_load.c | 62 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 452 +++++++++++++++++++++++++++++++++ lib/bpf/bpf_pkt.c | 84 +++--- lib/bpf/meson.build | 2 - lib/bpf/rte_bpf.h | 11 +- 8 files changed, 678 insertions(+), 41 deletions(-) create mode 100644 app/test/bpf/meson.build create mode 100644 app/test/bpf/test_bpf_filter.c create mode 100644 app/test/bpf/test_bpf_load.c
Signature · 1 line
2.51.0
PATCH v2 1/5 bpf: add allocation annotations to functions
In commit 80da7efbb4c4 ("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.
--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
lib/bpf/rte_bpf.h +8 -3
@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus
@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF
@@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. *
@@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus }
Signature · 1 line
2.51.0
PATCH v2 2/5 bpf: use bulk free on filtered packets
In reply to Stephen HemmingerWhen rte_pktmbuf_free_bulk is faster because it does single mempool operation rather than per-packet.
--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/bpf/bpf_pkt.c +1 -2
@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,if (drop !=0 ) {/* free filtered out mbufs */ -for (i =0 ; i != k; i++) -rte_pktmbuf_free (dr[i]); +rte_pktmbuf_free_bulk (dr, k); }else {/* copy filtered out mbufs beyond good ones */ for (i =0 ; i != k; i++)
Signature · 1 line
2.51.0
PATCH v2 3/5 bpf: add a test for BPF ELF load
In reply to Stephen HemmingerCreate an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.
Draft version made with Claude AI, but it didn't work.
--- app/test/bpf/meson.build | 52 +++++++++++++ app/test/bpf/test_bpf_load.c | 62 +++++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 146 +++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 app/test/bpf/meson.build create mode 100644 app/test/bpf/test_bpf_load.c
app/test/bpf/meson.build new file +52 -0
@@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2025 Stephen Hemminger <[email protected]> + +bpf_test_hdrs = [ ] + +# use clang to compile to bpf +clang_supports_bpf = false +clang = find_program('clang', required: false) +if clang.found() + clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', + check: false).returncode() == 0 +endif + +if not clang_supports_bpf + message('app/test_bpf: no BPF load tests missing clang BPF support') + subdir_done() + +endif + +xxd = find_program('xxd', required: false) +if not xxd.found() + message('app/test_bpf: missing xxd required to convert object to array') + subdir_done() +endif + +# BPF compiler flags +bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c'] + +# Enable test in test_bpf.c +cflags += '-DTEST_BPF_ELF_LOAD' + +# BPF sources to compile +test_bpf_progs = [ + 'test_bpf_load' +] + +foreach test_name : test_bpf_progs + # Compile BPF C source to object file + bpf_obj = custom_target(test_name + '_o', + input: test_name + '.c', + output: test_name + '.o', + command: [ clang, bpf_cflags, '@INPUT@', '-o', '@OUTPUT@']) + + # Convert object file to C header using xxd + bpf_test_h = custom_target(test_name + '_h', + input: bpf_obj, + output: test_name + '.h', + command: [ xxd, '-i', '-n', test_name + '_data','@INPUT@', '@OUTPUT@']) + + resources += bpf_test_h + +endforeach
app/test/bpf/test_bpf_load.c new file +62 -0
62 lines not on this page · raw message
app/test/meson.build +2 -0
@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_h
app/test/test_bpf.c +146 -0
164 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v2 4/5 bpf: add test for Rx and Tx filtering
In reply to Stephen HemmingerNew test using null device to test filtering with BPF.
---
Note: the BPF source will generate some false positives from checkpatch because it is not regular DPDK source, and must not use DPDK macros.
app/test/bpf/meson.build | 3 +- app/test/bpf/test_bpf_filter.c | 53 ++++++ app/test/test_bpf.c | 306 +++++++++++++++++++++++++++++++++ 3 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 app/test/bpf/test_bpf_filter.c
app/test/bpf/meson.build +2 -1
@@ -31,7 +31,8 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile test_bpf_progs = [ - 'test_bpf_load' + 'test_bpf_load', + 'test_bpf_filter', ] foreach test_name : test_bpf_progs
app/test/bpf/test_bpf_filter.c new file +53 -0
53 lines not on this page · raw message
app/test/test_bpf.c +306 -0
327 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v2 5/5 bpf: remove use of VLA
In reply to Stephen HemmingerReplace variable length array with two part loop which applies filter in bursts up to 32 packets.
--- lib/bpf/bpf_pkt.c | 81 ++++++++++++++++++++++++++------------------- lib/bpf/meson.build | 2 -- 2 files changed, 47 insertions(+), 36 deletions(-)
lib/bpf/bpf_pkt.c +47 -34
@@ -157,13 +157,16 @@ bpf_eth_cbh_add(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)/* * BPF packet processing routines. */ +#define BPF_FILTER_BURST 32ustatic inline uint32_t apply_filter (struct rte_mbuf *mb[],const uint64_t rc[],uint32_t num,uint32_t drop) {uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf *dr[BPF_FILTER_BURST ]; + +RTE_ASSERT (num <=BPF_FILTER_BURST );for (i =0 , j =0 , k =0 ; i != num; i++) {
@@ -191,65 +194,75 @@ static inline uint32_t pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint32_t i; -void *dp[num]; -uint64_t rc[num]; + uint32_t matched = 0; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +void *dp[BPF_FILTER_BURST ]; +uint64_t rc[BPF_FILTER_BURST ]; -for (i =0 ; i != num; i++) - dp[i] = rte_pktmbuf_mtod(mb[i], void *); +for (uint32_t j =0 ; j < burst_sz; j++) + dp[j] = rte_pktmbuf_mtod(mb[i + j], void *); -rte_bpf_exec_burst (bpf, dp, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, dp, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -void *dp; -uint64_t rc[num]; - - n =0 ; -for (i =0 ; i != num; i++) { - dp = rte_pktmbuf_mtod(mb[i], void *); - rc[i] = jit->func (dp); - n += (rc[i] ==0 ); - } +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; + +for (uint32_t j =0 ; j < burst_sz; j++) { + void *dp = rte_pktmbuf_mtod(mb[i + j], void *); -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); + rc[j] = jit->func (dp); + } -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_vm (const struct rte_bpf *bpf,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint64_t rc[num]; +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -rte_bpf_exec_burst (bpf, (void **)mb, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, (void **)mb, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -uint64_t rc[num]; +uint32_t matched =0 ; - n =0 ; -for (i =0 ; i != num; i++) { - rc[i] = jit->func (mb[i]); - n += (rc[i] ==0 ); - } +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); +for (uint32_t j =0 ; j < burst_sz; j++) + rc[j] = jit->func (mb[i + j]); -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }/*
lib/bpf/meson.build +0 -2
@@ -7,8 +7,6 @@ if is_windows subdir_done() endif -cflags += no_wvla_cflag - if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_32') build = false reason = 'not supported on 32-bit x86'
Signature · 1 line
2.51.0
v3 cover BPF enhancements
In reply to Stephen HemmingerThis is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
It is disappointing that there were no examples or tests when the code was submitted.
v3 - do not depend on xxd -n flag which is not present on versions
before Oct 2024
Stephen Hemminger (5):
bpf: add allocation annotations to functions
bpf: use bulk free on filtered packets
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
bpf: remove use of VLA
app/test/bpf/filter.c | 53 +++++ app/test/bpf/load.c | 62 ++++++ app/test/bpf/meson.build | 53 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 451 +++++++++++++++++++++++++++++++++++++++ lib/bpf/bpf_pkt.c | 84 ++++---- lib/bpf/meson.build | 2 - lib/bpf/rte_bpf.h | 11 +- 8 files changed, 677 insertions(+), 41 deletions(-) create mode 100644 app/test/bpf/filter.c create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
Signature · 1 line
2.51.0
PATCH v3 1/5 bpf: add allocation annotations to functions
In commit 80da7efbb4c4 ("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.
--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
lib/bpf/rte_bpf.h +8 -3
@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus
@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF
@@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. *
@@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus }
Signature · 1 line
2.51.0
RE: [PATCH v3 1/5] bpf: add allocation annotations to functions
Quoted 46 lines
In commit
80da7efbb4c4("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)lib/bpf/rte_bpf.h +6
-2@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF @@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. *
lib/bpf/rte_bpf.h struct bpf_program;
Quoted 10 lines
lib/bpf/rte_bpf.h +2
-2@@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus } --
Quoted 1 line
2.51.0
PATCH v3 2/5 bpf: use bulk free on filtered packets
In reply to Stephen HemmingerWhen rte_pktmbuf_free_bulk is faster because it does single mempool operation rather than per-packet.
--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/bpf/bpf_pkt.c +1 -2
@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,if (drop !=0 ) {/* free filtered out mbufs */ -for (i =0 ; i != k; i++) -rte_pktmbuf_free (dr[i]); +rte_pktmbuf_free_bulk (dr, k); }else {/* copy filtered out mbufs beyond good ones */ for (i =0 ; i != k; i++)
Signature · 1 line
2.51.0
PATCH v3 3/5 bpf: add a test for BPF ELF load
In reply to Stephen HemmingerCreate an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.
Draft version made with Claude AI, but it didn't work.
--- app/test/bpf/load.c | 62 +++++++++++++++++ app/test/bpf/meson.build | 52 ++++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 147 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
app/test/bpf/load.c new file +62 -0
@@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function: +* 1. Reads values from input structure +* 2. Performs some computations +* 3. Writes results to output structure +* 4. Returns sum of values +*/ +__attribute__((section ("func" ), used)) +uint64_t +test_func (struct dummy_vect8 *arg) +{ +uint64_t sum =0 ; +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; + +/* Calculate sum */ + sum = arg->in[0 ].u64; + sum += arg->in[0 ].u32; + sum += arg->in[0 ].u16; + sum += arg->in[0 ].u8; + sum += v32; + sum += v64; + +return sum; +}
app/test/bpf/meson.build new file +52 -0
52 lines not on this page · raw message
app/test/meson.build +2 -0
@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_h
app/test/test_bpf.c +147 -0
165 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v3 4/5 bpf: add test for Rx and Tx filtering
In reply to Stephen HemmingerNew test using null device to test filtering with BPF.
--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 304 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 app/test/bpf/filter.c
app/test/bpf/filter.c new file +53 -0
@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}
app/test/bpf/meson.build +1 -0
@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progs
app/test/test_bpf.c +304 -0
325 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v3 5/5 bpf: remove use of VLA
In reply to Stephen HemmingerReplace variable length array with two part loop which applies filter in bursts up to 32 packets.
--- lib/bpf/bpf_pkt.c | 81 ++++++++++++++++++++++++++------------------- lib/bpf/meson.build | 2 -- 2 files changed, 47 insertions(+), 36 deletions(-)
lib/bpf/bpf_pkt.c +47 -34
@@ -157,13 +157,16 @@ bpf_eth_cbh_add(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)/* * BPF packet processing routines. */ +#define BPF_FILTER_BURST 32ustatic inline uint32_t apply_filter (struct rte_mbuf *mb[],const uint64_t rc[],uint32_t num,uint32_t drop) {uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf *dr[BPF_FILTER_BURST ]; + +RTE_ASSERT (num <=BPF_FILTER_BURST );for (i =0 , j =0 , k =0 ; i != num; i++) {
@@ -191,65 +194,75 @@ static inline uint32_t pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint32_t i; -void *dp[num]; -uint64_t rc[num]; + uint32_t matched = 0; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +void *dp[BPF_FILTER_BURST ]; +uint64_t rc[BPF_FILTER_BURST ]; -for (i =0 ; i != num; i++) - dp[i] = rte_pktmbuf_mtod(mb[i], void *); +for (uint32_t j =0 ; j < burst_sz; j++) + dp[j] = rte_pktmbuf_mtod(mb[i + j], void *); -rte_bpf_exec_burst (bpf, dp, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, dp, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -void *dp; -uint64_t rc[num]; - - n =0 ; -for (i =0 ; i != num; i++) { - dp = rte_pktmbuf_mtod(mb[i], void *); - rc[i] = jit->func (dp); - n += (rc[i] ==0 ); - } +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; + +for (uint32_t j =0 ; j < burst_sz; j++) { + void *dp = rte_pktmbuf_mtod(mb[i + j], void *); -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); + rc[j] = jit->func (dp); + } -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_vm (const struct rte_bpf *bpf,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint64_t rc[num]; +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -rte_bpf_exec_burst (bpf, (void **)mb, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, (void **)mb, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -uint64_t rc[num]; +uint32_t matched =0 ; - n =0 ; -for (i =0 ; i != num; i++) { - rc[i] = jit->func (mb[i]); - n += (rc[i] ==0 ); - } +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); +for (uint32_t j =0 ; j < burst_sz; j++) + rc[j] = jit->func (mb[i + j]); -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }/*
lib/bpf/meson.build +0 -2
@@ -7,8 +7,6 @@ if is_windows subdir_done() endif -cflags += no_wvla_cflag - if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_32') build = false reason = 'not supported on 32-bit x86'
Signature · 1 line
2.51.0
RE: [PATCH v3 5/5] bpf: remove use of VLA
Quoted 23 lines
Replace variable length array with two part loop which applies filter in bursts up to 32 packets.
Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/bpf_pkt.c | 81 ++++++++++++++++++++++++++------------------- lib/bpf/meson.build | 2 -- 2 files changed, 47 insertions(+), 36 deletions(-)lib/bpf/bpf_pkt.c +1
-0@@ -157,13 +157,16 @@ bpf_eth_cbh_add(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)/* * BPF packet processing routines. */ +#define BPF_FILTER_BURST 32u static inline uint32_t
lib/bpf/bpf_pkt.c bpf_eth_cbh_add(struct bpf_eth_cbh *cbh, uint16_t · line 172
Quoted 10 lines
lib/bpf/bpf_pkt.c +3
-1apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num, uint32_t drop) { uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf *dr[BPF_FILTER_BURST ]; + +RTE_ASSERT (num <=BPF_FILTER_BURST );for (i =0 , j =0 , k =0 ; i != num; i++) {
After looking at the code more closely - it might not be that straightforward as I thought: apply_filter() groups 'good' packets consequently, and for 'bad' packets does one of two things:
a) if drop != 0, then it frees them out (RX filter)
b) if drop == 0, then it places them after 'good' packets (TX filter) While a) is simple to do with fixed length array (we just need read and write index), to support b) - we need to do something more awkward, i.e. - walk mb[] in backwards order, and/or keep copying bad packets to the end at each iteration.
All that looks quite complicated, so if for now you'll decide to switch back to alloca() based approach (first version of the patch) - I'll be ok with that.
Sorry for not spotting that earlier.
Konstantin
Quoted 126 lines
lib/bpf/bpf_pkt.c +43
-33@@ -191,65 +194,75 @@ static inline uint32_t pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint32_t i; -void *dp[num]; -uint64_t rc[num]; + uint32_t matched = 0; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +void *dp[BPF_FILTER_BURST ]; +uint64_t rc[BPF_FILTER_BURST ]; -for (i =0 ; i != num; i++) - dp[i] = rte_pktmbuf_mtod(mb[i], void *); +for (uint32_t j =0 ; j < burst_sz; j++) + dp[j] = rte_pktmbuf_mtod(mb[i + j], void *); -rte_bpf_exec_burst (bpf, dp, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, dp, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -void *dp; -uint64_t rc[num]; - - n =0 ; -for (i =0 ; i != num; i++) { - dp = rte_pktmbuf_mtod(mb[i], void *); - rc[i] = jit->func (dp); - n += (rc[i] ==0 ); - } +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; + +for (uint32_t j =0 ; j < burst_sz; j++) { + void *dp = rte_pktmbuf_mtod(mb[i + j], void *); -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); + rc[j] = jit->func (dp); + } -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_vm (const struct rte_bpf *bpf,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint64_t rc[num]; +uint32_t matched =0 ; + +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -rte_bpf_exec_burst (bpf, (void **)mb, rc, num); -return apply_filter (mb, rc, num, drop); +rte_bpf_exec_burst (bpf, (void **)mb, rc, burst_sz); + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }static inline uint32_t pkt_filter_mb_jit (const struct rte_bpf_jit *jit,struct rte_mbuf *mb[],uint32_t num,uint32_t drop) { -uint32_t i, n; -uint64_t rc[num]; +uint32_t matched =0 ; - n =0 ; -for (i =0 ; i != num; i++) { - rc[i] = jit->func (mb[i]); - n += (rc[i] ==0 ); - } +for (uint32_t i =0 ; i < num; i +=BPF_FILTER_BURST ) { +uint32_t burst_sz =RTE_MIN (num,BPF_FILTER_BURST ); +uint64_t rc[BPF_FILTER_BURST ]; -if (n !=0 ) - num =apply_filter (mb, rc, num, drop); +for (uint32_t j =0 ; j < burst_sz; j++) + rc[j] = jit->func (mb[i + j]); -return num; + matched +=apply_filter (mb + i, rc, burst_sz, drop); + } +return matched; }/*
lib/bpf/meson.build +0
-3@@ -7,8 +7,6 @@ if is_windows subdir_done() endif -cflags += no_wvla_cflag - if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_32') build = false reason = 'not supported on 32-bit x86' --2.51.0
v4 cover BPF enhancements
In reply to Stephen HemmingerThis is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
It is disappointing that there were no examples or tests when the code was submitted.
v4 - review feedback - use alloca instead of burst to remove dependency on VLA
v3 - do not depend on xxd -n flag which is not present on older versions
Stephen Hemminger (5):
bpf: add allocation annotations to functions
bpf: use bulk free on filtered packets
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
bpf: replace use of VLA
app/test/bpf/filter.c | 53 +++++ app/test/bpf/load.c | 62 ++++++ app/test/bpf/meson.build | 53 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 451 +++++++++++++++++++++++++++++++++++++++ lib/bpf/bpf_pkt.c | 15 +- lib/bpf/rte_bpf.h | 11 +- 7 files changed, 636 insertions(+), 11 deletions(-) create mode 100644 app/test/bpf/filter.c create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
Signature · 1 line
2.51.0
PATCH v4 1/5 bpf: add allocation annotations to functions
In commit 80da7efbb4c4 ("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.
--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
lib/bpf/rte_bpf.h +8 -3
@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus
@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF
@@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. *
@@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus }
Signature · 1 line
2.51.0
RE: [PATCH v4 1/5] bpf: add allocation annotations to functions
Quoted 63 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 4 November 2025 16:07
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v4 1/5] bpf: add allocation annotations to functionsIn commit
80da7efbb4c4("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)lib/bpf/rte_bpf.h +8
-4@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF @@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. * @@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus } --
Quoted 1 line
2.51.0
PATCH v4 2/5 bpf: use bulk free on filtered packets
In reply to Stephen HemmingerWhen rte_pktmbuf_free_bulk is faster because it does single mempool operation rather than per-packet.
--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/bpf/bpf_pkt.c +1 -2
@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,if (drop !=0 ) {/* free filtered out mbufs */ -for (i =0 ; i != k; i++) -rte_pktmbuf_free (dr[i]); +rte_pktmbuf_free_bulk (dr, k); }else {/* copy filtered out mbufs beyond good ones */ for (i =0 ; i != k; i++)
Signature · 1 line
2.51.0
RE: [PATCH v4 2/5] bpf: use bulk free on filtered packets
Quoted 24 lines
When
rte_pktmbuf_free_bulkis faster because it does single mempool operation rather than per-packet.Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)lib/bpf/bpf_pkt.c +1
-2@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[],uint32_t num, if (drop != 0) {/* free filtered out mbufs */ - for (i = 0; i != k; i++) - rte_pktmbuf_free(dr[i]); +rte_pktmbuf_free_bulk (dr ,k ); } else {/* copy filtered out mbufs beyond good ones */
Quoted 2 lines
for (i = 0; i != k; i++)
--
Quoted 1 line
2.51.0
RE: [PATCH v4 2/5] bpf: use bulk free on filtered packets
In reply to Stephen HemmingerQuoted 30 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 4 November 2025 16:07
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v4 2/5] bpf: use bulk free on filtered packetsWhen
rte_pktmbuf_free_bulkis faster because it does single mempool operation rather than per-packet.Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)lib/bpf/bpf_pkt.c +1
-3@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,if (drop !=0 ) {/* free filtered out mbufs */ -for (i =0 ; i != k; i++) -rte_pktmbuf_free (dr[i]); +rte_pktmbuf_free_bulk (dr, k); }else {/* copy filtered out mbufs beyond good ones */ for (i =0 ; i != k; i++) --
Quoted 1 line
2.51.0
PATCH v4 3/5 bpf: add a test for BPF ELF load
In reply to Stephen HemmingerCreate an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.
Draft version made with Claude AI, but it didn't work.
--- app/test/bpf/load.c | 62 +++++++++++++++++ app/test/bpf/meson.build | 52 ++++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 147 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
app/test/bpf/load.c new file +62 -0
@@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function: +* 1. Reads values from input structure +* 2. Performs some computations +* 3. Writes results to output structure +* 4. Returns sum of values +*/ +__attribute__((section ("func" ), used)) +uint64_t +test_func (struct dummy_vect8 *arg) +{ +uint64_t sum =0 ; +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; + +/* Calculate sum */ + sum = arg->in[0 ].u64; + sum += arg->in[0 ].u32; + sum += arg->in[0 ].u16; + sum += arg->in[0 ].u8; + sum += v32; + sum += v64; + +return sum; +}
app/test/bpf/meson.build new file +52 -0
52 lines not on this page · raw message
app/test/meson.build +2 -0
@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_h
app/test/test_bpf.c +147 -0
165 lines not on this page · raw message
Signature · 1 line
2.51.0
RE: [PATCH v4 3/5] bpf: add a test for BPF ELF load
Thank you for doing this! See comments inline.
Quoted 40 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 4 November 2025 16:07
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v4 3/5] bpf: add a test for BPF ELF loadCreate an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.Draft version made with Claude AI, but it didn't work.
Signed-off-by Stephen Hemminger <[email protected]>--- app/test/bpf/load.c | 62 +++++++++++++++++ app/test/bpf/meson.build | 52 ++++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 147 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.buildapp/test/bpf/load.c new file +11
-0@@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset {
app/test/bpf/load.c line 21
Quoted 10 lines
app/test/bpf/load.c new file +10
-0+uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +};
Nit: I am sympathetic to re-using types already defined in test_bpf.c, but we don't really need 8 elements, and first two members in dummy_offset are no longer atomic which may or may not create problems someday. Perhaps it would be easier and less confusing to just define another type for this test.
app/test/bpf/load.c line 24
Quoted 3 lines
app/test/bpf/load.c new file +3
-0+ +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64);
Any reason not to make first argument struct dummy_vect8? Not that it mattered for the test, just creates an impression that something is not working or not trivial here.
app/test/bpf/load.c line 34
Quoted 10 lines
app/test/bpf/load.c new file +10
-0+ +/* +* Test BPF function that will be loaded from ELF +* This function: +* 1. Reads values from input structure +* 2. Performs some computations +* 3. Writes results to output structure +* 4. Returns sum of values +*/ +__attribute__((section("func"), used))
Do we need used here and in other section attributes? I do not thing clang -c will eliminate non-static/inline functions from the object file, or maybe I don't understand the purpose (perhaps needs a comment).
Quoted 18 lines
app/test/bpf/load.c new file +18
-0+uint64_t +test_func (struct dummy_vect8 *arg) +{ +uint64_t sum =0 ; +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; +
app/test/bpf/load.c line 62
Quoted 10 lines
app/test/bpf/load.c new file +10
-0+/* Calculate sum */ + sum = arg->in[0 ].u64; + sum += arg->in[0 ].u32; + sum += arg->in[0 ].u16; + sum += arg->in[0 ].u8; + sum += v32; + sum += v64; + +return sum; +}
// snip meson files, lgtm
Quoted 22 lines
app/test/test_bpf.c +3
-0@@ -6,6 +6,7 @@#include <string.h> #include <stdint.h> #include <inttypes.h> +#include <unistd.h> #include <rte_memory.h> #include <rte_debug.h> @@ -14,6 +15,8 @@#include <rte_random.h> #include <rte_byteorder.h> #include <rte_errno.h> +#include <rte_bpf.h> +#include "test.h" #if !defined(RTE_LIB_BPF )@@ -3278,6 +3281,150 @@ test_bpf(void)REGISTER_FAST_TEST (bpf_autotest, true, true, test_bpf);
app/test/test_bpf.c test_bpf(void) · line 3292
Quoted 10 lines
app/test/test_bpf.c +9
-0+#ifdef TEST_BPF_ELF_LOAD + +/* +* Helper function to write BPF object data to temporary file. +* Returns temp file path on success, NULL on failure. +* Caller must free the returned path and unlink the file. +*/ +static char * +create_temp_bpf_file (const uint8_t *data,size_t size,const char *suffix)
Nit: last argument is not technically a suffix.
app/test/test_bpf.c test_bpf(void) · line 3298
Quoted 6 lines
app/test/test_bpf.c +6
-0+{ +char *tmpfile =NULL ; +int fd; +ssize_t written; + +if (asprintf (&tmpfile,"/tmp/dpdk_bpf_%s_XXXXXX" , suffix) <0 ) {
Not sure what the actual standard is, but I found in some previous discussions a suggestion to use $TMPDIR and only fall back to /tmp if it is not defined.
Also, maybe give it .o suffix and use mkstemps below?
Quoted 25 lines
app/test/test_bpf.c +25
-0+printf ("%s@%d: asprintf failed: %s\n" , + __func__, __LINE__,strerror (errno)); +return NULL ; + } + +/* Create and open temp file */ + fd =mkstemp (tmpfile); +if (fd <0 ) { +printf ("%s@%d: mkstemp(%s) failed: %s\n" , + __func__, __LINE__, tmpfile,strerror (errno)); +free (tmpfile); +return NULL ; + } + +/* Write BPF object data */ + written =write (fd, data, size); +close (fd); + +if (written != (ssize_t )size) { +printf ("%s@%d: write failed: %s\n" , + __func__, __LINE__,strerror (errno)); +unlink (tmpfile); +free (tmpfile); +return NULL ; + }
app/test/test_bpf.c test_bpf(void) · line 3333
Quoted 10 lines
app/test/test_bpf.c +10
-0+ +return tmpfile; +} + +#include "test_bpf_load.h" + +static int +test_bpf_elf_load (void ) +{ +uint8_t tbuf[sizeof (struct dummy_vect8 )];
Why do we have to use unit8_t array here instead of just struct dummy_vect8?
If we do have to, how is alignment ensured?
Quoted 47 lines
app/test/test_bpf.c +47
-0+const struct rte_bpf_xsym xsym[] = { + { + .name =RTE_STR (dummy_func1), + .type =RTE_BPF_XTYPE_FUNC , + .func = { + .val = (void *)dummy_func1, + .nb_args =3 , + .args = { + [0 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct dummy_offset ), + }, + [1 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (uint32_t ), + }, + [2 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (uint64_t ), + }, + }, + }, + }, + }; +int ret; + +/* Create temp file from embedded BPF object */ +char *tmpfile =create_temp_bpf_file (app_test_bpf_load_o, + app_test_bpf_load_o_len, +"load" ); +if (tmpfile ==NULL ) +return -1 ; + +/* Try to load BPF program from temp file */ +const struct rte_bpf_prm prm = { + .xsym = xsym, + .nb_xsym =RTE_DIM (xsym), + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (tbuf), + }, + }; +struct rte_bpf *bpf =rte_bpf_elf_load (&prm, tmpfile,"func" ); +TEST_ASSERT (bpf !=NULL ,"failed to load BPF from %s %d:%s" , + tmpfile, rte_errno,strerror (rte_errno)); + +/* Prepare test data */
app/test/test_bpf.c test_bpf(void) · line 3390
Quoted 10 lines
app/test/test_bpf.c +10
-0+struct dummy_vect8 *dv = (struct dummy_vect8 *)tbuf; +memset (dv,0 ,sizeof (*dv)); + dv->in[0 ].u64 = (int32_t )TEST_FILL_1 ; + dv->in[0 ].u32 = dv->in[0 ].u64; + dv->in[0 ].u16 = dv->in[0 ].u64; + dv->in[0 ].u8 = dv->in[0 ].u64; + +/* Execute loaded BPF program */ +uint64_t sum =rte_bpf_exec (bpf, tbuf); +TEST_ASSERT (sum !=0 ,"BPF execution returned: %" PRIu64, sum);
Could we be more specific with the value we expect?
Quoted 10 lines
app/test/test_bpf.c +10
-0+ +/* Test JIT if available */ +struct rte_bpf_jit jit; + ret =rte_bpf_get_jit (bpf, &jit); +TEST_ASSERT (ret ==0 ,"rte_bpf_get_jit failed: %d" , ret); + +if (jit.func !=NULL ) { +memset (dv,0 ,sizeof (*dv)); + dv->in[0 ].u64 = (int32_t )TEST_FILL_1 ; + dv->in[0 ].u32 = dv->in[0 ].u64;
app/test/test_bpf.c test_bpf(void) · line 3410
Quoted 10 lines
app/test/test_bpf.c +10
-0+ dv->in[0 ].u16 = dv->in[0 ].u64; + dv->in[0 ].u8 = dv->in[0 ].u64; + +uint64_t jsum = jit.func (tbuf); +TEST_ASSERT_EQUAL (sum, jsum,"BPF JIT execution difference" ); + } + +rte_bpf_destroy (bpf); +unlink (tmpfile); +free (tmpfile);
If any of the above asserts fail, leak sanitizer will also be unhappy. Perhaps we could unlink tmpfile just after the rte_bpf_elf_load call, and unload BPF before verifying return values from calls.
Quoted 22 lines
app/test/test_bpf.c +17
-1+ +printf ("%s: ELF load test passed\n" , __func__); +return TEST_SUCCESS ; +} +#else + +static int +test_bpf_elf_load (void ) +{ +printf ("BPF compile not supported, skipping test\n" ); +return TEST_SKIPPED ; +} + +#endif /* !TEST_BPF_ELF_LOAD */ + +REGISTER_FAST_TEST (bpf_elf_load_autotest, true, true, test_bpf_elf_load); +#ifndef RTE_HAS_LIBPCAP static int --2.51.0
RE: [PATCH v4 3/5] bpf: add a test for BPF ELF load
P.S. When running with UB sanitizer:
RTE>>bpf_elf_load_autotest
BPF: resolve_xsym(40): EBPF_PSEUDO_CALL to external function: dummy_func1../../src/lib/bpf/bpf_validate.c:247:24: runtime error: signed integer overflow: 9223372036854775807 + 2147483647 cannot be represented in type 'long int'
#0 0x2b1fdca in eval_add ../../src/lib/bpf/bpf_validate.c:247
#1 0x2b2b9dc in eval_alu ../../src/lib/bpf/bpf_validate.c:691
#2 0x2b3b187 in evaluate ../../src/lib/bpf/bpf_validate.c:2446
#3 0x2b3bf0f in __rte_bpf_validate ../../src/lib/bpf/bpf_validate.c:2537
#4 0x2b19b33 in rte_bpf_load ../../src/lib/bpf/bpf_load.c:113
#5 0x2b47d6d in bpf_load_elf ../../src/lib/bpf/bpf_load_elf.c:288
#6 0x2b47fe8 in rte_bpf_elf_load ../../src/lib/bpf/bpf_load_elf.c:320
#7 0x66b4b8 in test_bpf_elf_load ../../src/app/test/test_bpf.c:3382Re: [PATCH v4 3/5] bpf: add a test for BPF ELF load
Quoted 5 lines · On Fri, 7 Nov 2025 17:45:51 +0000 Marat Khalili <[email protected]>…
P.S. When running with UB sanitizer:RTE>>bpf_elf_load_autotest
BPF:resolve_xsym(40):EBPF_PSEUDO_CALLto external function:dummy_func1../../src/lib/bpf/bpf_validate.c:247:24: runtime error: signed integer overflow: 9223372036854775807 + 2147483647 cannot be represented in type 'long int'
Quoted 8 lines
#0 0x2b1fdca in eval_add ../../src/lib/bpf/bpf_validate.c:247
#1 0x2b2b9dc in eval_alu ../../src/lib/bpf/bpf_validate.c:691
#2 0x2b3b187 in evaluate ../../src/lib/bpf/bpf_validate.c:2446
#3 0x2b3bf0f in __rte_bpf_validate ../../src/lib/bpf/bpf_validate.c:2537
#4 0x2b19b33 in rte_bpf_load ../../src/lib/bpf/bpf_load.c:113
#5 0x2b47d6d in bpf_load_elf ../../src/lib/bpf/bpf_load_elf.c:288
#6 0x2b47fe8 in rte_bpf_elf_load ../../src/lib/bpf/bpf_load_elf.c:320
#7 0x66b4b8 in test_bpf_elf_load ../../src/app/test/test_bpf.c:3382Hmm. looks like a pre-existing BPF library bug since this code was never tested before.
RE: [PATCH v4 3/5] bpf: add a test for BPF ELF load
Quoted 13 lines
Quoted 3 lines
RTE>>bpf_elf_load_autotest
BPF:resolve_xsym(40):EBPF_PSEUDO_CALLto external function:dummy_func1../../src/lib/bpf/bpf_validate.c:247:24: runtime error: signed integer overflow: 9223372036854775807+ 2147483647 cannot be represented in type 'long int'
Quoted 8 lines
#0 0x2b1fdca in eval_add ../../src/lib/bpf/bpf_validate.c:247 #1 0x2b2b9dc in eval_alu ../../src/lib/bpf/bpf_validate.c:691 #2 0x2b3b187 in evaluate ../../src/lib/bpf/bpf_validate.c:2446 #3 0x2b3bf0f in __rte_bpf_validate ../../src/lib/bpf/bpf_validate.c:2537 #4 0x2b19b33 in rte_bpf_load ../../src/lib/bpf/bpf_load.c:113 #5 0x2b47d6d in bpf_load_elf ../../src/lib/bpf/bpf_load_elf.c:288 #6 0x2b47fe8 in rte_bpf_elf_load ../../src/lib/bpf/bpf_load_elf.c:320 #7 0x66b4b8 in test_bpf_elf_load ../../src/app/test/test_bpf.c:3382
Quoted 2 lines
Hmm. looks like a pre-existing BPF library bug since this code was never tested before.
Yes, that's correct. Sent some fixes for this and some other issues to the mailing list just now, maybe we should treat these problems separately.
Re: [PATCH v4 3/5] bpf: add a test for BPF ELF load
In reply to Marat KhaliliQuoted 14 lines · On Fri, 7 Nov 2025 17:33:08 +0000 Marat Khalili <[email protected]>…
app/test/bpf/load.c line 34
Quoted 9 lines
app/test/bpf/load.c new file +9
-0+/* +* Test BPF function that will be loaded from ELF +* This function: +* 1. Reads values from input structure +* 2. Performs some computations +* 3. Writes results to output structure +* 4. Returns sum of values +*/ +__attribute__((section("func"), used))Do we need
usedhere and in other section attributes? I do not thingclang -cwill eliminate non-static/inline functions from the object file, or maybe I don't understand the purpose (perhaps needs a comment).
Quoted 2 lines
app/test/bpf/load.c line 36
Quoted 2 lines
app/test/bpf/load.c new file +2
-0+uint64_t +test_func (struct dummy_vect8 *arg)
The BPF loader in DPDK uses section names as the way to find functions. The original version of this code was autogenerated off of examples used in kernel BPF.
PATCH v4 4/5 bpf: add test for Rx and Tx filtering
In reply to Stephen HemmingerNew test using null device to test filtering with BPF.
--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 304 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 app/test/bpf/filter.c
app/test/bpf/filter.c new file +53 -0
@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}
app/test/bpf/meson.build +1 -0
@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progs
app/test/test_bpf.c +304 -0
325 lines not on this page · raw message
Signature · 1 line
2.51.0
RE: [PATCH v4 4/5] bpf: add test for Rx and Tx filtering
Thank you for doing this! See comments below.
Quoted 34 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 4 November 2025 16:07
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v4 4/5] bpf: add test for Rx and Tx filteringNew test using null device to test filtering with BPF.
Signed-off-by Stephen Hemminger <[email protected]>--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 304 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 app/test/bpf/filter.capp/test/bpf/filter.c new file +11
-0@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets
app/test/bpf/filter.c line 21
Quoted 10 lines
app/test/bpf/filter.c new file +10
-0+* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section("filter"), used))
Could this section (and function) be called better, e.g. filter_ip_tcp?
app/test/bpf/filter.c line 25
Quoted 4 lines
app/test/bpf/filter.c new file +4
-0+uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt;
Does pkt point to packet data or an mbuf? I would think we need mbuf functions here to access data. Not sure why the test is not failing though, maybe I misunderstand it.
Quoted 59 lines
app/test/bpf/filter.c new file +28
-0+ +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}app/test/bpf/meson.build +1
-0@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progsapp/test/test_bpf.c +6
-0@@ -16,6 +16,12 @@#include <rte_byteorder.h> #include <rte_errno.h> #include <rte_bpf.h> +#include <rte_ethdev.h> +#include <rte_bpf_ethdev.h> +#include <rte_bus_vdev.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_tcp.h> #include "test.h" @@ -3412,6 +3418,288 @@ test_bpf_elf_load(void)printf ("%s: ELF load test passed\n" , __func__);
app/test/test_bpf.c test_bpf_elf_load(void) · line 3428
Quoted 10 lines
app/test/test_bpf.c +8
-0return TEST_SUCCESS ; } + +#include "test_bpf_filter.h" + +#define BPF_TEST_BURST 128 +#define BPF_TEST_POOLSIZE 256/* at least 2x burst */ +#define BPF_TEST_PKT_LEN 64/* Ether + IP + TCP */ + +static int null_vdev_setup (const char *name,uint16_t *port,struct rte_mempool *pool)
Nit: The function type should be on a line by itself preceding the function (also in other places).
// snip packets setup functions
app/test/test_bpf.c test_bpf_elf_load(void) · line 3517
Quoted 2 lines
app/test/test_bpf.c +2
-0+static int bpf_tx_test (uint16_t port,const char *tmpfile,struct rte_mempool *pool, +const char *fname,uint32_t flags)
Nit: not sure fname is a good name for ELF section.
app/test/test_bpf.c test_bpf_elf_load(void) · line 3521
Quoted 4 lines
app/test/test_bpf.c +4
-0+{ +const struct rte_bpf_prm prm = { + .prog_arg = { + .type =RTE_BPF_ARG_PTR ,
Should this be RTE_BPF_ARG_PTR_MBUF and include buf_size?
Quoted 21 lines
app/test/test_bpf.c +21
-0+ .size = sizeof(struct rte_mbuf), + }, + }; +int ret; + +struct rte_mbuf *pkts[BPF_TEST_BURST ] = { }; + ret =rte_pktmbuf_alloc_bulk (pool, pkts,BPF_TEST_BURST ); +TEST_ASSERT (ret ==0 ,"failed to allocate mbufs" ); + +uint16_t expect =setup_mbufs (pkts,BPF_TEST_BURST ); + +/* Try to load BPF TX program from temp file */ + ret =rte_bpf_eth_tx_elf_load (port,0 , &prm, tmpfile, fname, flags); +TEST_ASSERT (ret ==0 ,"failed to load BPF filter from temp file %s: %d" , + tmpfile, ret); + +uint16_t sent =rte_eth_tx_burst (port,0 , pkts,BPF_TEST_BURST ); +TEST_ASSERT_EQUAL (sent, expect,"rte_eth_tx_burst returned: %u expected %u" , + sent, expect); + +/* The unsent packets should be dropped */
app/test/test_bpf.c test_bpf_elf_load(void) · line 3552
Quoted 10 lines
app/test/test_bpf.c +10
-0+rte_pktmbuf_free_bulk (pkts + sent,BPF_TEST_BURST - sent); + +/* Pool should have same number of packets avail */ +unsigned int avail =rte_mempool_avail_count (pool); +TEST_ASSERT_EQUAL (avail,BPF_TEST_POOLSIZE , +"Mempool available %u != %u leaks?" , avail,BPF_TEST_POOLSIZE ); + +rte_bpf_eth_tx_unload (port,0 ); +return TEST_SUCCESS ; +}
// snip test_bpf_elf_tx_load
app/test/test_bpf.c test_bpf_elf_load(void) · line 3604
Quoted 2 lines
app/test/test_bpf.c +2
-0+static int bpf_rx_test (uint16_t port,const char *tmpfile,struct rte_mempool *pool, +const char *fname,uint32_t flags,uint16_t expected)
Is rx test significantly different from tx one? Maybe add a comment what each test does?
// snip the rest, lgtm
Re: [PATCH v4 4/5] bpf: add test for Rx and Tx filtering
Quoted 49 lines · On Fri, 7 Nov 2025 17:30:51 +0000 Marat Khalili <[email protected]>…
Thank you for doing this! See comments below.
Quoted 34 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 4 November 2025 16:07
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v4 4/5] bpf: add test for Rx and Tx filteringNew test using null device to test filtering with BPF.
Signed-off-by Stephen Hemminger <[email protected]>--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 304 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 app/test/bpf/filter.capp/test/bpf/filter.c new file +11
-0@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets app/test/bpf/filter.c line 21
Quoted 10 lines
app/test/bpf/filter.c new file +10
-0+* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section("filter"), used))Could this section (and function) be called better, e.g.
filter_ip_tcp?
Quoted 8 lines
app/test/bpf/filter.c line 25
Quoted 4 lines
app/test/bpf/filter.c new file +4
-0+uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt;Does pkt point to packet data or an mbuf? I would think we need mbuf functions here to access data. Not sure why the test is not failing though, maybe I misunderstand it.
The API for DPDK BPF gives option for packet data or mbuf. See rte_bpf_arg_type.
This test uses packet data. Mostly because it is easier, using mbuf requires more overhead and BPF validator often doesn't like it.
RE: [PATCH v4 4/5] bpf: add test for Rx and Tx filtering
Quoted 9 lines
Quoted 3 lines
Does pkt point to packet data or an mbuf? I would think we need mbuf functions here to access data. Not sure why the test is not failing though, maybe I misunderstand it.
The API for DPDK BPF gives option for packet data or mbuf. See
rte_bpf_arg_type.This test uses packet data. Mostly because it is easier, using mbuf requires more overhead and BPF validator often doesn't like it.
Oh yes, I missed the bits in bpf_pkt.c that actually look at this and call rte_pktmbuf_mtod if necessary.
PATCH v4 5/5 bpf: replace use of VLA
In reply to Stephen HemmingerVariable length arrays are extension not required in C standard and not available in Windows.
Use alloca() instead of variable length array.
--- lib/bpf/bpf_pkt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
lib/bpf/bpf_pkt.c +6 -6
@@ -163,7 +163,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num, uint32_t drop) {uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf **dr =alloca (num *sizeof (struct rte_mbuf *));for (i =0 , j =0 , k =0 ; i != num; i++) {
@@ -192,8 +192,8 @@ pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) {uint32_t i; -void *dp[num]; -uint64_t rc[num]; +void **dp =alloca (num *sizeof (void *)); +uint64_t *rc =alloca (num *sizeof (uint64_t ));for (i =0 ; i != num; i++) dp[i] = rte_pktmbuf_mtod(mb[i], void *);
@@ -208,7 +208,7 @@ pkt_filter_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], {uint32_t i, n;void *dp; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;for (i =0 ; i != num; i++) {
@@ -227,7 +227,7 @@ static inline uint32_t pkt_filter_mb_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint64_t rc[num]; + uint64_t *rc = alloca(num * sizeof(uint64_t));rte_bpf_exec_burst (bpf, (void **)mb, rc, num);return apply_filter (mb, rc, num, drop);
@@ -238,7 +238,7 @@ pkt_filter_mb_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { uint32_t i, n; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;for (i =0 ; i != num; i++) {
Signature · 1 line
2.51.0
RE: [PATCH v4 5/5] bpf: replace use of VLA
Quoted 65 lines
Variable length arrays are extension not required in C standard and not available in Windows.
Usealloca()instead of variable length array.Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/bpf_pkt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)lib/bpf/bpf_pkt.c +3
-3@@ -163,7 +163,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[],uint32_t num, uint32_t drop) { uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf **dr =alloca (num *sizeof (struct rte_mbuf *));for (i =0 , j =0 , k =0 ; i != num; i++) {@@ -192,8 +192,8 @@ pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) {uint32_t i; -void *dp[num]; -uint64_t rc[num]; +void **dp =alloca (num *sizeof (void *)); +uint64_t *rc =alloca (num *sizeof (uint64_t ));for (i =0 ; i != num; i++)lib/bpf/bpf_pkt.c +1
-1dp[i] = rte_pktmbuf_mtod(mb[i], void *);@@ -208,7 +208,7 @@ pkt_filter_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], {uint32_t i, n;void *dp; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;lib/bpf/bpf_pkt.c +2
-2for (i =0 ; i != num; i++) {@@ -227,7 +227,7 @@ static inline uint32_t pkt_filter_mb_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint64_t rc[num]; + uint64_t *rc = alloca(num * sizeof(uint64_t));rte_bpf_exec_burst (bpf, (void **)mb, rc, num);return apply_filter (mb, rc, num, drop);@@ -238,7 +238,7 @@ pkt_filter_mb_jit(const struct rte_bpf_jit *jit, structrte_mbuf *mb[], uint32_t num, uint32_t drop) { uint32_t i, n; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;
Quoted 2 lines
for (i = 0; i != num; i++) {
--
Quoted 1 line
2.51.0
RE: [PATCH v4 5/5] bpf: replace use of VLA
In reply to Stephen HemmingerQuoted 68 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 4 November 2025 16:08
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v4 5/5] bpf: replace use of VLAVariable length arrays are extension not required in C standard and not available in Windows.
Usealloca()instead of variable length array.Signed-off-by Stephen Hemminger <[email protected]>--- lib/bpf/bpf_pkt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)lib/bpf/bpf_pkt.c +6
-7@@ -163,7 +163,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num, uint32_t drop) {uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf **dr =alloca (num *sizeof (struct rte_mbuf *));for (i =0 , j =0 , k =0 ; i != num; i++) {@@ -192,8 +192,8 @@ pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) {uint32_t i; -void *dp[num]; -uint64_t rc[num]; +void **dp =alloca (num *sizeof (void *)); +uint64_t *rc =alloca (num *sizeof (uint64_t ));for (i =0 ; i != num; i++) dp[i] = rte_pktmbuf_mtod(mb[i], void *);@@ -208,7 +208,7 @@ pkt_filter_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], {uint32_t i, n;void *dp; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;for (i =0 ; i != num; i++) {@@ -227,7 +227,7 @@ static inline uint32_t pkt_filter_mb_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint64_t rc[num]; + uint64_t *rc = alloca(num * sizeof(uint64_t));rte_bpf_exec_burst (bpf, (void **)mb, rc, num);return apply_filter (mb, rc, num, drop);@@ -238,7 +238,7 @@ pkt_filter_mb_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { uint32_t i, n; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;for (i =0 ; i != num; i++) { --
Quoted 1 line
2.51.0
v5 cover BPF cleanup and tests
In reply to Stephen HemmingerThis is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
It is disappointing that there were no examples or tests when the code was submitted.
v5 - review feedback
make it clear that load test is mirroring previous test and check the result.
v4 - review feedback - use alloca instead of burst to remove dependency on VLA
v3 - do not depend on xxd -n flag which is not present on older versions
Stephen Hemminger (5):
bpf: add allocation annotations to functions
bpf: use bulk free on filtered packets
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
bpf: replace use of VLA
app/test/bpf/filter.c | 53 +++++ app/test/bpf/load.c | 51 +++++ app/test/bpf/meson.build | 53 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 463 +++++++++++++++++++++++++++++++++++++++ lib/bpf/bpf_pkt.c | 15 +- lib/bpf/rte_bpf.h | 11 +- 7 files changed, 637 insertions(+), 11 deletions(-) create mode 100644 app/test/bpf/filter.c create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
Signature · 1 line
2.51.0
PATCH v5 1/5 bpf: add allocation annotations to functions
In commit 80da7efbb4c4 ("eal: annotate allocation functions") helper macros were added to provide compiler information to detect misuse of alloc/free combinations. This covered many of the functions in DPDK but missed the case of data allocated by BPF load functions.
--- lib/bpf/rte_bpf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
lib/bpf/rte_bpf.h +8 -3
@@ -18,6 +18,7 @@#include <rte_common.h> #include <rte_mbuf.h> +#include <rte_malloc.h> #include <bpf_def.h> #ifdef __cplusplus
@@ -128,7 +129,8 @@ rte_bpf_destroy(struct rte_bpf *bpf);* - ENOMEM - can't reserve enough memory */ struct rte_bpf * -rte_bpf_load (const struct rte_bpf_prm *prm); +rte_bpf_load (const struct rte_bpf_prm *prm) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 );/** * Create a new eBPF execution context and load BPF code from given ELF
@@ -152,7 +154,9 @@ rte_bpf_load(const struct rte_bpf_prm *prm);*/ struct rte_bpf *rte_bpf_elf_load (const struct rte_bpf_prm *prm,const char *fname, -const char *sname); +const char *sname) + __rte_malloc__rte_dealloc (rte_bpf_destroy,1 ); +/** * Execute given BPF bytecode. *
@@ -228,7 +232,8 @@ struct bpf_program;* - ENOTSUP - operation not supported */ struct rte_bpf_prm * -rte_bpf_convert (const struct bpf_program *prog); +rte_bpf_convert (const struct bpf_program *prog) + __rte_malloc __rte_dealloc_free; #ifdef __cplusplus }
Signature · 1 line
2.51.0
PATCH v5 2/5 bpf: use bulk free on filtered packets
In reply to Stephen HemmingerWhen rte_pktmbuf_free_bulk is faster because it does single mempool operation rather than per-packet.
--- lib/bpf/bpf_pkt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/bpf/bpf_pkt.c +1 -2
@@ -177,8 +177,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,if (drop !=0 ) {/* free filtered out mbufs */ -for (i =0 ; i != k; i++) -rte_pktmbuf_free (dr[i]); +rte_pktmbuf_free_bulk (dr, k); }else {/* copy filtered out mbufs beyond good ones */ for (i =0 ; i != k; i++)
Signature · 1 line
2.51.0
PATCH v5 3/5 bpf: add a test for BPF ELF load
In reply to Stephen HemmingerCreate an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.
Draft version made with Claude AI, but it didn't work.
--- app/test/bpf/load.c | 51 +++++++++++++ app/test/bpf/meson.build | 52 +++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 156 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
app/test/bpf/load.c new file +51 -0
@@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function is compiled version of code used in test_call1 +*/ +__attribute__((section ("call1" ), used)) +uint64_t +test_call1 (struct dummy_vect8 *arg) +{ +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; + + v64 += v32; +return v64; +}
app/test/bpf/meson.build new file +52 -0
52 lines not on this page · raw message
app/test/meson.build +2 -0
@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_h
app/test/test_bpf.c +156 -0
174 lines not on this page · raw message
Signature · 1 line
2.51.0
RE: [PATCH v5 3/5] bpf: add a test for BPF ELF load
Quoted 26 lines
app/test/bpf/load.c new file +20
-0@@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ];
app/test/bpf/load.c line 30
Quoted 10 lines
app/test/bpf/load.c new file +10
-0+struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function is compiled version of code used in test_call1 +*/
Thank you for this clarification, now it makes more sense why we use these structs.
// snip
Quoted 26 lines
app/test/bpf/meson.build new file +20
-0@@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2025 Stephen Hemminger <[email protected]> + +bpf_test_hdrs = [ ] + +# use clang to compile to bpf +clang_supports_bpf = false +clang = find_program('clang', required: false) +if clang.found() + clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', + check: false).returncode() == 0 +endif + +if not clang_supports_bpf + message('app/test_bpf: no BPF load tests missing clang BPF support') + subdir_done() + +endif + +xxd = find_program('xxd', required: false)
app/test/bpf/meson.build line 30
Quoted 10 lines
app/test/bpf/meson.build new file +10
-0+if not xxd.found() + message('app/test_bpf: missing xxd required to convert object to array') + subdir_done() +endif + +# BPF compiler flags +bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c'] + +# Enable test in test_bpf.c +cflags += '-DTEST_BPF_ELF_LOAD'
Sorry for not noticing it earlier, we probably want these tests depend on RTE_LIBRTE_BPF_ELF, otherwise they fail when libelf is not installed.
// snip
app/test/bpf/meson.build line 35
Quoted 5 lines
app/test/bpf/meson.build new file +5
-0+static int +test_bpf_elf_load(void) +{ + static const char test_section[] = "call1"; + uint8_t tbuf[sizeof(struct dummy_vect8)];
I still think alignment may become an issue here.
Re: [PATCH v5 3/5] bpf: add a test for BPF ELF load
Quoted 6 lines · On Mon, 10 Nov 2025 16:38:08 +0000 Marat Khalili <[email protected]>…
Quoted 6 lines
app/test/bpf/meson.build new file +6
-0+xxd = find_program('xxd', required: false) +if not xxd.found() + message('app/test_bpf: missing xxd required to convert object to array') + subdir_done() +endif +
Quoted 8 lines
app/test/bpf/meson.build line 30
Quoted 5 lines
app/test/bpf/meson.build new file +5
-0+# BPF compiler flags +bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c'] + +# Enable test in test_bpf.c +cflags += '-DTEST_BPF_ELF_LOAD'Sorry for not noticing it earlier, we probably want these tests depend on
RTE_LIBRTE_BPF_ELF, otherwise they fail when libelf is not installed.
I tested without elf and it build ok.
Quoted 9 lines
// snip
app/test/test_bpf.c test_bpf(void) · line 3338
Quoted 5 lines
app/test/test_bpf.c +5
-0+static int +test_bpf_elf_load (void ) +{ +static const char test_section[] ="call1" ; +uint8_t tbuf[sizeof (struct dummy_vect8 )];I still think alignment may become an issue here.
On x86 alignment doesn't matter. But would be better to use a union to be safe.
RE: [PATCH v5 3/5] bpf: add a test for BPF ELF load
Quoted 4 lines
Quoted 2 lines
Sorry for not noticing it earlier, we probably want these tests depend on
RTE_LIBRTE_BPF_ELF, otherwise they fail when libelf is not installed.I tested without elf and it build ok.
Strange, meson tests fail here...
Re: [PATCH v5 3/5] bpf: add a test for BPF ELF load
In reply to Marat KhaliliQuoted 7 lines · On Mon, 10 Nov 2025 16:38:08 +0000 Marat Khalili <[email protected]>…
app/test/test_bpf.c test_bpf(void) · line 3338
Quoted 5 lines
app/test/test_bpf.c +5
-0+static int +test_bpf_elf_load (void ) +{ +static const char test_section[] ="call1" ; +uint8_t tbuf[sizeof (struct dummy_vect8 )];I still think alignment may become an issue here.
This was copied from earlier part (run_test).
RE: [PATCH v5 3/5] bpf: add a test for BPF ELF load
In reply to Stephen HemmingerQuoted 7 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Sunday 9 November 2025 20:08
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v5 3/5] bpf: add a test for BPF ELF load
Quoted 7 lines
Create an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.Draft version made with Claude AI, but it didn't work.
Signed-off-by Stephen Hemminger <[email protected]>
Don't know what's going on with the test without libelf, perhaps someone else needs to try.
Apart from this,
Quoted 320 lines
--- app/test/bpf/load.c | 51 +++++++++++++ app/test/bpf/meson.build | 52 +++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 156 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.buildapp/test/bpf/load.c new file +51
-0@@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function is compiled version of code used in test_call1 +*/ +__attribute__((section ("call1" ), used)) +uint64_t +test_call1 (struct dummy_vect8 *arg) +{ +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; + + v64 += v32; +return v64; +}app/test/bpf/meson.build new file +52
-0@@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2025 Stephen Hemminger <[email protected]> + +bpf_test_hdrs = [ ] + +# use clang to compile to bpf +clang_supports_bpf = false +clang = find_program('clang', required: false) +if clang.found() + clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', + check: false).returncode() == 0 +endif + +if not clang_supports_bpf + message('app/test_bpf: no BPF load tests missing clang BPF support') + subdir_done() + +endif + +xxd = find_program('xxd', required: false) +if not xxd.found() + message('app/test_bpf: missing xxd required to convert object to array') + subdir_done() +endif + +# BPF compiler flags +bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c'] + +# Enable test in test_bpf.c +cflags += '-DTEST_BPF_ELF_LOAD' + +# BPF sources to compile +bpf_progs = { + 'load' : 'test_bpf_load', +} + +foreach bpf_src, bpf_hdr : bpf_progs + # Compile BPF C source to object file + bpf_obj = custom_target(bpf_src + '_o', + input: bpf_src + '.c', + output: bpf_src + '.o', + command: [ clang, bpf_cflags, '@INPUT@', '-o', '@OUTPUT@']) + + # Convert object file to C header using xxd + bpf_test_h = custom_target(bpf_src + '_h', + input: bpf_obj, + output: bpf_hdr + '.h', + command: [ xxd, '-i', '@INPUT@', '@OUTPUT@']) + + resources += bpf_test_h + +endforeachapp/test/meson.build +2
-0@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_happ/test/test_bpf.c +156
-1@@ -6,6 +6,7 @@#include <string.h> #include <stdint.h> #include <inttypes.h> +#include <unistd.h> #include <rte_memory.h> #include <rte_debug.h> @@ -14,6 +15,8 @@#include <rte_random.h> #include <rte_byteorder.h> #include <rte_errno.h> +#include <rte_bpf.h> +#include "test.h" #if !defined(RTE_LIB_BPF )@@ -3278,6 +3281,159 @@ test_bpf(void)REGISTER_FAST_TEST (bpf_autotest, true, true, test_bpf); +#ifdef TEST_BPF_ELF_LOAD + +/* +* Helper function to write BPF object data to temporary file. +* Returns temp file path on success, NULL on failure. +* Caller must free the returned path and unlink the file. +*/ +static char * +create_temp_bpf_file (const uint8_t *data,size_t size,const char *name) +{ +char *tmpfile =NULL ; +int fd; +ssize_t written; + +if (asprintf (&tmpfile,"/tmp/dpdk_bpf_%s_XXXXXX.o" , name) <0 ) { +printf ("%s@%d: asprintf failed: %s\n" , + __func__, __LINE__,strerror (errno)); +return NULL ; + } + +/* Create and open temp file */ + fd =mkstemps (tmpfile,strlen (".o" )); +if (fd <0 ) { +printf ("%s@%d: mkstemps(%s) failed: %s\n" , + __func__, __LINE__, tmpfile,strerror (errno)); +free (tmpfile); +return NULL ; + } + +/* Write BPF object data */ + written =write (fd, data, size); +close (fd); + +if (written != (ssize_t )size) { +printf ("%s@%d: write failed: %s\n" , + __func__, __LINE__,strerror (errno)); +unlink (tmpfile); +free (tmpfile); +return NULL ; + } + +return tmpfile; +} + +#include "test_bpf_load.h" + +/* +* Test loading BPF program from an object file. +* This test uses same arguments as previous test_call1 example. +*/ +static int +test_bpf_elf_load (void ) +{ +static const char test_section[] ="call1" ; +uint8_t tbuf[sizeof (struct dummy_vect8 )]; +const struct rte_bpf_xsym xsym[] = { + { + .name =RTE_STR (dummy_func1), + .type =RTE_BPF_XTYPE_FUNC , + .func = { + .val = (void *)dummy_func1, + .nb_args =3 , + .args = { + [0 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct dummy_offset ), + }, + [1 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (uint32_t ), + }, + [2 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (uint64_t ), + }, + }, + }, + }, + }; +int ret; + +/* Create temp file from embedded BPF object */ +char *tmpfile =create_temp_bpf_file (app_test_bpf_load_o, + app_test_bpf_load_o_len, +"load" ); +if (tmpfile ==NULL ) +return -1 ; + +/* Try to load BPF program from temp file */ +const struct rte_bpf_prm prm = { + .xsym = xsym, + .nb_xsym =RTE_DIM (xsym), + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (tbuf), + }, + }; + +struct rte_bpf *bpf =rte_bpf_elf_load (&prm, tmpfile, test_section); +unlink (tmpfile); +free (tmpfile); + +TEST_ASSERT (bpf !=NULL ,"failed to load BPF %d:%s" , rte_errno,strerror (rte_errno)); + +/* Prepare test data */ +struct dummy_vect8 *dv = (struct dummy_vect8 *)tbuf; + +memset (dv,0 ,sizeof (*dv)); + dv->in[0 ].u64 = (int32_t )TEST_FILL_1 ; + dv->in[0 ].u32 = dv->in[0 ].u64; + dv->in[0 ].u16 = dv->in[0 ].u64; + dv->in[0 ].u8 = dv->in[0 ].u64; + +/* Execute loaded BPF program */ +uint64_t rc =rte_bpf_exec (bpf, tbuf); + ret =test_call1_check (rc, tbuf); +TEST_ASSERT (ret ==0 ,"test_call1_check failed: %d" , ret); + +/* Test JIT if available */ +struct rte_bpf_jit jit; + ret =rte_bpf_get_jit (bpf, &jit); +TEST_ASSERT (ret ==0 ,"rte_bpf_get_jit failed: %d" , ret); + +if (jit.func !=NULL ) { +memset (dv,0 ,sizeof (*dv)); + dv->in[0 ].u64 = (int32_t )TEST_FILL_1 ; + dv->in[0 ].u32 = dv->in[0 ].u64; + dv->in[0 ].u16 = dv->in[0 ].u64; + dv->in[0 ].u8 = dv->in[0 ].u64; + + rc = jit.func (tbuf); + ret =test_call1_check (rc, tbuf); +TEST_ASSERT (ret ==0 ,"jit test_call1_check failed: %d" , ret); + } + +rte_bpf_destroy (bpf); + +printf ("%s: ELF load test passed\n" , __func__); +return TEST_SUCCESS ; +} +#else + +static int +test_bpf_elf_load (void ) +{ +printf ("BPF compile not supported, skipping test\n" ); +return TEST_SKIPPED ; +} + +#endif /* !TEST_BPF_ELF_LOAD */ + +REGISTER_FAST_TEST (bpf_elf_load_autotest, true, true, test_bpf_elf_load); +#ifndef RTE_HAS_LIBPCAP static int --2.51.0
PATCH v5 4/5 bpf: add test for Rx and Tx filtering
In reply to Stephen HemmingerNew test using null device to test filtering with BPF.
--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 307 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 app/test/bpf/filter.c
app/test/bpf/filter.c new file +53 -0
@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}
app/test/bpf/meson.build +1 -0
@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progs
app/test/test_bpf.c +307 -0
328 lines not on this page · raw message
Signature · 1 line
2.51.0
RE: [PATCH v5 4/5] bpf: add test for Rx and Tx filtering
Quoted 10 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Sunday 9 November 2025 20:08
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev <[email protected]>
Subject: [PATCH v5 4/5] bpf: add test for Rx and Tx filteringNew test using null device to test filtering with BPF.
Signed-off-by Stephen Hemminger <[email protected]>
(Turns out there were some issues with the pre-existing code which I initially blamed on this patch set. Maybe some cleanup is needed.)
Quoted 415 lines
--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 307 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 app/test/bpf/filter.capp/test/bpf/filter.c new file +53
-0@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}app/test/bpf/meson.build +1
-0@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progsapp/test/test_bpf.c +307
-1@@ -16,6 +16,12 @@#include <rte_byteorder.h> #include <rte_errno.h> #include <rte_bpf.h> +#include <rte_ethdev.h> +#include <rte_bpf_ethdev.h> +#include <rte_bus_vdev.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_tcp.h> #include "test.h" @@ -3421,6 +3427,291 @@ test_bpf_elf_load(void)printf ("%s: ELF load test passed\n" , __func__);return TEST_SUCCESS ; } + +#include "test_bpf_filter.h" + +#define BPF_TEST_BURST 128 +#define BPF_TEST_POOLSIZE 256/* at least 2x burst */ +#define BPF_TEST_PKT_LEN 64/* Ether + IP + TCP */ + +static int null_vdev_setup (const char *name,uint16_t *port,struct rte_mempool *pool) +{ +int ret; + +/* Make a null device */ + ret =rte_vdev_init (name,NULL ); +TEST_ASSERT (ret ==0 ,"rte_vdev_init(%s) failed: %d" , name, ret); + + ret =rte_eth_dev_get_port_by_name (name, port); +TEST_ASSERT (ret ==0 ,"failed to get port id for %s: %d" , name, ret); + +struct rte_eth_conf conf = { }; + ret =rte_eth_dev_configure (*port,1 ,1 , &conf); +TEST_ASSERT (ret ==0 ,"failed to configure port %u: %d" , *port, ret); + +struct rte_eth_txconf txconf = { }; + ret =rte_eth_tx_queue_setup (*port,0 ,BPF_TEST_BURST ,SOCKET_ID_ANY , &txconf); +TEST_ASSERT (ret ==0 ,"failed to setup tx queue port %u: %d" , *port, ret); + +struct rte_eth_rxconf rxconf = { }; + ret =rte_eth_rx_queue_setup (*port,0 ,BPF_TEST_BURST ,SOCKET_ID_ANY , + &rxconf, pool); +TEST_ASSERT (ret ==0 ,"failed to setup rx queue port %u: %d" , *port, ret); + + ret =rte_eth_dev_start (*port); +TEST_ASSERT (ret ==0 ,"failed to start port %u: %d" , *port, ret); + +return 0 ; +} + +static unsigned int +setup_mbufs (struct rte_mbuf *burst[],unsigned int n) +{ +struct rte_ether_hdr eh = { + .ether_type =rte_cpu_to_be_16 (RTE_ETHER_TYPE_IPV4 ), + }; +const struct rte_ipv4_hdr iph = { + .version_ihl =RTE_IPV4_VHL_DEF , + .total_length =rte_cpu_to_be_16 (BPF_TEST_PKT_LEN -sizeof (eh)), + .time_to_live =IPDEFTTL , + .src_addr =rte_cpu_to_be_32 (ip_src_addr), + .dst_addr =rte_cpu_to_be_32 (ip_dst_addr), + }; +unsigned int tcp_count =0 ; + +rte_eth_random_addr (eh.dst_addr.addr_bytes); + +for (unsigned int i =0 ; i < n; i++) { +struct rte_mbuf *mb = burst[i]; + +/* Setup Ethernet header */ + *rte_pktmbuf_mtod(mb, struct rte_ether_hdr *) = eh; + +/* Setup IP header */ +struct rte_ipv4_hdr *ip + = rte_pktmbuf_mtod_offset(mb, struct rte_ipv4_hdr *, sizeof(eh)); + *ip = iph; + +if (rte_rand () &1 ) { +struct rte_udp_hdr *udp + = rte_pktmbuf_mtod_offset(mb, struct rte_udp_hdr *, +sizeof (eh) +sizeof (iph)); + + ip->next_proto_id =IPPROTO_UDP ; + *udp = (struct rte_udp_hdr ) { + .src_port =rte_cpu_to_be_16 (9 ),/* discard */ + .dst_port =rte_cpu_to_be_16 (9 ),/* discard */ + .dgram_len =BPF_TEST_PKT_LEN -sizeof (eh) -sizeof (iph), + }; + + }else { +struct rte_tcp_hdr *tcp + = rte_pktmbuf_mtod_offset(mb, struct rte_tcp_hdr *, +sizeof (eh) +sizeof (iph)); + + ip->next_proto_id =IPPROTO_TCP ; + *tcp = (struct rte_tcp_hdr ) { + .src_port =rte_cpu_to_be_16 (9 ),/* discard */ + .dst_port =rte_cpu_to_be_16 (9 ),/* discard */ + .tcp_flags =RTE_TCP_RST_FLAG , + }; + ++tcp_count; + } + } + +return tcp_count; +} + +static int bpf_tx_test (uint16_t port,const char *tmpfile,struct rte_mempool *pool, +const char *section,uint32_t flags) +{ +const struct rte_bpf_prm prm = { + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct rte_mbuf ), + }, + }; +int ret; + +struct rte_mbuf *pkts[BPF_TEST_BURST ] = { }; + ret =rte_pktmbuf_alloc_bulk (pool, pkts,BPF_TEST_BURST ); +TEST_ASSERT (ret ==0 ,"failed to allocate mbufs" ); + +uint16_t expect =setup_mbufs (pkts,BPF_TEST_BURST ); + +/* Try to load BPF TX program from temp file */ + ret =rte_bpf_eth_tx_elf_load (port,0 , &prm, tmpfile, section, flags); +TEST_ASSERT (ret ==0 ,"failed to load BPF filter from temp file %s: %d" , + tmpfile, ret); + +uint16_t sent =rte_eth_tx_burst (port,0 , pkts,BPF_TEST_BURST ); +TEST_ASSERT_EQUAL (sent, expect,"rte_eth_tx_burst returned: %u expected %u" , + sent, expect); + +/* The unsent packets should be dropped */ +rte_pktmbuf_free_bulk (pkts + sent,BPF_TEST_BURST - sent); + +/* Pool should have same number of packets avail */ +unsigned int avail =rte_mempool_avail_count (pool); +TEST_ASSERT_EQUAL (avail,BPF_TEST_POOLSIZE , +"Mempool available %u != %u leaks?" , avail,BPF_TEST_POOLSIZE ); + +rte_bpf_eth_tx_unload (port,0 ); +return TEST_SUCCESS ; +} + +/* Test loading a transmit filter which only allows IPv4 packets */ +static int +test_bpf_elf_tx_load (void ) +{ +static const char null_dev[] ="net_null_bpf0" ; +char *tmpfile =NULL ; +struct rte_mempool *mb_pool =NULL ; +uint16_t port =UINT16_MAX ; +int ret; + +printf ("%s start\n" , __func__); + +/* Make a pool for packets */ + mb_pool =rte_pktmbuf_pool_create ("bpf_tx_test_pool" ,BPF_TEST_POOLSIZE , +0 ,0 ,RTE_MBUF_DEFAULT_BUF_SIZE , +SOCKET_ID_ANY ); + + ret =null_vdev_setup (null_dev, &port, mb_pool); +if (ret !=0 ) + goto fail; + +/* Create temp file from embedded BPF object */ + tmpfile =create_temp_bpf_file (app_test_bpf_filter_o, app_test_bpf_filter_o_len,"tx" ); +if (tmpfile ==NULL ) + goto fail; + +/* Do test with VM */ + ret =bpf_tx_test (port, tmpfile, mb_pool,"filter" ,0 ); +if (ret !=0 ) + goto fail; + +/* Repeat with JIT */ + ret =bpf_tx_test (port, tmpfile, mb_pool,"filter" ,RTE_BPF_ETH_F_JIT ); +if (ret ==0 ) +printf ("%s: TX ELF load test passed\n" , __func__); + +fail: +if (tmpfile) { +unlink (tmpfile); +free (tmpfile); + } + +if (port !=UINT16_MAX ) +rte_vdev_uninit (null_dev); + +rte_mempool_free (mb_pool); + +return ret ==0 ?TEST_SUCCESS :TEST_FAILED ; +} + +/* Test loading a receive filter */ +static int bpf_rx_test (uint16_t port,const char *tmpfile,struct rte_mempool *pool, +const char *section,uint32_t flags,uint16_t expected) +{ +struct rte_mbuf *pkts[BPF_TEST_BURST ]; +const struct rte_bpf_prm prm = { + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct rte_mbuf ), + }, + }; +int ret; + +/* Load BPF program to drop all packets */ + ret =rte_bpf_eth_rx_elf_load (port,0 , &prm, tmpfile, section, flags); +TEST_ASSERT (ret ==0 ,"failed to load BPF filter from temp file %s: %d" , + tmpfile, ret); + +uint16_t rcvd =rte_eth_rx_burst (port,0 , pkts,BPF_TEST_BURST ); +TEST_ASSERT_EQUAL (rcvd, expected, +"rte_eth_rx_burst returned: %u expect: %u" , rcvd, expected); + +/* Drop the received packets */ +rte_pktmbuf_free_bulk (pkts, rcvd); + +rte_bpf_eth_rx_unload (port,0 ); + +/* Pool should now be full */ +unsigned int avail =rte_mempool_avail_count (pool); +TEST_ASSERT_EQUAL (avail,BPF_TEST_POOLSIZE , +"Mempool available %u != %u leaks?" , avail,BPF_TEST_POOLSIZE ); + +return TEST_SUCCESS ; +} + +/* Test loading a receive filters, first with drop all and then with allow all packets */ +static int +test_bpf_elf_rx_load (void ) +{ +static const char null_dev[] ="net_null_bpf0" ; +struct rte_mempool *pool =NULL ; +char *tmpfile =NULL ; +uint16_t port; +int ret; + +printf ("%s start\n" , __func__); + +/* Make a pool for packets */ + pool =rte_pktmbuf_pool_create ("bpf_rx_test_pool" ,2 *BPF_TEST_BURST , +0 ,0 ,RTE_MBUF_DEFAULT_BUF_SIZE , +SOCKET_ID_ANY ); +TEST_ASSERT (pool !=NULL ,"failed to create mempool" ); + + ret =null_vdev_setup (null_dev, &port, pool); +if (ret !=0 ) + goto fail; + +/* Create temp file from embedded BPF object */ + tmpfile =create_temp_bpf_file (app_test_bpf_filter_o, app_test_bpf_filter_o_len,"rx" ); +if (tmpfile ==NULL ) + goto fail; + +/* Do test with VM */ + ret =bpf_rx_test (port, tmpfile, pool,"drop" ,0 ,0 ); +if (ret !=0 ) + goto fail; + +/* Repeat with JIT */ + ret =bpf_rx_test (port, tmpfile, pool,"drop" ,RTE_BPF_ETH_F_JIT ,0 ); +if (ret !=0 ) + goto fail; + +/* Repeat with allow all */ + ret =bpf_rx_test (port, tmpfile, pool,"allow" ,0 ,BPF_TEST_BURST ); +if (ret !=0 ) + goto fail; + +/* Repeat with JIT */ + ret =bpf_rx_test (port, tmpfile, pool,"allow" ,RTE_BPF_ETH_F_JIT ,BPF_TEST_BURST ); +if (ret !=0 ) + goto fail; + +printf ("%s: RX ELF load test passed\n" , __func__); + +/* The filter should free the mbufs */ +unsigned int avail =rte_mempool_avail_count (pool); +TEST_ASSERT_EQUAL (avail,BPF_TEST_POOLSIZE , +"Mempool available %u != %u leaks?" , avail,BPF_TEST_POOLSIZE ); + +fail: +if (tmpfile) { +unlink (tmpfile); +free (tmpfile); + } + +if (port !=UINT16_MAX ) +rte_vdev_uninit (null_dev); + +rte_mempool_free (pool); + +return ret ==0 ?TEST_SUCCESS :TEST_FAILED ; +}#else static int@@ -3430,9 +3721,25 @@ test_bpf_elf_load(void) return TEST_SKIPPED; } +static int +test_bpf_elf_tx_load (void ) +{ +printf ("BPF compile not supported, skipping Tx test\n" ); +return TEST_SKIPPED ; +} + +static int +test_bpf_elf_rx_load (void ) +{ +printf ("BPF compile not supported, skipping Tx test\n" ); +return TEST_SKIPPED ; +} +#endif /* !TEST_BPF_ELF_LOAD */ REGISTER_FAST_TEST (bpf_elf_load_autotest, true, true, test_bpf_elf_load); +REGISTER_FAST_TEST (bpf_eth_tx_elf_load_autotest, true, true, test_bpf_elf_tx_load); +REGISTER_FAST_TEST (bpf_eth_rx_elf_load_autotest, true, true, test_bpf_elf_rx_load);#ifndef RTE_HAS_LIBPCAP --2.51.0
PATCH v5 5/5 bpf: replace use of VLA
In reply to Stephen HemmingerVariable length arrays are extension not required in C standard and not available in Windows.
Use alloca() instead of variable length array.
--- lib/bpf/bpf_pkt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
lib/bpf/bpf_pkt.c +6 -6
@@ -163,7 +163,7 @@ apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num, uint32_t drop) {uint32_t i, j, k; -struct rte_mbuf *dr[num]; +struct rte_mbuf **dr =alloca (num *sizeof (struct rte_mbuf *));for (i =0 , j =0 , k =0 ; i != num; i++) {
@@ -192,8 +192,8 @@ pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) {uint32_t i; -void *dp[num]; -uint64_t rc[num]; +void **dp =alloca (num *sizeof (void *)); +uint64_t *rc =alloca (num *sizeof (uint64_t ));for (i =0 ; i != num; i++) dp[i] = rte_pktmbuf_mtod(mb[i], void *);
@@ -208,7 +208,7 @@ pkt_filter_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], {uint32_t i, n;void *dp; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;for (i =0 ; i != num; i++) {
@@ -227,7 +227,7 @@ static inline uint32_t pkt_filter_mb_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { - uint64_t rc[num]; + uint64_t *rc = alloca(num * sizeof(uint64_t));rte_bpf_exec_burst (bpf, (void **)mb, rc, num);return apply_filter (mb, rc, num, drop);
@@ -238,7 +238,7 @@ pkt_filter_mb_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[], uint32_t num, uint32_t drop) { uint32_t i, n; -uint64_t rc[num]; +uint64_t *rc =alloca (num *sizeof (uint64_t )); n =0 ;for (i =0 ; i != num; i++) {
Signature · 1 line
2.51.0
Re: [PATCH v5 0/5] BPF cleanup and tests
In reply to Stephen Hemminger09/11/2025 21:07, Stephen Hemminger:
Quoted 14 lines
This is a series of fixes and tests for BPF functionality. The new tests for Tx and Rx filtering require the infrastructure to build a bpf program (clang) and the tool to convert object file to header (xxd). If missing the test will get skipped.
It is disappointing that there were no examples or tests when the code was submitted.
v5 - review feedback
make it clear that load test is mirroring previous test and check the result.v4 - review feedback - use alloca instead of burst to remove dependency on VLA
Quoted 8 lines
v3 - do not depend on xxd -n flag which is not present on older versions
Stephen Hemminger (5):
bpf: add allocation annotations to functions
bpf: use bulk free on filtered packets
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
bpf: replace use of VLA
I have these warnings:app/test/test_bpf.c: duplicated include: rte_bpf.h app/test/test_bpf.c: duplicated include: rte_ether.h app/test/test_bpf.c: duplicated include: rte_ip.h
Given some issues are reported by Marat with the test, I apply the patches which do not add new testing for now.
Please send a new version of the test when all is resolved.
v6 cover BPF tests
In reply to Stephen HemmingerAdd missing tests for BPF file loading functions.
This is subset of earlier patch.
v6 - rebase to main
- make sure that -ENOTSUP is handled
- remove duplicate includes
- combine all of the elf tests into one test instance
Stephen Hemminger (2):
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
app/test/bpf/filter.c | 53 +++++ app/test/bpf/load.c | 51 +++++ app/test/bpf/meson.build | 53 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 476 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 635 insertions(+) create mode 100644 app/test/bpf/filter.c create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
Signature · 1 line
2.51.0
PATCH v6 1/2 bpf: add a test for BPF ELF load
Create an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.
If libelf library is not available, then DPDK bpf will return -ENOTSUP to the test and the test will be skipped.
--- app/test/bpf/load.c | 51 +++++++++++++ app/test/bpf/meson.build | 52 +++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 159 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build
app/test/bpf/load.c new file +51 -0
@@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function is compiled version of code used in test_call1 +*/ +__attribute__((section ("call1" ), used)) +uint64_t +test_call1 (struct dummy_vect8 *arg) +{ +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; + + v64 += v32; +return v64; +}
app/test/bpf/meson.build new file +52 -0
52 lines not on this page · raw message
app/test/meson.build +2 -0
@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_h
app/test/test_bpf.c +159 -0
177 lines not on this page · raw message
Signature · 1 line
2.51.0
RE: [PATCH v6 1/2] bpf: add a test for BPF ELF load
Quoted 334 lines
Create an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.If libelf library is not available, then DPDK bpf will return
-ENOTSUPto the test and the test will be skipped.Signed-off-by Stephen Hemminger <[email protected]>Acked-by Marat Khalili <[email protected]>--- app/test/bpf/load.c | 51 +++++++++++++ app/test/bpf/meson.build | 52 +++++++++++++ app/test/meson.build | 2 + app/test/test_bpf.c | 159 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.buildapp/test/bpf/load.c new file +51
-0@@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* +* BPF program for testing rte_bpf_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* Match the structures from test_bpf.c */ +struct dummy_offset { +uint64_t u64; +uint32_t u32; +uint16_t u16; +uint8_t u8; +} __attribute__((packed)); + +struct dummy_vect8 { +struct dummy_offset in[8 ]; +struct dummy_offset out[8 ]; +}; + +/* External function declaration - provided by test via xsym */ +extern void dummy_func1 (const void *p,uint32_t *v32,uint64_t *v64); + +/* +* Test BPF function that will be loaded from ELF +* This function is compiled version of code used in test_call1 +*/ +__attribute__((section ("call1" ), used)) +uint64_t +test_call1 (struct dummy_vect8 *arg) +{ +uint32_t v32; +uint64_t v64; + +/* Load input values */ + v32 = arg->in[0 ].u32; + v64 = arg->in[0 ].u64; + +/* Call external function */ +dummy_func1 (arg, &v32, &v64); + +/* Store results */ + arg->out[0 ].u32 = v32; + arg->out[0 ].u64 = v64; + + v64 += v32; +return v64; +}app/test/bpf/meson.build new file +10
-0@@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2025 Stephen Hemminger <[email protected]> + +bpf_test_hdrs = [ ] + +# use clang to compile to bpf +clang_supports_bpf = false +clang = find_program('clang', required: false) +if clang.found() + clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus',
app/test/bpf/meson.build new file +42
-0+ check: false).returncode() == 0 +endif + +if not clang_supports_bpf + message('app/test_bpf: no BPF load tests missing clang BPF support') + subdir_done() + +endif + +xxd = find_program('xxd', required: false) +if not xxd.found() + message('app/test_bpf: missing xxd required to convert object to array') + subdir_done() +endif + +# BPF compiler flags +bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c'] + +# Enable test in test_bpf.c +cflags += '-DTEST_BPF_ELF_LOAD' + +# BPF sources to compile +bpf_progs = { + 'load' : 'test_bpf_load', +} + +foreach bpf_src, bpf_hdr : bpf_progs + # Compile BPF C source to object file + bpf_obj = custom_target(bpf_src + '_o', + input: bpf_src + '.c', + output: bpf_src + '.o', + command: [ clang, bpf_cflags, '@INPUT@', '-o', '@OUTPUT@']) + + # Convert object file to C header using xxd + bpf_test_h = custom_target(bpf_src + '_h', + input: bpf_obj, + output: bpf_hdr + '.h', + command: [ xxd, '-i', '@INPUT@', '@OUTPUT@']) + + resources += bpf_test_h + +endforeachapp/test/meson.build +2
-0@@ -281,6 +281,8 @@ if not is_windows install: false) endif +subdir('bpf') + subdir('test_cfgfiles') resources += test_cfgfile_happ/test/test_bpf.c +159
-0@@ -6,6 +6,7 @@#include <string.h> #include <stdint.h> #include <inttypes.h> +#include <unistd.h> #include <rte_memory.h> #include <rte_debug.h> @@ -14,6 +15,7 @@#include <rte_random.h> #include <rte_byteorder.h> #include <rte_errno.h> +#include "test.h" #if !defined(RTE_LIB_BPF )@@ -3278,6 +3280,163 @@ test_bpf(void)REGISTER_FAST_TEST (bpf_autotest, true, true, test_bpf); +#ifdef TEST_BPF_ELF_LOAD + +/* +* Helper function to write BPF object data to temporary file. +* Returns temp file path on success, NULL on failure. +* Caller must free the returned path and unlink the file. +*/ +static char * +create_temp_bpf_file (const uint8_t *data,size_t size,const char *name) +{ +char *tmpfile =NULL ; +int fd; +ssize_t written; + +if (asprintf (&tmpfile,"/tmp/dpdk_bpf_%s_XXXXXX.o" , name) <0 ) { +printf ("%s@%d: asprintf failed: %s\n" , + __func__, __LINE__,strerror (errno)); +return NULL ; + } + +/* Create and open temp file */ + fd =mkstemps (tmpfile,strlen (".o" )); +if (fd <0 ) { +printf ("%s@%d: mkstemps(%s) failed: %s\n" , + __func__, __LINE__, tmpfile,strerror (errno)); +free (tmpfile); +return NULL ; + } + +/* Write BPF object data */ + written =write (fd, data, size); +close (fd); + +if (written != (ssize_t )size) { +printf ("%s@%d: write failed: %s\n" , + __func__, __LINE__,strerror (errno)); +unlink (tmpfile); +free (tmpfile); +return NULL ; + } + +return tmpfile; +} + +#include "test_bpf_load.h" + +/* +* Test loading BPF program from an object file. +* This test uses same arguments as previous test_call1 example. +*/ +static int +test_bpf_elf_load (void ) +{ +static const char test_section[] ="call1" ; +uint8_t tbuf[sizeof (struct dummy_vect8 )]; +const struct rte_bpf_xsym xsym[] = { + { + .name =RTE_STR (dummy_func1), + .type =RTE_BPF_XTYPE_FUNC , + .func = { + .val = (void *)dummy_func1, + .nb_args =3 , + .args = { + [0 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct dummy_offset ), + }, + [1 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (uint32_t ), + }, + [2 ] = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (uint64_t ), + }, + }, + }, + }, + }; +int ret; + +/* Create temp file from embedded BPF object */ +char *tmpfile =create_temp_bpf_file (app_test_bpf_load_o, + app_test_bpf_load_o_len, +"load" ); +if (tmpfile ==NULL ) +return -1 ; + +/* Try to load BPF program from temp file */ +const struct rte_bpf_prm prm = { + .xsym = xsym, + .nb_xsym =RTE_DIM (xsym), + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (tbuf), + }, + }; + +struct rte_bpf *bpf =rte_bpf_elf_load (&prm, tmpfile, test_section); +unlink (tmpfile); +free (tmpfile); + +/* If libelf support is not available */ +if (bpf ==NULL && rte_errno ==ENOTSUP ) +return TEST_SKIPPED ; + +TEST_ASSERT (bpf !=NULL ,"failed to load BPF %d:%s" , rte_errno,strerror (rte_errno)); + +/* Prepare test data */ +struct dummy_vect8 *dv = (struct dummy_vect8 *)tbuf; + +memset (dv,0 ,sizeof (*dv)); + dv->in[0 ].u64 = (int32_t )TEST_FILL_1 ; + dv->in[0 ].u32 = dv->in[0 ].u64; + dv->in[0 ].u16 = dv->in[0 ].u64; + dv->in[0 ].u8 = dv->in[0 ].u64; + +/* Execute loaded BPF program */ +uint64_t rc =rte_bpf_exec (bpf, tbuf); + ret =test_call1_check (rc, tbuf); +TEST_ASSERT (ret ==0 ,"test_call1_check failed: %d" , ret); + +/* Test JIT if available */ +struct rte_bpf_jit jit; + ret =rte_bpf_get_jit (bpf, &jit); +TEST_ASSERT (ret ==0 ,"rte_bpf_get_jit failed: %d" , ret); + +if (jit.func !=NULL ) { +memset (dv,0 ,sizeof (*dv)); + dv->in[0 ].u64 = (int32_t )TEST_FILL_1 ; + dv->in[0 ].u32 = dv->in[0 ].u64; + dv->in[0 ].u16 = dv->in[0 ].u64; + dv->in[0 ].u8 = dv->in[0 ].u64; + + rc = jit.func (tbuf); + ret =test_call1_check (rc, tbuf); +TEST_ASSERT (ret ==0 ,"jit test_call1_check failed: %d" , ret); + } + +rte_bpf_destroy (bpf); + +printf ("%s: ELF load test passed\n" , __func__); +return TEST_SUCCESS ; +} +#else + +static int +test_bpf_elf_load (void ) +{ +printf ("BPF compile not supported, skipping test\n" ); +return TEST_SKIPPED ; +} + +#endif /* !TEST_BPF_ELF_LOAD */ + +REGISTER_FAST_TEST (bpf_elf_load_autotest, true, true, test_bpf_elf_load); +#ifndef RTE_HAS_LIBPCAP
Quoted 2 lines
static int
--
Quoted 1 line
2.51.0
PATCH v6 2/2 bpf: add test for Rx and Tx filtering
In reply to Stephen HemmingerNew test using null device to test filtering with BPF.
If libelf library is not available, then DPDK bpf will return -ENOTSUP to the test and the test will be skipped.
--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 321 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 app/test/bpf/filter.c
app/test/bpf/filter.c new file +53 -0
@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}
app/test/bpf/meson.build +1 -0
@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progs
app/test/test_bpf.c +319 -2
336 lines not on this page · raw message
Signature · 1 line
2.51.0
RE: [PATCH v6 2/2] bpf: add test for Rx and Tx filtering
Quoted 444 lines
New test using null device to test filtering with BPF.
If libelf library is not available, then DPDK bpf will return
-ENOTSUPto the test and the test will be skipped.Signed-off-by Stephen Hemminger <[email protected]>Acked-by Marat Khalili <[email protected]>--- app/test/bpf/filter.c | 53 +++++++ app/test/bpf/meson.build | 1 + app/test/test_bpf.c | 321 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 app/test/bpf/filter.capp/test/bpf/filter.c new file +53
-0@@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* BPF TX filter program for testing rte_bpf_eth_tx_elf_load +*/ + +typedef unsigned char uint8_t ; +typedef unsigned short uint16_t ; +typedef unsigned int uint32_t ; +typedef unsigned long uint64_t ; + +/* +* Simple TX filter that accepts TCP packets +* +* BPF TX programs receive pointer to data and should return: +* 0 = drop packet +* non-zero = rx/tx packet +* +* This filter checks: +* 1. Packet is IPv4 +* 2. Protocol is TCP (IPPROTO_TCP = 6) +*/ +__attribute__((section ("filter" ), used)) +uint64_t +test_filter (void *pkt) +{ +uint8_t *data = pkt; + +/* Read version and IHL (first byte of IP header) */ +uint8_t version_ihl = data[14 ]; + +/* Check IPv4 version (upper 4 bits should be 4) */ +if ((version_ihl >>4 ) !=4 ) +return 0 ; + +/* Protocol field (byte 9 of IP header) must be TCP (6) */ +uint8_t proto = data[14 +9 ]; +return (proto ==6 ); +} + +__attribute__((section ("drop" ), used)) +uint64_t +test_drop (void *pkt) +{ + (void )pkt; +return 0 ; +} + +__attribute__((section ("allow" ), used)) +uint64_t +test_allow (void *pkt) +{ + (void )pkt; +return 1 ; +}app/test/bpf/meson.build +1
-0@@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD' # BPF sources to compile bpf_progs = { 'load' : 'test_bpf_load', + 'filter' : 'test_bpf_filter', } foreach bpf_src, bpf_hdr : bpf_progsapp/test/test_bpf.c +84
-0@@ -3424,10 +3424,326 @@ test_bpf_elf_load(void) printf("%s: ELF load test passed\n", __func__); return TEST_SUCCESS; } + +#include <rte_ethdev.h> +#include <rte_bpf_ethdev.h> +#include <rte_bus_vdev.h> + +#include "test_bpf_filter.h" + +#define BPF_TEST_BURST 128 +#define BPF_TEST_POOLSIZE 256 /* at least 2x burst */ +#define BPF_TEST_PKT_LEN 64 /* Ether + IP + TCP */ + +static int null_vdev_setup(const char *name, uint16_t *port, struct rte_mempool *pool) +{ + int ret; + + /* Make a null device */ + ret = rte_vdev_init(name, NULL); + TEST_ASSERT(ret == 0, "rte_vdev_init(%s) failed: %d", name, ret); + + ret = rte_eth_dev_get_port_by_name(name, port); + TEST_ASSERT(ret == 0, "failed to get port id for %s: %d", name, ret); + + struct rte_eth_conf conf = { }; + ret = rte_eth_dev_configure(*port, 1, 1, &conf); + TEST_ASSERT(ret == 0, "failed to configure port %u: %d", *port, ret); + + struct rte_eth_txconf txconf = { }; + ret = rte_eth_tx_queue_setup(*port, 0, BPF_TEST_BURST, SOCKET_ID_ANY, &txconf); + TEST_ASSERT(ret == 0, "failed to setup tx queue port %u: %d", *port, ret); + + struct rte_eth_rxconf rxconf = { }; + ret = rte_eth_rx_queue_setup(*port, 0, BPF_TEST_BURST, SOCKET_ID_ANY, + &rxconf, pool); + TEST_ASSERT(ret == 0, "failed to setup rx queue port %u: %d", *port, ret); + + ret = rte_eth_dev_start(*port); + TEST_ASSERT(ret == 0, "failed to start port %u: %d", *port, ret); + + return 0; +} + +static unsigned int +setup_mbufs(struct rte_mbuf *burst[], unsigned int n) +{ + struct rte_ether_hdr eh = { + .ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4), + }; + const struct rte_ipv4_hdr iph = { + .version_ihl = RTE_IPV4_VHL_DEF, + .total_length = rte_cpu_to_be_16(BPF_TEST_PKT_LEN - sizeof(eh)), + .time_to_live = IPDEFTTL, + .src_addr = rte_cpu_to_be_32(ip_src_addr), + .dst_addr = rte_cpu_to_be_32(ip_dst_addr), + }; + unsigned int tcp_count = 0; + + rte_eth_random_addr(eh.dst_addr.addr_bytes); + + for (unsigned int i = 0; i < n; i++) { + struct rte_mbuf *mb = burst[i]; + + /* Setup Ethernet header */ + *rte_pktmbuf_mtod(mb, struct rte_ether_hdr *) = eh; + + /* Setup IP header */ + struct rte_ipv4_hdr *ip + = rte_pktmbuf_mtod_offset(mb, struct rte_ipv4_hdr *, sizeof(eh)); + *ip = iph; + + if (rte_rand() & 1) { + struct rte_udp_hdr *udp + = rte_pktmbuf_mtod_offset(mb, struct rte_udp_hdr *, + sizeof(eh) + sizeof(iph)); + + ip->next_proto_id = IPPROTO_UDP; + *udp = (struct rte_udp_hdr) { + .src_port = rte_cpu_to_be_16(9), /* discard */ + .dst_port = rte_cpu_to_be_16(9), /* discard */ + .dgram_len = BPF_TEST_PKT_LEN - sizeof(eh) - sizeof(iph), + }; + + } else { + struct rte_tcp_hdr *tcp + = rte_pktmbuf_mtod_offset(mb, struct rte_tcp_hdr*,
app/test/test_bpf.c +5
-0+ sizeof(eh) + sizeof(iph)); + + ip->next_proto_id =IPPROTO_TCP ; + *tcp = (struct rte_tcp_hdr ) { + .src_port =rte_cpu_to_be_16 (9 ),/* discard */
app/test/test_bpf.c +1
-0+ .dst_port = rte_cpu_to_be_16(9), /* discard*/
app/test/test_bpf.c +24
-0+ .tcp_flags = RTE_TCP_RST_FLAG, + }; + ++tcp_count; + } + } + +return tcp_count; +} + +static int bpf_tx_test (uint16_t port,const char *tmpfile,struct rte_mempool *pool, +const char *section,uint32_t flags) +{ +const struct rte_bpf_prm prm = { + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct rte_mbuf ), + }, + }; +int ret; + +/* Try to load BPF TX program from temp file */ + ret =rte_bpf_eth_tx_elf_load (port,0 , &prm, tmpfile, section, flags); +if (ret !=0 ) { + printf("%s@%d: failed to load BPF filter from file=%serror=%d:(%s)\n",
app/test/test_bpf.c +1
-0+ __func__, __LINE__, tmpfile, rte_errno,
rte_strerror(rte_errno));app/test/test_bpf.c +11
-0+return ret; + } + +struct rte_mbuf *pkts[BPF_TEST_BURST ] = { }; + ret =rte_pktmbuf_alloc_bulk (pool, pkts,BPF_TEST_BURST ); +TEST_ASSERT (ret ==0 ,"failed to allocate mbufs" ); + +uint16_t expect =setup_mbufs (pkts,BPF_TEST_BURST ); + +uint16_t sent =rte_eth_tx_burst (port,0 , pkts,BPF_TEST_BURST ); + TEST_ASSERT_EQUAL(sent, expect, "rte_eth_tx_burst returned: %uexpected %u",
app/test/test_bpf.c +9
-0+ sent, expect); + +/* The unsent packets should be dropped */ +rte_pktmbuf_free_bulk (pkts + sent,BPF_TEST_BURST - sent); + +/* Pool should have same number of packets avail */ +unsigned int avail =rte_mempool_avail_count (pool); + TEST_ASSERT_EQUAL(avail, BPF_TEST_POOLSIZE, + "Mempool available %u != %u leaks?", avail,
BPF_TEST_POOLSIZE);app/test/test_bpf.c +19
-0+ +rte_bpf_eth_tx_unload (port,0 ); +return TEST_SUCCESS ; +} + +/* Test loading a transmit filter which only allows IPv4 packets */ +static int +test_bpf_elf_tx_load (void ) +{ +static const char null_dev[] ="net_null_bpf0" ; +char *tmpfile =NULL ; +struct rte_mempool *mb_pool =NULL ; +uint16_t port =UINT16_MAX ; +int ret; + +printf ("%s start\n" , __func__); + +/* Make a pool for packets */ + mb_pool = rte_pktmbuf_pool_create("bpf_tx_test_pool",
BPF_TEST_POOLSIZE,app/test/test_bpf.c +9
-0+0 ,0 ,RTE_MBUF_DEFAULT_BUF_SIZE , + SOCKET_ID_ANY); + + ret =null_vdev_setup (null_dev, &port, mb_pool); +if (ret !=0 ) + goto fail; + +/* Create temp file from embedded BPF object */ + tmpfile = create_temp_bpf_file(app_test_bpf_filter_o,
app_test_bpf_filter_o_len, "tx");app/test/test_bpf.c +49
-0+if (tmpfile ==NULL ) + goto fail; + +/* Do test with VM */ + ret =bpf_tx_test (port, tmpfile, mb_pool,"filter" ,0 ); +if (ret !=0 ) + goto fail; + +/* Repeat with JIT */ + ret =bpf_tx_test (port, tmpfile, mb_pool,"filter" ,RTE_BPF_ETH_F_JIT ); +if (ret ==0 ) +printf ("%s: TX ELF load test passed\n" , __func__); + +fail: +if (tmpfile) { +unlink (tmpfile); +free (tmpfile); + } + +if (port !=UINT16_MAX ) +rte_vdev_uninit (null_dev); + +rte_mempool_free (mb_pool); + +if (ret ==0 ) +return TEST_SUCCESS ; +else if (ret == -ENOTSUP ) +return TEST_SKIPPED ; +else +return TEST_FAILED ; +} + +/* Test loading a receive filter */ +static int bpf_rx_test (uint16_t port,const char *tmpfile,struct rte_mempool *pool, +const char *section,uint32_t flags,uint16_t expected) +{ +struct rte_mbuf *pkts[BPF_TEST_BURST ]; +const struct rte_bpf_prm prm = { + .prog_arg = { + .type =RTE_BPF_ARG_PTR , + .size =sizeof (struct rte_mbuf ), + }, + }; +int ret; + +/* Load BPF program to drop all packets */ + ret =rte_bpf_eth_rx_elf_load (port,0 , &prm, tmpfile, section, flags); +if (ret !=0 ) { + printf("%s@%d: failed to load BPF filter from file=%serror=%d:(%s)\n",
app/test/test_bpf.c +1
-0+ __func__, __LINE__, tmpfile, rte_errno,
rte_strerror(rte_errno));app/test/test_bpf.c +6
-0+return ret; + } + +uint16_t rcvd =rte_eth_rx_burst (port,0 , pkts,BPF_TEST_BURST ); + TEST_ASSERT_EQUAL(rcvd, expected, + "rte_eth_rx_burst returned: %u expect: %u", rcvd,expected);
app/test/test_bpf.c +10
-0+ +/* Drop the received packets */ +rte_pktmbuf_free_bulk (pkts, rcvd); + +rte_bpf_eth_rx_unload (port,0 ); + +/* Pool should now be full */ +unsigned int avail =rte_mempool_avail_count (pool); + TEST_ASSERT_EQUAL(avail, BPF_TEST_POOLSIZE, + "Mempool available %u != %u leaks?", avail,
BPF_TEST_POOLSIZE);app/test/test_bpf.c +28
-0+ +return TEST_SUCCESS ; +} + +/* Test loading a receive filters, first with drop all and then with allow all packets */ +static int +test_bpf_elf_rx_load (void ) +{ +static const char null_dev[] ="net_null_bpf0" ; +struct rte_mempool *pool =NULL ; +char *tmpfile =NULL ; +uint16_t port; +int ret; + +printf ("%s start\n" , __func__); + +/* Make a pool for packets */ + pool =rte_pktmbuf_pool_create ("bpf_rx_test_pool" ,2 *BPF_TEST_BURST , +0 ,0 ,RTE_MBUF_DEFAULT_BUF_SIZE , +SOCKET_ID_ANY ); +TEST_ASSERT (pool !=NULL ,"failed to create mempool" ); + + ret =null_vdev_setup (null_dev, &port, pool); +if (ret !=0 ) + goto fail; + +/* Create temp file from embedded BPF object */ + tmpfile = create_temp_bpf_file(app_test_bpf_filter_o,
app_test_bpf_filter_o_len, "rx");app/test/test_bpf.c +20
-0+if (tmpfile ==NULL ) + goto fail; + +/* Do test with VM */ + ret =bpf_rx_test (port, tmpfile, pool,"drop" ,0 ,0 ); +if (ret !=0 ) + goto fail; + +/* Repeat with JIT */ + ret =bpf_rx_test (port, tmpfile, pool,"drop" ,RTE_BPF_ETH_F_JIT ,0 ); +if (ret !=0 ) + goto fail; + +/* Repeat with allow all */ + ret =bpf_rx_test (port, tmpfile, pool,"allow" ,0 ,BPF_TEST_BURST ); +if (ret !=0 ) + goto fail; + +/* Repeat with JIT */ + ret = bpf_rx_test(port, tmpfile, pool, "allow", RTE_BPF_ETH_F_JIT,
BPF_TEST_BURST);app/test/test_bpf.c +9
-0+if (ret !=0 ) + goto fail; + +printf ("%s: RX ELF load test passed\n" , __func__); + +/* The filter should free the mbufs */ +unsigned int avail =rte_mempool_avail_count (pool); + TEST_ASSERT_EQUAL(avail, BPF_TEST_POOLSIZE, + "Mempool available %u != %u leaks?", avail,
BPF_TEST_POOLSIZE);app/test/test_bpf.c +31
-1+ +fail: +if (tmpfile) { +unlink (tmpfile); +free (tmpfile); + } + +if (port !=UINT16_MAX ) +rte_vdev_uninit (null_dev); + +rte_mempool_free (pool); + + return ret == 0 ? TEST_SUCCESS : TEST_FAILED; +} + + +static int +test_bpf_elf (void ) +{ +int ret; + + ret =test_bpf_elf_load (); +if (ret ==TEST_SUCCESS ) + ret =test_bpf_elf_tx_load (); +if (ret ==TEST_SUCCESS ) + ret =test_bpf_elf_rx_load (); + +return ret; +} +#else static int -test_bpf_elf_load (void ) +test_bpf_elf (void ) {printf ("BPF compile not supported, skipping test\n" );return TEST_SKIPPED ;
app/test/test_bpf.c test_bpf_elf_load(void)
Quoted 10 lines
app/test/test_bpf.c +2
-2@@ -3435,7 +3751,8 @@ test_bpf_elf_load(void)#endif /* !TEST_BPF_ELF_LOAD */ -REGISTER_FAST_TEST (bpf_elf_load_autotest, true, true, test_bpf_elf_load); + +REGISTER_FAST_TEST (bpf_elf_autotest, true, true, test_bpf_elf);#ifndef RTE_HAS_LIBPCAP --
Quoted 1 line
2.51.0
RE: [PATCH v6 0/2] BPF tests
In reply to Stephen HemmingerWith this version the new test succeeds or gets correctly skipped for me for all combinations of installed packages.
Quoted 31 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday 11 November 2025 22:56
To: [email protected]
Cc: Stephen Hemminger <[email protected]>
Subject: [PATCH v6 0/2] BPF testsAdd missing tests for BPF file loading functions. This is subset of earlier patch.
v6 - rebase to main
- make sure that-ENOTSUPis handled
- remove duplicate includes
- combine all of the elf tests into one test instanceStephen Hemminger (2):
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filteringapp/test/bpf/filter.c | 53 +++++ app/test/bpf/load.c | 51 +++++ app/test/bpf/meson.build | 53 +++++ app/test/meson.build | 2 + app/test/test_bpf.c | 476 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 635 insertions(+) create mode 100644 app/test/bpf/filter.c create mode 100644 app/test/bpf/load.c create mode 100644 app/test/bpf/meson.build--
2.51.0
Thomas Monjalon
AppliedRe: [PATCH v6 0/2] BPF tests
In reply to Stephen Hemminger11/11/2025 23:55, Stephen Hemminger:
Quoted 11 lines
Add missing tests for BPF file loading functions.
This is subset of earlier patch.v6 - rebase to main
- make sure that-ENOTSUPis handled
- remove duplicate includes
- combine all of the elf tests into one test instanceStephen Hemminger (2):
bpf: add a test for BPF ELF load
bpf: add test for Rx and Tx filtering
Applied, thanks.