]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.25/patches.drivers/lpfc-8.2.8.7-update
Updated xen patches taken from suse.
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.drivers / lpfc-8.2.8.7-update
diff --git a/src/patches/suse-2.6.27.25/patches.drivers/lpfc-8.2.8.7-update b/src/patches/suse-2.6.27.25/patches.drivers/lpfc-8.2.8.7-update
new file mode 100644 (file)
index 0000000..9914a87
--- /dev/null
@@ -0,0 +1,1676 @@
+From: Jamie Wellnitz <jamie.wellnitz@emulex.com>
+Subject: Update lpfc to 8.2.8.7
+References: bnc#420767
+
+This patch updates the SLES 11 inbox lpfc driver 8.2.8.7, which fixes several
+issues we've found in testing on SLES 11 Beta.
+
+* Changed version number to 8.2.8.7
+* Fixed system lockup problem when HBA is in MSI-X mode (CR 85802, 85441)
+* Fixed slow vport deletes
+* Fixed locking issue when lpfc_ioctl_send_mgmt_cmd fails to allocate buffer
+* Changed mdelay to msleep in the ioctl path (CR 85606)
+* Fixed a discovery issue (CR 85714)
+* Extended Error Handling (EEH) support on IBM PowerPC P6 platform (CR 85671)
+* Fix allocation of HBQs should not be done in interrupt context (CR 84717)
+* Fixed loopback tests not working if auth enabled but auth service not
+  available (CR 85334)
+* Changed version number to 8.2.8.6
+* Fix memory leaks in netlink send code
+* Fixed fail-to-enable INTx when auth is enabled but auth service is not
+  running (CR 85162)
+* Fix build warning of uninitialized variable in lpfc_enable_intr
+* Fix crash when running latency test (CR 85257)
+* Fixed missing CNA attribute for sysfs (CR 85265)
+* Fixed false overrun failure for menlo commands that are less than the command
+  header size (CR 85168)
+* Fixed mbuf leak when lpfc_pci_probe_one hit an error exit in SLI-2 mode
+* Fix memory leak with dump mailbox completion
+* Fix bpl size to reflect the correct buffer's size
+* Fixed lpfc install problem for SLES 11 Beta2 (CR 84623)
+* Fix driver online notification to come after port is added to auth list
+* Added active interrupt test and automatic fallback for enabling
+  MSI/MSI-X/INTx (CR 84266)
+* Fixed NULL pointer dereference in lpfc_prep_els_iocb (CR 84470)
+* Fixed time out handling in the worker thread (CR 84540)
+* Changed version number to 8.2.8.5
+* Small cleanup to match upstream 8.2.8 changes
+* Fix bug with Interrupt Enable Block support (CR 84106)
+* Applied pci_max_read fix from 8.2.0.x driver series (CR 84414)
+* Added support for hps bit (CR 84425 84428)
+
+Signed-off-by: Jamie Wellnitz <jamie.wellnitz@emulex.com>
+Signed-off-by: Hannes Reinecke <hare@suse.de>
+
+diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
+index 7508dea..7bb7971 100644
+--- a/drivers/scsi/lpfc/lpfc.h
++++ b/drivers/scsi/lpfc/lpfc.h
+@@ -73,6 +73,9 @@ struct lpfc_sli2_slim;
+ /* Number of MSI-X vectors the driver uses */
+ #define LPFC_MSIX_VECTORS     2
++/* Active interrupt test threshold */
++#define LPFC_INTR_THRESHOLD   1
++
+ /* lpfc wait event data ready flag */
+ #define LPFC_DATA_READY               (1<<0)
+@@ -629,6 +632,7 @@ struct lpfc_hba {
+       uint32_t cfg_hba_queue_depth;
+       uint32_t cfg_enable_hba_reset;
+       uint32_t cfg_enable_hba_heartbeat;
++      uint32_t cfg_pci_max_read;
+       lpfc_vpd_t vpd;         /* vital product data */
+@@ -718,6 +722,8 @@ struct lpfc_hba {
+       struct fc_host_statistics link_stats;
+       enum intr_type_t intr_type;
++      uint32_t intr_mode;
++#define LPFC_INTR_ERROR       0xFFFFFFFF
+       struct msix_entry msix_entries[LPFC_MSIX_VECTORS];
+       struct lpfcdfc_host *dfc_host;
+diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
+index 1069491..2140408 100644
+--- a/drivers/scsi/lpfc/lpfc_attr.c
++++ b/drivers/scsi/lpfc/lpfc_attr.c
+@@ -42,6 +42,7 @@
+ #include "lpfc_crtn.h"
+ #include "lpfc_vport.h"
+ #include "lpfc_auth_access.h"
++#include "lpfc_security.h"
+ #define LPFC_DEF_DEVLOSS_TMO 30
+ #define LPFC_MIN_DEVLOSS_TMO 1
+@@ -3100,6 +3101,27 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
+  */
+ LPFC_ATTR_R(sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT, LPFC_DEFAULT_SG_SEG_CNT,
+           LPFC_MAX_SG_SEG_CNT, "Max Scatter Gather Segment Count");
++/*
++ * lpfc_pci_max_read:  Maximum DMA read byte count. This parameter can have
++ * values 512, 1024, 2048, 4096. Default value is 2048.
++ */
++static int lpfc_pci_max_read = 2048;
++module_param(lpfc_pci_max_read, int, 0);
++MODULE_PARM_DESC(lpfc_pci_max_read,
++      "Maximum DMA read byte count. Allowed values:"
++      " 512,1024,2048,4096.");
++static int
++lpfc_pci_max_read_init(struct lpfc_hba *phba, int val)
++{
++      phba->cfg_pci_max_read = 2048;
++      if ((val == 512) || (val == 1024) || (val == 2048) || (val == 4096))
++              phba->cfg_pci_max_read = val;
++      return 0;
++}
++
++lpfc_param_show(pci_max_read)
++static DEVICE_ATTR(lpfc_pci_max_read, S_IRUGO,
++                      lpfc_pci_max_read_show, NULL);
+ struct device_attribute *lpfc_hba_attrs[] = {
+       &dev_attr_info,
+@@ -3136,6 +3158,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
+       &dev_attr_lpfc_fdmi_on,
+       &dev_attr_lpfc_max_luns,
+       &dev_attr_lpfc_enable_npiv,
++      &dev_attr_lpfc_pci_max_read,
+       &dev_attr_nport_evt_cnt,
+       &dev_attr_board_mode,
+       &dev_attr_max_vpi,
+@@ -4176,12 +4199,22 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport)
+       if (error)
+               goto out_remove_ctlreg_attr;
++       error = sysfs_create_bin_file(&shost->shost_dev.kobj,
++                                      &sysfs_menlo_attr);
++      if (error)
++              goto out_remove_menlo_attr;
++
++
+       return 0;
+ out_remove_ctlreg_attr:
+       sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
+ out_remove_stat_attr:
+       sysfs_remove_bin_file(&shost->shost_dev.kobj,
+                       &sysfs_drvr_stat_data_attr);
++out_remove_menlo_attr:
++      sysfs_remove_bin_file(&shost->shost_dev.kobj,
++                      &sysfs_menlo_attr);
++
+ out:
+       return error;
+ }
+@@ -4269,6 +4302,13 @@ lpfc_get_host_port_state(struct Scsi_Host *shost)
+       if (vport->fc_flag & FC_OFFLINE_MODE)
+               fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+       else {
++              if ((vport->cfg_enable_auth) &&
++                  (lpfc_security_service_state == SECURITY_OFFLINE)) {
++                      fc_host_port_state(shost) = FC_PORTSTATE_ERROR;
++                      spin_unlock_irq(shost->host_lock);
++                      return;
++              }
++
+               switch (phba->link_state) {
+               case LPFC_LINK_UNKNOWN:
+               case LPFC_LINK_DOWN:
+@@ -4837,6 +4877,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
+       lpfc_link_speed_init(phba, lpfc_link_speed);
+       lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
+       lpfc_enable_npiv_init(phba, lpfc_enable_npiv);
++      lpfc_pci_max_read_init(phba, lpfc_pci_max_read);
+       lpfc_use_msi_init(phba, lpfc_use_msi);
+       lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
+       lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
+diff --git a/drivers/scsi/lpfc/lpfc_auth_access.c b/drivers/scsi/lpfc/lpfc_auth_access.c
+index 7481eb0..9f48c59 100644
+--- a/drivers/scsi/lpfc/lpfc_auth_access.c
++++ b/drivers/scsi/lpfc/lpfc_auth_access.c
+@@ -1,7 +1,7 @@
+ /*******************************************************************
+  * This file is part of the Emulex Linux Device Driver for         *
+  * Fibre Channel Host Bus Adapters.                                *
+- * Copyright (C) 2006-2007 Emulex.  All rights reserved.           *
++ * Copyright (C) 2006-2008 Emulex.  All rights reserved.           *
+  * EMULEX and SLI are trademarks of Emulex.                        *
+  * www.emulex.com                                                  *
+  *                                                                 *
+@@ -213,10 +213,11 @@ lpfc_fc_sc_request(struct lpfc_vport *vport,
+       fc_sc_req->tran_id = seq;
+       len = sizeof(struct fc_nl_sc_message) + auth_req_len;
+-      fc_nl_sc_msg = kzalloc(sizeof(struct fc_nl_sc_message) + auth_req_len,
+-                             GFP_KERNEL);
+-      if (!fc_nl_sc_msg)
++      fc_nl_sc_msg = kzalloc(len, GFP_KERNEL);
++      if (!fc_nl_sc_msg) {
++              kfree(fc_sc_req);
+               return -ENOMEM;
++      }
+       fc_nl_sc_msg->msgtype = msg_type;
+       fc_nl_sc_msg->data_len = auth_req_len;
+       memcpy(fc_nl_sc_msg->data, auth_req, auth_req_len);
+@@ -228,6 +229,7 @@ lpfc_fc_sc_request(struct lpfc_vport *vport,
+       scsi_nl_send_vendor_msg(fc_service_pid, shost->host_no,
+                               (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX),
+                               (char *) fc_nl_sc_msg, len);
++      kfree(fc_nl_sc_msg);
+       lpfc_fc_sc_add_timer(fc_sc_req, FC_SC_REQ_TIMEOUT,
+                            lpfc_fc_sc_req_times_out);
+       return 0;
+diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
+index 380acef..cedbfea 100644
+--- a/drivers/scsi/lpfc/lpfc_crtn.h
++++ b/drivers/scsi/lpfc/lpfc_crtn.h
+@@ -43,6 +43,7 @@ void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *);
+ void lpfc_read_lnk_stat(struct lpfc_hba *, LPFC_MBOXQ_t *);
+ int lpfc_reg_login(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *,
+                  LPFC_MBOXQ_t *, uint32_t);
++void lpfc_set_var(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+ void lpfc_unreg_login(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
+ void lpfc_unreg_did(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
+ void lpfc_reg_vpi(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
+@@ -166,6 +167,8 @@ void lpfc_offline(struct lpfc_hba *);
+ int lpfc_sli_setup(struct lpfc_hba *);
+ int lpfc_sli_queue_setup(struct lpfc_hba *);
++int  lpfc_sli_set_dma_length(struct lpfc_hba *, uint32_t);
++
+ void lpfc_handle_eratt(struct lpfc_hba *);
+ void lpfc_handle_latt(struct lpfc_hba *);
+diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
+index 7fecdbb..896c7b0 100644
+--- a/drivers/scsi/lpfc/lpfc_ct.c
++++ b/drivers/scsi/lpfc/lpfc_ct.c
+@@ -560,18 +560,25 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+               irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_ns_retry);
+       /* Don't bother processing response if vport is being torn down. */
+-      if (vport->load_flag & FC_UNLOADING)
++      if (vport->load_flag & FC_UNLOADING) {
++              if (vport->fc_flag & FC_RSCN_MODE)
++                      lpfc_els_flush_rscn(vport);
+               goto out;
++      }
+       if (lpfc_els_chk_latt(vport)) {
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+                                "0216 Link event during NS query\n");
++              if (vport->fc_flag & FC_RSCN_MODE)
++                      lpfc_els_flush_rscn(vport);
+               lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+               goto out;
+       }
+       if (lpfc_error_lost_link(irsp)) {
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+                                "0226 NS query failed due to link event\n");
++              if (vport->fc_flag & FC_RSCN_MODE)
++                      lpfc_els_flush_rscn(vport);
+               goto out;
+       }
+       if (irsp->ulpStatus) {
+@@ -587,6 +594,8 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+                       if (rc == 0)
+                               goto out;
+               }
++              if (vport->fc_flag & FC_RSCN_MODE)
++                      lpfc_els_flush_rscn(vport);
+               lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                "0257 GID_FT Query error: 0x%x 0x%x\n",
+diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
+index 3f58a4b..69f92e8 100644
+--- a/drivers/scsi/lpfc/lpfc_els.c
++++ b/drivers/scsi/lpfc/lpfc_els.c
+@@ -278,7 +278,8 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
+       return elsiocb;
+ els_iocb_free_pbuf_exit:
+-      lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
++      if (expectRsp)
++              lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+       kfree(pbuflist);
+ els_iocb_free_prsp_exit:
+@@ -943,6 +944,10 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_nodelist *ndlp;
++      if ((vport->cfg_enable_auth) &&
++          (lpfc_security_service_state == SECURITY_OFFLINE))
++              return 1;
++
+       vport->port_state = LPFC_FLOGI;
+       lpfc_set_disctmo(vport);
+@@ -4985,10 +4990,6 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
+       uint32_t timeout;
+       uint32_t remote_ID = 0xffffffff;
+-      /* If the timer is already canceled do nothing */
+-      if ((vport->work_port_events & WORKER_ELS_TMO) == 0) {
+-              return;
+-      }
+       spin_lock_irq(&phba->hbalock);
+       timeout = (uint32_t)(phba->fc_ratov << 1);
+diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
+index 358e9de..f02c2bb 100644
+--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
++++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
+@@ -39,6 +39,7 @@
+ #include "lpfc_crtn.h"
+ #include "lpfc_vport.h"
+ #include "lpfc_debugfs.h"
++#include "lpfc_security.h"
+ /* AlpaArray for assignment of scsid for scan-down and bind_method */
+ static uint8_t lpfcAlpaArray[] = {
+@@ -1008,9 +1009,12 @@ lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+       /* Start discovery by sending a FLOGI. port_state is identically
+        * LPFC_FLOGI while waiting for FLOGI cmpl
+        */
+-      if (vport->port_state != LPFC_FLOGI) {
++      if ((vport->cfg_enable_auth) &&
++          (lpfc_security_service_state == SECURITY_OFFLINE))
++              lpfc_issue_clear_la(phba, vport);
++      else if (vport->port_state != LPFC_FLOGI)
+               lpfc_initial_flogi(vport);
+-      }
++
+       return;
+ out:
+diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
+index c77d49e..f9a42f6 100644
+--- a/drivers/scsi/lpfc/lpfc_hw.h
++++ b/drivers/scsi/lpfc/lpfc_hw.h
+@@ -2370,6 +2370,14 @@ typedef struct {
+       uint32_t rsvd1;
+ } CLEAR_LA_VAR;
++/* Structure for MB Command SET_SLIM (33) */
++/* Values needed to set MAX_DMA_LENGTH parameter */
++#define SLIM_VAR_MAX_DMA_LENGTH 0x100506
++#define SLIM_VAL_MAX_DMA_512    0x0
++#define SLIM_VAL_MAX_DMA_1024   0x1
++#define SLIM_VAL_MAX_DMA_2048   0x2
++#define SLIM_VAL_MAX_DMA_4096   0x3
++
+ /* Structure for MB Command DUMP */
+ typedef struct {
+@@ -2621,10 +2629,17 @@ typedef struct {
+       uint32_t pcbLow;       /* bit 31:0  of memory based port config block */
+       uint32_t pcbHigh;      /* bit 63:32 of memory based port config block */
+-      uint32_t hbainit[6];
++      uint32_t hbainit[5];
++#ifdef __BIG_ENDIAN_BITFIELD
++      uint32_t hps       :  1; /* bit 31 word9 Host Pointer in slim */
++      uint32_t rsvd      : 31; /* least significant 31 bits of word 9 */
++#else   /*  __LITTLE_ENDIAN */
++      uint32_t rsvd      : 31; /* least significant 31 bits of word 9 */
++      uint32_t hps       :  1; /* bit 31 word9 Host Pointer in slim */
++#endif
+ #ifdef __BIG_ENDIAN_BITFIELD
+-      uint32_t rsvd      : 24;  /* Reserved                             */
++      uint32_t rsvd1     : 24;  /* Reserved                             */
+       uint32_t cmv       :  1;  /* Configure Max VPIs                   */
+       uint32_t ccrp      :  1;  /* Config Command Ring Polling          */
+       uint32_t csah      :  1;  /* Configure Synchronous Abort Handling */
+@@ -2642,7 +2657,7 @@ typedef struct {
+       uint32_t csah      :  1;  /* Configure Synchronous Abort Handling */
+       uint32_t ccrp      :  1;  /* Config Command Ring Polling          */
+       uint32_t cmv       :  1;  /* Configure Max VPIs                   */
+-      uint32_t rsvd      : 24;  /* Reserved                             */
++      uint32_t rsvd1     : 24;  /* Reserved                             */
+ #endif
+ #ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd2     : 24;  /* Reserved                             */
+diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
+index c151651..4032bda 100644
+--- a/drivers/scsi/lpfc/lpfc_init.c
++++ b/drivers/scsi/lpfc/lpfc_init.c
+@@ -290,8 +290,10 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+       /* character array used for decoding dist type. */
+       char dist_char[] = "nabx";
+-      if (pmboxq->mb.mbxStatus != MBX_SUCCESS)
++      if (pmboxq->mb.mbxStatus != MBX_SUCCESS) {
++              mempool_free(pmboxq, phba->mbox_mem_pool);
+               return;
++      }
+       prg = (struct prog_id *) &prog_id_word;
+@@ -309,6 +311,7 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+               sprintf(phba->OptionROMVersion, "%d.%d%d%c%d",
+                       prg->ver, prg->rev, prg->lev,
+                       dist, prg->num);
++      mempool_free(pmboxq, phba->mbox_mem_pool);
+       return;
+ }
+@@ -521,22 +524,46 @@ lpfc_config_port_post(struct lpfc_hba *phba)
+       /* Set up error attention (ERATT) polling timer */
+       mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
++      /* Use the existing MBOX buffer, it will be freed in mbox compl */
++      lpfc_config_async(phba, pmb, LPFC_ELS_RING);
++      pmb->mbox_cmpl = lpfc_config_async_cmpl;
++      pmb->vport = phba->pport;
++      rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
++      if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
++              lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
++                              "0456 Adapter failed to issue "
++                              "ASYNCEVT_ENABLE mbox status x%x \n.", rc);
++              mempool_free(pmb, phba->mbox_mem_pool);
++      }
++
++      /* Allocate new MBOX buffer, it will be freed in mbox compl */
++      pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
++      lpfc_dump_wakeup_param(phba, pmb);
++      pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
++      pmb->vport = phba->pport;
++      rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
++      if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
++              lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
++                              "0435 Adapter failed to get Option "
++                              "ROM version status x%x\n.", rc);
++              mempool_free(pmb, phba->mbox_mem_pool);
++      }
++
+       if (vport->cfg_enable_auth) {
+               if (lpfc_security_service_state == SECURITY_OFFLINE) {
+                       lpfc_printf_log(vport->phba, KERN_ERR, LOG_SECURITY,
+                               "1000 Authentication is enabled but "
+                               "authentication service is not running\n");
+                       vport->auth.auth_mode = FC_AUTHMODE_UNKNOWN;
+-                      phba->link_state = LPFC_HBA_ERROR;
+-                      mempool_free(pmb, phba->mbox_mem_pool);
+-                      return 0;
+               }
+       }
++      /* Allocate new MBOX buffer, will be freed in mbox compl */
++      pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
+       pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+-      if (rc != MBX_SUCCESS) {
++      if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0454 Adapter failed to init, mbxCmd x%x "
+                               "INIT_LINK, mbxStatus x%x\n",
+@@ -550,40 +577,9 @@ lpfc_config_port_post(struct lpfc_hba *phba)
+               readl(phba->HAregaddr); /* flush */
+               phba->link_state = LPFC_HBA_ERROR;
+-              if (rc != MBX_BUSY)
+-                      mempool_free(pmb, phba->mbox_mem_pool);
+-              return -EIO;
+-      }
+-      /* MBOX buffer will be freed in mbox compl */
+-      pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+-      lpfc_config_async(phba, pmb, LPFC_ELS_RING);
+-      pmb->mbox_cmpl = lpfc_config_async_cmpl;
+-      pmb->vport = phba->pport;
+-      rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+-
+-      if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+-              lpfc_printf_log(phba,
+-                              KERN_ERR,
+-                              LOG_INIT,
+-                              "0456 Adapter failed to issue "
+-                              "ASYNCEVT_ENABLE mbox status x%x \n.",
+-                              rc);
+-              mempool_free(pmb, phba->mbox_mem_pool);
+-      }
+-
+-      /* Get Option rom version */
+-      pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+-      lpfc_dump_wakeup_param(phba, pmb);
+-      pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
+-      pmb->vport = phba->pport;
+-      rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+-
+-      if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+-              lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
+-                              "to get Option ROM version status x%x\n.", rc);
+               mempool_free(pmb, phba->mbox_mem_pool);
++              return -EIO;
+       }
+-
+       return 0;
+ }
+@@ -788,11 +784,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
+               return;
+       spin_lock_irq(&phba->pport->work_port_lock);
+-      /* If the timer is already canceled do nothing */
+-      if (!(phba->pport->work_port_events & WORKER_HB_TMO)) {
+-              spin_unlock_irq(&phba->pport->work_port_lock);
+-              return;
+-      }
+       if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
+               jiffies)) {
+@@ -2307,6 +2298,51 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
+ }
+ /**
++ * lpfc_setup_max_dma_length: Check the host's chipset and adjust HBA's
++ * max DMA length.
++ * @phba: pointer to lpfc hba data structure.
++ *
++ * This routine is invoked to test the machines chipsets. Some of Emulex's
++ * HBA models expose bugs in these chipsets. To work around these bugs we
++ * tell the HBA to use a smaller maxium DMA length.
++ * This routine is only called during module init. The DMA length is passed
++ * to the driver as a module parameter(lpfc_pci_max_read).
++ *
++ * return: NONE.
++ **/
++void
++lpfc_setup_max_dma_length(struct lpfc_hba *phba)
++{
++      struct pci_dev *pdev = phba->pcidev;
++      struct pci_bus *bus = pdev->bus;
++      uint8_t rev;
++
++      while (bus) {
++              /*
++               * 0x7450 == PCI_DEVICE_ID_AMD_8131_BRIDGE for 2.6 kernels
++               * 0x7450 == PCI_DEVICE_ID_AMD_8131_APIC   for 2.4 kernels
++               */
++              if (bus->self &&
++                      (bus->self->vendor == PCI_VENDOR_ID_AMD) &&
++                      (bus->self->device == 0x7450)) {
++                      pci_read_config_byte(bus->self, 0x08, &rev);
++                      if (rev == 0x13) {
++                              /*
++                               * If set a value in module paramter,
++                               * use that value.
++                               */
++                              if (phba->cfg_pci_max_read == 2048)
++                                      phba->cfg_pci_max_read = 1024;
++                              return;
++                      }
++              }
++              bus = bus->parent;
++      }
++      return;
++}
++
++
++/**
+  * lpfc_enable_msix: Enable MSI-X interrupt mode.
+  * @phba: pointer to lpfc hba data structure.
+  *
+@@ -2340,8 +2376,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++)
+@@ -2358,9 +2393,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;
+       }
+@@ -2369,9 +2404,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;
+       }
+@@ -2392,7 +2427,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);
+@@ -2441,6 +2476,111 @@ 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.
+  *
+@@ -2454,60 +2594,47 @@ lpfc_disable_msix(struct lpfc_hba *phba)
+  *   0 - sucessful
+  *   other values - error
+  **/
+-static int
+-lpfc_enable_intr(struct lpfc_hba *phba)
++static uint32_t
++lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+ {
+-      int retval = 0;
+-
+-      /* Starting point of configuring interrupt method */
+-      phba->intr_type = NONE;
++      uint32_t intr_mode = LPFC_INTR_ERROR;
++      int retval;
+-      if (phba->cfg_use_msi == 2) {
++      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)
+-                      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");
++              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;
+-                              lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                              "0480 enable MSI-X mode.\n");
++                              intr_mode = 2;
+                       }
+               }
+       }
+       /* 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 (cfg_mode >= 1 && phba->intr_type == NONE) {
++              retval = lpfc_enable_msi(phba);
+               if (!retval) {
++                      /* Indicate initialization to MSI mode */
+                       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");
++                      intr_mode = 1;
++              }
+       }
+-      /* MSI-X is the only case the doesn't need to call request_irq */
+-      if (phba->intr_type != MSIX) {
++      /* 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) {
+-                      if (phba->intr_type == MSI)
+-                              pci_disable_msi(phba->pcidev);
+-                      lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+-                                      "0471 Enable interrupt handler "
+-                                      "failed\n");
+-              } else if (phba->intr_type != MSI)
++              if (!retval) {
++                      /* Indicate initialization to INTx mode */
+                       phba->intr_type = INTx;
++                      intr_mode = 0;
++              }
+       }
+-
+-      return retval;
++      return intr_mode;
+ }
+ /**
+@@ -2522,13 +2649,18 @@ lpfc_enable_intr(struct lpfc_hba *phba)
+ 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 {
++      else if (phba->intr_type == MSI)
++              lpfc_disable_msi(phba);
++      else if (phba->intr_type == INTx)
+               free_irq(phba->pcidev->irq, phba);
+-              if (phba->intr_type == MSI)
+-                      pci_disable_msi(phba->pcidev);
+-      }
++
++      /* Reset interrupt management states */
++      phba->intr_type = NONE;
++      phba->sli.slistat.sli_intr = 0;
++
+       return;
+ }
+@@ -2562,6 +2694,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;
+@@ -2593,6 +2726,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+        * establish the host.
+        */
+       lpfc_get_cfgparam(phba);
++      /* Check if we need to change the DMA length */
++      lpfc_setup_max_dma_length(phba);
++
+       phba->max_vpi = lpfc_hba_max_vpi(phba->pcidev->device);
+       /* Initialize timers used by driver */
+@@ -2615,6 +2751,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+       phba->eratt_poll.data = (unsigned long) phba;
+       pci_set_master(pdev);
++      pci_save_state(pdev);
+       pci_try_set_mwi(pdev);
+       if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
+@@ -2769,9 +2906,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+       if ((lpfc_get_security_enabled)(shost)) {
+               unsigned long flags;
+-              /* Triggers fcauthd to register if it is running */
+-              fc_host_post_event(shost, fc_get_event_number(),
+-                                 FCH_EVT_PORT_ONLINE, shost->host_no);
+               spin_lock_irqsave(&fc_security_user_lock, flags);
+               list_add_tail(&vport->sc_users, &fc_security_user_list);
+               spin_unlock_irqrestore(&fc_security_user_lock, flags);
+@@ -2779,6 +2913,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+                       lpfc_fc_queue_security_work(vport,
+                               &vport->sc_online_work);
+               }
++              /* Triggers fcauthd to register if it is running */
++              fc_host_post_event(shost, fc_get_event_number(),
++                                 FCH_EVT_PORT_ONLINE, shost->host_no);
+       }
+       phba->pport = vport;
+       lpfc_debugfs_initialize(vport);
+@@ -2791,34 +2928,66 @@ 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 */
+-      error = lpfc_enable_intr(phba);
+-      if (error) {
+-              lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+-                              "0426 Failed to enable interrupt.\n");
+-              goto out_destroy_port;
+-      }
+-
++      /* Confiugre sysfs attributes */
+       phba->dfc_host = lpfcdfc_host_add(pdev, shost, phba);
+       if (!phba->dfc_host) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+                               "1201 Failed to allocate dfc_host \n");
+               error = -ENOMEM;
+-              goto out_free_irq;
++              goto out_destroy_port;
+       }
+       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_del_dfc_host;
+       }
+-      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_INTR_THRESHOLD) {
++                      /* 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;
++              }
+       }
+       /*
+@@ -2850,16 +3019,19 @@ 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:
+-      if (phba->dfc_host)
+-              lpfcdfc_host_del(phba->dfc_host);
+       lpfc_stop_phba_timers(phba);
+       phba->pport->work_port_events = 0;
+       lpfc_disable_intr(phba);
++      lpfc_sli_hba_down(phba);
++      lpfc_sli_brdrestart(phba);
++out_free_sysfs_attr:
++      lpfc_free_sysfs_attr(vport);
++out_del_dfc_host:
++      if (phba->dfc_host)
++              lpfcdfc_host_del(phba->dfc_host);
+ out_destroy_port:
+       destroy_port(vport);
+ out_kthread_stop:
+@@ -3051,6 +3223,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,
+@@ -3073,19 +3246,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;
+ }
+@@ -3161,7 +3337,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;
++      uint32_t intr_mode;
+       dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
+       if (pci_enable_device_mem(pdev)) {
+@@ -3170,25 +3346,31 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+-      pci_set_master(pdev);
++      pci_restore_state(pdev);
++      if (pdev->is_busmaster)
++              pci_set_master(pdev);
+       spin_lock_irq(&phba->hbalock);
+       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+       spin_unlock_irq(&phba->hbalock);
+-      /* Enable configured interrupt method */
+-      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,
+                               "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;
+ }
+diff --git a/drivers/scsi/lpfc/lpfc_ioctl.c b/drivers/scsi/lpfc/lpfc_ioctl.c
+index e80d157..127b47b 100644
+--- a/drivers/scsi/lpfc/lpfc_ioctl.c
++++ b/drivers/scsi/lpfc/lpfc_ioctl.c
+@@ -162,7 +162,7 @@ lpfc_ioctl_hba_rnid(struct lpfc_hba * phba,
+       for (i0 = 0;
+            i0 < 10 && (pndl->nlp_flag & NLP_ELS_SND_MASK) == NLP_RNID_SND;
+            i0++) {
+-              mdelay(1000);
++              msleep(1000);
+       }
+       if (i0 == 10) {
+@@ -731,7 +731,6 @@ lpfc_ioctl_send_mgmt_cmd(struct lpfc_hba * phba,
+       outdmp = dfc_cmd_data_alloc(phba, NULL, bpl, snsbfrcnt);
+       if (!outdmp) {
+               rc = ENOMEM;
+-              spin_lock_irq(shost->host_lock);
+               goto send_mgmt_cmd_free_indmp;
+       }
+@@ -1104,7 +1103,7 @@ lpfc_ioctl_loopback_mode(struct lpfc_hba *phba,
+               if (i++ > 500)  /* wait up to 5 seconds */
+                       break;
+-              mdelay(10);
++              msleep(10);
+       }
+       memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
+index f0ab456..84619ed 100644
+--- a/drivers/scsi/lpfc/lpfc_mbox.c
++++ b/drivers/scsi/lpfc/lpfc_mbox.c
+@@ -835,6 +835,40 @@ lpfc_config_pcb_setup(struct lpfc_hba * phba)
+ }
+ /**
++ * lpfc_set_var: Prepare a mailbox command to write slim.
++ * @phba: pointer to lpfc hba data structure.
++ * @pmb: pointer to the driver internal queue element for mailbox command.
++ * @addr: This the set variable number that identifies the variable.
++ * @value:The value that we are setting the parameter to.
++ *
++ * The routine just sets the addr and value in the set variable mailbox
++ * command structure.
++ * returns: NONE.
++ **/
++void
++lpfc_set_var(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, uint32_t addr,
++            uint32_t value)
++{
++      MAILBOX_t *mb;
++
++      mb = &pmb->mb;
++      memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
++
++      /*
++       * Always turn on DELAYED ABTS for ELS timeouts
++       */
++      if ((addr == 0x052198) && (value == 0))
++              value = 1;
++
++      mb->un.varWords[0] = addr;
++      mb->un.varWords[1] = value;
++
++      mb->mbxCommand = MBX_SET_VARIABLE;
++      mb->mbxOwner = OWN_HOST;
++      return;
++}
++
++/**
+  * lpfc_read_rev: Prepare a mailbox command for reading HBA revision.
+  * @phba: pointer to lpfc hba data structure.
+  * @pmb: pointer to the driver internal queue element for mailbox command.
+@@ -1093,6 +1127,9 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+       mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr);
+       mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr);
++      /* Always Host Group Pointer is in SLIM */
++      mb->un.varCfgPort.hps = 1;
++
+       /* If HBA supports SLI=3 ask for it */
+       if (phba->sli_rev == 3 && phba->vpd.sli3Feat.cerbm) {
+@@ -1195,16 +1232,11 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+                                   sizeof(*phba->host_gp));
+       }
+-      /* Setup Port Group ring pointer */
+-      if (phba->sli3_options & LPFC_SLI3_INB_ENABLED) {
+-              pgp_offset = offsetof(struct lpfc_sli2_slim,
+-                                    mbx.us.s3_inb_pgp.port);
+-              phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
+-      } else if (phba->sli_rev == 3) {
++      /* Setup Port Group offset */
++      if (phba->sli_rev == 3)
+               pgp_offset = offsetof(struct lpfc_sli2_slim,
+                                     mbx.us.s3_pgp.port);
+-              phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
+-      } else
++      else
+               pgp_offset = offsetof(struct lpfc_sli2_slim, mbx.us.s2.port);
+       pdma_addr = phba->slim2p.phys + pgp_offset;
+       phba->pcb->pgpAddrHigh = putPaddrHigh(pdma_addr);
+diff --git a/drivers/scsi/lpfc/lpfc_menlo.c b/drivers/scsi/lpfc/lpfc_menlo.c
+index aa36c16..4f3b332 100644
+--- a/drivers/scsi/lpfc/lpfc_menlo.c
++++ b/drivers/scsi/lpfc/lpfc_menlo.c
+@@ -289,19 +289,16 @@ static void
+ sysfs_menlo_idle(struct lpfc_hba *phba,
+               struct lpfc_sysfs_menlo *sysfs_menlo)
+ {
+-      struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport);
+       spin_lock_irq(&phba->hbalock);
+       list_del_init(&sysfs_menlo->list);
+       spin_unlock_irq(&phba->hbalock);
+-      spin_lock_irq(shost->host_lock);
+       if (sysfs_menlo->cr.cmdiocbq)
+               sysfs_menlo_genreq_free(phba, &sysfs_menlo->cr);
+       if (sysfs_menlo->cx.cmdiocbq)
+               sysfs_menlo_genreq_free(phba, &sysfs_menlo->cx);
+-      spin_unlock_irq(shost->host_lock);
+       kfree(sysfs_menlo);
+ }
+@@ -543,14 +540,15 @@ lpfc_menlo_write(struct lpfc_hba *phba,
+       }
+       if ((count + sysfs_menlo->cr.offset) > sysfs_menlo->cmdhdr.cmdsize) {
+-              if ( sysfs_menlo->cmdhdr.cmdsize != 4) {
+-              lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
++              if (sysfs_menlo->cmdhdr.cmdsize >=
++                      sizeof(struct lpfc_sysfs_menlo_hdr)) {
++                      lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+                       "1213 FCoE cmd overflow: off %d + cnt %d > cmdsz %d\n",
+-                      (int)sysfs_menlo->cr.offset,
+-                      (int)count,
+-                      (int)sysfs_menlo->cmdhdr.cmdsize);
+-              sysfs_menlo_idle(phba, sysfs_menlo);
+-              return -ERANGE;
++                              (int)sysfs_menlo->cr.offset,
++                              (int)count,
++                              (int)sysfs_menlo->cmdhdr.cmdsize);
++                      sysfs_menlo_idle(phba, sysfs_menlo);
++                      return -ERANGE;
+               }
+       }
+diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
+index 0c25d97..8f548ad 100644
+--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
++++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
+@@ -1929,10 +1929,10 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+       if (vport->fc_flag & FC_RSCN_DEFERRED)
+               return ndlp->nlp_state;
++      lpfc_cancel_retry_delay_tmo(vport, ndlp);
+       spin_lock_irq(shost->host_lock);
+       ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
+       spin_unlock_irq(shost->host_lock);
+-      lpfc_cancel_retry_delay_tmo(vport, ndlp);
+       return ndlp->nlp_state;
+ }
+diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
+index daf400e..38a7faf 100644
+--- a/drivers/scsi/lpfc/lpfc_scsi.c
++++ b/drivers/scsi/lpfc/lpfc_scsi.c
+@@ -65,6 +65,8 @@ lpfc_update_stats(struct lpfc_hba *phba, struct  lpfc_scsi_buf *lpfc_cmd)
+       if (cmd->result)
+               return;
++      latency = jiffies_to_msecs((long)jiffies - (long)lpfc_cmd->start_time);
++
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (!vport->stat_data_enabled ||
+               vport->stat_data_blocked ||
+@@ -73,13 +75,15 @@ lpfc_update_stats(struct lpfc_hba *phba, struct  lpfc_scsi_buf *lpfc_cmd)
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               return;
+       }
+-      latency = jiffies_to_msecs(jiffies - lpfc_cmd->start_time);
+       if (phba->bucket_type == LPFC_LINEAR_BUCKET) {
+               i = (latency + phba->bucket_step - 1 - phba->bucket_base)/
+                       phba->bucket_step;
+-              if (i >= LPFC_MAX_BUCKET_COUNT)
+-                      i = LPFC_MAX_BUCKET_COUNT;
++              /* check array subscript bounds */
++              if (i < 0)
++                      i = 0;
++              else if (i >= LPFC_MAX_BUCKET_COUNT)
++                      i = LPFC_MAX_BUCKET_COUNT - 1;
+       } else {
+               for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++)
+                       if (latency <= (phba->bucket_base +
+@@ -413,14 +417,14 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
+       bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
+       bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
+       bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+-      bpl[0].tus.w = le32_to_cpu(bpl->tus.w);
++      bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
+       /* Setup the physical region for the FCP RSP */
+       bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
+       bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
+       bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
+       bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+-      bpl[1].tus.w = le32_to_cpu(bpl->tus.w);
++      bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
+       /*
+        * Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
+@@ -920,7 +924,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+               if (!pnode || !NLP_CHK_NODE_ACT(pnode)
+                   || (pnode->nlp_state != NLP_STE_MAPPED_NODE))
+                       cmd->result = ScsiResult(DID_TRANSPORT_DISRUPTED,
+-                      SAM_STAT_BUSY);
++                                               SAM_STAT_BUSY);
+       } else {
+               cmd->result = ScsiResult(DID_OK, 0);
+       }
+diff --git a/drivers/scsi/lpfc/lpfc_security.c b/drivers/scsi/lpfc/lpfc_security.c
+index c0fbe56..3d2a8c8 100644
+--- a/drivers/scsi/lpfc/lpfc_security.c
++++ b/drivers/scsi/lpfc/lpfc_security.c
+@@ -45,8 +45,7 @@ lpfc_security_service_online(struct Scsi_Host *shost)
+       lpfc_security_service_state = SECURITY_ONLINE;
+       if (vport->cfg_enable_auth &&
+-          vport->auth.auth_mode == FC_AUTHMODE_UNKNOWN &&
+-          vport->phba->link_state == LPFC_HBA_ERROR)
++          vport->auth.auth_mode == FC_AUTHMODE_UNKNOWN)
+               lpfc_selective_reset(vport->phba);
+ }
+diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
+index ac78493..5838e41 100644
+--- a/drivers/scsi/lpfc/lpfc_sli.c
++++ b/drivers/scsi/lpfc/lpfc_sli.c
+@@ -1262,68 +1262,6 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
+ }
+ /**
+- * lpfc_sli_replace_hbqbuff: Replace the HBQ buffer with a new buffer.
+- * @phba: Pointer to HBA context object.
+- * @tag: Tag for the HBQ buffer.
+- *
+- * This function is called from unsolicited event handler code path to get the
+- * HBQ buffer associated with an unsolicited iocb. This function is called with
+- * no lock held. It returns the buffer associated with the given tag and posts
+- * another buffer to the firmware. Note that the new buffer must be allocated
+- * before taking the hbalock and that the hba lock must be held until it is
+- * finished with the hbq entry swap.
+- **/
+-static struct lpfc_dmabuf *
+-lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
+-{
+-      struct hbq_dmabuf *hbq_entry, *new_hbq_entry;
+-      uint32_t hbqno;
+-      void *virt;             /* virtual address ptr */
+-      dma_addr_t phys;        /* mapped address */
+-      unsigned long flags;
+-
+-      hbqno = tag >> 16;
+-      new_hbq_entry = (phba->hbqs[hbqno].hbq_alloc_buffer)(phba);
+-      /* Check whether HBQ is still in use */
+-      spin_lock_irqsave(&phba->hbalock, flags);
+-      if (!phba->hbq_in_use) {
+-              if (new_hbq_entry)
+-                      (phba->hbqs[hbqno].hbq_free_buffer)(phba,
+-                                                          new_hbq_entry);
+-              spin_unlock_irqrestore(&phba->hbalock, flags);
+-              return NULL;
+-      }
+-
+-      hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
+-      if (hbq_entry == NULL) {
+-              if (new_hbq_entry)
+-                      (phba->hbqs[hbqno].hbq_free_buffer)(phba,
+-                                                          new_hbq_entry);
+-              spin_unlock_irqrestore(&phba->hbalock, flags);
+-              return NULL;
+-      }
+-      list_del(&hbq_entry->dbuf.list);
+-
+-      if (new_hbq_entry == NULL) {
+-              list_add_tail(&hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
+-              spin_unlock_irqrestore(&phba->hbalock, flags);
+-              return &hbq_entry->dbuf;
+-      }
+-      new_hbq_entry->tag = -1;
+-      phys = new_hbq_entry->dbuf.phys;
+-      virt = new_hbq_entry->dbuf.virt;
+-      new_hbq_entry->dbuf.phys = hbq_entry->dbuf.phys;
+-      new_hbq_entry->dbuf.virt = hbq_entry->dbuf.virt;
+-      hbq_entry->dbuf.phys = phys;
+-      hbq_entry->dbuf.virt = virt;
+-      lpfc_sli_free_hbq(phba, hbq_entry);
+-      list_add_tail(&new_hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
+-      spin_unlock_irqrestore(&phba->hbalock, flags);
+-
+-      return &new_hbq_entry->dbuf;
+-}
+-
+-/**
+  * lpfc_sli_get_buff: Get the buffer associated with the buffer tag.
+  * @phba: Pointer to HBA context object.
+  * @pring: Pointer to driver SLI ring object.
+@@ -1337,13 +1275,17 @@ lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
+  **/
+ static struct lpfc_dmabuf *
+ lpfc_sli_get_buff(struct lpfc_hba *phba,
+-                      struct lpfc_sli_ring *pring,
+-                      uint32_t tag)
++                struct lpfc_sli_ring *pring,
++                uint32_t tag)
+ {
++      struct hbq_dmabuf *hbq_entry;
++
+       if (tag & QUE_BUFTAG_BIT)
+               return lpfc_sli_ring_taggedbuf_get(phba, pring, tag);
+-      else
+-              return lpfc_sli_replace_hbqbuff(phba, tag);
++      hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
++      if (!hbq_entry)
++              return NULL;
++      return &hbq_entry->dbuf;
+ }
+@@ -1375,8 +1317,6 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+       match = 0;
+       irsp = &(saveq->iocb);
+-      if (irsp->ulpStatus == IOSTAT_NEED_BUFFER)
+-              return 1;
+       if (irsp->ulpCommand == CMD_ASYNC_STATUS) {
+               if (pring->lpfc_sli_rcv_async_status)
+                       pring->lpfc_sli_rcv_async_status(phba, pring, saveq);
+@@ -2776,6 +2716,86 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
+ }
+ /**
++ * lpfc_sli_set_dma_length: Set the HBA's max DMA length.
++ * @phba: Pointer to HBA context object.
++ * @polling: flag that indicates if interrupts are enabled.
++ *
++ * This function sets the HBA's max dma length by issuing a set variable
++ * mailbox command. The dma length is taking from the cfg_pci_max_read
++ * configuration parameter. This parameter is passed as a module parameter
++ * during the driver load. If the HBA does not support this set variable
++ * mbox command the failure status will reset the cfg_pci_max_read to the
++ * default(2048).
++ * If interrupts are not enabled yet then the polling flag = 1  should be
++ * be used so that the right mailbox routine is called.
++ * This function returns 0 for success, non 0 returned for failure.
++ **/
++int
++lpfc_sli_set_dma_length(struct lpfc_hba *phba, uint32_t polling)
++{
++      uint32_t dma_length;
++      LPFC_MBOXQ_t *mbox;
++      int ret = 0;
++
++      switch (phba->cfg_pci_max_read) {
++      case 512:
++              dma_length = SLIM_VAL_MAX_DMA_512;
++              break;
++      case 1024:
++              dma_length = SLIM_VAL_MAX_DMA_1024;
++              break;
++      case 2048:
++              dma_length = SLIM_VAL_MAX_DMA_2048;
++              break;
++      case 4096:
++              dma_length = SLIM_VAL_MAX_DMA_4096;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
++      if (!mbox)
++              goto failed;
++
++      lpfc_set_var(phba, mbox, SLIM_VAR_MAX_DMA_LENGTH, dma_length);
++
++      if (polling)
++              ret = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
++      else
++              ret = lpfc_sli_issue_mbox_wait(phba, mbox,
++                      LPFC_MBOX_TMO * 2);
++
++      if (ret != MBX_SUCCESS) {
++              if (mbox->mb.mbxStatus != MBXERR_UNKNOWN_CMD)
++                      lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
++                              "%d:0443 Adapter failed to set maximum"
++                              " DMA length mbxStatus x%x \n",
++                              phba->brd_no, mbox->mb.mbxStatus);
++              else
++                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
++                              "%d:0447 Adapter failed to set maximum"
++                              " DMA length mbxStatus x%x \n",
++                              phba->brd_no, mbox->mb.mbxStatus);
++              goto failed;
++      }
++
++      mempool_free(mbox, phba->mbox_mem_pool);
++      return 0;
++
++failed:
++      /* If mailbox command failed, reset the value to default value */
++      phba->cfg_pci_max_read = 2048;
++      if (ret == MBX_TIMEOUT) {
++              mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
++              return -EPERM;
++      } else if (mbox) {
++              mempool_free(mbox, phba->mbox_mem_pool);
++              return -EPERM;
++      } else
++              return -ENOMEM;
++}
++/**
+  * lpfc_sli_brdrestart: Restart the HBA.
+  * @phba: Pointer to HBA context object.
+  *
+@@ -3140,17 +3160,20 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
+                       phba->sli3_options |= LPFC_SLI3_CRP_ENABLED;
+               if (pmb->mb.un.varCfgPort.ginb) {
+                       phba->sli3_options |= LPFC_SLI3_INB_ENABLED;
++                      phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
+                       phba->port_gp = phba->mbox->us.s3_inb_pgp.port;
+                       phba->inb_ha_copy = &phba->mbox->us.s3_inb_pgp.ha_copy;
+                       phba->inb_counter = &phba->mbox->us.s3_inb_pgp.counter;
+                       phba->inb_last_counter =
+                                       phba->mbox->us.s3_inb_pgp.counter;
+               } else {
++                      phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
+                       phba->port_gp = phba->mbox->us.s3_pgp.port;
+                       phba->inb_ha_copy = NULL;
+                       phba->inb_counter = NULL;
+               }
+       } else {
++              phba->hbq_get = NULL;
+               phba->port_gp = phba->mbox->us.s2.port;
+               phba->inb_ha_copy = NULL;
+               phba->inb_counter = NULL;
+@@ -3226,6 +3249,9 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0444 Firmware in SLI %x mode. Max_vpi %d\n",
+                       phba->sli_rev, phba->max_vpi);
++
++      lpfc_sli_set_dma_length(phba, 1);
++
+       rc = lpfc_sli_ring_map(phba);
+       if (rc)
+@@ -3301,10 +3327,6 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring *pring;
+-      if (!(phba->pport->work_port_events & WORKER_MBOX_TMO)) {
+-              return;
+-      }
+-
+       /* Mbox cmd <mbxCommand> timeout */
+       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                       "0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
+@@ -5275,6 +5297,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+       uint32_t ha_copy;
+       uint32_t work_ha_copy;
+       unsigned long status;
++      unsigned long iflag;
+       uint32_t control;
+       MAILBOX_t *mbox, *pmbox;
+@@ -5307,7 +5330,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+               if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+                       return IRQ_NONE;
+               /* Need to read HA REG for slow-path events */
+-              spin_lock(&phba->hbalock);
++              spin_lock_irqsave(&phba->hbalock, iflag);
+               ha_copy = readl(phba->HAregaddr);
+               /* If somebody is waiting to handle an eratt don't process it
+                * here. The brdkill function will do this.
+@@ -5327,7 +5350,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+               writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
+                       phba->HAregaddr);
+               readl(phba->HAregaddr); /* flush */
+-              spin_unlock(&phba->hbalock);
++              spin_unlock_irqrestore(&phba->hbalock, iflag);
+       } else
+               ha_copy = phba->ha_copy;
+@@ -5340,13 +5363,13 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+                                * Turn off Link Attention interrupts
+                                * until CLEAR_LA done
+                                */
+-                              spin_lock(&phba->hbalock);
++                              spin_lock_irqsave(&phba->hbalock, iflag);
+                               phba->sli.sli_flag &= ~LPFC_PROCESS_LA;
+                               control = readl(phba->HCregaddr);
+                               control &= ~HC_LAINT_ENA;
+                               writel(control, phba->HCregaddr);
+                               readl(phba->HCregaddr); /* flush */
+-                              spin_unlock(&phba->hbalock);
++                              spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       }
+                       else
+                               work_ha_copy &= ~HA_LATT;
+@@ -5361,7 +5384,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+                               (HA_RXMASK  << (4*LPFC_ELS_RING)));
+                       status >>= (4*LPFC_ELS_RING);
+                       if (status & HA_RXMASK) {
+-                              spin_lock(&phba->hbalock);
++                              spin_lock_irqsave(&phba->hbalock, iflag);
+                               control = readl(phba->HCregaddr);
+                               lpfc_debugfs_slow_ring_trc(phba,
+@@ -5390,10 +5413,10 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+                                               (uint32_t)((unsigned long)
+                                               &phba->work_waitq));
+                               }
+-                              spin_unlock(&phba->hbalock);
++                              spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       }
+               }
+-              spin_lock(&phba->hbalock);
++              spin_lock_irqsave(&phba->hbalock, iflag);
+               if (work_ha_copy & HA_ERATT)
+                       lpfc_sli_read_hs(phba);
+               if ((work_ha_copy & HA_MBATT) && (phba->sli.mbox_active)) {
+@@ -5405,7 +5428,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+                       /* First check out the status word */
+                       lpfc_sli_pcimem_bcopy(mbox, pmbox, sizeof(uint32_t));
+                       if (pmbox->mbxOwner != OWN_HOST) {
+-                              spin_unlock(&phba->hbalock);
++                              spin_unlock_irqrestore(&phba->hbalock, iflag);
+                               /*
+                                * Stray Mailbox Interrupt, mbxCommand <cmd>
+                                * mbxStatus <status>
+@@ -5422,7 +5445,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+                               work_ha_copy &= ~HA_MBATT;
+                       } else {
+                               phba->sli.mbox_active = NULL;
+-                              spin_unlock(&phba->hbalock);
++                              spin_unlock_irqrestore(&phba->hbalock, iflag);
+                               phba->last_completion_time = jiffies;
+                               del_timer(&phba->sli.mbox_tmo);
+                               if (pmb->mbox_cmpl) {
+@@ -5480,14 +5503,18 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
+                                               goto send_current_mbox;
+                                       }
+                               }
+-                              spin_lock(&phba->pport->work_port_lock);
++                              spin_lock_irqsave(
++                                              &phba->pport->work_port_lock,
++                                              iflag);
+                               phba->pport->work_port_events &=
+                                       ~WORKER_MBOX_TMO;
+-                              spin_unlock(&phba->pport->work_port_lock);
++                              spin_unlock_irqrestore(
++                                              &phba->pport->work_port_lock,
++                                              iflag);
+                               lpfc_mbox_cmpl_put(phba, pmb);
+                       }
+               } else
+-                      spin_unlock(&phba->hbalock);
++                      spin_unlock_irqrestore(&phba->hbalock, iflag);
+               if ((work_ha_copy & HA_MBATT) &&
+                   (phba->sli.mbox_active == NULL)) {
+@@ -5503,9 +5530,9 @@ send_current_mbox:
+                                               "MBX_SUCCESS");
+               }
+-              spin_lock(&phba->hbalock);
++              spin_lock_irqsave(&phba->hbalock, iflag);
+               phba->work_ha |= work_ha_copy;
+-              spin_unlock(&phba->hbalock);
++              spin_unlock_irqrestore(&phba->hbalock, iflag);
+               lpfc_worker_wake_up(phba);
+       }
+       return IRQ_HANDLED;
+@@ -5537,6 +5564,7 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
+       struct lpfc_hba  *phba;
+       uint32_t ha_copy;
+       unsigned long status;
++      unsigned long iflag;
+       /* Get the driver's phba structure from the dev_id and
+        * assume the HBA is not interrupting.
+@@ -5562,11 +5590,11 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
+               /* Need to read HA REG for FCP ring and other ring events */
+               ha_copy = readl(phba->HAregaddr);
+               /* Clear up only attention source related to fast-path */
+-              spin_lock(&phba->hbalock);
++              spin_lock_irqsave(&phba->hbalock, iflag);
+               writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
+                       phba->HAregaddr);
+               readl(phba->HAregaddr); /* flush */
+-              spin_unlock(&phba->hbalock);
++              spin_unlock_irqrestore(&phba->hbalock, iflag);
+       } else
+               ha_copy = phba->ha_copy;
+diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
+index 57b559a..16626a5 100644
+--- a/drivers/scsi/lpfc/lpfc_version.h
++++ b/drivers/scsi/lpfc/lpfc_version.h
+@@ -18,7 +18,7 @@
+  * included with this package.                                     *
+  *******************************************************************/
+-#define LPFC_DRIVER_VERSION "8.2.8.4"
++#define LPFC_DRIVER_VERSION "8.2.8.7"
+ #define LPFC_DRIVER_NAME              "lpfc"
+ #define LPFC_SP_DRIVER_HANDLER_NAME   "lpfc:sp"