FROMLIST: binder: add spinlock to protect binder_node
(from https://patchwork.kernel.org/patch/9817769/) node->node_lock is used to protect elements of node. No need to acquire for fields that are invariant: debug_id, ptr, cookie. Change-Id: Ib7738e52fa7689767f17136e18cc05ff548b5717 Signed-off-by: Todd Kjos <tkjos@google.com>
This commit is contained in:
parent
57628830c3
commit
14c312e926
1 changed files with 165 additions and 55 deletions
|
@ -318,6 +318,7 @@ struct binder_error {
|
||||||
* @proc: binder_proc that owns this node
|
* @proc: binder_proc that owns this node
|
||||||
* (invariant after initialized)
|
* (invariant after initialized)
|
||||||
* @refs: list of references on this node
|
* @refs: list of references on this node
|
||||||
|
* (protected by @lock)
|
||||||
* @internal_strong_refs: used to take strong references when
|
* @internal_strong_refs: used to take strong references when
|
||||||
* initiating a transaction
|
* initiating a transaction
|
||||||
* (protected by @proc->inner_lock if @proc
|
* (protected by @proc->inner_lock if @proc
|
||||||
|
@ -351,6 +352,7 @@ struct binder_error {
|
||||||
* (protected by @proc->inner_lock if @proc
|
* (protected by @proc->inner_lock if @proc
|
||||||
* and by @lock)
|
* and by @lock)
|
||||||
* @has_async_transaction: async transaction to node in progress
|
* @has_async_transaction: async transaction to node in progress
|
||||||
|
* (protected by @lock)
|
||||||
* @accept_fds: file descriptor operations supported for node
|
* @accept_fds: file descriptor operations supported for node
|
||||||
* (invariant after initialized)
|
* (invariant after initialized)
|
||||||
* @min_priority: minimum scheduling priority
|
* @min_priority: minimum scheduling priority
|
||||||
|
@ -432,6 +434,7 @@ struct binder_ref_data {
|
||||||
* @rb_node_desc: node for lookup by @data.desc in proc's rb_tree
|
* @rb_node_desc: node for lookup by @data.desc in proc's rb_tree
|
||||||
* @rb_node_node: node for lookup by @node in proc's rb_tree
|
* @rb_node_node: node for lookup by @node in proc's rb_tree
|
||||||
* @node_entry: list entry for node->refs list in target node
|
* @node_entry: list entry for node->refs list in target node
|
||||||
|
* (protected by @node->lock)
|
||||||
* @proc: binder_proc containing ref
|
* @proc: binder_proc containing ref
|
||||||
* @node: binder_node of target node. When cleaning up a
|
* @node: binder_node of target node. When cleaning up a
|
||||||
* ref for deletion in binder_cleanup_ref, a non-NULL
|
* ref for deletion in binder_cleanup_ref, a non-NULL
|
||||||
|
@ -707,6 +710,43 @@ _binder_node_unlock(struct binder_node *node, int line)
|
||||||
spin_unlock(&node->lock);
|
spin_unlock(&node->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binder_node_inner_lock() - Acquire node and inner locks
|
||||||
|
* @node: struct binder_node to acquire
|
||||||
|
*
|
||||||
|
* Acquires node->lock. If node->proc also acquires
|
||||||
|
* proc->inner_lock. Used to protect binder_node fields
|
||||||
|
*/
|
||||||
|
#define binder_node_inner_lock(node) _binder_node_inner_lock(node, __LINE__)
|
||||||
|
static void
|
||||||
|
_binder_node_inner_lock(struct binder_node *node, int line)
|
||||||
|
{
|
||||||
|
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||||
|
"%s: line=%d\n", __func__, line);
|
||||||
|
spin_lock(&node->lock);
|
||||||
|
if (node->proc)
|
||||||
|
binder_inner_proc_lock(node->proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binder_node_unlock() - Release node and inner locks
|
||||||
|
* @node: struct binder_node to acquire
|
||||||
|
*
|
||||||
|
* Release lock acquired via binder_node_lock()
|
||||||
|
*/
|
||||||
|
#define binder_node_inner_unlock(node) _binder_node_inner_unlock(node, __LINE__)
|
||||||
|
static void
|
||||||
|
_binder_node_inner_unlock(struct binder_node *node, int line)
|
||||||
|
{
|
||||||
|
struct binder_proc *proc = node->proc;
|
||||||
|
|
||||||
|
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||||
|
"%s: line=%d\n", __func__, line);
|
||||||
|
if (proc)
|
||||||
|
binder_inner_proc_unlock(proc);
|
||||||
|
spin_unlock(&node->lock);
|
||||||
|
}
|
||||||
|
|
||||||
static bool binder_worklist_empty_ilocked(struct list_head *list)
|
static bool binder_worklist_empty_ilocked(struct list_head *list)
|
||||||
{
|
{
|
||||||
return list_empty(list);
|
return list_empty(list);
|
||||||
|
@ -925,12 +965,14 @@ static struct binder_node *binder_get_node(struct binder_proc *proc,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct binder_node *binder_new_node(struct binder_proc *proc,
|
static struct binder_node *binder_new_node(struct binder_proc *proc,
|
||||||
binder_uintptr_t ptr,
|
struct flat_binder_object *fp)
|
||||||
binder_uintptr_t cookie)
|
|
||||||
{
|
{
|
||||||
struct rb_node **p = &proc->nodes.rb_node;
|
struct rb_node **p = &proc->nodes.rb_node;
|
||||||
struct rb_node *parent = NULL;
|
struct rb_node *parent = NULL;
|
||||||
struct binder_node *node;
|
struct binder_node *node;
|
||||||
|
binder_uintptr_t ptr = fp ? fp->binder : 0;
|
||||||
|
binder_uintptr_t cookie = fp ? fp->cookie : 0;
|
||||||
|
__u32 flags = fp ? fp->flags : 0;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
parent = *p;
|
parent = *p;
|
||||||
|
@ -956,6 +998,8 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
|
||||||
node->ptr = ptr;
|
node->ptr = ptr;
|
||||||
node->cookie = cookie;
|
node->cookie = cookie;
|
||||||
node->work.type = BINDER_WORK_NODE;
|
node->work.type = BINDER_WORK_NODE;
|
||||||
|
node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
|
||||||
|
node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
|
||||||
spin_lock_init(&node->lock);
|
spin_lock_init(&node->lock);
|
||||||
INIT_LIST_HEAD(&node->work.entry);
|
INIT_LIST_HEAD(&node->work.entry);
|
||||||
INIT_LIST_HEAD(&node->async_todo);
|
INIT_LIST_HEAD(&node->async_todo);
|
||||||
|
@ -972,12 +1016,15 @@ static void binder_free_node(struct binder_node *node)
|
||||||
binder_stats_deleted(BINDER_STAT_NODE);
|
binder_stats_deleted(BINDER_STAT_NODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int binder_inc_node_ilocked(struct binder_node *node, int strong,
|
static int binder_inc_node_nilocked(struct binder_node *node, int strong,
|
||||||
int internal,
|
int internal,
|
||||||
struct list_head *target_list)
|
struct list_head *target_list)
|
||||||
{
|
{
|
||||||
if (node->proc)
|
struct binder_proc *proc = node->proc;
|
||||||
BUG_ON(!spin_is_locked(&node->proc->inner_lock));
|
|
||||||
|
BUG_ON(!spin_is_locked(&node->lock));
|
||||||
|
if (proc)
|
||||||
|
BUG_ON(!spin_is_locked(&proc->inner_lock));
|
||||||
if (strong) {
|
if (strong) {
|
||||||
if (internal) {
|
if (internal) {
|
||||||
if (target_list == NULL &&
|
if (target_list == NULL &&
|
||||||
|
@ -1017,20 +1064,19 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (node->proc)
|
binder_node_inner_lock(node);
|
||||||
binder_inner_proc_lock(node->proc);
|
ret = binder_inc_node_nilocked(node, strong, internal, target_list);
|
||||||
ret = binder_inc_node_ilocked(node, strong, internal, target_list);
|
binder_node_inner_unlock(node);
|
||||||
if (node->proc)
|
|
||||||
binder_inner_proc_unlock(node->proc);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool binder_dec_node_ilocked(struct binder_node *node,
|
static bool binder_dec_node_nilocked(struct binder_node *node,
|
||||||
int strong, int internal)
|
int strong, int internal)
|
||||||
{
|
{
|
||||||
struct binder_proc *proc = node->proc;
|
struct binder_proc *proc = node->proc;
|
||||||
|
|
||||||
|
BUG_ON(!spin_is_locked(&node->lock));
|
||||||
if (proc)
|
if (proc)
|
||||||
BUG_ON(!spin_is_locked(&proc->inner_lock));
|
BUG_ON(!spin_is_locked(&proc->inner_lock));
|
||||||
if (strong) {
|
if (strong) {
|
||||||
|
@ -1089,12 +1135,9 @@ static void binder_dec_node(struct binder_node *node, int strong, int internal)
|
||||||
{
|
{
|
||||||
bool free_node;
|
bool free_node;
|
||||||
|
|
||||||
if (node->proc)
|
binder_node_inner_lock(node);
|
||||||
binder_inner_proc_lock(node->proc);
|
free_node = binder_dec_node_nilocked(node, strong, internal);
|
||||||
free_node = binder_dec_node_ilocked(node, strong, internal);
|
binder_node_inner_unlock(node);
|
||||||
if (node->proc)
|
|
||||||
binder_inner_proc_unlock(node->proc);
|
|
||||||
|
|
||||||
if (free_node)
|
if (free_node)
|
||||||
binder_free_node(node);
|
binder_free_node(node);
|
||||||
}
|
}
|
||||||
|
@ -1124,6 +1167,7 @@ static void binder_inc_node_tmpref_ilocked(struct binder_node *node)
|
||||||
*/
|
*/
|
||||||
static void binder_inc_node_tmpref(struct binder_node *node)
|
static void binder_inc_node_tmpref(struct binder_node *node)
|
||||||
{
|
{
|
||||||
|
binder_node_lock(node);
|
||||||
if (node->proc)
|
if (node->proc)
|
||||||
binder_inner_proc_lock(node->proc);
|
binder_inner_proc_lock(node->proc);
|
||||||
else
|
else
|
||||||
|
@ -1133,6 +1177,7 @@ static void binder_inc_node_tmpref(struct binder_node *node)
|
||||||
binder_inner_proc_unlock(node->proc);
|
binder_inner_proc_unlock(node->proc);
|
||||||
else
|
else
|
||||||
spin_unlock(&binder_dead_nodes_lock);
|
spin_unlock(&binder_dead_nodes_lock);
|
||||||
|
binder_node_unlock(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1145,9 +1190,8 @@ static void binder_dec_node_tmpref(struct binder_node *node)
|
||||||
{
|
{
|
||||||
bool free_node;
|
bool free_node;
|
||||||
|
|
||||||
if (node->proc)
|
binder_node_inner_lock(node);
|
||||||
binder_inner_proc_lock(node->proc);
|
if (!node->proc)
|
||||||
else
|
|
||||||
spin_lock(&binder_dead_nodes_lock);
|
spin_lock(&binder_dead_nodes_lock);
|
||||||
node->tmp_refs--;
|
node->tmp_refs--;
|
||||||
BUG_ON(node->tmp_refs < 0);
|
BUG_ON(node->tmp_refs < 0);
|
||||||
|
@ -1159,9 +1203,8 @@ static void binder_dec_node_tmpref(struct binder_node *node)
|
||||||
* causes no actual reference to be released in binder_dec_node().
|
* causes no actual reference to be released in binder_dec_node().
|
||||||
* If that changes, a change is needed here too.
|
* If that changes, a change is needed here too.
|
||||||
*/
|
*/
|
||||||
free_node = binder_dec_node_ilocked(node, 0, 1);
|
free_node = binder_dec_node_nilocked(node, 0, 1);
|
||||||
if (node->proc)
|
binder_node_inner_unlock(node);
|
||||||
binder_inner_proc_unlock(node->proc);
|
|
||||||
if (free_node)
|
if (free_node)
|
||||||
binder_free_node(node);
|
binder_free_node(node);
|
||||||
}
|
}
|
||||||
|
@ -1265,19 +1308,21 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
|
||||||
}
|
}
|
||||||
rb_link_node(&new_ref->rb_node_desc, parent, p);
|
rb_link_node(&new_ref->rb_node_desc, parent, p);
|
||||||
rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
|
rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
|
||||||
|
|
||||||
|
binder_node_lock(node);
|
||||||
hlist_add_head(&new_ref->node_entry, &node->refs);
|
hlist_add_head(&new_ref->node_entry, &node->refs);
|
||||||
|
|
||||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||||
"%d new ref %d desc %d for node %d\n",
|
"%d new ref %d desc %d for node %d\n",
|
||||||
proc->pid, new_ref->data.debug_id, new_ref->data.desc,
|
proc->pid, new_ref->data.debug_id, new_ref->data.desc,
|
||||||
node->debug_id);
|
node->debug_id);
|
||||||
|
binder_node_unlock(node);
|
||||||
return new_ref;
|
return new_ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void binder_cleanup_ref(struct binder_ref *ref)
|
static void binder_cleanup_ref(struct binder_ref *ref)
|
||||||
{
|
{
|
||||||
bool delete_node = false;
|
bool delete_node = false;
|
||||||
struct binder_proc *node_proc = ref->node->proc;
|
|
||||||
|
|
||||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||||
"%d delete ref %d desc %d for node %d\n",
|
"%d delete ref %d desc %d for node %d\n",
|
||||||
|
@ -1287,15 +1332,13 @@ static void binder_cleanup_ref(struct binder_ref *ref)
|
||||||
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
|
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
|
||||||
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
|
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
|
||||||
|
|
||||||
if (node_proc)
|
binder_node_inner_lock(ref->node);
|
||||||
binder_inner_proc_lock(node_proc);
|
|
||||||
if (ref->data.strong)
|
if (ref->data.strong)
|
||||||
binder_dec_node_ilocked(ref->node, 1, 1);
|
binder_dec_node_nilocked(ref->node, 1, 1);
|
||||||
|
|
||||||
hlist_del(&ref->node_entry);
|
hlist_del(&ref->node_entry);
|
||||||
delete_node = binder_dec_node_ilocked(ref->node, 0, 1);
|
delete_node = binder_dec_node_nilocked(ref->node, 0, 1);
|
||||||
if (node_proc)
|
binder_node_inner_unlock(ref->node);
|
||||||
binder_inner_proc_unlock(node_proc);
|
|
||||||
/*
|
/*
|
||||||
* Clear ref->node unless we want the caller to free the node
|
* Clear ref->node unless we want the caller to free the node
|
||||||
*/
|
*/
|
||||||
|
@ -1990,12 +2033,9 @@ static int binder_translate_binder(struct flat_binder_object *fp,
|
||||||
|
|
||||||
node = binder_get_node(proc, fp->binder);
|
node = binder_get_node(proc, fp->binder);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = binder_new_node(proc, fp->binder, fp->cookie);
|
node = binder_new_node(proc, fp);
|
||||||
if (!node)
|
if (!node)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
|
|
||||||
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
|
|
||||||
}
|
}
|
||||||
if (fp->cookie != node->cookie) {
|
if (fp->cookie != node->cookie) {
|
||||||
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
|
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
|
||||||
|
@ -2056,6 +2096,7 @@ static int binder_translate_handle(struct flat_binder_object *fp,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binder_node_lock(node);
|
||||||
if (node->proc == target_proc) {
|
if (node->proc == target_proc) {
|
||||||
if (fp->hdr.type == BINDER_TYPE_HANDLE)
|
if (fp->hdr.type == BINDER_TYPE_HANDLE)
|
||||||
fp->hdr.type = BINDER_TYPE_BINDER;
|
fp->hdr.type = BINDER_TYPE_BINDER;
|
||||||
|
@ -2063,18 +2104,24 @@ static int binder_translate_handle(struct flat_binder_object *fp,
|
||||||
fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
|
fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
|
||||||
fp->binder = node->ptr;
|
fp->binder = node->ptr;
|
||||||
fp->cookie = node->cookie;
|
fp->cookie = node->cookie;
|
||||||
binder_inc_node(node,
|
if (node->proc)
|
||||||
|
binder_inner_proc_lock(node->proc);
|
||||||
|
binder_inc_node_nilocked(node,
|
||||||
fp->hdr.type == BINDER_TYPE_BINDER,
|
fp->hdr.type == BINDER_TYPE_BINDER,
|
||||||
0, NULL);
|
0, NULL);
|
||||||
|
if (node->proc)
|
||||||
|
binder_inner_proc_unlock(node->proc);
|
||||||
trace_binder_transaction_ref_to_node(t, node, &src_rdata);
|
trace_binder_transaction_ref_to_node(t, node, &src_rdata);
|
||||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||||
" ref %d desc %d -> node %d u%016llx\n",
|
" ref %d desc %d -> node %d u%016llx\n",
|
||||||
src_rdata.debug_id, src_rdata.desc, node->debug_id,
|
src_rdata.debug_id, src_rdata.desc, node->debug_id,
|
||||||
(u64)node->ptr);
|
(u64)node->ptr);
|
||||||
|
binder_node_unlock(node);
|
||||||
} else {
|
} else {
|
||||||
int ret;
|
int ret;
|
||||||
struct binder_ref_data dest_rdata;
|
struct binder_ref_data dest_rdata;
|
||||||
|
|
||||||
|
binder_node_unlock(node);
|
||||||
ret = binder_inc_ref_for_node(target_proc, node,
|
ret = binder_inc_ref_for_node(target_proc, node,
|
||||||
fp->hdr.type == BINDER_TYPE_HANDLE,
|
fp->hdr.type == BINDER_TYPE_HANDLE,
|
||||||
NULL, &dest_rdata);
|
NULL, &dest_rdata);
|
||||||
|
@ -2382,13 +2429,16 @@ static void binder_transaction(struct binder_proc *proc,
|
||||||
mutex_unlock(&context->context_mgr_node_lock);
|
mutex_unlock(&context->context_mgr_node_lock);
|
||||||
}
|
}
|
||||||
e->to_node = target_node->debug_id;
|
e->to_node = target_node->debug_id;
|
||||||
|
binder_node_lock(target_node);
|
||||||
target_proc = target_node->proc;
|
target_proc = target_node->proc;
|
||||||
if (target_proc == NULL) {
|
if (target_proc == NULL) {
|
||||||
|
binder_node_unlock(target_node);
|
||||||
return_error = BR_DEAD_REPLY;
|
return_error = BR_DEAD_REPLY;
|
||||||
return_error_line = __LINE__;
|
return_error_line = __LINE__;
|
||||||
goto err_dead_binder;
|
goto err_dead_binder;
|
||||||
}
|
}
|
||||||
target_proc->tmp_ref++;
|
target_proc->tmp_ref++;
|
||||||
|
binder_node_unlock(target_node);
|
||||||
if (security_binder_transaction(proc->tsk,
|
if (security_binder_transaction(proc->tsk,
|
||||||
target_proc->tsk) < 0) {
|
target_proc->tsk) < 0) {
|
||||||
return_error = BR_FAILED_REPLY;
|
return_error = BR_FAILED_REPLY;
|
||||||
|
@ -2705,6 +2755,7 @@ static void binder_transaction(struct binder_proc *proc,
|
||||||
}
|
}
|
||||||
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
|
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
|
||||||
binder_enqueue_work(proc, tcomplete, &thread->todo);
|
binder_enqueue_work(proc, tcomplete, &thread->todo);
|
||||||
|
t->work.type = BINDER_WORK_TRANSACTION;
|
||||||
|
|
||||||
if (reply) {
|
if (reply) {
|
||||||
if (target_thread->is_dead)
|
if (target_thread->is_dead)
|
||||||
|
@ -2712,6 +2763,7 @@ static void binder_transaction(struct binder_proc *proc,
|
||||||
BUG_ON(t->buffer->async_transaction != 0);
|
BUG_ON(t->buffer->async_transaction != 0);
|
||||||
binder_pop_transaction(target_thread, in_reply_to);
|
binder_pop_transaction(target_thread, in_reply_to);
|
||||||
binder_free_transaction(in_reply_to);
|
binder_free_transaction(in_reply_to);
|
||||||
|
binder_enqueue_work(target_proc, &t->work, target_list);
|
||||||
} else if (!(t->flags & TF_ONE_WAY)) {
|
} else if (!(t->flags & TF_ONE_WAY)) {
|
||||||
BUG_ON(t->buffer->async_transaction != 0);
|
BUG_ON(t->buffer->async_transaction != 0);
|
||||||
t->need_reply = 1;
|
t->need_reply = 1;
|
||||||
|
@ -2722,20 +2774,29 @@ static void binder_transaction(struct binder_proc *proc,
|
||||||
binder_pop_transaction(thread, t);
|
binder_pop_transaction(thread, t);
|
||||||
goto err_dead_proc_or_thread;
|
goto err_dead_proc_or_thread;
|
||||||
}
|
}
|
||||||
|
binder_enqueue_work(target_proc, &t->work, target_list);
|
||||||
} else {
|
} else {
|
||||||
BUG_ON(target_node == NULL);
|
BUG_ON(target_node == NULL);
|
||||||
BUG_ON(t->buffer->async_transaction != 1);
|
BUG_ON(t->buffer->async_transaction != 1);
|
||||||
|
binder_node_lock(target_node);
|
||||||
if (target_node->has_async_transaction) {
|
if (target_node->has_async_transaction) {
|
||||||
target_list = &target_node->async_todo;
|
target_list = &target_node->async_todo;
|
||||||
target_wait = NULL;
|
target_wait = NULL;
|
||||||
} else
|
} else
|
||||||
target_node->has_async_transaction = 1;
|
target_node->has_async_transaction = 1;
|
||||||
|
/*
|
||||||
|
* Test/set of has_async_transaction
|
||||||
|
* must be atomic with enqueue on
|
||||||
|
* async_todo
|
||||||
|
*/
|
||||||
if (target_proc->is_dead ||
|
if (target_proc->is_dead ||
|
||||||
(target_thread && target_thread->is_dead))
|
(target_thread && target_thread->is_dead)) {
|
||||||
|
binder_node_unlock(target_node);
|
||||||
goto err_dead_proc_or_thread;
|
goto err_dead_proc_or_thread;
|
||||||
}
|
}
|
||||||
t->work.type = BINDER_WORK_TRANSACTION;
|
|
||||||
binder_enqueue_work(target_proc, &t->work, target_list);
|
binder_enqueue_work(target_proc, &t->work, target_list);
|
||||||
|
binder_node_unlock(target_node);
|
||||||
|
}
|
||||||
if (target_wait) {
|
if (target_wait) {
|
||||||
if (reply || !(tr->flags & TF_ONE_WAY))
|
if (reply || !(tr->flags & TF_ONE_WAY))
|
||||||
wake_up_interruptible_sync(target_wait);
|
wake_up_interruptible_sync(target_wait);
|
||||||
|
@ -2914,6 +2975,7 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
binder_uintptr_t node_ptr;
|
binder_uintptr_t node_ptr;
|
||||||
binder_uintptr_t cookie;
|
binder_uintptr_t cookie;
|
||||||
struct binder_node *node;
|
struct binder_node *node;
|
||||||
|
bool free_node;
|
||||||
|
|
||||||
if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
|
if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -2941,13 +3003,13 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
binder_put_node(node);
|
binder_put_node(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
binder_inner_proc_lock(proc);
|
binder_node_inner_lock(node);
|
||||||
if (cmd == BC_ACQUIRE_DONE) {
|
if (cmd == BC_ACQUIRE_DONE) {
|
||||||
if (node->pending_strong_ref == 0) {
|
if (node->pending_strong_ref == 0) {
|
||||||
binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n",
|
binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n",
|
||||||
proc->pid, thread->pid,
|
proc->pid, thread->pid,
|
||||||
node->debug_id);
|
node->debug_id);
|
||||||
binder_inner_proc_unlock(proc);
|
binder_node_inner_unlock(node);
|
||||||
binder_put_node(node);
|
binder_put_node(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2957,20 +3019,22 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n",
|
binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n",
|
||||||
proc->pid, thread->pid,
|
proc->pid, thread->pid,
|
||||||
node->debug_id);
|
node->debug_id);
|
||||||
binder_inner_proc_unlock(proc);
|
binder_node_inner_unlock(node);
|
||||||
binder_put_node(node);
|
binder_put_node(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
node->pending_weak_ref = 0;
|
node->pending_weak_ref = 0;
|
||||||
}
|
}
|
||||||
binder_inner_proc_unlock(proc);
|
free_node = binder_dec_node_nilocked(node,
|
||||||
binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);
|
cmd == BC_ACQUIRE_DONE, 0);
|
||||||
|
WARN_ON(free_node);
|
||||||
binder_debug(BINDER_DEBUG_USER_REFS,
|
binder_debug(BINDER_DEBUG_USER_REFS,
|
||||||
"%d:%d %s node %d ls %d lw %d tr %d\n",
|
"%d:%d %s node %d ls %d lw %d tr %d\n",
|
||||||
proc->pid, thread->pid,
|
proc->pid, thread->pid,
|
||||||
cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
|
cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
|
||||||
node->debug_id, node->local_strong_refs,
|
node->debug_id, node->local_strong_refs,
|
||||||
node->local_weak_refs, node->tmp_refs);
|
node->local_weak_refs, node->tmp_refs);
|
||||||
|
binder_node_inner_unlock(node);
|
||||||
binder_put_node(node);
|
binder_put_node(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3016,9 +3080,9 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
struct binder_work *w;
|
struct binder_work *w;
|
||||||
|
|
||||||
buf_node = buffer->target_node;
|
buf_node = buffer->target_node;
|
||||||
|
binder_node_inner_lock(buf_node);
|
||||||
BUG_ON(!buf_node->has_async_transaction);
|
BUG_ON(!buf_node->has_async_transaction);
|
||||||
BUG_ON(buf_node->proc != proc);
|
BUG_ON(buf_node->proc != proc);
|
||||||
binder_inner_proc_lock(proc);
|
|
||||||
w = binder_dequeue_work_head_ilocked(
|
w = binder_dequeue_work_head_ilocked(
|
||||||
&buf_node->async_todo);
|
&buf_node->async_todo);
|
||||||
if (!w)
|
if (!w)
|
||||||
|
@ -3026,7 +3090,7 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
else
|
else
|
||||||
binder_enqueue_work_ilocked(
|
binder_enqueue_work_ilocked(
|
||||||
w, &thread->todo);
|
w, &thread->todo);
|
||||||
binder_inner_proc_unlock(proc);
|
binder_node_inner_unlock(buf_node);
|
||||||
}
|
}
|
||||||
trace_binder_transaction_buffer_release(buffer);
|
trace_binder_transaction_buffer_release(buffer);
|
||||||
binder_transaction_buffer_release(proc, buffer, NULL);
|
binder_transaction_buffer_release(proc, buffer, NULL);
|
||||||
|
@ -3151,6 +3215,7 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
INIT_LIST_HEAD(&death->work.entry);
|
INIT_LIST_HEAD(&death->work.entry);
|
||||||
death->cookie = cookie;
|
death->cookie = cookie;
|
||||||
ref->death = death;
|
ref->death = death;
|
||||||
|
binder_node_lock(ref->node);
|
||||||
if (ref->node->proc == NULL) {
|
if (ref->node->proc == NULL) {
|
||||||
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
|
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
|
||||||
if (thread->looper &
|
if (thread->looper &
|
||||||
|
@ -3169,10 +3234,13 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
&proc->wait);
|
&proc->wait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binder_node_unlock(ref->node);
|
||||||
} else {
|
} else {
|
||||||
|
binder_node_lock(ref->node);
|
||||||
if (ref->death == NULL) {
|
if (ref->death == NULL) {
|
||||||
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n",
|
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n",
|
||||||
proc->pid, thread->pid);
|
proc->pid, thread->pid);
|
||||||
|
binder_node_unlock(ref->node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
death = ref->death;
|
death = ref->death;
|
||||||
|
@ -3181,6 +3249,7 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
proc->pid, thread->pid,
|
proc->pid, thread->pid,
|
||||||
(u64)death->cookie,
|
(u64)death->cookie,
|
||||||
(u64)cookie);
|
(u64)cookie);
|
||||||
|
binder_node_unlock(ref->node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ref->death = NULL;
|
ref->death = NULL;
|
||||||
|
@ -3205,6 +3274,7 @@ static int binder_thread_write(struct binder_proc *proc,
|
||||||
death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
|
death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
|
||||||
}
|
}
|
||||||
binder_inner_proc_unlock(proc);
|
binder_inner_proc_unlock(proc);
|
||||||
|
binder_node_unlock(ref->node);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case BC_DEAD_BINDER_DONE: {
|
case BC_DEAD_BINDER_DONE: {
|
||||||
|
@ -3487,6 +3557,17 @@ retry:
|
||||||
(u64)node_cookie);
|
(u64)node_cookie);
|
||||||
rb_erase(&node->rb_node, &proc->nodes);
|
rb_erase(&node->rb_node, &proc->nodes);
|
||||||
binder_inner_proc_unlock(proc);
|
binder_inner_proc_unlock(proc);
|
||||||
|
binder_node_lock(node);
|
||||||
|
/*
|
||||||
|
* Acquire the node lock before freeing the
|
||||||
|
* node to serialize with other threads that
|
||||||
|
* may have been holding the node lock while
|
||||||
|
* decrementing this node (avoids race where
|
||||||
|
* this thread frees while the other thread
|
||||||
|
* is unlocking the node after the final
|
||||||
|
* decrement)
|
||||||
|
*/
|
||||||
|
binder_node_unlock(node);
|
||||||
binder_free_node(node);
|
binder_free_node(node);
|
||||||
} else
|
} else
|
||||||
binder_inner_proc_unlock(proc);
|
binder_inner_proc_unlock(proc);
|
||||||
|
@ -3974,16 +4055,18 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
|
||||||
} else {
|
} else {
|
||||||
context->binder_context_mgr_uid = curr_euid;
|
context->binder_context_mgr_uid = curr_euid;
|
||||||
}
|
}
|
||||||
new_node = binder_new_node(proc, 0, 0);
|
new_node = binder_new_node(proc, NULL);
|
||||||
if (!new_node) {
|
if (!new_node) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
binder_node_lock(new_node);
|
||||||
new_node->local_weak_refs++;
|
new_node->local_weak_refs++;
|
||||||
new_node->local_strong_refs++;
|
new_node->local_strong_refs++;
|
||||||
new_node->has_strong_ref = 1;
|
new_node->has_strong_ref = 1;
|
||||||
new_node->has_weak_ref = 1;
|
new_node->has_weak_ref = 1;
|
||||||
context->binder_context_mgr_node = new_node;
|
context->binder_context_mgr_node = new_node;
|
||||||
|
binder_node_unlock(new_node);
|
||||||
binder_put_node(new_node);
|
binder_put_node(new_node);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&context->context_mgr_node_lock);
|
mutex_unlock(&context->context_mgr_node_lock);
|
||||||
|
@ -4246,6 +4329,7 @@ static int binder_node_release(struct binder_node *node, int refs)
|
||||||
|
|
||||||
binder_release_work(proc, &node->async_todo);
|
binder_release_work(proc, &node->async_todo);
|
||||||
|
|
||||||
|
binder_node_lock(node);
|
||||||
binder_inner_proc_lock(proc);
|
binder_inner_proc_lock(proc);
|
||||||
binder_dequeue_work_ilocked(&node->work);
|
binder_dequeue_work_ilocked(&node->work);
|
||||||
/*
|
/*
|
||||||
|
@ -4254,6 +4338,7 @@ static int binder_node_release(struct binder_node *node, int refs)
|
||||||
BUG_ON(!node->tmp_refs);
|
BUG_ON(!node->tmp_refs);
|
||||||
if (hlist_empty(&node->refs) && node->tmp_refs == 1) {
|
if (hlist_empty(&node->refs) && node->tmp_refs == 1) {
|
||||||
binder_inner_proc_unlock(proc);
|
binder_inner_proc_unlock(proc);
|
||||||
|
binder_node_unlock(node);
|
||||||
binder_free_node(node);
|
binder_free_node(node);
|
||||||
|
|
||||||
return refs;
|
return refs;
|
||||||
|
@ -4290,6 +4375,7 @@ static int binder_node_release(struct binder_node *node, int refs)
|
||||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||||
"node %d now dead, refs %d, death %d\n",
|
"node %d now dead, refs %d, death %d\n",
|
||||||
node->debug_id, refs, death);
|
node->debug_id, refs, death);
|
||||||
|
binder_node_unlock(node);
|
||||||
binder_put_node(node);
|
binder_put_node(node);
|
||||||
|
|
||||||
return refs;
|
return refs;
|
||||||
|
@ -4533,12 +4619,15 @@ static void print_binder_thread_ilocked(struct seq_file *m,
|
||||||
m->count = start_pos;
|
m->count = start_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_binder_node(struct seq_file *m, struct binder_node *node)
|
static void print_binder_node_nlocked(struct seq_file *m,
|
||||||
|
struct binder_node *node)
|
||||||
{
|
{
|
||||||
struct binder_ref *ref;
|
struct binder_ref *ref;
|
||||||
struct binder_work *w;
|
struct binder_work *w;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
|
WARN_ON(!spin_is_locked(&node->lock));
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
hlist_for_each_entry(ref, &node->refs, node_entry)
|
hlist_for_each_entry(ref, &node->refs, node_entry)
|
||||||
count++;
|
count++;
|
||||||
|
@ -4565,11 +4654,13 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
|
||||||
|
|
||||||
static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
|
static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
|
||||||
{
|
{
|
||||||
|
binder_node_lock(ref->node);
|
||||||
seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
|
seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
|
||||||
ref->data.debug_id, ref->data.desc,
|
ref->data.debug_id, ref->data.desc,
|
||||||
ref->node->proc ? "" : "dead ",
|
ref->node->proc ? "" : "dead ",
|
||||||
ref->node->debug_id, ref->data.strong,
|
ref->node->debug_id, ref->data.strong,
|
||||||
ref->data.weak, ref->death);
|
ref->data.weak, ref->death);
|
||||||
|
binder_node_unlock(ref->node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_binder_proc(struct seq_file *m,
|
static void print_binder_proc(struct seq_file *m,
|
||||||
|
@ -4592,8 +4683,10 @@ static void print_binder_proc(struct seq_file *m,
|
||||||
for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
|
for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
|
||||||
struct binder_node *node = rb_entry(n, struct binder_node,
|
struct binder_node *node = rb_entry(n, struct binder_node,
|
||||||
rb_node);
|
rb_node);
|
||||||
|
binder_node_lock(node);
|
||||||
if (print_all || node->has_async_transaction)
|
if (print_all || node->has_async_transaction)
|
||||||
print_binder_node(m, node);
|
print_binder_node_nlocked(m, node);
|
||||||
|
binder_node_unlock(node);
|
||||||
}
|
}
|
||||||
if (print_all) {
|
if (print_all) {
|
||||||
for (n = rb_first(&proc->refs_by_desc);
|
for (n = rb_first(&proc->refs_by_desc);
|
||||||
|
@ -4765,6 +4858,7 @@ static int binder_state_show(struct seq_file *m, void *unused)
|
||||||
{
|
{
|
||||||
struct binder_proc *proc;
|
struct binder_proc *proc;
|
||||||
struct binder_node *node;
|
struct binder_node *node;
|
||||||
|
struct binder_node *last_node = NULL;
|
||||||
|
|
||||||
binder_lock(__func__);
|
binder_lock(__func__);
|
||||||
|
|
||||||
|
@ -4773,9 +4867,25 @@ static int binder_state_show(struct seq_file *m, void *unused)
|
||||||
spin_lock(&binder_dead_nodes_lock);
|
spin_lock(&binder_dead_nodes_lock);
|
||||||
if (!hlist_empty(&binder_dead_nodes))
|
if (!hlist_empty(&binder_dead_nodes))
|
||||||
seq_puts(m, "dead nodes:\n");
|
seq_puts(m, "dead nodes:\n");
|
||||||
hlist_for_each_entry(node, &binder_dead_nodes, dead_node)
|
hlist_for_each_entry(node, &binder_dead_nodes, dead_node) {
|
||||||
print_binder_node(m, node);
|
/*
|
||||||
|
* take a temporary reference on the node so it
|
||||||
|
* survives and isn't removed from the list
|
||||||
|
* while we print it.
|
||||||
|
*/
|
||||||
|
node->tmp_refs++;
|
||||||
spin_unlock(&binder_dead_nodes_lock);
|
spin_unlock(&binder_dead_nodes_lock);
|
||||||
|
if (last_node)
|
||||||
|
binder_put_node(last_node);
|
||||||
|
binder_node_lock(node);
|
||||||
|
print_binder_node_nlocked(m, node);
|
||||||
|
binder_node_unlock(node);
|
||||||
|
last_node = node;
|
||||||
|
spin_lock(&binder_dead_nodes_lock);
|
||||||
|
}
|
||||||
|
spin_unlock(&binder_dead_nodes_lock);
|
||||||
|
if (last_node)
|
||||||
|
binder_put_node(last_node);
|
||||||
|
|
||||||
mutex_lock(&binder_procs_lock);
|
mutex_lock(&binder_procs_lock);
|
||||||
hlist_for_each_entry(proc, &binder_procs, proc_node)
|
hlist_for_each_entry(proc, &binder_procs, proc_node)
|
||||||
|
|
Loading…
Add table
Reference in a new issue