sfc: Fix interface statistics running backward
authorBen Hutchings <bhutchings@solarflare.com>
Wed, 4 Jul 2012 21:25:07 +0000 (22:25 +0100)
committerBen Hutchings <bhutchings@solarflare.com>
Tue, 17 Jul 2012 15:12:34 +0000 (16:12 +0100)
Some interface statistics are computed in such a way that they can
sometimes decrease (and even underflow).  Since the computed value
will never be greater than the true value, we fix this by only storing
the computed value when it increases.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
drivers/net/ethernet/sfc/falcon_xmac.c
drivers/net/ethernet/sfc/nic.h
drivers/net/ethernet/sfc/siena.c

index 6106ef15dee38d84508456e8caabbee34cdd8bd9..8333865d4c956846578aeba1b0b55ba532af0a27 100644 (file)
@@ -341,12 +341,12 @@ void falcon_update_stats_xmac(struct efx_nic *efx)
        FALCON_STAT(efx, XgTxIpSrcErrPkt, tx_ip_src_error);
 
        /* Update derived statistics */
-       mac_stats->tx_good_bytes =
-               (mac_stats->tx_bytes - mac_stats->tx_bad_bytes -
-                mac_stats->tx_control * 64);
-       mac_stats->rx_bad_bytes =
-               (mac_stats->rx_bytes - mac_stats->rx_good_bytes -
-                mac_stats->rx_control * 64);
+       efx_update_diff_stat(&mac_stats->tx_good_bytes,
+                            mac_stats->tx_bytes - mac_stats->tx_bad_bytes -
+                            mac_stats->tx_control * 64);
+       efx_update_diff_stat(&mac_stats->rx_bad_bytes,
+                            mac_stats->rx_bytes - mac_stats->rx_good_bytes -
+                            mac_stats->rx_control * 64);
 }
 
 void falcon_poll_xmac(struct efx_nic *efx)
index f48ccf6bb3b961a3a8968aed0210fcb8e7d2f639..bab5cd9f5740bb8e9e7476849f089ba513346b9f 100644 (file)
@@ -294,6 +294,24 @@ extern bool falcon_xmac_check_fault(struct efx_nic *efx);
 extern int falcon_reconfigure_xmac(struct efx_nic *efx);
 extern void falcon_update_stats_xmac(struct efx_nic *efx);
 
+/* Some statistics are computed as A - B where A and B each increase
+ * linearly with some hardware counter(s) and the counters are read
+ * asynchronously.  If the counters contributing to B are always read
+ * after those contributing to A, the computed value may be lower than
+ * the true value by some variable amount, and may decrease between
+ * subsequent computations.
+ *
+ * We should never allow statistics to decrease or to exceed the true
+ * value.  Since the computed value will never be greater than the
+ * true value, we can achieve this by only storing the computed value
+ * when it increases.
+ */
+static inline void efx_update_diff_stat(u64 *stat, u64 diff)
+{
+       if ((s64)(diff - *stat) > 0)
+               *stat = diff;
+}
+
 /* Interrupts and test events */
 extern int efx_nic_init_interrupt(struct efx_nic *efx);
 extern void efx_nic_enable_interrupts(struct efx_nic *efx);
index 2354886293db7392e470d098513617f878b516d7..6bafd216e55e6eb668d12b5e636f14975ae7f110 100644 (file)
@@ -458,8 +458,8 @@ static int siena_try_update_nic_stats(struct efx_nic *efx)
 
        MAC_STAT(tx_bytes, TX_BYTES);
        MAC_STAT(tx_bad_bytes, TX_BAD_BYTES);
-       mac_stats->tx_good_bytes = (mac_stats->tx_bytes -
-                                   mac_stats->tx_bad_bytes);
+       efx_update_diff_stat(&mac_stats->tx_good_bytes,
+                            mac_stats->tx_bytes - mac_stats->tx_bad_bytes);
        MAC_STAT(tx_packets, TX_PKTS);
        MAC_STAT(tx_bad, TX_BAD_FCS_PKTS);
        MAC_STAT(tx_pause, TX_PAUSE_PKTS);
@@ -492,8 +492,8 @@ static int siena_try_update_nic_stats(struct efx_nic *efx)
        MAC_STAT(tx_ip_src_error, TX_IP_SRC_ERR_PKTS);
        MAC_STAT(rx_bytes, RX_BYTES);
        MAC_STAT(rx_bad_bytes, RX_BAD_BYTES);
-       mac_stats->rx_good_bytes = (mac_stats->rx_bytes -
-                                   mac_stats->rx_bad_bytes);
+       efx_update_diff_stat(&mac_stats->rx_good_bytes,
+                            mac_stats->rx_bytes - mac_stats->rx_bad_bytes);
        MAC_STAT(rx_packets, RX_PKTS);
        MAC_STAT(rx_good, RX_GOOD_PKTS);
        MAC_STAT(rx_bad, RX_BAD_FCS_PKTS);