diff mbox series

[net-next,v3,14/14] bpf: add simple bpf tests in the tx path for so_timstamping feature

Message ID 20241028110535.82999-15-kerneljasonxing@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net-timestamp: bpf extension to equip applications transparently | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5 this patch: 5
netdev/build_tools success Errors and warnings before: 0 (+0) this patch: 0 (+0)
netdev/cc_maintainers warning 2 maintainers not CCed: mykolal@fb.com linux-kselftest@vger.kernel.org
netdev/build_clang success Errors and warnings before: 4 this patch: 4
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 100 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Jason Xing Oct. 28, 2024, 11:05 a.m. UTC
From: Jason Xing <kernelxing@tencent.com>

Only check if we pass those three key points after we enable the
bpf extension for so_timestamping. During each point, we can choose
whether to print the current timestamp.

Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
 .../bpf/prog_tests/so_timestamping.c          |  98 ++++++++++++++
 .../selftests/bpf/progs/so_timestamping.c     | 123 ++++++++++++++++++
 2 files changed, 221 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/so_timestamping.c
 create mode 100644 tools/testing/selftests/bpf/progs/so_timestamping.c

Comments

Willem de Bruijn Oct. 29, 2024, 1:26 a.m. UTC | #1
Jason Xing wrote:
> From: Jason Xing <kernelxing@tencent.com>
> 
> Only check if we pass those three key points after we enable the
> bpf extension for so_timestamping. During each point, we can choose
> whether to print the current timestamp.
> 
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  .../bpf/prog_tests/so_timestamping.c          |  98 ++++++++++++++
>  .../selftests/bpf/progs/so_timestamping.c     | 123 ++++++++++++++++++
>  2 files changed, 221 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/so_timestamping.c
>  create mode 100644 tools/testing/selftests/bpf/progs/so_timestamping.c
> 
> diff --git a/tools/testing/selftests/bpf/prog_tests/so_timestamping.c b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> new file mode 100644
> index 000000000000..dfb7588c246d
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2024 Tencent */
> +
> +#define _GNU_SOURCE
> +#include <sched.h>
> +#include <linux/socket.h>
> +#include <linux/tls.h>
> +#include <net/if.h>
> +
> +#include "test_progs.h"
> +#include "cgroup_helpers.h"
> +#include "network_helpers.h"
> +
> +#include "so_timestamping.skel.h"
> +
> +#define CG_NAME "/so-timestamping-test"
> +
> +static const char addr4_str[] = "127.0.0.1";
> +static const char addr6_str[] = "::1";
> +static struct so_timestamping *skel;
> +static int cg_fd;
> +
> +static int create_netns(void)
> +{
> +	if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
> +		return -1;
> +
> +	if (!ASSERT_OK(system("ip link set dev lo up"), "set lo up"))
> +		return -1;
> +
> +	return 0;
> +}
> +
> +static void test_tcp(int family)
> +{
> +	struct so_timestamping__bss *bss = skel->bss;
> +	char buf[] = "testing testing";
> +	int sfd = -1, cfd = -1;
> +	int n;
> +
> +	memset(bss, 0, sizeof(*bss));
> +
> +	sfd = start_server(family, SOCK_STREAM,
> +			   family == AF_INET6 ? addr6_str : addr4_str, 0, 0);
> +	if (!ASSERT_GE(sfd, 0, "start_server"))
> +		goto out;
> +
> +	cfd = connect_to_fd(sfd, 0);
> +	if (!ASSERT_GE(cfd, 0, "connect_to_fd_server")) {
> +		close(sfd);
> +		goto out;
> +	}
> +
> +	n = write(cfd, buf, sizeof(buf));
> +	if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
> +		goto out;
> +
> +	ASSERT_EQ(bss->nr_active, 1, "nr_active");
> +	ASSERT_EQ(bss->nr_passive, 1, "nr_passive");
> +	ASSERT_EQ(bss->nr_sched, 1, "nr_sched");
> +	ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw");
> +	ASSERT_EQ(bss->nr_ack, 1, "nr_ack");
> +
> +out:
> +	if (sfd >= 0)
> +		close(sfd);
> +	if (cfd >= 0)
> +		close(cfd);
> +}
> +
> +void test_so_timestamping(void)
> +{
> +	cg_fd = test__join_cgroup(CG_NAME);
> +	if (cg_fd < 0)
> +		return;
> +
> +	if (create_netns())
> +		goto done;
> +
> +	skel = so_timestamping__open();
> +	if (!ASSERT_OK_PTR(skel, "open skel"))
> +		goto done;
> +
> +	if (!ASSERT_OK(so_timestamping__load(skel), "load skel"))
> +		goto done;
> +
> +	skel->links.skops_sockopt =
> +		bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd);
> +	if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup"))
> +		goto done;
> +
> +	test_tcp(AF_INET6);
> +	test_tcp(AF_INET);
> +
> +done:
> +	so_timestamping__destroy(skel);
> +	close(cg_fd);
> +}
> 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..a15317951786
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/so_timestamping.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2024 Tencent */
> +
> +#include "vmlinux.h"
> +#include "bpf_tracing_net.h"
> +#include <bpf/bpf_core_read.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include "bpf_misc.h"
> +
> +#define SO_TIMESTAMPING 37
> +#define SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK (SOF_TIMESTAMPING_SOFTWARE | \
> +					      SOF_TIMESTAMPING_TX_SCHED | \
> +					      SOF_TIMESTAMPING_TX_SOFTWARE | \
> +					      SOF_TIMESTAMPING_TX_ACK | \
> +					      SOF_TIMESTAMPING_OPT_ID | \
> +					      SOF_TIMESTAMPING_OPT_ID_TCP)
> +
> +extern unsigned long CONFIG_HZ __kconfig;
> +
> +int nr_active;
> +int nr_passive;
> +int nr_sched;
> +int nr_txsw;
> +int nr_ack;
> +
> +struct sockopt_test {
> +	int opt;
> +	int new;
> +	int expected;
> +};
> +
> +static const struct sockopt_test sol_socket_tests[] = {
> +	{ .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_TX_SCHED, .expected = 256, },
> +	{ .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK, .expected = 66450, },
> +	{ .opt = 0, },
> +};
> +
> +struct loop_ctx {
> +	void *ctx;
> +	struct sock *sk;
> +};
> +
> +static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
> +				const struct sockopt_test *t,
> +				int level)
> +{
> +	int tmp, new, expected, opt;
> +
> +	opt = t->opt;
> +	new = t->new;
> +	expected = t->expected;
> +
> +	if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
> +		return 1;
> +	if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
> +	    tmp != expected)
> +		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, 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;
> +}
> +
> +SEC("sockops")
> +int skops_sockopt(struct bpf_sock_ops *skops)
> +{
> +	struct bpf_sock *bpf_sk = skops->sk;
> +	struct sock *sk;
> +
> +	if (!bpf_sk)
> +		return 1;
> +
> +	sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
> +	if (!sk)
> +		return 1;
> +
> +	switch (skops->op) {
> +	case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
> +		nr_active += !bpf_test_sockopt(skops, sk);
> +		break;
> +	case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
> +		nr_passive += !bpf_test_sockopt(skops, sk);
> +		break;
> +	case BPF_SOCK_OPS_TS_SCHED_OPT_CB:
> +		nr_sched += 1;
> +		break;
> +	case BPF_SOCK_OPS_TS_SW_OPT_CB:
> +		nr_txsw += 1;
> +		break;
> +	case BPF_SOCK_OPS_TS_ACK_OPT_CB:
> +		nr_ack += 1;
> +		break;

Perhaps demonstrate what to do with the args on the new 
TS_*_OPT_CB.

> +	}
> +
> +	return 1;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> -- 
> 2.37.3
>
Jason Xing Oct. 29, 2024, 1:33 a.m. UTC | #2
On Tue, Oct 29, 2024 at 9:27 AM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> Jason Xing wrote:
> > From: Jason Xing <kernelxing@tencent.com>
> >
> > Only check if we pass those three key points after we enable the
> > bpf extension for so_timestamping. During each point, we can choose
> > whether to print the current timestamp.
> >
> > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > ---
> >  .../bpf/prog_tests/so_timestamping.c          |  98 ++++++++++++++
> >  .../selftests/bpf/progs/so_timestamping.c     | 123 ++++++++++++++++++
> >  2 files changed, 221 insertions(+)
> >  create mode 100644 tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> >  create mode 100644 tools/testing/selftests/bpf/progs/so_timestamping.c
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/so_timestamping.c b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > new file mode 100644
> > index 000000000000..dfb7588c246d
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > @@ -0,0 +1,98 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2024 Tencent */
> > +
> > +#define _GNU_SOURCE
> > +#include <sched.h>
> > +#include <linux/socket.h>
> > +#include <linux/tls.h>
> > +#include <net/if.h>
> > +
> > +#include "test_progs.h"
> > +#include "cgroup_helpers.h"
> > +#include "network_helpers.h"
> > +
> > +#include "so_timestamping.skel.h"
> > +
> > +#define CG_NAME "/so-timestamping-test"
> > +
> > +static const char addr4_str[] = "127.0.0.1";
> > +static const char addr6_str[] = "::1";
> > +static struct so_timestamping *skel;
> > +static int cg_fd;
> > +
> > +static int create_netns(void)
> > +{
> > +     if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
> > +             return -1;
> > +
> > +     if (!ASSERT_OK(system("ip link set dev lo up"), "set lo up"))
> > +             return -1;
> > +
> > +     return 0;
> > +}
> > +
> > +static void test_tcp(int family)
> > +{
> > +     struct so_timestamping__bss *bss = skel->bss;
> > +     char buf[] = "testing testing";
> > +     int sfd = -1, cfd = -1;
> > +     int n;
> > +
> > +     memset(bss, 0, sizeof(*bss));
> > +
> > +     sfd = start_server(family, SOCK_STREAM,
> > +                        family == AF_INET6 ? addr6_str : addr4_str, 0, 0);
> > +     if (!ASSERT_GE(sfd, 0, "start_server"))
> > +             goto out;
> > +
> > +     cfd = connect_to_fd(sfd, 0);
> > +     if (!ASSERT_GE(cfd, 0, "connect_to_fd_server")) {
> > +             close(sfd);
> > +             goto out;
> > +     }
> > +
> > +     n = write(cfd, buf, sizeof(buf));
> > +     if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
> > +             goto out;
> > +
> > +     ASSERT_EQ(bss->nr_active, 1, "nr_active");
> > +     ASSERT_EQ(bss->nr_passive, 1, "nr_passive");
> > +     ASSERT_EQ(bss->nr_sched, 1, "nr_sched");
> > +     ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw");
> > +     ASSERT_EQ(bss->nr_ack, 1, "nr_ack");
> > +
> > +out:
> > +     if (sfd >= 0)
> > +             close(sfd);
> > +     if (cfd >= 0)
> > +             close(cfd);
> > +}
> > +
> > +void test_so_timestamping(void)
> > +{
> > +     cg_fd = test__join_cgroup(CG_NAME);
> > +     if (cg_fd < 0)
> > +             return;
> > +
> > +     if (create_netns())
> > +             goto done;
> > +
> > +     skel = so_timestamping__open();
> > +     if (!ASSERT_OK_PTR(skel, "open skel"))
> > +             goto done;
> > +
> > +     if (!ASSERT_OK(so_timestamping__load(skel), "load skel"))
> > +             goto done;
> > +
> > +     skel->links.skops_sockopt =
> > +             bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd);
> > +     if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup"))
> > +             goto done;
> > +
> > +     test_tcp(AF_INET6);
> > +     test_tcp(AF_INET);
> > +
> > +done:
> > +     so_timestamping__destroy(skel);
> > +     close(cg_fd);
> > +}
> > 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..a15317951786
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/so_timestamping.c
> > @@ -0,0 +1,123 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2024 Tencent */
> > +
> > +#include "vmlinux.h"
> > +#include "bpf_tracing_net.h"
> > +#include <bpf/bpf_core_read.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include "bpf_misc.h"
> > +
> > +#define SO_TIMESTAMPING 37
> > +#define SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK (SOF_TIMESTAMPING_SOFTWARE | \
> > +                                           SOF_TIMESTAMPING_TX_SCHED | \
> > +                                           SOF_TIMESTAMPING_TX_SOFTWARE | \
> > +                                           SOF_TIMESTAMPING_TX_ACK | \
> > +                                           SOF_TIMESTAMPING_OPT_ID | \
> > +                                           SOF_TIMESTAMPING_OPT_ID_TCP)
> > +
> > +extern unsigned long CONFIG_HZ __kconfig;
> > +
> > +int nr_active;
> > +int nr_passive;
> > +int nr_sched;
> > +int nr_txsw;
> > +int nr_ack;
> > +
> > +struct sockopt_test {
> > +     int opt;
> > +     int new;
> > +     int expected;
> > +};
> > +
> > +static const struct sockopt_test sol_socket_tests[] = {
> > +     { .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_TX_SCHED, .expected = 256, },
> > +     { .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK, .expected = 66450, },
> > +     { .opt = 0, },
> > +};
> > +
> > +struct loop_ctx {
> > +     void *ctx;
> > +     struct sock *sk;
> > +};
> > +
> > +static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
> > +                             const struct sockopt_test *t,
> > +                             int level)
> > +{
> > +     int tmp, new, expected, opt;
> > +
> > +     opt = t->opt;
> > +     new = t->new;
> > +     expected = t->expected;
> > +
> > +     if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
> > +             return 1;
> > +     if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
> > +         tmp != expected)
> > +             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, 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;
> > +}
> > +
> > +SEC("sockops")
> > +int skops_sockopt(struct bpf_sock_ops *skops)
> > +{
> > +     struct bpf_sock *bpf_sk = skops->sk;
> > +     struct sock *sk;
> > +
> > +     if (!bpf_sk)
> > +             return 1;
> > +
> > +     sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
> > +     if (!sk)
> > +             return 1;
> > +
> > +     switch (skops->op) {
> > +     case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
> > +             nr_active += !bpf_test_sockopt(skops, sk);
> > +             break;
> > +     case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
> > +             nr_passive += !bpf_test_sockopt(skops, sk);
> > +             break;
> > +     case BPF_SOCK_OPS_TS_SCHED_OPT_CB:
> > +             nr_sched += 1;
> > +             break;
> > +     case BPF_SOCK_OPS_TS_SW_OPT_CB:
> > +             nr_txsw += 1;
> > +             break;
> > +     case BPF_SOCK_OPS_TS_ACK_OPT_CB:
> > +             nr_ack += 1;
> > +             break;
>
> Perhaps demonstrate what to do with the args on the new
> TS_*_OPT_CB.

Roger that.

I would like to know if the current patch is too big to review? Should
I split it into a few patches? But this series has 14 patches right
now which could possibly exceed the maximum limit.

Thanks,
Jason
Willem de Bruijn Oct. 29, 2024, 1:40 a.m. UTC | #3
Jason Xing wrote:
> On Tue, Oct 29, 2024 at 9:27 AM Willem de Bruijn
> <willemdebruijn.kernel@gmail.com> wrote:
> >
> > Jason Xing wrote:
> > > From: Jason Xing <kernelxing@tencent.com>
> > >
> > > Only check if we pass those three key points after we enable the
> > > bpf extension for so_timestamping. During each point, we can choose
> > > whether to print the current timestamp.
> > >
> > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > ---
> > >  .../bpf/prog_tests/so_timestamping.c          |  98 ++++++++++++++
> > >  .../selftests/bpf/progs/so_timestamping.c     | 123 ++++++++++++++++++
> > >  2 files changed, 221 insertions(+)
> > >  create mode 100644 tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > >  create mode 100644 tools/testing/selftests/bpf/progs/so_timestamping.c
> > >
> > > diff --git a/tools/testing/selftests/bpf/prog_tests/so_timestamping.c b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > > new file mode 100644
> > > index 000000000000..dfb7588c246d
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > > @@ -0,0 +1,98 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/* Copyright (c) 2024 Tencent */
> > > +
> > > +#define _GNU_SOURCE
> > > +#include <sched.h>
> > > +#include <linux/socket.h>
> > > +#include <linux/tls.h>
> > > +#include <net/if.h>
> > > +
> > > +#include "test_progs.h"
> > > +#include "cgroup_helpers.h"
> > > +#include "network_helpers.h"
> > > +
> > > +#include "so_timestamping.skel.h"
> > > +
> > > +#define CG_NAME "/so-timestamping-test"
> > > +
> > > +static const char addr4_str[] = "127.0.0.1";
> > > +static const char addr6_str[] = "::1";
> > > +static struct so_timestamping *skel;
> > > +static int cg_fd;
> > > +
> > > +static int create_netns(void)
> > > +{
> > > +     if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
> > > +             return -1;
> > > +
> > > +     if (!ASSERT_OK(system("ip link set dev lo up"), "set lo up"))
> > > +             return -1;
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static void test_tcp(int family)
> > > +{
> > > +     struct so_timestamping__bss *bss = skel->bss;
> > > +     char buf[] = "testing testing";
> > > +     int sfd = -1, cfd = -1;
> > > +     int n;
> > > +
> > > +     memset(bss, 0, sizeof(*bss));
> > > +
> > > +     sfd = start_server(family, SOCK_STREAM,
> > > +                        family == AF_INET6 ? addr6_str : addr4_str, 0, 0);
> > > +     if (!ASSERT_GE(sfd, 0, "start_server"))
> > > +             goto out;
> > > +
> > > +     cfd = connect_to_fd(sfd, 0);
> > > +     if (!ASSERT_GE(cfd, 0, "connect_to_fd_server")) {
> > > +             close(sfd);
> > > +             goto out;
> > > +     }
> > > +
> > > +     n = write(cfd, buf, sizeof(buf));
> > > +     if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
> > > +             goto out;
> > > +
> > > +     ASSERT_EQ(bss->nr_active, 1, "nr_active");
> > > +     ASSERT_EQ(bss->nr_passive, 1, "nr_passive");
> > > +     ASSERT_EQ(bss->nr_sched, 1, "nr_sched");
> > > +     ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw");
> > > +     ASSERT_EQ(bss->nr_ack, 1, "nr_ack");
> > > +
> > > +out:
> > > +     if (sfd >= 0)
> > > +             close(sfd);
> > > +     if (cfd >= 0)
> > > +             close(cfd);
> > > +}
> > > +
> > > +void test_so_timestamping(void)
> > > +{
> > > +     cg_fd = test__join_cgroup(CG_NAME);
> > > +     if (cg_fd < 0)
> > > +             return;
> > > +
> > > +     if (create_netns())
> > > +             goto done;
> > > +
> > > +     skel = so_timestamping__open();
> > > +     if (!ASSERT_OK_PTR(skel, "open skel"))
> > > +             goto done;
> > > +
> > > +     if (!ASSERT_OK(so_timestamping__load(skel), "load skel"))
> > > +             goto done;
> > > +
> > > +     skel->links.skops_sockopt =
> > > +             bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd);
> > > +     if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup"))
> > > +             goto done;
> > > +
> > > +     test_tcp(AF_INET6);
> > > +     test_tcp(AF_INET);
> > > +
> > > +done:
> > > +     so_timestamping__destroy(skel);
> > > +     close(cg_fd);
> > > +}
> > > 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..a15317951786
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/bpf/progs/so_timestamping.c
> > > @@ -0,0 +1,123 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/* Copyright (c) 2024 Tencent */
> > > +
> > > +#include "vmlinux.h"
> > > +#include "bpf_tracing_net.h"
> > > +#include <bpf/bpf_core_read.h>
> > > +#include <bpf/bpf_helpers.h>
> > > +#include <bpf/bpf_tracing.h>
> > > +#include "bpf_misc.h"
> > > +
> > > +#define SO_TIMESTAMPING 37
> > > +#define SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK (SOF_TIMESTAMPING_SOFTWARE | \
> > > +                                           SOF_TIMESTAMPING_TX_SCHED | \
> > > +                                           SOF_TIMESTAMPING_TX_SOFTWARE | \
> > > +                                           SOF_TIMESTAMPING_TX_ACK | \
> > > +                                           SOF_TIMESTAMPING_OPT_ID | \
> > > +                                           SOF_TIMESTAMPING_OPT_ID_TCP)
> > > +
> > > +extern unsigned long CONFIG_HZ __kconfig;
> > > +
> > > +int nr_active;
> > > +int nr_passive;
> > > +int nr_sched;
> > > +int nr_txsw;
> > > +int nr_ack;
> > > +
> > > +struct sockopt_test {
> > > +     int opt;
> > > +     int new;
> > > +     int expected;
> > > +};
> > > +
> > > +static const struct sockopt_test sol_socket_tests[] = {
> > > +     { .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_TX_SCHED, .expected = 256, },
> > > +     { .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK, .expected = 66450, },
> > > +     { .opt = 0, },
> > > +};
> > > +
> > > +struct loop_ctx {
> > > +     void *ctx;
> > > +     struct sock *sk;
> > > +};
> > > +
> > > +static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
> > > +                             const struct sockopt_test *t,
> > > +                             int level)
> > > +{
> > > +     int tmp, new, expected, opt;
> > > +
> > > +     opt = t->opt;
> > > +     new = t->new;
> > > +     expected = t->expected;
> > > +
> > > +     if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
> > > +             return 1;
> > > +     if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
> > > +         tmp != expected)
> > > +             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, 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;
> > > +}
> > > +
> > > +SEC("sockops")
> > > +int skops_sockopt(struct bpf_sock_ops *skops)
> > > +{
> > > +     struct bpf_sock *bpf_sk = skops->sk;
> > > +     struct sock *sk;
> > > +
> > > +     if (!bpf_sk)
> > > +             return 1;
> > > +
> > > +     sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
> > > +     if (!sk)
> > > +             return 1;
> > > +
> > > +     switch (skops->op) {
> > > +     case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
> > > +             nr_active += !bpf_test_sockopt(skops, sk);
> > > +             break;
> > > +     case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
> > > +             nr_passive += !bpf_test_sockopt(skops, sk);
> > > +             break;
> > > +     case BPF_SOCK_OPS_TS_SCHED_OPT_CB:
> > > +             nr_sched += 1;
> > > +             break;
> > > +     case BPF_SOCK_OPS_TS_SW_OPT_CB:
> > > +             nr_txsw += 1;
> > > +             break;
> > > +     case BPF_SOCK_OPS_TS_ACK_OPT_CB:
> > > +             nr_ack += 1;
> > > +             break;
> >
> > Perhaps demonstrate what to do with the args on the new
> > TS_*_OPT_CB.
> 
> Roger that.
> 
> I would like to know if the current patch is too big to review? Should
> I split it into a few patches? But this series has 14 patches right
> now which could possibly exceed the maximum limit.

For a test patch, this looks fine to me. They often are a longer than
feature patches. But much of it is easy to grasp.
Jason Xing Oct. 29, 2024, 3:13 a.m. UTC | #4
On Tue, Oct 29, 2024 at 9:40 AM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> Jason Xing wrote:
> > On Tue, Oct 29, 2024 at 9:27 AM Willem de Bruijn
> > <willemdebruijn.kernel@gmail.com> wrote:
> > >
> > > Jason Xing wrote:
> > > > From: Jason Xing <kernelxing@tencent.com>
> > > >
> > > > Only check if we pass those three key points after we enable the
> > > > bpf extension for so_timestamping. During each point, we can choose
> > > > whether to print the current timestamp.
> > > >
> > > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > > ---
> > > >  .../bpf/prog_tests/so_timestamping.c          |  98 ++++++++++++++
> > > >  .../selftests/bpf/progs/so_timestamping.c     | 123 ++++++++++++++++++
> > > >  2 files changed, 221 insertions(+)
> > > >  create mode 100644 tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > > >  create mode 100644 tools/testing/selftests/bpf/progs/so_timestamping.c
> > > >
> > > > diff --git a/tools/testing/selftests/bpf/prog_tests/so_timestamping.c b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > > > new file mode 100644
> > > > index 000000000000..dfb7588c246d
> > > > --- /dev/null
> > > > +++ b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
> > > > @@ -0,0 +1,98 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/* Copyright (c) 2024 Tencent */
> > > > +
> > > > +#define _GNU_SOURCE
> > > > +#include <sched.h>
> > > > +#include <linux/socket.h>
> > > > +#include <linux/tls.h>
> > > > +#include <net/if.h>
> > > > +
> > > > +#include "test_progs.h"
> > > > +#include "cgroup_helpers.h"
> > > > +#include "network_helpers.h"
> > > > +
> > > > +#include "so_timestamping.skel.h"
> > > > +
> > > > +#define CG_NAME "/so-timestamping-test"
> > > > +
> > > > +static const char addr4_str[] = "127.0.0.1";
> > > > +static const char addr6_str[] = "::1";
> > > > +static struct so_timestamping *skel;
> > > > +static int cg_fd;
> > > > +
> > > > +static int create_netns(void)
> > > > +{
> > > > +     if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
> > > > +             return -1;
> > > > +
> > > > +     if (!ASSERT_OK(system("ip link set dev lo up"), "set lo up"))
> > > > +             return -1;
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static void test_tcp(int family)
> > > > +{
> > > > +     struct so_timestamping__bss *bss = skel->bss;
> > > > +     char buf[] = "testing testing";
> > > > +     int sfd = -1, cfd = -1;
> > > > +     int n;
> > > > +
> > > > +     memset(bss, 0, sizeof(*bss));
> > > > +
> > > > +     sfd = start_server(family, SOCK_STREAM,
> > > > +                        family == AF_INET6 ? addr6_str : addr4_str, 0, 0);
> > > > +     if (!ASSERT_GE(sfd, 0, "start_server"))
> > > > +             goto out;
> > > > +
> > > > +     cfd = connect_to_fd(sfd, 0);
> > > > +     if (!ASSERT_GE(cfd, 0, "connect_to_fd_server")) {
> > > > +             close(sfd);
> > > > +             goto out;
> > > > +     }
> > > > +
> > > > +     n = write(cfd, buf, sizeof(buf));
> > > > +     if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
> > > > +             goto out;
> > > > +
> > > > +     ASSERT_EQ(bss->nr_active, 1, "nr_active");
> > > > +     ASSERT_EQ(bss->nr_passive, 1, "nr_passive");
> > > > +     ASSERT_EQ(bss->nr_sched, 1, "nr_sched");
> > > > +     ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw");
> > > > +     ASSERT_EQ(bss->nr_ack, 1, "nr_ack");
> > > > +
> > > > +out:
> > > > +     if (sfd >= 0)
> > > > +             close(sfd);
> > > > +     if (cfd >= 0)
> > > > +             close(cfd);
> > > > +}
> > > > +
> > > > +void test_so_timestamping(void)
> > > > +{
> > > > +     cg_fd = test__join_cgroup(CG_NAME);
> > > > +     if (cg_fd < 0)
> > > > +             return;
> > > > +
> > > > +     if (create_netns())
> > > > +             goto done;
> > > > +
> > > > +     skel = so_timestamping__open();
> > > > +     if (!ASSERT_OK_PTR(skel, "open skel"))
> > > > +             goto done;
> > > > +
> > > > +     if (!ASSERT_OK(so_timestamping__load(skel), "load skel"))
> > > > +             goto done;
> > > > +
> > > > +     skel->links.skops_sockopt =
> > > > +             bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd);
> > > > +     if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup"))
> > > > +             goto done;
> > > > +
> > > > +     test_tcp(AF_INET6);
> > > > +     test_tcp(AF_INET);
> > > > +
> > > > +done:
> > > > +     so_timestamping__destroy(skel);
> > > > +     close(cg_fd);
> > > > +}
> > > > 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..a15317951786
> > > > --- /dev/null
> > > > +++ b/tools/testing/selftests/bpf/progs/so_timestamping.c
> > > > @@ -0,0 +1,123 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/* Copyright (c) 2024 Tencent */
> > > > +
> > > > +#include "vmlinux.h"
> > > > +#include "bpf_tracing_net.h"
> > > > +#include <bpf/bpf_core_read.h>
> > > > +#include <bpf/bpf_helpers.h>
> > > > +#include <bpf/bpf_tracing.h>
> > > > +#include "bpf_misc.h"
> > > > +
> > > > +#define SO_TIMESTAMPING 37
> > > > +#define SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK (SOF_TIMESTAMPING_SOFTWARE | \
> > > > +                                           SOF_TIMESTAMPING_TX_SCHED | \
> > > > +                                           SOF_TIMESTAMPING_TX_SOFTWARE | \
> > > > +                                           SOF_TIMESTAMPING_TX_ACK | \
> > > > +                                           SOF_TIMESTAMPING_OPT_ID | \
> > > > +                                           SOF_TIMESTAMPING_OPT_ID_TCP)
> > > > +
> > > > +extern unsigned long CONFIG_HZ __kconfig;
> > > > +
> > > > +int nr_active;
> > > > +int nr_passive;
> > > > +int nr_sched;
> > > > +int nr_txsw;
> > > > +int nr_ack;
> > > > +
> > > > +struct sockopt_test {
> > > > +     int opt;
> > > > +     int new;
> > > > +     int expected;
> > > > +};
> > > > +
> > > > +static const struct sockopt_test sol_socket_tests[] = {
> > > > +     { .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_TX_SCHED, .expected = 256, },
> > > > +     { .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK, .expected = 66450, },
> > > > +     { .opt = 0, },
> > > > +};
> > > > +
> > > > +struct loop_ctx {
> > > > +     void *ctx;
> > > > +     struct sock *sk;
> > > > +};
> > > > +
> > > > +static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
> > > > +                             const struct sockopt_test *t,
> > > > +                             int level)
> > > > +{
> > > > +     int tmp, new, expected, opt;
> > > > +
> > > > +     opt = t->opt;
> > > > +     new = t->new;
> > > > +     expected = t->expected;
> > > > +
> > > > +     if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
> > > > +             return 1;
> > > > +     if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
> > > > +         tmp != expected)
> > > > +             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, 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;
> > > > +}
> > > > +
> > > > +SEC("sockops")
> > > > +int skops_sockopt(struct bpf_sock_ops *skops)
> > > > +{
> > > > +     struct bpf_sock *bpf_sk = skops->sk;
> > > > +     struct sock *sk;
> > > > +
> > > > +     if (!bpf_sk)
> > > > +             return 1;
> > > > +
> > > > +     sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
> > > > +     if (!sk)
> > > > +             return 1;
> > > > +
> > > > +     switch (skops->op) {
> > > > +     case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
> > > > +             nr_active += !bpf_test_sockopt(skops, sk);
> > > > +             break;
> > > > +     case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
> > > > +             nr_passive += !bpf_test_sockopt(skops, sk);
> > > > +             break;
> > > > +     case BPF_SOCK_OPS_TS_SCHED_OPT_CB:
> > > > +             nr_sched += 1;
> > > > +             break;
> > > > +     case BPF_SOCK_OPS_TS_SW_OPT_CB:
> > > > +             nr_txsw += 1;
> > > > +             break;
> > > > +     case BPF_SOCK_OPS_TS_ACK_OPT_CB:
> > > > +             nr_ack += 1;
> > > > +             break;
> > >
> > > Perhaps demonstrate what to do with the args on the new
> > > TS_*_OPT_CB.
> >
> > Roger that.
> >
> > I would like to know if the current patch is too big to review? Should
> > I split it into a few patches? But this series has 14 patches right
> > now which could possibly exceed the maximum limit.
>
> For a test patch, this looks fine to me. They often are a longer than
> feature patches. But much of it is easy to grasp.

Got it, I will do it as you said.

Thanks,
Jason
Martin KaFai Lau Oct. 30, 2024, 5:57 a.m. UTC | #5
On 10/28/24 4:05 AM, Jason Xing wrote:
> From: Jason Xing <kernelxing@tencent.com>
> 
> Only check if we pass those three key points after we enable the
> bpf extension for so_timestamping. During each point, we can choose
> whether to print the current timestamp.

The bpf prog usually does more than just print. The bpf prog aggregates data 
first before sending all raw data to the user space.

The selftests will be more useful for the reviewer and the future user if it can 
at least show how it can calculate the tx delay between [sendmsg, SCHED], 
[SCHED, SND], [SND, ACK].

[ ... ]

> +SEC("sockops")
> +int skops_sockopt(struct bpf_sock_ops *skops)
> +{
> +	struct bpf_sock *bpf_sk = skops->sk;
> +	struct sock *sk;
> +
> +	if (!bpf_sk)
> +		return 1;
> +
> +	sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
> +	if (!sk)
> +		return 1;
> +
> +	switch (skops->op) {
> +	case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
> +		nr_active += !bpf_test_sockopt(skops, sk);
> +		break;
> +	case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
> +		nr_passive += !bpf_test_sockopt(skops, sk);
> +		break;
> +	case BPF_SOCK_OPS_TS_SCHED_OPT_CB:
> +		nr_sched += 1;
> +		break;
> +	case BPF_SOCK_OPS_TS_SW_OPT_CB:
> +		nr_txsw += 1;
> +		break;
> +	case BPF_SOCK_OPS_TS_ACK_OPT_CB:
> +		nr_ack += 1;

> +		break;
> +	}
> +
> +	return 1;
> +}
> +
> +char _license[] SEC("license") = "GPL";
Jason Xing Oct. 30, 2024, 6:54 a.m. UTC | #6
On Wed, Oct 30, 2024 at 1:58 PM Martin KaFai Lau <martin.lau@linux.dev> wrote:
>
> On 10/28/24 4:05 AM, Jason Xing wrote:
> > From: Jason Xing <kernelxing@tencent.com>
> >
> > Only check if we pass those three key points after we enable the
> > bpf extension for so_timestamping. During each point, we can choose
> > whether to print the current timestamp.
>
> The bpf prog usually does more than just print. The bpf prog aggregates data
> first before sending all raw data to the user space.
>
> The selftests will be more useful for the reviewer and the future user if it can
> at least show how it can calculate the tx delay between [sendmsg, SCHED],
> [SCHED, SND], [SND, ACK].

Got it, I will dig into how to implement it and then post a new
version. Before this, I only used the bpf program to print timestamps
to one file without using those advanced functions (like aggregating
data) in bpf. Let me try :) If you know some good examples of this,
please show me :) Thanks in advance.

Thanks,
Jason
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/prog_tests/so_timestamping.c b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
new file mode 100644
index 000000000000..dfb7588c246d
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/so_timestamping.c
@@ -0,0 +1,98 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Tencent */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <linux/socket.h>
+#include <linux/tls.h>
+#include <net/if.h>
+
+#include "test_progs.h"
+#include "cgroup_helpers.h"
+#include "network_helpers.h"
+
+#include "so_timestamping.skel.h"
+
+#define CG_NAME "/so-timestamping-test"
+
+static const char addr4_str[] = "127.0.0.1";
+static const char addr6_str[] = "::1";
+static struct so_timestamping *skel;
+static int cg_fd;
+
+static int create_netns(void)
+{
+	if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
+		return -1;
+
+	if (!ASSERT_OK(system("ip link set dev lo up"), "set lo up"))
+		return -1;
+
+	return 0;
+}
+
+static void test_tcp(int family)
+{
+	struct so_timestamping__bss *bss = skel->bss;
+	char buf[] = "testing testing";
+	int sfd = -1, cfd = -1;
+	int n;
+
+	memset(bss, 0, sizeof(*bss));
+
+	sfd = start_server(family, SOCK_STREAM,
+			   family == AF_INET6 ? addr6_str : addr4_str, 0, 0);
+	if (!ASSERT_GE(sfd, 0, "start_server"))
+		goto out;
+
+	cfd = connect_to_fd(sfd, 0);
+	if (!ASSERT_GE(cfd, 0, "connect_to_fd_server")) {
+		close(sfd);
+		goto out;
+	}
+
+	n = write(cfd, buf, sizeof(buf));
+	if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
+		goto out;
+
+	ASSERT_EQ(bss->nr_active, 1, "nr_active");
+	ASSERT_EQ(bss->nr_passive, 1, "nr_passive");
+	ASSERT_EQ(bss->nr_sched, 1, "nr_sched");
+	ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw");
+	ASSERT_EQ(bss->nr_ack, 1, "nr_ack");
+
+out:
+	if (sfd >= 0)
+		close(sfd);
+	if (cfd >= 0)
+		close(cfd);
+}
+
+void test_so_timestamping(void)
+{
+	cg_fd = test__join_cgroup(CG_NAME);
+	if (cg_fd < 0)
+		return;
+
+	if (create_netns())
+		goto done;
+
+	skel = so_timestamping__open();
+	if (!ASSERT_OK_PTR(skel, "open skel"))
+		goto done;
+
+	if (!ASSERT_OK(so_timestamping__load(skel), "load skel"))
+		goto done;
+
+	skel->links.skops_sockopt =
+		bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd);
+	if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup"))
+		goto done;
+
+	test_tcp(AF_INET6);
+	test_tcp(AF_INET);
+
+done:
+	so_timestamping__destroy(skel);
+	close(cg_fd);
+}
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..a15317951786
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/so_timestamping.c
@@ -0,0 +1,123 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Tencent */
+
+#include "vmlinux.h"
+#include "bpf_tracing_net.h"
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+
+#define SO_TIMESTAMPING 37
+#define SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK (SOF_TIMESTAMPING_SOFTWARE | \
+					      SOF_TIMESTAMPING_TX_SCHED | \
+					      SOF_TIMESTAMPING_TX_SOFTWARE | \
+					      SOF_TIMESTAMPING_TX_ACK | \
+					      SOF_TIMESTAMPING_OPT_ID | \
+					      SOF_TIMESTAMPING_OPT_ID_TCP)
+
+extern unsigned long CONFIG_HZ __kconfig;
+
+int nr_active;
+int nr_passive;
+int nr_sched;
+int nr_txsw;
+int nr_ack;
+
+struct sockopt_test {
+	int opt;
+	int new;
+	int expected;
+};
+
+static const struct sockopt_test sol_socket_tests[] = {
+	{ .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_TX_SCHED, .expected = 256, },
+	{ .opt = SO_TIMESTAMPING, .new = SOF_TIMESTAMPING_BPF_SUPPPORTED_MASK, .expected = 66450, },
+	{ .opt = 0, },
+};
+
+struct loop_ctx {
+	void *ctx;
+	struct sock *sk;
+};
+
+static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
+				const struct sockopt_test *t,
+				int level)
+{
+	int tmp, new, expected, opt;
+
+	opt = t->opt;
+	new = t->new;
+	expected = t->expected;
+
+	if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
+		return 1;
+	if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
+	    tmp != expected)
+		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, 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;
+}
+
+SEC("sockops")
+int skops_sockopt(struct bpf_sock_ops *skops)
+{
+	struct bpf_sock *bpf_sk = skops->sk;
+	struct sock *sk;
+
+	if (!bpf_sk)
+		return 1;
+
+	sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
+	if (!sk)
+		return 1;
+
+	switch (skops->op) {
+	case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
+		nr_active += !bpf_test_sockopt(skops, sk);
+		break;
+	case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
+		nr_passive += !bpf_test_sockopt(skops, sk);
+		break;
+	case BPF_SOCK_OPS_TS_SCHED_OPT_CB:
+		nr_sched += 1;
+		break;
+	case BPF_SOCK_OPS_TS_SW_OPT_CB:
+		nr_txsw += 1;
+		break;
+	case BPF_SOCK_OPS_TS_ACK_OPT_CB:
+		nr_ack += 1;
+		break;
+	}
+
+	return 1;
+}
+
+char _license[] SEC("license") = "GPL";