1307 lines · 34.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* binder_alloc.c * * Android IPC Subsystem * * Copyright (C) 2007-2017 Google, Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/list.h>#include <linux/sched/mm.h>#include <linux/module.h>#include <linux/rtmutex.h>#include <linux/rbtree.h>#include <linux/seq_file.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/list_lru.h>#include <linux/ratelimit.h>#include <asm/cacheflush.h>#include <linux/uaccess.h>#include <linux/highmem.h>#include <linux/sizes.h>#include "binder_alloc.h"#include "binder_trace.h" struct list_lru binder_freelist; static DEFINE_MUTEX(binder_alloc_mmap_lock); enum {	BINDER_DEBUG_USER_ERROR             = 1U << 0,	BINDER_DEBUG_OPEN_CLOSE             = 1U << 1,	BINDER_DEBUG_BUFFER_ALLOC           = 1U << 2,	BINDER_DEBUG_BUFFER_ALLOC_ASYNC     = 1U << 3,};static uint32_t binder_alloc_debug_mask = BINDER_DEBUG_USER_ERROR; module_param_named(debug_mask, binder_alloc_debug_mask,		   uint, 0644); #define binder_alloc_debug(mask, x...) \	do { \		if (binder_alloc_debug_mask & mask) \			pr_info_ratelimited(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 - buffer->user_data;	return binder_buffer_next(buffer)->user_data - buffer->user_data;} static void binder_insert_free_buffer(struct binder_alloc *alloc,				      struct binder_buffer *new_buffer){	struct rb_node **p = &alloc->free_buffers.rb_node;	struct rb_node *parent = NULL;	struct binder_buffer *buffer;	size_t buffer_size;	size_t new_buffer_size; 	BUG_ON(!new_buffer->free); 	new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer); 	binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,		     "%d: add free buffer, size %zd, at %pK\n",		      alloc->pid, new_buffer_size, new_buffer); 	while (*p) {		parent = *p;		buffer = rb_entry(parent, struct binder_buffer, rb_node);		BUG_ON(!buffer->free); 		buffer_size = binder_alloc_buffer_size(alloc, buffer); 		if (new_buffer_size < buffer_size)			p = &parent->rb_left;		else			p = &parent->rb_right;	}	rb_link_node(&new_buffer->rb_node, parent, p);	rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers);} static void binder_insert_allocated_buffer_locked(		struct binder_alloc *alloc, struct binder_buffer *new_buffer){	struct rb_node **p = &alloc->allocated_buffers.rb_node;	struct rb_node *parent = NULL;	struct binder_buffer *buffer; 	BUG_ON(new_buffer->free); 	while (*p) {		parent = *p;		buffer = rb_entry(parent, struct binder_buffer, rb_node);		BUG_ON(buffer->free); 		if (new_buffer->user_data < buffer->user_data)			p = &parent->rb_left;		else if (new_buffer->user_data > buffer->user_data)			p = &parent->rb_right;		else			BUG();	}	rb_link_node(&new_buffer->rb_node, parent, p);	rb_insert_color(&new_buffer->rb_node, &alloc->allocated_buffers);} static struct binder_buffer *binder_alloc_prepare_to_free_locked(		struct binder_alloc *alloc,		unsigned long user_ptr){	struct rb_node *n = alloc->allocated_buffers.rb_node;	struct binder_buffer *buffer; 	while (n) {		buffer = rb_entry(n, struct binder_buffer, rb_node);		BUG_ON(buffer->free); 		if (user_ptr < buffer->user_data) {			n = n->rb_left;		} else if (user_ptr > buffer->user_data) {			n = n->rb_right;		} else {			/*			 * Guard against user threads attempting to			 * free the buffer when in use by kernel or			 * after it's already been freed.			 */			if (!buffer->allow_user_free)				return ERR_PTR(-EPERM);			buffer->allow_user_free = 0;			return buffer;		}	}	return NULL;} /** * binder_alloc_prepare_to_free() - get buffer given user ptr * @alloc:	binder_alloc for this proc * @user_ptr:	User pointer to buffer data * * Validate userspace pointer to buffer data and return buffer corresponding to * that user pointer. Search the rb tree for buffer that matches user data * pointer. * * Return:	Pointer to buffer or NULL */struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,						   unsigned long user_ptr){	struct binder_buffer *buffer; 	spin_lock(&alloc->lock);	buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);	spin_unlock(&alloc->lock);	return buffer;} static inline voidbinder_set_installed_page(struct binder_lru_page *lru_page,			  struct page *page){	/* Pairs with acquire in binder_get_installed_page() */	smp_store_release(&lru_page->page_ptr, page);} static inline struct page *binder_get_installed_page(struct binder_lru_page *lru_page){	/* Pairs with release in binder_set_installed_page() */	return smp_load_acquire(&lru_page->page_ptr);} static void binder_lru_freelist_add(struct binder_alloc *alloc,				    unsigned long start, unsigned long end){	struct binder_lru_page *page;	unsigned long page_addr; 	trace_binder_update_page_range(alloc, false, start, end); 	for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {		size_t index;		int ret; 		index = (page_addr - alloc->buffer) / PAGE_SIZE;		page = &alloc->pages[index]; 		if (!binder_get_installed_page(page))			continue; 		trace_binder_free_lru_start(alloc, index); 		ret = list_lru_add_obj(&binder_freelist, &page->lru);		WARN_ON(!ret); 		trace_binder_free_lru_end(alloc, index);	}} static int binder_install_single_page(struct binder_alloc *alloc,				      struct binder_lru_page *lru_page,				      unsigned long addr){	struct page *page;	int ret = 0; 	if (!mmget_not_zero(alloc->mm))		return -ESRCH; 	/*	 * Protected with mmap_sem in write mode as multiple tasks	 * might race to install the same page.	 */	mmap_write_lock(alloc->mm);	if (binder_get_installed_page(lru_page))		goto out; 	if (!alloc->vma) {		pr_err("%d: %s failed, no vma\n", alloc->pid, __func__);		ret = -ESRCH;		goto out;	} 	page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);	if (!page) {		pr_err("%d: failed to allocate page\n", alloc->pid);		ret = -ENOMEM;		goto out;	} 	ret = vm_insert_page(alloc->vma, addr, page);	if (ret) {		pr_err("%d: %s failed to insert page at offset %lx with %d\n",		       alloc->pid, __func__, addr - alloc->buffer, ret);		__free_page(page);		ret = -ENOMEM;		goto out;	} 	/* Mark page installation complete and safe to use */	binder_set_installed_page(lru_page, page);out:	mmap_write_unlock(alloc->mm);	mmput_async(alloc->mm);	return ret;} static int binder_install_buffer_pages(struct binder_alloc *alloc,				       struct binder_buffer *buffer,				       size_t size){	struct binder_lru_page *page;	unsigned long start, final;	unsigned long page_addr; 	start = buffer->user_data & PAGE_MASK;	final = PAGE_ALIGN(buffer->user_data + size); 	for (page_addr = start; page_addr < final; page_addr += PAGE_SIZE) {		unsigned long index;		int ret; 		index = (page_addr - alloc->buffer) / PAGE_SIZE;		page = &alloc->pages[index]; 		if (binder_get_installed_page(page))			continue; 		trace_binder_alloc_page_start(alloc, index); 		ret = binder_install_single_page(alloc, page, page_addr);		if (ret)			return ret; 		trace_binder_alloc_page_end(alloc, index);	} 	return 0;} /* The range of pages should exclude those shared with other buffers */static void binder_lru_freelist_del(struct binder_alloc *alloc,				    unsigned long start, unsigned long end){	struct binder_lru_page *page;	unsigned long page_addr; 	trace_binder_update_page_range(alloc, true, start, end); 	for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {		unsigned long index;		bool on_lru; 		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_obj(&binder_freelist, &page->lru);			WARN_ON(!on_lru); 			trace_binder_alloc_lru_end(alloc, index);			continue;		} 		if (index + 1 > alloc->pages_high)			alloc->pages_high = index + 1;	}} static inline void binder_alloc_set_vma(struct binder_alloc *alloc,		struct vm_area_struct *vma){	/* pairs with smp_load_acquire in binder_alloc_get_vma() */	smp_store_release(&alloc->vma, vma);} static inline struct vm_area_struct *binder_alloc_get_vma(		struct binder_alloc *alloc){	/* pairs with smp_store_release in binder_alloc_set_vma() */	return smp_load_acquire(&alloc->vma);} static void debug_no_space_locked(struct binder_alloc *alloc){	size_t largest_alloc_size = 0;	struct binder_buffer *buffer;	size_t allocated_buffers = 0;	size_t largest_free_size = 0;	size_t total_alloc_size = 0;	size_t total_free_size = 0;	size_t free_buffers = 0;	size_t buffer_size;	struct rb_node *n; 	for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) {		buffer = rb_entry(n, struct binder_buffer, rb_node);		buffer_size = binder_alloc_buffer_size(alloc, buffer);		allocated_buffers++;		total_alloc_size += buffer_size;		if (buffer_size > largest_alloc_size)			largest_alloc_size = buffer_size;	} 	for (n = rb_first(&alloc->free_buffers); n; n = rb_next(n)) {		buffer = rb_entry(n, struct binder_buffer, rb_node);		buffer_size = binder_alloc_buffer_size(alloc, buffer);		free_buffers++;		total_free_size += buffer_size;		if (buffer_size > largest_free_size)			largest_free_size = buffer_size;	} 	binder_alloc_debug(BINDER_DEBUG_USER_ERROR,			   "allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n",			   total_alloc_size, allocated_buffers,			   largest_alloc_size, total_free_size,			   free_buffers, largest_free_size);} static bool debug_low_async_space_locked(struct binder_alloc *alloc){	/*	 * Find the amount and size of buffers allocated by the current caller;	 * The idea is that once we cross the threshold, whoever is responsible	 * for the low async space is likely to try to send another async txn,	 * and at some point we'll catch them in the act. This is more efficient	 * than keeping a map per pid.	 */	struct binder_buffer *buffer;	size_t total_alloc_size = 0;	int pid = current->tgid;	size_t num_buffers = 0;	struct rb_node *n; 	/*	 * Only start detecting spammers once we have less than 20% of async	 * space left (which is less than 10% of total buffer size).	 */	if (alloc->free_async_space >= alloc->buffer_size / 10) {		alloc->oneway_spam_detected = false;		return false;	} 	for (n = rb_first(&alloc->allocated_buffers); n != NULL;		 n = rb_next(n)) {		buffer = rb_entry(n, struct binder_buffer, rb_node);		if (buffer->pid != pid)			continue;		if (!buffer->async_transaction)			continue;		total_alloc_size += binder_alloc_buffer_size(alloc, buffer);		num_buffers++;	} 	/*	 * Warn if this pid has more than 50 transactions, or more than 50% of	 * async space (which is 25% of total buffer size). Oneway spam is only	 * detected when the threshold is exceeded.	 */	if (num_buffers > 50 || total_alloc_size > alloc->buffer_size / 4) {		binder_alloc_debug(BINDER_DEBUG_USER_ERROR,			     "%d: pid %d spamming oneway? %zd buffers allocated for a total size of %zd\n",			      alloc->pid, pid, num_buffers, total_alloc_size);		if (!alloc->oneway_spam_detected) {			alloc->oneway_spam_detected = true;			return true;		}	}	return false;} /* Callers preallocate @new_buffer, it is freed by this function if unused */static struct binder_buffer *binder_alloc_new_buf_locked(				struct binder_alloc *alloc,				struct binder_buffer *new_buffer,				size_t size,				int is_async){	struct rb_node *n = alloc->free_buffers.rb_node;	struct rb_node *best_fit = NULL;	struct binder_buffer *buffer;	unsigned long next_used_page;	unsigned long curr_last_page;	size_t buffer_size; 	if (is_async && alloc->free_async_space < size) {		binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,			     "%d: binder_alloc_buf size %zd failed, no async space left\n",			      alloc->pid, size);		buffer = ERR_PTR(-ENOSPC);		goto out;	} 	while (n) {		buffer = rb_entry(n, struct binder_buffer, rb_node);		BUG_ON(!buffer->free);		buffer_size = binder_alloc_buffer_size(alloc, buffer); 		if (size < buffer_size) {			best_fit = n;			n = n->rb_left;		} else if (size > buffer_size) {			n = n->rb_right;		} else {			best_fit = n;			break;		}	} 	if (unlikely(!best_fit)) {		binder_alloc_debug(BINDER_DEBUG_USER_ERROR,				   "%d: binder_alloc_buf size %zd failed, no address space\n",				   alloc->pid, size);		debug_no_space_locked(alloc);		buffer = ERR_PTR(-ENOSPC);		goto out;	} 	if (buffer_size != size) {		/* Found an oversized buffer and needs to be split */		buffer = rb_entry(best_fit, struct binder_buffer, rb_node);		buffer_size = binder_alloc_buffer_size(alloc, buffer); 		WARN_ON(n || buffer_size == size);		new_buffer->user_data = buffer->user_data + size;		list_add(&new_buffer->entry, &buffer->entry);		new_buffer->free = 1;		binder_insert_free_buffer(alloc, new_buffer);		new_buffer = NULL;	} 	binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,		     "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",		      alloc->pid, size, buffer, buffer_size); 	/*	 * Now we remove the pages from the freelist. A clever calculation

Lines 1–500 of 1307.