qlcnic: Support SR-IOV enable and disable

o Add QLCNIC_SRIOV to Kconfig.
o Provide PCI sysfs hooks to enable and disable SR-IOV.
o Allow enabling only when CONFIG_QLCNIC_SRIOV is defined.
o qlcnic_sriov_pf.c has all the PF related SR-IOV
  functionality.
o qlcnic_sriov_common.c has VF functionality and SR-IOV
  functionality which is common between VF and PF.
o qlcnic_sriov.h is a common header file for SR-IOV defines.

Signed-off-by: Manish Chopra <manish.chopra@qlogic.com>
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Rajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Rajesh Borundia 2013-03-29 05:46:33 +00:00 committed by David S. Miller
parent c23343cfc9
commit 02feda1758
12 changed files with 613 additions and 7 deletions

View File

@ -35,6 +35,16 @@ config QLCNIC
This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet
devices. devices.
config QLCNIC_SRIOV
bool "QLOGIC QLCNIC 83XX family SR-IOV Support"
depends on QLCNIC && PCI_IOV
default y
---help---
This configuration parameter enables Single Root Input Output
Virtualization support for QLE83XX Converged Ethernet devices.
This allows for virtual function acceleration in virtualized
environments.
config QLGE config QLGE
tristate "QLogic QLGE 10Gb Ethernet Driver Support" tristate "QLogic QLGE 10Gb Ethernet Driver Support"
depends on PCI depends on PCI

View File

@ -8,4 +8,6 @@ qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \
qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \ qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \
qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \ qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \
qlcnic_83xx_init.o qlcnic_83xx_vnic.o \ qlcnic_83xx_init.o qlcnic_83xx_vnic.o \
qlcnic_minidump.o qlcnic_minidump.o qlcnic_sriov_common.o
qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o

View File

@ -449,6 +449,7 @@ struct qlcnic_hardware_context {
struct qlc_83xx_idc idc; struct qlc_83xx_idc idc;
struct qlc_83xx_fw_info fw_info; struct qlc_83xx_fw_info fw_info;
struct qlcnic_intrpt_config *intr_tbl; struct qlcnic_intrpt_config *intr_tbl;
struct qlcnic_sriov *sriov;
u32 *reg_tbl; u32 *reg_tbl;
u32 *ext_reg_tbl; u32 *ext_reg_tbl;
u32 mbox_aen[QLC_83XX_MBX_AEN_CNT]; u32 mbox_aen[QLC_83XX_MBX_AEN_CNT];
@ -915,6 +916,8 @@ struct qlcnic_ipaddr {
#define __QLCNIC_DIAG_RES_ALLOC 6 #define __QLCNIC_DIAG_RES_ALLOC 6
#define __QLCNIC_LED_ENABLE 7 #define __QLCNIC_LED_ENABLE 7
#define __QLCNIC_ELB_INPROGRESS 8 #define __QLCNIC_ELB_INPROGRESS 8
#define __QLCNIC_SRIOV_ENABLE 10
#define __QLCNIC_SRIOV_CAPABLE 11
#define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_INTERRUPT_TEST 1
#define QLCNIC_LOOPBACK_TEST 2 #define QLCNIC_LOOPBACK_TEST 2
@ -1051,7 +1054,11 @@ struct qlcnic_info_le {
u8 total_pf; u8 total_pf;
u8 total_rss_engines; u8 total_rss_engines;
__le16 max_vports; __le16 max_vports;
u8 reserved2[64]; __le16 linkstate_reg_offset;
__le16 bit_offsets;
__le16 max_local_ipv6_addrs;
__le16 max_remote_ipv6_addrs;
u8 reserved2[56];
} __packed; } __packed;
struct qlcnic_info { struct qlcnic_info {
@ -1083,6 +1090,10 @@ struct qlcnic_info {
u8 total_pf; u8 total_pf;
u8 total_rss_engines; u8 total_rss_engines;
u16 max_vports; u16 max_vports;
u16 linkstate_reg_offset;
u16 bit_offsets;
u16 max_local_ipv6_addrs;
u16 max_remote_ipv6_addrs;
}; };
struct qlcnic_pci_info_le { struct qlcnic_pci_info_le {
@ -1511,6 +1522,7 @@ int qlcnic_reset_npar_config(struct qlcnic_adapter *);
int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *); int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *);
void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int,
__le16); __le16);
int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
/* /*
* QLOGIC Board information * QLOGIC Board information
*/ */
@ -1843,5 +1855,9 @@ static inline bool qlcnic_83xx_check(struct qlcnic_adapter *adapter)
return (device == PCI_DEVICE_ID_QLOGIC_QLE834X) ? true : false; return (device == PCI_DEVICE_ID_QLOGIC_QLE834X) ? true : false;
} }
static inline bool qlcnic_sriov_pf_check(struct qlcnic_adapter *adapter)
{
return (adapter->ahw->op_mode == QLCNIC_SRIOV_PF_FUNC) ? true : false;
}
#endif /* __QLCNIC_H_ */ #endif /* __QLCNIC_H_ */

View File

@ -209,6 +209,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
{QLCNIC_CMD_SET_LED_CONFIG, 5, 1}, {QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
{QLCNIC_CMD_GET_LED_CONFIG, 1, 5}, {QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
{QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
{QLCNIC_CMD_CONFIG_VPORT, 4, 4},
}; };
static const u32 qlcnic_83xx_ext_reg_tbl[] = { static const u32 qlcnic_83xx_ext_reg_tbl[] = {
@ -775,6 +776,9 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,
ahw->fw_hal_version); ahw->fw_hal_version);
adapter->nic_ops = &qlcnic_vf_ops; adapter->nic_ops = &qlcnic_vf_ops;
} else { } else {
if (pci_find_ext_capability(adapter->pdev,
PCI_EXT_CAP_ID_SRIOV))
set_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state);
adapter->nic_ops = &qlcnic_83xx_ops; adapter->nic_ops = &qlcnic_83xx_ops;
} }
} }

View File

@ -243,6 +243,7 @@ struct qlc_83xx_idc {
#define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val) (val & 0x20000) #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val) (val & 0x20000)
#define QLC_83XX_VIRTUAL_NIC_MODE 0xFF #define QLC_83XX_VIRTUAL_NIC_MODE 0xFF
#define QLC_83XX_DEFAULT_MODE 0x0 #define QLC_83XX_DEFAULT_MODE 0x0
#define QLC_83XX_SRIOV_MODE 0x1
#define QLCNIC_BRDTYPE_83XX_10G 0x0083 #define QLCNIC_BRDTYPE_83XX_10G 0x0083
#define QLC_83XX_FLASH_SPI_STATUS 0x2808E010 #define QLC_83XX_FLASH_SPI_STATUS 0x2808E010

View File

@ -25,7 +25,6 @@
#define QLC_83XX_OPCODE_POLL_READ_LIST 0x0100 #define QLC_83XX_OPCODE_POLL_READ_LIST 0x0100
static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter); static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter);
static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev); static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev);
static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter); static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter);
@ -1918,6 +1917,9 @@ int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *adapter)
qlcnic_get_func_no(adapter); qlcnic_get_func_no(adapter);
op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE); op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE);
if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state))
op_mode = QLC_83XX_DEFAULT_OPMODE;
if (op_mode == QLC_83XX_DEFAULT_OPMODE) { if (op_mode == QLC_83XX_DEFAULT_OPMODE) {
adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver; adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;
ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
@ -1947,6 +1949,16 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
ahw->max_mac_filters = nic_info.max_mac_filters; ahw->max_mac_filters = nic_info.max_mac_filters;
ahw->max_mtu = nic_info.max_mtu; ahw->max_mtu = nic_info.max_mtu;
/* VNIC mode is detected by BIT_23 in capabilities. This bit is also
* set in case device is SRIOV capable. VNIC and SRIOV are mutually
* exclusive. So in case of sriov capable device load driver in
* default mode
*/
if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state)) {
ahw->nic_mode = QLC_83XX_DEFAULT_MODE;
return ahw->nic_mode;
}
if (ahw->capabilities & BIT_23) if (ahw->capabilities & BIT_23)
ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE; ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE;
else else
@ -1955,7 +1967,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
return ahw->nic_mode; return ahw->nic_mode;
} }
static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter) int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
{ {
int ret; int ret;

View File

@ -714,7 +714,8 @@ enum {
QLCNIC_MGMT_FUNC = 0, QLCNIC_MGMT_FUNC = 0,
QLCNIC_PRIV_FUNC = 1, QLCNIC_PRIV_FUNC = 1,
QLCNIC_NON_PRIV_FUNC = 2, QLCNIC_NON_PRIV_FUNC = 2,
QLCNIC_UNKNOWN_FUNC_MODE = 3 QLCNIC_SRIOV_PF_FUNC = 3,
QLCNIC_UNKNOWN_FUNC_MODE = 4
}; };
enum { enum {

View File

@ -83,6 +83,7 @@ enum qlcnic_regs {
#define QLCNIC_CMD_CONFIG_PORT 0x2e #define QLCNIC_CMD_CONFIG_PORT 0x2e
#define QLCNIC_CMD_TEMP_SIZE 0x2f #define QLCNIC_CMD_TEMP_SIZE 0x2f
#define QLCNIC_CMD_GET_TEMP_HDR 0x30 #define QLCNIC_CMD_GET_TEMP_HDR 0x30
#define QLCNIC_CMD_CONFIG_VPORT 0x32
#define QLCNIC_CMD_GET_MAC_STATS 0x37 #define QLCNIC_CMD_GET_MAC_STATS 0x37
#define QLCNIC_CMD_SET_DRV_VER 0x38 #define QLCNIC_CMD_SET_DRV_VER 0x38
#define QLCNIC_CMD_CONFIGURE_RSS 0x41 #define QLCNIC_CMD_CONFIGURE_RSS 0x41

View File

@ -9,6 +9,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include "qlcnic.h" #include "qlcnic.h"
#include "qlcnic_sriov.h"
#include "qlcnic_hw.h" #include "qlcnic_hw.h"
#include <linux/swab.h> #include <linux/swab.h>
@ -2022,11 +2023,13 @@ static void qlcnic_remove(struct pci_dev *pdev)
return; return;
netdev = adapter->netdev; netdev = adapter->netdev;
qlcnic_sriov_pf_disable(adapter);
qlcnic_cancel_idc_work(adapter); qlcnic_cancel_idc_work(adapter);
ahw = adapter->ahw; ahw = adapter->ahw;
unregister_netdev(netdev); unregister_netdev(netdev);
qlcnic_sriov_cleanup(adapter);
if (qlcnic_83xx_check(adapter)) { if (qlcnic_83xx_check(adapter)) {
qlcnic_83xx_free_mbx_intr(adapter); qlcnic_83xx_free_mbx_intr(adapter);
@ -3430,7 +3433,10 @@ static struct pci_driver qlcnic_driver = {
.resume = qlcnic_resume, .resume = qlcnic_resume,
#endif #endif
.shutdown = qlcnic_shutdown, .shutdown = qlcnic_shutdown,
.err_handler = &qlcnic_err_handler .err_handler = &qlcnic_err_handler,
#ifdef CONFIG_QLCNIC_SRIOV
.sriov_configure = qlcnic_pci_sriov_configure,
#endif
}; };

View File

@ -0,0 +1,58 @@
/*
* QLogic qlcnic NIC Driver
* Copyright (c) 2009-2013 QLogic Corporation
*
* See LICENSE.qlcnic for copyright and licensing details.
*/
#ifndef _QLCNIC_83XX_SRIOV_H_
#define _QLCNIC_83XX_SRIOV_H_
#include "qlcnic.h"
#include <linux/types.h>
#include <linux/pci.h>
struct qlcnic_resources {
u16 num_tx_mac_filters;
u16 num_rx_ucast_mac_filters;
u16 num_rx_mcast_mac_filters;
u16 num_txvlan_keys;
u16 num_rx_queues;
u16 num_tx_queues;
u16 num_rx_buf_rings;
u16 num_rx_status_rings;
u16 num_destip;
u32 num_lro_flows_supported;
u16 max_local_ipv6_addrs;
u16 max_remote_ipv6_addrs;
};
struct qlcnic_sriov {
u16 vp_handle;
u8 num_vfs;
struct qlcnic_resources ff_max;
};
int qlcnic_sriov_init(struct qlcnic_adapter *, int);
void qlcnic_sriov_cleanup(struct qlcnic_adapter *);
void __qlcnic_sriov_cleanup(struct qlcnic_adapter *);
static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter)
{
return test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state) ? true : false;
}
#ifdef CONFIG_QLCNIC_SRIOV
void qlcnic_sriov_pf_disable(struct qlcnic_adapter *);
void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *);
int qlcnic_pci_sriov_configure(struct pci_dev *, int);
#else
static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {}
static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {}
#endif
#endif

View File

@ -0,0 +1,40 @@
/*
* QLogic qlcnic NIC Driver
* Copyright (c) 2009-2013 QLogic Corporation
*
* See LICENSE.qlcnic for copyright and licensing details.
*/
#include "qlcnic_sriov.h"
#include "qlcnic.h"
#include <linux/types.h>
int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
{
struct qlcnic_sriov *sriov;
if (!qlcnic_sriov_enable_check(adapter))
return -EIO;
sriov = kzalloc(sizeof(struct qlcnic_sriov), GFP_KERNEL);
if (!sriov)
return -ENOMEM;
adapter->ahw->sriov = sriov;
sriov->num_vfs = num_vfs;
return 0;
}
void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter)
{
if (!qlcnic_sriov_enable_check(adapter))
return;
kfree(adapter->ahw->sriov);
}
void qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter)
{
if (qlcnic_sriov_pf_check(adapter))
qlcnic_sriov_pf_cleanup(adapter);
}

View File

@ -0,0 +1,455 @@
/*
* QLogic qlcnic NIC Driver
* Copyright (c) 2009-2013 QLogic Corporation
*
* See LICENSE.qlcnic for copyright and licensing details.
*/
#include "qlcnic_sriov.h"
#include "qlcnic.h"
#include <linux/types.h>
#define QLCNIC_SRIOV_VF_MAX_MAC 1
static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8);
static int qlcnic_sriov_pf_set_vport_info(struct qlcnic_adapter *adapter,
struct qlcnic_info *npar_info,
u16 vport_id)
{
struct qlcnic_cmd_args cmd;
int err;
if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO))
return -ENOMEM;
cmd.req.arg[1] = (vport_id << 16) | 0x1;
cmd.req.arg[2] = npar_info->bit_offsets;
cmd.req.arg[2] |= npar_info->min_tx_bw << 16;
cmd.req.arg[3] = npar_info->max_tx_bw | (npar_info->max_tx_ques << 16);
cmd.req.arg[4] = npar_info->max_tx_mac_filters;
cmd.req.arg[4] |= npar_info->max_rx_mcast_mac_filters << 16;
cmd.req.arg[5] = npar_info->max_rx_ucast_mac_filters |
(npar_info->max_rx_ip_addr << 16);
cmd.req.arg[6] = npar_info->max_rx_lro_flow |
(npar_info->max_rx_status_rings << 16);
cmd.req.arg[7] = npar_info->max_rx_buf_rings |
(npar_info->max_rx_ques << 16);
cmd.req.arg[8] = npar_info->max_tx_vlan_keys;
cmd.req.arg[8] |= npar_info->max_local_ipv6_addrs << 16;
cmd.req.arg[9] = npar_info->max_remote_ipv6_addrs;
err = qlcnic_issue_cmd(adapter, &cmd);
if (err)
dev_err(&adapter->pdev->dev,
"Failed to set vport info, err=%d\n", err);
qlcnic_free_mbx_args(&cmd);
return err;
}
static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter,
struct qlcnic_info *info, u16 func)
{
struct qlcnic_sriov *sriov = adapter->ahw->sriov;
struct qlcnic_resources *res = &sriov->ff_max;
int ret = -EIO, vpid;
u32 temp, num_vf_macs, num_vfs, max;
vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
if (vpid < 0)
return -EINVAL;
num_vfs = sriov->num_vfs;
max = num_vfs + 1;
info->bit_offsets = 0xffff;
info->min_tx_bw = 0;
info->max_tx_bw = MAX_BW;
info->max_tx_ques = res->num_tx_queues / max;
info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters;
num_vf_macs = QLCNIC_SRIOV_VF_MAX_MAC;
if (adapter->ahw->pci_func == func) {
temp = res->num_rx_mcast_mac_filters - (num_vfs * num_vf_macs);
info->max_rx_ucast_mac_filters = temp;
temp = res->num_tx_mac_filters - (num_vfs * num_vf_macs);
info->max_tx_mac_filters = temp;
} else {
info->max_rx_ucast_mac_filters = num_vf_macs;
info->max_tx_mac_filters = num_vf_macs;
}
info->max_rx_ip_addr = res->num_destip / max;
info->max_rx_status_rings = res->num_rx_status_rings / max;
info->max_rx_buf_rings = res->num_rx_buf_rings / max;
info->max_rx_ques = res->num_rx_queues / max;
info->max_rx_lro_flow = res->num_lro_flows_supported / max;
info->max_tx_vlan_keys = res->num_txvlan_keys;
info->max_local_ipv6_addrs = res->max_local_ipv6_addrs;
info->max_remote_ipv6_addrs = res->max_remote_ipv6_addrs;
ret = qlcnic_sriov_pf_set_vport_info(adapter, info, vpid);
if (ret)
return ret;
return 0;
}
static void qlcnic_sriov_pf_set_ff_max_res(struct qlcnic_adapter *adapter,
struct qlcnic_info *info)
{
struct qlcnic_resources *ff_max = &adapter->ahw->sriov->ff_max;
ff_max->num_tx_mac_filters = info->max_tx_mac_filters;
ff_max->num_rx_ucast_mac_filters = info->max_rx_ucast_mac_filters;
ff_max->num_rx_mcast_mac_filters = info->max_rx_mcast_mac_filters;
ff_max->num_txvlan_keys = info->max_tx_vlan_keys;
ff_max->num_rx_queues = info->max_rx_ques;
ff_max->num_tx_queues = info->max_tx_ques;
ff_max->num_lro_flows_supported = info->max_rx_lro_flow;
ff_max->num_destip = info->max_rx_ip_addr;
ff_max->num_rx_buf_rings = info->max_rx_buf_rings;
ff_max->num_rx_status_rings = info->max_rx_status_rings;
ff_max->max_remote_ipv6_addrs = info->max_remote_ipv6_addrs;
ff_max->max_local_ipv6_addrs = info->max_local_ipv6_addrs;
}
static int qlcnic_sriov_get_pf_info(struct qlcnic_adapter *adapter,
struct qlcnic_info *npar_info)
{
int err;
struct qlcnic_cmd_args cmd;
if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO))
return -ENOMEM;
cmd.req.arg[1] = 0x2;
err = qlcnic_issue_cmd(adapter, &cmd);
if (err) {
dev_err(&adapter->pdev->dev,
"Failed to get PF info, err=%d\n", err);
goto out;
}
npar_info->total_pf = cmd.rsp.arg[2] & 0xff;
npar_info->total_rss_engines = (cmd.rsp.arg[2] >> 8) & 0xff;
npar_info->max_vports = MSW(cmd.rsp.arg[2]);
npar_info->max_tx_ques = LSW(cmd.rsp.arg[3]);
npar_info->max_tx_mac_filters = MSW(cmd.rsp.arg[3]);
npar_info->max_rx_mcast_mac_filters = LSW(cmd.rsp.arg[4]);
npar_info->max_rx_ucast_mac_filters = MSW(cmd.rsp.arg[4]);
npar_info->max_rx_ip_addr = LSW(cmd.rsp.arg[5]);
npar_info->max_rx_lro_flow = MSW(cmd.rsp.arg[5]);
npar_info->max_rx_status_rings = LSW(cmd.rsp.arg[6]);
npar_info->max_rx_buf_rings = MSW(cmd.rsp.arg[6]);
npar_info->max_rx_ques = LSW(cmd.rsp.arg[7]);
npar_info->max_tx_vlan_keys = MSW(cmd.rsp.arg[7]);
npar_info->max_local_ipv6_addrs = LSW(cmd.rsp.arg[8]);
npar_info->max_remote_ipv6_addrs = MSW(cmd.rsp.arg[8]);
dev_info(&adapter->pdev->dev,
"\n\ttotal_pf: %d,\n"
"\n\ttotal_rss_engines: %d max_vports: %d max_tx_ques %d,\n"
"\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n"
"\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n"
"\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n"
"\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n"
"\tmax_local_ipv6_addrs: %d, max_remote_ipv6_addrs: %d\n",
npar_info->total_pf, npar_info->total_rss_engines,
npar_info->max_vports, npar_info->max_tx_ques,
npar_info->max_tx_mac_filters,
npar_info->max_rx_mcast_mac_filters,
npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr,
npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings,
npar_info->max_rx_buf_rings, npar_info->max_rx_ques,
npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs,
npar_info->max_remote_ipv6_addrs);
out:
qlcnic_free_mbx_args(&cmd);
return err;
}
static void qlcnic_sriov_pf_reset_vport_handle(struct qlcnic_adapter *adapter,
u8 func)
{
struct qlcnic_sriov *sriov = adapter->ahw->sriov;
if (adapter->ahw->pci_func == func)
sriov->vp_handle = 0;
}
static void qlcnic_sriov_pf_set_vport_handle(struct qlcnic_adapter *adapter,
u16 vport_handle, u8 func)
{
struct qlcnic_sriov *sriov = adapter->ahw->sriov;
if (adapter->ahw->pci_func == func)
sriov->vp_handle = vport_handle;
}
static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *adapter,
u8 func)
{
struct qlcnic_sriov *sriov = adapter->ahw->sriov;
if (adapter->ahw->pci_func == func)
return sriov->vp_handle;
return -EINVAL;
}
static int qlcnic_sriov_pf_config_vport(struct qlcnic_adapter *adapter,
u8 flag, u16 func)
{
struct qlcnic_cmd_args cmd;
int ret;
int vpid;
if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_VPORT))
return -ENOMEM;
if (flag) {
cmd.req.arg[3] = func << 8;
} else {
vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
if (vpid < 0) {
ret = -EINVAL;
goto out;
}
cmd.req.arg[3] = ((vpid & 0xffff) << 8) | 1;
}
ret = qlcnic_issue_cmd(adapter, &cmd);
if (ret) {
dev_err(&adapter->pdev->dev,
"Failed %s vport, err %d for func 0x%x\n",
(flag ? "enable" : "disable"), ret, func);
goto out;
}
if (flag) {
vpid = cmd.rsp.arg[2] & 0xffff;
qlcnic_sriov_pf_set_vport_handle(adapter, vpid, func);
} else {
qlcnic_sriov_pf_reset_vport_handle(adapter, func);
}
out:
qlcnic_free_mbx_args(&cmd);
return ret;
}
static int qlcnic_sriov_pf_cfg_eswitch(struct qlcnic_adapter *adapter,
u8 func, u8 enable)
{
struct qlcnic_cmd_args cmd;
int err = -EIO;
if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH))
return -ENOMEM;
cmd.req.arg[0] |= (3 << 29);
cmd.req.arg[1] = ((func & 0xf) << 2) | BIT_6 | BIT_1;
if (enable)
cmd.req.arg[1] |= BIT_0;
err = qlcnic_issue_cmd(adapter, &cmd);
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
"Failed to enable sriov eswitch%d\n", err);
err = -EIO;
}
qlcnic_free_mbx_args(&cmd);
return err;
}
void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter)
{
u8 func = adapter->ahw->pci_func;
if (!qlcnic_sriov_enable_check(adapter))
return;
qlcnic_sriov_pf_config_vport(adapter, 0, func);
qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0);
__qlcnic_sriov_cleanup(adapter);
adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
}
void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter)
{
if (!qlcnic_sriov_pf_check(adapter))
return;
if (!qlcnic_sriov_enable_check(adapter))
return;
pci_disable_sriov(adapter->pdev);
netdev_info(adapter->netdev,
"SR-IOV is disabled successfully on port %d\n",
adapter->portnum);
}
static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
if (netif_running(netdev))
__qlcnic_down(adapter, netdev);
qlcnic_sriov_pf_disable(adapter);
qlcnic_sriov_pf_cleanup(adapter);
/* After disabling SRIOV re-init the driver in default mode
configure opmode based on op_mode of function
*/
if (qlcnic_83xx_configure_opmode(adapter))
return -EIO;
if (netif_running(netdev))
__qlcnic_up(adapter, netdev);
return 0;
}
static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter)
{
struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_info nic_info, pf_info, vp_info;
int err;
u8 func = ahw->pci_func;
if (!qlcnic_sriov_enable_check(adapter))
return 0;
err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1);
if (err)
goto clear_sriov_enable;
err = qlcnic_sriov_pf_config_vport(adapter, 1, func);
if (err)
goto disable_eswitch;
err = qlcnic_sriov_get_pf_info(adapter, &pf_info);
if (err)
goto delete_vport;
qlcnic_sriov_pf_set_ff_max_res(adapter, &pf_info);
err = qlcnic_get_nic_info(adapter, &nic_info, func);
if (err)
goto delete_vport;
err = qlcnic_sriov_pf_cal_res_limit(adapter, &vp_info, func);
if (err)
goto delete_vport;
ahw->physical_port = (u8) nic_info.phys_port;
ahw->switch_mode = nic_info.switch_mode;
ahw->max_mtu = nic_info.max_mtu;
ahw->capabilities = nic_info.capabilities;
ahw->nic_mode = QLC_83XX_SRIOV_MODE;
return err;
delete_vport:
qlcnic_sriov_pf_config_vport(adapter, 0, func);
disable_eswitch:
qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0);
clear_sriov_enable:
__qlcnic_sriov_cleanup(adapter);
adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
return err;
}
static int qlcnic_sriov_pf_enable(struct qlcnic_adapter *adapter, int num_vfs)
{
int err;
if (!qlcnic_sriov_enable_check(adapter))
return 0;
err = pci_enable_sriov(adapter->pdev, num_vfs);
if (err)
qlcnic_sriov_pf_cleanup(adapter);
return err;
}
static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter,
int num_vfs)
{
int err = 0;
set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
adapter->ahw->op_mode = QLCNIC_SRIOV_PF_FUNC;
if (qlcnic_sriov_init(adapter, num_vfs)) {
clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
return -EIO;
}
if (qlcnic_sriov_pf_init(adapter))
return -EIO;
err = qlcnic_sriov_pf_enable(adapter, num_vfs);
return err;
}
static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
{
struct net_device *netdev = adapter->netdev;
int err;
if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
netdev_err(netdev,
"SR-IOV cannot be enabled, when legacy interrupts are enabled\n");
return -EIO;
}
if (netif_running(netdev))
__qlcnic_down(adapter, netdev);
err = __qlcnic_pci_sriov_enable(adapter, num_vfs);
if (err) {
netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
adapter->portnum);
if (qlcnic_83xx_configure_opmode(adapter))
goto error;
} else {
netdev_info(adapter->netdev,
"SR-IOV is enabled successfully on port %d\n",
adapter->portnum);
}
if (netif_running(netdev))
__qlcnic_up(adapter, netdev);
error:
return err;
}
int qlcnic_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
{
struct qlcnic_adapter *adapter = pci_get_drvdata(dev);
int err;
if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
return -EBUSY;
if (num_vfs == 0)
err = qlcnic_pci_sriov_disable(adapter);
else
err = qlcnic_pci_sriov_enable(adapter, num_vfs);
clear_bit(__QLCNIC_RESETTING, &adapter->state);
return err;
}