]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.drivers/lpfc-8.2.8.3-update
Reenabled linux-xen, added patches for Xen Kernel Version 2.6.27.31,
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.drivers / lpfc-8.2.8.3-update
diff --git a/src/patches/suse-2.6.27.31/patches.drivers/lpfc-8.2.8.3-update b/src/patches/suse-2.6.27.31/patches.drivers/lpfc-8.2.8.3-update
new file mode 100644 (file)
index 0000000..e4b57fd
--- /dev/null
@@ -0,0 +1,1355 @@
+From: Jamie Wellnitz <jamie.wellnitz@emulex.com>
+Subject: Update lpfc to 8.2.8.3
+References: bnc#420767
+    
+This patch updates the SLES 11 inbox lpfc driver to 8.2.8.3 which has several
+changes, mainly bugfixes:
+    
+  * Changed version number to 8.2.8.3
+  * Resolved uninitialized node access (CR 83287)
+  * Fixed failing ioctl commands (CR 83850)
+  * Cosmetic coding style clean up
+  * Fix echotest failure when NPIV is enabled (CR 75009)
+  * Fixed Port busy events
+  * Back out slow vports fix (CR 83103)
+  * Added a vendor unique RSCN event to send entire payload to mgmt application
+  * Fixed internal loopback Hornet hardware (CR 83323)
+  * Fixed sysfs write handler for mailbox interface (CR 83674)
+  * Implement driver support for Power Management Suspend/Resume operations (CR
+  74378)
+  * Changed version number to 8.2.8.2
+  * Added data structures required for new events.
+  * Streamlined interrupt enable/disable logic into helper routines
+  * Fixed incorrect decrement of cmd_pending count. (CR 83286)
+  * Fixed internal and external loopback on Hornet. (CR 83323)
+  * Removed unnecessary sleeps during HBA initialization. (CR 82846)
+  * Fixed RSCN address format not handled properly. (CR 82252)
+  * Fixed unload driver with vports locks up driver (CR 83334)
+  * Avoid polling HBA Error Attention when HBA's PCI channel is offline
+    
+Signed-off-by: Jamie Wellnitz <jamie.wellnitz@emulex.com>
+Signed-off-by: Hannes Reinecke <hare@suse.de>
+
+diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
+index 273aa4f..8e94902 100644
+--- a/drivers/scsi/lpfc/lpfc_attr.c
++++ b/drivers/scsi/lpfc/lpfc_attr.c
+@@ -3531,9 +3531,6 @@ sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr,
+       uint8_t *ext;
+       uint32_t size;
+-      if ((count + off) > MAILBOX_CMD_SIZE)
+-              return -ERANGE;
+-
+       if (off % 4 ||  count % 4 || (unsigned long)buf % 4)
+               return -EINVAL;
+diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
+index 0c90479..a93c555 100644
+--- a/drivers/scsi/lpfc/lpfc_crtn.h
++++ b/drivers/scsi/lpfc/lpfc_crtn.h
+@@ -323,11 +323,10 @@ void lpfc_fabric_abort_nport(struct lpfc_nodelist *);
+ void lpfc_fabric_abort_hba(struct lpfc_hba *);
+ void lpfc_fabric_block_timeout(unsigned long);
+ void lpfc_unblock_fabric_iocbs(struct lpfc_hba *);
+-void lpfc_adjust_queue_depth(struct lpfc_hba *);
++void lpfc_rampdown_queue_depth(struct lpfc_hba *);
+ void lpfc_ramp_down_queue_handler(struct lpfc_hba *);
+ void lpfc_ramp_up_queue_handler(struct lpfc_hba *);
+ void lpfc_scsi_dev_block(struct lpfc_hba *);
+-void lpfc_scsi_dev_rescan(struct lpfc_hba *);
+ void
+ lpfc_send_els_failure_event(struct lpfc_hba *, struct lpfc_iocbq *,
+diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
+index bce59ec..a95815e 100644
+--- a/drivers/scsi/lpfc/lpfc_els.c
++++ b/drivers/scsi/lpfc/lpfc_els.c
+@@ -224,7 +224,11 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
+               /* For ELS_REQUEST64_CR, use the VPI by default */
+               icmd->ulpContext = vport->vpi;
+               icmd->ulpCt_h = 0;
+-              icmd->ulpCt_l = 1;
++              /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
++              if (elscmd == ELS_CMD_ECHO)
++                      icmd->ulpCt_l = 0; /* context = invalid RPI */
++              else
++                      icmd->ulpCt_l = 1; /* context = VPI */
+       }
+       bpl = (struct ulp_bde64 *) pbuflist->virt;
+@@ -2504,6 +2508,15 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+       case IOSTAT_LOCAL_REJECT:
+               switch ((irsp->un.ulpWord[4] & 0xff)) {
+               case IOERR_LOOP_OPEN_FAILURE:
++                      if (cmd == ELS_CMD_FLOGI) {
++                              if (PCI_DEVICE_ID_HORNET ==
++                                      phba->pcidev->device) {
++                                      phba->fc_topology = TOPOLOGY_LOOP;
++                                      phba->pport->fc_myDID = 0;
++                                      phba->alpa_map[0] = 0;
++                                      phba->alpa_map[1] = 0;
++                              }
++                      }
+                       if (cmd == ELS_CMD_PLOGI && cmdiocb->retry == 0)
+                               delay = 1000;
+                       retry = 1;
+@@ -3870,27 +3883,21 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
+               while (payload_len) {
+                       rscn_did.un.word = be32_to_cpu(*lp++);
+                       payload_len -= sizeof(uint32_t);
+-                      switch (rscn_did.un.b.resv) {
+-                      case 0: /* Single N_Port ID effected */
++                      switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
++                      case RSCN_ADDRESS_FORMAT_PORT:
+                               if (ns_did.un.word == rscn_did.un.word)
+                                       goto return_did_out;
+                               break;
+-                      case 1: /* Whole N_Port Area effected */
++                      case RSCN_ADDRESS_FORMAT_AREA:
+                               if ((ns_did.un.b.domain == rscn_did.un.b.domain)
+                                   && (ns_did.un.b.area == rscn_did.un.b.area))
+                                       goto return_did_out;
+                               break;
+-                      case 2: /* Whole N_Port Domain effected */
++                      case RSCN_ADDRESS_FORMAT_DOMAIN:
+                               if (ns_did.un.b.domain == rscn_did.un.b.domain)
+                                       goto return_did_out;
+                               break;
+-                      default:
+-                              /* Unknown Identifier in RSCN node */
+-                              lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+-                                               "0217 Unknown Identifier in "
+-                                               "RSCN payload Data: x%x\n",
+-                                               rscn_did.un.word);
+-                      case 3: /* Whole Fabric effected */
++                      case RSCN_ADDRESS_FORMAT_FABRIC:
+                               goto return_did_out;
+                       }
+               }
+@@ -3934,6 +3941,49 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
+ }
+ /**
++ * lpfc_send_rscn_event: Send an RSCN event to management application.
++ * @vport: pointer to a host virtual N_Port data structure.
++ * @cmdiocb: pointer to lpfc command iocb data structure.
++ *
++ * lpfc_send_rscn_event sends an RSCN netlink event to management
++ * applications.
++ */
++static void
++lpfc_send_rscn_event(struct lpfc_vport *vport,
++              struct lpfc_iocbq *cmdiocb)
++{
++      struct lpfc_dmabuf *pcmd;
++      struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
++      uint32_t *payload_ptr;
++      uint32_t payload_len;
++      struct lpfc_rscn_event_header *rscn_event_data;
++
++      pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
++      payload_ptr = (uint32_t *) pcmd->virt;
++      payload_len = be32_to_cpu(*payload_ptr & ~ELS_CMD_MASK);
++
++      rscn_event_data = kmalloc(sizeof(struct lpfc_rscn_event_header) +
++              payload_len, GFP_KERNEL);
++      if (!rscn_event_data) {
++              lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
++                      "0147 Failed to allocate memory for RSCN event\n");
++              return;
++      }
++      rscn_event_data->event_type = FC_REG_RSCN_EVENT;
++      rscn_event_data->payload_length = payload_len;
++      memcpy(rscn_event_data->rscn_payload, payload_ptr,
++              payload_len);
++
++      fc_host_post_vendor_event(shost,
++              fc_get_event_number(),
++              sizeof(struct lpfc_els_event_header) + payload_len,
++              (char *)rscn_event_data,
++              LPFC_NL_VENDOR_ID);
++
++      kfree(rscn_event_data);
++}
++
++/**
+  * lpfc_els_rcv_rscn: Process an unsolicited rscn iocb.
+  * @vport: pointer to a host virtual N_Port data structure.
+  * @cmdiocb: pointer to lpfc command iocb data structure.
+@@ -3980,6 +4030,10 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+                        "0214 RSCN received Data: x%x x%x x%x x%x\n",
+                        vport->fc_flag, payload_len, *lp,
+                        vport->fc_rscn_id_cnt);
++
++      /* Send an RSCN event to the management application */
++      lpfc_send_rscn_event(vport, cmdiocb);
++
+       for (i = 0; i < payload_len/sizeof(uint32_t); i++)
+               fc_host_post_event(shost, fc_get_event_number(),
+                       FCH_EVT_RSCN, lp[i]);
+@@ -5532,7 +5586,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
+                       fc_get_event_number(),
+                       sizeof(lsrjt_event),
+                       (char *)&lsrjt_event,
+-                      SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++                      LPFC_NL_VENDOR_ID);
+               return;
+       }
+       if ((rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) ||
+@@ -5550,7 +5604,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
+                       fc_get_event_number(),
+                       sizeof(fabric_event),
+                       (char *)&fabric_event,
+-                      SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++                      LPFC_NL_VENDOR_ID);
+               return;
+       }
+@@ -5568,32 +5622,68 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
+ static void
+ lpfc_send_els_event(struct lpfc_vport *vport,
+                   struct lpfc_nodelist *ndlp,
+-                  uint32_t cmd)
++                  uint32_t *payload)
+ {
+-      struct lpfc_els_event_header els_data;
++      struct lpfc_els_event_header *els_data = NULL;
++      struct lpfc_logo_event *logo_data = NULL;
+       struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+-      els_data.event_type = FC_REG_ELS_EVENT;
+-      switch (cmd) {
++      if (*payload == ELS_CMD_LOGO) {
++              logo_data = kmalloc(sizeof(struct lpfc_logo_event), GFP_KERNEL);
++              if (!logo_data) {
++                      lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
++                              "0148 Failed to allocate memory "
++                              "for LOGO event\n");
++                      return;
++              }
++              els_data = &logo_data->header;
++      } else {
++              els_data = kmalloc(sizeof(struct lpfc_els_event_header),
++                      GFP_KERNEL);
++              if (!els_data) {
++                      lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
++                              "0149 Failed to allocate memory "
++                              "for ELS event\n");
++                      return;
++              }
++      }
++      els_data->event_type = FC_REG_ELS_EVENT;
++      switch (*payload) {
+       case ELS_CMD_PLOGI:
+-              els_data.subcategory = LPFC_EVENT_PLOGI_RCV;
++              els_data->subcategory = LPFC_EVENT_PLOGI_RCV;
+               break;
+       case ELS_CMD_PRLO:
+-              els_data.subcategory = LPFC_EVENT_PRLO_RCV;
++              els_data->subcategory = LPFC_EVENT_PRLO_RCV;
+               break;
+       case ELS_CMD_ADISC:
+-              els_data.subcategory = LPFC_EVENT_ADISC_RCV;
++              els_data->subcategory = LPFC_EVENT_ADISC_RCV;
++              break;
++      case ELS_CMD_LOGO:
++              els_data->subcategory = LPFC_EVENT_LOGO_RCV;
++              /* Copy the WWPN in the LOGO payload */
++              memcpy(logo_data->logo_wwpn, &payload[2],
++                      sizeof(struct lpfc_name));
+               break;
+       default:
+               return;
+       }
+-      memcpy(els_data.wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
+-      memcpy(els_data.wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
+-      fc_host_post_vendor_event(shost,
+-              fc_get_event_number(),
+-              sizeof(els_data),
+-              (char *)&els_data,
+-              SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++      memcpy(els_data->wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
++      memcpy(els_data->wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
++      if (*payload == ELS_CMD_LOGO) {
++              fc_host_post_vendor_event(shost,
++                      fc_get_event_number(),
++                      sizeof(struct lpfc_logo_event),
++                      (char *)logo_data,
++                      LPFC_NL_VENDOR_ID);
++              kfree(logo_data);
++      } else {
++              fc_host_post_vendor_event(shost,
++                      fc_get_event_number(),
++                      sizeof(struct lpfc_els_event_header),
++                      (char *)els_data,
++                      LPFC_NL_VENDOR_ID);
++              kfree(els_data);
++      }
+       return;
+ }
+@@ -5700,7 +5790,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+               phba->fc_stat.elsRcvPLOGI++;
+               ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
+-              lpfc_send_els_event(vport, ndlp, cmd);
++              lpfc_send_els_event(vport, ndlp, payload);
+               if (vport->port_state < LPFC_DISC_AUTH) {
+                       if (!(phba->pport->fc_flag & FC_PT2PT) ||
+                               (phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
+@@ -5738,6 +5828,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                       did, vport->port_state, ndlp->nlp_flag);
+               phba->fc_stat.elsRcvLOGO++;
++              lpfc_send_els_event(vport, ndlp, payload);
+               if (vport->port_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSRJT_UNABLE_TPC;
+                       break;
+@@ -5750,7 +5841,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                       did, vport->port_state, ndlp->nlp_flag);
+               phba->fc_stat.elsRcvPRLO++;
+-              lpfc_send_els_event(vport, ndlp, cmd);
++              lpfc_send_els_event(vport, ndlp, payload);
+               if (vport->port_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSRJT_UNABLE_TPC;
+                       break;
+@@ -5768,7 +5859,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                       "RCV ADISC:       did:x%x/ste:x%x flg:x%x",
+                       did, vport->port_state, ndlp->nlp_flag);
+-              lpfc_send_els_event(vport, ndlp, cmd);
++              lpfc_send_els_event(vport, ndlp, payload);
+               phba->fc_stat.elsRcvADISC++;
+               if (vport->port_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSRJT_UNABLE_TPC;
+diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
+index 3d825ff..502a9a5 100644
+--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
++++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
+@@ -391,7 +391,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
+                       evt_data_size = sizeof(fast_evt_data->un.
+                               read_check_error);
+               } else if ((evt_sub_category == LPFC_EVENT_FABRIC_BUSY) ||
+-                      (evt_sub_category == IOSTAT_NPORT_BSY)) {
++                      (evt_sub_category == LPFC_EVENT_PORT_BUSY)) {
+                       evt_data = (char *) &fast_evt_data->un.fabric_evt;
+                       evt_data_size = sizeof(fast_evt_data->un.fabric_evt);
+               } else {
+@@ -428,7 +428,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
+               fc_get_event_number(),
+               evt_data_size,
+               evt_data,
+-              SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++              LPFC_NL_VENDOR_ID);
+       lpfc_free_fast_evt(phba, fast_evt_data);
+       return;
+@@ -635,20 +635,25 @@ lpfc_do_work(void *p)
+       set_user_nice(current, -20);
+       phba->data_flags = 0;
+-      while (1) {
++      while (!kthread_should_stop()) {
+               /* wait and check worker queue activities */
+               rc = wait_event_interruptible(phba->work_waitq,
+                                       (test_and_clear_bit(LPFC_DATA_READY,
+                                                           &phba->data_flags)
+                                        || kthread_should_stop()));
+-              BUG_ON(rc);
+-
+-              if (kthread_should_stop())
++              /* Signal wakeup shall terminate the worker thread */
++              if (rc) {
++                      lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
++                                      "0433 Wakeup on signal: rc=x%x\n", rc);
+                       break;
++              }
+               /* Attend pending lpfc data processing */
+               lpfc_work_done(phba);
+       }
++      phba->worker_thread = NULL;
++      lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
++                      "0432 Worker thread stopped.\n");
+       return 0;
+ }
+@@ -1895,6 +1900,36 @@ lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+       lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
+                               NLP_STE_UNUSED_NODE);
+ }
++/**
++ * lpfc_initialize_node: Initialize all fields of node object.
++ * @vport: Pointer to Virtual Port object.
++ * @ndlp: Pointer to FC node object.
++ * @did: FC_ID of the node.
++ *    This function is always called when node object need to
++ * be initialized. It initializes all the fields of the node
++ * object.
++ **/
++static inline void
++lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
++      uint32_t did)
++{
++      INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
++      INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
++      INIT_LIST_HEAD(&ndlp->els_reauth_evt.evt_listp);
++      init_timer(&ndlp->nlp_delayfunc);
++      ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
++      ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
++      init_timer(&ndlp->nlp_reauth_tmr);
++      ndlp->nlp_reauth_tmr.function = lpfc_reauth_node;
++      ndlp->nlp_reauth_tmr.data = (unsigned long)ndlp;
++      ndlp->nlp_DID = did;
++      ndlp->vport = vport;
++      ndlp->nlp_sid = NLP_NO_SID;
++      kref_init(&ndlp->kref);
++      NLP_INT_NODE_ACT(ndlp);
++      atomic_set(&ndlp->cmd_pending, 0);
++      ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
++}
+ struct lpfc_nodelist *
+ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+@@ -1935,21 +1970,7 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+       /* re-initialize ndlp except of ndlp linked list pointer */
+       memset((((char *)ndlp) + sizeof (struct list_head)), 0,
+               sizeof (struct lpfc_nodelist) - sizeof (struct list_head));
+-      INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
+-      INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
+-      INIT_LIST_HEAD(&ndlp->els_reauth_evt.evt_listp);
+-      init_timer(&ndlp->nlp_delayfunc);
+-      ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+-      ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+-      init_timer(&ndlp->nlp_reauth_tmr);
+-      ndlp->nlp_reauth_tmr.function = lpfc_reauth_node;
+-      ndlp->nlp_reauth_tmr.data = (unsigned long)ndlp;
+-      ndlp->nlp_DID = did;
+-      ndlp->vport = vport;
+-      ndlp->nlp_sid = NLP_NO_SID;
+-      /* ndlp management re-initialize */
+-      kref_init(&ndlp->kref);
+-      NLP_INT_NODE_ACT(ndlp);
++      lpfc_initialize_node(vport, ndlp, did);
+       spin_unlock_irqrestore(&phba->ndlp_lock, flags);
+@@ -2561,7 +2582,8 @@ lpfc_disc_list_loopmap(struct lpfc_vport *vport)
+                       alpa = lpfcAlpaArray[index];
+                       if ((vport->fc_myDID & 0xff) == alpa)
+                               continue;
+-                      lpfc_setup_disc_node(vport, alpa);
++                      if (!(phba->link_flag & LS_LOOPBACK_MODE))
++                              lpfc_setup_disc_node(vport, alpa);
+               }
+       }
+       return;
+@@ -3204,23 +3226,8 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+             uint32_t did)
+ {
+       memset(ndlp, 0, sizeof (struct lpfc_nodelist));
+-      INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
+-      INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
+-      INIT_LIST_HEAD(&ndlp->els_reauth_evt.evt_listp);
+-      init_timer(&ndlp->nlp_delayfunc);
+-      ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+-      ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+-      init_timer(&ndlp->nlp_reauth_tmr);
+-      ndlp->nlp_reauth_tmr.function = lpfc_reauth_node;
+-      ndlp->nlp_reauth_tmr.data = (unsigned long)ndlp;
+-      ndlp->nlp_DID = did;
+-      ndlp->vport = vport;
+-      ndlp->nlp_sid = NLP_NO_SID;
++      lpfc_initialize_node(vport, ndlp, did);
+       INIT_LIST_HEAD(&ndlp->nlp_listp);
+-      kref_init(&ndlp->kref);
+-      NLP_INT_NODE_ACT(ndlp);
+-      atomic_set(&ndlp->cmd_pending, 0);
+-      ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+       lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
+               "node init:       did:x%x",
+diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
+index 9fc50ef..90d0c5a 100644
+--- a/drivers/scsi/lpfc/lpfc_hw.h
++++ b/drivers/scsi/lpfc/lpfc_hw.h
+@@ -66,6 +66,9 @@
+ #define BUF_SZ_4K             4096
++/* vendor ID used in SCSI netlink calls */
++#define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX)
++
+ /* Common Transport structures and definitions */
+ union CtRevisionId {
+@@ -891,6 +894,12 @@ typedef struct _D_ID {            /* Structure is in Big Endian format */
+       } un;
+ } D_ID;
++#define RSCN_ADDRESS_FORMAT_PORT      0x0
++#define RSCN_ADDRESS_FORMAT_AREA      0x1
++#define RSCN_ADDRESS_FORMAT_DOMAIN    0x2
++#define RSCN_ADDRESS_FORMAT_FABRIC    0x3
++#define RSCN_ADDRESS_FORMAT_MASK      0x3
++
+ /*
+  *  Structure to define all ELS Payload types
+  */
+diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
+index c19c631..c0ea4fc 100644
+--- a/drivers/scsi/lpfc/lpfc_init.c
++++ b/drivers/scsi/lpfc/lpfc_init.c
+@@ -879,8 +879,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+                                 sizeof(board_event),
+                                 (char *) &board_event,
+-                                SCSI_NL_VID_TYPE_PCI
+-                                | PCI_VENDOR_ID_EMULEX);
++                                LPFC_NL_VENDOR_ID);
+       if (phba->work_hs & HS_FFER6) {
+               /* Re-establishing Link */
+@@ -2383,6 +2382,98 @@ lpfc_disable_msix(struct lpfc_hba *phba)
+ }
+ /**
++ * lpfc_enable_intr: Enable device interrupt.
++ * @phba: pointer to lpfc hba data structure.
++ *
++ * This routine is invoked to enable device interrupt and associate driver's
++ * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
++ * mode configured to the driver, the driver will try to fallback from the
++ * configured interrupt mode to an interrupt mode which is supported by the
++ * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
++ *
++ * Return codes
++ *   0 - sucessful
++ *   other values - error
++ **/
++static int
++lpfc_enable_intr(struct lpfc_hba *phba)
++{
++      int retval = 0;
++
++      /* Starting point of configuring interrupt method */
++      phba->intr_type = NONE;
++
++      if (phba->cfg_use_msi == 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");
++                      /* Now, try to enable MSI-X interrupt mode */
++                      retval = lpfc_enable_msix(phba);
++                      if (!retval) {
++                              phba->intr_type = MSIX;
++                              lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
++                                              "0480 enable MSI-X mode.\n");
++                      }
++              }
++      }
++
++      /* Fallback to MSI if MSI-X initialization failed */
++      if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
++              retval = pci_enable_msi(phba->pcidev);
++              if (!retval) {
++                      phba->intr_type = MSI;
++                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
++                                      "0481 enable MSI mode.\n");
++              } else
++                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
++                                      "0470 enable IRQ mode.\n");
++      }
++
++      /* MSI-X is the only case the doesn't need to call request_irq */
++      if (phba->intr_type != MSIX) {
++              retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
++                                   IRQF_SHARED, LPFC_DRIVER_NAME, phba);
++              if (retval) {
++                      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)
++                      phba->intr_type = INTx;
++      }
++
++      return retval;
++}
++
++/**
++ * lpfc_disable_intr: Disable device interrupt.
++ * @phba: pointer to lpfc hba data structure.
++ *
++ * This routine is invoked to disable device interrupt and disassociate the
++ * driver's interrupt handler(s) from interrupt vector(s). Depending on the
++ * interrupt mode, the driver will release the interrupt vector(s) for the
++ * message signaled interrupt.
++ **/
++static void
++lpfc_disable_intr(struct lpfc_hba *phba)
++{
++      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);
++      }
++      return;
++}
++
++/**
+  * lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
+  * @pdev: pointer to PCI device
+  * @pid: pointer to PCI device identifier
+@@ -2634,7 +2725,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+       lpfc_debugfs_initialize(vport);
+       pci_set_drvdata(pdev, shost);
+-      phba->intr_type = NONE;
+       phba->MBslimaddr = phba->slim_memmap_p;
+       phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
+@@ -2643,48 +2733,11 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+       phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
+       /* Configure and enable interrupt */
+-      if (phba->cfg_use_msi == 2) {
+-              /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+-              error = lpfc_sli_config_port(phba, 3);
+-              if (error)
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                              "0427 Firmware not capable of SLI 3 mode.\n");
+-              else {
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                              "0426 Firmware capable of SLI 3 mode.\n");
+-                      /* Now, try to enable MSI-X interrupt mode */
+-                      error = lpfc_enable_msix(phba);
+-                      if (!error) {
+-                              phba->intr_type = MSIX;
+-                              lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                              "0430 enable MSI-X mode.\n");
+-                      }
+-              }
+-      }
+-
+-      /* Fallback to MSI if MSI-X initialization failed */
+-      if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
+-              retval = pci_enable_msi(phba->pcidev);
+-              if (!retval) {
+-                      phba->intr_type = MSI;
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                      "0473 enable MSI mode.\n");
+-              } else
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                      "0452 enable IRQ mode.\n");
+-      }
+-
+-      /* MSI-X is the only case the doesn't need to call request_irq */
+-      if (phba->intr_type != MSIX) {
+-              retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+-                                   IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+-              if (retval) {
+-                      lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable "
+-                                      "interrupt handler failed\n");
+-                      error = retval;
+-                      goto out_disable_msi;
+-              } else if (phba->intr_type != MSI)
+-                      phba->intr_type = INTx;
++      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;
+       }
+       phba->dfc_host = lpfcdfc_host_add(pdev, shost, phba);
+@@ -2731,7 +2784,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+               sizeof(adapter_event),
+               (char *) &adapter_event,
+-              SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++              LPFC_NL_VENDOR_ID);
+       scsi_scan_host(shost);
+@@ -2747,15 +2800,8 @@ out_free_irq:
+               lpfcdfc_host_del(phba->dfc_host);
+       lpfc_stop_phba_timers(phba);
+       phba->pport->work_port_events = 0;
+-
+-      if (phba->intr_type == MSIX)
+-              lpfc_disable_msix(phba);
+-      else
+-              free_irq(phba->pcidev->irq, phba);
+-
+-out_disable_msi:
+-      if (phba->intr_type == MSI)
+-              pci_disable_msi(phba->pcidev);
++      lpfc_disable_intr(phba);
++out_destroy_port:
+       destroy_port(vport);
+ out_kthread_stop:
+       kthread_stop(phba->worker_thread);
+@@ -2796,7 +2842,7 @@ out:
+  * @pdev: pointer to PCI device
+  *
+  * This routine is to be registered to the kernel's PCI subsystem. When an
+- * Emulex HBA is removed from PCI bus. It perform all the necessary cleanup
++ * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
+  * for the HBA device to be removed from the PCI subsystem properly.
+  **/
+ static void __devexit
+@@ -2804,12 +2850,11 @@ 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_vport **vports;
+       struct lpfc_hba   *phba = vport->phba;
++      int i;
+       int bars = pci_select_bars(pdev, IORESOURCE_MEM);
+-      /* In case PCI channel permanently disabled, rescan SCSI devices */
+-      if (pdev->error_state == pci_channel_io_perm_failure)
+-              lpfc_scsi_dev_rescan(phba);
+       lpfcdfc_host_del(phba->dfc_host);
+       phba->dfc_host = NULL;
+@@ -2822,6 +2867,14 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
+       kthread_stop(phba->worker_thread);
++      /* Release all the vports against this physical port */
++      vports = lpfc_create_vport_work_array(phba);
++      if (vports != NULL)
++              for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
++                      fc_vport_terminate(vports[i]->fc_vport);
++      lpfc_destroy_vport_work_array(phba, vports);
++
++      /* Remove FC host and then SCSI host with the physical port */
+       fc_remove_host(shost);
+       scsi_remove_host(shost);
+       lpfc_cleanup(vport);
+@@ -2841,13 +2894,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
+       lpfc_debugfs_terminate(vport);
+-      if (phba->intr_type == MSIX)
+-              lpfc_disable_msix(phba);
+-      else {
+-              free_irq(phba->pcidev->irq, phba);
+-              if (phba->intr_type == MSI)
+-                      pci_disable_msi(phba->pcidev);
+-      }
++      /* Disable interrupt */
++      lpfc_disable_intr(phba);
+       pci_set_drvdata(pdev, NULL);
+       scsi_host_put(shost);
+@@ -2879,6 +2927,111 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
+ }
+ /**
++ * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
++ * @pdev: pointer to PCI device
++ * @msg: power management message
++ *
++ * This routine is to be registered to the kernel's PCI subsystem to support
++ * system Power Management (PM). When PM invokes this method, it quiesces the
++ * device by stopping the driver's worker thread for the device, turning off
++ * device's interrupt and DMA, and bring the device offline. Note that as the
++ * driver implements the minimum PM requirements to a power-aware driver's PM
++ * support for suspend/resume -- all the possible PM messages (SUSPEND,
++ * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
++ * and the driver will fully reinitialize its device during resume() method
++ * call, the driver will set device to PCI_D3hot state in PCI config space
++ * instead of setting it according to the @msg provided by the PM.
++ *
++ * Return code
++ *   0 - driver suspended the device
++ *   Error otherwise
++ **/
++static int
++lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
++{
++      struct Scsi_Host *shost = pci_get_drvdata(pdev);
++      struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
++
++      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
++                      "0473 PCI device Power Management suspend.\n");
++
++      /* Bring down the device */
++      lpfc_offline_prep(phba);
++      lpfc_offline(phba);
++      kthread_stop(phba->worker_thread);
++
++      /* Disable interrupt from device */
++      lpfc_disable_intr(phba);
++
++      /* Save device state to PCI config space */
++      pci_save_state(pdev);
++      pci_set_power_state(pdev, PCI_D3hot);
++
++      return 0;
++}
++
++/**
++ * lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
++ * @pdev: pointer to PCI device
++ *
++ * This routine is to be registered to the kernel's PCI subsystem to support
++ * system Power Management (PM). When PM invokes this method, it restores
++ * the device's PCI config space state and fully reinitializes the device
++ * and brings it online. Note that as the driver implements the minimum PM
++ * requirements to a power-aware driver's PM for suspend/resume -- all
++ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
++ * method call will be treated as SUSPEND and the driver will fully
++ * reinitialize its device during resume() method call, the device will be
++ * set to PCI_D0 directly in PCI config space before restoring the state.
++ *
++ * Return code
++ *   0 - driver suspended the device
++ *   Error otherwise
++ **/
++static int
++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;
++      int error;
++
++      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
++                      "0452 PCI device Power Management resume.\n");
++
++      /* Restore device state from PCI config space */
++      pci_set_power_state(pdev, PCI_D0);
++      pci_restore_state(pdev);
++      if (pdev->is_busmaster)
++              pci_set_master(pdev);
++
++      /* Startup the kernel thread for this host adapter. */
++      phba->worker_thread = kthread_run(lpfc_do_work, phba,
++                                      "lpfc_worker_%d", phba->brd_no);
++      if (IS_ERR(phba->worker_thread)) {
++              error = PTR_ERR(phba->worker_thread);
++              lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
++                              "0434 PM resume failed to start worker "
++                              "thread: error=x%x.\n", error);
++              return error;
++      }
++
++      /* Enable interrupt from device */
++      error = lpfc_enable_intr(phba);
++      if (error) {
++              lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
++                              "0430 PM resume Failed to enable interrupt: "
++                              "error=x%x.\n", error);
++              return error;
++      }
++
++      /* Restart HBA and bring it online */
++      lpfc_sli_brdrestart(phba);
++      lpfc_online(phba);
++
++      return 0;
++}
++
++/**
+  * lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
+  * @pdev: pointer to PCI device.
+  * @state: the current PCI connection state.
+@@ -2921,13 +3074,8 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
+       pring = &psli->ring[psli->fcp_ring];
+       lpfc_sli_abort_iocb_ring(phba, pring);
+-      if (phba->intr_type == MSIX)
+-              lpfc_disable_msix(phba);
+-      else {
+-              free_irq(phba->pcidev->irq, phba);
+-              if (phba->intr_type == MSI)
+-                      pci_disable_msi(phba->pcidev);
+-      }
++      /* Disable interrupt */
++      lpfc_disable_intr(phba);
+       /* Request a slot reset. */
+       return PCI_ERS_RESULT_NEED_RESET;
+@@ -2955,7 +3103,7 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       struct lpfc_sli *psli = &phba->sli;
+-      int error, retval;
++      int error;
+       dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
+       if (pci_enable_device_mem(pdev)) {
+@@ -2971,48 +3119,12 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
+       spin_unlock_irq(&phba->hbalock);
+       /* Enable configured interrupt method */
+-      phba->intr_type = NONE;
+-      if (phba->cfg_use_msi == 2) {
+-              /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+-              error = lpfc_sli_config_port(phba, 3);
+-              if (error)
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                              "0478 Firmware not capable of SLI 3 mode.\n");
+-              else {
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                              "0479 Firmware capable of SLI 3 mode.\n");
+-                      /* Now, try to enable MSI-X interrupt mode */
+-                      error = lpfc_enable_msix(phba);
+-                      if (!error) {
+-                              phba->intr_type = MSIX;
+-                              lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                              "0480 enable MSI-X mode.\n");
+-                      }
+-              }
+-      }
+-
+-      /* Fallback to MSI if MSI-X initialization failed */
+-      if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
+-              retval = pci_enable_msi(phba->pcidev);
+-              if (!retval) {
+-                      phba->intr_type = MSI;
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                      "0481 enable MSI mode.\n");
+-              } else
+-                      lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+-                                      "0470 enable IRQ mode.\n");
+-      }
+-
+-      /* MSI-X is the only case the doesn't need to call request_irq */
+-      if (phba->intr_type != MSIX) {
+-              retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+-                                   IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+-              if (retval) {
+-                      lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+-                                      "0471 Enable interrupt handler "
+-                                      "failed\n");
+-              } else if (phba->intr_type != MSI)
+-                      phba->intr_type = INTx;
++      error = lpfc_enable_intr(phba);
++      if (error) {
++              lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
++                              "0427 Cannot re-enable interrupt after "
++                              "slot reset.\n");
++              return PCI_ERS_RESULT_DISCONNECT;
+       }
+       /* Take device offline; this will perform cleanup */
+@@ -3130,6 +3242,8 @@ static struct pci_driver lpfc_driver = {
+       .id_table       = lpfc_id_table,
+       .probe          = lpfc_pci_probe_one,
+       .remove         = __devexit_p(lpfc_pci_remove_one),
++      .suspend        = lpfc_pci_suspend_one,
++      .resume         = lpfc_pci_resume_one,
+       .err_handler    = &lpfc_err_handler,
+ };
+diff --git a/drivers/scsi/lpfc/lpfc_ioctl.c b/drivers/scsi/lpfc/lpfc_ioctl.c
+index 242bed3..e80d157 100644
+--- a/drivers/scsi/lpfc/lpfc_ioctl.c
++++ b/drivers/scsi/lpfc/lpfc_ioctl.c
+@@ -828,10 +828,10 @@ lpfc_ioctl_send_mgmt_cmd(struct lpfc_hba * phba,
+               rc = EIO;
+ send_mgmt_cmd_free_outdmp:
+-      spin_lock_irq(shost->host_lock);
+       dfc_cmd_data_free(phba, outdmp);
+ send_mgmt_cmd_free_indmp:
+       dfc_cmd_data_free(phba, indmp);
++      spin_lock_irq(shost->host_lock);
+ send_mgmt_cmd_free_bmpvirt:
+       lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ send_mgmt_cmd_free_bmp:
+@@ -2069,14 +2069,14 @@ __dfc_cmd_data_alloc(struct lpfc_hba * phba,
+                                       cnt)) {
+                                       goto out;
+                               }
+-
++                      bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+                       pci_dma_sync_single_for_device(phba->pcidev,
+                               dmp->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+-              } else
++              } else {
+                       memset((uint8_t *)dmp->dma.virt, 0, cnt);
+-              bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+-
++                      bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
++              }
+               /* build buffer ptr list for IOCB */
+               bpl->addrLow = le32_to_cpu(putPaddrLow(dmp->dma.phys));
+               bpl->addrHigh = le32_to_cpu(putPaddrHigh(dmp->dma.phys));
+diff --git a/drivers/scsi/lpfc/lpfc_menlo.c b/drivers/scsi/lpfc/lpfc_menlo.c
+index 60d3df8..aa36c16 100644
+--- a/drivers/scsi/lpfc/lpfc_menlo.c
++++ b/drivers/scsi/lpfc/lpfc_menlo.c
+@@ -42,6 +42,7 @@
+ #include "lpfc_vport.h"
+ #define MENLO_CMD_FW_DOWNLOAD                   0x00000002
++#define MENLO_CMD_LOOPBACK                    0x00000014
+ static void lpfc_menlo_iocb_timeout_cmpl(struct lpfc_hba *,
+                       struct lpfc_iocbq *, struct lpfc_iocbq *);
+@@ -686,6 +687,16 @@ lpfc_menlo_write(struct lpfc_hba *phba,
+               } else
+                       memcpy((uint8_t *) mlast->dma.virt, buf, count);
++              if (sysfs_menlo->cmdhdr.cmd == MENLO_CMD_LOOPBACK) {
++                      if (mlast) {
++                              tmpptr = (uint32_t *)mlast->dma.virt;
++                              if (*(tmpptr+2))
++                                      phba->link_flag |= LS_LOOPBACK_MODE;
++                              else
++                                      phba->link_flag &= ~LS_LOOPBACK_MODE;
++                      }
++              }
++
+               if (sysfs_menlo->cmdhdr.cmd == MENLO_CMD_FW_DOWNLOAD
+                       && genreq->offset < hdr_offset) {
+                       if (sysfs_menlo->cr.indmp
+diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
+index 1accb5a..991ad53 100644
+--- a/drivers/scsi/lpfc/lpfc_nl.h
++++ b/drivers/scsi/lpfc/lpfc_nl.h
+@@ -52,6 +52,13 @@
+  * The payload sent via the fc transport is one-way driver->application.
+  */
++/* RSCN event header */
++struct lpfc_rscn_event_header {
++      uint32_t event_type;
++      uint32_t payload_length; /* RSCN data length in bytes */
++      uint32_t rscn_payload[];
++};
++
+ /* els event header */
+ struct lpfc_els_event_header {
+       uint32_t event_type;
+@@ -65,6 +72,7 @@ struct lpfc_els_event_header {
+ #define LPFC_EVENT_PRLO_RCV           0x02
+ #define LPFC_EVENT_ADISC_RCV          0x04
+ #define LPFC_EVENT_LSRJT_RCV          0x08
++#define LPFC_EVENT_LOGO_RCV           0x10
+ /* special els lsrjt event */
+ struct lpfc_lsrjt_event {
+@@ -74,6 +82,11 @@ struct lpfc_lsrjt_event {
+       uint32_t explanation;
+ };
++/* special els logo event */
++struct lpfc_logo_event {
++      struct lpfc_els_event_header header;
++      uint8_t logo_wwpn[8];
++};
+ /* fabric event header */
+ struct lpfc_fabric_event_header {
+@@ -125,6 +138,7 @@ struct lpfc_scsi_varqueuedepth_event {
+ /* special case scsi check condition event */
+ struct lpfc_scsi_check_condition_event {
+       struct lpfc_scsi_event_header scsi_event;
++      uint8_t opcode;
+       uint8_t sense_key;
+       uint8_t asc;
+       uint8_t ascq;
+diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
+index a116875..a7ea952 100644
+--- a/drivers/scsi/lpfc/lpfc_scsi.c
++++ b/drivers/scsi/lpfc/lpfc_scsi.c
+@@ -147,12 +147,19 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
+       return;
+ }
+-/*
+- * This function is called with no lock held when there is a resource
+- * error in driver or in firmware.
+- */
++/**
++ * lpfc_rampdown_queue_depth: Post RAMP_DOWN_QUEUE event to worker thread.
++ * @phba: The Hba for which this call is being executed.
++ *
++ * This routine is called when there is resource error in driver or firmware.
++ * This routine posts WORKER_RAMP_DOWN_QUEUE event for @phba. This routine
++ * posts at most 1 event each second. This routine wakes up worker thread of
++ * @phba to process WORKER_RAM_DOWN_EVENT event.
++ *
++ * This routine should be called with no lock held.
++ **/
+ void
+-lpfc_adjust_queue_depth(struct lpfc_hba *phba)
++lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
+ {
+       unsigned long flags;
+       uint32_t evt_posted;
+@@ -335,22 +342,6 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
+       lpfc_destroy_vport_work_array(phba, vports);
+ }
+-void
+-lpfc_scsi_dev_rescan(struct lpfc_hba *phba)
+-{
+-      struct lpfc_vport **vports;
+-      struct Scsi_Host  *shost;
+-      int i;
+-
+-      vports = lpfc_create_vport_work_array(phba);
+-      if (vports != NULL)
+-              for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+-                      shost = lpfc_shost_from_vport(vports[i]);
+-                      scsi_scan_host(shost);
+-              }
+-      lpfc_destroy_vport_work_array(phba, vports);
+-}
+-
+ /*
+  * This routine allocates a scsi buffer, which contains all the necessary
+  * information needed to initiate a SCSI I/O.  The non-DMAable buffer region
+@@ -861,7 +852,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+       lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
+       lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
+-      atomic_dec(&pnode->cmd_pending);
++      if (pnode && NLP_CHK_NODE_ACT(pnode))
++              atomic_dec(&pnode->cmd_pending);
+       if (lpfc_cmd->status) {
+               if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
+@@ -951,23 +943,31 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+          time_after(jiffies, lpfc_cmd->start_time +
+               msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
+               spin_lock_irqsave(sdev->host->host_lock, flags);
+-              if ((pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) &&
+-                  (atomic_read(&pnode->cmd_pending) > LPFC_MIN_TGT_QDEPTH) &&
+-                  ((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10))))
+-                      pnode->cmd_qdepth = atomic_read(&pnode->cmd_pending);
+-
+-              pnode->last_change_time = jiffies;
++              if (pnode && NLP_CHK_NODE_ACT(pnode)) {
++                      if (pnode->cmd_qdepth >
++                              atomic_read(&pnode->cmd_pending) &&
++                              (atomic_read(&pnode->cmd_pending) >
++                              LPFC_MIN_TGT_QDEPTH) &&
++                              ((cmd->cmnd[0] == READ_10) ||
++                              (cmd->cmnd[0] == WRITE_10)))
++                              pnode->cmd_qdepth =
++                                      atomic_read(&pnode->cmd_pending);
++
++                      pnode->last_change_time = jiffies;
++              }
+               spin_unlock_irqrestore(sdev->host->host_lock, flags);
+-      } else if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
++      } else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
++              if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
+                  time_after(jiffies, pnode->last_change_time +
+-                      msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
+-              spin_lock_irqsave(sdev->host->host_lock, flags);
+-              pnode->cmd_qdepth += pnode->cmd_qdepth *
+-                      LPFC_TGTQ_RAMPUP_PCENT / 100;
+-              if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
+-                      pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+-              pnode->last_change_time = jiffies;
+-              spin_unlock_irqrestore(sdev->host->host_lock, flags);
++                            msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
++                      spin_lock_irqsave(sdev->host->host_lock, flags);
++                      pnode->cmd_qdepth += pnode->cmd_qdepth *
++                              LPFC_TGTQ_RAMPUP_PCENT / 100;
++                      if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
++                              pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
++                      pnode->last_change_time = jiffies;
++                      spin_unlock_irqrestore(sdev->host->host_lock, flags);
++              }
+       }
+       lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
+@@ -1363,13 +1363,13 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+               cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0);
+               goto out_fail_command;
+       }
+-
+-      if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
++      if (vport->cfg_max_scsicmpl_time &&
++              (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth))
+               goto out_host_busy;
+       lpfc_cmd = lpfc_get_scsi_buf(phba);
+       if (lpfc_cmd == NULL) {
+-              lpfc_adjust_queue_depth(phba);
++              lpfc_rampdown_queue_depth(phba);
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+                                "0707 driver's buffer pool is empty, "
+@@ -1397,9 +1397,10 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+       atomic_inc(&ndlp->cmd_pending);
+       err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
+                                 &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
+-      if (err)
++      if (err) {
++              atomic_dec(&ndlp->cmd_pending);
+               goto out_host_busy_free_buf;
+-
++      }
+       if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
+               lpfc_sli_poll_fcp_ring(phba);
+               if (phba->cfg_poll & DISABLE_FCP_RING_INT)
+@@ -1409,7 +1410,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+       return 0;
+  out_host_busy_free_buf:
+-      atomic_dec(&ndlp->cmd_pending);
+       lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
+       lpfc_release_scsi_buf(phba, lpfc_cmd);
+  out_host_busy:
+@@ -1575,7 +1575,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
+               fc_get_event_number(),
+               sizeof(scsi_event),
+               (char *)&scsi_event,
+-              SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++              LPFC_NL_VENDOR_ID);
+       if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+@@ -1672,7 +1672,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
+               fc_get_event_number(),
+               sizeof(scsi_event),
+               (char *)&scsi_event,
+-              SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++              LPFC_NL_VENDOR_ID);
+       lpfc_block_error_handler(cmnd);
+       /*
+diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
+index d4341df..ac78493 100644
+--- a/drivers/scsi/lpfc/lpfc_sli.c
++++ b/drivers/scsi/lpfc/lpfc_sli.c
+@@ -1985,7 +1985,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
+                       if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                               (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
+                               spin_unlock_irqrestore(&phba->hbalock, iflag);
+-                              lpfc_adjust_queue_depth(phba);
++                              lpfc_rampdown_queue_depth(phba);
+                               spin_lock_irqsave(&phba->hbalock, iflag);
+                       }
+@@ -2228,7 +2228,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
+                       if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                            (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
+                               spin_unlock_irqrestore(&phba->hbalock, iflag);
+-                              lpfc_adjust_queue_depth(phba);
++                              lpfc_rampdown_queue_depth(phba);
+                               spin_lock_irqsave(&phba->hbalock, iflag);
+                       }
+@@ -2793,7 +2793,6 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
+ {
+       MAILBOX_t *mb;
+       struct lpfc_sli *psli;
+-      uint16_t skip_post;
+       volatile uint32_t word0;
+       void __iomem *to_slim;
+@@ -2818,13 +2817,10 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
+       readl(to_slim); /* flush */
+       /* Only skip post after fc_ffinit is completed */
+-      if (phba->pport->port_state) {
+-              skip_post = 1;
++      if (phba->pport->port_state)
+               word0 = 1;      /* This is really setting up word1 */
+-      } else {
+-              skip_post = 0;
++      else
+               word0 = 0;      /* This is really setting up word1 */
+-      }
+       to_slim = phba->MBslimaddr + sizeof (uint32_t);
+       writel(*(uint32_t *) mb, to_slim);
+       readl(to_slim); /* flush */
+@@ -2838,10 +2834,8 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
+       memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
+       psli->stats_start = get_seconds();
+-      if (skip_post)
+-              mdelay(100);
+-      else
+-              mdelay(2000);
++      /* Give the INITFF and Post time to settle. */
++      mdelay(100);
+       lpfc_hba_down_post(phba);
+@@ -3087,7 +3081,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
+               spin_unlock_irq(&phba->hbalock);
+               phba->pport->port_state = LPFC_VPORT_UNKNOWN;
+               lpfc_sli_brdrestart(phba);
+-              msleep(2500);
+               rc = lpfc_sli_chipset_init(phba);
+               if (rc)
+                       break;
+@@ -4041,7 +4034,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
+       shost = lpfc_shost_from_vport(phba->pport);
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+               sizeof(temp_event_data), (char *) &temp_event_data,
+-              SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
++              LPFC_NL_VENDOR_ID);
+ }
+@@ -5220,6 +5213,10 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
+ {
+       uint32_t ha_copy;
++      /* If PCI channel is offline, don't process it */
++      if (unlikely(pci_channel_offline(phba->pcidev)))
++              return 0;
++
+       /* If somebody is waiting to handle an eratt, don't process it
+        * here. The brdkill function will do this.
+        */
+diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
+index 899a337..a42cef2 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.1"
++#define LPFC_DRIVER_VERSION "8.2.8.3"
+ #define LPFC_DRIVER_NAME              "lpfc"
+ #define LPFC_SP_DRIVER_HANDLER_NAME   "lpfc:sp"
+diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
+index 8761840..c3a3f6e 100644
+--- a/drivers/scsi/lpfc/lpfc_vport.c
++++ b/drivers/scsi/lpfc/lpfc_vport.c
+@@ -605,6 +605,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
+       spin_unlock_irq(&phba->hbalock);
+       kfree(vport->vname);
+       lpfc_debugfs_terminate(vport);
++
++      /* Remove FC host and then SCSI host with the vport */
+       fc_remove_host(lpfc_shost_from_vport(vport));
+       scsi_remove_host(lpfc_shost_from_vport(vport));
+@@ -689,8 +691,6 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
+               }
+               vport->unreg_vpi_cmpl = VPORT_INVAL;
+               timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
+-              if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
+-                      goto skip_logo;
+               if (!lpfc_issue_els_npiv_logo(vport, ndlp))
+                       while (vport->unreg_vpi_cmpl == VPORT_INVAL && timeout)
+                               timeout = schedule_timeout(timeout);