idr: make idr_find rcu-safe
authorNadia Derbey <Nadia.Derbey@bull.net>
Fri, 25 Jul 2008 08:48:01 +0000 (01:48 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 25 Jul 2008 17:53:42 +0000 (10:53 -0700)
Make idr_find rcu-safe: it can now be called inside an rcu_read critical
section.

Signed-off-by: Nadia Derbey <Nadia.Derbey@bull.net>
Reviewed-by: "Paul E. McKenney" <paulmck@us.ibm.com>
Cc: Manfred Spraul <manfred@colorfullife.com>
Cc: Jim Houston <jim.houston@comcast.net>
Cc: Pierre Peiffer <peifferp@gmail.com>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/idr.h
lib/idr.c

index 762c3f2c631d25c18005277fe5e4ccd2d6694ade..fa035f96f2a3b340c468e4b58d9010df3b21be69 100644 (file)
@@ -79,6 +79,22 @@ struct idr {
 
 #define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC)
 
+/**
+ * idr synchronization (stolen from radix-tree.h)
+ *
+ * idr_find() is able to be called locklessly, using RCU. The caller must
+ * ensure calls to this function are made within rcu_read_lock() regions.
+ * Other readers (lock-free or otherwise) and modifications may be running
+ * concurrently.
+ *
+ * It is still required that the caller manage the synchronization and
+ * lifetimes of the items. So if RCU lock-free lookups are used, typically
+ * this would mean that the items have their own locks, or are amenable to
+ * lock-free access; and that the items are freed by RCU (or only freed after
+ * having been deleted from the idr tree *and* a synchronize_rcu() grace
+ * period).
+ */
+
 /*
  * This is what we export.
  */
index 44ab3b2a4eba44cde95972d38687dcc427607b4b..21e12af1f231232a44197261c242b547874885df 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -456,7 +456,8 @@ EXPORT_SYMBOL(idr_destroy);
  * return indicates that @id is not valid or you passed %NULL in
  * idr_get_new().
  *
- * The caller must serialize idr_find() vs idr_get_new() and idr_remove().
+ * This function can be called under rcu_read_lock(), given that the leaf
+ * pointers lifetimes are correctly managed.
  */
 void *idr_find(struct idr *idp, int id)
 {
@@ -464,7 +465,7 @@ void *idr_find(struct idr *idp, int id)
        struct idr_layer *p;
 
        n = idp->layers * IDR_BITS;
-       p = idp->top;
+       p = rcu_dereference(idp->top);
 
        /* Mask off upper bits we don't use for the search. */
        id &= MAX_ID_MASK;
@@ -474,7 +475,7 @@ void *idr_find(struct idr *idp, int id)
 
        while (n > 0 && p) {
                n -= IDR_BITS;
-               p = p->ary[(id >> n) & IDR_MASK];
+               p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
        }
        return((void *)p);
 }
@@ -507,7 +508,7 @@ int idr_for_each(struct idr *idp,
        struct idr_layer **paa = &pa[0];
 
        n = idp->layers * IDR_BITS;
-       p = idp->top;
+       p = rcu_dereference(idp->top);
        max = 1 << n;
 
        id = 0;
@@ -515,7 +516,7 @@ int idr_for_each(struct idr *idp,
                while (n > 0 && p) {
                        n -= IDR_BITS;
                        *paa++ = p;
-                       p = p->ary[(id >> n) & IDR_MASK];
+                       p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
                }
 
                if (p) {