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

Configure Feed

Select the types of activity you want to include in your feed.

binder: Store lru freelist in binder_alloc

Store a pointer to the free pages list that the binder allocator should
use for a process inside of struct binder_alloc. This change allows
binder allocator code to be tested and debugged deterministically while
a system is using binder; i.e., without interfering with other binder
processes and independently of the shrinker. This is necessary to
convert the current binder_alloc_selftest into a kunit test that does
not rely on hijacking an existing binder_proc to run.

A binder process's binder_alloc->freelist should not be changed after
it is initialized. A sole exception is the process that runs the
existing binder_alloc selftest. Its freelist can be temporarily replaced
for the duration of the test because it runs as a single thread before
any pages can be added to the global binder freelist, and the test frees
every page it allocates before dropping the binder_selftest_lock. This
exception allows the existing selftest to be used to check for
regressions, but it will be dropped when the binder_alloc tests are
converted to kunit in a subsequent patch in this series.

Signed-off-by: Tiffany Yang <ynaffit@google.com>
Acked-by: Carlos Llamas <cmllamas@google.com>
Link: https://lore.kernel.org/r/20250714185321.2417234-3-ynaffit@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tiffany Yang and committed by
Greg Kroah-Hartman
4328a526 bea3e7bf

+67 -20
+16 -9
drivers/android/binder_alloc.c
··· 26 26 #include "binder_alloc.h" 27 27 #include "binder_trace.h" 28 28 29 - struct list_lru binder_freelist; 29 + static struct list_lru binder_freelist; 30 30 31 31 static DEFINE_MUTEX(binder_alloc_mmap_lock); 32 32 ··· 206 206 207 207 trace_binder_free_lru_start(alloc, index); 208 208 209 - ret = list_lru_add(&binder_freelist, 209 + ret = list_lru_add(alloc->freelist, 210 210 page_to_lru(page), 211 211 page_to_nid(page), 212 212 NULL); ··· 405 405 if (page) { 406 406 trace_binder_alloc_lru_start(alloc, index); 407 407 408 - on_lru = list_lru_del(&binder_freelist, 408 + on_lru = list_lru_del(alloc->freelist, 409 409 page_to_lru(page), 410 410 page_to_nid(page), 411 411 NULL); ··· 1003 1003 if (!page) 1004 1004 continue; 1005 1005 1006 - on_lru = list_lru_del(&binder_freelist, 1006 + on_lru = list_lru_del(alloc->freelist, 1007 1007 page_to_lru(page), 1008 1008 page_to_nid(page), 1009 1009 NULL); ··· 1223 1223 1224 1224 static struct shrinker *binder_shrinker; 1225 1225 1226 + static void __binder_alloc_init(struct binder_alloc *alloc, 1227 + struct list_lru *freelist) 1228 + { 1229 + alloc->pid = current->group_leader->pid; 1230 + alloc->mm = current->mm; 1231 + mmgrab(alloc->mm); 1232 + mutex_init(&alloc->mutex); 1233 + INIT_LIST_HEAD(&alloc->buffers); 1234 + alloc->freelist = freelist; 1235 + } 1236 + 1226 1237 /** 1227 1238 * binder_alloc_init() - called by binder_open() for per-proc initialization 1228 1239 * @alloc: binder_alloc for this proc ··· 1243 1232 */ 1244 1233 void binder_alloc_init(struct binder_alloc *alloc) 1245 1234 { 1246 - alloc->pid = current->group_leader->pid; 1247 - alloc->mm = current->mm; 1248 - mmgrab(alloc->mm); 1249 - mutex_init(&alloc->mutex); 1250 - INIT_LIST_HEAD(&alloc->buffers); 1235 + __binder_alloc_init(alloc, &binder_freelist); 1251 1236 } 1252 1237 1253 1238 int binder_alloc_shrinker_init(void)
+2 -1
drivers/android/binder_alloc.h
··· 15 15 #include <linux/list_lru.h> 16 16 #include <uapi/linux/android/binder.h> 17 17 18 - extern struct list_lru binder_freelist; 19 18 struct binder_transaction; 20 19 21 20 /** ··· 90 91 * @free_async_space: VA space available for async buffers. This is 91 92 * initialized at mmap time to 1/2 the full VA space 92 93 * @pages: array of struct page * 94 + * @freelist: lru list to use for free pages (invariant after init) 93 95 * @buffer_size: size of address space specified via mmap 94 96 * @pid: pid for associated binder_proc (invariant after init) 95 97 * @pages_high: high watermark of offset in @pages ··· 113 113 struct rb_root allocated_buffers; 114 114 size_t free_async_space; 115 115 struct page **pages; 116 + struct list_lru *freelist; 116 117 size_t buffer_size; 117 118 int pid; 118 119 size_t pages_high;
+49 -10
drivers/android/binder_alloc_selftest.c
··· 8 8 9 9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 10 11 - #include <linux/mm_types.h> 12 11 #include <linux/err.h> 12 + #include <linux/list_lru.h> 13 + #include <linux/mm_types.h> 13 14 #include "binder_alloc.h" 14 15 15 16 #define BUFFER_NUM 5 ··· 19 18 static bool binder_selftest_run = true; 20 19 static int binder_selftest_failures; 21 20 static DEFINE_MUTEX(binder_selftest_lock); 21 + static struct list_lru binder_selftest_freelist; 22 22 23 23 /** 24 24 * enum buf_end_align_type - Page alignment of a buffer ··· 144 142 for (i = 0; i < BUFFER_NUM; i++) 145 143 binder_alloc_free_buf(alloc, buffers[seq[i]]); 146 144 147 - /** 148 - * Error message on a free page can be false positive 149 - * if binder shrinker ran during binder_alloc_free_buf 150 - * calls above. 151 - */ 152 145 for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) { 153 146 if (list_empty(page_to_lru(alloc->pages[i]))) { 154 147 pr_err_size_seq(sizes, seq); ··· 159 162 int i; 160 163 unsigned long count; 161 164 162 - while ((count = list_lru_count(&binder_freelist))) { 163 - list_lru_walk(&binder_freelist, binder_alloc_free_page, 165 + while ((count = list_lru_count(&binder_selftest_freelist))) { 166 + list_lru_walk(&binder_selftest_freelist, binder_alloc_free_page, 164 167 NULL, count); 165 168 } 166 169 ··· 184 187 185 188 /* Allocate from lru. */ 186 189 binder_selftest_alloc_buf(alloc, buffers, sizes, seq); 187 - if (list_lru_count(&binder_freelist)) 190 + if (list_lru_count(&binder_selftest_freelist)) 188 191 pr_err("lru list should be empty but is not\n"); 189 192 190 193 binder_selftest_free_buf(alloc, buffers, sizes, seq, end); ··· 272 275 } 273 276 } 274 277 278 + int binder_selftest_alloc_get_page_count(struct binder_alloc *alloc) 279 + { 280 + struct page *page; 281 + int allocated = 0; 282 + int i; 283 + 284 + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { 285 + page = alloc->pages[i]; 286 + if (page) 287 + allocated++; 288 + } 289 + return allocated; 290 + } 291 + 275 292 /** 276 293 * binder_selftest_alloc() - Test alloc and free of buffer pages. 277 294 * @alloc: Pointer to alloc struct. ··· 297 286 */ 298 287 void binder_selftest_alloc(struct binder_alloc *alloc) 299 288 { 289 + struct list_lru *prev_freelist; 300 290 size_t end_offset[BUFFER_NUM]; 301 291 302 292 if (!binder_selftest_run) ··· 305 293 mutex_lock(&binder_selftest_lock); 306 294 if (!binder_selftest_run || !alloc->mapped) 307 295 goto done; 296 + 297 + prev_freelist = alloc->freelist; 298 + 299 + /* 300 + * It is not safe to modify this process's alloc->freelist if it has any 301 + * pages on a freelist. Since the test runs before any binder ioctls can 302 + * be dealt with, none of its pages should be allocated yet. 303 + */ 304 + if (binder_selftest_alloc_get_page_count(alloc)) { 305 + pr_err("process has existing alloc state\n"); 306 + goto cleanup; 307 + } 308 + 309 + if (list_lru_init(&binder_selftest_freelist)) { 310 + pr_err("failed to init test freelist\n"); 311 + goto cleanup; 312 + } 313 + 314 + alloc->freelist = &binder_selftest_freelist; 315 + 308 316 pr_info("STARTED\n"); 309 317 binder_selftest_alloc_offset(alloc, end_offset, 0); 310 - binder_selftest_run = false; 311 318 if (binder_selftest_failures > 0) 312 319 pr_info("%d tests FAILED\n", binder_selftest_failures); 313 320 else 314 321 pr_info("PASSED\n"); 315 322 323 + if (list_lru_count(&binder_selftest_freelist)) 324 + pr_err("expect test freelist to be empty\n"); 325 + 326 + cleanup: 327 + /* Even if we didn't run the test, it's no longer thread-safe. */ 328 + binder_selftest_run = false; 329 + alloc->freelist = prev_freelist; 330 + list_lru_destroy(&binder_selftest_freelist); 316 331 done: 317 332 mutex_unlock(&binder_selftest_lock); 318 333 }