答复: [PATCH v3] scsi: sd: mark disk dead on surprise host removal to avoid I/O hang

From: 胡连勤

Date: Mon Sep 14 2026 - 02:39:58 EST


> drivers/scsi/sd.c | 10 ++++++++++
> include/scsi/scsi_host.h | 8 ++++++++
> 2 files changed, 18 insertions(+)
>
> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
> index 599e75f33334..fb5086465a23 100644
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -4257,6 +4257,16 @@ static void sd_remove(struct scsi_device *sdp)
>
> scsi_autopm_get_device(sdkp->device);
>
> + /*
> + * If the host is being torn down (e.g. USB surprise removal),
> + * the device cannot handle I/O anymore.
> + * Mark the disk dead before del_gendisk() so that new I/O is
> + * rejected immediately and sync_filesystem() in del_gendisk()
> + * is skipped, avoiding a hang waiting for I/O that can never
> + * complete.
> + */
> + if (scsi_host_in_cancel(sdkp->device->host))
> + 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 f6b286fa59f2..03523ad3284d 100644
> --- a/include/scsi/scsi_host.h
> +++ b/include/scsi/scsi_host.h
> @@ -796,6 +796,14 @@ static inline int scsi_host_in_recovery(struct Scsi_Host *shost)
> shost->tmf_in_progress;
> }
>
> +static inline bool scsi_host_in_cancel(struct Scsi_Host *shost)
> +{
> + enum scsi_host_state state = scsi_get_host_state(shost);
> +
> + return state == SHOST_CANCEL ||
> + state == SHOST_CANCEL_RECOVERY;
> +}
> +
> extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *);
> extern void scsi_flush_work(struct Scsi_Host *);
>
> --
> 2.39.0


Friendly ping.

The block counterpart of this fix has been merged as 7e9a46004b47
("block: set QUEUE_FLAG_DYING unconditionally in blk_mark_disk_dead()")
via Jens' tree, with Reviewed-by from Christoph.

However, this SCSI part is still needed to fully fix the hang.
Without it, sd_remove() calls del_gendisk() directly, which invokes
blk_report_disk_dead(disk, false) ― where surprise is false ― before
GD_DEAD is ever set. This reaches fs_bdev_mark_dead() →
sync_filesystem() → __bio_queue_enter() and blocks indefinitely,
since QUEUE_FLAG_DYING is not yet set at that point.

This patch calls blk_mark_disk_dead() before del_gendisk() when the
host is in SHOST_CANCEL / SHOST_CANCEL_RECOVERY, so that GD_DEAD
and QUEUE_FLAG_DYING are set early and del_gendisk() skips the
blk_report_disk_dead(disk, false) call entirely ― matching the NVMe
surprise removal behavior.

Bart, since you recently touched scsi_host.h (4c461ee2b2a5), could
you take a look?

The patch still applies cleanly on top of current linux-next (only
context line offsets).

Thanks for your time.