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

android: binder: Add global lru shrinker to binder

Hold 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.

Signed-off-by: Sherry Yang <sherryy@android.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Sherry Yang and committed by
Greg Kroah-Hartman
f2517eb7 74310e06

+225 -40
+2
drivers/android/binder.c
··· 5243 5243 struct binder_device *device; 5244 5244 struct hlist_node *tmp; 5245 5245 5246 + binder_alloc_shrinker_init(); 5247 + 5246 5248 atomic_set(&binder_transaction_log.cur, ~0U); 5247 5249 atomic_set(&binder_transaction_log_failed.cur, ~0U); 5248 5250
+149 -23
drivers/android/binder_alloc.c
··· 27 27 #include <linux/vmalloc.h> 28 28 #include <linux/slab.h> 29 29 #include <linux/sched.h> 30 + #include <linux/list_lru.h> 30 31 #include "binder_alloc.h" 31 32 #include "binder_trace.h" 33 + 34 + struct list_lru binder_alloc_lru; 32 35 33 36 static DEFINE_MUTEX(binder_alloc_mmap_lock); 34 37 ··· 191 188 { 192 189 void *page_addr; 193 190 unsigned long user_page_addr; 194 - struct page **page; 195 - struct mm_struct *mm; 191 + struct binder_lru_page *page; 192 + struct mm_struct *mm = NULL; 193 + bool need_mm = false; 196 194 197 195 binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, 198 196 "%d: %s pages %pK-%pK\n", alloc->pid, ··· 204 200 205 201 trace_binder_update_page_range(alloc, allocate, start, end); 206 202 207 - if (vma) 208 - mm = NULL; 209 - else 203 + if (allocate == 0) 204 + goto free_range; 205 + 206 + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { 207 + page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; 208 + if (!page->page_ptr) { 209 + need_mm = true; 210 + break; 211 + } 212 + } 213 + 214 + if (!vma && need_mm) 210 215 mm = get_task_mm(alloc->tsk); 211 216 212 217 if (mm) { ··· 228 215 } 229 216 } 230 217 231 - if (allocate == 0) 232 - goto free_range; 233 - 234 - if (vma == NULL) { 218 + if (!vma && need_mm) { 235 219 pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", 236 220 alloc->pid); 237 221 goto err_no_vma; ··· 236 226 237 227 for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { 238 228 int ret; 229 + bool on_lru; 239 230 240 231 page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; 241 232 242 - BUG_ON(*page); 243 - *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); 244 - if (*page == NULL) { 233 + if (page->page_ptr) { 234 + on_lru = list_lru_del(&binder_alloc_lru, &page->lru); 235 + WARN_ON(!on_lru); 236 + continue; 237 + } 238 + 239 + if (WARN_ON(!vma)) 240 + goto err_page_ptr_cleared; 241 + 242 + page->page_ptr = alloc_page(GFP_KERNEL | 243 + __GFP_HIGHMEM | 244 + __GFP_ZERO); 245 + if (!page->page_ptr) { 245 246 pr_err("%d: binder_alloc_buf failed for page at %pK\n", 246 247 alloc->pid, page_addr); 247 248 goto err_alloc_page_failed; 248 249 } 250 + page->alloc = alloc; 251 + INIT_LIST_HEAD(&page->lru); 252 + 249 253 ret = map_kernel_range_noflush((unsigned long)page_addr, 250 - PAGE_SIZE, PAGE_KERNEL, page); 254 + PAGE_SIZE, PAGE_KERNEL, 255 + &page->page_ptr); 251 256 flush_cache_vmap((unsigned long)page_addr, 252 257 (unsigned long)page_addr + PAGE_SIZE); 253 258 if (ret != 1) { ··· 272 247 } 273 248 user_page_addr = 274 249 (uintptr_t)page_addr + alloc->user_buffer_offset; 275 - ret = vm_insert_page(vma, user_page_addr, page[0]); 250 + ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr); 276 251 if (ret) { 277 252 pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", 278 253 alloc->pid, user_page_addr); ··· 289 264 free_range: 290 265 for (page_addr = end - PAGE_SIZE; page_addr >= start; 291 266 page_addr -= PAGE_SIZE) { 267 + bool ret; 268 + 292 269 page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; 293 - if (vma) 294 - zap_page_range(vma, (uintptr_t)page_addr + 295 - alloc->user_buffer_offset, PAGE_SIZE); 270 + 271 + ret = list_lru_add(&binder_alloc_lru, &page->lru); 272 + WARN_ON(!ret); 273 + continue; 274 + 296 275 err_vm_insert_page_failed: 297 276 unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); 298 277 err_map_kernel_failed: 299 - __free_page(*page); 300 - *page = NULL; 278 + __free_page(page->page_ptr); 279 + page->page_ptr = NULL; 301 280 err_alloc_page_failed: 281 + err_page_ptr_cleared: 302 282 ; 303 283 } 304 284 err_no_vma: ··· 761 731 762 732 for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { 763 733 void *page_addr; 734 + bool on_lru; 764 735 765 - if (!alloc->pages[i]) 736 + if (!alloc->pages[i].page_ptr) 766 737 continue; 767 738 739 + on_lru = list_lru_del(&binder_alloc_lru, 740 + &alloc->pages[i].lru); 768 741 page_addr = alloc->buffer + i * PAGE_SIZE; 769 742 binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, 770 - "%s: %d: page %d at %pK not freed\n", 771 - __func__, alloc->pid, i, page_addr); 743 + "%s: %d: page %d at %pK %s\n", 744 + __func__, alloc->pid, i, page_addr, 745 + on_lru ? "on lru" : "active"); 772 746 unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); 773 - __free_page(alloc->pages[i]); 747 + __free_page(alloc->pages[i].page_ptr); 774 748 page_count++; 775 749 } 776 750 kfree(alloc->pages); ··· 851 817 } 852 818 853 819 /** 820 + * binder_alloc_free_page() - shrinker callback to free pages 821 + * @item: item to free 822 + * @lock: lock protecting the item 823 + * @cb_arg: callback argument 824 + * 825 + * Called from list_lru_walk() in binder_shrink_scan() to free 826 + * up pages when the system is under memory pressure. 827 + */ 828 + enum lru_status binder_alloc_free_page(struct list_head *item, 829 + struct list_lru_one *lru, 830 + spinlock_t *lock, 831 + void *cb_arg) 832 + { 833 + struct mm_struct *mm = NULL; 834 + struct binder_lru_page *page = container_of(item, 835 + struct binder_lru_page, 836 + lru); 837 + struct binder_alloc *alloc; 838 + uintptr_t page_addr; 839 + size_t index; 840 + 841 + alloc = page->alloc; 842 + if (!mutex_trylock(&alloc->mutex)) 843 + goto err_get_alloc_mutex_failed; 844 + 845 + if (!page->page_ptr) 846 + goto err_page_already_freed; 847 + 848 + index = page - alloc->pages; 849 + page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; 850 + if (alloc->vma) { 851 + mm = get_task_mm(alloc->tsk); 852 + if (!mm) 853 + goto err_get_task_mm_failed; 854 + if (!down_write_trylock(&mm->mmap_sem)) 855 + goto err_down_write_mmap_sem_failed; 856 + 857 + zap_page_range(alloc->vma, 858 + page_addr + alloc->user_buffer_offset, 859 + PAGE_SIZE); 860 + 861 + up_write(&mm->mmap_sem); 862 + mmput(mm); 863 + } 864 + 865 + unmap_kernel_range(page_addr, PAGE_SIZE); 866 + __free_page(page->page_ptr); 867 + page->page_ptr = NULL; 868 + 869 + list_lru_isolate(lru, item); 870 + 871 + mutex_unlock(&alloc->mutex); 872 + return LRU_REMOVED; 873 + 874 + err_down_write_mmap_sem_failed: 875 + mmput(mm); 876 + err_get_task_mm_failed: 877 + err_page_already_freed: 878 + mutex_unlock(&alloc->mutex); 879 + err_get_alloc_mutex_failed: 880 + return LRU_SKIP; 881 + } 882 + 883 + static unsigned long 884 + binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) 885 + { 886 + unsigned long ret = list_lru_count(&binder_alloc_lru); 887 + return ret; 888 + } 889 + 890 + static unsigned long 891 + binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) 892 + { 893 + unsigned long ret; 894 + 895 + ret = list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, 896 + NULL, sc->nr_to_scan); 897 + return ret; 898 + } 899 + 900 + struct shrinker binder_shrinker = { 901 + .count_objects = binder_shrink_count, 902 + .scan_objects = binder_shrink_scan, 903 + .seeks = DEFAULT_SEEKS, 904 + }; 905 + 906 + /** 854 907 * binder_alloc_init() - called by binder_open() for per-proc initialization 855 908 * @alloc: binder_alloc for this proc 856 909 * ··· 951 830 mutex_init(&alloc->mutex); 952 831 } 953 832 833 + void binder_alloc_shrinker_init(void) 834 + { 835 + list_lru_init(&binder_alloc_lru); 836 + register_shrinker(&binder_shrinker); 837 + }
+20 -3
drivers/android/binder_alloc.h
··· 21 21 #include <linux/rtmutex.h> 22 22 #include <linux/vmalloc.h> 23 23 #include <linux/slab.h> 24 + #include <linux/list_lru.h> 24 25 26 + extern struct list_lru binder_alloc_lru; 25 27 struct binder_transaction; 26 28 27 29 /** ··· 63 61 }; 64 62 65 63 /** 64 + * struct binder_lru_page - page object used for binder shrinker 65 + * @page_ptr: pointer to physical page in mmap'd space 66 + * @lru: entry in binder_alloc_lru 67 + * @alloc: binder_alloc for a proc 68 + */ 69 + struct binder_lru_page { 70 + struct list_head lru; 71 + struct page *page_ptr; 72 + struct binder_alloc *alloc; 73 + }; 74 + 75 + /** 66 76 * struct binder_alloc - per-binder proc state for binder allocator 67 77 * @vma: vm_area_struct passed to mmap_handler 68 78 * (invarient after mmap) ··· 89 75 * @allocated_buffers: rb tree of allocated buffers sorted by address 90 76 * @free_async_space: VA space available for async buffers. This is 91 77 * initialized at mmap time to 1/2 the full VA space 92 - * @pages: array of physical page addresses for each 93 - * page of mmap'd space 78 + * @pages: array of binder_lru_page 94 79 * @buffer_size: size of address space specified via mmap 95 80 * @pid: pid for associated binder_proc (invariant after init) 96 81 * ··· 109 96 struct rb_root free_buffers; 110 97 struct rb_root allocated_buffers; 111 98 size_t free_async_space; 112 - struct page **pages; 99 + struct binder_lru_page *pages; 113 100 size_t buffer_size; 114 101 uint32_t buffer_free; 115 102 int pid; ··· 120 107 #else 121 108 static inline void binder_selftest_alloc(struct binder_alloc *alloc) {} 122 109 #endif 110 + enum lru_status binder_alloc_free_page(struct list_head *item, 111 + struct list_lru_one *lru, 112 + spinlock_t *lock, void *cb_arg); 123 113 extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, 124 114 size_t data_size, 125 115 size_t offsets_size, 126 116 size_t extra_buffers_size, 127 117 int is_async); 128 118 extern void binder_alloc_init(struct binder_alloc *alloc); 119 + void binder_alloc_shrinker_init(void); 129 120 extern void binder_alloc_vma_close(struct binder_alloc *alloc); 130 121 extern struct binder_buffer * 131 122 binder_alloc_prepare_to_free(struct binder_alloc *alloc,
+54 -14
drivers/android/binder_alloc_selftest.c
··· 109 109 page_addr = buffer->data; 110 110 for (; page_addr < end; page_addr += PAGE_SIZE) { 111 111 page_index = (page_addr - alloc->buffer) / PAGE_SIZE; 112 - if (!alloc->pages[page_index]) { 113 - pr_err("incorrect alloc state at page index %d\n", 114 - page_index); 112 + if (!alloc->pages[page_index].page_ptr || 113 + !list_empty(&alloc->pages[page_index].lru)) { 114 + pr_err("expect alloc but is %s at page index %d\n", 115 + alloc->pages[page_index].page_ptr ? 116 + "lru" : "free", page_index); 115 117 return false; 116 118 } 117 119 } ··· 139 137 140 138 static void binder_selftest_free_buf(struct binder_alloc *alloc, 141 139 struct binder_buffer *buffers[], 142 - size_t *sizes, int *seq) 140 + size_t *sizes, int *seq, size_t end) 143 141 { 144 142 int i; 145 143 146 144 for (i = 0; i < BUFFER_NUM; i++) 147 145 binder_alloc_free_buf(alloc, buffers[seq[i]]); 148 146 147 + for (i = 0; i < end / PAGE_SIZE; i++) { 148 + /** 149 + * Error message on a free page can be false positive 150 + * if binder shrinker ran during binder_alloc_free_buf 151 + * calls above. 152 + */ 153 + if (list_empty(&alloc->pages[i].lru)) { 154 + pr_err_size_seq(sizes, seq); 155 + pr_err("expect lru but is %s at page index %d\n", 156 + alloc->pages[i].page_ptr ? "alloc" : "free", i); 157 + binder_selftest_failures++; 158 + } 159 + } 160 + } 161 + 162 + static void binder_selftest_free_page(struct binder_alloc *alloc) 163 + { 164 + int i; 165 + unsigned long count; 166 + 167 + while ((count = list_lru_count(&binder_alloc_lru))) { 168 + list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, 169 + NULL, count); 170 + } 171 + 149 172 for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { 150 - if ((!alloc->pages[i]) == (i == 0)) { 151 - pr_err("incorrect free state at page index %d\n", i); 173 + if (alloc->pages[i].page_ptr) { 174 + pr_err("expect free but is %s at page index %d\n", 175 + list_empty(&alloc->pages[i].lru) ? 176 + "alloc" : "lru", i); 152 177 binder_selftest_failures++; 153 178 } 154 179 } 155 180 } 156 181 157 182 static void binder_selftest_alloc_free(struct binder_alloc *alloc, 158 - size_t *sizes, int *seq) 183 + size_t *sizes, int *seq, size_t end) 159 184 { 160 185 struct binder_buffer *buffers[BUFFER_NUM]; 161 186 162 187 binder_selftest_alloc_buf(alloc, buffers, sizes, seq); 163 - binder_selftest_free_buf(alloc, buffers, sizes, seq); 188 + binder_selftest_free_buf(alloc, buffers, sizes, seq, end); 189 + 190 + /* Allocate from lru. */ 191 + binder_selftest_alloc_buf(alloc, buffers, sizes, seq); 192 + if (list_lru_count(&binder_alloc_lru)) 193 + pr_err("lru list should be empty but is not\n"); 194 + 195 + binder_selftest_free_buf(alloc, buffers, sizes, seq, end); 196 + binder_selftest_free_page(alloc); 164 197 } 165 198 166 199 static bool is_dup(int *seq, int index, int val) ··· 211 174 212 175 /* Generate BUFFER_NUM factorial free orders. */ 213 176 static void binder_selftest_free_seq(struct binder_alloc *alloc, 214 - size_t *sizes, int *seq, int index) 177 + size_t *sizes, int *seq, 178 + int index, size_t end) 215 179 { 216 180 int i; 217 181 218 182 if (index == BUFFER_NUM) { 219 - binder_selftest_alloc_free(alloc, sizes, seq); 183 + binder_selftest_alloc_free(alloc, sizes, seq, end); 220 184 return; 221 185 } 222 186 for (i = 0; i < BUFFER_NUM; i++) { 223 187 if (is_dup(seq, index, i)) 224 188 continue; 225 189 seq[index] = i; 226 - binder_selftest_free_seq(alloc, sizes, seq, index + 1); 190 + binder_selftest_free_seq(alloc, sizes, seq, index + 1, end); 227 191 } 228 192 } 229 193 ··· 249 211 * we need one giant buffer before getting to the last page. 250 212 */ 251 213 back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; 252 - binder_selftest_free_seq(alloc, front_sizes, seq, 0); 253 - binder_selftest_free_seq(alloc, back_sizes, seq, 0); 214 + binder_selftest_free_seq(alloc, front_sizes, seq, 0, 215 + end_offset[BUFFER_NUM - 1]); 216 + binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size); 254 217 } 255 218 256 219 static void binder_selftest_alloc_offset(struct binder_alloc *alloc, ··· 285 246 * 286 247 * Allocate BUFFER_NUM buffers to cover all page alignment cases, 287 248 * then free them in all orders possible. Check that pages are 288 - * allocated after buffer alloc and freed after freeing buffer. 249 + * correctly allocated, put onto lru when buffers are freed, and 250 + * are freed when binder_alloc_free_page is called. 289 251 */ 290 252 void binder_selftest_alloc(struct binder_alloc *alloc) 291 253 {