sh_eth: Mitigate lost statistics updates
The statistics registers have write-clear behaviour, which means we will lose any increment between the read and write. Mitigate this by only clearing when we read a non-zero value, so we will never falsely report a total of zero. This also saves time as we only handle error statistics here and they won't often be incremented. Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e5fd13f476
commit
4398f9c817
|
@ -2417,6 +2417,22 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The statistics registers have write-clear behaviour, which means we
|
||||||
|
* will lose any increment between the read and write. We mitigate
|
||||||
|
* this by only clearing when we read a non-zero value, so we will
|
||||||
|
* never falsely report a total of zero.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sh_eth_update_stat(struct net_device *ndev, unsigned long *stat, int reg)
|
||||||
|
{
|
||||||
|
u32 delta = sh_eth_read(ndev, reg);
|
||||||
|
|
||||||
|
if (delta) {
|
||||||
|
*stat += delta;
|
||||||
|
sh_eth_write(ndev, 0, reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
|
static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
|
||||||
{
|
{
|
||||||
struct sh_eth_private *mdp = netdev_priv(ndev);
|
struct sh_eth_private *mdp = netdev_priv(ndev);
|
||||||
|
@ -2427,21 +2443,18 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
|
||||||
if (!mdp->is_opened)
|
if (!mdp->is_opened)
|
||||||
return &ndev->stats;
|
return &ndev->stats;
|
||||||
|
|
||||||
ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
|
sh_eth_update_stat(ndev, &ndev->stats.tx_dropped, TROCR);
|
||||||
sh_eth_write(ndev, 0, TROCR); /* (write clear) */
|
sh_eth_update_stat(ndev, &ndev->stats.collisions, CDCR);
|
||||||
ndev->stats.collisions += sh_eth_read(ndev, CDCR);
|
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors, LCCR);
|
||||||
sh_eth_write(ndev, 0, CDCR); /* (write clear) */
|
|
||||||
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
|
|
||||||
sh_eth_write(ndev, 0, LCCR); /* (write clear) */
|
|
||||||
|
|
||||||
if (sh_eth_is_gether(mdp)) {
|
if (sh_eth_is_gether(mdp)) {
|
||||||
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
|
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
|
||||||
sh_eth_write(ndev, 0, CERCR); /* (write clear) */
|
CERCR);
|
||||||
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
|
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
|
||||||
sh_eth_write(ndev, 0, CEECR); /* (write clear) */
|
CEECR);
|
||||||
} else {
|
} else {
|
||||||
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
|
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
|
||||||
sh_eth_write(ndev, 0, CNDCR); /* (write clear) */
|
CNDCR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ndev->stats;
|
return &ndev->stats;
|
||||||
|
|
Loading…
Reference in New Issue