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