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
/* 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, ...@@ -420,9 +420,9 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev,
struct rtnl_link_stats64 *stats) struct rtnl_link_stats64 *stats)
{ {
struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_ring *tx_ring, *rx_ring;
struct i40e_vsi *vsi = np->vsi; struct i40e_vsi *vsi = np->vsi;
struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi); struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
struct i40e_ring *ring;
int i; int i;
if (test_bit(__I40E_VSI_DOWN, vsi->state)) if (test_bit(__I40E_VSI_DOWN, vsi->state))
...@@ -436,24 +436,26 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, ...@@ -436,24 +436,26 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev,
u64 bytes, packets; u64 bytes, packets;
unsigned int start; unsigned int start;
tx_ring = READ_ONCE(vsi->tx_rings[i]); ring = READ_ONCE(vsi->tx_rings[i]);
if (!tx_ring) if (!ring)
continue; 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 { do {
start = u64_stats_fetch_begin_irq(&rx_ring->syncp); start = u64_stats_fetch_begin_irq(&ring->syncp);
packets = rx_ring->stats.packets; packets = ring->stats.packets;
bytes = rx_ring->stats.bytes; bytes = ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start)); } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->rx_packets += packets; stats->rx_packets += packets;
stats->rx_bytes += bytes; stats->rx_bytes += bytes;
if (i40e_enabled_xdp_vsi(vsi))
i40e_get_netdev_stats_struct_tx(&rx_ring[1], stats);
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -6568,6 +6570,24 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up) ...@@ -6568,6 +6570,24 @@ static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
struct i40e_hw *hw = &pf->hw; struct i40e_hw *hw = &pf->hw;
i40e_status err; i40e_status err;
u64 mask; 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 */ /* Get the current phy config */
err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, 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) ...@@ -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, /* 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; return I40E_SUCCESS;
/* To force link we need to set bits for all supported PHY types, /* 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) ...@@ -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; config.phy_type_ext = is_up ? (u8)((mask >> 32) & 0xff) : 0;
/* Copy the old settings, except of phy_type */ /* Copy the old settings, except of phy_type */
config.abilities = abilities.abilities; config.abilities = abilities.abilities;
config.link_speed = abilities.link_speed; if (abilities.link_speed != 0)
config.link_speed = abilities.link_speed;
else
config.link_speed = speed;
config.eee_capability = abilities.eee_capability; config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val; config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan; config.low_power_ctrl = abilities.d3_lpan;
...@@ -13033,7 +13056,7 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, ...@@ -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++) for (vsi_idx = 0; vsi_idx < pf->num_alloc_vsi; vsi_idx++)
if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid) if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid)
break; 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", dev_info(&pf->pdev->dev, "vsi seid %d not found\n",
vsi_seid); vsi_seid);
return NULL; return NULL;
...@@ -14159,6 +14182,7 @@ static void i40e_remove(struct pci_dev *pdev) ...@@ -14159,6 +14182,7 @@ static void i40e_remove(struct pci_dev *pdev)
mutex_destroy(&hw->aq.asq_mutex); mutex_destroy(&hw->aq.asq_mutex);
/* Clear all dynamic memory lists of rings, q_vectors, and VSIs */ /* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
rtnl_lock();
i40e_clear_interrupt_scheme(pf); i40e_clear_interrupt_scheme(pf);
for (i = 0; i < pf->num_alloc_vsi; i++) { for (i = 0; i < pf->num_alloc_vsi; i++) {
if (pf->vsi[i]) { if (pf->vsi[i]) {
...@@ -14167,6 +14191,7 @@ static void i40e_remove(struct pci_dev *pdev) ...@@ -14167,6 +14191,7 @@ static void i40e_remove(struct pci_dev *pdev)
pf->vsi[i] = NULL; pf->vsi[i] = NULL;
} }
} }
rtnl_unlock();
for (i = 0; i < I40E_MAX_VEB; i++) { for (i = 0; i < I40E_MAX_VEB; i++) {
kfree(pf->veb[i]); kfree(pf->veb[i]);
...@@ -14378,7 +14403,13 @@ static void i40e_shutdown(struct pci_dev *pdev) ...@@ -14378,7 +14403,13 @@ static void i40e_shutdown(struct pci_dev *pdev)
wr32(hw, I40E_PFPM_WUFC, wr32(hw, I40E_PFPM_WUFC,
(pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); (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); i40e_clear_interrupt_scheme(pf);
rtnl_unlock();
if (system_state == SYSTEM_POWER_OFF) { if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, pf->wol_en); pci_wake_from_d3(pdev, pf->wol_en);
......
...@@ -694,7 +694,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) ...@@ -694,7 +694,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
if (!IS_ERR_OR_NULL(pf->ptp_clock)) if (!IS_ERR_OR_NULL(pf->ptp_clock))
return 0; 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.owner = THIS_MODULE;
pf->ptp_caps.max_adj = 999999999; pf->ptp_caps.max_adj = 999999999;
pf->ptp_caps.n_ext_ts = 0; pf->ptp_caps.n_ext_ts = 0;
......
...@@ -2458,7 +2458,7 @@ static inline int i40e_check_vf_permission(struct i40e_vf *vf, ...@@ -2458,7 +2458,7 @@ static inline int i40e_check_vf_permission(struct i40e_vf *vf,
!is_multicast_ether_addr(addr) && vf->pf_set_mac && !is_multicast_ether_addr(addr) && vf->pf_set_mac &&
!ether_addr_equal(addr, vf->default_lan_addr.addr)) { !ether_addr_equal(addr, vf->default_lan_addr.addr)) {
dev_err(&pf->pdev->dev, 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; return -EPERM;
} }
} }
...@@ -2569,6 +2569,16 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) ...@@ -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; ret = I40E_ERR_INVALID_MAC_ADDR;
goto error_param; 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]; 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) ...@@ -3873,9 +3883,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
mac, vf_id); 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); 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: error_param:
return ret; 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 @@ ...@@ -6,18 +6,12 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
struct i40evf_stats { #include "i40e_ethtool_stats.h"
char stat_string[ETH_GSTRING_LEN];
int stat_offset;
};
#define I40EVF_STAT(_name, _stat) { \ #define I40EVF_STAT(_name, _stat) \
.stat_string = _name, \ I40E_STAT(struct i40evf_adapter, _name, _stat)
.stat_offset = offsetof(struct i40evf_adapter, _stat) \
}
/* All stats are u64, so we don't need to track the size of the field. */ static const struct i40e_stats i40evf_gstrings_stats[] = {
static const struct i40evf_stats i40evf_gstrings_stats[] = {
I40EVF_STAT("rx_bytes", current_stats.rx_bytes), I40EVF_STAT("rx_bytes", current_stats.rx_bytes),
I40EVF_STAT("rx_unicast", current_stats.rx_unicast), I40EVF_STAT("rx_unicast", current_stats.rx_unicast),
I40EVF_STAT("rx_multicast", current_stats.rx_multicast), I40EVF_STAT("rx_multicast", current_stats.rx_multicast),
...@@ -32,13 +26,9 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = { ...@@ -32,13 +26,9 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = {
I40EVF_STAT("tx_errors", current_stats.tx_errors), I40EVF_STAT("tx_errors", current_stats.tx_errors),
}; };
#define I40EVF_GLOBAL_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats) #define I40EVF_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats)
#define I40EVF_QUEUE_STATS_LEN(_dev) \
(((struct i40evf_adapter *)\ #define I40EVF_QUEUE_STATS_LEN ARRAY_SIZE(i40e_gstrings_queue_stats)
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))
/* For now we have one and only one private flag and it is only defined /* 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 * 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, ...@@ -117,13 +107,13 @@ static int i40evf_get_link_ksettings(struct net_device *netdev,
* @netdev: network interface device structure * @netdev: network interface device structure
* @sset: id of string set * @sset: id of string set
* *
* Reports size of string table. This driver only supports * Reports size of various string tables.
* strings for statistics.
**/ **/
static int i40evf_get_sset_count(struct net_device *netdev, int sset) static int i40evf_get_sset_count(struct net_device *netdev, int sset)
{ {
if (sset == ETH_SS_STATS) 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) else if (sset == ETH_SS_PRIV_FLAGS)
return I40EVF_PRIV_FLAGS_STR_LEN; return I40EVF_PRIV_FLAGS_STR_LEN;
else else
...@@ -142,20 +132,66 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev, ...@@ -142,20 +132,66 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data) struct ethtool_stats *stats, u64 *data)
{ {
struct i40evf_adapter *adapter = netdev_priv(netdev); struct i40evf_adapter *adapter = netdev_priv(netdev);
unsigned int i, j; unsigned int i;
char *p;
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;
for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { /* Avoid accessing un-allocated queues */
p = (char *)adapter + i40evf_gstrings_stats[i].stat_offset; ring = (i < adapter->num_active_queues ?
data[i] = *(u64 *)p; &adapter->tx_rings[i] : NULL);
i40evf_add_queue_stats(&data, ring);
/* 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++) { rcu_read_unlock();
data[i++] = adapter->tx_rings[j].stats.packets; }
data[i++] = adapter->tx_rings[j].stats.bytes;
/**
* 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, ...@@ -165,38 +201,19 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev,
* @sset: id of string set * @sset: id of string set
* @data: buffer for string data * @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) static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data)
{ {
struct i40evf_adapter *adapter = netdev_priv(netdev); switch (sset) {
u8 *p = data; case ETH_SS_STATS:
int i; i40evf_get_stat_strings(netdev, data);
break;
if (sset == ETH_SS_STATS) { case ETH_SS_PRIV_FLAGS:
for (i = 0; i < (int)I40EVF_GLOBAL_STATS_LEN; i++) { i40evf_get_priv_flag_strings(netdev, data);
memcpy(p, i40evf_gstrings_stats[i].stat_string, break;
ETH_GSTRING_LEN); default:
p += ETH_GSTRING_LEN; break;
}
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;
}
} }
} }
......
...@@ -3120,18 +3120,19 @@ static int i40evf_set_features(struct net_device *netdev, ...@@ -3120,18 +3120,19 @@ static int i40evf_set_features(struct net_device *netdev,
{ {
struct i40evf_adapter *adapter = netdev_priv(netdev); struct i40evf_adapter *adapter = netdev_priv(netdev);
/* Don't allow changing VLAN_RX flag when VLAN is set for VF /* Don't allow changing VLAN_RX flag when adapter is not capable
* and return an error in this case * 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) if (features & NETIF_F_HW_VLAN_CTAG_RX)
adapter->aq_required |= adapter->aq_required |=
I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING; I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING;
else else
adapter->aq_required |= adapter->aq_required |=
I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING; I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING;
} else if ((netdev->features ^ features) & NETIF_F_HW_VLAN_CTAG_RX) {
return -EINVAL;
} }
return 0; return 0;
...@@ -3358,6 +3359,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter) ...@@ -3358,6 +3359,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN) if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN)
netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; 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. /* Do not turn on offloads when they are requested to be turned off.
* TSO needs minimum 576 bytes to work correctly. * TSO needs minimum 576 bytes to work correctly.
*/ */
...@@ -3907,6 +3910,8 @@ static void i40evf_remove(struct pci_dev *pdev) ...@@ -3907,6 +3910,8 @@ static void i40evf_remove(struct pci_dev *pdev)
if (adapter->watchdog_timer.function) if (adapter->watchdog_timer.function)
del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->watchdog_timer);
cancel_work_sync(&adapter->adminq_task);
i40evf_free_rss(adapter); i40evf_free_rss(adapter);
if (hw->aq.asq.count) if (hw->aq.asq.count)
......
...@@ -153,6 +153,32 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) ...@@ -153,6 +153,32 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
NULL, 0); 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 * i40evf_get_vf_config
* @adapter: private adapter structure * @adapter: private adapter structure
...@@ -195,6 +221,11 @@ int i40evf_get_vf_config(struct i40evf_adapter *adapter) ...@@ -195,6 +221,11 @@ int i40evf_get_vf_config(struct i40evf_adapter *adapter)
err = (i40e_status)le32_to_cpu(event.desc.cookie_low); err = (i40e_status)le32_to_cpu(event.desc.cookie_low);
memcpy(adapter->vf_res, event.msg_buf, min(event.msg_len, len)); 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); i40e_vf_parse_hw_config(hw, adapter->vf_res);
out_alloc: out_alloc:
kfree(event.msg_buf); kfree(event.msg_buf);
...@@ -1329,9 +1360,17 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, ...@@ -1329,9 +1360,17 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
I40E_MAX_VF_VSI * I40E_MAX_VF_VSI *
sizeof(struct virtchnl_vsi_resource); sizeof(struct virtchnl_vsi_resource);
memcpy(adapter->vf_res, msg, min(msglen, len)); memcpy(adapter->vf_res, msg, min(msglen, len));
i40evf_validate_num_queues(adapter);
i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res); i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res);
/* restore current mac address */ if (is_zero_ether_addr(adapter->hw.mac.addr)) {
ether_addr_copy(adapter->hw.mac.addr, netdev->dev_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); i40evf_process_config(adapter);
} }
break; break;
......
...@@ -573,7 +573,7 @@ struct virtchnl_filter { ...@@ -573,7 +573,7 @@ struct virtchnl_filter {
enum virtchnl_flow_type flow_type; enum virtchnl_flow_type flow_type;
enum virtchnl_action action; enum virtchnl_action action;
u32 action_meta; u32 action_meta;
__u8 field_flags; u8 field_flags;
}; };
VIRTCHNL_CHECK_STRUCT_LEN(272, virtchnl_filter); 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