Re: [PATCH v3] ata: libata: avoid long timeouts on hot-unplugged SATA DAS

Damien Le Moal
2025-12-08T03:52:11Z

On 12/1/25 6:46 PM, Henry Tseng wrote:
> When a SATA DAS enclosure is connected behind a Thunderbolt PCIe
> switch, hot-unplugging the whole enclosure causes pciehp to tear down
> the PCI hierarchy before the SCSI layer issues SYNCHRONIZE CACHE and
> START STOP UNIT for the disks.
>
> libata still queues these commands and the AHCI driver tries to access
> the HBA registers even though the PCI channel is already offline. This
> results in a series of timeouts and error recovery attempts, e.g.:
>
> [ 824.778346] pcieport 0000:00:07.0: pciehp: Slot(14): Link Down
> [ 891.612720] ata8.00: qc timeout after 5000 msecs (cmd 0xec)
> [ 902.876501] ata8.00: qc timeout after 10000 msecs (cmd 0xec)
> [ 934.107998] ata8.00: qc timeout after 30000 msecs (cmd 0xec)
> [ 936.206431] sd 7:0:0:0: [sda] Synchronize Cache(10) failed:
> Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK
> ...
> [ 1006.298356] ata1.00: qc timeout after 5000 msecs (cmd 0xec)
> [ 1017.561926] ata1.00: qc timeout after 10000 msecs (cmd 0xec)
> [ 1048.791790] ata1.00: qc timeout after 30000 msecs (cmd 0xec)
> [ 1050.890035] sd 0:0:0:0: [sdb] Synchronize Cache(10) failed:
> Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK
>
> With this patch applied, the same hot-unplug looks like:
>
> [ 59.965496] pcieport 0000:00:07.0: pciehp: Slot(14): Link Down
> [ 60.002502] sd 7:0:0:0: [sda] Synchronize Cache(10) failed:
> Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK
> ...
> [ 60.103050] sd 0:0:0:0: [sdb] Synchronize Cache(10) failed:
> Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK
>
> In this test setup with two disks, the hot-unplug sequence shrinks from
> about 226 seconds (~3.8 minutes) between the Link Down event and the
> last SYNCHRONIZE CACHE failure to under a second. Without this patch the
> total delay grows roughly with the number of disks, because each disk
> gets its own SYNCHRONIZE CACHE and qc timeout series.
>
> If the underlying PCI device is already gone, these commands cannot
> succeed anyway. Avoid issuing them by introducing
> ata_adapter_is_online(), which checks pci_channel_offline() for
> PCI-based hosts. It is used from ata_scsi_find_dev() to return NULL,
> causing the SCSI layer to fail new commands with DID_BAD_TARGET
> immediately, and from ata_qc_issue() to bail out before touching the
> HBA registers.
>
> Since such failures would otherwise trigger libata error handling,
> ata_adapter_is_online() is also consulted from ata_scsi_port_error_handler().
> When the adapter is offline, libata skips ap->ops->error_handler(ap) and
> completes error handling using the existing path, rather than running
> a full EH sequence against a dead adapter.
>
> With this change, SYNCHRONIZE CACHE and START STOP UNIT commands
> issued during hot-unplug fail quickly once the PCI channel is offline,
> without qc timeout spam or long libata EH delays.
>
> Suggested-by: Damien Le Moal <dlemoal@kernel.org>
> Signed-off-by: Henry Tseng <henrytseng@qnap.com>

Applied to for-6.20. Thanks!

--
Damien Le Moal
Western Digital Research