/* * klist.c - Routines for manipulating klists. * * Copyright (C) 2005 Patrick Mochel * * This file is released under the GPL v2. * * This klist interface provides a couple of structures that wrap around * struct list_head to provide explicit list "head" (struct klist) and list * "node" (struct klist_node) objects. For struct klist, a spinlock is * included that protects access to the actual list itself. struct * klist_node provides a pointer to the klist 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 klist_iter - that is declared * and initialized before an iteration. klist_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 klist's lock, decrements the * reference count of the previous klist_node and increments the count of * the next klist_node. It then drops the lock and returns. * * There are primitives for adding and removing nodes to/from a klist. * When deleting, klist_del() will simply decrement the reference count. * Only when the count goes to 0 is the node removed from the list. * klist_remove() 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 <linux/klist.h>#include <linux/export.h>#include <linux/sched.h>/* * Use the lowest bit of n_klist to mark deleted nodes and exclude * dead ones from iteration. */#define KNODE_DEAD 1LU#define KNODE_KLIST_MASK ~KNODE_DEADstaticstructklist*knode_klist(structklist_node*knode){return(structklist*)((unsignedlong)knode->n_klist&KNODE_KLIST_MASK);}staticboolknode_dead(structklist_node*knode){return(unsignedlong)knode->n_klist&KNODE_DEAD;}staticvoidknode_set_klist(structklist_node*knode,structklist*klist){knode->n_klist=klist;/* no knode deserves to start its life dead */WARN_ON(knode_dead(knode));}staticvoidknode_kill(structklist_node*knode){/* and no knode should die twice ever either, see we're very humane */WARN_ON(knode_dead(knode));*(unsignedlong*)&knode->n_klist|=KNODE_DEAD;}/** * klist_init - Initialize a klist structure. * @k: The klist we're initializing. * @get: The get function for the embedding object (NULL if none) * @put: The put function for the embedding object (NULL if none) * * Initialises the klist structure. If the klist_node structures are * going to be embedded in refcounted objects (necessary for safe * deletion) then the get/put arguments are used to initialise * functions that take and release references on the embedding * objects. */voidklist_init(structklist*k,void(*get)(structklist_node*),void(*put)(structklist_node*)){INIT_LIST_HEAD(&k->k_list);spin_lock_init(&k->k_lock);k->get=get;k->put=put;}EXPORT_SYMBOL_GPL(klist_init);staticvoidadd_head(structklist*k,structklist_node*n){spin_lock(&k->k_lock);list_add(&n->n_node,&k->k_list);spin_unlock(&k->k_lock);}staticvoidadd_tail(structklist*k,structklist_node*n){spin_lock(&k->k_lock);list_add_tail(&n->n_node,&k->k_list);spin_unlock(&k->k_lock);}staticvoidklist_node_init(structklist*k,structklist_node*n){INIT_LIST_HEAD(&n->n_node);kref_init(&n->n_ref);knode_set_klist(n,k);if(k->get)k->get(n);}/** * klist_add_head - Initialize a klist_node and add it to front. * @n: node we're adding. * @k: klist it's going on. */voidklist_add_head(structklist_node*n,structklist*k){klist_node_init(k,n);add_head(k,n);}EXPORT_SYMBOL_GPL(klist_add_head);/** * klist_add_tail - Initialize a klist_node and add it to back. * @n: node we're adding. * @k: klist it's going on. */voidklist_add_tail(structklist_node*n,structklist*k){klist_node_init(k,n);add_tail(k,n);}EXPORT_SYMBOL_GPL(klist_add_tail);/** * klist_add_behind - Init a klist_node and add it after an existing node * @n: node we're adding. * @pos: node to put @n after */voidklist_add_behind(structklist_node*n,structklist_node*pos){structklist*k=knode_klist(pos);klist_node_init(k,n);spin_lock(&k->k_lock);list_add(&n->n_node,&pos->n_node);spin_unlock(&k->k_lock);}EXPORT_SYMBOL_GPL(klist_add_behind);/** * klist_add_before - Init a klist_node and add it before an existing node * @n: node we're adding. * @pos: node to put @n after */voidklist_add_before(structklist_node*n,structklist_node*pos){structklist*k=knode_klist(pos);klist_node_init(k,n);spin_lock(&k->k_lock);list_add_tail(&n->n_node,&pos->n_node);spin_unlock(&k->k_lock);}EXPORT_SYMBOL_GPL(klist_add_before);structklist_waiter{structlist_headlist;structklist_node*node;structtask_struct*process;intwoken;};staticDEFINE_SPINLOCK(klist_remove_lock);staticLIST_HEAD(klist_remove_waiters);staticvoidklist_release(structkref*kref){structklist_waiter*waiter,*tmp;structklist_node*n=container_of(kref,structklist_node,n_ref);WARN_ON(!knode_dead(n));list_del(&n->n_node);spin_lock(&klist_remove_lock);list_for_each_entry_safe(waiter,tmp,&klist_remove_waiters,list){if(waiter->node!=n)continue;list_del(&waiter->list);waiter->woken=1;mb();wake_up_process(waiter->process);}spin_unlock(&klist_remove_lock);knode_set_klist(n,NULL);}staticintklist_dec_and_del(structklist_node*n){returnkref_put(&n->n_ref,klist_release);}staticvoidklist_put(structklist_node*n,boolkill){structklist*k=knode_klist(n);void(*put)(structklist_node*)=k->put;spin_lock(&k->k_lock);if(kill)knode_kill(n);if(!klist_dec_and_del(n))put=NULL;spin_unlock(&k->k_lock);if(put)put(n);}/** * klist_del - Decrement the reference count of node and try to remove. * @n: node we're deleting. */voidklist_del(structklist_node*n){klist_put(n,true);}EXPORT_SYMBOL_GPL(klist_del);/** * klist_remove - Decrement the refcount of node and wait for it to go away. * @n: node we're removing. */voidklist_remove(structklist_node*n){structklist_waiterwaiter;waiter.node=n;waiter.process=current;waiter.woken=0;spin_lock(&klist_remove_lock);list_add(&waiter.list,&klist_remove_waiters);spin_unlock(&klist_remove_lock);klist_del(n);for(;;){set_current_state(TASK_UNINTERRUPTIBLE);if(waiter.woken)break;schedule();}__set_current_state(TASK_RUNNING);}EXPORT_SYMBOL_GPL(klist_remove);/** * klist_node_attached - Say whether a node is bound to a list or not. * @n: Node that we're testing. */intklist_node_attached(structklist_node*n){return(n->n_klist!=NULL);}EXPORT_SYMBOL_GPL(klist_node_attached);/** * klist_iter_init_node - Initialize a klist_iter structure. * @k: klist we're iterating. * @i: klist_iter we're filling. * @n: node to start with. * * Similar to klist_iter_init(), but starts the action off with @n, * instead of with the list head. */voidklist_iter_init_node(structklist*k,structklist_iter*i,structklist_node*n){i->i_klist=k;i->i_cur=n;if(n)kref_get(&n->n_ref);}EXPORT_SYMBOL_GPL(klist_iter_init_node);/** * klist_iter_init - Iniitalize a klist_iter structure. * @k: klist we're iterating. * @i: klist_iter structure we're filling. * * Similar to klist_iter_init_node(), but start with the list head. */voidklist_iter_init(structklist*k,structklist_iter*i){klist_iter_init_node(k,i,NULL);}EXPORT_SYMBOL_GPL(klist_iter_init);/** * klist_iter_exit - Finish a list iteration. * @i: Iterator structure. * * Must be called when done iterating over list, as it decrements the * refcount of the current node. Necessary in case iteration exited before * the end of the list was reached, and always good form. */voidklist_iter_exit(structklist_iter*i){if(i->i_cur){klist_put(i->i_cur,false);i->i_cur=NULL;}}EXPORT_SYMBOL_GPL(klist_iter_exit);staticstructklist_node*to_klist_node(structlist_head*n){returncontainer_of(n,structklist_node,n_node);}/** * klist_next - Ante up next node in list. * @i: Iterator structure. * * First grab list lock. Decrement the reference count of the previous * node, if there was one. Grab the next node, increment its reference * count, drop the lock, and return that next node. */structklist_node*klist_next(structklist_iter*i){void(*put)(structklist_node*)=i->i_klist->put;structklist_node*last=i->i_cur;structklist_node*next;spin_lock(&i->i_klist->k_lock);if(last){next=to_klist_node(last->n_node.next);if(!klist_dec_and_del(last))put=NULL;}elsenext=to_klist_node(i->i_klist->k_list.next);i->i_cur=NULL;while(next!=to_klist_node(&i->i_klist->k_list)){if(likely(!knode_dead(next))){kref_get(&next->n_ref);i->i_cur=next;break;}next=to_klist_node(next->n_node.next);}spin_unlock(&i->i_klist->k_lock);if(put&&last)put(last);returni->i_cur;}EXPORT_SYMBOL_GPL(klist_next);