diff options
author | Jianpeng Ma <majianpeng@gmail.com> | 2013-07-03 07:25:24 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-20 11:43:03 -0400 |
commit | a6ad83fce072869921cef7c6f4e86bd91639dc34 (patch) | |
tree | 4530efd7361b1a8e95229a84ccb2229e1035aba1 /include | |
parent | dead45bd0527751cc9e71c0547d8f19f498441ed (diff) |
elevator: Fix a race in elevator switching
commit d50235b7bc3ee0a0427984d763ea7534149531b4 upstream.
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>
Cc: Jonghwan Choi <jhbird.choi@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/elevator.h | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/include/linux/elevator.h b/include/linux/elevator.h index acd0312d46fb..306dd8cd0b6f 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #ifdef CONFIG_BLOCK | 7 | #ifdef CONFIG_BLOCK |
8 | 8 | ||
9 | struct io_cq; | 9 | struct io_cq; |
10 | struct elevator_type; | ||
10 | 11 | ||
11 | typedef int (elevator_merge_fn) (struct request_queue *, struct request **, | 12 | typedef int (elevator_merge_fn) (struct request_queue *, struct request **, |
12 | struct bio *); | 13 | struct bio *); |
@@ -35,7 +36,8 @@ typedef void (elevator_put_req_fn) (struct request *); | |||
35 | typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *); | 36 | typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *); |
36 | typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *); | 37 | typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *); |
37 | 38 | ||
38 | typedef int (elevator_init_fn) (struct request_queue *); | 39 | typedef int (elevator_init_fn) (struct request_queue *, |
40 | struct elevator_type *e); | ||
39 | typedef void (elevator_exit_fn) (struct elevator_queue *); | 41 | typedef void (elevator_exit_fn) (struct elevator_queue *); |
40 | 42 | ||
41 | struct elevator_ops | 43 | struct elevator_ops |
@@ -155,6 +157,8 @@ extern int elevator_init(struct request_queue *, char *); | |||
155 | extern void elevator_exit(struct elevator_queue *); | 157 | extern void elevator_exit(struct elevator_queue *); |
156 | extern int elevator_change(struct request_queue *, const char *); | 158 | extern int elevator_change(struct request_queue *, const char *); |
157 | extern bool elv_rq_merge_ok(struct request *, struct bio *); | 159 | extern bool elv_rq_merge_ok(struct request *, struct bio *); |
160 | extern struct elevator_queue *elevator_alloc(struct request_queue *, | ||
161 | struct elevator_type *); | ||
158 | 162 | ||
159 | /* | 163 | /* |
160 | * Helper functions. | 164 | * Helper functions. |