diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_init.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 410 |
1 files changed, 274 insertions, 136 deletions
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4516d627deb9..e07f12a0871b 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2274,8 +2274,7 @@ lpfc_enable_msix(struct lpfc_hba *phba) ARRAY_SIZE(phba->msix_entries)); if (rc) { lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0420 Enable MSI-X failed (%d), continuing " - "with MSI\n", rc); + "0420 PCI enable MSI-X failed (%d)\n", rc); goto msi_fail_out; } else for (i = 0; i < LPFC_MSIX_VECTORS; i++) @@ -2292,9 +2291,9 @@ lpfc_enable_msix(struct lpfc_hba *phba) rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler, IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba); if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0421 MSI-X slow-path request_irq failed " - "(%d), continuing with MSI\n", rc); + "(%d)\n", rc); goto msi_fail_out; } @@ -2303,9 +2302,9 @@ lpfc_enable_msix(struct lpfc_hba *phba) IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba); if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0429 MSI-X fast-path request_irq failed " - "(%d), continuing with MSI\n", rc); + "(%d)\n", rc); goto irq_fail_out; } @@ -2326,7 +2325,7 @@ lpfc_enable_msix(struct lpfc_hba *phba) goto mbx_fail_out; rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); if (rc != MBX_SUCCESS) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, "0351 Config MSI mailbox command failed, " "mbxCmd x%x, mbxStatus x%x\n", pmb->mb.mbxCommand, pmb->mb.mbxStatus); @@ -2375,6 +2374,195 @@ lpfc_disable_msix(struct lpfc_hba *phba) } /** + * lpfc_enable_msi: Enable MSI interrupt mode. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to enable the MSI interrupt mode. The kernel + * function pci_enable_msi() is called to enable the MSI vector. The + * device driver is responsible for calling the request_irq() to register + * MSI vector with a interrupt the handler, which is done in this function. + * + * Return codes + * 0 - sucessful + * other values - error + */ +static int +lpfc_enable_msi(struct lpfc_hba *phba) +{ + int rc; + + rc = pci_enable_msi(phba->pcidev); + if (!rc) + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0462 PCI enable MSI mode success.\n"); + else { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0471 PCI enable MSI mode failed (%d)\n", rc); + return rc; + } + + rc = request_irq(phba->pcidev->irq, lpfc_intr_handler, + IRQF_SHARED, LPFC_DRIVER_NAME, phba); + if (rc) { + pci_disable_msi(phba->pcidev); + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "0478 MSI request_irq failed (%d)\n", rc); + } + return rc; +} + +/** + * lpfc_disable_msi: Disable MSI interrupt mode. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to disable the MSI interrupt mode. The driver + * calls free_irq() on MSI vector it has done request_irq() on before + * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and + * a device will be left with MSI enabled and leaks its vector. + */ + +static void +lpfc_disable_msi(struct lpfc_hba *phba) +{ + free_irq(phba->pcidev->irq, phba); + pci_disable_msi(phba->pcidev); + return; +} + +/** + * lpfc_log_intr_mode: Log the active interrupt mode + * @phba: pointer to lpfc hba data structure. + * @intr_mode: active interrupt mode adopted. + * + * This routine it invoked to log the currently used active interrupt mode + * to the device. + */ +static void +lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode) +{ + switch (intr_mode) { + case 0: + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0470 Enable INTx interrupt mode.\n"); + break; + case 1: + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0481 Enabled MSI interrupt mode.\n"); + break; + case 2: + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0480 Enabled MSI-X interrupt mode.\n"); + break; + default: + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0482 Illegal interrupt mode.\n"); + break; + } + return; +} + +static void +lpfc_stop_port(struct lpfc_hba *phba) +{ + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + /* Clear all pending interrupts */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + /* Reset some HBA SLI setup states */ + lpfc_stop_phba_timers(phba); + phba->pport->work_port_events = 0; + + return; +} + +/** + * lpfc_enable_intr: Enable device interrupt. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to enable device interrupt and associate driver's + * interrupt handler(s) to interrupt vector(s). Depends on the interrupt + * mode configured to the driver, the driver will try to fallback from the + * configured interrupt mode to an interrupt mode which is supported by the + * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ. + * + * Return codes + * 0 - sucessful + * other values - error + **/ +static uint32_t +lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) +{ + uint32_t intr_mode = LPFC_INTR_ERROR; + int retval; + + if (cfg_mode == 2) { + /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */ + retval = lpfc_sli_config_port(phba, 3); + if (!retval) { + /* Now, try to enable MSI-X interrupt mode */ + retval = lpfc_enable_msix(phba); + if (!retval) { + /* Indicate initialization to MSI-X mode */ + phba->intr_type = MSIX; + intr_mode = 2; + } + } + } + + /* Fallback to MSI if MSI-X initialization failed */ + if (cfg_mode >= 1 && phba->intr_type == NONE) { + retval = lpfc_enable_msi(phba); + if (!retval) { + /* Indicate initialization to MSI mode */ + phba->intr_type = MSI; + intr_mode = 1; + } + } + + /* Fallback to INTx if both MSI-X/MSI initalization failed */ + if (phba->intr_type == NONE) { + retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, + IRQF_SHARED, LPFC_DRIVER_NAME, phba); + if (!retval) { + /* Indicate initialization to INTx mode */ + phba->intr_type = INTx; + intr_mode = 0; + } + } + return intr_mode; +} + +/** + * lpfc_disable_intr: Disable device interrupt. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to disable device interrupt and disassociate the + * driver's interrupt handler(s) from interrupt vector(s). Depending on the + * interrupt mode, the driver will release the interrupt vector(s) for the + * message signaled interrupt. + **/ +static void +lpfc_disable_intr(struct lpfc_hba *phba) +{ + /* Disable the currently initialized interrupt mode */ + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else if (phba->intr_type == MSI) + lpfc_disable_msi(phba); + else if (phba->intr_type == INTx) + free_irq(phba->pcidev->irq, phba); + + /* Reset interrupt management states */ + phba->intr_type = NONE; + phba->sli.slistat.sli_intr = 0; + + return; +} + +/** * lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem. * @pdev: pointer to PCI device * @pid: pointer to PCI device identifier @@ -2404,6 +2592,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) int error = -ENODEV, retval; int i, hbq_count; uint16_t iotag; + uint32_t cfg_mode, intr_mode; int bars = pci_select_bars(pdev, IORESOURCE_MEM); struct lpfc_adapter_event_header adapter_event; @@ -2606,7 +2795,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) lpfc_debugfs_initialize(vport); pci_set_drvdata(pdev, shost); - phba->intr_type = NONE; phba->MBslimaddr = phba->slim_memmap_p; phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET; @@ -2614,63 +2802,58 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET; phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET; - /* Configure and enable interrupt */ - if (phba->cfg_use_msi == 2) { - /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */ - error = lpfc_sli_config_port(phba, 3); - if (error) - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0427 Firmware not capable of SLI 3 mode.\n"); - else { - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0426 Firmware capable of SLI 3 mode.\n"); - /* Now, try to enable MSI-X interrupt mode */ - error = lpfc_enable_msix(phba); - if (!error) { - phba->intr_type = MSIX; - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0430 enable MSI-X mode.\n"); - } - } - } - - /* Fallback to MSI if MSI-X initialization failed */ - if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) { - retval = pci_enable_msi(phba->pcidev); - if (!retval) { - phba->intr_type = MSI; - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0473 enable MSI mode.\n"); - } else - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0452 enable IRQ mode.\n"); - } - - /* MSI-X is the only case the doesn't need to call request_irq */ - if (phba->intr_type != MSIX) { - retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, - IRQF_SHARED, LPFC_DRIVER_NAME, phba); - if (retval) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable " - "interrupt handler failed\n"); - error = retval; - goto out_disable_msi; - } else if (phba->intr_type != MSI) - phba->intr_type = INTx; - } - + /* Configure sysfs attributes */ if (lpfc_alloc_sysfs_attr(vport)) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "1476 Failed to allocate sysfs attr\n"); error = -ENOMEM; - goto out_free_irq; + goto out_destroy_port; } - if (lpfc_sli_hba_setup(phba)) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1477 Failed to set up hba\n"); - error = -ENODEV; - goto out_remove_device; + cfg_mode = phba->cfg_use_msi; + while (true) { + /* Configure and enable interrupt */ + intr_mode = lpfc_enable_intr(phba, cfg_mode); + if (intr_mode == LPFC_INTR_ERROR) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0426 Failed to enable interrupt.\n"); + goto out_free_sysfs_attr; + } + /* HBA SLI setup */ + if (lpfc_sli_hba_setup(phba)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "1477 Failed to set up hba\n"); + error = -ENODEV; + goto out_remove_device; + } + + /* Wait 50ms for the interrupts of previous mailbox commands */ + msleep(50); + /* Check active interrupts received */ + if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) { + /* Log the current active interrupt mode */ + phba->intr_mode = intr_mode; + lpfc_log_intr_mode(phba, intr_mode); + break; + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0451 Configure interrupt mode (%d) " + "failed active interrupt test.\n", + intr_mode); + if (intr_mode == 0) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0479 Failed to enable " + "interrupt.\n"); + error = -ENODEV; + goto out_remove_device; + } + /* Stop HBA SLI setups */ + lpfc_stop_port(phba); + /* Disable the current interrupt mode */ + lpfc_disable_intr(phba); + /* Try next level of interrupt mode */ + cfg_mode = --intr_mode; + } } /* @@ -2700,22 +2883,15 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) return 0; out_remove_device: - lpfc_free_sysfs_attr(vport); spin_lock_irq(shost->host_lock); vport->load_flag |= FC_UNLOADING; spin_unlock_irq(shost->host_lock); -out_free_irq: lpfc_stop_phba_timers(phba); phba->pport->work_port_events = 0; - - if (phba->intr_type == MSIX) - lpfc_disable_msix(phba); - else - free_irq(phba->pcidev->irq, phba); - -out_disable_msi: - if (phba->intr_type == MSI) - pci_disable_msi(phba->pcidev); + lpfc_disable_intr(phba); +out_free_sysfs_attr: + lpfc_free_sysfs_attr(vport); +out_destroy_port: destroy_port(vport); out_kthread_stop: kthread_stop(phba->worker_thread); @@ -2804,13 +2980,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev) lpfc_debugfs_terminate(vport); - if (phba->intr_type == MSIX) - lpfc_disable_msix(phba); - else { - free_irq(phba->pcidev->irq, phba); - if (phba->intr_type == MSI) - pci_disable_msi(phba->pcidev); - } + /* Disable interrupt */ + lpfc_disable_intr(phba); pci_set_drvdata(pdev, NULL); scsi_host_put(shost); @@ -2908,6 +3079,7 @@ lpfc_pci_resume_one(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + uint32_t intr_mode; int error; lpfc_printf_log(phba, KERN_INFO, LOG_INIT, @@ -2930,19 +3102,22 @@ lpfc_pci_resume_one(struct pci_dev *pdev) return error; } - /* Enable interrupt from device */ - error = lpfc_enable_intr(phba); - if (error) { + /* Configure and enable interrupt */ + intr_mode = lpfc_enable_intr(phba, phba->intr_mode); + if (intr_mode == LPFC_INTR_ERROR) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0430 PM resume Failed to enable interrupt: " - "error=x%x.\n", error); - return error; - } + "0430 PM resume Failed to enable interrupt\n"); + return -EIO; + } else + phba->intr_mode = intr_mode; /* Restart HBA and bring it online */ lpfc_sli_brdrestart(phba); lpfc_online(phba); + /* Log the current active interrupt mode */ + lpfc_log_intr_mode(phba, phba->intr_mode); + return 0; } @@ -2989,13 +3164,8 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, pring = &psli->ring[psli->fcp_ring]; lpfc_sli_abort_iocb_ring(phba, pring); - if (phba->intr_type == MSIX) - lpfc_disable_msix(phba); - else { - free_irq(phba->pcidev->irq, phba); - if (phba->intr_type == MSI) - pci_disable_msi(phba->pcidev); - } + /* Disable interrupt */ + lpfc_disable_intr(phba); /* Request a slot reset. */ return PCI_ERS_RESULT_NEED_RESET; @@ -3023,7 +3193,7 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev) struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; struct lpfc_sli *psli = &phba->sli; - int error, retval; + uint32_t intr_mode; dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n"); if (pci_enable_device_mem(pdev)) { @@ -3040,55 +3210,23 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev) psli->sli_flag &= ~LPFC_SLI2_ACTIVE; spin_unlock_irq(&phba->hbalock); - /* Enable configured interrupt method */ - phba->intr_type = NONE; - if (phba->cfg_use_msi == 2) { - /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */ - error = lpfc_sli_config_port(phba, 3); - if (error) - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0478 Firmware not capable of SLI 3 mode.\n"); - else { - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0479 Firmware capable of SLI 3 mode.\n"); - /* Now, try to enable MSI-X interrupt mode */ - error = lpfc_enable_msix(phba); - if (!error) { - phba->intr_type = MSIX; - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0480 enable MSI-X mode.\n"); - } - } - } - - /* Fallback to MSI if MSI-X initialization failed */ - if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) { - retval = pci_enable_msi(phba->pcidev); - if (!retval) { - phba->intr_type = MSI; - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0481 enable MSI mode.\n"); - } else - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0470 enable IRQ mode.\n"); - } - - /* MSI-X is the only case the doesn't need to call request_irq */ - if (phba->intr_type != MSIX) { - retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, - IRQF_SHARED, LPFC_DRIVER_NAME, phba); - if (retval) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0471 Enable interrupt handler " - "failed\n"); - } else if (phba->intr_type != MSI) - phba->intr_type = INTx; - } + /* Configure and enable interrupt */ + intr_mode = lpfc_enable_intr(phba, phba->intr_mode); + if (intr_mode == LPFC_INTR_ERROR) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0427 Cannot re-enable interrupt after " + "slot reset.\n"); + return PCI_ERS_RESULT_DISCONNECT; + } else + phba->intr_mode = intr_mode; /* Take device offline; this will perform cleanup */ lpfc_offline(phba); lpfc_sli_brdrestart(phba); + /* Log the current active interrupt mode */ + lpfc_log_intr_mode(phba, phba->intr_mode); + return PCI_ERS_RESULT_RECOVERED; } |