/* * ktree.c - Routines for manipulating ktrees. * * Copyright (C) 2005 Patrick Mochel * * This file is released under the GPL v2. * * This ktree interface provides a couple of structures that wrap around * struct list_head to provide explicit list "head" (struct ktree) and list * "node" (struct ktree_node) objects. For struct ktree, a spinlock is * included that protects access to the actual list itself. struct * ktree_node provides a pointer to the ktree that owns it and a kref * reference count that indicates the number of current users of that node * in the list. * * The entire point is to provide an interface for iterating over a list * that is safe and allows for modification of the list during the * iteration (e.g. insertion and removal), including modification of the * current node on the list. * * It works using a 3rd object type - struct ktree_iter - that is declared * and initialized before an iteration. ktree_iter_next() is used to acquire the * next element in the list. It returns NULL if there are no more items. * Internally, that routine takes the ktree's lock, decrements the * reference count of the previous ktree_node and increments the count of * the next ktree_node. It then drops the lock and returns. * * There are primitives for adding and removing nodes to/from a ktree. * When deleting, ktree_del_node() will simply decrement the reference count. * Only when the count goes to 0 is the node removed from the list. * ktree_remove_node() will try to delete the node from the list and block until * it is actually removed. This is useful for objects (like devices) that * have been removed from the system and must be freed (but must wait until * all accessors have finished). */ #include #include #include #include static void knode_release(struct kref *kref); static void __ktree_put_node(struct ktree_node *node, bool kill); void ktree_put_node(struct ktree_node *node) { if (node) __ktree_put_node(node, false); } EXPORT_SYMBOL_GPL(ktree_put_node); static void knode_set_parent(struct ktree_node *node, struct ktree_node *parent) { struct ktree_node *old_parent = node->parent; WARN_ON(parent && parent->deleted); node->parent = ktree_get_node(parent); ktree_put_node(old_parent); } void ktree_init(struct ktree *ktree, void (*get)(struct ktree_node *), void (*put)(struct ktree_node *)) { ktree->root = NULL; spin_lock_init(&ktree->lock); ktree->get = get; ktree->put = put; } EXPORT_SYMBOL_GPL(ktree_init); static void ktree_node_init(struct ktree *ktree, struct ktree_node *parent, struct ktree_node *node) { node->deleted = false; INIT_LIST_HEAD(&node->siblings); INIT_LIST_HEAD(&node->children); kref_init(&node->refcount); node->ktree = ktree; knode_set_parent(node, parent); if (ktree->get) ktree->get(node); } void ktree_set_root(struct ktree *ktree, struct ktree_node *root) { ktree_node_init(ktree, NULL, root); ktree_del_node(ktree->root); ktree->root = root; } EXPORT_SYMBOL_GPL(ktree_set_root); void ktree_add_child_before(struct ktree_node *parent, struct ktree_node *child, struct ktree_node *pos) { struct ktree *ktree = parent->ktree; struct list_head *head; ktree_node_init(ktree, parent, child); spin_lock(&ktree->lock); head = pos ? &pos->siblings : &parent->children; list_add_tail(&child->siblings, head); spin_unlock(&ktree->lock); } EXPORT_SYMBOL_GPL(ktree_add_child_before); void ktree_add_child_after(struct ktree_node *parent, struct ktree_node *child, struct ktree_node *pos) { struct ktree *ktree = parent->ktree; struct list_head *head; ktree_node_init(ktree, parent, child); spin_lock(&ktree->lock); head = pos ? &pos->siblings : &parent->children; list_add(&child->siblings, head); spin_unlock(&ktree->lock); } EXPORT_SYMBOL_GPL(ktree_add_child_after); static void ktree_node_destroy(struct ktree_node *node) { struct ktree_node *parent = node->parent; struct ktree *ktree = node->ktree; WARN_ON(!ktree_is_leaf(node)); if (ktree->put) ktree->put(node); ktree_put_node(parent); } struct ktree_waiter { struct list_head list; struct ktree_node *node; struct task_struct *process; int woken; }; static DEFINE_SPINLOCK(ktree_remove_lock); static LIST_HEAD(ktree_remove_waiters); static void knode_release(struct kref *kref) { struct ktree_waiter *waiter, *tmp; struct ktree_node *node; struct ktree *ktree; node = container_of(kref, struct ktree_node, refcount); WARN_ON(!node->deleted || !node->ktree); ktree = node->ktree; assert_spin_locked(&ktree->lock); list_del(&node->siblings); if (node == ktree->root) ktree->root = NULL; spin_lock(&ktree_remove_lock); list_for_each_entry_safe(waiter, tmp, &ktree_remove_waiters, list) { if (waiter->node != node) continue; waiter->woken = 1; mb(); /* barrier before waking up waiting process */ wake_up_process(waiter->process); list_del(&waiter->list); } spin_unlock(&ktree_remove_lock); } typedef void (*put_fn)(struct ktree_node *); static bool __ktree_put_node_locked(struct ktree_node *node) { return !!kref_put(&node->refcount, knode_release); } static void __ktree_put_node_unlock(struct ktree_node *node) { struct ktree *ktree = node->ktree; if (__ktree_put_node_locked(node)) { spin_unlock(&ktree->lock); ktree_node_destroy(node); spin_lock(&ktree->lock); } } static void __ktree_put_node(struct ktree_node *node, bool kill) { struct ktree *ktree = node->ktree; bool destroy; spin_lock(&ktree->lock); if (kill) { WARN_ON(node->deleted); node->deleted = true; } destroy = __ktree_put_node_locked(node); spin_unlock(&ktree->lock); if (destroy) ktree_node_destroy(node); } int __ktree_del_node(struct ktree_node *child, void *arg) { ktree_del_node(child); return 0; } void ktree_del_node(struct ktree_node *node) { if (node) { ktree_for_each_child(node, __ktree_del_node, NULL); __ktree_put_node(node, true); } } EXPORT_SYMBOL_GPL(ktree_del_node); void ktree_remove_node(struct ktree_node *node) { struct ktree_waiter waiter; waiter.node = node; waiter.process = current; waiter.woken = 0; spin_lock(&ktree_remove_lock); list_add(&waiter.list, &ktree_remove_waiters); spin_unlock(&ktree_remove_lock); ktree_del_node(node); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); if (waiter.woken) break; schedule(); } __set_current_state(TASK_RUNNING); } EXPORT_SYMBOL_GPL(ktree_remove_node); static struct ktree_node *ktree_get_pos(struct ktree_node *parent, struct list_head *pos) { struct ktree_node *node = NULL; if (pos != &parent->children) node = container_of(pos, struct ktree_node, siblings); return ktree_get_node(node); } struct ktree_node *ktree_try_traverse_parent(struct ktree_node *node) { struct ktree_node *next; next = ktree_get_parent(node); if (next) ktree_put_node(node); return next; } struct ktree_node *ktree_traverse_parent(struct ktree_node *node) { struct ktree_node *next; next = ktree_get_parent(node); ktree_put_node(node); return next; } struct ktree_node *ktree_traverse_sibling(struct ktree_node *node, bool reverse) { struct ktree_node *next, *parent; struct list_head *pos; parent = node->parent; if (!parent) return NULL; spin_lock(&node->ktree->lock); for (;;) { pos = reverse ? node->siblings.prev : node->siblings.next; next = ktree_get_pos(parent, pos); __ktree_put_node_unlock(node); if (!next || !next->deleted) break; node = next; } spin_unlock(&node->ktree->lock); return next; } struct ktree_node *ktree_try_traverse_sibling(struct ktree_node *node, bool reverse) { struct ktree_node *next; node = ktree_get_node(node); next = ktree_traverse_sibling(node, reverse); if (next) ktree_put_node(node); return next; } struct ktree_node *ktree_traverse_child(struct ktree_node *node, bool reverse) { struct ktree_node *child; struct list_head *pos; spin_lock(&node->ktree->lock); pos = reverse ? node->children.prev : node->children.next; child = ktree_get_pos(node, pos); spin_unlock(&node->ktree->lock); ktree_put_node(node); if (child && child->deleted) child = ktree_traverse_sibling(child, reverse); return child; } struct ktree_node *ktree_try_traverse_child(struct ktree_node *node, bool reverse) { struct ktree_node *next; node = ktree_get_node(node); next = ktree_traverse_child(node, reverse); if (next) ktree_put_node(node); return next; } int ktree_for_each_child(struct ktree_node *parent, int (*visitor)(struct ktree_node *child, void *arg), void *arg) { struct ktree_node *node; bool reverse = false; int error; /* grab a reference that we drop as we traverse */ node = ktree_get_node(parent); for (node = ktree_traverse_child(node, reverse); node; node = ktree_traverse_sibling(node, reverse)) { error = visitor(node, arg); if (error) { ktree_put_node(node); return error; } } return 0; } EXPORT_SYMBOL_GPL(ktree_for_each_child); struct ktree_sort_params { int (*cmp)(struct ktree_node *a, struct ktree_node *b, void *arg); void *arg; }; static int __ktree_cmp(void *arg, struct list_head *_a, struct list_head *_b) { struct ktree_node *a = container_of(_a, struct ktree_node, siblings); struct ktree_node *b = container_of(_b, struct ktree_node, siblings); struct ktree_sort_params *params = arg; return params->cmp(a, b, params->arg); } void ktree_sort_children(struct ktree_node *parent, int (*cmp)(struct ktree_node *a, struct ktree_node *b, void *arg), void *arg) { struct ktree_sort_params params = { .cmp = cmp, .arg = arg }; spin_lock(&parent->ktree->lock); list_sort(¶ms, &parent->children, __ktree_cmp); spin_unlock(&parent->ktree->lock); } EXPORT_SYMBOL_GPL(ktree_sort_children);