2022-09-08 19:48:08 +03:00
|
|
|
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
|
|
/* Statistics for Ocelot switch family
|
|
|
|
*
|
|
|
|
* Copyright (c) 2017 Microsemi Corporation
|
2022-09-08 19:48:13 +03:00
|
|
|
* Copyright 2022 NXP
|
2022-09-08 19:48:08 +03:00
|
|
|
*/
|
2023-01-25 13:02:14 +02:00
|
|
|
#include <linux/ethtool_netlink.h>
|
2022-09-08 19:48:08 +03:00
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include "ocelot.h"
|
|
|
|
|
2022-11-19 15:14:05 -08:00
|
|
|
enum ocelot_stat {
|
|
|
|
OCELOT_STAT_RX_OCTETS,
|
|
|
|
OCELOT_STAT_RX_UNICAST,
|
|
|
|
OCELOT_STAT_RX_MULTICAST,
|
|
|
|
OCELOT_STAT_RX_BROADCAST,
|
|
|
|
OCELOT_STAT_RX_SHORTS,
|
|
|
|
OCELOT_STAT_RX_FRAGMENTS,
|
|
|
|
OCELOT_STAT_RX_JABBERS,
|
|
|
|
OCELOT_STAT_RX_CRC_ALIGN_ERRS,
|
|
|
|
OCELOT_STAT_RX_SYM_ERRS,
|
|
|
|
OCELOT_STAT_RX_64,
|
|
|
|
OCELOT_STAT_RX_65_127,
|
|
|
|
OCELOT_STAT_RX_128_255,
|
|
|
|
OCELOT_STAT_RX_256_511,
|
|
|
|
OCELOT_STAT_RX_512_1023,
|
|
|
|
OCELOT_STAT_RX_1024_1526,
|
|
|
|
OCELOT_STAT_RX_1527_MAX,
|
|
|
|
OCELOT_STAT_RX_PAUSE,
|
|
|
|
OCELOT_STAT_RX_CONTROL,
|
|
|
|
OCELOT_STAT_RX_LONGS,
|
|
|
|
OCELOT_STAT_RX_CLASSIFIED_DROPS,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_0,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_1,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_2,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_3,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_4,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_5,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_6,
|
|
|
|
OCELOT_STAT_RX_RED_PRIO_7,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_0,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_1,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_2,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_3,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_4,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_5,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_6,
|
|
|
|
OCELOT_STAT_RX_YELLOW_PRIO_7,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_0,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_1,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_2,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_3,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_4,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_5,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_6,
|
|
|
|
OCELOT_STAT_RX_GREEN_PRIO_7,
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
OCELOT_STAT_RX_ASSEMBLY_ERRS,
|
|
|
|
OCELOT_STAT_RX_SMD_ERRS,
|
|
|
|
OCELOT_STAT_RX_ASSEMBLY_OK,
|
|
|
|
OCELOT_STAT_RX_MERGE_FRAGMENTS,
|
|
|
|
OCELOT_STAT_RX_PMAC_OCTETS,
|
|
|
|
OCELOT_STAT_RX_PMAC_UNICAST,
|
|
|
|
OCELOT_STAT_RX_PMAC_MULTICAST,
|
|
|
|
OCELOT_STAT_RX_PMAC_BROADCAST,
|
|
|
|
OCELOT_STAT_RX_PMAC_SHORTS,
|
|
|
|
OCELOT_STAT_RX_PMAC_FRAGMENTS,
|
|
|
|
OCELOT_STAT_RX_PMAC_JABBERS,
|
|
|
|
OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS,
|
|
|
|
OCELOT_STAT_RX_PMAC_SYM_ERRS,
|
|
|
|
OCELOT_STAT_RX_PMAC_64,
|
|
|
|
OCELOT_STAT_RX_PMAC_65_127,
|
|
|
|
OCELOT_STAT_RX_PMAC_128_255,
|
|
|
|
OCELOT_STAT_RX_PMAC_256_511,
|
|
|
|
OCELOT_STAT_RX_PMAC_512_1023,
|
|
|
|
OCELOT_STAT_RX_PMAC_1024_1526,
|
|
|
|
OCELOT_STAT_RX_PMAC_1527_MAX,
|
|
|
|
OCELOT_STAT_RX_PMAC_PAUSE,
|
|
|
|
OCELOT_STAT_RX_PMAC_CONTROL,
|
|
|
|
OCELOT_STAT_RX_PMAC_LONGS,
|
2022-11-19 15:14:05 -08:00
|
|
|
OCELOT_STAT_TX_OCTETS,
|
|
|
|
OCELOT_STAT_TX_UNICAST,
|
|
|
|
OCELOT_STAT_TX_MULTICAST,
|
|
|
|
OCELOT_STAT_TX_BROADCAST,
|
|
|
|
OCELOT_STAT_TX_COLLISION,
|
|
|
|
OCELOT_STAT_TX_DROPS,
|
|
|
|
OCELOT_STAT_TX_PAUSE,
|
|
|
|
OCELOT_STAT_TX_64,
|
|
|
|
OCELOT_STAT_TX_65_127,
|
|
|
|
OCELOT_STAT_TX_128_255,
|
|
|
|
OCELOT_STAT_TX_256_511,
|
|
|
|
OCELOT_STAT_TX_512_1023,
|
|
|
|
OCELOT_STAT_TX_1024_1526,
|
|
|
|
OCELOT_STAT_TX_1527_MAX,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_0,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_1,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_2,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_3,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_4,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_5,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_6,
|
|
|
|
OCELOT_STAT_TX_YELLOW_PRIO_7,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_0,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_1,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_2,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_3,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_4,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_5,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_6,
|
|
|
|
OCELOT_STAT_TX_GREEN_PRIO_7,
|
|
|
|
OCELOT_STAT_TX_AGED,
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
OCELOT_STAT_TX_MM_HOLD,
|
|
|
|
OCELOT_STAT_TX_MERGE_FRAGMENTS,
|
|
|
|
OCELOT_STAT_TX_PMAC_OCTETS,
|
|
|
|
OCELOT_STAT_TX_PMAC_UNICAST,
|
|
|
|
OCELOT_STAT_TX_PMAC_MULTICAST,
|
|
|
|
OCELOT_STAT_TX_PMAC_BROADCAST,
|
|
|
|
OCELOT_STAT_TX_PMAC_PAUSE,
|
|
|
|
OCELOT_STAT_TX_PMAC_64,
|
|
|
|
OCELOT_STAT_TX_PMAC_65_127,
|
|
|
|
OCELOT_STAT_TX_PMAC_128_255,
|
|
|
|
OCELOT_STAT_TX_PMAC_256_511,
|
|
|
|
OCELOT_STAT_TX_PMAC_512_1023,
|
|
|
|
OCELOT_STAT_TX_PMAC_1024_1526,
|
|
|
|
OCELOT_STAT_TX_PMAC_1527_MAX,
|
2022-11-19 15:14:05 -08:00
|
|
|
OCELOT_STAT_DROP_LOCAL,
|
|
|
|
OCELOT_STAT_DROP_TAIL,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_0,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_1,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_2,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_3,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_4,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_5,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_6,
|
|
|
|
OCELOT_STAT_DROP_YELLOW_PRIO_7,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_0,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_1,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_2,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_3,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_4,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_5,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_6,
|
|
|
|
OCELOT_STAT_DROP_GREEN_PRIO_7,
|
|
|
|
OCELOT_NUM_STATS,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ocelot_stat_layout {
|
2023-04-12 15:47:35 +03:00
|
|
|
enum ocelot_reg reg;
|
2022-11-19 15:14:05 -08:00
|
|
|
char name[ETH_GSTRING_LEN];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 32-bit counter checked for wraparound by ocelot_port_update_stats()
|
|
|
|
* and copied to ocelot->stats.
|
|
|
|
*/
|
|
|
|
#define OCELOT_STAT(kind) \
|
|
|
|
[OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind }
|
|
|
|
/* Same as above, except also exported to ethtool -S. Standard counters should
|
|
|
|
* only be exposed to more specific interfaces rather than by their string name.
|
|
|
|
*/
|
|
|
|
#define OCELOT_STAT_ETHTOOL(kind, ethtool_name) \
|
|
|
|
[OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind, .name = ethtool_name }
|
|
|
|
|
|
|
|
#define OCELOT_COMMON_STATS \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_OCTETS, "rx_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_UNICAST, "rx_unicast"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_MULTICAST, "rx_multicast"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_BROADCAST, "rx_broadcast"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_SHORTS, "rx_shorts"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_FRAGMENTS, "rx_fragments"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_JABBERS, "rx_jabbers"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_CRC_ALIGN_ERRS, "rx_crc_align_errs"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_SYM_ERRS, "rx_sym_errs"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_64, "rx_frames_below_65_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_65_127, "rx_frames_65_to_127_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_128_255, "rx_frames_128_to_255_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_256_511, "rx_frames_256_to_511_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_512_1023, "rx_frames_512_to_1023_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_1024_1526, "rx_frames_1024_to_1526_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_1527_MAX, "rx_frames_over_1526_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_PAUSE, "rx_pause"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_CONTROL, "rx_control"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_LONGS, "rx_longs"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_CLASSIFIED_DROPS, "rx_classified_drops"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_0, "rx_red_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_1, "rx_red_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_2, "rx_red_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_3, "rx_red_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_4, "rx_red_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_5, "rx_red_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_6, "rx_red_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_7, "rx_red_prio_7"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_0, "rx_yellow_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_1, "rx_yellow_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_2, "rx_yellow_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_3, "rx_yellow_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_4, "rx_yellow_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_5, "rx_yellow_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_6, "rx_yellow_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_7, "rx_yellow_prio_7"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_0, "rx_green_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_1, "rx_green_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_2, "rx_green_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_3, "rx_green_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_4, "rx_green_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_5, "rx_green_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_6, "rx_green_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_7, "rx_green_prio_7"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_OCTETS, "tx_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_UNICAST, "tx_unicast"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_MULTICAST, "tx_multicast"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_BROADCAST, "tx_broadcast"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_COLLISION, "tx_collision"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_DROPS, "tx_drops"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_PAUSE, "tx_pause"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_64, "tx_frames_below_65_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_65_127, "tx_frames_65_to_127_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_128_255, "tx_frames_128_255_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_256_511, "tx_frames_256_511_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_512_1023, "tx_frames_512_1023_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_1024_1526, "tx_frames_1024_1526_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_1527_MAX, "tx_frames_over_1526_octets"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_0, "tx_yellow_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_1, "tx_yellow_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_2, "tx_yellow_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_3, "tx_yellow_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_4, "tx_yellow_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_5, "tx_yellow_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_6, "tx_yellow_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_7, "tx_yellow_prio_7"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_0, "tx_green_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_1, "tx_green_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_2, "tx_green_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_3, "tx_green_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_4, "tx_green_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_5, "tx_green_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_6, "tx_green_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_7, "tx_green_prio_7"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(TX_AGED, "tx_aged"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_LOCAL, "drop_local"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_TAIL, "drop_tail"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_0, "drop_yellow_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_1, "drop_yellow_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_2, "drop_yellow_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_3, "drop_yellow_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_4, "drop_yellow_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_5, "drop_yellow_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_6, "drop_yellow_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_7, "drop_yellow_prio_7"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_0, "drop_green_prio_0"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_1, "drop_green_prio_1"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_2, "drop_green_prio_2"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_3, "drop_green_prio_3"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_4, "drop_green_prio_4"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_5, "drop_green_prio_5"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_6, "drop_green_prio_6"), \
|
|
|
|
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_7, "drop_green_prio_7")
|
|
|
|
|
|
|
|
struct ocelot_stats_region {
|
|
|
|
struct list_head node;
|
2023-04-12 15:47:35 +03:00
|
|
|
enum ocelot_reg base;
|
net: mscc: ocelot: fix transfer from region->buf to ocelot->stats
To understand the problem, we need some definitions.
The driver is aware of multiple counters (enum ocelot_stat), yet not all
switches supported by the driver implement all counters. There are 2
statistics layouts: ocelot_stats_layout and ocelot_mm_stats_layout, the
latter having 36 counters more than the former.
ocelot->stats[] is not a compact array, i.e. there are elements within
it which are not going to be populated for ocelot_stats_layout. On the
other hand, ocelot->stats[] is easily indexable, for example "tx_octets"
for port 3 can be found at ocelot->stats[3 * OCELOT_NUM_STATS +
OCELOT_STAT_TX_OCTETS], and that is why we keep it sparse.
Regions, as created by ocelot_prepare_stats_regions(), are compact
(every element from region->buf will correspond to a counter that is
present in this switch's layout) but are not easily indexable.
Let's define holes as the ranges of values of enum ocelot_stat for which
ocelot_stats_layout doesn't have a "reg" defined. For example, there is
a hole between OCELOT_STAT_RX_GREEN_PRIO_7 and OCELOT_STAT_TX_OCTETS
which is of 23 elements that are only present on ocelot_mm_stats_layout,
and as such, they are also present in enum ocelot_stat. Let's define the
left extremity of the hole - the last enum ocelot_stat still defined -
as A (in this case OCELOT_STAT_RX_GREEN_PRIO_7) and the right extremity -
the first enum ocelot_stat that is defined after a series of undefined
ones - as B (in this case OCELOT_STAT_TX_OCTETS).
There is a bug in the procedure which transfers stats from region->buf[]
to ocelot->stats[].
For each hole in the ocelot_stats_layout, the logic transfers the stats
starting with enum ocelot_stat B to ocelot->stats[] index A + 1. So all
stats after a hole are saved to a position which is off by B - A + 1
elements.
This causes 2 kinds of issues:
(a) counters which shouldn't increment increment
(b) counters which should increment don't
Holes in the ocelot_stat_layout automatically imply the end of a region
and the beginning of a new one; however the reverse is not necessarily
true. For example, for ocelot_mm_stat_layout, there could be multiple
regions (which indicate discontinuities in register addresses) while
there is no hole (which indicates discontinuities in enum ocelot_stat
values).
In the example above, the stats from the second region->buf[] are not
transferred to ocelot->stats starting with index
"port * OCELOT_NUM_STATS + OCELOT_STAT_TX_OCTETS" as they should, but
rather, starting with element
"port * OCELOT_NUM_STATS + OCELOT_STAT_RX_GREEN_PRIO_7 + 1".
That stats[] array element is not reported to user space for switches
that use ocelot_stat_layout, and that is how issue (b) occurs.
However, if the length of the second region is larger than the hole,
then some stats will start to be transferred to the ocelot->stats[]
indices which *are* reported to user space, but those indices contain
wrong values (corresponding to unexpected counters). This is how issue
(a) occurs.
The procedure, as it was introduced in commit d87b1c08f38a ("net: mscc:
ocelot: use bulk reads for stats"), was not buggy, because there were no
holes in the struct ocelot_stat_layout instances at that time. The
problem is that when those holes were introduced, the function was not
updated to take them into consideration.
To update the procedure, we need to know, for each region, which enum
ocelot_stat corresponds to its region->base. We have no way of deducing
that based on the contents of struct ocelot_stats_region, so we need to
add this information.
Fixes: ab3f97a9610a ("net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-03-21 03:03:24 +02:00
|
|
|
enum ocelot_stat first_stat;
|
2022-11-19 15:14:05 -08:00
|
|
|
int count;
|
|
|
|
u32 *buf;
|
|
|
|
};
|
|
|
|
|
2022-11-19 15:14:04 -08:00
|
|
|
static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = {
|
|
|
|
OCELOT_COMMON_STATS,
|
|
|
|
};
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
static const struct ocelot_stat_layout ocelot_mm_stats_layout[OCELOT_NUM_STATS] = {
|
|
|
|
OCELOT_COMMON_STATS,
|
|
|
|
OCELOT_STAT(RX_ASSEMBLY_ERRS),
|
|
|
|
OCELOT_STAT(RX_SMD_ERRS),
|
|
|
|
OCELOT_STAT(RX_ASSEMBLY_OK),
|
|
|
|
OCELOT_STAT(RX_MERGE_FRAGMENTS),
|
|
|
|
OCELOT_STAT(TX_MERGE_FRAGMENTS),
|
net: mscc: ocelot: add TX_MM_HOLD to ocelot_mm_stats_layout
The lack of a definition for this counter is what initially prompted me
to investigate a problem which really manifested itself as the previous
change, "net: mscc: ocelot: fix transfer from region->buf to ocelot->stats".
When TX_MM_HOLD is defined in enum ocelot_stat but not in struct
ocelot_stat_layout ocelot_mm_stats_layout, this creates a hole, which
due to the aforementioned bug, makes all counters following TX_MM_HOLD
be recorded off by one compared to their correct position. So for
example, a non-zero TX_PMAC_OCTETS would be reported as TX_MERGE_FRAGMENTS,
TX_PMAC_UNICAST would be reported as TX_PMAC_OCTETS, TX_PMAC_64 would be
reported as TX_PMAC_PAUSE, etc etc. This is because the size of the hole
(1) is much smaller than the size of the region, so the phenomenon where
the stats are off-by-one, rather than lost, prevails.
However, the phenomenon where stats are lost can be seen too, for
example with DROP_LOCAL, which is at the beginning of its own region
(offset 0x000400 vs the previous 0x0002b0 constitutes a discontinuity).
This is also reported as off by one and saved to TX_PMAC_1527_MAX, but
that counter is not reported to the unstructured "ethtool -S", as
opposed to DROP_LOCAL which is (as "drop_local").
Fixes: ab3f97a9610a ("net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-03-21 03:03:25 +02:00
|
|
|
OCELOT_STAT(TX_MM_HOLD),
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
OCELOT_STAT(RX_PMAC_OCTETS),
|
|
|
|
OCELOT_STAT(RX_PMAC_UNICAST),
|
|
|
|
OCELOT_STAT(RX_PMAC_MULTICAST),
|
|
|
|
OCELOT_STAT(RX_PMAC_BROADCAST),
|
|
|
|
OCELOT_STAT(RX_PMAC_SHORTS),
|
|
|
|
OCELOT_STAT(RX_PMAC_FRAGMENTS),
|
|
|
|
OCELOT_STAT(RX_PMAC_JABBERS),
|
|
|
|
OCELOT_STAT(RX_PMAC_CRC_ALIGN_ERRS),
|
|
|
|
OCELOT_STAT(RX_PMAC_SYM_ERRS),
|
|
|
|
OCELOT_STAT(RX_PMAC_64),
|
|
|
|
OCELOT_STAT(RX_PMAC_65_127),
|
|
|
|
OCELOT_STAT(RX_PMAC_128_255),
|
|
|
|
OCELOT_STAT(RX_PMAC_256_511),
|
|
|
|
OCELOT_STAT(RX_PMAC_512_1023),
|
|
|
|
OCELOT_STAT(RX_PMAC_1024_1526),
|
|
|
|
OCELOT_STAT(RX_PMAC_1527_MAX),
|
|
|
|
OCELOT_STAT(RX_PMAC_PAUSE),
|
|
|
|
OCELOT_STAT(RX_PMAC_CONTROL),
|
|
|
|
OCELOT_STAT(RX_PMAC_LONGS),
|
|
|
|
OCELOT_STAT(TX_PMAC_OCTETS),
|
|
|
|
OCELOT_STAT(TX_PMAC_UNICAST),
|
|
|
|
OCELOT_STAT(TX_PMAC_MULTICAST),
|
|
|
|
OCELOT_STAT(TX_PMAC_BROADCAST),
|
|
|
|
OCELOT_STAT(TX_PMAC_PAUSE),
|
|
|
|
OCELOT_STAT(TX_PMAC_64),
|
|
|
|
OCELOT_STAT(TX_PMAC_65_127),
|
|
|
|
OCELOT_STAT(TX_PMAC_128_255),
|
|
|
|
OCELOT_STAT(TX_PMAC_256_511),
|
|
|
|
OCELOT_STAT(TX_PMAC_512_1023),
|
|
|
|
OCELOT_STAT(TX_PMAC_1024_1526),
|
|
|
|
OCELOT_STAT(TX_PMAC_1527_MAX),
|
|
|
|
};
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
static const struct ocelot_stat_layout *
|
|
|
|
ocelot_get_stats_layout(struct ocelot *ocelot)
|
|
|
|
{
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
if (ocelot->mm_supported)
|
|
|
|
return ocelot_mm_stats_layout;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
return ocelot_stats_layout;
|
|
|
|
}
|
|
|
|
|
2022-09-08 19:48:08 +03:00
|
|
|
/* Read the counters from hardware and keep them in region->buf.
|
|
|
|
* Caller must hold &ocelot->stat_view_lock.
|
|
|
|
*/
|
|
|
|
static int ocelot_port_update_stats(struct ocelot *ocelot, int port)
|
|
|
|
{
|
|
|
|
struct ocelot_stats_region *region;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Configure the port to read the stats from */
|
|
|
|
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG);
|
|
|
|
|
|
|
|
list_for_each_entry(region, &ocelot->stats_regions, node) {
|
|
|
|
err = ocelot_bulk_read(ocelot, region->base, region->buf,
|
|
|
|
region->count);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer the counters from region->buf to ocelot->stats.
|
|
|
|
* Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock.
|
|
|
|
*/
|
|
|
|
static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port)
|
|
|
|
{
|
|
|
|
struct ocelot_stats_region *region;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
list_for_each_entry(region, &ocelot->stats_regions, node) {
|
net: mscc: ocelot: fix transfer from region->buf to ocelot->stats
To understand the problem, we need some definitions.
The driver is aware of multiple counters (enum ocelot_stat), yet not all
switches supported by the driver implement all counters. There are 2
statistics layouts: ocelot_stats_layout and ocelot_mm_stats_layout, the
latter having 36 counters more than the former.
ocelot->stats[] is not a compact array, i.e. there are elements within
it which are not going to be populated for ocelot_stats_layout. On the
other hand, ocelot->stats[] is easily indexable, for example "tx_octets"
for port 3 can be found at ocelot->stats[3 * OCELOT_NUM_STATS +
OCELOT_STAT_TX_OCTETS], and that is why we keep it sparse.
Regions, as created by ocelot_prepare_stats_regions(), are compact
(every element from region->buf will correspond to a counter that is
present in this switch's layout) but are not easily indexable.
Let's define holes as the ranges of values of enum ocelot_stat for which
ocelot_stats_layout doesn't have a "reg" defined. For example, there is
a hole between OCELOT_STAT_RX_GREEN_PRIO_7 and OCELOT_STAT_TX_OCTETS
which is of 23 elements that are only present on ocelot_mm_stats_layout,
and as such, they are also present in enum ocelot_stat. Let's define the
left extremity of the hole - the last enum ocelot_stat still defined -
as A (in this case OCELOT_STAT_RX_GREEN_PRIO_7) and the right extremity -
the first enum ocelot_stat that is defined after a series of undefined
ones - as B (in this case OCELOT_STAT_TX_OCTETS).
There is a bug in the procedure which transfers stats from region->buf[]
to ocelot->stats[].
For each hole in the ocelot_stats_layout, the logic transfers the stats
starting with enum ocelot_stat B to ocelot->stats[] index A + 1. So all
stats after a hole are saved to a position which is off by B - A + 1
elements.
This causes 2 kinds of issues:
(a) counters which shouldn't increment increment
(b) counters which should increment don't
Holes in the ocelot_stat_layout automatically imply the end of a region
and the beginning of a new one; however the reverse is not necessarily
true. For example, for ocelot_mm_stat_layout, there could be multiple
regions (which indicate discontinuities in register addresses) while
there is no hole (which indicates discontinuities in enum ocelot_stat
values).
In the example above, the stats from the second region->buf[] are not
transferred to ocelot->stats starting with index
"port * OCELOT_NUM_STATS + OCELOT_STAT_TX_OCTETS" as they should, but
rather, starting with element
"port * OCELOT_NUM_STATS + OCELOT_STAT_RX_GREEN_PRIO_7 + 1".
That stats[] array element is not reported to user space for switches
that use ocelot_stat_layout, and that is how issue (b) occurs.
However, if the length of the second region is larger than the hole,
then some stats will start to be transferred to the ocelot->stats[]
indices which *are* reported to user space, but those indices contain
wrong values (corresponding to unexpected counters). This is how issue
(a) occurs.
The procedure, as it was introduced in commit d87b1c08f38a ("net: mscc:
ocelot: use bulk reads for stats"), was not buggy, because there were no
holes in the struct ocelot_stat_layout instances at that time. The
problem is that when those holes were introduced, the function was not
updated to take them into consideration.
To update the procedure, we need to know, for each region, which enum
ocelot_stat corresponds to its region->base. We have no way of deducing
that based on the contents of struct ocelot_stats_region, so we need to
add this information.
Fixes: ab3f97a9610a ("net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-03-21 03:03:24 +02:00
|
|
|
unsigned int idx = port * OCELOT_NUM_STATS + region->first_stat;
|
|
|
|
|
2022-09-08 19:48:08 +03:00
|
|
|
for (j = 0; j < region->count; j++) {
|
|
|
|
u64 *stat = &ocelot->stats[idx + j];
|
|
|
|
u64 val = region->buf[j];
|
|
|
|
|
|
|
|
if (val < (*stat & U32_MAX))
|
|
|
|
*stat += (u64)1 << 32;
|
|
|
|
|
|
|
|
*stat = (*stat & ~(u64)U32_MAX) + val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_check_stats_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct delayed_work *del_work = to_delayed_work(work);
|
|
|
|
struct ocelot *ocelot = container_of(del_work, struct ocelot,
|
|
|
|
stats_work);
|
|
|
|
int port, err;
|
|
|
|
|
|
|
|
mutex_lock(&ocelot->stat_view_lock);
|
|
|
|
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
|
|
err = ocelot_port_update_stats(ocelot, port);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
|
|
|
spin_lock(&ocelot->stats_lock);
|
|
|
|
ocelot_port_transfer_stats(ocelot, port);
|
|
|
|
spin_unlock(&ocelot->stats_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!err && ocelot->ops->update_stats)
|
|
|
|
ocelot->ops->update_stats(ocelot);
|
|
|
|
|
|
|
|
mutex_unlock(&ocelot->stat_view_lock);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err);
|
|
|
|
|
|
|
|
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
|
|
|
OCELOT_STATS_CHECK_DELAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
|
|
|
|
{
|
2023-01-19 14:27:02 +02:00
|
|
|
const struct ocelot_stat_layout *layout;
|
2023-04-12 15:47:36 +03:00
|
|
|
enum ocelot_stat i;
|
2022-09-08 19:48:08 +03:00
|
|
|
|
|
|
|
if (sset != ETH_SS_STATS)
|
|
|
|
return;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
layout = ocelot_get_stats_layout(ocelot);
|
|
|
|
|
2022-09-08 19:48:08 +03:00
|
|
|
for (i = 0; i < OCELOT_NUM_STATS; i++) {
|
2023-01-19 14:27:02 +02:00
|
|
|
if (layout[i].name[0] == '\0')
|
2022-09-08 19:48:08 +03:00
|
|
|
continue;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
memcpy(data, layout[i].name, ETH_GSTRING_LEN);
|
2023-01-19 14:27:01 +02:00
|
|
|
data += ETH_GSTRING_LEN;
|
2022-09-08 19:48:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_strings);
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
/* Update ocelot->stats for the given port and run the given callback */
|
|
|
|
static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv,
|
|
|
|
void (*cb)(struct ocelot *ocelot, int port,
|
|
|
|
void *priv))
|
2022-09-08 19:48:08 +03:00
|
|
|
{
|
2022-09-08 19:48:13 +03:00
|
|
|
int err;
|
2022-09-08 19:48:08 +03:00
|
|
|
|
|
|
|
mutex_lock(&ocelot->stat_view_lock);
|
|
|
|
|
|
|
|
err = ocelot_port_update_stats(ocelot, port);
|
2022-09-08 19:48:13 +03:00
|
|
|
if (err) {
|
|
|
|
dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n",
|
|
|
|
port, ERR_PTR(err));
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2022-09-08 19:48:08 +03:00
|
|
|
|
|
|
|
spin_lock(&ocelot->stats_lock);
|
|
|
|
|
|
|
|
ocelot_port_transfer_stats(ocelot, port);
|
2022-09-08 19:48:13 +03:00
|
|
|
cb(ocelot, port, priv);
|
2022-09-08 19:48:08 +03:00
|
|
|
|
|
|
|
spin_unlock(&ocelot->stats_lock);
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
out_unlock:
|
2022-09-08 19:48:08 +03:00
|
|
|
mutex_unlock(&ocelot->stat_view_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
|
|
|
|
{
|
2023-01-19 14:27:02 +02:00
|
|
|
const struct ocelot_stat_layout *layout;
|
2023-04-12 15:47:36 +03:00
|
|
|
enum ocelot_stat i;
|
|
|
|
int num_stats = 0;
|
2022-09-08 19:48:08 +03:00
|
|
|
|
|
|
|
if (sset != ETH_SS_STATS)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
layout = ocelot_get_stats_layout(ocelot);
|
|
|
|
|
2022-09-08 19:48:08 +03:00
|
|
|
for (i = 0; i < OCELOT_NUM_STATS; i++)
|
2023-01-19 14:27:02 +02:00
|
|
|
if (layout[i].name[0] != '\0')
|
2022-09-08 19:48:08 +03:00
|
|
|
num_stats++;
|
|
|
|
|
|
|
|
return num_stats;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_sset_count);
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
2023-01-19 14:27:02 +02:00
|
|
|
const struct ocelot_stat_layout *layout;
|
2023-04-12 15:47:36 +03:00
|
|
|
enum ocelot_stat i;
|
2022-09-08 19:48:13 +03:00
|
|
|
u64 *data = priv;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
layout = ocelot_get_stats_layout(ocelot);
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
/* Copy all supported counters */
|
|
|
|
for (i = 0; i < OCELOT_NUM_STATS; i++) {
|
|
|
|
int index = port * OCELOT_NUM_STATS + i;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
if (layout[i].name[0] == '\0')
|
2022-09-08 19:48:13 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
*data++ = ocelot->stats[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
|
|
|
|
{
|
|
|
|
ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_ethtool_stats);
|
|
|
|
|
|
|
|
static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_pause_stats *pause_stats = priv;
|
|
|
|
|
|
|
|
pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE];
|
|
|
|
pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE];
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
static void ocelot_port_pmac_pause_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_pause_stats *pause_stats = priv;
|
|
|
|
|
|
|
|
pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PMAC_PAUSE];
|
|
|
|
pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PMAC_PAUSE];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_port_mm_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_mm_stats *stats = priv;
|
|
|
|
|
|
|
|
stats->MACMergeFrameAssErrorCount = s[OCELOT_STAT_RX_ASSEMBLY_ERRS];
|
|
|
|
stats->MACMergeFrameSmdErrorCount = s[OCELOT_STAT_RX_SMD_ERRS];
|
|
|
|
stats->MACMergeFrameAssOkCount = s[OCELOT_STAT_RX_ASSEMBLY_OK];
|
|
|
|
stats->MACMergeFragCountRx = s[OCELOT_STAT_RX_MERGE_FRAGMENTS];
|
|
|
|
stats->MACMergeFragCountTx = s[OCELOT_STAT_TX_MERGE_FRAGMENTS];
|
|
|
|
stats->MACMergeHoldCount = s[OCELOT_STAT_TX_MM_HOLD];
|
|
|
|
}
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_pause_stats *pause_stats)
|
|
|
|
{
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
switch (pause_stats->src) {
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
|
|
|
ocelot_port_stats_run(ocelot, port, pause_stats,
|
|
|
|
ocelot_port_pause_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
|
|
|
if (ocelot->mm_supported)
|
|
|
|
ocelot_port_stats_run(ocelot, port, pause_stats,
|
|
|
|
ocelot_port_pmac_pause_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
|
|
|
dev = ocelot->ops->port_to_netdev(ocelot, port);
|
|
|
|
ethtool_aggregate_pause_stats(dev, pause_stats);
|
|
|
|
break;
|
|
|
|
}
|
2022-09-08 19:48:13 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats);
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
void ocelot_port_get_mm_stats(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_mm_stats *stats)
|
|
|
|
{
|
|
|
|
if (!ocelot->mm_supported)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ocelot_port_stats_run(ocelot, port, stats, ocelot_port_mm_stats_cb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ocelot_port_get_mm_stats);
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = {
|
|
|
|
{ 64, 64 },
|
|
|
|
{ 65, 127 },
|
|
|
|
{ 128, 255 },
|
|
|
|
{ 256, 511 },
|
|
|
|
{ 512, 1023 },
|
|
|
|
{ 1024, 1526 },
|
|
|
|
{ 1527, 65535 },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_rmon_stats *rmon_stats = priv;
|
|
|
|
|
|
|
|
rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS];
|
|
|
|
rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS];
|
|
|
|
rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS];
|
|
|
|
rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS];
|
|
|
|
|
|
|
|
rmon_stats->hist[0] = s[OCELOT_STAT_RX_64];
|
|
|
|
rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127];
|
|
|
|
rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255];
|
|
|
|
rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511];
|
|
|
|
rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023];
|
|
|
|
rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526];
|
|
|
|
rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX];
|
|
|
|
|
|
|
|
rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64];
|
|
|
|
rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127];
|
|
|
|
rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255];
|
2023-12-14 02:09:01 +02:00
|
|
|
rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_256_511];
|
|
|
|
rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_512_1023];
|
|
|
|
rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_1024_1526];
|
|
|
|
rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1527_MAX];
|
2022-09-08 19:48:13 +03:00
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
static void ocelot_port_pmac_rmon_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_rmon_stats *rmon_stats = priv;
|
|
|
|
|
|
|
|
rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_PMAC_SHORTS];
|
|
|
|
rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_PMAC_LONGS];
|
|
|
|
rmon_stats->fragments = s[OCELOT_STAT_RX_PMAC_FRAGMENTS];
|
|
|
|
rmon_stats->jabbers = s[OCELOT_STAT_RX_PMAC_JABBERS];
|
|
|
|
|
|
|
|
rmon_stats->hist[0] = s[OCELOT_STAT_RX_PMAC_64];
|
|
|
|
rmon_stats->hist[1] = s[OCELOT_STAT_RX_PMAC_65_127];
|
|
|
|
rmon_stats->hist[2] = s[OCELOT_STAT_RX_PMAC_128_255];
|
|
|
|
rmon_stats->hist[3] = s[OCELOT_STAT_RX_PMAC_256_511];
|
|
|
|
rmon_stats->hist[4] = s[OCELOT_STAT_RX_PMAC_512_1023];
|
|
|
|
rmon_stats->hist[5] = s[OCELOT_STAT_RX_PMAC_1024_1526];
|
|
|
|
rmon_stats->hist[6] = s[OCELOT_STAT_RX_PMAC_1527_MAX];
|
|
|
|
|
|
|
|
rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_PMAC_64];
|
|
|
|
rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_PMAC_65_127];
|
|
|
|
rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_PMAC_128_255];
|
2023-12-14 02:09:02 +02:00
|
|
|
rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_PMAC_256_511];
|
|
|
|
rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_PMAC_512_1023];
|
|
|
|
rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_PMAC_1024_1526];
|
|
|
|
rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_PMAC_1527_MAX];
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
}
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_rmon_stats *rmon_stats,
|
|
|
|
const struct ethtool_rmon_hist_range **ranges)
|
|
|
|
{
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
struct net_device *dev;
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
*ranges = ocelot_rmon_ranges;
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
switch (rmon_stats->src) {
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
|
|
|
ocelot_port_stats_run(ocelot, port, rmon_stats,
|
|
|
|
ocelot_port_rmon_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
|
|
|
if (ocelot->mm_supported)
|
|
|
|
ocelot_port_stats_run(ocelot, port, rmon_stats,
|
|
|
|
ocelot_port_pmac_rmon_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
|
|
|
dev = ocelot->ops->port_to_netdev(ocelot, port);
|
|
|
|
ethtool_aggregate_rmon_stats(dev, rmon_stats);
|
|
|
|
break;
|
|
|
|
}
|
2022-09-08 19:48:13 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats);
|
|
|
|
|
|
|
|
static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_eth_ctrl_stats *ctrl_stats = priv;
|
|
|
|
|
|
|
|
ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL];
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
static void ocelot_port_pmac_ctrl_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_eth_ctrl_stats *ctrl_stats = priv;
|
|
|
|
|
|
|
|
ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_PMAC_CONTROL];
|
|
|
|
}
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_eth_ctrl_stats *ctrl_stats)
|
|
|
|
{
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
switch (ctrl_stats->src) {
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
|
|
|
ocelot_port_stats_run(ocelot, port, ctrl_stats,
|
|
|
|
ocelot_port_ctrl_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
|
|
|
if (ocelot->mm_supported)
|
|
|
|
ocelot_port_stats_run(ocelot, port, ctrl_stats,
|
|
|
|
ocelot_port_pmac_ctrl_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
|
|
|
dev = ocelot->ops->port_to_netdev(ocelot, port);
|
|
|
|
ethtool_aggregate_ctrl_stats(dev, ctrl_stats);
|
|
|
|
break;
|
|
|
|
}
|
2022-09-08 19:48:13 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats);
|
|
|
|
|
|
|
|
static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_eth_mac_stats *mac_stats = priv;
|
|
|
|
|
|
|
|
mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS];
|
|
|
|
mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] +
|
|
|
|
s[OCELOT_STAT_TX_65_127] +
|
|
|
|
s[OCELOT_STAT_TX_128_255] +
|
|
|
|
s[OCELOT_STAT_TX_256_511] +
|
|
|
|
s[OCELOT_STAT_TX_512_1023] +
|
|
|
|
s[OCELOT_STAT_TX_1024_1526] +
|
|
|
|
s[OCELOT_STAT_TX_1527_MAX];
|
|
|
|
mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS];
|
|
|
|
mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_1] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_2] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_3] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_4] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_5] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_6] +
|
|
|
|
s[OCELOT_STAT_RX_GREEN_PRIO_7] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_0] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_1] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_2] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_3] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_4] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_5] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_6] +
|
|
|
|
s[OCELOT_STAT_RX_YELLOW_PRIO_7];
|
|
|
|
mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST];
|
|
|
|
mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST];
|
|
|
|
mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST];
|
|
|
|
mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST];
|
|
|
|
mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS];
|
|
|
|
/* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not
|
|
|
|
* counted individually.
|
|
|
|
*/
|
|
|
|
mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
|
|
|
|
mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
static void ocelot_port_pmac_mac_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_eth_mac_stats *mac_stats = priv;
|
|
|
|
|
|
|
|
mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_PMAC_OCTETS];
|
|
|
|
mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_PMAC_64] +
|
|
|
|
s[OCELOT_STAT_TX_PMAC_65_127] +
|
|
|
|
s[OCELOT_STAT_TX_PMAC_128_255] +
|
|
|
|
s[OCELOT_STAT_TX_PMAC_256_511] +
|
|
|
|
s[OCELOT_STAT_TX_PMAC_512_1023] +
|
|
|
|
s[OCELOT_STAT_TX_PMAC_1024_1526] +
|
|
|
|
s[OCELOT_STAT_TX_PMAC_1527_MAX];
|
|
|
|
mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_PMAC_OCTETS];
|
|
|
|
mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_PMAC_64] +
|
|
|
|
s[OCELOT_STAT_RX_PMAC_65_127] +
|
|
|
|
s[OCELOT_STAT_RX_PMAC_128_255] +
|
|
|
|
s[OCELOT_STAT_RX_PMAC_256_511] +
|
|
|
|
s[OCELOT_STAT_RX_PMAC_512_1023] +
|
|
|
|
s[OCELOT_STAT_RX_PMAC_1024_1526] +
|
|
|
|
s[OCELOT_STAT_RX_PMAC_1527_MAX];
|
|
|
|
mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_PMAC_MULTICAST];
|
|
|
|
mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_PMAC_BROADCAST];
|
|
|
|
mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_PMAC_MULTICAST];
|
|
|
|
mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_PMAC_BROADCAST];
|
|
|
|
mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_PMAC_LONGS];
|
|
|
|
/* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not
|
|
|
|
* counted individually.
|
|
|
|
*/
|
|
|
|
mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS];
|
|
|
|
mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS];
|
|
|
|
}
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_eth_mac_stats *mac_stats)
|
|
|
|
{
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
switch (mac_stats->src) {
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
|
|
|
ocelot_port_stats_run(ocelot, port, mac_stats,
|
|
|
|
ocelot_port_mac_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
|
|
|
if (ocelot->mm_supported)
|
|
|
|
ocelot_port_stats_run(ocelot, port, mac_stats,
|
|
|
|
ocelot_port_pmac_mac_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
|
|
|
dev = ocelot->ops->port_to_netdev(ocelot, port);
|
|
|
|
ethtool_aggregate_mac_stats(dev, mac_stats);
|
|
|
|
break;
|
|
|
|
}
|
2022-09-08 19:48:13 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats);
|
|
|
|
|
|
|
|
static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_eth_phy_stats *phy_stats = priv;
|
|
|
|
|
|
|
|
phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS];
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
static void ocelot_port_pmac_phy_stats_cb(struct ocelot *ocelot, int port,
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
struct ethtool_eth_phy_stats *phy_stats = priv;
|
|
|
|
|
|
|
|
phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_PMAC_SYM_ERRS];
|
|
|
|
}
|
|
|
|
|
2022-09-08 19:48:13 +03:00
|
|
|
void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_eth_phy_stats *phy_stats)
|
|
|
|
{
|
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-01-19 14:27:03 +02:00
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
switch (phy_stats->src) {
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
|
|
|
ocelot_port_stats_run(ocelot, port, phy_stats,
|
|
|
|
ocelot_port_phy_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
|
|
|
if (ocelot->mm_supported)
|
|
|
|
ocelot_port_stats_run(ocelot, port, phy_stats,
|
|
|
|
ocelot_port_pmac_phy_stats_cb);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
|
|
|
dev = ocelot->ops->port_to_netdev(ocelot, port);
|
|
|
|
ethtool_aggregate_phy_stats(dev, phy_stats);
|
|
|
|
break;
|
|
|
|
}
|
2022-09-08 19:48:13 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
|
|
|
|
|
2022-09-08 19:48:11 +03:00
|
|
|
void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
|
|
|
|
struct rtnl_link_stats64 *stats)
|
|
|
|
{
|
|
|
|
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
|
|
|
|
|
|
|
|
spin_lock(&ocelot->stats_lock);
|
|
|
|
|
|
|
|
/* Get Rx stats */
|
|
|
|
stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS];
|
|
|
|
stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] +
|
|
|
|
s[OCELOT_STAT_RX_FRAGMENTS] +
|
|
|
|
s[OCELOT_STAT_RX_JABBERS] +
|
|
|
|
s[OCELOT_STAT_RX_LONGS] +
|
|
|
|
s[OCELOT_STAT_RX_64] +
|
|
|
|
s[OCELOT_STAT_RX_65_127] +
|
|
|
|
s[OCELOT_STAT_RX_128_255] +
|
|
|
|
s[OCELOT_STAT_RX_256_511] +
|
|
|
|
s[OCELOT_STAT_RX_512_1023] +
|
|
|
|
s[OCELOT_STAT_RX_1024_1526] +
|
|
|
|
s[OCELOT_STAT_RX_1527_MAX];
|
|
|
|
stats->multicast = s[OCELOT_STAT_RX_MULTICAST];
|
|
|
|
stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL];
|
|
|
|
stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_1] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_2] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_3] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_4] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_5] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_6] +
|
|
|
|
s[OCELOT_STAT_RX_RED_PRIO_7] +
|
|
|
|
s[OCELOT_STAT_DROP_LOCAL] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_0] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_1] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_2] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_3] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_4] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_5] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_6] +
|
|
|
|
s[OCELOT_STAT_DROP_YELLOW_PRIO_7] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_0] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_1] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_2] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_3] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_4] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_5] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_6] +
|
|
|
|
s[OCELOT_STAT_DROP_GREEN_PRIO_7];
|
|
|
|
|
|
|
|
/* Get Tx stats */
|
|
|
|
stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS];
|
|
|
|
stats->tx_packets = s[OCELOT_STAT_TX_64] +
|
|
|
|
s[OCELOT_STAT_TX_65_127] +
|
|
|
|
s[OCELOT_STAT_TX_128_255] +
|
|
|
|
s[OCELOT_STAT_TX_256_511] +
|
|
|
|
s[OCELOT_STAT_TX_512_1023] +
|
|
|
|
s[OCELOT_STAT_TX_1024_1526] +
|
|
|
|
s[OCELOT_STAT_TX_1527_MAX];
|
|
|
|
stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] +
|
|
|
|
s[OCELOT_STAT_TX_AGED];
|
|
|
|
stats->collisions = s[OCELOT_STAT_TX_COLLISION];
|
|
|
|
|
|
|
|
spin_unlock(&ocelot->stats_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_port_get_stats64);
|
|
|
|
|
2022-09-08 19:48:08 +03:00
|
|
|
static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
struct ocelot_stats_region *region = NULL;
|
2023-01-19 14:27:02 +02:00
|
|
|
const struct ocelot_stat_layout *layout;
|
2023-04-12 15:47:35 +03:00
|
|
|
enum ocelot_reg last = 0;
|
2023-04-12 15:47:36 +03:00
|
|
|
enum ocelot_stat i;
|
2022-09-08 19:48:08 +03:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&ocelot->stats_regions);
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
layout = ocelot_get_stats_layout(ocelot);
|
|
|
|
|
2022-09-08 19:48:08 +03:00
|
|
|
for (i = 0; i < OCELOT_NUM_STATS; i++) {
|
2023-01-19 14:27:02 +02:00
|
|
|
if (!layout[i].reg)
|
2022-09-08 19:48:08 +03:00
|
|
|
continue;
|
|
|
|
|
2023-04-12 15:47:37 +03:00
|
|
|
/* enum ocelot_stat must be kept sorted in the same order
|
|
|
|
* as the addresses behind layout[i].reg in order to have
|
|
|
|
* efficient bulking
|
|
|
|
*/
|
|
|
|
if (last) {
|
|
|
|
WARN(ocelot->map[SYS][last & REG_MASK] >= ocelot->map[SYS][layout[i].reg & REG_MASK],
|
|
|
|
"reg 0x%x had address 0x%x but reg 0x%x has address 0x%x, bulking broken!",
|
|
|
|
last, ocelot->map[SYS][last & REG_MASK],
|
|
|
|
layout[i].reg, ocelot->map[SYS][layout[i].reg & REG_MASK]);
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: fix stats region batching
The blamed commit changed struct ocelot_stat_layout :: "u32 offset" to
"u32 reg".
However, "u32 reg" is not quite a register address, but an enum
ocelot_reg, which in itself encodes an enum ocelot_target target in the
upper bits, and an index into the ocelot->map[target][] array in the
lower bits.
So, whereas the previous code comparison between stats_layout[i].offset
and last + 1 was correct (because those "offsets" at the time were
32-bit relative addresses), the new code, comparing layout[i].reg to
last + 4 is not correct, because the "reg" here is an enum/index, not an
actual register address.
What we want to compare are indeed register addresses, but to do that,
we need to actually go through the same motions as
__ocelot_bulk_read_ix() itself.
With this bug, all statistics counters are deemed by
ocelot_prepare_stats_regions() as constituting their own region.
(Truncated) log on VSC9959 (Felix) below (prints added by me):
Before:
region of 1 contiguous counters starting with SYS:STAT:CNT[0x000]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x001]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x002]
...
region of 1 contiguous counters starting with SYS:STAT:CNT[0x041]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x042]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x080]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x081]
...
region of 1 contiguous counters starting with SYS:STAT:CNT[0x0ac]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x100]
region of 1 contiguous counters starting with SYS:STAT:CNT[0x101]
...
region of 1 contiguous counters starting with SYS:STAT:CNT[0x111]
After:
region of 67 contiguous counters starting with SYS:STAT:CNT[0x000]
region of 45 contiguous counters starting with SYS:STAT:CNT[0x080]
region of 18 contiguous counters starting with SYS:STAT:CNT[0x100]
Since commit d87b1c08f38a ("net: mscc: ocelot: use bulk reads for
stats") intended bulking as a performance improvement, and since now,
with trivial-sized regions, performance is even worse than without
bulking at all, this could easily qualify as a performance regression.
Fixes: d4c367650704 ("net: mscc: ocelot: keep ocelot_stat_layout by reg address, not offset")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Colin Foster <colin.foster@in-advantage.com>
Tested-by: Colin Foster <colin.foster@in-advantage.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-03-21 03:03:23 +02:00
|
|
|
if (region && ocelot->map[SYS][layout[i].reg & REG_MASK] ==
|
|
|
|
ocelot->map[SYS][last & REG_MASK] + 4) {
|
2022-09-08 19:48:08 +03:00
|
|
|
region->count++;
|
|
|
|
} else {
|
|
|
|
region = devm_kzalloc(ocelot->dev, sizeof(*region),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!region)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
region->base = layout[i].reg;
|
net: mscc: ocelot: fix transfer from region->buf to ocelot->stats
To understand the problem, we need some definitions.
The driver is aware of multiple counters (enum ocelot_stat), yet not all
switches supported by the driver implement all counters. There are 2
statistics layouts: ocelot_stats_layout and ocelot_mm_stats_layout, the
latter having 36 counters more than the former.
ocelot->stats[] is not a compact array, i.e. there are elements within
it which are not going to be populated for ocelot_stats_layout. On the
other hand, ocelot->stats[] is easily indexable, for example "tx_octets"
for port 3 can be found at ocelot->stats[3 * OCELOT_NUM_STATS +
OCELOT_STAT_TX_OCTETS], and that is why we keep it sparse.
Regions, as created by ocelot_prepare_stats_regions(), are compact
(every element from region->buf will correspond to a counter that is
present in this switch's layout) but are not easily indexable.
Let's define holes as the ranges of values of enum ocelot_stat for which
ocelot_stats_layout doesn't have a "reg" defined. For example, there is
a hole between OCELOT_STAT_RX_GREEN_PRIO_7 and OCELOT_STAT_TX_OCTETS
which is of 23 elements that are only present on ocelot_mm_stats_layout,
and as such, they are also present in enum ocelot_stat. Let's define the
left extremity of the hole - the last enum ocelot_stat still defined -
as A (in this case OCELOT_STAT_RX_GREEN_PRIO_7) and the right extremity -
the first enum ocelot_stat that is defined after a series of undefined
ones - as B (in this case OCELOT_STAT_TX_OCTETS).
There is a bug in the procedure which transfers stats from region->buf[]
to ocelot->stats[].
For each hole in the ocelot_stats_layout, the logic transfers the stats
starting with enum ocelot_stat B to ocelot->stats[] index A + 1. So all
stats after a hole are saved to a position which is off by B - A + 1
elements.
This causes 2 kinds of issues:
(a) counters which shouldn't increment increment
(b) counters which should increment don't
Holes in the ocelot_stat_layout automatically imply the end of a region
and the beginning of a new one; however the reverse is not necessarily
true. For example, for ocelot_mm_stat_layout, there could be multiple
regions (which indicate discontinuities in register addresses) while
there is no hole (which indicates discontinuities in enum ocelot_stat
values).
In the example above, the stats from the second region->buf[] are not
transferred to ocelot->stats starting with index
"port * OCELOT_NUM_STATS + OCELOT_STAT_TX_OCTETS" as they should, but
rather, starting with element
"port * OCELOT_NUM_STATS + OCELOT_STAT_RX_GREEN_PRIO_7 + 1".
That stats[] array element is not reported to user space for switches
that use ocelot_stat_layout, and that is how issue (b) occurs.
However, if the length of the second region is larger than the hole,
then some stats will start to be transferred to the ocelot->stats[]
indices which *are* reported to user space, but those indices contain
wrong values (corresponding to unexpected counters). This is how issue
(a) occurs.
The procedure, as it was introduced in commit d87b1c08f38a ("net: mscc:
ocelot: use bulk reads for stats"), was not buggy, because there were no
holes in the struct ocelot_stat_layout instances at that time. The
problem is that when those holes were introduced, the function was not
updated to take them into consideration.
To update the procedure, we need to know, for each region, which enum
ocelot_stat corresponds to its region->base. We have no way of deducing
that based on the contents of struct ocelot_stats_region, so we need to
add this information.
Fixes: ab3f97a9610a ("net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-03-21 03:03:24 +02:00
|
|
|
region->first_stat = i;
|
2022-09-08 19:48:08 +03:00
|
|
|
region->count = 1;
|
|
|
|
list_add_tail(®ion->node, &ocelot->stats_regions);
|
|
|
|
}
|
|
|
|
|
2023-01-19 14:27:02 +02:00
|
|
|
last = layout[i].reg;
|
2022-09-08 19:48:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(region, &ocelot->stats_regions, node) {
|
2023-04-12 15:47:32 +03:00
|
|
|
enum ocelot_target target;
|
|
|
|
u32 addr;
|
|
|
|
|
|
|
|
ocelot_reg_to_target_addr(ocelot, region->base, &target,
|
|
|
|
&addr);
|
|
|
|
|
|
|
|
dev_dbg(ocelot->dev,
|
|
|
|
"region of %d contiguous counters starting with SYS:STAT:CNT[0x%03x]\n",
|
|
|
|
region->count, addr / 4);
|
2022-09-08 19:48:08 +03:00
|
|
|
region->buf = devm_kcalloc(ocelot->dev, region->count,
|
|
|
|
sizeof(*region->buf), GFP_KERNEL);
|
|
|
|
if (!region->buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ocelot_stats_init(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
char queue_name[32];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ocelot->stats = devm_kcalloc(ocelot->dev,
|
|
|
|
ocelot->num_phys_ports * OCELOT_NUM_STATS,
|
|
|
|
sizeof(u64), GFP_KERNEL);
|
|
|
|
if (!ocelot->stats)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
snprintf(queue_name, sizeof(queue_name), "%s-stats",
|
|
|
|
dev_name(ocelot->dev));
|
|
|
|
ocelot->stats_queue = create_singlethread_workqueue(queue_name);
|
|
|
|
if (!ocelot->stats_queue)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock_init(&ocelot->stats_lock);
|
|
|
|
mutex_init(&ocelot->stat_view_lock);
|
|
|
|
|
|
|
|
ret = ocelot_prepare_stats_regions(ocelot);
|
|
|
|
if (ret) {
|
|
|
|
destroy_workqueue(ocelot->stats_queue);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
|
|
|
|
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
|
|
|
OCELOT_STATS_CHECK_DELAY);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ocelot_stats_deinit(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
cancel_delayed_work(&ocelot->stats_work);
|
|
|
|
destroy_workqueue(ocelot->stats_queue);
|
|
|
|
}
|