Commit 8243fe8a authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-dsa-add-stats64-support'

Oleksij Rempel says:

====================
net: dsa: add stats64 support

changes v8:
- stats.no_handler should not be assigned from HW stats

changes v7:
- move raw.filtered from rx_errors to rx_dropped counter

changes v6:
- move stats64 callback to ethtool section
- ar9331: diff. fixes
- ar9331: move stats calculation to the worker
- ar9331: extend rx/tx error counters
- use spin lock instead of u64_stats*

changes v5:
- read all stats in one regmap_bulk_read() request
- protect stats with u64_stats* helpers.

changes v4:
- do no read MIBs withing stats64 call
- change polling frequency to 0.3Hz

changes v3:
- fix wrong multiplication
- cancel port workers on remove

changes v2:
- use stats64 instead of get_ethtool_stats
- add worked to poll for the stats
====================

Link: https://lore.kernel.org/r/20210111104658.21930-1-o.rempel@pengutronix.deSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 46e05e1d bf9ce385
...@@ -101,6 +101,9 @@ ...@@ -101,6 +101,9 @@
AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \
AR9331_SW_PORT_STATUS_SPEED_M) AR9331_SW_PORT_STATUS_SPEED_M)
/* MIB registers */
#define AR9331_MIB_COUNTER(x) (0x20000 + ((x) * 0x100))
/* Phy bypass mode /* Phy bypass mode
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 |
...@@ -154,6 +157,66 @@ ...@@ -154,6 +157,66 @@
#define AR9331_SW_MDIO_POLL_SLEEP_US 1 #define AR9331_SW_MDIO_POLL_SLEEP_US 1
#define AR9331_SW_MDIO_POLL_TIMEOUT_US 20 #define AR9331_SW_MDIO_POLL_TIMEOUT_US 20
/* The interval should be small enough to avoid overflow of 32bit MIBs */
/*
* FIXME: until we can read MIBs from stats64 call directly (i.e. sleep
* there), we have to poll stats more frequently then it is actually needed.
* For overflow protection, normally, 100 sec interval should have been OK.
*/
#define STATS_INTERVAL_JIFFIES (3 * HZ)
struct ar9331_sw_stats_raw {
u32 rxbroad; /* 0x00 */
u32 rxpause; /* 0x04 */
u32 rxmulti; /* 0x08 */
u32 rxfcserr; /* 0x0c */
u32 rxalignerr; /* 0x10 */
u32 rxrunt; /* 0x14 */
u32 rxfragment; /* 0x18 */
u32 rx64byte; /* 0x1c */
u32 rx128byte; /* 0x20 */
u32 rx256byte; /* 0x24 */
u32 rx512byte; /* 0x28 */
u32 rx1024byte; /* 0x2c */
u32 rx1518byte; /* 0x30 */
u32 rxmaxbyte; /* 0x34 */
u32 rxtoolong; /* 0x38 */
u32 rxgoodbyte; /* 0x3c */
u32 rxgoodbyte_hi;
u32 rxbadbyte; /* 0x44 */
u32 rxbadbyte_hi;
u32 rxoverflow; /* 0x4c */
u32 filtered; /* 0x50 */
u32 txbroad; /* 0x54 */
u32 txpause; /* 0x58 */
u32 txmulti; /* 0x5c */
u32 txunderrun; /* 0x60 */
u32 tx64byte; /* 0x64 */
u32 tx128byte; /* 0x68 */
u32 tx256byte; /* 0x6c */
u32 tx512byte; /* 0x70 */
u32 tx1024byte; /* 0x74 */
u32 tx1518byte; /* 0x78 */
u32 txmaxbyte; /* 0x7c */
u32 txoversize; /* 0x80 */
u32 txbyte; /* 0x84 */
u32 txbyte_hi;
u32 txcollision; /* 0x8c */
u32 txabortcol; /* 0x90 */
u32 txmulticol; /* 0x94 */
u32 txsinglecol; /* 0x98 */
u32 txexcdefer; /* 0x9c */
u32 txdefer; /* 0xa0 */
u32 txlatecol; /* 0xa4 */
};
struct ar9331_sw_port {
int idx;
struct delayed_work mib_read;
struct rtnl_link_stats64 stats;
struct spinlock stats_lock;
};
struct ar9331_sw_priv { struct ar9331_sw_priv {
struct device *dev; struct device *dev;
struct dsa_switch ds; struct dsa_switch ds;
...@@ -165,8 +228,17 @@ struct ar9331_sw_priv { ...@@ -165,8 +228,17 @@ struct ar9331_sw_priv {
struct mii_bus *sbus; /* mdio slave */ struct mii_bus *sbus; /* mdio slave */
struct regmap *regmap; struct regmap *regmap;
struct reset_control *sw_reset; struct reset_control *sw_reset;
struct ar9331_sw_port port[AR9331_SW_PORTS];
}; };
static struct ar9331_sw_priv *ar9331_sw_port_to_priv(struct ar9331_sw_port *port)
{
struct ar9331_sw_port *p = port - port->idx;
return (struct ar9331_sw_priv *)((void *)p -
offsetof(struct ar9331_sw_priv, port));
}
/* Warning: switch reset will reset last AR9331_SW_MDIO_PHY_MODE_PAGE request /* Warning: switch reset will reset last AR9331_SW_MDIO_PHY_MODE_PAGE request
* If some kind of optimization is used, the request should be repeated. * If some kind of optimization is used, the request should be repeated.
*/ */
...@@ -424,6 +496,7 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, ...@@ -424,6 +496,7 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
phy_interface_t interface) phy_interface_t interface)
{ {
struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
struct ar9331_sw_port *p = &priv->port[port];
struct regmap *regmap = priv->regmap; struct regmap *regmap = priv->regmap;
int ret; int ret;
...@@ -431,6 +504,8 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, ...@@ -431,6 +504,8 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
AR9331_SW_PORT_STATUS_MAC_MASK, 0); AR9331_SW_PORT_STATUS_MAC_MASK, 0);
if (ret) if (ret)
dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
cancel_delayed_work_sync(&p->mib_read);
} }
static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
...@@ -441,10 +516,13 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, ...@@ -441,10 +516,13 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
bool tx_pause, bool rx_pause) bool tx_pause, bool rx_pause)
{ {
struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
struct ar9331_sw_port *p = &priv->port[port];
struct regmap *regmap = priv->regmap; struct regmap *regmap = priv->regmap;
u32 val; u32 val;
int ret; int ret;
schedule_delayed_work(&p->mib_read, 0);
val = AR9331_SW_PORT_STATUS_MAC_MASK; val = AR9331_SW_PORT_STATUS_MAC_MASK;
switch (speed) { switch (speed) {
case SPEED_1000: case SPEED_1000:
...@@ -477,6 +555,73 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, ...@@ -477,6 +555,73 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
} }
static void ar9331_read_stats(struct ar9331_sw_port *port)
{
struct ar9331_sw_priv *priv = ar9331_sw_port_to_priv(port);
struct rtnl_link_stats64 *stats = &port->stats;
struct ar9331_sw_stats_raw raw;
int ret;
/* Do the slowest part first, to avoid needless locking for long time */
ret = regmap_bulk_read(priv->regmap, AR9331_MIB_COUNTER(port->idx),
&raw, sizeof(raw) / sizeof(u32));
if (ret) {
dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
return;
}
/* All MIB counters are cleared automatically on read */
spin_lock(&port->stats_lock);
stats->rx_bytes += raw.rxgoodbyte;
stats->tx_bytes += raw.txbyte;
stats->rx_packets += raw.rx64byte + raw.rx128byte + raw.rx256byte +
raw.rx512byte + raw.rx1024byte + raw.rx1518byte + raw.rxmaxbyte;
stats->tx_packets += raw.tx64byte + raw.tx128byte + raw.tx256byte +
raw.tx512byte + raw.tx1024byte + raw.tx1518byte + raw.txmaxbyte;
stats->rx_length_errors += raw.rxrunt + raw.rxfragment + raw.rxtoolong;
stats->rx_crc_errors += raw.rxfcserr;
stats->rx_frame_errors += raw.rxalignerr;
stats->rx_missed_errors += raw.rxoverflow;
stats->rx_dropped += raw.filtered;
stats->rx_errors += raw.rxfcserr + raw.rxalignerr + raw.rxrunt +
raw.rxfragment + raw.rxoverflow + raw.rxtoolong;
stats->tx_window_errors += raw.txlatecol;
stats->tx_fifo_errors += raw.txunderrun;
stats->tx_aborted_errors += raw.txabortcol;
stats->tx_errors += raw.txoversize + raw.txabortcol + raw.txunderrun +
raw.txlatecol;
stats->multicast += raw.rxmulti;
stats->collisions += raw.txcollision;
spin_unlock(&port->stats_lock);
}
static void ar9331_do_stats_poll(struct work_struct *work)
{
struct ar9331_sw_port *port = container_of(work, struct ar9331_sw_port,
mib_read.work);
ar9331_read_stats(port);
schedule_delayed_work(&port->mib_read, STATS_INTERVAL_JIFFIES);
}
static void ar9331_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s)
{
struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
struct ar9331_sw_port *p = &priv->port[port];
spin_lock(&p->stats_lock);
memcpy(s, &p->stats, sizeof(*s));
spin_unlock(&p->stats_lock);
}
static const struct dsa_switch_ops ar9331_sw_ops = { static const struct dsa_switch_ops ar9331_sw_ops = {
.get_tag_protocol = ar9331_sw_get_tag_protocol, .get_tag_protocol = ar9331_sw_get_tag_protocol,
.setup = ar9331_sw_setup, .setup = ar9331_sw_setup,
...@@ -485,6 +630,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = { ...@@ -485,6 +630,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = {
.phylink_mac_config = ar9331_sw_phylink_mac_config, .phylink_mac_config = ar9331_sw_phylink_mac_config,
.phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down,
.phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up,
.get_stats64 = ar9331_get_stats64,
}; };
static irqreturn_t ar9331_sw_irq(int irq, void *data) static irqreturn_t ar9331_sw_irq(int irq, void *data)
...@@ -796,7 +942,7 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev) ...@@ -796,7 +942,7 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
{ {
struct ar9331_sw_priv *priv; struct ar9331_sw_priv *priv;
struct dsa_switch *ds; struct dsa_switch *ds;
int ret; int ret, i;
priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -831,6 +977,14 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev) ...@@ -831,6 +977,14 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
ds->ops = &priv->ops; ds->ops = &priv->ops;
dev_set_drvdata(&mdiodev->dev, priv); dev_set_drvdata(&mdiodev->dev, priv);
for (i = 0; i < ARRAY_SIZE(priv->port); i++) {
struct ar9331_sw_port *port = &priv->port[i];
port->idx = i;
spin_lock_init(&port->stats_lock);
INIT_DELAYED_WORK(&port->mib_read, ar9331_do_stats_poll);
}
ret = dsa_register_switch(ds); ret = dsa_register_switch(ds);
if (ret) if (ret)
goto err_remove_irq; goto err_remove_irq;
...@@ -846,6 +1000,13 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev) ...@@ -846,6 +1000,13 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
static void ar9331_sw_remove(struct mdio_device *mdiodev) static void ar9331_sw_remove(struct mdio_device *mdiodev)
{ {
struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev); struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev);
unsigned int i;
for (i = 0; i < ARRAY_SIZE(priv->port); i++) {
struct ar9331_sw_port *port = &priv->port[i];
cancel_delayed_work_sync(&port->mib_read);
}
irq_domain_remove(priv->irqdomain); irq_domain_remove(priv->irqdomain);
mdiobus_unregister(priv->mbus); mdiobus_unregister(priv->mbus);
......
...@@ -482,7 +482,7 @@ struct dsa_switch_ops { ...@@ -482,7 +482,7 @@ struct dsa_switch_ops {
void (*phylink_fixed_state)(struct dsa_switch *ds, int port, void (*phylink_fixed_state)(struct dsa_switch *ds, int port,
struct phylink_link_state *state); struct phylink_link_state *state);
/* /*
* ethtool hardware statistics. * Port statistics counters.
*/ */
void (*get_strings)(struct dsa_switch *ds, int port, void (*get_strings)(struct dsa_switch *ds, int port,
u32 stringset, uint8_t *data); u32 stringset, uint8_t *data);
...@@ -491,6 +491,8 @@ struct dsa_switch_ops { ...@@ -491,6 +491,8 @@ struct dsa_switch_ops {
int (*get_sset_count)(struct dsa_switch *ds, int port, int sset); int (*get_sset_count)(struct dsa_switch *ds, int port, int sset);
void (*get_ethtool_phy_stats)(struct dsa_switch *ds, void (*get_ethtool_phy_stats)(struct dsa_switch *ds,
int port, uint64_t *data); int port, uint64_t *data);
void (*get_stats64)(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s);
/* /*
* ethtool Wake-on-LAN * ethtool Wake-on-LAN
......
...@@ -1569,6 +1569,18 @@ static struct devlink_port *dsa_slave_get_devlink_port(struct net_device *dev) ...@@ -1569,6 +1569,18 @@ static struct devlink_port *dsa_slave_get_devlink_port(struct net_device *dev)
return dp->ds->devlink ? &dp->devlink_port : NULL; return dp->ds->devlink ? &dp->devlink_port : NULL;
} }
static void dsa_slave_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *s)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (ds->ops->get_stats64)
ds->ops->get_stats64(ds, dp->index, s);
else
dev_get_tstats64(dev, s);
}
static const struct net_device_ops dsa_slave_netdev_ops = { static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_open = dsa_slave_open, .ndo_open = dsa_slave_open,
.ndo_stop = dsa_slave_close, .ndo_stop = dsa_slave_close,
...@@ -1588,7 +1600,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = { ...@@ -1588,7 +1600,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
#endif #endif
.ndo_get_phys_port_name = dsa_slave_get_phys_port_name, .ndo_get_phys_port_name = dsa_slave_get_phys_port_name,
.ndo_setup_tc = dsa_slave_setup_tc, .ndo_setup_tc = dsa_slave_setup_tc,
.ndo_get_stats64 = dev_get_tstats64, .ndo_get_stats64 = dsa_slave_get_stats64,
.ndo_get_port_parent_id = dsa_slave_get_port_parent_id, .ndo_get_port_parent_id = dsa_slave_get_port_parent_id,
.ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid, .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment