diff options
Diffstat (limited to 'drivers/scsi/lpfc')
| -rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 29 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 742 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_bsg.c | 114 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_bsg.h | 4 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_crtn.h | 30 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_ct.c | 6 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.c | 130 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_disc.h | 5 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_els.c | 204 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 95 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_hw.h | 3 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_hw4.h | 51 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 791 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_mbox.c | 9 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_mem.c | 49 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_nportdisc.c | 70 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 907 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.h | 21 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 990 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.h | 13 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_sli4.h | 33 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_version.h | 6 | ||||
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_vport.c | 5 |
23 files changed, 3387 insertions, 920 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index bcc56cac4fd..434e9037908 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -73,8 +73,6 @@ struct lpfc_sli2_slim; */ /* 1 Second */ #define QUEUE_RAMP_DOWN_INTERVAL (msecs_to_jiffies(1000 * 1)) -/* 5 minutes */ -#define QUEUE_RAMP_UP_INTERVAL (msecs_to_jiffies(1000 * 300)) /* Number of exchanges reserved for discovery to complete */ #define LPFC_DISC_IOCB_BUFF_COUNT 20 @@ -421,6 +419,7 @@ struct lpfc_vport { uint32_t cfg_enable_da_id; uint32_t cfg_max_scsicmpl_time; uint32_t cfg_tgt_queue_depth; + uint32_t cfg_first_burst_size; uint32_t dev_loss_tmo_changed; @@ -641,6 +640,7 @@ struct lpfc_hba { #define HBA_DEVLOSS_TMO 0x2000 /* HBA in devloss timeout */ #define HBA_RRQ_ACTIVE 0x4000 /* process the rrq active list */ #define HBA_FCP_IOQ_FLUSH 0x8000 /* FCP I/O queues being flushed */ +#define HBA_FW_DUMP_OP 0x10000 /* Skips fn reset before FW dump */ uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/ struct lpfc_dmabuf slim2p; @@ -707,11 +707,10 @@ struct lpfc_hba { uint32_t cfg_multi_ring_type; uint32_t cfg_poll; uint32_t cfg_poll_tmo; + uint32_t cfg_task_mgmt_tmo; uint32_t cfg_use_msi; uint32_t cfg_fcp_imax; uint32_t cfg_fcp_cpu_map; - uint32_t cfg_fcp_wq_count; - uint32_t cfg_fcp_eq_count; uint32_t cfg_fcp_io_channel; uint32_t cfg_total_seg_cnt; uint32_t cfg_sg_seg_cnt; @@ -722,6 +721,20 @@ struct lpfc_hba { uint32_t cfg_hba_queue_depth; uint32_t cfg_enable_hba_reset; uint32_t cfg_enable_hba_heartbeat; + uint32_t cfg_fof; + uint32_t cfg_EnableXLane; + uint8_t cfg_oas_tgt_wwpn[8]; + uint8_t cfg_oas_vpt_wwpn[8]; + uint32_t cfg_oas_lun_state; +#define OAS_LUN_ENABLE 1 +#define OAS_LUN_DISABLE 0 + uint32_t cfg_oas_lun_status; +#define OAS_LUN_STATUS_EXISTS 0x01 + uint32_t cfg_oas_flags; +#define OAS_FIND_ANY_VPORT 0x01 +#define OAS_FIND_ANY_TARGET 0x02 +#define OAS_LUN_VALID 0x04 + uint32_t cfg_XLanePriority; uint32_t cfg_enable_bg; uint32_t cfg_hostmem_hgp; uint32_t cfg_log_verbose; @@ -730,6 +743,7 @@ struct lpfc_hba { uint32_t cfg_request_firmware_upgrade; uint32_t cfg_iocb_cnt; uint32_t cfg_suppress_link_up; + uint32_t cfg_rrq_xri_bitmap_sz; #define LPFC_INITIALIZE_LINK 0 /* do normal init_link mbox */ #define LPFC_DELAY_INIT_LINK 1 /* layered driver hold off */ #define LPFC_DELAY_INIT_LINK_INDEFINITELY 2 /* wait, manual intervention */ @@ -835,6 +849,7 @@ struct lpfc_hba { mempool_t *mbox_mem_pool; mempool_t *nlp_mem_pool; mempool_t *rrq_pool; + mempool_t *active_rrq_pool; struct fc_host_statistics link_stats; enum intr_type_t intr_type; @@ -869,7 +884,6 @@ struct lpfc_hba { atomic_t num_cmd_success; unsigned long last_rsrc_error_time; unsigned long last_ramp_down_time; - unsigned long last_ramp_up_time; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS struct dentry *hba_debugfs_root; atomic_t debugfs_vport_count; @@ -971,6 +985,9 @@ struct lpfc_hba { atomic_t sdev_cnt; uint8_t fips_spec_rev; uint8_t fips_level; + spinlock_t devicelock; /* lock for luns list */ + mempool_t *device_data_mem_pool; + struct list_head luns; }; static inline struct Scsi_Host * diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 3c5625b8b1f..1d7a5c34ee8 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -529,6 +529,27 @@ lpfc_sli4_protocol_show(struct device *dev, struct device_attribute *attr, } /** + * lpfc_oas_supported_show - Return whether or not Optimized Access Storage + * (OAS) is supported. + * @dev: class unused variable. + * @attr: device attribute, not used. + * @buf: on return contains the module description text. + * + * Returns: size of formatted string. + **/ +static ssize_t +lpfc_oas_supported_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; + struct lpfc_hba *phba = vport->phba; + + return snprintf(buf, PAGE_SIZE, "%d\n", + phba->sli4_hba.pc_sli4_params.oas_supported); +} + +/** * lpfc_link_state_store - Transition the link_state on an HBA port * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. @@ -674,9 +695,6 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type) int i; int rc; - if (phba->pport->fc_flag & FC_OFFLINE_MODE) - return 0; - init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, LPFC_EVT_OFFLINE_PREP); @@ -744,14 +762,15 @@ lpfc_selective_reset(struct lpfc_hba *phba) int status = 0; int rc; - if ((!phba->cfg_enable_hba_reset) || - (phba->pport->fc_flag & FC_OFFLINE_MODE)) + if (!phba->cfg_enable_hba_reset) return -EACCES; - status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); + if (!(phba->pport->fc_flag & FC_OFFLINE_MODE)) { + status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); - if (status != 0) - return status; + if (status != 0) + return status; + } init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, @@ -818,7 +837,7 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr, * the readyness after performing a firmware reset. * * Returns: - * zero for success, -EPERM when port does not have privilage to perform the + * zero for success, -EPERM when port does not have privilege to perform the * reset, -EIO when port timeout from recovering from the reset. * * Note: @@ -835,7 +854,7 @@ lpfc_sli4_pdev_status_reg_wait(struct lpfc_hba *phba) lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, &portstat_reg.word0); - /* verify if privilaged for the request operation */ + /* verify if privileged for the request operation */ if (!bf_get(lpfc_sliport_status_rn, &portstat_reg) && !bf_get(lpfc_sliport_status_err, &portstat_reg)) return -EPERM; @@ -900,10 +919,15 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) phba->cfg_sriov_nr_virtfn = 0; } + if (opcode == LPFC_FW_DUMP) + phba->hba_flag |= HBA_FW_DUMP_OP; + status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); - if (status != 0) + if (status != 0) { + phba->hba_flag &= ~HBA_FW_DUMP_OP; return status; + } /* wait for the device to be quiesced before firmware reset */ msleep(100); @@ -927,9 +951,9 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) rc = lpfc_sli4_pdev_status_reg_wait(phba); if (rc == -EPERM) { - /* no privilage for reset */ + /* no privilege for reset */ lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "3150 No privilage to perform the requested " + "3150 No privilege to perform the requested " "access: x%x\n", reg_val); } else if (rc == -EIO) { /* reset failed, there is nothing more we can do */ @@ -1867,8 +1891,10 @@ lpfc_##attr##_set(struct lpfc_vport *vport, uint val) \ { \ if (val >= minval && val <= maxval) {\ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, \ - "3053 lpfc_" #attr " changed from %d to %d\n", \ - vport->cfg_##attr, val); \ + "3053 lpfc_" #attr \ + " changed from %d (x%x) to %d (x%x)\n", \ + vport->cfg_##attr, vport->cfg_##attr, \ + val, val); \ vport->cfg_##attr = val;\ return 0;\ }\ @@ -2041,9 +2067,53 @@ static DEVICE_ATTR(lpfc_dss, S_IRUGO, lpfc_dss_show, NULL); static DEVICE_ATTR(lpfc_sriov_hw_max_virtfn, S_IRUGO, lpfc_sriov_hw_max_virtfn_show, NULL); static DEVICE_ATTR(protocol, S_IRUGO, lpfc_sli4_protocol_show, NULL); +static DEVICE_ATTR(lpfc_xlane_supported, S_IRUGO, lpfc_oas_supported_show, + NULL); static char *lpfc_soft_wwn_key = "C99G71SL8032A"; +#define WWN_SZ 8 +/** + * lpfc_wwn_set - Convert string to the 8 byte WWN value. + * @buf: WWN string. + * @cnt: Length of string. + * @wwn: Array to receive converted wwn value. + * + * Returns: + * -EINVAL if the buffer does not contain a valid wwn + * 0 success + **/ +static size_t +lpfc_wwn_set(const char *buf, size_t cnt, char wwn[]) +{ + unsigned int i, j; + + /* Count may include a LF at end of string */ + if (buf[cnt-1] == '\n') + cnt--; + if ((cnt < 16) || (cnt > 18) || ((cnt == 17) && (*buf++ != 'x')) || + ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) + return -EINVAL; + + memset(wwn, 0, WWN_SZ); + + /* Validate and store the new name */ + for (i = 0, j = 0; i < 16; i++) { + if ((*buf >= 'a') && (*buf <= 'f')) + j = ((j << 4) | ((*buf++ - 'a') + 10)); + else if ((*buf >= 'A') && (*buf <= 'F')) + j = ((j << 4) | ((*buf++ - 'A') + 10)); + else if ((*buf >= '0') && (*buf <= '9')) + j = ((j << 4) | (*buf++ - '0')); + else + return -EINVAL; + if (i % 2) { + wwn[i/2] = j & 0xff; + j = 0; + } + } + return 0; +} /** * lpfc_soft_wwn_enable_store - Allows setting of the wwn if the key is valid * @dev: class device that is converted into a Scsi_host. @@ -2132,9 +2202,9 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr, struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; struct completion online_compl; - int stat1=0, stat2=0; - unsigned int i, j, cnt=count; - u8 wwpn[8]; + int stat1 = 0, stat2 = 0; + unsigned int cnt = count; + u8 wwpn[WWN_SZ]; int rc; if (!phba->cfg_enable_hba_reset) @@ -2149,29 +2219,19 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr, if (buf[cnt-1] == '\n') cnt--; - if (!phba->soft_wwn_enable || (cnt < 16) || (cnt > 18) || - ((cnt == 17) && (*buf++ != 'x')) || - ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) + if (!phba->soft_wwn_enable) return -EINVAL; + /* lock setting wwpn, wwnn down */ phba->soft_wwn_enable = 0; - memset(wwpn, 0, sizeof(wwpn)); - - /* Validate and store the new name */ - for (i=0, j=0; i < 16; i++) { - int value; - - value = hex_to_bin(*buf++); - if (value >= 0) - j = (j << 4) | value; - else - return -EINVAL; - if (i % 2) { - wwpn[i/2] = j & 0xff; - j = 0; - } + rc = lpfc_wwn_set(buf, cnt, wwpn); + if (!rc) { + /* not able to set wwpn, unlock it */ + phba->soft_wwn_enable = 1; + return rc; } + phba->cfg_soft_wwpn = wwn_to_u64(wwpn); fc_host_port_name(shost) = phba->cfg_soft_wwpn; if (phba->cfg_soft_wwnn) @@ -2198,7 +2258,7 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr, "reinit adapter - %d\n", stat2); return (stat1 || stat2) ? -EIO : count; } -static DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\ +static DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR, lpfc_soft_wwpn_show, lpfc_soft_wwpn_store); /** @@ -2235,39 +2295,25 @@ lpfc_soft_wwnn_store(struct device *dev, struct device_attribute *attr, { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; - unsigned int i, j, cnt=count; - u8 wwnn[8]; + unsigned int cnt = count; + u8 wwnn[WWN_SZ]; + int rc; /* count may include a LF at end of string */ if (buf[cnt-1] == '\n') cnt--; - if (!phba->soft_wwn_enable || (cnt < 16) || (cnt > 18) || - ((cnt == 17) && (*buf++ != 'x')) || - ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) + if (!phba->soft_wwn_enable) return -EINVAL; - /* - * Allow wwnn to be set many times, as long as the enable is set. - * However, once the wwpn is set, everything locks. - */ - - memset(wwnn, 0, sizeof(wwnn)); - - /* Validate and store the new name */ - for (i=0, j=0; i < 16; i++) { - int value; - - value = hex_to_bin(*buf++); - if (value >= 0) - j = (j << 4) | value; - else - return -EINVAL; - if (i % 2) { - wwnn[i/2] = j & 0xff; - j = 0; - } + rc = lpfc_wwn_set(buf, cnt, wwnn); + if (!rc) { + /* Allow wwnn to be set many times, as long as the enable + * is set. However, once the wwpn is set, everything locks. + */ + return rc; } + phba->cfg_soft_wwnn = wwn_to_u64(wwnn); dev_printk(KERN_NOTICE, &phba->pcidev->dev, @@ -2276,9 +2322,438 @@ lpfc_soft_wwnn_store(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(lpfc_soft_wwnn, S_IRUGO | S_IWUSR,\ +static DEVICE_ATTR(lpfc_soft_wwnn, S_IRUGO | S_IWUSR, lpfc_soft_wwnn_show, lpfc_soft_wwnn_store); +/** + * lpfc_oas_tgt_show - Return wwpn of target whose luns maybe enabled for + * Optimized Access Storage (OAS) operations. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * + * Returns: + * value of count + **/ +static ssize_t +lpfc_oas_tgt_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + wwn_to_u64(phba->cfg_oas_tgt_wwpn)); +} + +/** + * lpfc_oas_tgt_store - Store wwpn of target whose luns maybe enabled for + * Optimized Access Storage (OAS) operations. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * @count: Size of the data buffer. + * + * Returns: + * -EINVAL count is invalid, invalid wwpn byte invalid + * -EPERM oas is not supported by hba + * value of count on success + **/ +static ssize_t +lpfc_oas_tgt_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + unsigned int cnt = count; + uint8_t wwpn[WWN_SZ]; + int rc; + + if (!phba->cfg_fof) + return -EPERM; + + /* count may include a LF at end of string */ + if (buf[cnt-1] == '\n') + cnt--; + + rc = lpfc_wwn_set(buf, cnt, wwpn); + if (rc) + return rc; + + memcpy(phba->cfg_oas_tgt_wwpn, wwpn, (8 * sizeof(uint8_t))); + memcpy(phba->sli4_hba.oas_next_tgt_wwpn, wwpn, (8 * sizeof(uint8_t))); + if (wwn_to_u64(wwpn) == 0) + phba->cfg_oas_flags |= OAS_FIND_ANY_TARGET; + else + phba->cfg_oas_flags &= ~OAS_FIND_ANY_TARGET; + phba->cfg_oas_flags &= ~OAS_LUN_VALID; + phba->sli4_hba.oas_next_lun = FIND_FIRST_OAS_LUN; + return count; +} +static DEVICE_ATTR(lpfc_xlane_tgt, S_IRUGO | S_IWUSR, + lpfc_oas_tgt_show, lpfc_oas_tgt_store); + +/** + * lpfc_oas_vpt_show - Return wwpn of vport whose targets maybe enabled + * for Optimized Access Storage (OAS) operations. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * + * Returns: + * value of count on success + **/ +static ssize_t +lpfc_oas_vpt_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + wwn_to_u64(phba->cfg_oas_vpt_wwpn)); +} + +/** + * lpfc_oas_vpt_store - Store wwpn of vport whose targets maybe enabled + * for Optimized Access Storage (OAS) operations. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * @count: Size of the data buffer. + * + * Returns: + * -EINVAL count is invalid, invalid wwpn byte invalid + * -EPERM oas is not supported by hba + * value of count on success + **/ +static ssize_t +lpfc_oas_vpt_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + unsigned int cnt = count; + uint8_t wwpn[WWN_SZ]; + int rc; + + if (!phba->cfg_fof) + return -EPERM; + + /* count may include a LF at end of string */ + if (buf[cnt-1] == '\n') + cnt--; + + rc = lpfc_wwn_set(buf, cnt, wwpn); + if (rc) + return rc; + + memcpy(phba->cfg_oas_vpt_wwpn, wwpn, (8 * sizeof(uint8_t))); + memcpy(phba->sli4_hba.oas_next_vpt_wwpn, wwpn, (8 * sizeof(uint8_t))); + if (wwn_to_u64(wwpn) == 0) + phba->cfg_oas_flags |= OAS_FIND_ANY_VPORT; + else + phba->cfg_oas_flags &= ~OAS_FIND_ANY_VPORT; + phba->cfg_oas_flags &= ~OAS_LUN_VALID; + phba->sli4_hba.oas_next_lun = FIND_FIRST_OAS_LUN; + return count; +} +static DEVICE_ATTR(lpfc_xlane_vpt, S_IRUGO | S_IWUSR, + lpfc_oas_vpt_show, lpfc_oas_vpt_store); + +/** + * lpfc_oas_lun_state_show - Return the current state (enabled or disabled) + * of whether luns will be enabled or disabled + * for Optimized Access Storage (OAS) operations. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * + * Returns: + * size of formatted string. + **/ +static ssize_t +lpfc_oas_lun_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + + return snprintf(buf, PAGE_SIZE, "%d\n", phba->cfg_oas_lun_state); +} + +/** + * lpfc_oas_lun_state_store - Store the state (enabled or disabled) + * of whether luns will be enabled or disabled + * for Optimized Access Storage (OAS) operations. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * @count: Size of the data buffer. + * + * Returns: + * -EINVAL count is invalid, invalid wwpn byte invalid + * -EPERM oas is not supported by hba + * value of count on success + **/ +static ssize_t +lpfc_oas_lun_state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + int val = 0; + + if (!phba->cfg_fof) + return -EPERM; + + if (!isdigit(buf[0])) + return -EINVAL; + + if (sscanf(buf, "%i", &val) != 1) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + phba->cfg_oas_lun_state = val; + + return strlen(buf); +} +static DEVICE_ATTR(lpfc_xlane_lun_state, S_IRUGO | S_IWUSR, + lpfc_oas_lun_state_show, lpfc_oas_lun_state_store); + +/** + * lpfc_oas_lun_status_show - Return the status of the Optimized Access + * Storage (OAS) lun returned by the + * lpfc_oas_lun_show function. + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * + * Returns: + * size of formatted string. + **/ +static ssize_t +lpfc_oas_lun_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + + if (!(phba->cfg_oas_flags & OAS_LUN_VALID)) + return -EFAULT; + + return snprintf(buf, PAGE_SIZE, "%d\n", phba->cfg_oas_lun_status); +} +static DEVICE_ATTR(lpfc_xlane_lun_status, S_IRUGO, + lpfc_oas_lun_status_show, NULL); + + +/** + * lpfc_oas_lun_state_set - enable or disable a lun for Optimized Access Storage + * (OAS) operations. + * @phba: lpfc_hba pointer. + * @ndlp: pointer to fcp target node. + * @lun: the fc lun for setting oas state. + * @oas_state: the oas state to be set to the lun. + * + * Returns: + * SUCCESS : 0 + * -EPERM OAS is not enabled or not supported by this port. + * + */ +static size_t +lpfc_oas_lun_state_set(struct lpfc_hba *phba, uint8_t vpt_wwpn[], + uint8_t tgt_wwpn[], uint64_t lun, uint32_t oas_state) +{ + + int rc = 0; + + if (!phba->cfg_fof) + return -EPERM; + + if (oas_state) { + if (!lpfc_enable_oas_lun(phba, (struct lpfc_name *)vpt_wwpn, + (struct lpfc_name *)tgt_wwpn, lun)) + rc = -ENOMEM; + } else { + lpfc_disable_oas_lun(phba, (struct lpfc_name *)vpt_wwpn, + (struct lpfc_name *)tgt_wwpn, lun); + } + return rc; + +} + +/** + * lpfc_oas_lun_get_next - get the next lun that has been enabled for Optimized + * Access Storage (OAS) operations. + * @phba: lpfc_hba pointer. + * @vpt_wwpn: wwpn of the vport associated with the returned lun + * @tgt_wwpn: wwpn of the target associated with the returned lun + * @lun_status: status of the lun returned lun + * + * Returns the first or next lun enabled for OAS operations for the vport/target + * specified. If a lun is found, its vport wwpn, target wwpn and status is + * returned. If the lun is not found, NOT_OAS_ENABLED_LUN is returned. + * + * Return: + * lun that is OAS enabled for the vport/target + * NOT_OAS_ENABLED_LUN when no oas enabled lun found. + */ +static uint64_t +lpfc_oas_lun_get_next(struct lpfc_hba *phba, uint8_t vpt_wwpn[], + uint8_t tgt_wwpn[], uint32_t *lun_status) +{ + uint64_t found_lun; + + if (unlikely(!phba) || !vpt_wwpn || !tgt_wwpn) + return NOT_OAS_ENABLED_LUN; + if (lpfc_find_next_oas_lun(phba, (struct lpfc_name *) + phba->sli4_hba.oas_next_vpt_wwpn, + (struct lpfc_name *) + phba->sli4_hba.oas_next_tgt_wwpn, + &phba->sli4_hba.oas_next_lun, + (struct lpfc_name *)vpt_wwpn, + (struct lpfc_name *)tgt_wwpn, + &found_lun, lun_status)) + return found_lun; + else + return NOT_OAS_ENABLED_LUN; +} + +/** + * lpfc_oas_lun_state_change - enable/disable a lun for OAS operations + * @phba: lpfc_hba pointer. + * @vpt_wwpn: vport wwpn by reference. + * @tgt_wwpn: target wwpn by reference. + * @lun: the fc lun for setting oas state. + * @oas_state: the oas state to be set to the oas_lun. + * + * This routine enables (OAS_LUN_ENABLE) or disables (OAS_LUN_DISABLE) + * a lun for OAS operations. + * + * Return: + * SUCCESS: 0 + * -ENOMEM: failed to enable an lun for OAS operations + * -EPERM: OAS is not enabled + */ +static ssize_t +lpfc_oas_lun_state_change(struct lpfc_hba *phba, uint8_t vpt_wwpn[], + uint8_t tgt_wwpn[], uint64_t lun, + uint32_t oas_state) +{ + + int rc; + + rc = lpfc_oas_lun_state_set(phba, vpt_wwpn, tgt_wwpn, lun, + oas_state); + return rc; +} + +/** + * lpfc_oas_lun_show - Return oas enabled luns from a chosen target + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * + * This routine returns a lun enabled for OAS each time the function + * is called. + * + * Returns: + * SUCCESS: size of formatted string. + * -EFAULT: target or vport wwpn was not set properly. + * -EPERM: oas is not enabled. + **/ +static ssize_t +lpfc_oas_lun_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + + uint64_t oas_lun; + int len = 0; + + if (!phba->cfg_fof) + return -EPERM; + + if (wwn_to_u64(phba->cfg_oas_vpt_wwpn) == 0) + if (!(phba->cfg_oas_flags & OAS_FIND_ANY_VPORT)) + return -EFAULT; + + if (wwn_to_u64(phba->cfg_oas_tgt_wwpn) == 0) + if (!(phba->cfg_oas_flags & OAS_FIND_ANY_TARGET)) + return -EFAULT; + + oas_lun = lpfc_oas_lun_get_next(phba, phba->cfg_oas_vpt_wwpn, + phba->cfg_oas_tgt_wwpn, + &phba->cfg_oas_lun_status); + if (oas_lun != NOT_OAS_ENABLED_LUN) + phba->cfg_oas_flags |= OAS_LUN_VALID; + + len += snprintf(buf + len, PAGE_SIZE-len, "0x%llx", oas_lun); + + return len; +} + +/** + * lpfc_oas_lun_store - Sets the OAS state for lun + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: buffer for passing information. + * + * This function sets the OAS state for lun. Before this function is called, + * the vport wwpn, target wwpn, and oas state need to be set. + * + * Returns: + * SUCCESS: size of formatted string. + * -EFAULT: target or vport wwpn was not set properly. + * -EPERM: oas is not enabled. + * size of formatted string. + **/ +static ssize_t +lpfc_oas_lun_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + uint64_t scsi_lun; + ssize_t rc; + + if (!phba->cfg_fof) + return -EPERM; + + if (wwn_to_u64(phba->cfg_oas_vpt_wwpn) == 0) + return -EFAULT; + + if (wwn_to_u64(phba->cfg_oas_tgt_wwpn) == 0) + return -EFAULT; + + if (!isdigit(buf[0])) + return -EINVAL; + + if (sscanf(buf, "0x%llx", &scsi_lun) != 1) + return -EINVAL; + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3372 Try to set vport 0x%llx target 0x%llx lun:%lld " + "with oas set to %d\n", + wwn_to_u64(phba->cfg_oas_vpt_wwpn), + wwn_to_u64(phba->cfg_oas_tgt_wwpn), scsi_lun, + phba->cfg_oas_lun_state); + + rc = lpfc_oas_lun_state_change(phba, phba->cfg_oas_vpt_wwpn, + phba->cfg_oas_tgt_wwpn, scsi_lun, + phba->cfg_oas_lun_state); + + if (rc) + return rc; + + return count; +} +static DEVICE_ATTR(lpfc_xlane_lun, S_IRUGO | S_IWUSR, + lpfc_oas_lun_show, lpfc_oas_lun_store); static int lpfc_poll = 0; module_param(lpfc_poll, int, S_IRUGO); @@ -2591,9 +3066,12 @@ LPFC_VPORT_ATTR_R(enable_da_id, 1, 0, 1, /* # lun_queue_depth: This parameter is used to limit the number of outstanding -# commands per FCP LUN. Value range is [1,128]. Default value is 30. +# commands per FCP LUN. Value range is [1,512]. Default value is 30. +# If this parameter value is greater than 1/8th the maximum number of exchanges +# supported by the HBA port, then the lun queue depth will be reduced to +# 1/8th the maximum number of exchanges. */ -LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 128, +LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 512, "Max number of FCP commands we can queue to a specific LUN"); /* @@ -2601,7 +3079,7 @@ LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 128, # commands per target port. Value range is [10,65535]. Default value is 65535. */ LPFC_VPORT_ATTR_R(tgt_queue_depth, 65535, 10, 65535, - "Max number of FCP commands we can queue to a specific target port"); + "Max number of FCP commands we can queue to a specific target port"); /* # hba_queue_depth: This parameter is used to limit the number of outstanding @@ -3815,7 +4293,7 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; struct lpfc_hba *phba = vport->phba; struct lpfc_vector_map_info *cpup; - int idx, len = 0; + int len = 0; if ((phba->sli_rev != LPFC_SLI_REV4) || (phba->intr_type != MSIX)) @@ -3843,23 +4321,39 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, break; } - cpup = phba->sli4_hba.cpu_map; - for (idx = 0; idx < phba->sli4_hba.num_present_cpu; idx++) { + while (phba->sli4_hba.curr_disp_cpu < phba->sli4_hba.num_present_cpu) { + cpup = &phba->sli4_hba.cpu_map[phba->sli4_hba.curr_disp_cpu]; + + /* margin should fit in this and the truncated message */ if (cpup->irq == LPFC_VECTOR_MAP_EMPTY) len += snprintf(buf + len, PAGE_SIZE-len, "CPU %02d io_chan %02d " "physid %d coreid %d\n", - idx, cpup->channel_id, cpup->phys_id, + phba->sli4_hba.curr_disp_cpu, + cpup->channel_id, cpup->phys_id, cpup->core_id); else len += snprintf(buf + len, PAGE_SIZE-len, "CPU %02d io_chan %02d " "physid %d coreid %d IRQ %d\n", - idx, cpup->channel_id, cpup->phys_id, + phba->sli4_hba.curr_disp_cpu, + cpup->channel_id, cpup->phys_id, cpup->core_id, cpup->irq); - cpup++; + phba->sli4_hba.curr_disp_cpu++; + + /* display max number of CPUs keeping some margin */ + if (phba->sli4_hba.curr_disp_cpu < + phba->sli4_hba.num_present_cpu && + (len >= (PAGE_SIZE - 64))) { + len += snprintf(buf + len, PAGE_SIZE-len, "more...\n"); + break; + } } + + if (phba->sli4_hba.curr_disp_cpu == phba->sli4_hba.num_present_cpu) + phba->sli4_hba.curr_disp_cpu = 0; + return len; } @@ -3949,6 +4443,14 @@ LPFC_VPORT_ATTR_RW(use_adisc, 0, 0, 1, "Use ADISC on rediscovery to authenticate FCP devices"); /* +# lpfc_first_burst_size: First burst size to use on the NPorts +# that support first burst. +# Value range is [0,65536]. Default value is 0. +*/ +LPFC_VPORT_ATTR_RW(first_burst_size, 0, 0, 65536, + "First burst size for Targets that support first burst"); + +/* # lpfc_max_scsicmpl_time: Use scsi command completion time to control I/O queue # depth. Default value is 0. When the value of this parameter is zero the # SCSI command completion time is not used for controlling I/O queue depth. When @@ -4002,8 +4504,11 @@ LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support"); # For [0], FCP commands are issued to Work Queues ina round robin fashion. # For [1], FCP commands are issued to a Work Queue associated with the # current CPU. +# It would be set to 1 by the driver if it's able to set up cpu affinity +# for FCP I/Os through Work Queue associated with the current CPU. Otherwise, +# roundrobin scheduling of FCP I/Os through WQs will be used. */ -LPFC_ATTR_RW(fcp_io_sched, 0, 0, 1, "Determine scheduling algrithmn for " +LPFC_ATTR_RW(fcp_io_sched, 0, 0, 1, "Determine scheduling algorithm for " "issuing commands [0] - Round Robin, [1] - Current CPU"); /* @@ -4070,11 +4575,28 @@ LPFC_VPORT_ATTR(discovery_threads, 32, 1, 64, "Maximum number of ELS commands " "during discovery"); /* -# lpfc_max_luns: maximum allowed LUN. +# lpfc_max_luns: maximum allowed LUN ID. This is the highest LUN ID that +# will be scanned by the SCSI midlayer when sequential scanning is +# used; and is also the highest LUN ID allowed when the SCSI midlayer +# parses REPORT_LUN responses. The lpfc driver has no LUN count or +# LUN ID limit, but the SCSI midlayer requires this field for the uses +# above. The lpfc driver limits the default value to 255 for two reasons. +# As it bounds the sequential scan loop, scanning for thousands of luns +# on a target can take minutes of wall clock time. Additionally, +# there are FC targets, such as JBODs, that only recognize 8-bits of +# LUN ID. When they receive a value greater than 8 bits, they chop off +# the high order bits. In other words, they see LUN IDs 0, 256, 512, +# and so on all as LUN ID 0. This causes the linux kernel, which sees +# valid responses at each of the LUN IDs, to believe there are multiple +# devices present, when in fact, there is only 1. +# A customer that is aware of their target behaviors, and the results as +# indicated above, is welcome to increase the lpfc_max_luns value. +# As mentioned, this value is not used by the lpfc driver, only the +# SCSI midlayer. # Value range is [0,65535]. Default value is 255. # NOTE: The SCSI layer might probe all allowed LUN on some old targets. */ -LPFC_VPORT_ATTR_R(max_luns, 255, 0, 65535, "Maximum allowed LUN"); +LPFC_VPORT_ATTR_R(max_luns, 255, 0, 65535, "Maximum allowed LUN ID"); /* # lpfc_poll_tmo: .Milliseconds driver will wait between polling FCP ring. @@ -4084,6 +4606,12 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255, "Milliseconds driver will wait between polling FCP ring"); /* +# lpfc_task_mgmt_tmo: Maximum time to wait for task management commands +# to complete in seconds. Value range is [5,180], default value is 60. +*/ +LPFC_ATTR_RW(task_mgmt_tmo, 60, 5, 180, + "Maximum time to wait for task management commands to complete"); +/* # lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that # support this feature # 0 = MSI disabled @@ -4095,25 +4623,6 @@ LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or " "MSI-X (2), if possible"); /* -# lpfc_fcp_wq_count: Set the number of fast-path FCP work queues -# This parameter is ignored and will eventually be depricated -# -# Value range is [1,7]. Default value is 4. -*/ -LPFC_ATTR_R(fcp_wq_count, LPFC_FCP_IO_CHAN_DEF, LPFC_FCP_IO_CHAN_MIN, - LPFC_FCP_IO_CHAN_MAX, - "Set the number of fast-path FCP work queues, if possible"); - -/* -# lpfc_fcp_eq_count: Set the number of FCP EQ/CQ/WQ IO channels -# -# Value range is [1,7]. Default value is 4. -*/ -LPFC_ATTR_R(fcp_eq_count, LPFC_FCP_IO_CHAN_DEF, LPFC_FCP_IO_CHAN_MIN, - LPFC_FCP_IO_CHAN_MAX, - "Set the number of fast-path FCP event queues, if possible"); - -/* # lpfc_fcp_io_channel: Set the number of FCP EQ/CQ/WQ IO channels # # Value range is [1,7]. Default value is 4. @@ -4139,6 +4648,21 @@ LPFC_ATTR_R(enable_hba_reset, 1, 0, 1, "Enable HBA resets from the driver."); LPFC_ATTR_R(enable_hba_heartbeat, 0, 0, 1, "Enable HBA Heartbeat."); /* +# lpfc_EnableXLane: Enable Express Lane Feature +# 0x0 Express Lane Feature disabled +# 0x1 Express Lane Feature enabled +# Value range is [0,1]. Default value is 0. +*/ +LPFC_ATTR_R(EnableXLane, 0, 0, 1, "Enable Express Lane Feature."); + +/* +# lpfc_XLanePriority: Define CS_CTL priority for Express Lane Feature +# 0x0 - 0x7f = CS_CTL field in FC header (high 7 bits) +# Value range is [0x0,0x7f]. Default value is 0 +*/ +LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature."); + +/* # lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF) # 0 = BlockGuard disabled (default) # 1 = BlockGuard enabled @@ -4259,6 +4783,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_devloss_tmo, &dev_attr_lpfc_fcp_class, &dev_attr_lpfc_use_adisc, + &dev_attr_lpfc_first_burst_size, &dev_attr_lpfc_ack0, &dev_attr_lpfc_topology, &dev_attr_lpfc_scan_down, @@ -4287,11 +4812,10 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_issue_reset, &dev_attr_lpfc_poll, &dev_attr_lpfc_poll_tmo, + &dev_attr_lpfc_task_mgmt_tmo, &dev_attr_lpfc_use_msi, &dev_attr_lpfc_fcp_imax, &dev_attr_lpfc_fcp_cpu_map, - &dev_attr_lpfc_fcp_wq_count, - &dev_attr_lpfc_fcp_eq_count, &dev_attr_lpfc_fcp_io_channel, &dev_attr_lpfc_enable_bg, &dev_attr_lpfc_soft_wwnn, @@ -4299,6 +4823,13 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_soft_wwn_enable, &dev_attr_lpfc_enable_hba_reset, &dev_attr_lpfc_enable_hba_heartbeat, + &dev_attr_lpfc_EnableXLane, + &dev_attr_lpfc_XLanePriority, + &dev_attr_lpfc_xlane_lun, + &dev_attr_lpfc_xlane_tgt, + &dev_attr_lpfc_xlane_vpt, + &dev_attr_lpfc_xlane_lun_state, + &dev_attr_lpfc_xlane_lun_status, &dev_attr_lpfc_sg_seg_cnt, &dev_attr_lpfc_max_scsicmpl_time, &dev_attr_lpfc_stat_data_ctrl, @@ -4317,6 +4848,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_dss, &dev_attr_lpfc_sriov_hw_max_virtfn, &dev_attr_protocol, + &dev_attr_lpfc_xlane_supported, NULL, }; @@ -4335,6 +4867,7 @@ struct device_attribute *lpfc_vport_attrs[] = { &dev_attr_lpfc_restrict_login, &dev_attr_lpfc_fcp_class, &dev_attr_lpfc_use_adisc, + &dev_attr_lpfc_first_burst_size, &dev_attr_lpfc_fdmi_on, &dev_attr_lpfc_max_luns, &dev_attr_nport_evt_cnt, @@ -5267,22 +5800,30 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_topology_init(phba, lpfc_topology); lpfc_link_speed_init(phba, lpfc_link_speed); lpfc_poll_tmo_init(phba, lpfc_poll_tmo); + lpfc_task_mgmt_tmo_init(phba, lpfc_task_mgmt_tmo); lpfc_enable_npiv_init(phba, lpfc_enable_npiv); lpfc_fcf_failover_policy_init(phba, lpfc_fcf_failover_policy); lpfc_enable_rrq_init(phba, lpfc_enable_rrq); lpfc_use_msi_init(phba, lpfc_use_msi); lpfc_fcp_imax_init(phba, lpfc_fcp_imax); lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map); - lpfc_fcp_wq_count_init(phba, lpfc_fcp_wq_count); - lpfc_fcp_eq_count_init(phba, lpfc_fcp_eq_count); lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel); lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset); lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat); + lpfc_EnableXLane_init(phba, lpfc_EnableXLane); + if (phba->sli_rev != LPFC_SLI_REV4) + phba->cfg_EnableXLane = 0; + lpfc_XLanePriority_init(phba, lpfc_XLanePriority); + memset(phba->cfg_oas_tgt_wwpn, 0, (8 * sizeof(uint8_t))); + memset(phba->cfg_oas_vpt_wwpn, 0, (8 * sizeof(uint8_t))); + phba->cfg_oas_lun_state = 0; + phba->cfg_oas_lun_status = 0; + phba->cfg_oas_flags = 0; lpfc_enable_bg_init(phba, lpfc_enable_bg); if (phba->sli_rev == LPFC_SLI_REV4) phba->cfg_poll = 0; else - phba->cfg_poll = lpfc_poll; + phba->cfg_poll = lpfc_poll; phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt); @@ -5314,6 +5855,7 @@ lpfc_get_vport_cfgparam(struct lpfc_vport *vport) lpfc_restrict_login_init(vport, lpfc_restrict_login); lpfc_fcp_class_init(vport, lpfc_fcp_class); lpfc_use_adisc_init(vport, lpfc_use_adisc); + lpfc_first_burst_size_init(vport, lpfc_first_burst_size); lpfc_max_scsicmpl_time_init(vport, lpfc_max_scsicmpl_time); lpfc_fdmi_on_init(vport, lpfc_fdmi_on); lpfc_discovery_threads_init(vport, lpfc_discovery_threads); diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 094be2cad65..5b5c825d957 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009-2012 Emulex. All rights reserved. * + * Copyright (C) 2009-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -317,6 +317,11 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba, } spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + /* Close the timeout handler abort window */ + spin_lock_irqsave(&phba->hbalock, flags); + cmdiocbq->iocb_flag &= ~LPFC_IO_CMD_OUTSTANDING; + spin_unlock_irqrestore(&phba->hbalock, flags); + iocb = &dd_data->context_un.iocb; ndlp = iocb->ndlp; rmp = iocb->rmp; @@ -387,6 +392,7 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) int request_nseg; int reply_nseg; struct bsg_job_data *dd_data; + unsigned long flags; uint32_t creg_val; int rc = 0; int iocb_stat; @@ -501,14 +507,24 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) } iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); - if (iocb_stat == IOCB_SUCCESS) + + if (iocb_stat == IOCB_SUCCESS) { + spin_lock_irqsave(&phba->hbalock, flags); + /* make sure the I/O had not been completed yet */ + if (cmdiocbq->iocb_flag & LPFC_IO_LIBDFC) { + /* open up abort window to timeout handler */ + cmdiocbq->iocb_flag |= LPFC_IO_CMD_OUTSTANDING; + } + spin_unlock_irqrestore(&phba->hbalock, flags); return 0; /* done for now */ - else if (iocb_stat == IOCB_BUSY) + } else if (iocb_stat == IOCB_BUSY) { rc = -EAGAIN; - else + } else { rc = -EIO; + } /* iocb failed so cleanup */ + job->dd_data = NULL; free_rmp: lpfc_free_bsg_buffers(phba, rmp); @@ -577,6 +593,11 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba, } spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + /* Close the timeout handler abort window */ + spin_lock_irqsave(&phba->hbalock, flags); + cmdiocbq->iocb_flag &= ~LPFC_IO_CMD_OUTSTANDING; + spin_unlock_irqrestore(&phba->hbalock, flags); + rsp = &rspiocbq->iocb; pcmd = (struct lpfc_dmabuf *)cmdiocbq->context2; prsp = (struct lpfc_dmabuf *)pcmd->list.next; @@ -639,6 +660,7 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) struct lpfc_iocbq *cmdiocbq; uint16_t rpi = 0; struct bsg_job_data *dd_data; + unsigned long flags; uint32_t creg_val; int rc = 0; @@ -721,15 +743,25 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); - if (rc == IOCB_SUCCESS) + if (rc == IOCB_SUCCESS) { + spin_lock_irqsave(&phba->hbalock, flags); + /* make sure the I/O had not been completed/released */ + if (cmdiocbq->iocb_flag & LPFC_IO_LIBDFC) { + /* open up abort window to timeout handler */ + cmdiocbq->iocb_flag |= LPFC_IO_CMD_OUTSTANDING; + } + spin_unlock_irqrestore(&phba->hbalock, flags); return 0; /* done for now */ - else if (rc == IOCB_BUSY) + } else if (rc == IOCB_BUSY) { rc = -EAGAIN; - else + } else { rc = -EIO; + } -linkdown_err: + /* iocb failed so cleanup */ + job->dd_data = NULL; +linkdown_err: cmdiocbq->context1 = ndlp; lpfc_els_free_iocb(phba, cmdiocbq); @@ -1249,7 +1281,7 @@ lpfc_bsg_hba_get_event(struct fc_bsg_job *job) struct lpfc_hba *phba = vport->phba; struct get_ct_event *event_req; struct get_ct_event_reply *event_reply; - struct lpfc_bsg_event *evt; + struct lpfc_bsg_event *evt, *evt_next; struct event_data *evt_dat = NULL; unsigned long flags; uint32_t rc = 0; @@ -1269,7 +1301,7 @@ lpfc_bsg_hba_get_event(struct fc_bsg_job *job) event_reply = (struct get_ct_event_reply *) job->reply->reply_data.vendor_reply.vendor_rsp; spin_lock_irqsave(&phba->ct_ev_lock, flags); - list_for_each_entry(evt, &phba->ct_ev_waiters, node) { + list_for_each_entry_safe(evt, evt_next, &phba->ct_ev_waiters, node) { if (evt->reg_id == event_req->ev_reg_id) { if (list_empty(&evt->events_to_get)) break; @@ -1370,6 +1402,11 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, } spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + /* Close the timeout handler abort window */ + spin_lock_irqsave(&phba->hbalock, flags); + cmdiocbq->iocb_flag &= ~LPFC_IO_CMD_OUTSTANDING; + spin_unlock_irqrestore(&phba->hbalock, flags); + ndlp = dd_data->context_un.iocb.ndlp; cmp = cmdiocbq->context2; bmp = cmdiocbq->context3; @@ -1433,6 +1470,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, int rc = 0; struct lpfc_nodelist *ndlp = NULL; struct bsg_job_data *dd_data; + unsigned long flags; uint32_t creg_val; /* allocate our bsg tracking structure */ @@ -1542,8 +1580,19 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); - if (rc == IOCB_SUCCESS) + if (rc == IOCB_SUCCESS) { + spin_lock_irqsave(&phba->hbalock, flags); + /* make sure the I/O had not been completed/released */ + if (ctiocb->iocb_flag & LPFC_IO_LIBDFC) { + /* open up abort window to timeout handler */ + ctiocb->iocb_flag |= LPFC_IO_CMD_OUTSTANDING; + } + spin_unlock_irqrestore(&phba->hbalock, flags); return 0; /* done for now */ + } + + /* iocb failed so cleanup */ + job->dd_data = NULL; issue_ct_rsp_exit: lpfc_sli_release_iocbq(phba, ctiocb); @@ -2498,7 +2547,7 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi, struct lpfc_sli_ct_request *ctreq = NULL; int ret_val = 0; int time_left; - int iocb_stat = 0; + int iocb_stat = IOCB_SUCCESS; unsigned long flags; *txxri = 0; @@ -2574,12 +2623,13 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi, cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; cmdiocbq->vport = phba->pport; + cmdiocbq->iocb_cmpl = NULL; iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq, (phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT); - if (iocb_stat) { + if ((iocb_stat != IOCB_SUCCESS) || (rsp->ulpStatus != IOSTAT_SUCCESS)) { ret_val = -EIO; goto err_get_xri_exit; } @@ -2627,7 +2677,7 @@ err_get_xri_exit: * @phba: Pointer to HBA context object * * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and. - * retruns the pointer to the buffer. + * returns the pointer to the buffer. **/ static struct lpfc_dmabuf * lpfc_bsg_dma_page_alloc(struct lpfc_hba *phba) @@ -2963,7 +3013,7 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job) uint8_t *ptr = NULL, *rx_databuf = NULL; int rc = 0; int time_left; - int iocb_stat; + int iocb_stat = IOCB_SUCCESS; unsigned long flags; void *dataout = NULL; uint32_t total_mem; @@ -3149,12 +3199,14 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job) } cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; cmdiocbq->vport = phba->pport; + cmdiocbq->iocb_cmpl = NULL; iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq, (phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT); - if ((iocb_stat != IOCB_SUCCESS) || ((phba->sli_rev < LPFC_SLI_REV4) && - (rsp->ulpStatus != IOCB_SUCCESS))) { + if ((iocb_stat != IOCB_SUCCESS) || + ((phba->sli_rev < LPFC_SLI_REV4) && + (rsp->ulpStatus != IOSTAT_SUCCESS))) { lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "3126 Failed loopback test issue iocb: " "iocb_stat:x%x\n", iocb_stat); @@ -3209,7 +3261,7 @@ err_loopback_test_exit: lpfc_bsg_event_unref(evt); /* delete */ spin_unlock_irqrestore(&phba->ct_ev_lock, flags); - if (cmdiocbq != NULL) + if ((cmdiocbq != NULL) && (iocb_stat != IOCB_TIMEDOUT)) lpfc_sli_release_iocbq(phba, cmdiocbq); if (rspiocbq != NULL) @@ -3392,6 +3444,7 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, case MBX_DOWN_LOAD: case MBX_UPDATE_CFG: case MBX_KILL_BOARD: + case MBX_READ_TOPOLOGY: case MBX_LOAD_AREA: case MBX_LOAD_EXP_ROM: case MBX_BEACON: @@ -3422,7 +3475,6 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, } break; case MBX_READ_SPARM64: - case MBX_READ_TOPOLOGY: case MBX_REG_LOGIN: case MBX_REG_LOGIN64: case MBX_CONFIG_PORT: @@ -4101,6 +4153,7 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, if (subsys == SLI_CONFIG_SUBSYS_FCOE) { switch (opcode) { case FCOE_OPCODE_READ_FCF: + case FCOE_OPCODE_GET_DPORT_RESULTS: lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2957 Handled SLI_CONFIG " "subsys_fcoe, opcode:x%x\n", @@ -4109,6 +4162,8 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, nemb_mse, dmabuf); break; case FCOE_OPCODE_ADD_FCF: + case FCOE_OPCODE_SET_DPORT_MODE: + case LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE: lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2958 Handled SLI_CONFIG " "subsys_fcoe, opcode:x%x\n", @@ -5282,9 +5337,15 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) * remove it from the txq queue and call cancel iocbs. * Otherwise, call abort iotag */ - cmdiocb = dd_data->context_un.iocb.cmdiocbq; - spin_lock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + spin_lock_irqsave(&phba->hbalock, flags); + /* make sure the I/O abort window is still open */ + if (!(cmdiocb->iocb_flag & LPFC_IO_CMD_OUTSTANDING)) { + spin_unlock_irqrestore(&phba->hbalock, flags); + return -EAGAIN; + } list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq, list) { if (check_iocb == cmdiocb) { @@ -5294,8 +5355,7 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) } if (list_empty(&completions)) lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); - spin_unlock_irq(&phba->hbalock); - spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + spin_unlock_irqrestore(&phba->hbalock, flags); if (!list_empty(&completions)) { lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, @@ -5319,9 +5379,10 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) * remove it from the txq queue and call cancel iocbs. * Otherwise, call abort iotag. */ - cmdiocb = dd_data->context_un.menlo.cmdiocbq; - spin_lock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + spin_lock_irqsave(&phba->hbalock, flags); list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq, list) { if (check_iocb == cmdiocb) { @@ -5331,8 +5392,7 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) } if (list_empty(&completions)) lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); - spin_unlock_irq(&phba->hbalock); - spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + spin_unlock_irqrestore(&phba->hbalock, flags); if (!list_empty(&completions)) { lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h index 67f7d0a160d..928ef609f36 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.h +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2010-2012 Emulex. All rights reserved. * + * Copyright (C) 2010-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -231,6 +231,8 @@ struct lpfc_sli_config_emb0_subsys { #define SLI_CONFIG_SUBSYS_FCOE 0x0C #define FCOE_OPCODE_READ_FCF 0x08 #define FCOE_OPCODE_ADD_FCF 0x09 +#define FCOE_OPCODE_SET_DPORT_MODE 0x27 +#define FCOE_OPCODE_GET_DPORT_RESULTS 0x28 }; struct lpfc_sli_config_emb1_subsys { diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index d41456e5f81..db5604f01a1 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2011 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -187,6 +187,11 @@ void lpfc_offline_prep(struct lpfc_hba *, int); void lpfc_offline(struct lpfc_hba *); void lpfc_reset_hba(struct lpfc_hba *); +int lpfc_fof_queue_create(struct lpfc_hba *); +int lpfc_fof_queue_setup(struct lpfc_hba *); +int lpfc_fof_queue_destroy(struct lpfc_hba *); +irqreturn_t lpfc_sli4_fof_intr_handler(int, void *); + int lpfc_sli_setup(struct lpfc_hba *); int lpfc_sli_queue_setup(struct lpfc_hba *); @@ -242,6 +247,7 @@ int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *, uint16_t); void lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *); int lpfc_mem_alloc(struct lpfc_hba *, int align); +int lpfc_mem_alloc_active_rrq_pool_s4(struct lpfc_hba *); void lpfc_mem_free(struct lpfc_hba *); void lpfc_mem_free_all(struct lpfc_hba *); void lpfc_stop_vport_timers(struct lpfc_vport *); @@ -283,6 +289,7 @@ int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t, void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t); void lpfc_sli_bemem_bcopy(void *, void *, uint32_t); void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *); +void lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba); void lpfc_sli_hba_iocb_abort(struct lpfc_hba *); void lpfc_sli_flush_fcp_rings(struct lpfc_hba *); int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *, @@ -304,6 +311,9 @@ int lpfc_sli_issue_abort_iotag(struct lpfc_hba *, struct lpfc_sli_ring *, int lpfc_sli_sum_iocb(struct lpfc_vport *, uint16_t, uint64_t, lpfc_ctx_cmd); int lpfc_sli_abort_iocb(struct lpfc_vport *, struct lpfc_sli_ring *, uint16_t, uint64_t, lpfc_ctx_cmd); +int +lpfc_sli_abort_taskmgmt(struct lpfc_vport *, struct lpfc_sli_ring *, + uint16_t, uint64_t, lpfc_ctx_cmd); void lpfc_mbox_timeout(unsigned long); void lpfc_mbox_timeout_handler(struct lpfc_hba *); @@ -399,7 +409,6 @@ void lpfc_fabric_block_timeout(unsigned long); void lpfc_unblock_fabric_iocbs(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 @@ -471,3 +480,20 @@ void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *); uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *); int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t); void lpfc_sli4_offline_eratt(struct lpfc_hba *); + +struct lpfc_device_data *lpfc_create_device_data(struct lpfc_hba *, + struct lpfc_name *, + struct lpfc_name *, + uint64_t, bool); +void lpfc_delete_device_data(struct lpfc_hba *, struct lpfc_device_data*); +struct lpfc_device_data *__lpfc_get_device_data(struct lpfc_hba *, + struct list_head *list, + struct lpfc_name *, + struct lpfc_name *, uint64_t); +bool lpfc_enable_oas_lun(struct lpfc_hba *, struct lpfc_name *, + struct lpfc_name *, uint64_t); +bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *, + struct lpfc_name *, uint64_t); +bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *, + struct lpfc_name *, uint64_t *, struct lpfc_name *, + struct lpfc_name *, uint64_t *, uint32_t *); diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index ae1a07c57ca..da61d8dc044 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2010 Emulex. All rights reserved. * + * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -280,7 +280,7 @@ lpfc_ct_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *ctiocb) buf_ptr = (struct lpfc_dmabuf *) ctiocb->context3; lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); kfree(buf_ptr); - ctiocb->context1 = NULL; + ctiocb->context3 = NULL; } lpfc_sli_release_iocbq(phba, ctiocb); return 0; @@ -895,7 +895,7 @@ lpfc_cmpl_ct(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (irsp->ulpStatus) { lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, - "0268 NS cmd %x Error (%d %d)\n", + "0268 NS cmd x%x Error (x%x x%x)\n", cmdcode, irsp->ulpStatus, irsp->un.ulpWord[4]); if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index f63f5ff7f27..b0aedce3f54 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2007-2012 Emulex. All rights reserved. * + * Copyright (C) 2007-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -1165,22 +1165,8 @@ out: static loff_t lpfc_debugfs_lseek(struct file *file, loff_t off, int whence) { - struct lpfc_debug *debug; - loff_t pos = -1; - - debug = file->private_data; - - switch (whence) { - case 0: - pos = off; - break; - case 1: - pos = file->f_pos + off; - break; - case 2: - pos = debug->len - off; - } - return (pos < 0 || pos > debug->len) ? -EINVAL : (file->f_pos = pos); + struct lpfc_debug *debug = file->private_data; + return fixed_size_llseek(file, off, whence, debug->len); } /** @@ -2294,6 +2280,104 @@ proc_cq: } } + if (phba->cfg_fof) { + /* FOF EQ */ + qp = phba->sli4_hba.fof_eq; + if (!qp) + goto out; + + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "\nFOF EQ info: " + "EQ-STAT[max:x%x noE:x%x " + "bs:x%x proc:x%llx]\n", + qp->q_cnt_1, qp->q_cnt_2, + qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); + + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "EQID[%02d], " + "QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]", + qp->queue_id, + qp->entry_count, + qp->entry_size, + qp->host_index, + qp->hba_index); + + /* Reset max counter */ + qp->EQ_max_eqe = 0; + + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); + if (len >= max_cnt) + goto too_big; + } + + if (phba->cfg_fof) { + + /* OAS CQ */ + qp = phba->sli4_hba.oas_cq; + if (qp) { + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "\tOAS CQ info: "); + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "AssocEQID[%02d]: " + "CQ STAT[max:x%x relw:x%x " + "xabt:x%x wq:x%llx]\n", + qp->assoc_qid, + qp->q_cnt_1, qp->q_cnt_2, + qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "\tCQID[%02d], " + "QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]", + qp->queue_id, qp->entry_count, + qp->entry_size, qp->host_index, + qp->hba_index); + + /* Reset max counter */ + qp->CQ_max_cqe = 0; + + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); + if (len >= max_cnt) + goto too_big; + } + + /* OAS WQ */ + qp = phba->sli4_hba.oas_wq; + if (qp) { + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "\t\tOAS WQ info: "); + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "AssocCQID[%02d]: " + "WQ-STAT[oflow:x%x posted:x%llx]\n", + qp->assoc_qid, + qp->q_cnt_1, (unsigned long long)qp->q_cnt_4); + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, + "\t\tWQID[%02d], " + "QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]", + qp->queue_id, + qp->entry_count, + qp->entry_size, + qp->host_index, + qp->hba_index); + + len += snprintf(pbuffer+len, + LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); + if (len >= max_cnt) + goto too_big; + } + } +out: spin_unlock_irq(&phba->hbalock); return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); @@ -3941,6 +4025,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) struct lpfc_hba *phba = vport->phba; char name[64]; uint32_t num, i; + bool pport_setup = false; if (!lpfc_debugfs_enable) return; @@ -3961,6 +4046,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) /* Setup funcX directory for specific HBA PCI function */ snprintf(name, sizeof(name), "fn%d", phba->brd_no); if (!phba->hba_debugfs_root) { + pport_setup = true; phba->hba_debugfs_root = debugfs_create_dir(name, lpfc_debugfs_root); if (!phba->hba_debugfs_root) { @@ -4015,7 +4101,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) goto debug_failed; } } else - phba->debug_dumpHBASlim = NULL; + phba->debug_dumpHostSlim = NULL; /* Setup dumpData */ snprintf(name, sizeof(name), "dumpData"); @@ -4253,6 +4339,14 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) } /* + * The following section is for additional directories/files for the + * physical port. + */ + + if (!pport_setup) + goto debug_failed; + + /* * iDiag debugfs root entry points for SLI4 device only */ if (phba->sli_rev < LPFC_SLI_REV4) diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index af49fb03dbb..1a6fe524940 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2008 Emulex. All rights reserved. * + * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -116,7 +116,7 @@ struct lpfc_nodelist { atomic_t cmd_pending; uint32_t cmd_qdepth; unsigned long last_change_time; - struct lpfc_node_rrqs active_rrqs; + unsigned long *active_rrqs_xri_bitmap; struct lpfc_scsicmd_bkt *lat_data; /* Latency data */ }; struct lpfc_node_rrq { @@ -154,6 +154,7 @@ struct lpfc_node_rrq { #define NLP_NODEV_REMOVE 0x08000000 /* Defer removal till discovery ends */ #define NLP_TARGET_REMOVE 0x10000000 /* Target remove in process */ #define NLP_SC_REQ 0x20000000 /* Target requires authentication */ +#define NLP_FIRSTBURST 0x40000000 /* Target supports FirstBurst */ #define NLP_RPI_REGISTERED 0x80000000 /* nlp_rpi is valid */ /* ndlp usage management macros */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 3cae0a92e8b..7a5d81a65be 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -1516,7 +1516,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, uint32_t rc, keepDID = 0; int put_node; int put_rport; - struct lpfc_node_rrqs rrq; + unsigned long *active_rrqs_xri_bitmap = NULL; /* Fabric nodes can have the same WWPN so we don't bother searching * by WWPN. Just return the ndlp that was given to us. @@ -1534,7 +1534,13 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, if (new_ndlp == ndlp && NLP_CHK_NODE_ACT(new_ndlp)) return ndlp; - memset(&rrq.xri_bitmap, 0, sizeof(new_ndlp->active_rrqs.xri_bitmap)); + if (phba->sli_rev == LPFC_SLI_REV4) { + active_rrqs_xri_bitmap = mempool_alloc(phba->active_rrq_pool, + GFP_KERNEL); + if (active_rrqs_xri_bitmap) + memset(active_rrqs_xri_bitmap, 0, + phba->cfg_rrq_xri_bitmap_sz); + } lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "3178 PLOGI confirm: ndlp %p x%x: new_ndlp %p\n", @@ -1543,41 +1549,58 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, if (!new_ndlp) { rc = memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)); - if (!rc) + if (!rc) { + if (active_rrqs_xri_bitmap) + mempool_free(active_rrqs_xri_bitmap, + phba->active_rrq_pool); return ndlp; + } new_ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); - if (!new_ndlp) + if (!new_ndlp) { + if (active_rrqs_xri_bitmap) + mempool_free(active_rrqs_xri_bitmap, + phba->active_rrq_pool); return ndlp; + } lpfc_nlp_init(vport, new_ndlp, ndlp->nlp_DID); } else if (!NLP_CHK_NODE_ACT(new_ndlp)) { rc = memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)); - if (!rc) + if (!rc) { + if (active_rrqs_xri_bitmap) + mempool_free(active_rrqs_xri_bitmap, + phba->active_rrq_pool); return ndlp; + } new_ndlp = lpfc_enable_node(vport, new_ndlp, NLP_STE_UNUSED_NODE); - if (!new_ndlp) + if (!new_ndlp) { + if (active_rrqs_xri_bitmap) + mempool_free(active_rrqs_xri_bitmap, + phba->active_rrq_pool); return ndlp; + } keepDID = new_ndlp->nlp_DID; - if (phba->sli_rev == LPFC_SLI_REV4) - memcpy(&rrq.xri_bitmap, - &new_ndlp->active_rrqs.xri_bitmap, - sizeof(new_ndlp->active_rrqs.xri_bitmap)); + if ((phba->sli_rev == LPFC_SLI_REV4) && active_rrqs_xri_bitmap) + memcpy(active_rrqs_xri_bitmap, + new_ndlp->active_rrqs_xri_bitmap, + phba->cfg_rrq_xri_bitmap_sz); } else { keepDID = new_ndlp->nlp_DID; - if (phba->sli_rev == LPFC_SLI_REV4) - memcpy(&rrq.xri_bitmap, - &new_ndlp->active_rrqs.xri_bitmap, - sizeof(new_ndlp->active_rrqs.xri_bitmap)); + if (phba->sli_rev == LPFC_SLI_REV4 && + active_rrqs_xri_bitmap) + memcpy(active_rrqs_xri_bitmap, + new_ndlp->active_rrqs_xri_bitmap, + phba->cfg_rrq_xri_bitmap_sz); } lpfc_unreg_rpi(vport, new_ndlp); new_ndlp->nlp_DID = ndlp->nlp_DID; new_ndlp->nlp_prev_state = ndlp->nlp_prev_state; if (phba->sli_rev == LPFC_SLI_REV4) - memcpy(new_ndlp->active_rrqs.xri_bitmap, - &ndlp->active_rrqs.xri_bitmap, - sizeof(ndlp->active_rrqs.xri_bitmap)); + memcpy(new_ndlp->active_rrqs_xri_bitmap, + ndlp->active_rrqs_xri_bitmap, + phba->cfg_rrq_xri_bitmap_sz); if (ndlp->nlp_flag & NLP_NPR_2B_DISC) new_ndlp->nlp_flag |= NLP_NPR_2B_DISC; @@ -1619,10 +1642,11 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, /* Two ndlps cannot have the same did on the nodelist */ ndlp->nlp_DID = keepDID; - if (phba->sli_rev == LPFC_SLI_REV4) - memcpy(&ndlp->active_rrqs.xri_bitmap, - &rrq.xri_bitmap, - sizeof(ndlp->active_rrqs.xri_bitmap)); + if (phba->sli_rev == LPFC_SLI_REV4 && + active_rrqs_xri_bitmap) + memcpy(ndlp->active_rrqs_xri_bitmap, + active_rrqs_xri_bitmap, + phba->cfg_rrq_xri_bitmap_sz); lpfc_drop_node(vport, ndlp); } else { @@ -1634,10 +1658,11 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, /* Two ndlps cannot have the same did */ ndlp->nlp_DID = keepDID; - if (phba->sli_rev == LPFC_SLI_REV4) - memcpy(&ndlp->active_rrqs.xri_bitmap, - &rrq.xri_bitmap, - sizeof(ndlp->active_rrqs.xri_bitmap)); + if (phba->sli_rev == LPFC_SLI_REV4 && + active_rrqs_xri_bitmap) + memcpy(ndlp->active_rrqs_xri_bitmap, + active_rrqs_xri_bitmap, + phba->cfg_rrq_xri_bitmap_sz); /* Since we are swapping the ndlp passed in with the new one * and the did has already been swapped, copy over state. @@ -1668,6 +1693,10 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, put_device(&rport->dev); } } + if (phba->sli_rev == LPFC_SLI_REV4 && + active_rrqs_xri_bitmap) + mempool_free(active_rrqs_xri_bitmap, + phba->active_rrq_pool); return new_ndlp; } @@ -2122,6 +2151,8 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } npr->estabImagePair = 1; npr->readXferRdyDis = 1; + if (vport->cfg_first_burst_size) + npr->writeXferRdyDis = 1; /* For FCP support */ npr->prliType = PRLI_FCP_TYPE; @@ -2770,6 +2801,7 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) /* This will cause the callback-function lpfc_cmpl_els_cmd to * trigger the release of node. */ + lpfc_nlp_put(ndlp); return 0; } @@ -6191,11 +6223,11 @@ lpfc_els_timeout(unsigned long ptr) spin_lock_irqsave(&vport->work_port_lock, iflag); tmo_posted = vport->work_port_events & WORKER_ELS_TMO; - if (!tmo_posted) + if ((!tmo_posted) && (!(vport->load_flag & FC_UNLOADING))) vport->work_port_events |= WORKER_ELS_TMO; spin_unlock_irqrestore(&vport->work_port_lock, iflag); - if (!tmo_posted) + if ((!tmo_posted) && (!(vport->load_flag & FC_UNLOADING))) lpfc_worker_wake_up(phba); return; } @@ -6221,19 +6253,26 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport) uint32_t els_command = 0; uint32_t timeout; uint32_t remote_ID = 0xffffffff; - LIST_HEAD(txcmplq_completions); LIST_HEAD(abort_list); timeout = (uint32_t)(phba->fc_ratov << 1); pring = &phba->sli.ring[LPFC_ELS_RING]; - + if ((phba->pport->load_flag & FC_UNLOADING)) + return; spin_lock_irq(&phba->hbalock); - list_splice_init(&pring->txcmplq, &txcmplq_completions); - spin_unlock_irq(&phba->hbalock); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + + if ((phba->pport->load_flag & FC_UNLOADING)) { + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); + spin_unlock_irq(&phba->hbalock); + return; + } - list_for_each_entry_safe(piocb, tmp_iocb, &txcmplq_completions, list) { + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { cmd = &piocb->iocb; if ((piocb->iocb_flag & LPFC_IO_LIBDFC) != 0 || @@ -6272,11 +6311,12 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport) } list_add_tail(&piocb->dlist, &abort_list); } - spin_lock_irq(&phba->hbalock); - list_splice(&txcmplq_completions, &pring->txcmplq); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); spin_unlock_irq(&phba->hbalock); list_for_each_entry_safe(piocb, tmp_iocb, &abort_list, dlist) { + cmd = &piocb->iocb; lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, "0127 ELS timeout Data: x%x x%x x%x " "x%x\n", els_command, @@ -6288,8 +6328,9 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport) } if (!list_empty(&phba->sli.ring[LPFC_ELS_RING].txcmplq)) - mod_timer(&vport->els_tmofunc, - jiffies + msecs_to_jiffies(1000 * timeout)); + if (!(phba->pport->load_flag & FC_UNLOADING)) + mod_timer(&vport->els_tmofunc, + jiffies + msecs_to_jiffies(1000 * timeout)); } /** @@ -6315,15 +6356,50 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport) void lpfc_els_flush_cmd(struct lpfc_vport *vport) { - LIST_HEAD(completions); + LIST_HEAD(abort_list); struct lpfc_hba *phba = vport->phba; struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; struct lpfc_iocbq *tmp_iocb, *piocb; IOCB_t *cmd = NULL; lpfc_fabric_abort_vport(vport); + /* + * For SLI3, only the hbalock is required. But SLI4 needs to coordinate + * with the ring insert operation. Because lpfc_sli_issue_abort_iotag + * ultimately grabs the ring_lock, the driver must splice the list into + * a working list and release the locks before calling the abort. + */ + spin_lock_irq(&phba->hbalock); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + if (piocb->iocb_flag & LPFC_IO_LIBDFC) + continue; + + if (piocb->vport != vport) + continue; + list_add_tail(&piocb->dlist, &abort_list); + } + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); + spin_unlock_irq(&phba->hbalock); + /* Abort each iocb on the aborted list and remove the dlist links. */ + list_for_each_entry_safe(piocb, tmp_iocb, &abort_list, dlist) { + spin_lock_irq(&phba->hbalock); + list_del_init(&piocb->dlist); + lpfc_sli_issue_abort_iotag(phba, pring, piocb); + spin_unlock_irq(&phba->hbalock); + } + if (!list_empty(&abort_list)) + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "3387 abort list for txq not empty\n"); + INIT_LIST_HEAD(&abort_list); spin_lock_irq(&phba->hbalock); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) { cmd = &piocb->iocb; @@ -6341,24 +6417,16 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) if (piocb->vport != vport) continue; - list_move_tail(&piocb->list, &completions); - } - - list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { - if (piocb->iocb_flag & LPFC_IO_LIBDFC) { - continue; - } - - if (piocb->vport != vport) - continue; - - lpfc_sli_issue_abort_iotag(phba, pring, piocb); + list_del_init(&piocb->list); + list_add_tail(&piocb->list, &abort_list); } + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); spin_unlock_irq(&phba->hbalock); /* Cancell all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); + lpfc_sli_cancel_iocbs(phba, &abort_list, + IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); return; } @@ -6383,35 +6451,9 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) void lpfc_els_flush_all_cmd(struct lpfc_hba *phba) { - LIST_HEAD(completions); - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; - struct lpfc_iocbq *tmp_iocb, *piocb; - IOCB_t *cmd = NULL; - - lpfc_fabric_abort_hba(phba); - spin_lock_irq(&phba->hbalock); - list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) { - cmd = &piocb->iocb; - if (piocb->iocb_flag & LPFC_IO_LIBDFC) - continue; - /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */ - if (cmd->ulpCommand == CMD_QUE_RING_BUF_CN || - cmd->ulpCommand == CMD_QUE_RING_BUF64_CN || - cmd->ulpCommand == CMD_CLOSE_XRI_CN || - cmd->ulpCommand == CMD_ABORT_XRI_CN) - continue; - list_move_tail(&piocb->list, &completions); - } - list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { - if (piocb->iocb_flag & LPFC_IO_LIBDFC) - continue; - lpfc_sli_issue_abort_iotag(phba, pring, piocb); - } - spin_unlock_irq(&phba->hbalock); - - /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); + struct lpfc_vport *vport; + list_for_each_entry(vport, &phba->port_list, listentry) + lpfc_els_flush_cmd(vport); return; } diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 0f6e2548f35..2a17e31265b 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -674,8 +674,6 @@ lpfc_work_done(struct lpfc_hba *phba) lpfc_fdmi_timeout_handler(vport); if (work_port_events & WORKER_RAMP_DOWN_QUEUE) lpfc_ramp_down_queue_handler(phba); - if (work_port_events & WORKER_RAMP_UP_QUEUE) - lpfc_ramp_up_queue_handler(phba); if (work_port_events & WORKER_DELAYED_DISC_TMO) lpfc_delayed_disc_timeout_handler(vport); } @@ -733,7 +731,7 @@ lpfc_do_work(void *p) struct lpfc_hba *phba = p; int rc; - set_user_nice(current, -20); + set_user_nice(current, MIN_NICE); current->flags |= PF_NOFREEZE; phba->data_flags = 0; @@ -2545,8 +2543,11 @@ lpfc_mbx_cmpl_fcf_rr_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) if (!new_fcf_record) { lpfc_printf_log(phba, KERN_WARNING, LOG_FIP, "2766 Mailbox command READ_FCF_RECORD " - "failed to retrieve a FCF record.\n"); - goto error_out; + "failed to retrieve a FCF record. " + "hba_flg x%x fcf_flg x%x\n", phba->hba_flag, + phba->fcf.fcf_flag); + lpfc_unregister_fcf_rescan(phba); + goto out; } /* Get the needed parameters from FCF record */ @@ -3973,7 +3974,10 @@ lpfc_nlp_counters(struct lpfc_vport *vport, int state, int count) vport->fc_map_cnt += count; break; case NLP_STE_NPR_NODE: - vport->fc_npr_cnt += count; + if (vport->fc_npr_cnt == 0 && count == -1) + vport->fc_npr_cnt = 0; + else + vport->fc_npr_cnt += count; break; } spin_unlock_irq(shost->host_lock); @@ -4171,8 +4175,6 @@ lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, NLP_INT_NODE_ACT(ndlp); atomic_set(&ndlp->cmd_pending, 0); ndlp->cmd_qdepth = vport->cfg_tgt_queue_depth; - if (vport->phba->sli_rev == LPFC_SLI_REV4) - ndlp->nlp_rpi = lpfc_sli4_alloc_rpi(vport->phba); } struct lpfc_nodelist * @@ -4182,6 +4184,7 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_hba *phba = vport->phba; uint32_t did; unsigned long flags; + unsigned long *active_rrqs_xri_bitmap = NULL; if (!ndlp) return NULL; @@ -4210,13 +4213,21 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Keep the original DID */ did = ndlp->nlp_DID; + if (phba->sli_rev == LPFC_SLI_REV4) + active_rrqs_xri_bitmap = ndlp->active_rrqs_xri_bitmap; /* 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)); lpfc_initialize_node(vport, ndlp, did); + if (phba->sli_rev == LPFC_SLI_REV4) + ndlp->active_rrqs_xri_bitmap = active_rrqs_xri_bitmap; + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + if (vport->phba->sli_rev == LPFC_SLI_REV4) + ndlp->nlp_rpi = lpfc_sli4_alloc_rpi(vport->phba); + if (state != NLP_STE_UNUSED_NODE) lpfc_nlp_set_state(vport, ndlp, state); @@ -4437,6 +4448,7 @@ lpfc_nlp_logo_unreg(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (!ndlp) return; lpfc_issue_els_logo(vport, ndlp, 0); + mempool_free(pmb, phba->mbox_mem_pool); } /* @@ -4456,7 +4468,15 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) int rc; uint16_t rpi; - if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { + if (ndlp->nlp_flag & NLP_RPI_REGISTERED || + ndlp->nlp_flag & NLP_REG_LOGIN_SEND) { + if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND) + lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI, + "3366 RPI x%x needs to be " + "unregistered nlp_flag x%x " + "did x%x\n", + ndlp->nlp_rpi, ndlp->nlp_flag, + ndlp->nlp_DID); mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { /* SLI4 ports require the physical rpi value. */ @@ -4789,9 +4809,10 @@ __lpfc_findnode_did(struct lpfc_vport *vport, uint32_t did) ((uint32_t) ndlp->nlp_rpi & 0xff)); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, "0929 FIND node DID " - "Data: x%p x%x x%x x%x\n", + "Data: x%p x%x x%x x%x %p\n", ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); + ndlp->nlp_flag, data1, + ndlp->active_rrqs_xri_bitmap); return ndlp; } } @@ -5608,6 +5629,17 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_initialize_node(vport, ndlp, did); INIT_LIST_HEAD(&ndlp->nlp_listp); + if (vport->phba->sli_rev == LPFC_SLI_REV4) { + ndlp->nlp_rpi = lpfc_sli4_alloc_rpi(vport->phba); + ndlp->active_rrqs_xri_bitmap = + mempool_alloc(vport->phba->active_rrq_pool, + GFP_KERNEL); + if (ndlp->active_rrqs_xri_bitmap) + memset(ndlp->active_rrqs_xri_bitmap, 0, + ndlp->phba->cfg_rrq_xri_bitmap_sz); + } + + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, "node init: did:x%x", @@ -5651,6 +5683,9 @@ lpfc_nlp_release(struct kref *kref) /* free ndlp memory for final ndlp release */ if (NLP_CHK_FREE_REQ(ndlp)) { kfree(ndlp->lat_data); + if (phba->sli_rev == LPFC_SLI_REV4) + mempool_free(ndlp->active_rrqs_xri_bitmap, + ndlp->phba->active_rrq_pool); mempool_free(ndlp, ndlp->phba->nlp_mem_pool); } } @@ -6157,13 +6192,41 @@ lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba, memcpy(&conn_entry->conn_rec, &conn_rec[i], sizeof(struct lpfc_fcf_conn_rec)); - conn_entry->conn_rec.vlan_tag = - le16_to_cpu(conn_entry->conn_rec.vlan_tag) & 0xFFF; - conn_entry->conn_rec.flags = - le16_to_cpu(conn_entry->conn_rec.flags); list_add_tail(&conn_entry->list, &phba->fcf_conn_rec_list); } + + if (!list_empty(&phba->fcf_conn_rec_list)) { + i = 0; + list_for_each_entry(conn_entry, &phba->fcf_conn_rec_list, + list) { + conn_rec = &conn_entry->conn_rec; + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3345 FCF connection list rec[%02d]: " + "flags:x%04x, vtag:x%04x, " + "fabric_name:x%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x, " + "switch_name:x%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x\n", i++, + conn_rec->flags, conn_rec->vlan_tag, + conn_rec->fabric_name[0], + conn_rec->fabric_name[1], + conn_rec->fabric_name[2], + conn_rec->fabric_name[3], + conn_rec->fabric_name[4], + conn_rec->fabric_name[5], + conn_rec->fabric_name[6], + conn_rec->fabric_name[7], + conn_rec->switch_name[0], + conn_rec->switch_name[1], + conn_rec->switch_name[2], + conn_rec->switch_name[3], + conn_rec->switch_name[4], + conn_rec->switch_name[5], + conn_rec->switch_name[6], + conn_rec->switch_name[7]); + } + } } /** diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 83700c18f46..23625925237 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2011 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -45,6 +45,7 @@ #define LPFC_EXTRA_RING 1 /* ring 1 for other protocols */ #define LPFC_ELS_RING 2 /* ring 2 for ELS commands */ #define LPFC_FCP_NEXT_RING 3 +#define LPFC_FCP_OAS_RING 3 #define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */ #define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */ diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 713a4613ec3..f432ec180cf 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009-2012 Emulex. All rights reserved. * + * Copyright (C) 2009-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -234,6 +234,9 @@ struct ulp_bde64 { uint32_t addrHigh; }; +/* Maximun size of immediate data that can fit into a 128 byte WQE */ +#define LPFC_MAX_BDE_IMM_SIZE 64 + struct lpfc_sli4_flags { uint32_t word0; #define lpfc_idx_rsrc_rdy_SHIFT 0 @@ -2585,6 +2588,9 @@ struct lpfc_sli4_parameters { #define cfg_mqv_WORD word6 uint32_t word7; uint32_t word8; +#define cfg_wqsize_SHIFT 8 +#define cfg_wqsize_MASK 0x0000000f +#define cfg_wqsize_WORD word8 #define cfg_wqv_SHIFT 14 #define cfg_wqv_MASK 0x00000003 #define cfg_wqv_WORD word8 @@ -2610,6 +2616,9 @@ struct lpfc_sli4_parameters { #define cfg_phwq_SHIFT 15 #define cfg_phwq_MASK 0x00000001 #define cfg_phwq_WORD word12 +#define cfg_oas_SHIFT 25 +#define cfg_oas_MASK 0x00000001 +#define cfg_oas_WORD word12 #define cfg_loopbk_scope_SHIFT 28 #define cfg_loopbk_scope_MASK 0x0000000f #define cfg_loopbk_scope_WORD word12 @@ -3316,6 +3325,9 @@ struct wqe_common { #define wqe_ebde_cnt_SHIFT 0 #define wqe_ebde_cnt_MASK 0x0000000f #define wqe_ebde_cnt_WORD word10 +#define wqe_oas_SHIFT 6 +#define wqe_oas_MASK 0x00000001 +#define wqe_oas_WORD word10 #define wqe_lenloc_SHIFT 7 #define wqe_lenloc_MASK 0x00000003 #define wqe_lenloc_WORD word10 @@ -3433,7 +3445,8 @@ struct els_request64_wqe { #define els_req64_hopcnt_SHIFT 24 #define els_req64_hopcnt_MASK 0x000000ff #define els_req64_hopcnt_WORD word13 - uint32_t reserved[2]; + uint32_t word14; + uint32_t max_response_payload_len; }; struct xmit_els_rsp64_wqe { @@ -3548,7 +3561,8 @@ struct gen_req64_wqe { uint32_t relative_offset; struct wqe_rctl_dfctl wge_ctl; /* word 5 */ struct wqe_common wqe_com; /* words 6-11 */ - uint32_t rsvd_12_15[4]; + uint32_t rsvd_12_14[3]; + uint32_t max_response_payload_len; }; struct create_xri_wqe { @@ -3578,7 +3592,13 @@ struct abort_cmd_wqe { struct fcp_iwrite64_wqe { struct ulp_bde64 bde; - uint32_t payload_offset_len; + uint32_t word3; +#define cmd_buff_len_SHIFT 16 +#define cmd_buff_len_MASK 0x00000ffff +#define cmd_buff_len_WORD word3 +#define payload_offset_len_SHIFT 0 +#define payload_offset_len_MASK 0x0000ffff +#define payload_offset_len_WORD word3 uint32_t total_xfer_len; uint32_t initial_xfer_len; struct wqe_common wqe_com; /* words 6-11 */ @@ -3588,7 +3608,13 @@ struct fcp_iwrite64_wqe { struct fcp_iread64_wqe { struct ulp_bde64 bde; - uint32_t payload_offset_len; /* word 3 */ + uint32_t word3; +#define cmd_buff_len_SHIFT 16 +#define cmd_buff_len_MASK 0x00000ffff +#define cmd_buff_len_WORD word3 +#define payload_offset_len_SHIFT 0 +#define payload_offset_len_MASK 0x0000ffff +#define payload_offset_len_WORD word3 uint32_t total_xfer_len; /* word 4 */ uint32_t rsrvd5; /* word 5 */ struct wqe_common wqe_com; /* words 6-11 */ @@ -3598,7 +3624,13 @@ struct fcp_iread64_wqe { struct fcp_icmnd64_wqe { struct ulp_bde64 bde; /* words 0-2 */ - uint32_t rsrvd3; /* word 3 */ + uint32_t word3; +#define cmd_buff_len_SHIFT 16 +#define cmd_buff_len_MASK 0x00000ffff +#define cmd_buff_len_WORD word3 +#define payload_offset_len_SHIFT 0 +#define payload_offset_len_MASK 0x0000ffff +#define payload_offset_len_WORD word3 uint32_t rsrvd4; /* word 4 */ uint32_t rsrvd5; /* word 5 */ struct wqe_common wqe_com; /* words 6-11 */ @@ -3622,6 +3654,13 @@ union lpfc_wqe { struct gen_req64_wqe gen_req; }; +union lpfc_wqe128 { + uint32_t words[32]; + struct lpfc_wqe_generic generic; + struct xmit_seq64_wqe xmit_sequence; + struct gen_req64_wqe gen_req; +}; + #define LPFC_GROUP_OJECT_MAGIC_NUM 0xfeaa0001 #define LPFC_FILE_TYPE_GROUP 0xf7 #define LPFC_FILE_ID_GROUP 0xa2 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index cb465b25391..06f9a5b79e6 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -60,7 +60,8 @@ unsigned long _dump_buf_dif_order; spinlock_t _dump_buf_lock; /* Used when mapping IRQ vectors in a driver centric manner */ -uint16_t lpfc_used_cpu[LPFC_MAX_CPU]; +uint16_t *lpfc_used_cpu; +uint32_t lpfc_present_cpu; static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *); static int lpfc_post_rcv_buf(struct lpfc_hba *); @@ -79,6 +80,7 @@ static void lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *); static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *); static void lpfc_sli4_disable_intr(struct lpfc_hba *); static uint32_t lpfc_sli4_enable_intr(struct lpfc_hba *, uint32_t); +static void lpfc_sli4_oas_verify(struct lpfc_hba *phba); static struct scsi_transport_template *lpfc_transport_template = NULL; static struct scsi_transport_template *lpfc_vport_transport_template = NULL; @@ -471,10 +473,22 @@ lpfc_config_port_post(struct lpfc_hba *phba) lpfc_sli_read_link_ste(phba); /* Reset the DFT_HBA_Q_DEPTH to the max xri */ - if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1)) - phba->cfg_hba_queue_depth = - (mb->un.varRdConfig.max_xri + 1) - - lpfc_sli4_get_els_iocb_cnt(phba); + i = (mb->un.varRdConfig.max_xri + 1); + if (phba->cfg_hba_queue_depth > i) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "3359 HBA queue depth changed from %d to %d\n", + phba->cfg_hba_queue_depth, i); + phba->cfg_hba_queue_depth = i; + } + + /* Reset the DFT_LUN_Q_DEPTH to (max xri >> 3) */ + i = (mb->un.varRdConfig.max_xri >> 3); + if (phba->pport->cfg_lun_queue_depth > i) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "3360 LUN queue depth changed from %d to %d\n", + phba->pport->cfg_lun_queue_depth, i); + phba->pport->cfg_lun_queue_depth = i; + } phba->lmt = mb->un.varRdConfig.lmt; @@ -806,57 +820,153 @@ lpfc_hba_down_prep(struct lpfc_hba *phba) } /** - * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset + * lpfc_sli4_free_sp_events - Cleanup sp_queue_events to free + * rspiocb which got deferred + * * @phba: pointer to lpfc HBA data structure. * - * This routine will do uninitialization after the HBA is reset when bring - * down the SLI Layer. + * This routine will cleanup completed slow path events after HBA is reset + * when bringing down the SLI Layer. + * * * Return codes - * 0 - success. - * Any other value - error. + * void. **/ -static int -lpfc_hba_down_post_s3(struct lpfc_hba *phba) +static void +lpfc_sli4_free_sp_events(struct lpfc_hba *phba) +{ + struct lpfc_iocbq *rspiocbq; + struct hbq_dmabuf *dmabuf; + struct lpfc_cq_event *cq_event; + + spin_lock_irq(&phba->hbalock); + phba->hba_flag &= ~HBA_SP_QUEUE_EVT; + spin_unlock_irq(&phba->hbalock); + + while (!list_empty(&phba->sli4_hba.sp_queue_event)) { + /* Get the response iocb from the head of work queue */ + spin_lock_irq(&phba->hbalock); + list_remove_head(&phba->sli4_hba.sp_queue_event, + cq_event, struct lpfc_cq_event, list); + spin_unlock_irq(&phba->hbalock); + + switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) { + case CQE_CODE_COMPL_WQE: + rspiocbq = container_of(cq_event, struct lpfc_iocbq, + cq_event); + lpfc_sli_release_iocbq(phba, rspiocbq); + break; + case CQE_CODE_RECEIVE: + case CQE_CODE_RECEIVE_V1: + dmabuf = container_of(cq_event, struct hbq_dmabuf, + cq_event); + lpfc_in_buf_free(phba, &dmabuf->dbuf); + } + } +} + +/** + * lpfc_hba_free_post_buf - Perform lpfc uninitialization after HBA reset + * @phba: pointer to lpfc HBA data structure. + * + * This routine will cleanup posted ELS buffers after the HBA is reset + * when bringing down the SLI Layer. + * + * + * Return codes + * void. + **/ +static void +lpfc_hba_free_post_buf(struct lpfc_hba *phba) { struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring; struct lpfc_dmabuf *mp, *next_mp; - LIST_HEAD(completions); - int i; + LIST_HEAD(buflist); + int count; if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) lpfc_sli_hbqbuf_free_all(phba); else { /* Cleanup preposted buffers on the ELS ring */ pring = &psli->ring[LPFC_ELS_RING]; - list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + spin_lock_irq(&phba->hbalock); + list_splice_init(&pring->postbufq, &buflist); + spin_unlock_irq(&phba->hbalock); + + count = 0; + list_for_each_entry_safe(mp, next_mp, &buflist, list) { list_del(&mp->list); - pring->postbufq_cnt--; + count++; lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); } + + spin_lock_irq(&phba->hbalock); + pring->postbufq_cnt -= count; + spin_unlock_irq(&phba->hbalock); } +} + +/** + * lpfc_hba_clean_txcmplq - Perform lpfc uninitialization after HBA reset + * @phba: pointer to lpfc HBA data structure. + * + * This routine will cleanup the txcmplq after the HBA is reset when bringing + * down the SLI Layer. + * + * Return codes + * void + **/ +static void +lpfc_hba_clean_txcmplq(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + LIST_HEAD(completions); + int i; - spin_lock_irq(&phba->hbalock); for (i = 0; i < psli->num_rings; i++) { pring = &psli->ring[i]; - + if (phba->sli_rev >= LPFC_SLI_REV4) + spin_lock_irq(&pring->ring_lock); + else + spin_lock_irq(&phba->hbalock); /* At this point in time the HBA is either reset or DOA. Either * way, nothing should be on txcmplq as it will NEVER complete. */ list_splice_init(&pring->txcmplq, &completions); - spin_unlock_irq(&phba->hbalock); + pring->txcmplq_cnt = 0; + + if (phba->sli_rev >= LPFC_SLI_REV4) + spin_unlock_irq(&pring->ring_lock); + else + spin_unlock_irq(&phba->hbalock); /* Cancel all the IOCBs from the completions list */ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); - lpfc_sli_abort_iocb_ring(phba, pring); - spin_lock_irq(&phba->hbalock); } - spin_unlock_irq(&phba->hbalock); +} +/** + * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset + int i; + * @phba: pointer to lpfc HBA data structure. + * + * This routine will do uninitialization after the HBA is reset when bring + * down the SLI Layer. + * + * Return codes + * 0 - success. + * Any other value - error. + **/ +static int +lpfc_hba_down_post_s3(struct lpfc_hba *phba) +{ + lpfc_hba_free_post_buf(phba); + lpfc_hba_clean_txcmplq(phba); return 0; } @@ -876,13 +986,12 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) { struct lpfc_scsi_buf *psb, *psb_next; LIST_HEAD(aborts); - int ret; unsigned long iflag = 0; struct lpfc_sglq *sglq_entry = NULL; - ret = lpfc_hba_down_post_s3(phba); - if (ret) - return ret; + lpfc_hba_free_post_buf(phba); + lpfc_hba_clean_txcmplq(phba); + /* At this point in time the HBA is either reset or DOA. Either * way, nothing should be on lpfc_abts_els_sgl_list, it needs to be * on the lpfc_sgl_list so that it can either be freed if the @@ -918,6 +1027,8 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag); list_splice(&aborts, &phba->lpfc_scsi_buf_list_put); spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag); + + lpfc_sli4_free_sp_events(phba); return 0; } @@ -992,9 +1103,14 @@ lpfc_rrq_timeout(unsigned long ptr) phba = (struct lpfc_hba *)ptr; spin_lock_irqsave(&phba->pport->work_port_lock, iflag); - phba->hba_flag |= HBA_RRQ_ACTIVE; + if (!(phba->pport->load_flag & FC_UNLOADING)) + phba->hba_flag |= HBA_RRQ_ACTIVE; + else + phba->hba_flag &= ~HBA_RRQ_ACTIVE; spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag); - lpfc_worker_wake_up(phba); + + if (!(phba->pport->load_flag & FC_UNLOADING)) + lpfc_worker_wake_up(phba); } /** @@ -1231,7 +1347,6 @@ static void lpfc_handle_deferred_eratt(struct lpfc_hba *phba) { uint32_t old_host_status = phba->work_hs; - struct lpfc_sli_ring *pring; struct lpfc_sli *psli = &phba->sli; /* If the pci channel is offline, ignore possible errors, @@ -1260,8 +1375,7 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba) * dropped by the firmware. Error iocb (I/O) on txcmplq and let the * SCSI layer retry it after re-establishing link. */ - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); + lpfc_sli_abort_fcp_rings(phba); /* * There was a firmware error. Take the hba offline and then @@ -1329,7 +1443,6 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba) { struct lpfc_vport *vport = phba->pport; struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; uint32_t event_data; unsigned long temperature; struct temp_event temp_event_data; @@ -1381,8 +1494,7 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba) * Error iocb (I/O) on txcmplq and let the SCSI layer * retry it after re-establishing link. */ - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); + lpfc_sli_abort_fcp_rings(phba); /* * There was a firmware error. Take the hba offline and then @@ -1455,7 +1567,8 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba) * for handling possible port resource change. **/ static int -lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action) +lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action, + bool en_rn_msg) { int rc; uint32_t intr_mode; @@ -1467,9 +1580,10 @@ lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action) rc = lpfc_sli4_pdev_status_reg_wait(phba); if (!rc) { /* need reset: attempt for port recovery */ - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2887 Reset Needed: Attempting Port " - "Recovery...\n"); + if (en_rn_msg) + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2887 Reset Needed: Attempting Port " + "Recovery...\n"); lpfc_offline_prep(phba, mbx_action); lpfc_offline(phba); /* release interrupt for possible resource change */ @@ -1509,6 +1623,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba) uint32_t reg_err1, reg_err2; uint32_t uerrlo_reg, uemasklo_reg; uint32_t pci_rd_rc1, pci_rd_rc2; + bool en_rn_msg = true; int rc; /* If the pci channel is offline, ignore possible errors, since @@ -1559,10 +1674,12 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba) break; } if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 && - reg_err2 == SLIPORT_ERR2_REG_FW_RESTART) + reg_err2 == SLIPORT_ERR2_REG_FW_RESTART) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3143 Port Down: Firmware Restarted\n"); - else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 && + "3143 Port Down: Firmware Update " + "Detected\n"); + en_rn_msg = false; + } else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 && reg_err2 == SLIPORT_ERR2_REG_FORCED_DUMP) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3144 Port Down: Debug Dump\n"); @@ -1572,7 +1689,8 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba) "3145 Port Down: Provisioning\n"); /* Check port status register for function reset */ - rc = lpfc_sli4_port_sta_fn_reset(phba, LPFC_MBX_NO_WAIT); + rc = lpfc_sli4_port_sta_fn_reset(phba, LPFC_MBX_NO_WAIT, + en_rn_msg); if (rc == 0) { /* don't report event on forced debug dump */ if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 && @@ -1915,78 +2033,81 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) switch (dev_id) { case PCI_DEVICE_ID_FIREFLY: - m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"}; + m = (typeof(m)){"LP6000", "PCI", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SUPERFLY: if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3) - m = (typeof(m)){"LP7000", "PCI", - "Fibre Channel Adapter"}; + m = (typeof(m)){"LP7000", "PCI", ""}; else - m = (typeof(m)){"LP7000E", "PCI", - "Fibre Channel Adapter"}; + m = (typeof(m)){"LP7000E", "PCI", ""}; + m.function = "Obsolete, Unsupported Fibre Channel Adapter"; break; case PCI_DEVICE_ID_DRAGONFLY: m = (typeof(m)){"LP8000", "PCI", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_CENTAUR: if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID) - m = (typeof(m)){"LP9002", "PCI", - "Fibre Channel Adapter"}; + m = (typeof(m)){"LP9002", "PCI", ""}; else - m = (typeof(m)){"LP9000", "PCI", - "Fibre Channel Adapter"}; + m = (typeof(m)){"LP9000", "PCI", ""}; + m.function = "Obsolete, Unsupported Fibre Channel Adapter"; break; case PCI_DEVICE_ID_RFLY: m = (typeof(m)){"LP952", "PCI", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PEGASUS: m = (typeof(m)){"LP9802", "PCI-X", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_THOR: m = (typeof(m)){"LP10000", "PCI-X", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_VIPER: m = (typeof(m)){"LPX1000", "PCI-X", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PFLY: m = (typeof(m)){"LP982", "PCI-X", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_TFLY: m = (typeof(m)){"LP1050", "PCI-X", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HELIOS: m = (typeof(m)){"LP11000", "PCI-X2", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HELIOS_SCSP: m = (typeof(m)){"LP11000-SP", "PCI-X2", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HELIOS_DCSP: m = (typeof(m)){"LP11002-SP", "PCI-X2", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_NEPTUNE: - m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"}; + m = (typeof(m)){"LPe1000", "PCIe", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_NEPTUNE_SCSP: - m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"}; + m = (typeof(m)){"LPe1000-SP", "PCIe", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_NEPTUNE_DCSP: - m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"}; + m = (typeof(m)){"LPe1002-SP", "PCIe", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_BMID: m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_BSMB: - m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"}; + m = (typeof(m)){"LP111", "PCI-X2", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_ZEPHYR: m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"}; @@ -2005,16 +2126,20 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LP101: - m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"}; + m = (typeof(m)){"LP101", "PCI-X", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LP10000S: - m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"}; + m = (typeof(m)){"LP10000-S", "PCI", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LP11000S: - m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"}; + m = (typeof(m)){"LP11000-S", "PCI-X2", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LPE11000S: - m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"}; + m = (typeof(m)){"LPe11000-S", "PCIe", + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT: m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"}; @@ -2035,20 +2160,21 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HORNET: - m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"}; + m = (typeof(m)){"LP21000", "PCIe", + "Obsolete, Unsupported FCoE Adapter"}; GE = 1; break; case PCI_DEVICE_ID_PROTEUS_VF: m = (typeof(m)){"LPev12000", "PCIe IOV", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PROTEUS_PF: m = (typeof(m)){"LPev12000", "PCIe IOV", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PROTEUS_S: m = (typeof(m)){"LPemv12002-S", "PCIe IOV", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_TIGERSHARK: oneConnect = 1; @@ -2064,17 +2190,24 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) break; case PCI_DEVICE_ID_BALIUS: m = (typeof(m)){"LPVe12002", "PCIe Shared I/O", - "Fibre Channel Adapter"}; + "Obsolete, Unsupported Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LANCER_FC: - case PCI_DEVICE_ID_LANCER_FC_VF: m = (typeof(m)){"LPe16000", "PCIe", "Fibre Channel Adapter"}; break; + case PCI_DEVICE_ID_LANCER_FC_VF: + m = (typeof(m)){"LPe16000", "PCIe", + "Obsolete, Unsupported Fibre Channel Adapter"}; + break; case PCI_DEVICE_ID_LANCER_FCOE: - case PCI_DEVICE_ID_LANCER_FCOE_VF: oneConnect = 1; m = (typeof(m)){"OCe15100", "PCIe", "FCoE"}; break; + case PCI_DEVICE_ID_LANCER_FCOE_VF: + oneConnect = 1; + m = (typeof(m)){"OCe15100", "PCIe", + "Obsolete, Unsupported FCoE"}; + break; case PCI_DEVICE_ID_SKYHAWK: case PCI_DEVICE_ID_SKYHAWK_VF: oneConnect = 1; @@ -3018,10 +3151,10 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) phba->sli4_hba.scsi_xri_max); spin_lock_irq(&phba->scsi_buf_list_get_lock); - spin_lock_irq(&phba->scsi_buf_list_put_lock); + spin_lock(&phba->scsi_buf_list_put_lock); list_splice_init(&phba->lpfc_scsi_buf_list_get, &scsi_sgl_list); list_splice(&phba->lpfc_scsi_buf_list_put, &scsi_sgl_list); - spin_unlock_irq(&phba->scsi_buf_list_put_lock); + spin_unlock(&phba->scsi_buf_list_put_lock); spin_unlock_irq(&phba->scsi_buf_list_get_lock); if (phba->sli4_hba.scsi_xri_cnt > phba->sli4_hba.scsi_xri_max) { @@ -3057,10 +3190,10 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; } spin_lock_irq(&phba->scsi_buf_list_get_lock); - spin_lock_irq(&phba->scsi_buf_list_put_lock); + spin_lock(&phba->scsi_buf_list_put_lock); list_splice_init(&scsi_sgl_list, &phba->lpfc_scsi_buf_list_get); INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - spin_unlock_irq(&phba->scsi_buf_list_put_lock); + spin_unlock(&phba->scsi_buf_list_put_lock); spin_unlock_irq(&phba->scsi_buf_list_get_lock); return 0; @@ -4049,52 +4182,6 @@ lpfc_sli4_perform_all_vport_cvl(struct lpfc_hba *phba) } /** - * lpfc_sli4_perform_inuse_fcf_recovery - Perform inuse fcf recovery - * @vport: pointer to lpfc hba data structure. - * - * This routine is to perform FCF recovery when the in-use FCF either dead or - * got modified. - **/ -static void -lpfc_sli4_perform_inuse_fcf_recovery(struct lpfc_hba *phba, - struct lpfc_acqe_fip *acqe_fip) -{ - int rc; - - spin_lock_irq(&phba->hbalock); - /* Mark the fast failover process in progress */ - phba->fcf.fcf_flag |= FCF_DEAD_DISC; - spin_unlock_irq(&phba->hbalock); - - lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY, - "2771 Start FCF fast failover process due to in-use " - "FCF DEAD/MODIFIED event: evt_tag:x%x, index:x%x\n", - acqe_fip->event_tag, acqe_fip->index); - rc = lpfc_sli4_redisc_fcf_table(phba); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, - "2772 Issue FCF rediscover mabilbox command " - "failed, fail through to FCF dead event\n"); - spin_lock_irq(&phba->hbalock); - phba->fcf.fcf_flag &= ~FCF_DEAD_DISC; - spin_unlock_irq(&phba->hbalock); - /* - * Last resort will fail over by treating this as a link - * down to FCF registration. - */ - lpfc_sli4_fcf_dead_failthrough(phba); - } else { - /* Reset FCF roundrobin bmask for new discovery */ - lpfc_sli4_clear_fcf_rr_bmask(phba); - /* - * Handling fast FCF failover to a DEAD FCF event is - * considered equalivant to receiving CVL to all vports. - */ - lpfc_sli4_perform_all_vport_cvl(phba); - } -} - -/** * lpfc_sli4_async_fip_evt - Process the asynchronous FCoE FIP event * @phba: pointer to lpfc hba data structure. * @acqe_link: pointer to the async fcoe completion queue entry. @@ -4159,22 +4246,9 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, break; } - /* If FCF has been in discovered state, perform rediscovery - * only if the FCF with the same index of the in-use FCF got - * modified during normal operation. Otherwise, do nothing. - */ - if (phba->pport->port_state > LPFC_FLOGI) { + /* If the FCF has been in discovered state, do nothing. */ + if (phba->fcf.fcf_flag & FCF_SCAN_DONE) { spin_unlock_irq(&phba->hbalock); - if (phba->fcf.current_rec.fcf_indx == - acqe_fip->index) { - lpfc_printf_log(phba, KERN_ERR, LOG_FIP, - "3300 In-use FCF (%d) " - "modified, perform FCF " - "rediscovery\n", - acqe_fip->index); - lpfc_sli4_perform_inuse_fcf_recovery(phba, - acqe_fip); - } break; } spin_unlock_irq(&phba->hbalock); @@ -4227,7 +4301,39 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, * is no longer valid as we are not in the middle of FCF * failover process already. */ - lpfc_sli4_perform_inuse_fcf_recovery(phba, acqe_fip); + spin_lock_irq(&phba->hbalock); + /* Mark the fast failover process in progress */ + phba->fcf.fcf_flag |= FCF_DEAD_DISC; + spin_unlock_irq(&phba->hbalock); + + lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY, + "2771 Start FCF fast failover process due to " + "FCF DEAD event: evt_tag:x%x, fcf_index:x%x " + "\n", acqe_fip->event_tag, acqe_fip->index); + rc = lpfc_sli4_redisc_fcf_table(phba); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_FIP | + LOG_DISCOVERY, + "2772 Issue FCF rediscover mabilbox " + "command failed, fail through to FCF " + "dead event\n"); + spin_lock_irq(&phba->hbalock); + phba->fcf.fcf_flag &= ~FCF_DEAD_DISC; + spin_unlock_irq(&phba->hbalock); + /* + * Last resort will fail over by treating this + * as a link down to FCF registration. + */ + lpfc_sli4_fcf_dead_failthrough(phba); + } else { + /* Reset FCF roundrobin bmask for new discovery */ + lpfc_sli4_clear_fcf_rr_bmask(phba); + /* + * Handling fast FCF failover to a DEAD FCF event is + * considered equalivant to receiving CVL to all vports. + */ + lpfc_sli4_perform_all_vport_cvl(phba); + } break; case LPFC_FIP_EVENT_TYPE_CVL: phba->fcoe_cvl_eventtag = acqe_fip->event_tag; @@ -4559,7 +4665,7 @@ lpfc_enable_pci_dev(struct lpfc_hba *phba) pci_save_state(pdev); /* PCIe EEH recovery on powerpc platforms needs fundamental reset */ - if (pci_find_capability(pdev, PCI_CAP_ID_EXP)) + if (pci_is_pcie(pdev)) pdev->needs_freset = 1; return 0; @@ -4595,8 +4701,6 @@ lpfc_disable_pci_dev(struct lpfc_hba *phba) /* Release PCI resource and disable PCI device */ pci_release_selected_regions(pdev, bars); pci_disable_device(pdev); - /* Null out PCI private reference to driver */ - pci_set_drvdata(pdev, NULL); return; } @@ -4618,7 +4722,10 @@ lpfc_reset_hba(struct lpfc_hba *phba) phba->link_state = LPFC_HBA_ERROR; return; } - lpfc_offline_prep(phba, LPFC_MBX_WAIT); + if (phba->sli.sli_flag & LPFC_SLI_ACTIVE) + lpfc_offline_prep(phba, LPFC_MBX_WAIT); + else + lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT); lpfc_offline(phba); lpfc_sli_brdrestart(phba); lpfc_online(phba); @@ -4872,6 +4979,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0}; struct lpfc_mqe *mqe; int longs; + int fof_vectors = 0; + + /* Get all the module params for configuring this host */ + lpfc_get_cfgparam(phba); /* Before proceed, wait for POST done and device ready */ rc = lpfc_sli4_post_status_check(phba); @@ -4916,20 +5027,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) sizeof(struct lpfc_mbox_ext_buf_ctx)); INIT_LIST_HEAD(&phba->mbox_ext_buf_ctx.ext_dmabuf_list); - /* - * We need to do a READ_CONFIG mailbox command here before - * calling lpfc_get_cfgparam. For VFs this will report the - * MAX_XRI, MAX_VPI, MAX_RPI, MAX_IOCB, and MAX_VFI settings. - * All of the resources allocated - * for this Port are tied to these values. - */ - /* Get all the module params for configuring this host */ - lpfc_get_cfgparam(phba); phba->max_vpi = LPFC_MAX_VPI; - /* Eventually cfg_fcp_eq_count / cfg_fcp_wq_count will be depricated */ - phba->cfg_fcp_io_channel = phba->cfg_fcp_eq_count; - /* This will be set to correct value after the read_config mbox */ phba->max_vports = 0; @@ -5086,6 +5185,9 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) rc = lpfc_sli4_read_config(phba); if (unlikely(rc)) goto out_free_bsmbx; + rc = lpfc_mem_alloc_active_rrq_pool_s4(phba); + if (unlikely(rc)) + goto out_free_bsmbx; /* IF Type 0 ports get initialized now. */ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == @@ -5143,6 +5245,12 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) } } mempool_free(mboxq, phba->mbox_mem_pool); + + /* Verify OAS is supported */ + lpfc_sli4_oas_verify(phba); + if (phba->cfg_fof) + fof_vectors = 1; + /* Verify all the SLI4 queues */ rc = lpfc_sli4_queue_verify(phba); if (rc) @@ -5184,7 +5292,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) phba->sli4_hba.fcp_eq_hdl = kzalloc((sizeof(struct lpfc_fcp_eq_hdl) * - phba->cfg_fcp_io_channel), GFP_KERNEL); + (fof_vectors + phba->cfg_fcp_io_channel)), + GFP_KERNEL); if (!phba->sli4_hba.fcp_eq_hdl) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2572 Failed allocate memory for " @@ -5194,7 +5303,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) } phba->sli4_hba.msix_entries = kzalloc((sizeof(struct msix_entry) * - phba->cfg_fcp_io_channel), GFP_KERNEL); + (fof_vectors + + phba->cfg_fcp_io_channel)), GFP_KERNEL); if (!phba->sli4_hba.msix_entries) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2573 Failed allocate memory for msi-x " @@ -5213,6 +5323,21 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) rc = -ENOMEM; goto out_free_msix; } + if (lpfc_used_cpu == NULL) { + lpfc_used_cpu = kzalloc((sizeof(uint16_t) * lpfc_present_cpu), + GFP_KERNEL); + if (!lpfc_used_cpu) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3335 Failed allocate memory for msi-x " + "interrupt vector mapping\n"); + kfree(phba->sli4_hba.cpu_map); + rc = -ENOMEM; + goto out_free_msix; + } + for (i = 0; i < lpfc_present_cpu; i++) + lpfc_used_cpu[i] = LPFC_VECTOR_MAP_EMPTY; + } + /* Initialize io channels for round robin */ cpup = phba->sli4_hba.cpu_map; rc = 0; @@ -5277,6 +5402,7 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) kfree(phba->sli4_hba.cpu_map); phba->sli4_hba.num_present_cpu = 0; phba->sli4_hba.num_online_cpu = 0; + phba->sli4_hba.curr_disp_cpu = 0; /* Free memory allocated for msi-x interrupt vector entries */ kfree(phba->sli4_hba.msix_entries); @@ -5400,6 +5526,10 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) /* Initialize FCF connection rec list */ INIT_LIST_HEAD(&phba->fcf_conn_rec_list); + /* Initialize OAS configuration list */ + spin_lock_init(&phba->devicelock); + INIT_LIST_HEAD(&phba->luns); + return 0; } @@ -6675,12 +6805,14 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) goto read_cfg_out; /* Reset the DFT_HBA_Q_DEPTH to the max xri */ - if (phba->cfg_hba_queue_depth > - (phba->sli4_hba.max_cfg_param.max_xri - - lpfc_sli4_get_els_iocb_cnt(phba))) - phba->cfg_hba_queue_depth = - phba->sli4_hba.max_cfg_param.max_xri - - lpfc_sli4_get_els_iocb_cnt(phba); + length = phba->sli4_hba.max_cfg_param.max_xri - + lpfc_sli4_get_els_iocb_cnt(phba); + if (phba->cfg_hba_queue_depth > length) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "3361 HBA queue depth changed from %d to %d\n", + phba->cfg_hba_queue_depth, length); + phba->cfg_hba_queue_depth = length; + } if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != LPFC_SLI_INTF_IF_TYPE_2) @@ -6824,8 +6956,7 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) int cfg_fcp_io_channel; uint32_t cpu; uint32_t i = 0; - uint32_t j = 0; - + int fof_vectors = phba->cfg_fof ? 1 : 0; /* * Sanity check for configured queue parameters against the run-time @@ -6839,10 +6970,10 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) for_each_present_cpu(cpu) { if (cpu_online(cpu)) i++; - j++; } phba->sli4_hba.num_online_cpu = i; - phba->sli4_hba.num_present_cpu = j; + phba->sli4_hba.num_present_cpu = lpfc_present_cpu; + phba->sli4_hba.curr_disp_cpu = 0; if (i < cfg_fcp_io_channel) { lpfc_printf_log(phba, @@ -6853,7 +6984,7 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) cfg_fcp_io_channel = i; } - if (cfg_fcp_io_channel > + if (cfg_fcp_io_channel + fof_vectors > phba->sli4_hba.max_cfg_param.max_eq) { if (phba->sli4_hba.max_cfg_param.max_eq < LPFC_FCP_IO_CHAN_MIN) { @@ -6870,14 +7001,11 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) "available EQs: from %d to %d\n", cfg_fcp_io_channel, phba->sli4_hba.max_cfg_param.max_eq); - cfg_fcp_io_channel = phba->sli4_hba.max_cfg_param.max_eq; + cfg_fcp_io_channel = phba->sli4_hba.max_cfg_param.max_eq - + fof_vectors; } - /* Eventually cfg_fcp_eq_count / cfg_fcp_wq_count will be depricated */ - /* The actual number of FCP event queues adopted */ - phba->cfg_fcp_eq_count = cfg_fcp_io_channel; - phba->cfg_fcp_wq_count = cfg_fcp_io_channel; phba->cfg_fcp_io_channel = cfg_fcp_io_channel; /* Get EQ depth from module parameter, fake the default for now */ @@ -7085,6 +7213,9 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) } phba->sli4_hba.dat_rq = qdesc; + /* Create the Queues needed for Flash Optimized Fabric operations */ + if (phba->cfg_fof) + lpfc_fof_queue_create(phba); return 0; out_error: @@ -7109,6 +7240,9 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) { int idx; + if (phba->cfg_fof) + lpfc_fof_queue_destroy(phba); + if (phba->sli4_hba.hba_eq != NULL) { /* Release HBA event queue */ for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) { @@ -7148,19 +7282,6 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) phba->sli4_hba.fcp_wq = NULL; } - if (phba->pci_bar0_memmap_p) { - iounmap(phba->pci_bar0_memmap_p); - phba->pci_bar0_memmap_p = NULL; - } - if (phba->pci_bar2_memmap_p) { - iounmap(phba->pci_bar2_memmap_p); - phba->pci_bar2_memmap_p = NULL; - } - if (phba->pci_bar4_memmap_p) { - iounmap(phba->pci_bar4_memmap_p); - phba->pci_bar4_memmap_p = NULL; - } - /* Release FCP CQ mapping array */ if (phba->sli4_hba.fcp_cq_map != NULL) { kfree(phba->sli4_hba.fcp_cq_map); @@ -7506,8 +7627,20 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.hdr_rq->queue_id, phba->sli4_hba.dat_rq->queue_id, phba->sli4_hba.els_cq->queue_id); + + if (phba->cfg_fof) { + rc = lpfc_fof_queue_setup(phba); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0549 Failed setup of FOF Queues: " + "rc = 0x%x\n", rc); + goto out_destroy_els_rq; + } + } return 0; +out_destroy_els_rq: + lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq); out_destroy_els_wq: lpfc_wq_destroy(phba, phba->sli4_hba.els_wq); out_destroy_mbx_wq: @@ -7546,6 +7679,9 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) { int fcp_qidx; + /* Unset the queues created for Flash Optimized Fabric operations */ + if (phba->cfg_fof) + lpfc_fof_queue_destroy(phba); /* Unset mailbox command work queue */ lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq); /* Unset ELS work queue */ @@ -7949,9 +8085,9 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) * particular PCI BARs regions is dependent on the type of * SLI4 device. */ - if (pci_resource_start(pdev, 0)) { - phba->pci_bar0_map = pci_resource_start(pdev, 0); - bar0map_len = pci_resource_len(pdev, 0); + if (pci_resource_start(pdev, PCI_64BIT_BAR0)) { + phba->pci_bar0_map = pci_resource_start(pdev, PCI_64BIT_BAR0); + bar0map_len = pci_resource_len(pdev, PCI_64BIT_BAR0); /* * Map SLI4 PCI Config Space Register base to a kernel virtual @@ -7965,6 +8101,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) "registers.\n"); goto out; } + phba->pci_bar0_memmap_p = phba->sli4_hba.conf_regs_memmap_p; /* Set up BAR0 PCI config space register memory map */ lpfc_sli4_bar0_register_memmap(phba, if_type); } else { @@ -7987,13 +8124,13 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) } if ((if_type == LPFC_SLI_INTF_IF_TYPE_0) && - (pci_resource_start(pdev, 2))) { + (pci_resource_start(pdev, PCI_64BIT_BAR2))) { /* * Map SLI4 if type 0 HBA Control Register base to a kernel * virtual address and setup the registers. */ - phba->pci_bar1_map = pci_resource_start(pdev, 2); - bar1map_len = pci_resource_len(pdev, 2); + phba->pci_bar1_map = pci_resource_start(pdev, PCI_64BIT_BAR2); + bar1map_len = pci_resource_len(pdev, PCI_64BIT_BAR2); phba->sli4_hba.ctrl_regs_memmap_p = ioremap(phba->pci_bar1_map, bar1map_len); if (!phba->sli4_hba.ctrl_regs_memmap_p) { @@ -8001,17 +8138,18 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) "ioremap failed for SLI4 HBA control registers.\n"); goto out_iounmap_conf; } + phba->pci_bar2_memmap_p = phba->sli4_hba.ctrl_regs_memmap_p; lpfc_sli4_bar1_register_memmap(phba); } if ((if_type == LPFC_SLI_INTF_IF_TYPE_0) && - (pci_resource_start(pdev, 4))) { + (pci_resource_start(pdev, PCI_64BIT_BAR4))) { /* * Map SLI4 if type 0 HBA Doorbell Register base to a kernel * virtual address and setup the registers. */ - phba->pci_bar2_map = pci_resource_start(pdev, 4); - bar2map_len = pci_resource_len(pdev, 4); + phba->pci_bar2_map = pci_resource_start(pdev, PCI_64BIT_BAR4); + bar2map_len = pci_resource_len(pdev, PCI_64BIT_BAR4); phba->sli4_hba.drbl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len); if (!phba->sli4_hba.drbl_regs_memmap_p) { @@ -8019,6 +8157,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) "ioremap failed for SLI4 HBA doorbell registers.\n"); goto out_iounmap_ctrl; } + phba->pci_bar4_memmap_p = phba->sli4_hba.drbl_regs_memmap_p; error = lpfc_sli4_bar2_register_memmap(phba, LPFC_VF0); if (error) goto out_iounmap_all; @@ -8412,7 +8551,8 @@ static int lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) { int i, idx, saved_chann, used_chann, cpu, phys_id; - int max_phys_id, num_io_channel, first_cpu; + int max_phys_id, min_phys_id; + int num_io_channel, first_cpu, chan; struct lpfc_vector_map_info *cpup; #ifdef CONFIG_X86 struct cpuinfo_x86 *cpuinfo; @@ -8430,6 +8570,7 @@ lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) phba->sli4_hba.num_present_cpu)); max_phys_id = 0; + min_phys_id = 0xff; phys_id = 0; num_io_channel = 0; first_cpu = LPFC_VECTOR_MAP_EMPTY; @@ -8453,9 +8594,12 @@ lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) if (cpup->phys_id > max_phys_id) max_phys_id = cpup->phys_id; + if (cpup->phys_id < min_phys_id) + min_phys_id = cpup->phys_id; cpup++; } + phys_id = min_phys_id; /* Now associate the HBA vectors with specific CPUs */ for (idx = 0; idx < vectors; idx++) { cpup = phba->sli4_hba.cpu_map; @@ -8466,13 +8610,25 @@ lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) for (i = 1; i < max_phys_id; i++) { phys_id++; if (phys_id > max_phys_id) - phys_id = 0; + phys_id = min_phys_id; cpu = lpfc_find_next_cpu(phba, phys_id); if (cpu == LPFC_VECTOR_MAP_EMPTY) continue; goto found; } + /* Use round robin for scheduling */ + phba->cfg_fcp_io_sched = LPFC_FCP_SCHED_ROUND_ROBIN; + chan = 0; + cpup = phba->sli4_hba.cpu_map; + for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + cpup->channel_id = chan; + cpup++; + chan++; + if (chan >= phba->cfg_fcp_io_channel) + chan = 0; + } + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3329 Cannot set affinity:" "Error mapping vector %d (%d)\n", @@ -8510,7 +8666,7 @@ found: /* Spread vector mapping across multple physical CPU nodes */ phys_id++; if (phys_id > max_phys_id) - phys_id = 0; + phys_id = min_phys_id; } /* @@ -8520,7 +8676,7 @@ found: * Base the remaining IO channel assigned, to IO channels already * assigned to other CPUs on the same phys_id. */ - for (i = 0; i <= max_phys_id; i++) { + for (i = min_phys_id; i <= max_phys_id; i++) { /* * If there are no io channels already mapped to * this phys_id, just round robin thru the io_channels. @@ -8602,10 +8758,11 @@ out: if (num_io_channel != phba->sli4_hba.num_present_cpu) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3333 Set affinity mismatch:" - "%d chann != %d cpus: %d vactors\n", + "%d chann != %d cpus: %d vectors\n", num_io_channel, phba->sli4_hba.num_present_cpu, vectors); + /* Enable using cpu affinity for scheduling */ phba->cfg_fcp_io_sched = LPFC_FCP_SCHED_BY_CPU; return 1; } @@ -8642,6 +8799,10 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) /* Configure MSI-X capability structure */ vectors = phba->cfg_fcp_io_channel; + if (phba->cfg_fof) { + phba->sli4_hba.msix_entries[index].entry = index; + vectors++; + } enable_msix_vectors: rc = pci_enable_msix(phba->pcidev, phba->sli4_hba.msix_entries, vectors); @@ -8671,7 +8832,15 @@ enable_msix_vectors: phba->sli4_hba.fcp_eq_hdl[index].idx = index; phba->sli4_hba.fcp_eq_hdl[index].phba = phba; atomic_set(&phba->sli4_hba.fcp_eq_hdl[index].fcp_eq_in_use, 1); - rc = request_irq(phba->sli4_hba.msix_entries[index].vector, + if (phba->cfg_fof && (index == (vectors - 1))) + rc = request_irq( + phba->sli4_hba.msix_entries[index].vector, + &lpfc_sli4_fof_intr_handler, IRQF_SHARED, + (char *)&phba->sli4_hba.handler_name[index], + &phba->sli4_hba.fcp_eq_hdl[index]); + else + rc = request_irq( + phba->sli4_hba.msix_entries[index].vector, &lpfc_sli4_hba_intr_handler, IRQF_SHARED, (char *)&phba->sli4_hba.handler_name[index], &phba->sli4_hba.fcp_eq_hdl[index]); @@ -8683,6 +8852,9 @@ enable_msix_vectors: } } + if (phba->cfg_fof) + vectors--; + if (vectors != phba->cfg_fcp_io_channel) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3238 Reducing IO channels to match number of " @@ -8696,9 +8868,12 @@ enable_msix_vectors: cfg_fail_out: /* free the irq already requested */ - for (--index; index >= 0; index--) + for (--index; index >= 0; index--) { + irq_set_affinity_hint(phba->sli4_hba.msix_entries[index]. + vector, NULL); free_irq(phba->sli4_hba.msix_entries[index].vector, &phba->sli4_hba.fcp_eq_hdl[index]); + } msi_fail_out: /* Unconfigure MSI-X capability structure */ @@ -8719,10 +8894,16 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba) int index; /* Free up MSI-X multi-message vectors */ - for (index = 0; index < phba->cfg_fcp_io_channel; index++) + for (index = 0; index < phba->cfg_fcp_io_channel; index++) { + irq_set_affinity_hint(phba->sli4_hba.msix_entries[index]. + vector, NULL); free_irq(phba->sli4_hba.msix_entries[index].vector, &phba->sli4_hba.fcp_eq_hdl[index]); - + } + if (phba->cfg_fof) { + free_irq(phba->sli4_hba.msix_entries[index].vector, + &phba->sli4_hba.fcp_eq_hdl[index]); + } /* Disable MSI-X */ pci_disable_msix(phba->pcidev); @@ -8772,6 +8953,10 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba) phba->sli4_hba.fcp_eq_hdl[index].phba = phba; } + if (phba->cfg_fof) { + phba->sli4_hba.fcp_eq_hdl[index].idx = index; + phba->sli4_hba.fcp_eq_hdl[index].phba = phba; + } return 0; } @@ -8854,6 +9039,12 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) atomic_set(&phba->sli4_hba.fcp_eq_hdl[index]. fcp_eq_in_use, 1); } + if (phba->cfg_fof) { + phba->sli4_hba.fcp_eq_hdl[index].idx = index; + phba->sli4_hba.fcp_eq_hdl[index].phba = phba; + atomic_set(&phba->sli4_hba.fcp_eq_hdl[index]. + fcp_eq_in_use, 1); + } } } return intr_mode; @@ -9164,10 +9355,12 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->sli3_options &= ~LPFC_SLI4_PHWQ_ENABLED; sli4_params->sge_supp_len = mbx_sli4_parameters->sge_supp_len; sli4_params->loopbk_scope = bf_get(loopbk_scope, mbx_sli4_parameters); + sli4_params->oas_supported = bf_get(cfg_oas, mbx_sli4_parameters); sli4_params->cqv = bf_get(cfg_cqv, mbx_sli4_parameters); sli4_params->mqv = bf_get(cfg_mqv, mbx_sli4_parameters); sli4_params->wqv = bf_get(cfg_wqv, mbx_sli4_parameters); sli4_params->rqv = bf_get(cfg_rqv, mbx_sli4_parameters); + sli4_params->wqsize = bf_get(cfg_wqsize, mbx_sli4_parameters); sli4_params->sgl_pages_max = bf_get(cfg_sgl_page_cnt, mbx_sli4_parameters); sli4_params->sgl_pp_align = bf_get(cfg_sgl_pp_align, @@ -9427,7 +9620,6 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev) /* Disable interrupt */ lpfc_sli_disable_intr(phba); - pci_set_drvdata(pdev, NULL); scsi_host_put(shost); /* @@ -9582,9 +9774,6 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev) static void lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba) { - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2723 PCI channel I/O abort preparing for recovery\n"); @@ -9592,8 +9781,7 @@ lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba) * There may be errored I/Os through HBA, abort all I/Os on txcmplq * and let the SCSI mid-layer to retry them to recover. */ - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); + lpfc_sli_abort_fcp_rings(phba); } /** @@ -10336,17 +10524,13 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev) static void lpfc_sli4_prep_dev_for_recover(struct lpfc_hba *phba) { - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2828 PCI channel I/O abort preparing for recovery\n"); /* * There may be errored I/Os through HBA, abort all I/Os on txcmplq * and let the SCSI mid-layer to retry them to recover. */ - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); + lpfc_sli_abort_fcp_rings(phba); } /** @@ -10797,6 +10981,168 @@ lpfc_io_resume(struct pci_dev *pdev) return; } +/** + * lpfc_sli4_oas_verify - Verify OAS is supported by this adapter + * @phba: pointer to lpfc hba data structure. + * + * This routine checks to see if OAS is supported for this adapter. If + * supported, the configure Flash Optimized Fabric flag is set. Otherwise, + * the enable oas flag is cleared and the pool created for OAS device data + * is destroyed. + * + **/ +void +lpfc_sli4_oas_verify(struct lpfc_hba *phba) +{ + + if (!phba->cfg_EnableXLane) + return; + + if (phba->sli4_hba.pc_sli4_params.oas_supported) { + phba->cfg_fof = 1; + } else { + phba->cfg_fof = 0; + if (phba->device_data_mem_pool) + mempool_destroy(phba->device_data_mem_pool); + phba->device_data_mem_pool = NULL; + } + + return; +} + +/** + * lpfc_fof_queue_setup - Set up all the fof queues + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to set up all the fof queues for the FC HBA + * operation. + * + * Return codes + * 0 - successful + * -ENOMEM - No available memory + **/ +int +lpfc_fof_queue_setup(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli = &phba->sli; + int rc; + + rc = lpfc_eq_create(phba, phba->sli4_hba.fof_eq, LPFC_MAX_IMAX); + if (rc) + return -ENOMEM; + + if (phba->cfg_fof) { + + rc = lpfc_cq_create(phba, phba->sli4_hba.oas_cq, + phba->sli4_hba.fof_eq, LPFC_WCQ, LPFC_FCP); + if (rc) + goto out_oas_cq; + + rc = lpfc_wq_create(phba, phba->sli4_hba.oas_wq, + phba->sli4_hba.oas_cq, LPFC_FCP); + if (rc) + goto out_oas_wq; + + phba->sli4_hba.oas_cq->pring = &psli->ring[LPFC_FCP_OAS_RING]; + phba->sli4_hba.oas_ring = &psli->ring[LPFC_FCP_OAS_RING]; + } + + return 0; + +out_oas_wq: + lpfc_cq_destroy(phba, phba->sli4_hba.oas_cq); +out_oas_cq: + lpfc_eq_destroy(phba, phba->sli4_hba.fof_eq); + return rc; + +} + +/** + * lpfc_fof_queue_create - Create all the fof queues + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to allocate all the fof queues for the FC HBA + * operation. For each SLI4 queue type, the parameters such as queue entry + * count (queue depth) shall be taken from the module parameter. For now, + * we just use some constant number as place holder. + * + * Return codes + * 0 - successful + * -ENOMEM - No availble memory + * -EIO - The mailbox failed to complete successfully. + **/ +int +lpfc_fof_queue_create(struct lpfc_hba *phba) +{ + struct lpfc_queue *qdesc; + + /* Create FOF EQ */ + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize, + phba->sli4_hba.eq_ecount); + if (!qdesc) + goto out_error; + + phba->sli4_hba.fof_eq = qdesc; + + if (phba->cfg_fof) { + + /* Create OAS CQ */ + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, + phba->sli4_hba.cq_ecount); + if (!qdesc) + goto out_error; + + phba->sli4_hba.oas_cq = qdesc; + + /* Create OAS WQ */ + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize, + phba->sli4_hba.wq_ecount); + if (!qdesc) + goto out_error; + + phba->sli4_hba.oas_wq = qdesc; + + } + return 0; + +out_error: + lpfc_fof_queue_destroy(phba); + return -ENOMEM; +} + +/** + * lpfc_fof_queue_destroy - Destroy all the fof queues + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to release all the SLI4 queues with the FC HBA + * operation. + * + * Return codes + * 0 - successful + **/ +int +lpfc_fof_queue_destroy(struct lpfc_hba *phba) +{ + /* Release FOF Event queue */ + if (phba->sli4_hba.fof_eq != NULL) { + lpfc_sli4_queue_free(phba->sli4_hba.fof_eq); + phba->sli4_hba.fof_eq = NULL; + } + + /* Release OAS Completion queue */ + if (phba->sli4_hba.oas_cq != NULL) { + lpfc_sli4_queue_free(phba->sli4_hba.oas_cq); + phba->sli4_hba.oas_cq = NULL; + } + + /* Release OAS Work queue */ + if (phba->sli4_hba.oas_wq != NULL) { + lpfc_sli4_queue_free(phba->sli4_hba.oas_wq); + phba->sli4_hba.oas_wq = NULL; + } + return 0; +} + static struct pci_device_id lpfc_id_table[] = { {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER, PCI_ANY_ID, PCI_ANY_ID, }, @@ -10967,8 +11313,10 @@ lpfc_init(void) } /* Initialize in case vector mapping is needed */ - for (cpu = 0; cpu < LPFC_MAX_CPU; cpu++) - lpfc_used_cpu[cpu] = LPFC_VECTOR_MAP_EMPTY; + lpfc_used_cpu = NULL; + lpfc_present_cpu = 0; + for_each_present_cpu(cpu) + lpfc_present_cpu++; error = pci_register_driver(&lpfc_driver); if (error) { @@ -11008,6 +11356,7 @@ lpfc_exit(void) (1L << _dump_buf_dif_order), _dump_buf_dif); free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order); } + kfree(lpfc_used_cpu); } module_init(lpfc_init); diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 41363db7d42..1f292e29d56 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -178,7 +178,8 @@ lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) mb->mbxOwner = OWN_HOST; mb->un.varDmp.cv = 1; mb->un.varDmp.type = DMP_NV_PARAMS; - mb->un.varDmp.entry_index = 0; + if (phba->sli_rev < LPFC_SLI_REV4) + mb->un.varDmp.entry_index = 0; mb->un.varDmp.region_id = WAKE_UP_PARMS_REGION_ID; mb->un.varDmp.word_cnt = WAKE_UP_PARMS_WORD_SIZE; mb->un.varDmp.co = 0; @@ -361,7 +362,7 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) /* NEW_FEATURE * SLI-2, Coalescing Response Feature. */ - if (phba->cfg_cr_delay) { + if (phba->cfg_cr_delay && (phba->sli_rev < LPFC_SLI_REV4)) { mb->un.varCfgLnk.cr = 1; mb->un.varCfgLnk.ci = 1; mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay; @@ -377,7 +378,7 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) mb->un.varCfgLnk.crtov = phba->fc_crtov; mb->un.varCfgLnk.citov = phba->fc_citov; - if (phba->cfg_ack0) + if (phba->cfg_ack0 && (phba->sli_rev < LPFC_SLI_REV4)) mb->un.varCfgLnk.ack0_enable = 1; mb->mbxCommand = MBX_CONFIG_LINK; diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 812d0cd7c86..3fa65338d3f 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -38,10 +38,29 @@ #include "lpfc_scsi.h" #include "lpfc.h" #include "lpfc_crtn.h" +#include "lpfc_logmsg.h" #define LPFC_MBUF_POOL_SIZE 64 /* max elements in MBUF safety pool */ #define LPFC_MEM_POOL_SIZE 64 /* max elem in non-DMA safety pool */ +#define LPFC_DEVICE_DATA_POOL_SIZE 64 /* max elements in device data pool */ +int +lpfc_mem_alloc_active_rrq_pool_s4(struct lpfc_hba *phba) { + size_t bytes; + int max_xri = phba->sli4_hba.max_cfg_param.max_xri; + + if (max_xri <= 0) + return -ENOMEM; + bytes = ((BITS_PER_LONG - 1 + max_xri) / BITS_PER_LONG) * + sizeof(unsigned long); + phba->cfg_rrq_xri_bitmap_sz = bytes; + phba->active_rrq_pool = mempool_create_kmalloc_pool(LPFC_MEM_POOL_SIZE, + bytes); + if (!phba->active_rrq_pool) + return -ENOMEM; + else + return 0; +} /** * lpfc_mem_alloc - create and allocate all PCI and memory pools @@ -146,6 +165,16 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align) phba->lpfc_drb_pool = NULL; } + if (phba->cfg_EnableXLane) { + phba->device_data_mem_pool = mempool_create_kmalloc_pool( + LPFC_DEVICE_DATA_POOL_SIZE, + sizeof(struct lpfc_device_data)); + if (!phba->device_data_mem_pool) + goto fail_free_hrb_pool; + } else { + phba->device_data_mem_pool = NULL; + } + return 0; fail_free_hrb_pool: pci_pool_destroy(phba->lpfc_hrb_pool); @@ -188,6 +217,7 @@ lpfc_mem_free(struct lpfc_hba *phba) { int i; struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + struct lpfc_device_data *device_data; /* Free HBQ pools */ lpfc_sli_hbqbuf_free_all(phba); @@ -209,6 +239,10 @@ lpfc_mem_free(struct lpfc_hba *phba) /* Free NLP memory pool */ mempool_destroy(phba->nlp_mem_pool); phba->nlp_mem_pool = NULL; + if (phba->sli_rev == LPFC_SLI_REV4 && phba->active_rrq_pool) { + mempool_destroy(phba->active_rrq_pool); + phba->active_rrq_pool = NULL; + } /* Free mbox memory pool */ mempool_destroy(phba->mbox_mem_pool); @@ -227,6 +261,19 @@ lpfc_mem_free(struct lpfc_hba *phba) pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool); phba->lpfc_scsi_dma_buf_pool = NULL; + /* Free Device Data memory pool */ + if (phba->device_data_mem_pool) { + /* Ensure all objects have been returned to the pool */ + while (!list_empty(&phba->luns)) { + device_data = list_first_entry(&phba->luns, + struct lpfc_device_data, + listentry); + list_del(&device_data->listentry); + mempool_free(device_data, phba->device_data_mem_pool); + } + mempool_destroy(phba->device_data_mem_pool); + } + phba->device_data_mem_pool = NULL; return; } diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 31e9b92f5a9..c342f6afd74 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -203,8 +203,6 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, int lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { - LIST_HEAD(completions); - LIST_HEAD(txcmplq_completions); LIST_HEAD(abort_list); struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; @@ -216,32 +214,27 @@ lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) "Data: x%x x%x x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); - + /* Clean up all fabric IOs first.*/ lpfc_fabric_abort_nport(ndlp); - /* First check the txq */ + /* + * Lock the ELS ring txcmplq for SLI3/SLI4 and build a local list + * of all ELS IOs that need an ABTS. The IOs need to stay on the + * txcmplq so that the abort operation completes them successfully. + */ spin_lock_irq(&phba->hbalock); - list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { - /* Check to see if iocb matches the nport we are looking for */ - if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) { - /* It matches, so deque and call compl with anp error */ - list_move_tail(&iocb->list, &completions); - } - } - - /* Next check the txcmplq */ - list_splice_init(&pring->txcmplq, &txcmplq_completions); - spin_unlock_irq(&phba->hbalock); - - list_for_each_entry_safe(iocb, next_iocb, &txcmplq_completions, list) { - /* Check to see if iocb matches the nport we are looking for */ + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + /* Add to abort_list on on NDLP match. */ if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) list_add_tail(&iocb->dlist, &abort_list); } - spin_lock_irq(&phba->hbalock); - list_splice(&txcmplq_completions, &pring->txcmplq); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); spin_unlock_irq(&phba->hbalock); + /* Abort the targeted IOs and remove them from the abort list. */ list_for_each_entry_safe(iocb, next_iocb, &abort_list, dlist) { spin_lock_irq(&phba->hbalock); list_del_init(&iocb->dlist); @@ -249,9 +242,28 @@ lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) spin_unlock_irq(&phba->hbalock); } + INIT_LIST_HEAD(&abort_list); + + /* Now process the txq */ + spin_lock_irq(&phba->hbalock); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) { + list_del_init(&iocb->list); + list_add_tail(&iocb->list, &abort_list); + } + } + + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); + spin_unlock_irq(&phba->hbalock); + /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); + lpfc_sli_cancel_iocbs(phba, &abort_list, + IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); lpfc_cancel_retry_delay_tmo(phba->pport, ndlp); return 0; @@ -690,11 +702,15 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; + ndlp->nlp_flag &= ~NLP_FIRSTBURST; if (npr->prliType == PRLI_FCP_TYPE) { if (npr->initiatorFunc) ndlp->nlp_type |= NLP_FCP_INITIATOR; - if (npr->targetFunc) + if (npr->targetFunc) { ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->writeXferRdyDis) + ndlp->nlp_flag |= NLP_FIRSTBURST; + } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; } @@ -1676,12 +1692,16 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Check out PRLI rsp */ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; + ndlp->nlp_flag &= ~NLP_FIRSTBURST; if ((npr->acceptRspCode == PRLI_REQ_EXECUTED) && (npr->prliType == PRLI_FCP_TYPE)) { if (npr->initiatorFunc) ndlp->nlp_type |= NLP_FCP_INITIATOR; - if (npr->targetFunc) + if (npr->targetFunc) { ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->writeXferRdyDis) + ndlp->nlp_flag |= NLP_FIRSTBURST; + } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; } diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 8523b278ec9..2df11daad85 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -68,14 +68,23 @@ struct scsi_dif_tuple { __be32 ref_tag; /* Target LBA or indirect LBA */ }; -#if !defined(SCSI_PROT_GUARD_CHECK) || !defined(SCSI_PROT_REF_CHECK) -#define scsi_prot_flagged(sc, flg) sc -#endif +static struct lpfc_rport_data * +lpfc_rport_data_from_scsi_device(struct scsi_device *sdev) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)sdev->host->hostdata; + + if (vport->phba->cfg_fof) + return ((struct lpfc_device_data *)sdev->hostdata)->rport_data; + else + return (struct lpfc_rport_data *)sdev->hostdata; +} static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); static void lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); +static int +lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc); static void lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd) @@ -134,6 +143,30 @@ lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd) } } +static inline unsigned +lpfc_cmd_blksize(struct scsi_cmnd *sc) +{ + return sc->device->sector_size; +} + +#define LPFC_CHECK_PROTECT_GUARD 1 +#define LPFC_CHECK_PROTECT_REF 2 +static inline unsigned +lpfc_cmd_protect(struct scsi_cmnd *sc, int flag) +{ + return 1; +} + +static inline unsigned +lpfc_cmd_guard_csum(struct scsi_cmnd *sc) +{ + if (lpfc_prot_group_type(NULL, sc) == LPFC_PG_TYPE_NO_DIF) + return 0; + if (scsi_host_get_guard(sc->device->host) == SHOST_DIX_GUARD_IP) + return 1; + return 0; +} + /** * lpfc_sli4_set_rsp_sgl_last - Set the last bit in the response sge. * @phba: Pointer to HBA object. @@ -282,9 +315,27 @@ lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) unsigned long new_queue_depth, old_queue_depth; old_queue_depth = sdev->queue_depth; - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + /* change request from sysfs, fall through */ + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + break; + case SCSI_QDEPTH_QFULL: + if (scsi_track_queue_full(sdev, qdepth) == 0) + return sdev->queue_depth; + + lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, + "0711 detected queue full - lun queue " + "depth adjusted to %d.\n", sdev->queue_depth); + break; + default: + return -EOPNOTSUPP; + } + new_queue_depth = sdev->queue_depth; - rdata = sdev->hostdata; + rdata = lpfc_rport_data_from_scsi_device(sdev); if (rdata) lpfc_send_sdev_queuedepth_change_event(phba, vport, rdata->pnode, sdev->lun, @@ -355,50 +406,6 @@ lpfc_rampdown_queue_depth(struct lpfc_hba *phba) } /** - * lpfc_rampup_queue_depth - Post RAMP_UP_QUEUE event for worker thread - * @phba: The Hba for which this call is being executed. - * - * This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine - * post at most 1 event every 5 minute after last_ramp_up_time or - * last_rsrc_error_time. This routine wakes up worker thread of @phba - * to process WORKER_RAM_DOWN_EVENT event. - * - * This routine should be called with no lock held. - **/ -static inline void -lpfc_rampup_queue_depth(struct lpfc_vport *vport, - uint32_t queue_depth) -{ - unsigned long flags; - struct lpfc_hba *phba = vport->phba; - uint32_t evt_posted; - atomic_inc(&phba->num_cmd_success); - - if (vport->cfg_lun_queue_depth <= queue_depth) - return; - spin_lock_irqsave(&phba->hbalock, flags); - if (time_before(jiffies, - phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) || - time_before(jiffies, - phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) { - spin_unlock_irqrestore(&phba->hbalock, flags); - return; - } - phba->last_ramp_up_time = jiffies; - spin_unlock_irqrestore(&phba->hbalock, flags); - - spin_lock_irqsave(&phba->pport->work_port_lock, flags); - evt_posted = phba->pport->work_port_events & WORKER_RAMP_UP_QUEUE; - if (!evt_posted) - phba->pport->work_port_events |= WORKER_RAMP_UP_QUEUE; - spin_unlock_irqrestore(&phba->pport->work_port_lock, flags); - - if (!evt_posted) - lpfc_worker_wake_up(phba); - return; -} - -/** * lpfc_ramp_down_queue_handler - WORKER_RAMP_DOWN_QUEUE event handler * @phba: The Hba for which this call is being executed. * @@ -450,41 +457,6 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) } /** - * lpfc_ramp_up_queue_handler - WORKER_RAMP_UP_QUEUE event handler - * @phba: The Hba for which this call is being executed. - * - * This routine is called to process WORKER_RAMP_UP_QUEUE event for worker - * thread.This routine increases queue depth for all scsi device on each vport - * associated with @phba by 1. This routine also sets @phba num_rsrc_err and - * num_cmd_success to zero. - **/ -void -lpfc_ramp_up_queue_handler(struct lpfc_hba *phba) -{ - struct lpfc_vport **vports; - struct Scsi_Host *shost; - struct scsi_device *sdev; - int i; - - vports = lpfc_create_vport_work_array(phba); - if (vports != NULL) - for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { - shost = lpfc_shost_from_vport(vports[i]); - shost_for_each_device(sdev, shost) { - if (vports[i]->cfg_lun_queue_depth <= - sdev->queue_depth) - continue; - lpfc_change_queue_depth(sdev, - sdev->queue_depth+1, - SCSI_QDEPTH_RAMP_UP); - } - } - lpfc_destroy_vport_work_array(phba, vports); - atomic_set(&phba->num_rsrc_err, 0); - atomic_set(&phba->num_cmd_success, 0); -} - -/** * lpfc_scsi_dev_block - set all scsi hosts to block state * @phba: Pointer to HBA context object. * @@ -904,10 +876,10 @@ lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba) /* get all SCSI buffers need to repost to a local list */ spin_lock_irq(&phba->scsi_buf_list_get_lock); - spin_lock_irq(&phba->scsi_buf_list_put_lock); + spin_lock(&phba->scsi_buf_list_put_lock); list_splice_init(&phba->lpfc_scsi_buf_list_get, &post_sblist); list_splice(&phba->lpfc_scsi_buf_list_put, &post_sblist); - spin_unlock_irq(&phba->scsi_buf_list_put_lock); + spin_unlock(&phba->scsi_buf_list_put_lock); spin_unlock_irq(&phba->scsi_buf_list_get_lock); /* post the list of scsi buffer sgls to port if available */ @@ -978,29 +950,37 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) } memset(psb->data, 0, phba->cfg_sg_dma_buf_size); - /* Page alignment is CRITICAL, double check to be sure */ - if (((unsigned long)(psb->data) & - (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0) { + /* + * 4K Page alignment is CRITICAL to BlockGuard, double check + * to be sure. + */ + if (phba->cfg_enable_bg && (((unsigned long)(psb->data) & + (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); break; } - /* Allocate iotag for psb->cur_iocbq. */ - iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); - if (iotag == 0) { + + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, - psb->data, psb->dma_handle); + psb->data, psb->dma_handle); kfree(psb); break; } - lxri = lpfc_sli4_next_xritag(phba); - if (lxri == NO_XRI) { + /* Allocate iotag for psb->cur_iocbq. */ + iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); + if (iotag == 0) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, - psb->data, psb->dma_handle); + psb->data, psb->dma_handle); kfree(psb); + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "3368 Failed to allocated IOTAG for" + " XRI:0x%x\n", lxri); + lpfc_sli4_free_xri(phba, lxri); break; } psb->cur_iocbq.sli4_lxritag = lxri; @@ -1112,22 +1092,21 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { struct lpfc_scsi_buf * lpfc_cmd = NULL; struct list_head *scsi_buf_list_get = &phba->lpfc_scsi_buf_list_get; - unsigned long gflag = 0; - unsigned long pflag = 0; + unsigned long iflag = 0; - spin_lock_irqsave(&phba->scsi_buf_list_get_lock, gflag); + spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag); list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_scsi_buf, list); if (!lpfc_cmd) { - spin_lock_irqsave(&phba->scsi_buf_list_put_lock, pflag); + spin_lock(&phba->scsi_buf_list_put_lock); list_splice(&phba->lpfc_scsi_buf_list_put, &phba->lpfc_scsi_buf_list_get); INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_scsi_buf, list); - spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, pflag); + spin_unlock(&phba->scsi_buf_list_put_lock); } - spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, gflag); + spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag); return lpfc_cmd; } /** @@ -1144,13 +1123,13 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) static struct lpfc_scsi_buf* lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { - struct lpfc_scsi_buf *lpfc_cmd ; - unsigned long gflag = 0; - unsigned long pflag = 0; + struct lpfc_scsi_buf *lpfc_cmd, *lpfc_cmd_next; + unsigned long iflag = 0; int found = 0; - spin_lock_irqsave(&phba->scsi_buf_list_get_lock, gflag); - list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list_get, list) { + spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag); + list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, + &phba->lpfc_scsi_buf_list_get, list) { if (lpfc_test_rrq_active(phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; @@ -1159,13 +1138,13 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) break; } if (!found) { - spin_lock_irqsave(&phba->scsi_buf_list_put_lock, pflag); + spin_lock(&phba->scsi_buf_list_put_lock); list_splice(&phba->lpfc_scsi_buf_list_put, &phba->lpfc_scsi_buf_list_get); INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, pflag); - list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list_get, - list) { + spin_unlock(&phba->scsi_buf_list_put_lock); + list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, + &phba->lpfc_scsi_buf_list_get, list) { if (lpfc_test_rrq_active( phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; @@ -1174,7 +1153,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) break; } } - spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, gflag); + spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag); if (!found) return NULL; return lpfc_cmd; @@ -1409,12 +1388,6 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) return 0; } -static inline unsigned -lpfc_cmd_blksize(struct scsi_cmnd *sc) -{ - return sc->device->sector_size; -} - #ifdef CONFIG_SCSI_LPFC_DEBUG_FS /* Return if if error injection is detected by Initiator */ @@ -1479,7 +1452,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, } /* Next check if we need to match the remote NPortID or WWPN */ - rdata = sc->device->hostdata; + rdata = lpfc_rport_data_from_scsi_device(sc->device); if (rdata && rdata->pnode) { ndlp = rdata->pnode; @@ -1847,10 +1820,9 @@ static int lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint8_t *txop, uint8_t *rxop) { - uint8_t guard_type = scsi_host_get_guard(sc->device->host); uint8_t ret = 0; - if (guard_type == SHOST_DIX_GUARD_IP) { + if (lpfc_cmd_guard_csum(sc)) { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: @@ -1928,10 +1900,9 @@ static int lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint8_t *txop, uint8_t *rxop) { - uint8_t guard_type = scsi_host_get_guard(sc->device->host); uint8_t ret = 0; - if (guard_type == SHOST_DIX_GUARD_IP) { + if (lpfc_cmd_guard_csum(sc)) { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: @@ -2078,12 +2049,12 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, * protection data is automatically generated, not checked. */ if (datadir == DMA_FROM_DEVICE) { - if (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_GUARD)) bf_set(pde6_ce, pde6, checking); else bf_set(pde6_ce, pde6, 0); - if (scsi_prot_flagged(sc, SCSI_PROT_REF_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_REF)) bf_set(pde6_re, pde6, checking); else bf_set(pde6_re, pde6, 0); @@ -2240,12 +2211,12 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, bf_set(pde6_optx, pde6, txop); bf_set(pde6_oprx, pde6, rxop); - if (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_GUARD)) bf_set(pde6_ce, pde6, checking); else bf_set(pde6_ce, pde6, 0); - if (scsi_prot_flagged(sc, SCSI_PROT_REF_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_REF)) bf_set(pde6_re, pde6, checking); else bf_set(pde6_re, pde6, 0); @@ -2454,12 +2425,12 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, * protection data is automatically generated, not checked. */ if (sc->sc_data_direction == DMA_FROM_DEVICE) { - if (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_GUARD)) bf_set(lpfc_sli4_sge_dif_ce, diseed, checking); else bf_set(lpfc_sli4_sge_dif_ce, diseed, 0); - if (scsi_prot_flagged(sc, SCSI_PROT_REF_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_REF)) bf_set(lpfc_sli4_sge_dif_re, diseed, checking); else bf_set(lpfc_sli4_sge_dif_re, diseed, 0); @@ -2610,7 +2581,7 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, diseed->ref_tag = cpu_to_le32(reftag); diseed->ref_tag_tran = diseed->ref_tag; - if (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK)) { + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_GUARD)) { bf_set(lpfc_sli4_sge_dif_ce, diseed, checking); } else { @@ -2629,7 +2600,7 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, } - if (scsi_prot_flagged(sc, SCSI_PROT_REF_CHECK)) + if (lpfc_cmd_protect(sc, LPFC_CHECK_PROTECT_REF)) bf_set(lpfc_sli4_sge_dif_re, diseed, checking); else bf_set(lpfc_sli4_sge_dif_re, diseed, 0); @@ -2792,11 +2763,12 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc) ret = LPFC_PG_TYPE_DIF_BUF; break; default: - lpfc_printf_log(phba, KERN_ERR, LOG_FCP, - "9021 Unsupported protection op:%d\n", op); + if (phba) + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "9021 Unsupported protection op:%d\n", + op); break; } - return ret; } @@ -2821,22 +2793,22 @@ lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba, /* Check if there is protection data on the wire */ if (sc->sc_data_direction == DMA_FROM_DEVICE) { - /* Read */ + /* Read check for protection data */ if (scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT) return fcpdl; } else { - /* Write */ + /* Write check for protection data */ if (scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP) return fcpdl; } /* * If we are in DIF Type 1 mode every data block has a 8 byte - * DIF (trailer) attached to it. Must ajust FCP data length. + * DIF (trailer) attached to it. Must ajust FCP data length + * to account for the protection data. */ - if (scsi_prot_flagged(sc, SCSI_PROT_TRANSFER_PI)) - fcpdl += (fcpdl / lpfc_cmd_blksize(sc)) * 8; + fcpdl += (fcpdl / lpfc_cmd_blksize(sc)) * 8; return fcpdl; } @@ -3073,9 +3045,9 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) chk_guard = 1; guard_type = scsi_host_get_guard(cmd->device->host); + src = (struct scsi_dif_tuple *)sg_virt(sgpe); start_ref_tag = (uint32_t)scsi_get_lba(cmd); /* Truncate LBA */ start_app_tag = src->app_tag; - src = (struct scsi_dif_tuple *)sg_virt(sgpe); len = sgpe->length; while (src && protsegcnt) { while (len) { @@ -3090,25 +3062,10 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) goto skipit; } - /* App Tag checking */ - app_tag = src->app_tag; - if (chk_app && (app_tag != start_app_tag)) { - err_type = BGS_APPTAG_ERR_MASK; - goto out; - } - - /* Reference Tag checking */ - ref_tag = be32_to_cpu(src->ref_tag); - if (chk_ref && (ref_tag != start_ref_tag)) { - err_type = BGS_REFTAG_ERR_MASK; - goto out; - } - start_ref_tag++; - - /* Guard Tag checking */ + /* First Guard Tag checking */ if (chk_guard) { guard_tag = src->guard_tag; - if (guard_type == SHOST_DIX_GUARD_IP) + if (lpfc_cmd_guard_csum(cmd)) sum = lpfc_bg_csum(data_src, blksize); else @@ -3119,6 +3076,21 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) goto out; } } + + /* Reference Tag checking */ + ref_tag = be32_to_cpu(src->ref_tag); + if (chk_ref && (ref_tag != start_ref_tag)) { + err_type = BGS_REFTAG_ERR_MASK; + goto out; + } + start_ref_tag++; + + /* App Tag checking */ + app_tag = src->app_tag; + if (chk_app && (app_tag != start_app_tag)) { + err_type = BGS_APPTAG_ERR_MASK; + goto out; + } skipit: len -= sizeof(struct scsi_dif_tuple); if (len < 0) @@ -3485,6 +3457,14 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) * we need to set word 4 of IOCB here */ iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd); + + /* + * If the OAS driver feature is enabled and the lun is enabled for + * OAS, set the oas iocb related flags. + */ + if ((phba->cfg_fof) && ((struct lpfc_device_data *) + scsi_cmnd->device->hostdata)->oas_enabled) + lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_OAS; return 0; } @@ -3950,11 +3930,11 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, /* * Check SLI validation that all the transfer was actually done - * (fcpi_parm should be zero). + * (fcpi_parm should be zero). Apply check only to reads. */ - } else if (fcpi_parm) { + } else if (fcpi_parm && (cmnd->sc_data_direction == DMA_FROM_DEVICE)) { lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR, - "9029 FCP Data Transfer Check Error: " + "9029 FCP Read Check Error Data: " "x%x x%x x%x x%x x%x\n", be32_to_cpu(fcpcmd->fcpDl), be32_to_cpu(fcprsp->rspResId), @@ -3999,7 +3979,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct lpfc_nodelist *pnode = rdata->pnode; struct scsi_cmnd *cmd; int result; - struct scsi_device *tmp_sdev; int depth; unsigned long flags; struct lpfc_fast_path_event *fast_path_evt; @@ -4074,7 +4053,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, cmd->device ? cmd->device->id : 0xffff, cmd->device ? cmd->device->lun : 0xffff, lpfc_cmd->status, lpfc_cmd->result, - vport->fc_myDID, pnode->nlp_DID, + vport->fc_myDID, + (pnode) ? pnode->nlp_DID : 0, phba->sli_rev == LPFC_SLI_REV4 ? lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff, pIocbOut->iocb.ulpContext, @@ -4243,32 +4223,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, return; } - if (!result) - lpfc_rampup_queue_depth(vport, queue_depth); - - /* - * Check for queue full. If the lun is reporting queue full, then - * back off the lun queue depth to prevent target overloads. - */ - if (result == SAM_STAT_TASK_SET_FULL && pnode && - NLP_CHK_NODE_ACT(pnode)) { - shost_for_each_device(tmp_sdev, shost) { - if (tmp_sdev->id != scsi_id) - continue; - depth = scsi_track_queue_full(tmp_sdev, - tmp_sdev->queue_depth-1); - if (depth <= 0) - continue; - lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, - "0711 detected queue full - lun queue " - "depth adjusted to %d.\n", depth); - lpfc_send_sdev_queuedepth_change_event(phba, vport, - pnode, - tmp_sdev->lun, - depth+1, depth); - } - } - spin_lock_irqsave(&phba->hbalock, flags); lpfc_cmd->pCmd = NULL; spin_unlock_irqrestore(&phba->hbalock, flags); @@ -4325,6 +4279,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, char tag[2]; uint8_t *ptr; bool sli4; + uint32_t fcpdl; if (!pnode || !NLP_CHK_NODE_ACT(pnode)) return; @@ -4359,6 +4314,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, fcp_cmnd->fcpCntl1 = SIMPLE_Q; sli4 = (phba->sli_rev == LPFC_SLI_REV4); + piocbq->iocb.un.fcpi.fcpi_XRdy = 0; /* * There are three possibilities here - use scatter-gather segment, use @@ -4369,11 +4325,15 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, if (scsi_sg_count(scsi_cmnd)) { if (datadir == DMA_TO_DEVICE) { iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR; - if (sli4) - iocb_cmd->ulpPU = PARM_READ_CHECK; - else { - iocb_cmd->un.fcpi.fcpi_parm = 0; - iocb_cmd->ulpPU = 0; + iocb_cmd->ulpPU = PARM_READ_CHECK; + if (vport->cfg_first_burst_size && + (pnode->nlp_flag & NLP_FIRSTBURST)) { + fcpdl = scsi_bufflen(scsi_cmnd); + if (fcpdl < vport->cfg_first_burst_size) + piocbq->iocb.un.fcpi.fcpi_XRdy = fcpdl; + else + piocbq->iocb.un.fcpi.fcpi_XRdy = + vport->cfg_first_burst_size; } fcp_cmnd->fcpCntl3 = WRITE_DATA; phba->fc4OutputRequests++; @@ -4462,10 +4422,10 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport, piocb->ulpContext = vport->phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; } - if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { - piocb->ulpFCP2Rcvy = 1; - } + piocb->ulpFCP2Rcvy = (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) ? 1 : 0; piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f); + piocb->ulpPU = 0; + piocb->un.fcpi.fcpi_parm = 0; /* ulpTimeout is only one byte */ if (lpfc_cmd->timeout > 0xff) { @@ -4665,12 +4625,13 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) { struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *ndlp; struct lpfc_scsi_buf *lpfc_cmd; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); int err; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); err = fc_remote_port_chkready(rport); if (err) { cmnd->result = err; @@ -4756,6 +4717,24 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB); if (err) { atomic_dec(&ndlp->cmd_pending); + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "3376 FCP could not issue IOCB err %x" + "FCP cmd x%x <%d/%d> " + "sid: x%x did: x%x oxid: x%x " + "Data: x%x x%x x%x x%x\n", + err, cmnd->cmnd[0], + cmnd->device ? cmnd->device->id : 0xffff, + cmnd->device ? cmnd->device->lun : 0xffff, + vport->fc_myDID, ndlp->nlp_DID, + phba->sli_rev == LPFC_SLI_REV4 ? + lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff, + lpfc_cmd->cur_iocbq.iocb.ulpContext, + lpfc_cmd->cur_iocbq.iocb.ulpIoTag, + lpfc_cmd->cur_iocbq.iocb.ulpTimeout, + (uint32_t) + (cmnd->request->timeout / 1000)); + + goto out_host_busy_free_buf; } if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { @@ -4804,7 +4783,9 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) struct lpfc_scsi_buf *lpfc_cmd; IOCB_t *cmd, *icmd; int ret = SUCCESS, status = 0; - unsigned long flags; + struct lpfc_sli_ring *pring_s4; + int ring_number, ret_val; + unsigned long flags, iflags; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq); status = fc_block_scsi_eh(cmnd); @@ -4855,12 +4836,23 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) BUG_ON(iocb->context1 != lpfc_cmd); + /* abort issued in recovery is still in progress */ + if (iocb->iocb_flag & LPFC_DRIVER_ABORTED) { + lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, + "3389 SCSI Layer I/O Abort Request is pending\n"); + spin_unlock_irqrestore(&phba->hbalock, flags); + goto wait_for_cmpl; + } + abtsiocb = __lpfc_sli_get_iocbq(phba); if (abtsiocb == NULL) { ret = FAILED; goto out_unlock; } + /* Indicate the IO is being aborted by the driver. */ + iocb->iocb_flag |= LPFC_DRIVER_ABORTED; + /* * The scsi command can not be in txq and it is in flight because the * pCmd is still pointig at the SCSI command we have to abort. There @@ -4890,11 +4882,23 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; abtsiocb->vport = vport; + if (phba->sli_rev == LPFC_SLI_REV4) { + ring_number = MAX_SLI3_CONFIGURED_RINGS + iocb->fcp_wqidx; + pring_s4 = &phba->sli.ring[ring_number]; + /* Note: both hbalock and ring_lock must be set here */ + spin_lock_irqsave(&pring_s4->ring_lock, iflags); + ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, + abtsiocb, 0); + spin_unlock_irqrestore(&pring_s4->ring_lock, iflags); + } else { + ret_val = __lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, + abtsiocb, 0); + } /* no longer need the lock after this point */ spin_unlock_irqrestore(&phba->hbalock, flags); - if (lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0) == - IOCB_ERROR) { + + if (ret_val == IOCB_ERROR) { lpfc_sli_release_iocbq(phba, abtsiocb); ret = FAILED; goto out; @@ -4904,12 +4908,16 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) lpfc_sli_handle_fast_ring_event(phba, &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); +wait_for_cmpl: lpfc_cmd->waitq = &waitq; /* Wait for abort to complete */ wait_event_timeout(waitq, (lpfc_cmd->pCmd != cmnd), msecs_to_jiffies(2*vport->cfg_devloss_tmo*1000)); + + spin_lock_irqsave(shost->host_lock, flags); lpfc_cmd->waitq = NULL; + spin_unlock_irqrestore(shost->host_lock, flags); if (lpfc_cmd->pCmd == cmnd) { ret = FAILED; @@ -4955,6 +4963,73 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd) } } + +/** + * lpfc_check_fcp_rsp - check the returned fcp_rsp to see if task failed + * @vport: The virtual port for which this call is being executed. + * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure. + * + * This routine checks the FCP RSP INFO to see if the tsk mgmt command succeded + * + * Return code : + * 0x2003 - Error + * 0x2002 - Success + **/ +static int +lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd) +{ + struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp; + uint32_t rsp_info; + uint32_t rsp_len; + uint8_t rsp_info_code; + int ret = FAILED; + + + if (fcprsp == NULL) + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "0703 fcp_rsp is missing\n"); + else { + rsp_info = fcprsp->rspStatus2; + rsp_len = be32_to_cpu(fcprsp->rspRspLen); + rsp_info_code = fcprsp->rspInfo3; + + + lpfc_printf_vlog(vport, KERN_INFO, + LOG_FCP, + "0706 fcp_rsp valid 0x%x," + " rsp len=%d code 0x%x\n", + rsp_info, + rsp_len, rsp_info_code); + + if ((fcprsp->rspStatus2&RSP_LEN_VALID) && (rsp_len == 8)) { + switch (rsp_info_code) { + case RSP_NO_FAILURE: + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "0715 Task Mgmt No Failure\n"); + ret = SUCCESS; + break; + case RSP_TM_NOT_SUPPORTED: /* TM rejected */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "0716 Task Mgmt Target " + "reject\n"); + break; + case RSP_TM_NOT_COMPLETED: /* TM failed */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "0717 Task Mgmt Target " + "failed TM\n"); + break; + case RSP_TM_INVALID_LU: /* TM to invalid LU! */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "0718 Task Mgmt to invalid " + "LUN\n"); + break; + } + } + } + return ret; +} + + /** * lpfc_send_taskmgmt - Generic SCSI Task Mgmt Handler * @vport: The virtual port for which this call is being executed. @@ -4989,7 +5064,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, lpfc_cmd = lpfc_get_scsi_buf(phba, rdata->pnode); if (lpfc_cmd == NULL) return FAILED; - lpfc_cmd->timeout = 60; + lpfc_cmd->timeout = phba->cfg_task_mgmt_tmo; lpfc_cmd->rdata = rdata; status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun_id, @@ -5005,6 +5080,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, lpfc_release_scsi_buf(phba, lpfc_cmd); return FAILED; } + iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "0702 Issue %s to TGT %d LUN %d " @@ -5015,13 +5091,8 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING, iocbq, iocbqrsp, lpfc_cmd->timeout); - if (status != IOCB_SUCCESS) { - if (status == IOCB_TIMEDOUT) { - iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; - ret = TIMEOUT_ERROR; - } else - ret = FAILED; - lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + if ((status != IOCB_SUCCESS) || + (iocbqrsp->iocb.ulpStatus != IOSTAT_SUCCESS)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0727 TMF %s to TGT %d LUN %d failed (%d, %d) " "iocb_flag x%x\n", @@ -5029,9 +5100,21 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, tgt_id, lun_id, iocbqrsp->iocb.ulpStatus, iocbqrsp->iocb.un.ulpWord[4], iocbq->iocb_flag); - } else if (status == IOCB_BUSY) - ret = FAILED; - else + /* if ulpStatus != IOCB_SUCCESS, then status == IOCB_SUCCESS */ + if (status == IOCB_SUCCESS) { + if (iocbqrsp->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR) + /* Something in the FCP_RSP was invalid. + * Check conditions */ + ret = lpfc_check_fcp_rsp(vport, lpfc_cmd); + else + ret = FAILED; + } else if (status == IOCB_TIMEDOUT) { + ret = TIMEOUT_ERROR; + } else { + ret = FAILED; + } + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + } else ret = SUCCESS; lpfc_sli_release_iocbq(phba, iocbqrsp); @@ -5057,10 +5140,11 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, static int lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd) { - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned long later; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) { lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "0797 Tgt Map rport failure: rdata x%p\n", rdata); @@ -5078,7 +5162,7 @@ lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd) if (pnode->nlp_state == NLP_STE_MAPPED_NODE) return SUCCESS; schedule_timeout_uninterruptible(msecs_to_jiffies(500)); - rdata = cmnd->device->hostdata; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) return FAILED; pnode = rdata->pnode; @@ -5115,8 +5199,9 @@ lpfc_reset_flush_io_context(struct lpfc_vport *vport, uint16_t tgt_id, cnt = lpfc_sli_sum_iocb(vport, tgt_id, lun_id, context); if (cnt) - lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], - tgt_id, lun_id, context); + lpfc_sli_abort_taskmgmt(vport, + &phba->sli.ring[phba->sli.fcp_ring], + tgt_id, lun_id, context); later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies; while (time_after(later, jiffies) && cnt) { schedule_timeout_uninterruptible(msecs_to_jiffies(20)); @@ -5150,13 +5235,14 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) { struct Scsi_Host *shost = cmnd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; unsigned int lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; - int status, ret = SUCCESS; + int status; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0798 Device Reset rport failure: rdata x%p\n", rdata); @@ -5196,9 +5282,11 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) * So, continue on. * We will report success if all the i/o aborts successfully. */ - ret = lpfc_reset_flush_io_context(vport, tgt_id, lun_id, + if (status == SUCCESS) + status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id, LPFC_CTX_LUN); - return ret; + + return status; } /** @@ -5217,13 +5305,14 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) { struct Scsi_Host *shost = cmnd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; - struct lpfc_rport_data *rdata = cmnd->device->hostdata; + struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; unsigned int lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; - int status, ret = SUCCESS; + int status; + rdata = lpfc_rport_data_from_scsi_device(cmnd->device); if (!rdata) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0799 Target Reset rport failure: rdata x%p\n", rdata); @@ -5263,9 +5352,10 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) * So, continue on. * We will report success if all the i/o aborts successfully. */ - ret = lpfc_reset_flush_io_context(vport, tgt_id, lun_id, + if (status == SUCCESS) + status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id, LPFC_CTX_TGT); - return ret; + return status; } /** @@ -5422,11 +5512,45 @@ lpfc_slave_alloc(struct scsi_device *sdev) uint32_t num_to_alloc = 0; int num_allocated = 0; uint32_t sdev_cnt; + struct lpfc_device_data *device_data; + unsigned long flags; + struct lpfc_name target_wwpn; if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; - sdev->hostdata = rport->dd_data; + if (phba->cfg_fof) { + + /* + * Check to see if the device data structure for the lun + * exists. If not, create one. + */ + + u64_to_wwn(rport->port_name, target_wwpn.u.wwn); + spin_lock_irqsave(&phba->devicelock, flags); + device_data = __lpfc_get_device_data(phba, + &phba->luns, + &vport->fc_portname, + &target_wwpn, + sdev->lun); + if (!device_data) { + spin_unlock_irqrestore(&phba->devicelock, flags); + device_data = lpfc_create_device_data(phba, + &vport->fc_portname, + &target_wwpn, + sdev->lun, true); + if (!device_data) + return -ENOMEM; + spin_lock_irqsave(&phba->devicelock, flags); + list_add_tail(&device_data->listentry, &phba->luns); + } + device_data->rport_data = rport->dd_data; + device_data->available = true; + spin_unlock_irqrestore(&phba->devicelock, flags); + sdev->hostdata = device_data; + } else { + sdev->hostdata = rport->dd_data; + } sdev_cnt = atomic_inc_return(&phba->sdev_cnt); /* @@ -5516,11 +5640,344 @@ lpfc_slave_destroy(struct scsi_device *sdev) { struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata; struct lpfc_hba *phba = vport->phba; + unsigned long flags; + struct lpfc_device_data *device_data = sdev->hostdata; + atomic_dec(&phba->sdev_cnt); + if ((phba->cfg_fof) && (device_data)) { + spin_lock_irqsave(&phba->devicelock, flags); + device_data->available = false; + if (!device_data->oas_enabled) + lpfc_delete_device_data(phba, device_data); + spin_unlock_irqrestore(&phba->devicelock, flags); + } sdev->hostdata = NULL; return; } +/** + * lpfc_create_device_data - creates and initializes device data structure for OAS + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun on target + * @atomic_create: Flag to indicate if memory should be allocated using the + * GFP_ATOMIC flag or not. + * + * This routine creates a device data structure which will contain identifying + * information for the device (host wwpn, target wwpn, lun), state of OAS, + * whether or not the corresponding lun is available by the system, + * and pointer to the rport data. + * + * Return codes: + * NULL - Error + * Pointer to lpfc_device_data - Success + **/ +struct lpfc_device_data* +lpfc_create_device_data(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun, + bool atomic_create) +{ + + struct lpfc_device_data *lun_info; + int memory_flags; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !(phba->cfg_fof)) + return NULL; + + /* Attempt to create the device data to contain lun info */ + + if (atomic_create) + memory_flags = GFP_ATOMIC; + else + memory_flags = GFP_KERNEL; + lun_info = mempool_alloc(phba->device_data_mem_pool, memory_flags); + if (!lun_info) + return NULL; + INIT_LIST_HEAD(&lun_info->listentry); + lun_info->rport_data = NULL; + memcpy(&lun_info->device_id.vport_wwpn, vport_wwpn, + sizeof(struct lpfc_name)); + memcpy(&lun_info->device_id.target_wwpn, target_wwpn, + sizeof(struct lpfc_name)); + lun_info->device_id.lun = lun; + lun_info->oas_enabled = false; + lun_info->available = false; + return lun_info; +} + +/** + * lpfc_delete_device_data - frees a device data structure for OAS + * @pha: Pointer to host bus adapter structure. + * @lun_info: Pointer to device data structure to free. + * + * This routine frees the previously allocated device data structure passed. + * + **/ +void +lpfc_delete_device_data(struct lpfc_hba *phba, + struct lpfc_device_data *lun_info) +{ + + if (unlikely(!phba) || !lun_info || + !(phba->cfg_fof)) + return; + + if (!list_empty(&lun_info->listentry)) + list_del(&lun_info->listentry); + mempool_free(lun_info, phba->device_data_mem_pool); + return; +} + +/** + * __lpfc_get_device_data - returns the device data for the specified lun + * @pha: Pointer to host bus adapter structure. + * @list: Point to list to search. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun on target + * + * This routine searches the list passed for the specified lun's device data. + * This function does not hold locks, it is the responsibility of the caller + * to ensure the proper lock is held before calling the function. + * + * Return codes: + * NULL - Error + * Pointer to lpfc_device_data - Success + **/ +struct lpfc_device_data* +__lpfc_get_device_data(struct lpfc_hba *phba, struct list_head *list, + struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun) +{ + + struct lpfc_device_data *lun_info; + + if (unlikely(!phba) || !list || !vport_wwpn || !target_wwpn || + !phba->cfg_fof) + return NULL; + + /* Check to see if the lun is already enabled for OAS. */ + + list_for_each_entry(lun_info, list, listentry) { + if ((memcmp(&lun_info->device_id.vport_wwpn, vport_wwpn, + sizeof(struct lpfc_name)) == 0) && + (memcmp(&lun_info->device_id.target_wwpn, target_wwpn, + sizeof(struct lpfc_name)) == 0) && + (lun_info->device_id.lun == lun)) + return lun_info; + } + + return NULL; +} + +/** + * lpfc_find_next_oas_lun - searches for the next oas lun + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @starting_lun: Pointer to the lun to start searching for + * @found_vport_wwpn: Pointer to the found lun's vport wwpn information + * @found_target_wwpn: Pointer to the found lun's target wwpn information + * @found_lun: Pointer to the found lun. + * @found_lun_status: Pointer to status of the found lun. + * + * This routine searches the luns list for the specified lun + * or the first lun for the vport/target. If the vport wwpn contains + * a zero value then a specific vport is not specified. In this case + * any vport which contains the lun will be considered a match. If the + * target wwpn contains a zero value then a specific target is not specified. + * In this case any target which contains the lun will be considered a + * match. If the lun is found, the lun, vport wwpn, target wwpn and lun status + * are returned. The function will also return the next lun if available. + * If the next lun is not found, starting_lun parameter will be set to + * NO_MORE_OAS_LUN. + * + * Return codes: + * non-0 - Error + * 0 - Success + **/ +bool +lpfc_find_next_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t *starting_lun, + struct lpfc_name *found_vport_wwpn, + struct lpfc_name *found_target_wwpn, + uint64_t *found_lun, + uint32_t *found_lun_status) +{ + + unsigned long flags; + struct lpfc_device_data *lun_info; + struct lpfc_device_id *device_id; + uint64_t lun; + bool found = false; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !starting_lun || !found_vport_wwpn || + !found_target_wwpn || !found_lun || !found_lun_status || + (*starting_lun == NO_MORE_OAS_LUN) || + !phba->cfg_fof) + return false; + + lun = *starting_lun; + *found_lun = NO_MORE_OAS_LUN; + *starting_lun = NO_MORE_OAS_LUN; + + /* Search for lun or the lun closet in value */ + + spin_lock_irqsave(&phba->devicelock, flags); + list_for_each_entry(lun_info, &phba->luns, listentry) { + if (((wwn_to_u64(vport_wwpn->u.wwn) == 0) || + (memcmp(&lun_info->device_id.vport_wwpn, vport_wwpn, + sizeof(struct lpfc_name)) == 0)) && + ((wwn_to_u64(target_wwpn->u.wwn) == 0) || + (memcmp(&lun_info->device_id.target_wwpn, target_wwpn, + sizeof(struct lpfc_name)) == 0)) && + (lun_info->oas_enabled)) { + device_id = &lun_info->device_id; + if ((!found) && + ((lun == FIND_FIRST_OAS_LUN) || + (device_id->lun == lun))) { + *found_lun = device_id->lun; + memcpy(found_vport_wwpn, + &device_id->vport_wwpn, + sizeof(struct lpfc_name)); + memcpy(found_target_wwpn, + &device_id->target_wwpn, + sizeof(struct lpfc_name)); + if (lun_info->available) + *found_lun_status = + OAS_LUN_STATUS_EXISTS; + else + *found_lun_status = 0; + if (phba->cfg_oas_flags & OAS_FIND_ANY_VPORT) + memset(vport_wwpn, 0x0, + sizeof(struct lpfc_name)); + if (phba->cfg_oas_flags & OAS_FIND_ANY_TARGET) + memset(target_wwpn, 0x0, + sizeof(struct lpfc_name)); + found = true; + } else if (found) { + *starting_lun = device_id->lun; + memcpy(vport_wwpn, &device_id->vport_wwpn, + sizeof(struct lpfc_name)); + memcpy(target_wwpn, &device_id->target_wwpn, + sizeof(struct lpfc_name)); + break; + } + } + } + spin_unlock_irqrestore(&phba->devicelock, flags); + return found; +} + +/** + * lpfc_enable_oas_lun - enables a lun for OAS operations + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun + * + * This routine enables a lun for oas operations. The routines does so by + * doing the following : + * + * 1) Checks to see if the device data for the lun has been created. + * 2) If found, sets the OAS enabled flag if not set and returns. + * 3) Otherwise, creates a device data structure. + * 4) If successfully created, indicates the device data is for an OAS lun, + * indicates the lun is not available and add to the list of luns. + * + * Return codes: + * false - Error + * true - Success + **/ +bool +lpfc_enable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun) +{ + + struct lpfc_device_data *lun_info; + unsigned long flags; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !phba->cfg_fof) + return false; + + spin_lock_irqsave(&phba->devicelock, flags); + + /* Check to see if the device data for the lun has been created */ + lun_info = __lpfc_get_device_data(phba, &phba->luns, vport_wwpn, + target_wwpn, lun); + if (lun_info) { + if (!lun_info->oas_enabled) + lun_info->oas_enabled = true; + spin_unlock_irqrestore(&phba->devicelock, flags); + return true; + } + + /* Create an lun info structure and add to list of luns */ + lun_info = lpfc_create_device_data(phba, vport_wwpn, target_wwpn, lun, + false); + if (lun_info) { + lun_info->oas_enabled = true; + lun_info->available = false; + list_add_tail(&lun_info->listentry, &phba->luns); + spin_unlock_irqrestore(&phba->devicelock, flags); + return true; + } + spin_unlock_irqrestore(&phba->devicelock, flags); + return false; +} + +/** + * lpfc_disable_oas_lun - disables a lun for OAS operations + * @pha: Pointer to host bus adapter structure. + * @vport_wwpn: Pointer to vport's wwpn information + * @target_wwpn: Pointer to target's wwpn information + * @lun: Lun + * + * This routine disables a lun for oas operations. The routines does so by + * doing the following : + * + * 1) Checks to see if the device data for the lun is created. + * 2) If present, clears the flag indicating this lun is for OAS. + * 3) If the lun is not available by the system, the device data is + * freed. + * + * Return codes: + * false - Error + * true - Success + **/ +bool +lpfc_disable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, + struct lpfc_name *target_wwpn, uint64_t lun) +{ + + struct lpfc_device_data *lun_info; + unsigned long flags; + + if (unlikely(!phba) || !vport_wwpn || !target_wwpn || + !phba->cfg_fof) + return false; + + spin_lock_irqsave(&phba->devicelock, flags); + + /* Check to see if the lun is available. */ + lun_info = __lpfc_get_device_data(phba, + &phba->luns, vport_wwpn, + target_wwpn, lun); + if (lun_info) { + lun_info->oas_enabled = false; + if (!lun_info->available) + lpfc_delete_device_data(phba, lun_info); + spin_unlock_irqrestore(&phba->devicelock, flags); + return true; + } + + spin_unlock_irqrestore(&phba->devicelock, flags); + return false; +} struct scsi_host_template lpfc_template = { .module = THIS_MODULE, diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 21a2ffe67ea..0389ac1e7b8 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -41,6 +41,20 @@ struct lpfc_rport_data { struct lpfc_nodelist *pnode; /* Pointer to the node structure. */ }; +struct lpfc_device_id { + struct lpfc_name vport_wwpn; + struct lpfc_name target_wwpn; + uint64_t lun; +}; + +struct lpfc_device_data { + struct list_head listentry; + struct lpfc_rport_data *rport_data; + struct lpfc_device_id device_id; + bool oas_enabled; + bool available; +}; + struct fcp_rsp { uint32_t rspRsvd1; /* FC Word 0, byte 0:3 */ uint32_t rspRsvd2; /* FC Word 1, byte 0:3 */ @@ -73,6 +87,7 @@ struct fcp_rsp { #define RSP_RO_MISMATCH_ERR 0x03 #define RSP_TM_NOT_SUPPORTED 0x04 /* Task mgmt function not supported */ #define RSP_TM_NOT_COMPLETED 0x05 /* Task mgmt function not performed */ +#define RSP_TM_INVALID_LU 0x09 /* Task mgmt function to invalid LU */ uint32_t rspInfoRsvd; /* FCP_RSP_INFO bytes 4-7 (reserved) */ @@ -165,3 +180,7 @@ struct lpfc_scsi_buf { #define LPFC_SCSI_DMA_EXT_SIZE 264 #define LPFC_BPL_SIZE 1024 #define MDAC_DIRECT_CMD 0x22 + +#define FIND_FIRST_OAS_LUN 0 +#define NO_MORE_OAS_LUN -1 +#define NOT_OAS_ENABLED_LUN NO_MORE_OAS_LUN diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 572579f87de..32ada050557 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -71,6 +71,8 @@ static int lpfc_sli4_post_els_sgl_list(struct lpfc_hba *, struct list_head *, int); static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *, struct lpfc_eqe *, uint32_t); +static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba); +static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba); static IOCB_t * lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq) @@ -263,6 +265,16 @@ lpfc_sli4_eq_get(struct lpfc_queue *q) return NULL; q->hba_index = idx; + + /* + * insert barrier for instruction interlock : data from the hardware + * must have the valid bit checked before it can be copied and acted + * upon. Given what was seen in lpfc_sli4_cq_get() of speculative + * instructions allowing action on content before valid bit checked, + * add barrier here as well. May not be needed as "content" is a + * single 32-bit entity here (vs multi word structure for cq's). + */ + mb(); return eqe; } @@ -368,6 +380,17 @@ lpfc_sli4_cq_get(struct lpfc_queue *q) cqe = q->qe[q->hba_index].cqe; q->hba_index = idx; + + /* + * insert barrier for instruction interlock : data from the hardware + * must have the valid bit checked before it can be copied and acted + * upon. Speculative instructions were allowing a bcopy at the start + * of lpfc_sli4_fp_handle_wcqe(), which is called immediately + * after our return, to copy data before the valid bit check above + * was done. As such, some of the copied data was stale. The barrier + * ensures the check is before any data is copied. + */ + mb(); return cqe; } @@ -633,7 +656,7 @@ lpfc_clr_rrq_active(struct lpfc_hba *phba, if (!ndlp) goto out; - if (test_and_clear_bit(xritag, ndlp->active_rrqs.xri_bitmap)) { + if (test_and_clear_bit(xritag, ndlp->active_rrqs_xri_bitmap)) { rrq->send_rrq = 0; rrq->xritag = 0; rrq->rrq_stop_time = 0; @@ -676,7 +699,8 @@ lpfc_handle_rrq_active(struct lpfc_hba *phba) next_time = rrq->rrq_stop_time; } spin_unlock_irqrestore(&phba->hbalock, iflags); - if (!list_empty(&phba->active_rrq_list)) + if ((!list_empty(&phba->active_rrq_list)) && + (!(phba->pport->load_flag & FC_UNLOADING))) mod_timer(&phba->rrq_tmr, next_time); list_for_each_entry_safe(rrq, nextrrq, &send_rrq, list) { list_del(&rrq->list); @@ -790,7 +814,9 @@ lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba) list_del(&rrq->list); lpfc_clr_rrq_active(phba, rrq->xritag, rrq); } - if (!list_empty(&phba->active_rrq_list)) + if ((!list_empty(&phba->active_rrq_list)) && + (!(phba->pport->load_flag & FC_UNLOADING))) + mod_timer(&phba->rrq_tmr, next_time); } @@ -811,7 +837,9 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, { if (!ndlp) return 0; - if (test_bit(xritag, ndlp->active_rrqs.xri_bitmap)) + if (!ndlp->active_rrqs_xri_bitmap) + return 0; + if (test_bit(xritag, ndlp->active_rrqs_xri_bitmap)) return 1; else return 0; @@ -861,7 +889,10 @@ lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING)) goto out; - if (test_and_set_bit(xritag, ndlp->active_rrqs.xri_bitmap)) + if (!ndlp->active_rrqs_xri_bitmap) + goto out; + + if (test_and_set_bit(xritag, ndlp->active_rrqs_xri_bitmap)) goto out; spin_unlock_irqrestore(&phba->hbalock, iflags); @@ -1011,17 +1042,6 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) else sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_lxritag); - /* - ** This should have been removed from the txcmplq before calling - ** iocbq_release. The normal completion - ** path should have already done the list_del_init. - */ - if (unlikely(!list_empty(&iocbq->list))) { - if (iocbq->iocb_flag & LPFC_IO_ON_TXCMPLQ) - iocbq->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; - list_del_init(&iocbq->list); - } - if (sglq) { if ((iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) && @@ -1070,13 +1090,6 @@ __lpfc_sli_release_iocbq_s3(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) { size_t start_clean = offsetof(struct lpfc_iocbq, iocb); - /* - ** This should have been removed from the txcmplq before calling - ** iocbq_release. The normal completion - ** path should have already done the list_del_init. - */ - if (unlikely(!list_empty(&iocbq->list))) - list_del_init(&iocbq->list); /* * Clean all volatile data fields, preserve iotag and node struct. @@ -1334,7 +1347,8 @@ lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if ((unlikely(pring->ringno == LPFC_ELS_RING)) && (piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) && - (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN)) { + (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN) && + (!(piocb->vport->load_flag & FC_UNLOADING))) { if (!piocb->vport) BUG(); else @@ -3279,7 +3293,7 @@ lpfc_sli_sp_handle_rspiocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if (free_saveq) { list_for_each_entry_safe(rspiocbp, next_iocb, &saveq->list, list) { - list_del(&rspiocbp->list); + list_del_init(&rspiocbp->list); __lpfc_sli_release_iocbq(phba, rspiocbp); } __lpfc_sli_release_iocbq(phba, saveq); @@ -3518,14 +3532,27 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) /* Error everything on txq and txcmplq * First do the txq. */ - spin_lock_irq(&phba->hbalock); - list_splice_init(&pring->txq, &completions); + if (phba->sli_rev >= LPFC_SLI_REV4) { + spin_lock_irq(&pring->ring_lock); + list_splice_init(&pring->txq, &completions); + pring->txq_cnt = 0; + spin_unlock_irq(&pring->ring_lock); - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) - lpfc_sli_issue_abort_iotag(phba, pring, iocb); + spin_lock_irq(&phba->hbalock); + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) + lpfc_sli_issue_abort_iotag(phba, pring, iocb); + spin_unlock_irq(&phba->hbalock); + } else { + spin_lock_irq(&phba->hbalock); + list_splice_init(&pring->txq, &completions); + pring->txq_cnt = 0; - spin_unlock_irq(&phba->hbalock); + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) + lpfc_sli_issue_abort_iotag(phba, pring, iocb); + spin_unlock_irq(&phba->hbalock); + } /* Cancel all the IOCBs from the completions list */ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, @@ -3533,6 +3560,36 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) } /** + * lpfc_sli_abort_fcp_rings - Abort all iocbs in all FCP rings + * @phba: Pointer to HBA context object. + * @pring: Pointer to driver SLI ring object. + * + * This function aborts all iocbs in FCP rings and frees all the iocb + * objects in txq. This function issues an abort iocb for all the iocb commands + * in txcmplq. The iocbs in the txcmplq is not guaranteed to complete before + * the return of this function. The caller is not required to hold any locks. + **/ +void +lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + uint32_t i; + + /* Look on all the FCP Rings for the iotag */ + if (phba->sli_rev >= LPFC_SLI_REV4) { + for (i = 0; i < phba->cfg_fcp_io_channel; i++) { + pring = &psli->ring[i + MAX_SLI3_CONFIGURED_RINGS]; + lpfc_sli_abort_iocb_ring(phba, pring); + } + } else { + pring = &psli->ring[psli->fcp_ring]; + lpfc_sli_abort_iocb_ring(phba, pring); + } +} + + +/** * lpfc_sli_flush_fcp_rings - flush all iocbs in the fcp ring * @phba: Pointer to HBA context object. * @@ -3549,28 +3606,55 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) LIST_HEAD(txcmplq); struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring; - - /* Currently, only one fcp ring */ - pring = &psli->ring[psli->fcp_ring]; + uint32_t i; spin_lock_irq(&phba->hbalock); - /* Retrieve everything on txq */ - list_splice_init(&pring->txq, &txq); - - /* Retrieve everything on the txcmplq */ - list_splice_init(&pring->txcmplq, &txcmplq); - /* Indicate the I/O queues are flushed */ phba->hba_flag |= HBA_FCP_IOQ_FLUSH; spin_unlock_irq(&phba->hbalock); - /* Flush the txq */ - lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT, - IOERR_SLI_DOWN); + /* Look on all the FCP Rings for the iotag */ + if (phba->sli_rev >= LPFC_SLI_REV4) { + for (i = 0; i < phba->cfg_fcp_io_channel; i++) { + pring = &psli->ring[i + MAX_SLI3_CONFIGURED_RINGS]; + + spin_lock_irq(&pring->ring_lock); + /* Retrieve everything on txq */ + list_splice_init(&pring->txq, &txq); + /* Retrieve everything on the txcmplq */ + list_splice_init(&pring->txcmplq, &txcmplq); + pring->txq_cnt = 0; + pring->txcmplq_cnt = 0; + spin_unlock_irq(&pring->ring_lock); + + /* Flush the txq */ + lpfc_sli_cancel_iocbs(phba, &txq, + IOSTAT_LOCAL_REJECT, + IOERR_SLI_DOWN); + /* Flush the txcmpq */ + lpfc_sli_cancel_iocbs(phba, &txcmplq, + IOSTAT_LOCAL_REJECT, + IOERR_SLI_DOWN); + } + } else { + pring = &psli->ring[psli->fcp_ring]; - /* Flush the txcmpq */ - lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT, - IOERR_SLI_DOWN); + spin_lock_irq(&phba->hbalock); + /* Retrieve everything on txq */ + list_splice_init(&pring->txq, &txq); + /* Retrieve everything on the txcmplq */ + list_splice_init(&pring->txcmplq, &txcmplq); + pring->txq_cnt = 0; + pring->txcmplq_cnt = 0; + spin_unlock_irq(&phba->hbalock); + + /* Flush the txq */ + lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT, + IOERR_SLI_DOWN); + /* Flush the txcmpq */ + lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT, + IOERR_SLI_DOWN); + } } /** @@ -3973,12 +4057,13 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba) { struct lpfc_sli *psli = &phba->sli; uint16_t cfg_value; - int rc; + int rc = 0; /* Reset HBA */ lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "0295 Reset HBA Data: x%x x%x\n", - phba->pport->port_state, psli->sli_flag); + "0295 Reset HBA Data: x%x x%x x%x\n", + phba->pport->port_state, psli->sli_flag, + phba->hba_flag); /* perform board reset */ phba->fc_eventTag = 0; @@ -3991,6 +4076,12 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba) phba->fcf.fcf_flag = 0; spin_unlock_irq(&phba->hbalock); + /* SLI4 INTF 2: if FW dump is being taken skip INIT_PORT */ + if (phba->hba_flag & HBA_FW_DUMP_OP) { + phba->hba_flag &= ~HBA_FW_DUMP_OP; + return rc; + } + /* Now physically reset the device */ lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "0389 Performing PCI function reset!\n"); @@ -4584,7 +4675,8 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba) } else { lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "2708 This device does not support " - "Advanced Error Reporting (AER)\n"); + "Advanced Error Reporting (AER): %d\n", + rc); phba->cfg_aer_support = 0; } } @@ -4986,12 +5078,19 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) LPFC_QUEUE_REARM); } while (++fcp_eqidx < phba->cfg_fcp_io_channel); } + + if (phba->cfg_fof) + lpfc_sli4_cq_release(phba->sli4_hba.oas_cq, LPFC_QUEUE_REARM); + if (phba->sli4_hba.hba_eq) { for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_io_channel; fcp_eqidx++) lpfc_sli4_eq_release(phba->sli4_hba.hba_eq[fcp_eqidx], LPFC_QUEUE_REARM); } + + if (phba->cfg_fof) + lpfc_sli4_eq_release(phba->sli4_hba.fof_eq, LPFC_QUEUE_REARM); } /** @@ -6180,6 +6279,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) kfree(vpd); goto out_free_mbox; } + mqe = &mboxq->u.mqe; phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev); if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev)) @@ -6266,6 +6366,16 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) phba->vpd.rev.fcphHigh, phba->vpd.rev.fcphLow, phba->vpd.rev.feaLevelHigh, phba->vpd.rev.feaLevelLow); + /* Reset the DFT_LUN_Q_DEPTH to (max xri >> 3) */ + rc = (phba->sli4_hba.max_cfg_param.max_xri >> 3); + if (phba->pport->cfg_lun_queue_depth > rc) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "3362 LUN queue depth changed from %d to %d\n", + phba->pport->cfg_lun_queue_depth, rc); + phba->pport->cfg_lun_queue_depth = rc; + } + + /* * Discover the port's supported feature set and match it against the * hosts requests. @@ -6572,6 +6682,108 @@ lpfc_mbox_timeout(unsigned long ptr) return; } +/** + * lpfc_sli4_mbox_completions_pending - check to see if any mailbox completions + * are pending + * @phba: Pointer to HBA context object. + * + * This function checks if any mailbox completions are present on the mailbox + * completion queue. + **/ +bool +lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba) +{ + + uint32_t idx; + struct lpfc_queue *mcq; + struct lpfc_mcqe *mcqe; + bool pending_completions = false; + + if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4)) + return false; + + /* Check for completions on mailbox completion queue */ + + mcq = phba->sli4_hba.mbx_cq; + idx = mcq->hba_index; + while (bf_get_le32(lpfc_cqe_valid, mcq->qe[idx].cqe)) { + mcqe = (struct lpfc_mcqe *)mcq->qe[idx].cqe; + if (bf_get_le32(lpfc_trailer_completed, mcqe) && + (!bf_get_le32(lpfc_trailer_async, mcqe))) { + pending_completions = true; + break; + } + idx = (idx + 1) % mcq->entry_count; + if (mcq->hba_index == idx) + break; + } + return pending_completions; + +} + +/** + * lpfc_sli4_process_missed_mbox_completions - process mbox completions + * that were missed. + * @phba: Pointer to HBA context object. + * + * For sli4, it is possible to miss an interrupt. As such mbox completions + * maybe missed causing erroneous mailbox timeouts to occur. This function + * checks to see if mbox completions are on the mailbox completion queue + * and will process all the completions associated with the eq for the + * mailbox completion queue. + **/ +bool +lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba) +{ + + uint32_t eqidx; + struct lpfc_queue *fpeq = NULL; + struct lpfc_eqe *eqe; + bool mbox_pending; + + if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4)) + return false; + + /* Find the eq associated with the mcq */ + + if (phba->sli4_hba.hba_eq) + for (eqidx = 0; eqidx < phba->cfg_fcp_io_channel; eqidx++) + if (phba->sli4_hba.hba_eq[eqidx]->queue_id == + phba->sli4_hba.mbx_cq->assoc_qid) { + fpeq = phba->sli4_hba.hba_eq[eqidx]; + break; + } + if (!fpeq) + return false; + + /* Turn off interrupts from this EQ */ + + lpfc_sli4_eq_clr_intr(fpeq); + + /* Check to see if a mbox completion is pending */ + + mbox_pending = lpfc_sli4_mbox_completions_pending(phba); + + /* + * If a mbox completion is pending, process all the events on EQ + * associated with the mbox completion queue (this could include + * mailbox commands, async events, els commands, receive queue data + * and fcp commands) + */ + + if (mbox_pending) + while ((eqe = lpfc_sli4_eq_get(fpeq))) { + lpfc_sli4_hba_handle_eqe(phba, eqe, eqidx); + fpeq->EQ_processed++; + } + + /* Always clear and re-arm the EQ */ + + lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM); + + return mbox_pending; + +} /** * lpfc_mbox_timeout_handler - Worker thread function to handle mailbox timeout @@ -6587,7 +6799,10 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba) LPFC_MBOXQ_t *pmbox = phba->sli.mbox_active; MAILBOX_t *mb = &pmbox->u.mb; struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; + + /* If the mailbox completed, process the completion and return */ + if (lpfc_sli4_process_missed_mbox_completions(phba)) + return; /* Check the pmbox pointer first. There is a race condition * between the mbox timeout handler getting executed in the @@ -6625,8 +6840,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba) psli->sli_flag &= ~LPFC_SLI_ACTIVE; spin_unlock_irq(&phba->hbalock); - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); + lpfc_sli_abort_fcp_rings(phba); lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "0345 Resetting board due to mailbox timeout\n"); @@ -7083,6 +7297,10 @@ lpfc_sli4_async_mbox_block(struct lpfc_hba *phba) 1000) + jiffies; spin_unlock_irq(&phba->hbalock); + /* Make sure the mailbox is really active */ + if (timeout) + lpfc_sli4_process_missed_mbox_completions(phba); + /* Wait for the outstnading mailbox command to complete */ while (phba->sli.mbox_active) { /* Check active mailbox complete status every 2ms */ @@ -7926,7 +8144,8 @@ lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba) struct lpfc_vector_map_info *cpup; int chann, cpu; - if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU) { + if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU + && phba->cfg_fcp_io_channel > 1) { cpu = smp_processor_id(); if (cpu < phba->sli4_hba.num_present_cpu) { cpup = phba->sli4_hba.cpu_map; @@ -7989,6 +8208,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, abort_tag = (uint32_t) iocbq->iotag; xritag = iocbq->sli4_xritag; wqe->generic.wqe_com.word7 = 0; /* The ct field has moved so reset */ + wqe->generic.wqe_com.word10 = 0; /* words0-2 bpl convert bde */ if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) { numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize / @@ -8082,6 +8302,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_qosd, &wqe->els_req.wqe_com, 1); bf_set(wqe_lenloc, &wqe->els_req.wqe_com, LPFC_WQE_LENLOC_NONE); bf_set(wqe_ebde_cnt, &wqe->els_req.wqe_com, 0); + wqe->els_req.max_response_payload_len = total_len - xmit_len; break; case CMD_XMIT_SEQUENCE64_CX: bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com, @@ -8126,8 +8347,10 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, command_type = FCP_COMMAND_DATA_OUT; /* word3 iocb=iotag wqe=payload_offset_len */ /* Add the FCP_CMD and FCP_RSP sizes to get the offset */ - wqe->fcp_iwrite.payload_offset_len = - xmit_len + sizeof(struct fcp_rsp); + bf_set(payload_offset_len, &wqe->fcp_iwrite, + xmit_len + sizeof(struct fcp_rsp)); + bf_set(cmd_buff_len, &wqe->fcp_iwrite, + 0); /* word4 iocb=parameter wqe=total_xfer_length memcpy */ /* word5 iocb=initial_xfer_len wqe=initial_xfer_len memcpy */ bf_set(wqe_erp, &wqe->fcp_iwrite.wqe_com, @@ -8141,12 +8364,22 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0); bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU); bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1); + if (iocbq->iocb_flag & LPFC_IO_OAS) { + bf_set(wqe_oas, &wqe->fcp_iwrite.wqe_com, 1); + if (phba->cfg_XLanePriority) { + bf_set(wqe_ccpe, &wqe->fcp_iwrite.wqe_com, 1); + bf_set(wqe_ccp, &wqe->fcp_iwrite.wqe_com, + (phba->cfg_XLanePriority << 1)); + } + } break; case CMD_FCP_IREAD64_CR: /* word3 iocb=iotag wqe=payload_offset_len */ /* Add the FCP_CMD and FCP_RSP sizes to get the offset */ - wqe->fcp_iread.payload_offset_len = - xmit_len + sizeof(struct fcp_rsp); + bf_set(payload_offset_len, &wqe->fcp_iread, + xmit_len + sizeof(struct fcp_rsp)); + bf_set(cmd_buff_len, &wqe->fcp_iread, + 0); /* word4 iocb=parameter wqe=total_xfer_length memcpy */ /* word5 iocb=initial_xfer_len wqe=initial_xfer_len memcpy */ bf_set(wqe_erp, &wqe->fcp_iread.wqe_com, @@ -8160,10 +8393,23 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0); bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU); bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1); + if (iocbq->iocb_flag & LPFC_IO_OAS) { + bf_set(wqe_oas, &wqe->fcp_iread.wqe_com, 1); + if (phba->cfg_XLanePriority) { + bf_set(wqe_ccpe, &wqe->fcp_iread.wqe_com, 1); + bf_set(wqe_ccp, &wqe->fcp_iread.wqe_com, + (phba->cfg_XLanePriority << 1)); + } + } break; case CMD_FCP_ICMND64_CR: + /* word3 iocb=iotag wqe=payload_offset_len */ + /* Add the FCP_CMD and FCP_RSP sizes to get the offset */ + bf_set(payload_offset_len, &wqe->fcp_icmd, + xmit_len + sizeof(struct fcp_rsp)); + bf_set(cmd_buff_len, &wqe->fcp_icmd, + 0); /* word3 iocb=IO_TAG wqe=reserved */ - wqe->fcp_icmd.rsrvd3 = 0; bf_set(wqe_pu, &wqe->fcp_icmd.wqe_com, 0); /* Always open the exchange */ bf_set(wqe_xc, &wqe->fcp_icmd.wqe_com, 0); @@ -8175,6 +8421,14 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_ebde_cnt, &wqe->fcp_icmd.wqe_com, 0); bf_set(wqe_erp, &wqe->fcp_icmd.wqe_com, iocbq->iocb.ulpFCP2Rcvy); + if (iocbq->iocb_flag & LPFC_IO_OAS) { + bf_set(wqe_oas, &wqe->fcp_icmd.wqe_com, 1); + if (phba->cfg_XLanePriority) { + bf_set(wqe_ccpe, &wqe->fcp_icmd.wqe_com, 1); + bf_set(wqe_ccp, &wqe->fcp_icmd.wqe_com, + (phba->cfg_XLanePriority << 1)); + } + } break; case CMD_GEN_REQUEST64_CR: /* For this command calculate the xmit length of the @@ -8209,6 +8463,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_qosd, &wqe->gen_req.wqe_com, 1); bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE); bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0); + wqe->gen_req.max_response_payload_len = total_len - xmit_len; command_type = OTHER_COMMAND; break; case CMD_XMIT_ELS_RSP64_CX: @@ -8406,6 +8661,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, { struct lpfc_sglq *sglq; union lpfc_wqe wqe; + struct lpfc_queue *wq; struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number]; if (piocb->sli4_xritag == NO_XRI) { @@ -8458,11 +8714,13 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, return IOCB_ERROR; if ((piocb->iocb_flag & LPFC_IO_FCP) || - (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - if (unlikely(!phba->sli4_hba.fcp_wq)) - return IOCB_ERROR; - if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx], - &wqe)) + (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { + if (!phba->cfg_fof || (!(piocb->iocb_flag & LPFC_IO_OAS))) { + wq = phba->sli4_hba.fcp_wq[piocb->fcp_wqidx]; + } else { + wq = phba->sli4_hba.oas_wq; + } + if (lpfc_sli4_wq_put(wq, &wqe)) return IOCB_ERROR; } else { if (unlikely(!phba->sli4_hba.els_wq)) @@ -8552,12 +8810,20 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, if (phba->sli_rev == LPFC_SLI_REV4) { if (piocb->iocb_flag & LPFC_IO_FCP) { - if (unlikely(!phba->sli4_hba.fcp_wq)) - return IOCB_ERROR; - idx = lpfc_sli4_scmd_to_wqidx_distr(phba); - piocb->fcp_wqidx = idx; - ring_number = MAX_SLI3_CONFIGURED_RINGS + idx; - + if (!phba->cfg_fof || (!(piocb->iocb_flag & + LPFC_IO_OAS))) { + if (unlikely(!phba->sli4_hba.fcp_wq)) + return IOCB_ERROR; + idx = lpfc_sli4_scmd_to_wqidx_distr(phba); + piocb->fcp_wqidx = idx; + ring_number = MAX_SLI3_CONFIGURED_RINGS + idx; + } else { + if (unlikely(!phba->sli4_hba.oas_wq)) + return IOCB_ERROR; + idx = 0; + piocb->fcp_wqidx = 0; + ring_number = LPFC_FCP_OAS_RING; + } pring = &phba->sli.ring[ring_number]; spin_lock_irqsave(&pring->ring_lock, iflags); rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, @@ -8731,7 +8997,7 @@ lpfc_sli4_abts_err_handler(struct lpfc_hba *phba, lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, "3116 Port generated FCP XRI ABORT event on " "vpi %d rpi %d xri x%x status 0x%x parameter x%x\n", - ndlp->vport->vpi, ndlp->nlp_rpi, + ndlp->vport->vpi, phba->sli4_hba.rpi_ids[ndlp->nlp_rpi], bf_get(lpfc_wcqe_xa_xri, axri), bf_get(lpfc_wcqe_xa_status, axri), axri->parameter); @@ -8979,6 +9245,7 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba) pring->sli.sli3.next_cmdidx = 0; pring->sli.sli3.local_getidx = 0; pring->sli.sli3.cmdidx = 0; + pring->flag = 0; INIT_LIST_HEAD(&pring->txq); INIT_LIST_HEAD(&pring->txcmplq); INIT_LIST_HEAD(&pring->iocb_continueq); @@ -9614,43 +9881,6 @@ abort_iotag_exit: } /** - * lpfc_sli_iocb_ring_abort - Unconditionally abort all iocbs on an iocb ring - * @phba: Pointer to HBA context object. - * @pring: Pointer to driver SLI ring object. - * - * This function aborts all iocbs in the given ring and frees all the iocb - * objects in txq. This function issues abort iocbs unconditionally for all - * the iocb commands in txcmplq. The iocbs in the txcmplq is not guaranteed - * to complete before the return of this function. The caller is not required - * to hold any locks. - **/ -static void -lpfc_sli_iocb_ring_abort(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) -{ - LIST_HEAD(completions); - struct lpfc_iocbq *iocb, *next_iocb; - - if (pring->ringno == LPFC_ELS_RING) - lpfc_fabric_abort_hba(phba); - - spin_lock_irq(&phba->hbalock); - - /* Take off all the iocbs on txq for cancelling */ - list_splice_init(&pring->txq, &completions); - pring->txq_cnt = 0; - - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) - lpfc_sli_abort_iotag_issue(phba, pring, iocb); - - spin_unlock_irq(&phba->hbalock); - - /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); -} - -/** * lpfc_sli_hba_iocb_abort - Abort all iocbs to an hba. * @phba: pointer to lpfc HBA data structure. * @@ -9665,7 +9895,7 @@ lpfc_sli_hba_iocb_abort(struct lpfc_hba *phba) for (i = 0; i < psli->num_rings; i++) { pring = &psli->ring[i]; - lpfc_sli_iocb_ring_abort(phba, pring); + lpfc_sli_abort_iocb_ring(phba, pring); } } @@ -9787,7 +10017,7 @@ lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_iocbq *rspiocb) { lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "3096 ABORT_XRI_CN completing on xri x%x " + "3096 ABORT_XRI_CN completing on rpi x%x " "original iotag x%x, abort cmd iotag x%x " "status 0x%x, reason 0x%x\n", cmdiocb->iocb.un.acxri.abortContextTag, @@ -9837,6 +10067,13 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, abort_cmd) != 0) continue; + /* + * If the iocbq is already being aborted, don't take a second + * action, but do count it. + */ + if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED) + continue; + /* issue ABTS for this IOCB based on iotag */ abtsiocb = lpfc_sli_get_iocbq(phba); if (abtsiocb == NULL) { @@ -9844,6 +10081,9 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, continue; } + /* indicate the IO is being aborted by the driver. */ + iocbq->iocb_flag |= LPFC_DRIVER_ABORTED; + cmd = &iocbq->iocb; abtsiocb->iocb.un.acxri.abortType = ABORT_TYPE_ABTS; abtsiocb->iocb.un.acxri.abortContextTag = cmd->ulpContext; @@ -9853,7 +10093,7 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag; abtsiocb->iocb.ulpLe = 1; abtsiocb->iocb.ulpClass = cmd->ulpClass; - abtsiocb->vport = phba->pport; + abtsiocb->vport = vport; /* ABTS WQE must go to the same WQ as the WQE to be aborted */ abtsiocb->fcp_wqidx = iocbq->fcp_wqidx; @@ -9880,6 +10120,124 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, } /** + * lpfc_sli_abort_taskmgmt - issue abort for all commands on a host/target/LUN + * @vport: Pointer to virtual port. + * @pring: Pointer to driver SLI ring object. + * @tgt_id: SCSI ID of the target. + * @lun_id: LUN ID of the scsi device. + * @taskmgmt_cmd: LPFC_CTX_LUN/LPFC_CTX_TGT/LPFC_CTX_HOST. + * + * This function sends an abort command for every SCSI command + * associated with the given virtual port pending on the ring + * filtered by lpfc_sli_validate_fcp_iocb function. + * When taskmgmt_cmd == LPFC_CTX_LUN, the function sends abort only to the + * FCP iocbs associated with lun specified by tgt_id and lun_id + * parameters + * When taskmgmt_cmd == LPFC_CTX_TGT, the function sends abort only to the + * FCP iocbs associated with SCSI target specified by tgt_id parameter. + * When taskmgmt_cmd == LPFC_CTX_HOST, the function sends abort to all + * FCP iocbs associated with virtual port. + * This function returns number of iocbs it aborted . + * This function is called with no locks held right after a taskmgmt + * command is sent. + **/ +int +lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, + uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_iocbq *abtsiocbq; + struct lpfc_iocbq *iocbq; + IOCB_t *icmd; + int sum, i, ret_val; + unsigned long iflags; + struct lpfc_sli_ring *pring_s4; + uint32_t ring_number; + + spin_lock_irq(&phba->hbalock); + + /* all I/Os are in process of being flushed */ + if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) { + spin_unlock_irq(&phba->hbalock); + return 0; + } + sum = 0; + + for (i = 1; i <= phba->sli.last_iotag; i++) { + iocbq = phba->sli.iocbq_lookup[i]; + + if (lpfc_sli_validate_fcp_iocb(iocbq, vport, tgt_id, lun_id, + cmd) != 0) + continue; + + /* + * If the iocbq is already being aborted, don't take a second + * action, but do count it. + */ + if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED) + continue; + + /* issue ABTS for this IOCB based on iotag */ + abtsiocbq = __lpfc_sli_get_iocbq(phba); + if (abtsiocbq == NULL) + continue; + + icmd = &iocbq->iocb; + abtsiocbq->iocb.un.acxri.abortType = ABORT_TYPE_ABTS; + abtsiocbq->iocb.un.acxri.abortContextTag = icmd->ulpContext; + if (phba->sli_rev == LPFC_SLI_REV4) + abtsiocbq->iocb.un.acxri.abortIoTag = + iocbq->sli4_xritag; + else + abtsiocbq->iocb.un.acxri.abortIoTag = icmd->ulpIoTag; + abtsiocbq->iocb.ulpLe = 1; + abtsiocbq->iocb.ulpClass = icmd->ulpClass; + abtsiocbq->vport = vport; + + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abtsiocbq->fcp_wqidx = iocbq->fcp_wqidx; + if (iocbq->iocb_flag & LPFC_IO_FCP) + abtsiocbq->iocb_flag |= LPFC_USE_FCPWQIDX; + + if (lpfc_is_link_up(phba)) + abtsiocbq->iocb.ulpCommand = CMD_ABORT_XRI_CN; + else + abtsiocbq->iocb.ulpCommand = CMD_CLOSE_XRI_CN; + + /* Setup callback routine and issue the command. */ + abtsiocbq->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; + + /* + * Indicate the IO is being aborted by the driver and set + * the caller's flag into the aborted IO. + */ + iocbq->iocb_flag |= LPFC_DRIVER_ABORTED; + + if (phba->sli_rev == LPFC_SLI_REV4) { + ring_number = MAX_SLI3_CONFIGURED_RINGS + + iocbq->fcp_wqidx; + pring_s4 = &phba->sli.ring[ring_number]; + /* Note: both hbalock and ring_lock must be set here */ + spin_lock_irqsave(&pring_s4->ring_lock, iflags); + ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, + abtsiocbq, 0); + spin_unlock_irqrestore(&pring_s4->ring_lock, iflags); + } else { + ret_val = __lpfc_sli_issue_iocb(phba, pring->ringno, + abtsiocbq, 0); + } + + + if (ret_val == IOCB_ERROR) + __lpfc_sli_release_iocbq(phba, abtsiocbq); + else + sum++; + } + spin_unlock_irq(&phba->hbalock); + return sum; +} + +/** * lpfc_sli_wake_iocb_wait - lpfc_sli_issue_iocb_wait's completion handler * @phba: Pointer to HBA context object. * @cmdiocbq: Pointer to command iocb. @@ -9906,6 +10264,24 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd; spin_lock_irqsave(&phba->hbalock, iflags); + if (cmdiocbq->iocb_flag & LPFC_IO_WAKE_TMO) { + + /* + * A time out has occurred for the iocb. If a time out + * completion handler has been supplied, call it. Otherwise, + * just free the iocbq. + */ + + spin_unlock_irqrestore(&phba->hbalock, iflags); + cmdiocbq->iocb_cmpl = cmdiocbq->wait_iocb_cmpl; + cmdiocbq->wait_iocb_cmpl = NULL; + if (cmdiocbq->iocb_cmpl) + (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, NULL); + else + lpfc_sli_release_iocbq(phba, cmdiocbq); + return; + } + cmdiocbq->iocb_flag |= LPFC_IO_WAKE; if (cmdiocbq->context2 && rspiocbq) memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, @@ -9961,10 +10337,16 @@ lpfc_chk_iocb_flg(struct lpfc_hba *phba, * @timeout: Timeout in number of seconds. * * This function issues the iocb to firmware and waits for the - * iocb to complete. If the iocb command is not - * completed within timeout seconds, it returns IOCB_TIMEDOUT. - * Caller should not free the iocb resources if this function - * returns IOCB_TIMEDOUT. + * iocb to complete. The iocb_cmpl field of the shall be used + * to handle iocbs which time out. If the field is NULL, the + * function shall free the iocbq structure. If more clean up is + * needed, the caller is expected to provide a completion function + * that will provide the needed clean up. If the iocb command is + * not completed within timeout seconds, the function will either + * free the iocbq structure (if iocb_cmpl == NULL) or execute the + * completion function set in the iocb_cmpl field and then return + * a status of IOCB_TIMEDOUT. The caller should not free the iocb + * resources if this function returns IOCB_TIMEDOUT. * The function waits for the iocb completion using an * non-interruptible wait. * This function will sleep while waiting for iocb completion. @@ -9997,6 +10379,9 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba, int txq_cnt = 0; int txcmplq_cnt = 0; struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + unsigned long iflags; + bool iocb_completed = true; + /* * If the caller has provided a response iocbq buffer, then context2 * is NULL or its an error. @@ -10007,9 +10392,10 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba, piocb->context2 = prspiocbq; } + piocb->wait_iocb_cmpl = piocb->iocb_cmpl; piocb->iocb_cmpl = lpfc_sli_wake_iocb_wait; piocb->context_un.wait_queue = &done_q; - piocb->iocb_flag &= ~LPFC_IO_WAKE; + piocb->iocb_flag &= ~(LPFC_IO_WAKE | LPFC_IO_WAKE_TMO); if (phba->cfg_poll & DISABLE_FCP_RING_INT) { if (lpfc_readl(phba->HCregaddr, &creg_val)) @@ -10026,10 +10412,26 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba, timeleft = wait_event_timeout(done_q, lpfc_chk_iocb_flg(phba, piocb, LPFC_IO_WAKE), timeout_req); + spin_lock_irqsave(&phba->hbalock, iflags); + if (!(piocb->iocb_flag & LPFC_IO_WAKE)) { - if (piocb->iocb_flag & LPFC_IO_WAKE) { + /* + * IOCB timed out. Inform the wake iocb wait + * completion function and set local status + */ + + iocb_completed = false; + piocb->iocb_flag |= LPFC_IO_WAKE_TMO; + } + spin_unlock_irqrestore(&phba->hbalock, iflags); + if (iocb_completed) { lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "0331 IOCB wake signaled\n"); + /* Note: we are not indicating if the IOCB has a success + * status or not - that's for the caller to check. + * IOCB_SUCCESS means just that the command was sent and + * completed. Not that it completed successfully. + * */ } else if (timeleft == 0) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0338 IOCB wait timeout error - no " @@ -10109,12 +10511,13 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq, uint32_t timeout) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_q); + MAILBOX_t *mb = NULL; int retval; unsigned long flag; - /* The caller must leave context1 empty. */ + /* The caller might set context1 for extended buffer */ if (pmboxq->context1) - return MBX_NOT_FINISHED; + mb = (MAILBOX_t *)pmboxq->context1; pmboxq->mbox_flag &= ~LPFC_MBX_WAKE; /* setup wake call as IOCB callback */ @@ -10130,19 +10533,22 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq, msecs_to_jiffies(timeout * 1000)); spin_lock_irqsave(&phba->hbalock, flag); - pmboxq->context1 = NULL; + /* restore the possible extended buffer for free resource */ + pmboxq->context1 = (uint8_t *)mb; /* * if LPFC_MBX_WAKE flag is set the mailbox is completed * else do not free the resources. */ if (pmboxq->mbox_flag & LPFC_MBX_WAKE) { retval = MBX_SUCCESS; - lpfc_sli4_swap_str(phba, pmboxq); } else { retval = MBX_TIMEOUT; pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl; } spin_unlock_irqrestore(&phba->hbalock, flag); + } else { + /* restore the possible extended buffer for free resource */ + pmboxq->context1 = (uint8_t *)mb; } return retval; @@ -11027,8 +11433,11 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbOut, struct lpfc_wcqe_complete *wcqe) { + int numBdes, i; unsigned long iflags; - uint32_t status; + uint32_t status, max_response; + struct lpfc_dmabuf *dmabuf; + struct ulp_bde64 *bpl, bde; size_t offset = offsetof(struct lpfc_iocbq, iocb); memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset, @@ -11045,7 +11454,36 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba, pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter; else { pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter; - pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed; + switch (pIocbOut->iocb.ulpCommand) { + case CMD_ELS_REQUEST64_CR: + dmabuf = (struct lpfc_dmabuf *)pIocbOut->context3; + bpl = (struct ulp_bde64 *)dmabuf->virt; + bde.tus.w = le32_to_cpu(bpl[1].tus.w); + max_response = bde.tus.f.bdeSize; + break; + case CMD_GEN_REQUEST64_CR: + max_response = 0; + if (!pIocbOut->context3) + break; + numBdes = pIocbOut->iocb.un.genreq64.bdl.bdeSize/ + sizeof(struct ulp_bde64); + dmabuf = (struct lpfc_dmabuf *)pIocbOut->context3; + bpl = (struct ulp_bde64 *)dmabuf->virt; + for (i = 0; i < numBdes; i++) { + bde.tus.w = le32_to_cpu(bpl[i].tus.w); + if (bde.tus.f.bdeFlags != BUFF_TYPE_BDE_64) + max_response += bde.tus.f.bdeSize; + } + break; + default: + max_response = wcqe->total_data_placed; + break; + } + if (max_response < wcqe->total_data_placed) + pIocbIn->iocb.un.genreq64.bdl.bdeSize = max_response; + else + pIocbIn->iocb.un.genreq64.bdl.bdeSize = + wcqe->total_data_placed; } /* Convert BG errors for completion status */ @@ -11925,6 +12363,175 @@ lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq) lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM); } + +/** + * lpfc_sli4_fof_handle_eqe - Process a Flash Optimized Fabric event queue + * entry + * @phba: Pointer to HBA context object. + * @eqe: Pointer to fast-path event queue entry. + * + * This routine process a event queue entry from the Flash Optimized Fabric + * event queue. It will check the MajorCode and MinorCode to determine this + * is for a completion event on a completion queue, if not, an error shall be + * logged and just return. Otherwise, it will get to the corresponding + * completion queue and process all the entries on the completion queue, rearm + * the completion queue, and then return. + **/ +static void +lpfc_sli4_fof_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) +{ + struct lpfc_queue *cq; + struct lpfc_cqe *cqe; + bool workposted = false; + uint16_t cqid; + int ecount = 0; + + if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "9147 Not a valid completion " + "event: majorcode=x%x, minorcode=x%x\n", + bf_get_le32(lpfc_eqe_major_code, eqe), + bf_get_le32(lpfc_eqe_minor_code, eqe)); + return; + } + + /* Get the reference to the corresponding CQ */ + cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); + + /* Next check for OAS */ + cq = phba->sli4_hba.oas_cq; + if (unlikely(!cq)) { + if (phba->sli.sli_flag & LPFC_SLI_ACTIVE) + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "9148 OAS completion queue " + "does not exist\n"); + return; + } + + if (unlikely(cqid != cq->queue_id)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "9149 Miss-matched fast-path compl " + "queue id: eqcqid=%d, fcpcqid=%d\n", + cqid, cq->queue_id); + return; + } + + /* Process all the entries to the OAS CQ */ + while ((cqe = lpfc_sli4_cq_get(cq))) { + workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, cqe); + if (!(++ecount % cq->entry_repost)) + lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM); + } + + /* Track the max number of CQEs processed in 1 EQ */ + if (ecount > cq->CQ_max_cqe) + cq->CQ_max_cqe = ecount; + + /* Catch the no cq entry condition */ + if (unlikely(ecount == 0)) + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "9153 No entry from fast-path completion " + "queue fcpcqid=%d\n", cq->queue_id); + + /* In any case, flash and re-arm the CQ */ + lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM); + + /* wake up worker thread if there are works to be done */ + if (workposted) + lpfc_worker_wake_up(phba); +} + +/** + * lpfc_sli4_fof_intr_handler - HBA interrupt handler to SLI-4 device + * @irq: Interrupt number. + * @dev_id: The device context pointer. + * + * This function is directly called from the PCI layer as an interrupt + * service routine when device with SLI-4 interface spec is enabled with + * MSI-X multi-message interrupt mode and there is a Flash Optimized Fabric + * IOCB ring event in the HBA. However, when the device is enabled with either + * MSI or Pin-IRQ interrupt mode, this function is called as part of the + * device-level interrupt handler. When the PCI slot is in error recovery + * or the HBA is undergoing initialization, the interrupt handler will not + * process the interrupt. The Flash Optimized Fabric ring event are handled in + * the intrrupt context. This function is called without any lock held. + * It gets the hbalock to access and update SLI data structures. Note that, + * the EQ to CQ are one-to-one map such that the EQ index is + * equal to that of CQ index. + * + * This function returns IRQ_HANDLED when interrupt is handled else it + * returns IRQ_NONE. + **/ +irqreturn_t +lpfc_sli4_fof_intr_handler(int irq, void *dev_id) +{ + struct lpfc_hba *phba; + struct lpfc_fcp_eq_hdl *fcp_eq_hdl; + struct lpfc_queue *eq; + struct lpfc_eqe *eqe; + unsigned long iflag; + int ecount = 0; + uint32_t eqidx; + + /* Get the driver's phba structure from the dev_id */ + fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id; + phba = fcp_eq_hdl->phba; + eqidx = fcp_eq_hdl->idx; + + if (unlikely(!phba)) + return IRQ_NONE; + + /* Get to the EQ struct associated with this vector */ + eq = phba->sli4_hba.fof_eq; + if (unlikely(!eq)) + return IRQ_NONE; + + /* Check device state for handling interrupt */ + if (unlikely(lpfc_intr_state_check(phba))) { + eq->EQ_badstate++; + /* Check again for link_state with lock held */ + spin_lock_irqsave(&phba->hbalock, iflag); + if (phba->link_state < LPFC_LINK_DOWN) + /* Flush, clear interrupt, and rearm the EQ */ + lpfc_sli4_eq_flush(phba, eq); + spin_unlock_irqrestore(&phba->hbalock, iflag); + return IRQ_NONE; + } + + /* + * Process all the event on FCP fast-path EQ + */ + while ((eqe = lpfc_sli4_eq_get(eq))) { + lpfc_sli4_fof_handle_eqe(phba, eqe); + if (!(++ecount % eq->entry_repost)) + lpfc_sli4_eq_release(eq, LPFC_QUEUE_NOARM); + eq->EQ_processed++; + } + + /* Track the max number of EQEs processed in 1 intr */ + if (ecount > eq->EQ_max_eqe) + eq->EQ_max_eqe = ecount; + + + if (unlikely(ecount == 0)) { + eq->EQ_no_entry++; + + if (phba->intr_type == MSIX) + /* MSI-X treated interrupt served as no EQ share INT */ + lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, + "9145 MSI-X interrupt with no EQE\n"); + else { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "9146 ISR interrupt with no EQE\n"); + /* Non MSI-X treated on interrupt as EQ share INT */ + return IRQ_NONE; + } + } + /* Always clear and re-arm the fast-path EQ */ + lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM); + return IRQ_HANDLED; +} + /** * lpfc_sli4_hba_intr_handler - HBA interrupt handler to SLI-4 device * @irq: Interrupt number. @@ -12080,6 +12687,13 @@ lpfc_sli4_intr_handler(int irq, void *dev_id) hba_handled |= true; } + if (phba->cfg_fof) { + hba_irq_rc = lpfc_sli4_fof_intr_handler(irq, + &phba->sli4_hba.fcp_eq_hdl[0]); + if (hba_irq_rc == IRQ_HANDLED) + hba_handled |= true; + } + return (hba_handled == true) ? IRQ_HANDLED : IRQ_NONE; } /* lpfc_sli4_intr_handler */ @@ -12196,7 +12810,6 @@ static void __iomem * lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset) { struct pci_dev *pdev; - unsigned long bar_map, bar_map_len; if (!phba->pcidev) return NULL; @@ -12205,25 +12818,10 @@ lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset) switch (pci_barset) { case WQ_PCI_BAR_0_AND_1: - if (!phba->pci_bar0_memmap_p) { - bar_map = pci_resource_start(pdev, PCI_64BIT_BAR0); - bar_map_len = pci_resource_len(pdev, PCI_64BIT_BAR0); - phba->pci_bar0_memmap_p = ioremap(bar_map, bar_map_len); - } return phba->pci_bar0_memmap_p; case WQ_PCI_BAR_2_AND_3: - if (!phba->pci_bar2_memmap_p) { - bar_map = pci_resource_start(pdev, PCI_64BIT_BAR2); - bar_map_len = pci_resource_len(pdev, PCI_64BIT_BAR2); - phba->pci_bar2_memmap_p = ioremap(bar_map, bar_map_len); - } return phba->pci_bar2_memmap_p; case WQ_PCI_BAR_4_AND_5: - if (!phba->pci_bar4_memmap_p) { - bar_map = pci_resource_start(pdev, PCI_64BIT_BAR4); - bar_map_len = pci_resource_len(pdev, PCI_64BIT_BAR4); - phba->pci_bar4_memmap_p = ioremap(bar_map, bar_map_len); - } return phba->pci_bar4_memmap_p; default: break; @@ -12832,10 +13430,44 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, wq->page_count); bf_set(lpfc_mbx_wq_create_cq_id, &wq_create->u.request, cq->queue_id); + + /* wqv is the earliest version supported, NOT the latest */ bf_set(lpfc_mbox_hdr_version, &shdr->request, phba->sli4_hba.pc_sli4_params.wqv); - if (phba->sli4_hba.pc_sli4_params.wqv == LPFC_Q_CREATE_VERSION_1) { + switch (phba->sli4_hba.pc_sli4_params.wqv) { + case LPFC_Q_CREATE_VERSION_0: + switch (wq->entry_size) { + default: + case 64: + /* Nothing to do, version 0 ONLY supports 64 byte */ + page = wq_create->u.request.page; + break; + case 128: + if (!(phba->sli4_hba.pc_sli4_params.wqsize & + LPFC_WQ_SZ128_SUPPORT)) { + status = -ERANGE; + goto out; + } + /* If we get here the HBA MUST also support V1 and + * we MUST use it + */ + bf_set(lpfc_mbox_hdr_version, &shdr->request, + LPFC_Q_CREATE_VERSION_1); + + bf_set(lpfc_mbx_wq_create_wqe_count, + &wq_create->u.request_1, wq->entry_count); + bf_set(lpfc_mbx_wq_create_wqe_size, + &wq_create->u.request_1, + LPFC_WQ_WQE_SIZE_128); + bf_set(lpfc_mbx_wq_create_page_size, + &wq_create->u.request_1, + (PAGE_SIZE/SLI4_PAGE_SIZE)); + page = wq_create->u.request_1.page; + break; + } + break; + case LPFC_Q_CREATE_VERSION_1: bf_set(lpfc_mbx_wq_create_wqe_count, &wq_create->u.request_1, wq->entry_count); switch (wq->entry_size) { @@ -12846,6 +13478,11 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_64); break; case 128: + if (!(phba->sli4_hba.pc_sli4_params.wqsize & + LPFC_WQ_SZ128_SUPPORT)) { + status = -ERANGE; + goto out; + } bf_set(lpfc_mbx_wq_create_wqe_size, &wq_create->u.request_1, LPFC_WQ_WQE_SIZE_128); @@ -12854,9 +13491,12 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, (PAGE_SIZE/SLI4_PAGE_SIZE)); page = wq_create->u.request_1.page; - } else { - page = wq_create->u.request.page; + break; + default: + status = -ERANGE; + goto out; } + list_for_each_entry(dmabuf, &wq->page_list, list) { memset(dmabuf->virt, 0, hw_page_size); page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); @@ -14677,14 +15317,20 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) first_iocbq->iocb.unsli3.rcvsli3.vpi = vport->phba->vpi_ids[vport->vpi]; /* put the first buffer into the first IOCBq */ + tot_len = bf_get(lpfc_rcqe_length, + &seq_dmabuf->cq_event.cqe.rcqe_cmpl); + first_iocbq->context2 = &seq_dmabuf->dbuf; first_iocbq->context3 = NULL; first_iocbq->iocb.ulpBdeCount = 1; - first_iocbq->iocb.un.cont64[0].tus.f.bdeSize = + if (tot_len > LPFC_DATA_BUF_SIZE) + first_iocbq->iocb.un.cont64[0].tus.f.bdeSize = LPFC_DATA_BUF_SIZE; + else + first_iocbq->iocb.un.cont64[0].tus.f.bdeSize = tot_len; + first_iocbq->iocb.un.rcvels.remoteID = sid; - tot_len = bf_get(lpfc_rcqe_length, - &seq_dmabuf->cq_event.cqe.rcqe_cmpl); + first_iocbq->iocb.unsli3.rcvsli3.acc_len = tot_len; } iocbq = first_iocbq; @@ -14700,14 +15346,17 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) if (!iocbq->context3) { iocbq->context3 = d_buf; iocbq->iocb.ulpBdeCount++; - pbde = (struct ulp_bde64 *) - &iocbq->iocb.unsli3.sli3Words[4]; - pbde->tus.f.bdeSize = LPFC_DATA_BUF_SIZE; - /* We need to get the size out of the right CQE */ hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf); len = bf_get(lpfc_rcqe_length, &hbq_buf->cq_event.cqe.rcqe_cmpl); + pbde = (struct ulp_bde64 *) + &iocbq->iocb.unsli3.sli3Words[4]; + if (len > LPFC_DATA_BUF_SIZE) + pbde->tus.f.bdeSize = LPFC_DATA_BUF_SIZE; + else + pbde->tus.f.bdeSize = len; + iocbq->iocb.unsli3.rcvsli3.acc_len += len; tot_len += len; } else { @@ -14722,16 +15371,19 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) lpfc_in_buf_free(vport->phba, d_buf); continue; } + /* We need to get the size out of the right CQE */ + hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf); + len = bf_get(lpfc_rcqe_length, + &hbq_buf->cq_event.cqe.rcqe_cmpl); iocbq->context2 = d_buf; iocbq->context3 = NULL; iocbq->iocb.ulpBdeCount = 1; - iocbq->iocb.un.cont64[0].tus.f.bdeSize = + if (len > LPFC_DATA_BUF_SIZE) + iocbq->iocb.un.cont64[0].tus.f.bdeSize = LPFC_DATA_BUF_SIZE; + else + iocbq->iocb.un.cont64[0].tus.f.bdeSize = len; - /* We need to get the size out of the right CQE */ - hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf); - len = bf_get(lpfc_rcqe_length, - &hbq_buf->cq_event.cqe.rcqe_cmpl); tot_len += len; iocbq->iocb.unsli3.rcvsli3.acc_len = tot_len; @@ -15013,6 +15665,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) uint16_t max_rpi, rpi_limit; uint16_t rpi_remaining, lrpi = 0; struct lpfc_rpi_hdr *rpi_hdr; + unsigned long iflag; max_rpi = phba->sli4_hba.max_cfg_param.max_rpi; rpi_limit = phba->sli4_hba.next_rpi; @@ -15021,7 +15674,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) * Fetch the next logical rpi. Because this index is logical, * the driver starts at 0 each time. */ - spin_lock_irq(&phba->hbalock); + spin_lock_irqsave(&phba->hbalock, iflag); rpi = find_next_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit, 0); if (rpi >= rpi_limit) rpi = LPFC_RPI_ALLOC_ERROR; @@ -15037,7 +15690,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) */ if ((rpi == LPFC_RPI_ALLOC_ERROR) && (phba->sli4_hba.rpi_count >= max_rpi)) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflag); return rpi; } @@ -15046,7 +15699,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) * extents. */ if (!phba->sli4_hba.rpi_hdrs_in_use) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflag); return rpi; } @@ -15057,7 +15710,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) * how many are supported max by the device. */ rpi_remaining = phba->sli4_hba.next_rpi - phba->sli4_hba.rpi_count; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflag); if (rpi_remaining < LPFC_RPI_LOW_WATER_MARK) { rpi_hdr = lpfc_sli4_create_rpi_hdr(phba); if (!rpi_hdr) { @@ -15717,7 +16370,7 @@ lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *phba, uint16_t fcf_index) void lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index) { - struct lpfc_fcf_pri *fcf_pri; + struct lpfc_fcf_pri *fcf_pri, *fcf_pri_next; if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { lpfc_printf_log(phba, KERN_ERR, LOG_FIP, "2762 FCF (x%x) reached driver's book " @@ -15727,7 +16380,8 @@ lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index) } /* Clear the eligible FCF record index bmask */ spin_lock_irq(&phba->hbalock); - list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) { + list_for_each_entry_safe(fcf_pri, fcf_pri_next, &phba->fcf.fcf_pri_list, + list) { if (fcf_pri->fcf_rec.fcf_index == fcf_index) { list_del_init(&fcf_pri->list); break; @@ -16297,14 +16951,14 @@ lpfc_drain_txq(struct lpfc_hba *phba) { LIST_HEAD(completions); struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; - struct lpfc_iocbq *piocbq = 0; + struct lpfc_iocbq *piocbq = NULL; unsigned long iflags = 0; char *fail_msg = NULL; struct lpfc_sglq *sglq; union lpfc_wqe wqe; int txq_cnt = 0; - spin_lock_irqsave(&phba->hbalock, iflags); + spin_lock_irqsave(&pring->ring_lock, iflags); list_for_each_entry(piocbq, &pring->txq, list) { txq_cnt++; } @@ -16312,14 +16966,14 @@ lpfc_drain_txq(struct lpfc_hba *phba) if (txq_cnt > pring->txq_max) pring->txq_max = txq_cnt; - spin_unlock_irqrestore(&phba->hbalock, iflags); + spin_unlock_irqrestore(&pring->ring_lock, iflags); while (!list_empty(&pring->txq)) { - spin_lock_irqsave(&phba->hbalock, iflags); + spin_lock_irqsave(&pring->ring_lock, iflags); piocbq = lpfc_sli_ringtx_get(phba, pring); if (!piocbq) { - spin_unlock_irqrestore(&phba->hbalock, iflags); + spin_unlock_irqrestore(&pring->ring_lock, iflags); lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "2823 txq empty and txq_cnt is %d\n ", txq_cnt); @@ -16328,7 +16982,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) sglq = __lpfc_sli_get_sglq(phba, piocbq); if (!sglq) { __lpfc_sli_ringtx_put(phba, pring, piocbq); - spin_unlock_irqrestore(&phba->hbalock, iflags); + spin_unlock_irqrestore(&pring->ring_lock, iflags); break; } txq_cnt--; @@ -16356,7 +17010,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) piocbq->iotag, piocbq->sli4_xritag); list_add_tail(&piocbq->list, &completions); } - spin_unlock_irqrestore(&phba->hbalock, iflags); + spin_unlock_irqrestore(&pring->ring_lock, iflags); } /* Cancel all the IOCBs that cannot be issued */ diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 9d2e0c6fe33..edb48832c39 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -58,9 +58,10 @@ struct lpfc_iocbq { IOCB_t iocb; /* IOCB cmd */ uint8_t retry; /* retry counter for IOCB cmd - if needed */ - uint16_t iocb_flag; + uint32_t iocb_flag; #define LPFC_IO_LIBDFC 1 /* libdfc iocb */ -#define LPFC_IO_WAKE 2 /* High Priority Queue signal flag */ +#define LPFC_IO_WAKE 2 /* Synchronous I/O completed */ +#define LPFC_IO_WAKE_TMO LPFC_IO_WAKE /* Synchronous I/O timed out */ #define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */ #define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */ #define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */ @@ -72,11 +73,13 @@ struct lpfc_iocbq { #define LPFC_IO_DIF_PASS 0x400 /* T10 DIF IO pass-thru prot */ #define LPFC_IO_DIF_STRIP 0x800 /* T10 DIF IO strip prot */ #define LPFC_IO_DIF_INSERT 0x1000 /* T10 DIF IO insert prot */ +#define LPFC_IO_CMD_OUTSTANDING 0x2000 /* timeout handler abort window */ #define LPFC_FIP_ELS_ID_MASK 0xc000 /* ELS_ID range 0-3, non-shifted mask */ #define LPFC_FIP_ELS_ID_SHIFT 14 - uint8_t rsvd2; +#define LPFC_IO_OAS 0x10000 /* OAS FCP IO */ + uint32_t drvrTimeout; /* driver timeout in seconds */ uint32_t fcp_wqidx; /* index to FCP work queue */ struct lpfc_vport *vport;/* virtual port pointer */ @@ -93,6 +96,8 @@ struct lpfc_iocbq { void (*fabric_iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); + void (*wait_iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); }; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 67af460184b..7f50aa04d66 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009-2011 Emulex. All rights reserved. * + * Copyright (C) 2009-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -39,6 +39,10 @@ #define LPFC_FCP_IO_CHAN_MIN 1 #define LPFC_FCP_IO_CHAN_MAX 16 +/* Number of channels used for Flash Optimized Fabric (FOF) operations */ + +#define LPFC_FOF_IO_CHAN_NUM 1 + /* * Provide the default FCF Record attributes used by the driver * when nonFIP mode is configured and there is no other default @@ -117,6 +121,7 @@ union sli4_qe { struct lpfc_rcqe_complete *rcqe_complete; struct lpfc_mqe *mqe; union lpfc_wqe *wqe; + union lpfc_wqe128 *wqe128; struct lpfc_rqe *rqe; }; @@ -325,12 +330,14 @@ struct lpfc_bmbx { #define LPFC_EQE_SIZE_16B 16 #define LPFC_CQE_SIZE 16 #define LPFC_WQE_SIZE 64 +#define LPFC_WQE128_SIZE 128 #define LPFC_MQE_SIZE 256 #define LPFC_RQE_SIZE 8 #define LPFC_EQE_DEF_COUNT 1024 #define LPFC_CQE_DEF_COUNT 1024 #define LPFC_WQE_DEF_COUNT 256 +#define LPFC_WQE128_DEF_COUNT 128 #define LPFC_MQE_DEF_COUNT 16 #define LPFC_RQE_DEF_COUNT 512 @@ -396,6 +403,7 @@ struct lpfc_pc_sli4_params { uint32_t if_page_sz; uint32_t rq_db_window; uint32_t loopbk_scope; + uint32_t oas_supported; uint32_t eq_pages_max; uint32_t eqe_size; uint32_t cq_pages_max; @@ -416,6 +424,9 @@ struct lpfc_pc_sli4_params { uint8_t mqv; uint8_t wqv; uint8_t rqv; + uint8_t wqsize; +#define LPFC_WQ_SZ64_SUPPORT 1 +#define LPFC_WQ_SZ128_SUPPORT 2 }; struct lpfc_iov { @@ -433,6 +444,8 @@ struct lpfc_sli4_lnk_info { uint8_t lnk_no; }; +#define LPFC_SLI4_HANDLER_CNT (LPFC_FCP_IO_CHAN_MAX+ \ + LPFC_FOF_IO_CHAN_NUM) #define LPFC_SLI4_HANDLER_NAME_SZ 16 /* Used for IRQ vector to CPU mapping */ @@ -444,7 +457,6 @@ struct lpfc_vector_map_info { struct cpumask maskbits; }; #define LPFC_VECTOR_MAP_EMPTY 0xffff -#define LPFC_MAX_CPU 256 /* SLI4 HBA data structure entries */ struct lpfc_sli4_hba { @@ -502,7 +514,7 @@ struct lpfc_sli4_hba { struct lpfc_register sli_intf; struct lpfc_pc_sli4_params pc_sli4_params; struct msix_entry *msix_entries; - uint8_t handler_name[LPFC_FCP_IO_CHAN_MAX][LPFC_SLI4_HANDLER_NAME_SZ]; + uint8_t handler_name[LPFC_SLI4_HANDLER_CNT][LPFC_SLI4_HANDLER_NAME_SZ]; struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */ /* Pointers to the constructed SLI4 queues */ @@ -518,10 +530,21 @@ struct lpfc_sli4_hba { struct lpfc_queue *hdr_rq; /* Slow-path Header Receive queue */ struct lpfc_queue *dat_rq; /* Slow-path Data Receive queue */ - uint8_t fw_func_mode; /* FW function protocol mode */ + uint32_t fw_func_mode; /* FW function protocol mode */ uint32_t ulp0_mode; /* ULP0 protocol mode */ uint32_t ulp1_mode; /* ULP1 protocol mode */ + struct lpfc_queue *fof_eq; /* Flash Optimized Fabric Event queue */ + + /* Optimized Access Storage specific queues/structures */ + + struct lpfc_queue *oas_cq; /* OAS completion queue */ + struct lpfc_queue *oas_wq; /* OAS Work queue */ + struct lpfc_sli_ring *oas_ring; + uint64_t oas_next_lun; + uint8_t oas_next_tgt_wwpn[8]; + uint8_t oas_next_vpt_wwpn[8]; + /* Setup information for various queue parameters */ int eq_esize; int eq_ecount; @@ -584,6 +607,7 @@ struct lpfc_sli4_hba { struct lpfc_vector_map_info *cpu_map; uint16_t num_online_cpu; uint16_t num_present_cpu; + uint16_t curr_disp_cpu; }; enum lpfc_sge_type { @@ -668,6 +692,7 @@ void lpfc_sli4_queue_unset(struct lpfc_hba *); int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t); int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *); uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *); +void lpfc_sli4_free_xri(struct lpfc_hba *, int); int lpfc_sli4_post_async_mbox(struct lpfc_hba *); int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int); struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index a38dc3b1696..41675c1193e 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.3.39" +#define LPFC_DRIVER_VERSION "10.2.8001.0." #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ @@ -30,4 +30,4 @@ #define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ LPFC_DRIVER_VERSION -#define LPFC_COPYRIGHT "Copyright(c) 2004-2009 Emulex. All rights reserved." +#define LPFC_COPYRIGHT "Copyright(c) 2004-2014 Emulex. All rights reserved." diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index e28e431564b..a87ee33f4f2 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2008 Emulex. All rights reserved. * + * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -387,6 +387,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) /* Create binary sysfs attribute for vport */ lpfc_alloc_sysfs_attr(vport); + /* Set the DFT_LUN_Q_DEPTH accordingly */ + vport->cfg_lun_queue_depth = phba->pport->cfg_lun_queue_depth; + *(struct lpfc_vport **)fc_vport->dd_data = vport; vport->fc_vport = fc_vport; |
