* drivers/binder/binder_node.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#define LOG_TAG "BinderNode"
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <debug.h>
#include <sched.h>
#include <nuttx/fs/fs.h>
#include <nuttx/android/binder.h>
#include <nuttx/mutex.h>
#include <nuttx/nuttx.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <nuttx/wqueue.h>
#include "binder_internal.h"
* Pre-processor Definitions
****************************************************************************/
static struct list_node binder_dead_nodes = LIST_INITIAL_VALUE(
binder_dead_nodes);
static mutex_t binder_dead_nodes_lock = NXMUTEX_INITIALIZER;
* Private Functions
****************************************************************************/
static struct binder_node *binder_init_node_ilocked(
FAR struct binder_proc *proc, FAR struct binder_node *new_node,
FAR struct flat_binder_object *fp)
{
signed char priority;
FAR struct binder_node *node;
uint32_t flags = fp ? fp->flags : 0;
binder_uintptr_t ptr = fp ? fp->binder : 0;
binder_uintptr_t cookie = fp ? fp->cookie : 0;
binder_inner_proc_assert_locked(proc);
list_for_every_entry(&proc->nodes, node, struct binder_node, rb_node)
{
if (ptr == node->ptr)
{
* The node was already added by another thread.
* Abandon the init and return it.
*/
binder_inc_node_tmpref_ilocked(node);
return node;
}
}
node = new_node;
node->tmp_refs++;
node->debug_id = binder_last_debug_id++;
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >>
FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
if (node->sched_policy == 0)
{
struct binder_priority proc_priority;
binder_get_priority(proc->pid, &proc_priority);
node->sched_policy = proc_priority.sched_policy;
node->min_priority = proc_priority.sched_prio;
}
else
{
node->min_priority = priority;
}
node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
node->txn_security_ctx = !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX);
nxmutex_init(&node->lock);
list_initialize(&node->work.entry_node);
list_initialize(&node->async_todo);
list_initialize(&node->rb_node);
list_initialize(&node->refs);
list_add_head(&proc->nodes, &node->rb_node);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d %"PRIx64" %"PRIx64" created\n",
proc->pid, gettid(), node->debug_id,
node->ptr, node->cookie);
return node;
}
* Pubilc Functions
****************************************************************************/
FAR struct binder_node *binder_get_node(FAR struct binder_proc *proc,
binder_uintptr_t ptr)
{
FAR struct binder_node *itr;
FAR struct binder_node *node;
binder_inner_proc_lock(proc);
node = NULL;
list_for_every_entry(&proc->nodes, itr, struct binder_node, rb_node)
{
if (ptr == itr->ptr)
{
* node stays alive until call to binder_dec_node_tmpref()
*/
node = itr;
binder_inc_node_tmpref_ilocked(node);
break;
}
}
binder_inner_proc_unlock(proc);
return node;
}
int binder_inc_node_nilocked(FAR struct binder_node *node, int strong,
int internal, FAR struct list_node *target_list)
{
binder_node_inner_assert_locked(node);
if (strong)
{
if (internal)
{
if (target_list == NULL && node->internal_strong_refs == 0 &&
!(node->proc && node == node->proc->context->mgr_node &&
node->has_strong_ref))
{
binder_debug(BINDER_DEBUG_ERROR,
"invalid inc strong node for %d\n",
node->debug_id);
return -EINVAL;
}
node->internal_strong_refs++;
}
else
{
node->local_strong_refs++;
}
if (!node->has_strong_ref && target_list)
{
struct binder_thread *thread = container_of(target_list,
struct binder_thread,
todo);
binder_dequeue_work_ilocked(&node->work);
BUG_ON(&thread->todo != target_list);
binder_enqueue_deferred_thread_work_ilocked(thread, &node->work);
}
}
else
{
if (!internal)
{
node->local_weak_refs++;
}
if (!node->has_weak_ref && list_is_empty(&node->work.entry_node))
{
if (target_list == NULL)
{
binder_debug(BINDER_DEBUG_ERROR,
"invalid inc weak node for %d\n", node->debug_id);
return -EINVAL;
}
binder_enqueue_work_ilocked(&node->work, target_list);
}
}
return 0;
}
int binder_inc_node(FAR struct binder_node *node, int strong, int internal,
FAR struct list_node *target_list)
{
int ret;
binder_node_inner_lock(node);
ret = binder_inc_node_nilocked(node, strong, internal, target_list);
binder_node_inner_unlock(node);
return ret;
}
bool binder_dec_node_nilocked(FAR struct binder_node *node, int strong,
int internal)
{
FAR struct binder_proc *proc = node->proc;
binder_node_inner_assert_locked(node);
if (strong)
{
if (internal)
{
node->internal_strong_refs--;
}
else
{
node->local_strong_refs--;
}
if (node->local_strong_refs || node->internal_strong_refs)
{
return false;
}
}
else
{
if (!internal)
{
node->local_weak_refs--;
}
if (node->local_weak_refs || node->tmp_refs ||
!list_is_empty(&node->refs))
{
return false;
}
}
if (proc && (node->has_strong_ref || node->has_weak_ref))
{
if (list_is_empty(&node->work.entry_node))
{
binder_enqueue_work_ilocked(&node->work, &proc->todo_list);
binder_wakeup_proc_ilocked(proc);
}
}
else
{
if (list_is_empty(&node->refs) && !node->local_strong_refs &&
!node->local_weak_refs && !node->tmp_refs)
{
if (proc)
{
binder_dequeue_work_ilocked(&node->work);
list_delete_init(&node->rb_node);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"refless node %d deleted\n", node->debug_id);
}
else
{
BUG_ON(!list_is_empty(&node->work.entry_node));
nxmutex_lock(&binder_dead_nodes_lock);
if (node->tmp_refs)
{
nxmutex_unlock(&binder_dead_nodes_lock);
return false;
}
list_delete_init(&node->dead_node);
nxmutex_unlock(&binder_dead_nodes_lock);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"dead node %d deleted\n", node->debug_id);
}
return true;
}
}
return false;
}
void binder_dec_node(FAR struct binder_node *node, int strong, int internal)
{
bool free_node;
binder_node_inner_lock(node);
free_node = binder_dec_node_nilocked(node, strong, internal);
binder_node_inner_unlock(node);
if (free_node)
{
binder_free_node(node);
}
}
* Name: binder_inc_node_tmpref
*
* Description:
* Take reference on node to prevent the node from being freed while
* referenced only by a local variable. The inner lock is needed to
* serialize with the node work on the queue (which isn't needed after
* the node is dead).
*
* If the node is dead (node->proc is NULL), use
* binder_dead_nodes_lock to protect node->tmp_refs against
* dead-node-only cases where the node lock cannot be acquired
* (eg traversing the dead node list to print nodes)
*
****************************************************************************/
static void binder_inc_node_tmpref(FAR struct binder_node *node)
{
FAR struct binder_proc *proc = node->proc;
binder_node_lock(node);
if (proc != NULL)
{
binder_inner_proc_lock(proc);
}
else
{
nxmutex_lock(&binder_dead_nodes_lock);
}
binder_inc_node_tmpref_ilocked(node);
if (proc != NULL)
{
binder_inner_proc_unlock(node->proc);
}
else
{
nxmutex_unlock(&binder_dead_nodes_lock);
}
binder_node_unlock(node);
}
* Name: binder_dec_node_tmpref
*
* Description:
* remove a temporary reference on node to reference. Release temporary
* reference on node taken via binder_inc_node_tmpref()
*
****************************************************************************/
void binder_dec_node_tmpref(FAR struct binder_node *node)
{
bool free_node;
binder_node_inner_lock(node);
if (!node->proc)
{
nxmutex_lock(&binder_dead_nodes_lock);
}
node->tmp_refs--;
BUG_ON(node->tmp_refs < 0);
if (!node->proc)
{
nxmutex_unlock(&binder_dead_nodes_lock);
}
* and cleanup is needed. Calling with strong=0 and internal=1
* causes no actual reference to be released in binder_dec_node().
* If that changes, a change is needed here too.
*/
free_node = binder_dec_node_nilocked(node, 0, 1);
binder_node_inner_unlock(node);
if (free_node)
{
binder_free_node(node);
}
}
FAR struct binder_node *binder_new_node(FAR struct binder_proc *proc,
FAR struct flat_binder_object *fp)
{
FAR struct binder_node *node;
FAR struct binder_node *new_node;
new_node = kmm_zalloc(sizeof(struct binder_node));
if (new_node == NULL)
{
return NULL;
}
binder_inner_proc_lock(proc);
node = binder_init_node_ilocked(proc, new_node, fp);
binder_inner_proc_unlock(proc);
if (node != new_node)
{
kmm_free(new_node);
}
return node;
}
* Name: binder_get_node_from_ref
*
* Description:
* get the node from the given proc/desc
*
* Input Parameters:
* proc - proc containing the ref
* desc - the handle associated with the ref
* need_strong_ref - if true, only return node if ref is strong
* rdata - the id/refcount data for the ref
*
* Returned Value:
* a binder_node or NULL if not found or not strong when strong required
*
****************************************************************************/
FAR struct binder_node *
binder_get_node_from_ref(FAR struct binder_proc *proc,
uint32_t desc, bool need_strong_ref,
FAR struct binder_ref_data *rdata)
{
FAR struct binder_node *node;
FAR struct binder_ref *ref;
binder_proc_lock(proc);
ref = binder_get_ref_olocked(proc, desc, need_strong_ref);
if (!ref)
{
goto err_no_ref;
}
node = ref->node;
* it stays alive until the call to binder_dec_node_tmpref()
*/
binder_inc_node_tmpref(node);
if (rdata)
{
*rdata = ref->data;
}
binder_proc_unlock(proc);
return node;
err_no_ref:
binder_proc_unlock(proc);
return NULL;
}
int binder_node_release(FAR struct binder_node *node, int refs)
{
int death = 0;
FAR struct binder_ref *ref;
FAR struct binder_proc *proc = node->proc;
binder_release_work(proc, &node->async_todo);
binder_node_lock(node);
binder_inner_proc_lock(proc);
binder_dequeue_work_ilocked(&node->work);
BUG_ON(!node->tmp_refs);
if (list_is_empty(&node->refs) && node->tmp_refs == 1)
{
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
binder_free_node(node);
return refs;
}
node->proc = NULL;
node->local_strong_refs = 0;
node->local_weak_refs = 0;
binder_inner_proc_unlock(proc);
nxmutex_lock(&binder_dead_nodes_lock);
list_add_head(&binder_dead_nodes, &node->dead_node);
nxmutex_unlock(&binder_dead_nodes_lock);
list_for_every_entry(&node->refs, ref, struct binder_ref, node_entry)
{
refs++;
* notification requests and the inner lock to
* synchronize with queued death notifications.
*/
binder_inner_proc_lock(ref->proc);
if (!ref->death)
{
binder_inner_proc_unlock(ref->proc);
continue;
}
death++;
BUG_ON(!list_is_empty(&ref->death->work.entry_node));
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
binder_enqueue_work_ilocked(&ref->death->work, &ref->proc->todo_list);
binder_wakeup_proc_ilocked(ref->proc);
binder_inner_proc_unlock(ref->proc);
}
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"node %d now dead, refs %d, death %d\n", node->debug_id, refs,
death);
binder_node_unlock(node);
binder_dec_node_tmpref(node);
return refs;
}