* [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang
@ 2026-10-09 11:29 胡连勤
2026-10-12 14:49 ` Christoph Hellwig
2026-10-13 8:14 ` [usb-storage] " Oliver Neukum
0 siblings, 2 replies; 5+ messages in thread
From: 胡连勤 @ 2026-10-09 11:29 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Bart Van Assche,
Greg Kroah-Hartman, Alan Stern
Cc: Christoph Hellwig, Jens Axboe, Hannes Reinecke, Mike Christie,
Damien Le Moal, John Garry, Shin'ichiro Kawasaki,
linux-scsi@vger.kernel.org, linux-usb@vger.kernel.org,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
usb-storage@lists.one-eyed-alien.net, stable@vger.kernel.org
When a USB mass storage device with a mounted filesystem (e.g. exFAT)
enters runtime suspend (autosuspend), physical removal triggers
scsi_remove_host() which invokes sd_remove() -> del_gendisk() ->
blk_report_disk_dead(). This triggers sync_filesystem() to write
back dirty data, submits bio and blocks in __bio_queue_enter()
waiting for I/O completion that can never happen since the device
is already gone, leading to a hung task.
del_gendisk() unconditionally calls blk_report_disk_dead(disk, false)
when GD_DEAD is not yet set, which always attempts to sync the
filesystem. For surprise removal where the device cannot handle I/O,
this sync is futile and hangs forever.
Add a surprise_removal flag to struct Scsi_Host that LLDDs can set
before calling scsi_remove_host() to indicate that the device is
already gone and cannot handle I/O. In sd_remove(), check this flag
and call blk_mark_disk_dead() to set GD_DEAD, so that del_gendisk()
sees GD_DEAD already set and skips the sync. For orderly removal
where the flag is not set, sync proceeds normally.
The actual setting of this flag by the USB storage driver is done
in a follow-up patch.
Call trace on USB storage surprise removal:
INFO: task kworker/1:0:31 blocked for more than 147 seconds.
Call trace:
__switch_to+0x198/0x380
__schedule+0x548/0xfbc
schedule+0x4c/0x118
__bio_queue_enter+0xb8/0x174
blk_mq_submit_bio+0x640/0x7e4
__submit_bio+0x1a4/0x314
__submit_bio_noacct_mq+0x38/0x8c
submit_bio_noacct+0x678/0x750
submit_bio+0xa8/0x1c8
submit_bh_wbc+0x148/0x1b4
__sync_dirty_buffer+0x120/0x1f8
exfat_sync_fs+0xa4/0xe0
sync_filesystem+0xa8/0xdc
fs_bdev_mark_dead+0x30/0x88
bdev_mark_dead+0x54/0xc4
blk_report_disk_dead+0x8c/0xd4
del_gendisk+0xa0/0x2f8
sd_remove+0x30/0x60
device_release_driver_internal+0x1c4/0x2bc
device_release_driver+0x18/0x28
bus_remove_device+0x158/0x170
device_del+0x1c8/0x320
__scsi_remove_device+0x9c/0x184
scsi_forget_host+0x50/0x70
scsi_remove_host+0x88/0x18c
usb_stor_disconnect+0x68/0xf4
usb_unbind_interface+0x13c/0x340
device_release_driver_internal+0x1c4/0x2bc
device_release_driver+0x18/0x28
bus_remove_device+0x158/0x170
device_del+0x1c8/0x320
usb_disable_device+0x84/0x190
usb_disconnect+0xe8/0x338
hub_event+0xbd8/0x19ac
process_scheduled_works+0x200/0x9d8
worker_thread+0x154/0x3b0
Fixes: 6f8191fdf41d ("block: simplify disk shutdown")
Cc: stable@vger.kernel.org
Signed-off-by: Lianqin Hu <hulianqin@vivo.com>
---
Changes in v5:
- Fix v4 data loss risk: scsi_host_in_cancel() cannot distinguish
surprise removal from orderly removal, causing sync_filesystem()
to be skipped for all host removals.
- Replace with an explicit surprise_removal flag in struct Scsi_Host
that LLDDs set only when the device is physically gone.
- Split into a 2-patch series; patch 2/2 sets the flag in USB storage.
- Link to v4: https://lore.kernel.org/all/TYUPR06MB6217570B9AFA4B5980FCCED0D2932@TYUPR06MB6217.apcprd06.prod.outlook.com/
Changes in v4:
- Repost: v3 did not receive any review feedback.
- The block layer counterpart (7e9a46004b47 'block: set QUEUE_FLAG_DYING
unconditionally in blk_mark_disk_dead()') has been merged, but it alone
is not sufficient as the queue is not frozen during autosuspend. This
SCSI patch is still needed to mark the disk dead before del_gendisk().
- Link to v3: https://lore.kernel.org/all/TYUPR06MB62177178F9305DF5C1A7D046D2A92@TYUPR06MB6217.apcprd06.prod.outlook.com/
Changes in v3:
- Repost after merge window closure as requested by maintainer.
- Use scsi_get_host_state() to match scsi_host_in_recovery() pattern.
- Fix Link to v1 URL.
- Link to v2: https://lore.kernel.org/all/PUZPR06MB622453C37D2BABC5E71B195DD2DC2@PUZPR06MB6224.apcprd06.prod.outlook.com/
Changes in v2:
- Split into a two-patch series for independent review by block and SCSI maintainers.
- This is the SCSI part extracted from v1.
- Link to v1: https://lore.kernel.org/all/TYUPR06MB62172E412054140753D5E782D2C82@TYUPR06MB6217.apcprd06.prod.outlook.com/
drivers/scsi/sd.c | 4 ++++
include/scsi/scsi_host.h | 3 +++
2 files changed, 7 insertions(+)
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index a1b21ea14e54..98f861fb7e80 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -4272,6 +4272,10 @@ static void sd_remove(struct scsi_device *sdp)
scsi_autopm_get_device(sdkp->device);
+ /* Device is gone; avoid sync_filesystem() hang in del_gendisk(). */
+ if (sdkp->device->host->surprise_removal)
+ blk_mark_disk_dead(sdkp->disk);
+
device_del(&sdkp->disk_dev);
del_gendisk(sdkp->disk);
if (!sdkp->suspended)
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 16243ec376cd..6788e284877b 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -698,6 +698,9 @@ struct Scsi_Host {
/* Host responded with short (<36 bytes) INQUIRY result */
unsigned short_inquiry:1;
+ /* Device is physically gone; skip sync in sd_remove() */
+ unsigned surprise_removal:1;
+
/* The transport requires the LUN bits NOT to be stored in CDB[1] */
unsigned no_scsi2_lun_in_cdb:1;
--
2.48.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang
2026-10-09 11:29 [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang 胡连勤
@ 2026-10-12 14:49 ` Christoph Hellwig
2026-10-13 2:14 ` 答复: " 胡连勤
2026-10-13 8:14 ` [usb-storage] " Oliver Neukum
1 sibling, 1 reply; 5+ messages in thread
From: Christoph Hellwig @ 2026-10-12 14:49 UTC (permalink / raw)
To: 胡连勤
Cc: James E.J. Bottomley, Martin K. Petersen, Bart Van Assche,
Greg Kroah-Hartman, Alan Stern, Christoph Hellwig, Jens Axboe,
Hannes Reinecke, Mike Christie, Damien Le Moal, John Garry,
Shin'ichiro Kawasaki, linux-scsi@vger.kernel.org,
linux-usb@vger.kernel.org, linux-block@vger.kernel.org,
linux-kernel@vger.kernel.org,
usb-storage@lists.one-eyed-alien.net, stable@vger.kernel.org
On Fri, Oct 09, 2026 at 11:29:36AM +0000, 胡连勤 wrote:
> unsigned short_inquiry:1;
>
> + /* Device is physically gone; skip sync in sd_remove() */
> + unsigned surprise_removal:1;
Placing this right in the middle of static fields is a bit weird.
Maybe we should get started with an unsigned long and atomic bitops
for something like that actually is known to set it at any point in
runtime and thus most suspect to corruption non-atomic bitfields?
^ permalink raw reply [flat|nested] 5+ messages in thread
* 答复: [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang
2026-10-12 14:49 ` Christoph Hellwig
@ 2026-10-13 2:14 ` 胡连勤
0 siblings, 0 replies; 5+ messages in thread
From: 胡连勤 @ 2026-10-13 2:14 UTC (permalink / raw)
To: Christoph Hellwig
Cc: James E.J. Bottomley, Martin K. Petersen, Bart Van Assche,
Greg Kroah-Hartman, Alan Stern, Jens Axboe, Hannes Reinecke,
Mike Christie, Damien Le Moal, John Garry,
Shin'ichiro Kawasaki, linux-scsi@vger.kernel.org,
linux-usb@vger.kernel.org, linux-block@vger.kernel.org,
linux-kernel@vger.kernel.org,
usb-storage@lists.one-eyed-alien.net, stable@vger.kernel.org
Hi Christoph,
> > unsigned short_inquiry:1;
> >
> > + /* Device is physically gone; skip sync in sd_remove() */
> > + unsigned surprise_removal:1;
>
> Placing this right in the middle of static fields is a bit weird.
>
> Maybe we should get started with an unsigned long and atomic bitops
> for something like that actually is known to set it at any point in
> runtime and thus most suspect to corruption non-atomic bitfields?
Thanks for the review. You're right on both points.
I plan to change it as follows:
drivers/scsi/sd.c | 4 ++++
include/scsi/scsi_host.h | 7 +++++++
2 files changed, 11 insertions(+)
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index a1b21ea14e54..075a50c08404 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -4272,6 +4272,10 @@ static void sd_remove(struct scsi_device *sdp)
scsi_autopm_get_device(sdkp->device);
+ /* Device is gone; avoid sync_filesystem() hang in del_gendisk(). */
+ if (test_bit(SHOST_SURPRISE_REMOVAL, &sdkp->device->host->flags))
+ blk_mark_disk_dead(sdkp->disk);
+
device_del(&sdkp->disk_dev);
del_gendisk(sdkp->disk);
if (!sdkp->suspended)
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 16243ec376cd..66aa9ae607de 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -757,6 +757,13 @@ struct Scsi_Host {
/* Delay for runtime autosuspend */
int rpm_autosuspend_delay;
+ /*
+ * Host flags for runtime state that may be set concurrently.
+ * Use atomic bitops (set_bit/test_bit) to access.
+ */
+ unsigned long flags;
+#define SHOST_SURPRISE_REMOVAL 0
+
/*
* We should ensure that this is aligned, both for better performance
* and also because some compilers (m68k) don't automatically force
drivers/usb/storage/usb.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 064c7fc8e368..3a5a0cac2b78 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -930,6 +930,9 @@ static void quiesce_and_remove_host(struct us_data *us)
if (us->pusb_dev->state == USB_STATE_NOTATTACHED) {
set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
wake_up(&us->delay_wait);
+
+ /* Device is physically gone, cannot handle I/O */
+ set_bit(SHOST_SURPRISE_REMOVAL, &host->flags);
}
The flags field is placed after rpm_autosuspend_delay, away from the
static bitfields. set_bit()/test_bit() ensure atomic access when the
flag is set at disconnect time and read in sd_remove().
Regards,
Lianqin
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [usb-storage] [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang
2026-10-09 11:29 [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang 胡连勤
2026-10-12 14:49 ` Christoph Hellwig
@ 2026-10-13 8:14 ` Oliver Neukum
2026-10-13 9:14 ` 答复: " 胡连勤
1 sibling, 1 reply; 5+ messages in thread
From: Oliver Neukum @ 2026-10-13 8:14 UTC (permalink / raw)
To: 胡连勤, James E.J. Bottomley,
Martin K. Petersen, Bart Van Assche, Greg Kroah-Hartman,
Alan Stern
Cc: Christoph Hellwig, Jens Axboe, Hannes Reinecke, Mike Christie,
Damien Le Moal, John Garry, Shin'ichiro Kawasaki,
linux-scsi@vger.kernel.org, linux-usb@vger.kernel.org,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
usb-storage@lists.one-eyed-alien.net, stable@vger.kernel.org
On 09.10.26 13:29, '胡连勤' via USB Mass Storage on Linux wrote:
> index a1b21ea14e54..98f861fb7e80 100644
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -4272,6 +4272,10 @@ static void sd_remove(struct scsi_device *sdp)
>
> scsi_autopm_get_device(sdkp->device);
Here you resume the device
> + /* Device is gone; avoid sync_filesystem() hang in del_gendisk(). */
> + if (sdkp->device->host->surprise_removal)
> + blk_mark_disk_dead(sdkp->disk);
Here you check whether it exists.
That seems a bit inverted. What is the point of resuming a device
that has gone away?
Regards
Oliver
^ permalink raw reply [flat|nested] 5+ messages in thread
* 答复: [usb-storage] [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang
2026-10-13 8:14 ` [usb-storage] " Oliver Neukum
@ 2026-10-13 9:14 ` 胡连勤
0 siblings, 0 replies; 5+ messages in thread
From: 胡连勤 @ 2026-10-13 9:14 UTC (permalink / raw)
To: Oliver Neukum, James E.J. Bottomley, Martin K. Petersen,
Bart Van Assche, Greg Kroah-Hartman, Alan Stern
Cc: Christoph Hellwig, Jens Axboe, Hannes Reinecke, Mike Christie,
Damien Le Moal, John Garry, Shin'ichiro Kawasaki,
linux-scsi@vger.kernel.org, linux-usb@vger.kernel.org,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
usb-storage@lists.one-eyed-alien.net, stable@vger.kernel.org
Hi Oliver,
> > index a1b21ea14e54..98f861fb7e80 100644
> > --- a/drivers/scsi/sd.c
> > +++ b/drivers/scsi/sd.c
> > @@ -4272,6 +4272,10 @@ static void sd_remove(struct scsi_device *sdp)
> >
> > scsi_autopm_get_device(sdkp->device);
>
> Here you resume the device
>
> > + /* Device is gone; avoid sync_filesystem() hang in del_gendisk(). */
> > + if (sdkp->device->host->surprise_removal)
> > + blk_mark_disk_dead(sdkp->disk);
>
> Here you check whether it exists.
>
> That seems a bit inverted. What is the point of resuming a device
> that has gone away?
>
You're right. It makes no sense to resume a device that is already gone.
I'll move the surprise_removal check before scsi_autopm_get_device()
and skip the resume entirely for surprise removal:
if (test_bit(SHOST_SURPRISE_REMOVAL, &sdkp->device->host->flags))
blk_mark_disk_dead(sdkp->disk);
else
scsi_autopm_get_device(sdkp->device);
This way, for surprise removal we skip the resume and go straight to
marking the disk dead. For orderly removal, the resume proceeds as
before.
The complete changes:
drivers/scsi/sd.c | 6 +++++-
drivers/usb/storage/usb.c | 3 +++
include/scsi/scsi_host.h | 7 +++++++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index a1b21ea14e54..db06fe67f4ce 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -4270,7 +4270,11 @@ static void sd_remove(struct scsi_device *sdp)
struct device *dev = &sdp->sdev_gendev;
struct scsi_disk *sdkp = dev_get_drvdata(dev);
- scsi_autopm_get_device(sdkp->device);
+ /* Surprise removal: skip resume and sync to avoid I/O hang. */
+ if (test_bit(SHOST_SURPRISE_REMOVAL, &sdkp->device->host->flags))
+ blk_mark_disk_dead(sdkp->disk);
+ else
+ scsi_autopm_get_device(sdkp->device);
device_del(&sdkp->disk_dev);
del_gendisk(sdkp->disk);
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 16243ec376cd..66aa9ae607de 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -757,6 +757,13 @@ struct Scsi_Host {
/* Delay for runtime autosuspend */
int rpm_autosuspend_delay;
+ /*
+ * Host flags for runtime state that may be set concurrently.
+ * Use atomic bitops (set_bit/test_bit) to access.
+ */
+ unsigned long flags;
+#define SHOST_SURPRISE_REMOVAL 0
+
/*
* We should ensure that this is aligned, both for better performance
* and also because some compilers (m68k) don't automatically force
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 064c7fc8e368..3a5a0cac2b78 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -930,6 +930,9 @@ static void quiesce_and_remove_host(struct us_data *us)
if (us->pusb_dev->state == USB_STATE_NOTATTACHED) {
set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
wake_up(&us->delay_wait);
+
+ /* Device is physically gone, cannot handle I/O */
+ set_bit(SHOST_SURPRISE_REMOVAL, &host->flags);
}
Regards,
Lianqin
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-13 9:14 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 11:29 [PATCH v5 1/2] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang 胡连勤
2026-10-12 14:49 ` Christoph Hellwig
2026-10-13 2:14 ` 答复: " 胡连勤
2026-10-13 8:14 ` [usb-storage] " Oliver Neukum
2026-10-13 9:14 ` 答复: " 胡连勤
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox