scsi: arcmsr: Handle adapter removed due to thunderbolt cable disconnection.
authorChing Huang <ching2048@areca.com.tw>
Thu, 15 Mar 2018 06:33:36 +0000 (14:33 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 21 Mar 2018 22:46:30 +0000 (18:46 -0400)
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>
drivers/scsi/arcmsr/arcmsr_hba.c

index 75e828bd30e3f1cc11c5eea0f84aca58309dc4fc..2f52c53e4faa7b2965498c9c8ae6649cd83cfe45 100644 (file)
@@ -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);