Commit 72196393 authored by Todd Kjos's avatar Todd Kjos Committed by Greg Kroah-Hartman

binder: add spinlocks to protect todo lists

The todo lists in the proc, thread, and node structures
are accessed by other procs/threads to place work
items on the queue.

The todo lists are protected by the new proc->inner_lock.
No locks should ever be nested under these locks. As the
name suggests, an outer lock will be introduced in
a later patch.
Signed-off-by: default avatarTodd Kjos <tkjos@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ed29721e
...@@ -279,8 +279,16 @@ struct binder_device { ...@@ -279,8 +279,16 @@ struct binder_device {
struct binder_context context; struct binder_context context;
}; };
/**
* struct binder_work - work enqueued on a worklist
* @entry: node enqueued on list
* @type: type of work to be performed
*
* There are separate work lists for proc, thread, and node (async).
*/
struct binder_work { struct binder_work {
struct list_head entry; struct list_head entry;
enum { enum {
BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE, BINDER_WORK_TRANSACTION_COMPLETE,
...@@ -303,6 +311,7 @@ struct binder_error { ...@@ -303,6 +311,7 @@ struct binder_error {
* (invariant after initialized) * (invariant after initialized)
* @lock: lock for node fields * @lock: lock for node fields
* @work: worklist element for node work * @work: worklist element for node work
* (protected by @proc->inner_lock)
* @rb_node: element for proc->nodes tree * @rb_node: element for proc->nodes tree
* @dead_node: element for binder_dead_nodes list * @dead_node: element for binder_dead_nodes list
* (protected by binder_dead_nodes_lock) * (protected by binder_dead_nodes_lock)
...@@ -347,6 +356,7 @@ struct binder_error { ...@@ -347,6 +356,7 @@ struct binder_error {
* @min_priority: minimum scheduling priority * @min_priority: minimum scheduling priority
* (invariant after initialized) * (invariant after initialized)
* @async_todo: list of async work items * @async_todo: list of async work items
* (protected by @proc->inner_lock)
* *
* Bookkeeping structure for binder nodes. * Bookkeeping structure for binder nodes.
*/ */
...@@ -388,6 +398,11 @@ struct binder_node { ...@@ -388,6 +398,11 @@ struct binder_node {
}; };
struct binder_ref_death { struct binder_ref_death {
/**
* @work: worklist element for death notifications
* (protected by inner_lock of the proc that
* this ref belongs to)
*/
struct binder_work work; struct binder_work work;
binder_uintptr_t cookie; binder_uintptr_t cookie;
}; };
...@@ -467,11 +482,13 @@ enum binder_deferred_state { ...@@ -467,11 +482,13 @@ enum binder_deferred_state {
* @is_dead: process is dead and awaiting free * @is_dead: process is dead and awaiting free
* when outstanding transactions are cleaned up * when outstanding transactions are cleaned up
* @todo: list of work for this process * @todo: list of work for this process
* (protected by @inner_lock)
* @wait: wait queue head to wait for proc work * @wait: wait queue head to wait for proc work
* (invariant after initialized) * (invariant after initialized)
* @stats: per-process binder statistics * @stats: per-process binder statistics
* (atomics, no lock needed) * (atomics, no lock needed)
* @delivered_death: list of delivered death notification * @delivered_death: list of delivered death notification
* (protected by @inner_lock)
* @max_threads: cap on number of binder threads * @max_threads: cap on number of binder threads
* @requested_threads: number of binder threads requested but not * @requested_threads: number of binder threads requested but not
* yet started. In current implementation, can * yet started. In current implementation, can
...@@ -542,6 +559,7 @@ enum { ...@@ -542,6 +559,7 @@ enum {
* (no lock needed) * (no lock needed)
* @transaction_stack: stack of in-progress transactions for this thread * @transaction_stack: stack of in-progress transactions for this thread
* @todo: list of work to do for this thread * @todo: list of work to do for this thread
* (protected by @proc->inner_lock)
* @return_error: transaction errors reported by this thread * @return_error: transaction errors reported by this thread
* (only accessed by this thread) * (only accessed by this thread)
* @reply_error: transaction errors reported by target thread * @reply_error: transaction errors reported by target thread
...@@ -689,6 +707,111 @@ _binder_node_unlock(struct binder_node *node, int line) ...@@ -689,6 +707,111 @@ _binder_node_unlock(struct binder_node *node, int line)
spin_unlock(&node->lock); spin_unlock(&node->lock);
} }
static bool binder_worklist_empty_ilocked(struct list_head *list)
{
return list_empty(list);
}
/**
* binder_worklist_empty() - Check if no items on the work list
* @proc: binder_proc associated with list
* @list: list to check
*
* Return: true if there are no items on list, else false
*/
static bool binder_worklist_empty(struct binder_proc *proc,
struct list_head *list)
{
bool ret;
binder_inner_proc_lock(proc);
ret = binder_worklist_empty_ilocked(list);
binder_inner_proc_unlock(proc);
return ret;
}
static void
binder_enqueue_work_ilocked(struct binder_work *work,
struct list_head *target_list)
{
BUG_ON(target_list == NULL);
BUG_ON(work->entry.next && !list_empty(&work->entry));
list_add_tail(&work->entry, target_list);
}
/**
* binder_enqueue_work() - Add an item to the work list
* @proc: binder_proc associated with list
* @work: struct binder_work to add to list
* @target_list: list to add work to
*
* Adds the work to the specified list. Asserts that work
* is not already on a list.
*/
static void
binder_enqueue_work(struct binder_proc *proc,
struct binder_work *work,
struct list_head *target_list)
{
binder_inner_proc_lock(proc);
binder_enqueue_work_ilocked(work, target_list);
binder_inner_proc_unlock(proc);
}
static void
binder_dequeue_work_ilocked(struct binder_work *work)
{
list_del_init(&work->entry);
}
/**
* binder_dequeue_work() - Removes an item from the work list
* @proc: binder_proc associated with list
* @work: struct binder_work to remove from list
*
* Removes the specified work item from whatever list it is on.
* Can safely be called if work is not on any list.
*/
static void
binder_dequeue_work(struct binder_proc *proc, struct binder_work *work)
{
binder_inner_proc_lock(proc);
binder_dequeue_work_ilocked(work);
binder_inner_proc_unlock(proc);
}
static struct binder_work *binder_dequeue_work_head_ilocked(
struct list_head *list)
{
struct binder_work *w;
w = list_first_entry_or_null(list, struct binder_work, entry);
if (w)
list_del_init(&w->entry);
return w;
}
/**
* binder_dequeue_work_head() - Dequeues the item at head of list
* @proc: binder_proc associated with list
* @list: list to dequeue head
*
* Removes the head of the list if there are items on the list
*
* Return: pointer dequeued binder_work, NULL if list was empty
*/
static struct binder_work *binder_dequeue_work_head(
struct binder_proc *proc,
struct list_head *list)
{
struct binder_work *w;
binder_inner_proc_lock(proc);
w = binder_dequeue_work_head_ilocked(list);
binder_inner_proc_unlock(proc);
return w;
}
static void static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer); binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
static void binder_free_thread(struct binder_thread *thread); static void binder_free_thread(struct binder_thread *thread);
...@@ -870,8 +993,8 @@ static int binder_inc_node_ilocked(struct binder_node *node, int strong, ...@@ -870,8 +993,8 @@ static int binder_inc_node_ilocked(struct binder_node *node, int strong,
} else } else
node->local_strong_refs++; node->local_strong_refs++;
if (!node->has_strong_ref && target_list) { if (!node->has_strong_ref && target_list) {
list_del_init(&node->work.entry); binder_dequeue_work_ilocked(&node->work);
list_add_tail(&node->work.entry, target_list); binder_enqueue_work_ilocked(&node->work, target_list);
} }
} else { } else {
if (!internal) if (!internal)
...@@ -882,7 +1005,7 @@ static int binder_inc_node_ilocked(struct binder_node *node, int strong, ...@@ -882,7 +1005,7 @@ static int binder_inc_node_ilocked(struct binder_node *node, int strong,
node->debug_id); node->debug_id);
return -EINVAL; return -EINVAL;
} }
list_add_tail(&node->work.entry, target_list); binder_enqueue_work_ilocked(&node->work, target_list);
} }
} }
return 0; return 0;
...@@ -926,19 +1049,20 @@ static bool binder_dec_node_ilocked(struct binder_node *node, ...@@ -926,19 +1049,20 @@ static bool binder_dec_node_ilocked(struct binder_node *node,
if (proc && (node->has_strong_ref || node->has_weak_ref)) { if (proc && (node->has_strong_ref || node->has_weak_ref)) {
if (list_empty(&node->work.entry)) { if (list_empty(&node->work.entry)) {
list_add_tail(&node->work.entry, &node->proc->todo); binder_enqueue_work_ilocked(&node->work, &proc->todo);
wake_up_interruptible(&node->proc->wait); wake_up_interruptible(&node->proc->wait);
} }
} else { } else {
if (hlist_empty(&node->refs) && !node->local_strong_refs && if (hlist_empty(&node->refs) && !node->local_strong_refs &&
!node->local_weak_refs && !node->tmp_refs) { !node->local_weak_refs && !node->tmp_refs) {
list_del_init(&node->work.entry);
if (proc) { if (proc) {
rb_erase(&node->rb_node, &node->proc->nodes); binder_dequeue_work_ilocked(&node->work);
rb_erase(&node->rb_node, &proc->nodes);
binder_debug(BINDER_DEBUG_INTERNAL_REFS, binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"refless node %d deleted\n", "refless node %d deleted\n",
node->debug_id); node->debug_id);
} else { } else {
BUG_ON(!list_empty(&node->work.entry));
spin_lock(&binder_dead_nodes_lock); spin_lock(&binder_dead_nodes_lock);
/* /*
* tmp_refs could have changed so * tmp_refs could have changed so
...@@ -1188,7 +1312,7 @@ static void binder_cleanup_ref(struct binder_ref *ref) ...@@ -1188,7 +1312,7 @@ static void binder_cleanup_ref(struct binder_ref *ref)
"%d delete ref %d desc %d has death notification\n", "%d delete ref %d desc %d has death notification\n",
ref->proc->pid, ref->data.debug_id, ref->proc->pid, ref->data.debug_id,
ref->data.desc); ref->data.desc);
list_del(&ref->death->work.entry); binder_dequeue_work(ref->proc, &ref->death->work);
binder_stats_deleted(BINDER_STAT_DEATH); binder_stats_deleted(BINDER_STAT_DEATH);
} }
binder_stats_deleted(BINDER_STAT_REF); binder_stats_deleted(BINDER_STAT_REF);
...@@ -1539,8 +1663,9 @@ static void binder_send_failed_reply(struct binder_transaction *t, ...@@ -1539,8 +1663,9 @@ static void binder_send_failed_reply(struct binder_transaction *t,
binder_pop_transaction(target_thread, t); binder_pop_transaction(target_thread, t);
if (target_thread->reply_error.cmd == BR_OK) { if (target_thread->reply_error.cmd == BR_OK) {
target_thread->reply_error.cmd = error_code; target_thread->reply_error.cmd = error_code;
list_add_tail( binder_enqueue_work(
&target_thread->reply_error.work.entry, target_thread->proc,
&target_thread->reply_error.work,
&target_thread->todo); &target_thread->todo);
wake_up_interruptible(&target_thread->wait); wake_up_interruptible(&target_thread->wait);
} else { } else {
...@@ -2578,7 +2703,7 @@ static void binder_transaction(struct binder_proc *proc, ...@@ -2578,7 +2703,7 @@ static void binder_transaction(struct binder_proc *proc,
} }
} }
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo); binder_enqueue_work(proc, tcomplete, &thread->todo);
if (reply) { if (reply) {
if (target_thread->is_dead) if (target_thread->is_dead)
...@@ -2609,7 +2734,7 @@ static void binder_transaction(struct binder_proc *proc, ...@@ -2609,7 +2734,7 @@ static void binder_transaction(struct binder_proc *proc,
goto err_dead_proc_or_thread; goto err_dead_proc_or_thread;
} }
t->work.type = BINDER_WORK_TRANSACTION; t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list); binder_enqueue_work(target_proc, &t->work, target_list);
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);
...@@ -2685,12 +2810,14 @@ static void binder_transaction(struct binder_proc *proc, ...@@ -2685,12 +2810,14 @@ static void binder_transaction(struct binder_proc *proc,
BUG_ON(thread->return_error.cmd != BR_OK); BUG_ON(thread->return_error.cmd != BR_OK);
if (in_reply_to) { if (in_reply_to) {
thread->return_error.cmd = BR_TRANSACTION_COMPLETE; thread->return_error.cmd = BR_TRANSACTION_COMPLETE;
list_add_tail(&thread->return_error.work.entry, binder_enqueue_work(thread->proc,
&thread->return_error.work,
&thread->todo); &thread->todo);
binder_send_failed_reply(in_reply_to, return_error); binder_send_failed_reply(in_reply_to, return_error);
} else { } else {
thread->return_error.cmd = return_error; thread->return_error.cmd = return_error;
list_add_tail(&thread->return_error.work.entry, binder_enqueue_work(thread->proc,
&thread->return_error.work,
&thread->todo); &thread->todo);
} }
} }
...@@ -2884,11 +3011,21 @@ static int binder_thread_write(struct binder_proc *proc, ...@@ -2884,11 +3011,21 @@ static int binder_thread_write(struct binder_proc *proc,
buffer->transaction = NULL; buffer->transaction = NULL;
} }
if (buffer->async_transaction && buffer->target_node) { if (buffer->async_transaction && buffer->target_node) {
BUG_ON(!buffer->target_node->has_async_transaction); struct binder_node *buf_node;
if (list_empty(&buffer->target_node->async_todo)) struct binder_work *w;
buffer->target_node->has_async_transaction = 0;
buf_node = buffer->target_node;
BUG_ON(!buf_node->has_async_transaction);
BUG_ON(buf_node->proc != proc);
binder_inner_proc_lock(proc);
w = binder_dequeue_work_head_ilocked(
&buf_node->async_todo);
if (!w)
buf_node->has_async_transaction = 0;
else else
list_move_tail(buffer->target_node->async_todo.next, &thread->todo); binder_enqueue_work_ilocked(
w, &thread->todo);
binder_inner_proc_unlock(proc);
} }
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);
...@@ -3000,8 +3137,9 @@ static int binder_thread_write(struct binder_proc *proc, ...@@ -3000,8 +3137,9 @@ static int binder_thread_write(struct binder_proc *proc,
WARN_ON(thread->return_error.cmd != WARN_ON(thread->return_error.cmd !=
BR_OK); BR_OK);
thread->return_error.cmd = BR_ERROR; thread->return_error.cmd = BR_ERROR;
list_add_tail( binder_enqueue_work(
&thread->return_error.work.entry, thread->proc,
&thread->return_error.work,
&thread->todo); &thread->todo);
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
"%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
...@@ -3014,11 +3152,20 @@ static int binder_thread_write(struct binder_proc *proc, ...@@ -3014,11 +3152,20 @@ static int binder_thread_write(struct binder_proc *proc,
ref->death = death; ref->death = death;
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 & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { if (thread->looper &
list_add_tail(&ref->death->work.entry, &thread->todo); (BINDER_LOOPER_STATE_REGISTERED |
} else { BINDER_LOOPER_STATE_ENTERED))
list_add_tail(&ref->death->work.entry, &proc->todo); binder_enqueue_work(
wake_up_interruptible(&proc->wait); proc,
&ref->death->work,
&thread->todo);
else {
binder_enqueue_work(
proc,
&ref->death->work,
&proc->todo);
wake_up_interruptible(
&proc->wait);
} }
} }
} else { } else {
...@@ -3036,18 +3183,27 @@ static int binder_thread_write(struct binder_proc *proc, ...@@ -3036,18 +3183,27 @@ static int binder_thread_write(struct binder_proc *proc,
break; break;
} }
ref->death = NULL; ref->death = NULL;
binder_inner_proc_lock(proc);
if (list_empty(&death->work.entry)) { if (list_empty(&death->work.entry)) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { if (thread->looper &
list_add_tail(&death->work.entry, &thread->todo); (BINDER_LOOPER_STATE_REGISTERED |
} else { BINDER_LOOPER_STATE_ENTERED))
list_add_tail(&death->work.entry, &proc->todo); binder_enqueue_work_ilocked(
wake_up_interruptible(&proc->wait); &death->work,
&thread->todo);
else {
binder_enqueue_work_ilocked(
&death->work,
&proc->todo);
wake_up_interruptible(
&proc->wait);
} }
} else { } else {
BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER); BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);
death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR; death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
} }
binder_inner_proc_unlock(proc);
} }
} break; } break;
case BC_DEAD_BINDER_DONE: { case BC_DEAD_BINDER_DONE: {
...@@ -3059,8 +3215,13 @@ static int binder_thread_write(struct binder_proc *proc, ...@@ -3059,8 +3215,13 @@ static int binder_thread_write(struct binder_proc *proc,
return -EFAULT; return -EFAULT;
ptr += sizeof(cookie); ptr += sizeof(cookie);
list_for_each_entry(w, &proc->delivered_death, entry) { binder_inner_proc_lock(proc);
struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work); list_for_each_entry(w, &proc->delivered_death,
entry) {
struct binder_ref_death *tmp_death =
container_of(w,
struct binder_ref_death,
work);
if (tmp_death->cookie == cookie) { if (tmp_death->cookie == cookie) {
death = tmp_death; death = tmp_death;
...@@ -3074,19 +3235,25 @@ static int binder_thread_write(struct binder_proc *proc, ...@@ -3074,19 +3235,25 @@ static int binder_thread_write(struct binder_proc *proc,
if (death == NULL) { if (death == NULL) {
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n", binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n",
proc->pid, thread->pid, (u64)cookie); proc->pid, thread->pid, (u64)cookie);
binder_inner_proc_unlock(proc);
break; break;
} }
binder_dequeue_work_ilocked(&death->work);
list_del_init(&death->work.entry);
if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) { if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { if (thread->looper &
list_add_tail(&death->work.entry, &thread->todo); (BINDER_LOOPER_STATE_REGISTERED |
} else { BINDER_LOOPER_STATE_ENTERED))
list_add_tail(&death->work.entry, &proc->todo); binder_enqueue_work_ilocked(
&death->work, &thread->todo);
else {
binder_enqueue_work_ilocked(
&death->work,
&proc->todo);
wake_up_interruptible(&proc->wait); wake_up_interruptible(&proc->wait);
} }
} }
binder_inner_proc_unlock(proc);
} break; } break;
default: default:
...@@ -3113,12 +3280,14 @@ static void binder_stat_br(struct binder_proc *proc, ...@@ -3113,12 +3280,14 @@ static void binder_stat_br(struct binder_proc *proc,
static int binder_has_proc_work(struct binder_proc *proc, static int binder_has_proc_work(struct binder_proc *proc,
struct binder_thread *thread) struct binder_thread *thread)
{ {
return !list_empty(&proc->todo) || thread->looper_need_return; return !binder_worklist_empty(proc, &proc->todo) ||
thread->looper_need_return;
} }
static int binder_has_thread_work(struct binder_thread *thread) static int binder_has_thread_work(struct binder_thread *thread)
{ {
return !list_empty(&thread->todo) || thread->looper_need_return; return !binder_worklist_empty(thread->proc, &thread->todo) ||
thread->looper_need_return;
} }
static int binder_put_node_cmd(struct binder_proc *proc, static int binder_put_node_cmd(struct binder_proc *proc,
...@@ -3172,7 +3341,7 @@ static int binder_thread_read(struct binder_proc *proc, ...@@ -3172,7 +3341,7 @@ static int binder_thread_read(struct binder_proc *proc,
retry: retry:
wait_for_proc_work = thread->transaction_stack == NULL && wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo); binder_worklist_empty(proc, &thread->todo);
thread->looper |= BINDER_LOOPER_STATE_WAITING; thread->looper |= BINDER_LOOPER_STATE_WAITING;
if (wait_for_proc_work) if (wait_for_proc_work)
...@@ -3182,7 +3351,7 @@ static int binder_thread_read(struct binder_proc *proc, ...@@ -3182,7 +3351,7 @@ static int binder_thread_read(struct binder_proc *proc,
trace_binder_wait_for_work(wait_for_proc_work, trace_binder_wait_for_work(wait_for_proc_work,
!!thread->transaction_stack, !!thread->transaction_stack,
!list_empty(&thread->todo)); !binder_worklist_empty(proc, &thread->todo));
if (wait_for_proc_work) { if (wait_for_proc_work) {
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) { BINDER_LOOPER_STATE_ENTERED))) {
...@@ -3217,18 +3386,20 @@ static int binder_thread_read(struct binder_proc *proc, ...@@ -3217,18 +3386,20 @@ static int binder_thread_read(struct binder_proc *proc,
while (1) { while (1) {
uint32_t cmd; uint32_t cmd;
struct binder_transaction_data tr; struct binder_transaction_data tr;
struct binder_work *w; struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL; struct binder_transaction *t = NULL;
struct binder_thread *t_from; struct binder_thread *t_from;
binder_inner_proc_lock(proc); binder_inner_proc_lock(proc);
if (!list_empty(&thread->todo)) { if (!binder_worklist_empty_ilocked(&thread->todo))
w = list_first_entry(&thread->todo, struct binder_work, list = &thread->todo;
entry); else if (!binder_worklist_empty_ilocked(&proc->todo) &&
} else if (!list_empty(&proc->todo) && wait_for_proc_work) { wait_for_proc_work)
w = list_first_entry(&proc->todo, struct binder_work, list = &proc->todo;
entry); else {
} else { binder_inner_proc_unlock(proc);
/* no data added */ /* no data added */
if (ptr - buffer == 4 && !thread->looper_need_return) if (ptr - buffer == 4 && !thread->looper_need_return)
goto retry; goto retry;
...@@ -3239,7 +3410,7 @@ static int binder_thread_read(struct binder_proc *proc, ...@@ -3239,7 +3410,7 @@ static int binder_thread_read(struct binder_proc *proc,
binder_inner_proc_unlock(proc); binder_inner_proc_unlock(proc);
break; break;
} }
list_del_init(&w->entry); w = binder_dequeue_work_head_ilocked(list);
switch (w->type) { switch (w->type) {
case BINDER_WORK_TRANSACTION: { case BINDER_WORK_TRANSACTION: {
...@@ -3388,8 +3559,8 @@ static int binder_thread_read(struct binder_proc *proc, ...@@ -3388,8 +3559,8 @@ static int binder_thread_read(struct binder_proc *proc,
binder_stats_deleted(BINDER_STAT_DEATH); binder_stats_deleted(BINDER_STAT_DEATH);
} else { } else {
binder_inner_proc_lock(proc); binder_inner_proc_lock(proc);
list_add_tail(&w->entry, binder_enqueue_work_ilocked(
&proc->delivered_death); w, &proc->delivered_death);
binder_inner_proc_unlock(proc); binder_inner_proc_unlock(proc);
} }
if (cmd == BR_DEAD_BINDER) if (cmd == BR_DEAD_BINDER)
...@@ -3499,13 +3670,16 @@ static int binder_thread_read(struct binder_proc *proc, ...@@ -3499,13 +3670,16 @@ static int binder_thread_read(struct binder_proc *proc,
return 0; return 0;
} }
static void binder_release_work(struct list_head *list) static void binder_release_work(struct binder_proc *proc,
struct list_head *list)
{ {
struct binder_work *w; struct binder_work *w;
while (!list_empty(list)) { while (1) {
w = list_first_entry(list, struct binder_work, entry); w = binder_dequeue_work_head(proc, list);
list_del_init(&w->entry); if (!w)
return;
switch (w->type) { switch (w->type) {
case BINDER_WORK_TRANSACTION: { case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t; struct binder_transaction *t;
...@@ -3669,7 +3843,7 @@ static int binder_thread_release(struct binder_proc *proc, ...@@ -3669,7 +3843,7 @@ static int binder_thread_release(struct binder_proc *proc,
if (send_reply) if (send_reply)
binder_send_failed_reply(send_reply, BR_DEAD_REPLY); binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
binder_release_work(&thread->todo); binder_release_work(proc, &thread->todo);
binder_thread_dec_tmpref(thread); binder_thread_dec_tmpref(thread);
return active_transactions; return active_transactions;
} }
...@@ -3686,7 +3860,7 @@ static unsigned int binder_poll(struct file *filp, ...@@ -3686,7 +3860,7 @@ static unsigned int binder_poll(struct file *filp,
thread = binder_get_thread(proc); thread = binder_get_thread(proc);
wait_for_proc_work = thread->transaction_stack == NULL && wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo); binder_worklist_empty(proc, &thread->todo);
binder_unlock(__func__); binder_unlock(__func__);
...@@ -3749,7 +3923,7 @@ static int binder_ioctl_write_read(struct file *filp, ...@@ -3749,7 +3923,7 @@ static int binder_ioctl_write_read(struct file *filp,
&bwr.read_consumed, &bwr.read_consumed,
filp->f_flags & O_NONBLOCK); filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret); trace_binder_read_done(ret);
if (!list_empty(&proc->todo)) if (!binder_worklist_empty(proc, &proc->todo))
wake_up_interruptible(&proc->wait); wake_up_interruptible(&proc->wait);
if (ret < 0) { if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
...@@ -4069,10 +4243,10 @@ static int binder_node_release(struct binder_node *node, int refs) ...@@ -4069,10 +4243,10 @@ static int binder_node_release(struct binder_node *node, int refs)
int death = 0; int death = 0;
struct binder_proc *proc = node->proc; struct binder_proc *proc = node->proc;
binder_release_work(&node->async_todo); binder_release_work(proc, &node->async_todo);
binder_inner_proc_lock(proc); binder_inner_proc_lock(proc);
list_del_init(&node->work.entry); binder_dequeue_work_ilocked(&node->work);
/* /*
* The caller must have taken a temporary ref on the node, * The caller must have taken a temporary ref on the node,
*/ */
...@@ -4101,13 +4275,15 @@ static int binder_node_release(struct binder_node *node, int refs) ...@@ -4101,13 +4275,15 @@ static int binder_node_release(struct binder_node *node, int refs)
death++; death++;
binder_inner_proc_lock(ref->proc);
if (list_empty(&ref->death->work.entry)) { if (list_empty(&ref->death->work.entry)) {
ref->death->work.type = BINDER_WORK_DEAD_BINDER; ref->death->work.type = BINDER_WORK_DEAD_BINDER;
list_add_tail(&ref->death->work.entry, binder_enqueue_work_ilocked(&ref->death->work,
&ref->proc->todo); &ref->proc->todo);
wake_up_interruptible(&ref->proc->wait); wake_up_interruptible(&ref->proc->wait);
} else } else
BUG(); BUG();
binder_inner_proc_unlock(ref->proc);
} }
binder_debug(BINDER_DEBUG_DEAD_BINDER, binder_debug(BINDER_DEBUG_DEAD_BINDER,
...@@ -4183,8 +4359,8 @@ static void binder_deferred_release(struct binder_proc *proc) ...@@ -4183,8 +4359,8 @@ static void binder_deferred_release(struct binder_proc *proc)
binder_free_ref(ref); binder_free_ref(ref);
} }
binder_release_work(&proc->todo); binder_release_work(proc, &proc->todo);
binder_release_work(&proc->delivered_death); binder_release_work(proc, &proc->delivered_death);
binder_debug(BINDER_DEBUG_OPEN_CLOSE, binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n", "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n",
...@@ -4275,7 +4451,7 @@ static void print_binder_transaction(struct seq_file *m, const char *prefix, ...@@ -4275,7 +4451,7 @@ static void print_binder_transaction(struct seq_file *m, const char *prefix,
t->buffer->data); t->buffer->data);
} }
static void print_binder_work(struct seq_file *m, const char *prefix, static void print_binder_work_ilocked(struct seq_file *m, const char *prefix,
const char *transaction_prefix, const char *transaction_prefix,
struct binder_work *w) struct binder_work *w)
{ {
...@@ -4318,7 +4494,7 @@ static void print_binder_work(struct seq_file *m, const char *prefix, ...@@ -4318,7 +4494,7 @@ static void print_binder_work(struct seq_file *m, const char *prefix,
} }
} }
static void print_binder_thread(struct seq_file *m, static void print_binder_thread_ilocked(struct seq_file *m,
struct binder_thread *thread, struct binder_thread *thread,
int print_always) int print_always)
{ {
...@@ -4327,6 +4503,7 @@ static void print_binder_thread(struct seq_file *m, ...@@ -4327,6 +4503,7 @@ static void print_binder_thread(struct seq_file *m,
size_t start_pos = m->count; size_t start_pos = m->count;
size_t header_pos; size_t header_pos;
WARN_ON(!spin_is_locked(&thread->proc->inner_lock));
seq_printf(m, " thread %d: l %02x need_return %d tr %d\n", seq_printf(m, " thread %d: l %02x need_return %d tr %d\n",
thread->pid, thread->looper, thread->pid, thread->looper,
thread->looper_need_return, thread->looper_need_return,
...@@ -4348,7 +4525,8 @@ static void print_binder_thread(struct seq_file *m, ...@@ -4348,7 +4525,8 @@ static void print_binder_thread(struct seq_file *m,
} }
} }
list_for_each_entry(w, &thread->todo, entry) { list_for_each_entry(w, &thread->todo, entry) {
print_binder_work(m, " ", " pending transaction", w); print_binder_work_ilocked(m, " ",
" pending transaction", w);
} }
if (!print_always && m->count == header_pos) if (!print_always && m->count == header_pos)
m->count = start_pos; m->count = start_pos;
...@@ -4375,9 +4553,13 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node) ...@@ -4375,9 +4553,13 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
seq_printf(m, " %d", ref->proc->pid); seq_printf(m, " %d", ref->proc->pid);
} }
seq_puts(m, "\n"); seq_puts(m, "\n");
if (node->proc) {
binder_inner_proc_lock(node->proc);
list_for_each_entry(w, &node->async_todo, entry) list_for_each_entry(w, &node->async_todo, entry)
print_binder_work(m, " ", print_binder_work_ilocked(m, " ",
" pending async transaction", w); " pending async transaction", w);
binder_inner_proc_unlock(node->proc);
}
} }
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)
...@@ -4401,9 +4583,11 @@ static void print_binder_proc(struct seq_file *m, ...@@ -4401,9 +4583,11 @@ static void print_binder_proc(struct seq_file *m,
seq_printf(m, "context %s\n", proc->context->name); seq_printf(m, "context %s\n", proc->context->name);
header_pos = m->count; header_pos = m->count;
binder_inner_proc_lock(proc);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
print_binder_thread(m, rb_entry(n, struct binder_thread, print_binder_thread_ilocked(m, rb_entry(n, struct binder_thread,
rb_node), print_all); rb_node), print_all);
binder_inner_proc_unlock(proc);
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);
...@@ -4418,12 +4602,14 @@ static void print_binder_proc(struct seq_file *m, ...@@ -4418,12 +4602,14 @@ static void print_binder_proc(struct seq_file *m,
rb_node_desc)); rb_node_desc));
} }
binder_alloc_print_allocated(m, &proc->alloc); binder_alloc_print_allocated(m, &proc->alloc);
binder_inner_proc_lock(proc);
list_for_each_entry(w, &proc->todo, entry) list_for_each_entry(w, &proc->todo, entry)
print_binder_work(m, " ", " pending transaction", w); print_binder_work_ilocked(m, " ", " pending transaction", w);
list_for_each_entry(w, &proc->delivered_death, entry) { list_for_each_entry(w, &proc->delivered_death, entry) {
seq_puts(m, " has delivered dead binder\n"); seq_puts(m, " has delivered dead binder\n");
break; break;
} }
binder_inner_proc_unlock(proc);
if (!print_all && m->count == header_pos) if (!print_all && m->count == header_pos)
m->count = start_pos; m->count = start_pos;
} }
...@@ -4562,15 +4748,12 @@ static void print_binder_proc_stats(struct seq_file *m, ...@@ -4562,15 +4748,12 @@ static void print_binder_proc_stats(struct seq_file *m,
seq_printf(m, " buffers: %d\n", count); seq_printf(m, " buffers: %d\n", count);
count = 0; count = 0;
binder_inner_proc_lock(proc);
list_for_each_entry(w, &proc->todo, entry) { list_for_each_entry(w, &proc->todo, entry) {
switch (w->type) { if (w->type == BINDER_WORK_TRANSACTION)
case BINDER_WORK_TRANSACTION:
count++; count++;
break;
default:
break;
}
} }
binder_inner_proc_unlock(proc);
seq_printf(m, " pending transactions: %d\n", count); seq_printf(m, " pending transactions: %d\n", count);
print_binder_stats(m, " ", &proc->stats); print_binder_stats(m, " ", &proc->stats);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment