be2net: use stats-sync to read/write 64-bit stats
authorSathya Perla <sathya.perla@emulex.com>
Mon, 25 Jul 2011 19:10:15 +0000 (19:10 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 1 Aug 2011 07:12:00 +0000 (00:12 -0700)
64-bit stats in be2net are written/read as follows using the stats-sync
interface for safe access in 32-bit archs:

64-bit  sync writer reader
stats
------------------------------------------------------------------------------
tx_stats tx_stats->sync be_xmit be_get_stats64,
ethtool
tx-compl tx_stats->sync_compl tx-compl-processing ethtool
rx-stats rx_stats->sync rx-compl-processing be_get_stats64,
ethtool,
eqd-update

This patch is based on Stephen Hemminger's earlier patch on the same issue...

Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/benet/be.h
drivers/net/benet/be_cmds.c
drivers/net/benet/be_ethtool.c
drivers/net/benet/be_main.c

index 68227fd..af57b51 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/interrupt.h>
 #include <linux/firmware.h>
 #include <linux/slab.h>
+#include <linux/u64_stats_sync.h>
 
 #include "be_hw.h"
 
@@ -174,6 +175,8 @@ struct be_tx_stats {
        u64 tx_compl;
        ulong tx_jiffies;
        u32 tx_stops;
+       struct u64_stats_sync sync;
+       struct u64_stats_sync sync_compl;
 };
 
 struct be_tx_obj {
@@ -206,6 +209,7 @@ struct be_rx_stats {
        u32 rx_mcast_pkts;
        u32 rx_compl_err;       /* completions with err set */
        u32 rx_pps;             /* pkts per second */
+       struct u64_stats_sync sync;
 };
 
 struct be_rx_compl_info {
@@ -518,7 +522,6 @@ static inline bool be_multi_rxq(const struct be_adapter *adapter)
 extern void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm,
                u16 num_popped);
 extern void be_link_status_update(struct be_adapter *adapter, bool link_up);
-extern void netdev_stats_update(struct be_adapter *adapter);
 extern void be_parse_stats(struct be_adapter *adapter);
 extern int be_load_fw(struct be_adapter *adapter, u8 *func);
 #endif                         /* BE_H */
index e15f06a..7dc4741 100644 (file)
@@ -83,7 +83,6 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
                         (compl->tag0 == OPCODE_ETH_GET_PPORT_STATS)) &&
                        (compl->tag1 == CMD_SUBSYSTEM_ETH)) {
                        be_parse_stats(adapter);
-                       netdev_stats_update(adapter);
                        adapter->stats_cmd_sent = false;
                }
        } else {
index 0300b9d..e92a8d8 100644 (file)
@@ -74,10 +74,12 @@ static const struct be_ethtool_stat et_stats[] = {
 };
 #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats)
 
-/* Stats related to multi RX queues */
+/* Stats related to multi RX queues: get_stats routine assumes bytes, pkts
+ * are first and second members respectively.
+ */
 static const struct be_ethtool_stat et_rx_stats[] = {
-       {DRVSTAT_RX_INFO(rx_bytes)},
-       {DRVSTAT_RX_INFO(rx_pkts)},
+       {DRVSTAT_RX_INFO(rx_bytes)},/* If moving this member see above note */
+       {DRVSTAT_RX_INFO(rx_pkts)}, /* If moving this member see above note */
        {DRVSTAT_RX_INFO(rx_polls)},
        {DRVSTAT_RX_INFO(rx_events)},
        {DRVSTAT_RX_INFO(rx_compl)},
@@ -88,8 +90,11 @@ static const struct be_ethtool_stat et_rx_stats[] = {
 };
 #define ETHTOOL_RXSTATS_NUM (ARRAY_SIZE(et_rx_stats))
 
-/* Stats related to multi TX queues */
+/* Stats related to multi TX queues: get_stats routine assumes compl is the
+ * first member
+ */
 static const struct be_ethtool_stat et_tx_stats[] = {
+       {DRVSTAT_TX_INFO(tx_compl)}, /* If moving this member see above note */
        {DRVSTAT_TX_INFO(tx_bytes)},
        {DRVSTAT_TX_INFO(tx_pkts)},
        {DRVSTAT_TX_INFO(tx_reqs)},
@@ -243,32 +248,48 @@ be_get_ethtool_stats(struct net_device *netdev,
        struct be_rx_obj *rxo;
        struct be_tx_obj *txo;
        void *p;
-       int i, j, base;
+       unsigned int i, j, base = 0, start;
 
        for (i = 0; i < ETHTOOL_STATS_NUM; i++) {
                p = (u8 *)&adapter->drv_stats + et_stats[i].offset;
-               data[i] = (et_stats[i].size == sizeof(u64)) ?
-                               *(u64 *)p: *(u32 *)p;
+               data[i] = *(u32 *)p;
        }
+       base += ETHTOOL_STATS_NUM;
 
-       base = ETHTOOL_STATS_NUM;
        for_all_rx_queues(adapter, rxo, j) {
-               for (i = 0; i < ETHTOOL_RXSTATS_NUM; i++) {
-                       p = (u8 *)rx_stats(rxo) + et_rx_stats[i].offset;
-                       data[base + j * ETHTOOL_RXSTATS_NUM + i] =
-                               (et_rx_stats[i].size == sizeof(u64)) ?
-                                       *(u64 *)p: *(u32 *)p;
+               struct be_rx_stats *stats = rx_stats(rxo);
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&stats->sync);
+                       data[base] = stats->rx_bytes;
+                       data[base + 1] = stats->rx_pkts;
+               } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+
+               for (i = 2; i < ETHTOOL_RXSTATS_NUM; i++) {
+                       p = (u8 *)stats + et_rx_stats[i].offset;
+                       data[base + i] = *(u32 *)p;
                }
+               base += ETHTOOL_RXSTATS_NUM;
        }
 
-       base = ETHTOOL_STATS_NUM + adapter->num_rx_qs * ETHTOOL_RXSTATS_NUM;
        for_all_tx_queues(adapter, txo, j) {
-               for (i = 0; i < ETHTOOL_TXSTATS_NUM; i++) {
-                       p = (u8 *)tx_stats(txo) + et_tx_stats[i].offset;
-                       data[base + j * ETHTOOL_TXSTATS_NUM + i] =
-                               (et_tx_stats[i].size == sizeof(u64)) ?
-                                       *(u64 *)p: *(u32 *)p;
-               }
+               struct be_tx_stats *stats = tx_stats(txo);
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&stats->sync_compl);
+                       data[base] = stats->tx_compl;
+               } while (u64_stats_fetch_retry_bh(&stats->sync_compl, start));
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&stats->sync);
+                       for (i = 1; i < ETHTOOL_TXSTATS_NUM; i++) {
+                               p = (u8 *)stats + et_tx_stats[i].offset;
+                               data[base + i] =
+                                       (et_tx_stats[i].size == sizeof(u64)) ?
+                                               *(u64 *)p : *(u32 *)p;
+                       }
+               } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+               base += ETHTOOL_TXSTATS_NUM;
        }
 }
 
index 9cfbfdf..9f2f66c 100644 (file)
@@ -396,36 +396,44 @@ void be_parse_stats(struct be_adapter *adapter)
                        erx->rx_drops_no_fragments[rxo->q.id];
 }
 
-void netdev_stats_update(struct be_adapter *adapter)
+static struct rtnl_link_stats64 *be_get_stats64(struct net_device *netdev,
+                                       struct rtnl_link_stats64 *stats)
 {
+       struct be_adapter *adapter = netdev_priv(netdev);
        struct be_drv_stats *drvs = &adapter->drv_stats;
-       struct net_device_stats *dev_stats = &adapter->netdev->stats;
        struct be_rx_obj *rxo;
        struct be_tx_obj *txo;
-       unsigned long pkts = 0, bytes = 0, mcast = 0, drops = 0;
+       u64 pkts, bytes;
+       unsigned int start;
        int i;
 
        for_all_rx_queues(adapter, rxo, i) {
-               pkts += rx_stats(rxo)->rx_pkts;
-               bytes += rx_stats(rxo)->rx_bytes;
-               mcast += rx_stats(rxo)->rx_mcast_pkts;
-               drops += rx_stats(rxo)->rx_drops_no_skbs;
+               const struct be_rx_stats *rx_stats = rx_stats(rxo);
+               do {
+                       start = u64_stats_fetch_begin_bh(&rx_stats->sync);
+                       pkts = rx_stats(rxo)->rx_pkts;
+                       bytes = rx_stats(rxo)->rx_bytes;
+               } while (u64_stats_fetch_retry_bh(&rx_stats->sync, start));
+               stats->rx_packets += pkts;
+               stats->rx_bytes += bytes;
+               stats->multicast += rx_stats(rxo)->rx_mcast_pkts;
+               stats->rx_dropped += rx_stats(rxo)->rx_drops_no_skbs +
+                                       rx_stats(rxo)->rx_drops_no_frags;
        }
-       dev_stats->rx_packets = pkts;
-       dev_stats->rx_bytes = bytes;
-       dev_stats->multicast = mcast;
-       dev_stats->rx_dropped = drops;
 
-       pkts = bytes = 0;
        for_all_tx_queues(adapter, txo, i) {
-               pkts += tx_stats(txo)->tx_pkts;
-               bytes += tx_stats(txo)->tx_bytes;
+               const struct be_tx_stats *tx_stats = tx_stats(txo);
+               do {
+                       start = u64_stats_fetch_begin_bh(&tx_stats->sync);
+                       pkts = tx_stats(txo)->tx_pkts;
+                       bytes = tx_stats(txo)->tx_bytes;
+               } while (u64_stats_fetch_retry_bh(&tx_stats->sync, start));
+               stats->tx_packets += pkts;
+               stats->tx_bytes += bytes;
        }
-       dev_stats->tx_packets = pkts;
-       dev_stats->tx_bytes = bytes;
 
        /* bad pkts received */
-       dev_stats->rx_errors = drvs->rx_crc_errors +
+       stats->rx_errors = drvs->rx_crc_errors +
                drvs->rx_alignment_symbol_errors +
                drvs->rx_in_range_errors +
                drvs->rx_out_range_errors +
@@ -434,26 +442,24 @@ void netdev_stats_update(struct be_adapter *adapter)
                drvs->rx_dropped_too_short +
                drvs->rx_dropped_header_too_small +
                drvs->rx_dropped_tcp_length +
-               drvs->rx_dropped_runt +
-               drvs->rx_tcp_checksum_errs +
-               drvs->rx_ip_checksum_errs +
-               drvs->rx_udp_checksum_errs;
+               drvs->rx_dropped_runt;
 
        /* detailed rx errors */
-       dev_stats->rx_length_errors = drvs->rx_in_range_errors +
+       stats->rx_length_errors = drvs->rx_in_range_errors +
                drvs->rx_out_range_errors +
                drvs->rx_frame_too_long;
 
-       dev_stats->rx_crc_errors = drvs->rx_crc_errors;
+       stats->rx_crc_errors = drvs->rx_crc_errors;
 
        /* frame alignment errors */
-       dev_stats->rx_frame_errors = drvs->rx_alignment_symbol_errors;
+       stats->rx_frame_errors = drvs->rx_alignment_symbol_errors;
 
        /* receiver fifo overrun */
        /* drops_no_pbuf is no per i/f, it's per BE card */
-       dev_stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop +
+       stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop +
                                drvs->rx_input_fifo_overflow_drop +
                                drvs->rx_drops_no_pbuf;
+       return stats;
 }
 
 void be_link_status_update(struct be_adapter *adapter, bool link_up)
@@ -479,12 +485,14 @@ static void be_tx_stats_update(struct be_tx_obj *txo,
 {
        struct be_tx_stats *stats = tx_stats(txo);
 
+       u64_stats_update_begin(&stats->sync);
        stats->tx_reqs++;
        stats->tx_wrbs += wrb_cnt;
        stats->tx_bytes += copied;
        stats->tx_pkts += (gso_segs ? gso_segs : 1);
        if (stopped)
                stats->tx_stops++;
+       u64_stats_update_end(&stats->sync);
 }
 
 /* Determine number of WRB entries needed to xmit data in an skb */
@@ -905,7 +913,8 @@ static void be_rx_eqd_update(struct be_adapter *adapter, struct be_rx_obj *rxo)
        struct be_rx_stats *stats = rx_stats(rxo);
        ulong now = jiffies;
        ulong delta = now - stats->rx_jiffies;
-       u32 eqd;
+       u64 pkts;
+       unsigned int start, eqd;
 
        if (!rx_eq->enable_aic)
                return;
@@ -920,8 +929,13 @@ static void be_rx_eqd_update(struct be_adapter *adapter, struct be_rx_obj *rxo)
        if (delta < HZ)
                return;
 
-       stats->rx_pps = (stats->rx_pkts - stats->rx_pkts_prev) / (delta / HZ);
-       stats->rx_pkts_prev = stats->rx_pkts;
+       do {
+               start = u64_stats_fetch_begin_bh(&stats->sync);
+               pkts = stats->rx_pkts;
+       } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+
+       stats->rx_pps = (pkts - stats->rx_pkts_prev) / (delta / HZ);
+       stats->rx_pkts_prev = pkts;
        stats->rx_jiffies = now;
        eqd = stats->rx_pps / 110000;
        eqd = eqd << 3;
@@ -942,6 +956,7 @@ static void be_rx_stats_update(struct be_rx_obj *rxo,
 {
        struct be_rx_stats *stats = rx_stats(rxo);
 
+       u64_stats_update_begin(&stats->sync);
        stats->rx_compl++;
        stats->rx_bytes += rxcp->pkt_size;
        stats->rx_pkts++;
@@ -949,6 +964,7 @@ static void be_rx_stats_update(struct be_rx_obj *rxo,
                stats->rx_mcast_pkts++;
        if (rxcp->err)
                stats->rx_compl_err++;
+       u64_stats_update_end(&stats->sync);
 }
 
 static inline bool csum_passed(struct be_rx_compl_info *rxcp)
@@ -1878,8 +1894,9 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
                                netif_wake_subqueue(adapter->netdev, i);
                        }
 
-                       adapter->drv_stats.tx_events++;
+                       u64_stats_update_begin(&tx_stats(txo)->sync_compl);
                        tx_stats(txo)->tx_compl += tx_compl;
+                       u64_stats_update_end(&tx_stats(txo)->sync_compl);
                }
        }
 
@@ -1893,6 +1910,7 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
        napi_complete(napi);
 
        be_eq_notify(adapter, tx_eq->q.id, true, false, 0);
+       adapter->drv_stats.tx_events++;
        return 1;
 }
 
@@ -2843,6 +2861,7 @@ static struct net_device_ops be_netdev_ops = {
        .ndo_set_rx_mode        = be_set_multicast_list,
        .ndo_set_mac_address    = be_mac_addr_set,
        .ndo_change_mtu         = be_change_mtu,
+       .ndo_get_stats64        = be_get_stats64,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_vlan_rx_add_vid    = be_vlan_add_vid,
        .ndo_vlan_rx_kill_vid   = be_vlan_rem_vid,