};
 #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)},
 };
 #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)},
        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;
        }
 }
 
 
                        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 +
                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)
 {
        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 */
        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;
        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;
 {
        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++;
                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)
                                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);
                }
        }
 
        napi_complete(napi);
 
        be_eq_notify(adapter, tx_eq->q.id, true, false, 0);
+       adapter->drv_stats.tx_events++;
        return 1;
 }
 
        .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,