| From e169bd4fb2b36c4b2bee63c35c740c85daeb2e86 Mon Sep 17 00:00:00 2001 |
| From: Maksim Kiselev <bigunclemax@gmail.com> |
| Date: Wed, 24 Jan 2024 10:24:36 +0300 |
| Subject: aoe: avoid potential deadlock at set_capacity |
| |
| From: Maksim Kiselev <bigunclemax@gmail.com> |
| |
| commit e169bd4fb2b36c4b2bee63c35c740c85daeb2e86 upstream. |
| |
| Move set_capacity() outside of the section procected by (&d->lock). |
| To avoid possible interrupt unsafe locking scenario: |
| |
| CPU0 CPU1 |
| ---- ---- |
| [1] lock(&bdev->bd_size_lock); |
| local_irq_disable(); |
| [2] lock(&d->lock); |
| [3] lock(&bdev->bd_size_lock); |
| <Interrupt> |
| [4] lock(&d->lock); |
| |
| *** DEADLOCK *** |
| |
| Where [1](&bdev->bd_size_lock) hold by zram_add()->set_capacity(). |
| [2]lock(&d->lock) hold by aoeblk_gdalloc(). And aoeblk_gdalloc() |
| is trying to acquire [3](&bdev->bd_size_lock) at set_capacity() call. |
| In this situation an attempt to acquire [4]lock(&d->lock) from |
| aoecmd_cfg_rsp() will lead to deadlock. |
| |
| So the simplest solution is breaking lock dependency |
| [2](&d->lock) -> [3](&bdev->bd_size_lock) by moving set_capacity() |
| outside. |
| |
| Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com> |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Link: https://lore.kernel.org/r/20240124072436.3745720-2-bigunclemax@gmail.com |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| [ Larry: backport to 5.15.y. Minor conflict resolved due to missing commit d9c2bd252a457 |
| aoe: add error handling support for add_disk() ] |
| Signed-off-by: Larry Bassel <larry.bassel@oracle.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/block/aoe/aoeblk.c | 5 ++++- |
| 1 file changed, 4 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/block/aoe/aoeblk.c |
| +++ b/drivers/block/aoe/aoeblk.c |
| @@ -346,6 +346,7 @@ aoeblk_gdalloc(void *vp) |
| struct gendisk *gd; |
| mempool_t *mp; |
| struct blk_mq_tag_set *set; |
| + sector_t ssize; |
| ulong flags; |
| int late = 0; |
| int err; |
| @@ -408,7 +409,7 @@ aoeblk_gdalloc(void *vp) |
| gd->minors = AOE_PARTITIONS; |
| gd->fops = &aoe_bdops; |
| gd->private_data = d; |
| - set_capacity(gd, d->ssize); |
| + ssize = d->ssize; |
| snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d", |
| d->aoemajor, d->aoeminor); |
| |
| @@ -417,6 +418,8 @@ aoeblk_gdalloc(void *vp) |
| |
| spin_unlock_irqrestore(&d->lock, flags); |
| |
| + set_capacity(gd, ssize); |
| + |
| device_add_disk(NULL, gd, aoe_attr_groups); |
| aoedisk_add_debugfs(d); |
| |