]> err.no Git - linux-2.6/blobdiff - drivers/scsi/lpfc/lpfc_init.c
Merge branch 'kvm-updates-2.6.26' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6] / drivers / scsi / lpfc / lpfc_init.c
index c6b30a8617bc24c8ad1a69c7678354a393d42ca7..fa757b251f8287505a21f271897598ae0141d3ea 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2007 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -461,11 +461,21 @@ lpfc_config_port_post(struct lpfc_hba *phba)
 int
 lpfc_hba_down_prep(struct lpfc_hba *phba)
 {
+       struct lpfc_vport **vports;
+       int i;
        /* Disable interrupts */
        writel(0, phba->HCregaddr);
        readl(phba->HCregaddr); /* flush */
 
-       lpfc_cleanup_discovery_resources(phba->pport);
+       if (phba->pport->load_flag & FC_UNLOADING)
+               lpfc_cleanup_discovery_resources(phba->pport);
+       else {
+               vports = lpfc_create_vport_work_array(phba);
+               if (vports != NULL)
+                       for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+                               lpfc_cleanup_discovery_resources(vports[i]);
+               lpfc_destroy_vport_work_array(phba, vports);
+       }
        return 0;
 }
 
@@ -484,6 +494,9 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring *pring;
        struct lpfc_dmabuf *mp, *next_mp;
+       struct lpfc_iocbq *iocb;
+       IOCB_t *cmd = NULL;
+       LIST_HEAD(completions);
        int i;
 
        if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
@@ -499,10 +512,36 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
                }
        }
 
+       spin_lock_irq(&phba->hbalock);
        for (i = 0; i < psli->num_rings; i++) {
                pring = &psli->ring[i];
+
+               /* At this point in time the HBA is either reset or DOA. Either
+                * way, nothing should be on txcmplq as it will NEVER complete.
+                */
+               list_splice_init(&pring->txcmplq, &completions);
+               pring->txcmplq_cnt = 0;
+               spin_unlock_irq(&phba->hbalock);
+
+               while (!list_empty(&completions)) {
+                       iocb = list_get_first(&completions, struct lpfc_iocbq,
+                               list);
+                       cmd = &iocb->iocb;
+                       list_del_init(&iocb->list);
+
+                       if (!iocb->iocb_cmpl)
+                               lpfc_sli_release_iocbq(phba, iocb);
+                       else {
+                               cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       }
+               }
+
                lpfc_sli_abort_iocb_ring(phba, pring);
+               spin_lock_irq(&phba->hbalock);
        }
+       spin_unlock_irq(&phba->hbalock);
 
        return 0;
 }
@@ -520,8 +559,10 @@ lpfc_hb_timeout(unsigned long ptr)
                phba->pport->work_port_events |= WORKER_HB_TMO;
        spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
 
+       spin_lock_irqsave(&phba->hbalock, iflag);
        if (phba->work_wait)
                wake_up(phba->work_wait);
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
        return;
 }
 
@@ -641,6 +682,26 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
        }
 }
 
+static void
+lpfc_offline_eratt(struct lpfc_hba *phba)
+{
+       struct lpfc_sli   *psli = &phba->sli;
+
+       spin_lock_irq(&phba->hbalock);
+       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+       spin_unlock_irq(&phba->hbalock);
+       lpfc_offline_prep(phba);
+
+       lpfc_offline(phba);
+       lpfc_reset_barrier(phba);
+       lpfc_sli_brdreset(phba);
+       lpfc_hba_down_post(phba);
+       lpfc_sli_brdready(phba, HS_MBRDY);
+       lpfc_unblock_mgmt_io(phba);
+       phba->link_state = LPFC_HBA_ERROR;
+       return;
+}
+
 /************************************************************************/
 /*                                                                      */
 /*    lpfc_handle_eratt                                                 */
@@ -655,12 +716,10 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
        struct lpfc_vport *vport = phba->pport;
        struct lpfc_sli   *psli = &phba->sli;
        struct lpfc_sli_ring  *pring;
-       struct lpfc_vport **vports;
        uint32_t event_data;
        unsigned long temperature;
        struct temp_event temp_event_data;
        struct Scsi_Host  *shost;
-       int i;
 
        /* If the pci channel is offline, ignore possible errors,
         * since we cannot communicate with the pci card anyway. */
@@ -670,25 +729,14 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
        if (!phba->cfg_enable_hba_reset)
                return;
 
-       if (phba->work_hs & HS_FFER6 ||
-           phba->work_hs & HS_FFER5) {
+       if (phba->work_hs & HS_FFER6) {
                /* Re-establishing Link */
                lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
                                "1301 Re-establishing Link "
                                "Data: x%x x%x x%x\n",
                                phba->work_hs,
                                phba->work_status[0], phba->work_status[1]);
-               vports = lpfc_create_vport_work_array(phba);
-               if (vports != NULL)
-                       for(i = 0;
-                           i < LPFC_MAX_VPORTS && vports[i] != NULL;
-                           i++){
-                               shost = lpfc_shost_from_vport(vports[i]);
-                               spin_lock_irq(shost->host_lock);
-                               vports[i]->fc_flag |= FC_ESTABLISH_LINK;
-                               spin_unlock_irq(shost->host_lock);
-                       }
-               lpfc_destroy_vport_work_array(vports);
+
                spin_lock_irq(&phba->hbalock);
                psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
                spin_unlock_irq(&phba->hbalock);
@@ -702,7 +750,6 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
                pring = &psli->ring[psli->fcp_ring];
                lpfc_sli_abort_iocb_ring(phba, pring);
 
-
                /*
                 * There was a firmware error.  Take the hba offline and then
                 * attempt to restart it.
@@ -711,7 +758,6 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
                lpfc_offline(phba);
                lpfc_sli_brdrestart(phba);
                if (lpfc_online(phba) == 0) {   /* Initialize the HBA */
-                       mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60);
                        lpfc_unblock_mgmt_io(phba);
                        return;
                }
@@ -737,14 +783,9 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
                                          | PCI_VENDOR_ID_EMULEX);
 
                spin_lock_irq(&phba->hbalock);
-               psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
                phba->over_temp_state = HBA_OVER_TEMP;
                spin_unlock_irq(&phba->hbalock);
-               lpfc_offline_prep(phba);
-               lpfc_offline(phba);
-               lpfc_unblock_mgmt_io(phba);
-               phba->link_state = LPFC_HBA_ERROR;
-               lpfc_hba_down_post(phba);
+               lpfc_offline_eratt(phba);
 
        } else {
                /* The if clause above forces this code path when the status
@@ -763,14 +804,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
                                sizeof(event_data), (char *) &event_data,
                                SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
 
-               spin_lock_irq(&phba->hbalock);
-               psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
-               spin_unlock_irq(&phba->hbalock);
-               lpfc_offline_prep(phba);
-               lpfc_offline(phba);
-               lpfc_unblock_mgmt_io(phba);
-               phba->link_state = LPFC_HBA_ERROR;
-               lpfc_hba_down_post(phba);
+               lpfc_offline_eratt(phba);
        }
 }
 
@@ -790,21 +824,25 @@ lpfc_handle_latt(struct lpfc_hba *phba)
        LPFC_MBOXQ_t *pmb;
        volatile uint32_t control;
        struct lpfc_dmabuf *mp;
-       int rc = -ENOMEM;
+       int rc = 0;
 
        pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
-       if (!pmb)
+       if (!pmb) {
+               rc = 1;
                goto lpfc_handle_latt_err_exit;
+       }
 
        mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
-       if (!mp)
+       if (!mp) {
+               rc = 2;
                goto lpfc_handle_latt_free_pmb;
+       }
 
        mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
-       if (!mp->virt)
+       if (!mp->virt) {
+               rc = 3;
                goto lpfc_handle_latt_free_mp;
-
-       rc = -EIO;
+       }
 
        /* Cleanup any outstanding ELS commands */
        lpfc_els_flush_all_cmd(phba);
@@ -814,8 +852,10 @@ lpfc_handle_latt(struct lpfc_hba *phba)
        pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la;
        pmb->vport = vport;
        rc = lpfc_sli_issue_mbox (phba, pmb, MBX_NOWAIT);
-       if (rc == MBX_NOT_FINISHED)
+       if (rc == MBX_NOT_FINISHED) {
+               rc = 4;
                goto lpfc_handle_latt_free_mbuf;
+       }
 
        /* Clear Link Attention in HA REG */
        spin_lock_irq(&phba->hbalock);
@@ -847,10 +887,8 @@ lpfc_handle_latt_err_exit:
        lpfc_linkdown(phba);
        phba->link_state = LPFC_HBA_ERROR;
 
-       /* The other case is an error from issue_mbox */
-       if (rc == -ENOMEM)
-               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
-                               "0300 READ_LA: no buffers\n");
+       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                    "0300 LATT: Cannot issue READ_LA: Data:%d\n", rc);
 
        return;
 }
@@ -1381,9 +1419,39 @@ lpfc_cleanup(struct lpfc_vport *vport)
                lpfc_port_link_failure(vport);
 
        list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
+               if (!NLP_CHK_NODE_ACT(ndlp)) {
+                       ndlp = lpfc_enable_node(vport, ndlp,
+                                               NLP_STE_UNUSED_NODE);
+                       if (!ndlp)
+                               continue;
+                       spin_lock_irq(&phba->ndlp_lock);
+                       NLP_SET_FREE_REQ(ndlp);
+                       spin_unlock_irq(&phba->ndlp_lock);
+                       /* Trigger the release of the ndlp memory */
+                       lpfc_nlp_put(ndlp);
+                       continue;
+               }
+               spin_lock_irq(&phba->ndlp_lock);
+               if (NLP_CHK_FREE_REQ(ndlp)) {
+                       /* The ndlp should not be in memory free mode already */
+                       spin_unlock_irq(&phba->ndlp_lock);
+                       continue;
+               } else
+                       /* Indicate request for freeing ndlp memory */
+                       NLP_SET_FREE_REQ(ndlp);
+               spin_unlock_irq(&phba->ndlp_lock);
+
+               if (vport->port_type != LPFC_PHYSICAL_PORT &&
+                   ndlp->nlp_DID == Fabric_DID) {
+                       /* Just free up ndlp with Fabric_DID for vports */
+                       lpfc_nlp_put(ndlp);
+                       continue;
+               }
+
                if (ndlp->nlp_type & NLP_FABRIC)
                        lpfc_disc_state_machine(vport, ndlp, NULL,
                                        NLP_EVT_DEVICE_RECOVERY);
+
                lpfc_disc_state_machine(vport, ndlp, NULL,
                                             NLP_EVT_DEVICE_RM);
        }
@@ -1397,6 +1465,17 @@ lpfc_cleanup(struct lpfc_vport *vport)
                if (i++ > 3000) {
                        lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
                                "0233 Nodelist not empty\n");
+                       list_for_each_entry_safe(ndlp, next_ndlp,
+                                               &vport->fc_nodes, nlp_listp) {
+                               lpfc_printf_vlog(ndlp->vport, KERN_ERR,
+                                               LOG_NODE,
+                                               "0282: did:x%x ndlp:x%p "
+                                               "usgmap:x%x refcnt:%d\n",
+                                               ndlp->nlp_DID, (void *)ndlp,
+                                               ndlp->nlp_usg_map,
+                                               atomic_read(
+                                                       &ndlp->kref.refcount));
+                       }
                        break;
                }
 
@@ -1406,31 +1485,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
        return;
 }
 
-static void
-lpfc_establish_link_tmo(unsigned long ptr)
-{
-       struct lpfc_hba   *phba = (struct lpfc_hba *) ptr;
-       struct lpfc_vport **vports;
-       unsigned long iflag;
-       int i;
-
-       /* Re-establishing Link, timer expired */
-       lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
-                       "1300 Re-establishing Link, timer expired "
-                       "Data: x%x x%x\n",
-                       phba->pport->fc_flag, phba->pport->port_state);
-       vports = lpfc_create_vport_work_array(phba);
-       if (vports != NULL)
-               for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++) {
-                       struct Scsi_Host *shost;
-                       shost = lpfc_shost_from_vport(vports[i]);
-                       spin_lock_irqsave(shost->host_lock, iflag);
-                       vports[i]->fc_flag &= ~FC_ESTABLISH_LINK;
-                       spin_unlock_irqrestore(shost->host_lock, iflag);
-               }
-       lpfc_destroy_vport_work_array(vports);
-}
-
 void
 lpfc_stop_vport_timers(struct lpfc_vport *vport)
 {
@@ -1444,7 +1498,6 @@ static void
 lpfc_stop_phba_timers(struct lpfc_hba *phba)
 {
        del_timer_sync(&phba->fcp_poll_timer);
-       del_timer_sync(&phba->fc_estabtmo);
        lpfc_stop_vport_timers(phba->pport);
        del_timer_sync(&phba->sli.mbox_tmo);
        del_timer_sync(&phba->fabric_block_timer);
@@ -1493,7 +1546,7 @@ lpfc_online(struct lpfc_hba *phba)
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++) {
+               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
                        struct Scsi_Host *shost;
                        shost = lpfc_shost_from_vport(vports[i]);
                        spin_lock_irq(shost->host_lock);
@@ -1502,7 +1555,7 @@ lpfc_online(struct lpfc_hba *phba)
                                vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
                        spin_unlock_irq(shost->host_lock);
                }
-               lpfc_destroy_vport_work_array(vports);
+               lpfc_destroy_vport_work_array(phba, vports);
 
        lpfc_unblock_mgmt_io(phba);
        return 0;
@@ -1536,7 +1589,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
        /* Issue an unreg_login to all nodes on all vports */
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL) {
-               for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++) {
+               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
                        struct Scsi_Host *shost;
 
                        if (vports[i]->load_flag & FC_UNLOADING)
@@ -1545,6 +1598,8 @@ lpfc_offline_prep(struct lpfc_hba * phba)
                        list_for_each_entry_safe(ndlp, next_ndlp,
                                                 &vports[i]->fc_nodes,
                                                 nlp_listp) {
+                               if (!NLP_CHK_NODE_ACT(ndlp))
+                                       continue;
                                if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
                                        continue;
                                if (ndlp->nlp_type & NLP_FABRIC) {
@@ -1560,7 +1615,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
                        }
                }
        }
-       lpfc_destroy_vport_work_array(vports);
+       lpfc_destroy_vport_work_array(phba, vports);
 
        lpfc_sli_flush_mbox_queue(phba);
 }
@@ -1579,9 +1634,9 @@ lpfc_offline(struct lpfc_hba *phba)
        lpfc_stop_phba_timers(phba);
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++)
+               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
                        lpfc_stop_vport_timers(vports[i]);
-       lpfc_destroy_vport_work_array(vports);
+       lpfc_destroy_vport_work_array(phba, vports);
        lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
                        "0460 Bring Adapter offline\n");
        /* Bring down the SLI Layer and cleanup.  The HBA is offline
@@ -1592,14 +1647,14 @@ lpfc_offline(struct lpfc_hba *phba)
        spin_unlock_irq(&phba->hbalock);
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++) {
+               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
                        shost = lpfc_shost_from_vport(vports[i]);
                        spin_lock_irq(shost->host_lock);
                        vports[i]->work_port_events = 0;
                        vports[i]->fc_flag |= FC_OFFLINE_MODE;
                        spin_unlock_irq(shost->host_lock);
                }
-       lpfc_destroy_vport_work_array(vports);
+       lpfc_destroy_vport_work_array(phba, vports);
 }
 
 /******************************************************************************
@@ -1654,9 +1709,9 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
 
        vport = (struct lpfc_vport *) shost->hostdata;
        vport->phba = phba;
-
        vport->load_flag |= FC_LOADING;
        vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+       vport->fc_rscn_flush = 0;
 
        lpfc_get_vport_cfgparam(vport);
        shost->unique_id = instance;
@@ -1838,6 +1893,42 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
        spin_unlock_irq(shost->host_lock);
 }
 
+static int
+lpfc_enable_msix(struct lpfc_hba *phba)
+{
+       int error;
+
+       phba->msix_entries[0].entry = 0;
+       phba->msix_entries[0].vector = 0;
+
+       error = pci_enable_msix(phba->pcidev, phba->msix_entries,
+                               ARRAY_SIZE(phba->msix_entries));
+       if (error) {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0420 Enable MSI-X failed (%d), continuing "
+                               "with MSI\n", error);
+               pci_disable_msix(phba->pcidev);
+               return error;
+       }
+
+       error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0,
+                           LPFC_DRIVER_NAME, phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0421 MSI-X request_irq failed (%d), "
+                               "continuing with MSI\n", error);
+               pci_disable_msix(phba->pcidev);
+       }
+       return error;
+}
+
+static void
+lpfc_disable_msix(struct lpfc_hba *phba)
+{
+       free_irq(phba->msix_entries[0].vector, phba);
+       pci_disable_msix(phba->pcidev);
+}
+
 static int __devinit
 lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
 {
@@ -1851,10 +1942,11 @@ 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;
+       int bars = pci_select_bars(pdev, IORESOURCE_MEM);
 
-       if (pci_enable_device(pdev))
+       if (pci_enable_device_mem(pdev))
                goto out;
-       if (pci_request_regions(pdev, LPFC_DRIVER_NAME))
+       if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
                goto out_disable_device;
 
        phba = kzalloc(sizeof (struct lpfc_hba), GFP_KERNEL);
@@ -1863,6 +1955,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
 
        spin_lock_init(&phba->hbalock);
 
+       /* Initialize ndlp management spinlock */
+       spin_lock_init(&phba->ndlp_lock);
+
        phba->pcidev = pdev;
 
        /* Assign an unused board number */
@@ -1878,10 +1973,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
        phba->max_vpi = LPFC_MAX_VPI;
 
        /* Initialize timers used by driver */
-       init_timer(&phba->fc_estabtmo);
-       phba->fc_estabtmo.function = lpfc_establish_link_tmo;
-       phba->fc_estabtmo.data = (unsigned long)phba;
-
        init_timer(&phba->hb_tmofunc);
        phba->hb_tmofunc.function = lpfc_hb_timeout;
        phba->hb_tmofunc.data = (unsigned long)phba;
@@ -1960,6 +2051,8 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
 
        memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
 
+       INIT_LIST_HEAD(&phba->hbqbuf_in_list);
+
        /* Initialize the SLI Layer to run with lpfc HBAs. */
        lpfc_sli_setup(phba);
        lpfc_sli_queue_setup(phba);
@@ -2035,24 +2128,36 @@ 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;
 
-       if (phba->cfg_use_msi) {
+       if (phba->cfg_use_msi == 2) {
+               error = lpfc_enable_msix(phba);
+               if (!error)
+                       phba->intr_type = MSIX;
+       }
+
+       /* 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->using_msi = 1;
+                       phba->intr_type = MSI;
                else
                        lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
                                        "0452 Enable MSI failed, continuing "
                                        "with IRQ\n");
        }
 
-       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;
+       /* 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;
        }
 
        phba->MBslimaddr = phba->slim_memmap_p;
@@ -2097,9 +2202,14 @@ out_remove_device:
 out_free_irq:
        lpfc_stop_phba_timers(phba);
        phba->pport->work_port_events = 0;
-       free_irq(phba->pcidev->irq, phba);
+
+       if (phba->intr_type == MSIX)
+               lpfc_disable_msix(phba);
+       else
+               free_irq(phba->pcidev->irq, phba);
+
 out_disable_msi:
-       if (phba->using_msi)
+       if (phba->intr_type == MSI)
                pci_disable_msi(phba->pcidev);
        destroy_port(vport);
 out_kthread_stop:
@@ -2126,7 +2236,7 @@ out_idr_remove:
 out_free_phba:
        kfree(phba);
 out_release_regions:
-       pci_release_regions(pdev);
+       pci_release_selected_regions(pdev, bars);
 out_disable_device:
        pci_disable_device(pdev);
 out:
@@ -2142,6 +2252,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
        struct Scsi_Host  *shost = pci_get_drvdata(pdev);
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_hba   *phba = vport->phba;
+       int bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
        spin_lock_irq(&phba->hbalock);
        vport->load_flag |= FC_UNLOADING;
        spin_unlock_irq(&phba->hbalock);
@@ -2149,6 +2261,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
        kfree(vport->vname);
        lpfc_free_sysfs_attr(vport);
 
+       kthread_stop(phba->worker_thread);
+
        fc_remove_host(shost);
        scsi_remove_host(shost);
        lpfc_cleanup(vport);
@@ -2168,12 +2282,13 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
 
        lpfc_debugfs_terminate(vport);
 
-       kthread_stop(phba->worker_thread);
-
-       /* Release the irq reservation */
-       free_irq(phba->pcidev->irq, phba);
-       if (phba->using_msi)
-               pci_disable_msi(phba->pcidev);
+       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);
+       }
 
        pci_set_drvdata(pdev, NULL);
        scsi_host_put(shost);
@@ -2200,7 +2315,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
 
        kfree(phba);
 
-       pci_release_regions(pdev);
+       pci_release_selected_regions(pdev, bars);
        pci_disable_device(pdev);
 }
 
@@ -2232,10 +2347,13 @@ 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);
 
-       /* Release the irq reservation */
-       free_irq(phba->pcidev->irq, phba);
-       if (phba->using_msi)
-               pci_disable_msi(phba->pcidev);
+       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);
+       }
 
        /* Request a slot reset. */
        return PCI_ERS_RESULT_NEED_RESET;
@@ -2252,10 +2370,10 @@ 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 bars = pci_select_bars(pdev, IORESOURCE_MEM);
+       int error, retval;
 
        dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
-       if (pci_enable_device_bars(pdev, bars)) {
+       if (pci_enable_device_mem(pdev)) {
                printk(KERN_ERR "lpfc: Cannot re-enable "
                        "PCI device after reset.\n");
                return PCI_ERS_RESULT_DISCONNECT;
@@ -2263,15 +2381,40 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
 
        pci_set_master(pdev);
 
-       /* Re-establishing Link */
-       spin_lock_irq(shost->host_lock);
-       phba->pport->fc_flag |= FC_ESTABLISH_LINK;
-       spin_unlock_irq(shost->host_lock);
-
        spin_lock_irq(&phba->hbalock);
        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) {
+               error = lpfc_enable_msix(phba);
+               if (!error)
+                       phba->intr_type = MSIX;
+       }
+
+       /* 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;
+               else
+                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                                       "0470 Enable MSI failed, continuing "
+                                       "with IRQ\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;
+       }
 
        /* Take device offline; this will perform cleanup */
        lpfc_offline(phba);
@@ -2292,9 +2435,7 @@ static void lpfc_io_resume(struct pci_dev *pdev)
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
 
-       if (lpfc_online(phba) == 0) {
-               mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60);
-       }
+       lpfc_online(phba);
 }
 
 static struct pci_device_id lpfc_id_table[] = {