i40e: Add support for 64 bit netstats
authorAlexander Duyck <alexander.h.duyck@intel.com>
Sat, 28 Sep 2013 06:01:03 +0000 (06:01 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Thu, 10 Oct 2013 06:16:27 +0000 (23:16 -0700)
This change brings support for 64 bit netstats to the driver. Previously
the stats were 64 bit but highly racy due to the fact that 64 bit
transactions are not atomic on 32 bit systems.  This change makes is so
that the 64 bit byte and packet stats are reliable on all architectures.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Tested-by: Kavindya Deegala <kavindya.s.deegala@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx.h

index 50153ea2d63c6f968c129fbd5ab5c9f1cd586ca8..1b86138fa9e19fb0a87510db8d05c5dafd347a61 100644 (file)
@@ -579,6 +579,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
        char *p;
        int j;
        struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi);
+       unsigned int start;
 
        i40e_update_stats(vsi);
 
@@ -587,12 +588,30 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat ==
                        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
+       rcu_read_lock();
        for (j = 0; j < vsi->num_queue_pairs; j++, i += 4) {
-               data[i] = vsi->tx_rings[j]->stats.packets;
-               data[i + 1] = vsi->tx_rings[j]->stats.bytes;
-               data[i + 2] = vsi->rx_rings[j]->stats.packets;
-               data[i + 3] = vsi->rx_rings[j]->stats.bytes;
+               struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[j]);
+               struct i40e_ring *rx_ring;
+
+               if (!tx_ring)
+                       continue;
+
+               /* process Tx ring statistics */
+               do {
+                       start = u64_stats_fetch_begin_bh(&tx_ring->syncp);
+                       data[i] = tx_ring->stats.packets;
+                       data[i + 1] = tx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start));
+
+               /* Rx ring is the 2nd half of the queue pair */
+               rx_ring = &tx_ring[1];
+               do {
+                       start = u64_stats_fetch_begin_bh(&rx_ring->syncp);
+                       data[i + 2] = rx_ring->stats.packets;
+                       data[i + 3] = rx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start));
        }
+       rcu_read_unlock();
        if (vsi == pf->vsi[pf->lan_vsi]) {
                for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
                        p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
index 3cf23f5ffe20a872200ad1c7f249ca8960ab49ea..24ee5d46b758fc2c41fd10962c8b1cd43608ffd0 100644 (file)
@@ -347,14 +347,53 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi)
  **/
 static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
                                             struct net_device *netdev,
-                                            struct rtnl_link_stats64 *storage)
+                                            struct rtnl_link_stats64 *stats)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
+       struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
+       int i;
+
+       rcu_read_lock();
+       for (i = 0; i < vsi->num_queue_pairs; i++) {
+               struct i40e_ring *tx_ring, *rx_ring;
+               u64 bytes, packets;
+               unsigned int start;
+
+               tx_ring = ACCESS_ONCE(vsi->tx_rings[i]);
+               if (!tx_ring)
+                       continue;
 
-       *storage = *i40e_get_vsi_stats_struct(vsi);
+               do {
+                       start = u64_stats_fetch_begin_bh(&tx_ring->syncp);
+                       packets = tx_ring->stats.packets;
+                       bytes   = tx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start));
+
+               stats->tx_packets += packets;
+               stats->tx_bytes   += bytes;
+               rx_ring = &tx_ring[1];
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&rx_ring->syncp);
+                       packets = rx_ring->stats.packets;
+                       bytes   = rx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start));
 
-       return storage;
+               stats->rx_packets += packets;
+               stats->rx_bytes   += bytes;
+       }
+       rcu_read_unlock();
+
+       /* following stats updated by ixgbe_watchdog_task() */
+       stats->multicast        = vsi_stats->multicast;
+       stats->tx_errors        = vsi_stats->tx_errors;
+       stats->tx_dropped       = vsi_stats->tx_dropped;
+       stats->rx_errors        = vsi_stats->rx_errors;
+       stats->rx_crc_errors    = vsi_stats->rx_crc_errors;
+       stats->rx_length_errors = vsi_stats->rx_length_errors;
+
+       return stats;
 }
 
 /**
@@ -708,21 +747,38 @@ void i40e_update_stats(struct i40e_vsi *vsi)
        tx_restart = tx_busy = 0;
        rx_page = 0;
        rx_buf = 0;
+       rcu_read_lock();
        for (q = 0; q < vsi->num_queue_pairs; q++) {
                struct i40e_ring *p;
+               u64 bytes, packets;
+               unsigned int start;
 
-               p = vsi->rx_rings[q];
-               rx_b += p->stats.bytes;
-               rx_p += p->stats.packets;
-               rx_buf += p->rx_stats.alloc_rx_buff_failed;
-               rx_page += p->rx_stats.alloc_rx_page_failed;
+               /* locate Tx ring */
+               p = ACCESS_ONCE(vsi->tx_rings[q]);
 
-               p = vsi->tx_rings[q];
-               tx_b += p->stats.bytes;
-               tx_p += p->stats.packets;
+               do {
+                       start = u64_stats_fetch_begin_bh(&p->syncp);
+                       packets = p->stats.packets;
+                       bytes = p->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+               tx_b += bytes;
+               tx_p += packets;
                tx_restart += p->tx_stats.restart_queue;
                tx_busy += p->tx_stats.tx_busy;
+
+               /* Rx queue is part of the same block as Tx queue */
+               p = &p[1];
+               do {
+                       start = u64_stats_fetch_begin_bh(&p->syncp);
+                       packets = p->stats.packets;
+                       bytes = p->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+               rx_b += bytes;
+               rx_p += packets;
+               rx_buf += p->rx_stats.alloc_rx_buff_failed;
+               rx_page += p->rx_stats.alloc_rx_page_failed;
        }
+       rcu_read_unlock();
        vsi->tx_restart = tx_restart;
        vsi->tx_busy = tx_busy;
        vsi->rx_page_failed = rx_page;
index 9eee551aa49e1cb8ce30a75de9fdd65944455f31..dc89e72fd0f48401ac5e5f121e9f7566fa67b479 100644 (file)
@@ -411,8 +411,10 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
 
        i += tx_ring->count;
        tx_ring->next_to_clean = i;
+       u64_stats_update_begin(&tx_ring->syncp);
        tx_ring->stats.bytes += total_bytes;
        tx_ring->stats.packets += total_packets;
+       u64_stats_update_end(&tx_ring->syncp);
        tx_ring->q_vector->tx.total_bytes += total_bytes;
        tx_ring->q_vector->tx.total_packets += total_packets;
 
@@ -1075,8 +1077,10 @@ next_desc:
        }
 
        rx_ring->next_to_clean = i;
+       u64_stats_update_begin(&rx_ring->syncp);
        rx_ring->stats.packets += total_rx_packets;
        rx_ring->stats.bytes += total_rx_bytes;
+       u64_stats_update_end(&rx_ring->syncp);
        rx_ring->q_vector->rx.total_packets += total_rx_packets;
        rx_ring->q_vector->rx.total_bytes += total_rx_bytes;
 
index 5db36c38573e15e2ec89e6791e7a3b5b6e3be580..db55d9947f151cf19c17f2f55e8ee24e69364cbd 100644 (file)
@@ -218,6 +218,7 @@ struct i40e_ring {
 
        /* stats structs */
        struct i40e_queue_stats stats;
+       struct u64_stats_sync syncp;
        union {
                struct i40e_tx_queue_stats tx_stats;
                struct i40e_rx_queue_stats rx_stats;