/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2004-2012 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * * Public License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID. See the GNU General Public License for * * more details, a copy of which can be found in the file COPYING * * included with this package. * *******************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lpfc_hw4.h" #include "lpfc_hw.h" #include "lpfc_sli.h" #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" #include "lpfc_scsi.h" #include "lpfc.h" #include "lpfc_logmsg.h" #include "lpfc_version.h" #include "lpfc_compat.h" #include "lpfc_crtn.h" #include "lpfc_vport.h" #define LPFC_DEF_DEVLOSS_TMO 30 #define LPFC_MIN_DEVLOSS_TMO 1 #define LPFC_MAX_DEVLOSS_TMO 255 /* * Write key size should be multiple of 4. If write key is changed * make sure that library write key is also changed. */ #define LPFC_REG_WRITE_KEY_SIZE 4 #define LPFC_REG_WRITE_KEY "EMLX" /** * lpfc_jedec_to_ascii - Hex to ascii convertor according to JEDEC rules * @incr: integer to convert. * @hdw: ascii string holding converted integer plus a string terminator. * * Description: * JEDEC Joint Electron Device Engineering Council. * Convert a 32 bit integer composed of 8 nibbles into an 8 byte ascii * character string. The string is then terminated with a NULL in byte 9. * Hex 0-9 becomes ascii '0' to '9'. * Hex a-f becomes ascii '=' to 'B' capital B. * * Notes: * Coded for 32 bit integers only. **/ static void lpfc_jedec_to_ascii(int incr, char hdw[]) { int i, j; for (i = 0; i < 8; i++) { j = (incr & 0xf); if (j <= 9) hdw[7 - i] = 0x30 + j; else hdw[7 - i] = 0x61 + j - 10; incr = (incr >> 4); } hdw[8] = 0; return; } /** * lpfc_drvr_version_show - Return the Emulex driver string with version number * @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_drvr_version_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n"); } /** * lpfc_enable_fip_show - Return the fip mode of the HBA * @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_enable_fip_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; if (phba->hba_flag & HBA_FIP_SUPPORT) return snprintf(buf, PAGE_SIZE, "1\n"); else return snprintf(buf, PAGE_SIZE, "0\n"); } static ssize_t lpfc_bg_info_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; if (phba->cfg_enable_bg) if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) return snprintf(buf, PAGE_SIZE, "BlockGuard Enabled\n"); else return snprintf(buf, PAGE_SIZE, "BlockGuard Not Supported\n"); else return snprintf(buf, PAGE_SIZE, "BlockGuard Disabled\n"); } static ssize_t lpfc_bg_guard_err_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, "%llu\n", (unsigned long long)phba->bg_guard_err_cnt); } static ssize_t lpfc_bg_apptag_err_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, "%llu\n", (unsigned long long)phba->bg_apptag_err_cnt); } static ssize_t lpfc_bg_reftag_err_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, "%llu\n", (unsigned long long)phba->bg_reftag_err_cnt); } /** * lpfc_info_show - Return some pci info about the host in ascii * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the formatted text from lpfc_info(). * * Returns: size of formatted string. **/ static ssize_t lpfc_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct Scsi_Host *host = class_to_shost(dev); return snprintf(buf, PAGE_SIZE, "%s\n",lpfc_info(host)); } /** * lpfc_serialnum_show - Return the hba serial number in ascii * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the formatted text serial number. * * Returns: size of formatted string. **/ static ssize_t lpfc_serialnum_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, "%s\n",phba->SerialNumber); } /** * lpfc_temp_sensor_show - Return the temperature sensor level * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the formatted support level. * * Description: * Returns a number indicating the temperature sensor level currently * supported, zero or one in ascii. * * Returns: size of formatted string. **/ static ssize_t lpfc_temp_sensor_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->temp_sensor_support); } /** * lpfc_modeldesc_show - Return the model description of the hba * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the scsi vpd model description. * * Returns: size of formatted string. **/ static ssize_t lpfc_modeldesc_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, "%s\n",phba->ModelDesc); } /** * lpfc_modelname_show - Return the model name of the hba * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the scsi vpd model name. * * Returns: size of formatted string. **/ static ssize_t lpfc_modelname_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, "%s\n",phba->ModelName); } /** * lpfc_programtype_show - Return the program type of the hba * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the scsi vpd program type. * * Returns: size of formatted string. **/ static ssize_t lpfc_programtype_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, "%s\n",phba->ProgramType); } /** * lpfc_mlomgmt_show - Return the Menlo Maintenance sli flag * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the Menlo Maintenance sli flag. * * Returns: size of formatted string. **/ static ssize_t lpfc_mlomgmt_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->sli.sli_flag & LPFC_MENLO_MAINT)); } /** * lpfc_vportnum_show - Return the port number in ascii of the hba * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains scsi vpd program type. * * Returns: size of formatted string. **/ static ssize_t lpfc_vportnum_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, "%s\n",phba->Port); } /** * lpfc_fwrev_show - Return the firmware rev running in the hba * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the scsi vpd program type. * * Returns: size of formatted string. **/ static ssize_t lpfc_fwrev_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; uint32_t if_type; uint8_t sli_family; char fwrev[FW_REV_STR_SIZE]; int len; lpfc_decode_firmware_rev(phba, fwrev, 1); if_type = phba->sli4_hba.pc_sli4_params.if_type; sli_family = phba->sli4_hba.pc_sli4_params.sli_family; if (phba->sli_rev < LPFC_SLI_REV4) len = snprintf(buf, PAGE_SIZE, "%s, sli-%d\n", fwrev, phba->sli_rev); else len = snprintf(buf, PAGE_SIZE, "%s, sli-%d:%d:%x\n", fwrev, phba->sli_rev, if_type, sli_family); return len; } /** * lpfc_hdw_show - Return the jedec information about the hba * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the scsi vpd program type. * * Returns: size of formatted string. **/ static ssize_t lpfc_hdw_show(struct device *dev, struct device_attribute *attr, char *buf) { char hdw[9]; struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; lpfc_vpd_t *vp = &phba->vpd; lpfc_jedec_to_ascii(vp->rev.biuRev, hdw); return snprintf(buf, PAGE_SIZE, "%s\n", hdw); } /** * lpfc_option_rom_version_show - Return the adapter ROM FCode version * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the ROM and FCode ascii strings. * * Returns: size of formatted string. **/ static ssize_t lpfc_option_rom_version_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, "%s\n", phba->OptionROMVersion); } /** * lpfc_state_show - Return the link state of the port * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains text describing the state of the link. * * Notes: * The switch statement has no default so zero will be returned. * * Returns: size of formatted string. **/ static ssize_t lpfc_link_state_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; int len = 0; switch (phba->link_state) { case LPFC_LINK_UNKNOWN: case LPFC_WARM_START: case LPFC_INIT_START: case LPFC_INIT_MBX_CMDS: case LPFC_LINK_DOWN: case LPFC_HBA_ERROR: if (phba->hba_flag & LINK_DISABLED) len += snprintf(buf + len, PAGE_SIZE-len, "Link Down - User disabled\n"); else len += snprintf(buf + len, PAGE_SIZE-len, "Link Down\n"); break; case LPFC_LINK_UP: case LPFC_CLEAR_LA: case LPFC_HBA_READY: len += snprintf(buf + len, PAGE_SIZE-len, "Link Up - "); switch (vport->port_state) { case LPFC_LOCAL_CFG_LINK: len += snprintf(buf + len, PAGE_SIZE-len, "Configuring Link\n"); break; case LPFC_FDISC: case LPFC_FLOGI: case LPFC_FABRIC_CFG_LINK: case LPFC_NS_REG: case LPFC_NS_QRY: case LPFC_BUILD_DISC_LIST: case LPFC_DISC_AUTH: len += snprintf(buf + len, PAGE_SIZE - len, "Discovery\n"); break; case LPFC_VPORT_READY: len += snprintf(buf + len, PAGE_SIZE - len, "Ready\n"); break; case LPFC_VPORT_FAILED: len += snprintf(buf + len, PAGE_SIZE - len, "Failed\n"); break; case LPFC_VPORT_UNKNOWN: len += snprintf(buf + len, PAGE_SIZE - len, "Unknown\n"); break; } if (phba->sli.sli_flag & LPFC_MENLO_MAINT) len += snprintf(buf + len, PAGE_SIZE-len, " Menlo Maint Mode\n"); else if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) { if (vport->fc_flag & FC_PUBLIC_LOOP) len += snprintf(buf + len, PAGE_SIZE-len, " Public Loop\n"); else len += snprintf(buf + len, PAGE_SIZE-len, " Private Loop\n"); } else { if (vport->fc_flag & FC_FABRIC) len += snprintf(buf + len, PAGE_SIZE-len, " Fabric\n"); else len += snprintf(buf + len, PAGE_SIZE-len, " Point-2-Point\n"); } } return len; } /** * lpfc_sli4_protocol_show - Return the fip mode of the HBA * @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_sli4_protocol_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; if (phba->sli_rev < LPFC_SLI_REV4) return snprintf(buf, PAGE_SIZE, "fc\n"); if (phba->sli4_hba.lnk_info.lnk_dv == LPFC_LNK_DAT_VAL) { if (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_GE) return snprintf(buf, PAGE_SIZE, "fcoe\n"); if (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC) return snprintf(buf, PAGE_SIZE, "fc\n"); } return snprintf(buf, PAGE_SIZE, "unknown\n"); } /** * 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. * @buf: one or more lpfc_polling_flags values. * @count: not used. * * Returns: * -EINVAL if the buffer is not "up" or "down" * return from link state change function if non-zero * length of the buf on success **/ static ssize_t lpfc_link_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_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; int status = -EINVAL; if ((strncmp(buf, "up", sizeof("up") - 1) == 0) && (phba->link_state == LPFC_LINK_DOWN)) status = phba->lpfc_hba_init_link(phba, MBX_NOWAIT); else if ((strncmp(buf, "down", sizeof("down") - 1) == 0) && (phba->link_state >= LPFC_LINK_UP)) status = phba->lpfc_hba_down_link(phba, MBX_NOWAIT); if (status == 0) return strlen(buf); else return status; } /** * lpfc_num_discovered_ports_show - Return sum of mapped and unmapped vports * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the sum of fc mapped and unmapped. * * Description: * Returns the ascii text number of the sum of the fc mapped and unmapped * vport counts. * * Returns: size of formatted string. **/ static ssize_t lpfc_num_discovered_ports_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; return snprintf(buf, PAGE_SIZE, "%d\n", vport->fc_map_cnt + vport->fc_unmap_cnt); } /** * lpfc_issue_lip - Misnomer, name carried over from long ago * @shost: Scsi_Host pointer. * * Description: * Bring the link down gracefully then re-init the link. The firmware will * re-init the fiber channel interface as required. Does not issue a LIP. * * Returns: * -EPERM port offline or management commands are being blocked * -ENOMEM cannot allocate memory for the mailbox command * -EIO error sending the mailbox command * zero for success **/ static int lpfc_issue_lip(struct Scsi_Host *shost) { struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *pmboxq; int mbxstatus = MBXERR_ERROR; if ((vport->fc_flag & FC_OFFLINE_MODE) || (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)) return -EPERM; pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL); if (!pmboxq) return -ENOMEM; memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK; pmboxq->u.mb.mbxOwner = OWN_HOST; mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO * 2); if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0 || pmboxq->u.mb.mbxStatus == MBXERR_LINK_DOWN)) { memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); lpfc_init_link(phba, pmboxq, phba->cfg_topology, phba->cfg_link_speed); mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == MBXERR_SEC_NO_PERMISSION)) lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "2859 SLI authentication is required " "for INIT_LINK but has not done yet\n"); } lpfc_set_loopback_flag(phba); if (mbxstatus != MBX_TIMEOUT) mempool_free(pmboxq, phba->mbox_mem_pool); if (mbxstatus == MBXERR_ERROR) return -EIO; return 0; } /** * lpfc_do_offline - Issues a mailbox command to bring the link down * @phba: lpfc_hba pointer. * @type: LPFC_EVT_OFFLINE, LPFC_EVT_WARM_START, LPFC_EVT_KILL. * * Notes: * Assumes any error from lpfc_do_offline() will be negative. * Can wait up to 5 seconds for the port ring buffers count * to reach zero, prints a warning if it is not zero and continues. * lpfc_workq_post_event() returns a non-zero return code if call fails. * * Returns: * -EIO error posting the event * zero for success **/ static int lpfc_do_offline(struct lpfc_hba *phba, uint32_t type) { struct completion online_compl; struct lpfc_sli_ring *pring; struct lpfc_sli *psli; int status = 0; int cnt = 0; int i; int rc; init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, LPFC_EVT_OFFLINE_PREP); if (rc == 0) return -ENOMEM; wait_for_completion(&online_compl); if (status != 0) return -EIO; psli = &phba->sli; /* Wait a little for things to settle down, but not * long enough for dev loss timeout to expire. */ for (i = 0; i < psli->num_rings; i++) { pring = &psli->ring[i]; while (pring->txcmplq_cnt) { msleep(10); if (cnt++ > 500) { /* 5 secs */ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0466 Outstanding IO when " "bringing Adapter offline\n"); break; } } } init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, type); if (rc == 0) return -ENOMEM; wait_for_completion(&online_compl); if (status != 0) return -EIO; return 0; } /** * lpfc_selective_reset - Offline then onlines the port * @phba: lpfc_hba pointer. * * Description: * If the port is configured to allow a reset then the hba is brought * offline then online. * * Notes: * Assumes any error from lpfc_do_offline() will be negative. * Do not make this function static. * * Returns: * lpfc_do_offline() return code if not zero * -EIO reset not configured or error posting the event * zero for success **/ int lpfc_selective_reset(struct lpfc_hba *phba) { struct completion online_compl; int status = 0; int rc; if (!phba->cfg_enable_hba_reset) return -EACCES; status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); if (status != 0) return status; init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, LPFC_EVT_ONLINE); if (rc == 0) return -ENOMEM; wait_for_completion(&online_compl); if (status != 0) return -EIO; return 0; } /** * lpfc_issue_reset - Selectively resets an adapter * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: containing the string "selective". * @count: unused variable. * * Description: * If the buf contains the string "selective" then lpfc_selective_reset() * is called to perform the reset. * * Notes: * Assumes any error from lpfc_selective_reset() will be negative. * If lpfc_selective_reset() returns zero then the length of the buffer * is returned which indicates success * * Returns: * -EINVAL if the buffer does not contain the string "selective" * length of buf if lpfc-selective_reset() if the call succeeds * return value of lpfc_selective_reset() if the call fails **/ static ssize_t lpfc_issue_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; int status = -EINVAL; if (!phba->cfg_enable_hba_reset) return -EACCES; if (strncmp(buf, "selective", sizeof("selective") - 1) == 0) status = phba->lpfc_selective_reset(phba); if (status == 0) return strlen(buf); else return status; } /** * lpfc_sli4_pdev_status_reg_wait - Wait for pdev status register for readyness * @phba: lpfc_hba pointer. * * Description: * SLI4 interface type-2 device to wait on the sliport status register for * the readyness after performing a firmware reset. * * Returns: * zero for success, -EPERM when port does not have privilage to perform the * reset, -EIO when port timeout from recovering from the reset. * * Note: * As the caller will interpret the return code by value, be careful in making * change or addition to return codes. **/ int lpfc_sli4_pdev_status_reg_wait(struct lpfc_hba *phba) { struct lpfc_register portstat_reg = {0}; int i; msleep(100); lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, &portstat_reg.word0); /* verify if privilaged for the request operation */ if (!bf_get(lpfc_sliport_status_rn, &portstat_reg) && !bf_get(lpfc_sliport_status_err, &portstat_reg)) return -EPERM; /* wait for the SLI port firmware ready after firmware reset */ for (i = 0; i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT; i++) { msleep(10); lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, &portstat_reg.word0); if (!bf_get(lpfc_sliport_status_err, &portstat_reg)) continue; if (!bf_get(lpfc_sliport_status_rn, &portstat_reg)) continue; if (!bf_get(lpfc_sliport_status_rdy, &portstat_reg)) continue; break; } if (i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT) return 0; else return -EIO; } /** * lpfc_sli4_pdev_reg_request - Request physical dev to perform a register acc * @phba: lpfc_hba pointer. * * Description: * Request SLI4 interface type-2 device to perform a physical register set * access. * * Returns: * zero for success **/ static ssize_t lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) { struct completion online_compl; struct pci_dev *pdev = phba->pcidev; uint32_t before_fc_flag; uint32_t sriov_nr_virtfn; uint32_t reg_val; int status = 0, rc = 0; int job_posted = 1, sriov_err; if (!phba->cfg_enable_hba_reset) return -EACCES; if ((phba->sli_rev < LPFC_SLI_REV4) || (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != LPFC_SLI_INTF_IF_TYPE_2)) return -EPERM; /* Keep state if we need to restore back */ before_fc_flag = phba->pport->fc_flag; sriov_nr_virtfn = phba->cfg_sriov_nr_virtfn; /* Disable SR-IOV virtual functions if enabled */ if (phba->cfg_sriov_nr_virtfn) { pci_disable_sriov(pdev); phba->cfg_sriov_nr_virtfn = 0; } status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); if (status != 0) return status; /* wait for the device to be quiesced before firmware reset */ msleep(100); reg_val = readl(phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET); if (opcode == LPFC_FW_DUMP) reg_val |= LPFC_FW_DUMP_REQUEST; else if (opcode == LPFC_FW_RESET) reg_val |= LPFC_CTL_PDEV_CTL_FRST; else if (opcode == LPFC_DV_RESET) reg_val |= LPFC_CTL_PDEV_CTL_DRST; writel(reg_val, phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET); /* flush */ readl(phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET); /* delay driver action following IF_TYPE_2 reset */ rc = lpfc_sli4_pdev_status_reg_wait(phba); if (rc == -EPERM) { /* no privilage for reset */ lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "3150 No privilage to perform the requested " "access: x%x\n", reg_val); } else if (rc == -EIO) { /* reset failed, there is nothing more we can do */ lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "3153 Fail to perform the requested " "access: x%x\n", reg_val); return rc; } /* keep the original port state */ if (before_fc_flag & FC_OFFLINE_MODE) goto out; init_completion(&online_compl); job_posted = lpfc_workq_post_event(phba, &status, &online_compl, LPFC_EVT_ONLINE); if (!job_posted) goto out; wait_for_completion(&online_compl); out: /* in any case, restore the virtual functions enabled as before */ if (sriov_nr_virtfn) { sriov_err = lpfc_sli_probe_sriov_nr_virtfn(phba, sriov_nr_virtfn); if (!sriov_err) phba->cfg_sriov_nr_virtfn = sriov_nr_virtfn; } /* return proper error code */ if (!rc) { if (!job_posted) rc = -ENOMEM; else if (status) rc = -EIO; } return rc; } /** * lpfc_nport_evt_cnt_show - Return the number of nport events * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the ascii number of nport events. * * Returns: size of formatted string. **/ static ssize_t lpfc_nport_evt_cnt_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->nport_event_cnt); } /** * lpfc_board_mode_show - Return the state of the board * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the state of the adapter. * * Returns: size of formatted string. **/ static ssize_t lpfc_board_mode_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; char * state; if (phba->link_state == LPFC_HBA_ERROR) state = "error"; else if (phba->link_state == LPFC_WARM_START) state = "warm start"; else if (phba->link_state == LPFC_INIT_START) state = "offline"; else state = "online"; return snprintf(buf, PAGE_SIZE, "%s\n", state); } /** * lpfc_board_mode_store - Puts the hba in online, offline, warm or error state * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: containing one of the strings "online", "offline", "warm" or "error". * @count: unused variable. * * Returns: * -EACCES if enable hba reset not enabled * -EINVAL if the buffer does not contain a valid string (see above) * -EIO if lpfc_workq_post_event() or lpfc_do_offline() fails * buf length greater than zero indicates success **/ static ssize_t lpfc_board_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; struct completion online_compl; char *board_mode_str = NULL; int status = 0; int rc; if (!phba->cfg_enable_hba_reset) { status = -EACCES; goto board_mode_out; } lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "3050 lpfc_board_mode set to %s\n", buf); init_completion(&online_compl); if(strncmp(buf, "online", sizeof("online") - 1) == 0) { rc = lpfc_workq_post_event(phba, &status, &online_compl, LPFC_EVT_ONLINE); if (rc == 0) { status = -ENOMEM; goto board_mode_out; } wait_for_completion(&online_compl); } else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0) status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0) if (phba->sli_rev == LPFC_SLI_REV4) status = -EINVAL; else status = lpfc_do_offline(phba, LPFC_EVT_WARM_START); else if (strncmp(buf, "error", sizeof("error") - 1) == 0) if (phba->sli_rev == LPFC_SLI_REV4) status = -EINVAL; else status = lpfc_do_offline(phba, LPFC_EVT_KILL); else if (strncmp(buf, "dump", sizeof("dump") - 1) == 0) status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_DUMP); else if (strncmp(buf, "fw_reset", sizeof("fw_reset") - 1) == 0) status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_RESET); else if (strncmp(buf, "dv_reset", sizeof("dv_reset") - 1) == 0) status = lpfc_sli4_pdev_reg_request(phba, LPFC_DV_RESET); else status = -EINVAL; board_mode_out: if (!status) return strlen(buf); else { board_mode_str = strchr(buf, '\n'); if (board_mode_str) *board_mode_str = '\0'; lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "3097 Failed \"%s\", status(%d), " "fc_flag(x%x)\n", buf, status, phba->pport->fc_flag); return status; } } /** * lpfc_get_hba_info - Return various bits of informaton about the adapter * @phba: pointer to the adapter structure. * @mxri: max xri count. * @axri: available xri count. * @mrpi: max rpi count. * @arpi: available rpi count. * @mvpi: max vpi count. * @avpi: available vpi count. * * Description: * If an integer pointer for an count is not null then the value for the * count is returned. * * Returns: * zero on error * one for success **/ static int lpfc_get_hba_info(struct lpfc_hba *phba, uint32_t *mxri, uint32_t *axri, uint32_t *mrpi, uint32_t *arpi, uint32_t *mvpi, uint32_t *avpi) { struct lpfc_mbx_read_config *rd_config; LPFC_MBOXQ_t *pmboxq; MAILBOX_t *pmb; int rc = 0; uint32_t max_vpi; /* * prevent udev from issuing mailbox commands until the port is * configured. */ if (phba->link_state < LPFC_LINK_DOWN || !phba->mbox_mem_pool || (phba->sli.sli_flag & LPFC_SLI_ACTIVE) == 0) return 0; if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) return 0; pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmboxq) return 0; memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t)); pmb = &pmboxq->u.mb; pmb->mbxCommand = MBX_READ_CONFIG; pmb->mbxOwner = OWN_HOST; pmboxq->context1 = NULL; if (phba->pport->fc_flag & FC_OFFLINE_MODE) rc = MBX_NOT_FINISHED; else rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); if (rc != MBX_SUCCESS) { if (rc != MBX_TIMEOUT) mempool_free(pmboxq, phba->mbox_mem_pool); return 0; } if (phba->sli_rev == LPFC_SLI_REV4) { rd_config = &pmboxq->u.mqe.un.rd_config; if (mrpi) *mrpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config); if (arpi) *arpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config) - phba->sli4_hba.max_cfg_param.rpi_used; if (mxri) *mxri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config); if (axri) *axri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config) - phba->sli4_hba.max_cfg_param.xri_used; /* Account for differences with SLI-3. Get vpi count from * mailbox data and subtract one for max vpi value. */ max_vpi = (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) > 0) ? (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) - 1) : 0; if (mvpi) *mvpi = max_vpi; if (avpi) *avpi = max_vpi - phba->sli4_hba.max_cfg_param.vpi_used; } else { if (mrpi) *mrpi = pmb->un.varRdConfig.max_rpi; if (arpi) *arpi = pmb->un.varRdConfig.avail_rpi; if (mxri) *mxri = pmb->un.varRdConfig.max_xri; if (axri) *axri = pmb->un.varRdConfig.avail_xri; if (mvpi) *mvpi = pmb->un.varRdConfig.max_vpi; if (avpi) *avpi = pmb->un.varRdConfig.avail_vpi; } mempool_free(pmboxq, phba->mbox_mem_pool); return 1; } /** * lpfc_max_rpi_show - Return maximum rpi * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the maximum rpi count in decimal or "Unknown". * * Description: * Calls lpfc_get_hba_info() asking for just the mrpi count. * If lpfc_get_hba_info() returns zero (failure) the buffer text is set * to "Unknown" and the buffer length is returned, therefore the caller * must check for "Unknown" in the buffer to detect a failure. * * Returns: size of formatted string. **/ static ssize_t lpfc_max_rpi_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; uint32_t cnt; if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, NULL, NULL, NULL)) return snprintf(buf, PAGE_SIZE, "%d\n", cnt); return snprintf(buf, PAGE_SIZE, "Unknown\n"); } /** * lpfc_used_rpi_show - Return maximum rpi minus available rpi * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: containing the used rpi count in decimal or "Unknown". * * Description: * Calls lpfc_get_hba_info() asking for just the mrpi and arpi counts. * If lpfc_get_hba_info() returns zero (failure) the buffer text is set * to "Unknown" and the buffer length is returned, therefore the caller * must check for "Unknown" in the buffer to detect a failure. * * Returns: size of formatted string. **/ static ssize_t lpfc_used_rpi_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; uint32_t cnt, acnt; if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, &acnt, NULL, NULL)) return snprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); return snprintf(buf, PAGE_SIZE, "Unknown\n"); } /** * lpfc_max_xri_show - Return maximum xri * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the maximum xri count in decimal or "Unknown". * * Description: * Calls lpfc_get_hba_info() asking for just the mrpi count. * If lpfc_get_hba_info() returns zero (failure) the buffer text is set * to "Unknown" and the buffer length is returned, therefore the caller * must check for "Unknown" in the buffer to detect a failure. * * Returns: size of formatted string. **/ static ssize_t lpfc_max_xri_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; uint32_t cnt; if (lpfc_get_hba_info(phba, &cnt, NULL, NULL, NULL, NULL, NULL)) return snprintf(buf, PAGE_SIZE, "%d\n", cnt); return snprintf(buf, PAGE_SIZE, "Unknown\n"); } /** * lpfc_used_xri_show - Return maximum xpi minus the available xpi * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the used xri count in decimal or "Unknown". * * Description: * Calls lpfc_get_hba_info() asking for just the mxri and axri counts. * If lpfc_get_hba_info() returns zero (failure) the buffer text is set * to "Unknown" and the buffer length is returned, therefore the caller * must check for "Unknown" in the buffer to detect a failure. * * Returns: size of formatted string. **/ static ssize_t lpfc_used_xri_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; uint32_t cnt, acnt; if (lpfc_get_hba_info(phba, &cnt, &acnt, NULL, NULL, NULL, NULL)) return snprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); return snprintf(buf, PAGE_SIZE, "Unknown\n"); } /** * lpfc_max_vpi_show - Return maximum vpi * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the maximum vpi count in decimal or "Unknown". * * Description: * Calls lpfc_get_hba_info() asking for just the mvpi count. * If lpfc_get_hba_info() returns zero (failure) the buffer text is set * to "Unknown" and the buffer length is returned, therefore the caller * must check for "Unknown" in the buffer to detect a failure. * * Returns: size of formatted string. **/ static ssize_t lpfc_max_vpi_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; uint32_t cnt; if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, NULL)) return snprintf(buf, PAGE_SIZE, "%d\n", cnt); return snprintf(buf, PAGE_SIZE, "Unknown\n"); } /** * lpfc_used_vpi_show - Return maximum vpi minus the available vpi * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the used vpi count in decimal or "Unknown". * * Description: * Calls lpfc_get_hba_info() asking for just the mvpi and avpi counts. * If lpfc_get_hba_info() returns zero (failure) the buffer text is set * to "Unknown" and the buffer length is returned, therefore the caller * must check for "Unknown" in the buffer to detect a failure. * * Returns: size of formatted string. **/ static ssize_t lpfc_used_vpi_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; uint32_t cnt, acnt; if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, &acnt)) return snprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); return snprintf(buf, PAGE_SIZE, "Unknown\n"); } /** * lpfc_npiv_info_show - Return text about NPIV support for the adapter * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: text that must be interpreted to determine if npiv is supported. * * Description: * Buffer will contain text indicating npiv is not suppoerted on the port, * the port is an NPIV physical port, or it is an npiv virtual port with * the id of the vport. * * Returns: size of formatted string. **/ static ssize_t lpfc_npiv_info_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; if (!(phba->max_vpi)) return snprintf(buf, PAGE_SIZE, "NPIV Not Supported\n"); if (vport->port_type == LPFC_PHYSICAL_PORT) return snprintf(buf, PAGE_SIZE, "NPIV Physical\n"); return snprintf(buf, PAGE_SIZE, "NPIV Virtual (VPI %d)\n", vport->vpi); } /** * lpfc_poll_show - Return text about poll support for the adapter * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the cfg_poll in hex. * * Notes: * cfg_poll should be a lpfc_polling_flags type. * * Returns: size of formatted string. **/ static ssize_t lpfc_poll_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, "%#x\n", phba->cfg_poll); } /** * lpfc_poll_store - Set the value of cfg_poll for the adapter * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: one or more lpfc_polling_flags values. * @count: not used. * * Notes: * buf contents converted to integer and checked for a valid value. * * Returns: * -EINVAL if the buffer connot be converted or is out of range * length of the buf on success **/ static ssize_t lpfc_poll_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; uint32_t creg_val; uint32_t old_val; int val=0; if (!isdigit(buf[0])) return -EINVAL; if (sscanf(buf, "%i", &val) != 1) return -EINVAL; if ((val & 0x3) != val) return -EINVAL; if (phba->sli_rev == LPFC_SLI_REV4) val = 0; lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "3051 lpfc_poll changed from %d to %d\n", phba->cfg_poll, val); spin_lock_irq(&phba->hbalock); old_val = phba->cfg_poll; if (val & ENABLE_FCP_RING_POLLING) { if ((val & DISABLE_FCP_RING_INT) && !(old_val & DISABLE_FCP_RING_INT)) { if (lpfc_readl(phba->HCregaddr, &creg_val)) { spin_unlock_irq(&phba->hbalock); return -EINVAL; } creg_val &= ~(HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ lpfc_poll_start_timer(phba); } } else if (val != 0x0) { spin_unlock_irq(&phba->hbalock); return -EINVAL; } if (!(val & DISABLE_FCP_RING_INT) && (old_val & DISABLE_FCP_RING_INT)) { spin_unlock_irq(&phba->hbalock); del_timer(&phba->fcp_poll_timer); spin_lock_irq(&phba->hbalock); if (lpfc_readl(phba->HCregaddr, &creg_val)) { spin_unlock_irq(&phba->hbalock); return -EINVAL; } creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ } phba->cfg_poll = val; spin_unlock_irq(&phba->hbalock); return strlen(buf); } /** * lpfc_fips_level_show - Return the current FIPS level for the HBA * @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_fips_level_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->fips_level); } /** * lpfc_fips_rev_show - Return the FIPS Spec revision for the HBA * @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_fips_rev_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->fips_spec_rev); } /** * lpfc_dss_show - Return the current state of dss and the configured state * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the formatted text. * * Returns: size of formatted string. **/ static ssize_t lpfc_dss_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, "%s - %sOperational\n", (phba->cfg_enable_dss) ? "Enabled" : "Disabled", (phba->sli3_options & LPFC_SLI3_DSS_ENABLED) ? "" : "Not "); } /** * lpfc_sriov_hw_max_virtfn_show - Return maximum number of virtual functions * @dev: class converted to a Scsi_host structure. * @attr: device attribute, not used. * @buf: on return contains the formatted support level. * * Description: * Returns the maximum number of virtual functions a physical function can * support, 0 will be returned if called on virtual function. * * Returns: size of formatted string. **/ static ssize_t lpfc_sriov_hw_max_virtfn_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; uint16_t max_nr_virtfn; max_nr_virtfn = lpfc_sli_sriov_nr_virtfn_get(phba); return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn); } /** * lpfc_param_show - Return a cfg attribute value in decimal * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_show. * * lpfc_##attr##_show: Return the decimal value of an adapters cfg_xxx field. * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the attribute value in decimal. * * Returns: size of formatted string. **/ #define lpfc_param_show(attr) \ static ssize_t \ lpfc_##attr##_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;\ uint val = 0;\ val = phba->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%d\n",\ phba->cfg_##attr);\ } /** * lpfc_param_hex_show - Return a cfg attribute value in hex * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_show * * lpfc_##attr##_show: Return the hex value of an adapters cfg_xxx field. * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the attribute value in hexadecimal. * * Returns: size of formatted string. **/ #define lpfc_param_hex_show(attr) \ static ssize_t \ lpfc_##attr##_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;\ uint val = 0;\ val = phba->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%#x\n",\ phba->cfg_##attr);\ } /** * lpfc_param_init - Initializes a cfg attribute * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_init. The macro also * takes a default argument, a minimum and maximum argument. * * lpfc_##attr##_init: Initializes an attribute. * @phba: pointer the the adapter structure. * @val: integer attribute value. * * Validates the min and max values then sets the adapter config field * accordingly, or uses the default if out of range and prints an error message. * * Returns: * zero on success * -EINVAL if default used **/ #define lpfc_param_init(attr, default, minval, maxval) \ static int \ lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \ { \ if (val >= minval && val <= maxval) {\ phba->cfg_##attr = val;\ return 0;\ }\ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, \ "0449 lpfc_"#attr" attribute cannot be set to %d, "\ "allowed range is ["#minval", "#maxval"]\n", val); \ phba->cfg_##attr = default;\ return -EINVAL;\ } /** * lpfc_param_set - Set a cfg attribute value * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_set * * lpfc_##attr##_set: Sets an attribute value. * @phba: pointer the the adapter structure. * @val: integer attribute value. * * Description: * Validates the min and max values then sets the * adapter config field if in the valid range. prints error message * and does not set the parameter if invalid. * * Returns: * zero on success * -EINVAL if val is invalid **/ #define lpfc_param_set(attr, default, minval, maxval) \ static int \ lpfc_##attr##_set(struct lpfc_hba *phba, uint val) \ { \ if (val >= minval && val <= maxval) {\ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, \ "3052 lpfc_" #attr " changed from %d to %d\n", \ phba->cfg_##attr, val); \ phba->cfg_##attr = val;\ return 0;\ }\ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, \ "0450 lpfc_"#attr" attribute cannot be set to %d, "\ "allowed range is ["#minval", "#maxval"]\n", val); \ return -EINVAL;\ } /** * lpfc_param_store - Set a vport attribute value * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_store. * * lpfc_##attr##_store: Set an sttribute value. * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: contains the attribute value in ascii. * @count: not used. * * Description: * Convert the ascii text number to an integer, then * use the lpfc_##attr##_set function to set the value. * * Returns: * -EINVAL if val is invalid or lpfc_##attr##_set() fails * length of buffer upon success. **/ #define lpfc_param_store(attr) \ static ssize_t \ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ struct lpfc_hba *phba = vport->phba;\ uint val = 0;\ if (!isdigit(buf[0]))\ return -EINVAL;\ if (sscanf(buf, "%i", &val) != 1)\ return -EINVAL;\ if (lpfc_##attr##_set(phba, val) == 0) \ return strlen(buf);\ else \ return -EINVAL;\ } /** * lpfc_vport_param_show - Return decimal formatted cfg attribute value * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_show * * lpfc_##attr##_show: prints the attribute value in decimal. * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the attribute value in decimal. * * Returns: length of formatted string. **/ #define lpfc_vport_param_show(attr) \ static ssize_t \ lpfc_##attr##_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;\ uint val = 0;\ val = vport->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_##attr);\ } /** * lpfc_vport_param_hex_show - Return hex formatted attribute value * * Description: * Macro that given an attr e.g. * hba_queue_depth expands into a function with the name * lpfc_hba_queue_depth_show * * lpfc_##attr##_show: prints the attribute value in hexadecimal. * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the attribute value in hexadecimal. * * Returns: length of formatted string. **/ #define lpfc_vport_param_hex_show(attr) \ static ssize_t \ lpfc_##attr##_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;\ uint val = 0;\ val = vport->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%#x\n", vport->cfg_##attr);\ } /** * lpfc_vport_param_init - Initialize a vport cfg attribute * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_init. The macro also * takes a default argument, a minimum and maximum argument. * * lpfc_##attr##_init: validates the min and max values then sets the * adapter config field accordingly, or uses the default if out of range * and prints an error message. * @phba: pointer the the adapter structure. * @val: integer attribute value. * * Returns: * zero on success * -EINVAL if default used **/ #define lpfc_vport_param_init(attr, default, minval, maxval) \ static int \ lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \ { \ if (val >= minval && val <= maxval) {\ vport->cfg_##attr = val;\ return 0;\ }\ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, \ "0423 lpfc_"#attr" attribute cannot be set to %d, "\ "allowed range is ["#minval", "#maxval"]\n", val); \ vport->cfg_##attr = default;\ return -EINVAL;\ } /** * lpfc_vport_param_set - Set a vport cfg attribute * * Description: * Macro that given an attr e.g. hba_queue_depth expands * into a function with the name lpfc_hba_queue_depth_set * * lpfc_##attr##_set: validates the min and max values then sets the * adapter config field if in the valid range. prints error message * and does not set the parameter if invalid. * @phba: pointer the the adapter structure. * @val: integer attribute value. * * Returns: * zero on success * -EINVAL if val is invalid **/ #define lpfc_vport_param_set(attr, default, minval, maxval) \ static int \ 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); \ vport->cfg_##attr = val;\ return 0;\ }\ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, \ "0424 lpfc_"#attr" attribute cannot be set to %d, "\ "allowed range is ["#minval", "#maxval"]\n", val); \ return -EINVAL;\ } /** * lpfc_vport_param_store - Set a vport attribute * * Description: * Macro that given an attr e.g. hba_queue_depth * expands into a function with the name lpfc_hba_queue_depth_store * * lpfc_##attr##_store: convert the ascii text number to an integer, then * use the lpfc_##attr##_set function to set the value. * @cdev: class device that is converted into a Scsi_host. * @buf: contains the attribute value in decimal. * @count: not used. * * Returns: * -EINVAL if val is invalid or lpfc_##attr##_set() fails * length of buffer upon success. **/ #define lpfc_vport_param_store(attr) \ static ssize_t \ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ uint val = 0;\ if (!isdigit(buf[0]))\ return -EINVAL;\ if (sscanf(buf, "%i", &val) != 1)\ return -EINVAL;\ if (lpfc_##attr##_set(vport, val) == 0) \ return strlen(buf);\ else \ return -EINVAL;\ } #define LPFC_ATTR(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_init(name, defval, minval, maxval) #define LPFC_ATTR_R(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ lpfc_param_set(name, defval, minval, maxval)\ lpfc_param_store(name)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) #define LPFC_ATTR_HEX_R(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_hex_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_ATTR_HEX_RW(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_hex_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ lpfc_param_set(name, defval, minval, maxval)\ lpfc_param_store(name)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) #define LPFC_VPORT_ATTR(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_init(name, defval, minval, maxval) #define LPFC_VPORT_ATTR_R(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ lpfc_vport_param_set(name, defval, minval, maxval)\ lpfc_vport_param_store(name)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) #define LPFC_VPORT_ATTR_HEX_R(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_hex_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_VPORT_ATTR_HEX_RW(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_hex_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ lpfc_vport_param_set(name, defval, minval, maxval)\ lpfc_vport_param_store(name)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL); static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL); static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL); static DEVICE_ATTR(bg_reftag_err, S_IRUGO, lpfc_bg_reftag_err_show, NULL); static DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL); static DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL); static DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL); static DEVICE_ATTR(modelname, S_IRUGO, lpfc_modelname_show, NULL); static DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL); static DEVICE_ATTR(portnum, S_IRUGO, lpfc_vportnum_show, NULL); static DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL); static DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL); static DEVICE_ATTR(link_state, S_IRUGO | S_IWUSR, lpfc_link_state_show, lpfc_link_state_store); static DEVICE_ATTR(option_rom_version, S_IRUGO, lpfc_option_rom_version_show, NULL); static DEVICE_ATTR(num_discovered_ports, S_IRUGO, lpfc_num_discovered_ports_show, NULL); static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL); static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL); static DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL); static DEVICE_ATTR(lpfc_enable_fip, S_IRUGO, lpfc_enable_fip_show, NULL); static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, lpfc_board_mode_show, lpfc_board_mode_store); static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); static DEVICE_ATTR(max_vpi, S_IRUGO, lpfc_max_vpi_show, NULL); static DEVICE_ATTR(used_vpi, S_IRUGO, lpfc_used_vpi_show, NULL); static DEVICE_ATTR(max_rpi, S_IRUGO, lpfc_max_rpi_show, NULL); static DEVICE_ATTR(used_rpi, S_IRUGO, lpfc_used_rpi_show, NULL); static DEVICE_ATTR(max_xri, S_IRUGO, lpfc_max_xri_show, NULL); static DEVICE_ATTR(used_xri, S_IRUGO, lpfc_used_xri_show, NULL); static DEVICE_ATTR(npiv_info, S_IRUGO, lpfc_npiv_info_show, NULL); static DEVICE_ATTR(lpfc_temp_sensor, S_IRUGO, lpfc_temp_sensor_show, NULL); static DEVICE_ATTR(lpfc_fips_level, S_IRUGO, lpfc_fips_level_show, NULL); static DEVICE_ATTR(lpfc_fips_rev, S_IRUGO, lpfc_fips_rev_show, NULL); 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 char *lpfc_soft_wwn_key = "C99G71SL8032A"; /** * 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. * @attr: device attribute, not used. * @buf: containing the string lpfc_soft_wwn_key. * @count: must be size of lpfc_soft_wwn_key. * * Returns: * -EINVAL if the buffer does not contain lpfc_soft_wwn_key * length of buf indicates success **/ static ssize_t lpfc_soft_wwn_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; unsigned int cnt = count; /* * We're doing a simple sanity check for soft_wwpn setting. * We require that the user write a specific key to enable * the soft_wwpn attribute to be settable. Once the attribute * is written, the enable key resets. If further updates are * desired, the key must be written again to re-enable the * attribute. * * The "key" is not secret - it is a hardcoded string shown * here. The intent is to protect against the random user or * application that is just writing attributes. */ /* count may include a LF at end of string */ if (buf[cnt-1] == '\n') cnt--; if ((cnt != strlen(lpfc_soft_wwn_key)) || (strncmp(buf, lpfc_soft_wwn_key, strlen(lpfc_soft_wwn_key)) != 0)) return -EINVAL; phba->soft_wwn_enable = 1; return count; } static DEVICE_ATTR(lpfc_soft_wwn_enable, S_IWUSR, NULL, lpfc_soft_wwn_enable_store); /** * lpfc_soft_wwpn_show - Return the cfg soft ww port name of the adapter * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. * @buf: on return contains the wwpn in hexadecimal. * * Returns: size of formatted string. **/ static ssize_t lpfc_soft_wwpn_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, "0x%llx\n", (unsigned long long)phba->cfg_soft_wwpn); } /** * lpfc_soft_wwpn_store - Set the ww port name of the adapter * @dev clas