History

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.

Manfred Spraul committed 17 years ago

Commit 6ff2d391 parent 1d678f32 files · +14 −3Browse files

Unified · Split

2 files
  1. include/linux/idr.h +2 −1
  2. lib/idr.c +12 −2
include/linux/idr.h+2−1
@@ -52,13 +52,14 @@ struct idr_layer { · 51 unmodified lines
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};
lib/idr.c+12−2
@@ -185,6 +185,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) · 184 unmodified lines
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: · 19 unmodified lines
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: · 21 unmodified lines
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) · 250 unmodified lines
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) · 72 unmodified lines
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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t