aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormochel@digitalimplant.org <mochel@digitalimplant.org>2005-03-21 14:45:16 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2005-06-20 18:15:14 -0400
commit9a19fea43616066561e221359596ce532e631395 (patch)
treef776bee1bcb1051bf75323b65fa887347412409e
parent6034a080f98b0bbc0a058e2ac65a538f75cffeee (diff)
[PATCH] Add initial implementation of klist helpers.
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. 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). 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). Signed-off-by: Patrick Mochel <mochel@digitalimplant.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> diff -Nru a/include/linux/klist.h b/include/linux/klist.h
-rw-r--r--include/linux/klist.h53
-rw-r--r--lib/Makefile7
-rw-r--r--lib/klist.c248
3 files changed, 305 insertions, 3 deletions
diff --git a/include/linux/klist.h b/include/linux/klist.h
new file mode 100644
index 000000000000..fb52f9d9d611
--- /dev/null
+++ b/include/linux/klist.h
@@ -0,0 +1,53 @@
1/*
2 * klist.h - Some generic list helpers, extending struct list_head a bit.
3 *
4 * Implementations are found in lib/klist.c
5 *
6 *
7 * Copyright (C) 2005 Patrick Mochel
8 *
9 * This file is rleased under the GPL v2.
10 */
11
12#include <linux/spinlock.h>
13#include <linux/completion.h>
14#include <linux/kref.h>
15#include <linux/list.h>
16
17
18struct klist {
19 spinlock_t k_lock;
20 struct list_head k_list;
21};
22
23
24extern void klist_init(struct klist * k);
25
26
27struct klist_node {
28 struct klist * n_klist;
29 struct list_head n_node;
30 struct kref n_ref;
31 struct completion n_removed;
32};
33
34extern void klist_add_tail(struct klist * k, struct klist_node * n);
35extern void klist_add_head(struct klist * k, struct klist_node * n);
36
37extern void klist_del(struct klist_node * n);
38extern void klist_remove(struct klist_node * n);
39
40
41struct klist_iter {
42 struct klist * i_klist;
43 struct list_head * i_head;
44 struct klist_node * i_cur;
45};
46
47
48extern void klist_iter_init(struct klist * k, struct klist_iter * i);
49extern void klist_iter_init_node(struct klist * k, struct klist_iter * i,
50 struct klist_node * n);
51extern void klist_iter_exit(struct klist_iter * i);
52extern struct klist_node * klist_next(struct klist_iter * i);
53
diff --git a/lib/Makefile b/lib/Makefile
index 7c70db79c0e0..9eccea9429a7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -4,9 +4,10 @@
4 4
5lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ 5lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
6 bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ 6 bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
7 kobject.o kref.o idr.o div64.o int_sqrt.o \ 7 idr.o div64.o int_sqrt.o bitmap.o extable.o prio_tree.o \
8 bitmap.o extable.o kobject_uevent.o prio_tree.o sha1.o \ 8 sha1.o halfmd4.o
9 halfmd4.o 9
10lib-y += kobject.o kref.o kobject_uevent.o klist.o
10 11
11obj-y += sort.o parser.o 12obj-y += sort.o parser.o
12 13
diff --git a/lib/klist.c b/lib/klist.c
new file mode 100644
index 000000000000..6f760424648b
--- /dev/null
+++ b/lib/klist.c
@@ -0,0 +1,248 @@
1/*
2 * klist.c - Routines for manipulating klists.
3 *
4 *
5 * This klist interface provides a couple of structures that wrap around
6 * struct list_head to provide explicit list "head" (struct klist) and
7 * list "node" (struct klist_node) objects. For struct klist, a spinlock
8 * is included that protects access to the actual list itself. struct
9 * klist_node provides a pointer to the klist that owns it and a kref
10 * reference count that indicates the number of current users of that node
11 * in the list.
12 *
13 * The entire point is to provide an interface for iterating over a list
14 * that is safe and allows for modification of the list during the
15 * iteration (e.g. insertion and removal), including modification of the
16 * current node on the list.
17 *
18 * It works using a 3rd object type - struct klist_iter - that is declared
19 * and initialized before an iteration. klist_next() is used to acquire the
20 * next element in the list. It returns NULL if there are no more items.
21 * Internally, that routine takes the klist's lock, decrements the reference
22 * count of the previous klist_node and increments the count of the next
23 * klist_node. It then drops the lock and returns.
24 *
25 * There are primitives for adding and removing nodes to/from a klist.
26 * When deleting, klist_del() will simply decrement the reference count.
27 * Only when the count goes to 0 is the node removed from the list.
28 * klist_remove() will try to delete the node from the list and block
29 * until it is actually removed. This is useful for objects (like devices)
30 * that have been removed from the system and must be freed (but must wait
31 * until all accessors have finished).
32 *
33 * Copyright (C) 2005 Patrick Mochel
34 *
35 * This file is released under the GPL v2.
36 */
37
38#include <linux/klist.h>
39#include <linux/module.h>
40
41
42/**
43 * klist_init - Initialize a klist structure.
44 * @k: The klist we're initializing.
45 */
46
47void klist_init(struct klist * k)
48{
49 INIT_LIST_HEAD(&k->k_list);
50 spin_lock_init(&k->k_lock);
51}
52
53EXPORT_SYMBOL_GPL(klist_init);
54
55
56static void add_head(struct klist * k, struct klist_node * n)
57{
58 spin_lock(&k->k_lock);
59 list_add(&n->n_node, &k->k_list);
60 spin_unlock(&k->k_lock);
61}
62
63static void add_tail(struct klist * k, struct klist_node * n)
64{
65 spin_lock(&k->k_lock);
66 list_add_tail(&n->n_node, &k->k_list);
67 spin_unlock(&k->k_lock);
68}
69
70
71static void klist_node_init(struct klist * k, struct klist_node * n)
72{
73 INIT_LIST_HEAD(&n->n_node);
74 init_completion(&n->n_removed);
75 kref_init(&n->n_ref);
76 n->n_klist = k;
77}
78
79
80/**
81 * klist_add_head - Initialize a klist_node and add it to front.
82 * @k: klist it's going on.
83 * @n: node we're adding.
84 */
85
86void klist_add_head(struct klist * k, struct klist_node * n)
87{
88 klist_node_init(k, n);
89 add_head(k, n);
90}
91
92EXPORT_SYMBOL_GPL(klist_add_head);
93
94
95/**
96 * klist_add_tail - Initialize a klist_node and add it to back.
97 * @k: klist it's going on.
98 * @n: node we're adding.
99 */
100
101void klist_add_tail(struct klist * k, struct klist_node * n)
102{
103 klist_node_init(k, n);
104 add_tail(k, n);
105}
106
107EXPORT_SYMBOL_GPL(klist_add_tail);
108
109
110static void klist_release(struct kref * kref)
111{
112 struct klist_node * n = container_of(kref, struct klist_node, n_ref);
113 list_del(&n->n_node);
114 complete(&n->n_removed);
115}
116
117static int klist_dec_and_del(struct klist_node * n)
118{
119 return kref_put(&n->n_ref, klist_release);
120}
121
122
123/**
124 * klist_del - Decrement the reference count of node and try to remove.
125 * @n: node we're deleting.
126 */
127
128void klist_del(struct klist_node * n)
129{
130 struct klist * k = n->n_klist;
131
132 spin_lock(&k->k_lock);
133 klist_dec_and_del(n);
134 spin_unlock(&k->k_lock);
135}
136
137EXPORT_SYMBOL_GPL(klist_del);
138
139
140/**
141 * klist_remove - Decrement the refcount of node and wait for it to go away.
142 * @n: node we're removing.
143 */
144
145void klist_remove(struct klist_node * n)
146{
147 spin_lock(&n->n_klist->k_lock);
148 klist_dec_and_del(n);
149 spin_unlock(&n->n_klist->k_lock);
150 wait_for_completion(&n->n_removed);
151}
152
153EXPORT_SYMBOL_GPL(klist_remove);
154
155
156/**
157 * klist_iter_init_node - Initialize a klist_iter structure.
158 * @k: klist we're iterating.
159 * @i: klist_iter we're filling.
160 * @n: node to start with.
161 *
162 * Similar to klist_iter_init(), but starts the action off with @n,
163 * instead of with the list head.
164 */
165
166void klist_iter_init_node(struct klist * k, struct klist_iter * i, struct klist_node * n)
167{
168 i->i_klist = k;
169 i->i_head = &k->k_list;
170 i->i_cur = n;
171}
172
173EXPORT_SYMBOL_GPL(klist_iter_init_node);
174
175
176/**
177 * klist_iter_init - Iniitalize a klist_iter structure.
178 * @k: klist we're iterating.
179 * @i: klist_iter structure we're filling.
180 *
181 * Similar to klist_iter_init_node(), but start with the list head.
182 */
183
184void klist_iter_init(struct klist * k, struct klist_iter * i)
185{
186 klist_iter_init_node(k, i, NULL);
187}
188
189EXPORT_SYMBOL_GPL(klist_iter_init);
190
191
192/**
193 * klist_iter_exit - Finish a list iteration.
194 * @i: Iterator structure.
195 *
196 * Must be called when done iterating over list, as it decrements the
197 * refcount of the current node. Necessary in case iteration exited before
198 * the end of the list was reached, and always good form.
199 */
200
201void klist_iter_exit(struct klist_iter * i)
202{
203 if (i->i_cur) {
204 klist_del(i->i_cur);
205 i->i_cur = NULL;
206 }
207}
208
209EXPORT_SYMBOL_GPL(klist_iter_exit);
210
211
212static struct klist_node * to_klist_node(struct list_head * n)
213{
214 return container_of(n, struct klist_node, n_node);
215}
216
217
218/**
219 * klist_next - Ante up next node in list.
220 * @i: Iterator structure.
221 *
222 * First grab list lock. Decrement the reference count of the previous
223 * node, if there was one. Grab the next node, increment its reference
224 * count, drop the lock, and return that next node.
225 */
226
227struct klist_node * klist_next(struct klist_iter * i)
228{
229 struct list_head * next;
230 struct klist_node * knode = NULL;
231
232 spin_lock(&i->i_klist->k_lock);
233 if (i->i_cur) {
234 next = i->i_cur->n_node.next;
235 klist_dec_and_del(i->i_cur);
236 } else
237 next = i->i_head->next;
238
239 if (next != i->i_head) {
240 knode = to_klist_node(next);
241 kref_get(&knode->n_ref);
242 }
243 i->i_cur = knode;
244 spin_unlock(&i->i_klist->k_lock);
245 return knode;
246}
247
248EXPORT_SYMBOL_GPL(klist_next);