123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- // SPDX-License-Identifier: (GPL-2.0 OR MIT)
- /* Statistics for Ocelot switch family
- *
- * Copyright (c) 2017 Microsemi Corporation
- * Copyright 2022 NXP
- */
- #include <linux/spinlock.h>
- #include <linux/mutex.h>
- #include <linux/workqueue.h>
- #include "ocelot.h"
- /* 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)
- {
- unsigned int idx = port * OCELOT_NUM_STATS;
- struct ocelot_stats_region *region;
- int j;
- list_for_each_entry(region, &ocelot->stats_regions, node) {
- 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;
- }
- idx += region->count;
- }
- }
- 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)
- {
- int i;
- if (sset != ETH_SS_STATS)
- return;
- for (i = 0; i < OCELOT_NUM_STATS; i++) {
- if (ocelot->stats_layout[i].name[0] == '\0')
- continue;
- memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
- ETH_GSTRING_LEN);
- }
- }
- EXPORT_SYMBOL(ocelot_get_strings);
- /* 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))
- {
- int err;
- mutex_lock(&ocelot->stat_view_lock);
- err = ocelot_port_update_stats(ocelot, port);
- if (err) {
- dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n",
- port, ERR_PTR(err));
- goto out_unlock;
- }
- spin_lock(&ocelot->stats_lock);
- ocelot_port_transfer_stats(ocelot, port);
- cb(ocelot, port, priv);
- spin_unlock(&ocelot->stats_lock);
- out_unlock:
- mutex_unlock(&ocelot->stat_view_lock);
- }
- int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
- {
- int i, num_stats = 0;
- if (sset != ETH_SS_STATS)
- return -EOPNOTSUPP;
- for (i = 0; i < OCELOT_NUM_STATS; i++)
- if (ocelot->stats_layout[i].name[0] != '\0')
- num_stats++;
- return num_stats;
- }
- EXPORT_SYMBOL(ocelot_get_sset_count);
- static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port,
- void *priv)
- {
- u64 *data = priv;
- int i;
- /* Copy all supported counters */
- for (i = 0; i < OCELOT_NUM_STATS; i++) {
- int index = port * OCELOT_NUM_STATS + i;
- if (ocelot->stats_layout[i].name[0] == '\0')
- 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];
- }
- void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
- struct ethtool_pause_stats *pause_stats)
- {
- ocelot_port_stats_run(ocelot, port, pause_stats,
- ocelot_port_pause_stats_cb);
- }
- EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats);
- 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];
- rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255];
- rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511];
- rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023];
- rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526];
- }
- void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
- {
- *ranges = ocelot_rmon_ranges;
- ocelot_port_stats_run(ocelot, port, rmon_stats,
- ocelot_port_rmon_stats_cb);
- }
- 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];
- }
- void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
- {
- ocelot_port_stats_run(ocelot, port, ctrl_stats,
- ocelot_port_ctrl_stats_cb);
- }
- 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];
- }
- void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
- struct ethtool_eth_mac_stats *mac_stats)
- {
- ocelot_port_stats_run(ocelot, port, mac_stats,
- ocelot_port_mac_stats_cb);
- }
- 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];
- }
- void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
- struct ethtool_eth_phy_stats *phy_stats)
- {
- ocelot_port_stats_run(ocelot, port, phy_stats,
- ocelot_port_phy_stats_cb);
- }
- EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
- 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);
- static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
- {
- struct ocelot_stats_region *region = NULL;
- unsigned int last;
- int i;
- INIT_LIST_HEAD(&ocelot->stats_regions);
- for (i = 0; i < OCELOT_NUM_STATS; i++) {
- if (!ocelot->stats_layout[i].reg)
- continue;
- if (region && ocelot->map[SYS][ocelot->stats_layout[i].reg & REG_MASK] ==
- ocelot->map[SYS][last & REG_MASK] + 4) {
- region->count++;
- } else {
- region = devm_kzalloc(ocelot->dev, sizeof(*region),
- GFP_KERNEL);
- if (!region)
- return -ENOMEM;
- region->base = ocelot->stats_layout[i].reg;
- region->count = 1;
- list_add_tail(®ion->node, &ocelot->stats_regions);
- }
- last = ocelot->stats_layout[i].reg;
- }
- list_for_each_entry(region, &ocelot->stats_regions, node) {
- 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);
- }
|