Message ID | a74e58b9cf12bc9c64a024d18e6e58999202f853.1743336056.git.pav@iki.fi (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | [BlueZ] tools: add BPF timestamping tests | expand |
Context | Check | Description |
---|---|---|
tedd_an/pre-ci_am | success | Success |
tedd_an/BuildEll | success | Build ELL PASS |
tedd_an/BluezMake | success | Bluez Make PASS |
tedd_an/MakeCheck | success | Bluez Make Check PASS |
tedd_an/MakeDistcheck | fail | Make Distcheck FAIL: make[2]: *** No rule to make target 'tools/tester.skel.h', needed by 'distdir-am'. Stop. make[1]: *** [Makefile:12503: distdir] Error 2 make: *** [Makefile:12579: dist] Error 2 |
tedd_an/CheckValgrind | success | Check Valgrind PASS |
tedd_an/CheckSmatch | success | CheckSparse PASS |
tedd_an/bluezmakeextell | success | Make External ELL PASS |
tedd_an/ScanBuild | success | Scan Build PASS |
This is automated email and please do not reply to this email! Dear submitter, Thank you for submitting the patches to the linux bluetooth mailing list. This is a CI test results with your patch series: PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=948351 ---Test result--- Test Summary: CheckPatch PENDING 0.21 seconds GitLint PENDING 0.19 seconds BuildEll PASS 20.62 seconds BluezMake PASS 1469.53 seconds MakeCheck PASS 12.86 seconds MakeDistcheck FAIL 8.87 seconds CheckValgrind PASS 221.66 seconds CheckSmatch PASS 294.34 seconds bluezmakeextell PASS 101.95 seconds IncrementalBuild PENDING 0.22 seconds ScanBuild PASS 862.20 seconds Details ############################## Test: CheckPatch - PENDING Desc: Run checkpatch.pl script Output: ############################## Test: GitLint - PENDING Desc: Run gitlint Output: ############################## Test: MakeDistcheck - FAIL Desc: Run Bluez Make Distcheck Output: make[2]: *** No rule to make target 'tools/tester.skel.h', needed by 'distdir-am'. Stop. make[1]: *** [Makefile:12503: distdir] Error 2 make: *** [Makefile:12579: dist] Error 2 ############################## Test: IncrementalBuild - PENDING Desc: Incremental build with the patches in the series Output: --- Regards, Linux Bluetooth
Hi Pauli, On Sun, Mar 30, 2025 at 8:16 AM Pauli Virtanen <pav@iki.fi> wrote: > > Add some tests for BPF timestamping on Bluetooth sockets. > > These require additional tester kernel config, and at build time > the vmlinux image. > > Add cgroup mount to test-runner. > > Add documentation to tester config for this. > > Add tests: > > ISO Send - TX BPF Timestamping > --- > > Notes: > This probably needs to wait for the corresponding kernel patch series to > settle down. > > Makefile.tools | 29 ++++++ > configure.ac | 36 ++++++- > doc/test-runner.rst | 26 ++++- > doc/tester.config | 5 + > tools/iso-tester.c | 71 ++++++++++++- > tools/test-runner.c | 1 + > tools/tester.bpf.c | 92 +++++++++++++++++ > tools/tester.bpf.h | 7 ++ Usually we only do one . for source files, so names shall probably be tester-bpf.{c,h}. > tools/tester.h | 244 ++++++++++++++++++++++++++++++++++++-------- > 9 files changed, 463 insertions(+), 48 deletions(-) > create mode 100644 tools/tester.bpf.c > create mode 100644 tools/tester.bpf.h > > diff --git a/Makefile.tools b/Makefile.tools > index e60c31b1d..a35af54fc 100644 > --- a/Makefile.tools > +++ b/Makefile.tools > @@ -144,6 +144,7 @@ tools_l2cap_tester_SOURCES = tools/l2cap-tester.c tools/tester.h monitor/bt.h \ > emulator/smp.c > tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \ > src/libshared-glib.la $(GLIB_LIBS) > +tools_l2cap_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ > emulator/hciemu.h emulator/hciemu.c \ > @@ -191,6 +192,7 @@ tools_sco_tester_SOURCES = tools/sco-tester.c tools/tester.h monitor/bt.h \ > emulator/smp.c > tools_sco_tester_LDADD = lib/libbluetooth-internal.la \ > src/libshared-glib.la $(GLIB_LIBS) > +tools_sco_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h > tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS) > @@ -212,6 +214,7 @@ tools_iso_tester_SOURCES = tools/iso-tester.c tools/tester.h monitor/bt.h \ > emulator/smp.c > tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ > src/libshared-glib.la $(GLIB_LIBS) > +tools_iso_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ > emulator/hciemu.h emulator/hciemu.c \ > @@ -221,6 +224,32 @@ tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ > emulator/smp.c > tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \ > src/libshared-glib.la $(GLIB_LIBS) > + > +if TESTING_BPF > +tools/vmlinux.h: $(BPF_VMLINUX) > + bpftool btf dump file $(BPF_VMLINUX) format c > $@.new > + mv -f $@.new $@ > + > +tools/tester.bpf.o: $(srcdir)/tools/tester.bpf.c tools/vmlinux.h > + $(CLANG_BPF) -Wall -Werror -Os -g --target=bpf -Itools -c -o $@ $< > + > +tools/tester.skel.h: tools/tester.bpf.o > + bpftool gen skeleton $< > $@.new > + mv -f $@.new $@ > + > +tools_sco_tester_SOURCES += $(builddir)/tools/tester.skel.h > +tools_iso_tester_SOURCES += $(builddir)/tools/tester.skel.h > +tools_l2cap_tester_SOURCES += $(builddir)/tools/tester.skel.h > + > +tools_sco_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > +tools_iso_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > +tools_l2cap_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > + > +tools_sco_tester_LDADD += $(LIBBPF_LIBS) > +tools_iso_tester_LDADD += $(LIBBPF_LIBS) > +tools_l2cap_tester_LDADD += $(LIBBPF_LIBS) > +endif > + > endif > > if TOOLS > diff --git a/configure.ac b/configure.ac > index 0fa49f686..627b91e77 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -390,10 +390,38 @@ AC_ARG_ENABLE(testing, AS_HELP_STRING([--enable-testing], > AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes") > > if (test "${enable_testing}" = "yes"); then > - AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], > - [], [], [[#include <time.h> > - #include <linux/errqueue.h> > - #include <linux/net_tstamp.h>]]) > + AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], > + [], [], [[#include <time.h> > + #include <linux/errqueue.h> > + #include <linux/net_tstamp.h>]]) > +fi > + > +AC_ARG_ENABLE(testing-bpf, AS_HELP_STRING([--enable-testing-bpf[=PATH/TO/VMLINUX]], > + [enable BPF testing tools]), > + [enable_testing_bpf=yes; enable_testing_bpf_arg=${enableval}], > + [enable_bpf=no]) > +AM_CONDITIONAL(TESTING_BPF, test "${enable_testing_bpf}" = "yes") > + > +if (test "${enable_testing_bpf}" = "yes"); then > + AC_ARG_VAR(CLANG_BPF, [CLANG compiler (for BPF)]) > + AC_ARG_VAR(BPFTOOL, [bpftool]) > + AC_ARG_VAR(BPF_VMLINUX, [vmlinux image to use for BPF testing]) > + AC_PATH_PROG([CLANG_BPF], [clang], "no") > + if (test "${CLANG_BPF}" == "no"); then > + AC_MSG_ERROR([clang for BPF missing]) > + fi > + AC_PATH_PROG([BPFTOOL], [bpftool], "no") > + if (test "${BPFTOOL}" == "no"); then > + AC_MSG_ERROR([bpftool missing]) > + fi > + PKG_CHECK_MODULES(LIBBPF, libbpf >= 1.4, [], [AC_MSG_ERROR([libbpf missing])]) > + if (test "${enable_testing_bpf_arg}" != "yes"); then > + BPF_VMLINUX=${enable_testing_bpf_arg} > + elif (test "${BPF_VMLINUX}" = ""); then > + BPF_VMLINUX=/sys/kernel/btf/vmlinux > + fi > + AC_MSG_NOTICE([using BPF_VMLINUX=${BPF_VMLINUX} for BPF testing]) > + AC_DEFINE(HAVE_BPF, 1, [Define to 1 if bpf testing is required]) > fi > > AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental], > diff --git a/doc/test-runner.rst b/doc/test-runner.rst > index 423a9379c..549b2bcba 100644 > --- a/doc/test-runner.rst > +++ b/doc/test-runner.rst > @@ -91,8 +91,8 @@ Bluetooth > > CONFIG_UHID=y > > -Lock debuging > -------------- > +Lock debugging > +-------------- > > To catch locking related issues the following set of kernel config > options may be useful: > @@ -110,6 +110,19 @@ options may be useful: > CONFIG_DEBUG_MUTEXES=y > CONFIG_KASAN=y > > +BPF testing > +----------- > + > +For BPF related tests: > + > +.. code-block:: > + > + CONFIG_BPF=y > + CONFIG_BPF_JIT=y > + CONFIG_CGROUPS=y > + CONFIG_CGROUP_BPF=y > + > + > EXAMPLES > ======== > > @@ -127,6 +140,15 @@ Running a specific test of mgmt-tester > > $ tools/test-runner -k /pathto/bzImage -- tools/mgmt-tester -s "<name>" > > +Compiling and running BPF tests > +------------------------------- > + > +.. code-block:: > + > + $ ./configure --enable-testing --enable-testing-bpf=/home/me/linux/vmlinux > + $ make > + $ tools/test-runner -k /home/me/linux/arch/x86_64/boot/bzImage -- tools/iso-tester -s BPF > + > Running bluetoothctl with emulated controller > --------------------------------------------- > > diff --git a/doc/tester.config b/doc/tester.config > index 099eddc79..548e4c629 100644 > --- a/doc/tester.config > +++ b/doc/tester.config > @@ -57,3 +57,8 @@ CONFIG_PROVE_RCU=y > CONFIG_LOCKDEP=y > CONFIG_DEBUG_MUTEXES=y > CONFIG_KASAN=y > + > +CONFIG_BPF=y > +CONFIG_BPF_JIT=y > +CONFIG_CGROUPS=y > +CONFIG_CGROUP_BPF=y > diff --git a/tools/iso-tester.c b/tools/iso-tester.c > index 350775fdd..da164c771 100644 > --- a/tools/iso-tester.c > +++ b/tools/iso-tester.c > @@ -517,6 +517,9 @@ struct iso_client_data { > > /* Disable BT_POLL_ERRQUEUE before enabling TX timestamping */ > bool no_poll_errqueue; > + > + /* Enable BPF TX timestamping */ > + bool bpf_timestamping; Let's keep it short, bpf_ts. > }; > > typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io, > @@ -697,6 +700,13 @@ static void test_pre_setup(const void *test_data) > return; > } > > +#ifndef HAVE_BPF > + if (isodata && isodata->bpf_timestamping) { > + if (tester_pre_setup_skip_by_default()) > + return; > + } > +#endif > + > data->mgmt = mgmt_new_default(); > if (!data->mgmt) { > tester_warn("Failed to setup management interface"); > @@ -738,6 +748,8 @@ static void test_post_teardown(const void *test_data) > NULL, NULL, NULL); > } > > + tx_tstamp_teardown(&data->tx_ts); > + > hciemu_unref(data->hciemu); > data->hciemu = NULL; > } > @@ -776,7 +788,7 @@ static void test_data_free(void *test_data) > user->accept_reason = reason; \ > tester_add_full(name, data, \ > test_pre_setup, setup, func, NULL, \ > - test_post_teardown, 2, user, test_data_free); \ > + test_post_teardown, 3, user, test_data_free); \ > } while (0) > > #define test_iso(name, data, setup, func) \ > @@ -1094,6 +1106,19 @@ static const struct iso_client_data connect_send_tx_no_poll_timestamping = { > .no_poll_errqueue = true, > }; > > +static const struct iso_client_data connect_send_tx_bpf_timestamping = { > + .qos = QOS_16_2_1, > + .expect_err = 0, > + .send = &send_16_2_1, > + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | > + SOF_TIMESTAMPING_OPT_ID | > + SOF_TIMESTAMPING_TX_SOFTWARE | > + SOF_TIMESTAMPING_TX_COMPLETION), > + .repeat_send = 1, > + .repeat_send_pre_ts = 2, > + .bpf_timestamping = true, > +}; > + > static const struct iso_client_data listen_16_2_1_recv = { > .qos = QOS_16_2_1, > .expect_err = 0, > @@ -2254,6 +2279,24 @@ static gboolean iso_fail_errqueue(GIOChannel *io, GIOCondition cond, > return FALSE; > } > > +static gboolean iso_bpf_io(GIOChannel *io, GIOCondition cond, > + gpointer user_data) > +{ > + struct test_data *data = user_data; > + int err; > + > + err = tx_tstamp_bpf_process(&data->tx_ts, &data->step); > + if (err > 0) > + return TRUE; > + else if (err) > + tester_test_failed(); > + else if (!data->step) > + tester_test_passed(); > + > + data->io_id[2] = 0; > + return FALSE; > +} > + > static gboolean iso_timer_errqueue(gpointer user_data) > { > struct test_data *data = user_data; > @@ -2293,7 +2336,24 @@ static void iso_tx_timestamping(struct test_data *data, GIOChannel *io) > > sk = g_io_channel_unix_get_fd(io); > > - if (isodata->no_poll_errqueue) { > + if (isodata->bpf_timestamping) { > + GIOChannel *bpf_io; > + > + err = tx_tstamp_bpf_start(&data->tx_ts, sk); > + if (err < 0) { > + tester_warn("BPF timestamping failed: %s (%d)", > + strerror(-err), err); > + tester_test_failed(); > + return; > + } > + > + bpf_io = g_io_channel_unix_new(err); > + data->io_id[2] = g_io_add_watch(bpf_io, > + G_IO_IN | G_IO_ERR | G_IO_HUP, > + iso_bpf_io, data); > + g_io_channel_unref(bpf_io); > + return; > + } else if (isodata->no_poll_errqueue) { > uint32_t flag = 0; > > err = setsockopt(sk, SOL_BLUETOOTH, BT_POLL_ERRQUEUE, > @@ -2393,6 +2453,8 @@ static void iso_send(struct test_data *data, GIOChannel *io) > for (count = 0; count < isodata->repeat_send + 1; ++count) > iso_send_data(data, io); > > + g_io_channel_set_close_on_unref(io, FALSE); > + > if (isodata->bcast) { > tester_test_passed(); > return; > @@ -3647,6 +3709,11 @@ int main(int argc, char *argv[]) > &connect_send_tx_no_poll_timestamping, setup_powered, > test_connect); > > + /* Test TX timestamping using BPF */ > + test_iso("ISO Send - TX BPF Timestamping", > + &connect_send_tx_bpf_timestamping, setup_powered, > + test_connect); > + > test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered, > test_listen); > > diff --git a/tools/test-runner.c b/tools/test-runner.c > index 1d770330c..84c0f90ad 100644 > --- a/tools/test-runner.c > +++ b/tools/test-runner.c > @@ -127,6 +127,7 @@ static const struct { > { "tmpfs", "/run", "mode=0755", MS_NOSUID|MS_NODEV|MS_STRICTATIME }, > { "tmpfs", "/tmp", NULL, 0 }, > { "debugfs", "/sys/kernel/debug", NULL, 0 }, > + { "cgroup2", "/sys/fs/cgroup", NULL, 0 }, > { } > }; > > diff --git a/tools/tester.bpf.c b/tools/tester.bpf.c > new file mode 100644 > index 000000000..9aed99aad > --- /dev/null > +++ b/tools/tester.bpf.c > @@ -0,0 +1,92 @@ > +#include "vmlinux.h" > + > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > +#include <bpf/bpf_core_read.h> > + > +#ifndef AF_BLUETOOTH > +#define AF_BLUETOOTH 31 > +#endif > + > +#ifndef SOL_SOCKET > +#define SOL_SOCKET 1 > +#endif > + > +#include "tester.bpf.h" > + > +struct { > + __uint(type, BPF_MAP_TYPE_RINGBUF); > + __uint(max_entries, 256 * 1024); > +} tx_tstamp_events SEC(".maps"); > + > +static inline void tx_tstamp_event_emit(__u32 type, __u32 tskey) > +{ > + struct tx_tstamp_event *event; > + > + event = bpf_ringbuf_reserve(&tx_tstamp_events, sizeof(*event), 0); > + if (!event) > + return; > + > + event->type = type; > + event->id = tskey; > + event->nsec = bpf_ktime_get_ns(); > + > + bpf_ringbuf_submit(event, 0); > +} > + > +SEC("sockops") > +int skops_sockopt(struct bpf_sock_ops *skops) > +{ > + struct bpf_sock *bpf_sk = skops->sk; > + struct bpf_sock_ops_kern *skops_kern; > + struct skb_shared_info *shinfo; > + const struct sk_buff *skb; > + > + if (!bpf_sk) > + return 1; > + > + if (skops->family != AF_BLUETOOTH) > + return 1; > + > + skops_kern = bpf_cast_to_kern_ctx(skops); > + skb = skops_kern->skb; > + shinfo = bpf_core_cast(skb->head + skb->end, struct skb_shared_info); > + > + switch (skops->op) { > + case BPF_SOCK_OPS_TSTAMP_SENDMSG_CB: > + bpf_sock_ops_enable_tx_tstamp(skops_kern, 0); > + break; > + case BPF_SOCK_OPS_TSTAMP_SCHED_CB: > + tx_tstamp_event_emit(SCM_TSTAMP_SCHED, shinfo->tskey); > + break; > + case BPF_SOCK_OPS_TSTAMP_SND_SW_CB: > + tx_tstamp_event_emit(SCM_TSTAMP_SND, shinfo->tskey); > + break; > + case BPF_SOCK_OPS_TSTAMP_ACK_CB: > + tx_tstamp_event_emit(SCM_TSTAMP_ACK, shinfo->tskey); > + break; > + case BPF_SOCK_OPS_TSTAMP_COMPLETION_CB: > + tx_tstamp_event_emit(SCM_TSTAMP_COMPLETION, shinfo->tskey); > + break; > + } > + > + return 1; > +} > + > +SEC("cgroup/setsockopt") > +int _setsockopt(struct bpf_sockopt *ctx) > +{ > + if (ctx->level == SOL_CUSTOM_TESTER) { > + int flag = SK_BPF_CB_TX_TIMESTAMPING; > + > + bpf_setsockopt(ctx->sk, SOL_SOCKET, > + SK_BPF_CB_FLAGS, &flag, sizeof(flag)); > + > + ctx->optlen = -1; > + return 1; > + } > + > + return 1; > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/tester.bpf.h b/tools/tester.bpf.h > new file mode 100644 > index 000000000..1b3d06bc8 > --- /dev/null > +++ b/tools/tester.bpf.h > @@ -0,0 +1,7 @@ > +struct tx_tstamp_event { > + __u32 type; > + __u32 id; > + __u64 nsec; > +}; > + > +#define SOL_CUSTOM_TESTER 0x89abcdef > diff --git a/tools/tester.h b/tools/tester.h > index 4e7d7226b..02d2f1d34 100644 > --- a/tools/tester.h > +++ b/tools/tester.h > @@ -11,13 +11,22 @@ > #include <stdlib.h> > #include <stdint.h> > #include <time.h> > +#include <fcntl.h> > +#include <sys/stat.h> > #include <sys/socket.h> > #include <linux/errqueue.h> > #include <linux/net_tstamp.h> > > #include <glib.h> > > -#define SEC_NSEC(_t) ((_t) * 1000000000LL) > +#ifdef HAVE_BPF > +#include <linux/bpf.h> > +#include <bpf/libbpf.h> > +#include "tester.bpf.h" > +#include "tester.skel.h" > +#endif > + > +#define SEC_NSEC(_t) ((_t) * 1000000000ULL) > #define TS_NSEC(_ts) (SEC_NSEC((_ts)->tv_sec) + (_ts)->tv_nsec) > > #if !HAVE_DECL_SOF_TIMESTAMPING_TX_COMPLETION > @@ -39,6 +48,12 @@ struct tx_tstamp_data { > unsigned int sent; > uint32_t so_timestamping; > bool stream; > +#ifdef HAVE_BPF > + struct tester_bpf *skel; > + struct ring_buffer *buf; > + int cgroup_fd; > + int bpf_err; > +#endif > }; > > static inline void tx_tstamp_init(struct tx_tstamp_data *data, > @@ -88,6 +103,51 @@ static inline int tx_tstamp_expect(struct tx_tstamp_data *data, size_t len) > return steps; > } > > +static inline int tx_tstamp_validate(struct tx_tstamp_data *data, > + const char *source, uint32_t type, uint32_t id, > + uint64_t nsec, uint64_t now) > +{ > + unsigned int i; > + > + if (now < nsec || now > nsec + SEC_NSEC(10)) { > + tester_warn("nonsense in timestamp"); > + return -EINVAL; > + } > + > + if (data->pos >= data->count) { > + tester_warn("Too many timestamps"); > + return -EINVAL; > + } > + > + /* Find first unreceived timestamp of the right type */ > + for (i = 0; i < data->count; ++i) { > + if (data->expect[i].type >= 0xffff) > + continue; > + > + if (type == data->expect[i].type) { > + data->expect[i].type = 0xffff; > + break; > + } > + } > + if (i == data->count) { > + tester_warn("Bad timestamp type %u", type); > + return -EINVAL; > + } > + > + if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && > + id != data->expect[i].id) { > + tester_warn("Bad timestamp id %u", id); > + return -EINVAL; > + } > + > + tester_print("Got valid %s TX timestamp %u (type %u, id %u)", > + source, i, type, id); > + > + ++data->pos; > + > + return data->count - data->pos; > +} > + > static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) > { > unsigned char control[512]; > @@ -99,7 +159,6 @@ static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) > struct scm_timestamping *tss = NULL; > struct sock_extended_err *serr = NULL; > struct timespec now; > - unsigned int i; > > iov.iov_base = buf; > iov.iov_len = sizeof(buf); > @@ -159,42 +218,147 @@ static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) > > clock_gettime(CLOCK_REALTIME, &now); > > - if (TS_NSEC(&now) < TS_NSEC(tss->ts) || > - TS_NSEC(&now) > TS_NSEC(tss->ts) + SEC_NSEC(10)) { > - tester_warn("nonsense in timestamp"); > - return -EINVAL; > - } > - > - if (data->pos >= data->count) { > - tester_warn("Too many timestamps"); > - return -EINVAL; > - } > - > - /* Find first unreceived timestamp of the right type */ > - for (i = 0; i < data->count; ++i) { > - if (data->expect[i].type >= 0xffff) > - continue; > - > - if (serr->ee_info == data->expect[i].type) { > - data->expect[i].type = 0xffff; > - break; > - } > - } > - if (i == data->count) { > - tester_warn("Bad timestamp type %u", serr->ee_info); > - return -EINVAL; > - } > - > - if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && > - serr->ee_data != data->expect[i].id) { > - tester_warn("Bad timestamp id %u", serr->ee_data); > - return -EINVAL; > - } > - > - tester_print("Got valid TX timestamp %u (type %u, id %u)", i, > - serr->ee_info, serr->ee_data); > - > - ++data->pos; > - > - return data->count - data->pos; > + return tx_tstamp_validate(data, "socket", serr->ee_info, serr->ee_data, > + TS_NSEC(tss->ts), TS_NSEC(&now)); > } > + > + > +#ifdef HAVE_BPF > + > +static inline int tx_tstamp_event_handler(void *ctx, void *buf, size_t size) > +{ > + struct tx_tstamp_data *data = ctx; > + struct tx_tstamp_event *event = buf; > + struct timespec now; > + > + if (size < sizeof(*event)) { > + tester_warn("Bad BPF event"); > + return -EIO; > + } > + > + clock_gettime(CLOCK_MONOTONIC, &now); > + > + data->bpf_err = tx_tstamp_validate(data, "BPF", event->type, event->id, > + event->nsec, TS_NSEC(&now)); > + return data->bpf_err; > +} > + > +static inline int tx_tstamp_bpf_start(struct tx_tstamp_data *data, int sk) > +{ > + int flag; > + > + data->cgroup_fd = open("/sys/fs/cgroup", O_RDONLY); > + if (data->cgroup_fd < 0) { > + tester_warn("opening cgroup failed"); > + goto fail; > + } > + > + data->skel = tester_bpf__open_and_load(); > + if (!data->skel) > + goto fail; > + > + data->buf = ring_buffer__new( > + bpf_map__fd(data->skel->maps.tx_tstamp_events), > + tx_tstamp_event_handler, data, NULL); > + if (!data->buf) { > + tester_warn("ringbuffer failed"); > + goto fail; > + } > + > + if (tester_bpf__attach(data->skel)) { > + tester_warn("attach failed"); > + goto fail; > + } > + > + data->skel->links.skops_sockopt = > + bpf_program__attach_cgroup(data->skel->progs.skops_sockopt, > + data->cgroup_fd); > + if (!data->skel->links.skops_sockopt) { > + tester_warn("BFP sockops attach cgroup failed"); > + goto fail; > + } > + > + data->skel->links._setsockopt = > + bpf_program__attach_cgroup(data->skel->progs._setsockopt, > + data->cgroup_fd); > + if (!data->skel->links._setsockopt) { > + tester_warn("BFP setsockopt attach cgroup failed"); > + goto fail; > + } > + > + flag = 0; > + if (setsockopt(sk, SOL_CUSTOM_TESTER, 0, &flag, sizeof(flag))) { > + tester_warn("BFP setsockopt failed"); > + goto fail; > + } > + > + tester_print("BPF test program attached"); > + return ring_buffer__epoll_fd(data->buf); > + > +fail: > + if (data->buf) > + ring_buffer__free(data->buf); > + if (data->skel) > + tester_bpf__destroy(data->skel); > + if (data->cgroup_fd > 0) > + close(data->cgroup_fd); > + data->buf = NULL; > + data->skel = NULL; > + data->cgroup_fd = 0; > + return -EIO; > +} > + > +static inline bool tx_tstamp_bpf_process(struct tx_tstamp_data *data, int *step) > +{ > + int err; > + > + err = ring_buffer__consume(data->buf); > + if (err < 0) { > + data->bpf_err = err; > + } else if (step) { > + if (*step >= err) > + *step -= err; > + else > + data->bpf_err = -E2BIG; > + } > + > + return data->bpf_err; > +} > + > +static inline void tx_tstamp_teardown(struct tx_tstamp_data *data) > +{ > + if (data->skel) > + tester_bpf__detach(data->skel); > + if (data->cgroup_fd > 0) > + close(data->cgroup_fd); > + if (data->buf) > + ring_buffer__free(data->buf); > + if (data->skel) { > + tester_bpf__destroy(data->skel); > + tester_print("BPF test program removed"); > + } > + > + data->buf = NULL; > + data->skel = NULL; > + data->cgroup_fd = 0; > +} > + > +#else > + > +static inline int tx_tstamp_bpf_start(struct tx_tstamp_data *data, int sk) > +{ > + tester_warn("Tester compiled without BPF"); > + return -EOPNOTSUPP; > +} > + > +static inline bool tx_tstamp_bpf_process(struct tx_tstamp_data *data, int *step) > +{ > + return false; > +} > + > +static inline void tx_tstamp_teardown(struct tx_tstamp_data *data) > +{ > +} > + > +#endif > + > -- > 2.49.0 Btw, does BPF also use the error queue? Or better yet can we avoid waiting up bluetoothd id the process/pipewire use the BPF mode to be notified of packet completion?
Hi Luiz, ma, 2025-03-31 kello 11:59 -0400, Luiz Augusto von Dentz kirjoitti: > Hi Pauli, > > On Sun, Mar 30, 2025 at 8:16 AM Pauli Virtanen <pav@iki.fi> wrote: > > > > Add some tests for BPF timestamping on Bluetooth sockets. > > > > These require additional tester kernel config, and at build time > > the vmlinux image. > > > > Add cgroup mount to test-runner. > > > > Add documentation to tester config for this. > > > > Add tests: > > > > ISO Send - TX BPF Timestamping > > --- > > > > Notes: > > This probably needs to wait for the corresponding kernel patch series to > > settle down. > > > > Makefile.tools | 29 ++++++ > > configure.ac | 36 ++++++- > > doc/test-runner.rst | 26 ++++- > > doc/tester.config | 5 + > > tools/iso-tester.c | 71 ++++++++++++- > > tools/test-runner.c | 1 + > > tools/tester.bpf.c | 92 +++++++++++++++++ > > tools/tester.bpf.h | 7 ++ > > Usually we only do one . for source files, so names shall probably be > tester-bpf.{c,h}. Ok. The .bpf.c convention was used used in some places. > > tools/tester.h | 244 ++++++++++++++++++++++++++++++++++++-------- > > 9 files changed, 463 insertions(+), 48 deletions(-) > > create mode 100644 tools/tester.bpf.c > > create mode 100644 tools/tester.bpf.h > > > > diff --git a/Makefile.tools b/Makefile.tools > > index e60c31b1d..a35af54fc 100644 > > --- a/Makefile.tools > > +++ b/Makefile.tools > > @@ -144,6 +144,7 @@ tools_l2cap_tester_SOURCES = tools/l2cap-tester.c tools/tester.h monitor/bt.h \ > > emulator/smp.c > > tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \ > > src/libshared-glib.la $(GLIB_LIBS) > > +tools_l2cap_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > > > tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ > > emulator/hciemu.h emulator/hciemu.c \ > > @@ -191,6 +192,7 @@ tools_sco_tester_SOURCES = tools/sco-tester.c tools/tester.h monitor/bt.h \ > > emulator/smp.c > > tools_sco_tester_LDADD = lib/libbluetooth-internal.la \ > > src/libshared-glib.la $(GLIB_LIBS) > > +tools_sco_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > > > tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h > > tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS) > > @@ -212,6 +214,7 @@ tools_iso_tester_SOURCES = tools/iso-tester.c tools/tester.h monitor/bt.h \ > > emulator/smp.c > > tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ > > src/libshared-glib.la $(GLIB_LIBS) > > +tools_iso_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > > > tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ > > emulator/hciemu.h emulator/hciemu.c \ > > @@ -221,6 +224,32 @@ tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ > > emulator/smp.c > > tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \ > > src/libshared-glib.la $(GLIB_LIBS) > > + > > +if TESTING_BPF > > +tools/vmlinux.h: $(BPF_VMLINUX) > > + bpftool btf dump file $(BPF_VMLINUX) format c > $@.new > > + mv -f $@.new $@ > > + > > +tools/tester.bpf.o: $(srcdir)/tools/tester.bpf.c tools/vmlinux.h > > + $(CLANG_BPF) -Wall -Werror -Os -g --target=bpf -Itools -c -o $@ $< > > + > > +tools/tester.skel.h: tools/tester.bpf.o > > + bpftool gen skeleton $< > $@.new > > + mv -f $@.new $@ > > + > > +tools_sco_tester_SOURCES += $(builddir)/tools/tester.skel.h > > +tools_iso_tester_SOURCES += $(builddir)/tools/tester.skel.h > > +tools_l2cap_tester_SOURCES += $(builddir)/tools/tester.skel.h > > + > > +tools_sco_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > > +tools_iso_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > > +tools_l2cap_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > > + > > +tools_sco_tester_LDADD += $(LIBBPF_LIBS) > > +tools_iso_tester_LDADD += $(LIBBPF_LIBS) > > +tools_l2cap_tester_LDADD += $(LIBBPF_LIBS) > > +endif > > + > > endif > > > > if TOOLS > > diff --git a/configure.ac b/configure.ac > > index 0fa49f686..627b91e77 100644 > > --- a/configure.ac > > +++ b/configure.ac > > @@ -390,10 +390,38 @@ AC_ARG_ENABLE(testing, AS_HELP_STRING([--enable-testing], > > AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes") > > > > if (test "${enable_testing}" = "yes"); then > > - AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], > > - [], [], [[#include <time.h> > > - #include <linux/errqueue.h> > > - #include <linux/net_tstamp.h>]]) > > + AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], > > + [], [], [[#include <time.h> > > + #include <linux/errqueue.h> > > + #include <linux/net_tstamp.h>]]) > > +fi > > + > > +AC_ARG_ENABLE(testing-bpf, AS_HELP_STRING([--enable-testing-bpf[=PATH/TO/VMLINUX]], > > + [enable BPF testing tools]), > > + [enable_testing_bpf=yes; enable_testing_bpf_arg=${enableval}], > > + [enable_bpf=no]) > > +AM_CONDITIONAL(TESTING_BPF, test "${enable_testing_bpf}" = "yes") > > + > > +if (test "${enable_testing_bpf}" = "yes"); then > > + AC_ARG_VAR(CLANG_BPF, [CLANG compiler (for BPF)]) > > + AC_ARG_VAR(BPFTOOL, [bpftool]) > > + AC_ARG_VAR(BPF_VMLINUX, [vmlinux image to use for BPF testing]) > > + AC_PATH_PROG([CLANG_BPF], [clang], "no") > > + if (test "${CLANG_BPF}" == "no"); then > > + AC_MSG_ERROR([clang for BPF missing]) > > + fi > > + AC_PATH_PROG([BPFTOOL], [bpftool], "no") > > + if (test "${BPFTOOL}" == "no"); then > > + AC_MSG_ERROR([bpftool missing]) > > + fi > > + PKG_CHECK_MODULES(LIBBPF, libbpf >= 1.4, [], [AC_MSG_ERROR([libbpf missing])]) > > + if (test "${enable_testing_bpf_arg}" != "yes"); then > > + BPF_VMLINUX=${enable_testing_bpf_arg} > > + elif (test "${BPF_VMLINUX}" = ""); then > > + BPF_VMLINUX=/sys/kernel/btf/vmlinux > > + fi > > + AC_MSG_NOTICE([using BPF_VMLINUX=${BPF_VMLINUX} for BPF testing]) > > + AC_DEFINE(HAVE_BPF, 1, [Define to 1 if bpf testing is required]) > > fi > > > > AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental], > > diff --git a/doc/test-runner.rst b/doc/test-runner.rst > > index 423a9379c..549b2bcba 100644 > > --- a/doc/test-runner.rst > > +++ b/doc/test-runner.rst > > @@ -91,8 +91,8 @@ Bluetooth > > > > CONFIG_UHID=y > > > > -Lock debuging > > -------------- > > +Lock debugging > > +-------------- > > > > To catch locking related issues the following set of kernel config > > options may be useful: > > @@ -110,6 +110,19 @@ options may be useful: > > CONFIG_DEBUG_MUTEXES=y > > CONFIG_KASAN=y > > > > +BPF testing > > +----------- > > + > > +For BPF related tests: > > + > > +.. code-block:: > > + > > + CONFIG_BPF=y > > + CONFIG_BPF_JIT=y > > + CONFIG_CGROUPS=y > > + CONFIG_CGROUP_BPF=y > > + > > + > > EXAMPLES > > ======== > > > > @@ -127,6 +140,15 @@ Running a specific test of mgmt-tester > > > > $ tools/test-runner -k /pathto/bzImage -- tools/mgmt-tester -s "<name>" > > > > +Compiling and running BPF tests > > +------------------------------- > > + > > +.. code-block:: > > + > > + $ ./configure --enable-testing --enable-testing-bpf=/home/me/linux/vmlinux > > + $ make > > + $ tools/test-runner -k /home/me/linux/arch/x86_64/boot/bzImage -- tools/iso-tester -s BPF > > + > > Running bluetoothctl with emulated controller > > --------------------------------------------- > > > > diff --git a/doc/tester.config b/doc/tester.config > > index 099eddc79..548e4c629 100644 > > --- a/doc/tester.config > > +++ b/doc/tester.config > > @@ -57,3 +57,8 @@ CONFIG_PROVE_RCU=y > > CONFIG_LOCKDEP=y > > CONFIG_DEBUG_MUTEXES=y > > CONFIG_KASAN=y > > + > > +CONFIG_BPF=y > > +CONFIG_BPF_JIT=y > > +CONFIG_CGROUPS=y > > +CONFIG_CGROUP_BPF=y > > diff --git a/tools/iso-tester.c b/tools/iso-tester.c > > index 350775fdd..da164c771 100644 > > --- a/tools/iso-tester.c > > +++ b/tools/iso-tester.c > > @@ -517,6 +517,9 @@ struct iso_client_data { > > > > /* Disable BT_POLL_ERRQUEUE before enabling TX timestamping */ > > bool no_poll_errqueue; > > + > > + /* Enable BPF TX timestamping */ > > + bool bpf_timestamping; > > Let's keep it short, bpf_ts. Ok. [clip] > Btw, does BPF also use the error queue? Or better yet can we avoid > waiting up bluetoothd id the process/pipewire use the BPF mode to be > notified of packet completion? BPF timestamping does not use errqueue, so it avoids the wakeup problem. It's harder to deploy: - needs CAP_NET_ADMIN afaik, and to be attached to a cgroup - BPF part may need recompilation if kernel structs it uses change (maybe not that often since it's mostly skb_shared_info) Not so great for sound servers running as user.
Hi Pauli, On Mon, Mar 31, 2025 at 12:28 PM Pauli Virtanen <pav@iki.fi> wrote: > > Hi Luiz, > > ma, 2025-03-31 kello 11:59 -0400, Luiz Augusto von Dentz kirjoitti: > > Hi Pauli, > > > > On Sun, Mar 30, 2025 at 8:16 AM Pauli Virtanen <pav@iki.fi> wrote: > > > > > > Add some tests for BPF timestamping on Bluetooth sockets. > > > > > > These require additional tester kernel config, and at build time > > > the vmlinux image. > > > > > > Add cgroup mount to test-runner. > > > > > > Add documentation to tester config for this. > > > > > > Add tests: > > > > > > ISO Send - TX BPF Timestamping > > > --- > > > > > > Notes: > > > This probably needs to wait for the corresponding kernel patch series to > > > settle down. > > > > > > Makefile.tools | 29 ++++++ > > > configure.ac | 36 ++++++- > > > doc/test-runner.rst | 26 ++++- > > > doc/tester.config | 5 + > > > tools/iso-tester.c | 71 ++++++++++++- > > > tools/test-runner.c | 1 + > > > tools/tester.bpf.c | 92 +++++++++++++++++ > > > tools/tester.bpf.h | 7 ++ > > > > Usually we only do one . for source files, so names shall probably be > > tester-bpf.{c,h}. > > Ok. The .bpf.c convention was used used in some places. > > > > tools/tester.h | 244 ++++++++++++++++++++++++++++++++++++-------- > > > 9 files changed, 463 insertions(+), 48 deletions(-) > > > create mode 100644 tools/tester.bpf.c > > > create mode 100644 tools/tester.bpf.h > > > > > > diff --git a/Makefile.tools b/Makefile.tools > > > index e60c31b1d..a35af54fc 100644 > > > --- a/Makefile.tools > > > +++ b/Makefile.tools > > > @@ -144,6 +144,7 @@ tools_l2cap_tester_SOURCES = tools/l2cap-tester.c tools/tester.h monitor/bt.h \ > > > emulator/smp.c > > > tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \ > > > src/libshared-glib.la $(GLIB_LIBS) > > > +tools_l2cap_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > > > > > tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ > > > emulator/hciemu.h emulator/hciemu.c \ > > > @@ -191,6 +192,7 @@ tools_sco_tester_SOURCES = tools/sco-tester.c tools/tester.h monitor/bt.h \ > > > emulator/smp.c > > > tools_sco_tester_LDADD = lib/libbluetooth-internal.la \ > > > src/libshared-glib.la $(GLIB_LIBS) > > > +tools_sco_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > > > > > tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h > > > tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS) > > > @@ -212,6 +214,7 @@ tools_iso_tester_SOURCES = tools/iso-tester.c tools/tester.h monitor/bt.h \ > > > emulator/smp.c > > > tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ > > > src/libshared-glib.la $(GLIB_LIBS) > > > +tools_iso_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) > > > > > > tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ > > > emulator/hciemu.h emulator/hciemu.c \ > > > @@ -221,6 +224,32 @@ tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ > > > emulator/smp.c > > > tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \ > > > src/libshared-glib.la $(GLIB_LIBS) > > > + > > > +if TESTING_BPF > > > +tools/vmlinux.h: $(BPF_VMLINUX) > > > + bpftool btf dump file $(BPF_VMLINUX) format c > $@.new > > > + mv -f $@.new $@ > > > + > > > +tools/tester.bpf.o: $(srcdir)/tools/tester.bpf.c tools/vmlinux.h > > > + $(CLANG_BPF) -Wall -Werror -Os -g --target=bpf -Itools -c -o $@ $< > > > + > > > +tools/tester.skel.h: tools/tester.bpf.o > > > + bpftool gen skeleton $< > $@.new > > > + mv -f $@.new $@ > > > + > > > +tools_sco_tester_SOURCES += $(builddir)/tools/tester.skel.h > > > +tools_iso_tester_SOURCES += $(builddir)/tools/tester.skel.h > > > +tools_l2cap_tester_SOURCES += $(builddir)/tools/tester.skel.h > > > + > > > +tools_sco_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > > > +tools_iso_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > > > +tools_l2cap_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) > > > + > > > +tools_sco_tester_LDADD += $(LIBBPF_LIBS) > > > +tools_iso_tester_LDADD += $(LIBBPF_LIBS) > > > +tools_l2cap_tester_LDADD += $(LIBBPF_LIBS) > > > +endif > > > + > > > endif > > > > > > if TOOLS > > > diff --git a/configure.ac b/configure.ac > > > index 0fa49f686..627b91e77 100644 > > > --- a/configure.ac > > > +++ b/configure.ac > > > @@ -390,10 +390,38 @@ AC_ARG_ENABLE(testing, AS_HELP_STRING([--enable-testing], > > > AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes") > > > > > > if (test "${enable_testing}" = "yes"); then > > > - AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], > > > - [], [], [[#include <time.h> > > > - #include <linux/errqueue.h> > > > - #include <linux/net_tstamp.h>]]) > > > + AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], > > > + [], [], [[#include <time.h> > > > + #include <linux/errqueue.h> > > > + #include <linux/net_tstamp.h>]]) > > > +fi > > > + > > > +AC_ARG_ENABLE(testing-bpf, AS_HELP_STRING([--enable-testing-bpf[=PATH/TO/VMLINUX]], > > > + [enable BPF testing tools]), > > > + [enable_testing_bpf=yes; enable_testing_bpf_arg=${enableval}], > > > + [enable_bpf=no]) > > > +AM_CONDITIONAL(TESTING_BPF, test "${enable_testing_bpf}" = "yes") > > > + > > > +if (test "${enable_testing_bpf}" = "yes"); then > > > + AC_ARG_VAR(CLANG_BPF, [CLANG compiler (for BPF)]) > > > + AC_ARG_VAR(BPFTOOL, [bpftool]) > > > + AC_ARG_VAR(BPF_VMLINUX, [vmlinux image to use for BPF testing]) > > > + AC_PATH_PROG([CLANG_BPF], [clang], "no") > > > + if (test "${CLANG_BPF}" == "no"); then > > > + AC_MSG_ERROR([clang for BPF missing]) > > > + fi > > > + AC_PATH_PROG([BPFTOOL], [bpftool], "no") > > > + if (test "${BPFTOOL}" == "no"); then > > > + AC_MSG_ERROR([bpftool missing]) > > > + fi > > > + PKG_CHECK_MODULES(LIBBPF, libbpf >= 1.4, [], [AC_MSG_ERROR([libbpf missing])]) > > > + if (test "${enable_testing_bpf_arg}" != "yes"); then > > > + BPF_VMLINUX=${enable_testing_bpf_arg} > > > + elif (test "${BPF_VMLINUX}" = ""); then > > > + BPF_VMLINUX=/sys/kernel/btf/vmlinux > > > + fi > > > + AC_MSG_NOTICE([using BPF_VMLINUX=${BPF_VMLINUX} for BPF testing]) > > > + AC_DEFINE(HAVE_BPF, 1, [Define to 1 if bpf testing is required]) > > > fi > > > > > > AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental], > > > diff --git a/doc/test-runner.rst b/doc/test-runner.rst > > > index 423a9379c..549b2bcba 100644 > > > --- a/doc/test-runner.rst > > > +++ b/doc/test-runner.rst > > > @@ -91,8 +91,8 @@ Bluetooth > > > > > > CONFIG_UHID=y > > > > > > -Lock debuging > > > -------------- > > > +Lock debugging > > > +-------------- > > > > > > To catch locking related issues the following set of kernel config > > > options may be useful: > > > @@ -110,6 +110,19 @@ options may be useful: > > > CONFIG_DEBUG_MUTEXES=y > > > CONFIG_KASAN=y > > > > > > +BPF testing > > > +----------- > > > + > > > +For BPF related tests: > > > + > > > +.. code-block:: > > > + > > > + CONFIG_BPF=y > > > + CONFIG_BPF_JIT=y > > > + CONFIG_CGROUPS=y > > > + CONFIG_CGROUP_BPF=y > > > + > > > + > > > EXAMPLES > > > ======== > > > > > > @@ -127,6 +140,15 @@ Running a specific test of mgmt-tester > > > > > > $ tools/test-runner -k /pathto/bzImage -- tools/mgmt-tester -s "<name>" > > > > > > +Compiling and running BPF tests > > > +------------------------------- > > > + > > > +.. code-block:: > > > + > > > + $ ./configure --enable-testing --enable-testing-bpf=/home/me/linux/vmlinux > > > + $ make > > > + $ tools/test-runner -k /home/me/linux/arch/x86_64/boot/bzImage -- tools/iso-tester -s BPF > > > + > > > Running bluetoothctl with emulated controller > > > --------------------------------------------- > > > > > > diff --git a/doc/tester.config b/doc/tester.config > > > index 099eddc79..548e4c629 100644 > > > --- a/doc/tester.config > > > +++ b/doc/tester.config > > > @@ -57,3 +57,8 @@ CONFIG_PROVE_RCU=y > > > CONFIG_LOCKDEP=y > > > CONFIG_DEBUG_MUTEXES=y > > > CONFIG_KASAN=y > > > + > > > +CONFIG_BPF=y > > > +CONFIG_BPF_JIT=y > > > +CONFIG_CGROUPS=y > > > +CONFIG_CGROUP_BPF=y > > > diff --git a/tools/iso-tester.c b/tools/iso-tester.c > > > index 350775fdd..da164c771 100644 > > > --- a/tools/iso-tester.c > > > +++ b/tools/iso-tester.c > > > @@ -517,6 +517,9 @@ struct iso_client_data { > > > > > > /* Disable BT_POLL_ERRQUEUE before enabling TX timestamping */ > > > bool no_poll_errqueue; > > > + > > > + /* Enable BPF TX timestamping */ > > > + bool bpf_timestamping; > > > > Let's keep it short, bpf_ts. > > Ok. > > [clip] > > Btw, does BPF also use the error queue? Or better yet can we avoid > > waiting up bluetoothd id the process/pipewire use the BPF mode to be > > notified of packet completion? > > BPF timestamping does not use errqueue, so it avoids the wakeup > problem. > > It's harder to deploy: > > - needs CAP_NET_ADMIN afaik, and to be attached to a cgroup > > - BPF part may need recompilation if kernel structs it uses change > (maybe not that often since it's mostly skb_shared_info) > > Not so great for sound servers running as user. Ouch, then indeed it is not that great, I thought it could be one of the motivations why you were investing time to implement it, well I guess it is more for the sake of completeness?
diff --git a/Makefile.tools b/Makefile.tools index e60c31b1d..a35af54fc 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -144,6 +144,7 @@ tools_l2cap_tester_SOURCES = tools/l2cap-tester.c tools/tester.h monitor/bt.h \ emulator/smp.c tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) +tools_l2cap_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ @@ -191,6 +192,7 @@ tools_sco_tester_SOURCES = tools/sco-tester.c tools/tester.h monitor/bt.h \ emulator/smp.c tools_sco_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) +tools_sco_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS) @@ -212,6 +214,7 @@ tools_iso_tester_SOURCES = tools/iso-tester.c tools/tester.h monitor/bt.h \ emulator/smp.c tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) +tools_iso_tester_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ emulator/hciemu.h emulator/hciemu.c \ @@ -221,6 +224,32 @@ tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \ emulator/smp.c tools_ioctl_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) + +if TESTING_BPF +tools/vmlinux.h: $(BPF_VMLINUX) + bpftool btf dump file $(BPF_VMLINUX) format c > $@.new + mv -f $@.new $@ + +tools/tester.bpf.o: $(srcdir)/tools/tester.bpf.c tools/vmlinux.h + $(CLANG_BPF) -Wall -Werror -Os -g --target=bpf -Itools -c -o $@ $< + +tools/tester.skel.h: tools/tester.bpf.o + bpftool gen skeleton $< > $@.new + mv -f $@.new $@ + +tools_sco_tester_SOURCES += $(builddir)/tools/tester.skel.h +tools_iso_tester_SOURCES += $(builddir)/tools/tester.skel.h +tools_l2cap_tester_SOURCES += $(builddir)/tools/tester.skel.h + +tools_sco_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) +tools_iso_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) +tools_l2cap_tester_CPPFLAGS += -I$(builddir)/tools $(LIBBPF_CFLAGS) + +tools_sco_tester_LDADD += $(LIBBPF_LIBS) +tools_iso_tester_LDADD += $(LIBBPF_LIBS) +tools_l2cap_tester_LDADD += $(LIBBPF_LIBS) +endif + endif if TOOLS diff --git a/configure.ac b/configure.ac index 0fa49f686..627b91e77 100644 --- a/configure.ac +++ b/configure.ac @@ -390,10 +390,38 @@ AC_ARG_ENABLE(testing, AS_HELP_STRING([--enable-testing], AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes") if (test "${enable_testing}" = "yes"); then - AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], - [], [], [[#include <time.h> - #include <linux/errqueue.h> - #include <linux/net_tstamp.h>]]) + AC_CHECK_DECLS([SOF_TIMESTAMPING_TX_COMPLETION, SCM_TSTAMP_COMPLETION], + [], [], [[#include <time.h> + #include <linux/errqueue.h> + #include <linux/net_tstamp.h>]]) +fi + +AC_ARG_ENABLE(testing-bpf, AS_HELP_STRING([--enable-testing-bpf[=PATH/TO/VMLINUX]], + [enable BPF testing tools]), + [enable_testing_bpf=yes; enable_testing_bpf_arg=${enableval}], + [enable_bpf=no]) +AM_CONDITIONAL(TESTING_BPF, test "${enable_testing_bpf}" = "yes") + +if (test "${enable_testing_bpf}" = "yes"); then + AC_ARG_VAR(CLANG_BPF, [CLANG compiler (for BPF)]) + AC_ARG_VAR(BPFTOOL, [bpftool]) + AC_ARG_VAR(BPF_VMLINUX, [vmlinux image to use for BPF testing]) + AC_PATH_PROG([CLANG_BPF], [clang], "no") + if (test "${CLANG_BPF}" == "no"); then + AC_MSG_ERROR([clang for BPF missing]) + fi + AC_PATH_PROG([BPFTOOL], [bpftool], "no") + if (test "${BPFTOOL}" == "no"); then + AC_MSG_ERROR([bpftool missing]) + fi + PKG_CHECK_MODULES(LIBBPF, libbpf >= 1.4, [], [AC_MSG_ERROR([libbpf missing])]) + if (test "${enable_testing_bpf_arg}" != "yes"); then + BPF_VMLINUX=${enable_testing_bpf_arg} + elif (test "${BPF_VMLINUX}" = ""); then + BPF_VMLINUX=/sys/kernel/btf/vmlinux + fi + AC_MSG_NOTICE([using BPF_VMLINUX=${BPF_VMLINUX} for BPF testing]) + AC_DEFINE(HAVE_BPF, 1, [Define to 1 if bpf testing is required]) fi AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental], diff --git a/doc/test-runner.rst b/doc/test-runner.rst index 423a9379c..549b2bcba 100644 --- a/doc/test-runner.rst +++ b/doc/test-runner.rst @@ -91,8 +91,8 @@ Bluetooth CONFIG_UHID=y -Lock debuging -------------- +Lock debugging +-------------- To catch locking related issues the following set of kernel config options may be useful: @@ -110,6 +110,19 @@ options may be useful: CONFIG_DEBUG_MUTEXES=y CONFIG_KASAN=y +BPF testing +----------- + +For BPF related tests: + +.. code-block:: + + CONFIG_BPF=y + CONFIG_BPF_JIT=y + CONFIG_CGROUPS=y + CONFIG_CGROUP_BPF=y + + EXAMPLES ======== @@ -127,6 +140,15 @@ Running a specific test of mgmt-tester $ tools/test-runner -k /pathto/bzImage -- tools/mgmt-tester -s "<name>" +Compiling and running BPF tests +------------------------------- + +.. code-block:: + + $ ./configure --enable-testing --enable-testing-bpf=/home/me/linux/vmlinux + $ make + $ tools/test-runner -k /home/me/linux/arch/x86_64/boot/bzImage -- tools/iso-tester -s BPF + Running bluetoothctl with emulated controller --------------------------------------------- diff --git a/doc/tester.config b/doc/tester.config index 099eddc79..548e4c629 100644 --- a/doc/tester.config +++ b/doc/tester.config @@ -57,3 +57,8 @@ CONFIG_PROVE_RCU=y CONFIG_LOCKDEP=y CONFIG_DEBUG_MUTEXES=y CONFIG_KASAN=y + +CONFIG_BPF=y +CONFIG_BPF_JIT=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_BPF=y diff --git a/tools/iso-tester.c b/tools/iso-tester.c index 350775fdd..da164c771 100644 --- a/tools/iso-tester.c +++ b/tools/iso-tester.c @@ -517,6 +517,9 @@ struct iso_client_data { /* Disable BT_POLL_ERRQUEUE before enabling TX timestamping */ bool no_poll_errqueue; + + /* Enable BPF TX timestamping */ + bool bpf_timestamping; }; typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io, @@ -697,6 +700,13 @@ static void test_pre_setup(const void *test_data) return; } +#ifndef HAVE_BPF + if (isodata && isodata->bpf_timestamping) { + if (tester_pre_setup_skip_by_default()) + return; + } +#endif + data->mgmt = mgmt_new_default(); if (!data->mgmt) { tester_warn("Failed to setup management interface"); @@ -738,6 +748,8 @@ static void test_post_teardown(const void *test_data) NULL, NULL, NULL); } + tx_tstamp_teardown(&data->tx_ts); + hciemu_unref(data->hciemu); data->hciemu = NULL; } @@ -776,7 +788,7 @@ static void test_data_free(void *test_data) user->accept_reason = reason; \ tester_add_full(name, data, \ test_pre_setup, setup, func, NULL, \ - test_post_teardown, 2, user, test_data_free); \ + test_post_teardown, 3, user, test_data_free); \ } while (0) #define test_iso(name, data, setup, func) \ @@ -1094,6 +1106,19 @@ static const struct iso_client_data connect_send_tx_no_poll_timestamping = { .no_poll_errqueue = true, }; +static const struct iso_client_data connect_send_tx_bpf_timestamping = { + .qos = QOS_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_OPT_ID | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_TX_COMPLETION), + .repeat_send = 1, + .repeat_send_pre_ts = 2, + .bpf_timestamping = true, +}; + static const struct iso_client_data listen_16_2_1_recv = { .qos = QOS_16_2_1, .expect_err = 0, @@ -2254,6 +2279,24 @@ static gboolean iso_fail_errqueue(GIOChannel *io, GIOCondition cond, return FALSE; } +static gboolean iso_bpf_io(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = user_data; + int err; + + err = tx_tstamp_bpf_process(&data->tx_ts, &data->step); + if (err > 0) + return TRUE; + else if (err) + tester_test_failed(); + else if (!data->step) + tester_test_passed(); + + data->io_id[2] = 0; + return FALSE; +} + static gboolean iso_timer_errqueue(gpointer user_data) { struct test_data *data = user_data; @@ -2293,7 +2336,24 @@ static void iso_tx_timestamping(struct test_data *data, GIOChannel *io) sk = g_io_channel_unix_get_fd(io); - if (isodata->no_poll_errqueue) { + if (isodata->bpf_timestamping) { + GIOChannel *bpf_io; + + err = tx_tstamp_bpf_start(&data->tx_ts, sk); + if (err < 0) { + tester_warn("BPF timestamping failed: %s (%d)", + strerror(-err), err); + tester_test_failed(); + return; + } + + bpf_io = g_io_channel_unix_new(err); + data->io_id[2] = g_io_add_watch(bpf_io, + G_IO_IN | G_IO_ERR | G_IO_HUP, + iso_bpf_io, data); + g_io_channel_unref(bpf_io); + return; + } else if (isodata->no_poll_errqueue) { uint32_t flag = 0; err = setsockopt(sk, SOL_BLUETOOTH, BT_POLL_ERRQUEUE, @@ -2393,6 +2453,8 @@ static void iso_send(struct test_data *data, GIOChannel *io) for (count = 0; count < isodata->repeat_send + 1; ++count) iso_send_data(data, io); + g_io_channel_set_close_on_unref(io, FALSE); + if (isodata->bcast) { tester_test_passed(); return; @@ -3647,6 +3709,11 @@ int main(int argc, char *argv[]) &connect_send_tx_no_poll_timestamping, setup_powered, test_connect); + /* Test TX timestamping using BPF */ + test_iso("ISO Send - TX BPF Timestamping", + &connect_send_tx_bpf_timestamping, setup_powered, + test_connect); + test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered, test_listen); diff --git a/tools/test-runner.c b/tools/test-runner.c index 1d770330c..84c0f90ad 100644 --- a/tools/test-runner.c +++ b/tools/test-runner.c @@ -127,6 +127,7 @@ static const struct { { "tmpfs", "/run", "mode=0755", MS_NOSUID|MS_NODEV|MS_STRICTATIME }, { "tmpfs", "/tmp", NULL, 0 }, { "debugfs", "/sys/kernel/debug", NULL, 0 }, + { "cgroup2", "/sys/fs/cgroup", NULL, 0 }, { } }; diff --git a/tools/tester.bpf.c b/tools/tester.bpf.c new file mode 100644 index 000000000..9aed99aad --- /dev/null +++ b/tools/tester.bpf.c @@ -0,0 +1,92 @@ +#include "vmlinux.h" + +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_core_read.h> + +#ifndef AF_BLUETOOTH +#define AF_BLUETOOTH 31 +#endif + +#ifndef SOL_SOCKET +#define SOL_SOCKET 1 +#endif + +#include "tester.bpf.h" + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} tx_tstamp_events SEC(".maps"); + +static inline void tx_tstamp_event_emit(__u32 type, __u32 tskey) +{ + struct tx_tstamp_event *event; + + event = bpf_ringbuf_reserve(&tx_tstamp_events, sizeof(*event), 0); + if (!event) + return; + + event->type = type; + event->id = tskey; + event->nsec = bpf_ktime_get_ns(); + + bpf_ringbuf_submit(event, 0); +} + +SEC("sockops") +int skops_sockopt(struct bpf_sock_ops *skops) +{ + struct bpf_sock *bpf_sk = skops->sk; + struct bpf_sock_ops_kern *skops_kern; + struct skb_shared_info *shinfo; + const struct sk_buff *skb; + + if (!bpf_sk) + return 1; + + if (skops->family != AF_BLUETOOTH) + return 1; + + skops_kern = bpf_cast_to_kern_ctx(skops); + skb = skops_kern->skb; + shinfo = bpf_core_cast(skb->head + skb->end, struct skb_shared_info); + + switch (skops->op) { + case BPF_SOCK_OPS_TSTAMP_SENDMSG_CB: + bpf_sock_ops_enable_tx_tstamp(skops_kern, 0); + break; + case BPF_SOCK_OPS_TSTAMP_SCHED_CB: + tx_tstamp_event_emit(SCM_TSTAMP_SCHED, shinfo->tskey); + break; + case BPF_SOCK_OPS_TSTAMP_SND_SW_CB: + tx_tstamp_event_emit(SCM_TSTAMP_SND, shinfo->tskey); + break; + case BPF_SOCK_OPS_TSTAMP_ACK_CB: + tx_tstamp_event_emit(SCM_TSTAMP_ACK, shinfo->tskey); + break; + case BPF_SOCK_OPS_TSTAMP_COMPLETION_CB: + tx_tstamp_event_emit(SCM_TSTAMP_COMPLETION, shinfo->tskey); + break; + } + + return 1; +} + +SEC("cgroup/setsockopt") +int _setsockopt(struct bpf_sockopt *ctx) +{ + if (ctx->level == SOL_CUSTOM_TESTER) { + int flag = SK_BPF_CB_TX_TIMESTAMPING; + + bpf_setsockopt(ctx->sk, SOL_SOCKET, + SK_BPF_CB_FLAGS, &flag, sizeof(flag)); + + ctx->optlen = -1; + return 1; + } + + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/tester.bpf.h b/tools/tester.bpf.h new file mode 100644 index 000000000..1b3d06bc8 --- /dev/null +++ b/tools/tester.bpf.h @@ -0,0 +1,7 @@ +struct tx_tstamp_event { + __u32 type; + __u32 id; + __u64 nsec; +}; + +#define SOL_CUSTOM_TESTER 0x89abcdef diff --git a/tools/tester.h b/tools/tester.h index 4e7d7226b..02d2f1d34 100644 --- a/tools/tester.h +++ b/tools/tester.h @@ -11,13 +11,22 @@ #include <stdlib.h> #include <stdint.h> #include <time.h> +#include <fcntl.h> +#include <sys/stat.h> #include <sys/socket.h> #include <linux/errqueue.h> #include <linux/net_tstamp.h> #include <glib.h> -#define SEC_NSEC(_t) ((_t) * 1000000000LL) +#ifdef HAVE_BPF +#include <linux/bpf.h> +#include <bpf/libbpf.h> +#include "tester.bpf.h" +#include "tester.skel.h" +#endif + +#define SEC_NSEC(_t) ((_t) * 1000000000ULL) #define TS_NSEC(_ts) (SEC_NSEC((_ts)->tv_sec) + (_ts)->tv_nsec) #if !HAVE_DECL_SOF_TIMESTAMPING_TX_COMPLETION @@ -39,6 +48,12 @@ struct tx_tstamp_data { unsigned int sent; uint32_t so_timestamping; bool stream; +#ifdef HAVE_BPF + struct tester_bpf *skel; + struct ring_buffer *buf; + int cgroup_fd; + int bpf_err; +#endif }; static inline void tx_tstamp_init(struct tx_tstamp_data *data, @@ -88,6 +103,51 @@ static inline int tx_tstamp_expect(struct tx_tstamp_data *data, size_t len) return steps; } +static inline int tx_tstamp_validate(struct tx_tstamp_data *data, + const char *source, uint32_t type, uint32_t id, + uint64_t nsec, uint64_t now) +{ + unsigned int i; + + if (now < nsec || now > nsec + SEC_NSEC(10)) { + tester_warn("nonsense in timestamp"); + return -EINVAL; + } + + if (data->pos >= data->count) { + tester_warn("Too many timestamps"); + return -EINVAL; + } + + /* Find first unreceived timestamp of the right type */ + for (i = 0; i < data->count; ++i) { + if (data->expect[i].type >= 0xffff) + continue; + + if (type == data->expect[i].type) { + data->expect[i].type = 0xffff; + break; + } + } + if (i == data->count) { + tester_warn("Bad timestamp type %u", type); + return -EINVAL; + } + + if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && + id != data->expect[i].id) { + tester_warn("Bad timestamp id %u", id); + return -EINVAL; + } + + tester_print("Got valid %s TX timestamp %u (type %u, id %u)", + source, i, type, id); + + ++data->pos; + + return data->count - data->pos; +} + static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) { unsigned char control[512]; @@ -99,7 +159,6 @@ static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) struct scm_timestamping *tss = NULL; struct sock_extended_err *serr = NULL; struct timespec now; - unsigned int i; iov.iov_base = buf; iov.iov_len = sizeof(buf); @@ -159,42 +218,147 @@ static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) clock_gettime(CLOCK_REALTIME, &now); - if (TS_NSEC(&now) < TS_NSEC(tss->ts) || - TS_NSEC(&now) > TS_NSEC(tss->ts) + SEC_NSEC(10)) { - tester_warn("nonsense in timestamp"); - return -EINVAL; - } - - if (data->pos >= data->count) { - tester_warn("Too many timestamps"); - return -EINVAL; - } - - /* Find first unreceived timestamp of the right type */ - for (i = 0; i < data->count; ++i) { - if (data->expect[i].type >= 0xffff) - continue; - - if (serr->ee_info == data->expect[i].type) { - data->expect[i].type = 0xffff; - break; - } - } - if (i == data->count) { - tester_warn("Bad timestamp type %u", serr->ee_info); - return -EINVAL; - } - - if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && - serr->ee_data != data->expect[i].id) { - tester_warn("Bad timestamp id %u", serr->ee_data); - return -EINVAL; - } - - tester_print("Got valid TX timestamp %u (type %u, id %u)", i, - serr->ee_info, serr->ee_data); - - ++data->pos; - - return data->count - data->pos; + return tx_tstamp_validate(data, "socket", serr->ee_info, serr->ee_data, + TS_NSEC(tss->ts), TS_NSEC(&now)); } + + +#ifdef HAVE_BPF + +static inline int tx_tstamp_event_handler(void *ctx, void *buf, size_t size) +{ + struct tx_tstamp_data *data = ctx; + struct tx_tstamp_event *event = buf; + struct timespec now; + + if (size < sizeof(*event)) { + tester_warn("Bad BPF event"); + return -EIO; + } + + clock_gettime(CLOCK_MONOTONIC, &now); + + data->bpf_err = tx_tstamp_validate(data, "BPF", event->type, event->id, + event->nsec, TS_NSEC(&now)); + return data->bpf_err; +} + +static inline int tx_tstamp_bpf_start(struct tx_tstamp_data *data, int sk) +{ + int flag; + + data->cgroup_fd = open("/sys/fs/cgroup", O_RDONLY); + if (data->cgroup_fd < 0) { + tester_warn("opening cgroup failed"); + goto fail; + } + + data->skel = tester_bpf__open_and_load(); + if (!data->skel) + goto fail; + + data->buf = ring_buffer__new( + bpf_map__fd(data->skel->maps.tx_tstamp_events), + tx_tstamp_event_handler, data, NULL); + if (!data->buf) { + tester_warn("ringbuffer failed"); + goto fail; + } + + if (tester_bpf__attach(data->skel)) { + tester_warn("attach failed"); + goto fail; + } + + data->skel->links.skops_sockopt = + bpf_program__attach_cgroup(data->skel->progs.skops_sockopt, + data->cgroup_fd); + if (!data->skel->links.skops_sockopt) { + tester_warn("BFP sockops attach cgroup failed"); + goto fail; + } + + data->skel->links._setsockopt = + bpf_program__attach_cgroup(data->skel->progs._setsockopt, + data->cgroup_fd); + if (!data->skel->links._setsockopt) { + tester_warn("BFP setsockopt attach cgroup failed"); + goto fail; + } + + flag = 0; + if (setsockopt(sk, SOL_CUSTOM_TESTER, 0, &flag, sizeof(flag))) { + tester_warn("BFP setsockopt failed"); + goto fail; + } + + tester_print("BPF test program attached"); + return ring_buffer__epoll_fd(data->buf); + +fail: + if (data->buf) + ring_buffer__free(data->buf); + if (data->skel) + tester_bpf__destroy(data->skel); + if (data->cgroup_fd > 0) + close(data->cgroup_fd); + data->buf = NULL; + data->skel = NULL; + data->cgroup_fd = 0; + return -EIO; +} + +static inline bool tx_tstamp_bpf_process(struct tx_tstamp_data *data, int *step) +{ + int err; + + err = ring_buffer__consume(data->buf); + if (err < 0) { + data->bpf_err = err; + } else if (step) { + if (*step >= err) + *step -= err; + else + data->bpf_err = -E2BIG; + } + + return data->bpf_err; +} + +static inline void tx_tstamp_teardown(struct tx_tstamp_data *data) +{ + if (data->skel) + tester_bpf__detach(data->skel); + if (data->cgroup_fd > 0) + close(data->cgroup_fd); + if (data->buf) + ring_buffer__free(data->buf); + if (data->skel) { + tester_bpf__destroy(data->skel); + tester_print("BPF test program removed"); + } + + data->buf = NULL; + data->skel = NULL; + data->cgroup_fd = 0; +} + +#else + +static inline int tx_tstamp_bpf_start(struct tx_tstamp_data *data, int sk) +{ + tester_warn("Tester compiled without BPF"); + return -EOPNOTSUPP; +} + +static inline bool tx_tstamp_bpf_process(struct tx_tstamp_data *data, int *step) +{ + return false; +} + +static inline void tx_tstamp_teardown(struct tx_tstamp_data *data) +{ +} + +#endif +