|
@@ -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 @@ check_data_pointer:
|
|
|
"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;
|
|
|
|