Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next

Jeff Kirsher says:

====================
Intel Wired LAN Driver Updates 2015-01-13

This series contains updates to i40e and i40evf.

Mitch provides a fix for i40e to move the call to pci_disable_sriov() so
that it is called earlier to ensure that the PF driver won't free VF
resources before the VF remove routine can complete.  Also cleans up
redundant and duplicate code in the i40evf.  Refactors the i40evf
shutdown code and let the watchdog take care of shutting things down.
Fix a possible memory leak, if we are using VLANs and the communication
with the PF fail during shutdown.  On some versions of the firmware, the
VF admin send queue may become stalled.  In this case, the easiest
solution is to place another descriptor on the queue and the firmware
will then process both requests.

Greg adds a warning when the NPAR enabled partitions detected a link speed
less than 10 Gpbs.

Vasu removes redundant VN2VN MAC address which were already added by
the FCoE stack.

Shannon adds code to find how many partitions there are per port and
what is the current partition_id when in NPAR mode.  In multifunction
mode, make sure we only allow SR/IOV on the master PF of a port and
only allow partition 1 to set WoL, speed and flow control.

Kamil adds code to read the PBA block from shadow RAM and returns
the part number in a string format.

Catherine provides a fix to check if link state and link speed has
changed before exiting link event

v2: remove un-needed {} in patch #3 of the series based on feedback from
    Sergei Shtylyov
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2015-01-13 16:06:07 -05:00
commit 721f795122
10 changed files with 257 additions and 63 deletions

View File

@ -741,6 +741,65 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
}
#endif
/**
* i40e_read_pba_string - Reads part number string from EEPROM
* @hw: pointer to hardware structure
* @pba_num: stores the part number string from the EEPROM
* @pba_num_size: part number string buffer length
*
* Reads the part number string from the EEPROM.
**/
i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num,
u32 pba_num_size)
{
i40e_status status = 0;
u16 pba_word = 0;
u16 pba_size = 0;
u16 pba_ptr = 0;
u16 i = 0;
status = i40e_read_nvm_word(hw, I40E_SR_PBA_FLAGS, &pba_word);
if (status || (pba_word != 0xFAFA)) {
hw_dbg(hw, "Failed to read PBA flags or flag is invalid.\n");
return status;
}
status = i40e_read_nvm_word(hw, I40E_SR_PBA_BLOCK_PTR, &pba_ptr);
if (status) {
hw_dbg(hw, "Failed to read PBA Block pointer.\n");
return status;
}
status = i40e_read_nvm_word(hw, pba_ptr, &pba_size);
if (status) {
hw_dbg(hw, "Failed to read PBA Block size.\n");
return status;
}
/* Subtract one to get PBA word count (PBA Size word is included in
* total size)
*/
pba_size--;
if (pba_num_size < (((u32)pba_size * 2) + 1)) {
hw_dbg(hw, "Buffer to small for PBA data.\n");
return I40E_ERR_PARAM;
}
for (i = 0; i < pba_size; i++) {
status = i40e_read_nvm_word(hw, (pba_ptr + 1) + i, &pba_word);
if (status) {
hw_dbg(hw, "Failed to read PBA Block word %d.\n", i);
return status;
}
pba_num[(i * 2)] = (pba_word >> 8) & 0xFF;
pba_num[(i * 2) + 1] = pba_word & 0xFF;
}
pba_num[(pba_size * 2)] = '\0';
return status;
}
/**
* i40e_get_media_type - Gets media type
* @hw: pointer to the hardware structure
@ -2034,6 +2093,43 @@ i40e_status i40e_aq_send_msg_to_vf(struct i40e_hw *hw, u16 vfid,
return status;
}
/**
* i40e_aq_debug_read_register
* @hw: pointer to the hw struct
* @reg_addr: register address
* @reg_val: register value
* @cmd_details: pointer to command details structure or NULL
*
* Read the register using the admin queue commands
**/
i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw,
u32 reg_addr, u64 *reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aq_desc desc;
struct i40e_aqc_debug_reg_read_write *cmd_resp =
(struct i40e_aqc_debug_reg_read_write *)&desc.params.raw;
i40e_status status;
if (reg_val == NULL)
return I40E_ERR_PARAM;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_debug_read_reg);
cmd_resp->address = cpu_to_le32(reg_addr);
status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
if (!status) {
*reg_val = ((u64)cmd_resp->value_high << 32) |
(u64)cmd_resp->value_low;
*reg_val = le64_to_cpu(*reg_val);
}
return status;
}
/**
* i40e_aq_debug_write_register
* @hw: pointer to the hw struct
@ -2292,6 +2388,7 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
enum i40e_admin_queue_opc list_type_opc)
{
struct i40e_aqc_list_capabilities_element_resp *cap;
u32 valid_functions, num_functions;
u32 number, logical_id, phys_id;
struct i40e_hw_capabilities *p;
u32 i = 0;
@ -2427,6 +2524,34 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
if (p->npar_enable || p->mfp_mode_1)
p->fcoe = false;
/* count the enabled ports (aka the "not disabled" ports) */
hw->num_ports = 0;
for (i = 0; i < 4; i++) {
u32 port_cfg_reg = I40E_PRTGEN_CNF + (4 * i);
u64 port_cfg = 0;
/* use AQ read to get the physical register offset instead
* of the port relative offset
*/
i40e_aq_debug_read_register(hw, port_cfg_reg, &port_cfg, NULL);
if (!(port_cfg & I40E_PRTGEN_CNF_PORT_DIS_MASK))
hw->num_ports++;
}
valid_functions = p->valid_functions;
num_functions = 0;
while (valid_functions) {
if (valid_functions & 1)
num_functions++;
valid_functions >>= 1;
}
/* partition id is 1-based, and functions are evenly spread
* across the ports as partitions
*/
hw->partition_id = (hw->pf_id / hw->num_ports) + 1;
hw->num_partitions = num_functions / hw->num_ports;
/* additional HW specific goodies that might
* someday be HW version specific
*/

View File

@ -218,6 +218,16 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = {
#define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN)
/**
* i40e_partition_setting_complaint - generic complaint for MFP restriction
* @pf: the PF struct
**/
static void i40e_partition_setting_complaint(struct i40e_pf *pf)
{
dev_info(&pf->pdev->dev,
"The link settings are allowed to be changed only from the first partition of a given port. Please switch to the first partition in order to change the setting.\n");
}
/**
* i40e_get_settings - Get Link Speed and Duplex settings
* @netdev: network interface device structure
@ -485,6 +495,14 @@ static int i40e_set_settings(struct net_device *netdev,
u8 autoneg;
u32 advertise;
/* Changing port settings is not supported if this isn't the
* port's controlling PF
*/
if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf);
return -EOPNOTSUPP;
}
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
@ -687,6 +705,14 @@ static int i40e_set_pauseparam(struct net_device *netdev,
u8 aq_failures;
int err = 0;
/* Changing the port's flow control is not supported if this isn't the
* port's controlling PF
*/
if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf);
return -EOPNOTSUPP;
}
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
@ -1503,7 +1529,7 @@ static void i40e_get_wol(struct net_device *netdev,
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
if ((1 << hw->port) & wol_nvm_bits) {
if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) {
wol->supported = 0;
wol->wolopts = 0;
} else {
@ -1512,13 +1538,28 @@ static void i40e_get_wol(struct net_device *netdev,
}
}
/**
* i40e_set_wol - set the WakeOnLAN configuration
* @netdev: the netdev in question
* @wol: the ethtool WoL setting data
**/
static int i40e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_vsi *vsi = np->vsi;
struct i40e_hw *hw = &pf->hw;
u16 wol_nvm_bits;
/* WoL not supported if this isn't the controlling PF on the port */
if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf);
return -EOPNOTSUPP;
}
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
if (((1 << hw->port) & wol_nvm_bits))

View File

@ -1515,8 +1515,6 @@ void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi)
i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0, false, false);
i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0, false, false);
i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0, false, false);
i40e_add_filter(vsi, FIP_ALL_VN2VN_MACS, 0, false, false);
i40e_add_filter(vsi, FIP_ALL_P2P_MACS, 0, false, false);
/* use san mac */
ether_addr_copy(netdev->dev_addr, hw->mac.san_addr);

View File

@ -4557,6 +4557,15 @@ static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
return;
}
/* Warn user if link speed on NPAR enabled partition is not at
* least 10GB
*/
if (vsi->back->hw.func_caps.npar_enable &&
(vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_1GB ||
vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_100MB))
netdev_warn(vsi->netdev,
"The partition detected link speed that is less than 10Gbps\n");
switch (vsi->back->hw.phy.link_info.link_speed) {
case I40E_LINK_SPEED_40GB:
strlcpy(speed, "40 Gbps", SPEED_SIZE);
@ -5494,14 +5503,18 @@ static void i40e_link_event(struct i40e_pf *pf)
{
bool new_link, old_link;
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
u8 new_link_speed, old_link_speed;
/* set this to force the get_link_status call to refresh state */
pf->hw.phy.get_link_info = true;
old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP);
new_link = i40e_get_link_status(&pf->hw);
old_link_speed = pf->hw.phy.link_info_old.link_speed;
new_link_speed = pf->hw.phy.link_info.link_speed;
if (new_link == old_link &&
new_link_speed == old_link_speed &&
(test_bit(__I40E_DOWN, &vsi->state) ||
new_link == netif_carrier_ok(vsi->netdev)))
return;
@ -7306,7 +7319,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
#endif /* I40E_FCOE */
#ifdef CONFIG_PCI_IOV
if (pf->hw.func_caps.num_vfs) {
if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) {
pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF;
pf->flags |= I40E_FLAG_SRIOV_ENABLED;
pf->num_req_vfs = min_t(int,

View File

@ -71,6 +71,9 @@ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw,
i40e_status i40e_aq_debug_write_register(struct i40e_hw *hw,
u32 reg_addr, u64 reg_val,
struct i40e_asq_cmd_details *cmd_details);
i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw,
u32 reg_addr, u64 *reg_val,
struct i40e_asq_cmd_details *cmd_details);
i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags,
struct i40e_asq_cmd_details *cmd_details);
i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, u16 vsi_id,
@ -245,6 +248,8 @@ void i40e_clear_pxe_mode(struct i40e_hw *hw);
bool i40e_get_link_status(struct i40e_hw *hw);
i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr);
i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr);
i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num,
u32 pba_num_size);
i40e_status i40e_validate_mac_addr(u8 *mac_addr);
void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable);
#ifdef I40E_FCOE

View File

@ -431,7 +431,7 @@ struct i40e_hw {
u8 __iomem *hw_addr;
void *back;
/* function pointer structs */
/* subsystem structs */
struct i40e_phy_info phy;
struct i40e_mac_info mac;
struct i40e_bus_info bus;
@ -458,6 +458,11 @@ struct i40e_hw {
u8 pf_id;
u16 main_vsi_seid;
/* for multi-function MACs */
u16 partition_id;
u16 num_partitions;
u16 num_ports;
/* Closest numa node to the device */
u16 numa_node;
@ -1135,6 +1140,8 @@ struct i40e_hw_port_stats {
/* Checksum and Shadow RAM pointers */
#define I40E_SR_NVM_CONTROL_WORD 0x00
#define I40E_SR_EMP_MODULE_PTR 0x0F
#define I40E_SR_PBA_FLAGS 0x15
#define I40E_SR_PBA_BLOCK_PTR 0x16
#define I40E_SR_NVM_IMAGE_VERSION 0x18
#define I40E_SR_NVM_WAKE_ON_LAN 0x19
#define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27

View File

@ -791,10 +791,18 @@ void i40e_free_vfs(struct i40e_pf *pf)
if (!pf->vf)
return;
/* Disable IOV before freeing resources. This lets any VF drivers
* running in the host get themselves cleaned up before we yank
* the carpet out from underneath their feet.
*/
if (!pci_vfs_assigned(pf->pdev))
pci_disable_sriov(pf->pdev);
msleep(20); /* let any messages in transit get finished up */
/* Disable interrupt 0 so we don't try to handle the VFLR. */
i40e_irq_dynamic_disable_icr0(pf);
mdelay(10); /* let any messages in transit get finished up */
/* free up vf resources */
tmp = pf->num_alloc_vfs;
pf->num_alloc_vfs = 0;
@ -813,7 +821,6 @@ void i40e_free_vfs(struct i40e_pf *pf)
* before this function ever gets called.
*/
if (!pci_vfs_assigned(pf->pdev)) {
pci_disable_sriov(pf->pdev);
/* Acknowledge VFLR for all VFS. Without this, VFs will fail to
* work correctly when SR-IOV gets re-enabled.
*/

View File

@ -425,7 +425,7 @@ struct i40e_hw {
u8 __iomem *hw_addr;
void *back;
/* function pointer structs */
/* subsystem structs */
struct i40e_phy_info phy;
struct i40e_mac_info mac;
struct i40e_bus_info bus;
@ -452,6 +452,11 @@ struct i40e_hw {
u8 pf_id;
u16 main_vsi_seid;
/* for multi-function MACs */
u16 partition_id;
u16 num_partitions;
u16 num_ports;
/* Closest numa node to the device */
u16 numa_node;

View File

@ -313,10 +313,6 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data)
val = val | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK;
wr32(hw, I40E_VFINT_DYN_CTL01, val);
/* re-enable interrupt causes */
wr32(hw, I40E_VFINT_ICR0_ENA1, ena_mask);
wr32(hw, I40E_VFINT_DYN_CTL01, I40E_VFINT_DYN_CTL01_INTENA_MASK);
/* schedule work on the private workqueue */
schedule_work(&adapter->adminq_task);
@ -946,30 +942,6 @@ static int i40evf_up_complete(struct i40evf_adapter *adapter)
return 0;
}
/**
* i40evf_clean_all_rx_rings - Free Rx Buffers for all queues
* @adapter: board private structure
**/
static void i40evf_clean_all_rx_rings(struct i40evf_adapter *adapter)
{
int i;
for (i = 0; i < adapter->num_active_queues; i++)
i40evf_clean_rx_ring(adapter->rx_rings[i]);
}
/**
* i40evf_clean_all_tx_rings - Free Tx Buffers for all queues
* @adapter: board private structure
**/
static void i40evf_clean_all_tx_rings(struct i40evf_adapter *adapter)
{
int i;
for (i = 0; i < adapter->num_active_queues; i++)
i40evf_clean_tx_ring(adapter->tx_rings[i]);
}
/**
* i40e_down - Shutdown the connection processing
* @adapter: board private structure
@ -982,6 +954,12 @@ void i40evf_down(struct i40evf_adapter *adapter)
if (adapter->state == __I40EVF_DOWN)
return;
while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
&adapter->crit_section))
usleep_range(500, 1000);
i40evf_irq_disable(adapter);
/* remove all MAC filters */
list_for_each_entry(f, &adapter->mac_filter_list, list) {
f->remove = true;
@ -992,25 +970,27 @@ void i40evf_down(struct i40evf_adapter *adapter)
}
if (!(adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) &&
adapter->state != __I40EVF_RESETTING) {
adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
/* cancel any current operation */
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
adapter->aq_pending = 0;
/* Schedule operations to close down the HW. Don't wait
* here for this to complete. The watchdog is still running
* and it will take care of this.
*/
adapter->aq_required = I40EVF_FLAG_AQ_DEL_MAC_FILTER;
adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
/* disable receives */
adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
msleep(20);
}
netif_tx_disable(netdev);
netif_tx_stop_all_queues(netdev);
i40evf_irq_disable(adapter);
i40evf_napi_disable_all(adapter);
netif_carrier_off(netdev);
msleep(20);
i40evf_clean_all_tx_rings(adapter);
i40evf_clean_all_rx_rings(adapter);
netif_carrier_off(netdev);
clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
}
/**
@ -1356,8 +1336,13 @@ static void i40evf_watchdog_task(struct work_struct *work)
/* Process admin queue tasks. After init, everything gets done
* here so we don't race on the admin queue.
*/
if (adapter->aq_pending)
if (adapter->aq_pending) {
if (!i40evf_asq_done(hw)) {
dev_dbg(&adapter->pdev->dev, "Admin queue timeout\n");
i40evf_send_api_ver(adapter);
}
goto watchdog_done;
}
if (adapter->aq_required & I40EVF_FLAG_AQ_MAP_VECTORS) {
i40evf_map_queues(adapter);
@ -1401,11 +1386,14 @@ static void i40evf_watchdog_task(struct work_struct *work)
if (adapter->state == __I40EVF_RUNNING)
i40evf_request_stats(adapter);
i40evf_irq_enable(adapter, true);
i40evf_fire_sw_int(adapter, 0xFF);
watchdog_done:
if (adapter->state == __I40EVF_RUNNING) {
i40evf_irq_enable_queues(adapter, ~0);
i40evf_fire_sw_int(adapter, 0xFF);
} else {
i40evf_fire_sw_int(adapter, 0x1);
}
clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
restart_watchdog:
if (adapter->state == __I40EVF_REMOVE)
@ -1633,12 +1621,12 @@ static void i40evf_adminq_task(struct work_struct *work)
u16 pending;
if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
return;
goto out;
event.buf_len = I40EVF_MAX_AQ_BUF_SIZE;
event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL);
if (!event.msg_buf)
return;
goto out;
v_msg = (struct i40e_virtchnl_msg *)&event.desc;
do {
@ -1688,10 +1676,10 @@ static void i40evf_adminq_task(struct work_struct *work)
if (oldval != val)
wr32(hw, hw->aq.asq.len, val);
kfree(event.msg_buf);
out:
/* re-enable Admin queue interrupt cause */
i40evf_misc_irq_enable(adapter);
kfree(event.msg_buf);
}
/**
@ -2053,12 +2041,8 @@ static void i40evf_init_task(struct work_struct *work)
/* aq msg sent, awaiting reply */
err = i40evf_verify_api_ver(adapter);
if (err) {
dev_info(&pdev->dev, "Unable to verify API version (%d), retrying\n",
err);
if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
dev_info(&pdev->dev, "Resending request\n");
if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK)
err = i40evf_send_api_ver(adapter);
}
goto err;
}
err = i40evf_send_vf_config_msg(adapter);
@ -2081,7 +2065,6 @@ static void i40evf_init_task(struct work_struct *work)
}
err = i40evf_get_vf_config(adapter);
if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
dev_info(&pdev->dev, "Resending VF config request\n");
err = i40evf_send_vf_config_msg(adapter);
goto err;
}
@ -2440,6 +2423,7 @@ static void i40evf_remove(struct pci_dev *pdev)
struct i40evf_adapter *adapter = netdev_priv(netdev);
struct i40evf_mac_filter *f, *ftmp;
struct i40e_hw *hw = &adapter->hw;
int count = 50;
cancel_delayed_work_sync(&adapter->init_task);
cancel_work_sync(&adapter->reset_task);
@ -2448,6 +2432,11 @@ static void i40evf_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
adapter->netdev_registered = false;
}
while (count-- && adapter->aq_required)
msleep(50);
if (count < 0)
dev_err(&pdev->dev, "Timed out waiting for PF driver.\n");
adapter->state = __I40EVF_REMOVE;
if (adapter->msix_entries) {
@ -2477,6 +2466,10 @@ static void i40evf_remove(struct pci_dev *pdev)
list_del(&f->list);
kfree(f);
}
list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
list_del(&f->list);
kfree(f);
}
free_netdev(netdev);

View File

@ -715,14 +715,14 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
}
return;
}
if (v_opcode != adapter->current_op)
dev_info(&adapter->pdev->dev, "Pending op is %d, received %d\n",
adapter->current_op, v_opcode);
if (v_retval) {
dev_err(&adapter->pdev->dev, "%s: PF returned error %d to our request %d\n",
__func__, v_retval, v_opcode);
}
switch (v_opcode) {
case I40E_VIRTCHNL_OP_VERSION:
/* no action, but also not an error */
break;
case I40E_VIRTCHNL_OP_GET_STATS: {
struct i40e_eth_stats *stats =
(struct i40e_eth_stats *)msg;