aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorey Minyard <minyard@acm.org>2007-02-10 04:45:42 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-11 13:51:31 -0500
commit3678d62f028689abc8ac5693b254e48f605f94ba (patch)
tree7a897ae6d308411586c274e23f940b4d280b7ff1
parent16cf5b39b81b95d1e3d81df3ba8c82cadf54f551 (diff)
[PATCH] add an RCU version of list splicing
This patch is in support of the IPMI driver. I have tested this with the IPMI driver changes coming in the next patch. Add a list_splice_init_rcu() function to splice an RCU-protected list into another list. This takes the sync function as an argument, so one would do something like: INIT_LIST_HEAD(&list); list_splice_init_rcu(&source, &dest, synchronize_rcu); The idea being to keep the RCU API proliferation down to a dull roar. [akpm@osdl.org: build fix] Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: Corey Minyard <minyard@acm.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/list.h56
1 files changed, 56 insertions, 0 deletions
diff --git a/include/linux/list.h b/include/linux/list.h
index 611059d633f4..cdc96559e5ae 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -360,6 +360,62 @@ static inline void list_splice_init(struct list_head *list,
360} 360}
361 361
362/** 362/**
363 * list_splice_init_rcu - splice an RCU-protected list into an existing list.
364 * @list: the RCU-protected list to splice
365 * @head: the place in the list to splice the first list into
366 * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
367 *
368 * @head can be RCU-read traversed concurrently with this function.
369 *
370 * Note that this function blocks.
371 *
372 * Important note: the caller must take whatever action is necessary to
373 * prevent any other updates to @head. In principle, it is possible
374 * to modify the list as soon as sync() begins execution.
375 * If this sort of thing becomes necessary, an alternative version
376 * based on call_rcu() could be created. But only if -really-
377 * needed -- there is no shortage of RCU API members.
378 */
379static inline void list_splice_init_rcu(struct list_head *list,
380 struct list_head *head,
381 void (*sync)(void))
382{
383 struct list_head *first = list->next;
384 struct list_head *last = list->prev;
385 struct list_head *at = head->next;
386
387 if (list_empty(head))
388 return;
389
390 /* "first" and "last" tracking list, so initialize it. */
391
392 INIT_LIST_HEAD(list);
393
394 /*
395 * At this point, the list body still points to the source list.
396 * Wait for any readers to finish using the list before splicing
397 * the list body into the new list. Any new readers will see
398 * an empty list.
399 */
400
401 sync();
402
403 /*
404 * Readers are finished with the source list, so perform splice.
405 * The order is important if the new list is global and accessible
406 * to concurrent RCU readers. Note that RCU readers are not
407 * permitted to traverse the prev pointers without excluding
408 * this function.
409 */
410
411 last->next = at;
412 smp_wmb();
413 head->next = first;
414 first->prev = head;
415 at->prev = last;
416}
417
418/**
363 * list_entry - get the struct for this entry 419 * list_entry - get the struct for this entry
364 * @ptr: the &struct list_head pointer. 420 * @ptr: the &struct list_head pointer.
365 * @type: the type of the struct this is embedded in. 421 * @type: the type of the struct this is embedded in.