idr: Fix idr_get_next race with idr_remove
[linux-2.6-microblaze.git] / tools / testing / radix-tree / idr-test.c
index 1b63bdb..fe33be4 100644 (file)
@@ -287,6 +287,51 @@ static void idr_align_test(struct idr *idr)
        }
 }
 
+DEFINE_IDR(find_idr);
+
+static void *idr_throbber(void *arg)
+{
+       time_t start = time(NULL);
+       int id = *(int *)arg;
+
+       rcu_register_thread();
+       do {
+               idr_alloc(&find_idr, xa_mk_value(id), id, id + 1, GFP_KERNEL);
+               idr_remove(&find_idr, id);
+       } while (time(NULL) < start + 10);
+       rcu_unregister_thread();
+
+       return NULL;
+}
+
+void idr_find_test_1(int anchor_id, int throbber_id)
+{
+       pthread_t throbber;
+       time_t start = time(NULL);
+
+       pthread_create(&throbber, NULL, idr_throbber, &throbber_id);
+
+       BUG_ON(idr_alloc(&find_idr, xa_mk_value(anchor_id), anchor_id,
+                               anchor_id + 1, GFP_KERNEL) != anchor_id);
+
+       do {
+               int id = 0;
+               void *entry = idr_get_next(&find_idr, &id);
+               BUG_ON(entry != xa_mk_value(id));
+       } while (time(NULL) < start + 11);
+
+       pthread_join(throbber, NULL);
+
+       idr_remove(&find_idr, anchor_id);
+       BUG_ON(!idr_is_empty(&find_idr));
+}
+
+void idr_find_test(void)
+{
+       idr_find_test_1(100000, 0);
+       idr_find_test_1(0, 100000);
+}
+
 void idr_checks(void)
 {
        unsigned long i;
@@ -368,6 +413,7 @@ void idr_checks(void)
        idr_u32_test(1);
        idr_u32_test(0);
        idr_align_test(&idr);
+       idr_find_test();
 }
 
 #define module_init(x)