mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-15 22:41:38 +00:00
For the second argument of bpf_kptr_xchg(), if the reg type contains
MEM_ALLOC and MEM_PERCPU, which means a percpu allocation,
after bpf_kptr_xchg(), the argument is marked as MEM_RCU and MEM_PERCPU
if in rcu critical section. This way, re-reading from the map value
is not needed. Remove it from the percpu_alloc_array.c selftest.
Without previous kernel change, the test will fail like below:
0: R1=ctx(off=0,imm=0) R10=fp0
; int BPF_PROG(test_array_map_10, int a)
0: (b4) w1 = 0 ; R1_w=0
; int i, index = 0;
1: (63) *(u32 *)(r10 -4) = r1 ; R1_w=0 R10=fp0 fp-8=0000????
2: (bf) r2 = r10 ; R2_w=fp0 R10=fp0
;
3: (07) r2 += -4 ; R2_w=fp-4
; e = bpf_map_lookup_elem(&array, &index);
4: (18) r1 = 0xffff88810e771800 ; R1_w=map_ptr(off=0,ks=4,vs=16,imm=0)
6: (85) call bpf_map_lookup_elem#1 ; R0_w=map_value_or_null(id=1,off=0,ks=4,vs=16,imm=0)
7: (bf) r6 = r0 ; R0_w=map_value_or_null(id=1,off=0,ks=4,vs=16,imm=0) R6_w=map_value_or_null(id=1,off=0,ks=4,vs=16,imm=0)
; if (!e)
8: (15) if r6 == 0x0 goto pc+81 ; R6_w=map_value(off=0,ks=4,vs=16,imm=0)
; bpf_rcu_read_lock();
9: (85) call bpf_rcu_read_lock#87892 ;
; p = e->pc;
10: (bf) r7 = r6 ; R6=map_value(off=0,ks=4,vs=16,imm=0) R7_w=map_value(off=0,ks=4,vs=16,imm=0)
11: (07) r7 += 8 ; R7_w=map_value(off=8,ks=4,vs=16,imm=0)
12: (79) r6 = *(u64 *)(r6 +8) ; R6_w=percpu_rcu_ptr_or_null_val_t(id=2,off=0,imm=0)
; if (!p) {
13: (55) if r6 != 0x0 goto pc+13 ; R6_w=0
; p = bpf_percpu_obj_new(struct val_t);
14: (18) r1 = 0x12 ; R1_w=18
16: (b7) r2 = 0 ; R2_w=0
17: (85) call bpf_percpu_obj_new_impl#87883 ; R0_w=percpu_ptr_or_null_val_t(id=4,ref_obj_id=4,off=0,imm=0) refs=4
18: (bf) r6 = r0 ; R0=percpu_ptr_or_null_val_t(id=4,ref_obj_id=4,off=0,imm=0) R6=percpu_ptr_or_null_val_t(id=4,ref_obj_id=4,off=0,imm=0) refs=4
; if (!p)
19: (15) if r6 == 0x0 goto pc+69 ; R6=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0) refs=4
; p1 = bpf_kptr_xchg(&e->pc, p);
20: (bf) r1 = r7 ; R1_w=map_value(off=8,ks=4,vs=16,imm=0) R7=map_value(off=8,ks=4,vs=16,imm=0) refs=4
21: (bf) r2 = r6 ; R2_w=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0) R6=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0) refs=4
22: (85) call bpf_kptr_xchg#194 ; R0_w=percpu_ptr_or_null_val_t(id=6,ref_obj_id=6,off=0,imm=0) refs=6
; if (p1) {
23: (15) if r0 == 0x0 goto pc+3 ; R0_w=percpu_ptr_val_t(ref_obj_id=6,off=0,imm=0) refs=6
; bpf_percpu_obj_drop(p1);
24: (bf) r1 = r0 ; R0_w=percpu_ptr_val_t(ref_obj_id=6,off=0,imm=0) R1_w=percpu_ptr_val_t(ref_obj_id=6,off=0,imm=0) refs=6
25: (b7) r2 = 0 ; R2_w=0 refs=6
26: (85) call bpf_percpu_obj_drop_impl#87882 ;
; v = bpf_this_cpu_ptr(p);
27: (bf) r1 = r6 ; R1_w=scalar(id=7) R6=scalar(id=7)
28: (85) call bpf_this_cpu_ptr#154
R1 type=scalar expected=percpu_ptr_, percpu_rcu_ptr_, percpu_trusted_ptr_
The R1 which gets its value from R6 is a scalar. But before insn 22, R6 is
R6=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0)
Its type is changed to a scalar at insn 22 without previous patch.
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20230827152821.2001129-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
184 lines
2.8 KiB
C
184 lines
2.8 KiB
C
#include "bpf_experimental.h"
|
|
|
|
struct val_t {
|
|
long b, c, d;
|
|
};
|
|
|
|
struct elem {
|
|
long sum;
|
|
struct val_t __percpu_kptr *pc;
|
|
};
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
|
__uint(max_entries, 1);
|
|
__type(key, int);
|
|
__type(value, struct elem);
|
|
} array SEC(".maps");
|
|
|
|
void bpf_rcu_read_lock(void) __ksym;
|
|
void bpf_rcu_read_unlock(void) __ksym;
|
|
|
|
const volatile int nr_cpus;
|
|
|
|
/* Initialize the percpu object */
|
|
SEC("?fentry/bpf_fentry_test1")
|
|
int BPF_PROG(test_array_map_1)
|
|
{
|
|
struct val_t __percpu_kptr *p;
|
|
struct elem *e;
|
|
int index = 0;
|
|
|
|
e = bpf_map_lookup_elem(&array, &index);
|
|
if (!e)
|
|
return 0;
|
|
|
|
p = bpf_percpu_obj_new(struct val_t);
|
|
if (!p)
|
|
return 0;
|
|
|
|
p = bpf_kptr_xchg(&e->pc, p);
|
|
if (p)
|
|
bpf_percpu_obj_drop(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Update percpu data */
|
|
SEC("?fentry/bpf_fentry_test2")
|
|
int BPF_PROG(test_array_map_2)
|
|
{
|
|
struct val_t __percpu_kptr *p;
|
|
struct val_t *v;
|
|
struct elem *e;
|
|
int index = 0;
|
|
|
|
e = bpf_map_lookup_elem(&array, &index);
|
|
if (!e)
|
|
return 0;
|
|
|
|
p = e->pc;
|
|
if (!p)
|
|
return 0;
|
|
|
|
v = bpf_per_cpu_ptr(p, 0);
|
|
if (!v)
|
|
return 0;
|
|
v->c = 1;
|
|
v->d = 2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cpu0_field_d, sum_field_c;
|
|
|
|
/* Summarize percpu data */
|
|
SEC("?fentry/bpf_fentry_test3")
|
|
int BPF_PROG(test_array_map_3)
|
|
{
|
|
struct val_t __percpu_kptr *p;
|
|
int i, index = 0;
|
|
struct val_t *v;
|
|
struct elem *e;
|
|
|
|
e = bpf_map_lookup_elem(&array, &index);
|
|
if (!e)
|
|
return 0;
|
|
|
|
p = e->pc;
|
|
if (!p)
|
|
return 0;
|
|
|
|
bpf_for(i, 0, nr_cpus) {
|
|
v = bpf_per_cpu_ptr(p, i);
|
|
if (v) {
|
|
if (i == 0)
|
|
cpu0_field_d = v->d;
|
|
sum_field_c += v->c;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Explicitly free allocated percpu data */
|
|
SEC("?fentry/bpf_fentry_test4")
|
|
int BPF_PROG(test_array_map_4)
|
|
{
|
|
struct val_t __percpu_kptr *p;
|
|
struct elem *e;
|
|
int index = 0;
|
|
|
|
e = bpf_map_lookup_elem(&array, &index);
|
|
if (!e)
|
|
return 0;
|
|
|
|
/* delete */
|
|
p = bpf_kptr_xchg(&e->pc, NULL);
|
|
if (p) {
|
|
bpf_percpu_obj_drop(p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SEC("?fentry.s/bpf_fentry_test1")
|
|
int BPF_PROG(test_array_map_10)
|
|
{
|
|
struct val_t __percpu_kptr *p, *p1;
|
|
int i, index = 0;
|
|
struct val_t *v;
|
|
struct elem *e;
|
|
|
|
e = bpf_map_lookup_elem(&array, &index);
|
|
if (!e)
|
|
return 0;
|
|
|
|
bpf_rcu_read_lock();
|
|
p = e->pc;
|
|
if (!p) {
|
|
p = bpf_percpu_obj_new(struct val_t);
|
|
if (!p)
|
|
goto out;
|
|
|
|
p1 = bpf_kptr_xchg(&e->pc, p);
|
|
if (p1) {
|
|
/* race condition */
|
|
bpf_percpu_obj_drop(p1);
|
|
}
|
|
}
|
|
|
|
v = bpf_this_cpu_ptr(p);
|
|
v->c = 3;
|
|
v = bpf_this_cpu_ptr(p);
|
|
v->c = 0;
|
|
|
|
v = bpf_per_cpu_ptr(p, 0);
|
|
if (!v)
|
|
goto out;
|
|
v->c = 1;
|
|
v->d = 2;
|
|
|
|
/* delete */
|
|
p1 = bpf_kptr_xchg(&e->pc, NULL);
|
|
if (!p1)
|
|
goto out;
|
|
|
|
bpf_for(i, 0, nr_cpus) {
|
|
v = bpf_per_cpu_ptr(p, i);
|
|
if (v) {
|
|
if (i == 0)
|
|
cpu0_field_d = v->d;
|
|
sum_field_c += v->c;
|
|
}
|
|
}
|
|
|
|
/* finally release p */
|
|
bpf_percpu_obj_drop(p1);
|
|
out:
|
|
bpf_rcu_read_unlock();
|
|
return 0;
|
|
}
|
|
|
|
char _license[] SEC("license") = "GPL";
|