From: Ching Huang <ching2048@areca.com.tw>
Date: Thu, 15 Mar 2018 14:33:36 +0800
Subject: [PATCH] scsi: arcmsr: Handle adapter removed due to thunderbolt cable
Git-commit: c4c1adb3490ea4b8b13da2dae8d563350ded4988
Patch-mainline: v4.17-rc1
References: bsc#1118139
 disconnection.

Handle adapter removed due to thunderbolt cable disconnection.

Signed-off-by: Ching Huang <ching2048@areca.com.tw>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Acked-by: Hannes Reinecke <hare@suse.com>
---
 drivers/scsi/arcmsr/arcmsr_hba.c | 80 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 75e828bd30e3..2f52c53e4faa 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -1446,12 +1446,80 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
 	}
 }
 
+static void arcmsr_remove_scsi_devices(struct AdapterControlBlock *acb)
+{
+	char *acb_dev_map = (char *)acb->device_map;
+	int target, lun, i;
+	struct scsi_device *psdev;
+	struct CommandControlBlock *ccb;
+	char temp;
+
+	for (i = 0; i < acb->maxFreeCCB; i++) {
+		ccb = acb->pccb_pool[i];
+		if (ccb->startdone == ARCMSR_CCB_START) {
+			ccb->pcmd->result = DID_NO_CONNECT << 16;
+			arcmsr_pci_unmap_dma(ccb);
+			ccb->pcmd->scsi_done(ccb->pcmd);
+		}
+	}
+	for (target = 0; target < ARCMSR_MAX_TARGETID; target++) {
+		temp = *acb_dev_map;
+		if (temp) {
+			for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
+				if (temp & 1) {
+					psdev = scsi_device_lookup(acb->host,
+						0, target, lun);
+					if (psdev != NULL) {
+						scsi_remove_device(psdev);
+						scsi_device_put(psdev);
+					}
+				}
+				temp >>= 1;
+			}
+			*acb_dev_map = 0;
+		}
+		acb_dev_map++;
+	}
+}
+
+static void arcmsr_free_pcidev(struct AdapterControlBlock *acb)
+{
+	struct pci_dev *pdev;
+	struct Scsi_Host *host;
+
+	host = acb->host;
+	arcmsr_free_sysfs_attr(acb);
+	scsi_remove_host(host);
+	flush_work(&acb->arcmsr_do_message_isr_bh);
+	del_timer_sync(&acb->eternal_timer);
+	if (set_date_time)
+		del_timer_sync(&acb->refresh_timer);
+	pdev = acb->pdev;
+	arcmsr_free_irq(pdev, acb);
+	arcmsr_free_ccb_pool(acb);
+	arcmsr_free_mu(acb);
+	arcmsr_unmap_pciregion(acb);
+	pci_release_regions(pdev);
+	scsi_host_put(host);
+	pci_disable_device(pdev);
+}
+
 static void arcmsr_remove(struct pci_dev *pdev)
 {
 	struct Scsi_Host *host = pci_get_drvdata(pdev);
 	struct AdapterControlBlock *acb =
 		(struct AdapterControlBlock *) host->hostdata;
 	int poll_count = 0;
+	uint16_t dev_id;
+
+	pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id);
+	if (dev_id == 0xffff) {
+		acb->acb_flags &= ~ACB_F_IOP_INITED;
+		acb->acb_flags |= ACB_F_ADAPTER_REMOVED;
+		arcmsr_remove_scsi_devices(acb);
+		arcmsr_free_pcidev(acb);
+		return;
+	}
 	arcmsr_free_sysfs_attr(acb);
 	scsi_remove_host(host);
 	flush_work(&acb->arcmsr_do_message_isr_bh);
@@ -1499,6 +1567,8 @@ static void arcmsr_shutdown(struct pci_dev *pdev)
 	struct Scsi_Host *host = pci_get_drvdata(pdev);
 	struct AdapterControlBlock *acb =
 		(struct AdapterControlBlock *)host->hostdata;
+	if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+		return;
 	del_timer_sync(&acb->eternal_timer);
 	if (set_date_time)
 		del_timer_sync(&acb->refresh_timer);
@@ -2931,6 +3001,12 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd,
 	struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
 	struct CommandControlBlock *ccb;
 	int target = cmd->device->id;
+
+	if (acb->acb_flags & ACB_F_ADAPTER_REMOVED) {
+		cmd->result = (DID_NO_CONNECT << 16);
+		cmd->scsi_done(cmd);
+		return 0;
+	}
 	cmd->scsi_done = done;
 	cmd->host_scribble = NULL;
 	cmd->result = 0;
@@ -4177,6 +4253,8 @@ static int arcmsr_bus_reset(struct scsi_cmnd *cmd)
 	int retry_count = 0;
 	int rtn = FAILED;
 	acb = (struct AdapterControlBlock *) cmd->device->host->hostdata;
+	if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+		return SUCCESS;
 	pr_notice("arcmsr: executing bus reset eh.....num_resets = %d,"
 		" num_aborts = %d \n", acb->num_resets, acb->num_aborts);
 	acb->num_resets++;
@@ -4243,6 +4321,8 @@ static int arcmsr_abort(struct scsi_cmnd *cmd)
 	int rtn = FAILED;
 	uint32_t intmask_org;
 
+	if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+		return SUCCESS;
 	printk(KERN_NOTICE
 		"arcmsr%d: abort device command of scsi id = %d lun = %d\n",
 		acb->host->host_no, cmd->device->id, (u32)cmd->device->lun);
-- 
2.12.3

