diff options
author | Bart Van Assche <bvanassche@acm.org> | 2012-11-28 07:45:56 -0500 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2012-12-06 08:32:30 -0500 |
commit | 704605711ef048a7c6ad2ec599f15d2e0baf86b2 (patch) | |
tree | 16620b55d00b289a9a343d1d2c150684fbb1661c /block/blk-core.c | |
parent | c246e80d86736312933646896c4157daf511dadc (diff) |
block: Avoid scheduling delayed work on a dead queue
Running a queue must continue after it has been marked dying until
it has been marked dead. So the function blk_run_queue_async() must
not schedule delayed work after blk_cleanup_queue() has marked a queue
dead. Hence add a test for that queue state in blk_run_queue_async()
and make sure that queue_unplugged() invokes that function with the
queue lock held. This avoids that the queue state can change after
it has been tested and before mod_delayed_work() is invoked. Drop
the queue dying test in queue_unplugged() since it is now
superfluous: __blk_run_queue() already tests whether or not the
queue is dead.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Cc: Mike Christie <michaelc@cs.wisc.edu>
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/blk-core.c')
-rw-r--r-- | block/blk-core.c | 33 |
1 files changed, 9 insertions, 24 deletions
diff --git a/block/blk-core.c b/block/blk-core.c index f52d05ff5d24..9fb23537c7ad 100644 --- a/block/blk-core.c +++ b/block/blk-core.c | |||
@@ -219,12 +219,13 @@ static void blk_delay_work(struct work_struct *work) | |||
219 | * Description: | 219 | * Description: |
220 | * Sometimes queueing needs to be postponed for a little while, to allow | 220 | * Sometimes queueing needs to be postponed for a little while, to allow |
221 | * resources to come back. This function will make sure that queueing is | 221 | * resources to come back. This function will make sure that queueing is |
222 | * restarted around the specified time. | 222 | * restarted around the specified time. Queue lock must be held. |
223 | */ | 223 | */ |
224 | void blk_delay_queue(struct request_queue *q, unsigned long msecs) | 224 | void blk_delay_queue(struct request_queue *q, unsigned long msecs) |
225 | { | 225 | { |
226 | queue_delayed_work(kblockd_workqueue, &q->delay_work, | 226 | if (likely(!blk_queue_dead(q))) |
227 | msecs_to_jiffies(msecs)); | 227 | queue_delayed_work(kblockd_workqueue, &q->delay_work, |
228 | msecs_to_jiffies(msecs)); | ||
228 | } | 229 | } |
229 | EXPORT_SYMBOL(blk_delay_queue); | 230 | EXPORT_SYMBOL(blk_delay_queue); |
230 | 231 | ||
@@ -334,11 +335,11 @@ EXPORT_SYMBOL(__blk_run_queue); | |||
334 | * | 335 | * |
335 | * Description: | 336 | * Description: |
336 | * Tells kblockd to perform the equivalent of @blk_run_queue on behalf | 337 | * Tells kblockd to perform the equivalent of @blk_run_queue on behalf |
337 | * of us. | 338 | * of us. The caller must hold the queue lock. |
338 | */ | 339 | */ |
339 | void blk_run_queue_async(struct request_queue *q) | 340 | void blk_run_queue_async(struct request_queue *q) |
340 | { | 341 | { |
341 | if (likely(!blk_queue_stopped(q))) | 342 | if (likely(!blk_queue_stopped(q) && !blk_queue_dead(q))) |
342 | mod_delayed_work(kblockd_workqueue, &q->delay_work, 0); | 343 | mod_delayed_work(kblockd_workqueue, &q->delay_work, 0); |
343 | } | 344 | } |
344 | EXPORT_SYMBOL(blk_run_queue_async); | 345 | EXPORT_SYMBOL(blk_run_queue_async); |
@@ -2913,27 +2914,11 @@ static void queue_unplugged(struct request_queue *q, unsigned int depth, | |||
2913 | { | 2914 | { |
2914 | trace_block_unplug(q, depth, !from_schedule); | 2915 | trace_block_unplug(q, depth, !from_schedule); |
2915 | 2916 | ||
2916 | /* | 2917 | if (from_schedule) |
2917 | * Don't mess with a dying queue. | ||
2918 | */ | ||
2919 | if (unlikely(blk_queue_dying(q))) { | ||
2920 | spin_unlock(q->queue_lock); | ||
2921 | return; | ||
2922 | } | ||
2923 | |||
2924 | /* | ||
2925 | * If we are punting this to kblockd, then we can safely drop | ||
2926 | * the queue_lock before waking kblockd (which needs to take | ||
2927 | * this lock). | ||
2928 | */ | ||
2929 | if (from_schedule) { | ||
2930 | spin_unlock(q->queue_lock); | ||
2931 | blk_run_queue_async(q); | 2918 | blk_run_queue_async(q); |
2932 | } else { | 2919 | else |
2933 | __blk_run_queue(q); | 2920 | __blk_run_queue(q); |
2934 | spin_unlock(q->queue_lock); | 2921 | spin_unlock(q->queue_lock); |
2935 | } | ||
2936 | |||
2937 | } | 2922 | } |
2938 | 2923 | ||
2939 | static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) | 2924 | static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) |