aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManfred Spraul <manfred@colorfullife.com>2008-12-01 16:14:02 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-12-01 22:55:25 -0500
commit6ff2d39b91aec3dcae951afa982059e3dd9b49dc (patch)
tree8d480975d95adf85f3a87dd478e36e4ac0b0edd0
parent1d678f365dae28420fa7329a2a35390b3582678d (diff)
lib/idr.c: fix rcu related race with idr_find
2nd part of the fixes needed for http://bugzilla.kernel.org/show_bug.cgi?id=11796. When the idr tree is either grown or shrunk, then the update to the number of layers and the top pointer were not atomic. This race caused crashes. The attached patch fixes that by replicating the layers counter in each layer, thus idr_find doesn't need idp->layers anymore. Signed-off-by: Manfred Spraul <manfred@colorfullife.com> Cc: Clement Calmels <cboulte@gmail.com> Cc: Nadia Derbey <Nadia.Derbey@bull.net> Cc: Pierre Peiffer <peifferp@gmail.com> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/idr.h3
-rw-r--r--lib/idr.c14
2 files changed, 14 insertions, 3 deletions
diff --git a/include/linux/idr.h b/include/linux/idr.h
index fa035f96f2a3..dd846df8cd32 100644
--- a/include/linux/idr.h
+++ b/include/linux/idr.h
@@ -52,13 +52,14 @@ struct idr_layer {
52 unsigned long bitmap; /* A zero bit means "space here" */ 52 unsigned long bitmap; /* A zero bit means "space here" */
53 struct idr_layer *ary[1<<IDR_BITS]; 53 struct idr_layer *ary[1<<IDR_BITS];
54 int count; /* When zero, we can release it */ 54 int count; /* When zero, we can release it */
55 int layer; /* distance from leaf */
55 struct rcu_head rcu_head; 56 struct rcu_head rcu_head;
56}; 57};
57 58
58struct idr { 59struct idr {
59 struct idr_layer *top; 60 struct idr_layer *top;
60 struct idr_layer *id_free; 61 struct idr_layer *id_free;
61 int layers; 62 int layers; /* only valid without concurrent changes */
62 int id_free_cnt; 63 int id_free_cnt;
63 spinlock_t lock; 64 spinlock_t lock;
64}; 65};
diff --git a/lib/idr.c b/lib/idr.c
index e728c7fccc4d..7a785a0c2ea0 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -185,6 +185,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa)
185 new = get_from_free_list(idp); 185 new = get_from_free_list(idp);
186 if (!new) 186 if (!new)
187 return -1; 187 return -1;
188 new->layer = l-1;
188 rcu_assign_pointer(p->ary[m], new); 189 rcu_assign_pointer(p->ary[m], new);
189 p->count++; 190 p->count++;
190 } 191 }
@@ -210,6 +211,7 @@ build_up:
210 if (unlikely(!p)) { 211 if (unlikely(!p)) {
211 if (!(p = get_from_free_list(idp))) 212 if (!(p = get_from_free_list(idp)))
212 return -1; 213 return -1;
214 p->layer = 0;
213 layers = 1; 215 layers = 1;
214 } 216 }
215 /* 217 /*
@@ -237,6 +239,7 @@ build_up:
237 } 239 }
238 new->ary[0] = p; 240 new->ary[0] = p;
239 new->count = 1; 241 new->count = 1;
242 new->layer = layers-1;
240 if (p->bitmap == IDR_FULL) 243 if (p->bitmap == IDR_FULL)
241 __set_bit(0, &new->bitmap); 244 __set_bit(0, &new->bitmap);
242 p = new; 245 p = new;
@@ -493,17 +496,21 @@ void *idr_find(struct idr *idp, int id)
493 int n; 496 int n;
494 struct idr_layer *p; 497 struct idr_layer *p;
495 498
496 n = idp->layers * IDR_BITS;
497 p = rcu_dereference(idp->top); 499 p = rcu_dereference(idp->top);
500 if (!p)
501 return NULL;
502 n = (p->layer+1) * IDR_BITS;
498 503
499 /* Mask off upper bits we don't use for the search. */ 504 /* Mask off upper bits we don't use for the search. */
500 id &= MAX_ID_MASK; 505 id &= MAX_ID_MASK;
501 506
502 if (id >= (1 << n)) 507 if (id >= (1 << n))
503 return NULL; 508 return NULL;
509 BUG_ON(n == 0);
504 510
505 while (n > 0 && p) { 511 while (n > 0 && p) {
506 n -= IDR_BITS; 512 n -= IDR_BITS;
513 BUG_ON(n != p->layer*IDR_BITS);
507 p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]); 514 p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
508 } 515 }
509 return((void *)p); 516 return((void *)p);
@@ -582,8 +589,11 @@ void *idr_replace(struct idr *idp, void *ptr, int id)
582 int n; 589 int n;
583 struct idr_layer *p, *old_p; 590 struct idr_layer *p, *old_p;
584 591
585 n = idp->layers * IDR_BITS;
586 p = idp->top; 592 p = idp->top;
593 if (!p)
594 return ERR_PTR(-EINVAL);
595
596 n = (p->layer+1) * IDR_BITS;
587 597
588 id &= MAX_ID_MASK; 598 id &= MAX_ID_MASK;
589 599