Skip to content

Commit

Permalink
[SCSI] lpfc 8.3.24: Add SR-IOV control
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Iannicelli <alex.iannicelli@emulex.com>
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <jbottomley@parallels.com>
  • Loading branch information
James Smart authored and James Bottomley committed May 27, 2011
1 parent c0c1151 commit 912e3ac
Show file tree
Hide file tree
Showing 7 changed files with 536 additions and 5 deletions.
1 change: 1 addition & 0 deletions drivers/scsi/lpfc/lpfc.h
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ struct lpfc_hba {
uint32_t cfg_hostmem_hgp;
uint32_t cfg_log_verbose;
uint32_t cfg_aer_support;
uint32_t cfg_sriov_nr_virtfn;
uint32_t cfg_iocb_cnt;
uint32_t cfg_suppress_link_up;
#define LPFC_INITIALIZE_LINK 0 /* do normal init_link mbox */
Expand Down
237 changes: 234 additions & 3 deletions drivers/scsi/lpfc/lpfc_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,102 @@ lpfc_dss_show(struct device *dev, struct device_attribute *attr,
"" : "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;
struct pci_dev *pdev = phba->pcidev;
union lpfc_sli4_cfg_shdr *shdr;
uint32_t shdr_status, shdr_add_status;
LPFC_MBOXQ_t *mboxq;
struct lpfc_mbx_get_prof_cfg *get_prof_cfg;
struct lpfc_rsrc_desc_pcie *desc;
uint32_t max_nr_virtfn;
uint32_t desc_count;
int length, rc, i;

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;

if (!pdev->is_physfn)
return snprintf(buf, PAGE_SIZE, "%d\n", 0);

mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq)
return -ENOMEM;

/* get the maximum number of virtfn support by physfn */
length = (sizeof(struct lpfc_mbx_get_prof_cfg) -
sizeof(struct lpfc_sli4_cfg_mhdr));
lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG,
length, LPFC_SLI4_MBX_EMBED);
shdr = (union lpfc_sli4_cfg_shdr *)
&mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
bf_set(lpfc_mbox_hdr_pf_num, &shdr->request,
phba->sli4_hba.iov.pf_number + 1);

get_prof_cfg = &mboxq->u.mqe.un.get_prof_cfg;
bf_set(lpfc_mbx_get_prof_cfg_prof_tp, &get_prof_cfg->u.request,
LPFC_CFG_TYPE_CURRENT_ACTIVE);

rc = lpfc_sli_issue_mbox_wait(phba, mboxq,
lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG));

if (rc != MBX_TIMEOUT) {
/* check return status */
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
&shdr->response);
if (shdr_status || shdr_add_status || rc)
goto error_out;

} else
goto error_out;

desc_count = get_prof_cfg->u.response.prof_cfg.rsrc_desc_count;

for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) {
desc = (struct lpfc_rsrc_desc_pcie *)
&get_prof_cfg->u.response.prof_cfg.desc[i];
if (LPFC_RSRC_DESC_TYPE_PCIE ==
bf_get(lpfc_rsrc_desc_pcie_type, desc)) {
max_nr_virtfn = bf_get(lpfc_rsrc_desc_pcie_nr_virtfn,
desc);
break;
}
}

if (i < LPFC_RSRC_DESC_MAX_NUM) {
if (rc != MBX_TIMEOUT)
mempool_free(mboxq, phba->mbox_mem_pool);
return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn);
}

error_out:
if (rc != MBX_TIMEOUT)
mempool_free(mboxq, phba->mbox_mem_pool);
return -EIO;
}

/**
* lpfc_param_show - Return a cfg attribute value in decimal
*
Expand Down Expand Up @@ -1824,6 +1920,8 @@ 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 char *lpfc_soft_wwn_key = "C99G71SL8032A";

Expand Down Expand Up @@ -3076,7 +3174,7 @@ static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
*
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing the string "selective".
* @buf: containing enable or disable aer flag.
* @count: unused variable.
*
* Description:
Expand Down Expand Up @@ -3160,7 +3258,7 @@ lpfc_param_show(aer_support)
/**
* lpfc_aer_support_init - Set the initial adapters aer support flag
* @phba: lpfc_hba pointer.
* @val: link speed value.
* @val: enable aer or disable aer flag.
*
* Description:
* If val is in a valid range [0,1], then set the adapter's initial
Expand Down Expand Up @@ -3199,7 +3297,7 @@ static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
* lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing the string "selective".
* @buf: containing flag 1 for aer cleanup state.
* @count: unused variable.
*
* Description:
Expand Down Expand Up @@ -3242,6 +3340,136 @@ lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL,
lpfc_aer_cleanup_state);

/**
* lpfc_sriov_nr_virtfn_store - Enable the adapter for sr-iov virtual functions
*
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing the string the number of vfs to be enabled.
* @count: unused variable.
*
* Description:
* When this api is called either through user sysfs, the driver shall
* try to enable or disable SR-IOV virtual functions according to the
* following:
*
* If zero virtual function has been enabled to the physical function,
* the driver shall invoke the pci enable virtual function api trying
* to enable the virtual functions. If the nr_vfn provided is greater
* than the maximum supported, the maximum virtual function number will
* be used for invoking the api; otherwise, the nr_vfn provided shall
* be used for invoking the api. If the api call returned success, the
* actual number of virtual functions enabled will be set to the driver
* cfg_sriov_nr_virtfn; otherwise, -EINVAL shall be returned and driver
* cfg_sriov_nr_virtfn remains zero.
*
* If none-zero virtual functions have already been enabled to the
* physical function, as reflected by the driver's cfg_sriov_nr_virtfn,
* -EINVAL will be returned and the driver does nothing;
*
* If the nr_vfn provided is zero and none-zero virtual functions have
* been enabled, as indicated by the driver's cfg_sriov_nr_virtfn, the
* disabling virtual function api shall be invoded to disable all the
* virtual functions and driver's cfg_sriov_nr_virtfn shall be set to
* zero. Otherwise, if zero virtual function has been enabled, do
* nothing.
*
* Returns:
* length of the buf on success if val is in range the intended mode
* is supported.
* -EINVAL if val out of range or intended mode is not supported.
**/
static ssize_t
lpfc_sriov_nr_virtfn_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 pci_dev *pdev = phba->pcidev;
int val = 0, rc = -EINVAL;

/* Sanity check on user data */
if (!isdigit(buf[0]))
return -EINVAL;
if (sscanf(buf, "%i", &val) != 1)
return -EINVAL;
if (val < 0)
return -EINVAL;

/* Request disabling virtual functions */
if (val == 0) {
if (phba->cfg_sriov_nr_virtfn > 0) {
pci_disable_sriov(pdev);
phba->cfg_sriov_nr_virtfn = 0;
}
return strlen(buf);
}

/* Request enabling virtual functions */
if (phba->cfg_sriov_nr_virtfn > 0) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3018 There are %d virtual functions "
"enabled on physical function.\n",
phba->cfg_sriov_nr_virtfn);
return -EEXIST;
}

if (val <= LPFC_MAX_VFN_PER_PFN)
phba->cfg_sriov_nr_virtfn = val;
else {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3019 Enabling %d virtual functions is not "
"allowed.\n", val);
return -EINVAL;
}

rc = lpfc_sli_probe_sriov_nr_virtfn(phba, phba->cfg_sriov_nr_virtfn);
if (rc) {
phba->cfg_sriov_nr_virtfn = 0;
rc = -EPERM;
} else
rc = strlen(buf);

return rc;
}

static int lpfc_sriov_nr_virtfn = LPFC_DEF_VFN_PER_PFN;
module_param(lpfc_sriov_nr_virtfn, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(lpfc_sriov_nr_virtfn, "Enable PCIe device SR-IOV virtual fn");
lpfc_param_show(sriov_nr_virtfn)

/**
* lpfc_sriov_nr_virtfn_init - Set the initial sr-iov virtual function enable
* @phba: lpfc_hba pointer.
* @val: link speed value.
*
* Description:
* If val is in a valid range [0,255], then set the adapter's initial
* cfg_sriov_nr_virtfn field. If it's greater than the maximum, the maximum
* number shall be used instead. It will be up to the driver's probe_one
* routine to determine whether the device's SR-IOV is supported or not.
*
* Returns:
* zero if val saved.
* -EINVAL val out of range
**/
static int
lpfc_sriov_nr_virtfn_init(struct lpfc_hba *phba, int val)
{
if (val >= 0 && val <= LPFC_MAX_VFN_PER_PFN) {
phba->cfg_sriov_nr_virtfn = val;
return 0;
}

lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3017 Enabling %d virtual functions is not "
"allowed.\n", val);
return -EINVAL;
}
static DEVICE_ATTR(lpfc_sriov_nr_virtfn, S_IRUGO | S_IWUSR,
lpfc_sriov_nr_virtfn_show, lpfc_sriov_nr_virtfn_store);

/*
# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
# Value range is [2,3]. Default value is 3.
Expand Down Expand Up @@ -3559,6 +3787,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_prot_sg_seg_cnt,
&dev_attr_lpfc_aer_support,
&dev_attr_lpfc_aer_state_cleanup,
&dev_attr_lpfc_sriov_nr_virtfn,
&dev_attr_lpfc_suppress_link_up,
&dev_attr_lpfc_iocb_cnt,
&dev_attr_iocb_hw,
Expand All @@ -3567,6 +3796,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_fips_level,
&dev_attr_lpfc_fips_rev,
&dev_attr_lpfc_dss,
&dev_attr_lpfc_sriov_hw_max_virtfn,
NULL,
};

Expand Down Expand Up @@ -4767,6 +4997,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
lpfc_aer_support_init(phba, lpfc_aer_support);
lpfc_sriov_nr_virtfn_init(phba, lpfc_sriov_nr_virtfn);
lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up);
lpfc_iocb_cnt_init(phba, lpfc_iocb_cnt);
phba->cfg_enable_dss = 1;
Expand Down
2 changes: 2 additions & 0 deletions drivers/scsi/lpfc/lpfc_crtn.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,5 @@ void lpfc_cleanup_wt_rrqs(struct lpfc_hba *);
void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *);
struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t,
uint32_t);
/* functions to support SR-IOV */
int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int);
2 changes: 2 additions & 0 deletions drivers/scsi/lpfc/lpfc_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,8 @@ struct RRQ { /* Structure is in Big Endian format */
#define rrq_rxid_WORD rrq_exchg
};

#define LPFC_MAX_VFN_PER_PFN 255 /* Maximum VFs allowed per ARI */
#define LPFC_DEF_VFN_PER_PFN 0 /* Default VFs due to platform limitation*/

struct RTV_RSP { /* Structure is in Big Endian format */
uint32_t ratov;
Expand Down
Loading

0 comments on commit 912e3ac

Please sign in to comment.