diff options
author | Vladimir Oltean <vladimir.oltean@nxp.com> | 2022-09-08 18:48:04 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2022-09-09 11:59:12 +0200 |
commit | 96980ff7c2caa5baef0c684e719547a53762e82c (patch) | |
tree | e5f702255b90a8d9d33d45de3cc4bf7543b1977a /drivers/net/ethernet/mscc/ocelot.c | |
parent | net: dsa: felix: add definitions for the stream filter counters (diff) | |
download | linux-96980ff7c2caa5baef0c684e719547a53762e82c.tar.xz linux-96980ff7c2caa5baef0c684e719547a53762e82c.zip |
net: mscc: ocelot: make access to STAT_VIEW sleepable again
To support SPI-controlled switches in the future, access to
SYS_STAT_CFG_STAT_VIEW needs to be done outside of any spinlock
protected region, but it still needs to be serialized (by a mutex).
Split the ocelot->stats_lock spinlock into a mutex that serializes
indirect access to hardware registers (ocelot->stat_view_lock) and a
spinlock that serializes access to the u64 ocelot->stats array.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot.c')
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 48 |
1 files changed, 37 insertions, 11 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index dddaffdaad9a..a677a18239c5 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1870,12 +1870,13 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) } EXPORT_SYMBOL(ocelot_get_strings); -/* Caller must hold &ocelot->stats_lock */ +/* 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) { - unsigned int idx = port * OCELOT_NUM_STATS; struct ocelot_stats_region *region; - int err, j; + int err; /* Configure the port to read the stats from */ ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); @@ -1885,7 +1886,21 @@ static int ocelot_port_update_stats(struct ocelot *ocelot, int port) 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]; @@ -1898,8 +1913,6 @@ static int ocelot_port_update_stats(struct ocelot *ocelot, int port) idx += region->count; } - - return err; } static void ocelot_check_stats_work(struct work_struct *work) @@ -1907,15 +1920,21 @@ 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 i, err; + int port, err; - spin_lock(&ocelot->stats_lock); - for (i = 0; i < ocelot->num_phys_ports; i++) { - err = ocelot_port_update_stats(ocelot, i); + 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); } - spin_unlock(&ocelot->stats_lock); + + mutex_unlock(&ocelot->stat_view_lock); if (err) dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); @@ -1928,11 +1947,15 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) { int i, err; - spin_lock(&ocelot->stats_lock); + mutex_lock(&ocelot->stat_view_lock); /* check and update now */ err = ocelot_port_update_stats(ocelot, port); + spin_lock(&ocelot->stats_lock); + + ocelot_port_transfer_stats(ocelot, port); + /* Copy all supported counters */ for (i = 0; i < OCELOT_NUM_STATS; i++) { int index = port * OCELOT_NUM_STATS + i; @@ -1945,6 +1968,8 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) spin_unlock(&ocelot->stats_lock); + mutex_unlock(&ocelot->stat_view_lock); + if (err) dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); } @@ -3396,6 +3421,7 @@ int ocelot_init(struct ocelot *ocelot) return -ENOMEM; spin_lock_init(&ocelot->stats_lock); + mutex_init(&ocelot->stat_view_lock); mutex_init(&ocelot->ptp_lock); mutex_init(&ocelot->mact_lock); mutex_init(&ocelot->fwd_domain_lock); |