From 3ba5ad4ed89a362609fe58c0bc555a946d0485c2 Mon Sep 17 00:00:00 2001 From: LeoLiu-oc Date: Mon, 22 Sep 2025 15:14:17 +0800 Subject: [PATCH] anolis: usb: storage: Fix deadlock issue when remove TBT dock in system sleep state ANBZ: #25255 On USB4 support platform, plug TBT3 dock into typec port, then plug an ext4 format Udisk into the TBT3 dock. Then put system into Hibernation after device is enumerated. If plug out TBT3 dock during sleep state then wakeup system, the system may randomly encounter deadlocks during restore phase of hibernation. And restore cannot be successfully completed finally. More explanations about deadlocks are as follows: This TBT3 dock that consists of a PCIe switch and a PCIe endpoint. RP-- 00.0-+ [8086:15ef] Upstream Port +-02.0-+ [8086:15ef] Downstream Port | +-00.0 [8086:15f0] Thunderbolt 3 USB Controller +-04.0 [8086:15ef] Downstream Port During the resume process, the PCI driver detected that the switch under RP was disconnected, so it started hot unplugging processing, which will remove the entire PCIe hierarchy behind RP. The removal process is as follows: pciehp_unconfigure_device pci_stop_and_remove_bus_device pci_stop_dev ... xhci_pci_remove usb_remove_hcd usb_disconnect usb_disable_device usb_unbind_interface usb_stor_disconnect quiesce_and_remove_host scsi_remove_host scsi_forget_host __scsi_remove_device sd_remove del_gendisk invalidate_partition fsync_bdev sync_filesystem __sync_filesystem sb->s_op->sync_fs ext4_sync_fs blkdev_issue_flush submit_bio_wait submit_bio generic_make_request blk_queue_enter Finally, it will stuck on the blk_queue_enter function and will never return, As request queue not mark dying and only pm request is allowed. On the other hand, udisk and sd device resume also need to get device_lock which has already been obtained during the remove process. Therefore, a deadlock will occur here. To fix this issue, when deleting a SCSI device, if it is detected that the device was suprise removed, mark the device's request queue as dying. At the same time, add callback function to usb storage driver to identify surprise remove. Reviewed-by: Tony W. Wang Tested-by: Lyle Li Signed-off-by: LeoLiu-oc --- drivers/scsi/sd.c | 4 ++++ drivers/usb/storage/scsiglue.c | 12 +++++++++++- drivers/usb/storage/uas.c | 10 ++++++++++ include/scsi/scsi_host.h | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 873c920eb0cf..021e25a0e5cf 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3809,9 +3809,13 @@ static int sd_probe(struct device *dev) static int sd_remove(struct device *dev) { struct scsi_disk *sdkp = dev_get_drvdata(dev); + struct scsi_device *sdev = sdkp->device; scsi_autopm_get_device(sdkp->device); + if (sdev->host->hostt->mark_dead && sdev->host->hostt->mark_dead(sdev->host)) + blk_mark_disk_dead(sdkp->disk); + device_del(&sdkp->disk_dev); del_gendisk(sdkp->disk); if (!sdkp->suspended) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 12cf9940e5b6..2a62d85b7f2a 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -496,6 +496,16 @@ static int bus_reset(struct scsi_cmnd *srb) return result < 0 ? FAILED : SUCCESS; } +static bool device_dead(struct Scsi_Host *shost) +{ + struct us_data *us = host_to_us(shost); + + if (us->pusb_dev->state < USB_STATE_CONFIGURED) + return true; + + return false; +} + /* * Report a driver-initiated device reset to the SCSI layer. * Calling this for a SCSI-initiated reset is unnecessary but harmless. @@ -634,7 +644,7 @@ static const struct scsi_host_template usb_stor_host_template = { .eh_abort_handler = command_abort, .eh_device_reset_handler = device_reset, .eh_bus_reset_handler = bus_reset, - + .mark_dead = device_dead, /* queue commands only, only one command per LUN */ .can_queue = 1, diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index f794cb39cc31..09873f6af9cb 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -805,6 +805,15 @@ static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) return SUCCESS; } +static bool uas_device_dead(struct Scsi_Host *shost) +{ + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + + if (devinfo->udev->state < USB_STATE_CONFIGURED) + return true; + return false; +} + static int uas_target_alloc(struct scsi_target *starget) { struct uas_dev_info *devinfo = (struct uas_dev_info *) @@ -904,6 +913,7 @@ static const struct scsi_host_template uas_host_template = { .module = THIS_MODULE, .name = "uas", .queuecommand = uas_queuecommand, + .mark_dead = uas_device_dead, .target_alloc = uas_target_alloc, .slave_alloc = uas_slave_alloc, .slave_configure = uas_slave_configure, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 9232930d73c4..8c35a34da632 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -154,7 +154,7 @@ struct scsi_host_template { int (* eh_target_reset_handler)(struct scsi_cmnd *); int (* eh_bus_reset_handler)(struct scsi_cmnd *); int (* eh_host_reset_handler)(struct scsi_cmnd *); - + bool (*mark_dead)(struct Scsi_Host *); /* * Before the mid layer attempts to scan for a new device where none * currently exists, it will call this entry in your driver. Should -- Gitee