From: Jamie Wellnitz 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 Signed-off-by: Hannes Reinecke 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);