blob: d4c65e2109e2ff1af6f04c0b6b75dec90d452655 [file] [log] [blame]
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
* Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.broadcom.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 <linux/ctype.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/aer.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
#include <linux/nvme-fc-driver.h>
#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.h"
#include "lpfc_scsi.h"
#include "lpfc_nvme.h"
#include "lpfc_nvmet.h"
#include "lpfc_logmsg.h"
#include "lpfc_version.h"
#include "lpfc_compat.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_attr.h"
#define LPFC_DEF_DEVLOSS_TMO 30
#define LPFC_MIN_DEVLOSS_TMO 1
#define LPFC_MAX_DEVLOSS_TMO 255
#define LPFC_DEF_MRQ_POST 512
#define LPFC_MIN_MRQ_POST 512
#define LPFC_MAX_MRQ_POST 2048
/*
* 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"
const char *const trunk_errmsg[] = { /* map errcode */
"", /* There is no such error code at index 0*/
"link negotiated speed does not match existing"
" trunk - link was \"low\" speed",
"link negotiated speed does not match"
" existing trunk - link was \"middle\" speed",
"link negotiated speed does not match existing"
" trunk - link was \"high\" speed",
"Attached to non-trunking port - F_Port",
"Attached to non-trunking port - N_Port",
"FLOGI response timeout",
"non-FLOGI frame received",
"Invalid FLOGI response",
"Trunking initialization protocol",
"Trunk peer device mismatch",
};
/**
* 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 scnprintf(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 scnprintf(buf, PAGE_SIZE, "1\n");
else
return scnprintf(buf, PAGE_SIZE, "0\n");
}
static ssize_t
lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = shost_priv(shost);
struct lpfc_hba *phba = vport->phba;
struct lpfc_nvmet_tgtport *tgtp;
struct nvme_fc_local_port *localport;
struct lpfc_nvme_lport *lport;
struct lpfc_nvme_rport *rport;
struct lpfc_nodelist *ndlp;
struct nvme_fc_remote_port *nrport;
struct lpfc_fc4_ctrl_stat *cstat;
uint64_t data1, data2, data3;
uint64_t totin, totout, tot;
char *statep;
int i;
int len = 0;
char tmp[LPFC_MAX_NVME_INFO_TMP_LEN] = {0};
unsigned long iflags = 0;
if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n");
return len;
}
if (phba->nvmet_support) {
if (!phba->targetport) {
len = scnprintf(buf, PAGE_SIZE,
"NVME Target: x%llx is not allocated\n",
wwn_to_u64(vport->fc_portname.u.wwn));
return len;
}
/* Port state is only one of two values for now. */
if (phba->targetport->port_id)
statep = "REGISTERED";
else
statep = "INIT";
scnprintf(tmp, sizeof(tmp),
"NVME Target Enabled State %s\n",
statep);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"%s%d WWPN x%llx WWNN x%llx DID x%06x\n",
"NVME Target: lpfc",
phba->brd_no,
wwn_to_u64(vport->fc_portname.u.wwn),
wwn_to_u64(vport->fc_nodename.u.wwn),
phba->targetport->port_id);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
if (strlcat(buf, "\nNVME Target: Statistics\n", PAGE_SIZE)
>= PAGE_SIZE)
goto buffer_done;
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
scnprintf(tmp, sizeof(tmp),
"LS: Rcv %08x Drop %08x Abort %08x\n",
atomic_read(&tgtp->rcv_ls_req_in),
atomic_read(&tgtp->rcv_ls_req_drop),
atomic_read(&tgtp->xmt_ls_abort));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
if (atomic_read(&tgtp->rcv_ls_req_in) !=
atomic_read(&tgtp->rcv_ls_req_out)) {
scnprintf(tmp, sizeof(tmp),
"Rcv LS: in %08x != out %08x\n",
atomic_read(&tgtp->rcv_ls_req_in),
atomic_read(&tgtp->rcv_ls_req_out));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
}
scnprintf(tmp, sizeof(tmp),
"LS: Xmt %08x Drop %08x Cmpl %08x\n",
atomic_read(&tgtp->xmt_ls_rsp),
atomic_read(&tgtp->xmt_ls_drop),
atomic_read(&tgtp->xmt_ls_rsp_cmpl));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"LS: RSP Abort %08x xb %08x Err %08x\n",
atomic_read(&tgtp->xmt_ls_rsp_aborted),
atomic_read(&tgtp->xmt_ls_rsp_xb_set),
atomic_read(&tgtp->xmt_ls_rsp_error));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"FCP: Rcv %08x Defer %08x Release %08x "
"Drop %08x\n",
atomic_read(&tgtp->rcv_fcp_cmd_in),
atomic_read(&tgtp->rcv_fcp_cmd_defer),
atomic_read(&tgtp->xmt_fcp_release),
atomic_read(&tgtp->rcv_fcp_cmd_drop));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
if (atomic_read(&tgtp->rcv_fcp_cmd_in) !=
atomic_read(&tgtp->rcv_fcp_cmd_out)) {
scnprintf(tmp, sizeof(tmp),
"Rcv FCP: in %08x != out %08x\n",
atomic_read(&tgtp->rcv_fcp_cmd_in),
atomic_read(&tgtp->rcv_fcp_cmd_out));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
}
scnprintf(tmp, sizeof(tmp),
"FCP Rsp: RD %08x rsp %08x WR %08x rsp %08x "
"drop %08x\n",
atomic_read(&tgtp->xmt_fcp_read),
atomic_read(&tgtp->xmt_fcp_read_rsp),
atomic_read(&tgtp->xmt_fcp_write),
atomic_read(&tgtp->xmt_fcp_rsp),
atomic_read(&tgtp->xmt_fcp_drop));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"FCP Rsp Cmpl: %08x err %08x drop %08x\n",
atomic_read(&tgtp->xmt_fcp_rsp_cmpl),
atomic_read(&tgtp->xmt_fcp_rsp_error),
atomic_read(&tgtp->xmt_fcp_rsp_drop));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"FCP Rsp Abort: %08x xb %08x xricqe %08x\n",
atomic_read(&tgtp->xmt_fcp_rsp_aborted),
atomic_read(&tgtp->xmt_fcp_rsp_xb_set),
atomic_read(&tgtp->xmt_fcp_xri_abort_cqe));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"ABORT: Xmt %08x Cmpl %08x\n",
atomic_read(&tgtp->xmt_fcp_abort),
atomic_read(&tgtp->xmt_fcp_abort_cmpl));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"ABORT: Sol %08x Usol %08x Err %08x Cmpl %08x\n",
atomic_read(&tgtp->xmt_abort_sol),
atomic_read(&tgtp->xmt_abort_unsol),
atomic_read(&tgtp->xmt_abort_rsp),
atomic_read(&tgtp->xmt_abort_rsp_error));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"DELAY: ctx %08x fod %08x wqfull %08x\n",
atomic_read(&tgtp->defer_ctx),
atomic_read(&tgtp->defer_fod),
atomic_read(&tgtp->defer_wqfull));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
/* Calculate outstanding IOs */
tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
tot += atomic_read(&tgtp->xmt_fcp_release);
tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
scnprintf(tmp, sizeof(tmp),
"IO_CTX: %08x WAIT: cur %08x tot %08x\n"
"CTX Outstanding %08llx\n\n",
phba->sli4_hba.nvmet_xri_cnt,
phba->sli4_hba.nvmet_io_wait_cnt,
phba->sli4_hba.nvmet_io_wait_total,
tot);
strlcat(buf, tmp, PAGE_SIZE);
goto buffer_done;
}
localport = vport->localport;
if (!localport) {
len = scnprintf(buf, PAGE_SIZE,
"NVME Initiator x%llx is not allocated\n",
wwn_to_u64(vport->fc_portname.u.wwn));
return len;
}
lport = (struct lpfc_nvme_lport *)localport->private;
if (strlcat(buf, "\nNVME Initiator Enabled\n", PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
rcu_read_lock();
scnprintf(tmp, sizeof(tmp),
"XRI Dist lpfc%d Total %d IO %d ELS %d\n",
phba->brd_no,
phba->sli4_hba.max_cfg_param.max_xri,
phba->sli4_hba.io_xri_max,
lpfc_sli4_get_els_iocb_cnt(phba));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
/* Port state is only one of two values for now. */
if (localport->port_id)
statep = "ONLINE";
else
statep = "UNKNOWN ";
scnprintf(tmp, sizeof(tmp),
"%s%d WWPN x%llx WWNN x%llx DID x%06x %s\n",
"NVME LPORT lpfc",
phba->brd_no,
wwn_to_u64(vport->fc_portname.u.wwn),
wwn_to_u64(vport->fc_nodename.u.wwn),
localport->port_id, statep);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
nrport = NULL;
spin_lock_irqsave(&vport->phba->hbalock, iflags);
rport = lpfc_ndlp_get_nrport(ndlp);
if (rport)
nrport = rport->remoteport;
spin_unlock_irqrestore(&vport->phba->hbalock, iflags);
if (!nrport)
continue;
/* Port state is only one of two values for now. */
switch (nrport->port_state) {
case FC_OBJSTATE_ONLINE:
statep = "ONLINE";
break;
case FC_OBJSTATE_UNKNOWN:
statep = "UNKNOWN ";
break;
default:
statep = "UNSUPPORTED";
break;
}
/* Tab in to show lport ownership. */
if (strlcat(buf, "NVME RPORT ", PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
if (phba->brd_no >= 10) {
if (strlcat(buf, " ", PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
}
scnprintf(tmp, sizeof(tmp), "WWPN x%llx ",
nrport->port_name);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
scnprintf(tmp, sizeof(tmp), "WWNN x%llx ",
nrport->node_name);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
scnprintf(tmp, sizeof(tmp), "DID x%06x ",
nrport->port_id);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
/* An NVME rport can have multiple roles. */
if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) {
if (strlcat(buf, "INITIATOR ", PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
}
if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) {
if (strlcat(buf, "TARGET ", PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
}
if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) {
if (strlcat(buf, "DISCSRVC ", PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
}
if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
FC_PORT_ROLE_NVME_TARGET |
FC_PORT_ROLE_NVME_DISCOVERY)) {
scnprintf(tmp, sizeof(tmp), "UNKNOWN ROLE x%x",
nrport->port_role);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
}
scnprintf(tmp, sizeof(tmp), "%s\n", statep);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto rcu_unlock_buf_done;
}
rcu_read_unlock();
if (!lport)
goto buffer_done;
if (strlcat(buf, "\nNVME Statistics\n", PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"LS: Xmt %010x Cmpl %010x Abort %08x\n",
atomic_read(&lport->fc4NvmeLsRequests),
atomic_read(&lport->fc4NvmeLsCmpls),
atomic_read(&lport->xmt_ls_abort));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"LS XMIT: Err %08x CMPL: xb %08x Err %08x\n",
atomic_read(&lport->xmt_ls_err),
atomic_read(&lport->cmpl_ls_xb),
atomic_read(&lport->cmpl_ls_err));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
totin = 0;
totout = 0;
for (i = 0; i < phba->cfg_hdw_queue; i++) {
cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
tot = cstat->io_cmpls;
totin += tot;
data1 = cstat->input_requests;
data2 = cstat->output_requests;
data3 = cstat->control_requests;
totout += (data1 + data2 + data3);
}
scnprintf(tmp, sizeof(tmp),
"Total FCP Cmpl %016llx Issue %016llx "
"OutIO %016llx\n",
totin, totout, totout - totin);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"\tabort %08x noxri %08x nondlp %08x qdepth %08x "
"wqerr %08x err %08x\n",
atomic_read(&lport->xmt_fcp_abort),
atomic_read(&lport->xmt_fcp_noxri),
atomic_read(&lport->xmt_fcp_bad_ndlp),
atomic_read(&lport->xmt_fcp_qdepth),
atomic_read(&lport->xmt_fcp_err),
atomic_read(&lport->xmt_fcp_wqerr));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp),
"FCP CMPL: xb %08x Err %08x\n",
atomic_read(&lport->cmpl_fcp_xb),
atomic_read(&lport->cmpl_fcp_err));
strlcat(buf, tmp, PAGE_SIZE);
/* RCU is already unlocked. */
goto buffer_done;
rcu_unlock_buf_done:
rcu_read_unlock();
buffer_done:
len = strnlen(buf, PAGE_SIZE);
if (unlikely(len >= (PAGE_SIZE - 1))) {
lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
"6314 Catching potential buffer "
"overflow > PAGE_SIZE = %lu bytes\n",
PAGE_SIZE);
strlcpy(buf + PAGE_SIZE - 1 -
strnlen(LPFC_NVME_INFO_MORE_STR, PAGE_SIZE - 1),
LPFC_NVME_INFO_MORE_STR,
strnlen(LPFC_NVME_INFO_MORE_STR, PAGE_SIZE - 1)
+ 1);
}
return len;
}
static ssize_t
lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = shost_priv(shost);
struct lpfc_hba *phba = vport->phba;
int len;
struct lpfc_fc4_ctrl_stat *cstat;
u64 data1, data2, data3;
u64 tot, totin, totout;
int i;
char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};
if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
(phba->sli_rev != LPFC_SLI_REV4))
return 0;
scnprintf(buf, PAGE_SIZE, "SCSI HDWQ Statistics\n");
totin = 0;
totout = 0;
for (i = 0; i < phba->cfg_hdw_queue; i++) {
cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
tot = cstat->io_cmpls;
totin += tot;
data1 = cstat->input_requests;
data2 = cstat->output_requests;
data3 = cstat->control_requests;
totout += (data1 + data2 + data3);
scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
"IO %016llx ", i, data1, data2, data3);
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
tot, ((data1 + data2 + data3) - tot));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
}
scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
"OutIO %016llx\n", totin, totout, totout - totin);
strlcat(buf, tmp, PAGE_SIZE);
buffer_done:
len = strnlen(buf, PAGE_SIZE);
return len;
}
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 scnprintf(buf, PAGE_SIZE,
"BlockGuard Enabled\n");
else
return scnprintf(buf, PAGE_SIZE,
"BlockGuard Not Supported\n");
} else
return scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 = scnprintf(buf, PAGE_SIZE, "%s, sli-%d\n",
fwrev, phba->sli_rev);
else
len = scnprintf(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 scnprintf(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;
char fwrev[FW_REV_STR_SIZE];
if (phba->sli_rev < LPFC_SLI_REV4)
return scnprintf(buf, PAGE_SIZE, "%s\n",
phba->OptionROMVersion);
lpfc_decode_firmware_rev(phba, fwrev, 1);
return scnprintf(buf, PAGE_SIZE, "%s\n", fwrev);
}
/**
* 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 += scnprintf(buf + len, PAGE_SIZE-len,
"Link Down - User disabled\n");
else
len += scnprintf(buf + len, PAGE_SIZE-len,
"Link Down\n");
break;
case LPFC_LINK_UP:
case LPFC_CLEAR_LA:
case LPFC_HBA_READY:
len += scnprintf(buf + len, PAGE_SIZE-len, "Link Up - ");
switch (vport->port_state) {
case LPFC_LOCAL_CFG_LINK:
len += scnprintf(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 += scnprintf(buf + len, PAGE_SIZE - len,
"Discovery\n");
break;
case LPFC_VPORT_READY:
len += scnprintf(buf + len, PAGE_SIZE - len,
"Ready\n");
break;
case LPFC_VPORT_FAILED:
len += scnprintf(buf + len, PAGE_SIZE - len,
"Failed\n");
break;
case LPFC_VPORT_UNKNOWN:
len += scnprintf(buf + len, PAGE_SIZE - len,
"Unknown\n");
break;
}
if (phba->sli.sli_flag & LPFC_MENLO_MAINT)
len += scnprintf(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 += scnprintf(buf + len, PAGE_SIZE-len,
" Public Loop\n");
else
len += scnprintf(buf + len, PAGE_SIZE-len,
" Private Loop\n");
} else {
if (vport->fc_flag & FC_FABRIC)
len += scnprintf(buf + len, PAGE_SIZE-len,
" Fabric\n");
else
len += scnprintf(buf + len, PAGE_SIZE-len,
" Point-2-Point\n");
}
}
if ((phba->sli_rev == LPFC_SLI_REV4) &&
((bf_get(lpfc_sli_intf_if_type,
&phba->sli4_hba.sli_intf) ==
LPFC_SLI_INTF_IF_TYPE_6))) {
struct lpfc_trunk_link link = phba->trunk_link;
if (bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba))
len += scnprintf(buf + len, PAGE_SIZE - len,
"Trunk port 0: Link %s %s\n",
(link.link0.state == LPFC_LINK_UP) ?
"Up" : "Down. ",
trunk_errmsg[link.link0.fault]);
if (bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba))
len += scnprintf(buf + len, PAGE_SIZE - len,
"Trunk port 1: Link %s %s\n",
(link.link1.state == LPFC_LINK_UP) ?
"Up" : "Down. ",
trunk_errmsg[link.link1.fault]);
if (bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba))
len += scnprintf(buf + len, PAGE_SIZE - len,
"Trunk port 2: Link %s %s\n",
(link.link2.state == LPFC_LINK_UP) ?
"Up" : "Down. ",
trunk_errmsg[link.link2.fault]);
if (bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba))
len += scnprintf(buf + len, PAGE_SIZE - len,
"Trunk port 3: Link %s %s\n",
(link.link3.state == LPFC_LINK_UP) ?
"Up" : "Down. ",
trunk_errmsg[link.link3.fault]);
}
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 scnprintf(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 scnprintf(buf, PAGE_SIZE, "fcoe\n");
if (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC)
return scnprintf(buf, PAGE_SIZE, "fc\n");
}
return scnprintf(buf, PAGE_SIZE, "unknown\n");
}
/**
* 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 scnprintf(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.
* @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 scnprintf(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 the link is offline, disabled or BLOCK_MGMT_IO
* it doesn't make any sense to allow issue_lip
*/
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
(phba->hba_flag & LINK_DISABLED) ||
(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;
}
int
lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *q, spinlock_t *lock)
{
int cnt = 0;
spin_lock_irq(lock);
while (!list_empty(q)) {
spin_unlock_irq(lock);
msleep(20);
if (cnt++ > 250) { /* 5 secs */
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0466 %s %s\n",
"Outstanding IO when ",
"bringing Adapter offline\n");
return 0;
}
spin_lock_irq(lock);
}
spin_unlock_irq(lock);
return 1;
}
/**
* 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_queue *qp = NULL;
struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
int status = 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;
/*
* If freeing the queues have already started, don't access them.
* Otherwise set FREE_WAIT to indicate that queues are being used
* to hold the freeing process until we finish.
*/
spin_lock_irq(&phba->hbalock);
if (!(psli->sli_flag & LPFC_QUEUE_FREE_INIT)) {
psli->sli_flag |= LPFC_QUEUE_FREE_WAIT;
} else {
spin_unlock_irq(&phba->hbalock);
goto skip_wait;
}
spin_unlock_irq(&phba->hbalock);
/* Wait a little for things to settle down, but not
* long enough for dev loss timeout to expire.
*/
if (phba->sli_rev != LPFC_SLI_REV4) {
for (i = 0; i < psli->num_rings; i++) {
pring = &psli->sli3_ring[i];
if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
&phba->hbalock))
goto out;
}
} else {
list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
pring = qp->pring;
if (!pring)
continue;
if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
&pring->ring_lock))
goto out;
}
}
out:
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_QUEUE_FREE_WAIT;
spin_unlock_irq(&phba->hbalock);
skip_wait:
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_reset_pci_bus - resets PCI bridge controller's secondary bus of an HBA
* @phba: lpfc_hba pointer.
*
* Description:
* Issues a PCI secondary bus reset for the phba->pcidev.
*
* Notes:
* First walks the bus_list to ensure only PCI devices with Emulex
* vendor id, device ids that support hot reset, only one occurrence
* of function 0, and all ports on the bus are in offline mode to ensure the
* hot reset only affects one valid HBA.
*
* Returns:
* -ENOTSUPP, cfg_enable_hba_reset must be of value 2
* -ENODEV, NULL ptr to pcidev
* -EBADSLT, detected invalid device
* -EBUSY, port is not in offline state
* 0, successful
*/
static int
lpfc_reset_pci_bus(struct lpfc_hba *phba)
{
struct pci_dev *pdev = phba->pcidev;
struct Scsi_Host *shost = NULL;
struct lpfc_hba *phba_other = NULL;
struct pci_dev *ptr = NULL;
int res;
if (phba->cfg_enable_hba_reset != 2)
return -ENOTSUPP;
if (!pdev) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "8345 pdev NULL!\n");
return -ENODEV;
}
res = lpfc_check_pci_resettable(phba);
if (res)
return res;
/* Walk the list of devices on the pci_dev's bus */
list_for_each_entry(ptr, &pdev->bus->devices, bus_list) {
/* Check port is offline */
shost = pci_get_drvdata(ptr);
if (shost) {
phba_other =
((struct lpfc_vport *)shost->hostdata)->phba;
if (!(phba_other->pport->fc_flag & FC_OFFLINE_MODE)) {
lpfc_printf_log(phba_other, KERN_INFO, LOG_INIT,
"8349 WWPN = 0x%02x%02x%02x%02x"
"%02x%02x%02x%02x is not "
"offline!\n",
phba_other->wwpn[0],
phba_other->wwpn[1],
phba_other->wwpn[2],
phba_other->wwpn[3],
phba_other->wwpn[4],
phba_other->wwpn[5],
phba_other->wwpn[6],
phba_other->wwpn[7]);
return -EBUSY;
}
}
}
/* Issue PCI bus reset */
res = pci_reset_bus(pdev);
if (res) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"8350 PCI reset bus failed: %d\n", res);
}
return res;
}
/**
* 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;
if (!(phba->pport->fc_flag & FC_OFFLINE_MODE)) {
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 privilege 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 privileged 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;
}
if (opcode == LPFC_FW_DUMP)
phba->hba_flag |= HBA_FW_DUMP_OP;
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
if (status != 0) {
phba->hba_flag &= ~HBA_FW_DUMP_OP;
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 privilege for reset */
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"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 */
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 scnprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt);
}
static int
lpfc_set_trunking(struct lpfc_hba *phba, char *buff_out)
{
LPFC_MBOXQ_t *mbox = NULL;
unsigned long val = 0;
char *pval = 0;
int rc = 0;
if (!strncmp("enable", buff_out,
strlen("enable"))) {
pval = buff_out + strlen("enable") + 1;
rc = kstrtoul(pval, 0, &val);
if (rc)
return rc; /* Invalid number */
} else if (!strncmp("disable", buff_out,
strlen("disable"))) {
val = 0;
} else {
return -EINVAL; /* Invalid command */
}
switch (val) {
case 0:
val = 0x0; /* Disable */
break;
case 2:
val = 0x1; /* Enable two port trunk */
break;
case 4:
val = 0x2; /* Enable four port trunk */
break;
default:
return -EINVAL;
}
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
"0070 Set trunk mode with val %ld ", val);
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
LPFC_MBOX_OPCODE_FCOE_FC_SET_TRUNK_MODE,
12, LPFC_SLI4_MBX_EMBED);
bf_set(lpfc_mbx_set_trunk_mode,
&mbox->u.mqe.un.set_trunk_mode,
val);
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
if (rc)
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
"0071 Set trunk mode failed with status: %d",
rc);
if (rc != MBX_TIMEOUT)
mempool_free(mbox, phba->mbox_mem_pool);
return 0;
}
/**
* 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 scnprintf(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);
if (status)
status = -EIO;
} 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 if (strncmp(buf, "pci_bus_reset", sizeof("pci_bus_reset") - 1)
== 0)
status = lpfc_reset_pci_bus(phba);
else if (strncmp(buf, "trunk", sizeof("trunk") - 1) == 0)
status = lpfc_set_trunking(phba, (char *)buf + sizeof("trunk"));
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->ctx_buf = 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;
/* Limit the max we support */
if (max_vpi > LPFC_MAX_VPI)
max_vpi = LPFC_MAX_VPI;
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) {
/* avail_vpi is only valid if link is up and ready */
if (phba->link_state == LPFC_HBA_READY)
*avpi = pmb->un.varRdConfig.avail_vpi;
else
*avpi = pmb->un.varRdConfig.max_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 scnprintf(buf, PAGE_SIZE, "%d\n", cnt);
return scnprintf(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 scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
return scnprintf(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 scnprintf(buf, PAGE_SIZE, "%d\n", cnt);
return scnprintf(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 scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
return scnprintf(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 scnprintf(buf, PAGE_SIZE, "%d\n", cnt);
return scnprintf(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 scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
return scnprintf(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 scnprintf(buf, PAGE_SIZE, "NPIV Not Supported\n");
if (vport->port_type == LPFC_PHYSICAL_PORT)
return scnprintf(buf, PAGE_SIZE, "NPIV Physical\n");
return scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(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 scnprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn);
}
static inline bool lpfc_rangecheck(uint val, uint min, uint max)
{
return val >= min && val <= max;
}
/**
* lpfc_enable_bbcr_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
*/
static ssize_t
lpfc_enable_bbcr_set(struct lpfc_hba *phba, uint val)
{
if (lpfc_rangecheck(val, 0, 1) && phba->sli_rev == LPFC_SLI_REV4) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3068 %s_enable_bbcr changed from %d to %d\n",
LPFC_DRIVER_NAME, phba->cfg_enable_bbcr, val);
phba->cfg_enable_bbcr = val;
return 0;
}
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0451 %s_enable_bbcr cannot set to %d, range is 0, 1\n",
LPFC_DRIVER_NAME, val);
return -EINVAL;
}
/**
* 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;\
return scnprintf(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 scnprintf(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 (lpfc_rangecheck(val, minval, 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 (lpfc_rangecheck(val, minval, 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;\
return scnprintf(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;\
return scnprintf(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 (lpfc_rangecheck(val, minval, 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 (lpfc_rangecheck(val, minval, maxval)) {\
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, \
"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;\
}\
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;\
}
static DEVICE_ATTR(nvme_info, 0444, lpfc_nvme_info_show, NULL);
static DEVICE_ATTR(scsi_stat, 0444, lpfc_scsi_stat_show, NULL);
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_RO(lpfc_drvr_version);
static DEVICE_ATTR_RO(lpfc_enable_fip);
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_RO(lpfc_temp_sensor);
static DEVICE_ATTR_RO(lpfc_fips_level);
static DEVICE_ATTR_RO(lpfc_fips_rev);
static DEVICE_ATTR_RO(lpfc_dss);
static DEVICE_ATTR_RO(lpfc_sriov_hw_max_virtfn);
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)