summaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2005-10-20 10:56:41 +0200
committerJens Axboe <axboe@nelson.home.kernel.dk>2005-10-28 08:15:58 +0200
commit2824bc9328467127083c1325f54b67d298c333b2 (patch)
tree0d19aeb555c35ddbdbcf195b976964523fecc404 /drivers/block
parentFollowing the same idea, it occurs to me that we should only update (diff)
downloadlinux-2824bc9328467127083c1325f54b67d298c333b2.tar.xz
linux-2824bc9328467127083c1325f54b67d298c333b2.zip
[PATCH] fix try_module_get race in elevator_find
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>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/elevator.c24
1 files changed, 14 insertions, 10 deletions
diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c
index 98f0126a2deb..4144f30d82a9 100644
--- a/drivers/block/elevator.c
+++ b/drivers/block/elevator.c
@@ -97,7 +97,6 @@ static struct elevator_type *elevator_find(const char *name)
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;
@@ -108,7 +107,6 @@ static struct elevator_type *elevator_find(const char *name)
break;
}
}
- spin_unlock_irq(&elv_list_lock);
return e;
}
@@ -120,12 +118,15 @@ static void elevator_put(struct elevator_type *e)
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;
}
@@ -153,11 +154,15 @@ static char chosen_elevator[16];
static void elevator_setup_default(void)
{
+ struct elevator_type *e;
+
/*
* 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;
+ }
#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)
{
+ spin_lock_irq(&elv_list_lock);
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);