diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/klist.c | 43 |
1 files changed, 40 insertions, 3 deletions
diff --git a/lib/klist.c b/lib/klist.c index bbdd3015c2c7..573d6068a42e 100644 --- a/lib/klist.c +++ b/lib/klist.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | 36 | ||
| 37 | #include <linux/klist.h> | 37 | #include <linux/klist.h> |
| 38 | #include <linux/module.h> | 38 | #include <linux/module.h> |
| 39 | #include <linux/sched.h> | ||
| 39 | 40 | ||
| 40 | /* | 41 | /* |
| 41 | * Use the lowest bit of n_klist to mark deleted nodes and exclude | 42 | * Use the lowest bit of n_klist to mark deleted nodes and exclude |
| @@ -108,7 +109,6 @@ static void add_tail(struct klist *k, struct klist_node *n) | |||
| 108 | static void klist_node_init(struct klist *k, struct klist_node *n) | 109 | static void klist_node_init(struct klist *k, struct klist_node *n) |
| 109 | { | 110 | { |
| 110 | INIT_LIST_HEAD(&n->n_node); | 111 | INIT_LIST_HEAD(&n->n_node); |
| 111 | init_completion(&n->n_removed); | ||
| 112 | kref_init(&n->n_ref); | 112 | kref_init(&n->n_ref); |
| 113 | knode_set_klist(n, k); | 113 | knode_set_klist(n, k); |
| 114 | if (k->get) | 114 | if (k->get) |
| @@ -171,13 +171,34 @@ void klist_add_before(struct klist_node *n, struct klist_node *pos) | |||
| 171 | } | 171 | } |
| 172 | EXPORT_SYMBOL_GPL(klist_add_before); | 172 | EXPORT_SYMBOL_GPL(klist_add_before); |
| 173 | 173 | ||
| 174 | struct klist_waiter { | ||
| 175 | struct list_head list; | ||
| 176 | struct klist_node *node; | ||
| 177 | struct task_struct *process; | ||
| 178 | int woken; | ||
| 179 | }; | ||
| 180 | |||
| 181 | static DEFINE_SPINLOCK(klist_remove_lock); | ||
| 182 | static LIST_HEAD(klist_remove_waiters); | ||
| 183 | |||
| 174 | static void klist_release(struct kref *kref) | 184 | static void klist_release(struct kref *kref) |
| 175 | { | 185 | { |
| 186 | struct klist_waiter *waiter, *tmp; | ||
| 176 | struct klist_node *n = container_of(kref, struct klist_node, n_ref); | 187 | struct klist_node *n = container_of(kref, struct klist_node, n_ref); |
| 177 | 188 | ||
| 178 | WARN_ON(!knode_dead(n)); | 189 | WARN_ON(!knode_dead(n)); |
| 179 | list_del(&n->n_node); | 190 | list_del(&n->n_node); |
| 180 | complete(&n->n_removed); | 191 | spin_lock(&klist_remove_lock); |
| 192 | list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) { | ||
| 193 | if (waiter->node != n) | ||
| 194 | continue; | ||
| 195 | |||
| 196 | waiter->woken = 1; | ||
| 197 | mb(); | ||
| 198 | wake_up_process(waiter->process); | ||
| 199 | list_del(&waiter->list); | ||
| 200 | } | ||
| 201 | spin_unlock(&klist_remove_lock); | ||
| 181 | knode_set_klist(n, NULL); | 202 | knode_set_klist(n, NULL); |
| 182 | } | 203 | } |
| 183 | 204 | ||
| @@ -217,8 +238,24 @@ EXPORT_SYMBOL_GPL(klist_del); | |||
| 217 | */ | 238 | */ |
| 218 | void klist_remove(struct klist_node *n) | 239 | void klist_remove(struct klist_node *n) |
| 219 | { | 240 | { |
| 241 | struct klist_waiter waiter; | ||
| 242 | |||
| 243 | waiter.node = n; | ||
| 244 | waiter.process = current; | ||
| 245 | waiter.woken = 0; | ||
| 246 | spin_lock(&klist_remove_lock); | ||
| 247 | list_add(&waiter.list, &klist_remove_waiters); | ||
| 248 | spin_unlock(&klist_remove_lock); | ||
| 249 | |||
| 220 | klist_del(n); | 250 | klist_del(n); |
| 221 | wait_for_completion(&n->n_removed); | 251 | |
| 252 | for (;;) { | ||
| 253 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 254 | if (waiter.woken) | ||
| 255 | break; | ||
| 256 | schedule(); | ||
| 257 | } | ||
| 258 | __set_current_state(TASK_RUNNING); | ||
| 222 | } | 259 | } |
| 223 | EXPORT_SYMBOL_GPL(klist_remove); | 260 | EXPORT_SYMBOL_GPL(klist_remove); |
| 224 | 261 | ||
