@@ -144,6 +144,8 @@ 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)
+nodist_tools_l2cap_tester_SOURCES =
tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu.c \
@@ -191,6 +193,8 @@ 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)
+nodist_tools_sco_tester_SOURCES =
tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h
tools_hci_tester_LDADD = src/libshared-glib.la $(GLIB_LIBS)
@@ -212,6 +216,8 @@ 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)
+nodist_tools_iso_tester_SOURCES =
tools_ioctl_tester_SOURCES = tools/ioctl-tester.c monitor/bt.h \
emulator/hciemu.h emulator/hciemu.c \
@@ -221,6 +227,39 @@ 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)
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
+ bpftool btf dump file $(BPF_VMLINUX) format c > $@.new && \
+ mv -f $@.new $@
+
+tools/tester-bpf.o: $(srcdir)/tools/tester-bpf.c tools/vmlinux.h
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
+ $(CLANG_BPF) -Wall -Werror -Os -g --target=bpf -Itools -c -o $@ $<
+
+tools/tester-skel.h: tools/tester-bpf.o
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
+ bpftool gen skeleton $< > $@.new && \
+ mv -f $@.new $@
+
+BPF_BUILT_SOURCES = $(builddir)/tools/tester-skel.h $(builddir)/tools/vmlinux.h
+
+nodist_tools_sco_tester_SOURCES += $(BPF_BUILT_SOURCES)
+nodist_tools_iso_tester_SOURCES += $(BPF_BUILT_SOURCES)
+nodist_tools_l2cap_tester_SOURCES += $(BPF_BUILT_SOURCES)
+BUILT_SOURCES += $(BPF_BUILT_SOURCES)
+CLEANFILES += $(BPF_BUILT_SOURCES)
+
+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
@@ -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],
@@ -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,21 @@ options may be useful:
CONFIG_DEBUG_MUTEXES=y
CONFIG_KASAN=y
+BPF testing
+-----------
+
+For BPF related tests:
+
+.. code-block::
+
+ CONFIG_BPF=y
+ CONFIG_BPF_SYSCALL=y
+ CONFIG_BPF_JIT=y
+ CONFIG_CGROUPS=y
+ CONFIG_CGROUP_BPF=y
+ CONFIG_DEBUG_INFO_DWARF5=y
+ CONFIG_DEBUG_INFO_BTF=y
+
EXAMPLES
========
@@ -127,6 +142,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
---------------------------------------------
@@ -57,3 +57,11 @@ CONFIG_PROVE_RCU=y
CONFIG_LOCKDEP=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_KASAN=y
+
+CONFIG_BPF=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_BPF=y
+CONFIG_DEBUG_INFO_DWARF5=y
+CONFIG_DEBUG_INFO_BTF=y
@@ -471,12 +471,13 @@ struct test_data {
uint16_t handle;
uint16_t acl_handle;
struct queue *io_queue;
- unsigned int io_id[4];
+ unsigned int io_id[5];
uint8_t client_num;
int step;
bool reconnect;
bool suspending;
struct tx_tstamp_data tx_ts;
+ struct tx_tstamp_data bpf_tx_ts;
};
struct iso_client_data {
@@ -517,6 +518,9 @@ struct iso_client_data {
/* Disable BT_POLL_ERRQUEUE before enabling TX timestamping */
bool no_poll_errqueue;
+
+ /* Enable BPF TX timestamping */
+ bool bpf_ts;
};
typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io,
@@ -697,6 +701,13 @@ static void test_pre_setup(const void *test_data)
return;
}
+#ifndef HAVE_BPF
+ if (isodata && isodata->bpf_ts) {
+ 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 +749,9 @@ static void test_post_teardown(const void *test_data)
NULL, NULL, NULL);
}
+ tx_tstamp_teardown(&data->tx_ts);
+ tx_tstamp_teardown(&data->bpf_tx_ts);
+
hciemu_unref(data->hciemu);
data->hciemu = NULL;
}
@@ -776,7 +790,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 +1108,29 @@ 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 = 0,
+ .repeat_send = 1,
+ .repeat_send_pre_ts = 2,
+ .bpf_ts = true,
+};
+
+static const struct iso_client_data connect_send_tx_bpf_sk_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_ts = true,
+};
+
static const struct iso_client_data listen_16_2_1_recv = {
.qos = QOS_16_2_1,
.expect_err = 0,
@@ -2254,6 +2291,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->bpf_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[4] = 0;
+ return FALSE;
+}
+
static gboolean iso_timer_errqueue(gpointer user_data)
{
struct test_data *data = user_data;
@@ -2281,18 +2336,40 @@ static void iso_tx_timestamping(struct test_data *data, GIOChannel *io)
int err;
unsigned int count;
- if (!(isodata->so_timestamping & TS_TX_RECORD_MASK))
+ if (!(isodata->so_timestamping & TS_TX_RECORD_MASK) && !isodata->bpf_ts)
return;
tester_print("Enabling TX timestamping");
- tx_tstamp_init(&data->tx_ts, isodata->so_timestamping, false);
+ tx_tstamp_init(&data->tx_ts, isodata->so_timestamping, false, false);
+ tx_tstamp_init(&data->bpf_tx_ts, isodata->so_timestamping, false, true);
- for (count = 0; count < isodata->repeat_send + 1; ++count)
+ for (count = 0; count < isodata->repeat_send + 1; ++count) {
data->step += tx_tstamp_expect(&data->tx_ts, 0);
+ if (isodata->bpf_ts)
+ data->step += tx_tstamp_expect(&data->bpf_tx_ts, 0);
+ }
sk = g_io_channel_unix_get_fd(io);
+ if (isodata->bpf_ts) {
+ GIOChannel *bpf_io;
+
+ err = tx_tstamp_bpf_start(&data->bpf_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[4] = 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);
+ }
+
if (isodata->no_poll_errqueue) {
uint32_t flag = 0;
@@ -2393,6 +2470,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 +3726,14 @@ 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 Send - TX BPF + Socket Timestamping",
+ &connect_send_tx_bpf_sk_timestamping, setup_powered,
+ test_connect);
+
test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered,
test_listen);
@@ -1382,7 +1382,7 @@ static void l2cap_tx_timestamping(struct test_data *data, GIOChannel *io)
tester_print("Enabling TX timestamping");
tx_tstamp_init(&data->tx_ts, l2data->so_timestamping,
- l2data->sock_type == SOCK_STREAM);
+ l2data->sock_type == SOCK_STREAM, false);
for (count = 0; count < l2data->repeat_send + 1; ++count)
data->step += tx_tstamp_expect(&data->tx_ts, l2data->data_len);
@@ -767,7 +767,7 @@ static void sco_tx_timestamping(struct test_data *data, GIOChannel *io)
tester_print("Enabling TX timestamping");
- tx_tstamp_init(&data->tx_ts, scodata->so_timestamping, false);
+ tx_tstamp_init(&data->tx_ts, scodata->so_timestamping, false, false);
for (count = 0; count < scodata->repeat_send + 1; ++count)
data->step += tx_tstamp_expect(&data->tx_ts, 0);
@@ -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 },
{ }
};
new file mode 100644
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2025 Pauli Virtanen
+ *
+ */
+
+#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";
new file mode 100644
@@ -0,0 +1,7 @@
+struct tx_tstamp_event {
+ __u32 type;
+ __u32 id;
+ __u64 nsec;
+};
+
+#define SOL_CUSTOM_TESTER 0x89abcdef
@@ -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,16 +48,24 @@ struct tx_tstamp_data {
unsigned int sent;
uint32_t so_timestamping;
bool stream;
+ bool bpf;
+#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,
- uint32_t so_timestamping, bool stream)
+ uint32_t so_timestamping, bool stream, bool bpf)
{
memset(data, 0, sizeof(*data));
memset(data->expect, 0xff, sizeof(data->expect));
data->so_timestamping = so_timestamping;
data->stream = stream;
+ data->bpf = bpf;
}
static inline int tx_tstamp_expect(struct tx_tstamp_data *data, size_t len)
@@ -59,6 +76,21 @@ static inline int tx_tstamp_expect(struct tx_tstamp_data *data, size_t len)
if (data->stream && len)
data->sent += len - 1;
+ if (data->bpf) {
+ bool have_tskey =
+ data->so_timestamping & SOF_TIMESTAMPING_OPT_ID &&
+ data->so_timestamping & SOF_TIMESTAMPING_TX_RECORD_MASK;
+
+ g_assert(pos + 2 <= ARRAY_SIZE(data->expect));
+ data->expect[pos].type = SCM_TSTAMP_SND;
+ data->expect[pos].id = have_tskey ? data->sent : 0;
+ pos++;
+ data->expect[pos].type = SCM_TSTAMP_COMPLETION;
+ data->expect[pos].id = have_tskey ? data->sent : 0;
+ pos++;
+ goto done;
+ }
+
if (data->so_timestamping & SOF_TIMESTAMPING_TX_SCHED) {
g_assert(pos < ARRAY_SIZE(data->expect));
data->expect[pos].type = SCM_TSTAMP_SCHED;
@@ -80,6 +112,7 @@ static inline int tx_tstamp_expect(struct tx_tstamp_data *data, size_t len)
pos++;
}
+done:
if (!data->stream || len)
data->sent++;
@@ -88,6 +121,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 || data->bpf) &&
+ 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 +177,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 +236,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("BPF 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("BPF setsockopt attach cgroup failed");
+ goto fail;
+ }
+
+ flag = 0;
+ if (setsockopt(sk, SOL_CUSTOM_TESTER, 0, &flag, sizeof(flag))) {
+ tester_warn("BPF 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 int 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 int 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
+