From 96980ff7c2caa5baef0c684e719547a53762e82c Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 8 Sep 2022 19:48:04 +0300 Subject: [PATCH] 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 Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix_vsc9959.c | 4 +-- drivers/net/ethernet/mscc/ocelot.c | 48 ++++++++++++++++++++++++++-------- include/soc/mscc/ocelot.h | 9 ++++--- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 3c80df4..858ccf1 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -2574,7 +2574,7 @@ static void vsc9959_psfp_sgi_table_del(struct ocelot *ocelot, static void vsc9959_psfp_counters_get(struct ocelot *ocelot, u32 index, struct felix_stream_filter_counters *counters) { - spin_lock(&ocelot->stats_lock); + mutex_lock(&ocelot->stat_view_lock); ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(index), SYS_STAT_CFG_STAT_VIEW_M, @@ -2593,7 +2593,7 @@ static void vsc9959_psfp_counters_get(struct ocelot *ocelot, u32 index, SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10), SYS_STAT_CFG); - spin_unlock(&ocelot->stats_lock); + mutex_unlock(&ocelot->stat_view_lock); } static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index dddaffd..a677a18 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); diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 99d6792..e85fb3b 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -901,12 +901,15 @@ struct ocelot { struct ocelot_psfp_list psfp; - /* Workqueue to check statistics for overflow with its lock */ - spinlock_t stats_lock; - u64 *stats; + /* Workqueue to check statistics for overflow */ struct delayed_work stats_work; struct workqueue_struct *stats_queue; + /* Lock for serializing access to the statistics array */ + spinlock_t stats_lock; + u64 *stats; + /* Lock for serializing indirect access to STAT_VIEW registers */ + struct mutex stat_view_lock; /* Lock for serializing access to the MAC table */ struct mutex mact_lock; /* Lock for serializing forwarding domain changes */ -- 2.7.4