block/dm: allow DM to defer blk_register_queue() until ready
Subsystems (2)
v5 cover block/dm: allow DM to defer blk_register_queue() until ready
Hi Jens,
I'm submitting this v5 with more feeling now ;)
I've distilled the changes down to be quite minimal. Hopefully this will help you and others review.
I've also done a dry-run of applying 4.16 block changes and then merging dm-4.16; no merge conflicts:
https://git.kernel.org/pub/scm/linux/kernel/git/snitzer/linux.git/log/?h=block-4.16
https://git.kernel.org/pub/scm/linux/kernel/git/snitzer/linux.git/log/?h=dm-4.16
https://git.kernel.org/pub/scm/linux/kernel/git/snitzer/linux.git/log/?h=block-4.16_dm-4.16
And all tests very well for me.
Please consider for 4.16, thanks!
Mike
Mike Snitzer (4):
block: only bdi_unregister() in del_gendisk() if !GENHD_FL_HIDDEN
block: properly protect the 'queue' kobj in blk_unregister_queue
block: allow gendisk's request_queue registration to be deferred
dm: fix incomplete request_queue initialization
block/blk-sysfs.c | 18 +++++++++++++++--- block/genhd.c | 23 +++++++++++++++++++---- drivers/md/dm-rq.c | 9 --------- drivers/md/dm.c | 11 ++++++++++- include/linux/genhd.h | 5 +++++ 5 files changed, 49 insertions(+), 17 deletions(-)
Signature · 1 line
2.15.0
PATCH v5 1/4 block: only bdi_unregister() in del_gendisk() if !GENHD_FL_HIDDEN
device_add_disk() will only call bdi_register_owner() if !GENHD_FL_HIDDEN, so it follows that del_gendisk() should only call bdi_unregister() if !GENHD_FL_HIDDEN.
Found with code inspection. bdi_unregister() won't do any harm if bdi_register_owner() wasn't used but best to avoid the unnecessary call to bdi_unregister().
--- block/genhd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
block/genhd.c +2 -1
@@ -725,7 +725,8 @@ void del_gendisk(struct gendisk *disk)* Unregister bdi before releasing device numbers (as they can * get reused and we'd get clashes in sysfs). */ -bdi_unregister (disk->queue->backing_dev_info); +if (!(disk->flags &GENHD_FL_HIDDEN )) +bdi_unregister (disk->queue->backing_dev_info);blk_unregister_queue (disk); }else {WARN_ON (1 );
Signature · 1 line
2.15.0
PATCH v5 2/4 block: properly protect the 'queue' kobj in blk_unregister_queue
In reply to Mike SnitzerThe 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.
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.
--- block/blk-sysfs.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
block/blk-sysfs.c +10 -3
@@ -929,12 +929,17 @@ void blk_unregister_queue(struct gendisk *disk)if (WARN_ON (!q))return ; +/* +* Protect against the 'queue' kobj being accessed +* while/after it is removed. +*/ mutex_lock (&q->sysfs_lock); -queue_flag_clear_unlocked (QUEUE_FLAG_REGISTERED , q); -mutex_unlock (&q->sysfs_lock); -wbt_exit (q); +spin_lock_irq (q->queue_lock); +queue_flag_clear (QUEUE_FLAG_REGISTERED , q); +spin_unlock_irq (q->queue_lock); +wbt_exit (q);if (q->mq_ops)blk_mq_unregister_dev (disk_to_dev (disk), q);
@@ -946,4 +951,6 @@ void blk_unregister_queue(struct gendisk *disk)kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); + +mutex_unlock (&q->sysfs_lock); }
Signature · 1 line
2.15.0
Re: [for-4.16 PATCH v5 2/4] block: properly protect the 'queue' kobj in blk_unregister_queue
Quoted 13 lines · Mike Snitzer
The original commit
e9a823fb34a8b(block: fix warning when I/O elevator is changed asrequest_queueis being removed) is pretty conflated. "conflated" because the resource being protected byq->sysfs_lockisn't thequeue_flags(it is the 'queue' kobj).
q->sysfs_lockserializes__elevator_change()(viaelv_iosched_store) from racing withblk_unregister_queue():
1) By holdingq->sysfs_lockfirst,__elevator_change()can complete before a racingblk_unregister_queue().
2) Conversely,__elevator_change()is testing forQUEUE_FLAG_REGISTEREDin caseelv_iosched_store()loses the race withblk_unregister_queue(), it needs a way to know the 'queue' kobj isn't there.
Quoted 2 lines
Expand the scope of blk_unregister_queue()'s
q->sysfs_lockuse so it is held until after the 'queue' kobj is removed.
This way will cause deadlock, see blow.
Quoted 26 lines
Also,
blk_unregister_queue()should useq->queue_lockto protect against any concurrent writes toq->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 <[email protected]>--- block/blk-sysfs.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)block/blk-sysfs.c +4
-2@@ -929,12 +929,17 @@ void blk_unregister_queue(struct gendisk *disk)if (WARN_ON (!q))return ; +/* +* Protect against the 'queue' kobj being accessed +* while/after it is removed. +*/ mutex_lock (&q->sysfs_lock); -queue_flag_clear_unlocked (QUEUE_FLAG_REGISTERED , q); -mutex_unlock (&q->sysfs_lock);
block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 945
Quoted 10 lines
block/blk-sysfs.c +4
-1-wbt_exit (q); +spin_lock_irq (q->queue_lock); +queue_flag_clear (QUEUE_FLAG_REGISTERED , q); +spin_unlock_irq (q->queue_lock); +wbt_exit (q);if (q->mq_ops)blk_mq_unregister_dev (disk_to_dev (disk), q);
void blk_mq_unregister_dev(struct device *dev, struct request_queue *q)
{
mutex_lock(&q->sysfs_lock);
__blk_mq_unregister_dev(dev, q);
mutex_unlock(&q->sysfs_lock);
}block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 956
Quoted 7 lines
block/blk-sysfs.c +2
-0@@ -946,4 +951,6 @@ void blk_unregister_queue(struct gendisk *disk)kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); + +mutex_unlock (&q->sysfs_lock); }
Except for above, I remember there is also lockdep warning between sysfs_lock and driver core's lock if the sysfs_lock is extended in this way(I tried it before, but forget the details now), so please just hold queue_lock inside the sysfs lock.
Thanks,
Ming
Re: [for-4.16 PATCH v5 2/4] block: properly protect the 'queue' kobj in blk_unregister_queue
Quoted 14 lines · Ming Lei
Quoted 13 lines · Mike Snitzer
The original commit
e9a823fb34a8b(block: fix warning when I/O elevator is changed asrequest_queueis being removed) is pretty conflated. "conflated" because the resource being protected byq->sysfs_lockisn't thequeue_flags(it is the 'queue' kobj).
q->sysfs_lockserializes__elevator_change()(viaelv_iosched_store) from racing withblk_unregister_queue():
1) By holdingq->sysfs_lockfirst,__elevator_change()can complete before a racingblk_unregister_queue().
2) Conversely,__elevator_change()is testing forQUEUE_FLAG_REGISTEREDin caseelv_iosched_store()loses the race withblk_unregister_queue(), it needs a way to know the 'queue' kobj isn't there.
Quoted 4 lines
Quoted 2 lines
Expand the scope of blk_unregister_queue()'s
q->sysfs_lockuse so it is held until after the 'queue' kobj is removed.This way will cause deadlock, see blow.
Ngh... I thought I tested blk-mq with this patch applied, apparently not.
Quoted 44 lines
Quoted 26 lines
Also,
blk_unregister_queue()should useq->queue_lockto protect against any concurrent writes toq->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 <[email protected]>--- block/blk-sysfs.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)block/blk-sysfs.c +4
-2@@ -929,12 +929,17 @@ void blk_unregister_queue(struct gendisk *disk)if (WARN_ON (!q))return ; +/* +* Protect against the 'queue' kobj being accessed +* while/after it is removed. +*/ mutex_lock (&q->sysfs_lock); -queue_flag_clear_unlocked (QUEUE_FLAG_REGISTERED , q); -mutex_unlock (&q->sysfs_lock);block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 945
Quoted 10 lines
block/blk-sysfs.c +4
-1-wbt_exit (q); +spin_lock_irq (q->queue_lock); +queue_flag_clear (QUEUE_FLAG_REGISTERED , q); +spin_unlock_irq (q->queue_lock); +wbt_exit (q);if (q->mq_ops)blk_mq_unregister_dev (disk_to_dev (disk), q);void
blk_mq_unregister_dev(struct device *dev, struct request_queue *q){ mutex_lock(&q->sysfs_lock); __blk_mq_unregister_dev(dev, q); mutex_unlock(&q->sysfs_lock); }
Quoted 12 lines
block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 956
Quoted 7 lines
block/blk-sysfs.c +2
-0@@ -946,4 +951,6 @@ void blk_unregister_queue(struct gendisk *disk)kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); + +mutex_unlock (&q->sysfs_lock); }Except for above, I remember there is also lockdep warning between
sysfs_lockand driver core's lock if thesysfs_lockis extended in thisway(I tried it before, but forget the details now), so please just holdqueue_lockinside the sysfs lock.
No good deed goes unpunished.
I'll fix this up.
Mike
PATCH v6 2/4 block: properly protect the 'queue' kobj in blk_unregister_queue
In reply to Mike SnitzerThe 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.
--- block/blk-mq-sysfs.c | 9 +-------- block/blk-sysfs.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 11 deletions(-)
v6: blk_mq_unregister_dev now requires q->sysfs_lock be held, Ming: I am not seeing any lockdep complaints with this. I've tested bio-based, blk-mq and old .request_fn request-based.
block/blk-mq-sysfs.c +1 -8
@@ -248,7 +248,7 @@ static int blk_mq_register_hctx(struct blk_mq_hw_ctx *hctx)return ret; } -static void __blk_mq_unregister_dev (struct device *dev,struct request_queue *q) +void blk_mq_unregister_dev (struct device *dev,struct request_queue *q) {struct blk_mq_hw_ctx *hctx;int i;
@@ -265,13 +265,6 @@ static void __blk_mq_unregister_dev(struct device *dev, struct request_queue *q) q->mq_sysfs_init_done = false; } -void blk_mq_unregister_dev (struct device *dev,struct request_queue *q) -{ -mutex_lock (&q->sysfs_lock); -__blk_mq_unregister_dev (dev, q); -mutex_unlock (&q->sysfs_lock); -} -void blk_mq_hctx_kobj_init (struct blk_mq_hw_ctx *hctx) {kobject_init (&hctx->kobj, &blk_mq_hw_ktype);
block/blk-sysfs.c +10 -3
@@ -929,12 +929,17 @@ void blk_unregister_queue(struct gendisk *disk)if (WARN_ON (!q))return ; +/* +* Protect against the 'queue' kobj being accessed +* while/after it is removed. +*/ mutex_lock (&q->sysfs_lock); -queue_flag_clear_unlocked (QUEUE_FLAG_REGISTERED , q); -mutex_unlock (&q->sysfs_lock); -wbt_exit (q); +spin_lock_irq (q->queue_lock); +queue_flag_clear (QUEUE_FLAG_REGISTERED , q); +spin_unlock_irq (q->queue_lock); +wbt_exit (q);if (q->mq_ops)blk_mq_unregister_dev (disk_to_dev (disk), q);
@@ -946,4 +951,6 @@ void blk_unregister_queue(struct gendisk *disk)kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); + +mutex_unlock (&q->sysfs_lock); }
Signature · 1 line
2.15.0
Re: [for-4.16 PATCH v6 2/4] block: properly protect the 'queue' kobj in blk_unregister_queue
Quoted 87 lines · Mike Snitzer
The original commit
e9a823fb34a8b(block: fix warning when I/O elevator is changed asrequest_queueis being removed) is pretty conflated. "conflated" because the resource being protected byq->sysfs_lockisn't thequeue_flags(it is the 'queue' kobj).
q->sysfs_lockserializes__elevator_change()(viaelv_iosched_store) from racing withblk_unregister_queue():
1) By holdingq->sysfs_lockfirst,__elevator_change()can complete before a racingblk_unregister_queue().
2) Conversely,__elevator_change()is testing forQUEUE_FLAG_REGISTEREDin caseelv_iosched_store()loses the race withblk_unregister_queue(), it needs a way to know the 'queue' kobj isn't there.Expand the scope of blk_unregister_queue()'s
q->sysfs_lockuse so it is held until after the 'queue' kobj is removed.To do so
blk_mq_unregister_dev()must not also takeq->sysfs_lock. So rename__blk_mq_unregister_dev()toblk_mq_unregister_dev().Also,
blk_unregister_queue()should useq->queue_lockto protect against any concurrent writes toq->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 <[email protected]>--- block/blk-mq-sysfs.c | 9 +-------- block/blk-sysfs.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 11 deletions(-)v6:
blk_mq_unregister_devnow requiresq->sysfs_lockbe held, Ming: I am not seeing any lockdep complaints with this. I've tested bio-based, blk-mq and old .request_fn request-based.block/blk-mq-sysfs.c +1
-8@@ -248,7 +248,7 @@ static int blk_mq_register_hctx(struct blk_mq_hw_ctx *hctx)return ret; } -static void __blk_mq_unregister_dev (struct device *dev,struct request_queue *q) +void blk_mq_unregister_dev (struct device *dev,struct request_queue *q) {struct blk_mq_hw_ctx *hctx;int i;@@ -265,13 +265,6 @@ static void __blk_mq_unregister_dev(struct device *dev, struct request_queue *q) q->mq_sysfs_init_done = false; } -void blk_mq_unregister_dev (struct device *dev,struct request_queue *q) -{ -mutex_lock (&q->sysfs_lock); -__blk_mq_unregister_dev (dev, q); -mutex_unlock (&q->sysfs_lock); -} -void blk_mq_hctx_kobj_init (struct blk_mq_hw_ctx *hctx) {kobject_init (&hctx->kobj, &blk_mq_hw_ktype);block/blk-sysfs.c +8
-3@@ -929,12 +929,17 @@ void blk_unregister_queue(struct gendisk *disk)if (WARN_ON (!q))return ; +/* +* Protect against the 'queue' kobj being accessed +* while/after it is removed. +*/ mutex_lock (&q->sysfs_lock); -queue_flag_clear_unlocked (QUEUE_FLAG_REGISTERED , q); -mutex_unlock (&q->sysfs_lock); -wbt_exit (q); +spin_lock_irq (q->queue_lock); +queue_flag_clear (QUEUE_FLAG_REGISTERED , q); +spin_unlock_irq (q->queue_lock); +wbt_exit (q);if (q->mq_ops)blk_mq_unregister_dev (disk_to_dev (disk), q);
block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 956
Quoted 6 lines
block/blk-sysfs.c +2
-0@@ -946,4 +951,6 @@ void blk_unregister_queue(struct gendisk *disk)kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); + +mutex_unlock (&q->sysfs_lock); }
Signature · 1 line
Ming
PATCH v5 3/4 block: allow gendisk's request_queue registration to be deferred
In reply to Mike SnitzerSince I can remember DM has forced the block layer to allow the allocation and initialization of the request_queue to be distinct operations. Reason for this is block/genhd.c:add_disk() has requires that the request_queue (and associated bdi) be tied to the gendisk before add_disk() is called -- because add_disk() also deals with exposing the request_queue via blk_register_queue().
DM's dynamic creation of arbitrary device types (and associated request_queue types) requires the DM device's gendisk be available so that DM table loads can establish a master/slave relationship with subordinate devices that are referenced by loaded DM tables -- using bd_link_disk_holder(). But until these DM tables, and their associated subordinate devices, are known DM cannot know what type of request_queue it needs -- nor what its queue_limits should be.
This chicken and egg scenario has created all manner of problems for DM and, at times, the block layer.
Summary of changes:
- Add device_add_disk_no_queue_reg() and add_disk_no_queue_reg() variant that drivers may use to add a disk without also calling blk_register_queue(). Driver must call blk_register_queue() once its request_queue is fully initialized.
- Return early from blk_unregister_queue() if QUEUE_FLAG_REGISTERED is not set. It won't be set if driver used add_disk_no_queue_reg() but driver encounters an error and must del_gendisk() before calling blk_register_queue().
- Export blk_register_queue().
These changes allow DM to use add_disk_no_queue_reg() to anchor its gendisk as the "master" for master/slave relationships DM must establish with subordinate devices referenced in DM tables that get loaded. Once all "slave" devices for a DM device are known its request_queue can be properly initialized and then advertised via sysfs -- important improvement being that no request_queue resource initialization performed by blk_register_queue() is missed for DM devices anymore.
--- block/blk-sysfs.c | 5 +++++ block/genhd.c | 20 +++++++++++++++++--- include/linux/genhd.h | 5 +++++ 3 files changed, 27 insertions(+), 3 deletions(-)
block/blk-sysfs.c +5 -0
@@ -921,6 +921,7 @@ int blk_register_queue(struct gendisk *disk)mutex_unlock (&q->sysfs_lock);return ret; } +EXPORT_SYMBOL_GPL (blk_register_queue);void blk_unregister_queue (struct gendisk *disk) {
@@ -929,6 +930,10 @@ void blk_unregister_queue(struct gendisk *disk)if (WARN_ON (!q))return ; +/* Return early if disk->queue was never registered. */ +if (!test_bit (QUEUE_FLAG_REGISTERED , &q->queue_flags)) +return ; +/* * Protect against the 'queue' kobj being accessed * while/after it is removed.
block/genhd.c +17 -3
@@ -629,7 +629,7 @@ static void register_disk(struct device *parent, struct gendisk *disk) }/** -* device_add_disk - add partitioning information to kernel list +* device_add_disk_no_queue_reg - add partitioning information to kernel list * @parent: parent device for the disk * @disk: per-device partitioning information *
@@ -638,7 +638,7 @@ static void register_disk(struct device *parent, struct gendisk *disk)* * FIXME: error handling */ -void device_add_disk (struct device *parent,struct gendisk *disk) +void device_add_disk_no_queue_reg (struct device *parent,struct gendisk *disk) {dev_t devt;int retval;
@@ -682,7 +682,6 @@ void device_add_disk(struct device *parent, struct gendisk *disk) exact_match, exact_lock, disk); }register_disk (parent, disk); -blk_register_queue (disk);/* * Take an extra ref on queue which will be put on disk_release()
@@ -693,6 +692,21 @@ void device_add_disk(struct device *parent, struct gendisk *disk)disk_add_events (disk);blk_integrity_add (disk); } +EXPORT_SYMBOL (device_add_disk_no_queue_reg); + +/** +* device_add_disk - add disk information to kernel list +* @parent: parent device for the disk +* @disk: per-device disk information +* +* Adds partitioning information and also registers the +* associated request_queue to @disk. +*/ +void device_add_disk (struct device *parent,struct gendisk *disk) +{ +device_add_disk_no_queue_reg (parent, disk); +blk_register_queue (disk); +}EXPORT_SYMBOL (device_add_disk);void del_gendisk (struct gendisk *disk)
include/linux/genhd.h +5 -0
@@ -395,6 +395,11 @@ static inline void add_disk(struct gendisk *disk) {device_add_disk (NULL , disk); } +extern void device_add_disk_no_queue_reg (struct device *parent,struct gendisk *disk); +static inline void add_disk_no_queue_reg (struct gendisk *disk) +{ +device_add_disk_no_queue_reg (NULL , disk); +}extern void del_gendisk (struct gendisk *gp);extern struct gendisk *get_gendisk (dev_t dev,int *partno);
Signature · 1 line
2.15.0
PATCH v5 4/4 dm: fix incomplete request_queue initialization
In reply to Mike SnitzerDM is no longer prone to having its request_queue be improperly initialized.
Summary of changes:
- defer DM's blk_register_queue() from add_disk()-time until dm_setup_md_queue() by using add_disk_no_queue_reg() in alloc_dev().
- dm_setup_md_queue() is updated to fully initialize DM's request_queue (_after_ all table loads have occurred and the request_queue's type, features and limits are known).
A very welcome side-effect of these changes is DM no longer needs to:
1) backfill the "mq" sysfs entry (because historically DM didn't initialize the request_queue to use blk-mq until _after_ blk_register_queue() was called via add_disk()).
2) call elv_register_queue() to get .request_fn request-based DM device's "iosched" exposed in syfs.
In addition, blk-mq debugfs support is now made available because request-based DM's blk-mq request_queue is now properly initialized before dm_setup_md_queue() calls blk_register_queue().
These changes also stave off the need to introduce new DM-specific workarounds in block core, e.g. this proposal:
https://patchwork.kernel.org/patch/10067961/
In the end DM devices should be less unicorn in nature (relative to initialization and availability of block core infrastructure provided by the request_queue).
--- drivers/md/dm-rq.c | 9 --------- drivers/md/dm.c | 11 ++++++++++- 2 files changed, 10 insertions(+), 10 deletions(-)
drivers/md/dm-rq.c +0 -9
@@ -713,8 +713,6 @@ int dm_old_init_request_queue(struct mapped_device *md, struct dm_table *t)return error; } -elv_register_queue (md->queue); -return 0 ; }
@@ -812,15 +810,8 @@ int dm_mq_init_request_queue(struct mapped_device *md, struct dm_table *t) }dm_init_md_queue (md); -/* backfill 'mq' sysfs registration normally done in blk_register_queue */ - err =blk_mq_register_dev (disk_to_dev (md->disk), q); -if (err) - goto out_cleanup_queue; -return 0 ; -out_cleanup_queue: -blk_cleanup_queue (q); out_tag_set:blk_mq_free_tag_set (md->tag_set); out_kfree_tag_set:
drivers/md/dm.c +10 -1
@@ -1761,7 +1761,7 @@ static struct mapped_device *alloc_dev(int minor) goto bad; md->dax_dev = dax_dev; -add_disk (md->disk); +add_disk_no_queue_reg (md->disk);format_dev_t (md->name,MKDEV (_major, minor)); md->wq =alloc_workqueue ("kdmflush" ,WQ_MEM_RECLAIM ,0 );
@@ -2021,6 +2021,7 @@ EXPORT_SYMBOL_GPL(dm_get_queue_limits);int dm_setup_md_queue (struct mapped_device *md,struct dm_table *t) {int r; +struct queue_limits limits;enum dm_queue_mode type =dm_get_md_type (md);switch (type) {
@@ -2057,6 +2058,14 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t)break ; } + r =dm_calculate_queue_limits (t, &limits); +if (r) { +DMERR ("Cannot calculate initial queue limits" ); +return r; + } +dm_table_set_restrictions (t, md->queue, &limits); +blk_register_queue (md->disk); +return 0 ; }
Signature · 1 line
2.15.0
Quoted 3 lines · Mike Snitzer
Hi Jens,
I'm submitting this v5 with more feeling now ;)
Of course PATCH 02/04 needed a v6.
Quoted 2 lines
I've distilled the changes down to be quite minimal. Hopefully this will help you and others review.
I could make it even simpler by dropping 02/04 entirely. But I do think it worthwhile to try to get 02/04 in at this time too.
Quoted 9 lines
I've also done a dry-run of applying 4.16 block changes and then merging dm-4.16; no merge conflicts:
https://git.kernel.org/pub/scm/linux/kernel/git/snitzer/linux.git/log/?h=block-4.16
https://git.kernel.org/pub/scm/linux/kernel/git/snitzer/linux.git/log/?h=dm-4.16
https://git.kernel.org/pub/scm/linux/kernel/git/snitzer/linux.git/log/?h=block-4.16_dm-4.16And all tests very well for me.
Please consider for 4.16, thanks!
Anyway, I've genuinely tested with blk-mq now. Sorry for all the iterations on this patchset in such rapid succession. Obviously I know it doesn't instill confidence...
But I really would appreciate you giving this patchset serious consideration.
DM is long overdue for this fix.
Thanks,
Mike
Quoted 1 line · Mike Snitzer
I'm submitting this v5 with more feeling now ;)
Hello Mike,
Have these patches been tested with lockdep enabled? The following appeared in the kernel log when after I started testing Jens' for-next tree of this morning:
======================================================
WARNING: possible circular locking dependency detected 4.15.0-rc7-dbg+ #1 Not tainted
------------------------------------------------------
02-mq/1211 is trying to acquire lock:
(&q->sysfs_lock){+.+.}, at: [<000000008b65bdad>] queue_attr_store+0x35/0x80
but task is already holding lock:
(kn->count#213){++++}, at: [<000000007a18ad18>] kernfs_fop_write+0xe5/0x190
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (kn->count#213){++++}:
kernfs_remove+0x1a/0x30
kobject_del.part.3+0xe/0x40
blk_unregister_queue+0xa7/0xe0
del_gendisk+0x12f/0x260
sd_remove+0x58/0xc0 [sd_mod]
device_release_driver_internal+0x15a/0x220
bus_remove_device+0xf4/0x170
device_del+0x12f/0x330
__scsi_remove_device+0xef/0x120 [scsi_mod]
scsi_forget_host+0x1b/0x60 [scsi_mod]
scsi_remove_host+0x6f/0x110 [scsi_mod]
0xffffffffc09ed6e4
process_one_work+0x21c/0x6d0
worker_thread+0x35/0x380
kthread+0x117/0x130
ret_from_fork+0x24/0x30-> #0 (&q->sysfs_lock){+.+.}:
__mutex_lock+0x6c/0x9e0
queue_attr_store+0x35/0x80
kernfs_fop_write+0x109/0x190
__vfs_write+0x1e/0x130
vfs_write+0xb9/0x1b0
SyS_write+0x40/0xa0
entry_SYSCALL_64_fastpath+0x23/0x9aother info that might help us debug this:
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(kn->count#213);
lock(&q->sysfs_lock);
lock(kn->count#213); lock(&q->sysfs_lock);
*** DEADLOCK ***
3 locks held by 02-mq/1211:
#0: (sb_writers#6){.+.+}, at: [<00000000afdb61d3>] vfs_write+0x17f/0x1b0
#1: (&of->mutex){+.+.}, at: [<00000000b291cabb>] kernfs_fop_write+0xdd/0x190
#2: (kn->count#213){++++}, at: [<000000007a18ad18>] kernfs_fop_write+0xe5/0x190stack backtrace: CPU: 2 PID: 1211 Comm: 02-mq Not tainted 4.15.0-rc7-dbg+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.0.0-prebuilt.qemu-project.org 04/01/2014 Call Trace: dump_stack+0x85/0xbf print_circular_bug.isra.35+0x1d7/0x1e4 __lock_acquire+0x12af/0x1370 ? lock_acquire+0xa8/0x230 lock_acquire+0xa8/0x230 ? queue_attr_store+0x35/0x80 ? queue_attr_store+0x35/0x80 __mutex_lock+0x6c/0x9e0 ? queue_attr_store+0x35/0x80 ? kernfs_fop_write+0xdd/0x190 ? __mutex_lock+0x6c/0x9e0 ? __mutex_lock+0x3d0/0x9e0 ? lock_acquire+0xa8/0x230 ? __lock_is_held+0x55/0x90 ? queue_attr_store+0x35/0x80 queue_attr_store+0x35/0x80 kernfs_fop_write+0x109/0x190 __vfs_write+0x1e/0x130 ? rcu_read_lock_sched_held+0x66/0x70 ? rcu_sync_lockdep_assert+0x23/0x50 ? __sb_start_write+0x152/0x1f0 ? __sb_start_write+0x168/0x1f0 vfs_write+0xb9/0x1b0 SyS_write+0x40/0xa0 entry_SYSCALL_64_fastpath+0x23/0x9a RIP: 0033:0x7fab8be8d054 RSP: 002b:00007ffd39f0def8 EFLAGS: 00000246
Thanks,
Bart.
Quoted 7 lines · Bart Van Assche
Quoted 1 line · Mike Snitzer
I'm submitting this v5 with more feeling now ;)
Hello Mike,
Have these patches been tested with lockdep enabled? The following appeared in the kernel log when after I started testing Jens' for-next tree of this morning:
So you replied to v5, I emailed a v6 out for the relevant patch. Just want to make sure you're testing with either Jens' latest tree or are using my v6 that fixed blk_mq_unregister_dev() to require caller holds q->sysfs_lock ?
With the latest code that Jens merged, I do have lockdep enabled in my kernel... but haven't seen any issues on the 2 testbeds I've been hammering the changes on.
Ming was concerned about the potential for a lockdep splat (said he tried expanding the scope of q->sysfs_lock in blk_unregister_queue before and in doing so got lockdep to trigger).
Once you respond with what it is you're testing, and if it is latest code, I'll look closer at what you've provided and see if it is real or that lockdep just needs some training.
Thanks,
Mike
Quoted 4 lines · Mike Snitzer
So you replied to v5, I emailed a v6 out for the relevant patch. Just want to make sure you're testing with either Jens' latest tree or are using my v6 that fixed
blk_mq_unregister_dev()to require caller holdsq->sysfs_lock?
Hello Mike,
In my test I was using Jens' latest for-next tree (commit 563877ae7dae).
Bart.
Quoted 9 lines · Bart Van Assche
Quoted 4 lines · Mike Snitzer
So you replied to v5, I emailed a v6 out for the relevant patch. Just want to make sure you're testing with either Jens' latest tree or are using my v6 that fixed
blk_mq_unregister_dev()to require caller holdsq->sysfs_lock?Hello Mike,
In my test I was using Jens' latest for-next tree (commit
563877ae7dae).
OK, that includes his latest for-4.16/block (commit c100ec49fdd22228) so I'll take a closer look at this lockdep splat.
Do I need to do something more to enable lockdep aside from set CONFIG_LOCKDEP_SUPPORT=y ?
Thanks,
Mike
Quoted 2 lines · Mike Snitzer
Do I need to do something more to enable lockdep aside from set
CONFIG_LOCKDEP_SUPPORT=y?
Hello Mike,
I think you also need to set CONFIG_PROVE_LOCKING=y.
Bart.
Quoted 7 lines · Bart Van Assche
Quoted 2 lines · Mike Snitzer
Do I need to do something more to enable lockdep aside from set
CONFIG_LOCKDEP_SUPPORT=y?Hello Mike,
I think you also need to set
CONFIG_PROVE_LOCKING=y.
Ah ok, was missing that, thanks.
Mike
Quoted 59 lines · Bart Van Assche
Quoted 1 line · Mike Snitzer
I'm submitting this v5 with more feeling now ;)
Hello Mike,
Have these patches been tested with lockdep enabled? The following appeared in the kernel log when after I started testing Jens' for-next tree of this morning:
======================================================
WARNING: possible circular locking dependency detected 4.15.0-rc7-dbg+ #1 Not tainted
------------------------------------------------------
02-mq/1211 is trying to acquire lock:
(&q->sysfs_lock){+.+.}, at: [<000000008b65bdad>]queue_attr_store+0x35/0x80but task is already holding lock:
(kn->count#213){++++}, at: [<000000007a18ad18>]kernfs_fop_write+0xe5/0x190which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (kn->count#213){++++}:
kernfs_remove+0x1a/0x30 kobject_del.part.3+0xe/0x40 blk_unregister_queue+0xa7/0xe0 del_gendisk+0x12f/0x260 sd_remove+0x58/0xc0 [sd_mod] device_release_driver_internal+0x15a/0x220 bus_remove_device+0xf4/0x170 device_del+0x12f/0x330 __scsi_remove_device+0xef/0x120 [scsi_mod] scsi_forget_host+0x1b/0x60 [scsi_mod] scsi_remove_host+0x6f/0x110 [scsi_mod] 0xffffffffc09ed6e4 process_one_work+0x21c/0x6d0 worker_thread+0x35/0x380 kthread+0x117/0x130 ret_from_fork+0x24/0x30-> #0 (&q->sysfs_lock){+.+.}:
__mutex_lock+0x6c/0x9e0 queue_attr_store+0x35/0x80 kernfs_fop_write+0x109/0x190 __vfs_write+0x1e/0x130 vfs_write+0xb9/0x1b0 SyS_write+0x40/0xa0 entry_SYSCALL_64_fastpath+0x23/0x9aother info that might help us debug this:
Possible unsafe locking scenario:CPU0 CPU1 ---- ---- lock(kn->count#213); lock(&q->sysfs_lock); lock(kn->count#213);
Quoted 8 lines
lock(&q->sysfs_lock);*** DEADLOCK ***
3 locks held by 02-mq/1211:
#0: (sb_writers#6){.+.+}, at: [<00000000afdb61d3>] vfs_write+0x17f/0x1b0 #1: (&of->mutex){+.+.}, at: [<00000000b291cabb>] kernfs_fop_write+0xdd/0x190 #2: (kn->count#213){++++}, at: [<000000007a18ad18>] kernfs_fop_write+0xe5/0x190
sysfs write op calls kernfs_fop_write which takes:of->mutex then kn->count#213 (no idea what that is) then q->sysfs_lock (via queue_attr_store)
vs
blk_unregister_queue takes:q->sysfs_lock thenkernfs_mutex (via kernfs_remove)
seems lockdep thinks "kernfs_mutex" is "kn->count#213"?
Feels like lockdep code in fs/kernfs/file.c and fs/kernfs/dir.c is triggering false positives.. because these seem like different kernfs locks yet they are reported as "kn->count#213".
Certainly feeling out of my depth with kernfs's locking though.
Mike
Quoted 11 lines · Mike Snitzer
sysfs write op calls
kernfs_fop_writewhich takes:of->mutexthen kn->count#213 (no idea what that is) thenq->sysfs_lock(viaqueue_attr_store)vs
blk_unregister_queuetakes:q->sysfs_lockthenkernfs_mutex(viakernfs_remove)
seems lockdep thinks "kernfs_mutex" is "kn->count#213"?
Quoted 5 lines
Feels like lockdep code in
fs/kernfs/file.candfs/kernfs/dir.cis triggering false positives.. because these seem like different kernfs locks yet they are reported as "kn->count#213".Certainly feeling out of my depth with kernfs's locking though.
Hello Mike,
I don't think that this is a false positive but rather the following traditional pattern of a potential deadlock involving sysfs attributes:
* One context obtains a mutex from inside a sysfs attribute method:
queue_attr_store() obtains q->sysfs_lock.
* Another context removes a sysfs attribute while holding a mutex:
blk_unregister_queue() removes the queue sysfs attributes while holding q->sysfs_lock.
This can result in a real deadlock because the code that removes sysfs objects waits until all ongoing attribute callbacks have finished.
Since commit 667257e8b298 ("block: properly protect the 'queue' kobj in blk_unregister_queue") modified blk_unregister_queue() such that q->sysfs_lock is held around the kobject_del(&q->kobj) call I think this is a regression introduced by that commit.
Bart.
Quoted 28 lines · Bart Van Assche
Quoted 11 lines · Mike Snitzer
sysfs write op calls
kernfs_fop_writewhich takes:of->mutexthen kn->count#213 (no idea what that is) thenq->sysfs_lock(viaqueue_attr_store)vs
blk_unregister_queuetakes:q->sysfs_lockthenkernfs_mutex(viakernfs_remove)
seems lockdep thinks "kernfs_mutex" is "kn->count#213"?Quoted 5 lines
Feels like lockdep code in
fs/kernfs/file.candfs/kernfs/dir.cis triggering false positives.. because these seem like different kernfs locks yet they are reported as "kn->count#213".Certainly feeling out of my depth with kernfs's locking though.
Hello Mike,
I don't think that this is a false positive but rather the following traditional pattern of a potential deadlock involving sysfs attributes:
* One context obtains a mutex from inside a sysfs attribute method:
queue_attr_store()obtainsq->sysfs_lock.
* Another context removes a sysfs attribute while holding a mutex:
blk_unregister_queue()removes the queue sysfs attributes while holdingq->sysfs_lock.
Quoted 7 lines
This can result in a real deadlock because the code that removes sysfs objects waits until all ongoing attribute callbacks have finished.
Since commit
667257e8b298("block: properly protect the 'queue' kobj inblk_unregister_queue") modifiedblk_unregister_queue()such thatq->sysfs_lockis held around thekobject_del(&q->kobj)call I think this is a regression introduced by that commit.
Sure, of course it is a regression.
Aside from moving the mutex_unlock(&q->sysfs_lock) above the kobject_del(&q->kobj) I don't know how to fix it.
Though, realistically that'd be an adequate fix (given the way the code was before).
Mike
Quoted 39 lines · Mike Snitzer
Quoted 28 lines · Bart Van Assche
Quoted 11 lines · Mike Snitzer
sysfs write op calls
kernfs_fop_writewhich takes:of->mutexthen kn->count#213 (no idea what that is) thenq->sysfs_lock(viaqueue_attr_store)vs
blk_unregister_queuetakes:q->sysfs_lockthenkernfs_mutex(viakernfs_remove)
seems lockdep thinks "kernfs_mutex" is "kn->count#213"?Quoted 5 lines
Feels like lockdep code in
fs/kernfs/file.candfs/kernfs/dir.cis triggering false positives.. because these seem like different kernfs locks yet they are reported as "kn->count#213".Certainly feeling out of my depth with kernfs's locking though.
Hello Mike,
I don't think that this is a false positive but rather the following traditional pattern of a potential deadlock involving sysfs attributes:
* One context obtains a mutex from inside a sysfs attribute method:
queue_attr_store()obtainsq->sysfs_lock.
* Another context removes a sysfs attribute while holding a mutex:
blk_unregister_queue()removes the queue sysfs attributes while holdingq->sysfs_lock.Quoted 7 lines
This can result in a real deadlock because the code that removes sysfs objects waits until all ongoing attribute callbacks have finished.
Since commit
667257e8b298("block: properly protect the 'queue' kobj inblk_unregister_queue") modifiedblk_unregister_queue()such thatq->sysfs_lockis held around thekobject_del(&q->kobj)call I think this is a regression introduced by that commit.
Quoted 7 lines
Sure, of course it is a regression.
Aside from moving the
mutex_unlock(&q->sysfs_lock)above thekobject_del(&q->kobj)I don't know how to fix it.Though, realistically that'd be an adequate fix (given the way the code was before).
Any chance you apply this and re-run your srp_test that triggered the lockdep splat?
block/blk-sysfs.c +2 -2
@@ -952,10 +952,10 @@ void blk_unregister_queue(struct gendisk *disk)if (q->request_fn || (q->mq_ops && q->elevator))elv_unregister_queue (q); +mutex_unlock (&q->sysfs_lock); +kobject_uevent (&q->kobj,KOBJ_REMOVE );kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); - -mutex_unlock (&q->sysfs_lock); }
Quoted 60 lines · Mike Snitzer
Quoted 39 lines · Mike Snitzer
Quoted 28 lines · Bart Van Assche
Quoted 11 lines · Mike Snitzer
sysfs write op calls
kernfs_fop_writewhich takes:of->mutexthen kn->count#213 (no idea what that is) thenq->sysfs_lock(viaqueue_attr_store)vs
blk_unregister_queuetakes:q->sysfs_lockthenkernfs_mutex(viakernfs_remove)
seems lockdep thinks "kernfs_mutex" is "kn->count#213"?Quoted 5 lines
Feels like lockdep code in
fs/kernfs/file.candfs/kernfs/dir.cis triggering false positives.. because these seem like different kernfs locks yet they are reported as "kn->count#213".Certainly feeling out of my depth with kernfs's locking though.
Hello Mike,
I don't think that this is a false positive but rather the following traditional pattern of a potential deadlock involving sysfs attributes:
* One context obtains a mutex from inside a sysfs attribute method:
queue_attr_store()obtainsq->sysfs_lock.
* Another context removes a sysfs attribute while holding a mutex:
blk_unregister_queue()removes the queue sysfs attributes while holdingq->sysfs_lock.Quoted 7 lines
This can result in a real deadlock because the code that removes sysfs objects waits until all ongoing attribute callbacks have finished.
Since commit
667257e8b298("block: properly protect the 'queue' kobj inblk_unregister_queue") modifiedblk_unregister_queue()such thatq->sysfs_lockis held around thekobject_del(&q->kobj)call I think this is a regression introduced by that commit.Quoted 7 lines
Sure, of course it is a regression.
Aside from moving the
mutex_unlock(&q->sysfs_lock)above thekobject_del(&q->kobj)I don't know how to fix it.Though, realistically that'd be an adequate fix (given the way the code was before).
Any chance you apply this and re-run your
srp_testthat triggered the lockdep splat?block/blk-sysfs.c +0
-0@@ -952,10 +952,10 @@ void blk_unregister_queue(struct gendisk *disk)if (q->request_fn || (q->mq_ops && q->elevator))elv_unregister_queue (q);
block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 961
Quoted 10 lines
block/blk-sysfs.c +2
-2+mutex_unlock (&q->sysfs_lock); +kobject_uevent (&q->kobj,KOBJ_REMOVE );kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); - -mutex_unlock (&q->sysfs_lock); }
If this patch is required, similar change should be needed in failure path of blk_register_queue() too.
Signature · 1 line
Ming Lei
Quoted 60 lines · Mike Snitzer
Quoted 39 lines · Mike Snitzer
Quoted 28 lines · Bart Van Assche
Quoted 11 lines · Mike Snitzer
sysfs write op calls
kernfs_fop_writewhich takes:of->mutexthen kn->count#213 (no idea what that is) thenq->sysfs_lock(viaqueue_attr_store)vs
blk_unregister_queuetakes:q->sysfs_lockthenkernfs_mutex(viakernfs_remove)
seems lockdep thinks "kernfs_mutex" is "kn->count#213"?Quoted 5 lines
Feels like lockdep code in
fs/kernfs/file.candfs/kernfs/dir.cis triggering false positives.. because these seem like different kernfs locks yet they are reported as "kn->count#213".Certainly feeling out of my depth with kernfs's locking though.
Hello Mike,
I don't think that this is a false positive but rather the following traditional pattern of a potential deadlock involving sysfs attributes:
* One context obtains a mutex from inside a sysfs attribute method:
queue_attr_store()obtainsq->sysfs_lock.
* Another context removes a sysfs attribute while holding a mutex:
blk_unregister_queue()removes the queue sysfs attributes while holdingq->sysfs_lock.Quoted 7 lines
This can result in a real deadlock because the code that removes sysfs objects waits until all ongoing attribute callbacks have finished.
Since commit
667257e8b298("block: properly protect the 'queue' kobj inblk_unregister_queue") modifiedblk_unregister_queue()such thatq->sysfs_lockis held around thekobject_del(&q->kobj)call I think this is a regression introduced by that commit.Quoted 7 lines
Sure, of course it is a regression.
Aside from moving the
mutex_unlock(&q->sysfs_lock)above thekobject_del(&q->kobj)I don't know how to fix it.Though, realistically that'd be an adequate fix (given the way the code was before).
Any chance you apply this and re-run your
srp_testthat triggered the lockdep splat?block/blk-sysfs.c +0
-0@@ -952,10 +952,10 @@ void blk_unregister_queue(struct gendisk *disk)if (q->request_fn || (q->mq_ops && q->elevator))elv_unregister_queue (q);
block/blk-sysfs.c void blk_unregister_queue(struct gendisk *disk) · line 961
Quoted 10 lines
block/blk-sysfs.c +2
-2+mutex_unlock (&q->sysfs_lock); +kobject_uevent (&q->kobj,KOBJ_REMOVE );kobject_del (&q->kobj);blk_trace_remove_sysfs (disk_to_dev (disk));kobject_put (&disk_to_dev (disk)->kobj); - -mutex_unlock (&q->sysfs_lock); }
Hello Mike,
Today sysfs_lock protects a whole bunch of state information. I think for the longer term we should split it into multiple mutexes. For the short term, please have a look at the patch series I just posted on the linux-block mailing list.
Thanks,
Bart.