diff options
author | Nadia Derbey <Nadia.Derbey@bull.net> | 2008-07-25 10:48:01 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 19:53:42 +0200 |
commit | f9c46d6ea5ce138a886c3a0f10a46130afab75f5 (patch) | |
tree | 1eedc2428866fb0b3f9f864e52ffb6eaf00911a4 | |
parent | idr: make idr_get_new* rcu-safe (diff) | |
download | linux-f9c46d6ea5ce138a886c3a0f10a46130afab75f5.tar.xz linux-f9c46d6ea5ce138a886c3a0f10a46130afab75f5.zip |
idr: make idr_find rcu-safe
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>
-rw-r--r-- | include/linux/idr.h | 16 | ||||
-rw-r--r-- | lib/idr.c | 11 |
2 files changed, 22 insertions, 5 deletions
diff --git a/include/linux/idr.h b/include/linux/idr.h index 762c3f2c631d..fa035f96f2a3 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -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. */ diff --git a/lib/idr.c b/lib/idr.c index 44ab3b2a4eba..21e12af1f231 100644 --- 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) { |