Skip to content

Commit

Permalink
sh_eth: Mitigate lost statistics updates
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ben Hutchings authored and David S. Miller committed Mar 4, 2015
1 parent e5fd13f commit 4398f9c
Showing 1 changed file with 25 additions and 12 deletions.
37 changes: 25 additions & 12 deletions drivers/net/ethernet/renesas/sh_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -2417,6 +2417,22 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
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)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
Expand All @@ -2427,21 +2443,18 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
if (!mdp->is_opened)
return &ndev->stats;

ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
sh_eth_write(ndev, 0, TROCR); /* (write clear) */
ndev->stats.collisions += sh_eth_read(ndev, CDCR);
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) */
sh_eth_update_stat(ndev, &ndev->stats.tx_dropped, TROCR);
sh_eth_update_stat(ndev, &ndev->stats.collisions, CDCR);
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors, LCCR);

if (sh_eth_is_gether(mdp)) {
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
sh_eth_write(ndev, 0, CERCR); /* (write clear) */
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
sh_eth_write(ndev, 0, CEECR); /* (write clear) */
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
CERCR);
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
CEECR);
} else {
ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
sh_eth_write(ndev, 0, CNDCR); /* (write clear) */
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
CNDCR);
}

return &ndev->stats;
Expand Down

0 comments on commit 4398f9c

Please sign in to comment.