Linux block layer
 help / color / mirror / Atom feed
* [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

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