Commit ee713b6d authored by David S. Miller's avatar David S. Miller

Merge branch '40GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue

Jeff Kirsher says:

====================
40GbE Intel Wired LAN Driver Updates 2018-08-30

This series contains updates to i40e, i40evf and virtchnl.

Jake implements helper functions to use an array to handle the queue
stats which reduces the boiler plate code as well as keep the complexity
localized to a few functions.

Paweł adds the ability to change a VF's MAC address from the host side
without having to reload the VF driver on the guest side.

Paul adds a check to ensure that the number of queues that the PF sends
to the VF is equal to or less than the maximum number of queues the VF
can support.

Mitch fixes an issue caught by GCC 8, where we need to not include the
terminating null in the length of the string for strncpy().

Lihong fixes a VF issue to ensure that it does not enter into
promiscuous mode when macvlan is added to the VF.  Fixed a potential
crash after a VF is removed, since the workqueue sync for the adminq
task was not being cancelled.

Harshitha fixes the type for field_flags in the virtchnl_filter struct.

Martyna removes an unnecessary check in a conditional if statement.

Björn fixes an issue reported by Jesper Dangaard Brouer, where the
driver was reporting incorrect statistics when XDP was enabled.

Jan fixes the potential reporting of incorrect speed settings.

Patryk fixed an issue where the flag
I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING was getting set when any offload is
set via ethtool.  Resolved by only setting this flag when VLAN offload
is enabled.  Also ensure we hold the rtnl lock when we are clearing the
interrupt scheme.  Added a check when deleting the MAC address from the
VF to ensure that the MAC address was not set by the PF and if it was,
do not delete it.

v2: updated patch 2 in the series based on community feedback from David
    Miller to inline a function
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f0259b6a 5907cf6c
......@@ -6,25 +6,8 @@
#include "i40e.h"
#include "i40e_diag.h"
struct i40e_stats {
/* The stat_string is expected to be a format string formatted using
* vsnprintf by i40e_add_stat_strings. Every member of a stats array
* should use the same format specifiers as they will be formatted
* using the same variadic arguments.
*/
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
#include "i40e_ethtool_stats.h"
#define I40E_STAT(_type, _name, _stat) { \
.stat_string = _name, \
.sizeof_stat = FIELD_SIZEOF(_type, _stat), \
.stat_offset = offsetof(_type, _stat) \
}
#define I40E_NETDEV_STAT(_net_stat) \
I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
#define I40E_PF_STAT(_name, _stat) \
I40E_STAT(struct i40e_pf, _name, _stat)
#define I40E_VSI_STAT(_name, _stat) \
......@@ -33,6 +16,8 @@ struct i40e_stats {
I40E_STAT(struct i40e_veb, _name, _stat)
#define I40E_PFC_STAT(_name, _stat) \
I40E_STAT(struct i40e_pfc_stats, _name, _stat)
#define I40E_QUEUE_STAT(_name, _stat) \
I40E_STAT(struct i40e_ring, _name, _stat)
static const struct i40e_stats i40e_gstrings_net_stats[] = {
I40E_NETDEV_STAT(rx_packets),
......@@ -171,20 +156,11 @@ static const struct i40e_stats i40e_gstrings_pfc_stats[] = {
I40E_PFC_STAT("port.rx_priority_%u_xon_2_xoff", priority_xon_2_xoff),
};
/* We use num_tx_queues here as a proxy for the maximum number of queues
* available because we always allocate queues symmetrically.
*/
#define I40E_MAX_NUM_QUEUES(n) ((n)->num_tx_queues)
#define I40E_QUEUE_STATS_LEN(n) \
(I40E_MAX_NUM_QUEUES(n) \
* 2 /* Tx and Rx together */ \
* (sizeof(struct i40e_queue_stats) / sizeof(u64)))
#define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats)
#define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats)
#define I40E_MISC_STATS_LEN ARRAY_SIZE(i40e_gstrings_misc_stats)
#define I40E_VSI_STATS_LEN(n) (I40E_NETDEV_STATS_LEN + \
I40E_MISC_STATS_LEN + \
I40E_QUEUE_STATS_LEN((n)))
#define I40E_VSI_STATS_LEN (I40E_NETDEV_STATS_LEN + I40E_MISC_STATS_LEN)
#define I40E_PFC_STATS_LEN (ARRAY_SIZE(i40e_gstrings_pfc_stats) * \
I40E_MAX_USER_PRIORITY)
......@@ -193,10 +169,15 @@ static const struct i40e_stats i40e_gstrings_pfc_stats[] = {
(ARRAY_SIZE(i40e_gstrings_veb_tc_stats) * \
I40E_MAX_TRAFFIC_CLASS))
#define I40E_PF_STATS_LEN(n) (I40E_GLOBAL_STATS_LEN + \
#define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats)
#define I40E_PF_STATS_LEN (I40E_GLOBAL_STATS_LEN + \
I40E_PFC_STATS_LEN + \
I40E_VEB_STATS_LEN + \
I40E_VSI_STATS_LEN((n)))
I40E_VSI_STATS_LEN)
/* Length of stats for a single queue */
#define I40E_QUEUE_STATS_LEN ARRAY_SIZE(i40e_gstrings_queue_stats)
enum i40e_ethtool_test_id {
I40E_ETH_TEST_REG = 0,
......@@ -1701,11 +1682,30 @@ static int i40e_get_stats_count(struct net_device *netdev)
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
int stats_len;
if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1)
return I40E_PF_STATS_LEN(netdev);
stats_len = I40E_PF_STATS_LEN;
else
return I40E_VSI_STATS_LEN(netdev);
stats_len = I40E_VSI_STATS_LEN;
/* The number of stats reported for a given net_device must remain
* constant throughout the life of that device.
*
* This is because the API for obtaining the size, strings, and stats
* is spread out over three separate ethtool ioctls. There is no safe
* way to lock the number of stats across these calls, so we must
* assume that they will never change.
*
* Due to this, we report the maximum number of queues, even if not
* every queue is currently configured. Since we always allocate
* queues in pairs, we'll just use netdev->num_tx_queues * 2. This
* works because the num_tx_queues is set at device creation and never
* changes.
*/
stats_len += I40E_QUEUE_STATS_LEN * 2 * netdev->num_tx_queues;
return stats_len;
}
static int i40e_get_sset_count(struct net_device *netdev, int sset)
......@@ -1727,89 +1727,6 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset)
}
}
/**
* i40e_add_one_ethtool_stat - copy the stat into the supplied buffer
* @data: location to store the stat value
* @pointer: basis for where to copy from
* @stat: the stat definition
*
* Copies the stat data defined by the pointer and stat structure pair into
* the memory supplied as data. Used to implement i40e_add_ethtool_stats.
* If the pointer is null, data will be zero'd.
*/
static inline void
i40e_add_one_ethtool_stat(u64 *data, void *pointer,
const struct i40e_stats *stat)
{
char *p;
if (!pointer) {
/* ensure that the ethtool data buffer is zero'd for any stats
* which don't have a valid pointer.
*/
*data = 0;
return;
}
p = (char *)pointer + stat->stat_offset;
switch (stat->sizeof_stat) {
case sizeof(u64):
*data = *((u64 *)p);
break;
case sizeof(u32):
*data = *((u32 *)p);
break;
case sizeof(u16):
*data = *((u16 *)p);
break;
case sizeof(u8):
*data = *((u8 *)p);
break;
default:
WARN_ONCE(1, "unexpected stat size for %s",
stat->stat_string);
*data = 0;
}
}
/**
* __i40e_add_ethtool_stats - copy stats into the ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location to copy stats from
* @stats: array of stats to copy
* @size: the size of the stats definition
*
* Copy the stats defined by the stats array using the pointer as a base into
* the data buffer supplied by ethtool. Updates the data pointer to point to
* the next empty location for successive calls to __i40e_add_ethtool_stats.
* If pointer is null, set the data values to zero and update the pointer to
* skip these stats.
**/
static inline void
__i40e_add_ethtool_stats(u64 **data, void *pointer,
const struct i40e_stats stats[],
const unsigned int size)
{
unsigned int i;
for (i = 0; i < size; i++)
i40e_add_one_ethtool_stat((*data)++, pointer, &stats[i]);
}
/**
* i40e_add_ethtool_stats - copy stats into ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location where stats are stored
* @stats: static const array of stat definitions
*
* Macro to ease the use of __i40e_add_ethtool_stats by taking a static
* constant stats array and passing the ARRAY_SIZE(). This avoids typos by
* ensuring that we pass the size associated with the given stats array.
* Assumes that stats is an array.
**/
#define i40e_add_ethtool_stats(data, pointer, stats) \
__i40e_add_ethtool_stats(data, pointer, stats, ARRAY_SIZE(stats))
/**
* i40e_get_pfc_stats - copy HW PFC statistics to formatted structure
* @pf: the PF device structure
......@@ -1853,12 +1770,10 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_ring *tx_ring, *rx_ring;
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
struct i40e_veb *veb = pf->veb[pf->lan_veb];
unsigned int i;
unsigned int start;
bool veb_stats;
u64 *p = data;
......@@ -1870,38 +1785,12 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
i40e_add_ethtool_stats(&data, vsi, i40e_gstrings_misc_stats);
rcu_read_lock();
for (i = 0; i < I40E_MAX_NUM_QUEUES(netdev) ; i++) {
tx_ring = READ_ONCE(vsi->tx_rings[i]);
if (!tx_ring) {
/* Bump the stat counter to skip these stats, and make
* sure the memory is zero'd
*/
*(data++) = 0;
*(data++) = 0;
*(data++) = 0;
*(data++) = 0;
continue;
}
/* process Tx ring statistics */
do {
start = u64_stats_fetch_begin_irq(&tx_ring->syncp);
data[0] = tx_ring->stats.packets;
data[1] = tx_ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start));
data += 2;
/* Rx ring is the 2nd half of the queue pair */
rx_ring = &tx_ring[1];
do {
start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
data[0] = rx_ring->stats.packets;
data[1] = rx_ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
data += 2;
for (i = 0; i < netdev->num_tx_queues; i++) {
i40e_add_queue_stats(&data, READ_ONCE(vsi->tx_rings[i]));
i40e_add_queue_stats(&data, READ_ONCE(vsi->rx_rings[i]));
}
rcu_read_unlock();
if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
goto check_data_pointer;
......@@ -1932,42 +1821,6 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
"ethtool stats count mismatch!");
}
/**
* __i40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
* @size: size of the stats array
*
* Format and copy the strings described by stats into the buffer pointed at
* by p.
**/
static void __i40e_add_stat_strings(u8 **p, const struct i40e_stats stats[],
const unsigned int size, ...)
{
unsigned int i;
for (i = 0; i < size; i++) {
va_list args;
va_start(args, size);
vsnprintf(*p, ETH_GSTRING_LEN, stats[i].stat_string, args);
*p += ETH_GSTRING_LEN;
va_end(args);
}
}
/**
* 40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
*
* Format and copy the strings described by the const static stats value into
* the buffer pointed at by p. Assumes that stats can have ARRAY_SIZE called
* for it.
**/
#define i40e_add_stat_strings(p, stats, ...) \
__i40e_add_stat_strings(p, stats, ARRAY_SIZE(stats), ## __VA_ARGS__)
/**
* i40e_get_stat_strings - copy stat strings into supplied buffer
* @netdev: the netdev to collect strings for
......@@ -1990,16 +1843,13 @@ static void i40e_get_stat_strings(struct net_device *netdev, u8 *data)
i40e_add_stat_strings(&data, i40e_gstrings_misc_stats);
for (i = 0; i < I40E_MAX_NUM_QUEUES(netdev); i++) {
snprintf(data, ETH_GSTRING_LEN, "tx-%u.tx_packets", i);
data += ETH_GSTRING_LEN;
snprintf(data, ETH_GSTRING_LEN, "tx-%u.tx_bytes", i);
data += ETH_GSTRING_LEN;
snprintf(data, ETH_GSTRING_LEN, "rx-%u.rx_packets", i);
data += ETH_GSTRING_LEN;
snprintf(data, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i);
data += ETH_GSTRING_LEN;
for (i = 0; i < netdev->num_tx_queues; i++) {
i40e_add_stat_strings(&data, i40e_gstrings_queue_stats,
"tx", i);
i40e_add_stat_strings(&data, i40e_gstrings_queue_stats,
"rx", i);
}
if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
return;
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2013 - 2018 Intel Corporation. */
/* ethtool statistics helpers */
/**
* struct i40e_stats - definition for an ethtool statistic
* @stat_string: statistic name to display in ethtool -S output
* @sizeof_stat: the sizeof() the stat, must be no greater than sizeof(u64)
* @stat_offset: offsetof() the stat from a base pointer
*
* This structure defines a statistic to be added to the ethtool stats buffer.
* It defines a statistic as offset from a common base pointer. Stats should
* be defined in constant arrays using the I40E_STAT macro, with every element
* of the array using the same _type for calculating the sizeof_stat and
* stat_offset.
*
* The @sizeof_stat is expected to be sizeof(u8), sizeof(u16), sizeof(u32) or
* sizeof(u64). Other sizes are not expected and will produce a WARN_ONCE from
* the i40e_add_ethtool_stat() helper function.
*
* The @stat_string is interpreted as a format string, allowing formatted
* values to be inserted while looping over multiple structures for a given
* statistics array. Thus, every statistic string in an array should have the
* same type and number of format specifiers, to be formatted by variadic
* arguments to the i40e_add_stat_string() helper function.
**/
struct i40e_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
/* Helper macro to define an i40e_stat structure with proper size and type.
* Use this when defining constant statistics arrays. Note that @_type expects
* only a type name and is used multiple times.
*/
#define I40E_STAT(_type, _name, _stat) { \
.stat_string = _name, \
.sizeof_stat = FIELD_SIZEOF(_type, _stat), \
.stat_offset = offsetof(_type, _stat) \
}
/* Helper macro for defining some statistics directly copied from the netdev
* stats structure.
*/
#define I40E_NETDEV_STAT(_net_stat) \
I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
/* Helper macro for defining some statistics related to queues */
#define I40E_QUEUE_STAT(_name, _stat) \
I40E_STAT(struct i40e_ring, _name, _stat)
/* Stats associated with a Tx or Rx ring */
static const struct i40e_stats i40e_gstrings_queue_stats[] = {
I40E_QUEUE_STAT("%s-%u.packets", stats.packets),
I40E_QUEUE_STAT("%s-%u.bytes", stats.bytes),
};
/**
* i40e_add_one_ethtool_stat - copy the stat into the supplied buffer
* @data: location to store the stat value
* @pointer: basis for where to copy from
* @stat: the stat definition
*
* Copies the stat data defined by the pointer and stat structure pair into
* the memory supplied as data. Used to implement i40e_add_ethtool_stats and
* i40e_add_queue_stats. If the pointer is null, data will be zero'd.
*/
static inline void
i40e_add_one_ethtool_stat(u64 *data, void *pointer,
const struct i40e_stats *stat)
{
char *p;
if (!pointer) {
/* ensure that the ethtool data buffer is zero'd for any stats
* which don't have a valid pointer.
*/
*data = 0;
return;
}
p = (char *)pointer + stat->stat_offset;
switch (stat->sizeof_stat) {
case sizeof(u64):
*data = *((u64 *)p);
break;
case sizeof(u32):
*data = *((u32 *)p);
break;
case sizeof(u16):
*data = *((u16 *)p);
break;
case sizeof(u8):
*data = *((u8 *)p);
break;
default:
WARN_ONCE(1, "unexpected stat size for %s",
stat->stat_string);
*data = 0;
}
}
/**
* __i40e_add_ethtool_stats - copy stats into the ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location to copy stats from
* @stats: array of stats to copy
* @size: the size of the stats definition
*
* Copy the stats defined by the stats array using the pointer as a base into
* the data buffer supplied by ethtool. Updates the data pointer to point to
* the next empty location for successive calls to __i40e_add_ethtool_stats.
* If pointer is null, set the data values to zero and update the pointer to
* skip these stats.
**/
static inline void
__i40e_add_ethtool_stats(u64 **data, void *pointer,
const struct i40e_stats stats[],
const unsigned int size)
{
unsigned int i;
for (i = 0; i < size; i++)
i40e_add_one_ethtool_stat((*data)++, pointer, &stats[i]);
}
/**
* i40e_add_ethtool_stats - copy stats into ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location where stats are stored
* @stats: static const array of stat definitions
*
* Macro to ease the use of __i40e_add_ethtool_stats by taking a static
* constant stats array and passing the ARRAY_SIZE(). This avoids typos by
* ensuring that we pass the size associated with the given stats array.
*
* The parameter @stats is evaluated twice, so parameters with side effects
* should be avoided.
**/
#define i40e_add_ethtool_stats(data, pointer, stats) \
__i40e_add_ethtool_stats(data, pointer, stats, ARRAY_SIZE(stats))
/**
* i40e_add_queue_stats - copy queue statistics into supplied buffer
* @data: ethtool stats buffer
* @ring: the ring to copy
*
* Queue statistics must be copied while protected by
* u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats.
* Assumes that queue stats are defined in i40e_gstrings_queue_stats. If the
* ring pointer is null, zero out the queue stat values and update the data
* pointer. Otherwise safely copy the stats from the ring into the supplied
* buffer and update the data pointer when finished.
*
* This function expects to be called while under rcu_read_lock().
**/
static inline void
i40e_add_queue_stats(u64 **data, struct i40e_ring *ring)
{
const unsigned int size = ARRAY_SIZE(i40e_gstrings_queue_stats);
const struct i40e_stats *stats = i40e_gstrings_queue_stats;
unsigned int start;
unsigned int i;
/* To avoid invalid statistics values, ensure that we keep retrying
* the copy until we get a consistent value according to
* u64_stats_fetch_retry_irq. But first, make sure our ring is
* non-null before attempting to access its syncp.
*/
do {
start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp);
for (i = 0; i < size; i++) {
i40e_add_one_ethtool_stat(&(*data)[i], ring,
&stats[i]);
}
} while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start));
/* Once we successfully copy the stats in, update the data pointer */
*data += size;
}
/**
* __i40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
* @size: size of the stats array
*
* Format and copy the strings described by stats into the buffer pointed at
* by p.
**/
static inline void __i40e_add_stat_strings(u8 **p, const struct i40e_stats stats[],
const unsigned int size, ...)
{
unsigned int i;
for (i = 0; i < size; i++) {
va_list args;
va_start(args, size);
vsnprintf(*p, ETH_GSTRING_LEN, stats[i].stat_string, args);
*p += ETH_GSTRING_LEN;
va_end(args);
}
}
/**
* 40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
*
* Format and copy the strings described by the const static stats value into
* the buffer pointed at by p.
*
* The parameter @stats is evaluated twice, so parameters with side effects
* should be avoided. Additionally, stats must be an array such that
* ARRAY_SIZE can be called on it.
**/
#define i40e_add_stat_strings(p, stats, ...) \
__i40e_add_stat_strings(p, stats, ARRAY_SIZE(stats), ## __VA_ARGS__)
......@@ -420,9 +420,9 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev,
struct rtnl_link_stats64 *stats)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_ring *tx_ring, *rx_ring;
struct i40e_vsi *vsi = np->vsi;
struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
struct i40e_ring *ring;
int i;
if (test_bit(__I40E_VSI_DOWN, vsi->state))
......@@ -436,24 +436,26 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev,
u64 bytes, packets;
unsigned int start;
tx_ring = READ_ONCE(vsi->tx_rings[i]);
if (!tx_ring)
ring = READ_ONCE(vsi->tx_rings[i]);
if (!ring)
continue;
i40e_get_netdev_stats_struct_tx(tx_ring, stats);
i40e_get_netdev_stats_struct_tx(ring, stats);
rx_ring = &tx_ring[1];
if (i40e_enabled_xdp_vsi(vsi)) {
ring++;
i40e_get_netdev_stats_struct_tx(ring, stats);
}
ring++;
do {
start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
packets = rx_ring->stats.packets;
bytes = rx_ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
start = u64_stats_fetch_begin_irq(&ring->syncp);
packets = ring->stats.packets;
bytes = ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->rx_packets += packets;
stats->rx_bytes += bytes;
if (i40e_enabled_xdp_vsi(vsi))
i40e_get_netdev_stats_struct_tx(&rx_ring[1], stats);
}
rcu_read_unlock();
......@@ -6568,6 +6570,24 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
struct i40e_hw *hw = &pf->hw;
i40e_status err;
u64 mask;
u8 speed;
/* Card might've been put in an unstable state by other drivers
* and applications, which causes incorrect speed values being
* set on startup. In order to clear speed registers, we call
* get_phy_capabilities twice, once to get initial state of
* available speeds, and once to get current PHY config.
*/
err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities,
NULL);
if (err) {
dev_err(&pf->pdev->dev,
"failed to get phy cap., ret = %s last_status = %s\n",
i40e_stat_str(hw, err),
i40e_aq_str(hw, hw->aq.asq_last_status));
return err;
}
speed = abilities.link_speed;
/* Get the current phy config */
err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
......@@ -6581,9 +6601,9 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
}
/* If link needs to go up, but was not forced to go down,
* no need for a flap
* and its speed values are OK, no need for a flap
*/
if (is_up && abilities.phy_type != 0)
if (is_up && abilities.phy_type != 0 && abilities.link_speed != 0)
return I40E_SUCCESS;
/* To force link we need to set bits for all supported PHY types,
......@@ -6595,7 +6615,10 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
config.phy_type_ext = is_up ? (u8)((mask >> 32) & 0xff) : 0;
/* Copy the old settings, except of phy_type */
config.abilities = abilities.abilities;
if (abilities.link_speed != 0)
config.link_speed = abilities.link_speed;
else
config.link_speed = speed;
config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan;
......@@ -13033,7 +13056,7 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags,
for (vsi_idx = 0; vsi_idx < pf->num_alloc_vsi; vsi_idx++)
if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid)
break;
if (vsi_idx >= pf->num_alloc_vsi && vsi_seid != 0) {
if (vsi_idx == pf->num_alloc_vsi && vsi_seid != 0) {
dev_info(&pf->pdev->dev, "vsi seid %d not found\n",
vsi_seid);
return NULL;
......@@ -14159,6 +14182,7 @@ static void i40e_remove(struct pci_dev *pdev)
mutex_destroy(&hw->aq.asq_mutex);
/* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
rtnl_lock();
i40e_clear_interrupt_scheme(pf);
for (i = 0; i < pf->num_alloc_vsi; i++) {
if (pf->vsi[i]) {
......@@ -14167,6 +14191,7 @@ static void i40e_remove(struct pci_dev *pdev)
pf->vsi[i] = NULL;
}
}
rtnl_unlock();
for (i = 0; i < I40E_MAX_VEB; i++) {
kfree(pf->veb[i]);
......@@ -14378,7 +14403,13 @@ static void i40e_shutdown(struct pci_dev *pdev)
wr32(hw, I40E_PFPM_WUFC,
(pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0));
/* Since we're going to destroy queues during the
* i40e_clear_interrupt_scheme() we should hold the RTNL lock for this
* whole section
*/
rtnl_lock();
i40e_clear_interrupt_scheme(pf);
rtnl_unlock();
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, pf->wol_en);
......
......@@ -694,7 +694,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
if (!IS_ERR_OR_NULL(pf->ptp_clock))
return 0;
strncpy(pf->ptp_caps.name, i40e_driver_name, sizeof(pf->ptp_caps.name));
strncpy(pf->ptp_caps.name, i40e_driver_name,
sizeof(pf->ptp_caps.name) - 1);
pf->ptp_caps.owner = THIS_MODULE;
pf->ptp_caps.max_adj = 999999999;
pf->ptp_caps.n_ext_ts = 0;
......
......@@ -2458,7 +2458,7 @@ static inline int i40e_check_vf_permission(struct i40e_vf *vf,
!is_multicast_ether_addr(addr) && vf->pf_set_mac &&
!ether_addr_equal(addr, vf->default_lan_addr.addr)) {
dev_err(&pf->pdev->dev,
"VF attempting to override administratively set MAC address, reload the VF driver to resume normal operation\n");
"VF attempting to override administratively set MAC address, bring down and up the VF interface to resume normal operation\n");
return -EPERM;
}
}
......@@ -2569,6 +2569,16 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
ret = I40E_ERR_INVALID_MAC_ADDR;
goto error_param;
}
if (vf->pf_set_mac &&
ether_addr_equal(al->list[i].addr,
vf->default_lan_addr.addr)) {
dev_err(&pf->pdev->dev,
"MAC addr %pM has been set by PF, cannot delete it for VF %d, reset VF to change MAC addr\n",
vf->default_lan_addr.addr, vf->vf_id);
ret = I40E_ERR_PARAM;
goto error_param;
}
}
vsi = pf->vsi[vf->lan_vsi_idx];
......@@ -3873,9 +3883,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
mac, vf_id);
}
/* Force the VF driver stop so it has to reload with new MAC address */
/* Force the VF interface down so it has to bring up with new MAC
* address
*/
i40e_vc_disable_vf(vf);
dev_info(&pf->pdev->dev, "Reload the VF driver to make this change effective.\n");
dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n");
error_param:
return ret;
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2013 - 2018 Intel Corporation. */
/* ethtool statistics helpers */
/**
* struct i40e_stats - definition for an ethtool statistic
* @stat_string: statistic name to display in ethtool -S output
* @sizeof_stat: the sizeof() the stat, must be no greater than sizeof(u64)
* @stat_offset: offsetof() the stat from a base pointer
*
* This structure defines a statistic to be added to the ethtool stats buffer.
* It defines a statistic as offset from a common base pointer. Stats should
* be defined in constant arrays using the I40E_STAT macro, with every element
* of the array using the same _type for calculating the sizeof_stat and
* stat_offset.
*
* The @sizeof_stat is expected to be sizeof(u8), sizeof(u16), sizeof(u32) or
* sizeof(u64). Other sizes are not expected and will produce a WARN_ONCE from
* the i40e_add_ethtool_stat() helper function.
*
* The @stat_string is interpreted as a format string, allowing formatted
* values to be inserted while looping over multiple structures for a given
* statistics array. Thus, every statistic string in an array should have the
* same type and number of format specifiers, to be formatted by variadic
* arguments to the i40e_add_stat_string() helper function.
**/
struct i40e_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
/* Helper macro to define an i40e_stat structure with proper size and type.
* Use this when defining constant statistics arrays. Note that @_type expects
* only a type name and is used multiple times.
*/
#define I40E_STAT(_type, _name, _stat) { \
.stat_string = _name, \
.sizeof_stat = FIELD_SIZEOF(_type, _stat), \
.stat_offset = offsetof(_type, _stat) \
}
/* Helper macro for defining some statistics directly copied from the netdev
* stats structure.
*/
#define I40E_NETDEV_STAT(_net_stat) \
I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
/* Helper macro for defining some statistics related to queues */
#define I40E_QUEUE_STAT(_name, _stat) \
I40E_STAT(struct i40e_ring, _name, _stat)
/* Stats associated with a Tx or Rx ring */
static const struct i40e_stats i40e_gstrings_queue_stats[] = {
I40E_QUEUE_STAT("%s-%u.packets", stats.packets),
I40E_QUEUE_STAT("%s-%u.bytes", stats.bytes),
};
/**
* i40evf_add_one_ethtool_stat - copy the stat into the supplied buffer
* @data: location to store the stat value
* @pointer: basis for where to copy from
* @stat: the stat definition
*
* Copies the stat data defined by the pointer and stat structure pair into
* the memory supplied as data. Used to implement i40e_add_ethtool_stats and
* i40evf_add_queue_stats. If the pointer is null, data will be zero'd.
*/
static inline void
i40evf_add_one_ethtool_stat(u64 *data, void *pointer,
const struct i40e_stats *stat)
{
char *p;
if (!pointer) {
/* ensure that the ethtool data buffer is zero'd for any stats
* which don't have a valid pointer.
*/
*data = 0;
return;
}
p = (char *)pointer + stat->stat_offset;
switch (stat->sizeof_stat) {
case sizeof(u64):
*data = *((u64 *)p);
break;
case sizeof(u32):
*data = *((u32 *)p);
break;
case sizeof(u16):
*data = *((u16 *)p);
break;
case sizeof(u8):
*data = *((u8 *)p);
break;
default:
WARN_ONCE(1, "unexpected stat size for %s",
stat->stat_string);
*data = 0;
}
}
/**
* __i40evf_add_ethtool_stats - copy stats into the ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location to copy stats from
* @stats: array of stats to copy
* @size: the size of the stats definition
*
* Copy the stats defined by the stats array using the pointer as a base into
* the data buffer supplied by ethtool. Updates the data pointer to point to
* the next empty location for successive calls to __i40evf_add_ethtool_stats.
* If pointer is null, set the data values to zero and update the pointer to
* skip these stats.
**/
static inline void
__i40evf_add_ethtool_stats(u64 **data, void *pointer,
const struct i40e_stats stats[],
const unsigned int size)
{
unsigned int i;
for (i = 0; i < size; i++)
i40evf_add_one_ethtool_stat((*data)++, pointer, &stats[i]);
}
/**
* i40e_add_ethtool_stats - copy stats into ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location where stats are stored
* @stats: static const array of stat definitions
*
* Macro to ease the use of __i40evf_add_ethtool_stats by taking a static
* constant stats array and passing the ARRAY_SIZE(). This avoids typos by
* ensuring that we pass the size associated with the given stats array.
*
* The parameter @stats is evaluated twice, so parameters with side effects
* should be avoided.
**/
#define i40e_add_ethtool_stats(data, pointer, stats) \
__i40evf_add_ethtool_stats(data, pointer, stats, ARRAY_SIZE(stats))
/**
* i40evf_add_queue_stats - copy queue statistics into supplied buffer
* @data: ethtool stats buffer
* @ring: the ring to copy
*
* Queue statistics must be copied while protected by
* u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats.
* Assumes that queue stats are defined in i40e_gstrings_queue_stats. If the
* ring pointer is null, zero out the queue stat values and update the data
* pointer. Otherwise safely copy the stats from the ring into the supplied
* buffer and update the data pointer when finished.
*
* This function expects to be called while under rcu_read_lock().
**/
static inline void
i40evf_add_queue_stats(u64 **data, struct i40e_ring *ring)
{
const unsigned int size = ARRAY_SIZE(i40e_gstrings_queue_stats);
const struct i40e_stats *stats = i40e_gstrings_queue_stats;
unsigned int start;
unsigned int i;
/* To avoid invalid statistics values, ensure that we keep retrying
* the copy until we get a consistent value according to
* u64_stats_fetch_retry_irq. But first, make sure our ring is
* non-null before attempting to access its syncp.
*/
do {
start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp);
for (i = 0; i < size; i++) {
i40evf_add_one_ethtool_stat(&(*data)[i], ring,
&stats[i]);
}
} while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start));
/* Once we successfully copy the stats in, update the data pointer */
*data += size;
}
/**
* __i40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
* @size: size of the stats array
*
* Format and copy the strings described by stats into the buffer pointed at
* by p.
**/
static void __i40e_add_stat_strings(u8 **p, const struct i40e_stats stats[],
const unsigned int size, ...)
{
unsigned int i;
for (i = 0; i < size; i++) {
va_list args;
va_start(args, size);
vsnprintf(*p, ETH_GSTRING_LEN, stats[i].stat_string, args);
*p += ETH_GSTRING_LEN;
va_end(args);
}
}
/**
* 40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
*
* Format and copy the strings described by the const static stats value into
* the buffer pointed at by p.
*
* The parameter @stats is evaluated twice, so parameters with side effects
* should be avoided. Additionally, stats must be an array such that
* ARRAY_SIZE can be called on it.
**/
#define i40e_add_stat_strings(p, stats, ...) \
__i40e_add_stat_strings(p, stats, ARRAY_SIZE(stats), ## __VA_ARGS__)
......@@ -6,18 +6,12 @@
#include <linux/uaccess.h>
struct i40evf_stats {
char stat_string[ETH_GSTRING_LEN];
int stat_offset;
};
#include "i40e_ethtool_stats.h"
#define I40EVF_STAT(_name, _stat) { \
.stat_string = _name, \
.stat_offset = offsetof(struct i40evf_adapter, _stat) \
}
#define I40EVF_STAT(_name, _stat) \
I40E_STAT(struct i40evf_adapter, _name, _stat)
/* All stats are u64, so we don't need to track the size of the field. */
static const struct i40evf_stats i40evf_gstrings_stats[] = {
static const struct i40e_stats i40evf_gstrings_stats[] = {
I40EVF_STAT("rx_bytes", current_stats.rx_bytes),
I40EVF_STAT("rx_unicast", current_stats.rx_unicast),
I40EVF_STAT("rx_multicast", current_stats.rx_multicast),
......@@ -32,13 +26,9 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = {
I40EVF_STAT("tx_errors", current_stats.tx_errors),
};
#define I40EVF_GLOBAL_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats)
#define I40EVF_QUEUE_STATS_LEN(_dev) \
(((struct i40evf_adapter *)\
netdev_priv(_dev))->num_active_queues \
* 2 * (sizeof(struct i40e_queue_stats) / sizeof(u64)))
#define I40EVF_STATS_LEN(_dev) \
(I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev))
#define I40EVF_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats)
#define I40EVF_QUEUE_STATS_LEN ARRAY_SIZE(i40e_gstrings_queue_stats)
/* For now we have one and only one private flag and it is only defined
* when we have support for the SKIP_CPU_SYNC DMA attribute. Instead
......@@ -117,13 +107,13 @@ static int i40evf_get_link_ksettings(struct net_device *netdev,
* @netdev: network interface device structure
* @sset: id of string set
*
* Reports size of string table. This driver only supports
* strings for statistics.
* Reports size of various string tables.
**/
static int i40evf_get_sset_count(struct net_device *netdev, int sset)
{
if (sset == ETH_SS_STATS)
return I40EVF_STATS_LEN(netdev);
return I40EVF_STATS_LEN +
(I40EVF_QUEUE_STATS_LEN * 2 * I40EVF_MAX_REQ_QUEUES);
else if (sset == ETH_SS_PRIV_FLAGS)
return I40EVF_PRIV_FLAGS_STR_LEN;
else
......@@ -142,20 +132,66 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
unsigned int i, j;
char *p;
unsigned int i;
i40e_add_ethtool_stats(&data, adapter, i40evf_gstrings_stats);
rcu_read_lock();
for (i = 0; i < I40EVF_MAX_REQ_QUEUES; i++) {
struct i40e_ring *ring;
/* Avoid accessing un-allocated queues */
ring = (i < adapter->num_active_queues ?
&adapter->tx_rings[i] : NULL);
i40evf_add_queue_stats(&data, ring);
for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) {
p = (char *)adapter + i40evf_gstrings_stats[i].stat_offset;
data[i] = *(u64 *)p;
/* Avoid accessing un-allocated queues */
ring = (i < adapter->num_active_queues ?
&adapter->rx_rings[i] : NULL);
i40evf_add_queue_stats(&data, ring);
}
for (j = 0; j < adapter->num_active_queues; j++) {
data[i++] = adapter->tx_rings[j].stats.packets;
data[i++] = adapter->tx_rings[j].stats.bytes;
rcu_read_unlock();
}
/**
* i40evf_get_priv_flag_strings - Get private flag strings
* @netdev: network interface device structure
* @data: buffer for string data
*
* Builds the private flags string table
**/
static void i40evf_get_priv_flag_strings(struct net_device *netdev, u8 *data)
{
unsigned int i;
for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) {
snprintf(data, ETH_GSTRING_LEN, "%s",
i40evf_gstrings_priv_flags[i].flag_string);
data += ETH_GSTRING_LEN;
}
for (j = 0; j < adapter->num_active_queues; j++) {
data[i++] = adapter->rx_rings[j].stats.packets;
data[i++] = adapter->rx_rings[j].stats.bytes;
}
/**
* i40evf_get_stat_strings - Get stat strings
* @netdev: network interface device structure
* @data: buffer for string data
*
* Builds the statistics string table
**/
static void i40evf_get_stat_strings(struct net_device *netdev, u8 *data)
{
unsigned int i;
i40e_add_stat_strings(&data, i40evf_gstrings_stats);
/* Queues are always allocated in pairs, so we just use num_tx_queues
* for both Tx and Rx queues.
*/
for (i = 0; i < netdev->num_tx_queues; i++) {
i40e_add_stat_strings(&data, i40e_gstrings_queue_stats,
"tx", i);
i40e_add_stat_strings(&data, i40e_gstrings_queue_stats,
"rx", i);
}
}
......@@ -165,38 +201,19 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev,
* @sset: id of string set
* @data: buffer for string data
*
* Builds stats string table.
* Builds string tables for various string sets
**/
static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
u8 *p = data;
int i;
if (sset == ETH_SS_STATS) {
for (i = 0; i < (int)I40EVF_GLOBAL_STATS_LEN; i++) {
memcpy(p, i40evf_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
for (i = 0; i < adapter->num_active_queues; i++) {
snprintf(p, ETH_GSTRING_LEN, "tx-%u.packets", i);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "tx-%u.bytes", i);
p += ETH_GSTRING_LEN;
}
for (i = 0; i < adapter->num_active_queues; i++) {
snprintf(p, ETH_GSTRING_LEN, "rx-%u.packets", i);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "rx-%u.bytes", i);
p += ETH_GSTRING_LEN;
}
} else if (sset == ETH_SS_PRIV_FLAGS) {
for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) {
snprintf(p, ETH_GSTRING_LEN, "%s",
i40evf_gstrings_priv_flags[i].flag_string);
p += ETH_GSTRING_LEN;
}
switch (sset) {
case ETH_SS_STATS:
i40evf_get_stat_strings(netdev, data);
break;
case ETH_SS_PRIV_FLAGS:
i40evf_get_priv_flag_strings(netdev, data);
break;
default:
break;
}
}
......
......@@ -3120,18 +3120,19 @@ static int i40evf_set_features(struct net_device *netdev,
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
/* Don't allow changing VLAN_RX flag when VLAN is set for VF
* and return an error in this case
/* Don't allow changing VLAN_RX flag when adapter is not capable
* of VLAN offload
*/
if (VLAN_ALLOWED(adapter)) {
if (!VLAN_ALLOWED(adapter)) {
if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX)
return -EINVAL;
} else if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX) {
if (features & NETIF_F_HW_VLAN_CTAG_RX)
adapter->aq_required |=
I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING;
else
adapter->aq_required |=
I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING;
} else if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX) {
return -EINVAL;
}
return 0;
......@@ -3358,6 +3359,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN)
netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
netdev->priv_flags |= IFF_UNICAST_FLT;
/* Do not turn on offloads when they are requested to be turned off.
* TSO needs minimum 576 bytes to work correctly.
*/
......@@ -3907,6 +3910,8 @@ static void i40evf_remove(struct pci_dev *pdev)
if (adapter->watchdog_timer.function)
del_timer_sync(&adapter->watchdog_timer);
cancel_work_sync(&adapter->adminq_task);
i40evf_free_rss(adapter);
if (hw->aq.asq.count)
......
......@@ -153,6 +153,32 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
NULL, 0);
}
/**
* i40evf_validate_num_queues
* @adapter: adapter structure
*
* Validate that the number of queues the PF has sent in
* VIRTCHNL_OP_GET_VF_RESOURCES is not larger than the VF can handle.
**/
static void i40evf_validate_num_queues(struct i40evf_adapter *adapter)
{
if (adapter->vf_res->num_queue_pairs > I40EVF_MAX_REQ_QUEUES) {
struct virtchnl_vsi_resource *vsi_res;
int i;
dev_info(&adapter->pdev->dev, "Received %d queues, but can only have a max of %d\n",
adapter->vf_res->num_queue_pairs,
I40EVF_MAX_REQ_QUEUES);
dev_info(&adapter->pdev->dev, "Fixing by reducing queues to %d\n",
I40EVF_MAX_REQ_QUEUES);
adapter->vf_res->num_queue_pairs = I40EVF_MAX_REQ_QUEUES;
for (i = 0; i < adapter->vf_res->num_vsis; i++) {
vsi_res = &adapter->vf_res->vsi_res[i];
vsi_res->num_queue_pairs = I40EVF_MAX_REQ_QUEUES;
}
}
}
/**
* i40evf_get_vf_config
* @adapter: private adapter structure
......@@ -195,6 +221,11 @@ int i40evf_get_vf_config(struct i40evf_adapter *adapter)
err = (i40e_status)le32_to_cpu(event.desc.cookie_low);
memcpy(adapter->vf_res, event.msg_buf, min(event.msg_len, len));
/* some PFs send more queues than we should have so validate that
* we aren't getting too many queues
*/
if (!err)
i40evf_validate_num_queues(adapter);
i40e_vf_parse_hw_config(hw, adapter->vf_res);
out_alloc:
kfree(event.msg_buf);
......@@ -1329,9 +1360,17 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
I40E_MAX_VF_VSI *
sizeof(struct virtchnl_vsi_resource);
memcpy(adapter->vf_res, msg, min(msglen, len));
i40evf_validate_num_queues(adapter);
i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res);
if (is_zero_ether_addr(adapter->hw.mac.addr)) {
/* restore current mac address */
ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
} else {
/* refresh current mac address if changed */
ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
ether_addr_copy(netdev->perm_addr,
adapter->hw.mac.addr);
}
i40evf_process_config(adapter);
}
break;
......
......@@ -573,7 +573,7 @@ struct virtchnl_filter {
enum virtchnl_flow_type flow_type;
enum virtchnl_action action;
u32 action_meta;
__u8 field_flags;
u8 field_flags;
};
VIRTCHNL_CHECK_STRUCT_LEN(272, virtchnl_filter);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment