block: properly protect the 'queue' kobj in blk_unregister_queue
authorMike Snitzer <snitzer@redhat.com>
Thu, 11 Jan 2018 19:11:01 +0000 (14:11 -0500)
committerJens Axboe <axboe@kernel.dk>
Mon, 15 Jan 2018 15:41:38 +0000 (08:41 -0700)
commit667257e8b2988c0183ba23e2bcd6900e87961606
treeae0ec92e1fe69f71788169e07789096357f0c4ba
parentbc8d062c36e3525e81ea8237ff0ab3264c2317b6
block: properly protect the 'queue' kobj in blk_unregister_queue

The original commit e9a823fb34a8b (block: fix warning when I/O elevator
is changed as request_queue is being removed) is pretty conflated.
"conflated" because the resource being protected by q->sysfs_lock isn't
the queue_flags (it is the 'queue' kobj).

q->sysfs_lock serializes __elevator_change() (via elv_iosched_store)
from racing with blk_unregister_queue():
1) By holding q->sysfs_lock first, __elevator_change() can complete
before a racing blk_unregister_queue().
2) Conversely, __elevator_change() is testing for QUEUE_FLAG_REGISTERED
in case elv_iosched_store() loses the race with blk_unregister_queue(),
it needs a way to know the 'queue' kobj isn't there.

Expand the scope of blk_unregister_queue()'s q->sysfs_lock use so it is
held until after the 'queue' kobj is removed.

To do so blk_mq_unregister_dev() must not also take q->sysfs_lock.  So
rename __blk_mq_unregister_dev() to blk_mq_unregister_dev().

Also, blk_unregister_queue() should use q->queue_lock to protect against
any concurrent writes to q->queue_flags -- even though chances are the
queue is being cleaned up so no concurrent writes are likely.

Fixes: e9a823fb34a8b ("block: fix warning when I/O elevator is changed as request_queue is being removed")
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-mq-sysfs.c
block/blk-sysfs.c