* [PATCH] aoe: Do not free a device while its work is queued or running
@ 2026-10-29 15:45 Bart Van Assche
2026-10-29 15:54 ` Jens Axboe
0 siblings, 1 reply; 2+ messages in thread
From: Bart Van Assche @ 2026-10-29 15:45 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, Bart Van Assche, Justin Sanders, He Zhe
Serialize flush() with a mutex because dropping devlist_lock during pass
one otherwise allows a concurrent flush() call to enter pass two and free
a device that is still being torn down.
Set DEVFL_TKILL under d->lock before unlocking and calling
aoedev_downdev() because commit 430380b4637a ("block: aoe: Fix kernel
crash due to atomic sleep when exiting") dropped d->lock and devlist_lock
before setting DEVFL_TKILL, allowing d->work to be re-queued while
aoedev_downdev() sleeps.
Guard queue_work(aoe_wq, &d->work) with a DEVFL_TKILL check under d->lock
in rexmit_timer(), ataid_complete(), and aoeblk_gdalloc() so that timers,
ATA ID completions, and gendisk allocation failures cannot re-queue
d->work once teardown has started.
Cancel d->work synchronously in flush() before aoedev_downdev() and in
freedev() after timer_delete_sync() so that pending work cannot run
concurrently with device teardown or outlive the freed struct aoedev.
Check DEVFL_TKILL under d->lock in aoecmd_sleepwork() and aoeblk_gdalloc()
instead of triggering WARN_ON() or setting DEVFL_UP on a device that is
being killed.
This patch is AI-generated and has only been compile-tested.
Cc: Justin Sanders <justin@coraid.com>
Fixes: 430380b4637a ("block: aoe: Fix kernel crash due to atomic sleep when exiting")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/block/aoe/aoeblk.c | 8 ++++++--
drivers/block/aoe/aoecmd.c | 15 ++++++++++++---
drivers/block/aoe/aoedev.c | 8 +++++++-
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 34ead75e7e02..69586dc6d379 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -385,7 +385,10 @@ aoeblk_gdalloc(void *vp)
spin_lock_irqsave(&d->lock, flags);
WARN_ON(!(d->flags & DEVFL_GD_NOW));
WARN_ON(!(d->flags & DEVFL_GDALLOC));
- WARN_ON(d->flags & DEVFL_TKILL);
+ if (d->flags & DEVFL_TKILL) {
+ spin_unlock_irqrestore(&d->lock, flags);
+ goto out_disk_cleanup;
+ }
WARN_ON(d->gd);
WARN_ON(d->flags & DEVFL_UP);
d->bufpool = mp;
@@ -427,7 +430,8 @@ aoeblk_gdalloc(void *vp)
err:
spin_lock_irqsave(&d->lock, flags);
d->flags &= ~DEVFL_GD_NOW;
- queue_work(aoe_wq, &d->work);
+ if (!(d->flags & DEVFL_TKILL))
+ queue_work(aoe_wq, &d->work);
spin_unlock_irqrestore(&d->lock, flags);
}
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index a4744a30a8af..c7f762180b9b 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -787,7 +787,8 @@ rexmit_timer(struct timer_list *timer)
*/
list_splice(&flist, &d->factive[0]);
d->flags |= DEVFL_DEAD;
- queue_work(aoe_wq, &d->work);
+ if (!(d->flags & DEVFL_TKILL))
+ queue_work(aoe_wq, &d->work);
goto out;
}
@@ -899,6 +900,13 @@ aoecmd_sleepwork(struct work_struct *work)
{
struct aoedev *d = container_of(work, struct aoedev, work);
+ spin_lock_irq(&d->lock);
+ if (d->flags & DEVFL_TKILL) {
+ spin_unlock_irq(&d->lock);
+ return;
+ }
+ spin_unlock_irq(&d->lock);
+
if (d->flags & DEVFL_DEAD)
aoedev_downdev(d);
@@ -909,7 +917,8 @@ aoecmd_sleepwork(struct work_struct *work)
set_capacity_and_notify(d->gd, d->ssize);
spin_lock_irq(&d->lock);
- d->flags |= DEVFL_UP;
+ if (!(d->flags & DEVFL_TKILL))
+ d->flags |= DEVFL_UP;
d->flags &= ~DEVFL_NEWSIZE;
spin_unlock_irq(&d->lock);
}
@@ -974,7 +983,7 @@ ataid_complete(struct aoedev *d, struct aoetgt *t, unsigned char *id)
d->fw_ver, (long long)ssize);
d->ssize = ssize;
d->geo.start = 0;
- if (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
+ if (d->flags & (DEVFL_GDALLOC | DEVFL_NEWSIZE | DEVFL_TKILL))
return;
if (d->gd != NULL)
d->flags |= DEVFL_NEWSIZE;
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c
index 6e8b3807c1c8..4eb2e68439db 100644
--- a/drivers/block/aoe/aoedev.c
+++ b/drivers/block/aoe/aoedev.c
@@ -286,6 +286,7 @@ freedev(struct aoedev *d)
return;
timer_delete_sync(&d->timer);
+ cancel_work_sync(&d->work);
if (d->gd) {
aoedisk_rm_debugfs(d);
del_gendisk(d->gd);
@@ -311,6 +312,8 @@ enum flush_parms {
EXITING = 1,
};
+static DEFINE_MUTEX(flush_mutex);
+
static int
flush(const char __user *str, size_t cnt, int exiting)
{
@@ -333,6 +336,7 @@ flush(const char __user *str, size_t cnt, int exiting)
specified = 1;
}
+ mutex_lock(&flush_mutex);
flush_workqueue(aoe_wq);
/* pass one: do aoedev_downdev, which might sleep */
restart1:
@@ -353,10 +357,11 @@ flush(const char __user *str, size_t cnt, int exiting)
|| d->ref)
goto cont;
+ d->flags |= DEVFL_TKILL;
spin_unlock(&d->lock);
spin_unlock_irqrestore(&devlist_lock, flags);
+ cancel_work_sync(&d->work);
aoedev_downdev(d);
- d->flags |= DEVFL_TKILL;
goto restart1;
cont:
spin_unlock(&d->lock);
@@ -397,6 +402,7 @@ flush(const char __user *str, size_t cnt, int exiting)
kfree(doomed);
}
spin_unlock_irqrestore(&devlist_lock, flags);
+ mutex_unlock(&flush_mutex);
return 0;
}
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] aoe: Do not free a device while its work is queued or running
2026-10-29 15:45 [PATCH] aoe: Do not free a device while its work is queued or running Bart Van Assche
@ 2026-10-29 15:54 ` Jens Axboe
0 siblings, 0 replies; 2+ messages in thread
From: Jens Axboe @ 2026-10-29 15:54 UTC (permalink / raw)
To: Bart Van Assche; +Cc: linux-block, Justin Sanders, He Zhe
On 10/29/26 9:45 AM, Bart Van Assche wrote:
> Serialize flush() with a mutex because dropping devlist_lock during pass
> one otherwise allows a concurrent flush() call to enter pass two and free
> a device that is still being torn down.
>
> Set DEVFL_TKILL under d->lock before unlocking and calling
> aoedev_downdev() because commit 430380b4637a ("block: aoe: Fix kernel
> crash due to atomic sleep when exiting") dropped d->lock and devlist_lock
> before setting DEVFL_TKILL, allowing d->work to be re-queued while
> aoedev_downdev() sleeps.
>
> Guard queue_work(aoe_wq, &d->work) with a DEVFL_TKILL check under d->lock
> in rexmit_timer(), ataid_complete(), and aoeblk_gdalloc() so that timers,
> ATA ID completions, and gendisk allocation failures cannot re-queue
> d->work once teardown has started.
>
> Cancel d->work synchronously in flush() before aoedev_downdev() and in
> freedev() after timer_delete_sync() so that pending work cannot run
> concurrently with device teardown or outlive the freed struct aoedev.
>
> Check DEVFL_TKILL under d->lock in aoecmd_sleepwork() and aoeblk_gdalloc()
> instead of triggering WARN_ON() or setting DEVFL_UP on a device that is
> being killed.
Thanks Bart, I've replaced the previous patch with this one.
--
Jens Axboe
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-29 15:54 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 15:45 [PATCH] aoe: Do not free a device while its work is queued or running Bart Van Assche
2026-10-29 15:54 ` Jens Axboe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox