Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Test preemption between bpf_obj_new() and bpf_obj_drop()

The test case creates 4 threads and then pins these 4 threads in CPU 0.
These 4 threads will run different bpf program through
bpf_prog_test_run_opts() and these bpf program will use bpf_obj_new()
and bpf_obj_drop() to allocate and free local kptrs concurrently.

Under preemptible kernel, bpf_obj_new() and bpf_obj_drop() may preempt
each other, bpf_obj_new() may return NULL and the test will fail before
applying these fixes as shown below:

test_preempted_bpf_ma_op:PASS:open_and_load 0 nsec
test_preempted_bpf_ma_op:PASS:attach 0 nsec
test_preempted_bpf_ma_op:PASS:no test prog 0 nsec
test_preempted_bpf_ma_op:PASS:no test prog 0 nsec
test_preempted_bpf_ma_op:PASS:no test prog 0 nsec
test_preempted_bpf_ma_op:PASS:no test prog 0 nsec
test_preempted_bpf_ma_op:PASS:pthread_create 0 nsec
test_preempted_bpf_ma_op:PASS:pthread_create 0 nsec
test_preempted_bpf_ma_op:PASS:pthread_create 0 nsec
test_preempted_bpf_ma_op:PASS:pthread_create 0 nsec
test_preempted_bpf_ma_op:PASS:run prog err 0 nsec
test_preempted_bpf_ma_op:PASS:run prog err 0 nsec
test_preempted_bpf_ma_op:PASS:run prog err 0 nsec
test_preempted_bpf_ma_op:PASS:run prog err 0 nsec
test_preempted_bpf_ma_op:FAIL:ENOMEM unexpected ENOMEM: got TRUE
#168 preempted_bpf_ma_op:FAIL
Summary: 0/0 PASSED, 0 SKIPPED, 1 FAILED

Signed-off-by: Hou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/20230901111954.1804721-4-houtao@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Hou Tao and committed by
Alexei Starovoitov
29c11aa8 62cf51cb

+195
+89
tools/testing/selftests/bpf/prog_tests/preempted_bpf_ma_op.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2023. Huawei Technologies Co., Ltd */ 3 + #define _GNU_SOURCE 4 + #include <sched.h> 5 + #include <pthread.h> 6 + #include <stdbool.h> 7 + #include <test_progs.h> 8 + 9 + #include "preempted_bpf_ma_op.skel.h" 10 + 11 + #define ALLOC_THREAD_NR 4 12 + #define ALLOC_LOOP_NR 512 13 + 14 + struct alloc_ctx { 15 + /* output */ 16 + int run_err; 17 + /* input */ 18 + int fd; 19 + bool *nomem_err; 20 + }; 21 + 22 + static void *run_alloc_prog(void *data) 23 + { 24 + struct alloc_ctx *ctx = data; 25 + cpu_set_t cpu_set; 26 + int i; 27 + 28 + CPU_ZERO(&cpu_set); 29 + CPU_SET(0, &cpu_set); 30 + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set); 31 + 32 + for (i = 0; i < ALLOC_LOOP_NR && !*ctx->nomem_err; i++) { 33 + LIBBPF_OPTS(bpf_test_run_opts, topts); 34 + int err; 35 + 36 + err = bpf_prog_test_run_opts(ctx->fd, &topts); 37 + ctx->run_err |= err | topts.retval; 38 + } 39 + 40 + return NULL; 41 + } 42 + 43 + void test_preempted_bpf_ma_op(void) 44 + { 45 + struct alloc_ctx ctx[ALLOC_THREAD_NR]; 46 + struct preempted_bpf_ma_op *skel; 47 + pthread_t tid[ALLOC_THREAD_NR]; 48 + int i, err; 49 + 50 + skel = preempted_bpf_ma_op__open_and_load(); 51 + if (!ASSERT_OK_PTR(skel, "open_and_load")) 52 + return; 53 + 54 + err = preempted_bpf_ma_op__attach(skel); 55 + if (!ASSERT_OK(err, "attach")) 56 + goto out; 57 + 58 + for (i = 0; i < ARRAY_SIZE(ctx); i++) { 59 + struct bpf_program *prog; 60 + char name[8]; 61 + 62 + snprintf(name, sizeof(name), "test%d", i); 63 + prog = bpf_object__find_program_by_name(skel->obj, name); 64 + if (!ASSERT_OK_PTR(prog, "no test prog")) 65 + goto out; 66 + 67 + ctx[i].run_err = 0; 68 + ctx[i].fd = bpf_program__fd(prog); 69 + ctx[i].nomem_err = &skel->bss->nomem_err; 70 + } 71 + 72 + memset(tid, 0, sizeof(tid)); 73 + for (i = 0; i < ARRAY_SIZE(tid); i++) { 74 + err = pthread_create(&tid[i], NULL, run_alloc_prog, &ctx[i]); 75 + if (!ASSERT_OK(err, "pthread_create")) 76 + break; 77 + } 78 + 79 + for (i = 0; i < ARRAY_SIZE(tid); i++) { 80 + if (!tid[i]) 81 + break; 82 + pthread_join(tid[i], NULL); 83 + ASSERT_EQ(ctx[i].run_err, 0, "run prog err"); 84 + } 85 + 86 + ASSERT_FALSE(skel->bss->nomem_err, "ENOMEM"); 87 + out: 88 + preempted_bpf_ma_op__destroy(skel); 89 + }
+106
tools/testing/selftests/bpf/progs/preempted_bpf_ma_op.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2023. Huawei Technologies Co., Ltd */ 3 + #include <vmlinux.h> 4 + #include <bpf/bpf_tracing.h> 5 + #include <bpf/bpf_helpers.h> 6 + 7 + #include "bpf_experimental.h" 8 + 9 + struct bin_data { 10 + char data[256]; 11 + struct bpf_spin_lock lock; 12 + }; 13 + 14 + struct map_value { 15 + struct bin_data __kptr * data; 16 + }; 17 + 18 + struct { 19 + __uint(type, BPF_MAP_TYPE_ARRAY); 20 + __type(key, int); 21 + __type(value, struct map_value); 22 + __uint(max_entries, 2048); 23 + } array SEC(".maps"); 24 + 25 + char _license[] SEC("license") = "GPL"; 26 + 27 + bool nomem_err = false; 28 + 29 + static int del_array(unsigned int i, int *from) 30 + { 31 + struct map_value *value; 32 + struct bin_data *old; 33 + 34 + value = bpf_map_lookup_elem(&array, from); 35 + if (!value) 36 + return 1; 37 + 38 + old = bpf_kptr_xchg(&value->data, NULL); 39 + if (old) 40 + bpf_obj_drop(old); 41 + 42 + (*from)++; 43 + return 0; 44 + } 45 + 46 + static int add_array(unsigned int i, int *from) 47 + { 48 + struct bin_data *old, *new; 49 + struct map_value *value; 50 + 51 + value = bpf_map_lookup_elem(&array, from); 52 + if (!value) 53 + return 1; 54 + 55 + new = bpf_obj_new(typeof(*new)); 56 + if (!new) { 57 + nomem_err = true; 58 + return 1; 59 + } 60 + 61 + old = bpf_kptr_xchg(&value->data, new); 62 + if (old) 63 + bpf_obj_drop(old); 64 + 65 + (*from)++; 66 + return 0; 67 + } 68 + 69 + static void del_then_add_array(int from) 70 + { 71 + int i; 72 + 73 + i = from; 74 + bpf_loop(512, del_array, &i, 0); 75 + 76 + i = from; 77 + bpf_loop(512, add_array, &i, 0); 78 + } 79 + 80 + SEC("fentry/bpf_fentry_test1") 81 + int BPF_PROG2(test0, int, a) 82 + { 83 + del_then_add_array(0); 84 + return 0; 85 + } 86 + 87 + SEC("fentry/bpf_fentry_test2") 88 + int BPF_PROG2(test1, int, a, u64, b) 89 + { 90 + del_then_add_array(512); 91 + return 0; 92 + } 93 + 94 + SEC("fentry/bpf_fentry_test3") 95 + int BPF_PROG2(test2, char, a, int, b, u64, c) 96 + { 97 + del_then_add_array(1024); 98 + return 0; 99 + } 100 + 101 + SEC("fentry/bpf_fentry_test4") 102 + int BPF_PROG2(test3, void *, a, char, b, int, c, u64, d) 103 + { 104 + del_then_add_array(1536); 105 + return 0; 106 + }