/** * 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; }
/** * 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; }
/** * 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); } }