| // SPDX-License-Identifier: GPL-2.0 | |
| /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ | |
| #define _GNU_SOURCE | |
| #include <unistd.h> | |
| #include <sys/syscall.h> | |
| #include <test_progs.h> | |
| #include "free_timer.skel.h" | |
| struct run_ctx { | |
| struct bpf_program *start_prog; | |
| struct bpf_program *overwrite_prog; | |
| pthread_barrier_t notify; | |
| int loop; | |
| bool start; | |
| bool stop; | |
| }; | |
| static void start_threads(struct run_ctx *ctx) | |
| { | |
| ctx->start = true; | |
| } | |
| static void stop_threads(struct run_ctx *ctx) | |
| { | |
| ctx->stop = true; | |
| /* Guarantee the order between ->stop and ->start */ | |
| __atomic_store_n(&ctx->start, true, __ATOMIC_RELEASE); | |
| } | |
| static int wait_for_start(struct run_ctx *ctx) | |
| { | |
| while (!__atomic_load_n(&ctx->start, __ATOMIC_ACQUIRE)) | |
| usleep(10); | |
| return ctx->stop; | |
| } | |
| static void *overwrite_timer_fn(void *arg) | |
| { | |
| struct run_ctx *ctx = arg; | |
| int loop, fd, err; | |
| cpu_set_t cpuset; | |
| long ret = 0; | |
| /* Pin on CPU 0 */ | |
| CPU_ZERO(&cpuset); | |
| CPU_SET(0, &cpuset); | |
| pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); | |
| /* Is the thread being stopped ? */ | |
| err = wait_for_start(ctx); | |
| if (err) | |
| return NULL; | |
| fd = bpf_program__fd(ctx->overwrite_prog); | |
| loop = ctx->loop; | |
| while (loop-- > 0) { | |
| LIBBPF_OPTS(bpf_test_run_opts, opts); | |
| /* Wait for start thread to complete */ | |
| pthread_barrier_wait(&ctx->notify); | |
| /* Overwrite timers */ | |
| err = bpf_prog_test_run_opts(fd, &opts); | |
| if (err) | |
| ret |= 1; | |
| else if (opts.retval) | |
| ret |= 2; | |
| /* Notify start thread to start timers */ | |
| pthread_barrier_wait(&ctx->notify); | |
| } | |
| return (void *)ret; | |
| } | |
| static void *start_timer_fn(void *arg) | |
| { | |
| struct run_ctx *ctx = arg; | |
| int loop, fd, err; | |
| cpu_set_t cpuset; | |
| long ret = 0; | |
| /* Pin on CPU 1 */ | |
| CPU_ZERO(&cpuset); | |
| CPU_SET(1, &cpuset); | |
| pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); | |
| /* Is the thread being stopped ? */ | |
| err = wait_for_start(ctx); | |
| if (err) | |
| return NULL; | |
| fd = bpf_program__fd(ctx->start_prog); | |
| loop = ctx->loop; | |
| while (loop-- > 0) { | |
| LIBBPF_OPTS(bpf_test_run_opts, opts); | |
| /* Run the prog to start timer */ | |
| err = bpf_prog_test_run_opts(fd, &opts); | |
| if (err) | |
| ret |= 4; | |
| else if (opts.retval) | |
| ret |= 8; | |
| /* Notify overwrite thread to do overwrite */ | |
| pthread_barrier_wait(&ctx->notify); | |
| /* Wait for overwrite thread to complete */ | |
| pthread_barrier_wait(&ctx->notify); | |
| } | |
| return (void *)ret; | |
| } | |
| void test_free_timer(void) | |
| { | |
| struct free_timer *skel; | |
| struct bpf_program *prog; | |
| struct run_ctx ctx; | |
| pthread_t tid[2]; | |
| void *ret; | |
| int err; | |
| skel = free_timer__open_and_load(); | |
| if (!skel && errno == EOPNOTSUPP) { | |
| test__skip(); | |
| return; | |
| } | |
| if (!ASSERT_OK_PTR(skel, "open_load")) | |
| return; | |
| memset(&ctx, 0, sizeof(ctx)); | |
| prog = bpf_object__find_program_by_name(skel->obj, "start_timer"); | |
| if (!ASSERT_OK_PTR(prog, "find start prog")) | |
| goto out; | |
| ctx.start_prog = prog; | |
| prog = bpf_object__find_program_by_name(skel->obj, "overwrite_timer"); | |
| if (!ASSERT_OK_PTR(prog, "find overwrite prog")) | |
| goto out; | |
| ctx.overwrite_prog = prog; | |
| pthread_barrier_init(&ctx.notify, NULL, 2); | |
| ctx.loop = 10; | |
| err = pthread_create(&tid[0], NULL, start_timer_fn, &ctx); | |
| if (!ASSERT_OK(err, "create start_timer")) | |
| goto out; | |
| err = pthread_create(&tid[1], NULL, overwrite_timer_fn, &ctx); | |
| if (!ASSERT_OK(err, "create overwrite_timer")) { | |
| stop_threads(&ctx); | |
| goto out; | |
| } | |
| start_threads(&ctx); | |
| ret = NULL; | |
| err = pthread_join(tid[0], &ret); | |
| ASSERT_EQ(err | (long)ret, 0, "start_timer"); | |
| ret = NULL; | |
| err = pthread_join(tid[1], &ret); | |
| ASSERT_EQ(err | (long)ret, 0, "overwrite_timer"); | |
| out: | |
| free_timer__destroy(skel); | |
| } |