// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ #define _GNU_SOURCE #include #include #include #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 (!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); }