binder: make sure todo lists are handled in-order
It is assumed that the work items on the thread and proc todo lists are handled in order. Since multiple threads could be looking at the lists, the current thread handling the work item at the head of the list freezes the list to prevent other threads from dequeuing items. Bug: 33250092 32225111 Change-Id: Iacd47a083a8d0af61a1287d79f28bd2419390909 Signed-off-by: Todd Kjos <tkjos@google.com>
This commit is contained in:
committed by
Thierry Strudel
parent
097a1e2bfc
commit
9cce95e7c6
@@ -257,6 +257,7 @@ struct binder_device {
|
||||
struct binder_worklist {
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
bool freeze;
|
||||
};
|
||||
|
||||
struct binder_work {
|
||||
@@ -422,12 +423,23 @@ static void binder_init_worklist(struct binder_worklist *wlist)
|
||||
{
|
||||
spin_lock_init(&wlist->lock);
|
||||
INIT_LIST_HEAD(&wlist->list);
|
||||
wlist->freeze = false;
|
||||
}
|
||||
|
||||
static void binder_freeze_worklist(struct binder_worklist *wlist)
|
||||
{
|
||||
wlist->freeze = true;
|
||||
}
|
||||
|
||||
static void binder_unfreeze_worklist(struct binder_worklist *wlist)
|
||||
{
|
||||
wlist->freeze = false;
|
||||
}
|
||||
|
||||
static inline bool _binder_worklist_empty(struct binder_worklist *wlist)
|
||||
{
|
||||
BUG_ON(!spin_is_locked(&wlist->lock));
|
||||
return list_empty(&wlist->list);
|
||||
return wlist->freeze || list_empty(&wlist->list);
|
||||
}
|
||||
|
||||
static inline bool binder_worklist_empty(struct binder_worklist *wlist)
|
||||
@@ -2666,7 +2678,7 @@ static int binder_thread_read(struct binder_proc *proc,
|
||||
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
|
||||
void __user *ptr = buffer + *consumed;
|
||||
void __user *end = buffer + size;
|
||||
|
||||
struct binder_worklist *wlist = NULL;
|
||||
int ret = 0;
|
||||
int wait_for_proc_work;
|
||||
|
||||
@@ -2758,12 +2770,16 @@ retry:
|
||||
struct binder_transaction_data tr;
|
||||
struct binder_transaction *t = NULL;
|
||||
struct binder_work *w = NULL;
|
||||
wlist = NULL;
|
||||
|
||||
binder_proc_lock(thread->proc, __LINE__);
|
||||
spin_lock(&thread->todo.lock);
|
||||
if (!_binder_worklist_empty(&thread->todo)) {
|
||||
w = list_first_entry(&thread->todo.list,
|
||||
struct binder_work,
|
||||
entry);
|
||||
wlist = &thread->todo;
|
||||
binder_freeze_worklist(wlist);
|
||||
}
|
||||
spin_unlock(&thread->todo.lock);
|
||||
if (!w) {
|
||||
@@ -2773,9 +2789,12 @@ retry:
|
||||
w = list_first_entry(&proc->todo.list,
|
||||
struct binder_work,
|
||||
entry);
|
||||
wlist = &proc->todo;
|
||||
binder_freeze_worklist(wlist);
|
||||
}
|
||||
spin_unlock(&proc->todo.lock);
|
||||
if (!w) {
|
||||
binder_proc_unlock(thread->proc, __LINE__);
|
||||
/* no data added */
|
||||
if (ptr - buffer == 4 &&
|
||||
!(thread->looper &
|
||||
@@ -2785,8 +2804,12 @@ retry:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end - ptr < sizeof(tr) + 4)
|
||||
binder_proc_unlock(thread->proc, __LINE__);
|
||||
if (end - ptr < sizeof(tr) + 4) {
|
||||
if (wlist)
|
||||
binder_unfreeze_worklist(wlist);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (w->type) {
|
||||
case BINDER_WORK_TRANSACTION: {
|
||||
@@ -2794,8 +2817,10 @@ retry:
|
||||
} break;
|
||||
case BINDER_WORK_TRANSACTION_COMPLETE: {
|
||||
cmd = BR_TRANSACTION_COMPLETE;
|
||||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
if (put_user(cmd, (uint32_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
@@ -2803,6 +2828,7 @@ retry:
|
||||
"%d:%d BR_TRANSACTION_COMPLETE\n",
|
||||
proc->pid, thread->pid);
|
||||
binder_dequeue_work(w, __LINE__);
|
||||
binder_unfreeze_worklist(wlist);
|
||||
kfree(w);
|
||||
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
|
||||
} break;
|
||||
@@ -2842,16 +2868,22 @@ retry:
|
||||
}
|
||||
if (cmd != BR_NOOP) {
|
||||
binder_proc_unlock(proc, __LINE__);
|
||||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
if (put_user(cmd, (uint32_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(node->ptr,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
(binder_uintptr_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
if (put_user(node->cookie,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
(binder_uintptr_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
@@ -2860,6 +2892,7 @@ retry:
|
||||
proc->pid, thread->pid, cmd_name,
|
||||
node->debug_id,
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
binder_unfreeze_worklist(wlist);
|
||||
} else {
|
||||
binder_dequeue_work(w, __LINE__);
|
||||
if (!weak && !strong && !node->is_zombie) {
|
||||
@@ -2880,6 +2913,7 @@ retry:
|
||||
(u64)node->ptr,
|
||||
(u64)node->cookie);
|
||||
}
|
||||
binder_unfreeze_worklist(wlist);
|
||||
}
|
||||
} break;
|
||||
case BINDER_WORK_DEAD_BINDER:
|
||||
@@ -2893,12 +2927,16 @@ retry:
|
||||
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
|
||||
else
|
||||
cmd = BR_DEAD_BINDER;
|
||||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
if (put_user(cmd, (uint32_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(death->cookie,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
(binder_uintptr_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
|
||||
@@ -2919,14 +2957,20 @@ retry:
|
||||
__LINE__);
|
||||
|
||||
}
|
||||
binder_unfreeze_worklist(wlist);
|
||||
if (cmd == BR_DEAD_BINDER)
|
||||
goto done; /* DEAD_BINDER notifications can cause transactions */
|
||||
} break;
|
||||
default:
|
||||
pr_err("%s: Unknown work type: %d\n",
|
||||
__func__, w->type);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (!t)
|
||||
continue;
|
||||
|
||||
BUG_ON(!wlist || !wlist->freeze);
|
||||
BUG_ON(t->buffer == NULL);
|
||||
if (t->buffer->target_node) {
|
||||
struct binder_node *target_node = t->buffer->target_node;
|
||||
@@ -2968,11 +3012,15 @@ retry:
|
||||
ALIGN(t->buffer->data_size,
|
||||
sizeof(void *));
|
||||
|
||||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
if (put_user(cmd, (uint32_t __user *)ptr)) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(uint32_t);
|
||||
if (copy_to_user(ptr, &tr, sizeof(tr)))
|
||||
if (copy_to_user(ptr, &tr, sizeof(tr))) {
|
||||
binder_unfreeze_worklist(wlist);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(tr);
|
||||
|
||||
trace_binder_transaction_received(t);
|
||||
@@ -2988,6 +3036,7 @@ retry:
|
||||
(u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
|
||||
|
||||
binder_dequeue_work(&t->work, __LINE__);
|
||||
binder_unfreeze_worklist(wlist);
|
||||
t->buffer->allow_user_free = 1;
|
||||
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
|
||||
binder_proc_lock(thread->proc, __LINE__);
|
||||
@@ -3004,7 +3053,6 @@ retry:
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
*consumed = ptr - buffer;
|
||||
binder_proc_lock(thread->proc, __LINE__);
|
||||
if (proc->requested_threads +
|
||||
@@ -3035,8 +3083,17 @@ static void binder_release_work(struct binder_worklist *wlist)
|
||||
|
||||
spin_lock(&wlist->lock);
|
||||
while (!list_empty(&wlist->list)) {
|
||||
w = list_first_entry(&wlist->list, struct binder_work, entry);
|
||||
|
||||
if (wlist->freeze) {
|
||||
/* Very rare race. We can't release work now
|
||||
* since the list is in use by a worker thread.
|
||||
* This can be safely done when the zombie object
|
||||
* is being reaped.
|
||||
*/
|
||||
spin_unlock(&wlist->lock);
|
||||
return;
|
||||
}
|
||||
w = list_first_entry(&wlist->list, struct binder_work, entry);
|
||||
_binder_dequeue_work(w, __LINE__);
|
||||
|
||||
spin_unlock(&wlist->lock);
|
||||
@@ -3064,6 +3121,7 @@ static void binder_release_work(struct binder_worklist *wlist)
|
||||
kfree(w);
|
||||
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
|
||||
} break;
|
||||
case BINDER_WORK_DEAD_BINDER:
|
||||
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
|
||||
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
|
||||
struct binder_ref_death *death;
|
||||
@@ -3072,12 +3130,22 @@ static void binder_release_work(struct binder_worklist *wlist)
|
||||
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
|
||||
"undelivered death notification, %016llx\n",
|
||||
(u64)death->cookie);
|
||||
/*
|
||||
* For the non-CLEAR cases, kfree ref->death freeing
|
||||
* done in zombie cleanup path so avoid doing it here
|
||||
*/
|
||||
if (w->type == BINDER_WORK_DEAD_BINDER)
|
||||
break;
|
||||
kfree(death);
|
||||
binder_stats_deleted(BINDER_STAT_DEATH);
|
||||
} break;
|
||||
case BINDER_WORK_NODE:
|
||||
pr_info("unfinished BINDER_WORK_NODE\n");
|
||||
break;
|
||||
default:
|
||||
pr_err("unexpected work type, %d, not freed\n",
|
||||
w->type);
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
spin_lock(&wlist->lock);
|
||||
@@ -3971,6 +4039,7 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
|
||||
hlist_del_init(&node->dead_node);
|
||||
binder_dequeue_work(&node->work, __LINE__);
|
||||
BUG_ON(!list_empty(&node->work.entry));
|
||||
BUG_ON(node->async_todo.freeze);
|
||||
binder_release_work(&node->async_todo);
|
||||
BUG_ON(!binder_worklist_empty(&node->async_todo));
|
||||
kfree(node);
|
||||
@@ -3980,6 +4049,7 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
|
||||
hlist_for_each_entry_safe(thread, tmp, &threads_to_free,
|
||||
zombie_thread) {
|
||||
hlist_del_init(&thread->zombie_thread);
|
||||
BUG_ON(thread->todo.freeze);
|
||||
binder_release_work(&thread->todo);
|
||||
BUG_ON(!binder_worklist_empty(&thread->todo));
|
||||
kfree(thread);
|
||||
@@ -4031,6 +4101,7 @@ static void binder_clear_zombies(void)
|
||||
|
||||
proc = container_of(z, struct binder_proc, zombie_proc);
|
||||
if (binder_proc_clear_zombies(proc)) {
|
||||
BUG_ON(proc->todo.freeze);
|
||||
BUG_ON(!list_empty(&proc->zombie_proc.list_node));
|
||||
binder_release_work(&proc->todo);
|
||||
binder_alloc_deferred_release(&proc->alloc);
|
||||
|
||||
Reference in New Issue
Block a user