On Tue, Feb 11, 2025 at 4:05 PM Martin KaFai Lau <martin.lau@xxxxxxxxx> wrote: > > On 2/8/25 2:32 AM, Jason Xing wrote: > > --- > > .../bpf/prog_tests/so_timestamping.c | 79 +++++ > > .../selftests/bpf/progs/so_timestamping.c | 312 ++++++++++++++++++ > > A bike shedding. s/so_timestamping.c/net_timestamping.c/ Will rename them. > > > diff --git a/tools/testing/selftests/bpf/progs/so_timestamping.c b/tools/testing/selftests/bpf/progs/so_timestamping.c > > new file mode 100644 > > index 000000000000..4974552cdecb > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/progs/so_timestamping.c > > @@ -0,0 +1,312 @@ > > +#include "vmlinux.h" > > +#include "bpf_tracing_net.h" > > +#include <bpf/bpf_helpers.h> > > +#include <bpf/bpf_tracing.h> > > +#include "bpf_misc.h" > > +#include "bpf_kfuncs.h" > > +#define BPF_PROG_TEST_TCP_HDR_OPTIONS > > +#include "test_tcp_hdr_options.h" > > +#include <errno.h> > > + > > +#define SK_BPF_CB_FLAGS 1009 > > +#define SK_BPF_CB_TX_TIMESTAMPING 1 > > + > > +int nr_active; > > +int nr_snd; > > +int nr_passive; > > +int nr_sched; > > +int nr_txsw; > > +int nr_ack; > > + > > +struct sockopt_test { > > + int opt; > > + int new; > > +}; > > + > > +static const struct sockopt_test sol_socket_tests[] = { > > + { .opt = SK_BPF_CB_FLAGS, .new = SK_BPF_CB_TX_TIMESTAMPING, }, > > + { .opt = 0, }, > > +}; > > + > > +struct loop_ctx { > > + void *ctx; > > + const struct sock *sk; > > +}; > > + > > +struct sk_stg { > > + __u64 sendmsg_ns; /* record ts when sendmsg is called */ > > +}; > > + > > +struct sk_tskey { > > + u64 cookie; > > + u32 tskey; > > +}; > > + > > +struct delay_info { > > + u64 sendmsg_ns; /* record ts when sendmsg is called */ > > + u32 sched_delay; /* SCHED_OPT_CB - sendmsg_ns */ > > + u32 sw_snd_delay; /* SW_OPT_CB - SCHED_OPT_CB */ > > + u32 ack_delay; /* ACK_OPT_CB - SW_OPT_CB */ > > +}; > > + > > +struct { > > + __uint(type, BPF_MAP_TYPE_SK_STORAGE); > > + __uint(map_flags, BPF_F_NO_PREALLOC); > > + __type(key, int); > > + __type(value, struct sk_stg); > > +} sk_stg_map SEC(".maps"); > > + > > +struct { > > + __uint(type, BPF_MAP_TYPE_HASH); > > + __type(key, struct sk_tskey); > > + __type(value, struct delay_info); > > + __uint(max_entries, 1024); > > +} time_map SEC(".maps"); > > + > > +static u64 delay_tolerance_nsec = 10000000000; /* 10 second as an example */ > > + > > +extern int bpf_sock_ops_enable_tx_tstamp(struct bpf_sock_ops_kern *skops) __ksym; > > + > > +static int bpf_test_sockopt_int(void *ctx, const struct sock *sk, > > + const struct sockopt_test *t, > > + int level) > > This should be the only one that is needed even when supporting the future RX > timestamping. > > TX and RX timestamping need to be tested independently. Looping it will either > enabling them together or disabling them together. It cannot test whether RX > will work by itself. > > Thus, the bpf_loop won't help. Lets remove it to simplify the test. Got it. Will remove it. > > > +{ > > + int new, opt, tmp; > > + > > + opt = t->opt; > > + new = t->new; > > + > > + if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new))) > > + return 1; > > + > > + if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) || > > + tmp != new) > > + return 1; > > + > > + return 0; > > +} > > + > > +static int bpf_test_socket_sockopt(__u32 i, struct loop_ctx *lc) > > +{ > > + const struct sockopt_test *t; > > + > > + if (i >= ARRAY_SIZE(sol_socket_tests)) > > + return 1; > > + > > + t = &sol_socket_tests[i]; > > + if (!t->opt) > > + return 1; > > + > > + return bpf_test_sockopt_int(lc->ctx, lc->sk, t, SOL_SOCKET); > > +} > > + > > +static int bpf_test_sockopt(void *ctx, const struct sock *sk) > > +{ > > + struct loop_ctx lc = { .ctx = ctx, .sk = sk, }; > > + int n; > > + > > + n = bpf_loop(ARRAY_SIZE(sol_socket_tests), bpf_test_socket_sockopt, &lc, 0); > > + if (n != ARRAY_SIZE(sol_socket_tests)) > > + return -1; > > + > > + return 0; > > +} > > + > > +static bool bpf_test_access_sockopt(void *ctx) > > +{ > > + const struct sockopt_test *t; > > + int tmp, ret, i = 0; > > + int level = SOL_SOCKET; > > + > > + t = &sol_socket_tests[i]; > > + > > + for (; t->opt;) { > > It really does not need a loop here. It only needs to test "one" optname to > ensure it is -EOPNOTSUPP. > > > + ret = bpf_setsockopt(ctx, level, t->opt, (void *)&t->new, sizeof(t->new)); > > + if (ret != -EOPNOTSUPP) > > + return true; > > + > > + ret = bpf_getsockopt(ctx, level, t->opt, &tmp, sizeof(tmp)); > > + if (ret != -EOPNOTSUPP) > > + return true; > > + > > + if (++i >= ARRAY_SIZE(sol_socket_tests)) > > + break; > > + } > > + > > + return false; > > +} > > + > > +/* Adding a simple test to see if we can get an expected value */ > > +static bool bpf_test_access_load_hdr_opt(struct bpf_sock_ops *skops) > > +{ > > + struct tcp_opt reg_opt; > > Just noticed this one. Use a plain u8 array. Then no need to include the > "test_tcp_hdr_options.h" from an unrelated test. Will update it. Thanks, Jason > > > + int load_flags = 0; > > + int ret; > > + > > + reg_opt.kind = TCPOPT_EXP; > > The kind could be any integer, e.g. 2. > > > + reg_opt.len = 0; > > + reg_opt.data32 = 0; > > + ret = bpf_load_hdr_opt(skops, ®_opt, sizeof(reg_opt), load_flags); > > + if (ret != -EOPNOTSUPP) > > + return true; > > + > > + return false; > > +}