xref: /linux/tools/testing/selftests/bpf/progs/percpu_alloc_array.c (revision be239684b18e1cdcafcf8c7face4a2f562c745ad)
1 #include "bpf_experimental.h"
2 
3 struct val_t {
4 	long b, c, d;
5 };
6 
7 struct elem {
8 	long sum;
9 	struct val_t __percpu_kptr *pc;
10 };
11 
12 struct {
13 	__uint(type, BPF_MAP_TYPE_ARRAY);
14 	__uint(max_entries, 1);
15 	__type(key, int);
16 	__type(value, struct elem);
17 } array SEC(".maps");
18 
19 void bpf_rcu_read_lock(void) __ksym;
20 void bpf_rcu_read_unlock(void) __ksym;
21 
22 const volatile int nr_cpus;
23 
24 /* Initialize the percpu object */
25 SEC("?fentry/bpf_fentry_test1")
26 int BPF_PROG(test_array_map_1)
27 {
28 	struct val_t __percpu_kptr *p;
29 	struct elem *e;
30 	int index = 0;
31 
32 	e = bpf_map_lookup_elem(&array, &index);
33 	if (!e)
34 		return 0;
35 
36 	p = bpf_percpu_obj_new(struct val_t);
37 	if (!p)
38 		return 0;
39 
40 	p = bpf_kptr_xchg(&e->pc, p);
41 	if (p)
42 		bpf_percpu_obj_drop(p);
43 
44 	return 0;
45 }
46 
47 /* Update percpu data */
48 SEC("?fentry/bpf_fentry_test2")
49 int BPF_PROG(test_array_map_2)
50 {
51 	struct val_t __percpu_kptr *p;
52 	struct val_t *v;
53 	struct elem *e;
54 	int index = 0;
55 
56 	e = bpf_map_lookup_elem(&array, &index);
57 	if (!e)
58 		return 0;
59 
60 	p = e->pc;
61 	if (!p)
62 		return 0;
63 
64 	v = bpf_per_cpu_ptr(p, 0);
65 	if (!v)
66 		return 0;
67 	v->c = 1;
68 	v->d = 2;
69 
70 	return 0;
71 }
72 
73 int cpu0_field_d, sum_field_c;
74 int my_pid;
75 
76 /* Summarize percpu data */
77 SEC("?fentry/bpf_fentry_test3")
78 int BPF_PROG(test_array_map_3)
79 {
80 	struct val_t __percpu_kptr *p;
81 	int i, index = 0;
82 	struct val_t *v;
83 	struct elem *e;
84 
85 	if ((bpf_get_current_pid_tgid() >> 32) != my_pid)
86 		return 0;
87 
88 	e = bpf_map_lookup_elem(&array, &index);
89 	if (!e)
90 		return 0;
91 
92 	p = e->pc;
93 	if (!p)
94 		return 0;
95 
96 	bpf_for(i, 0, nr_cpus) {
97 		v = bpf_per_cpu_ptr(p, i);
98 		if (v) {
99 			if (i == 0)
100 				cpu0_field_d = v->d;
101 			sum_field_c += v->c;
102 		}
103 	}
104 
105 	return 0;
106 }
107 
108 /* Explicitly free allocated percpu data */
109 SEC("?fentry/bpf_fentry_test4")
110 int BPF_PROG(test_array_map_4)
111 {
112 	struct val_t __percpu_kptr *p;
113 	struct elem *e;
114 	int index = 0;
115 
116 	e = bpf_map_lookup_elem(&array, &index);
117 	if (!e)
118 		return 0;
119 
120 	/* delete */
121 	p = bpf_kptr_xchg(&e->pc, NULL);
122 	if (p) {
123 		bpf_percpu_obj_drop(p);
124 	}
125 
126 	return 0;
127 }
128 
129 SEC("?fentry.s/bpf_fentry_test1")
130 int BPF_PROG(test_array_map_10)
131 {
132 	struct val_t __percpu_kptr *p, *p1;
133 	int i, index = 0;
134 	struct val_t *v;
135 	struct elem *e;
136 
137 	if ((bpf_get_current_pid_tgid() >> 32) != my_pid)
138 		return 0;
139 
140 	e = bpf_map_lookup_elem(&array, &index);
141 	if (!e)
142 		return 0;
143 
144 	bpf_rcu_read_lock();
145 	p = e->pc;
146 	if (!p) {
147 		p = bpf_percpu_obj_new(struct val_t);
148 		if (!p)
149 			goto out;
150 
151 		p1 = bpf_kptr_xchg(&e->pc, p);
152 		if (p1) {
153 			/* race condition */
154 			bpf_percpu_obj_drop(p1);
155 		}
156 	}
157 
158 	v = bpf_this_cpu_ptr(p);
159 	v->c = 3;
160 	v = bpf_this_cpu_ptr(p);
161 	v->c = 0;
162 
163 	v = bpf_per_cpu_ptr(p, 0);
164 	if (!v)
165 		goto out;
166 	v->c = 1;
167 	v->d = 2;
168 
169 	/* delete */
170 	p1 = bpf_kptr_xchg(&e->pc, NULL);
171 	if (!p1)
172 		goto out;
173 
174 	bpf_for(i, 0, nr_cpus) {
175 		v = bpf_per_cpu_ptr(p, i);
176 		if (v) {
177 			if (i == 0)
178 				cpu0_field_d = v->d;
179 			sum_field_c += v->c;
180 		}
181 	}
182 
183 	/* finally release p */
184 	bpf_percpu_obj_drop(p1);
185 out:
186 	bpf_rcu_read_unlock();
187 	return 0;
188 }
189 
190 char _license[] SEC("license") = "GPL";
191