Commit e9dedc13 authored by John Garry's avatar John Garry Committed by Martin K. Petersen
Browse files

scsi: hisi_sas: Fix rescan after deleting a disk

Removing an ATA device via sysfs means that the device may not be found
through re-scanning:

root@ubuntu:/home/john# lsscsi
[0:0:0:0] disk SanDisk LT0200MO P404 /dev/sda
[0:0:1:0] disk ATA HGST HUS724040AL A8B0 /dev/sdb
[0:0:8:0] enclosu 12G SAS Expander RevB -
root@ubuntu:/home/john# echo 1 > /sys/block/sdb/device/delete
root@ubuntu:/home/john# echo "- - -" > /sys/class/scsi_host/host0/scan
root@ubuntu:/home/john# lsscsi
[0:0:0:0] disk SanDisk LT0200MO P404 /dev/sda
[0:0:8:0] enclosu 12G SAS Expander RevB -
root@ubuntu:/home/john#

The problem is that the rescan of the device may conflict with the device
in being re-initialized, as follows:

 - In the rescan we call hisi_sas_slave_alloc() in store_scan() ->
   sas_user_scan() -> [__]scsi_scan_target() -> scsi_probe_and_add_lunc()
   -> scsi_alloc_sdev() -> hisi_sas_slave_alloc() -> hisi_sas_init_device()
   In hisi_sas_init_device() we issue an IT nexus reset for ATA devices

 - That IT nexus causes the remote PHY to go down and this triggers a bcast
   event

 - In parallel libsas processes the bcast event, finds that the phy is down
   and marks the device as gone

The hard reset issued in hisi_sas_init_device() is unncessary - as
described in the code comment - so remove it. Also set dev status as
HISI_SAS_DEV_NORMAL as the hisi_sas_init_device() call.

Link: https://lore.kernel.org/r/1652354134-171343-4-git-send-email-john.garry@huawei.com


Fixes: 36c6b761 ("scsi: hisi_sas: Initialise devices in .slave_alloc callback")
Tested-by: default avatarYihang Li <liyihang6@hisilicon.com>
Reviewed-by: default avatarXiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: default avatarJohn Garry <john.garry@huawei.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 71453bd9
Loading
Loading
Loading
Loading
+18 −29
Original line number Original line Diff line number Diff line
@@ -709,8 +709,6 @@ static int hisi_sas_init_device(struct domain_device *device)
	struct scsi_lun lun;
	struct scsi_lun lun;
	int retry = HISI_SAS_DISK_RECOVER_CNT;
	int retry = HISI_SAS_DISK_RECOVER_CNT;
	struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
	struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
	struct device *dev = hisi_hba->dev;
	struct sas_phy *local_phy;


	switch (device->dev_type) {
	switch (device->dev_type) {
	case SAS_END_DEVICE:
	case SAS_END_DEVICE:
@@ -729,30 +727,18 @@ static int hisi_sas_init_device(struct domain_device *device)
	case SAS_SATA_PM_PORT:
	case SAS_SATA_PM_PORT:
	case SAS_SATA_PENDING:
	case SAS_SATA_PENDING:
		/*
		/*
		 * send HARD RESET to clear previous affiliation of
		 * If an expander is swapped when a SATA disk is attached then
		 * STP target port
		 * we should issue a hard reset to clear previous affiliation
		 * of STP target port, see SPL (chapter 6.19.4).
		 *
		 * However we don't need to issue a hard reset here for these
		 * reasons:
		 * a. When probing the device, libsas/libata already issues a
		 * hard reset in sas_probe_sata() -> ata_sas_async_probe().
		 * Note that in hisi_sas_debug_I_T_nexus_reset() we take care
		 * to issue a hard reset by checking the dev status (== INIT).
		 * b. When resetting the controller, this is simply unnecessary.
		 */
		 */
		local_phy = sas_get_local_phy(device);
		if (!scsi_is_sas_phy_local(local_phy) &&
		    !test_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags)) {
			unsigned long deadline = ata_deadline(jiffies, 20000);
			struct sata_device *sata_dev = &device->sata_dev;
			struct ata_host *ata_host = sata_dev->ata_host;
			struct ata_port_operations *ops = ata_host->ops;
			struct ata_port *ap = sata_dev->ap;
			struct ata_link *link;
			unsigned int classes;

			ata_for_each_link(link, ap, EDGE)
				rc = ops->hardreset(link, &classes,
						    deadline);
		}
		sas_put_local_phy(local_phy);
		if (rc) {
			dev_warn(dev, "SATA disk hardreset fail: %d\n", rc);
			return rc;
		}

		while (retry-- > 0) {
		while (retry-- > 0) {
			rc = hisi_sas_softreset_ata_disk(device);
			rc = hisi_sas_softreset_ata_disk(device);
			if (!rc)
			if (!rc)
@@ -768,15 +754,19 @@ static int hisi_sas_init_device(struct domain_device *device)


int hisi_sas_slave_alloc(struct scsi_device *sdev)
int hisi_sas_slave_alloc(struct scsi_device *sdev)
{
{
	struct domain_device *ddev;
	struct domain_device *ddev = sdev_to_domain_dev(sdev);
	struct hisi_sas_device *sas_dev = ddev->lldd_dev;
	int rc;
	int rc;


	rc = sas_slave_alloc(sdev);
	rc = sas_slave_alloc(sdev);
	if (rc)
	if (rc)
		return rc;
		return rc;
	ddev = sdev_to_domain_dev(sdev);


	return hisi_sas_init_device(ddev);
	rc = hisi_sas_init_device(ddev);
	if (rc)
		return rc;
	sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
	return 0;
}
}
EXPORT_SYMBOL_GPL(hisi_sas_slave_alloc);
EXPORT_SYMBOL_GPL(hisi_sas_slave_alloc);


@@ -826,7 +816,6 @@ static int hisi_sas_dev_found(struct domain_device *device)
	dev_info(dev, "dev[%d:%x] found\n",
	dev_info(dev, "dev[%d:%x] found\n",
		sas_dev->device_id, sas_dev->dev_type);
		sas_dev->device_id, sas_dev->dev_type);


	sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
	return 0;
	return 0;


err_out:
err_out: