From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 9FD0448ADA; Tue, 11 Nov 2025 10:46:21 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 668464026D; Tue, 11 Nov 2025 10:46:21 +0100 (CET) Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) by mails.dpdk.org (Postfix) with ESMTP id 109CE4026A for ; Tue, 11 Nov 2025 10:46:19 +0100 (CET) Received: from mail.maildlp.com (unknown [172.18.186.231]) by frasgout.his.huawei.com (SkyGuard) with ESMTPS id 4d5M9P4cLNzHnH83; Tue, 11 Nov 2025 17:46:01 +0800 (CST) Received: from frapema100001.china.huawei.com (unknown [7.182.19.23]) by mail.maildlp.com (Postfix) with ESMTPS id 82F5F1402F7; Tue, 11 Nov 2025 17:46:18 +0800 (CST) Received: from frapema500003.china.huawei.com (7.182.19.114) by frapema100001.china.huawei.com (7.182.19.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Tue, 11 Nov 2025 10:46:18 +0100 Received: from frapema500003.china.huawei.com ([7.182.19.114]) by frapema500003.china.huawei.com ([7.182.19.114]) with mapi id 15.02.1544.011; Tue, 11 Nov 2025 10:46:18 +0100 From: Marat Khalili To: Stephen Hemminger CC: Konstantin Ananyev , "dev@dpdk.org" Subject: RE: [PATCH v5 3/5] bpf: add a test for BPF ELF load Thread-Topic: [PATCH v5 3/5] bpf: add a test for BPF ELF load Thread-Index: AQHcUbTTQ0Sz/b9pTEyJ8a2Nx5TSDrTtO89w Date: Tue, 11 Nov 2025 09:46:18 +0000 Message-ID: References: <20251030173732.246435-1-stephen@networkplumber.org> <20251109200854.45942-1-stephen@networkplumber.org> <20251109200854.45942-4-stephen@networkplumber.org> In-Reply-To: <20251109200854.45942-4-stephen@networkplumber.org> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.206.137.70] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org > -----Original Message----- > From: Stephen Hemminger > Sent: Sunday 9 November 2025 20:08 > To: dev@dpdk.org > Cc: Stephen Hemminger ; Konstantin Ananyev > Subject: [PATCH v5 3/5] bpf: add a test for BPF ELF load >=20 > 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. >=20 > Draft version made with Claude AI, but it didn't work. >=20 > Signed-off-by: Stephen Hemminger Don't know what's going on with the test without libelf, perhaps someone else needs to try. Apart from this, Acked-by: Marat Khalili > --- > 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 >=20 > diff --git a/app/test/bpf/load.c b/app/test/bpf/load.c > new file mode 100644 > index 0000000000..a4d3d61d7a > --- /dev/null > +++ b/app/test/bpf/load.c > @@ -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 =3D arg->in[0].u32; > + v64 =3D arg->in[0].u64; > + > + /* Call external function */ > + dummy_func1(arg, &v32, &v64); > + > + /* Store results */ > + arg->out[0].u32 =3D v32; > + arg->out[0].u64 =3D v64; > + > + v64 +=3D v32; > + return v64; > +} > diff --git a/app/test/bpf/meson.build b/app/test/bpf/meson.build > new file mode 100644 > index 0000000000..b4f54aa976 > --- /dev/null > +++ b/app/test/bpf/meson.build > @@ -0,0 +1,52 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright 2025 Stephen Hemminger > + > +bpf_test_hdrs =3D [ ] > + > +# use clang to compile to bpf > +clang_supports_bpf =3D false > +clang =3D find_program('clang', required: false) > +if clang.found() > + clang_supports_bpf =3D run_command(clang, '-target', 'bpf', '--print= -supported-cpus', > + check: false).returncode() =3D=3D 0 > +endif > + > +if not clang_supports_bpf > + message('app/test_bpf: no BPF load tests missing clang BPF support') > + subdir_done() > + > +endif > + > +xxd =3D find_program('xxd', required: false) > +if not xxd.found() > + message('app/test_bpf: missing xxd required to convert object to arr= ay') > + subdir_done() > +endif > + > +# BPF compiler flags > +bpf_cflags =3D [ '-O2', '-target', 'bpf', '-g', '-c'] > + > +# Enable test in test_bpf.c > +cflags +=3D '-DTEST_BPF_ELF_LOAD' > + > +# BPF sources to compile > +bpf_progs =3D { > + 'load' : 'test_bpf_load', > +} > + > +foreach bpf_src, bpf_hdr : bpf_progs > + # Compile BPF C source to object file > + bpf_obj =3D 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 =3D custom_target(bpf_src + '_h', > + input: bpf_obj, > + output: bpf_hdr + '.h', > + command: [ xxd, '-i', '@INPUT@', '@OUTPUT@']) > + > + resources +=3D bpf_test_h > + > +endforeach > diff --git a/app/test/meson.build b/app/test/meson.build > index 8df8d3edd1..efec42a6bf 100644 > --- a/app/test/meson.build > +++ b/app/test/meson.build > @@ -281,6 +281,8 @@ if not is_windows > install: false) > endif >=20 > +subdir('bpf') > + > subdir('test_cfgfiles') >=20 > resources +=3D test_cfgfile_h > diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c > index 90e10d7d2c..a80f41a543 100644 > --- a/app/test/test_bpf.c > +++ b/app/test/test_bpf.c > @@ -6,6 +6,7 @@ > #include > #include > #include > +#include >=20 > #include > #include > @@ -14,6 +15,8 @@ > #include > #include > #include > +#include > + > #include "test.h" >=20 > #if !defined(RTE_LIB_BPF) > @@ -3278,6 +3281,159 @@ test_bpf(void) >=20 > REGISTER_FAST_TEST(bpf_autotest, true, true, test_bpf); >=20 > +#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 =3D 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 =3D 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 =3D write(fd, data, size); > + close(fd); > + > + if (written !=3D (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[] =3D "call1"; > + uint8_t tbuf[sizeof(struct dummy_vect8)]; > + const struct rte_bpf_xsym xsym[] =3D { > + { > + .name =3D RTE_STR(dummy_func1), > + .type =3D RTE_BPF_XTYPE_FUNC, > + .func =3D { > + .val =3D (void *)dummy_func1, > + .nb_args =3D 3, > + .args =3D { > + [0] =3D { > + .type =3D RTE_BPF_ARG_PTR, > + .size =3D sizeof(struct dummy_offset), > + }, > + [1] =3D { > + .type =3D RTE_BPF_ARG_PTR, > + .size =3D sizeof(uint32_t), > + }, > + [2] =3D { > + .type =3D RTE_BPF_ARG_PTR, > + .size =3D sizeof(uint64_t), > + }, > + }, > + }, > + }, > + }; > + int ret; > + > + /* Create temp file from embedded BPF object */ > + char *tmpfile =3D create_temp_bpf_file(app_test_bpf_load_o, > + app_test_bpf_load_o_len, > + "load"); > + if (tmpfile =3D=3D NULL) > + return -1; > + > + /* Try to load BPF program from temp file */ > + const struct rte_bpf_prm prm =3D { > + .xsym =3D xsym, > + .nb_xsym =3D RTE_DIM(xsym), > + .prog_arg =3D { > + .type =3D RTE_BPF_ARG_PTR, > + .size =3D sizeof(tbuf), > + }, > + }; > + > + struct rte_bpf *bpf =3D rte_bpf_elf_load(&prm, tmpfile, test_section); > + unlink(tmpfile); > + free(tmpfile); > + > + TEST_ASSERT(bpf !=3D NULL, "failed to load BPF %d:%s", rte_errno, strer= ror(rte_errno)); > + > + /* Prepare test data */ > + struct dummy_vect8 *dv =3D (struct dummy_vect8 *)tbuf; > + > + memset(dv, 0, sizeof(*dv)); > + dv->in[0].u64 =3D (int32_t)TEST_FILL_1; > + dv->in[0].u32 =3D dv->in[0].u64; > + dv->in[0].u16 =3D dv->in[0].u64; > + dv->in[0].u8 =3D dv->in[0].u64; > + > + /* Execute loaded BPF program */ > + uint64_t rc =3D rte_bpf_exec(bpf, tbuf); > + ret =3D test_call1_check(rc, tbuf); > + TEST_ASSERT(ret =3D=3D 0, "test_call1_check failed: %d", ret); > + > + /* Test JIT if available */ > + struct rte_bpf_jit jit; > + ret =3D rte_bpf_get_jit(bpf, &jit); > + TEST_ASSERT(ret =3D=3D 0, "rte_bpf_get_jit failed: %d", ret); > + > + if (jit.func !=3D NULL) { > + memset(dv, 0, sizeof(*dv)); > + dv->in[0].u64 =3D (int32_t)TEST_FILL_1; > + dv->in[0].u32 =3D dv->in[0].u64; > + dv->in[0].u16 =3D dv->in[0].u64; > + dv->in[0].u8 =3D dv->in[0].u64; > + > + rc =3D jit.func(tbuf); > + ret =3D test_call1_check(rc, tbuf); > + TEST_ASSERT(ret =3D=3D 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 >=20 > static int > -- > 2.51.0