xref: /linux/tools/testing/selftests/bpf/libarena/selftests/test_buddy.bpf.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3 
4 #include <libarena/common.h>
5 
6 #include <libarena/asan.h>
7 #include <libarena/buddy.h>
8 
9 extern struct buddy __arena buddy;
10 
11 struct segarr_entry {
12 	u8 __arena *block;
13 	size_t sz;
14 	u8 poison;
15 };
16 
17 #define SEGARRLEN (512)
18 static struct segarr_entry __arena segarr[SEGARRLEN];
19 static void __arena *ptrs[17];
20 size_t __arena alloc_sizes[] = { 3, 17, 1025, 129, 16350, 333, 9, 517 };
21 size_t __arena alloc_multiple_sizes[] = { 3, 17, 1025, 129, 16350, 333, 9, 517, 2099 };
22 size_t __arena alloc_free_sizes[] = { 3, 17, 64, 129, 256, 333, 512, 517 };
23 size_t __arena alignment_sizes[] = { 1, 3, 7, 8, 9, 15, 16, 17, 31,
24 				     32, 64, 100, 128, 255, 256, 512, 1000 };
25 
26 SEC("syscall")
test_buddy_create(void)27 __weak int test_buddy_create(void)
28 {
29 	const int iters = 10;
30 	int ret, i;
31 
32 	for (i = zero; i < iters && can_loop; i++) {
33 		ret = buddy_init(&buddy);
34 		if (ret)
35 			return ret;
36 
37 		ret = buddy_destroy(&buddy);
38 		if (ret)
39 			return ret;
40 	}
41 
42 	return 0;
43 }
44 
45 SEC("syscall")
test_buddy_alloc(void)46 __weak int test_buddy_alloc(void)
47 {
48 	void __arena *mem;
49 	int ret, i;
50 
51 	for (i = zero; i < 8 && can_loop; i++) {
52 		ret = buddy_init(&buddy);
53 		if (ret)
54 			return ret;
55 
56 		mem = buddy_alloc(&buddy, alloc_sizes[i]);
57 		if (!mem) {
58 			buddy_destroy(&buddy);
59 			return -ENOMEM;
60 		}
61 
62 		buddy_destroy(&buddy);
63 	}
64 
65 	return 0;
66 }
67 
68 SEC("syscall")
test_buddy_alloc_free(void)69 __weak int test_buddy_alloc_free(void)
70 {
71 	const int iters = 800;
72 	void __arena *mem;
73 	int ret, i;
74 
75 	ret = buddy_init(&buddy);
76 	if (ret)
77 		return ret;
78 
79 	for (i = zero; i < iters && can_loop; i++) {
80 		mem = buddy_alloc(&buddy, alloc_free_sizes[(i * 5) % 8]);
81 		if (!mem) {
82 			buddy_destroy(&buddy);
83 			return -ENOMEM;
84 		}
85 
86 		buddy_free(&buddy, mem);
87 	}
88 
89 	buddy_destroy(&buddy);
90 
91 	return 0;
92 }
93 
94 SEC("syscall")
test_buddy_alloc_multiple(void)95 __weak int test_buddy_alloc_multiple(void)
96 {
97 	int ret, j;
98 	u32 i, idx;
99 	u8 __arena *mem;
100 	size_t sz;
101 	u8 poison;
102 
103 	ret = buddy_init(&buddy);
104 	if (ret)
105 		return ret;
106 
107 	/*
108 	 * Cycle through each size, allocating an entry in the
109 	 * segarr. Continue for SEGARRLEN iterations. For every
110 	 * allocation write down the size, use the current index
111 	 * as a poison value, and log it with the pointer in the
112 	 * segarr entry. Use the poison value to poison the entire
113 	 * allocated memory according to the size given.
114 	 */
115 	for (i = zero; i < SEGARRLEN && can_loop; i++) {
116 		sz = alloc_multiple_sizes[i % 9];
117 		poison = (u8)i;
118 
119 		mem = buddy_alloc(&buddy, sz);
120 		if (!mem) {
121 			buddy_destroy(&buddy);
122 			arena_stdout("%s:%d", __func__, __LINE__);
123 			return -ENOMEM;
124 		}
125 
126 		segarr[i].block = mem;
127 		segarr[i].sz = sz;
128 		segarr[i].poison = poison;
129 
130 		for (j = zero; j < sz && can_loop; j++) {
131 			mem[j] = poison;
132 			if (mem[j] != poison) {
133 				buddy_destroy(&buddy);
134 				return -EINVAL;
135 			}
136 		}
137 	}
138 
139 	/*
140 	 * Go to (i * 17) % SEGARRLEN, and free the block pointed to.
141 	 * Before freeing, check all bytes have the poisoned value
142 	 * corresponding to the element. If any values are unexpected,
143 	 * return an error. Skip some elements to test destroying the
144 	 * buddy allocator while data is still allocated.
145 	 */
146 	for (i = 10; i < SEGARRLEN && can_loop; i++) {
147 		idx = (i * 17) % SEGARRLEN;
148 
149 		mem = segarr[idx].block;
150 		sz = segarr[idx].sz;
151 		poison = segarr[idx].poison;
152 
153 		for (j = zero; j < sz && can_loop; j++) {
154 			if (mem[j] != poison) {
155 				buddy_destroy(&buddy);
156 				arena_stdout("%s:%d %lx %u vs %u", __func__,
157 					   __LINE__, (uintptr_t)&mem[j],
158 					   mem[j], poison);
159 				return -EINVAL;
160 			}
161 		}
162 
163 		buddy_free(&buddy, mem);
164 	}
165 
166 	buddy_destroy(&buddy);
167 
168 	return 0;
169 }
170 
171 SEC("syscall")
test_buddy_alignment(void)172 __weak int test_buddy_alignment(void)
173 {
174 	int ret;
175 	u32 i;
176 
177 	ret = buddy_init(&buddy);
178 	if (ret)
179 		return ret;
180 
181 	/* Allocate various sizes and check alignment */
182 	for (i = zero; i < 17 && can_loop; i++) {
183 		barrier_var(i);
184 		ptrs[i] = buddy_alloc(&buddy, alignment_sizes[i]);
185 		if (!ptrs[i]) {
186 			arena_stdout("alignment test: alloc failed for size %lu",
187 				   alignment_sizes[i]);
188 			buddy_destroy(&buddy);
189 			return -ENOMEM;
190 		}
191 
192 		/* Check 8-byte alignment */
193 		if ((u64)ptrs[i] & 0x7) {
194 			arena_stdout(
195 				"alignment test: ptr %llx not 8-byte aligned (size %lu)",
196 				(u64)ptrs[i], alignment_sizes[i]);
197 			buddy_destroy(&buddy);
198 			return -EINVAL;
199 		}
200 	}
201 
202 	/* Free all allocations */
203 	for (i = zero; i < 17 && can_loop; i++) {
204 		barrier_var(i);
205 		buddy_free(&buddy, ptrs[i]);
206 	}
207 
208 	buddy_destroy(&buddy);
209 
210 	return 0;
211 }
212 
213 __weak char _license[] SEC("license") = "GPL";
214