forked from Minki/linux
binder: move binder_alloc to separate file
Move the binder allocator functionality to its own file Continuation of splitting the binder allocator from the binder driver. Split binder_alloc functions from normal binder functions. Add kernel doc comments to functions declared extern in binder_alloc.h Signed-off-by: Todd Kjos <tkjos@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
19c987241c
commit
0c972a05cd
@ -1,3 +1,3 @@
|
||||
ccflags-y += -I$(src) # needed for trace events
|
||||
|
||||
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
|
||||
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nsproxy.h>
|
||||
@ -35,8 +34,6 @@
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
@ -45,11 +42,11 @@
|
||||
#endif
|
||||
|
||||
#include <uapi/linux/android/binder.h>
|
||||
#include "binder_alloc.h"
|
||||
#include "binder_trace.h"
|
||||
|
||||
static DEFINE_MUTEX(binder_main_lock);
|
||||
static DEFINE_MUTEX(binder_deferred_lock);
|
||||
static DEFINE_MUTEX(binder_alloc_mmap_lock);
|
||||
|
||||
static HLIST_HEAD(binder_devices);
|
||||
static HLIST_HEAD(binder_procs);
|
||||
@ -157,27 +154,6 @@ module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
|
||||
#define to_binder_fd_array_object(hdr) \
|
||||
container_of(hdr, struct binder_fd_array_object, hdr)
|
||||
|
||||
/*
|
||||
* debug declarations for binder_alloc. To be
|
||||
* moved to binder_alloc.c
|
||||
*/
|
||||
enum {
|
||||
BINDER_ALLOC_DEBUG_OPEN_CLOSE = 1U << 1,
|
||||
BINDER_ALLOC_DEBUG_BUFFER_ALLOC = 1U << 2,
|
||||
BINDER_ALLOC_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 3,
|
||||
};
|
||||
static uint32_t binder_alloc_debug_mask;
|
||||
|
||||
module_param_named(alloc_debug_mask, binder_alloc_debug_mask,
|
||||
uint, 0644);
|
||||
|
||||
#define binder_alloc_debug(mask, x...) \
|
||||
do { \
|
||||
if (binder_alloc_debug_mask & mask) \
|
||||
pr_info(x); \
|
||||
} while (0)
|
||||
/* end of binder_alloc debug declarations */
|
||||
|
||||
enum binder_stat_types {
|
||||
BINDER_STAT_PROC,
|
||||
BINDER_STAT_THREAD,
|
||||
@ -314,68 +290,12 @@ struct binder_ref {
|
||||
struct binder_ref_death *death;
|
||||
};
|
||||
|
||||
struct binder_buffer {
|
||||
struct list_head entry; /* free and allocated entries by address */
|
||||
struct rb_node rb_node; /* free entry by size or allocated entry */
|
||||
/* by address */
|
||||
unsigned free:1;
|
||||
unsigned allow_user_free:1;
|
||||
unsigned async_transaction:1;
|
||||
unsigned debug_id:29;
|
||||
|
||||
struct binder_transaction *transaction;
|
||||
|
||||
struct binder_node *target_node;
|
||||
size_t data_size;
|
||||
size_t offsets_size;
|
||||
size_t extra_buffers_size;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
enum binder_deferred_state {
|
||||
BINDER_DEFERRED_PUT_FILES = 0x01,
|
||||
BINDER_DEFERRED_FLUSH = 0x02,
|
||||
BINDER_DEFERRED_RELEASE = 0x04,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_alloc - per-binder proc state for binder allocator
|
||||
* @vma: vm_area_struct passed to mmap_handler
|
||||
* (invarient after mmap)
|
||||
* @vma_vm_mm: copy of vma->vm_mm (invarient after mmap)
|
||||
* @buffer: base of per-proc address space mapped via mmap
|
||||
* @user_buffer_offset: offset between user and kernel VAs for buffer
|
||||
* @buffers: list of all buffers for this proc
|
||||
* @free_buffers: rb tree of buffers available for allocation
|
||||
* sorted by size
|
||||
* @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
|
||||
* @buffer_size: size of address space (could be less than requested)
|
||||
*
|
||||
* Bookkeeping structure for per-proc address space management for binder
|
||||
* buffers. It is normally initialized during binder_init() and binder_mmap()
|
||||
* calls. The address space is used for both user-visible buffers and for
|
||||
* struct binder_buffer objects used to track the user buffers
|
||||
*/
|
||||
struct binder_alloc {
|
||||
struct mutex mutex;
|
||||
struct task_struct *tsk;
|
||||
struct vm_area_struct *vma;
|
||||
struct mm_struct *vma_vm_mm;
|
||||
void *buffer;
|
||||
ptrdiff_t user_buffer_offset;
|
||||
struct list_head buffers;
|
||||
struct rb_root free_buffers;
|
||||
struct rb_root allocated_buffers;
|
||||
size_t free_async_space;
|
||||
struct page **pages;
|
||||
size_t buffer_size;
|
||||
int pid;
|
||||
};
|
||||
|
||||
struct binder_proc {
|
||||
struct hlist_node proc_node;
|
||||
struct rb_root threads;
|
||||
@ -445,56 +365,6 @@ struct binder_transaction {
|
||||
kuid_t sender_euid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Forward declarations of binder_alloc functions.
|
||||
* These will be moved to binder_alloc.h when
|
||||
* binder_alloc is moved to its own files.
|
||||
*/
|
||||
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);
|
||||
extern void binder_alloc_vma_close(struct binder_alloc *alloc);
|
||||
extern struct binder_buffer *
|
||||
binder_alloc_buffer_lookup(struct binder_alloc *alloc,
|
||||
uintptr_t user_ptr);
|
||||
extern void binder_alloc_free_buf(struct binder_alloc *alloc,
|
||||
struct binder_buffer *buffer);
|
||||
extern int binder_alloc_mmap_handler(struct binder_alloc *alloc,
|
||||
struct vm_area_struct *vma);
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static inline ptrdiff_t
|
||||
binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc)
|
||||
{
|
||||
/*
|
||||
* user_buffer_offset is constant if vma is set and
|
||||
* undefined if vma is not set. It is possible to
|
||||
* get here with !alloc->vma if the target process
|
||||
* is dying while a transaction is being initiated.
|
||||
* Returning the old value is ok in this case and
|
||||
* the transaction will fail.
|
||||
*/
|
||||
return alloc->user_buffer_offset;
|
||||
}
|
||||
/* end of binder_alloc declarations */
|
||||
|
||||
static void
|
||||
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
|
||||
|
||||
@ -578,462 +448,6 @@ static void binder_set_nice(long nice)
|
||||
binder_user_error("%d RLIMIT_NICE not set\n", current->pid);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_ALLOC_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(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 < buffer)
|
||||
p = &parent->rb_left;
|
||||
else if (new_buffer > buffer)
|
||||
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_buffer_lookup_locked(
|
||||
struct binder_alloc *alloc,
|
||||
uintptr_t user_ptr)
|
||||
{
|
||||
struct rb_node *n = alloc->allocated_buffers.rb_node;
|
||||
struct binder_buffer *buffer;
|
||||
struct binder_buffer *kern_ptr;
|
||||
|
||||
kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset
|
||||
- offsetof(struct binder_buffer, data));
|
||||
|
||||
while (n) {
|
||||
buffer = rb_entry(n, struct binder_buffer, rb_node);
|
||||
BUG_ON(buffer->free);
|
||||
|
||||
if (kern_ptr < buffer)
|
||||
n = n->rb_left;
|
||||
else if (kern_ptr > buffer)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct binder_buffer *binder_alloc_buffer_lookup(struct binder_alloc *alloc,
|
||||
uintptr_t user_ptr)
|
||||
{
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
buffer = binder_alloc_buffer_lookup_locked(alloc, user_ptr);
|
||||
mutex_unlock(&alloc->mutex);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
|
||||
void *start, void *end,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
void *page_addr;
|
||||
unsigned long user_page_addr;
|
||||
struct page **page;
|
||||
struct mm_struct *mm;
|
||||
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: %s pages %pK-%pK\n", alloc->pid,
|
||||
allocate ? "allocate" : "free", start, end);
|
||||
|
||||
if (end <= start)
|
||||
return 0;
|
||||
|
||||
trace_binder_update_page_range(alloc, allocate, start, end);
|
||||
|
||||
if (vma)
|
||||
mm = NULL;
|
||||
else
|
||||
mm = get_task_mm(alloc->tsk);
|
||||
|
||||
if (mm) {
|
||||
down_write(&mm->mmap_sem);
|
||||
vma = alloc->vma;
|
||||
if (vma && mm != alloc->vma_vm_mm) {
|
||||
pr_err("%d: vma mm and task mm mismatch\n",
|
||||
alloc->pid);
|
||||
vma = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (allocate == 0)
|
||||
goto free_range;
|
||||
|
||||
if (vma == NULL) {
|
||||
pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
|
||||
alloc->pid);
|
||||
goto err_no_vma;
|
||||
}
|
||||
|
||||
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
|
||||
int ret;
|
||||
|
||||
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
|
||||
|
||||
BUG_ON(*page);
|
||||
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
|
||||
if (*page == NULL) {
|
||||
pr_err("%d: binder_alloc_buf failed for page at %pK\n",
|
||||
alloc->pid, page_addr);
|
||||
goto err_alloc_page_failed;
|
||||
}
|
||||
ret = map_kernel_range_noflush((unsigned long)page_addr,
|
||||
PAGE_SIZE, PAGE_KERNEL, page);
|
||||
flush_cache_vmap((unsigned long)page_addr,
|
||||
(unsigned long)page_addr + PAGE_SIZE);
|
||||
if (ret != 1) {
|
||||
pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n",
|
||||
alloc->pid, page_addr);
|
||||
goto err_map_kernel_failed;
|
||||
}
|
||||
user_page_addr =
|
||||
(uintptr_t)page_addr + alloc->user_buffer_offset;
|
||||
ret = vm_insert_page(vma, user_page_addr, page[0]);
|
||||
if (ret) {
|
||||
pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
|
||||
alloc->pid, user_page_addr);
|
||||
goto err_vm_insert_page_failed;
|
||||
}
|
||||
/* vm_insert_page does not seem to increment the refcount */
|
||||
}
|
||||
if (mm) {
|
||||
up_write(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
}
|
||||
return 0;
|
||||
|
||||
free_range:
|
||||
for (page_addr = end - PAGE_SIZE; page_addr >= start;
|
||||
page_addr -= PAGE_SIZE) {
|
||||
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
|
||||
if (vma)
|
||||
zap_page_range(vma, (uintptr_t)page_addr +
|
||||
alloc->user_buffer_offset, PAGE_SIZE);
|
||||
err_vm_insert_page_failed:
|
||||
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
|
||||
err_map_kernel_failed:
|
||||
__free_page(*page);
|
||||
*page = NULL;
|
||||
err_alloc_page_failed:
|
||||
;
|
||||
}
|
||||
err_no_vma:
|
||||
if (mm) {
|
||||
up_write(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct binder_buffer *binder_alloc_new_buf_locked(
|
||||
struct binder_alloc *alloc, size_t data_size,
|
||||
size_t offsets_size, size_t extra_buffers_size, int is_async)
|
||||
{
|
||||
struct rb_node *n = alloc->free_buffers.rb_node;
|
||||
struct binder_buffer *buffer;
|
||||
size_t buffer_size;
|
||||
struct rb_node *best_fit = NULL;
|
||||
void *has_page_addr;
|
||||
void *end_page_addr;
|
||||
size_t size, data_offsets_size;
|
||||
|
||||
if (alloc->vma == NULL) {
|
||||
pr_err("%d: binder_alloc_buf, no vma\n",
|
||||
alloc->pid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_offsets_size = ALIGN(data_size, sizeof(void *)) +
|
||||
ALIGN(offsets_size, sizeof(void *));
|
||||
|
||||
if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: got transaction with invalid size %zd-%zd\n",
|
||||
alloc->pid, data_size, offsets_size);
|
||||
return NULL;
|
||||
}
|
||||
size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
|
||||
if (size < data_offsets_size || size < extra_buffers_size) {
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: got transaction with invalid extra_buffers_size %zd\n",
|
||||
alloc->pid, extra_buffers_size);
|
||||
return NULL;
|
||||
}
|
||||
if (is_async &&
|
||||
alloc->free_async_space < size + sizeof(struct binder_buffer)) {
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: binder_alloc_buf size %zd failed, no async space left\n",
|
||||
alloc->pid, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 (best_fit == NULL) {
|
||||
pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
|
||||
alloc->pid, size);
|
||||
return NULL;
|
||||
}
|
||||
if (n == NULL) {
|
||||
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
|
||||
buffer_size = binder_alloc_buffer_size(alloc, buffer);
|
||||
}
|
||||
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: %s size %zd got buffer %pK size %zd\n",
|
||||
alloc->pid, __func__, size, buffer, buffer_size);
|
||||
|
||||
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);
|
||||
}
|
||||
end_page_addr =
|
||||
(void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
|
||||
if (end_page_addr > has_page_addr)
|
||||
end_page_addr = has_page_addr;
|
||||
if (binder_update_page_range(alloc, 1,
|
||||
(void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
|
||||
return NULL;
|
||||
|
||||
rb_erase(best_fit, &alloc->free_buffers);
|
||||
buffer->free = 0;
|
||||
binder_insert_allocated_buffer(alloc, buffer);
|
||||
if (buffer_size != size) {
|
||||
struct binder_buffer *new_buffer = (void *)buffer->data + size;
|
||||
|
||||
list_add(&new_buffer->entry, &buffer->entry);
|
||||
new_buffer->free = 1;
|
||||
binder_insert_free_buffer(alloc, new_buffer);
|
||||
}
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: %s size %zd got %pK\n",
|
||||
alloc->pid, __func__, size, buffer);
|
||||
buffer->data_size = data_size;
|
||||
buffer->offsets_size = offsets_size;
|
||||
buffer->async_transaction = is_async;
|
||||
buffer->extra_buffers_size = extra_buffers_size;
|
||||
if (is_async) {
|
||||
alloc->free_async_space -= size + sizeof(struct binder_buffer);
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC_ASYNC,
|
||||
"%d: binder_alloc_buf size %zd async free %zd\n",
|
||||
alloc->pid, size, alloc->free_async_space);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size,
|
||||
extra_buffers_size, is_async);
|
||||
mutex_unlock(&alloc->mutex);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void *buffer_start_page(struct binder_buffer *buffer)
|
||||
{
|
||||
return (void *)((uintptr_t)buffer & PAGE_MASK);
|
||||
}
|
||||
|
||||
static void *buffer_end_page(struct binder_buffer *buffer)
|
||||
{
|
||||
return (void *)(((uintptr_t)(buffer + 1) - 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;
|
||||
|
||||
BUG_ON(alloc->buffers.next == &buffer->entry);
|
||||
prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
|
||||
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;
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: merge free, buffer %pK share page with %pK\n",
|
||||
alloc->pid, buffer, prev);
|
||||
}
|
||||
|
||||
if (!list_is_last(&buffer->entry, &alloc->buffers)) {
|
||||
next = list_entry(buffer->entry.next,
|
||||
struct binder_buffer, entry);
|
||||
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;
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: merge free, buffer %pK share page with %pK\n",
|
||||
alloc->pid, buffer, prev);
|
||||
}
|
||||
}
|
||||
list_del(&buffer->entry);
|
||||
if (free_page_start || free_page_end) {
|
||||
binder_alloc_debug(BINDER_ALLOC_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);
|
||||
}
|
||||
}
|
||||
|
||||
static void binder_free_buf_locked(struct binder_alloc *alloc,
|
||||
struct binder_buffer *buffer)
|
||||
{
|
||||
size_t size, buffer_size;
|
||||
|
||||
buffer_size = binder_alloc_buffer_size(alloc, buffer);
|
||||
|
||||
size = ALIGN(buffer->data_size, sizeof(void *)) +
|
||||
ALIGN(buffer->offsets_size, sizeof(void *)) +
|
||||
ALIGN(buffer->extra_buffers_size, sizeof(void *));
|
||||
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%d: binder_free_buf %pK size %zd buffer_size %zd\n",
|
||||
alloc->pid, buffer, size, buffer_size);
|
||||
|
||||
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);
|
||||
|
||||
if (buffer->async_transaction) {
|
||||
alloc->free_async_space += size + sizeof(struct binder_buffer);
|
||||
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC_ASYNC,
|
||||
"%d: binder_free_buf size %zd async free %zd\n",
|
||||
alloc->pid, size, alloc->free_async_space);
|
||||
}
|
||||
|
||||
binder_update_page_range(alloc, 0,
|
||||
(void *)PAGE_ALIGN((uintptr_t)buffer->data),
|
||||
(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
|
||||
NULL);
|
||||
|
||||
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);
|
||||
|
||||
if (next->free) {
|
||||
rb_erase(&next->rb_node, &alloc->free_buffers);
|
||||
binder_delete_free_buffer(alloc, next);
|
||||
}
|
||||
}
|
||||
if (alloc->buffers.next != &buffer->entry) {
|
||||
struct binder_buffer *prev = list_entry(buffer->entry.prev,
|
||||
struct binder_buffer, entry);
|
||||
|
||||
if (prev->free) {
|
||||
binder_delete_free_buffer(alloc, buffer);
|
||||
rb_erase(&prev->rb_node, &alloc->free_buffers);
|
||||
buffer = prev;
|
||||
}
|
||||
}
|
||||
binder_insert_free_buffer(alloc, buffer);
|
||||
}
|
||||
|
||||
void binder_alloc_free_buf(struct binder_alloc *alloc,
|
||||
struct binder_buffer *buffer)
|
||||
{
|
||||
mutex_lock(&alloc->mutex);
|
||||
binder_free_buf_locked(alloc, buffer);
|
||||
mutex_unlock(&alloc->mutex);
|
||||
}
|
||||
|
||||
static struct binder_node *binder_get_node(struct binder_proc *proc,
|
||||
binder_uintptr_t ptr)
|
||||
{
|
||||
@ -3467,12 +2881,6 @@ static void binder_vma_open(struct vm_area_struct *vma)
|
||||
(unsigned long)pgprot_val(vma->vm_page_prot));
|
||||
}
|
||||
|
||||
void binder_alloc_vma_close(struct binder_alloc *alloc)
|
||||
{
|
||||
WRITE_ONCE(alloc->vma, NULL);
|
||||
WRITE_ONCE(alloc->vma_vm_mm, NULL);
|
||||
}
|
||||
|
||||
static void binder_vma_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct binder_proc *proc = vma->vm_private_data;
|
||||
@ -3497,86 +2905,6 @@ static const struct vm_operations_struct binder_vm_ops = {
|
||||
.fault = binder_vm_fault,
|
||||
};
|
||||
|
||||
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
struct vm_struct *area;
|
||||
const char *failure_string;
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
mutex_lock(&binder_alloc_mmap_lock);
|
||||
if (alloc->buffer) {
|
||||
ret = -EBUSY;
|
||||
failure_string = "already mapped";
|
||||
goto err_already_mapped;
|
||||
}
|
||||
|
||||
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
|
||||
if (area == NULL) {
|
||||
ret = -ENOMEM;
|
||||
failure_string = "get_vm_area";
|
||||
goto err_get_vm_area_failed;
|
||||
}
|
||||
alloc->buffer = area->addr;
|
||||
alloc->user_buffer_offset =
|
||||
vma->vm_start - (uintptr_t)alloc->buffer;
|
||||
mutex_unlock(&binder_alloc_mmap_lock);
|
||||
|
||||
#ifdef CONFIG_CPU_CACHE_VIPT
|
||||
if (cache_is_vipt_aliasing()) {
|
||||
while (CACHE_COLOUR(
|
||||
pr_info("%s: %d %lx-%lx maps %pK bad alignment\n",
|
||||
__func__,
|
||||
alloc->pid, vma->vm_start, vma->vm_end,
|
||||
alloc->buffer);
|
||||
vma->vm_start += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
|
||||
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
|
||||
GFP_KERNEL);
|
||||
if (alloc->pages == NULL) {
|
||||
ret = -ENOMEM;
|
||||
failure_string = "alloc page array";
|
||||
goto err_alloc_pages_failed;
|
||||
}
|
||||
alloc->buffer_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
if (binder_update_page_range(alloc, 1, alloc->buffer,
|
||||
alloc->buffer + PAGE_SIZE, vma)) {
|
||||
ret = -ENOMEM;
|
||||
failure_string = "alloc small buf";
|
||||
goto err_alloc_small_buf_failed;
|
||||
}
|
||||
buffer = alloc->buffer;
|
||||
INIT_LIST_HEAD(&alloc->buffers);
|
||||
list_add(&buffer->entry, &alloc->buffers);
|
||||
buffer->free = 1;
|
||||
binder_insert_free_buffer(alloc, buffer);
|
||||
alloc->free_async_space = alloc->buffer_size / 2;
|
||||
barrier();
|
||||
alloc->vma = vma;
|
||||
alloc->vma_vm_mm = vma->vm_mm;
|
||||
|
||||
return 0;
|
||||
|
||||
err_alloc_small_buf_failed:
|
||||
kfree(alloc->pages);
|
||||
alloc->pages = NULL;
|
||||
err_alloc_pages_failed:
|
||||
mutex_lock(&binder_alloc_mmap_lock);
|
||||
vfree(alloc->buffer);
|
||||
alloc->buffer = NULL;
|
||||
err_get_vm_area_failed:
|
||||
err_already_mapped:
|
||||
mutex_unlock(&binder_alloc_mmap_lock);
|
||||
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
|
||||
alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
@ -3616,13 +2944,6 @@ err_bad_arg:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void binder_alloc_init(struct binder_alloc *alloc)
|
||||
{
|
||||
alloc->tsk = current->group_leader;
|
||||
alloc->pid = current->group_leader->pid;
|
||||
mutex_init(&alloc->mutex);
|
||||
}
|
||||
|
||||
static int binder_open(struct inode *nodp, struct file *filp)
|
||||
{
|
||||
struct binder_proc *proc;
|
||||
@ -3758,55 +3079,6 @@ static int binder_node_release(struct binder_node *node, int refs)
|
||||
return refs;
|
||||
}
|
||||
|
||||
void binder_alloc_deferred_release(struct binder_alloc *alloc)
|
||||
{
|
||||
struct rb_node *n;
|
||||
int buffers, page_count;
|
||||
|
||||
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 */
|
||||
BUG_ON(buffer->transaction);
|
||||
|
||||
binder_free_buf_locked(alloc, buffer);
|
||||
buffers++;
|
||||
}
|
||||
|
||||
page_count = 0;
|
||||
if (alloc->pages) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
|
||||
void *page_addr;
|
||||
|
||||
if (!alloc->pages[i])
|
||||
continue;
|
||||
|
||||
page_addr = alloc->buffer + i * PAGE_SIZE;
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_BUFFER_ALLOC,
|
||||
"%s: %d: page %d at %pK not freed\n",
|
||||
__func__, alloc->pid, i, page_addr);
|
||||
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
|
||||
__free_page(alloc->pages[i]);
|
||||
page_count++;
|
||||
}
|
||||
kfree(alloc->pages);
|
||||
vfree(alloc->buffer);
|
||||
}
|
||||
mutex_unlock(&alloc->mutex);
|
||||
|
||||
binder_alloc_debug(BINDER_ALLOC_DEBUG_OPEN_CLOSE,
|
||||
"%s: %d buffers %d, pages %d\n",
|
||||
__func__, alloc->pid, buffers, page_count);
|
||||
}
|
||||
|
||||
static void binder_deferred_release(struct binder_proc *proc)
|
||||
{
|
||||
struct binder_context *context = proc->context;
|
||||
@ -4051,27 +3323,6 @@ static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
|
||||
ref->node->debug_id, ref->strong, ref->weak, ref->death);
|
||||
}
|
||||
|
||||
static void print_binder_buffer(struct seq_file *m, const char *prefix,
|
||||
struct binder_buffer *buffer)
|
||||
{
|
||||
seq_printf(m, "%s %d: %pK size %zd:%zd %s\n",
|
||||
prefix, buffer->debug_id, buffer->data,
|
||||
buffer->data_size, buffer->offsets_size,
|
||||
buffer->transaction ? "active" : "delivered");
|
||||
}
|
||||
|
||||
void binder_alloc_print_allocated(struct seq_file *m,
|
||||
struct binder_alloc *alloc)
|
||||
{
|
||||
struct rb_node *n;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
|
||||
print_binder_buffer(m, " buffer",
|
||||
rb_entry(n, struct binder_buffer, rb_node));
|
||||
mutex_unlock(&alloc->mutex);
|
||||
}
|
||||
|
||||
static void print_binder_proc(struct seq_file *m,
|
||||
struct binder_proc *proc, int print_all)
|
||||
{
|
||||
@ -4198,18 +3449,6 @@ static void print_binder_stats(struct seq_file *m, const char *prefix,
|
||||
}
|
||||
}
|
||||
|
||||
int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
|
||||
{
|
||||
struct rb_node *n;
|
||||
int count = 0;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
|
||||
count++;
|
||||
mutex_unlock(&alloc->mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void print_binder_proc_stats(struct seq_file *m,
|
||||
struct binder_proc *proc)
|
||||
{
|
||||
|
759
drivers/android/binder_alloc.c
Normal file
759
drivers/android/binder_alloc.c
Normal file
@ -0,0 +1,759 @@
|
||||
/* binder_alloc.c
|
||||
*
|
||||
* Android IPC Subsystem
|
||||
*
|
||||
* Copyright (C) 2007-2017 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#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 "binder_alloc.h"
|
||||
#include "binder_trace.h"
|
||||
|
||||
static DEFINE_MUTEX(binder_alloc_mmap_lock);
|
||||
|
||||
enum {
|
||||
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;
|
||||
|
||||
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(x); \
|
||||
} while (0)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 < buffer)
|
||||
p = &parent->rb_left;
|
||||
else if (new_buffer > buffer)
|
||||
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_buffer_lookup_locked(
|
||||
struct binder_alloc *alloc,
|
||||
uintptr_t user_ptr)
|
||||
{
|
||||
struct rb_node *n = alloc->allocated_buffers.rb_node;
|
||||
struct binder_buffer *buffer;
|
||||
struct binder_buffer *kern_ptr;
|
||||
|
||||
kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset
|
||||
- offsetof(struct binder_buffer, data));
|
||||
|
||||
while (n) {
|
||||
buffer = rb_entry(n, struct binder_buffer, rb_node);
|
||||
BUG_ON(buffer->free);
|
||||
|
||||
if (kern_ptr < buffer)
|
||||
n = n->rb_left;
|
||||
else if (kern_ptr > buffer)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_buffer_lookup() - 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_buffer_lookup(struct binder_alloc *alloc,
|
||||
uintptr_t user_ptr)
|
||||
{
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
buffer = binder_alloc_buffer_lookup_locked(alloc, user_ptr);
|
||||
mutex_unlock(&alloc->mutex);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
|
||||
void *start, void *end,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
void *page_addr;
|
||||
unsigned long user_page_addr;
|
||||
struct page **page;
|
||||
struct mm_struct *mm;
|
||||
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: %s pages %pK-%pK\n", alloc->pid,
|
||||
allocate ? "allocate" : "free", start, end);
|
||||
|
||||
if (end <= start)
|
||||
return 0;
|
||||
|
||||
trace_binder_update_page_range(alloc, allocate, start, end);
|
||||
|
||||
if (vma)
|
||||
mm = NULL;
|
||||
else
|
||||
mm = get_task_mm(alloc->tsk);
|
||||
|
||||
if (mm) {
|
||||
down_write(&mm->mmap_sem);
|
||||
vma = alloc->vma;
|
||||
if (vma && mm != alloc->vma_vm_mm) {
|
||||
pr_err("%d: vma mm and task mm mismatch\n",
|
||||
alloc->pid);
|
||||
vma = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (allocate == 0)
|
||||
goto free_range;
|
||||
|
||||
if (vma == NULL) {
|
||||
pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
|
||||
alloc->pid);
|
||||
goto err_no_vma;
|
||||
}
|
||||
|
||||
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
|
||||
int ret;
|
||||
|
||||
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
|
||||
|
||||
BUG_ON(*page);
|
||||
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
|
||||
if (*page == NULL) {
|
||||
pr_err("%d: binder_alloc_buf failed for page at %pK\n",
|
||||
alloc->pid, page_addr);
|
||||
goto err_alloc_page_failed;
|
||||
}
|
||||
ret = map_kernel_range_noflush((unsigned long)page_addr,
|
||||
PAGE_SIZE, PAGE_KERNEL, page);
|
||||
flush_cache_vmap((unsigned long)page_addr,
|
||||
(unsigned long)page_addr + PAGE_SIZE);
|
||||
if (ret != 1) {
|
||||
pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n",
|
||||
alloc->pid, page_addr);
|
||||
goto err_map_kernel_failed;
|
||||
}
|
||||
user_page_addr =
|
||||
(uintptr_t)page_addr + alloc->user_buffer_offset;
|
||||
ret = vm_insert_page(vma, user_page_addr, page[0]);
|
||||
if (ret) {
|
||||
pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
|
||||
alloc->pid, user_page_addr);
|
||||
goto err_vm_insert_page_failed;
|
||||
}
|
||||
/* vm_insert_page does not seem to increment the refcount */
|
||||
}
|
||||
if (mm) {
|
||||
up_write(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
}
|
||||
return 0;
|
||||
|
||||
free_range:
|
||||
for (page_addr = end - PAGE_SIZE; page_addr >= start;
|
||||
page_addr -= PAGE_SIZE) {
|
||||
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
|
||||
if (vma)
|
||||
zap_page_range(vma, (uintptr_t)page_addr +
|
||||
alloc->user_buffer_offset, PAGE_SIZE);
|
||||
err_vm_insert_page_failed:
|
||||
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
|
||||
err_map_kernel_failed:
|
||||
__free_page(*page);
|
||||
*page = NULL;
|
||||
err_alloc_page_failed:
|
||||
;
|
||||
}
|
||||
err_no_vma:
|
||||
if (mm) {
|
||||
up_write(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc,
|
||||
size_t data_size,
|
||||
size_t offsets_size,
|
||||
size_t extra_buffers_size,
|
||||
int is_async)
|
||||
{
|
||||
struct rb_node *n = alloc->free_buffers.rb_node;
|
||||
struct binder_buffer *buffer;
|
||||
size_t buffer_size;
|
||||
struct rb_node *best_fit = NULL;
|
||||
void *has_page_addr;
|
||||
void *end_page_addr;
|
||||
size_t size, data_offsets_size;
|
||||
|
||||
if (alloc->vma == NULL) {
|
||||
pr_err("%d: binder_alloc_buf, no vma\n",
|
||||
alloc->pid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_offsets_size = ALIGN(data_size, sizeof(void *)) +
|
||||
ALIGN(offsets_size, sizeof(void *));
|
||||
|
||||
if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: got transaction with invalid size %zd-%zd\n",
|
||||
alloc->pid, data_size, offsets_size);
|
||||
return NULL;
|
||||
}
|
||||
size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
|
||||
if (size < data_offsets_size || size < extra_buffers_size) {
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: got transaction with invalid extra_buffers_size %zd\n",
|
||||
alloc->pid, extra_buffers_size);
|
||||
return NULL;
|
||||
}
|
||||
if (is_async &&
|
||||
alloc->free_async_space < size + sizeof(struct binder_buffer)) {
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: binder_alloc_buf size %zd failed, no async space left\n",
|
||||
alloc->pid, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 (best_fit == NULL) {
|
||||
pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
|
||||
alloc->pid, size);
|
||||
return NULL;
|
||||
}
|
||||
if (n == NULL) {
|
||||
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
|
||||
buffer_size = binder_alloc_buffer_size(alloc, buffer);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
end_page_addr =
|
||||
(void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
|
||||
if (end_page_addr > has_page_addr)
|
||||
end_page_addr = has_page_addr;
|
||||
if (binder_update_page_range(alloc, 1,
|
||||
(void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
|
||||
return NULL;
|
||||
|
||||
rb_erase(best_fit, &alloc->free_buffers);
|
||||
buffer->free = 0;
|
||||
binder_insert_allocated_buffer_locked(alloc, buffer);
|
||||
if (buffer_size != size) {
|
||||
struct binder_buffer *new_buffer = (void *)buffer->data + size;
|
||||
|
||||
list_add(&new_buffer->entry, &buffer->entry);
|
||||
new_buffer->free = 1;
|
||||
binder_insert_free_buffer(alloc, new_buffer);
|
||||
}
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: binder_alloc_buf size %zd got %pK\n",
|
||||
alloc->pid, size, buffer);
|
||||
buffer->data_size = data_size;
|
||||
buffer->offsets_size = offsets_size;
|
||||
buffer->async_transaction = is_async;
|
||||
buffer->extra_buffers_size = extra_buffers_size;
|
||||
if (is_async) {
|
||||
alloc->free_async_space -= size + sizeof(struct binder_buffer);
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
|
||||
"%d: binder_alloc_buf size %zd async free %zd\n",
|
||||
alloc->pid, size, alloc->free_async_space);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_new_buf() - Allocate a new binder buffer
|
||||
* @alloc: binder_alloc for this proc
|
||||
* @data_size: size of user data buffer
|
||||
* @offsets_size: user specified buffer offset
|
||||
* @extra_buffers_size: size of extra space for meta-data (eg, security context)
|
||||
* @is_async: buffer for async transaction
|
||||
*
|
||||
* Allocate a new buffer given the requested sizes. Returns
|
||||
* the kernel version of the buffer pointer. The size allocated
|
||||
* is the sum of the three given sizes (each rounded up to
|
||||
* pointer-sized boundary)
|
||||
*
|
||||
* Return: The allocated buffer or %NULL if error
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size,
|
||||
extra_buffers_size, is_async);
|
||||
mutex_unlock(&alloc->mutex);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void *buffer_start_page(struct binder_buffer *buffer)
|
||||
{
|
||||
return (void *)((uintptr_t)buffer & PAGE_MASK);
|
||||
}
|
||||
|
||||
static void *buffer_end_page(struct binder_buffer *buffer)
|
||||
{
|
||||
return (void *)(((uintptr_t)(buffer + 1) - 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;
|
||||
|
||||
BUG_ON(alloc->buffers.next == &buffer->entry);
|
||||
prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
|
||||
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;
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: merge free, buffer %pK share page with %pK\n",
|
||||
alloc->pid, buffer, prev);
|
||||
}
|
||||
|
||||
if (!list_is_last(&buffer->entry, &alloc->buffers)) {
|
||||
next = list_entry(buffer->entry.next,
|
||||
struct binder_buffer, entry);
|
||||
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;
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: merge free, buffer %pK share page with %pK\n",
|
||||
alloc->pid, buffer, prev);
|
||||
}
|
||||
}
|
||||
list_del(&buffer->entry);
|
||||
if (free_page_start || free_page_end) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void binder_free_buf_locked(struct binder_alloc *alloc,
|
||||
struct binder_buffer *buffer)
|
||||
{
|
||||
size_t size, buffer_size;
|
||||
|
||||
buffer_size = binder_alloc_buffer_size(alloc, buffer);
|
||||
|
||||
size = ALIGN(buffer->data_size, sizeof(void *)) +
|
||||
ALIGN(buffer->offsets_size, sizeof(void *)) +
|
||||
ALIGN(buffer->extra_buffers_size, sizeof(void *));
|
||||
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: binder_free_buf %pK size %zd buffer_size %zd\n",
|
||||
alloc->pid, buffer, size, buffer_size);
|
||||
|
||||
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);
|
||||
|
||||
if (buffer->async_transaction) {
|
||||
alloc->free_async_space += size + sizeof(struct binder_buffer);
|
||||
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
|
||||
"%d: binder_free_buf size %zd async free %zd\n",
|
||||
alloc->pid, size, alloc->free_async_space);
|
||||
}
|
||||
|
||||
binder_update_page_range(alloc, 0,
|
||||
(void *)PAGE_ALIGN((uintptr_t)buffer->data),
|
||||
(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
|
||||
NULL);
|
||||
|
||||
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);
|
||||
|
||||
if (next->free) {
|
||||
rb_erase(&next->rb_node, &alloc->free_buffers);
|
||||
binder_delete_free_buffer(alloc, next);
|
||||
}
|
||||
}
|
||||
if (alloc->buffers.next != &buffer->entry) {
|
||||
struct binder_buffer *prev = list_entry(buffer->entry.prev,
|
||||
struct binder_buffer, entry);
|
||||
|
||||
if (prev->free) {
|
||||
binder_delete_free_buffer(alloc, buffer);
|
||||
rb_erase(&prev->rb_node, &alloc->free_buffers);
|
||||
buffer = prev;
|
||||
}
|
||||
}
|
||||
binder_insert_free_buffer(alloc, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_free_buf() - free a binder buffer
|
||||
* @alloc: binder_alloc for this proc
|
||||
* @buffer: kernel pointer to buffer
|
||||
*
|
||||
* Free the buffer allocated via binder_alloc_new_buffer()
|
||||
*/
|
||||
void binder_alloc_free_buf(struct binder_alloc *alloc,
|
||||
struct binder_buffer *buffer)
|
||||
{
|
||||
mutex_lock(&alloc->mutex);
|
||||
binder_free_buf_locked(alloc, buffer);
|
||||
mutex_unlock(&alloc->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_mmap_handler() - map virtual address space for proc
|
||||
* @alloc: alloc structure for this proc
|
||||
* @vma: vma passed to mmap()
|
||||
*
|
||||
* Called by binder_mmap() to initialize the space specified in
|
||||
* vma for allocating binder buffers
|
||||
*
|
||||
* Return:
|
||||
* 0 = success
|
||||
* -EBUSY = address space already mapped
|
||||
* -ENOMEM = failed to map memory to given address space
|
||||
*/
|
||||
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
struct vm_struct *area;
|
||||
const char *failure_string;
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
mutex_lock(&binder_alloc_mmap_lock);
|
||||
if (alloc->buffer) {
|
||||
ret = -EBUSY;
|
||||
failure_string = "already mapped";
|
||||
goto err_already_mapped;
|
||||
}
|
||||
|
||||
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
|
||||
if (area == NULL) {
|
||||
ret = -ENOMEM;
|
||||
failure_string = "get_vm_area";
|
||||
goto err_get_vm_area_failed;
|
||||
}
|
||||
alloc->buffer = area->addr;
|
||||
alloc->user_buffer_offset =
|
||||
vma->vm_start - (uintptr_t)alloc->buffer;
|
||||
mutex_unlock(&binder_alloc_mmap_lock);
|
||||
|
||||
#ifdef CONFIG_CPU_CACHE_VIPT
|
||||
if (cache_is_vipt_aliasing()) {
|
||||
while (CACHE_COLOUR(
|
||||
(vma->vm_start ^ (uint32_t)alloc->buffer))) {
|
||||
pr_info("%s: %d %lx-%lx maps %pK bad alignment\n",
|
||||
__func__, alloc->pid, vma->vm_start,
|
||||
vma->vm_end, alloc->buffer);
|
||||
vma->vm_start += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
|
||||
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
|
||||
GFP_KERNEL);
|
||||
if (alloc->pages == NULL) {
|
||||
ret = -ENOMEM;
|
||||
failure_string = "alloc page array";
|
||||
goto err_alloc_pages_failed;
|
||||
}
|
||||
alloc->buffer_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
if (binder_update_page_range(alloc, 1, alloc->buffer,
|
||||
alloc->buffer + PAGE_SIZE, vma)) {
|
||||
ret = -ENOMEM;
|
||||
failure_string = "alloc small buf";
|
||||
goto err_alloc_small_buf_failed;
|
||||
}
|
||||
buffer = alloc->buffer;
|
||||
INIT_LIST_HEAD(&alloc->buffers);
|
||||
list_add(&buffer->entry, &alloc->buffers);
|
||||
buffer->free = 1;
|
||||
binder_insert_free_buffer(alloc, buffer);
|
||||
alloc->free_async_space = alloc->buffer_size / 2;
|
||||
barrier();
|
||||
alloc->vma = vma;
|
||||
alloc->vma_vm_mm = vma->vm_mm;
|
||||
|
||||
return 0;
|
||||
|
||||
err_alloc_small_buf_failed:
|
||||
kfree(alloc->pages);
|
||||
alloc->pages = NULL;
|
||||
err_alloc_pages_failed:
|
||||
mutex_lock(&binder_alloc_mmap_lock);
|
||||
vfree(alloc->buffer);
|
||||
alloc->buffer = NULL;
|
||||
err_get_vm_area_failed:
|
||||
err_already_mapped:
|
||||
mutex_unlock(&binder_alloc_mmap_lock);
|
||||
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
|
||||
alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void binder_alloc_deferred_release(struct binder_alloc *alloc)
|
||||
{
|
||||
struct rb_node *n;
|
||||
int buffers, page_count;
|
||||
|
||||
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 */
|
||||
BUG_ON(buffer->transaction);
|
||||
|
||||
binder_free_buf_locked(alloc, buffer);
|
||||
buffers++;
|
||||
}
|
||||
|
||||
page_count = 0;
|
||||
if (alloc->pages) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
|
||||
void *page_addr;
|
||||
|
||||
if (!alloc->pages[i])
|
||||
continue;
|
||||
|
||||
page_addr = alloc->buffer + i * PAGE_SIZE;
|
||||
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%s: %d: page %d at %pK not freed\n",
|
||||
__func__, alloc->pid, i, page_addr);
|
||||
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
|
||||
__free_page(alloc->pages[i]);
|
||||
page_count++;
|
||||
}
|
||||
kfree(alloc->pages);
|
||||
vfree(alloc->buffer);
|
||||
}
|
||||
mutex_unlock(&alloc->mutex);
|
||||
|
||||
binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,
|
||||
"%s: %d buffers %d, pages %d\n",
|
||||
__func__, alloc->pid, buffers, page_count);
|
||||
}
|
||||
|
||||
static void print_binder_buffer(struct seq_file *m, const char *prefix,
|
||||
struct binder_buffer *buffer)
|
||||
{
|
||||
seq_printf(m, "%s %d: %pK size %zd:%zd %s\n",
|
||||
prefix, buffer->debug_id, buffer->data,
|
||||
buffer->data_size, buffer->offsets_size,
|
||||
buffer->transaction ? "active" : "delivered");
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_print_allocated() - print buffer info
|
||||
* @m: seq_file for output via seq_printf()
|
||||
* @alloc: binder_alloc for this proc
|
||||
*
|
||||
* Prints information about every buffer associated with
|
||||
* the binder_alloc state to the given seq_file
|
||||
*/
|
||||
void binder_alloc_print_allocated(struct seq_file *m,
|
||||
struct binder_alloc *alloc)
|
||||
{
|
||||
struct rb_node *n;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
|
||||
print_binder_buffer(m, " buffer",
|
||||
rb_entry(n, struct binder_buffer, rb_node));
|
||||
mutex_unlock(&alloc->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_get_allocated_count() - return count of buffers
|
||||
* @alloc: binder_alloc for this proc
|
||||
*
|
||||
* Return: count of allocated buffers
|
||||
*/
|
||||
int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
|
||||
{
|
||||
struct rb_node *n;
|
||||
int count = 0;
|
||||
|
||||
mutex_lock(&alloc->mutex);
|
||||
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
|
||||
count++;
|
||||
mutex_unlock(&alloc->mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* binder_alloc_vma_close() - invalidate address space
|
||||
* @alloc: binder_alloc for this proc
|
||||
*
|
||||
* Called from binder_vma_close() when releasing address space.
|
||||
* Clears alloc->vma to prevent new incoming transactions from
|
||||
* allocating more buffers.
|
||||
*/
|
||||
void binder_alloc_vma_close(struct binder_alloc *alloc)
|
||||
{
|
||||
WRITE_ONCE(alloc->vma, NULL);
|
||||
WRITE_ONCE(alloc->vma_vm_mm, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_init() - called by binder_open() for per-proc initialization
|
||||
* @alloc: binder_alloc for this proc
|
||||
*
|
||||
* Called from binder_open() to initialize binder_alloc fields for
|
||||
* new binder proc
|
||||
*/
|
||||
void binder_alloc_init(struct binder_alloc *alloc)
|
||||
{
|
||||
alloc->tsk = current->group_leader;
|
||||
alloc->pid = current->group_leader->pid;
|
||||
mutex_init(&alloc->mutex);
|
||||
}
|
||||
|
162
drivers/android/binder_alloc.h
Normal file
162
drivers/android/binder_alloc.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_BINDER_ALLOC_H
|
||||
#define _LINUX_BINDER_ALLOC_H
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/rtmutex.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct binder_transaction;
|
||||
|
||||
/**
|
||||
* struct binder_buffer - buffer used for binder transactions
|
||||
* @entry: entry alloc->buffers
|
||||
* @rb_node: node for allocated_buffers/free_buffers rb trees
|
||||
* @free: true if buffer is free
|
||||
* @allow_user_free: describe the second member of struct blah,
|
||||
* @async_transaction: describe the second member of struct blah,
|
||||
* @debug_id: describe the second member of struct blah,
|
||||
* @transaction: describe the second member of struct blah,
|
||||
* @target_node: describe the second member of struct blah,
|
||||
* @data_size: describe the second member of struct blah,
|
||||
* @offsets_size: describe the second member of struct blah,
|
||||
* @extra_buffers_size: describe the second member of struct blah,
|
||||
* @data:i describe the second member of struct blah,
|
||||
*
|
||||
* Bookkeeping structure for binder transaction buffers
|
||||
*/
|
||||
struct binder_buffer {
|
||||
struct list_head entry; /* free and allocated entries by address */
|
||||
struct rb_node rb_node; /* free entry by size or allocated entry */
|
||||
/* by address */
|
||||
unsigned free:1;
|
||||
unsigned allow_user_free:1;
|
||||
unsigned async_transaction:1;
|
||||
unsigned debug_id:29;
|
||||
|
||||
struct binder_transaction *transaction;
|
||||
|
||||
struct binder_node *target_node;
|
||||
size_t data_size;
|
||||
size_t offsets_size;
|
||||
size_t extra_buffers_size;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_alloc - per-binder proc state for binder allocator
|
||||
* @vma: vm_area_struct passed to mmap_handler
|
||||
* (invarient after mmap)
|
||||
* @tsk: tid for task that called init for this proc
|
||||
* (invariant after init)
|
||||
* @vma_vm_mm: copy of vma->vm_mm (invarient after mmap)
|
||||
* @buffer: base of per-proc address space mapped via mmap
|
||||
* @user_buffer_offset: offset between user and kernel VAs for buffer
|
||||
* @buffers: list of all buffers for this proc
|
||||
* @free_buffers: rb tree of buffers available for allocation
|
||||
* sorted by size
|
||||
* @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
|
||||
* @buffer_size: size of address space specified via mmap
|
||||
* @pid: pid for associated binder_proc (invariant after init)
|
||||
*
|
||||
* Bookkeeping structure for per-proc address space management for binder
|
||||
* buffers. It is normally initialized during binder_init() and binder_mmap()
|
||||
* calls. The address space is used for both user-visible buffers and for
|
||||
* struct binder_buffer objects used to track the user buffers
|
||||
*/
|
||||
struct binder_alloc {
|
||||
struct mutex mutex;
|
||||
struct task_struct *tsk;
|
||||
struct vm_area_struct *vma;
|
||||
struct mm_struct *vma_vm_mm;
|
||||
void *buffer;
|
||||
ptrdiff_t user_buffer_offset;
|
||||
struct list_head buffers;
|
||||
struct rb_root free_buffers;
|
||||
struct rb_root allocated_buffers;
|
||||
size_t free_async_space;
|
||||
struct page **pages;
|
||||
size_t buffer_size;
|
||||
uint32_t buffer_free;
|
||||
int pid;
|
||||
};
|
||||
|
||||
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);
|
||||
extern void binder_alloc_vma_close(struct binder_alloc *alloc);
|
||||
extern struct binder_buffer *
|
||||
binder_alloc_buffer_lookup(struct binder_alloc *alloc,
|
||||
uintptr_t user_ptr);
|
||||
extern void binder_alloc_free_buf(struct binder_alloc *alloc,
|
||||
struct binder_buffer *buffer);
|
||||
extern int binder_alloc_mmap_handler(struct binder_alloc *alloc,
|
||||
struct vm_area_struct *vma);
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_alloc_get_user_buffer_offset() - get offset between kernel/user addrs
|
||||
* @alloc: binder_alloc for this proc
|
||||
*
|
||||
* Return: the offset between kernel and user-space addresses to use for
|
||||
* virtual address conversion
|
||||
*/
|
||||
static inline ptrdiff_t
|
||||
binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc)
|
||||
{
|
||||
/*
|
||||
* user_buffer_offset is constant if vma is set and
|
||||
* undefined if vma is not set. It is possible to
|
||||
* get here with !alloc->vma if the target process
|
||||
* is dying while a transaction is being initiated.
|
||||
* Returning the old value is ok in this case and
|
||||
* the transaction will fail.
|
||||
*/
|
||||
return alloc->user_buffer_offset;
|
||||
}
|
||||
|
||||
#endif /* _LINUX_BINDER_ALLOC_H */
|
||||
|
Loading…
Reference in New Issue
Block a user