예제 #1
0
파일: node.c 프로젝트: D-os/kdbus
/**
 * kdbus_node_unref() - Drop object reference
 * @node:	node to drop reference to (or NULL)
 *
 * This drops an object reference to @node. You must not access the node if you
 * no longer own a reference.
 * If the ref-count drops to 0, the object will be destroyed (->free_cb will be
 * called).
 *
 * If you linked or activated the node, you must deactivate the node before you
 * drop your last reference! If you didn't link or activate the node, you can
 * drop any reference you want.
 *
 * Note that this calls into ->free_cb() and thus _might_ sleep. The ->free_cb()
 * callbacks must not acquire any outer locks, though. So you can safely drop
 * references while holding locks.
 *
 * If @node is NULL, this is a no-op.
 *
 * Return: This always returns NULL
 */
struct kdbus_node *kdbus_node_unref(struct kdbus_node *node)
{
	if (node && atomic_dec_and_test(&node->refcnt)) {
		struct kdbus_node safe = *node;

		WARN_ON(atomic_read(&node->active) != KDBUS_NODE_DRAINED);
		WARN_ON(!RB_EMPTY_NODE(&node->rb));

		if (node->free_cb)
			node->free_cb(node);
		if (safe.id > 0)
			ida_simple_remove(&kdbus_node_ida, safe.id);

		kfree(safe.name);

		/*
		 * kdbusfs relies on the parent to be available even after the
		 * node was deactivated and unlinked. Therefore, we pin it
		 * until a node is destroyed.
		 */
		kdbus_node_unref(safe.parent);
	}

	return NULL;
}
예제 #2
0
파일: node.c 프로젝트: archetipo/kdbus
/**
 * kdbus_node_unref() - Drop object reference
 * @node:	node to drop reference to (or NULL)
 *
 * This drops an object reference to @node. You must not access the node if you
 * no longer own a reference.
 * If the ref-count drops to 0, the object will be destroyed (->free_cb will be
 * called).
 *
 * If you linked or activated the node, you must deactivate the node before you
 * drop your last reference! If you didn't link or activate the node, you can
 * drop any reference you want.
 *
 * Note that this calls into ->free_cb() and thus _might_ sleep. The ->free_cb()
 * callbacks must not acquire any outer locks, though. So you can safely drop
 * references while holding locks.
 *
 * If @node is NULL, this is a no-op.
 *
 * Return: This always returns NULL
 */
struct kdbus_node *kdbus_node_unref(struct kdbus_node *node)
{
	if (node && atomic_dec_and_test(&node->refcnt)) {
		struct kdbus_node safe = *node;

		WARN_ON(atomic_read(&node->active) != KDBUS_NODE_DRAINED);
		WARN_ON(!RB_EMPTY_NODE(&node->rb));

		if (node->free_cb)
			node->free_cb(node);

		down_write(&kdbus_node_idr_lock);
		if (safe.id > 0)
			idr_remove(&kdbus_node_idr, safe.id);
		/* drop caches after last node to not leak memory on unload */
		if (idr_is_empty(&kdbus_node_idr)) {
			idr_destroy(&kdbus_node_idr);
			idr_init(&kdbus_node_idr);
		}
		up_write(&kdbus_node_idr_lock);

		kfree(safe.name);

		/*
		 * kdbusfs relies on the parent to be available even after the
		 * node was deactivated and unlinked. Therefore, we pin it
		 * until a node is destroyed.
		 */
		kdbus_node_unref(safe.parent);
	}

	return NULL;
}
예제 #3
0
파일: node.c 프로젝트: D-os/kdbus
/**
 * kdbus_node_deactivate() - deactivate a node
 * @node:	The node to deactivate.
 *
 * This function recursively deactivates this node and all its children. It
 * returns only once all children and the node itself were recursively disabled
 * (even if you call this function multiple times in parallel).
 *
 * It is safe to call this function on _any_ node that was initialized _any_
 * number of times.
 *
 * This call may sleep, as it waits for all active references to be dropped.
 */
void kdbus_node_deactivate(struct kdbus_node *node)
{
	struct kdbus_node *pos, *child;
	struct rb_node *rb;
	int v_pre, v_post;

	pos = node;

	/*
	 * To avoid recursion, we perform back-tracking while deactivating
	 * nodes. For each node we enter, we first mark the active-counter as
	 * deactivated by adding BIAS. If the node as children, we set the first
	 * child as current position and start over. If the node has no
	 * children, we drain the node by waiting for all active refs to be
	 * dropped and then releasing the node.
	 *
	 * After the node is released, we set its parent as current position
	 * and start over. If the current position was the initial node, we're
	 * done.
	 *
	 * Note that this function can be called in parallel by multiple
	 * callers. We make sure that each node is only released once, and any
	 * racing caller will wait until the other thread fully released that
	 * node.
	 */

	for (;;) {
		/*
		 * Add BIAS to node->active to mark it as inactive. If it was
		 * never active before, immediately mark it as RELEASE_INACTIVE
		 * so we remember this state.
		 * We cannot remember v_pre as we might iterate into the
		 * children, overwriting v_pre, before we can release our node.
		 */
		mutex_lock(&pos->lock);
		v_pre = atomic_read(&pos->active);
		if (v_pre >= 0)
			atomic_add_return(KDBUS_NODE_BIAS, &pos->active);
		else if (v_pre == KDBUS_NODE_NEW)
			atomic_set(&pos->active, KDBUS_NODE_RELEASE_DIRECT);
		mutex_unlock(&pos->lock);

		/* wait until all active references were dropped */
		wait_event(pos->waitq,
			   atomic_read(&pos->active) <= KDBUS_NODE_BIAS);

		mutex_lock(&pos->lock);
		/* recurse into first child if any */
		rb = rb_first(&pos->children);
		if (rb) {
			child = kdbus_node_ref(kdbus_node_from_rb(rb));
			mutex_unlock(&pos->lock);
			pos = child;
			continue;
		}

		/* mark object as RELEASE */
		v_post = atomic_read(&pos->active);
		if (v_post == KDBUS_NODE_BIAS ||
		    v_post == KDBUS_NODE_RELEASE_DIRECT)
			atomic_set(&pos->active, KDBUS_NODE_RELEASE);
		mutex_unlock(&pos->lock);

		/*
		 * If this is the thread that marked the object as RELEASE, we
		 * perform the actual release. Otherwise, we wait until the
		 * release is done and the node is marked as DRAINED.
		 */
		if (v_post == KDBUS_NODE_BIAS ||
		    v_post == KDBUS_NODE_RELEASE_DIRECT) {
			if (pos->release_cb)
				pos->release_cb(pos, v_post == KDBUS_NODE_BIAS);

			if (pos->parent) {
				mutex_lock(&pos->parent->lock);
				if (!RB_EMPTY_NODE(&pos->rb)) {
					rb_erase(&pos->rb,
						 &pos->parent->children);
					RB_CLEAR_NODE(&pos->rb);
				}
				mutex_unlock(&pos->parent->lock);
			}

			/* mark as DRAINED */
			atomic_set(&pos->active, KDBUS_NODE_DRAINED);
			wake_up_all(&pos->waitq);

			/* drop VFS cache */
			kdbus_fs_flush(pos);

			/*
			 * If the node was activated and someone subtracted BIAS
			 * from it to deactivate it, we, and only us, are
			 * responsible to release the extra ref-count that was
			 * taken once in kdbus_node_activate().
			 * If the node was never activated, no-one ever
			 * subtracted BIAS, but instead skipped that state and
			 * immediately went to NODE_RELEASE_DIRECT. In that case
			 * we must not drop the reference.
			 */
			if (v_post == KDBUS_NODE_BIAS)
				kdbus_node_unref(pos);
		} else {
			/* wait until object is DRAINED */
			wait_event(pos->waitq,
			    atomic_read(&pos->active) == KDBUS_NODE_DRAINED);
		}

		/*
		 * We're done with the current node. Continue on its parent
		 * again, which will try deactivating its next child, or itself
		 * if no child is left.
		 * If we've reached our initial node again, we are done and
		 * can safely return.
		 */
		if (pos == node)
			break;

		child = pos;
		pos = pos->parent;
		kdbus_node_unref(child);
	}
}