binder: introduce locking helper functions
There are 3 main spinlocks which must be acquired in this order: 1) proc->outer_lock : protects most fields of binder_proc, binder_thread, and binder_ref structures. binder_proc_lock() and binder_proc_unlock() are used to acq/rel. 2) node->lock : protects most fields of binder_node. binder_node_lock() and binder_node_unlock() are used to acq/rel 3) proc->inner_lock : protects the thread and node lists (proc->threads, proc->nodes) and all todo lists associated with the binder_proc (proc->todo, thread->todo, proc->delivered_death and node->async_todo). binder_inner_proc_lock() and binder_inner_proc_unlock() are used to acq/rel Any lock under procA must never be nested under any lock at the same level or below on procB. Functions that require a lock held on entry indicate which lock in the suffix of the function name: foo_olocked() : requires node->outer_lock foo_nlocked() : requires node->lock foo_ilocked() : requires proc->inner_lock foo_iolocked(): requires proc->outer_lock and proc->inner_lock foo_nilocked(): requires node->lock and proc->inner_lock Signed-off-by: Todd Kjos <tkjos@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
adc1884222
commit
9630fe8839
|
@ -15,6 +15,39 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Locking overview
|
||||
*
|
||||
* There are 3 main spinlocks which must be acquired in the
|
||||
* order shown:
|
||||
*
|
||||
* 1) proc->outer_lock : protects binder_ref
|
||||
* binder_proc_lock() and binder_proc_unlock() are
|
||||
* used to acq/rel.
|
||||
* 2) node->lock : protects most fields of binder_node.
|
||||
* binder_node_lock() and binder_node_unlock() are
|
||||
* used to acq/rel
|
||||
* 3) proc->inner_lock : protects the thread and node lists
|
||||
* (proc->threads, proc->nodes) and all todo lists associated
|
||||
* with the binder_proc (proc->todo, thread->todo,
|
||||
* proc->delivered_death and node->async_todo).
|
||||
* binder_inner_proc_lock() and binder_inner_proc_unlock()
|
||||
* are used to acq/rel
|
||||
*
|
||||
* Any lock under procA must never be nested under any lock at the same
|
||||
* level or below on procB.
|
||||
*
|
||||
* Functions that require a lock held on entry indicate which lock
|
||||
* in the suffix of the function name:
|
||||
*
|
||||
* foo_olocked() : requires node->outer_lock
|
||||
* foo_nlocked() : requires node->lock
|
||||
* foo_ilocked() : requires proc->inner_lock
|
||||
* foo_oilocked(): requires proc->outer_lock and proc->inner_lock
|
||||
* foo_nilocked(): requires node->lock and proc->inner_lock
|
||||
* ...
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
@ -36,6 +69,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
|
||||
#define BINDER_IPC_32BIT 1
|
||||
|
@ -106,6 +140,7 @@ enum {
|
|||
BINDER_DEBUG_FREE_BUFFER = 1U << 11,
|
||||
BINDER_DEBUG_INTERNAL_REFS = 1U << 12,
|
||||
BINDER_DEBUG_PRIORITY_CAP = 1U << 13,
|
||||
BINDER_DEBUG_SPINLOCKS = 1U << 14,
|
||||
};
|
||||
static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR |
|
||||
BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;
|
||||
|
@ -262,8 +297,43 @@ struct binder_error {
|
|||
uint32_t cmd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_node - binder node bookkeeping
|
||||
* @debug_id: unique ID for debugging
|
||||
* (invariant after initialized)
|
||||
* @lock: lock for node fields
|
||||
* @work: worklist element for node work
|
||||
* @rb_node: element for proc->nodes tree
|
||||
* @dead_node: element for binder_dead_nodes list
|
||||
* (protected by binder_dead_nodes_lock)
|
||||
* @proc: binder_proc that owns this node
|
||||
* (invariant after initialized)
|
||||
* @refs: list of references on this node
|
||||
* @internal_strong_refs: used to take strong references when
|
||||
* initiating a transaction
|
||||
* @local_weak_refs: weak user refs from local process
|
||||
* @local_strong_refs: strong user refs from local process
|
||||
* @tmp_refs: temporary kernel refs
|
||||
* @ptr: userspace pointer for node
|
||||
* (invariant, no lock needed)
|
||||
* @cookie: userspace cookie for node
|
||||
* (invariant, no lock needed)
|
||||
* @has_strong_ref: userspace notified of strong ref
|
||||
* @pending_strong_ref: userspace has acked notification of strong ref
|
||||
* @has_weak_ref: userspace notified of weak ref
|
||||
* @pending_weak_ref: userspace has acked notification of weak ref
|
||||
* @has_async_transaction: async transaction to node in progress
|
||||
* @accept_fds: file descriptor operations supported for node
|
||||
* (invariant after initialized)
|
||||
* @min_priority: minimum scheduling priority
|
||||
* (invariant after initialized)
|
||||
* @async_todo: list of async work items
|
||||
*
|
||||
* Bookkeeping structure for binder nodes.
|
||||
*/
|
||||
struct binder_node {
|
||||
int debug_id;
|
||||
spinlock_t lock;
|
||||
struct binder_work work;
|
||||
union {
|
||||
struct rb_node rb_node;
|
||||
|
@ -346,6 +416,51 @@ enum binder_deferred_state {
|
|||
BINDER_DEFERRED_RELEASE = 0x04,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_proc - binder process bookkeeping
|
||||
* @proc_node: element for binder_procs list
|
||||
* @threads: rbtree of binder_threads in this proc
|
||||
* @nodes: rbtree of binder nodes associated with
|
||||
* this proc ordered by node->ptr
|
||||
* @refs_by_desc: rbtree of refs ordered by ref->desc
|
||||
* @refs_by_node: rbtree of refs ordered by ref->node
|
||||
* @pid PID of group_leader of process
|
||||
* (invariant after initialized)
|
||||
* @tsk task_struct for group_leader of process
|
||||
* (invariant after initialized)
|
||||
* @files files_struct for process
|
||||
* (invariant after initialized)
|
||||
* @deferred_work_node: element for binder_deferred_list
|
||||
* (protected by binder_deferred_lock)
|
||||
* @deferred_work: bitmap of deferred work to perform
|
||||
* (protected by binder_deferred_lock)
|
||||
* @is_dead: process is dead and awaiting free
|
||||
* when outstanding transactions are cleaned up
|
||||
* @todo: list of work for this process
|
||||
* @wait: wait queue head to wait for proc work
|
||||
* (invariant after initialized)
|
||||
* @stats: per-process binder statistics
|
||||
* (atomics, no lock needed)
|
||||
* @delivered_death: list of delivered death notification
|
||||
* @max_threads: cap on number of binder threads
|
||||
* @requested_threads: number of binder threads requested but not
|
||||
* yet started. In current implementation, can
|
||||
* only be 0 or 1.
|
||||
* @requested_threads_started: number binder threads started
|
||||
* @ready_threads: number of threads waiting for proc work
|
||||
* @tmp_ref: temporary reference to indicate proc is in use
|
||||
* @default_priority: default scheduler priority
|
||||
* (invariant after initialized)
|
||||
* @debugfs_entry: debugfs node
|
||||
* @alloc: binder allocator bookkeeping
|
||||
* @context: binder_context for this proc
|
||||
* (invariant after initialized)
|
||||
* @inner_lock: can nest under outer_lock and/or node lock
|
||||
* @outer_lock: no nesting under innor or node lock
|
||||
* Lock order: 1) outer, 2) node, 3) inner
|
||||
*
|
||||
* Bookkeeping structure for binder processes
|
||||
*/
|
||||
struct binder_proc {
|
||||
struct hlist_node proc_node;
|
||||
struct rb_root threads;
|
||||
|
@ -372,6 +487,8 @@ struct binder_proc {
|
|||
struct dentry *debugfs_entry;
|
||||
struct binder_alloc alloc;
|
||||
struct binder_context *context;
|
||||
spinlock_t inner_lock;
|
||||
spinlock_t outer_lock;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -382,6 +499,33 @@ enum {
|
|||
BINDER_LOOPER_STATE_WAITING = 0x10,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_thread - binder thread bookkeeping
|
||||
* @proc: binder process for this thread
|
||||
* (invariant after initialization)
|
||||
* @rb_node: element for proc->threads rbtree
|
||||
* @pid: PID for this thread
|
||||
* (invariant after initialization)
|
||||
* @looper: bitmap of looping state
|
||||
* (only accessed by this thread)
|
||||
* @looper_needs_return: looping thread needs to exit driver
|
||||
* (no lock needed)
|
||||
* @transaction_stack: stack of in-progress transactions for this thread
|
||||
* @todo: list of work to do for this thread
|
||||
* @return_error: transaction errors reported by this thread
|
||||
* (only accessed by this thread)
|
||||
* @reply_error: transaction errors reported by target thread
|
||||
* @wait: wait queue for thread work
|
||||
* @stats: per-thread statistics
|
||||
* (atomics, no lock needed)
|
||||
* @tmp_ref: temporary reference to indicate thread is in use
|
||||
* (atomic since @proc->inner_lock cannot
|
||||
* always be acquired)
|
||||
* @is_dead: thread is dead and awaiting free
|
||||
* when outstanding transactions are cleaned up
|
||||
*
|
||||
* Bookkeeping structure for binder threads.
|
||||
*/
|
||||
struct binder_thread {
|
||||
struct binder_proc *proc;
|
||||
struct rb_node rb_node;
|
||||
|
@ -424,6 +568,97 @@ struct binder_transaction {
|
|||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* binder_proc_lock() - Acquire outer lock for given binder_proc
|
||||
* @proc: struct binder_proc to acquire
|
||||
*
|
||||
* Acquires proc->outer_lock. Used to protect binder_ref
|
||||
* structures associated with the given proc.
|
||||
*/
|
||||
#define binder_proc_lock(proc) _binder_proc_lock(proc, __LINE__)
|
||||
static void
|
||||
_binder_proc_lock(struct binder_proc *proc, int line)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_lock(&proc->outer_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_proc_unlock() - Release spinlock for given binder_proc
|
||||
* @proc: struct binder_proc to acquire
|
||||
*
|
||||
* Release lock acquired via binder_proc_lock()
|
||||
*/
|
||||
#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__)
|
||||
static void
|
||||
_binder_proc_unlock(struct binder_proc *proc, int line)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_unlock(&proc->outer_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_inner_proc_lock() - Acquire inner lock for given binder_proc
|
||||
* @proc: struct binder_proc to acquire
|
||||
*
|
||||
* Acquires proc->inner_lock. Used to protect todo lists
|
||||
*/
|
||||
#define binder_inner_proc_lock(proc) _binder_inner_proc_lock(proc, __LINE__)
|
||||
static void
|
||||
_binder_inner_proc_lock(struct binder_proc *proc, int line)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_lock(&proc->inner_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_inner_proc_unlock() - Release inner lock for given binder_proc
|
||||
* @proc: struct binder_proc to acquire
|
||||
*
|
||||
* Release lock acquired via binder_inner_proc_lock()
|
||||
*/
|
||||
#define binder_inner_proc_unlock(proc) _binder_inner_proc_unlock(proc, __LINE__)
|
||||
static void
|
||||
_binder_inner_proc_unlock(struct binder_proc *proc, int line)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_unlock(&proc->inner_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_node_lock() - Acquire spinlock for given binder_node
|
||||
* @node: struct binder_node to acquire
|
||||
*
|
||||
* Acquires node->lock. Used to protect binder_node fields
|
||||
*/
|
||||
#define binder_node_lock(node) _binder_node_lock(node, __LINE__)
|
||||
static void
|
||||
_binder_node_lock(struct binder_node *node, int line)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_lock(&node->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_node_unlock() - Release spinlock for given binder_proc
|
||||
* @node: struct binder_node to acquire
|
||||
*
|
||||
* Release lock acquired via binder_node_lock()
|
||||
*/
|
||||
#define binder_node_unlock(node) _binder_node_unlock(node, __LINE__)
|
||||
static void
|
||||
_binder_node_unlock(struct binder_node *node, int line)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_unlock(&node->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
|
||||
static void binder_free_thread(struct binder_thread *thread);
|
||||
|
@ -568,6 +803,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
|
|||
node->ptr = ptr;
|
||||
node->cookie = cookie;
|
||||
node->work.type = BINDER_WORK_NODE;
|
||||
spin_lock_init(&node->lock);
|
||||
INIT_LIST_HEAD(&node->work.entry);
|
||||
INIT_LIST_HEAD(&node->async_todo);
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
|
@ -3599,6 +3835,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
|
|||
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
|
||||
if (proc == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock_init(&proc->inner_lock);
|
||||
spin_lock_init(&proc->outer_lock);
|
||||
get_task_struct(current->group_leader);
|
||||
proc->tsk = current->group_leader;
|
||||
INIT_LIST_HEAD(&proc->todo);
|
||||
|
|
Loading…
Reference in New Issue