diff options
author | Jianpeng Ma <majianpeng@gmail.com> | 2013-07-03 07:25:24 -0400 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2013-07-03 07:25:24 -0400 |
commit | d50235b7bc3ee0a0427984d763ea7534149531b4 (patch) | |
tree | acf1916e7926c1a0dddbe08db11ca2426a3816cc /block/cfq-iosched.c | |
parent | a6b3f7614ca690e49e934c291f707b0c19312194 (diff) |
elevator: Fix a race in elevator switching
There's a race between elevator switching and normal io operation.
Because the allocation of struct elevator_queue and struct elevator_data
don't in a atomic operation.So there are have chance to use NULL
->elevator_data.
For example:
Thread A: Thread B
blk_queu_bio elevator_switch
spin_lock_irq(q->queue_block) elevator_alloc
elv_merge elevator_init_fn
Because call elevator_alloc, it can't hold queue_lock and the
->elevator_data is NULL.So at the same time, threadA call elv_merge and
nedd some info of elevator_data.So the crash happened.
Move the elevator_alloc into func elevator_init_fn, it make the
operations in a atomic operation.
Using the follow method can easy reproduce this bug
1:dd if=/dev/sdb of=/dev/null
2:while true;do echo noop > scheduler;echo deadline > scheduler;done
The test method also use this method.
Signed-off-by: Jianpeng Ma <majianpeng@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/cfq-iosched.c')
-rw-r--r-- | block/cfq-iosched.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d5cd3131c57a..d5bbdcfd0dab 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
@@ -4347,18 +4347,28 @@ static void cfq_exit_queue(struct elevator_queue *e) | |||
4347 | kfree(cfqd); | 4347 | kfree(cfqd); |
4348 | } | 4348 | } |
4349 | 4349 | ||
4350 | static int cfq_init_queue(struct request_queue *q) | 4350 | static int cfq_init_queue(struct request_queue *q, struct elevator_type *e) |
4351 | { | 4351 | { |
4352 | struct cfq_data *cfqd; | 4352 | struct cfq_data *cfqd; |
4353 | struct blkcg_gq *blkg __maybe_unused; | 4353 | struct blkcg_gq *blkg __maybe_unused; |
4354 | int i, ret; | 4354 | int i, ret; |
4355 | struct elevator_queue *eq; | ||
4356 | |||
4357 | eq = elevator_alloc(q, e); | ||
4358 | if (!eq) | ||
4359 | return -ENOMEM; | ||
4355 | 4360 | ||
4356 | cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node); | 4361 | cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node); |
4357 | if (!cfqd) | 4362 | if (!cfqd) { |
4363 | kobject_put(&eq->kobj); | ||
4358 | return -ENOMEM; | 4364 | return -ENOMEM; |
4365 | } | ||
4366 | eq->elevator_data = cfqd; | ||
4359 | 4367 | ||
4360 | cfqd->queue = q; | 4368 | cfqd->queue = q; |
4361 | q->elevator->elevator_data = cfqd; | 4369 | spin_lock_irq(q->queue_lock); |
4370 | q->elevator = eq; | ||
4371 | spin_unlock_irq(q->queue_lock); | ||
4362 | 4372 | ||
4363 | /* Init root service tree */ | 4373 | /* Init root service tree */ |
4364 | cfqd->grp_service_tree = CFQ_RB_ROOT; | 4374 | cfqd->grp_service_tree = CFQ_RB_ROOT; |
@@ -4433,6 +4443,7 @@ static int cfq_init_queue(struct request_queue *q) | |||
4433 | 4443 | ||
4434 | out_free: | 4444 | out_free: |
4435 | kfree(cfqd); | 4445 | kfree(cfqd); |
4446 | kobject_put(&eq->kobj); | ||
4436 | return ret; | 4447 | return ret; |
4437 | } | 4448 | } |
4438 | 4449 | ||