android: binder: move allocator metadata and add shrinker
cover android: binder: move allocator metadata and add shrinker
This patch set moves internal kernel data in the binder driver out of mmap regions that is readable by user space. A shrinker is added to the driver to dynamically manage the memory used by binder transactions and only free pages when the system is under memory pressure. This patch set also adds tests and refactoring in binder allocator.
android: binder: Refactor prev and next buffer into a helper function
android: binder: Add allocator selftest
android: binder: Move buffer out of area shared with user space
android: binder: Add global lru shrinker to binder
android: binder: Add shrinker tracepoints
android: binder: Add page usage in binder stats
v3: Fix crash if open is called without followed by mmap. Also add page usage information in binder stats
drivers/android/Kconfig | 10 + drivers/android/Makefile | 1 + drivers/android/binder.c | 6 + drivers/android/binder_alloc.c | 395 ++++++++++++++++------ drivers/android/binder_alloc.h | 32 +- drivers/android/binder_alloc_selftest.c | 310 ++++++++++++++++++ drivers/android/binder_trace.h | 55 ++++ 7 files changed, 711 insertions(+), 98 deletions(-)
PATCH v3 1/6 android: binder: Refactor prev and next buffer into a helper function
Use helper functions buffer_next and buffer_prev instead of list_entry to get the next and previous buffers.
--- drivers/android/binder_alloc.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
drivers/android/binder_alloc.c +15 -9
@@ -48,14 +48,23 @@ module_param_named(debug_mask, binder_alloc_debug_mask,pr_info (x); \ }while (0 ) +static struct binder_buffer *binder_buffer_next(struct binder_buffer *buffer) +{ + return list_entry(buffer->entry.next, struct binder_buffer, entry); +} + +static struct binder_buffer *binder_buffer_prev (struct binder_buffer *buffer) +{ + return list_entry(buffer->entry.prev, struct binder_buffer, entry); +} +static size_t binder_alloc_buffer_size (struct binder_alloc *alloc,struct binder_buffer *buffer) {if (list_is_last (&buffer->entry, &alloc->buffers))return alloc->buffer + alloc->buffer_size - (void *)buffer->data; -return (size_t )list_entry (buffer->entry.next, - struct binder_buffer, entry) - (size_t)buffer->data; +return (size_t )binder_buffer_next (buffer) - (size_t )buffer->data; } static void binder_insert_free_buffer(struct binder_alloc *alloc,
@@ -470,7 +479,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc,int free_page_start =1 ;BUG_ON (alloc->buffers.next == &buffer->entry); - prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); + prev =binder_buffer_prev (buffer);BUG_ON (!prev->free);if (buffer_end_page (prev) ==buffer_start_page (buffer)) { free_page_start =0 ;
@@ -482,8 +491,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc, }if (!list_is_last (&buffer->entry, &alloc->buffers)) { - next =list_entry (buffer->entry.next, - struct binder_buffer, entry); + next =binder_buffer_next (buffer);if (buffer_start_page (next) ==buffer_end_page (buffer)) { free_page_end =0 ; if (buffer_start_page(next) ==
@@ -544,8 +552,7 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,rb_erase (&buffer->rb_node, &alloc->allocated_buffers); buffer->free =1 ;if (!list_is_last (&buffer->entry, &alloc->buffers)) { -struct binder_buffer *next =list_entry (buffer->entry.next, - struct binder_buffer, entry); +struct binder_buffer *next =binder_buffer_next (buffer);if (next->free) {rb_erase (&next->rb_node, &alloc->free_buffers);
@@ -553,8 +560,7 @@ static void binder_free_buf_locked(struct binder_alloc *alloc, } }if (alloc->buffers.next != &buffer->entry) { -struct binder_buffer *prev =list_entry (buffer->entry.prev, - struct binder_buffer, entry); +struct binder_buffer *prev =binder_buffer_prev (buffer);if (prev->free) {binder_delete_free_buffer (alloc, buffer);
Signature · 1 line
2.14.1.342.g6490525c54-goog
Greg Kroah-Hartman
maintainerRe: [PATCH v3 1/6] android: binder: Refactor prev and next buffer into a helper function
Quoted 7 lines · Sherry Yang
Use helper functions
buffer_nextandbuffer_previnstead oflist_entryto get the next and previous buffers.Signed-off-by Sherry Yang <[email protected]>--- drivers/android/binder_alloc.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
What changed from the previous version?
Always put the changes below the --- line. Like the documentation says to do so.
Also, don't I already have these patches in my tree? Do you want me to revert the existing ones and use the new ones, or what about a fixup patch for any differences?
confused,
greg k-h
Re: [PATCH v3 1/6] android: binder: Refactor prev and next buffer into a helper function
Quoted 10 lines · Greg Kroah-Hartman
Quoted 7 lines · Sherry Yang
Use helper functions
buffer_nextandbuffer_previnstead oflist_entryto get the next and previous buffers.Signed-off-by Sherry Yang <[email protected]>--- drivers/android/binder_alloc.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)What changed from the previous version?
This specific patch didn't change. The change is only in [patch v3 3/6] (crash fix) and in [patch v3 6/6] (new patch that add stats).
Quoted 2 lines
Always put the changes below the --- line. Like the documentation says to do so.
Do you mean I should use --annotate to git send-email to specify what has changed across versions for each patch? I used --compose and put what changed from v2 to v3 in the introductory message [patch 0/6].
Quoted 3 lines
Also, don't I already have these patches in my tree? Do you want me to revert the existing ones and use the new ones, or what about a fixup patch for any differences?
I got a message saying a test failed on [patch 3/5]. I resubmitted the entire thread because I wasn't sure if you would drop the failing patch set. If the entire failing patch set is dropped, then v3 can be used as a replacement.
If you prefer a fixup patch, I can post another patch set (the crash fix and the new patch) on top of what you already applied, but it might be easier to do what's described in the previous paragraph (drop v2 and apply v3).
Sherry Yang
Quoted 3 lines
confused,
greg k-h
Re: [PATCH v3 1/6] android: binder: Refactor prev and next buffer into a helper function
Quoted 2 lines · Sherry Yang
I used --compose and put what changed from v2 to v3 in the introductory message [patch 0/6].
Hm... I never got [patch 0/6] (I'm on [email protected]).
regards,
dan carpenter
Greg Kroah-Hartman
maintainerRe: [PATCH v3 1/6] android: binder: Refactor prev and next buffer into a helper function
In reply to Sherry YangQuoted 11 lines · Sherry Yang
Quoted 9 lines · Greg Kroah-Hartman
Quoted 7 lines · Sherry Yang
Use helper functions
buffer_nextandbuffer_previnstead oflist_entryto get the next and previous buffers.Signed-off-by Sherry Yang <[email protected]>--- drivers/android/binder_alloc.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
Quoted 4 lines
Quoted 1 line
What changed from the previous version?
This specific patch didn't change. The change is only in [patch v3 3/6] (crash fix) and in [patch v3 6/6] (new patch that add stats).
I need to know that :)
Quoted 6 lines
Quoted 2 lines
Always put the changes below the --- line. Like the documentation says to do so.
Do you mean I should use --annotate to git send-email to specify what has changed across versions for each patch? I used --compose and put what changed from v2 to v3 in the introductory message [patch 0/6].
I never got a patch 0/6 here, and honestly, almost always ignore them as patches should be self-obvious :)
Quoted 8 lines
Quoted 3 lines
Also, don't I already have these patches in my tree? Do you want me to revert the existing ones and use the new ones, or what about a fixup patch for any differences?
I got a message saying a test failed on [patch 3/5]. I resubmitted the entire thread because I wasn't sure if you would drop the failing patch set. If the entire failing patch set is dropped, then v3 can be used as a replacement.
Do you want me to revert all of these? I can not "drop" them as my tree can not rebase. If it's just a simple fix, I'll gladly take that instead.
Quoted 4 lines
If you prefer a fixup patch, I can post another patch set (the crash fix and the new patch) on top of what you already applied, but it might be easier to do what's described in the previous paragraph (drop v2 and apply v3).
Ok, exactly what git commit ids should I revert from the tree? But really, a fix would be easier at this point :)
thanks,
greg k-h
PATCH 1/1 android: binder: fixup crash introduced by moving buffer hdr
Fix crash introduced by 74310e06be4d74dcf67cd108366710dee5c576d5 (android: binder: Move buffer out of area shared with user space) when close is called after open without mmap in between.
--- drivers/android/binder_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/android/binder_alloc.c +1 -1
@@ -713,7 +713,6 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, } buffer->data = alloc->buffer; -INIT_LIST_HEAD (&alloc->buffers);list_add (&buffer->entry, &alloc->buffers); buffer->free =1 ;binder_insert_free_buffer (alloc, buffer);
@@ -972,6 +971,7 @@ void binder_alloc_init(struct binder_alloc *alloc) alloc->tsk = current->group_leader; alloc->pid = current->group_leader->pid;mutex_init (&alloc->mutex); +INIT_LIST_HEAD (&alloc->buffers); }void binder_alloc_shrinker_init (void )
Signature · 1 line
2.14.1.581.gf28d330327-goog
Greg KH
maintainerRe: [PATCH] android: binder: fixup crash introduced by moving buffer hdr
Quoted 8 lines · Sherry Yang
Fix crash introduced by
74310e06be4d74dcf67cd108366710dee5c576d5(android: binder: Move buffer out of area shared with user space) when close is called after open without mmap in between.Signed-off-by Sherry Yang <[email protected]>--- drivers/android/binder_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
In the future, please include a "Reported-by:" tag that shows who reported the issue, and a Fixes: tag with the info for what commit this fixes.
And no need to give the full sha1, you can shorten it, this is the standard for kernel commit:
74310e06be4d ("android: binder: Move buffer out of area shared with user space")
I'll edit this patch up with that info, but in the future, please do this yourself.
thanks,
greg k-h
Re: [PATCH v3 1/6] android: binder: Refactor prev and next buffer into a helper function
In reply to Greg Kroah-HartmanQuoted 2 lines · Greg Kroah-Hartman
Ok, exactly what git commit ids should I revert from the tree? But really, a fix would be easier at this point :)
I sent a fixup patch as a separate reply to this email. Please also apply [patch v3 6/6] as it was not in v2, which you applied.
Thanks,
Sherry
Greg Kroah-Hartman
maintainerRe: [PATCH v3 1/6] android: binder: Refactor prev and next buffer into a helper function
Quoted 7 lines · Sherry Yang
Quoted 2 lines · Greg Kroah-Hartman
Ok, exactly what git commit ids should I revert from the tree? But really, a fix would be easier at this point :)
I sent a fixup patch as a separate reply to this email. Please also apply [patch v3 6/6] as it was not in v2, which you applied.
Please resend that patch, it's no longer in my queue.
thanks,
greg k-h
PATCH 1/1 android: binder: Add page usage in binder stats
Add the number of active, lru, and free pages for
each binder process in binder stats
--- drivers/android/binder.c | 2 ++ drivers/android/binder_alloc.c | 28 ++++++++++++++++++++++++++++ drivers/android/binder_alloc.h | 2 ++ 3 files changed, 32 insertions(+)
drivers/android/binder.c +2 -0
@@ -5047,6 +5047,8 @@ static void print_binder_proc_stats(struct seq_file *m, count =binder_alloc_get_allocated_count (&proc->alloc);seq_printf (m," buffers: %d\n" , count); +binder_alloc_print_pages (m, &proc->alloc); + count =0 ;binder_inner_proc_lock (proc);list_for_each_entry (w, &proc->todo, entry) {
drivers/android/binder_alloc.c +28 -0
@@ -831,6 +831,34 @@ void binder_alloc_print_allocated(struct seq_file *m,mutex_unlock (&alloc->mutex); } +/** +* binder_alloc_print_pages() - print page usage +* @m: seq_file for output via seq_printf() +* @alloc: binder_alloc for this proc +*/ +void binder_alloc_print_pages (struct seq_file *m, +struct binder_alloc *alloc) +{ +struct binder_lru_page *page; +int i; +int active =0 ; +int lru =0 ; +int free =0 ; + +mutex_lock (&alloc->mutex); +for (i =0 ; i < alloc->buffer_size /PAGE_SIZE ; i++) { + page = &alloc->pages[i]; +if (!page->page_ptr) + free++; +else if (list_empty (&page->lru)) + active++; +else + lru++; + } +mutex_unlock (&alloc->mutex); +seq_printf (m," pages: %d:%d:%d\n" , active, lru, free); +} +/** * binder_alloc_get_allocated_count() - return count of buffers * @alloc: binder_alloc for this proc
drivers/android/binder_alloc.h +2 -0
@@ -142,6 +142,8 @@ extern void binder_alloc_deferred_release(struct binder_alloc *alloc);extern int binder_alloc_get_allocated_count (struct binder_alloc *alloc);extern void binder_alloc_print_allocated (struct seq_file *m,struct binder_alloc *alloc); +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
Signature · 1 line
2.14.1.581.gf28d330327-goog
PATCH v3 2/6 android: binder: Add allocator selftest
In reply to Sherry Yangbinder_alloc_selftest tests that alloc_new_buf handles page allocation and deallocation properly when allocate and free buffers. The test allocates 5 buffers of various sizes to cover all possible page alignment cases, and frees the buffers using a list of exhaustive freeing order.
Test: boot the device with ANDROID_BINDER_IPC_SELFTEST config option enabled. Allocator selftest passes.
--- drivers/android/Kconfig | 10 ++ drivers/android/Makefile | 1 + drivers/android/binder.c | 2 + drivers/android/binder_alloc.h | 5 + drivers/android/binder_alloc_selftest.c | 271 ++++++++++++++++++++++++++++++++ 5 files changed, 289 insertions(+) create mode 100644 drivers/android/binder_alloc_selftest.c
drivers/android/Kconfig +10 -0
@@ -44,6 +44,16 @@ config ANDROID_BINDER_IPC_32BIT Note that enabling this will break newer Android user-space. +config ANDROID_BINDER_IPC_SELFTEST + bool "Android Binder IPC Driver Selftest" + depends on ANDROID_BINDER_IPC + ---help--- + This feature allows binder selftest to run. + + Binder selftest checks the allocation and free of binder buffers + exhaustively with combinations of various buffer sizes and + alignments. + endif # if ANDROID endmenu
drivers/android/Makefile +1 -0
@@ -1,3 +1,4 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o +obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o
drivers/android/binder.c +2 -0
@@ -4225,6 +4225,8 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)/*pr_info("binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ +binder_selftest_alloc (&proc->alloc); +trace_binder_ioctl (cmd, arg); ret =wait_event_interruptible (binder_user_error_wait, binder_stop_on_user_error <2 );
drivers/android/binder_alloc.h +5 -0
@@ -102,6 +102,11 @@ struct binder_alloc {int pid; }; +#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST +void binder_selftest_alloc (struct binder_alloc *alloc); +#else +static inline void binder_selftest_alloc (struct binder_alloc *alloc) {} +#endif extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, size_t data_size, size_t offsets_size,
drivers/android/binder_alloc_selftest.c new file +271 -0
271 lines not on this page · raw message
Signature · 1 line
2.14.1.342.g6490525c54-goog
PATCH v3 3/6 android: binder: Move buffer out of area shared with user space
In reply to Sherry YangBinder driver allocates buffer meta data in a region that is mapped in user space. These meta data contain pointers in the kernel.
This patch allocates buffer meta data on the kernel heap that is not mapped in user space, and uses a pointer to refer to the data mapped.
Also move alloc->buffers initialization from mmap to init since it's now used even when mmap failed or was not called.
--- drivers/android/binder_alloc.c | 146 +++++++++++++++++++------------- drivers/android/binder_alloc.h | 2 +- drivers/android/binder_alloc_selftest.c | 11 ++- 3 files changed, 91 insertions(+), 68 deletions(-)
drivers/android/binder_alloc.c +85 -61
@@ -62,9 +62,9 @@ static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, struct binder_buffer *buffer) {if (list_is_last (&buffer->entry, &alloc->buffers)) -return alloc->buffer + - alloc->buffer_size - (void *)buffer->data; -return (size_t )binder_buffer_next (buffer) - (size_t )buffer->data; +return (u8 *)alloc->buffer + + alloc->buffer_size - (u8 *)buffer->data; +return (u8 *)binder_buffer_next (buffer)->data - (u8 *)buffer->data; } static void binder_insert_free_buffer(struct binder_alloc *alloc,
@@ -114,9 +114,9 @@ static void binder_insert_allocated_buffer_locked( buffer = rb_entry(parent, struct binder_buffer, rb_node);BUG_ON (buffer->free); -if (new_buffer < buffer) +if (new_buffer->data < buffer->data) p = &parent->rb_left; -else if (new_buffer > buffer) +else if (new_buffer->data > buffer->data) p = &parent->rb_right;else BUG ();
@@ -131,18 +131,17 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked( {struct rb_node *n = alloc->allocated_buffers.rb_node;struct binder_buffer *buffer; -struct binder_buffer *kern_ptr; +void *kern_ptr; - kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset - - offsetof(struct binder_buffer , data)); + kern_ptr = (void *)(user_ptr - alloc->user_buffer_offset);while (n) { buffer = rb_entry(n, struct binder_buffer, rb_node);BUG_ON (buffer->free); -if (kern_ptr < buffer) +if (kern_ptr < buffer->data) n = n->rb_left; -else if (kern_ptr > buffer) +else if (kern_ptr > buffer->data) n = n->rb_right;else {/*
@@ -330,6 +329,9 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc,return ERR_PTR (-ENOSPC ); } +/* Pad 0-size buffers so they get assigned unique addresses */ + size =max (size,sizeof (void *)); +while (n) { buffer = rb_entry(n, struct binder_buffer, rb_node);BUG_ON (!buffer->free);
@@ -389,14 +391,9 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, has_page_addr = (void *)(((uintptr_t )buffer->data + buffer_size) &PAGE_MASK ); -if (n ==NULL ) { -if (size +sizeof (struct binder_buffer ) +4 >= buffer_size) - buffer_size = size;/* no room for other buffers */ -else - buffer_size = size +sizeof (struct binder_buffer ); - } +WARN_ON (n && buffer_size != size); end_page_addr = - (void *)PAGE_ALIGN ((uintptr_t )buffer->data + buffer_size); + (void *)PAGE_ALIGN ((uintptr_t )buffer->data + size);if (end_page_addr > has_page_addr) end_page_addr = has_page_addr; ret = binder_update_page_range(alloc, 1,
@@ -404,17 +401,25 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc,if (ret)return ERR_PTR (ret); -rb_erase (best_fit, &alloc->free_buffers); - buffer->free =0 ; - buffer->free_in_progress =0 ; -binder_insert_allocated_buffer_locked (alloc, buffer);if (buffer_size != size) { -struct binder_buffer *new_buffer = (void *)buffer->data + size; +struct binder_buffer *new_buffer; + new_buffer =kzalloc (sizeof (*buffer),GFP_KERNEL ); +if (!new_buffer) { +pr_err ("%s: %d failed to alloc new buffer struct\n" , + __func__, alloc->pid); + goto err_alloc_buf_struct_failed; + } + new_buffer->data = (u8 *)buffer->data + size;list_add (&new_buffer->entry, &buffer->entry); new_buffer->free =1 ;binder_insert_free_buffer (alloc, new_buffer); } + +rb_erase (best_fit, &alloc->free_buffers); + buffer->free =0 ; + buffer->free_in_progress =0 ; +binder_insert_allocated_buffer_locked (alloc, buffer);binder_alloc_debug (BINDER_DEBUG_BUFFER_ALLOC ,"%d: binder_alloc_buf size %zd got %pK\n" , alloc->pid, size, buffer);
@@ -429,6 +434,12 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, alloc->pid, size, alloc->free_async_space); }return buffer; + +err_alloc_buf_struct_failed: +binder_update_page_range (alloc,0 , + (void *)PAGE_ALIGN ((uintptr_t )buffer->data), + end_page_addr,NULL ); +return ERR_PTR (-ENOMEM ); }/**
@@ -463,56 +474,59 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,static void *buffer_start_page (struct binder_buffer *buffer) { -return (void *)((uintptr_t )buffer &PAGE_MASK ); +return (void *)((uintptr_t )buffer->data &PAGE_MASK ); } -static void *buffer_end_page (struct binder_buffer *buffer) +static void *prev_buffer_end_page (struct binder_buffer *buffer) { -return (void *)(((uintptr_t )(buffer +1 ) -1 ) &PAGE_MASK ); +return (void *)(((uintptr_t )(buffer->data) -1 ) &PAGE_MASK ); }static void binder_delete_free_buffer (struct binder_alloc *alloc,struct binder_buffer *buffer) {struct binder_buffer *prev, *next =NULL ; -int free_page_end =1 ; -int free_page_start =1 ; - +bool to_free = true;BUG_ON (alloc->buffers.next == &buffer->entry); prev =binder_buffer_prev (buffer);BUG_ON (!prev->free); -if (buffer_end_page (prev) ==buffer_start_page (buffer)) { - free_page_start =0 ; -if (buffer_end_page (prev) ==buffer_end_page (buffer)) - free_page_end =0 ; +if (prev_buffer_end_page (prev) ==buffer_start_page (buffer)) { + to_free = false;binder_alloc_debug (BINDER_DEBUG_BUFFER_ALLOC , -"%d: merge free, buffer %pK share page with %pK\n" , - alloc->pid, buffer, prev); +"%d: merge free, buffer %pK share page with %pK\n" , + alloc->pid, buffer->data, prev->data); }if (!list_is_last (&buffer->entry, &alloc->buffers)) { next =binder_buffer_next (buffer); -if (buffer_start_page (next) ==buffer_end_page (buffer)) { - free_page_end =0 ; -if (buffer_start_page (next) == -buffer_start_page (buffer)) - free_page_start =0 ; +if (buffer_start_page (next) ==buffer_start_page (buffer)) { + to_free = false;binder_alloc_debug (BINDER_DEBUG_BUFFER_ALLOC , -"%d: merge free, buffer %pK share page with %pK\n" , - alloc->pid, buffer, prev); +"%d: merge free, buffer %pK share page with %pK\n" , + alloc->pid, + buffer->data, + next->data); } } -list_del (&buffer->entry); -if (free_page_start || free_page_end) { + +if (PAGE_ALIGNED (buffer->data)) { +binder_alloc_debug (BINDER_DEBUG_BUFFER_ALLOC , +"%d: merge free, buffer start %pK is page aligned\n" , + alloc->pid, buffer->data); + to_free = false; + } + +if (to_free) {binder_alloc_debug (BINDER_DEBUG_BUFFER_ALLOC , -"%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n" , - alloc->pid, buffer, free_page_start ?"" :" end" , - free_page_end ?"" :" start" , prev, next); -binder_update_page_range (alloc,0 , free_page_start ? -buffer_start_page (buffer) :buffer_end_page (buffer), - (free_page_end ?buffer_end_page (buffer) : -buffer_start_page (buffer)) +PAGE_SIZE ,NULL ); +"%d: merge free, buffer %pK do not share page with %pK or %pK\n" , + alloc->pid, buffer->data, + prev->data, next->data); +binder_update_page_range (alloc,0 ,buffer_start_page (buffer), +buffer_start_page (buffer) +PAGE_SIZE , +NULL ); } +list_del (&buffer->entry); +kfree (buffer); } static void binder_free_buf_locked(struct binder_alloc *alloc,
@@ -533,8 +547,8 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,BUG_ON (buffer->free);BUG_ON (size > buffer_size);BUG_ON (buffer->transaction !=NULL ); -BUG_ON ((void *)buffer < alloc->buffer); -BUG_ON ((void *)buffer > alloc->buffer + alloc->buffer_size); +BUG_ON (buffer->data < alloc->buffer); +BUG_ON (buffer->data > alloc->buffer + alloc->buffer_size);if (buffer->async_transaction) { alloc->free_async_space += size +sizeof (struct binder_buffer );
@@ -646,14 +660,14 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, } alloc->buffer_size = vma->vm_end - vma->vm_start; -if (binder_update_page_range (alloc,1 , alloc->buffer, - alloc->buffer +PAGE_SIZE , vma)) { + buffer =kzalloc (sizeof (*buffer),GFP_KERNEL ); +if (!buffer) { ret = -ENOMEM ; - failure_string ="alloc small buf" ; - goto err_alloc_small_buf_failed; + failure_string ="alloc buffer struct" ; + goto err_alloc_buf_struct_failed; } - buffer = alloc->buffer; -INIT_LIST_HEAD (&alloc->buffers); + + buffer->data = alloc->buffer;list_add (&buffer->entry, &alloc->buffers); buffer->free =1 ;binder_insert_free_buffer (alloc, buffer);
@@ -664,7 +678,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,return 0 ; -err_alloc_small_buf_failed: +err_alloc_buf_struct_failed:kfree (alloc->pages); alloc->pages =NULL ; err_alloc_pages_failed:
@@ -684,14 +698,13 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) {struct rb_node *n;int buffers, page_count; +struct binder_buffer *buffer;BUG_ON (alloc->vma); buffers =0 ;mutex_lock (&alloc->mutex);while ((n =rb_first (&alloc->allocated_buffers))) { -struct binder_buffer *buffer; - buffer = rb_entry(n, struct binder_buffer, rb_node);/* Transaction should already have been freed */
@@ -701,6 +714,16 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) buffers++; } +while (!list_empty (&alloc->buffers)) { + buffer =list_first_entry (&alloc->buffers, + struct binder_buffer, entry); +WARN_ON (!buffer->free); + +list_del (&buffer->entry); +WARN_ON_ONCE (!list_empty (&alloc->buffers)); +kfree (buffer); + } + page_count =0 ;if (alloc->pages) {int i;
@@ -804,5 +827,6 @@ void binder_alloc_init(struct binder_alloc *alloc) alloc->tsk = current->group_leader; alloc->pid = current->group_leader->pid;mutex_init (&alloc->mutex); +INIT_LIST_HEAD (&alloc->buffers); }
drivers/android/binder_alloc.h +1 -1
@@ -57,7 +57,7 @@ struct binder_buffer {size_t data_size;size_t offsets_size;size_t extra_buffers_size; -uint8_t data[0 ]; +void *data; };/**
drivers/android/binder_alloc_selftest.c +5 -6
@@ -105,8 +105,9 @@ static bool check_buffer_pages_allocated(struct binder_alloc *alloc,void *page_addr, *end;int page_index; - end = (void *)PAGE_ALIGN ((uintptr_t )buffer + size); -for (page_addr = buffer; page_addr < end; page_addr +=PAGE_SIZE ) { + end = (void *)PAGE_ALIGN ((uintptr_t )buffer->data + size); + page_addr = buffer->data; +for (; page_addr < end; page_addr +=PAGE_SIZE ) { page_index = (page_addr - alloc->buffer) /PAGE_SIZE ;if (!alloc->pages[page_index]) { pr_err("incorrect alloc state at page index %d\n",
@@ -209,8 +210,7 @@ static void binder_selftest_alloc_size(struct binder_alloc *alloc,* Only BUFFER_NUM - 1 buffer sizes are adjustable since * we need one giant buffer before getting to the last page. */ - back_sizes[0 ] += alloc->buffer_size - end_offset[BUFFER_NUM -1 ] - -sizeof (struct binder_buffer ) *BUFFER_NUM ; + back_sizes[0 ] += alloc->buffer_size - end_offset[BUFFER_NUM -1 ];binder_selftest_free_seq (alloc, front_sizes, seq,0 );binder_selftest_free_seq (alloc, back_sizes, seq,0 ); }
@@ -228,8 +228,7 @@ static void binder_selftest_alloc_offset(struct binder_alloc *alloc, prev = index ==0 ?0 : end_offset[index -1 ]; end = prev; -BUILD_BUG_ON ((BUFFER_MIN_SIZE +sizeof (struct binder_buffer )) - *BUFFER_NUM >=PAGE_SIZE ); +BUILD_BUG_ON (BUFFER_MIN_SIZE *BUFFER_NUM >=PAGE_SIZE );for (align =SAME_PAGE_UNALIGNED ; align <LOOP_END ; align++) {if (align %2 )
Signature · 1 line
2.14.1.342.g6490525c54-goog
Re: [PATCH v3 3/6] android: binder: Move buffer out of area shared with user space
Quoted 11 lines · Sherry Yang
Binder driver allocates buffer meta data in a region that is mapped in user space. These meta data contain pointers in the kernel.
This patch allocates buffer meta data on the kernel heap that is not mapped in user space, and uses a pointer to refer to the data mapped.
Also move
alloc->buffersinitialization from mmap to init since it's now used even when mmap failed or was not called.Signed-off-by Sherry Yang <[email protected]>---
The difference between v2 and v3 is that we've shifted some initialization around to fix the crashing bug that kbuild found. You should not that difference here under the --- cut off.
Quoted 4 lines
drivers/android/binder_alloc.c | 146 +++++++++++++++++++------------- drivers/android/binder_alloc.h | 2 +- drivers/android/binder_alloc_selftest.c | 11 ++- 3 files changed, 91 insertions(+), 68 deletions(-)
But really we still need to have some answers or discussion about the questions that Greg and I raised. Greg asked if the other Android devs had Acked this. Please ping Arve to Ack this.
I was curious about the security impact or why we were writing this patch 3/6. It seems we are fixing an information disclosure bug. Or is it something worse than that? Or have I misunderstood entirely.
We probably original put the buffers in userspace for accounting reasons so we could kill programs that used too much RAM. This patch doesn't create a problem with that hopefully? We're just moving the metadata to kernel space?
regards,
dan carpenter
Arve Hjønnevåg
maintainerRe: [PATCH v3 3/6] android: binder: Move buffer out of area shared with user space
Quoted 17 lines · Dan Carpenter
Quoted 11 lines · Sherry Yang
Binder driver allocates buffer meta data in a region that is mapped in user space. These meta data contain pointers in the kernel.
This patch allocates buffer meta data on the kernel heap that is not mapped in user space, and uses a pointer to refer to the data mapped.
Also move
alloc->buffersinitialization from mmap to init since it's now used even when mmap failed or was not called.Signed-off-by Sherry Yang <[email protected]>---
The difference between v2 and v3 is that we've shifted some initialization around to fix the crashing bug that kbuild found. You should not that difference here under the --- cut off.
Quoted 9 lines
Quoted 4 lines
drivers/android/binder_alloc.c | 146 +++++++++++++++++++------------- drivers/android/binder_alloc.h | 2 +- drivers/android/binder_alloc_selftest.c | 11 ++- 3 files changed, 91 insertions(+), 68 deletions(-)But really we still need to have some answers or discussion about the questions that Greg and I raised. Greg asked if the other Android devs had Acked this. Please ping Arve to Ack this.
[email protected] replied and ack'ed v2. The changes have been reviewed on android-review.googlesource.com. Do you want and ack or review tag included in the patchset or do you want separate ack emails on each patchset (or on each patch)?
Quoted 9 lines
I was curious about the security impact or why we were writing this patch 3/6. It seems we are fixing an information disclosure bug. Or is it something worse than that? Or have I misunderstood entirely.
We probably original put the buffers in userspace for accounting reasons so we could kill programs that used too much RAM. This patch doesn't create a problem with that hopefully? We're just moving the metadata to kernel space?
The buffer headers have never been used by user-space. They are readable by user-space because the content after the header has to be readable from user-space (and only whole pages can be mapped). It was simpler to have the header in the same shared region as well. At the time this code was written the content of kernel pointers where not considered secret and available elsewhere anyway (e.g. kernel log, /proc/kallsyms).
Signature · 1 line
Arve Hjønnevåg
Re: [PATCH v3 3/6] android: binder: Move buffer out of area shared with user space
Quoted 23 lines · Arve Hjønnevåg
Quoted 22 lines · Dan Carpenter
Quoted 11 lines · Sherry Yang
Binder driver allocates buffer meta data in a region that is mapped in user space. These meta data contain pointers in the kernel.
This patch allocates buffer meta data on the kernel heap that is not mapped in user space, and uses a pointer to refer to the data mapped.
Also move
alloc->buffersinitialization from mmap to init since it's now used even when mmap failed or was not called.Signed-off-by Sherry Yang <[email protected]>---
The difference between v2 and v3 is that we've shifted some initialization around to fix the crashing bug that kbuild found. You should not that difference here under the --- cut off.
Quoted 4 lines
drivers/android/binder_alloc.c | 146 +++++++++++++++++++------------- drivers/android/binder_alloc.h | 2 +- drivers/android/binder_alloc_selftest.c | 11 ++- 3 files changed, 91 insertions(+), 68 deletions(-)
Quoted 8 lines
Quoted 4 lines
But really we still need to have some answers or discussion about the questions that Greg and I raised. Greg asked if the other Android devs had Acked this. Please ping Arve to Ack this.
[email protected] replied and ack'ed v2. The changes have been reviewed on android-review.googlesource.com. Do you want and ack or review tag included in the patchset or do you want separate ack emails on each patchset (or on each patch)?
Just acking it once is fine. I don't see that email from Todd in my inbox and can't find it on the web archive either... Something must have gone wrong but I don't know what.
regards,
dan carpenter
Re: [PATCH v3 3/6] android: binder: Move buffer out of area shared with user space
I just went back through it -- turns out my email bounced back from [email protected] (reason was "may contain a virus"). Sorry I didn't notice that and resend.
Quoted 40 lines · Dan Carpenter
Quoted 23 lines · Arve Hjønnevåg
Quoted 22 lines · Dan Carpenter
Quoted 11 lines · Sherry Yang
Binder driver allocates buffer meta data in a region that is mapped in user space. These meta data contain pointers in the kernel.
This patch allocates buffer meta data on the kernel heap that is not mapped in user space, and uses a pointer to refer to the data mapped.
Also move
alloc->buffersinitialization from mmap to init since it's now used even when mmap failed or was not called.Signed-off-by Sherry Yang <[email protected]>---
The difference between v2 and v3 is that we've shifted some initialization around to fix the crashing bug that kbuild found. You should not that difference here under the --- cut off.
Quoted 4 lines
drivers/android/binder_alloc.c | 146 +++++++++++++++++++------------- drivers/android/binder_alloc.h | 2 +- drivers/android/binder_alloc_selftest.c | 11 ++- 3 files changed, 91 insertions(+), 68 deletions(-)Quoted 8 lines
Quoted 4 lines
But really we still need to have some answers or discussion about the questions that Greg and I raised. Greg asked if the other Android devs had Acked this. Please ping Arve to Ack this.
[email protected] replied and ack'ed v2. The changes have been reviewed on android-review.googlesource.com. Do you want and ack or review tag included in the patchset or do you want separate ack emails on each patchset (or on each patch)?
Just acking it once is fine. I don't see that email from Todd in my inbox and can't find it on the web archive either... Something must have gone wrong but I don't know what.
regards,
dan carpenter
PATCH v3 4/6 android: binder: Add global lru shrinker to binder
In reply to Sherry YangHold on to the pages allocated and mapped for transaction buffers until the system is under memory pressure. When that happens, use linux shrinker to free pages. Without using shrinker, patch "android: binder: Move buffer out of area shared with user space" will cause a significant slow down for small transactions that fit into the first page because free list buffer header used to be inlined with buffer data.
In addition to prevent the performance regression for small transactions, this patch improves the performance for transactions that take up more than one page.
Modify alloc selftest to work with the shrinker change.
Test: Run memory intensive applications (Chrome and Camera) to trigger shrinker callbacks. Binder frees memory as expected.
Test: Run binderThroughputTest with high memory pressure option enabled.
--- drivers/android/binder.c | 2 + drivers/android/binder_alloc.c | 172 +++++++++++++++++++++++++++----- drivers/android/binder_alloc.h | 23 ++++- drivers/android/binder_alloc_selftest.c | 68 ++++++++++--- 4 files changed, 225 insertions(+), 40 deletions(-)
drivers/android/binder.c +2 -0
@@ -5243,6 +5243,8 @@ static int __init binder_init(void)struct binder_device *device;struct hlist_node *tmp; +binder_alloc_shrinker_init (); +atomic_set (&binder_transaction_log.cur, ~0U );atomic_set (&binder_transaction_log_failed.cur, ~0U );
drivers/android/binder_alloc.c +149 -23
251 lines not on this page · raw message
drivers/android/binder_alloc.h +20 -3
@@ -21,7 +21,9 @@#include <linux/rtmutex.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/list_lru.h> +extern struct list_lru binder_alloc_lru;struct binder_transaction ;/**
@@ -60,6 +62,18 @@ struct binder_buffer {void *data; }; +/** +* struct binder_lru_page - page object used for binder shrinker +* @page_ptr: pointer to physical page in mmap'd space +* @lru: entry in binder_alloc_lru +* @alloc: binder_alloc for a proc +*/ +struct binder_lru_page { +struct list_head lru; +struct page *page_ptr; +struct binder_alloc *alloc; +}; +/** * struct binder_alloc - per-binder proc state for binder allocator * @vma: vm_area_struct passed to mmap_handler
@@ -75,8 +89,7 @@ struct binder_buffer {* @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 physical page addresses for each -* page of mmap'd space +* @pages: array of binder_lru_page * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) *
@@ -96,7 +109,7 @@ struct binder_alloc {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;uint32_t buffer_free;int pid;
@@ -107,12 +120,16 @@ void binder_selftest_alloc(struct binder_alloc *alloc);#else static inline void binder_selftest_alloc (struct binder_alloc *alloc) {}#endif +enum lru_status binder_alloc_free_page (struct list_head *item, +struct list_lru_one *lru, +spinlock_t *lock,void *cb_arg);extern struct binder_buffer *binder_alloc_new_buf (struct binder_alloc *alloc,size_t data_size,size_t offsets_size,size_t extra_buffers_size,int is_async);extern void binder_alloc_init (struct binder_alloc *alloc); +void binder_alloc_shrinker_init (void );extern void binder_alloc_vma_close (struct binder_alloc *alloc);extern struct binder_buffer * binder_alloc_prepare_to_free(struct binder_alloc *alloc,
drivers/android/binder_alloc_selftest.c +54 -14
125 lines not on this page · raw message
Signature · 1 line
2.14.1.342.g6490525c54-goog
PATCH v3 5/6 android: binder: Add shrinker tracepoints
In reply to Sherry YangAdd tracepoints in binder transaction allocator to record lru hits and alloc/free page.
--- drivers/android/binder_alloc.c | 27 +++++++++++++++++++-- drivers/android/binder_trace.h | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-)
drivers/android/binder_alloc.c +25 -2
@@ -237,18 +237,25 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,for (page_addr = start; page_addr < end; page_addr +=PAGE_SIZE ) {int ret;bool on_lru; +size_t index; - page = &alloc->pages[(page_addr - alloc->buffer) /PAGE_SIZE ]; + index = (page_addr - alloc->buffer) /PAGE_SIZE ; + page = &alloc->pages[index];if (page->page_ptr) { +trace_binder_alloc_lru_start (alloc, index); + on_lru =list_lru_del (&binder_alloc_lru, &page->lru);WARN_ON (!on_lru); + +trace_binder_alloc_lru_end (alloc, index);continue ; }if (WARN_ON (!vma)) goto err_page_ptr_cleared; +trace_binder_alloc_page_start (alloc, index); page->page_ptr =alloc_page (GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
@@ -278,6 +285,8 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, alloc->pid, user_page_addr); goto err_vm_insert_page_failed; } + +trace_binder_alloc_page_end (alloc, index);/* vm_insert_page does not seem to increment the refcount */ }if (mm) {
@@ -290,11 +299,17 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,for (page_addr = end -PAGE_SIZE ; page_addr >= start; page_addr -=PAGE_SIZE ) {bool ret; +size_t index; - page = &alloc->pages[(page_addr - alloc->buffer) /PAGE_SIZE ]; + index = (page_addr - alloc->buffer) /PAGE_SIZE ; + page = &alloc->pages[index]; + +trace_binder_free_lru_start (alloc, index); ret =list_lru_add (&binder_alloc_lru, &page->lru);WARN_ON (!ret); + +trace_binder_free_lru_end (alloc, index);continue ; err_vm_insert_page_failed:
@@ -887,18 +902,26 @@ enum lru_status binder_alloc_free_page(struct list_head *item,if (!down_write_trylock (&mm->mmap_sem)) goto err_down_write_mmap_sem_failed; +trace_binder_unmap_user_start (alloc, index); +zap_page_range (alloc->vma, page_addr + alloc->user_buffer_offset,PAGE_SIZE ); +trace_binder_unmap_user_end (alloc, index); +up_write (&mm->mmap_sem);mmput (mm); } +trace_binder_unmap_kernel_start (alloc, index); +unmap_kernel_range (page_addr,PAGE_SIZE );__free_page (page->page_ptr); page->page_ptr =NULL ; +trace_binder_unmap_kernel_end (alloc, index); +list_lru_isolate (lru, item);mutex_unlock (&alloc->mutex);
drivers/android/binder_trace.h +55 -0
61 lines not on this page · raw message
Signature · 1 line
2.14.1.342.g6490525c54-goog
PATCH v3 6/6 android: binder: Add page usage in binder stats
In reply to Sherry YangAdd the number of active, lru, and free pages for
each binder process in binder stats
--- drivers/android/binder.c | 2 ++ drivers/android/binder_alloc.c | 28 ++++++++++++++++++++++++++++ drivers/android/binder_alloc.h | 2 ++ 3 files changed, 32 insertions(+)
drivers/android/binder.c +2 -0
@@ -5047,6 +5047,8 @@ static void print_binder_proc_stats(struct seq_file *m, count =binder_alloc_get_allocated_count (&proc->alloc);seq_printf (m," buffers: %d\n" , count); +binder_alloc_print_pages (m, &proc->alloc); + count =0 ;binder_inner_proc_lock (proc);list_for_each_entry (w, &proc->todo, entry) {
drivers/android/binder_alloc.c +28 -0
@@ -831,6 +831,34 @@ void binder_alloc_print_allocated(struct seq_file *m,mutex_unlock (&alloc->mutex); } +/** +* binder_alloc_print_pages() - print page usage +* @m: seq_file for output via seq_printf() +* @alloc: binder_alloc for this proc +*/ +void binder_alloc_print_pages (struct seq_file *m, +struct binder_alloc *alloc) +{ +struct binder_lru_page *page; +int i; +int active =0 ; +int lru =0 ; +int free =0 ; + +mutex_lock (&alloc->mutex); +for (i =0 ; i < alloc->buffer_size /PAGE_SIZE ; i++) { + page = &alloc->pages[i]; +if (!page->page_ptr) + free++; +else if (list_empty (&page->lru)) + active++; +else + lru++; + } +mutex_unlock (&alloc->mutex); +seq_printf (m," pages: %d:%d:%d\n" , active, lru, free); +} +/** * binder_alloc_get_allocated_count() - return count of buffers * @alloc: binder_alloc for this proc
drivers/android/binder_alloc.h +2 -0
@@ -142,6 +142,8 @@ extern void binder_alloc_deferred_release(struct binder_alloc *alloc);extern int binder_alloc_get_allocated_count (struct binder_alloc *alloc);extern void binder_alloc_print_allocated (struct seq_file *m,struct binder_alloc *alloc); +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
Signature · 1 line
2.14.1.342.g6490525c54-goog