aboutsummaryrefslogtreecommitdiff
path: root/lib/klist.c
blob: 6f760424648bf320eef69d6baa171af0c4e31c6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/*
 *	klist.c - Routines for manipulating klists.
 *
 *
 *	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).
 *
 *	Copyright (C) 2005 Patrick Mochel
 *
 *	This file is released under the GPL v2.
 */

#include <linux/klist.h>
#include <linux/module.h>


/**
 *	klist_init - Initialize a klist structure. 
 *	@k:	The klist we're initializing.
 */

void klist_init(struct klist * k)
{
	INIT_LIST_HEAD(&k->k_list);
	spin_lock_init(&k->k_lock);
}

EXPORT_SYMBOL_GPL(klist_init);


static void add_head(struct klist * k, struct klist_node * n)
{
	spin_lock(&k->k_lock);
	list_add(&n->n_node, &k->k_list);
	spin_unlock(&k->k_lock);
}

static void add_tail(struct klist * k, struct klist_node * n)
{
	spin_lock(&k->k_lock);
	list_add_tail(&n->n_node, &k->k_list);
	spin_unlock(&k->k_lock);
}


static void klist_node_init(struct klist * k, struct klist_node * n)
{
	INIT_LIST_HEAD(&n->n_node);
	init_completion(&n->n_removed);
	kref_init(&n->n_ref);
	n->n_klist = k;
}


/**
 *	klist_add_head - Initialize a klist_node and add it to front.
 *	@k:	klist it's going on.
 *	@n:	node we're adding.
 */

void klist_add_head(struct klist * k, struct klist_node * n)
{
	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.
 *	@k:	klist it's going on.
 *	@n:	node we're adding.
 */

void klist_add_tail(struct klist * k, struct klist_node * n)
{
	klist_node_init(k, n);
	add_tail(k, n);
}

EXPORT_SYMBOL_GPL(klist_add_tail);


static void klist_release(struct kref * kref)
{
	struct klist_node * n = container_of(kref, struct klist_node, n_ref);
	list_del(&n->n_node);
	complete(&n->n_removed);
}

static int klist_dec_and_del(struct klist_node * n)
{
	return kref_put(&n->n_ref, klist_release);
}


/**
 *	klist_del - Decrement the reference count of node and try to remove.
 *	@n:	node we're deleting.
 */

void klist_del(struct klist_node * n)
{
	struct klist * k = n->n_klist;

	spin_lock(&k->k_lock);
	klist_dec_and_del(n);
	spin_unlock(&k->k_lock);
}

EXPORT_SYMBOL_GPL(klist_del);


/**
 *	klist_remove - Decrement the refcount of node and wait for it to go away.
 *	@n:	node we're removing.
 */

void klist_remove(struct klist_node * n)
{
	spin_lock(&n->n_klist->k_lock);
	klist_dec_and_del(n);
	spin_unlock(&n->n_klist->k_lock);
	wait_for_completion(&n->n_removed);
}

EXPORT_SYMBOL_GPL(klist_remove);


/**
 *	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.
 */

void klist_iter_init_node(struct klist * k, struct klist_iter * i, struct klist_node * n)
{
	i->i_klist = k;
	i->i_head = &k->k_list;
	i->i_cur = n;
}

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.
 */

void klist_iter_init(struct klist * k, struct klist_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.
 */

void klist_iter_exit(struct klist_iter * i)
{
	if (i->i_cur) {
		klist_del(i->i_cur);
		i->i_cur = NULL;
	}
}

EXPORT_SYMBOL_GPL(klist_iter_exit);


static struct klist_node * to_klist_node(struct list_head * n)
{
	return container_of(n, struct klist_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.
 */

struct klist_node * klist_next(struct klist_iter * i)
{
	struct list_head * next;
	struct klist_node * knode = NULL;

	spin_lock(&i->i_klist->k_lock);
	if (i->i_cur) {
		next = i->i_cur->n_node.next;
		klist_dec_and_del(i->i_cur);
	} else
		next = i->i_head->next;

	if (next != i->i_head) {
		knode = to_klist_node(next);
		kref_get(&knode->n_ref);
	}
	i->i_cur = knode;
	spin_unlock(&i->i_klist->k_lock);
	return knode;
}

EXPORT_SYMBOL_GPL(klist_next);