[PATCH] fix try_module_get race in elevator_find
authorTejun Heo <htejun@gmail.com>
Thu, 20 Oct 2005 08:56:41 +0000 (10:56 +0200)
committerJens Axboe <axboe@nelson.home.kernel.dk>
Fri, 28 Oct 2005 06:15:58 +0000 (08:15 +0200)
This patch removes try_module_get race in elevator_find.
try_module_get should always be called with the spinlock protecting
what the module init/cleanup routines register/unregister to held. In
the case of elevators, we should be holding elv_list to avoid it going
away between spin_unlock_irq and try_module_get.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jens Axboe <axboe@suse.de>
drivers/block/elevator.c

index 98f0126a2deb3228e0866abcb44bf8b2c1af5859..4144f30d82a97aeb6de21d1a87ba463e20e18c1f 100644 (file)
@@ -97,7 +97,6 @@ static struct elevator_type *elevator_find(const char *name)
        struct elevator_type *e = NULL;
        struct list_head *entry;
 
        struct elevator_type *e = NULL;
        struct list_head *entry;
 
-       spin_lock_irq(&elv_list_lock);
        list_for_each(entry, &elv_list) {
                struct elevator_type *__e;
 
        list_for_each(entry, &elv_list) {
                struct elevator_type *__e;
 
@@ -108,7 +107,6 @@ static struct elevator_type *elevator_find(const char *name)
                        break;
                }
        }
                        break;
                }
        }
-       spin_unlock_irq(&elv_list_lock);
 
        return e;
 }
 
        return e;
 }
@@ -120,12 +118,15 @@ static void elevator_put(struct elevator_type *e)
 
 static struct elevator_type *elevator_get(const char *name)
 {
 
 static struct elevator_type *elevator_get(const char *name)
 {
-       struct elevator_type *e = elevator_find(name);
+       struct elevator_type *e;
 
 
-       if (!e)
-               return NULL;
-       if (!try_module_get(e->elevator_owner))
-               return NULL;
+       spin_lock_irq(&elv_list_lock);
+
+       e = elevator_find(name);
+       if (e && !try_module_get(e->elevator_owner))
+               e = NULL;
+
+       spin_unlock_irq(&elv_list_lock);
 
        return e;
 }
 
        return e;
 }
@@ -153,11 +154,15 @@ static char chosen_elevator[16];
 
 static void elevator_setup_default(void)
 {
 
 static void elevator_setup_default(void)
 {
+       struct elevator_type *e;
+
        /*
         * check if default is set and exists
         */
        /*
         * check if default is set and exists
         */
-       if (chosen_elevator[0] && elevator_find(chosen_elevator))
+       if (chosen_elevator[0] && (e = elevator_get(chosen_elevator))) {
+               elevator_put(e);
                return;
                return;
+       }
 
 #if defined(CONFIG_IOSCHED_AS)
        strcpy(chosen_elevator, "anticipatory");
 
 #if defined(CONFIG_IOSCHED_AS)
        strcpy(chosen_elevator, "anticipatory");
@@ -555,10 +560,9 @@ void elv_unregister_queue(struct request_queue *q)
 
 int elv_register(struct elevator_type *e)
 {
 
 int elv_register(struct elevator_type *e)
 {
+       spin_lock_irq(&elv_list_lock);
        if (elevator_find(e->elevator_name))
                BUG();
        if (elevator_find(e->elevator_name))
                BUG();
-
-       spin_lock_irq(&elv_list_lock);
        list_add_tail(&e->list, &elv_list);
        spin_unlock_irq(&elv_list_lock);
 
        list_add_tail(&e->list, &elv_list);
        spin_unlock_irq(&elv_list_lock);