1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Test cases for binder allocator code.
4 *
5 * Copyright 2025 Google LLC.
6 * Author: Tiffany Yang <ynaffit@google.com>
7 */
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <kunit/test.h>
12 #include <linux/anon_inodes.h>
13 #include <linux/err.h>
14 #include <linux/file.h>
15 #include <linux/fs.h>
16 #include <linux/mm.h>
17 #include <linux/mman.h>
18 #include <linux/seq_buf.h>
19 #include <linux/sizes.h>
20
21 #include "../binder_alloc.h"
22 #include "../binder_internal.h"
23
24 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
25
26 #define BINDER_MMAP_SIZE SZ_128K
27
28 #define BUFFER_NUM 5
29 #define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
30
31 #define FREESEQ_BUFLEN ((3 * BUFFER_NUM) + 1)
32
33 #define ALIGN_TYPE_STRLEN (12)
34
35 #define ALIGNMENTS_BUFLEN (((ALIGN_TYPE_STRLEN + 6) * BUFFER_NUM) + 1)
36
37 #define PRINT_ALL_CASES (0)
38
39 /* 5^5 alignment combinations * 2 places to share pages * 5! free sequences */
40 #define TOTAL_EXHAUSTIVE_CASES (3125 * 2 * 120)
41
42 /**
43 * enum buf_end_align_type - Page alignment of a buffer
44 * end with regard to the end of the previous buffer.
45 *
46 * In the pictures below, buf2 refers to the buffer we
47 * are aligning. buf1 refers to previous buffer by addr.
48 * Symbol [ means the start of a buffer, ] means the end
49 * of a buffer, and | means page boundaries.
50 */
51 enum buf_end_align_type {
52 /**
53 * @SAME_PAGE_UNALIGNED: The end of this buffer is on
54 * the same page as the end of the previous buffer and
55 * is not page aligned. Examples:
56 * buf1 ][ buf2 ][ ...
57 * buf1 ]|[ buf2 ][ ...
58 */
59 SAME_PAGE_UNALIGNED = 0,
60 /**
61 * @SAME_PAGE_ALIGNED: When the end of the previous buffer
62 * is not page aligned, the end of this buffer is on the
63 * same page as the end of the previous buffer and is page
64 * aligned. When the previous buffer is page aligned, the
65 * end of this buffer is aligned to the next page boundary.
66 * Examples:
67 * buf1 ][ buf2 ]| ...
68 * buf1 ]|[ buf2 ]| ...
69 */
70 SAME_PAGE_ALIGNED,
71 /**
72 * @NEXT_PAGE_UNALIGNED: The end of this buffer is on
73 * the page next to the end of the previous buffer and
74 * is not page aligned. Examples:
75 * buf1 ][ buf2 | buf2 ][ ...
76 * buf1 ]|[ buf2 | buf2 ][ ...
77 */
78 NEXT_PAGE_UNALIGNED,
79 /**
80 * @NEXT_PAGE_ALIGNED: The end of this buffer is on
81 * the page next to the end of the previous buffer and
82 * is page aligned. Examples:
83 * buf1 ][ buf2 | buf2 ]| ...
84 * buf1 ]|[ buf2 | buf2 ]| ...
85 */
86 NEXT_PAGE_ALIGNED,
87 /**
88 * @NEXT_NEXT_UNALIGNED: The end of this buffer is on
89 * the page that follows the page after the end of the
90 * previous buffer and is not page aligned. Examples:
91 * buf1 ][ buf2 | buf2 | buf2 ][ ...
92 * buf1 ]|[ buf2 | buf2 | buf2 ][ ...
93 */
94 NEXT_NEXT_UNALIGNED,
95 /**
96 * @LOOP_END: The number of enum values in &buf_end_align_type.
97 * It is used for controlling loop termination.
98 */
99 LOOP_END,
100 };
101
102 static const char *const buf_end_align_type_strs[LOOP_END] = {
103 [SAME_PAGE_UNALIGNED] = "SP_UNALIGNED",
104 [SAME_PAGE_ALIGNED] = " SP_ALIGNED ",
105 [NEXT_PAGE_UNALIGNED] = "NP_UNALIGNED",
106 [NEXT_PAGE_ALIGNED] = " NP_ALIGNED ",
107 [NEXT_NEXT_UNALIGNED] = "NN_UNALIGNED",
108 };
109
110 struct binder_alloc_test_case_info {
111 char alignments[ALIGNMENTS_BUFLEN];
112 struct seq_buf alignments_sb;
113 size_t *buffer_sizes;
114 int *free_sequence;
115 bool front_pages;
116 };
117
stringify_free_seq(struct kunit * test,int * seq,struct seq_buf * sb)118 static void stringify_free_seq(struct kunit *test, int *seq, struct seq_buf *sb)
119 {
120 int i;
121
122 for (i = 0; i < BUFFER_NUM; i++)
123 seq_buf_printf(sb, "[%d]", seq[i]);
124
125 KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb));
126 }
127
stringify_alignments(struct kunit * test,int * alignments,struct seq_buf * sb)128 static void stringify_alignments(struct kunit *test, int *alignments,
129 struct seq_buf *sb)
130 {
131 int i;
132
133 for (i = 0; i < BUFFER_NUM; i++)
134 seq_buf_printf(sb, "[ %d:%s ]", i,
135 buf_end_align_type_strs[alignments[i]]);
136
137 KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb));
138 }
139
check_buffer_pages_allocated(struct kunit * test,struct binder_alloc * alloc,struct binder_buffer * buffer,size_t size)140 static bool check_buffer_pages_allocated(struct kunit *test,
141 struct binder_alloc *alloc,
142 struct binder_buffer *buffer,
143 size_t size)
144 {
145 unsigned long page_addr;
146 unsigned long end;
147 int page_index;
148
149 end = PAGE_ALIGN(buffer->user_data + size);
150 page_addr = buffer->user_data;
151 for (; page_addr < end; page_addr += PAGE_SIZE) {
152 page_index = (page_addr - alloc->vm_start) / PAGE_SIZE;
153 if (!alloc->pages[page_index] ||
154 !list_empty(page_to_lru(alloc->pages[page_index]))) {
155 kunit_err(test, "expect alloc but is %s at page index %d\n",
156 alloc->pages[page_index] ?
157 "lru" : "free", page_index);
158 return false;
159 }
160 }
161 return true;
162 }
163
binder_alloc_test_alloc_buf(struct kunit * test,struct binder_alloc * alloc,struct binder_buffer * buffers[],size_t * sizes,int * seq)164 static unsigned long binder_alloc_test_alloc_buf(struct kunit *test,
165 struct binder_alloc *alloc,
166 struct binder_buffer *buffers[],
167 size_t *sizes, int *seq)
168 {
169 unsigned long failures = 0;
170 int i;
171
172 for (i = 0; i < BUFFER_NUM; i++) {
173 buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0);
174 if (IS_ERR(buffers[i]) ||
175 !check_buffer_pages_allocated(test, alloc, buffers[i], sizes[i]))
176 failures++;
177 }
178
179 return failures;
180 }
181
binder_alloc_test_free_buf(struct kunit * test,struct binder_alloc * alloc,struct binder_buffer * buffers[],size_t * sizes,int * seq,size_t end)182 static unsigned long binder_alloc_test_free_buf(struct kunit *test,
183 struct binder_alloc *alloc,
184 struct binder_buffer *buffers[],
185 size_t *sizes, int *seq, size_t end)
186 {
187 unsigned long failures = 0;
188 int i;
189
190 for (i = 0; i < BUFFER_NUM; i++)
191 binder_alloc_free_buf(alloc, buffers[seq[i]]);
192
193 for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) {
194 if (list_empty(page_to_lru(alloc->pages[i]))) {
195 kunit_err(test, "expect lru but is %s at page index %d\n",
196 alloc->pages[i] ? "alloc" : "free", i);
197 failures++;
198 }
199 }
200
201 return failures;
202 }
203
binder_alloc_test_free_page(struct kunit * test,struct binder_alloc * alloc)204 static unsigned long binder_alloc_test_free_page(struct kunit *test,
205 struct binder_alloc *alloc)
206 {
207 unsigned long failures = 0;
208 unsigned long count;
209 int i;
210
211 while ((count = list_lru_count(alloc->freelist))) {
212 list_lru_walk(alloc->freelist, binder_alloc_free_page,
213 NULL, count);
214 }
215
216 for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
217 if (alloc->pages[i]) {
218 kunit_err(test, "expect free but is %s at page index %d\n",
219 list_empty(page_to_lru(alloc->pages[i])) ?
220 "alloc" : "lru", i);
221 failures++;
222 }
223 }
224
225 return failures;
226 }
227
228 /* Executes one full test run for the given test case. */
binder_alloc_test_alloc_free(struct kunit * test,struct binder_alloc * alloc,struct binder_alloc_test_case_info * tc,size_t end)229 static bool binder_alloc_test_alloc_free(struct kunit *test,
230 struct binder_alloc *alloc,
231 struct binder_alloc_test_case_info *tc,
232 size_t end)
233 {
234 unsigned long pages = PAGE_ALIGN(end) / PAGE_SIZE;
235 struct binder_buffer *buffers[BUFFER_NUM];
236 unsigned long failures;
237 bool failed = false;
238
239 failures = binder_alloc_test_alloc_buf(test, alloc, buffers,
240 tc->buffer_sizes,
241 tc->free_sequence);
242 failed = failed || failures;
243 KUNIT_EXPECT_EQ_MSG(test, failures, 0,
244 "Initial allocation failed: %lu/%u buffers with errors",
245 failures, BUFFER_NUM);
246
247 failures = binder_alloc_test_free_buf(test, alloc, buffers,
248 tc->buffer_sizes,
249 tc->free_sequence, end);
250 failed = failed || failures;
251 KUNIT_EXPECT_EQ_MSG(test, failures, 0,
252 "Initial buffers not freed correctly: %lu/%lu pages not on lru list",
253 failures, pages);
254
255 /* Allocate from lru. */
256 failures = binder_alloc_test_alloc_buf(test, alloc, buffers,
257 tc->buffer_sizes,
258 tc->free_sequence);
259 failed = failed || failures;
260 KUNIT_EXPECT_EQ_MSG(test, failures, 0,
261 "Reallocation failed: %lu/%u buffers with errors",
262 failures, BUFFER_NUM);
263
264 failures = list_lru_count(alloc->freelist);
265 failed = failed || failures;
266 KUNIT_EXPECT_EQ_MSG(test, failures, 0,
267 "lru list should be empty after reallocation but still has %lu pages",
268 failures);
269
270 failures = binder_alloc_test_free_buf(test, alloc, buffers,
271 tc->buffer_sizes,
272 tc->free_sequence, end);
273 failed = failed || failures;
274 KUNIT_EXPECT_EQ_MSG(test, failures, 0,
275 "Reallocated buffers not freed correctly: %lu/%lu pages not on lru list",
276 failures, pages);
277
278 failures = binder_alloc_test_free_page(test, alloc);
279 failed = failed || failures;
280 KUNIT_EXPECT_EQ_MSG(test, failures, 0,
281 "Failed to clean up allocated pages: %lu/%lu pages still installed",
282 failures, (alloc->buffer_size / PAGE_SIZE));
283
284 return failed;
285 }
286
is_dup(int * seq,int index,int val)287 static bool is_dup(int *seq, int index, int val)
288 {
289 int i;
290
291 for (i = 0; i < index; i++) {
292 if (seq[i] == val)
293 return true;
294 }
295 return false;
296 }
297
298 /* Generate BUFFER_NUM factorial free orders. */
permute_frees(struct kunit * test,struct binder_alloc * alloc,struct binder_alloc_test_case_info * tc,unsigned long * runs,unsigned long * failures,int index,size_t end)299 static void permute_frees(struct kunit *test, struct binder_alloc *alloc,
300 struct binder_alloc_test_case_info *tc,
301 unsigned long *runs, unsigned long *failures,
302 int index, size_t end)
303 {
304 bool case_failed;
305 int i;
306
307 if (index == BUFFER_NUM) {
308 DECLARE_SEQ_BUF(freeseq_sb, FREESEQ_BUFLEN);
309
310 case_failed = binder_alloc_test_alloc_free(test, alloc, tc, end);
311 *runs += 1;
312 *failures += case_failed;
313
314 if (case_failed || PRINT_ALL_CASES) {
315 stringify_free_seq(test, tc->free_sequence,
316 &freeseq_sb);
317 kunit_err(test, "case %lu: [%s] | %s - %s - %s", *runs,
318 case_failed ? "FAILED" : "PASSED",
319 tc->front_pages ? "front" : "back ",
320 seq_buf_str(&tc->alignments_sb),
321 seq_buf_str(&freeseq_sb));
322 }
323
324 return;
325 }
326 for (i = 0; i < BUFFER_NUM; i++) {
327 if (is_dup(tc->free_sequence, index, i))
328 continue;
329 tc->free_sequence[index] = i;
330 permute_frees(test, alloc, tc, runs, failures, index + 1, end);
331 }
332 }
333
gen_buf_sizes(struct kunit * test,struct binder_alloc * alloc,struct binder_alloc_test_case_info * tc,size_t * end_offset,unsigned long * runs,unsigned long * failures)334 static void gen_buf_sizes(struct kunit *test,
335 struct binder_alloc *alloc,
336 struct binder_alloc_test_case_info *tc,
337 size_t *end_offset, unsigned long *runs,
338 unsigned long *failures)
339 {
340 size_t last_offset, offset = 0;
341 size_t front_sizes[BUFFER_NUM];
342 size_t back_sizes[BUFFER_NUM];
343 int seq[BUFFER_NUM] = {0};
344 int i;
345
346 tc->free_sequence = seq;
347 for (i = 0; i < BUFFER_NUM; i++) {
348 last_offset = offset;
349 offset = end_offset[i];
350 front_sizes[i] = offset - last_offset;
351 back_sizes[BUFFER_NUM - i - 1] = front_sizes[i];
352 }
353 back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1];
354
355 /*
356 * Buffers share the first or last few pages.
357 * Only BUFFER_NUM - 1 buffer sizes are adjustable since
358 * we need one giant buffer before getting to the last page.
359 */
360 tc->front_pages = true;
361 tc->buffer_sizes = front_sizes;
362 permute_frees(test, alloc, tc, runs, failures, 0,
363 end_offset[BUFFER_NUM - 1]);
364
365 tc->front_pages = false;
366 tc->buffer_sizes = back_sizes;
367 permute_frees(test, alloc, tc, runs, failures, 0, alloc->buffer_size);
368 }
369
gen_buf_offsets(struct kunit * test,struct binder_alloc * alloc,size_t * end_offset,int * alignments,unsigned long * runs,unsigned long * failures,int index)370 static void gen_buf_offsets(struct kunit *test, struct binder_alloc *alloc,
371 size_t *end_offset, int *alignments,
372 unsigned long *runs, unsigned long *failures,
373 int index)
374 {
375 size_t end, prev;
376 int align;
377
378 if (index == BUFFER_NUM) {
379 struct binder_alloc_test_case_info tc = {0};
380
381 seq_buf_init(&tc.alignments_sb, tc.alignments,
382 ALIGNMENTS_BUFLEN);
383 stringify_alignments(test, alignments, &tc.alignments_sb);
384
385 gen_buf_sizes(test, alloc, &tc, end_offset, runs, failures);
386 return;
387 }
388 prev = index == 0 ? 0 : end_offset[index - 1];
389 end = prev;
390
391 BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE);
392
393 for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) {
394 if (align % 2)
395 end = ALIGN(end, PAGE_SIZE);
396 else
397 end += BUFFER_MIN_SIZE;
398 end_offset[index] = end;
399 alignments[index] = align;
400 gen_buf_offsets(test, alloc, end_offset, alignments, runs,
401 failures, index + 1);
402 }
403 }
404
405 struct binder_alloc_test {
406 struct binder_alloc alloc;
407 struct list_lru binder_test_freelist;
408 struct file *filp;
409 unsigned long mmap_uaddr;
410 };
411
binder_alloc_test_init_freelist(struct kunit * test)412 static void binder_alloc_test_init_freelist(struct kunit *test)
413 {
414 struct binder_alloc_test *priv = test->priv;
415
416 KUNIT_EXPECT_PTR_EQ(test, priv->alloc.freelist,
417 &priv->binder_test_freelist);
418 }
419
binder_alloc_test_mmap(struct kunit * test)420 static void binder_alloc_test_mmap(struct kunit *test)
421 {
422 struct binder_alloc_test *priv = test->priv;
423 struct binder_alloc *alloc = &priv->alloc;
424 struct binder_buffer *buf;
425 struct rb_node *n;
426
427 KUNIT_EXPECT_EQ(test, alloc->mapped, true);
428 KUNIT_EXPECT_EQ(test, alloc->buffer_size, BINDER_MMAP_SIZE);
429
430 n = rb_first(&alloc->allocated_buffers);
431 KUNIT_EXPECT_PTR_EQ(test, n, NULL);
432
433 n = rb_first(&alloc->free_buffers);
434 buf = rb_entry(n, struct binder_buffer, rb_node);
435 KUNIT_EXPECT_EQ(test, binder_alloc_buffer_size(alloc, buf),
436 BINDER_MMAP_SIZE);
437 KUNIT_EXPECT_TRUE(test, list_is_last(&buf->entry, &alloc->buffers));
438 }
439
440 /**
441 * binder_alloc_exhaustive_test() - Exhaustively test alloc and free of buffer pages.
442 * @test: The test context object.
443 *
444 * Allocate BUFFER_NUM buffers to cover all page alignment cases,
445 * then free them in all orders possible. Check that pages are
446 * correctly allocated, put onto lru when buffers are freed, and
447 * are freed when binder_alloc_free_page() is called.
448 */
binder_alloc_exhaustive_test(struct kunit * test)449 static void binder_alloc_exhaustive_test(struct kunit *test)
450 {
451 struct binder_alloc_test *priv = test->priv;
452 size_t end_offset[BUFFER_NUM];
453 int alignments[BUFFER_NUM];
454 unsigned long failures = 0;
455 unsigned long runs = 0;
456
457 gen_buf_offsets(test, &priv->alloc, end_offset, alignments, &runs,
458 &failures, 0);
459
460 KUNIT_EXPECT_EQ(test, runs, TOTAL_EXHAUSTIVE_CASES);
461 KUNIT_EXPECT_EQ(test, failures, 0);
462 }
463
464 /* ===== End test cases ===== */
465
binder_alloc_test_vma_close(struct vm_area_struct * vma)466 static void binder_alloc_test_vma_close(struct vm_area_struct *vma)
467 {
468 struct binder_alloc *alloc = vma->vm_private_data;
469
470 binder_alloc_vma_close(alloc);
471 }
472
473 static const struct vm_operations_struct binder_alloc_test_vm_ops = {
474 .close = binder_alloc_test_vma_close,
475 .fault = binder_vm_fault,
476 };
477
binder_alloc_test_mmap_handler(struct file * filp,struct vm_area_struct * vma)478 static int binder_alloc_test_mmap_handler(struct file *filp,
479 struct vm_area_struct *vma)
480 {
481 struct binder_alloc *alloc = filp->private_data;
482
483 vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE);
484
485 vma->vm_ops = &binder_alloc_test_vm_ops;
486 vma->vm_private_data = alloc;
487
488 return binder_alloc_mmap_handler(alloc, vma);
489 }
490
491 static const struct file_operations binder_alloc_test_fops = {
492 .mmap = binder_alloc_test_mmap_handler,
493 };
494
binder_alloc_test_init(struct kunit * test)495 static int binder_alloc_test_init(struct kunit *test)
496 {
497 struct binder_alloc_test *priv;
498 int ret;
499
500 priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
501 if (!priv)
502 return -ENOMEM;
503 test->priv = priv;
504
505 ret = list_lru_init(&priv->binder_test_freelist);
506 if (ret) {
507 kunit_err(test, "Failed to initialize test freelist\n");
508 return ret;
509 }
510
511 /* __binder_alloc_init requires mm to be attached */
512 ret = kunit_attach_mm();
513 if (ret) {
514 kunit_err(test, "Failed to attach mm\n");
515 return ret;
516 }
517 __binder_alloc_init(&priv->alloc, &priv->binder_test_freelist);
518
519 priv->filp = anon_inode_getfile("binder_alloc_kunit",
520 &binder_alloc_test_fops, &priv->alloc,
521 O_RDWR | O_CLOEXEC);
522 if (IS_ERR_OR_NULL(priv->filp)) {
523 kunit_err(test, "Failed to open binder alloc test driver file\n");
524 return priv->filp ? PTR_ERR(priv->filp) : -ENOMEM;
525 }
526
527 priv->mmap_uaddr = kunit_vm_mmap(test, priv->filp, 0, BINDER_MMAP_SIZE,
528 PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
529 0);
530 if (!priv->mmap_uaddr) {
531 kunit_err(test, "Could not map the test's transaction memory\n");
532 return -ENOMEM;
533 }
534
535 return 0;
536 }
537
binder_alloc_test_exit(struct kunit * test)538 static void binder_alloc_test_exit(struct kunit *test)
539 {
540 struct binder_alloc_test *priv = test->priv;
541
542 /* Close the backing file to make sure binder_alloc_vma_close runs */
543 if (!IS_ERR_OR_NULL(priv->filp))
544 fput(priv->filp);
545
546 if (priv->alloc.mm)
547 binder_alloc_deferred_release(&priv->alloc);
548
549 /* Make sure freelist is empty */
550 KUNIT_EXPECT_EQ(test, list_lru_count(&priv->binder_test_freelist), 0);
551 list_lru_destroy(&priv->binder_test_freelist);
552 }
553
554 static struct kunit_case binder_alloc_test_cases[] = {
555 KUNIT_CASE(binder_alloc_test_init_freelist),
556 KUNIT_CASE(binder_alloc_test_mmap),
557 KUNIT_CASE(binder_alloc_exhaustive_test),
558 {}
559 };
560
561 static struct kunit_suite binder_alloc_test_suite = {
562 .name = "binder_alloc",
563 .test_cases = binder_alloc_test_cases,
564 .init = binder_alloc_test_init,
565 .exit = binder_alloc_test_exit,
566 };
567
568 kunit_test_suite(binder_alloc_test_suite);
569
570 MODULE_AUTHOR("Tiffany Yang <ynaffit@google.com>");
571 MODULE_DESCRIPTION("Binder Alloc KUnit tests");
572 MODULE_LICENSE("GPL");
573