block: Fix a race between request queue removal and the block cgroup controller

Avoid that the following race can occur:

blk_cleanup_queue()               blkcg_print_blkgs()
  spin_lock_irq(lock) (1)           spin_lock_irq(blkg->q->queue_lock) (2,5)
    q->queue_lock = &q->__queue_lock (3)
  spin_unlock_irq(lock) (4)
                                    spin_unlock_irq(blkg->q->queue_lock) (6)

(1) take driver lock;
(2) busy loop for driver lock;
(3) override driver lock with internal lock;
(4) unlock driver lock;
(5) can take driver lock now;
(6) but unlock internal lock.

This change is safe because only the SCSI core and the NVME core keep
a reference on a request queue after having called blk_cleanup_queue().
Neither driver accesses any of the removed data structures between its
blk_cleanup_queue() and blk_put_queue() calls.

Reported-by: Joseph Qi <[email protected]>
Signed-off-by: Bart Van Assche <[email protected]>
Reviewed-by: Joseph Qi <[email protected]>
Cc: Jan Kara <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
This commit is contained in:
Bart Van Assche 2018-02-28 10:15:33 -08:00 • committed by Jens Axboe
commit a063057d7c
2 changed files with 31 additions and 7 deletions

View file

@ -798,13 +798,6 @@ static void __blk_release_queue(struct work_struct *work)
if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
blk_stat_remove_callback(q, q->poll_cb);
blk_stat_free_callback(q->poll_cb);
bdi_put(q->backing_dev_info);
blkcg_exit_queue(q);
if (q->elevator) {
ioc_clear_queue(q);
elevator_exit(q, q->elevator);
}
blk_free_queue_stats(q->stats);