If zoned block devices are stacked and if a lower driver reports an I/O error, this triggers nested locking of spinlocks. Rework the zoned block device code such that this doesn't happen anymore. This patch fixes the following kernel warning: WARNING: possible recursive locking detected -------------------------------------------- kworker/6:1H/203 is trying to acquire lock: ffffff8881697130 (&zwplug->lock){-...}-{2:2}, at: blk_zone_write_plug_bio_endio+0x144/0x290 but task is already holding lock: ffffff884e99d930 (&zwplug->lock){-...}-{2:2}, at: blk_zone_wplug_bio_work+0x30/0x250 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&zwplug->lock); lock(&zwplug->lock); *** DEADLOCK *** May be due to missing lock nesting notation 3 locks held by kworker/6:1H/203: #0: ffffff88128ed358 ((wq_completion)sde_zwplugs){+.+.}-{0:0}, at: process_one_work+0x1bc/0x65c #1: ffffffc088343d70 ((work_completion)(&zwplug->bio_work)){+.+.}-{0:0}, at: process_one_work+0x1e4/0x65c #2: ffffff884e99d930 (&zwplug->lock){-...}-{2:2}, at: blk_zone_wplug_bio_work+0x30/0x250 stack backtrace: Workqueue: sde_zwplugs blk_zone_wplug_bio_work Call trace: dump_backtrace+0xfc/0x17c show_stack+0x18/0x28 dump_stack_lvl+0x40/0xa0 dump_stack+0x18/0x24 print_deadlock_bug+0x38c/0x398 __lock_acquire+0x13e8/0x2e1c lock_acquire+0x134/0x2b4 _raw_spin_lock_irqsave+0x5c/0x80 blk_zone_write_plug_bio_endio+0x144/0x290 bio_endio+0x9c/0x240 __dm_io_complete+0x210/0x27c clone_endio+0xe8/0x214 bio_endio+0x218/0x240 blk_crypto_fallback_encrypt_endio+0x78/0x94 bio_endio+0x218/0x240 blk_zone_wplug_bio_work+0xa8/0x250 process_one_work+0x26c/0x65c worker_thread+0x33c/0x498 kthread+0x110/0x134 ret_from_fork+0x10/0x20 Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx> --- block/blk-zoned.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/block/blk-zoned.c b/block/blk-zoned.c index ce5604c92fea..4bff76a06204 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -614,12 +614,11 @@ static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug) pr_warn_ratelimited("%s: zone %u: Aborting plugged BIOs\n", zwplug->disk->disk_name, zwplug->zone_no); for (;;) { - scoped_guard(spinlock_irqsave, &zwplug->lock) { + scoped_guard(spinlock_irqsave, &zwplug->lock) bio = bio_list_pop(&zwplug->bio_list); - if (!bio) - break; - blk_zone_wplug_bio_io_error(zwplug, bio); - } + if (!bio) + break; + blk_zone_wplug_bio_io_error(zwplug, bio); } } @@ -1236,8 +1235,9 @@ void blk_zone_write_plug_bio_endio(struct bio *bio) * needing a write pointer update. */ if (bio->bi_status != BLK_STS_OK) { - spin_lock_irqsave(&zwplug->lock, flags); disk_zone_wplug_abort(zwplug); + + spin_lock_irqsave(&zwplug->lock, flags); zwplug->flags |= BLK_ZONE_WPLUG_NEED_WP_UPDATE; spin_unlock_irqrestore(&zwplug->lock, flags); } @@ -1288,13 +1288,12 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) unsigned long flags; struct bio *bio; +again: /* * Submit the next plugged BIO. If we do not have any, clear * the plugged flag. */ spin_lock_irqsave(&zwplug->lock, flags); - -again: bio = bio_list_pop(&zwplug->bio_list); if (!bio) { zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED; @@ -1303,6 +1302,7 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) } if (!blk_zone_wplug_prepare_bio(zwplug, bio)) { + spin_unlock_irqrestore(&zwplug->lock, flags); blk_zone_wplug_bio_io_error(zwplug, bio); goto again; }