ANDROID: binder: KMI-break workaround for binder_alloc

Work-around the KMI to backport patches from [1].

Bug: 451083029
Bug: 410746221
Link: https://lore.kernel.org/all/20241210143114.661252-1-cmllamas@google.com/ [1]
Change-Id: I7ced0d1b3c576a02b27ed0bc53b312bf3501efaa
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas
2025-05-01 17:56:52 +00:00
parent 49b52c1a99
commit cb5b1d91a0
5 changed files with 115 additions and 70 deletions
+5 -3
View File
@@ -5575,7 +5575,7 @@ static void binder_free_proc(struct binder_proc *proc)
binder_stats_deleted(BINDER_STAT_PROC);
dbitmap_free(&proc->dmap);
trace_android_vh_binder_free_proc(proc);
kfree(proc);
kfree(proc_wrapper(proc));
}
static void binder_free_thread(struct binder_thread *thread)
@@ -6327,6 +6327,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc_wrap *proc_wrap;
struct binder_proc *proc, *itr;
struct binder_device *binder_dev;
struct binderfs_info *info;
@@ -6336,9 +6337,10 @@ static int binder_open(struct inode *nodp, struct file *filp)
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
current->group_leader->pid, current->pid);
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
proc_wrap = kzalloc(sizeof(*proc_wrap), GFP_KERNEL);
if (proc_wrap == NULL)
return -ENOMEM;
proc = &proc_wrap->proc;
dbitmap_init(&proc->dmap);
spin_lock_init(&proc->inner_lock);
+37 -34
View File
@@ -23,10 +23,13 @@
#include <linux/uaccess.h>
#include <linux/highmem.h>
#include <linux/sizes.h>
#include "binder_alloc.h"
#include "binder_internal.h"
#include "binder_trace.h"
#include <trace/hooks/binder.h>
ANDROID_KABI_DECLONLY(binder_node);
ANDROID_KABI_DECLONLY(binder_transaction);
struct list_lru binder_freelist;
static DEFINE_MUTEX(binder_alloc_mmap_lock);
@@ -170,9 +173,9 @@ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
{
struct binder_buffer *buffer;
mutex_lock(&alloc->mutex);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
return buffer;
}
@@ -182,14 +185,14 @@ binder_set_installed_page(struct binder_alloc *alloc,
struct page *page)
{
/* Pairs with acquire in binder_get_installed_page() */
smp_store_release(&alloc->pages[index], page);
smp_store_release(&alloc_to_wrap(alloc)->pages[index], page);
}
static inline struct page *
binder_get_installed_page(struct binder_alloc *alloc, unsigned long index)
{
/* Pairs with release in binder_set_installed_page() */
return smp_load_acquire(&alloc->pages[index]);
return smp_load_acquire(&alloc_to_wrap(alloc)->pages[index]);
}
static void binder_lru_freelist_add(struct binder_alloc *alloc,
@@ -225,13 +228,13 @@ static inline
void binder_alloc_set_mapped(struct binder_alloc *alloc, bool state)
{
/* pairs with smp_load_acquire in binder_alloc_is_mapped() */
smp_store_release(&alloc->mapped, state);
smp_store_release(&alloc_to_wrap(alloc)->mapped, state);
}
static inline bool binder_alloc_is_mapped(struct binder_alloc *alloc)
{
/* pairs with smp_store_release in binder_alloc_set_mapped() */
return smp_load_acquire(&alloc->mapped);
return smp_load_acquire(&alloc_to_wrap(alloc)->mapped);
}
static struct page *binder_page_lookup(struct binder_alloc *alloc,
@@ -690,10 +693,10 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
if (!next)
return ERR_PTR(-ENOMEM);
mutex_lock(&alloc->mutex);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async);
if (IS_ERR(buffer)) {
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
goto out;
}
@@ -701,7 +704,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
buffer->offsets_size = offsets_size;
buffer->extra_buffers_size = extra_buffers_size;
buffer->pid = current->tgid;
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
ret = binder_install_buffer_pages(alloc, buffer, size);
if (ret) {
@@ -833,7 +836,7 @@ static struct page *binder_alloc_get_page(struct binder_alloc *alloc,
*pgoffp = pgoff;
return alloc->pages[index];
return alloc_to_wrap(alloc)->pages[index];
}
/**
@@ -885,9 +888,9 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
binder_alloc_clear_buf(alloc, buffer);
buffer->clear_on_free = false;
}
mutex_lock(&alloc->mutex);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
binder_free_buf_locked(alloc, buffer);
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
}
/**
@@ -928,10 +931,10 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
alloc->buffer = vma->vm_start;
alloc->pages = kvcalloc(alloc->buffer_size / PAGE_SIZE,
sizeof(alloc->pages[0]),
alloc_to_wrap(alloc)->pages = kvcalloc(alloc->buffer_size / PAGE_SIZE,
sizeof(alloc_to_wrap(alloc)->pages[0]),
GFP_KERNEL);
if (!alloc->pages) {
if (!alloc_to_wrap(alloc)->pages) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
@@ -956,8 +959,8 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
return 0;
err_alloc_buf_struct_failed:
kvfree(alloc->pages);
alloc->pages = NULL;
kvfree(alloc_to_wrap(alloc)->pages);
alloc_to_wrap(alloc)->pages = NULL;
err_alloc_pages_failed:
alloc->buffer = 0;
mutex_lock(&binder_alloc_mmap_lock);
@@ -980,8 +983,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
struct binder_buffer *buffer;
buffers = 0;
mutex_lock(&alloc->mutex);
BUG_ON(alloc->mapped);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
BUG_ON(alloc_to_wrap(alloc)->mapped);
while ((n = rb_first(&alloc->allocated_buffers))) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
@@ -1008,7 +1011,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
}
page_count = 0;
if (alloc->pages) {
if (alloc_to_wrap(alloc)->pages) {
int i;
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
@@ -1031,8 +1034,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
page_count++;
}
}
mutex_unlock(&alloc->mutex);
kvfree(alloc->pages);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
kvfree(alloc_to_wrap(alloc)->pages);
if (alloc->mm)
mmdrop(alloc->mm);
@@ -1055,7 +1058,7 @@ void binder_alloc_print_allocated(struct seq_file *m,
struct binder_buffer *buffer;
struct rb_node *n;
mutex_lock(&alloc->mutex);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n",
@@ -1065,7 +1068,7 @@ void binder_alloc_print_allocated(struct seq_file *m,
buffer->extra_buffers_size,
buffer->transaction ? "active" : "delivered");
}
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
}
/**
@@ -1082,7 +1085,7 @@ void binder_alloc_print_pages(struct seq_file *m,
int lru = 0;
int free = 0;
mutex_lock(&alloc->mutex);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
/*
* Make sure the binder_alloc is fully initialized, otherwise we might
* read inconsistent state.
@@ -1098,7 +1101,7 @@ void binder_alloc_print_pages(struct seq_file *m,
lru++;
}
}
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
seq_printf(m, " pages: %d:%d:%d\n", active, lru, free);
seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high);
}
@@ -1114,10 +1117,10 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
struct rb_node *n;
int count = 0;
mutex_lock(&alloc->mutex);
mutex_lock(&alloc_to_wrap(alloc)->mutex);
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
count++;
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
return count;
}
@@ -1175,7 +1178,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
vma = vma_lookup(mm, page_addr);
}
if (!mutex_trylock(&alloc->mutex))
if (!mutex_trylock(&alloc_to_wrap(alloc)->mutex))
goto err_get_alloc_mutex_failed;
/*
@@ -1188,7 +1191,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
trace_binder_unmap_kernel_start(alloc, index);
page_to_free = alloc->pages[index];
page_to_free = alloc_to_wrap(alloc)->pages[index];
binder_set_installed_page(alloc, index, NULL);
trace_binder_unmap_kernel_end(alloc, index);
@@ -1204,7 +1207,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
trace_binder_unmap_user_end(alloc, index);
}
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
if (mm_locked)
mmap_read_unlock(mm);
else
@@ -1216,7 +1219,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
return LRU_REMOVED_RETRY;
err_invalid_vma:
mutex_unlock(&alloc->mutex);
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
err_get_alloc_mutex_failed:
if (mm_locked)
mmap_read_unlock(mm);
@@ -1255,7 +1258,7 @@ void binder_alloc_init(struct binder_alloc *alloc)
alloc->pid = current->group_leader->pid;
alloc->mm = current->mm;
mmgrab(alloc->mm);
mutex_init(&alloc->mutex);
mutex_init(&alloc_to_wrap(alloc)->mutex);
INIT_LIST_HEAD(&alloc->buffers);
}
+14 -24
View File
@@ -9,6 +9,7 @@
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/rtmutex.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
@@ -70,6 +71,12 @@ struct binder_shrinker_mdata {
unsigned long page_index;
};
struct binder_lru_page {
struct list_head lru;
struct page *page_ptr;
struct binder_alloc *alloc;
};
static inline struct list_head *page_to_lru(struct page *p)
{
struct binder_shrinker_mdata *mdata;
@@ -81,7 +88,9 @@ static inline struct list_head *page_to_lru(struct page *p)
/**
* struct binder_alloc - per-binder proc state for binder allocator
* @mutex: protects binder_alloc fields
* @lock: protects binder_alloc fields
* @vma: vm_area_struct passed to mmap_handler
* (invariant after mmap)
* @mm: copy of task->mm (invariant after open)
* @buffer: base of per-proc address space mapped via mmap
* @buffers: list of all buffers for this proc
@@ -90,12 +99,10 @@ static inline struct list_head *page_to_lru(struct page *p)
* @allocated_buffers: rb tree of allocated buffers sorted by address
* @free_async_space: VA space available for async buffers. This is
* initialized at mmap time to 1/2 the full VA space
* @pages: array of struct page *
* @pages: array of binder_lru_page
* @buffer_size: size of address space specified via mmap
* @pid: pid for associated binder_proc (invariant after init)
* @pages_high: high watermark of offset in @pages
* @mapped: whether the vm area is mapped, each binder instance is
* allowed a single mapping throughout its lifetime
* @oneway_spam_detected: %true if oneway spam detection fired, clear that
* flag once the async buffer has returned to a healthy state
*
@@ -105,18 +112,18 @@ static inline struct list_head *page_to_lru(struct page *p)
* struct binder_buffer objects used to track the user buffers
*/
struct binder_alloc {
struct mutex mutex;
spinlock_t lock;
struct vm_area_struct *vma;
struct mm_struct *mm;
unsigned long buffer;
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
struct page **pages;
struct binder_lru_page *pages;
size_t buffer_size;
int pid;
size_t pages_high;
bool mapped;
bool oneway_spam_detected;
ANDROID_OEM_DATA(1);
};
@@ -152,23 +159,6 @@ void binder_alloc_print_allocated(struct seq_file *m,
void binder_alloc_print_pages(struct seq_file *m,
struct binder_alloc *alloc);
/**
* binder_alloc_get_free_async_space() - get free space available for async
* @alloc: binder_alloc for this proc
*
* Return: the bytes remaining in the address-space for async transactions
*/
static inline size_t
binder_alloc_get_free_async_space(struct binder_alloc *alloc)
{
size_t free_async_space;
mutex_lock(&alloc->mutex);
free_async_space = alloc->free_async_space;
mutex_unlock(&alloc->mutex);
return free_async_space;
}
unsigned long
binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
struct binder_buffer *buffer,
+9 -9
View File
@@ -10,7 +10,7 @@
#include <linux/mm_types.h>
#include <linux/err.h>
#include "binder_alloc.h"
#include "binder_internal.h"
#define BUFFER_NUM 5
#define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
@@ -105,10 +105,10 @@ static bool check_buffer_pages_allocated(struct binder_alloc *alloc,
page_addr = buffer->user_data;
for (; page_addr < end; page_addr += PAGE_SIZE) {
page_index = (page_addr - alloc->buffer) / PAGE_SIZE;
if (!alloc->pages[page_index] ||
!list_empty(page_to_lru(alloc->pages[page_index]))) {
if (!alloc_to_wrap(alloc)->pages[page_index] ||
!list_empty(page_to_lru(alloc_to_wrap(alloc)->pages[page_index]))) {
pr_err("expect alloc but is %s at page index %d\n",
alloc->pages[page_index] ?
alloc_to_wrap(alloc)->pages[page_index] ?
"lru" : "free", page_index);
return false;
}
@@ -148,10 +148,10 @@ static void binder_selftest_free_buf(struct binder_alloc *alloc,
* if binder shrinker ran during binder_alloc_free_buf
* calls above.
*/
if (list_empty(page_to_lru(alloc->pages[i]))) {
if (list_empty(page_to_lru(alloc_to_wrap(alloc)->pages[i]))) {
pr_err_size_seq(sizes, seq);
pr_err("expect lru but is %s at page index %d\n",
alloc->pages[i] ? "alloc" : "free", i);
alloc_to_wrap(alloc)->pages[i] ? "alloc" : "free", i);
binder_selftest_failures++;
}
}
@@ -168,9 +168,9 @@ static void binder_selftest_free_page(struct binder_alloc *alloc)
}
for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
if (alloc->pages[i]) {
if (alloc_to_wrap(alloc)->pages[i]) {
pr_err("expect free but is %s at page index %d\n",
list_empty(page_to_lru(alloc->pages[i])) ?
list_empty(page_to_lru(alloc_to_wrap(alloc)->pages[i])) ?
"alloc" : "lru", i);
binder_selftest_failures++;
}
@@ -291,7 +291,7 @@ void binder_selftest_alloc(struct binder_alloc *alloc)
if (!binder_selftest_run)
return;
mutex_lock(&binder_selftest_lock);
if (!binder_selftest_run || !alloc->mapped)
if (!binder_selftest_run || !alloc_to_wrap(alloc)->mapped)
goto done;
pr_info("STARTED\n");
binder_selftest_alloc_offset(alloc, end_offset, 0);
+50
View File
@@ -494,6 +494,56 @@ struct binder_proc {
ANDROID_OEM_DATA(1);
};
/**
* struct binder_proc_wrap - wrapper to preserve KMI in binder_proc
* @proc: binder_proc being wrapped
* @mutex: protects binder_alloc fields
* @pages: array of struct page *
* @mapped: whether the vm area is mapped, each binderinstance
* is allowed a single mapping throughout its lifetime
*/
struct binder_proc_wrap {
struct binder_proc proc;
struct binder_alloc_wrap {
struct mutex mutex;
struct page **pages;
bool mapped;
} alloc;
};
static inline
struct binder_proc_wrap *proc_wrapper(struct binder_proc *proc)
{
return container_of(proc, struct binder_proc_wrap, proc);
}
static inline
struct binder_alloc_wrap *alloc_to_wrap(struct binder_alloc *alloc)
{
struct binder_proc *proc;
proc = container_of(alloc, struct binder_proc, alloc);
return &proc_wrapper(proc)->alloc;
}
/**
* binder_alloc_get_free_async_space() - get free space available for async
* @alloc: binder_alloc for this proc
*
* Return: the bytes remaining in the address-space for async transactions
*/
static inline size_t
binder_alloc_get_free_async_space(struct binder_alloc *alloc)
{
size_t free_async_space;
mutex_lock(&alloc_to_wrap(alloc)->mutex);
free_async_space = alloc->free_async_space;
mutex_unlock(&alloc_to_wrap(alloc)->mutex);
return free_async_space;
}
/**
* struct binder_thread - binder thread bookkeeping
* @proc: binder process for this thread