netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable
@ 2021-01-08 16:31 Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 01/16] net: mark dev_base_lock for deprecation Vladimir Oltean
                   ` (15 more replies)
  0 siblings, 16 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

There is a desire to standardize the counters that have previously been
reported through ethtool statistics into something that can be more
uniformly queried from user space. The ndo_get_stats64 are a good
candidate to keep standardized counters, as well as add new ones (like
RMON MIBs) but unfortunately, as things stand, migration will not be
smooth sailing if the ndo_get_stats64 does not offer the same calling
context as ethtool does. Namely, currently ndo_get_stats64 assumes
atomic context, and this series would like to change that.

The reason why sleeping while retrieving counters would be desirable in
the first place is that if we have hardware that needs to be accessed
through a slow bus like SPI, or through a firmware. Today we cannot do
that directly in .ndo_get_stats64, so we would have to poll counters
periodically and return a cached (not up to date) copy in the atomic NDO
callback. This is undesirable on both ends: more work than strictly
needed is being done, and the end result is also worse (not guaranteed
to be up to date). Also, retrieving counters from the hardware rather
than software counters incremented by the driver is more compatible with
the concept of interfaces with offload for L2 or L3 forwarding, where
the CPU otherwise never has a chance to keep accurate counters for most
of the traffic.

I could not test most of the drivers that were modified, so the only
guarantee is that make allyesconfig passes.

Changes in v5:
- Changed bonding and net_failover to use rcu_read_lock().
- Actually propagating errors from bond_get_stats now.

Changes in v4:
- Propagated errors from ndo_get_stats64.

Changes in v3:
- Resolved some memory leak issues in the bonding patch 10/12.

Changes in v2:
- Addressed the recursion issues in .ndo_get_stats64 from bonding and
  net_failover.
- Renamed netdev_lists_lock to netif_lists_lock
- Stopped taking netif_lists_lock from drivers as much as possible.

Vladimir Oltean (16):
  net: mark dev_base_lock for deprecation
  net: introduce a mutex for the netns interface lists
  net: procfs: hold netif_lists_lock when retrieving device statistics
  net: sysfs: don't hold dev_base_lock while retrieving device
    statistics
  s390/appldata_net_sum: hold the netdev lists lock when retrieving
    device statistics
  parisc/led: hold the netdev lists lock when retrieving device
    statistics
  net: remove return value from dev_get_stats
  net: allow ndo_get_stats64 to return an int error code
  scsi: fcoe: propagate errors from dev_get_stats
  net: openvswitch: propagate errors from dev_get_stats
  net: propagate errors from dev_get_stats
  net: terminate errors from dev_get_stats
  net: openvswitch: ensure dev_get_stats can sleep
  net: net_failover: ensure .ndo_get_stats64 can sleep
  net: bonding: ensure .ndo_get_stats64 can sleep
  net: mark ndo_get_stats64 as being able to sleep

 Documentation/networking/netdevices.rst       |   8 +-
 Documentation/networking/statistics.rst       |   9 +-
 arch/s390/appldata/appldata_net_sum.c         |  41 +++---
 drivers/infiniband/hw/hfi1/vnic_main.c        |   6 +-
 drivers/infiniband/ulp/ipoib/ipoib_main.c     |   9 +-
 .../infiniband/ulp/opa_vnic/opa_vnic_netdev.c |   9 +-
 drivers/leds/trigger/ledtrig-netdev.c         |  16 ++-
 drivers/net/bonding/bond_main.c               | 131 ++++++++++--------
 drivers/net/dummy.c                           |   6 +-
 drivers/net/ethernet/alacritech/slicoss.c     |   6 +-
 drivers/net/ethernet/amazon/ena/ena_netdev.c  |   8 +-
 drivers/net/ethernet/amd/xgbe/xgbe-drv.c      |   6 +-
 drivers/net/ethernet/apm/xgene-v2/main.c      |   6 +-
 .../ethernet/apm/xgene/xgene_enet_ethtool.c   |   9 +-
 .../net/ethernet/apm/xgene/xgene_enet_main.c  |   7 +-
 drivers/net/ethernet/atheros/alx/main.c       |   6 +-
 drivers/net/ethernet/broadcom/b44.c           |   6 +-
 drivers/net/ethernet/broadcom/bcmsysport.c    |   6 +-
 drivers/net/ethernet/broadcom/bnx2.c          |   5 +-
 drivers/net/ethernet/broadcom/bnxt/bnxt.c     |   6 +-
 drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c |   4 +-
 drivers/net/ethernet/broadcom/tg3.c           |   8 +-
 drivers/net/ethernet/brocade/bna/bnad.c       |   4 +-
 drivers/net/ethernet/calxeda/xgmac.c          |   4 +-
 .../net/ethernet/cavium/liquidio/lio_main.c   |   6 +-
 .../ethernet/cavium/liquidio/lio_vf_main.c    |   6 +-
 .../net/ethernet/cavium/liquidio/lio_vf_rep.c |   8 +-
 .../net/ethernet/cavium/thunder/nicvf_main.c  |   5 +-
 .../net/ethernet/chelsio/cxgb4/cxgb4_main.c   |   8 +-
 drivers/net/ethernet/cisco/enic/enic_main.c   |   8 +-
 drivers/net/ethernet/cortina/gemini.c         |   6 +-
 drivers/net/ethernet/ec_bhf.c                 |   4 +-
 drivers/net/ethernet/emulex/benet/be_main.c   |   6 +-
 .../net/ethernet/freescale/dpaa/dpaa_eth.c    |   6 +-
 .../net/ethernet/freescale/dpaa2/dpaa2-eth.c  |   6 +-
 drivers/net/ethernet/google/gve/gve_main.c    |   4 +-
 drivers/net/ethernet/hisilicon/hns/hns_enet.c |   6 +-
 .../net/ethernet/hisilicon/hns/hns_ethtool.c  |  56 ++++----
 .../net/ethernet/hisilicon/hns3/hns3_enet.c   |   8 +-
 .../net/ethernet/huawei/hinic/hinic_main.c    |   6 +-
 drivers/net/ethernet/ibm/ehea/ehea_main.c     |   6 +-
 drivers/net/ethernet/intel/e1000e/e1000.h     |   4 +-
 drivers/net/ethernet/intel/e1000e/ethtool.c   |   9 +-
 drivers/net/ethernet/intel/e1000e/netdev.c    |   6 +-
 .../net/ethernet/intel/fm10k/fm10k_netdev.c   |   6 +-
 drivers/net/ethernet/intel/i40e/i40e_main.c   |  10 +-
 drivers/net/ethernet/intel/ice/ice_main.c     |   6 +-
 drivers/net/ethernet/intel/igb/igb_main.c     |  10 +-
 drivers/net/ethernet/intel/igc/igc_main.c     |   6 +-
 .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c  |  14 +-
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |   6 +-
 drivers/net/ethernet/intel/ixgbevf/ethtool.c  |  14 +-
 .../net/ethernet/intel/ixgbevf/ixgbevf_main.c |   6 +-
 drivers/net/ethernet/marvell/mvneta.c         |   4 +-
 .../net/ethernet/marvell/mvpp2/mvpp2_main.c   |   4 +-
 .../marvell/octeontx2/nic/otx2_common.c       |   6 +-
 .../marvell/octeontx2/nic/otx2_common.h       |   4 +-
 .../ethernet/marvell/prestera/prestera_main.c |   6 +-
 drivers/net/ethernet/marvell/sky2.c           |   6 +-
 drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   6 +-
 drivers/net/ethernet/mediatek/mtk_star_emac.c |   6 +-
 .../net/ethernet/mellanox/mlx4/en_netdev.c    |   4 +-
 drivers/net/ethernet/mellanox/mlx5/core/en.h  |   2 +-
 .../net/ethernet/mellanox/mlx5/core/en_main.c |   4 +-
 .../net/ethernet/mellanox/mlx5/core/en_rep.c  |   4 +-
 .../ethernet/mellanox/mlx5/core/ipoib/ipoib.c |   4 +-
 .../ethernet/mellanox/mlx5/core/ipoib/ipoib.h |   2 +-
 .../net/ethernet/mellanox/mlxsw/spectrum.c    |   4 +-
 .../net/ethernet/mellanox/mlxsw/switchx2.c    |   4 +-
 drivers/net/ethernet/microchip/lan743x_main.c |   6 +-
 drivers/net/ethernet/mscc/ocelot_net.c        |   6 +-
 .../net/ethernet/myricom/myri10ge/myri10ge.c  |  12 +-
 .../net/ethernet/neterion/vxge/vxge-main.c    |   4 +-
 .../ethernet/netronome/nfp/nfp_net_common.c   |   6 +-
 .../net/ethernet/netronome/nfp/nfp_net_repr.c |   6 +-
 drivers/net/ethernet/nvidia/forcedeth.c       |   4 +-
 .../net/ethernet/pensando/ionic/ionic_lif.c   |   6 +-
 .../net/ethernet/pensando/ionic/ionic_lif.h   |   4 +-
 .../ethernet/qlogic/netxen/netxen_nic_main.c  |  10 +-
 drivers/net/ethernet/qlogic/qede/qede_main.c  |   6 +-
 drivers/net/ethernet/qualcomm/emac/emac.c     |   6 +-
 .../net/ethernet/qualcomm/rmnet/rmnet_vnd.c   |   8 +-
 drivers/net/ethernet/realtek/8139too.c        |   8 +-
 drivers/net/ethernet/realtek/r8169_main.c     |   4 +-
 .../net/ethernet/samsung/sxgbe/sxgbe_main.c   |   6 +-
 drivers/net/ethernet/sfc/efx_common.c         |   4 +-
 drivers/net/ethernet/sfc/efx_common.h         |   2 +-
 drivers/net/ethernet/sfc/falcon/efx.c         |   6 +-
 drivers/net/ethernet/socionext/sni_ave.c      |   6 +-
 drivers/net/ethernet/sun/niu.c                |   6 +-
 .../net/ethernet/synopsys/dwc-xlgmac-net.c    |   6 +-
 drivers/net/ethernet/ti/am65-cpsw-nuss.c      |   6 +-
 drivers/net/ethernet/ti/netcp_core.c          |   4 +-
 drivers/net/ethernet/via/via-rhine.c          |   8 +-
 drivers/net/fjes/fjes_main.c                  |   6 +-
 drivers/net/hyperv/netvsc_drv.c               |   6 +-
 drivers/net/ifb.c                             |   5 +-
 drivers/net/ipvlan/ipvlan_main.c              |   6 +-
 drivers/net/loopback.c                        |   6 +-
 drivers/net/macsec.c                          |   8 +-
 drivers/net/macvlan.c                         |   6 +-
 drivers/net/mhi_net.c                         |   6 +-
 drivers/net/net_failover.c                    |  96 +++++++++----
 drivers/net/netdevsim/netdev.c                |   4 +-
 drivers/net/nlmon.c                           |   4 +-
 drivers/net/ppp/ppp_generic.c                 |   4 +-
 drivers/net/slip/slip.c                       |   4 +-
 drivers/net/team/team.c                       |   4 +-
 drivers/net/thunderbolt.c                     |   6 +-
 drivers/net/tun.c                             |   4 +-
 drivers/net/veth.c                            |   6 +-
 drivers/net/virtio_net.c                      |   6 +-
 drivers/net/vmxnet3/vmxnet3_ethtool.c         |   4 +-
 drivers/net/vmxnet3/vmxnet3_int.h             |   4 +-
 drivers/net/vrf.c                             |   6 +-
 drivers/net/vsockmon.c                        |   4 +-
 drivers/net/xen-netfront.c                    |   6 +-
 drivers/parisc/led.c                          |  42 +++---
 drivers/s390/net/qeth_core.h                  |   2 +-
 drivers/s390/net/qeth_core_main.c             |   4 +-
 drivers/scsi/fcoe/fcoe_sysfs.c                |   9 +-
 drivers/scsi/fcoe/fcoe_transport.c            |  28 ++--
 drivers/scsi/libfc/fc_rport.c                 |   5 +-
 drivers/staging/fsl-dpaa2/ethsw/ethsw.c       |   8 +-
 drivers/staging/netlogic/xlr_net.c            |   4 +-
 drivers/usb/gadget/function/rndis.c           |  47 +++----
 include/linux/netdevice.h                     |  22 ++-
 include/net/bonding.h                         |  54 ++++++++
 include/net/net_namespace.h                   |   6 +
 include/scsi/fcoe_sysfs.h                     |  12 +-
 include/scsi/libfc.h                          |   2 +-
 include/scsi/libfcoe.h                        |   8 +-
 net/8021q/vlan_dev.c                          |   6 +-
 net/8021q/vlanproc.c                          |  20 +--
 net/core/dev.c                                |  79 +++++++----
 net/core/net-procfs.c                         |  62 +++++----
 net/core/net-sysfs.c                          |  12 +-
 net/core/rtnetlink.c                          |  15 +-
 net/l2tp/l2tp_eth.c                           |   6 +-
 net/mac80211/iface.c                          |   4 +-
 net/openvswitch/datapath.c                    |  63 +++++----
 net/openvswitch/vport.c                       |  35 +++--
 net/openvswitch/vport.h                       |   2 +-
 net/sched/sch_teql.c                          |   6 +-
 144 files changed, 1009 insertions(+), 563 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 01/16] net: mark dev_base_lock for deprecation
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 02/16] net: introduce a mutex for the netns interface lists Vladimir Oltean
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

There is a movement to eliminate the usage of dev_base_lock, which
exists since as far as I could track the kernel history down (the
"7a2deb329241 Import changeset" commit from the bitkeeper branch).

The dev_base_lock approach has multiple issues:
- It is global and not per netns.
- Its meaning has mutated over the years and the vast majority of
  current users is using it to protect against changes of network device
  state, as opposed to changes of the interface list.
- It is overlapping with other protection mechanisms introduced in the
  meantime, which have higher performance for readers, like RCU
  introduced in 2009 by Eric Dumazet for kernel 2.6.

The old comment that I just deleted made this distinction: writers
protect only against readers by holding dev_base_lock, whereas they need
to protect against other writers by holding the RTNL mutex. This is
slightly confusing/incorrect, since a rwlock_t cannot have more than one
concurrently running writer, so that explanation does not justify why
the RTNL mutex would be necessary.

Instead, Stephen Hemminger makes this clarification here:
https://lore.kernel.org/netdev/20201129211230.4d704931@hermes.local/T/#u

| There are really two different domains being covered by locks here.
|
| The first area is change of state of network devices. This has traditionally
| been covered by RTNL because there are places that depend on coordinating
| state between multiple devices. RTNL is too big and held too long but getting
| rid of it is hard because there are corner cases (like state changes from userspace
| for VPN devices).
|
| The other area is code that wants to do read access to look at list of devices.
| These pure readers can/should be converted to RCU by now. Writers should hold RTNL.

This patch edits the comment for dev_base_lock, minimizing its role in
the protection of network interface lists, and clarifies that it is has
other purposes as well.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
None.

 net/core/dev.c | 33 ++++++++++++++++-----------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 7afbb642e203..8e02240bb11c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -169,23 +169,22 @@ static int call_netdevice_notifiers_extack(unsigned long val,
 static struct napi_struct *napi_by_id(unsigned int napi_id);
 
 /*
- * The @dev_base_head list is protected by @dev_base_lock and the rtnl
- * semaphore.
- *
- * Pure readers hold dev_base_lock for reading, or rcu_read_lock()
- *
- * Writers must hold the rtnl semaphore while they loop through the
- * dev_base_head list, and hold dev_base_lock for writing when they do the
- * actual updates.  This allows pure readers to access the list even
- * while a writer is preparing to update it.
- *
- * To put it another way, dev_base_lock is held for writing only to
- * protect against pure readers; the rtnl semaphore provides the
- * protection against other writers.
- *
- * See, for example usages, register_netdevice() and
- * unregister_netdevice(), which must be called with the rtnl
- * semaphore held.
+ * The network interface list of a netns (@net->dev_base_head) and the hash
+ * tables per ifindex (@net->dev_index_head) and per name (@net->dev_name_head)
+ * are protected using the following rules:
+ *
+ * Pure readers should hold rcu_read_lock() which should protect them against
+ * concurrent changes to the interface lists made by the writers. Pure writers
+ * must serialize by holding the RTNL mutex while they loop through the list
+ * and make changes to it.
+ *
+ * It is also possible to hold the global rwlock_t @dev_base_lock for
+ * protection (holding its read side as an alternative to rcu_read_lock, and
+ * its write side as an alternative to the RTNL mutex), however this should not
+ * be done in new code, since it is deprecated and pending removal.
+ *
+ * One other role of @dev_base_lock is to protect against changes in the
+ * operational state of a network interface.
  */
 DEFINE_RWLOCK(dev_base_lock);
 EXPORT_SYMBOL(dev_base_lock);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 02/16] net: introduce a mutex for the netns interface lists
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 01/16] net: mark dev_base_lock for deprecation Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 03/16] net: procfs: hold netif_lists_lock when retrieving device statistics Vladimir Oltean
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Currently, any writer that wants to alter the lists of network
interfaces (either the plain list net->dev_base_head, or the hash tables
net->dev_index_head and net->dev_name_head) can keep other writers at
bay using the RTNL mutex.

However, the RTNL mutex has become a very contended resource over the
years, so there is a movement to do finer grained locking.

This patch adds one more way for writers to the network interface lists
to serialize themselves. We assume that all writers to the network
interface lists are easily identifiable because the write side of
dev_base_lock also needs to be held (note that some instances of that
were deliberately skipped, since they only dealt with protecting the
operational state of the netdev).

Holding the RTNL mutex is now optional for new code that alters the
lists, since all relevant writers were made to also hold the new lock.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
None.

Changes in v3:
None.

 include/linux/netdevice.h   | 10 +++++++++
 include/net/net_namespace.h |  6 +++++
 net/core/dev.c              | 44 +++++++++++++++++++++++++------------
 3 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 1ec3ac5d5bbf..8aae2386bd37 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4376,6 +4376,16 @@ static inline void netif_addr_unlock_bh(struct net_device *dev)
 	spin_unlock_bh(&dev->addr_list_lock);
 }
 
+static inline void netif_lists_lock(struct net *net)
+{
+	mutex_lock(&net->netif_lists_lock);
+}
+
+static inline void netif_lists_unlock(struct net *net)
+{
+	mutex_unlock(&net->netif_lists_lock);
+}
+
 /*
  * dev_addrs walker. Should be used only for read access. Call with
  * rcu_read_lock held.
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 29567875f428..cac64c3c7ce0 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -183,6 +183,12 @@ struct net {
 	struct sock		*crypto_nlsk;
 #endif
 	struct sock		*diag_nlsk;
+
+	/* Serializes writers to @dev_base_head, @dev_name_head and
+	 * @dev_index_head. It does _not_ protect the netif adjacency lists
+	 * (struct net_device::adj_list).
+	 */
+	struct mutex		netif_lists_lock;
 } __randomize_layout;
 
 #include <linux/seq_file_net.h>
diff --git a/net/core/dev.c b/net/core/dev.c
index 8e02240bb11c..53c12f92025c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -175,13 +175,16 @@ static struct napi_struct *napi_by_id(unsigned int napi_id);
  *
  * Pure readers should hold rcu_read_lock() which should protect them against
  * concurrent changes to the interface lists made by the writers. Pure writers
- * must serialize by holding the RTNL mutex while they loop through the list
- * and make changes to it.
+ * must serialize by holding the @net->netif_lists_lock mutex while they loop
+ * through the list and make changes to it.
+ *
+ * It is possible to hold the RTNL mutex for serializing the writers too, but
+ * this should be avoided in new code due to lock contention.
  *
  * It is also possible to hold the global rwlock_t @dev_base_lock for
  * protection (holding its read side as an alternative to rcu_read_lock, and
- * its write side as an alternative to the RTNL mutex), however this should not
- * be done in new code, since it is deprecated and pending removal.
+ * its write side as an alternative to @net->netif_lists_lock), however this
+ * should not be done in new code, since it is deprecated and pending removal.
  *
  * One other role of @dev_base_lock is to protect against changes in the
  * operational state of a network interface.
@@ -360,12 +363,14 @@ static void list_netdevice(struct net_device *dev)
 
 	ASSERT_RTNL();
 
+	netif_lists_lock(net);
 	write_lock_bh(&dev_base_lock);
 	list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
 	netdev_name_node_add(net, dev->name_node);
 	hlist_add_head_rcu(&dev->index_hlist,
 			   dev_index_hash(net, dev->ifindex));
 	write_unlock_bh(&dev_base_lock);
+	netif_lists_unlock(net);
 
 	dev_base_seq_inc(net);
 }
@@ -375,16 +380,20 @@ static void list_netdevice(struct net_device *dev)
  */
 static void unlist_netdevice(struct net_device *dev)
 {
+	struct net *net = dev_net(dev);
+
 	ASSERT_RTNL();
 
 	/* Unlink dev from the device chain */
+	netif_lists_lock(net);
 	write_lock_bh(&dev_base_lock);
 	list_del_rcu(&dev->dev_list);
 	netdev_name_node_del(dev->name_node);
 	hlist_del_rcu(&dev->index_hlist);
 	write_unlock_bh(&dev_base_lock);
+	netif_lists_unlock(net);
 
-	dev_base_seq_inc(dev_net(dev));
+	dev_base_seq_inc(net);
 }
 
 /*
@@ -850,11 +859,11 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
  *	@net: the applicable net namespace
  *	@name: name to find
  *
- *	Find an interface by name. Must be called under RTNL semaphore
- *	or @dev_base_lock. If the name is found a pointer to the device
- *	is returned. If the name is not found then %NULL is returned. The
- *	reference counters are not incremented so the caller must be
- *	careful with locks.
+ *	Find an interface by name. Must be called under RTNL semaphore,
+ *	@net->netif_lists_lock or @dev_base_lock. If the name is found,
+ *	a pointer to the device is returned. If the name is not found then
+ *	%NULL is returned. The reference counters are not incremented so the
+ *	caller must be careful with locks.
  */
 
 struct net_device *__dev_get_by_name(struct net *net, const char *name)
@@ -920,8 +929,8 @@ EXPORT_SYMBOL(dev_get_by_name);
  *	Search for an interface by index. Returns %NULL if the device
  *	is not found or a pointer to the device. The device has not
  *	had its reference counter increased so the caller must be careful
- *	about locking. The caller must hold either the RTNL semaphore
- *	or @dev_base_lock.
+ *	about locking. The caller must hold either the RTNL semaphore,
+ *	@net->netif_lists_lock or @dev_base_lock.
  */
 
 struct net_device *__dev_get_by_index(struct net *net, int ifindex)
@@ -1330,15 +1339,19 @@ int dev_change_name(struct net_device *dev, const char *newname)
 
 	netdev_adjacent_rename_links(dev, oldname);
 
+	netif_lists_lock(net);
 	write_lock_bh(&dev_base_lock);
 	netdev_name_node_del(dev->name_node);
 	write_unlock_bh(&dev_base_lock);
+	netif_lists_unlock(net);
 
 	synchronize_rcu();
 
+	netif_lists_lock(net);
 	write_lock_bh(&dev_base_lock);
 	netdev_name_node_add(net, dev->name_node);
 	write_unlock_bh(&dev_base_lock);
+	netif_lists_unlock(net);
 
 	ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
 	ret = notifier_to_errno(ret);
@@ -9415,8 +9428,9 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
  *	@net: the applicable net namespace
  *
  *	Returns a suitable unique value for a new device interface
- *	number.  The caller must hold the rtnl semaphore or the
- *	dev_base_lock to be sure it remains unique.
+ *	number.
+ *	The caller must hold the rtnl semaphore, @net->netif_lists_lock or the
+ *	@dev_base_lock to be sure it remains unique.
  */
 static int dev_new_index(struct net *net)
 {
@@ -10999,6 +11013,8 @@ static int __net_init netdev_init(struct net *net)
 	if (net->dev_index_head == NULL)
 		goto err_idx;
 
+	mutex_init(&net->netif_lists_lock);
+
 	RAW_INIT_NOTIFIER_HEAD(&net->netdev_chain);
 
 	return 0;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 03/16] net: procfs: hold netif_lists_lock when retrieving device statistics
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 01/16] net: mark dev_base_lock for deprecation Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 02/16] net: introduce a mutex for the netns interface lists Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 04/16] net: sysfs: don't hold dev_base_lock while " Vladimir Oltean
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

In the effort of making .ndo_get_stats64 be able to sleep, we need to
ensure the callers of dev_get_stats do not use atomic context.

The /proc/net/dev file uses an RCU read-side critical section to ensure
the integrity of the list of network interfaces, because it iterates
through all net devices in the netns to show their statistics.

To offer the equivalent protection against an interface registering or
deregistering, while also remaining in sleepable context, we can use the
netns mutex for the interface lists.

Cc: Cong Wang <xiyou.wangcong@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
None.

 net/core/net-procfs.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index c714e6a9dad4..4784703c1e39 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -21,7 +21,7 @@ static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff
 	unsigned int count = 0, offset = get_offset(*pos);
 
 	h = &net->dev_index_head[get_bucket(*pos)];
-	hlist_for_each_entry_rcu(dev, h, index_hlist) {
+	hlist_for_each_entry(dev, h, index_hlist) {
 		if (++count == offset)
 			return dev;
 	}
@@ -51,9 +51,11 @@ static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *p
  *	in detail.
  */
 static void *dev_seq_start(struct seq_file *seq, loff_t *pos)
-	__acquires(RCU)
 {
-	rcu_read_lock();
+	struct net *net = seq_file_net(seq);
+
+	netif_lists_lock(net);
+
 	if (!*pos)
 		return SEQ_START_TOKEN;
 
@@ -70,9 +72,10 @@ static void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 }
 
 static void dev_seq_stop(struct seq_file *seq, void *v)
-	__releases(RCU)
 {
-	rcu_read_unlock();
+	struct net *net = seq_file_net(seq);
+
+	netif_lists_unlock(net);
 }
 
 static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 04/16] net: sysfs: don't hold dev_base_lock while retrieving device statistics
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (2 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 03/16] net: procfs: hold netif_lists_lock when retrieving device statistics Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 05/16] s390/appldata_net_sum: hold the netdev lists lock when " Vladimir Oltean
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

In the effort of making .ndo_get_stats64 be able to sleep, we need to
ensure the callers of dev_get_stats do not use atomic context.

I need to preface this by saying that I have no idea why netstat_show
takes the dev_base_lock rwlock. Two things can be observed:
(a) it does not appear to be due to dev_isalive requiring it for some
    reason, because broadcast_show() also calls dev_isalive() and has
    had no problem existing since the beginning of git.
(b) the dev_get_stats function definitely does not need dev_base_lock
    protection either. In fact, holding the dev_base_lock is the entire
    problem here, because we want to make dev_get_stats sleepable, and
    holding a rwlock gives us atomic context.

So since no protection seems to be necessary, just run unlocked while
retrieving the /sys/class/net/eth0/statistics/* values.

Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
None.

 net/core/net-sysfs.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index daf502c13d6d..8604183678fc 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -585,14 +585,13 @@ static ssize_t netstat_show(const struct device *d,
 	WARN_ON(offset > sizeof(struct rtnl_link_stats64) ||
 		offset % sizeof(u64) != 0);
 
-	read_lock(&dev_base_lock);
 	if (dev_isalive(dev)) {
 		struct rtnl_link_stats64 temp;
 		const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
 
 		ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *)stats) + offset));
 	}
-	read_unlock(&dev_base_lock);
+
 	return ret;
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 05/16] s390/appldata_net_sum: hold the netdev lists lock when retrieving device statistics
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (3 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 04/16] net: sysfs: don't hold dev_base_lock while " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 06/16] parisc/led: " Vladimir Oltean
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

In the effort of making .ndo_get_stats64 be able to sleep, we need to
ensure the callers of dev_get_stats do not use atomic context.

In the case of the appldata driver, an RCU read-side critical section is
used to ensure the integrity of the list of network interfaces, because
the driver iterates through all net devices in the netns to aggregate
statistics. We still need some protection against an interface
registering or deregistering, and the writer-side lock, the netns's
mutex, is fine for that, because it offers sleepable context.

The ops->callback function is called from under appldata_ops_mutex
protection, so this is proof that the context is sleepable and holding
a mutex is therefore fine.

Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: linux-s390@vger.kernel.org
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
None.

 arch/s390/appldata/appldata_net_sum.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c
index 59c282ca002f..4db886980cba 100644
--- a/arch/s390/appldata/appldata_net_sum.c
+++ b/arch/s390/appldata/appldata_net_sum.c
@@ -78,8 +78,9 @@ static void appldata_get_net_sum_data(void *data)
 	tx_dropped = 0;
 	collisions = 0;
 
-	rcu_read_lock();
-	for_each_netdev_rcu(&init_net, dev) {
+	netif_lists_lock(&init_net);
+
+	for_each_netdev(&init_net, dev) {
 		const struct rtnl_link_stats64 *stats;
 		struct rtnl_link_stats64 temp;
 
@@ -95,7 +96,8 @@ static void appldata_get_net_sum_data(void *data)
 		collisions += stats->collisions;
 		i++;
 	}
-	rcu_read_unlock();
+
+	netif_lists_unlock(&init_net);
 
 	net_data->nr_interfaces = i;
 	net_data->rx_packets = rx_packets;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 06/16] parisc/led: hold the netdev lists lock when retrieving device statistics
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (4 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 05/16] s390/appldata_net_sum: hold the netdev lists lock when " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 07/16] net: remove return value from dev_get_stats Vladimir Oltean
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

In the effort of making .ndo_get_stats64 be able to sleep, we need to
ensure the callers of dev_get_stats do not use atomic context.

The LED driver for HP-PARISC workstations uses a workqueue to
periodically check for updates in network interface statistics, and
flicker when those have changed (i.e. there has been activity on the
line). Ignoring the fact that this driver is completely duplicating
drivers/leds/trigger/ledtrig-netdev.c, there is an even bigger problem.
Now, the dev_get_stats call can sleep, and iterating through the list of
network interfaces still needs to ensure the integrity of list of
network interfaces. So that leaves us only one locking option given the
current design of the network stack, and that is the netns mutex.

This patch also reindents the code that gathers device statistics, since
the standard in the Linux kernel is to use one tab character per
indentation level.

Cc: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
Cc: Helge Deller <deller@gmx.de>
Cc: linux-parisc@vger.kernel.org
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
Squashed with the reindenting of the code.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
None.

 drivers/parisc/led.c | 38 ++++++++++++++++++++++----------------
 1 file changed, 22 insertions(+), 16 deletions(-)

diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index 36c6613f7a36..c8c6b2301dc9 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -38,7 +38,6 @@
 #include <linux/ctype.h>
 #include <linux/blkdev.h>
 #include <linux/workqueue.h>
-#include <linux/rcupdate.h>
 #include <asm/io.h>
 #include <asm/processor.h>
 #include <asm/hardware.h>
@@ -355,22 +354,29 @@ static __inline__ int led_get_net_activity(void)
 	int retval;
 
 	rx_total = tx_total = 0;
-	
-	/* we are running as a workqueue task, so we can use an RCU lookup */
-	rcu_read_lock();
-	for_each_netdev_rcu(&init_net, dev) {
-	    const struct rtnl_link_stats64 *stats;
-	    struct rtnl_link_stats64 temp;
-	    struct in_device *in_dev = __in_dev_get_rcu(dev);
-	    if (!in_dev || !in_dev->ifa_list)
-		continue;
-	    if (ipv4_is_loopback(in_dev->ifa_list->ifa_local))
-		continue;
-	    stats = dev_get_stats(dev, &temp);
-	    rx_total += stats->rx_packets;
-	    tx_total += stats->tx_packets;
+
+	/* we are running as a workqueue task, so we can sleep */
+	netif_lists_lock(&init_net);
+
+	for_each_netdev(&init_net, dev) {
+		struct in_device *in_dev = in_dev_get(dev);
+		const struct rtnl_link_stats64 *stats;
+		struct rtnl_link_stats64 temp;
+
+		if (!in_dev || !in_dev->ifa_list ||
+		    ipv4_is_loopback(in_dev->ifa_list->ifa_local)) {
+			in_dev_put(in_dev);
+			continue;
+		}
+
+		in_dev_put(in_dev);
+
+		stats = dev_get_stats(dev, &temp);
+		rx_total += stats->rx_packets;
+		tx_total += stats->tx_packets;
 	}
-	rcu_read_unlock();
+
+	netif_lists_unlock(&init_net);
 
 	retval = 0;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 07/16] net: remove return value from dev_get_stats
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (5 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 06/16] parisc/led: " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 08/16] net: allow ndo_get_stats64 to return an int error code Vladimir Oltean
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

After commit 28172739f0a2 ("net: fix 64 bit counters on 32 bit arches"),
dev_get_stats got an additional argument for storage of statistics. At
this point, dev_get_stats could return either the passed "storage"
argument, or the output of .ndo_get_stats64.

Then commit caf586e5f23c ("net: add a core netdev->rx_dropped counter")
came, and the output of .ndo_get_stats64 (still returning a pointer to
struct rtnl_link_stats64) started being ignored.

Then came commit bc1f44709cf2 ("net: make ndo_get_stats64 a void
function") which made .ndo_get_stats64 stop returning anything.

So now, dev_get_stats always reports the "storage" pointer received as
argument. This is useless. Some drivers are dealing with unnecessary
complexity due to this, using another pointer to poke around the
returned statistics, when they can do that directly through the
stack-allocated struct rtnl_link_stats64.

Refactor these callers to ignore the return value completely and just
access the values from their struct rtnl_link_stats64 local variable.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
- Renamed "stats" to "dev_stats" in fcoe_transport.c.
- Slightly reworded the commit title and message.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
Patch is new.

 arch/s390/appldata/appldata_net_sum.c         | 25 +++++----
 drivers/leds/trigger/ledtrig-netdev.c         |  9 ++--
 drivers/net/bonding/bond_main.c               |  7 ++-
 .../net/ethernet/hisilicon/hns/hns_ethtool.c  | 51 +++++++++----------
 .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c  |  7 ++-
 drivers/net/ethernet/intel/ixgbevf/ethtool.c  |  7 ++-
 drivers/net/net_failover.c                    | 13 +++--
 drivers/parisc/led.c                          |  9 ++--
 drivers/scsi/fcoe/fcoe_transport.c            |  6 +--
 drivers/usb/gadget/function/rndis.c           | 45 ++++++----------
 include/linux/netdevice.h                     |  3 +-
 net/8021q/vlanproc.c                          | 15 +++---
 net/core/dev.c                                |  6 +--
 net/core/net-procfs.c                         | 35 ++++++-------
 net/core/net-sysfs.c                          |  7 +--
 net/openvswitch/vport.c                       | 25 +++++----
 16 files changed, 123 insertions(+), 147 deletions(-)

diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c
index 4db886980cba..6146606ac9a3 100644
--- a/arch/s390/appldata/appldata_net_sum.c
+++ b/arch/s390/appldata/appldata_net_sum.c
@@ -81,19 +81,18 @@ static void appldata_get_net_sum_data(void *data)
 	netif_lists_lock(&init_net);
 
 	for_each_netdev(&init_net, dev) {
-		const struct rtnl_link_stats64 *stats;
-		struct rtnl_link_stats64 temp;
-
-		stats = dev_get_stats(dev, &temp);
-		rx_packets += stats->rx_packets;
-		tx_packets += stats->tx_packets;
-		rx_bytes   += stats->rx_bytes;
-		tx_bytes   += stats->tx_bytes;
-		rx_errors  += stats->rx_errors;
-		tx_errors  += stats->tx_errors;
-		rx_dropped += stats->rx_dropped;
-		tx_dropped += stats->tx_dropped;
-		collisions += stats->collisions;
+		struct rtnl_link_stats64 stats;
+
+		dev_get_stats(dev, &stats);
+		rx_packets += stats.rx_packets;
+		tx_packets += stats.tx_packets;
+		rx_bytes   += stats.rx_bytes;
+		tx_bytes   += stats.tx_bytes;
+		rx_errors  += stats.rx_errors;
+		tx_errors  += stats.tx_errors;
+		rx_dropped += stats.rx_dropped;
+		tx_dropped += stats.tx_dropped;
+		collisions += stats.collisions;
 		i++;
 	}
 
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index d5e774d83021..4382ee278309 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -347,9 +347,8 @@ static void netdev_trig_work(struct work_struct *work)
 {
 	struct led_netdev_data *trigger_data =
 		container_of(work, struct led_netdev_data, work.work);
-	struct rtnl_link_stats64 *dev_stats;
+	struct rtnl_link_stats64 dev_stats;
 	unsigned int new_activity;
-	struct rtnl_link_stats64 temp;
 	unsigned long interval;
 	int invert;
 
@@ -364,12 +363,12 @@ static void netdev_trig_work(struct work_struct *work)
 	    !test_bit(NETDEV_LED_RX, &trigger_data->mode))
 		return;
 
-	dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
+	dev_get_stats(trigger_data->net_dev, &dev_stats);
 	new_activity =
 	    (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
-		dev_stats->tx_packets : 0) +
+		dev_stats.tx_packets : 0) +
 	    (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
-		dev_stats->rx_packets : 0);
+		dev_stats.rx_packets : 0);
 
 	if (trigger_data->last_activity != new_activity) {
 		led_stop_software_blink(trigger_data->led_cdev);
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 5fe5232cc3f3..714aa0e5d041 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3753,13 +3753,12 @@ static void bond_get_stats(struct net_device *bond_dev,
 	memcpy(stats, &bond->bond_stats, sizeof(*stats));
 
 	bond_for_each_slave_rcu(bond, slave, iter) {
-		const struct rtnl_link_stats64 *new =
-			dev_get_stats(slave->dev, &temp);
+		dev_get_stats(slave->dev, &temp);
 
-		bond_fold_stats(stats, new, &slave->slave_stats);
+		bond_fold_stats(stats, &temp, &slave->slave_stats);
 
 		/* save off the slave stats for the next run */
-		memcpy(&slave->slave_stats, new, sizeof(*new));
+		memcpy(&slave->slave_stats, &temp, sizeof(temp));
 	}
 
 	memcpy(&bond->bond_stats, stats, sizeof(*stats));
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index a6e3f07caf99..ee2172011051 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -839,8 +839,7 @@ static void hns_get_ethtool_stats(struct net_device *netdev,
 	u64 *p = data;
 	struct hns_nic_priv *priv = netdev_priv(netdev);
 	struct hnae_handle *h = priv->ae_handle;
-	const struct rtnl_link_stats64 *net_stats;
-	struct rtnl_link_stats64 temp;
+	struct rtnl_link_stats64 net_stats;
 
 	if (!h->dev->ops->get_stats || !h->dev->ops->update_stats) {
 		netdev_err(netdev, "get_stats or update_stats is null!\n");
@@ -849,32 +848,32 @@ static void hns_get_ethtool_stats(struct net_device *netdev,
 
 	h->dev->ops->update_stats(h, &netdev->stats);
 
-	net_stats = dev_get_stats(netdev, &temp);
+	dev_get_stats(netdev, &net_stats);
 
 	/* get netdev statistics */
-	p[0] = net_stats->rx_packets;
-	p[1] = net_stats->tx_packets;
-	p[2] = net_stats->rx_bytes;
-	p[3] = net_stats->tx_bytes;
-	p[4] = net_stats->rx_errors;
-	p[5] = net_stats->tx_errors;
-	p[6] = net_stats->rx_dropped;
-	p[7] = net_stats->tx_dropped;
-	p[8] = net_stats->multicast;
-	p[9] = net_stats->collisions;
-	p[10] = net_stats->rx_over_errors;
-	p[11] = net_stats->rx_crc_errors;
-	p[12] = net_stats->rx_frame_errors;
-	p[13] = net_stats->rx_fifo_errors;
-	p[14] = net_stats->rx_missed_errors;
-	p[15] = net_stats->tx_aborted_errors;
-	p[16] = net_stats->tx_carrier_errors;
-	p[17] = net_stats->tx_fifo_errors;
-	p[18] = net_stats->tx_heartbeat_errors;
-	p[19] = net_stats->rx_length_errors;
-	p[20] = net_stats->tx_window_errors;
-	p[21] = net_stats->rx_compressed;
-	p[22] = net_stats->tx_compressed;
+	p[0] = net_stats.rx_packets;
+	p[1] = net_stats.tx_packets;
+	p[2] = net_stats.rx_bytes;
+	p[3] = net_stats.tx_bytes;
+	p[4] = net_stats.rx_errors;
+	p[5] = net_stats.tx_errors;
+	p[6] = net_stats.rx_dropped;
+	p[7] = net_stats.tx_dropped;
+	p[8] = net_stats.multicast;
+	p[9] = net_stats.collisions;
+	p[10] = net_stats.rx_over_errors;
+	p[11] = net_stats.rx_crc_errors;
+	p[12] = net_stats.rx_frame_errors;
+	p[13] = net_stats.rx_fifo_errors;
+	p[14] = net_stats.rx_missed_errors;
+	p[15] = net_stats.tx_aborted_errors;
+	p[16] = net_stats.tx_carrier_errors;
+	p[17] = net_stats.tx_fifo_errors;
+	p[18] = net_stats.tx_heartbeat_errors;
+	p[19] = net_stats.rx_length_errors;
+	p[20] = net_stats.tx_window_errors;
+	p[21] = net_stats.rx_compressed;
+	p[22] = net_stats.tx_compressed;
 
 	p[23] = netdev->rx_dropped.counter;
 	p[24] = netdev->tx_dropped.counter;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index a280aa34ca1d..2b8084664403 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -1295,19 +1295,18 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
 				    struct ethtool_stats *stats, u64 *data)
 {
 	struct ixgbe_adapter *adapter = netdev_priv(netdev);
-	struct rtnl_link_stats64 temp;
-	const struct rtnl_link_stats64 *net_stats;
+	struct rtnl_link_stats64 net_stats;
 	unsigned int start;
 	struct ixgbe_ring *ring;
 	int i, j;
 	char *p = NULL;
 
 	ixgbe_update_stats(adapter);
-	net_stats = dev_get_stats(netdev, &temp);
+	dev_get_stats(netdev, &net_stats);
 	for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
 		switch (ixgbe_gstrings_stats[i].type) {
 		case NETDEV_STATS:
-			p = (char *) net_stats +
+			p = (char *) &net_stats +
 					ixgbe_gstrings_stats[i].stat_offset;
 			break;
 		case IXGBE_STATS:
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index e49fb1cd9a99..3b9b7e5c2998 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -420,19 +420,18 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev,
 				      struct ethtool_stats *stats, u64 *data)
 {
 	struct ixgbevf_adapter *adapter = netdev_priv(netdev);
-	struct rtnl_link_stats64 temp;
-	const struct rtnl_link_stats64 *net_stats;
+	struct rtnl_link_stats64 net_stats;
 	unsigned int start;
 	struct ixgbevf_ring *ring;
 	int i, j;
 	char *p;
 
 	ixgbevf_update_stats(adapter);
-	net_stats = dev_get_stats(netdev, &temp);
+	dev_get_stats(netdev, &net_stats);
 	for (i = 0; i < IXGBEVF_GLOBAL_STATS_LEN; i++) {
 		switch (ixgbevf_gstrings_stats[i].type) {
 		case NETDEV_STATS:
-			p = (char *)net_stats +
+			p = (char *)&net_stats +
 					ixgbevf_gstrings_stats[i].stat_offset;
 			break;
 		case IXGBEVF_STATS:
diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index 2a4892402ed8..4f83165412bd 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -183,7 +183,6 @@ static void net_failover_get_stats(struct net_device *dev,
 				   struct rtnl_link_stats64 *stats)
 {
 	struct net_failover_info *nfo_info = netdev_priv(dev);
-	const struct rtnl_link_stats64 *new;
 	struct rtnl_link_stats64 temp;
 	struct net_device *slave_dev;
 
@@ -194,16 +193,16 @@ static void net_failover_get_stats(struct net_device *dev,
 
 	slave_dev = rcu_dereference(nfo_info->primary_dev);
 	if (slave_dev) {
-		new = dev_get_stats(slave_dev, &temp);
-		net_failover_fold_stats(stats, new, &nfo_info->primary_stats);
-		memcpy(&nfo_info->primary_stats, new, sizeof(*new));
+		dev_get_stats(slave_dev, &temp);
+		net_failover_fold_stats(stats, &temp, &nfo_info->primary_stats);
+		memcpy(&nfo_info->primary_stats, &temp, sizeof(temp));
 	}
 
 	slave_dev = rcu_dereference(nfo_info->standby_dev);
 	if (slave_dev) {
-		new = dev_get_stats(slave_dev, &temp);
-		net_failover_fold_stats(stats, new, &nfo_info->standby_stats);
-		memcpy(&nfo_info->standby_stats, new, sizeof(*new));
+		dev_get_stats(slave_dev, &temp);
+		net_failover_fold_stats(stats, &temp, &nfo_info->standby_stats);
+		memcpy(&nfo_info->standby_stats, &temp, sizeof(temp));
 	}
 
 	rcu_read_unlock();
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index c8c6b2301dc9..cc6108785323 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -360,8 +360,7 @@ static __inline__ int led_get_net_activity(void)
 
 	for_each_netdev(&init_net, dev) {
 		struct in_device *in_dev = in_dev_get(dev);
-		const struct rtnl_link_stats64 *stats;
-		struct rtnl_link_stats64 temp;
+		struct rtnl_link_stats64 stats;
 
 		if (!in_dev || !in_dev->ifa_list ||
 		    ipv4_is_loopback(in_dev->ifa_list->ifa_local)) {
@@ -371,9 +370,9 @@ static __inline__ int led_get_net_activity(void)
 
 		in_dev_put(in_dev);
 
-		stats = dev_get_stats(dev, &temp);
-		rx_total += stats->rx_packets;
-		tx_total += stats->tx_packets;
+		dev_get_stats(dev, &stats);
+		rx_total += stats.rx_packets;
+		tx_total += stats.tx_packets;
 	}
 
 	netif_lists_unlock(&init_net);
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index b927b3d84523..213ee9efb044 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -170,11 +170,11 @@ void __fcoe_get_lesb(struct fc_lport *lport,
 		     struct fc_els_lesb *fc_lesb,
 		     struct net_device *netdev)
 {
+	struct rtnl_link_stats64 dev_stats;
 	unsigned int cpu;
 	u32 lfc, vlfc, mdac;
 	struct fc_stats *stats;
 	struct fcoe_fc_els_lesb *lesb;
-	struct rtnl_link_stats64 temp;
 
 	lfc = 0;
 	vlfc = 0;
@@ -190,8 +190,8 @@ void __fcoe_get_lesb(struct fc_lport *lport,
 	lesb->lesb_link_fail = htonl(lfc);
 	lesb->lesb_vlink_fail = htonl(vlfc);
 	lesb->lesb_miss_fka = htonl(mdac);
-	lesb->lesb_fcs_error =
-			htonl(dev_get_stats(netdev, &temp)->rx_crc_errors);
+	dev_get_stats(netdev, &dev_stats);
+	lesb->lesb_fcs_error = htonl(dev_stats.rx_crc_errors);
 }
 EXPORT_SYMBOL_GPL(__fcoe_get_lesb);
 
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 64de9f1b874c..7ec29e007ae9 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -169,14 +169,13 @@ static const u32 oid_supported_list[] = {
 static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
 			       unsigned buf_len, rndis_resp_t *r)
 {
+	struct rtnl_link_stats64 stats;
 	int retval = -ENOTSUPP;
 	u32 length = 4;	/* usually */
 	__le32 *outbuf;
 	int i, count;
 	rndis_query_cmplt_type *resp;
 	struct net_device *net;
-	struct rtnl_link_stats64 temp;
-	const struct rtnl_link_stats64 *stats;
 
 	if (!r) return -ENOMEM;
 	resp = (rndis_query_cmplt_type *)r->buf;
@@ -199,7 +198,7 @@ static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
 	resp->InformationBufferOffset = cpu_to_le32(16);
 
 	net = params->dev;
-	stats = dev_get_stats(net, &temp);
+	dev_get_stats(net, &stats);
 
 	switch (OID) {
 
@@ -353,51 +352,41 @@ static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
 	case RNDIS_OID_GEN_XMIT_OK:
 		if (rndis_debug > 1)
 			pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__);
-		if (stats) {
-			*outbuf = cpu_to_le32(stats->tx_packets
-				- stats->tx_errors - stats->tx_dropped);
-			retval = 0;
-		}
+		*outbuf = cpu_to_le32(stats.tx_packets - stats.tx_errors -
+				      stats.tx_dropped);
+		retval = 0;
 		break;
 
 	/* mandatory */
 	case RNDIS_OID_GEN_RCV_OK:
 		if (rndis_debug > 1)
 			pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__);
-		if (stats) {
-			*outbuf = cpu_to_le32(stats->rx_packets
-				- stats->rx_errors - stats->rx_dropped);
-			retval = 0;
-		}
+		*outbuf = cpu_to_le32(stats.rx_packets - stats.rx_errors -
+				      stats.rx_dropped);
+		retval = 0;
 		break;
 
 	/* mandatory */
 	case RNDIS_OID_GEN_XMIT_ERROR:
 		if (rndis_debug > 1)
 			pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__);
-		if (stats) {
-			*outbuf = cpu_to_le32(stats->tx_errors);
-			retval = 0;
-		}
+		*outbuf = cpu_to_le32(stats.tx_errors);
+		retval = 0;
 		break;
 
 	/* mandatory */
 	case RNDIS_OID_GEN_RCV_ERROR:
 		if (rndis_debug > 1)
 			pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__);
-		if (stats) {
-			*outbuf = cpu_to_le32(stats->rx_errors);
-			retval = 0;
-		}
+		*outbuf = cpu_to_le32(stats.rx_errors);
+		retval = 0;
 		break;
 
 	/* mandatory */
 	case RNDIS_OID_GEN_RCV_NO_BUFFER:
 		pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__);
-		if (stats) {
-			*outbuf = cpu_to_le32(stats->rx_dropped);
-			retval = 0;
-		}
+		*outbuf = cpu_to_le32(stats.rx_dropped);
+		retval = 0;
 		break;
 
 	/* ieee802.3 OIDs (table 4-3) */
@@ -449,10 +438,8 @@ static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
 	/* mandatory */
 	case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT:
 		pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
-		if (stats) {
-			*outbuf = cpu_to_le32(stats->rx_frame_errors);
-			retval = 0;
-		}
+		*outbuf = cpu_to_le32(stats.rx_frame_errors);
+		retval = 0;
 		break;
 
 	/* mandatory */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 8aae2386bd37..9faaacbfe45a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4545,8 +4545,7 @@ void netdev_notify_peers(struct net_device *dev);
 void netdev_features_change(struct net_device *dev);
 /* Load a device via the kmod */
 void dev_load(struct net *net, const char *name);
-struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
-					struct rtnl_link_stats64 *storage);
+void dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage);
 void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
 			     const struct net_device_stats *netdev_stats);
 void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c
index ec87dea23719..3a6682d79630 100644
--- a/net/8021q/vlanproc.c
+++ b/net/8021q/vlanproc.c
@@ -242,26 +242,25 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset)
 {
 	struct net_device *vlandev = (struct net_device *) seq->private;
 	const struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
-	struct rtnl_link_stats64 temp;
-	const struct rtnl_link_stats64 *stats;
 	static const char fmt64[] = "%30s %12llu\n";
+	struct rtnl_link_stats64 stats;
 	int i;
 
 	if (!is_vlan_dev(vlandev))
 		return 0;
 
-	stats = dev_get_stats(vlandev, &temp);
+	dev_get_stats(vlandev, &stats);
 	seq_printf(seq,
 		   "%s  VID: %d	 REORDER_HDR: %i  dev->priv_flags: %hx\n",
 		   vlandev->name, vlan->vlan_id,
 		   (int)(vlan->flags & 1), vlandev->priv_flags);
 
-	seq_printf(seq, fmt64, "total frames received", stats->rx_packets);
-	seq_printf(seq, fmt64, "total bytes received", stats->rx_bytes);
-	seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats->multicast);
+	seq_printf(seq, fmt64, "total frames received", stats.rx_packets);
+	seq_printf(seq, fmt64, "total bytes received", stats.rx_bytes);
+	seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats.multicast);
 	seq_puts(seq, "\n");
-	seq_printf(seq, fmt64, "total frames transmitted", stats->tx_packets);
-	seq_printf(seq, fmt64, "total bytes transmitted", stats->tx_bytes);
+	seq_printf(seq, fmt64, "total frames transmitted", stats.tx_packets);
+	seq_printf(seq, fmt64, "total bytes transmitted", stats.tx_bytes);
 	seq_printf(seq, "Device: %s", vlan->real_dev->name);
 	/* now show all PRIORITY mappings relating to this VLAN */
 	seq_printf(seq, "\nINGRESS priority mappings: "
diff --git a/net/core/dev.c b/net/core/dev.c
index 53c12f92025c..2b423d7a39e7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -10398,13 +10398,12 @@ EXPORT_SYMBOL(netdev_stats_to_stats64);
  *	@dev: device to get statistics from
  *	@storage: place to store stats
  *
- *	Get network statistics from device. Return @storage.
+ *	Get network statistics from device.
  *	The device driver may provide its own method by setting
  *	dev->netdev_ops->get_stats64 or dev->netdev_ops->get_stats;
  *	otherwise the internal statistics structure is used.
  */
-struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
-					struct rtnl_link_stats64 *storage)
+void dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
 
@@ -10419,7 +10418,6 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
 	storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped);
 	storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped);
 	storage->rx_nohandler += (unsigned long)atomic_long_read(&dev->rx_nohandler);
-	return storage;
 }
 EXPORT_SYMBOL(dev_get_stats);
 
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index 4784703c1e39..64666ba7ccab 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -80,26 +80,27 @@ static void dev_seq_stop(struct seq_file *seq, void *v)
 
 static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
 {
-	struct rtnl_link_stats64 temp;
-	const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
+	struct rtnl_link_stats64 stats;
+
+	dev_get_stats(dev, &stats);
 
 	seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
 		   "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
-		   dev->name, stats->rx_bytes, stats->rx_packets,
-		   stats->rx_errors,
-		   stats->rx_dropped + stats->rx_missed_errors,
-		   stats->rx_fifo_errors,
-		   stats->rx_length_errors + stats->rx_over_errors +
-		    stats->rx_crc_errors + stats->rx_frame_errors,
-		   stats->rx_compressed, stats->multicast,
-		   stats->tx_bytes, stats->tx_packets,
-		   stats->tx_errors, stats->tx_dropped,
-		   stats->tx_fifo_errors, stats->collisions,
-		   stats->tx_carrier_errors +
-		    stats->tx_aborted_errors +
-		    stats->tx_window_errors +
-		    stats->tx_heartbeat_errors,
-		   stats->tx_compressed);
+		   dev->name, stats.rx_bytes, stats.rx_packets,
+		   stats.rx_errors,
+		   stats.rx_dropped + stats.rx_missed_errors,
+		   stats.rx_fifo_errors,
+		   stats.rx_length_errors + stats.rx_over_errors +
+		    stats.rx_crc_errors + stats.rx_frame_errors,
+		   stats.rx_compressed, stats.multicast,
+		   stats.tx_bytes, stats.tx_packets,
+		   stats.tx_errors, stats.tx_dropped,
+		   stats.tx_fifo_errors, stats.collisions,
+		   stats.tx_carrier_errors +
+		    stats.tx_aborted_errors +
+		    stats.tx_window_errors +
+		    stats.tx_heartbeat_errors,
+		   stats.tx_compressed);
 }
 
 /*
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 8604183678fc..5d89c85b42d4 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -586,10 +586,11 @@ static ssize_t netstat_show(const struct device *d,
 		offset % sizeof(u64) != 0);
 
 	if (dev_isalive(dev)) {
-		struct rtnl_link_stats64 temp;
-		const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
+		struct rtnl_link_stats64 stats;
 
-		ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *)stats) + offset));
+		dev_get_stats(dev, &stats);
+
+		ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *)&stats) + offset));
 	}
 
 	return ret;
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 4ed7e52c7012..215a818bf9ce 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -269,19 +269,18 @@ void ovs_vport_del(struct vport *vport)
  */
 void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
 {
-	const struct rtnl_link_stats64 *dev_stats;
-	struct rtnl_link_stats64 temp;
-
-	dev_stats = dev_get_stats(vport->dev, &temp);
-	stats->rx_errors  = dev_stats->rx_errors;
-	stats->tx_errors  = dev_stats->tx_errors;
-	stats->tx_dropped = dev_stats->tx_dropped;
-	stats->rx_dropped = dev_stats->rx_dropped;
-
-	stats->rx_bytes	  = dev_stats->rx_bytes;
-	stats->rx_packets = dev_stats->rx_packets;
-	stats->tx_bytes	  = dev_stats->tx_bytes;
-	stats->tx_packets = dev_stats->tx_packets;
+	struct rtnl_link_stats64 dev_stats;
+
+	dev_get_stats(vport->dev, &dev_stats);
+	stats->rx_errors  = dev_stats.rx_errors;
+	stats->tx_errors  = dev_stats.tx_errors;
+	stats->tx_dropped = dev_stats.tx_dropped;
+	stats->rx_dropped = dev_stats.rx_dropped;
+
+	stats->rx_bytes	  = dev_stats.rx_bytes;
+	stats->rx_packets = dev_stats.rx_packets;
+	stats->tx_bytes	  = dev_stats.tx_bytes;
+	stats->tx_packets = dev_stats.tx_packets;
 }
 
 /**
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 08/16] net: allow ndo_get_stats64 to return an int error code
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (6 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 07/16] net: remove return value from dev_get_stats Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 09/16] scsi: fcoe: propagate errors from dev_get_stats Vladimir Oltean
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Some drivers need to do special tricks to comply with the new policy of
ndo_get_stats64 being sleepable. For example, the bonding driver, which
derives its stats from its lower interfaces, must recurse with
dev_get_stats into its lowers with no locks held. But for that to work,
it needs to dynamically allocate some memory for a refcounted copy of
its array of slave interfaces (because recursing unlocked means that the
original one is subject to disappearing). And since memory allocation
can fail under pressure, we should not let it go unnoticed, but instead
propagate the error code.

This patch converts all implementations of .ndo_get_stats64 to return
int, and propagates that to the dev_get_stats calling site. Error
checking will be done in further patches.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
Patch is new (Eric's suggestion).

 drivers/infiniband/hw/hfi1/vnic_main.c               |  6 ++++--
 drivers/infiniband/ulp/ipoib/ipoib_main.c            |  9 ++++++---
 drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c    |  9 ++++++---
 drivers/net/bonding/bond_main.c                      | 11 +++++++----
 drivers/net/dummy.c                                  |  6 ++++--
 drivers/net/ethernet/alacritech/slicoss.c            |  6 ++++--
 drivers/net/ethernet/amazon/ena/ena_netdev.c         |  8 +++++---
 drivers/net/ethernet/amd/xgbe/xgbe-drv.c             |  6 ++++--
 drivers/net/ethernet/apm/xgene-v2/main.c             |  6 ++++--
 drivers/net/ethernet/apm/xgene/xgene_enet_main.c     |  7 ++++---
 drivers/net/ethernet/atheros/alx/main.c              |  6 ++++--
 drivers/net/ethernet/broadcom/b44.c                  |  6 ++++--
 drivers/net/ethernet/broadcom/bcmsysport.c           |  6 ++++--
 drivers/net/ethernet/broadcom/bnx2.c                 |  5 +++--
 drivers/net/ethernet/broadcom/bnxt/bnxt.c            |  6 ++++--
 drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c        |  4 +++-
 drivers/net/ethernet/broadcom/tg3.c                  |  8 +++++---
 drivers/net/ethernet/brocade/bna/bnad.c              |  4 +++-
 drivers/net/ethernet/calxeda/xgmac.c                 |  4 +++-
 drivers/net/ethernet/cavium/liquidio/lio_main.c      |  6 ++++--
 drivers/net/ethernet/cavium/liquidio/lio_vf_main.c   |  6 ++++--
 drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c    |  8 +++++---
 drivers/net/ethernet/cavium/thunder/nicvf_main.c     |  5 +++--
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c      |  8 +++++---
 drivers/net/ethernet/cisco/enic/enic_main.c          |  8 +++++---
 drivers/net/ethernet/cortina/gemini.c                |  6 ++++--
 drivers/net/ethernet/ec_bhf.c                        |  4 +++-
 drivers/net/ethernet/emulex/benet/be_main.c          |  6 ++++--
 drivers/net/ethernet/freescale/dpaa/dpaa_eth.c       |  6 ++++--
 drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c     |  6 ++++--
 drivers/net/ethernet/google/gve/gve_main.c           |  4 +++-
 drivers/net/ethernet/hisilicon/hns/hns_enet.c        |  6 ++++--
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.c      |  8 +++++---
 drivers/net/ethernet/huawei/hinic/hinic_main.c       |  6 ++++--
 drivers/net/ethernet/ibm/ehea/ehea_main.c            |  6 ++++--
 drivers/net/ethernet/intel/e1000e/e1000.h            |  4 ++--
 drivers/net/ethernet/intel/e1000e/netdev.c           |  6 ++++--
 drivers/net/ethernet/intel/fm10k/fm10k_netdev.c      |  6 ++++--
 drivers/net/ethernet/intel/i40e/i40e_main.c          | 10 ++++++----
 drivers/net/ethernet/intel/ice/ice_main.c            |  6 ++++--
 drivers/net/ethernet/intel/igb/igb_main.c            | 10 ++++++----
 drivers/net/ethernet/intel/igc/igc_main.c            |  6 ++++--
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c        |  6 ++++--
 drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c    |  6 ++++--
 drivers/net/ethernet/marvell/mvneta.c                |  4 +++-
 drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c      |  4 +++-
 .../net/ethernet/marvell/octeontx2/nic/otx2_common.c |  6 ++++--
 .../net/ethernet/marvell/octeontx2/nic/otx2_common.h |  4 ++--
 .../net/ethernet/marvell/prestera/prestera_main.c    |  6 ++++--
 drivers/net/ethernet/marvell/sky2.c                  |  6 ++++--
 drivers/net/ethernet/mediatek/mtk_eth_soc.c          |  6 ++++--
 drivers/net/ethernet/mediatek/mtk_star_emac.c        |  6 ++++--
 drivers/net/ethernet/mellanox/mlx4/en_netdev.c       |  4 +++-
 drivers/net/ethernet/mellanox/mlx5/core/en.h         |  2 +-
 drivers/net/ethernet/mellanox/mlx5/core/en_main.c    |  4 +++-
 drivers/net/ethernet/mellanox/mlx5/core/en_rep.c     |  4 +++-
 .../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c    |  4 +++-
 .../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h    |  2 +-
 drivers/net/ethernet/mellanox/mlxsw/spectrum.c       |  4 +++-
 drivers/net/ethernet/mellanox/mlxsw/switchx2.c       |  4 +++-
 drivers/net/ethernet/microchip/lan743x_main.c        |  6 ++++--
 drivers/net/ethernet/mscc/ocelot_net.c               |  6 ++++--
 drivers/net/ethernet/myricom/myri10ge/myri10ge.c     | 12 +++++++-----
 drivers/net/ethernet/neterion/vxge/vxge-main.c       |  4 +++-
 drivers/net/ethernet/netronome/nfp/nfp_net_common.c  |  6 ++++--
 drivers/net/ethernet/netronome/nfp/nfp_net_repr.c    |  6 ++++--
 drivers/net/ethernet/nvidia/forcedeth.c              |  4 +++-
 drivers/net/ethernet/pensando/ionic/ionic_lif.c      |  6 ++++--
 drivers/net/ethernet/pensando/ionic/ionic_lif.h      |  4 ++--
 drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c | 10 ++++++----
 drivers/net/ethernet/qlogic/qede/qede_main.c         |  6 ++++--
 drivers/net/ethernet/qualcomm/emac/emac.c            |  6 ++++--
 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c      |  8 +++++---
 drivers/net/ethernet/realtek/8139too.c               |  8 +++++---
 drivers/net/ethernet/realtek/r8169_main.c            |  4 +++-
 drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c      |  6 ++++--
 drivers/net/ethernet/sfc/efx_common.c                |  4 +++-
 drivers/net/ethernet/sfc/efx_common.h                |  2 +-
 drivers/net/ethernet/sfc/falcon/efx.c                |  6 ++++--
 drivers/net/ethernet/socionext/sni_ave.c             |  6 ++++--
 drivers/net/ethernet/sun/niu.c                       |  6 ++++--
 drivers/net/ethernet/synopsys/dwc-xlgmac-net.c       |  6 ++++--
 drivers/net/ethernet/ti/am65-cpsw-nuss.c             |  6 ++++--
 drivers/net/ethernet/ti/netcp_core.c                 |  4 +++-
 drivers/net/ethernet/via/via-rhine.c                 |  8 +++++---
 drivers/net/fjes/fjes_main.c                         |  6 ++++--
 drivers/net/hyperv/netvsc_drv.c                      |  6 ++++--
 drivers/net/ifb.c                                    |  5 +++--
 drivers/net/ipvlan/ipvlan_main.c                     |  6 ++++--
 drivers/net/loopback.c                               |  6 ++++--
 drivers/net/macsec.c                                 |  8 +++++---
 drivers/net/macvlan.c                                |  6 ++++--
 drivers/net/mhi_net.c                                |  6 ++++--
 drivers/net/net_failover.c                           |  6 ++++--
 drivers/net/netdevsim/netdev.c                       |  4 +++-
 drivers/net/nlmon.c                                  |  4 +++-
 drivers/net/ppp/ppp_generic.c                        |  4 +++-
 drivers/net/slip/slip.c                              |  4 +++-
 drivers/net/team/team.c                              |  4 +++-
 drivers/net/thunderbolt.c                            |  6 ++++--
 drivers/net/tun.c                                    |  4 +++-
 drivers/net/veth.c                                   |  6 ++++--
 drivers/net/virtio_net.c                             |  6 ++++--
 drivers/net/vmxnet3/vmxnet3_ethtool.c                |  4 +++-
 drivers/net/vmxnet3/vmxnet3_int.h                    |  4 ++--
 drivers/net/vrf.c                                    |  6 ++++--
 drivers/net/vsockmon.c                               |  4 +++-
 drivers/net/xen-netfront.c                           |  6 ++++--
 drivers/s390/net/qeth_core.h                         |  2 +-
 drivers/s390/net/qeth_core_main.c                    |  4 +++-
 drivers/staging/fsl-dpaa2/ethsw/ethsw.c              |  8 +++-----
 drivers/staging/netlogic/xlr_net.c                   |  4 +++-
 include/linux/netdevice.h                            | 10 +++++-----
 net/8021q/vlan_dev.c                                 |  6 ++++--
 net/core/dev.c                                       | 11 ++++++++---
 net/l2tp/l2tp_eth.c                                  |  6 ++++--
 net/mac80211/iface.c                                 |  4 +++-
 net/sched/sch_teql.c                                 |  6 ++++--
 118 files changed, 450 insertions(+), 234 deletions(-)

diff --git a/drivers/infiniband/hw/hfi1/vnic_main.c b/drivers/infiniband/hw/hfi1/vnic_main.c
index a90824de0f57..bf0b5c9d8e3b 100644
--- a/drivers/infiniband/hw/hfi1/vnic_main.c
+++ b/drivers/infiniband/hw/hfi1/vnic_main.c
@@ -220,13 +220,15 @@ static void hfi1_vnic_update_rx_counters(struct hfi1_vnic_vport_info *vinfo,
 }
 
 /* This function is overloaded for opa_vnic specific implementation */
-static void hfi1_vnic_get_stats64(struct net_device *netdev,
-				  struct rtnl_link_stats64 *stats)
+static int hfi1_vnic_get_stats64(struct net_device *netdev,
+				 struct rtnl_link_stats64 *stats)
 {
 	struct opa_vnic_stats *vstats = (struct opa_vnic_stats *)stats;
 	struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev);
 
 	hfi1_vnic_update_stats(vinfo, vstats);
+
+	return 0;
 }
 
 static u64 create_bypass_pbc(u32 vl, u32 dw_len)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index a6f413491321..ce8f333deaa4 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -268,15 +268,18 @@ static int ipoib_change_mtu(struct net_device *dev, int new_mtu)
 	return ret;
 }
 
-static void ipoib_get_stats(struct net_device *dev,
-			    struct rtnl_link_stats64 *stats)
+static int ipoib_get_stats(struct net_device *dev,
+			   struct rtnl_link_stats64 *stats)
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
+	int err = 0;
 
 	if (priv->rn_ops->ndo_get_stats64)
-		priv->rn_ops->ndo_get_stats64(dev, stats);
+		err = priv->rn_ops->ndo_get_stats64(dev, stats);
 	else
 		netdev_stats_to_stats64(stats, &dev->stats);
+
+	return err;
 }
 
 /* Called with an RCU read lock taken */
diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c
index aeff68f582d3..4b0ca1707b9d 100644
--- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c
+++ b/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c
@@ -62,17 +62,20 @@
 			ALIGN((OPA_VNIC_HDR_LEN + OPA_VNIC_SKB_MDATA_LEN), 8)
 
 /* This function is overloaded for opa_vnic specific implementation */
-static void opa_vnic_get_stats64(struct net_device *netdev,
-				 struct rtnl_link_stats64 *stats)
+static int opa_vnic_get_stats64(struct net_device *netdev,
+				struct rtnl_link_stats64 *stats)
 {
 	struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev);
 	struct opa_vnic_stats vstats;
+	int err;
 
 	memset(&vstats, 0, sizeof(vstats));
 	spin_lock(&adapter->stats_lock);
-	adapter->rn_ops->ndo_get_stats64(netdev, &vstats.netstats);
+	err = adapter->rn_ops->ndo_get_stats64(netdev, &vstats.netstats);
 	spin_unlock(&adapter->stats_lock);
 	memcpy(stats, &vstats.netstats, sizeof(*stats));
+
+	return err;
 }
 
 /* opa_netdev_start_xmit - transmit function */
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 714aa0e5d041..5a3178b3dba3 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -252,8 +252,8 @@ static struct flow_dissector flow_keys_bonding __read_mostly;
 
 static int bond_init(struct net_device *bond_dev);
 static void bond_uninit(struct net_device *bond_dev);
-static void bond_get_stats(struct net_device *bond_dev,
-			   struct rtnl_link_stats64 *stats);
+static int bond_get_stats(struct net_device *bond_dev,
+			  struct rtnl_link_stats64 *stats);
 static void bond_slave_arr_handler(struct work_struct *work);
 static bool bond_time_in_interval(struct bonding *bond, unsigned long last_act,
 				  int mod);
@@ -2070,6 +2070,7 @@ static int __bond_release_one(struct net_device *bond_dev,
 	struct sockaddr_storage ss;
 	int old_flags = bond_dev->flags;
 	netdev_features_t old_features = bond_dev->features;
+	int err;
 
 	/* slave is not a slave or master is not master of this slave */
 	if (!(slave_dev->flags & IFF_SLAVE) ||
@@ -3734,8 +3735,8 @@ static int bond_get_lowest_level_rcu(struct net_device *dev)
 }
 #endif
 
-static void bond_get_stats(struct net_device *bond_dev,
-			   struct rtnl_link_stats64 *stats)
+static int bond_get_stats(struct net_device *bond_dev,
+			  struct rtnl_link_stats64 *stats)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
 	struct rtnl_link_stats64 temp;
@@ -3764,6 +3765,8 @@ static void bond_get_stats(struct net_device *bond_dev,
 	memcpy(&bond->bond_stats, stats, sizeof(*stats));
 	spin_unlock(&bond->stats_lock);
 	rcu_read_unlock();
+
+	return 0;
 }
 
 static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index f82ad7419508..97b6b5f7e43f 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -50,10 +50,12 @@ static void set_multicast_list(struct net_device *dev)
 {
 }
 
-static void dummy_get_stats64(struct net_device *dev,
-			      struct rtnl_link_stats64 *stats)
+static int dummy_get_stats64(struct net_device *dev,
+			     struct rtnl_link_stats64 *stats)
 {
 	dev_lstats_read(dev, &stats->tx_packets, &stats->tx_bytes);
+
+	return 0;
 }
 
 static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/net/ethernet/alacritech/slicoss.c b/drivers/net/ethernet/alacritech/slicoss.c
index 696517eae77f..c68a025e17da 100644
--- a/drivers/net/ethernet/alacritech/slicoss.c
+++ b/drivers/net/ethernet/alacritech/slicoss.c
@@ -1457,8 +1457,8 @@ static netdev_tx_t slic_xmit(struct sk_buff *skb, struct net_device *dev)
 	return NETDEV_TX_OK;
 }
 
-static void slic_get_stats(struct net_device *dev,
-			   struct rtnl_link_stats64 *lst)
+static int slic_get_stats(struct net_device *dev,
+			  struct rtnl_link_stats64 *lst)
 {
 	struct slic_device *sdev = netdev_priv(dev);
 	struct slic_stats *stats = &sdev->stats;
@@ -1475,6 +1475,8 @@ static void slic_get_stats(struct net_device *dev,
 	SLIC_GET_STATS_COUNTER(lst->rx_crc_errors, stats, rx_crc);
 	SLIC_GET_STATS_COUNTER(lst->rx_fifo_errors, stats, rx_oflow802);
 	SLIC_GET_STATS_COUNTER(lst->tx_carrier_errors, stats, tx_carrier);
+
+	return 0;
 }
 
 static int slic_get_sset_count(struct net_device *dev, int sset)
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index 06596fa1f9fe..a6c52f07025a 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -3229,8 +3229,8 @@ int ena_update_hw_stats(struct ena_adapter *adapter)
 	return 0;
 }
 
-static void ena_get_stats64(struct net_device *netdev,
-			    struct rtnl_link_stats64 *stats)
+static int ena_get_stats64(struct net_device *netdev,
+			   struct rtnl_link_stats64 *stats)
 {
 	struct ena_adapter *adapter = netdev_priv(netdev);
 	struct ena_ring *rx_ring, *tx_ring;
@@ -3240,7 +3240,7 @@ static void ena_get_stats64(struct net_device *netdev,
 	int i;
 
 	if (!test_bit(ENA_FLAG_DEV_UP, &adapter->flags))
-		return;
+		return 0;
 
 	for (i = 0; i < adapter->num_io_queues; i++) {
 		u64 bytes, packets;
@@ -3289,6 +3289,8 @@ static void ena_get_stats64(struct net_device *netdev,
 
 	stats->rx_errors = 0;
 	stats->tx_errors = 0;
+
+	return 0;
 }
 
 static const struct net_device_ops ena_netdev_ops = {
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 99b6d5a9f1d9..4b3b8b573c69 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -2074,8 +2074,8 @@ static void xgbe_tx_timeout(struct net_device *netdev, unsigned int txqueue)
 	schedule_work(&pdata->restart_work);
 }
 
-static void xgbe_get_stats64(struct net_device *netdev,
-			     struct rtnl_link_stats64 *s)
+static int xgbe_get_stats64(struct net_device *netdev,
+			    struct rtnl_link_stats64 *s)
 {
 	struct xgbe_prv_data *pdata = netdev_priv(netdev);
 	struct xgbe_mmc_stats *pstats = &pdata->mmc_stats;
@@ -2101,6 +2101,8 @@ static void xgbe_get_stats64(struct net_device *netdev,
 	s->tx_dropped = netdev->stats.tx_dropped;
 
 	DBGPR("<--%s\n", __func__);
+
+	return 0;
 }
 
 static int xgbe_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c
index 860c18fb7aae..6b72b488c987 100644
--- a/drivers/net/ethernet/apm/xgene-v2/main.c
+++ b/drivers/net/ethernet/apm/xgene-v2/main.c
@@ -606,8 +606,8 @@ static void xge_timeout(struct net_device *ndev, unsigned int txqueue)
 	rtnl_unlock();
 }
 
-static void xge_get_stats64(struct net_device *ndev,
-			    struct rtnl_link_stats64 *storage)
+static int xge_get_stats64(struct net_device *ndev,
+			   struct rtnl_link_stats64 *storage)
 {
 	struct xge_pdata *pdata = netdev_priv(ndev);
 	struct xge_stats *stats = &pdata->stats;
@@ -618,6 +618,8 @@ static void xge_get_stats64(struct net_device *ndev,
 	storage->rx_packets += stats->rx_packets;
 	storage->rx_bytes += stats->rx_bytes;
 	storage->rx_errors += stats->rx_errors;
+
+	return 0;
 }
 
 static const struct net_device_ops xgene_ndev_ops = {
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
index 5f1fc6582d74..8567b85a8bb8 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
@@ -1465,9 +1465,8 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
 	return ret;
 }
 
-static void xgene_enet_get_stats64(
-			struct net_device *ndev,
-			struct rtnl_link_stats64 *stats)
+static int xgene_enet_get_stats64(struct net_device *ndev,
+				  struct rtnl_link_stats64 *stats)
 {
 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 	struct xgene_enet_desc_ring *ring;
@@ -1500,6 +1499,8 @@ static void xgene_enet_get_stats64(
 			stats->rx_fifo_errors += ring->rx_fifo_errors;
 		}
 	}
+
+	return 0;
 }
 
 static int xgene_enet_set_mac_address(struct net_device *ndev, void *addr)
diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c
index 9b7f1af5f574..b15ac4a2e94a 100644
--- a/drivers/net/ethernet/atheros/alx/main.c
+++ b/drivers/net/ethernet/atheros/alx/main.c
@@ -1624,8 +1624,8 @@ static void alx_poll_controller(struct net_device *netdev)
 }
 #endif
 
-static void alx_get_stats64(struct net_device *dev,
-			    struct rtnl_link_stats64 *net_stats)
+static int alx_get_stats64(struct net_device *dev,
+			   struct rtnl_link_stats64 *net_stats)
 {
 	struct alx_priv *alx = netdev_priv(dev);
 	struct alx_hw_stats *hw_stats = &alx->hw.stats;
@@ -1669,6 +1669,8 @@ static void alx_get_stats64(struct net_device *dev,
 	net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors;
 
 	spin_unlock(&alx->stats_lock);
+
+	return 0;
 }
 
 static const struct net_device_ops alx_netdev_ops = {
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index b455b60a5434..03e53b6bb7c9 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -1670,8 +1670,8 @@ static int b44_close(struct net_device *dev)
 	return 0;
 }
 
-static void b44_get_stats64(struct net_device *dev,
-			    struct rtnl_link_stats64 *nstat)
+static int b44_get_stats64(struct net_device *dev,
+			   struct rtnl_link_stats64 *nstat)
 {
 	struct b44 *bp = netdev_priv(dev);
 	struct b44_hw_stats *hwstat = &bp->hw_stats;
@@ -1714,6 +1714,8 @@ static void b44_get_stats64(struct net_device *dev,
 #endif
 	} while (u64_stats_fetch_retry_irq(&hwstat->syncp, start));
 
+	return 0;
+
 }
 
 static int __b44_load_mcast(struct b44 *bp, struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index a8b20441ca7c..8b2b77ece3c7 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -1859,8 +1859,8 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p)
 	return 0;
 }
 
-static void bcm_sysport_get_stats64(struct net_device *dev,
-				    struct rtnl_link_stats64 *stats)
+static int bcm_sysport_get_stats64(struct net_device *dev,
+				   struct rtnl_link_stats64 *stats)
 {
 	struct bcm_sysport_priv *priv = netdev_priv(dev);
 	struct bcm_sysport_stats64 *stats64 = &priv->stats64;
@@ -1876,6 +1876,8 @@ static void bcm_sysport_get_stats64(struct net_device *dev,
 		stats->rx_packets = stats64->rx_packets;
 		stats->rx_bytes = stats64->rx_bytes;
 	} while (u64_stats_fetch_retry_irq(&priv->syncp, start));
+
+	return 0;
 }
 
 static void bcm_sysport_netif_start(struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 3e8a179f39db..7aa9cb8b3578 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -6812,13 +6812,13 @@ bnx2_save_stats(struct bnx2 *bp)
 	(unsigned long) (bp->stats_blk->ctr +			\
 			 bp->temp_stats_blk->ctr)
 
-static void
+static int
 bnx2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
 {
 	struct bnx2 *bp = netdev_priv(dev);
 
 	if (!bp->stats_blk)
-		return;
+		return 0;
 
 	net_stats->rx_packets =
 		GET_64BIT_NET_STATS(stat_IfHCInUcastPkts) +
@@ -6882,6 +6882,7 @@ bnx2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
 		GET_32BIT_NET_STATS(stat_IfInMBUFDiscards) +
 		GET_32BIT_NET_STATS(stat_FwRxDrop);
 
+	return 0;
 }
 
 /* All ethtool functions called with rtnl_lock */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index d31a5ad7522a..e169343f2e64 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -10096,7 +10096,7 @@ static void bnxt_add_prev_stats(struct bnxt *bp,
 	stats->tx_dropped += prev_stats->tx_dropped;
 }
 
-static void
+static int
 bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct bnxt *bp = netdev_priv(dev);
@@ -10109,7 +10109,7 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
 		clear_bit(BNXT_STATE_READ_STATS, &bp->state);
 		*stats = bp->net_stats_prev;
-		return;
+		return 0;
 	}
 
 	bnxt_get_ring_stats(bp, stats);
@@ -10138,6 +10138,8 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		stats->tx_errors = BNXT_GET_TX_PORT_STATS64(tx, tx_err);
 	}
 	clear_bit(BNXT_STATE_READ_STATS, &bp->state);
+
+	return 0;
 }
 
 static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
index 4b5c8fd76a51..5d60073af2e6 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
@@ -129,7 +129,7 @@ static netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb,
 	return rc;
 }
 
-static void
+static int
 bnxt_vf_rep_get_stats64(struct net_device *dev,
 			struct rtnl_link_stats64 *stats)
 {
@@ -139,6 +139,8 @@ bnxt_vf_rep_get_stats64(struct net_device *dev,
 	stats->rx_bytes = vf_rep->rx_stats.bytes;
 	stats->tx_packets = vf_rep->tx_stats.packets;
 	stats->tx_bytes = vf_rep->tx_stats.bytes;
+
+	return 0;
 }
 
 static int bnxt_vf_rep_setup_tc_block_cb(enum tc_setup_type type,
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 5143cdd0eeca..d632a4a502cd 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -14201,8 +14201,8 @@ static const struct ethtool_ops tg3_ethtool_ops = {
 	.set_link_ksettings	= tg3_set_link_ksettings,
 };
 
-static void tg3_get_stats64(struct net_device *dev,
-			    struct rtnl_link_stats64 *stats)
+static int tg3_get_stats64(struct net_device *dev,
+			   struct rtnl_link_stats64 *stats)
 {
 	struct tg3 *tp = netdev_priv(dev);
 
@@ -14210,11 +14210,13 @@ static void tg3_get_stats64(struct net_device *dev,
 	if (!tp->hw_stats || !tg3_flag(tp, INIT_COMPLETE)) {
 		*stats = tp->net_stats_prev;
 		spin_unlock_bh(&tp->lock);
-		return;
+		return 0;
 	}
 
 	tg3_get_nstats(tp, stats);
 	spin_unlock_bh(&tp->lock);
+
+	return 0;
 }
 
 static void tg3_set_rx_mode(struct net_device *dev)
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 7e4e831d720f..a56dcdb93430 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -3096,7 +3096,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
  * Used spin_lock to synchronize reading of stats structures, which
  * is written by BNA under the same lock.
  */
-static void
+static int
 bnad_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 {
 	struct bnad *bnad = netdev_priv(netdev);
@@ -3108,6 +3108,8 @@ bnad_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 	bnad_netdev_hwstats_fill(bnad, stats);
 
 	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return 0;
 }
 
 static void
diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
index bbb453c6a5f7..5e41dc5125f9 100644
--- a/drivers/net/ethernet/calxeda/xgmac.c
+++ b/drivers/net/ethernet/calxeda/xgmac.c
@@ -1438,7 +1438,7 @@ static void xgmac_poll_controller(struct net_device *dev)
 }
 #endif
 
-static void
+static int
 xgmac_get_stats64(struct net_device *dev,
 		  struct rtnl_link_stats64 *storage)
 {
@@ -1468,6 +1468,8 @@ xgmac_get_stats64(struct net_device *dev,
 
 	writel(0, base + XGMAC_MMC_CTRL);
 	spin_unlock_bh(&priv->stats_lock);
+
+	return 0;
 }
 
 static int xgmac_set_mac_address(struct net_device *dev, void *p)
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 7c5af4beedc6..6483c461f009 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -2021,7 +2021,7 @@ static int liquidio_set_mac(struct net_device *netdev, void *p)
 	return 0;
 }
 
-static void
+static int
 liquidio_get_stats64(struct net_device *netdev,
 		     struct rtnl_link_stats64 *lstats)
 {
@@ -2035,7 +2035,7 @@ liquidio_get_stats64(struct net_device *netdev,
 	oct = lio->oct_dev;
 
 	if (ifstate_check(lio, LIO_IFSTATE_RESETTING))
-		return;
+		return 0;
 
 	for (i = 0; i < oct->num_iqs; i++) {
 		iq_no = lio->linfo.txpciq[i].s.q_no;
@@ -2091,6 +2091,8 @@ liquidio_get_stats64(struct net_device *netdev,
 	lstats->tx_errors = lstats->tx_aborted_errors +
 		lstats->tx_carrier_errors +
 		lstats->tx_fifo_errors;
+
+	return 0;
 }
 
 /**
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
index 516f166ceff8..8f4a8aaa3fe5 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
@@ -1161,7 +1161,7 @@ static int liquidio_set_mac(struct net_device *netdev, void *p)
 	return 0;
 }
 
-static void
+static int
 liquidio_get_stats64(struct net_device *netdev,
 		     struct rtnl_link_stats64 *lstats)
 {
@@ -1175,7 +1175,7 @@ liquidio_get_stats64(struct net_device *netdev,
 	oct = lio->oct_dev;
 
 	if (ifstate_check(lio, LIO_IFSTATE_RESETTING))
-		return;
+		return 0;
 
 	for (i = 0; i < oct->num_iqs; i++) {
 		iq_no = lio->linfo.txpciq[i].s.q_no;
@@ -1226,6 +1226,8 @@ liquidio_get_stats64(struct net_device *netdev,
 
 	lstats->tx_errors = lstats->tx_aborted_errors +
 		lstats->tx_carrier_errors;
+
+	return 0;
 }
 
 /**
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
index 600de587d7a9..763248863c79 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
@@ -34,8 +34,8 @@ static netdev_tx_t lio_vf_rep_pkt_xmit(struct sk_buff *skb,
 static void lio_vf_rep_tx_timeout(struct net_device *netdev, unsigned int txqueue);
 static int lio_vf_rep_phys_port_name(struct net_device *dev,
 				     char *buf, size_t len);
-static void lio_vf_rep_get_stats64(struct net_device *dev,
-				   struct rtnl_link_stats64 *stats64);
+static int lio_vf_rep_get_stats64(struct net_device *dev,
+				  struct rtnl_link_stats64 *stats64);
 static int lio_vf_rep_change_mtu(struct net_device *ndev, int new_mtu);
 static int lio_vf_get_port_parent_id(struct net_device *dev,
 				     struct netdev_phys_item_id *ppid);
@@ -179,7 +179,7 @@ lio_vf_rep_tx_timeout(struct net_device *ndev, unsigned int txqueue)
 	netif_wake_queue(ndev);
 }
 
-static void
+static int
 lio_vf_rep_get_stats64(struct net_device *dev,
 		       struct rtnl_link_stats64 *stats64)
 {
@@ -193,6 +193,8 @@ lio_vf_rep_get_stats64(struct net_device *dev,
 	stats64->rx_packets = vf_rep->stats.tx_packets;
 	stats64->rx_bytes   = vf_rep->stats.tx_bytes;
 	stats64->rx_dropped = vf_rep->stats.tx_dropped;
+
+	return 0;
 }
 
 static int
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index f3b7b443f964..567e657a86f2 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -1720,8 +1720,8 @@ void nicvf_update_stats(struct nicvf *nic)
 		nicvf_update_sq_stats(nic, qidx);
 }
 
-static void nicvf_get_stats64(struct net_device *netdev,
-			      struct rtnl_link_stats64 *stats)
+static int nicvf_get_stats64(struct net_device *netdev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct nicvf *nic = netdev_priv(netdev);
 	struct nicvf_hw_stats *hw_stats = &nic->hw_stats;
@@ -1737,6 +1737,7 @@ static void nicvf_get_stats64(struct net_device *netdev,
 	stats->tx_packets = hw_stats->tx_frames;
 	stats->tx_dropped = hw_stats->tx_drops;
 
+	return 0;
 }
 
 static void nicvf_tx_timeout(struct net_device *dev, unsigned int txqueue)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 15542661e3d2..993e03e73822 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -2999,8 +2999,8 @@ int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid,
 }
 EXPORT_SYMBOL(cxgb4_remove_server_filter);
 
-static void cxgb_get_stats(struct net_device *dev,
-			   struct rtnl_link_stats64 *ns)
+static int cxgb_get_stats(struct net_device *dev,
+			  struct rtnl_link_stats64 *ns)
 {
 	struct port_stats stats;
 	struct port_info *p = netdev_priv(dev);
@@ -3013,7 +3013,7 @@ static void cxgb_get_stats(struct net_device *dev,
 	spin_lock(&adapter->stats_lock);
 	if (!netif_device_present(dev)) {
 		spin_unlock(&adapter->stats_lock);
-		return;
+		return 0;
 	}
 	t4_get_port_stats_offset(adapter, p->tx_chan, &stats,
 				 &p->stats_base);
@@ -3047,6 +3047,8 @@ static void cxgb_get_stats(struct net_device *dev,
 	ns->tx_errors = stats.tx_error_frames;
 	ns->rx_errors = stats.rx_symbol_err + stats.rx_fcs_err +
 		ns->rx_length_errors + stats.rx_len_err + ns->rx_fifo_errors;
+
+	return 0;
 }
 
 static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index f04ec53544ae..ca3ea583451e 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -871,8 +871,8 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
 }
 
 /* dev_base_lock rwlock held, nominally process context */
-static void enic_get_stats(struct net_device *netdev,
-			   struct rtnl_link_stats64 *net_stats)
+static int enic_get_stats(struct net_device *netdev,
+			  struct rtnl_link_stats64 *net_stats)
 {
 	struct enic *enic = netdev_priv(netdev);
 	struct vnic_stats *stats;
@@ -884,7 +884,7 @@ static void enic_get_stats(struct net_device *netdev,
 	 * recorded stats.
 	 */
 	if (err == -ENOMEM)
-		return;
+		return 0;
 
 	net_stats->tx_packets = stats->tx.tx_frames_ok;
 	net_stats->tx_bytes = stats->tx.tx_bytes_ok;
@@ -898,6 +898,8 @@ static void enic_get_stats(struct net_device *netdev,
 	net_stats->rx_over_errors = enic->rq_truncated_pkts;
 	net_stats->rx_crc_errors = enic->rq_bad_fcs;
 	net_stats->rx_dropped = stats->rx.rx_no_bufs + stats->rx.rx_drop;
+
+	return 0;
 }
 
 static int enic_mc_sync(struct net_device *netdev, const u8 *mc_addr)
diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c
index 8df6f081f244..84b86dcc0420 100644
--- a/drivers/net/ethernet/cortina/gemini.c
+++ b/drivers/net/ethernet/cortina/gemini.c
@@ -1907,8 +1907,8 @@ static void gmac_clear_hw_stats(struct net_device *netdev)
 	readl(port->gmac_base + GMAC_IN_MAC2);
 }
 
-static void gmac_get_stats64(struct net_device *netdev,
-			     struct rtnl_link_stats64 *stats)
+static int gmac_get_stats64(struct net_device *netdev,
+			    struct rtnl_link_stats64 *stats)
 {
 	struct gemini_ethernet_port *port = netdev_priv(netdev);
 	unsigned int start;
@@ -1954,6 +1954,8 @@ static void gmac_get_stats64(struct net_device *netdev,
 	} while (u64_stats_fetch_retry(&port->tx_stats_syncp, start));
 
 	stats->rx_dropped += stats->rx_missed_errors;
+
+	return 0;
 }
 
 static int gmac_change_mtu(struct net_device *netdev, int new_mtu)
diff --git a/drivers/net/ethernet/ec_bhf.c b/drivers/net/ethernet/ec_bhf.c
index 46b0dbab8aad..0bff80b16c67 100644
--- a/drivers/net/ethernet/ec_bhf.c
+++ b/drivers/net/ethernet/ec_bhf.c
@@ -448,7 +448,7 @@ static int ec_bhf_stop(struct net_device *net_dev)
 	return 0;
 }
 
-static void
+static int
 ec_bhf_get_stats(struct net_device *net_dev,
 		 struct rtnl_link_stats64 *stats)
 {
@@ -463,6 +463,8 @@ ec_bhf_get_stats(struct net_device *net_dev,
 
 	stats->tx_bytes = priv->stat_tx_bytes;
 	stats->rx_bytes = priv->stat_rx_bytes;
+
+	return 0;
 }
 
 static const struct net_device_ops ec_bhf_netdev_ops = {
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index b6eba29d8e99..f642e1561151 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -650,8 +650,8 @@ void be_parse_stats(struct be_adapter *adapter)
 	}
 }
 
-static void be_get_stats64(struct net_device *netdev,
-			   struct rtnl_link_stats64 *stats)
+static int 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;
@@ -715,6 +715,8 @@ static void be_get_stats64(struct net_device *netdev,
 	stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop +
 				drvs->rx_input_fifo_overflow_drop +
 				drvs->rx_drops_no_pbuf;
+
+	return 0;
 }
 
 void be_link_status_update(struct be_adapter *adapter, u8 link_status)
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
index 4360ce4d3fb6..2028792aab26 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
@@ -350,8 +350,8 @@ static void dpaa_tx_timeout(struct net_device *net_dev, unsigned int txqueue)
 /* Calculates the statistics for the given device by adding the statistics
  * collected by each CPU.
  */
-static void dpaa_get_stats64(struct net_device *net_dev,
-			     struct rtnl_link_stats64 *s)
+static int dpaa_get_stats64(struct net_device *net_dev,
+			    struct rtnl_link_stats64 *s)
 {
 	int numstats = sizeof(struct rtnl_link_stats64) / sizeof(u64);
 	struct dpaa_priv *priv = netdev_priv(net_dev);
@@ -369,6 +369,8 @@ static void dpaa_get_stats64(struct net_device *net_dev,
 		for (j = 0; j < numstats; j++)
 			netstats[j] += cpustats[j];
 	}
+
+	return 0;
 }
 
 static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type,
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index fb0bcd18ec0c..688c27f209b7 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -1896,8 +1896,8 @@ static int dpaa2_eth_set_addr(struct net_device *net_dev, void *addr)
 /** Fill in counters maintained by the GPP driver. These may be different from
  * the hardware counters obtained by ethtool.
  */
-static void dpaa2_eth_get_stats(struct net_device *net_dev,
-				struct rtnl_link_stats64 *stats)
+static int dpaa2_eth_get_stats(struct net_device *net_dev,
+			       struct rtnl_link_stats64 *stats)
 {
 	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
 	struct rtnl_link_stats64 *percpu_stats;
@@ -1912,6 +1912,8 @@ static void dpaa2_eth_get_stats(struct net_device *net_dev,
 		for (j = 0; j < num; j++)
 			netstats[j] += cpustats[j];
 	}
+
+	return 0;
 }
 
 /* Copy mac unicast addresses from @net_dev to @priv.
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index 7302498c6df3..4a08bf37805b 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -26,7 +26,7 @@
 const char gve_version_str[] = GVE_VERSION;
 static const char gve_version_prefix[] = GVE_VERSION_PREFIX;
 
-static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s)
+static int gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s)
 {
 	struct gve_priv *priv = netdev_priv(dev);
 	unsigned int start;
@@ -54,6 +54,8 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s)
 						       start));
 		}
 	}
+
+	return 0;
 }
 
 static int gve_alloc_counter_array(struct gve_priv *priv)
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index 858cb293152a..67f704459fd8 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -1876,8 +1876,8 @@ static void hns_nic_set_rx_mode(struct net_device *ndev)
 		netdev_err(ndev, "sync uc address fail\n");
 }
 
-static void hns_nic_get_stats64(struct net_device *ndev,
-				struct rtnl_link_stats64 *stats)
+static int hns_nic_get_stats64(struct net_device *ndev,
+			       struct rtnl_link_stats64 *stats)
 {
 	int idx = 0;
 	u64 tx_bytes = 0;
@@ -1919,6 +1919,8 @@ static void hns_nic_get_stats64(struct net_device *ndev,
 	stats->tx_window_errors = ndev->stats.tx_window_errors;
 	stats->rx_compressed = ndev->stats.rx_compressed;
 	stats->tx_compressed = ndev->stats.tx_compressed;
+
+	return 0;
 }
 
 static u16
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 405e49033417..43d9d207b50f 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -1710,8 +1710,8 @@ static netdev_features_t hns3_features_check(struct sk_buff *skb,
 	return features;
 }
 
-static void hns3_nic_get_stats64(struct net_device *netdev,
-				 struct rtnl_link_stats64 *stats)
+static int hns3_nic_get_stats64(struct net_device *netdev,
+				struct rtnl_link_stats64 *stats)
 {
 	struct hns3_nic_priv *priv = netdev_priv(netdev);
 	int queue_num = priv->ae_handle->kinfo.num_tqps;
@@ -1732,7 +1732,7 @@ static void hns3_nic_get_stats64(struct net_device *netdev,
 	u64 rx_drop = 0;
 
 	if (test_bit(HNS3_NIC_STATE_DOWN, &priv->state))
-		return;
+		return 0;
 
 	handle->ae_algo->ops->update_stats(handle, &netdev->stats);
 
@@ -1795,6 +1795,8 @@ static void hns3_nic_get_stats64(struct net_device *netdev,
 	stats->tx_window_errors = netdev->stats.tx_window_errors;
 	stats->rx_compressed = netdev->stats.rx_compressed;
 	stats->tx_compressed = netdev->stats.tx_compressed;
+
+	return 0;
 }
 
 static int hns3_setup_tc(struct net_device *netdev, void *type_data)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index 9a9b09401d01..ec30bf395a6e 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -854,8 +854,8 @@ static void hinic_tx_timeout(struct net_device *netdev, unsigned int txqueue)
 	}
 }
 
-static void hinic_get_stats64(struct net_device *netdev,
-			      struct rtnl_link_stats64 *stats)
+static int hinic_get_stats64(struct net_device *netdev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct hinic_dev *nic_dev = netdev_priv(netdev);
 	struct hinic_rxq_stats *nic_rx_stats;
@@ -878,6 +878,8 @@ static void hinic_get_stats64(struct net_device *netdev,
 	stats->tx_bytes   = nic_tx_stats->bytes;
 	stats->tx_packets = nic_tx_stats->pkts;
 	stats->tx_errors  = nic_tx_stats->tx_dropped;
+
+	return 0;
 }
 
 static int hinic_set_features(struct net_device *netdev,
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
index c2e740475786..9297043fdf10 100644
--- a/drivers/net/ethernet/ibm/ehea/ehea_main.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c
@@ -314,8 +314,8 @@ static void ehea_update_bcmc_registrations(void)
 	spin_unlock_irqrestore(&ehea_bcmc_regs.lock, flags);
 }
 
-static void ehea_get_stats64(struct net_device *dev,
-			     struct rtnl_link_stats64 *stats)
+static int ehea_get_stats64(struct net_device *dev,
+			    struct rtnl_link_stats64 *stats)
 {
 	struct ehea_port *port = netdev_priv(dev);
 	u64 rx_packets = 0, tx_packets = 0, rx_bytes = 0, tx_bytes = 0;
@@ -338,6 +338,8 @@ static void ehea_get_stats64(struct net_device *dev,
 
 	stats->multicast = port->stats.multicast;
 	stats->rx_errors = port->stats.rx_errors;
+
+	return 0;
 }
 
 static void ehea_update_stats(struct work_struct *work)
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 5b2143f4b1f8..f30fcbaf2731 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -476,8 +476,8 @@ int e1000e_setup_rx_resources(struct e1000_ring *ring);
 int e1000e_setup_tx_resources(struct e1000_ring *ring);
 void e1000e_free_rx_resources(struct e1000_ring *ring);
 void e1000e_free_tx_resources(struct e1000_ring *ring);
-void e1000e_get_stats64(struct net_device *netdev,
-			struct rtnl_link_stats64 *stats);
+int e1000e_get_stats64(struct net_device *netdev,
+		       struct rtnl_link_stats64 *stats);
 void e1000e_set_interrupt_capability(struct e1000_adapter *adapter);
 void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter);
 void e1000e_get_hw_control(struct e1000_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index e9b82c209c2d..0707d6281233 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -5992,8 +5992,8 @@ static void e1000_reset_task(struct work_struct *work)
  *
  * Returns the address of the device statistics structure.
  **/
-void e1000e_get_stats64(struct net_device *netdev,
-			struct rtnl_link_stats64 *stats)
+int e1000e_get_stats64(struct net_device *netdev,
+		       struct rtnl_link_stats64 *stats)
 {
 	struct e1000_adapter *adapter = netdev_priv(netdev);
 
@@ -6029,6 +6029,8 @@ void e1000e_get_stats64(struct net_device *netdev,
 	/* Tx Dropped needs to be maintained elsewhere */
 
 	spin_unlock(&adapter->stats64_lock);
+
+	return 0;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
index 2fb52bd6fc0e..ca0de7b7b27d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
@@ -1212,8 +1212,8 @@ void fm10k_reset_rx_state(struct fm10k_intfc *interface)
  * Obtain 64bit statistics in a way that is safe for both 32bit and 64bit
  * architectures.
  */
-static void fm10k_get_stats64(struct net_device *netdev,
-			      struct rtnl_link_stats64 *stats)
+static int fm10k_get_stats64(struct net_device *netdev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct fm10k_intfc *interface = netdev_priv(netdev);
 	struct fm10k_ring *ring;
@@ -1258,6 +1258,8 @@ static void fm10k_get_stats64(struct net_device *netdev,
 
 	/* following stats updated by fm10k_service_task() */
 	stats->rx_missed_errors	= netdev->stats.rx_missed_errors;
+
+	return 0;
 }
 
 int fm10k_setup_tc(struct net_device *dev, u8 tc)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 521ea9df38d5..0952ac5530c4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -407,8 +407,8 @@ static void i40e_get_netdev_stats_struct_tx(struct i40e_ring *ring,
  * Returns the address of the device statistics structure.
  * The statistics are actually updated from the service task.
  **/
-static void i40e_get_netdev_stats_struct(struct net_device *netdev,
-				  struct rtnl_link_stats64 *stats)
+static int i40e_get_netdev_stats_struct(struct net_device *netdev,
+					struct rtnl_link_stats64 *stats)
 {
 	struct i40e_netdev_priv *np = netdev_priv(netdev);
 	struct i40e_vsi *vsi = np->vsi;
@@ -417,10 +417,10 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev,
 	int i;
 
 	if (test_bit(__I40E_VSI_DOWN, vsi->state))
-		return;
+		return 0;
 
 	if (!vsi->tx_rings)
-		return;
+		return 0;
 
 	rcu_read_lock();
 	for (i = 0; i < vsi->num_queue_pairs; i++) {
@@ -462,6 +462,8 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev,
 	stats->rx_dropped	= vsi_stats->rx_dropped;
 	stats->rx_crc_errors	= vsi_stats->rx_crc_errors;
 	stats->rx_length_errors	= vsi_stats->rx_length_errors;
+
+	return 0;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 6e251dfffc91..323b8820367b 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -5539,7 +5539,7 @@ void ice_update_pf_stats(struct ice_pf *pf)
  * @stats: main device statistics structure
  */
 static
-void ice_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
+int ice_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 {
 	struct ice_netdev_priv *np = netdev_priv(netdev);
 	struct rtnl_link_stats64 *vsi_stats;
@@ -5548,7 +5548,7 @@ void ice_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 	vsi_stats = &vsi->net_stats;
 
 	if (!vsi->num_txq || !vsi->num_rxq)
-		return;
+		return 0;
 
 	/* netdev packet/byte stats come from ring counter. These are obtained
 	 * by summing up ring counters (done by ice_update_vsi_ring_stats).
@@ -5573,6 +5573,8 @@ void ice_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 	stats->rx_dropped = vsi_stats->rx_dropped;
 	stats->rx_crc_errors = vsi_stats->rx_crc_errors;
 	stats->rx_length_errors = vsi_stats->rx_length_errors;
+
+	return 0;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 03f78fdb0dcd..20dd1bb8512f 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -124,8 +124,8 @@ static void igb_update_phy_info(struct timer_list *);
 static void igb_watchdog(struct timer_list *);
 static void igb_watchdog_task(struct work_struct *);
 static netdev_tx_t igb_xmit_frame(struct sk_buff *skb, struct net_device *);
-static void igb_get_stats64(struct net_device *dev,
-			    struct rtnl_link_stats64 *stats);
+static int igb_get_stats64(struct net_device *dev,
+			   struct rtnl_link_stats64 *stats);
 static int igb_change_mtu(struct net_device *, int);
 static int igb_set_mac(struct net_device *, void *);
 static void igb_set_uta(struct igb_adapter *adapter, bool set);
@@ -6479,8 +6479,8 @@ static void igb_reset_task(struct work_struct *work)
  *  @netdev: network interface device structure
  *  @stats: rtnl_link_stats64 pointer
  **/
-static void igb_get_stats64(struct net_device *netdev,
-			    struct rtnl_link_stats64 *stats)
+static int igb_get_stats64(struct net_device *netdev,
+			   struct rtnl_link_stats64 *stats)
 {
 	struct igb_adapter *adapter = netdev_priv(netdev);
 
@@ -6488,6 +6488,8 @@ static void igb_get_stats64(struct net_device *netdev,
 	igb_update_stats(adapter);
 	memcpy(stats, &adapter->stats64, sizeof(*stats));
 	spin_unlock(&adapter->stats64_lock);
+
+	return 0;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index afd6a62da29d..495a614d34ae 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -3899,8 +3899,8 @@ static int igc_change_mtu(struct net_device *netdev, int new_mtu)
  * Returns the address of the device statistics structure.
  * The statistics are updated here and also from the timer callback.
  */
-static void igc_get_stats64(struct net_device *netdev,
-			    struct rtnl_link_stats64 *stats)
+static int igc_get_stats64(struct net_device *netdev,
+			   struct rtnl_link_stats64 *stats)
 {
 	struct igc_adapter *adapter = netdev_priv(netdev);
 
@@ -3909,6 +3909,8 @@ static void igc_get_stats64(struct net_device *netdev,
 		igc_update_stats(adapter);
 	memcpy(stats, &adapter->stats64, sizeof(*stats));
 	spin_unlock(&adapter->stats64_lock);
+
+	return 0;
 }
 
 static netdev_features_t igc_fix_features(struct net_device *netdev,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 6cbbe09ce8a0..91713357e844 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -8926,8 +8926,8 @@ static void ixgbe_get_ring_stats64(struct rtnl_link_stats64 *stats,
 	}
 }
 
-static void ixgbe_get_stats64(struct net_device *netdev,
-			      struct rtnl_link_stats64 *stats)
+static int ixgbe_get_stats64(struct net_device *netdev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct ixgbe_adapter *adapter = netdev_priv(netdev);
 	int i;
@@ -8967,6 +8967,8 @@ static void ixgbe_get_stats64(struct net_device *netdev,
 	stats->rx_length_errors	= netdev->stats.rx_length_errors;
 	stats->rx_crc_errors	= netdev->stats.rx_crc_errors;
 	stats->rx_missed_errors	= netdev->stats.rx_missed_errors;
+
+	return 0;
 }
 
 #ifdef CONFIG_IXGBE_DCB
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 4061cd7db5dd..efe8b35e80b3 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -4357,8 +4357,8 @@ static void ixgbevf_get_tx_ring_stats(struct rtnl_link_stats64 *stats,
 	}
 }
 
-static void ixgbevf_get_stats(struct net_device *netdev,
-			      struct rtnl_link_stats64 *stats)
+static int ixgbevf_get_stats(struct net_device *netdev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct ixgbevf_adapter *adapter = netdev_priv(netdev);
 	unsigned int start;
@@ -4392,6 +4392,8 @@ static void ixgbevf_get_stats(struct net_device *netdev,
 		ixgbevf_get_tx_ring_stats(stats, ring);
 	}
 	rcu_read_unlock();
+
+	return 0;
 }
 
 #define IXGBEVF_MAX_MAC_HDR_LEN		127
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 3369ec717a51..5569d15eae3a 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -760,7 +760,7 @@ static void mvneta_mib_counters_clear(struct mvneta_port *pp)
 }
 
 /* Get System Network Statistics */
-static void
+static int
 mvneta_get_stats64(struct net_device *dev,
 		   struct rtnl_link_stats64 *stats)
 {
@@ -797,6 +797,8 @@ mvneta_get_stats64(struct net_device *dev,
 	}
 
 	stats->tx_dropped	= dev->stats.tx_dropped;
+
+	return 0;
 }
 
 /* Rx descriptors helper methods */
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 4b1808acef58..67f2220a6081 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -4701,7 +4701,7 @@ static int mvpp2_check_pagepool_dma(struct mvpp2_port *port)
 	return err;
 }
 
-static void
+static int
 mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct mvpp2_port *port = netdev_priv(dev);
@@ -4733,6 +4733,8 @@ mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	stats->rx_errors	= dev->stats.rx_errors;
 	stats->rx_dropped	= dev->stats.rx_dropped;
 	stats->tx_dropped	= dev->stats.tx_dropped;
+
+	return 0;
 }
 
 static int mvpp2_set_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index bdfa2e293531..2f8b8c705cd6 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -110,8 +110,8 @@ void otx2_get_dev_stats(struct otx2_nic *pfvf)
 			       dev_stats->tx_ucast_frames;
 }
 
-void otx2_get_stats64(struct net_device *netdev,
-		      struct rtnl_link_stats64 *stats)
+int otx2_get_stats64(struct net_device *netdev,
+		     struct rtnl_link_stats64 *stats)
 {
 	struct otx2_nic *pfvf = netdev_priv(netdev);
 	struct otx2_dev_stats *dev_stats;
@@ -127,6 +127,8 @@ void otx2_get_stats64(struct net_device *netdev,
 	stats->tx_bytes = dev_stats->tx_bytes;
 	stats->tx_packets = dev_stats->tx_frames;
 	stats->tx_dropped = dev_stats->tx_drops;
+
+	return 0;
 }
 EXPORT_SYMBOL(otx2_get_stats64);
 
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index 143ae04c8ad5..6987ef8b00eb 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -665,8 +665,8 @@ void mbox_handler_nix_bp_enable(struct otx2_nic *pfvf,
 
 /* Device stats APIs */
 void otx2_get_dev_stats(struct otx2_nic *pfvf);
-void otx2_get_stats64(struct net_device *netdev,
-		      struct rtnl_link_stats64 *stats);
+int otx2_get_stats64(struct net_device *netdev,
+		     struct rtnl_link_stats64 *stats);
 void otx2_update_lmac_stats(struct otx2_nic *pfvf);
 int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx);
 int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx);
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c
index 25dd903a3e92..4e8ecd9623b7 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
@@ -152,8 +152,8 @@ static int prestera_port_change_mtu(struct net_device *dev, int mtu)
 	return 0;
 }
 
-static void prestera_port_get_stats64(struct net_device *dev,
-				      struct rtnl_link_stats64 *stats)
+static int prestera_port_get_stats64(struct net_device *dev,
+				     struct rtnl_link_stats64 *stats)
 {
 	struct prestera_port *port = netdev_priv(dev);
 	struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats;
@@ -180,6 +180,8 @@ static void prestera_port_get_stats64(struct net_device *dev,
 	stats->collisions = port_stats->excessive_collision;
 
 	stats->rx_crc_errors = port_stats->bad_crc;
+
+	return 0;
 }
 
 static void prestera_port_get_hw_stats(struct prestera_port *port)
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index ebe1406c6e64..1aa9b234c55a 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -3883,8 +3883,8 @@ static void sky2_set_multicast(struct net_device *dev)
 	gma_write16(hw, port, GM_RX_CTRL, reg);
 }
 
-static void sky2_get_stats(struct net_device *dev,
-			   struct rtnl_link_stats64 *stats)
+static int sky2_get_stats(struct net_device *dev,
+			  struct rtnl_link_stats64 *stats)
 {
 	struct sky2_port *sky2 = netdev_priv(dev);
 	struct sky2_hw *hw = sky2->hw;
@@ -3924,6 +3924,8 @@ static void sky2_get_stats(struct net_device *dev,
 	stats->rx_dropped = dev->stats.rx_dropped;
 	stats->rx_fifo_errors = dev->stats.rx_fifo_errors;
 	stats->tx_fifo_errors = dev->stats.tx_fifo_errors;
+
+	return 0;
 }
 
 /* Can have one global because blinking is controlled by
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 6d2d60675ffd..301a510f41f7 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -722,8 +722,8 @@ static void mtk_stats_update(struct mtk_eth *eth)
 	}
 }
 
-static void mtk_get_stats64(struct net_device *dev,
-			    struct rtnl_link_stats64 *storage)
+static int mtk_get_stats64(struct net_device *dev,
+			   struct rtnl_link_stats64 *storage)
 {
 	struct mtk_mac *mac = netdev_priv(dev);
 	struct mtk_hw_stats *hw_stats = mac->hw_stats;
@@ -754,6 +754,8 @@ static void mtk_get_stats64(struct net_device *dev,
 	storage->tx_errors = dev->stats.tx_errors;
 	storage->rx_dropped = dev->stats.rx_dropped;
 	storage->tx_dropped = dev->stats.tx_dropped;
+
+	return 0;
 }
 
 static inline int mtk_max_frag_size(int mtu)
diff --git a/drivers/net/ethernet/mediatek/mtk_star_emac.c b/drivers/net/ethernet/mediatek/mtk_star_emac.c
index a8641a407c06..f98faf299a8a 100644
--- a/drivers/net/ethernet/mediatek/mtk_star_emac.c
+++ b/drivers/net/ethernet/mediatek/mtk_star_emac.c
@@ -1104,14 +1104,16 @@ static void mtk_star_tx_complete_all(struct mtk_star_priv *priv)
 	spin_unlock(&priv->lock);
 }
 
-static void mtk_star_netdev_get_stats64(struct net_device *ndev,
-					struct rtnl_link_stats64 *stats)
+static int mtk_star_netdev_get_stats64(struct net_device *ndev,
+				       struct rtnl_link_stats64 *stats)
 {
 	struct mtk_star_priv *priv = netdev_priv(ndev);
 
 	mtk_star_update_stats(priv);
 
 	memcpy(stats, &priv->stats, sizeof(*stats));
+
+	return 0;
 }
 
 static void mtk_star_set_rx_mode(struct net_device *ndev)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 51b9700fce83..396ee31020af 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -1385,7 +1385,7 @@ static void mlx4_en_tx_timeout(struct net_device *dev, unsigned int txqueue)
 }
 
 
-static void
+static int
 mlx4_en_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -1394,6 +1394,8 @@ mlx4_en_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	mlx4_en_fold_software_stats(dev);
 	netdev_stats_to_stats64(stats, &dev->stats);
 	spin_unlock_bh(&priv->stats_lock);
+
+	return 0;
 }
 
 static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 055baf3b6cb1..bb553c79915c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -895,7 +895,7 @@ bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev);
 bool mlx5e_striding_rq_possible(struct mlx5_core_dev *mdev,
 				struct mlx5e_params *params);
 
-void mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
+int mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
 void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s);
 
 void mlx5e_init_l2_addr(struct mlx5e_priv *priv);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index f27f509ab028..98259ae32e0e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -3677,7 +3677,7 @@ void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
 	}
 }
 
-void
+int
 mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct mlx5e_priv *priv = netdev_priv(dev);
@@ -3715,6 +3715,8 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
 			   stats->rx_frame_errors;
 	stats->tx_errors = stats->tx_aborted_errors + stats->tx_carrier_errors;
+
+	return 0;
 }
 
 static void mlx5e_set_rx_mode(struct net_device *dev)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index cfa0e8552975..07a6df627a65 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -554,7 +554,7 @@ static int mlx5e_rep_get_offload_stats(int attr_id, const struct net_device *dev
 	return -EINVAL;
 }
 
-static void
+static int
 mlx5e_rep_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct mlx5e_priv *priv = netdev_priv(dev);
@@ -562,6 +562,8 @@ mlx5e_rep_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	/* update HW stats in background for next time */
 	mlx5e_queue_update_stats(priv);
 	memcpy(stats, &priv->stats.vf_vport, sizeof(*stats));
+
+	return 0;
 }
 
 static int mlx5e_rep_change_mtu(struct net_device *netdev, int new_mtu)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
index 97b5fcb1f406..10922ff26fef 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
@@ -142,7 +142,7 @@ static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv)
 	memcpy(&priv->stats.sw, &s, sizeof(s));
 }
 
-void mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
+int mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct mlx5e_priv     *priv   = mlx5i_epriv(dev);
 	struct mlx5e_sw_stats *sstats = &priv->stats.sw;
@@ -154,6 +154,8 @@ void mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	stats->tx_packets = sstats->tx_packets;
 	stats->tx_bytes   = sstats->tx_bytes;
 	stats->tx_dropped = sstats->tx_queue_dropped;
+
+	return 0;
 }
 
 int mlx5i_init_underlay_qp(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
index b79dc1e28c41..01dfeb6d29ab 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
@@ -118,7 +118,7 @@ struct mlx5i_tx_wqe {
 
 void mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
 		   struct mlx5_av *av, u32 dqpn, u32 dqkey, bool xmit_more);
-void mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
+int mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
 
 #endif /* CONFIG_MLX5_CORE_IPOIB */
 #endif /* __MLX5E_IPOB_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 1650d9852b5b..4f2fdfd395c3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -834,13 +834,15 @@ static void update_stats_cache(struct work_struct *work)
 /* Return the stats from a cache that is updated periodically,
  * as this function might get called in an atomic context.
  */
-static void
+static int
 mlxsw_sp_port_get_stats64(struct net_device *dev,
 			  struct rtnl_link_stats64 *stats)
 {
 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
 
 	memcpy(stats, &mlxsw_sp_port->periodic_hw_stats.stats, sizeof(*stats));
+
+	return 0;
 }
 
 static int __mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index 40e2e79d4517..c115755d82e9 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -346,7 +346,7 @@ static int mlxsw_sx_port_change_mtu(struct net_device *dev, int mtu)
 	return 0;
 }
 
-static void
+static int
 mlxsw_sx_port_get_stats64(struct net_device *dev,
 			  struct rtnl_link_stats64 *stats)
 {
@@ -375,6 +375,8 @@ mlxsw_sx_port_get_stats64(struct net_device *dev,
 		tx_dropped	+= p->tx_dropped;
 	}
 	stats->tx_dropped	= tx_dropped;
+
+	return 0;
 }
 
 static struct devlink_port *
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
index 3804310c853a..2cc7593f6a51 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.c
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -2605,8 +2605,8 @@ static int lan743x_netdev_change_mtu(struct net_device *netdev, int new_mtu)
 	return ret;
 }
 
-static void lan743x_netdev_get_stats64(struct net_device *netdev,
-				       struct rtnl_link_stats64 *stats)
+static int lan743x_netdev_get_stats64(struct net_device *netdev,
+				      struct rtnl_link_stats64 *stats)
 {
 	struct lan743x_adapter *adapter = netdev_priv(netdev);
 
@@ -2650,6 +2650,8 @@ static void lan743x_netdev_get_stats64(struct net_device *netdev,
 					     STAT_TX_MULTIPLE_COLLISIONS) +
 			    lan743x_csr_read(adapter,
 					     STAT_TX_LATE_COLLISIONS);
+
+	return 0;
 }
 
 static int lan743x_netdev_set_mac_address(struct net_device *netdev,
diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index 2bd2840d88bd..e4b44c57cddc 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -556,8 +556,8 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
 	return 0;
 }
 
-static void ocelot_get_stats64(struct net_device *dev,
-			       struct rtnl_link_stats64 *stats)
+static int ocelot_get_stats64(struct net_device *dev,
+			      struct rtnl_link_stats64 *stats)
 {
 	struct ocelot_port_private *priv = netdev_priv(dev);
 	struct ocelot *ocelot = priv->port.ocelot;
@@ -593,6 +593,8 @@ static void ocelot_get_stats64(struct net_device *dev,
 	stats->tx_dropped = ocelot_read(ocelot, SYS_COUNT_TX_DROPS) +
 			    ocelot_read(ocelot, SYS_COUNT_TX_AGING);
 	stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION);
+
+	return 0;
 }
 
 static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index 1634ca6d4a8f..67c9c10f68f1 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -362,8 +362,8 @@ static inline void put_be32(__be32 val, __be32 __iomem * p)
 	__raw_writel((__force __u32) val, (__force void __iomem *)p);
 }
 
-static void myri10ge_get_stats(struct net_device *dev,
-			       struct rtnl_link_stats64 *stats);
+static int myri10ge_get_stats(struct net_device *dev,
+			      struct rtnl_link_stats64 *stats);
 
 static void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated)
 {
@@ -1790,7 +1790,7 @@ myri10ge_get_ethtool_stats(struct net_device *netdev,
 
 	/* force stats update */
 	memset(&link_stats, 0, sizeof(link_stats));
-	(void)myri10ge_get_stats(netdev, &link_stats);
+	myri10ge_get_stats(netdev, &link_stats);
 	for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
 		data[i] = ((u64 *)&link_stats)[i];
 
@@ -2914,8 +2914,8 @@ static netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb,
 	return NETDEV_TX_OK;
 }
 
-static void myri10ge_get_stats(struct net_device *dev,
-			       struct rtnl_link_stats64 *stats)
+static int myri10ge_get_stats(struct net_device *dev,
+			      struct rtnl_link_stats64 *stats)
 {
 	const struct myri10ge_priv *mgp = netdev_priv(dev);
 	const struct myri10ge_slice_netstats *slice_stats;
@@ -2930,6 +2930,8 @@ static void myri10ge_get_stats(struct net_device *dev,
 		stats->rx_dropped += slice_stats->rx_dropped;
 		stats->tx_dropped += slice_stats->tx_dropped;
 	}
+
+	return 0;
 }
 
 static void myri10ge_set_multicast_list(struct net_device *dev)
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index 87892bd992b1..ce1a98cd3783 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -3100,7 +3100,7 @@ static int vxge_change_mtu(struct net_device *dev, int new_mtu)
  * @net_stats: pointer to struct rtnl_link_stats64
  *
  */
-static void
+static int
 vxge_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
 {
 	struct vxgedev *vdev = netdev_priv(dev);
@@ -3139,6 +3139,8 @@ vxge_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
 		net_stats->tx_bytes += bytes;
 		net_stats->tx_errors += txstats->tx_errors;
 	}
+
+	return 0;
 }
 
 static enum vxge_hw_status vxge_timestamp_config(struct __vxge_hw_device *devh)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 7ba8f4c7f26d..b477dedae6a5 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -3360,8 +3360,8 @@ nfp_net_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
 	return nfp_net_mbox_reconfig_and_unlock(nn, cmd);
 }
 
-static void nfp_net_stat64(struct net_device *netdev,
-			   struct rtnl_link_stats64 *stats)
+static int nfp_net_stat64(struct net_device *netdev,
+			  struct rtnl_link_stats64 *stats)
 {
 	struct nfp_net *nn = netdev_priv(netdev);
 	int r;
@@ -3400,6 +3400,8 @@ static void nfp_net_stat64(struct net_device *netdev,
 
 	stats->tx_dropped += nn_readq(nn, NFP_NET_CFG_STATS_TX_DISCARDS);
 	stats->tx_errors += nn_readq(nn, NFP_NET_CFG_STATS_TX_ERRORS);
+
+	return 0;
 }
 
 static int nfp_net_set_features(struct net_device *netdev,
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
index b3cabc274121..fc2569c50d46 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
@@ -86,13 +86,13 @@ nfp_repr_vnic_get_stats64(struct nfp_port *port,
 	stats->rx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_TX_DISCARDS);
 }
 
-static void
+static int
 nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 {
 	struct nfp_repr *repr = netdev_priv(netdev);
 
 	if (WARN_ON(!repr->port))
-		return;
+		return 0;
 
 	switch (repr->port->type) {
 	case NFP_PORT_PHYS_PORT:
@@ -106,6 +106,8 @@ nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 	default:
 		break;
 	}
+
+	return 0;
 }
 
 static bool
diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index 8724d6a9ed02..c2057cc6df9c 100644
--- a/drivers/net/ethernet/nvidia/forcedeth.c
+++ b/drivers/net/ethernet/nvidia/forcedeth.c
@@ -1764,7 +1764,7 @@ static void nv_get_stats(int cpu, struct fe_priv *np,
  * Called with read_lock(&dev_base_lock) held for read -
  * only synchronized against unregister_netdevice.
  */
-static void
+static int
 nv_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage)
 	__acquires(&netdev_priv(dev)->hwstats_lock)
 	__releases(&netdev_priv(dev)->hwstats_lock)
@@ -1812,6 +1812,8 @@ nv_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage)
 
 		spin_unlock_bh(&np->hwstats_lock);
 	}
+
+	return 0;
 }
 
 /*
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 11140915c2da..0c952604dedf 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -904,8 +904,8 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget)
 	return work_done;
 }
 
-void ionic_get_stats64(struct net_device *netdev,
-		       struct rtnl_link_stats64 *ns)
+int ionic_get_stats64(struct net_device *netdev,
+		      struct rtnl_link_stats64 *ns)
 {
 	struct ionic_lif *lif = netdev_priv(netdev);
 	struct ionic_lif_stats *ls;
@@ -955,6 +955,8 @@ void ionic_get_stats64(struct net_device *netdev,
 			ns->rx_missed_errors;
 
 	ns->tx_errors = ns->tx_aborted_errors;
+
+	return 0;
 }
 
 static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 9bed42719ae5..0e8c56696924 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -252,8 +252,8 @@ static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs)
 typedef void (*ionic_reset_cb)(struct ionic_lif *lif, void *arg);
 
 void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep);
-void ionic_get_stats64(struct net_device *netdev,
-		       struct rtnl_link_stats64 *ns);
+int ionic_get_stats64(struct net_device *netdev,
+		      struct rtnl_link_stats64 *ns);
 void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
 				struct ionic_deferred_work *work);
 int ionic_lif_alloc(struct ionic *ionic);
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index f21847739ef1..4ce1d74ac1d8 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -71,8 +71,8 @@ static irqreturn_t netxen_msix_intr(int irq, void *data);
 
 static void netxen_free_ip_list(struct netxen_adapter *, bool);
 static void netxen_restore_indev_addr(struct net_device *dev, unsigned long);
-static void netxen_nic_get_stats(struct net_device *dev,
-				 struct rtnl_link_stats64 *stats);
+static int netxen_nic_get_stats(struct net_device *dev,
+				struct rtnl_link_stats64 *stats);
 static int netxen_nic_set_mac(struct net_device *netdev, void *p);
 
 /*  PCI Device ID Table  */
@@ -2269,8 +2269,8 @@ static void netxen_tx_timeout_task(struct work_struct *work)
 	clear_bit(__NX_RESETTING, &adapter->state);
 }
 
-static void netxen_nic_get_stats(struct net_device *netdev,
-				 struct rtnl_link_stats64 *stats)
+static int netxen_nic_get_stats(struct net_device *netdev,
+				struct rtnl_link_stats64 *stats)
 {
 	struct netxen_adapter *adapter = netdev_priv(netdev);
 
@@ -2280,6 +2280,8 @@ static void netxen_nic_get_stats(struct net_device *netdev,
 	stats->tx_bytes = adapter->stats.txbytes;
 	stats->rx_dropped = adapter->stats.rxdropped;
 	stats->tx_dropped = adapter->stats.txdropped;
+
+	return 0;
 }
 
 static irqreturn_t netxen_intr(int irq, void *data)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index 4bf94797aac5..68628b9d305f 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -412,8 +412,8 @@ void qede_fill_by_demand_stats(struct qede_dev *edev)
 	}
 }
 
-static void qede_get_stats64(struct net_device *dev,
-			     struct rtnl_link_stats64 *stats)
+static int qede_get_stats64(struct net_device *dev,
+			    struct rtnl_link_stats64 *stats)
 {
 	struct qede_dev *edev = netdev_priv(dev);
 	struct qede_stats_common *p_common;
@@ -440,6 +440,8 @@ static void qede_get_stats64(struct net_device *dev,
 		stats->collisions = edev->stats.bb.tx_total_collisions;
 	stats->rx_crc_errors = p_common->rx_crc_errors;
 	stats->rx_frame_errors = p_common->rx_align_errors;
+
+	return 0;
 }
 
 #ifdef CONFIG_QED_SRIOV
diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c
index 8543bf3c3484..a48d3f3e7882 100644
--- a/drivers/net/ethernet/qualcomm/emac/emac.c
+++ b/drivers/net/ethernet/qualcomm/emac/emac.c
@@ -332,8 +332,8 @@ void emac_update_hw_stats(struct emac_adapter *adpt)
 }
 
 /* Provide network statistics info for the interface */
-static void emac_get_stats64(struct net_device *netdev,
-			     struct rtnl_link_stats64 *net_stats)
+static int emac_get_stats64(struct net_device *netdev,
+			    struct rtnl_link_stats64 *net_stats)
 {
 	struct emac_adapter *adpt = netdev_priv(netdev);
 	struct emac_stats *stats = &adpt->stats;
@@ -368,6 +368,8 @@ static void emac_get_stats64(struct net_device *netdev,
 	net_stats->tx_window_errors = stats->tx_late_col;
 
 	spin_unlock(&stats->lock);
+
+	return 0;
 }
 
 static const struct net_device_ops emac_netdev_ops = {
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
index 41fbd2ceeede..aca3e50de06c 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
@@ -122,8 +122,8 @@ static void rmnet_vnd_uninit(struct net_device *dev)
 	free_percpu(priv->pcpu_stats);
 }
 
-static void rmnet_get_stats64(struct net_device *dev,
-			      struct rtnl_link_stats64 *s)
+static int rmnet_get_stats64(struct net_device *dev,
+			     struct rtnl_link_stats64 *s)
 {
 	struct rmnet_priv *priv = netdev_priv(dev);
 	struct rmnet_vnd_stats total_stats;
@@ -151,6 +151,8 @@ static void rmnet_get_stats64(struct net_device *dev,
 	s->tx_packets = total_stats.tx_pkts;
 	s->tx_bytes = total_stats.tx_bytes;
 	s->tx_dropped = total_stats.tx_drops;
+
+	return 0;
 }
 
 static const struct net_device_ops rmnet_vnd_ops = {
@@ -354,4 +356,4 @@ int rmnet_vnd_update_dev_mtu(struct rmnet_port *port,
 	}
 
 	return 0;
-}
\ No newline at end of file
+}
diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c
index 1e5a453dea14..a654f7f6fb8d 100644
--- a/drivers/net/ethernet/realtek/8139too.c
+++ b/drivers/net/ethernet/realtek/8139too.c
@@ -654,8 +654,8 @@ static int rtl8139_poll(struct napi_struct *napi, int budget);
 static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance);
 static int rtl8139_close (struct net_device *dev);
 static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
-static void rtl8139_get_stats64(struct net_device *dev,
-				struct rtnl_link_stats64 *stats);
+static int rtl8139_get_stats64(struct net_device *dev,
+			       struct rtnl_link_stats64 *stats);
 static void rtl8139_set_rx_mode (struct net_device *dev);
 static void __set_rx_mode (struct net_device *dev);
 static void rtl8139_hw_start (struct net_device *dev);
@@ -2513,7 +2513,7 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 }
 
 
-static void
+static int
 rtl8139_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct rtl8139_private *tp = netdev_priv(dev);
@@ -2541,6 +2541,8 @@ rtl8139_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		stats->tx_packets = tp->tx_stats.packets;
 		stats->tx_bytes = tp->tx_stats.bytes;
 	} while (u64_stats_fetch_retry_irq(&tp->tx_stats.syncp, start));
+
+	return 0;
 }
 
 /* Set or clear the multicast filter for this adaptor.
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index c9abc7ccbb6f..02d61eaf0035 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -4733,7 +4733,7 @@ static int rtl_open(struct net_device *dev)
 	goto out;
 }
 
-static void
+static int
 rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
@@ -4766,6 +4766,8 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		le16_to_cpu(tp->tc_offset.rx_missed);
 
 	pm_runtime_put_noidle(&pdev->dev);
+
+	return 0;
 }
 
 static void rtl8169_net_suspend(struct rtl8169_private *tp)
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
index 971f1e54b652..a69d4df60ee3 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
@@ -1708,8 +1708,8 @@ static inline u64 sxgbe_get_stat64(void __iomem *ioaddr, int reg_lo, int reg_hi)
  *  executed to see device statistics. Statistics are number of
  *  bytes sent or received, errors occurred etc.
  */
-static void sxgbe_get_stats64(struct net_device *dev,
-			      struct rtnl_link_stats64 *stats)
+static int sxgbe_get_stats64(struct net_device *dev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct sxgbe_priv_data *priv = netdev_priv(dev);
 	void __iomem *ioaddr = priv->ioaddr;
@@ -1760,6 +1760,8 @@ static void sxgbe_get_stats64(struct net_device *dev,
 						 SXGBE_MMC_TXUFLWHI_GBCNT_REG);
 	writel(0, ioaddr + SXGBE_MMC_CTL_REG);
 	spin_unlock(&priv->stats_lock);
+
+	return 0;
 }
 
 /*  sxgbe_set_features - entry point to set offload features of the device.
diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c
index de797e1ac5a9..e76f5f961f61 100644
--- a/drivers/net/ethernet/sfc/efx_common.c
+++ b/drivers/net/ethernet/sfc/efx_common.c
@@ -597,13 +597,15 @@ void efx_stop_all(struct efx_nic *efx)
 }
 
 /* Context: process, dev_base_lock or RTNL held, non-blocking. */
-void efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats)
+int efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats)
 {
 	struct efx_nic *efx = netdev_priv(net_dev);
 
 	spin_lock_bh(&efx->stats_lock);
 	efx_nic_update_stats_atomic(efx, NULL, stats);
 	spin_unlock_bh(&efx->stats_lock);
+
+	return 0;
 }
 
 /* Push loopback/power/transmit disable settings to the PHY, and reconfigure
diff --git a/drivers/net/ethernet/sfc/efx_common.h b/drivers/net/ethernet/sfc/efx_common.h
index 65513fd0cf6c..96282cbebe3e 100644
--- a/drivers/net/ethernet/sfc/efx_common.h
+++ b/drivers/net/ethernet/sfc/efx_common.h
@@ -31,7 +31,7 @@ void efx_link_set_wanted_fc(struct efx_nic *efx, u8);
 void efx_start_all(struct efx_nic *efx);
 void efx_stop_all(struct efx_nic *efx);
 
-void efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats);
+int efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats);
 
 int efx_create_reset_workqueue(void);
 void efx_queue_reset_work(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c
index f8979991970e..78846737441c 100644
--- a/drivers/net/ethernet/sfc/falcon/efx.c
+++ b/drivers/net/ethernet/sfc/falcon/efx.c
@@ -2097,14 +2097,16 @@ int ef4_net_stop(struct net_device *net_dev)
 }
 
 /* Context: process, dev_base_lock or RTNL held, non-blocking. */
-static void ef4_net_stats(struct net_device *net_dev,
-			  struct rtnl_link_stats64 *stats)
+static int ef4_net_stats(struct net_device *net_dev,
+			 struct rtnl_link_stats64 *stats)
 {
 	struct ef4_nic *efx = netdev_priv(net_dev);
 
 	spin_lock_bh(&efx->stats_lock);
 	efx->type->update_stats(efx, NULL, stats);
 	spin_unlock_bh(&efx->stats_lock);
+
+	return 0;
 }
 
 /* Context: netif_tx_lock held, BHs disabled. */
diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c
index 501b9c7aba56..a62793cf2be5 100644
--- a/drivers/net/ethernet/socionext/sni_ave.c
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -1499,8 +1499,8 @@ static void ave_set_rx_mode(struct net_device *ndev)
 	}
 }
 
-static void ave_get_stats64(struct net_device *ndev,
-			    struct rtnl_link_stats64 *stats)
+static int ave_get_stats64(struct net_device *ndev,
+			   struct rtnl_link_stats64 *stats)
 {
 	struct ave_private *priv = netdev_priv(ndev);
 	unsigned int start;
@@ -1523,6 +1523,8 @@ static void ave_get_stats64(struct net_device *ndev,
 	stats->tx_dropped     = priv->stats_tx.dropped;
 	stats->rx_fifo_errors = priv->stats_rx.fifo_errors;
 	stats->collisions     = priv->stats_tx.collisions;
+
+	return 0;
 }
 
 static int ave_set_mac_address(struct net_device *ndev, void *p)
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index 68695d4afacd..aa5da0951020 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -6274,8 +6274,8 @@ static void niu_get_tx_stats(struct niu *np,
 	stats->tx_errors = errors;
 }
 
-static void niu_get_stats(struct net_device *dev,
-			  struct rtnl_link_stats64 *stats)
+static int niu_get_stats(struct net_device *dev,
+			 struct rtnl_link_stats64 *stats)
 {
 	struct niu *np = netdev_priv(dev);
 
@@ -6283,6 +6283,8 @@ static void niu_get_stats(struct net_device *dev,
 		niu_get_rx_stats(np, stats);
 		niu_get_tx_stats(np, stats);
 	}
+
+	return 0;
 }
 
 static void niu_load_hash_xmac(struct niu *np, u16 *hash)
diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c
index 26aa7f32151f..1381a4d6bcc9 100644
--- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c
+++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c
@@ -764,8 +764,8 @@ static netdev_tx_t xlgmac_xmit(struct sk_buff *skb, struct net_device *netdev)
 	return NETDEV_TX_OK;
 }
 
-static void xlgmac_get_stats64(struct net_device *netdev,
-			       struct rtnl_link_stats64 *s)
+static int xlgmac_get_stats64(struct net_device *netdev,
+			      struct rtnl_link_stats64 *s)
 {
 	struct xlgmac_pdata *pdata = netdev_priv(netdev);
 	struct xlgmac_stats *pstats = &pdata->stats;
@@ -787,6 +787,8 @@ static void xlgmac_get_stats64(struct net_device *netdev,
 	s->tx_bytes = pstats->txoctetcount_gb;
 	s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g;
 	s->tx_dropped = netdev->stats.tx_dropped;
+
+	return 0;
 }
 
 static int xlgmac_set_mac_address(struct net_device *netdev, void *addr)
diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
index 766e8866bbef..e2766299f720 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c
+++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
@@ -1407,8 +1407,8 @@ static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
 	return phy_mii_ioctl(port->slave.phy, req, cmd);
 }
 
-static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev,
-					 struct rtnl_link_stats64 *stats)
+static int am65_cpsw_nuss_ndo_get_stats(struct net_device *dev,
+					struct rtnl_link_stats64 *stats)
 {
 	struct am65_cpsw_ndev_priv *ndev_priv = netdev_priv(dev);
 	unsigned int start;
@@ -1439,6 +1439,8 @@ static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev,
 	stats->rx_errors	= dev->stats.rx_errors;
 	stats->rx_dropped	= dev->stats.rx_dropped;
 	stats->tx_dropped	= dev->stats.tx_dropped;
+
+	return 0;
 }
 
 static const struct net_device_ops am65_cpsw_nuss_netdev_ops = {
diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c
index d7a144b4a09f..2b6a6d027410 100644
--- a/drivers/net/ethernet/ti/netcp_core.c
+++ b/drivers/net/ethernet/ti/netcp_core.c
@@ -1908,7 +1908,7 @@ static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type,
 	return 0;
 }
 
-static void
+static int
 netcp_get_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
 {
 	struct netcp_intf *netcp = netdev_priv(ndev);
@@ -1937,6 +1937,8 @@ netcp_get_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
 	stats->rx_errors = p->rx_errors;
 	stats->rx_dropped = p->rx_dropped;
 	stats->tx_dropped = p->tx_dropped;
+
+	return 0;
 }
 
 static const struct net_device_ops netcp_netdev_ops = {
diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c
index 73ca597ebd1b..c4acb823cdad 100644
--- a/drivers/net/ethernet/via/via-rhine.c
+++ b/drivers/net/ethernet/via/via-rhine.c
@@ -507,8 +507,8 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance);
 static void rhine_tx(struct net_device *dev);
 static int rhine_rx(struct net_device *dev, int limit);
 static void rhine_set_rx_mode(struct net_device *dev);
-static void rhine_get_stats64(struct net_device *dev,
-			      struct rtnl_link_stats64 *stats);
+static int rhine_get_stats64(struct net_device *dev,
+			     struct rtnl_link_stats64 *stats);
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static const struct ethtool_ops netdev_ethtool_ops;
 static int  rhine_close(struct net_device *dev);
@@ -2207,7 +2207,7 @@ static void rhine_slow_event_task(struct work_struct *work)
 	mutex_unlock(&rp->task_lock);
 }
 
-static void
+static int
 rhine_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct rhine_private *rp = netdev_priv(dev);
@@ -2230,6 +2230,8 @@ rhine_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		stats->tx_packets = rp->tx_stats.packets;
 		stats->tx_bytes = rp->tx_stats.bytes;
 	} while (u64_stats_fetch_retry_irq(&rp->tx_stats.syncp, start));
+
+	return 0;
 }
 
 static void rhine_set_rx_mode(struct net_device *dev)
diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c
index 466622664424..df333411f40a 100644
--- a/drivers/net/fjes/fjes_main.c
+++ b/drivers/net/fjes/fjes_main.c
@@ -44,7 +44,7 @@ static void fjes_raise_intr_rxdata_task(struct work_struct *);
 static void fjes_tx_stall_task(struct work_struct *);
 static void fjes_force_close_task(struct work_struct *);
 static irqreturn_t fjes_intr(int, void*);
-static void fjes_get_stats64(struct net_device *, struct rtnl_link_stats64 *);
+static int fjes_get_stats64(struct net_device *, struct rtnl_link_stats64 *);
 static int fjes_change_mtu(struct net_device *, int);
 static int fjes_vlan_rx_add_vid(struct net_device *, __be16 proto, u16);
 static int fjes_vlan_rx_kill_vid(struct net_device *, __be16 proto, u16);
@@ -802,12 +802,14 @@ static void fjes_tx_retry(struct net_device *netdev, unsigned int txqueue)
 	netif_tx_wake_queue(queue);
 }
 
-static void
+static int
 fjes_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
 {
 	struct fjes_adapter *adapter = netdev_priv(netdev);
 
 	memcpy(stats, &adapter->stats64, sizeof(struct rtnl_link_stats64));
+
+	return 0;
 }
 
 static int fjes_change_mtu(struct net_device *netdev, int new_mtu)
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index f32f28311d57..0af96b314671 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -1357,8 +1357,8 @@ static void netvsc_get_pcpu_stats(struct net_device *net,
 	}
 }
 
-static void netvsc_get_stats64(struct net_device *net,
-			       struct rtnl_link_stats64 *t)
+static int netvsc_get_stats64(struct net_device *net,
+			      struct rtnl_link_stats64 *t)
 {
 	struct net_device_context *ndev_ctx = netdev_priv(net);
 	struct netvsc_device *nvdev;
@@ -1410,6 +1410,8 @@ static void netvsc_get_stats64(struct net_device *net,
 	}
 out:
 	rcu_read_unlock();
+
+	return 0;
 }
 
 static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index fa63d4dee0ba..de402f5045ef 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -123,8 +123,7 @@ static void ifb_ri_tasklet(unsigned long _txp)
 
 }
 
-static void ifb_stats64(struct net_device *dev,
-			struct rtnl_link_stats64 *stats)
+static int ifb_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct ifb_dev_private *dp = netdev_priv(dev);
 	struct ifb_q_private *txp = dp->tx_private;
@@ -151,6 +150,8 @@ static void ifb_stats64(struct net_device *dev,
 	}
 	stats->rx_dropped = dev->stats.rx_dropped;
 	stats->tx_dropped = dev->stats.tx_dropped;
+
+	return 0;
 }
 
 static int ifb_dev_init(struct net_device *dev)
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index a707502a0c0f..7c4d88579f46 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -286,8 +286,8 @@ static void ipvlan_set_multicast_mac_filter(struct net_device *dev)
 	dev_mc_sync(ipvlan->phy_dev, dev);
 }
 
-static void ipvlan_get_stats64(struct net_device *dev,
-			       struct rtnl_link_stats64 *s)
+static int ipvlan_get_stats64(struct net_device *dev,
+			      struct rtnl_link_stats64 *s)
 {
 	struct ipvl_dev *ipvlan = netdev_priv(dev);
 
@@ -324,6 +324,8 @@ static void ipvlan_get_stats64(struct net_device *dev,
 		s->rx_dropped = rx_errs;
 		s->tx_dropped = tx_drps;
 	}
+
+	return 0;
 }
 
 static int ipvlan_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index a1c77cc00416..3d76829161a6 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -115,8 +115,8 @@ void dev_lstats_read(struct net_device *dev, u64 *packets, u64 *bytes)
 }
 EXPORT_SYMBOL(dev_lstats_read);
 
-static void loopback_get_stats64(struct net_device *dev,
-				 struct rtnl_link_stats64 *stats)
+static int loopback_get_stats64(struct net_device *dev,
+				struct rtnl_link_stats64 *stats)
 {
 	u64 packets, bytes;
 
@@ -126,6 +126,8 @@ static void loopback_get_stats64(struct net_device *dev,
 	stats->tx_packets = packets;
 	stats->rx_bytes   = bytes;
 	stats->tx_bytes   = bytes;
+
+	return 0;
 }
 
 static u32 always_on(struct net_device *dev)
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 92425e1fd70c..ef05c5cf3abc 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -3645,16 +3645,18 @@ static int macsec_change_mtu(struct net_device *dev, int new_mtu)
 	return 0;
 }
 
-static void macsec_get_stats64(struct net_device *dev,
-			       struct rtnl_link_stats64 *s)
+static int macsec_get_stats64(struct net_device *dev,
+			      struct rtnl_link_stats64 *s)
 {
 	if (!dev->tstats)
-		return;
+		return 0;
 
 	dev_fetch_sw_netstats(s, dev->tstats);
 
 	s->rx_dropped = dev->stats.rx_dropped;
 	s->tx_dropped = dev->stats.tx_dropped;
+
+	return 0;
 }
 
 static int macsec_get_iflink(const struct net_device *dev)
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 9a9a5cf36a4b..bdcf38564320 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -927,8 +927,8 @@ static void macvlan_uninit(struct net_device *dev)
 		macvlan_port_destroy(port->dev);
 }
 
-static void macvlan_dev_get_stats64(struct net_device *dev,
-				    struct rtnl_link_stats64 *stats)
+static int macvlan_dev_get_stats64(struct net_device *dev,
+				   struct rtnl_link_stats64 *stats)
 {
 	struct macvlan_dev *vlan = netdev_priv(dev);
 
@@ -965,6 +965,8 @@ static void macvlan_dev_get_stats64(struct net_device *dev,
 		stats->rx_dropped	= rx_errors;
 		stats->tx_dropped	= tx_dropped;
 	}
+
+	return 0;
 }
 
 static int macvlan_vlan_rx_add_vid(struct net_device *dev,
diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c
index 5f3a4cc92a88..57454b505cce 100644
--- a/drivers/net/mhi_net.c
+++ b/drivers/net/mhi_net.c
@@ -89,8 +89,8 @@ static int mhi_ndo_xmit(struct sk_buff *skb, struct net_device *ndev)
 	return NETDEV_TX_OK;
 }
 
-static void mhi_ndo_get_stats64(struct net_device *ndev,
-				struct rtnl_link_stats64 *stats)
+static int mhi_ndo_get_stats64(struct net_device *ndev,
+			       struct rtnl_link_stats64 *stats)
 {
 	struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
 	unsigned int start;
@@ -110,6 +110,8 @@ static void mhi_ndo_get_stats64(struct net_device *ndev,
 		stats->tx_errors = u64_stats_read(&mhi_netdev->stats.tx_errors);
 		stats->tx_dropped = u64_stats_read(&mhi_netdev->stats.tx_dropped);
 	} while (u64_stats_fetch_retry_irq(&mhi_netdev->stats.tx_syncp, start));
+
+	return 0;
 }
 
 static const struct net_device_ops mhi_netdev_ops = {
diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index 4f83165412bd..e032ad1c5e22 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -179,8 +179,8 @@ static void net_failover_fold_stats(struct rtnl_link_stats64 *_res,
 	}
 }
 
-static void net_failover_get_stats(struct net_device *dev,
-				   struct rtnl_link_stats64 *stats)
+static int net_failover_get_stats(struct net_device *dev,
+				  struct rtnl_link_stats64 *stats)
 {
 	struct net_failover_info *nfo_info = netdev_priv(dev);
 	struct rtnl_link_stats64 temp;
@@ -209,6 +209,8 @@ static void net_failover_get_stats(struct net_device *dev,
 
 	memcpy(&nfo_info->failover_stats, stats, sizeof(*stats));
 	spin_unlock(&nfo_info->stats_lock);
+
+	return 0;
 }
 
 static int net_failover_change_mtu(struct net_device *dev, int new_mtu)
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index aec92440eef1..88d86930b941 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -60,7 +60,7 @@ static int nsim_change_mtu(struct net_device *dev, int new_mtu)
 	return 0;
 }
 
-static void
+static int
 nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct netdevsim *ns = netdev_priv(dev);
@@ -71,6 +71,8 @@ nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		stats->tx_bytes = ns->tx_bytes;
 		stats->tx_packets = ns->tx_packets;
 	} while (u64_stats_fetch_retry(&ns->syncp, start));
+
+	return 0;
 }
 
 static int
diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c
index 5e19a6839dea..ac405cab8b2d 100644
--- a/drivers/net/nlmon.c
+++ b/drivers/net/nlmon.c
@@ -48,7 +48,7 @@ static int nlmon_close(struct net_device *dev)
 	return netlink_remove_tap(&nlmon->nt);
 }
 
-static void
+static int
 nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	u64 packets, bytes;
@@ -60,6 +60,8 @@ nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 
 	stats->rx_bytes = bytes;
 	stats->tx_bytes = 0;
+
+	return 0;
 }
 
 static u32 always_on(struct net_device *dev)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 09c27f7773f9..fe6c6b62b0f6 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1488,7 +1488,7 @@ ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 	return err;
 }
 
-static void
+static int
 ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
 {
 	struct ppp *ppp = netdev_priv(dev);
@@ -1508,6 +1508,8 @@ ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
 	stats64->rx_dropped       = dev->stats.rx_dropped;
 	stats64->tx_dropped       = dev->stats.tx_dropped;
 	stats64->rx_length_errors = dev->stats.rx_length_errors;
+
+	return 0;
 }
 
 static int ppp_dev_init(struct net_device *dev)
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index f81fb0b13a94..1f3fda7f410f 100644
--- a/drivers/net/slip/slip.c
+++ b/drivers/net/slip/slip.c
@@ -570,7 +570,7 @@ static int sl_change_mtu(struct net_device *dev, int new_mtu)
 
 /* Netdevice get statistics request */
 
-static void
+static int
 sl_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct net_device_stats *devstats = &dev->stats;
@@ -601,6 +601,8 @@ sl_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		stats->collisions     += comp->sls_o_misses;
 	}
 #endif
+
+	return 0;
 }
 
 /* Netdevice register callback */
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index c19dac21c468..dc9b179a7fd6 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1835,7 +1835,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
 	return err;
 }
 
-static void
+static int
 team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct team *team = netdev_priv(dev);
@@ -1872,6 +1872,8 @@ team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	stats->rx_dropped	= rx_dropped;
 	stats->tx_dropped	= tx_dropped;
 	stats->rx_nohandler	= rx_nohandler;
+
+	return 0;
 }
 
 static int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c
index ed3743dc62b9..6ddf1e70308c 100644
--- a/drivers/net/thunderbolt.c
+++ b/drivers/net/thunderbolt.c
@@ -1144,8 +1144,8 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb,
 	return NETDEV_TX_OK;
 }
 
-static void tbnet_get_stats64(struct net_device *dev,
-			      struct rtnl_link_stats64 *stats)
+static int tbnet_get_stats64(struct net_device *dev,
+			     struct rtnl_link_stats64 *stats)
 {
 	struct tbnet *net = netdev_priv(dev);
 
@@ -1161,6 +1161,8 @@ static void tbnet_get_stats64(struct net_device *dev,
 	stats->rx_over_errors = net->stats.rx_over_errors;
 	stats->rx_crc_errors = net->stats.rx_crc_errors;
 	stats->rx_missed_errors = net->stats.rx_missed_errors;
+
+	return 0;
 }
 
 static const struct net_device_ops tbnet_netdev_ops = {
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 02a93cfdb6b1..12685d2bcc22 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1089,7 +1089,7 @@ static void tun_set_headroom(struct net_device *dev, int new_hr)
 	tun->align = new_hr;
 }
 
-static void
+static int
 tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct tun_struct *tun = netdev_priv(dev);
@@ -1098,6 +1098,8 @@ tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 
 	stats->rx_frame_errors +=
 		(unsigned long)atomic_long_read(&tun->rx_frame_errors);
+
+	return 0;
 }
 
 static int tun_xdp_set(struct net_device *dev, struct bpf_prog *prog,
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 02bfcdf50a7a..49a1db371bbf 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -361,8 +361,8 @@ static void veth_stats_rx(struct veth_stats *result, struct net_device *dev)
 	}
 }
 
-static void veth_get_stats64(struct net_device *dev,
-			     struct rtnl_link_stats64 *tot)
+static int veth_get_stats64(struct net_device *dev,
+			    struct rtnl_link_stats64 *tot)
 {
 	struct veth_priv *priv = netdev_priv(dev);
 	struct net_device *peer;
@@ -393,6 +393,8 @@ static void veth_get_stats64(struct net_device *dev,
 		tot->tx_packets += rx.xdp_packets;
 	}
 	rcu_read_unlock();
+
+	return 0;
 }
 
 /* fake multicast ability */
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 508408fbe78f..c7cc59f265ad 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1741,8 +1741,8 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p)
 	return ret;
 }
 
-static void virtnet_stats(struct net_device *dev,
-			  struct rtnl_link_stats64 *tot)
+static int virtnet_stats(struct net_device *dev,
+			 struct rtnl_link_stats64 *tot)
 {
 	struct virtnet_info *vi = netdev_priv(dev);
 	unsigned int start;
@@ -1777,6 +1777,8 @@ static void virtnet_stats(struct net_device *dev,
 	tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
 	tot->rx_length_errors = dev->stats.rx_length_errors;
 	tot->rx_frame_errors = dev->stats.rx_frame_errors;
+
+	return 0;
 }
 
 static void virtnet_ack_link_announce(struct virtnet_info *vi)
diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
index 7ec8652f2c26..446f57f802f7 100644
--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c
+++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
@@ -113,7 +113,7 @@ vmxnet3_global_stats[] = {
 };
 
 
-void
+int
 vmxnet3_get_stats64(struct net_device *netdev,
 		   struct rtnl_link_stats64 *stats)
 {
@@ -160,6 +160,8 @@ vmxnet3_get_stats64(struct net_device *netdev,
 		stats->rx_dropped += drvRxStats->drop_total;
 		stats->multicast +=  devRxStats->mcastPktsRxOK;
 	}
+
+	return 0;
 }
 
 static int
diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h
index d958b92c9429..14739197fe26 100644
--- a/drivers/net/vmxnet3/vmxnet3_int.h
+++ b/drivers/net/vmxnet3/vmxnet3_int.h
@@ -484,8 +484,8 @@ vmxnet3_create_queues(struct vmxnet3_adapter *adapter,
 
 void vmxnet3_set_ethtool_ops(struct net_device *netdev);
 
-void vmxnet3_get_stats64(struct net_device *dev,
-			 struct rtnl_link_stats64 *stats);
+int vmxnet3_get_stats64(struct net_device *dev,
+			struct rtnl_link_stats64 *stats);
 
 extern char vmxnet3_driver_name[];
 #endif
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 6d9130859c55..3300c006f696 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -145,8 +145,8 @@ static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb)
 	kfree_skb(skb);
 }
 
-static void vrf_get_stats64(struct net_device *dev,
-			    struct rtnl_link_stats64 *stats)
+static int vrf_get_stats64(struct net_device *dev,
+			   struct rtnl_link_stats64 *stats)
 {
 	int i;
 
@@ -170,6 +170,8 @@ static void vrf_get_stats64(struct net_device *dev,
 		stats->rx_bytes += rbytes;
 		stats->rx_packets += rpkts;
 	}
+
+	return 0;
 }
 
 static struct vrf_map *netns_vrf_map(struct net *net)
diff --git a/drivers/net/vsockmon.c b/drivers/net/vsockmon.c
index b1bb1b04b664..4716dc0af471 100644
--- a/drivers/net/vsockmon.c
+++ b/drivers/net/vsockmon.c
@@ -55,13 +55,15 @@ static netdev_tx_t vsockmon_xmit(struct sk_buff *skb, struct net_device *dev)
 	return NETDEV_TX_OK;
 }
 
-static void
+static int
 vsockmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	dev_lstats_read(dev, &stats->rx_packets, &stats->rx_bytes);
 
 	stats->tx_packets = 0;
 	stats->tx_bytes = 0;
+
+	return 0;
 }
 
 static int vsockmon_is_valid_mtu(int new_mtu)
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index b01848ef4649..84f08e42e362 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1247,8 +1247,8 @@ static int xennet_change_mtu(struct net_device *dev, int mtu)
 	return 0;
 }
 
-static void xennet_get_stats64(struct net_device *dev,
-			       struct rtnl_link_stats64 *tot)
+static int xennet_get_stats64(struct net_device *dev,
+			      struct rtnl_link_stats64 *tot)
 {
 	struct netfront_info *np = netdev_priv(dev);
 	int cpu;
@@ -1279,6 +1279,8 @@ static void xennet_get_stats64(struct net_device *dev,
 
 	tot->rx_errors  = dev->stats.rx_errors;
 	tot->tx_dropped = dev->stats.tx_dropped;
+
+	return 0;
 }
 
 static void xennet_release_tx_bufs(struct netfront_queue *queue)
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 6f5ddc3eab8c..ef96dcebb92a 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -1135,7 +1135,7 @@ netdev_features_t qeth_fix_features(struct net_device *, netdev_features_t);
 netdev_features_t qeth_features_check(struct sk_buff *skb,
 				      struct net_device *dev,
 				      netdev_features_t features);
-void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats);
+int qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats);
 int qeth_set_real_num_tx_queues(struct qeth_card *card, unsigned int count);
 u16 qeth_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
 			  u8 cast_type, struct net_device *sb_dev);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index f4b60294a969..b0efc2a7c498 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -7129,7 +7129,7 @@ netdev_features_t qeth_features_check(struct sk_buff *skb,
 }
 EXPORT_SYMBOL_GPL(qeth_features_check);
 
-void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+int qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	struct qeth_card *card = dev->ml_priv;
 	struct qeth_qdio_out_q *queue;
@@ -7158,6 +7158,8 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 		stats->tx_errors += queue->stats.tx_errors;
 		stats->tx_dropped += queue->stats.tx_dropped;
 	}
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(qeth_get_stats64);
 
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
index d524e92051a3..7badd3b0939e 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -358,8 +358,8 @@ static int dpaa2_switch_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 						    addr);
 }
 
-static void dpaa2_switch_port_get_stats(struct net_device *netdev,
-					struct rtnl_link_stats64 *stats)
+static int dpaa2_switch_port_get_stats(struct net_device *netdev,
+				       struct rtnl_link_stats64 *stats)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	u64 tmp;
@@ -418,10 +418,8 @@ static void dpaa2_switch_port_get_stats(struct net_device *netdev,
 	if (err)
 		goto error;
 
-	return;
-
 error:
-	netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
+	return err;
 }
 
 static bool dpaa2_switch_port_has_offload_stats(const struct net_device *netdev,
diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c
index 69ea61faf8fa..cfe39e2c493b 100644
--- a/drivers/staging/netlogic/xlr_net.c
+++ b/drivers/staging/netlogic/xlr_net.c
@@ -317,7 +317,7 @@ static void xlr_set_rx_mode(struct net_device *ndev)
 	xlr_nae_wreg(priv->base_addr, R_MAC_FILTER_CONFIG, regval);
 }
 
-static void xlr_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
+static int xlr_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
 {
 	struct xlr_net_priv *priv = netdev_priv(ndev);
 
@@ -360,6 +360,8 @@ static void xlr_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
 						 TX_DROP_FRAME_COUNTER);
 	stats->tx_fifo_errors = xlr_nae_rdreg(priv->base_addr,
 					      TX_DROP_FRAME_COUNTER);
+
+	return 0;
 }
 
 static const struct net_device_ops xlr_netdev_ops = {
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9faaacbfe45a..bd471f1e1fa3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1039,8 +1039,8 @@ struct netdev_net_notifier {
  *	Callback used when the transmitter has not made any progress
  *	for dev->watchdog ticks.
  *
- * void (*ndo_get_stats64)(struct net_device *dev,
- *                         struct rtnl_link_stats64 *storage);
+ * int (*ndo_get_stats64)(struct net_device *dev,
+ *			  struct rtnl_link_stats64 *storage);
  * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
  *	Called when a user wants to get the network device usage
  *	statistics. Drivers must do one of the following:
@@ -1304,7 +1304,7 @@ struct net_device_ops {
 	void			(*ndo_tx_timeout) (struct net_device *dev,
 						   unsigned int txqueue);
 
-	void			(*ndo_get_stats64)(struct net_device *dev,
+	int			(*ndo_get_stats64)(struct net_device *dev,
 						   struct rtnl_link_stats64 *storage);
 	bool			(*ndo_has_offload_stats)(const struct net_device *dev, int attr_id);
 	int			(*ndo_get_offload_stats)(int attr_id,
@@ -4545,12 +4545,12 @@ void netdev_notify_peers(struct net_device *dev);
 void netdev_features_change(struct net_device *dev);
 /* Load a device via the kmod */
 void dev_load(struct net *net, const char *name);
-void dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage);
+int dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage);
 void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
 			     const struct net_device_stats *netdev_stats);
 void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
 			   const struct pcpu_sw_netstats __percpu *netstats);
-void dev_get_tstats64(struct net_device *dev, struct rtnl_link_stats64 *s);
+int dev_get_tstats64(struct net_device *dev, struct rtnl_link_stats64 *s);
 
 extern int		netdev_max_backlog;
 extern int		netdev_tstamp_prequeue;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index ec8408d1638f..4d31184cbfe4 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -683,8 +683,8 @@ static int vlan_ethtool_get_ts_info(struct net_device *dev,
 	return 0;
 }
 
-static void vlan_dev_get_stats64(struct net_device *dev,
-				 struct rtnl_link_stats64 *stats)
+static int vlan_dev_get_stats64(struct net_device *dev,
+				struct rtnl_link_stats64 *stats)
 {
 	struct vlan_pcpu_stats *p;
 	u32 rx_errors = 0, tx_dropped = 0;
@@ -715,6 +715,8 @@ static void vlan_dev_get_stats64(struct net_device *dev,
 	}
 	stats->rx_errors  = rx_errors;
 	stats->tx_dropped = tx_dropped;
+
+	return 0;
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
diff --git a/net/core/dev.c b/net/core/dev.c
index 2b423d7a39e7..dfbd66ba3cad 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -10403,13 +10403,14 @@ EXPORT_SYMBOL(netdev_stats_to_stats64);
  *	dev->netdev_ops->get_stats64 or dev->netdev_ops->get_stats;
  *	otherwise the internal statistics structure is used.
  */
-void dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage)
+int dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
+	int err = 0;
 
 	if (ops->ndo_get_stats64) {
 		memset(storage, 0, sizeof(*storage));
-		ops->ndo_get_stats64(dev, storage);
+		err = ops->ndo_get_stats64(dev, storage);
 	} else if (ops->ndo_get_stats) {
 		netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev));
 	} else {
@@ -10418,6 +10419,8 @@ void dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage)
 	storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped);
 	storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped);
 	storage->rx_nohandler += (unsigned long)atomic_long_read(&dev->rx_nohandler);
+
+	return err;
 }
 EXPORT_SYMBOL(dev_get_stats);
 
@@ -10463,10 +10466,12 @@ EXPORT_SYMBOL_GPL(dev_fetch_sw_netstats);
  *	Populate @s from dev->stats and dev->tstats. Can be used as
  *	ndo_get_stats64() callback.
  */
-void dev_get_tstats64(struct net_device *dev, struct rtnl_link_stats64 *s)
+int dev_get_tstats64(struct net_device *dev, struct rtnl_link_stats64 *s)
 {
 	netdev_stats_to_stats64(s, &dev->stats);
 	dev_fetch_sw_netstats(s, dev->tstats);
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(dev_get_tstats64);
 
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index 6cd97c75445c..9a554d47a816 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -87,8 +87,8 @@ static netdev_tx_t l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev
 	return NETDEV_TX_OK;
 }
 
-static void l2tp_eth_get_stats64(struct net_device *dev,
-				 struct rtnl_link_stats64 *stats)
+static int l2tp_eth_get_stats64(struct net_device *dev,
+				struct rtnl_link_stats64 *stats)
 {
 	struct l2tp_eth *priv = netdev_priv(dev);
 
@@ -98,6 +98,8 @@ static void l2tp_eth_get_stats64(struct net_device *dev,
 	stats->rx_bytes   = (unsigned long)atomic_long_read(&priv->rx_bytes);
 	stats->rx_packets = (unsigned long)atomic_long_read(&priv->rx_packets);
 	stats->rx_errors  = (unsigned long)atomic_long_read(&priv->rx_errors);
+
+	return 0;
 }
 
 static const struct net_device_ops l2tp_eth_netdev_ops = {
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 3b9ec4ef81c3..a2cb9dc8ada0 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -698,10 +698,12 @@ static u16 ieee80211_netdev_select_queue(struct net_device *dev,
 	return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
 }
 
-static void
+static int
 ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
 	dev_fetch_sw_netstats(stats, dev->tstats);
+
+	return 0;
 }
 
 static const struct net_device_ops ieee80211_dataif_ops = {
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 2f1f0a378408..eeca5c6acb80 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -398,8 +398,8 @@ static int teql_master_close(struct net_device *dev)
 	return 0;
 }
 
-static void teql_master_stats64(struct net_device *dev,
-				struct rtnl_link_stats64 *stats)
+static int teql_master_stats64(struct net_device *dev,
+			       struct rtnl_link_stats64 *stats)
 {
 	struct teql_master *m = netdev_priv(dev);
 
@@ -407,6 +407,8 @@ static void teql_master_stats64(struct net_device *dev,
 	stats->tx_bytes		= m->tx_bytes;
 	stats->tx_errors	= m->tx_errors;
 	stats->tx_dropped	= m->tx_dropped;
+
+	return 0;
 }
 
 static int teql_master_mtu(struct net_device *dev, int new_mtu)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 09/16] scsi: fcoe: propagate errors from dev_get_stats
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (7 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 08/16] net: allow ndo_get_stats64 to return an int error code Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 10/16] net: openvswitch: " Vladimir Oltean
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The FCoE callback for the Link Error Status Block retrieves the FCS
error count using dev_get_stats. This function can now return errors.
Propagate these all the way to the sysfs device attributes.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
Patch is new (Eric's suggestion).

 drivers/scsi/fcoe/fcoe_sysfs.c     |  9 +++++++--
 drivers/scsi/fcoe/fcoe_transport.c | 24 +++++++++++++++---------
 drivers/scsi/libfc/fc_rport.c      |  5 ++++-
 include/scsi/fcoe_sysfs.h          | 12 ++++++------
 include/scsi/libfc.h               |  2 +-
 include/scsi/libfcoe.h             |  8 ++++----
 6 files changed, 37 insertions(+), 23 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
index af658aa38fed..a197e66ffa8a 100644
--- a/drivers/scsi/fcoe/fcoe_sysfs.c
+++ b/drivers/scsi/fcoe/fcoe_sysfs.c
@@ -139,8 +139,13 @@ static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
 					    char *buf)			\
 {									\
 	struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);		\
-	if (ctlr->f->get_fcoe_ctlr_##field)				\
-		ctlr->f->get_fcoe_ctlr_##field(ctlr);			\
+	int err;							\
+									\
+	if (ctlr->f->get_fcoe_ctlr_##field) {				\
+		err = ctlr->f->get_fcoe_ctlr_##field(ctlr);		\
+		if (err)						\
+			return err;					\
+	}								\
 	return snprintf(buf, sz, format_string,				\
 			cast fcoe_ctlr_##field(ctlr));			\
 }
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 213ee9efb044..5d19650e9bc1 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -166,15 +166,16 @@ EXPORT_SYMBOL_GPL(fcoe_link_speed_update);
  * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6
  * Clause 7.11 in v1.04.
  */
-void __fcoe_get_lesb(struct fc_lport *lport,
-		     struct fc_els_lesb *fc_lesb,
-		     struct net_device *netdev)
+int __fcoe_get_lesb(struct fc_lport *lport,
+		    struct fc_els_lesb *fc_lesb,
+		    struct net_device *netdev)
 {
 	struct rtnl_link_stats64 dev_stats;
 	unsigned int cpu;
 	u32 lfc, vlfc, mdac;
 	struct fc_stats *stats;
 	struct fcoe_fc_els_lesb *lesb;
+	int err;
 
 	lfc = 0;
 	vlfc = 0;
@@ -190,8 +191,14 @@ void __fcoe_get_lesb(struct fc_lport *lport,
 	lesb->lesb_link_fail = htonl(lfc);
 	lesb->lesb_vlink_fail = htonl(vlfc);
 	lesb->lesb_miss_fka = htonl(mdac);
-	dev_get_stats(netdev, &dev_stats);
+
+	err = dev_get_stats(netdev, &dev_stats);
+	if (err)
+		return err;
+
 	lesb->lesb_fcs_error = htonl(dev_stats.rx_crc_errors);
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(__fcoe_get_lesb);
 
@@ -200,12 +207,11 @@ EXPORT_SYMBOL_GPL(__fcoe_get_lesb);
  * @lport: the local port
  * @fc_lesb: the link error status block
  */
-void fcoe_get_lesb(struct fc_lport *lport,
-			 struct fc_els_lesb *fc_lesb)
+int fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb)
 {
 	struct net_device *netdev = fcoe_get_netdev(lport);
 
-	__fcoe_get_lesb(lport, fc_lesb, netdev);
+	return __fcoe_get_lesb(lport, fc_lesb, netdev);
 }
 EXPORT_SYMBOL_GPL(fcoe_get_lesb);
 
@@ -215,14 +221,14 @@ EXPORT_SYMBOL_GPL(fcoe_get_lesb);
  * @ctlr_dev: The given fcoe controller device
  *
  */
-void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
+int fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
 {
 	struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
 	struct net_device *netdev = fcoe_get_netdev(fip->lp);
 	struct fc_els_lesb *fc_lesb;
 
 	fc_lesb = (struct fc_els_lesb *)(&ctlr_dev->lesb);
-	__fcoe_get_lesb(fip->lp, fc_lesb, netdev);
+	return __fcoe_get_lesb(fip->lp, fc_lesb, netdev);
 }
 EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb);
 
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 56003208d2e7..cb299fef7a78 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -1633,6 +1633,7 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 	struct fc_els_lesb *lesb;
 	struct fc_seq_els_data rjt_data;
 	struct fc_host_statistics *hst;
+	int err;
 
 	lockdep_assert_held(&rdata->rp_mutex);
 
@@ -1659,7 +1660,9 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 	lesb = &rsp->rls_lesb;
 	if (lport->tt.get_lesb) {
 		/* get LESB from LLD if it supports it */
-		lport->tt.get_lesb(lport, lesb);
+		err = lport->tt.get_lesb(lport, lesb);
+		if (err)
+			goto out_rjt;
 	} else {
 		fc_get_host_stats(lport->host);
 		hst = &lport->host_stats;
diff --git a/include/scsi/fcoe_sysfs.h b/include/scsi/fcoe_sysfs.h
index 4b1216de3f22..076b593f2625 100644
--- a/include/scsi/fcoe_sysfs.h
+++ b/include/scsi/fcoe_sysfs.h
@@ -16,12 +16,12 @@ struct fcoe_ctlr_device;
 struct fcoe_fcf_device;
 
 struct fcoe_sysfs_function_template {
-	void (*get_fcoe_ctlr_link_fail)(struct fcoe_ctlr_device *);
-	void (*get_fcoe_ctlr_vlink_fail)(struct fcoe_ctlr_device *);
-	void (*get_fcoe_ctlr_miss_fka)(struct fcoe_ctlr_device *);
-	void (*get_fcoe_ctlr_symb_err)(struct fcoe_ctlr_device *);
-	void (*get_fcoe_ctlr_err_block)(struct fcoe_ctlr_device *);
-	void (*get_fcoe_ctlr_fcs_error)(struct fcoe_ctlr_device *);
+	int  (*get_fcoe_ctlr_link_fail)(struct fcoe_ctlr_device *);
+	int  (*get_fcoe_ctlr_vlink_fail)(struct fcoe_ctlr_device *);
+	int  (*get_fcoe_ctlr_miss_fka)(struct fcoe_ctlr_device *);
+	int  (*get_fcoe_ctlr_symb_err)(struct fcoe_ctlr_device *);
+	int  (*get_fcoe_ctlr_err_block)(struct fcoe_ctlr_device *);
+	int  (*get_fcoe_ctlr_fcs_error)(struct fcoe_ctlr_device *);
 	void (*set_fcoe_ctlr_mode)(struct fcoe_ctlr_device *);
 	int  (*set_fcoe_ctlr_enabled)(struct fcoe_ctlr_device *);
 	void (*get_fcoe_fcf_selected)(struct fcoe_fcf_device *);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 9b87e1a1c646..510654796db5 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -501,7 +501,7 @@ struct libfc_function_template {
 	 *
 	 * STATUS: OPTIONAL
 	 */
-	void (*get_lesb)(struct fc_lport *, struct fc_els_lesb *lesb);
+	int (*get_lesb)(struct fc_lport *, struct fc_els_lesb *lesb);
 
 	/*
 	 * Reset an exchange manager, completing all sequences and exchanges.
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 2568cb0627ec..a42cbe847ce6 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -255,13 +255,13 @@ int fcoe_libfc_config(struct fc_lport *, struct fcoe_ctlr *,
 u32 fcoe_fc_crc(struct fc_frame *fp);
 int fcoe_start_io(struct sk_buff *skb);
 int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type);
-void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb,
-		     struct net_device *netdev);
+int __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb,
+		    struct net_device *netdev);
 void fcoe_wwn_to_str(u64 wwn, char *buf, int len);
 int fcoe_validate_vport_create(struct fc_vport *vport);
 int fcoe_link_speed_update(struct fc_lport *);
-void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
-void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev);
+int fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
+int fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev);
 
 /**
  * is_fip_mode() - returns true if FIP mode selected.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 10/16] net: openvswitch: propagate errors from dev_get_stats
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (8 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 09/16] scsi: fcoe: propagate errors from dev_get_stats Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 11/16] net: " Vladimir Oltean
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The dev_get_stats function can now return an error code, so the code
that retrieves vport statistics and sends them through netlink needs to
propagate that error code.

Modify the drastic BUG_ON checks to operate only on the -EMSGSIZE error
code (the only error code previously possible), and not crash the kernel
in case dev_get_stats fails. This is in line with what rtnetlink.c does.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
Still keeping the BUG_ON condition except for the output of
ovs_vport_get_stats.

Changes in v4:
Patch is new (Eric's suggestion).

 net/openvswitch/datapath.c | 25 +++++++++++++++++++------
 net/openvswitch/vport.c    | 10 ++++++++--
 net/openvswitch/vport.h    |  2 +-
 3 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 9d6ef6cb9b26..160b8dc453da 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1987,7 +1987,10 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
 			goto nla_put_failure;
 	}
 
-	ovs_vport_get_stats(vport, &vport_stats);
+	err = ovs_vport_get_stats(vport, &vport_stats);
+	if (err)
+		goto error;
+
 	if (nla_put_64bit(skb, OVS_VPORT_ATTR_STATS,
 			  sizeof(struct ovs_vport_stats), &vport_stats,
 			  OVS_VPORT_ATTR_PAD))
@@ -2028,7 +2031,9 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
 
 	retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd,
 					 GFP_KERNEL);
-	BUG_ON(retval < 0);
+	BUG_ON(retval == -EMSGSIZE);
+	if (retval)
+		return ERR_PTR(retval);
 
 	return skb;
 }
@@ -2173,6 +2178,9 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
 				      OVS_VPORT_CMD_NEW, GFP_KERNEL);
+	BUG_ON(err == -EMSGSIZE);
+	if (err)
+		goto exit_unlock_free;
 
 	new_headroom = netdev_get_fwd_headroom(vport->dev);
 
@@ -2181,7 +2189,6 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 	else
 		netdev_set_rx_headroom(vport->dev, dp->max_headroom);
 
-	BUG_ON(err < 0);
 	ovs_unlock();
 
 	ovs_notify(&dp_vport_genl_family, reply, info);
@@ -2234,7 +2241,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
 				      OVS_VPORT_CMD_SET, GFP_KERNEL);
-	BUG_ON(err < 0);
+	BUG_ON(err == -EMSGSIZE);
+	if (err)
+		goto exit_unlock_free;
 
 	ovs_unlock();
 	ovs_notify(&dp_vport_genl_family, reply, info);
@@ -2274,7 +2283,9 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
 				      OVS_VPORT_CMD_DEL, GFP_KERNEL);
-	BUG_ON(err < 0);
+	BUG_ON(err == -EMSGSIZE);
+	if (err)
+		goto exit_unlock_free;
 
 	/* the vport deletion may trigger dp headroom update */
 	dp = vport->dp;
@@ -2321,7 +2332,9 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
 				      OVS_VPORT_CMD_GET, GFP_ATOMIC);
-	BUG_ON(err < 0);
+	BUG_ON(err == -EMSGSIZE);
+	if (err)
+		goto exit_unlock_free;
 	rcu_read_unlock();
 
 	return genlmsg_reply(reply, info);
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 215a818bf9ce..e66c949fd97a 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -267,11 +267,15 @@ void ovs_vport_del(struct vport *vport)
  *
  * Must be called with ovs_mutex or rcu_read_lock.
  */
-void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
+int ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
 {
 	struct rtnl_link_stats64 dev_stats;
+	int err;
+
+	err = dev_get_stats(vport->dev, &dev_stats);
+	if (err)
+		return err;
 
-	dev_get_stats(vport->dev, &dev_stats);
 	stats->rx_errors  = dev_stats.rx_errors;
 	stats->tx_errors  = dev_stats.tx_errors;
 	stats->tx_dropped = dev_stats.tx_dropped;
@@ -281,6 +285,8 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
 	stats->rx_packets = dev_stats.rx_packets;
 	stats->tx_bytes	  = dev_stats.tx_bytes;
 	stats->tx_packets = dev_stats.tx_packets;
+
+	return 0;
 }
 
 /**
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index 1eb7495ac5b4..8927ba5c491b 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -30,7 +30,7 @@ void ovs_vport_del(struct vport *);
 
 struct vport *ovs_vport_locate(const struct net *net, const char *name);
 
-void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *);
+int ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *);
 
 int ovs_vport_set_options(struct vport *, struct nlattr *options);
 int ovs_vport_get_options(const struct vport *, struct sk_buff *);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 11/16] net: propagate errors from dev_get_stats
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (9 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 10/16] net: openvswitch: " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 17:22   ` Vladimir Oltean
  2021-01-11 10:55   ` Dan Carpenter
  2021-01-08 16:31 ` [PATCH v5 net-next 12/16] net: terminate " Vladimir Oltean
                   ` (4 subsequent siblings)
  15 siblings, 2 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

dev_get_stats can now return error codes. Take the remaining call sites
where those errors can be propagated, which are all trivial, and convert
them to look at that error code and stop processing.

The effects of simulating a kernel error (returning -ENOMEM) upon
existing programs or kernel interfaces:

- cat /proc/net/dev prints up until the interface that failed, and there
  it returns:
cat: read error: Cannot allocate memory

- ifstat simply returns this and prints nothing:
Error: Buffer too small for object.
Dump terminated

- ip -s -s link show behaves the same as ifstat.

- ifconfig prints only the info for the interfaces whose statistics did
  not fail.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
- Actually propagate errors from bonding and net_failover from within
  this patch.
- Properly propagating the dev_get_stats() error code from
  rtnl_fill_stats now, and not -EMSGSIZE.

Changes in v4:
Patch is new (Eric's suggestion).

 drivers/net/bonding/bond_main.c     | 24 ++++++++++++++++-----
 drivers/net/net_failover.c          | 33 ++++++++++++++++++++++-------
 drivers/parisc/led.c                |  7 +++++-
 drivers/usb/gadget/function/rndis.c |  4 +++-
 net/8021q/vlanproc.c                |  7 ++++--
 net/core/net-procfs.c               | 16 ++++++++++----
 net/core/net-sysfs.c                |  4 +++-
 net/core/rtnetlink.c                | 15 +++++++++----
 8 files changed, 84 insertions(+), 26 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 5a3178b3dba3..2352ef64486b 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1757,7 +1757,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
 
 	slave_dev->priv_flags |= IFF_BONDING;
 	/* initialize slave stats */
-	dev_get_stats(new_slave->dev, &new_slave->slave_stats);
+	res = dev_get_stats(new_slave->dev, &new_slave->slave_stats);
+	if (res) {
+		slave_err(bond_dev, slave_dev, "dev_get_stats returned %d\n",
+			  res);
+		goto err_close;
+	}
 
 	if (bond_is_lb(bond)) {
 		/* bond_alb_init_slave() must be called before all other stages since
@@ -2094,7 +2099,13 @@ static int __bond_release_one(struct net_device *bond_dev,
 	bond_sysfs_slave_del(slave);
 
 	/* recompute stats just before removing the slave */
-	bond_get_stats(bond->dev, &bond->bond_stats);
+	err = bond_get_stats(bond->dev, &bond->bond_stats);
+	if (err) {
+		slave_info(bond_dev, slave_dev, "dev_get_stats returned %d\n",
+			   err);
+		unblock_netpoll_tx();
+		return err;
+	}
 
 	bond_upper_dev_unlink(bond, slave);
 	/* unregister rx_handler early so bond_handle_frame wouldn't be called
@@ -3743,7 +3754,7 @@ static int bond_get_stats(struct net_device *bond_dev,
 	struct list_head *iter;
 	struct slave *slave;
 	int nest_level = 0;
-
+	int res = 0;
 
 	rcu_read_lock();
 #ifdef CONFIG_LOCKDEP
@@ -3754,7 +3765,9 @@ static int bond_get_stats(struct net_device *bond_dev,
 	memcpy(stats, &bond->bond_stats, sizeof(*stats));
 
 	bond_for_each_slave_rcu(bond, slave, iter) {
-		dev_get_stats(slave->dev, &temp);
+		res = dev_get_stats(slave->dev, &temp);
+		if (res)
+			goto out;
 
 		bond_fold_stats(stats, &temp, &slave->slave_stats);
 
@@ -3763,10 +3776,11 @@ static int bond_get_stats(struct net_device *bond_dev,
 	}
 
 	memcpy(&bond->bond_stats, stats, sizeof(*stats));
+out:
 	spin_unlock(&bond->stats_lock);
 	rcu_read_unlock();
 
-	return 0;
+	return res;
 }
 
 static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index e032ad1c5e22..7f70555e68d1 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -185,6 +185,7 @@ static int net_failover_get_stats(struct net_device *dev,
 	struct net_failover_info *nfo_info = netdev_priv(dev);
 	struct rtnl_link_stats64 temp;
 	struct net_device *slave_dev;
+	int err = 0;
 
 	spin_lock(&nfo_info->stats_lock);
 	memcpy(stats, &nfo_info->failover_stats, sizeof(*stats));
@@ -193,24 +194,29 @@ static int net_failover_get_stats(struct net_device *dev,
 
 	slave_dev = rcu_dereference(nfo_info->primary_dev);
 	if (slave_dev) {
-		dev_get_stats(slave_dev, &temp);
+		err = dev_get_stats(slave_dev, &temp);
+		if (err)
+			goto out;
 		net_failover_fold_stats(stats, &temp, &nfo_info->primary_stats);
 		memcpy(&nfo_info->primary_stats, &temp, sizeof(temp));
 	}
 
 	slave_dev = rcu_dereference(nfo_info->standby_dev);
 	if (slave_dev) {
-		dev_get_stats(slave_dev, &temp);
+		err = dev_get_stats(slave_dev, &temp);
+		if (err)
+			goto out;
 		net_failover_fold_stats(stats, &temp, &nfo_info->standby_stats);
 		memcpy(&nfo_info->standby_stats, &temp, sizeof(temp));
 	}
 
+out:
 	rcu_read_unlock();
 
 	memcpy(&nfo_info->failover_stats, stats, sizeof(*stats));
 	spin_unlock(&nfo_info->stats_lock);
 
-	return 0;
+	return err;
 }
 
 static int net_failover_change_mtu(struct net_device *dev, int new_mtu)
@@ -545,11 +551,15 @@ static int net_failover_slave_register(struct net_device *slave_dev,
 	if (slave_is_standby) {
 		rcu_assign_pointer(nfo_info->standby_dev, slave_dev);
 		standby_dev = slave_dev;
-		dev_get_stats(standby_dev, &nfo_info->standby_stats);
+		err = dev_get_stats(standby_dev, &nfo_info->standby_stats);
+		if (err)
+			goto err_stats_get;
 	} else {
 		rcu_assign_pointer(nfo_info->primary_dev, slave_dev);
 		primary_dev = slave_dev;
-		dev_get_stats(primary_dev, &nfo_info->primary_stats);
+		err = dev_get_stats(primary_dev, &nfo_info->primary_stats);
+		if (err)
+			goto err_stats_get;
 		failover_dev->min_mtu = slave_dev->min_mtu;
 		failover_dev->max_mtu = slave_dev->max_mtu;
 	}
@@ -564,6 +574,8 @@ static int net_failover_slave_register(struct net_device *slave_dev,
 
 	return 0;
 
+err_stats_get:
+	vlan_vids_del_by_dev(slave_dev, failover_dev);
 err_vlan_add:
 	dev_uc_unsync(slave_dev, failover_dev);
 	dev_mc_unsync(slave_dev, failover_dev);
@@ -597,6 +609,7 @@ static int net_failover_slave_unregister(struct net_device *slave_dev,
 	struct net_device *standby_dev, *primary_dev;
 	struct net_failover_info *nfo_info;
 	bool slave_is_standby;
+	int err;
 
 	nfo_info = netdev_priv(failover_dev);
 	primary_dev = rtnl_dereference(nfo_info->primary_dev);
@@ -611,7 +624,8 @@ static int net_failover_slave_unregister(struct net_device *slave_dev,
 	dev_close(slave_dev);
 
 	nfo_info = netdev_priv(failover_dev);
-	dev_get_stats(failover_dev, &nfo_info->failover_stats);
+	/* Proceed with the deregistration anyway */
+	err = dev_get_stats(failover_dev, &nfo_info->failover_stats);
 
 	slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent;
 	if (slave_is_standby) {
@@ -631,7 +645,7 @@ static int net_failover_slave_unregister(struct net_device *slave_dev,
 	netdev_info(failover_dev, "failover %s slave:%s unregistered\n",
 		    slave_is_standby ? "standby" : "primary", slave_dev->name);
 
-	return 0;
+	return err;
 }
 
 static int net_failover_slave_link_change(struct net_device *slave_dev,
@@ -639,6 +653,7 @@ static int net_failover_slave_link_change(struct net_device *slave_dev,
 {
 	struct net_device *primary_dev, *standby_dev;
 	struct net_failover_info *nfo_info;
+	int err;
 
 	nfo_info = netdev_priv(failover_dev);
 
@@ -653,7 +668,9 @@ static int net_failover_slave_link_change(struct net_device *slave_dev,
 		netif_carrier_on(failover_dev);
 		netif_tx_wake_all_queues(failover_dev);
 	} else {
-		dev_get_stats(failover_dev, &nfo_info->failover_stats);
+		err = dev_get_stats(failover_dev, &nfo_info->failover_stats);
+		if (err)
+			return err;
 		netif_carrier_off(failover_dev);
 		netif_tx_stop_all_queues(failover_dev);
 	}
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index cc6108785323..d17d0fbf878d 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -370,7 +370,12 @@ static __inline__ int led_get_net_activity(void)
 
 		in_dev_put(in_dev);
 
-		dev_get_stats(dev, &stats);
+		retval = dev_get_stats(dev, &stats);
+		if (retval) {
+			netif_lists_unlock(&init_net);
+			return retval;
+		}
+
 		rx_total += stats.rx_packets;
 		tx_total += stats.tx_packets;
 	}
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 7ec29e007ae9..bec474819c3d 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -198,7 +198,9 @@ static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
 	resp->InformationBufferOffset = cpu_to_le32(16);
 
 	net = params->dev;
-	dev_get_stats(net, &stats);
+	retval = dev_get_stats(net, &stats);
+	if (retval)
+		return retval;
 
 	switch (OID) {
 
diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c
index 3a6682d79630..d89b75804834 100644
--- a/net/8021q/vlanproc.c
+++ b/net/8021q/vlanproc.c
@@ -244,12 +244,15 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset)
 	const struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
 	static const char fmt64[] = "%30s %12llu\n";
 	struct rtnl_link_stats64 stats;
-	int i;
+	int err, i;
 
 	if (!is_vlan_dev(vlandev))
 		return 0;
 
-	dev_get_stats(vlandev, &stats);
+	err = dev_get_stats(vlandev, &stats);
+	if (err)
+		return err;
+
 	seq_printf(seq,
 		   "%s  VID: %d	 REORDER_HDR: %i  dev->priv_flags: %hx\n",
 		   vlandev->name, vlan->vlan_id,
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index 64666ba7ccab..ee19c35f6e00 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -78,11 +78,14 @@ static void dev_seq_stop(struct seq_file *seq, void *v)
 	netif_lists_unlock(net);
 }
 
-static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
+static int dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
 {
 	struct rtnl_link_stats64 stats;
+	int err;
 
-	dev_get_stats(dev, &stats);
+	err = dev_get_stats(dev, &stats);
+	if (err)
+		return err;
 
 	seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
 		   "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
@@ -101,6 +104,8 @@ static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
 		    stats.tx_window_errors +
 		    stats.tx_heartbeat_errors,
 		   stats.tx_compressed);
+
+	return 0;
 }
 
 /*
@@ -109,6 +114,8 @@ static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
  */
 static int dev_seq_show(struct seq_file *seq, void *v)
 {
+	int err = 0;
+
 	if (v == SEQ_START_TOKEN)
 		seq_puts(seq, "Inter-|   Receive                            "
 			      "                    |  Transmit\n"
@@ -116,8 +123,9 @@ static int dev_seq_show(struct seq_file *seq, void *v)
 			      "compressed multicast|bytes    packets errs "
 			      "drop fifo colls carrier compressed\n");
 	else
-		dev_seq_printf_stats(seq, v);
-	return 0;
+		err = dev_seq_printf_stats(seq, v);
+
+	return err;
 }
 
 static u32 softnet_backlog_len(struct softnet_data *sd)
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 5d89c85b42d4..6f789e178e92 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -588,7 +588,9 @@ static ssize_t netstat_show(const struct device *d,
 	if (dev_isalive(dev)) {
 		struct rtnl_link_stats64 stats;
 
-		dev_get_stats(dev, &stats);
+		ret = dev_get_stats(dev, &stats);
+		if (ret)
+			return ret;
 
 		ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *)&stats) + offset));
 	}
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index bb0596c41b3e..656c482befac 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1201,6 +1201,7 @@ static noinline_for_stack int rtnl_fill_stats(struct sk_buff *skb,
 {
 	struct rtnl_link_stats64 *sp;
 	struct nlattr *attr;
+	int err;
 
 	attr = nla_reserve_64bit(skb, IFLA_STATS64,
 				 sizeof(struct rtnl_link_stats64), IFLA_PAD);
@@ -1208,7 +1209,9 @@ static noinline_for_stack int rtnl_fill_stats(struct sk_buff *skb,
 		return -EMSGSIZE;
 
 	sp = nla_data(attr);
-	dev_get_stats(dev, sp);
+	err = dev_get_stats(dev, sp);
+	if (err)
+		return err;
 
 	attr = nla_reserve(skb, IFLA_STATS,
 			   sizeof(struct rtnl_link_stats));
@@ -1707,6 +1710,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
 {
 	struct ifinfomsg *ifm;
 	struct nlmsghdr *nlh;
+	int err = -EMSGSIZE;
 
 	ASSERT_RTNL();
 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
@@ -1780,7 +1784,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
 	if (rtnl_phys_switch_id_fill(skb, dev))
 		goto nla_put_failure;
 
-	if (rtnl_fill_stats(skb, dev))
+	err = rtnl_fill_stats(skb, dev);
+	if (err)
 		goto nla_put_failure;
 
 	if (rtnl_fill_vf(skb, dev, ext_filter_mask))
@@ -1826,7 +1831,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
 	rcu_read_unlock();
 nla_put_failure:
 	nlmsg_cancel(skb, nlh);
-	return -EMSGSIZE;
+	return err;
 }
 
 static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
@@ -5135,7 +5140,9 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
 			goto nla_put_failure;
 
 		sp = nla_data(attr);
-		dev_get_stats(dev, sp);
+		err = dev_get_stats(dev, sp);
+		if (err)
+			goto nla_put_failure;
 	}
 
 	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, *idxattr)) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 12/16] net: terminate errors from dev_get_stats
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (10 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 11/16] net: " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 13/16] net: openvswitch: ensure dev_get_stats can sleep Vladimir Oltean
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

dev_get_stats can now return errors. Some call sites are coming from a
context that returns void (ethtool stats, workqueue context). So since
we can't report to the upper layer, do the next best thing: shout.

This patch wraps up the conversion of existing dev_get_stats callers, so
we can add the __must_check attribute now to ensure that future callers
keep doing this too.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
Added the __must_check to dev_get_stats.

Changes in v4:
Patch is new (Eric's suggestion).

 arch/s390/appldata/appldata_net_sum.c               | 10 ++++++++--
 drivers/leds/trigger/ledtrig-netdev.c               |  9 ++++++++-
 drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c |  9 +++++++--
 drivers/net/ethernet/hisilicon/hns/hns_ethtool.c    |  7 ++++++-
 drivers/net/ethernet/intel/e1000e/ethtool.c         |  9 +++++++--
 drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c    |  9 +++++++--
 drivers/net/ethernet/intel/ixgbevf/ethtool.c        |  9 +++++++--
 include/linux/netdevice.h                           |  3 ++-
 net/core/dev.c                                      |  3 ++-
 9 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c
index 6146606ac9a3..72cb5344e488 100644
--- a/arch/s390/appldata/appldata_net_sum.c
+++ b/arch/s390/appldata/appldata_net_sum.c
@@ -58,11 +58,11 @@ struct appldata_net_sum_data {
  */
 static void appldata_get_net_sum_data(void *data)
 {
-	int i;
 	struct appldata_net_sum_data *net_data;
 	struct net_device *dev;
 	unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes, rx_errors,
 			tx_errors, rx_dropped, tx_dropped, collisions;
+	int ret, i;
 
 	net_data = data;
 	net_data->sync_count_1++;
@@ -83,7 +83,13 @@ static void appldata_get_net_sum_data(void *data)
 	for_each_netdev(&init_net, dev) {
 		struct rtnl_link_stats64 stats;
 
-		dev_get_stats(dev, &stats);
+		ret = dev_get_stats(dev, &stats);
+		if (ret) {
+			netif_lists_unlock(&init_net);
+			netdev_err(dev, "dev_get_stats returned %d\n", ret);
+			return;
+		}
+
 		rx_packets += stats.rx_packets;
 		tx_packets += stats.tx_packets;
 		rx_bytes   += stats.rx_bytes;
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 4382ee278309..c717b7e7dd81 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -351,6 +351,7 @@ static void netdev_trig_work(struct work_struct *work)
 	unsigned int new_activity;
 	unsigned long interval;
 	int invert;
+	int err;
 
 	/* If we dont have a device, insure we are off */
 	if (!trigger_data->net_dev) {
@@ -363,7 +364,13 @@ static void netdev_trig_work(struct work_struct *work)
 	    !test_bit(NETDEV_LED_RX, &trigger_data->mode))
 		return;
 
-	dev_get_stats(trigger_data->net_dev, &dev_stats);
+	err = dev_get_stats(trigger_data->net_dev, &dev_stats);
+	if (err) {
+		netdev_err(trigger_data->net_dev,
+			   "dev_get_stats returned %d\n", err);
+		return;
+	}
+
 	new_activity =
 	    (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
 		dev_stats.tx_packets : 0) +
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
index ada70425b48c..aab6a81f0438 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
@@ -266,9 +266,14 @@ static void xgene_get_ethtool_stats(struct net_device *ndev,
 {
 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 	struct rtnl_link_stats64 stats;
-	int i;
+	int err, i;
+
+	err = dev_get_stats(ndev, &stats);
+	if (err) {
+		netdev_err(ndev, "dev_get_stats returned %d\n", err);
+		return;
+	}
 
-	dev_get_stats(ndev, &stats);
 	for (i = 0; i < XGENE_STATS_LEN; i++)
 		data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset);
 
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index ee2172011051..d05fa7b3f6e0 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -840,6 +840,7 @@ static void hns_get_ethtool_stats(struct net_device *netdev,
 	struct hns_nic_priv *priv = netdev_priv(netdev);
 	struct hnae_handle *h = priv->ae_handle;
 	struct rtnl_link_stats64 net_stats;
+	int err;
 
 	if (!h->dev->ops->get_stats || !h->dev->ops->update_stats) {
 		netdev_err(netdev, "get_stats or update_stats is null!\n");
@@ -848,7 +849,11 @@ static void hns_get_ethtool_stats(struct net_device *netdev,
 
 	h->dev->ops->update_stats(h, &netdev->stats);
 
-	dev_get_stats(netdev, &net_stats);
+	err = dev_get_stats(netdev, &net_stats);
+	if (err) {
+		netdev_err(netdev, "dev_get_stats returned %d\n", err);
+		return;
+	}
 
 	/* get netdev statistics */
 	p[0] = net_stats.rx_packets;
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index 06442e6bef73..41bd3e0598ce 100644
--- a/drivers/net/ethernet/intel/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -2060,15 +2060,20 @@ static void e1000_get_ethtool_stats(struct net_device *netdev,
 {
 	struct e1000_adapter *adapter = netdev_priv(netdev);
 	struct rtnl_link_stats64 net_stats;
-	int i;
 	char *p = NULL;
+	int err, i;
 
 	pm_runtime_get_sync(netdev->dev.parent);
 
-	dev_get_stats(netdev, &net_stats);
+	err = dev_get_stats(netdev, &net_stats);
 
 	pm_runtime_put_sync(netdev->dev.parent);
 
+	if (err) {
+		netdev_err(netdev, "dev_get_stats returned %d\n", err);
+		return;
+	}
+
 	for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
 		switch (e1000_gstrings_stats[i].type) {
 		case NETDEV_STATS:
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index 2b8084664403..a647e2774f76 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -1298,11 +1298,16 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
 	struct rtnl_link_stats64 net_stats;
 	unsigned int start;
 	struct ixgbe_ring *ring;
-	int i, j;
 	char *p = NULL;
+	int err, i, j;
 
 	ixgbe_update_stats(adapter);
-	dev_get_stats(netdev, &net_stats);
+	err = dev_get_stats(netdev, &net_stats);
+	if (err) {
+		netdev_err(netdev, "dev_get_stats returned %d\n", err);
+		return;
+	}
+
 	for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
 		switch (ixgbe_gstrings_stats[i].type) {
 		case NETDEV_STATS:
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index 3b9b7e5c2998..665e39301092 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -423,11 +423,16 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev,
 	struct rtnl_link_stats64 net_stats;
 	unsigned int start;
 	struct ixgbevf_ring *ring;
-	int i, j;
+	int err, i, j;
 	char *p;
 
 	ixgbevf_update_stats(adapter);
-	dev_get_stats(netdev, &net_stats);
+	err = dev_get_stats(netdev, &net_stats);
+	if (err) {
+		netdev_err(netdev, "dev_get_stats returned %d\n", err);
+		return;
+	}
+
 	for (i = 0; i < IXGBEVF_GLOBAL_STATS_LEN; i++) {
 		switch (ixgbevf_gstrings_stats[i].type) {
 		case NETDEV_STATS:
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index bd471f1e1fa3..b1aebab916a8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4545,7 +4545,8 @@ void netdev_notify_peers(struct net_device *dev);
 void netdev_features_change(struct net_device *dev);
 /* Load a device via the kmod */
 void dev_load(struct net *net, const char *name);
-int dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage);
+int __must_check dev_get_stats(struct net_device *dev,
+			       struct rtnl_link_stats64 *storage);
 void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
 			     const struct net_device_stats *netdev_stats);
 void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
diff --git a/net/core/dev.c b/net/core/dev.c
index dfbd66ba3cad..30facac95d5e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -10403,7 +10403,8 @@ EXPORT_SYMBOL(netdev_stats_to_stats64);
  *	dev->netdev_ops->get_stats64 or dev->netdev_ops->get_stats;
  *	otherwise the internal statistics structure is used.
  */
-int dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage)
+int __must_check dev_get_stats(struct net_device *dev,
+			       struct rtnl_link_stats64 *storage)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
 	int err = 0;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 13/16] net: openvswitch: ensure dev_get_stats can sleep
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (11 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 12/16] net: terminate " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 14/16] net: net_failover: ensure .ndo_get_stats64 " Vladimir Oltean
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

There is an effort to convert .ndo_get_stats64 to sleepable context, and
for that to work, we need to prevent callers of dev_get_stats from using
atomic locking.

The OVS vport driver calls ovs_vport_get_stats from
ovs_vport_cmd_fill_info, a function with 7 callers: 5 under ovs_lock() and
2 under rcu_read_lock(). The RCU-protected callers are the doit and
dumpit callbacks of the OVS_VPORT_CMD_GET genetlink event. Things have
been this way ever since the OVS introduction in commit ccb1352e76cf
("net: Add Open vSwitch kernel components."), probably so that
OVS_PORT_CMD_GET doesn't have to serialize with all the others through
ovs_mutex. Sadly, now they do have to, otherwise we don't have
protection while accessing the datapath and vport structures.

Convert all callers of ovs_vport_cmd_fill_info to assume ovs_mutex
protection. This means that we can get rid of the gfp argument, since
all callers are now sleepable, we can just use GFP_KERNEL for memory
allocation.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
Patch is new.

 net/openvswitch/datapath.c | 38 ++++++++++++++++++--------------------
 net/openvswitch/vport.c    |  2 +-
 2 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 160b8dc453da..318caa8f12c2 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1957,10 +1957,10 @@ static struct genl_family dp_datapath_genl_family __ro_after_init = {
 	.module = THIS_MODULE,
 };
 
-/* Called with ovs_mutex or RCU read lock. */
+/* Called with ovs_mutex */
 static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
 				   struct net *net, u32 portid, u32 seq,
-				   u32 flags, u8 cmd, gfp_t gfp)
+				   u32 flags, u8 cmd)
 {
 	struct ovs_header *ovs_header;
 	struct ovs_vport_stats vport_stats;
@@ -1981,7 +1981,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
 		goto nla_put_failure;
 
 	if (!net_eq(net, dev_net(vport->dev))) {
-		int id = peernet2id_alloc(net, dev_net(vport->dev), gfp);
+		int id = peernet2id_alloc(net, dev_net(vport->dev), GFP_KERNEL);
 
 		if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id))
 			goto nla_put_failure;
@@ -2029,8 +2029,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
 	if (!skb)
 		return ERR_PTR(-ENOMEM);
 
-	retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd,
-					 GFP_KERNEL);
+	retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd);
 	BUG_ON(retval == -EMSGSIZE);
 	if (retval)
 		return ERR_PTR(retval);
@@ -2038,7 +2037,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
 	return skb;
 }
 
-/* Called with ovs_mutex or RCU read lock. */
+/* Called with ovs_mutex */
 static struct vport *lookup_vport(struct net *net,
 				  const struct ovs_header *ovs_header,
 				  struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
@@ -2177,7 +2176,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
-				      OVS_VPORT_CMD_NEW, GFP_KERNEL);
+				      OVS_VPORT_CMD_NEW);
 	BUG_ON(err == -EMSGSIZE);
 	if (err)
 		goto exit_unlock_free;
@@ -2240,7 +2239,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
-				      OVS_VPORT_CMD_SET, GFP_KERNEL);
+				      OVS_VPORT_CMD_SET);
 	BUG_ON(err == -EMSGSIZE);
 	if (err)
 		goto exit_unlock_free;
@@ -2282,7 +2281,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
 
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
-				      OVS_VPORT_CMD_DEL, GFP_KERNEL);
+				      OVS_VPORT_CMD_DEL);
 	BUG_ON(err == -EMSGSIZE);
 	if (err)
 		goto exit_unlock_free;
@@ -2324,23 +2323,23 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
 	if (!reply)
 		return -ENOMEM;
 
-	rcu_read_lock();
+	ovs_lock();
 	vport = lookup_vport(sock_net(skb->sk), ovs_header, a);
 	err = PTR_ERR(vport);
 	if (IS_ERR(vport))
 		goto exit_unlock_free;
 	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
 				      info->snd_portid, info->snd_seq, 0,
-				      OVS_VPORT_CMD_GET, GFP_ATOMIC);
+				      OVS_VPORT_CMD_GET);
 	BUG_ON(err == -EMSGSIZE);
 	if (err)
 		goto exit_unlock_free;
-	rcu_read_unlock();
+	ovs_unlock();
 
 	return genlmsg_reply(reply, info);
 
 exit_unlock_free:
-	rcu_read_unlock();
+	ovs_unlock();
 	kfree_skb(reply);
 	return err;
 }
@@ -2352,25 +2351,24 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 	int bucket = cb->args[0], skip = cb->args[1];
 	int i, j = 0;
 
-	rcu_read_lock();
-	dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex);
+	ovs_lock();
+	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
 	if (!dp) {
-		rcu_read_unlock();
+		ovs_unlock();
 		return -ENODEV;
 	}
 	for (i = bucket; i < DP_VPORT_HASH_BUCKETS; i++) {
 		struct vport *vport;
 
 		j = 0;
-		hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node) {
+		hlist_for_each_entry(vport, &dp->ports[i], dp_hash_node) {
 			if (j >= skip &&
 			    ovs_vport_cmd_fill_info(vport, skb,
 						    sock_net(skb->sk),
 						    NETLINK_CB(cb->skb).portid,
 						    cb->nlh->nlmsg_seq,
 						    NLM_F_MULTI,
-						    OVS_VPORT_CMD_GET,
-						    GFP_ATOMIC) < 0)
+						    OVS_VPORT_CMD_GET) < 0)
 				goto out;
 
 			j++;
@@ -2378,7 +2376,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 		skip = 0;
 	}
 out:
-	rcu_read_unlock();
+	ovs_unlock();
 
 	cb->args[0] = i;
 	cb->args[1] = j;
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index e66c949fd97a..ba1a52addff2 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -265,7 +265,7 @@ void ovs_vport_del(struct vport *vport)
  *
  * Retrieves transmit, receive, and error stats for the given device.
  *
- * Must be called with ovs_mutex or rcu_read_lock.
+ * Must be called with ovs_mutex.
  */
 int ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
 {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 14/16] net: net_failover: ensure .ndo_get_stats64 can sleep
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (12 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 13/16] net: openvswitch: ensure dev_get_stats can sleep Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 15/16] net: bonding: " Vladimir Oltean
  2021-01-08 16:31 ` [PATCH v5 net-next 16/16] net: mark ndo_get_stats64 as being able to sleep Vladimir Oltean
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The failover framework sets up a virtio_net interface [ when it has the
VIRTIO_NET_F_STANDBY feature ] and a VF interface, having the same MAC
address, in a standby/active relationship. When the active VF is
unplugged, the standby virtio_net temporarily kicks in.

The failover framework registers a common upper for the active and the
standby interface, which is what the application layer uses. This is
similar to bonding/team. The statistics of the upper interface are the
sum of the statistics of the active and of the standby interface.

There is an effort to convert .ndo_get_stats64 to sleepable context, and
for that to work, we need to prevent callers of dev_get_stats from using
atomic locking. The failover driver needs protection via an RCU
read-side critical section to access the standby and the active
interface. This has two features:
- It is atomic: this needs to change.
- It is reentrant: this is ok, because generally speaking, dev_get_stats
  is recursive, and taking global locks is a bad thing from a recursive
  context.

The existing logic can be rehashed just a little bit such that the
recursive dev_get_stats call will not be under any lock. We can achieve
that by "cheating" a little bit and use dev_hold() to take a reference
on the active and backup interfaces, and netdev_wait_allrefs() will just
have to wait until dev_get_stats() finishes.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
Use rcu_read_lock() and do not change the locking architecture of the
driver.

Changes in v4:
Now there is code to propagate errors.

Changes in v3:
None.

Changes in v2:
Switched to the new scheme of holding just a refcnt to the slave
interfaces while recursing with dev_get_stats.

 drivers/net/net_failover.c | 64 ++++++++++++++++++++++++++++----------
 1 file changed, 47 insertions(+), 17 deletions(-)

diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index 7f70555e68d1..3e8a4046c748 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -183,38 +183,64 @@ static int net_failover_get_stats(struct net_device *dev,
 				  struct rtnl_link_stats64 *stats)
 {
 	struct net_failover_info *nfo_info = netdev_priv(dev);
-	struct rtnl_link_stats64 temp;
-	struct net_device *slave_dev;
+	struct rtnl_link_stats64 primary_stats;
+	struct rtnl_link_stats64 standby_stats;
+	struct net_device *primary_dev;
+	struct net_device *standby_dev;
 	int err = 0;
 
-	spin_lock(&nfo_info->stats_lock);
-	memcpy(stats, &nfo_info->failover_stats, sizeof(*stats));
-
 	rcu_read_lock();
 
-	slave_dev = rcu_dereference(nfo_info->primary_dev);
-	if (slave_dev) {
-		err = dev_get_stats(slave_dev, &temp);
+	primary_dev = rcu_dereference(nfo_info->primary_dev);
+	if (primary_dev)
+		dev_hold(primary_dev);
+
+	standby_dev = rcu_dereference(nfo_info->standby_dev);
+	if (standby_dev)
+		dev_hold(standby_dev);
+
+	rcu_read_unlock();
+
+	/* Don't hold rcu_read_lock while calling dev_get_stats, just a
+	 * reference to ensure they won't get unregistered.
+	 */
+	if (primary_dev) {
+		err = dev_get_stats(primary_dev, &primary_stats);
 		if (err)
 			goto out;
-		net_failover_fold_stats(stats, &temp, &nfo_info->primary_stats);
-		memcpy(&nfo_info->primary_stats, &temp, sizeof(temp));
 	}
 
-	slave_dev = rcu_dereference(nfo_info->standby_dev);
-	if (slave_dev) {
-		err = dev_get_stats(slave_dev, &temp);
+	if (standby_dev) {
+		err = dev_get_stats(standby_dev, &standby_stats);
 		if (err)
 			goto out;
-		net_failover_fold_stats(stats, &temp, &nfo_info->standby_stats);
-		memcpy(&nfo_info->standby_stats, &temp, sizeof(temp));
 	}
 
-out:
-	rcu_read_unlock();
+	spin_lock(&nfo_info->stats_lock);
+
+	memcpy(stats, &nfo_info->failover_stats, sizeof(*stats));
+
+	if (primary_dev) {
+		net_failover_fold_stats(stats, &primary_stats,
+					&nfo_info->primary_stats);
+		memcpy(&nfo_info->primary_stats, &primary_stats,
+		       sizeof(primary_stats));
+	}
+	if (standby_dev) {
+		net_failover_fold_stats(stats, &standby_stats,
+					&nfo_info->standby_stats);
+		memcpy(&nfo_info->standby_stats, &standby_stats,
+		       sizeof(standby_stats));
+	}
 
 	memcpy(&nfo_info->failover_stats, stats, sizeof(*stats));
+
 	spin_unlock(&nfo_info->stats_lock);
+out:
+	if (primary_dev)
+		dev_put(primary_dev);
+	if (standby_dev)
+		dev_put(standby_dev);
 
 	return err;
 }
@@ -728,6 +754,7 @@ static struct failover_ops net_failover_ops = {
 struct failover *net_failover_create(struct net_device *standby_dev)
 {
 	struct device *dev = standby_dev->dev.parent;
+	struct net_failover_info *nfo_info;
 	struct net_device *failover_dev;
 	struct failover *failover;
 	int err;
@@ -772,6 +799,9 @@ struct failover *net_failover_create(struct net_device *standby_dev)
 	failover_dev->min_mtu = standby_dev->min_mtu;
 	failover_dev->max_mtu = standby_dev->max_mtu;
 
+	nfo_info = netdev_priv(failover_dev);
+	spin_lock_init(&nfo_info->stats_lock);
+
 	err = register_netdev(failover_dev);
 	if (err) {
 		dev_err(dev, "Unable to register failover_dev!\n");
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 15/16] net: bonding: ensure .ndo_get_stats64 can sleep
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (13 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 14/16] net: net_failover: ensure .ndo_get_stats64 " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  2021-01-09  1:43   ` Jakub Kicinski
  2021-01-08 16:31 ` [PATCH v5 net-next 16/16] net: mark ndo_get_stats64 as being able to sleep Vladimir Oltean
  15 siblings, 1 reply; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

There is an effort to convert .ndo_get_stats64 to sleepable context, and
for that to work, we need to prevent callers of dev_get_stats from using
atomic locking.

The bonding driver retrieves its statistics recursively from its lower
interfaces, with additional care to only count packets sent/received
while those lowers were actually enslaved to the bond - see commit
5f0c5f73e5ef ("bonding: make global bonding stats more reliable").

Since commit 87163ef9cda7 ("bonding: remove last users of bond->lock and
bond->lock itself"), the bonding driver uses the following protection
for its array of slaves: RCU for readers and rtnl_mutex for updaters.
This is not great because there is another movement [ somehow
simultaneous with the one to make .ndo_get_stats64 sleepable ] to reduce
driver usage of rtnl_mutex. This makes sense, because the rtnl_mutex has
become a very contended resource.

The aforementioned commit removed an interesting comment:

	/* [...] we can't hold bond->lock [...] because we'll
	 * deadlock. The only solution is to rely on the fact
	 * that we're under rtnl_lock here, and the slaves
	 * list won't change. This doesn't solve the problem
	 * of setting the slave's MTU while it is
	 * transmitting, but the assumption is that the base
	 * driver can handle that.
	 *
	 * TODO: figure out a way to safely iterate the slaves
	 * list, but without holding a lock around the actual
	 * call to the base driver.
	 */

The above summarizes pretty well the challenges we have with nested
bonding interfaces (bond over bond over bond over...), which need to be
addressed by a better locking scheme that also not relies on the bloated
rtnl_mutex for the update side of the slaves array. That issue is not
addressed here, but there is a way around it.

To solve the nesting problem, the simple way is to not hold any locks
when recursing into the slave netdev operation. We can "cheat" and use
dev_hold to take a reference on the slave net_device, which is enough to
ensure that netdev_wait_allrefs() waits until we finish, and the kernel
won't fault.

However, the slave structure might no longer be valid, just its
associated net_device. So we need to do some more work to ensure that
the slave exists after we took the statistics, and if it still does,
reapply the logic from Andy's commit 5f0c5f73e5ef.

Tested using the following two scripts running in parallel:

	#!/bin/bash

	while :; do
		ip link del bond0
		ip link del bond1
		ip link add bond0 type bond mode 802.3ad
		ip link add bond1 type bond mode 802.3ad
		ip link set sw0p1 down && ip link set sw0p1 master bond0 && ip link set sw0p1 up
		ip link set sw0p2 down && ip link set sw0p2 master bond0 && ip link set sw0p2 up
		ip link set sw0p3 down && ip link set sw0p3 master bond0 && ip link set sw0p3 up
		ip link set bond0 down && ip link set bond0 master bond1 && ip link set bond0 up
		ip link set sw1p1 down && ip link set sw1p1 master bond1 && ip link set sw1p1 up
		ip link set bond1 up
		ip -s -s link show
		cat /sys/class/net/bond1/statistics/*
	done

	#!/bin/bash

	while :; do
		echo spi2.0 > /sys/bus/spi/drivers/sja1105/unbind
		echo spi2.0 > /sys/bus/spi/drivers/sja1105/bind
		sleep 30
	done

where the sja1105 driver was explicitly modified for the purpose of this
test to have a msleep(500) in its .ndo_get_stats64 method, to catch some
more potential races.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
- Use rcu_read_lock() and do not change the locking architecture of
  the driver.
- Gave some details on my testing procedure.

Changes in v4:
Now there is code to propagate errors.

Changes in v3:
- Added kfree(dev_stats);
- Removed the now useless stats_lock (bond->bond_stats and
  slave->slave_stats are now protected by bond->slaves_lock)

Changes in v2:
Switched to the new scheme of holding just a refcnt to the slave
interfaces while recursing with dev_get_stats.

 drivers/net/bonding/bond_main.c | 113 +++++++++++++++-----------------
 include/net/bonding.h           |  54 +++++++++++++++
 2 files changed, 108 insertions(+), 59 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 2352ef64486b..77c3a40adbf4 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3705,80 +3705,75 @@ static void bond_fold_stats(struct rtnl_link_stats64 *_res,
 	}
 }
 
-#ifdef CONFIG_LOCKDEP
-static int bond_get_lowest_level_rcu(struct net_device *dev)
-{
-	struct net_device *ldev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
-	struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
-	int cur = 0, max = 0;
-
-	now = dev;
-	iter = &dev->adj_list.lower;
-
-	while (1) {
-		next = NULL;
-		while (1) {
-			ldev = netdev_next_lower_dev_rcu(now, &iter);
-			if (!ldev)
-				break;
-
-			next = ldev;
-			niter = &ldev->adj_list.lower;
-			dev_stack[cur] = now;
-			iter_stack[cur++] = iter;
-			if (max <= cur)
-				max = cur;
-			break;
-		}
-
-		if (!next) {
-			if (!cur)
-				return max;
-			next = dev_stack[--cur];
-			niter = iter_stack[cur];
-		}
-
-		now = next;
-		iter = niter;
-	}
-
-	return max;
-}
-#endif
-
 static int bond_get_stats(struct net_device *bond_dev,
 			  struct rtnl_link_stats64 *stats)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
-	struct rtnl_link_stats64 temp;
-	struct list_head *iter;
-	struct slave *slave;
-	int nest_level = 0;
-	int res = 0;
+	struct rtnl_link_stats64 *dev_stats;
+	struct bonding_slave_dev *s;
+	struct list_head slaves;
+	int res, num_slaves;
+	int i = 0;
 
-	rcu_read_lock();
-#ifdef CONFIG_LOCKDEP
-	nest_level = bond_get_lowest_level_rcu(bond_dev);
-#endif
+	res = bond_get_slaves(bond, &slaves, &num_slaves);
+	if (res)
+		return res;
 
-	spin_lock_nested(&bond->stats_lock, nest_level);
-	memcpy(stats, &bond->bond_stats, sizeof(*stats));
+	dev_stats = kcalloc(num_slaves, sizeof(*dev_stats), GFP_KERNEL);
+	if (!dev_stats) {
+		bond_put_slaves(&slaves);
+		return -ENOMEM;
+	}
 
-	bond_for_each_slave_rcu(bond, slave, iter) {
-		res = dev_get_stats(slave->dev, &temp);
+	/* Recurse with no locks taken */
+	list_for_each_entry(s, &slaves, list) {
+		res = dev_get_stats(s->ndev, &dev_stats[i]);
 		if (res)
 			goto out;
+		i++;
+	}
+
+	spin_lock(&bond->stats_lock);
+
+	memcpy(stats, &bond->bond_stats, sizeof(*stats));
+
+	/* Because we released the RCU lock in bond_get_slaves, the new slave
+	 * array might be different from the original one, so we need to take
+	 * it again and only update the stats of the slaves that still exist.
+	 */
+	rcu_read_lock();
+
+	i = 0;
+
+	list_for_each_entry(s, &slaves, list) {
+		struct list_head *iter;
+		struct slave *slave;
 
-		bond_fold_stats(stats, &temp, &slave->slave_stats);
+		bond_for_each_slave_rcu(bond, slave, iter) {
+			if (slave->dev != s->ndev)
+				continue;
 
-		/* save off the slave stats for the next run */
-		memcpy(&slave->slave_stats, &temp, sizeof(temp));
+			bond_fold_stats(stats, &dev_stats[i],
+					&slave->slave_stats);
+
+			/* save off the slave stats for the next run */
+			memcpy(&slave->slave_stats, &dev_stats[i],
+			       sizeof(dev_stats[i]));
+			break;
+		}
+
+		i++;
 	}
 
+	rcu_read_unlock();
+
 	memcpy(&bond->bond_stats, stats, sizeof(*stats));
-out:
+
 	spin_unlock(&bond->stats_lock);
-	rcu_read_unlock();
+
+out:
+	kfree(dev_stats);
+	bond_put_slaves(&slaves);
 
 	return res;
 }
diff --git a/include/net/bonding.h b/include/net/bonding.h
index adc3da776970..149d445f935f 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -449,6 +449,60 @@ static inline void bond_hw_addr_copy(u8 *dst, const u8 *src, unsigned int len)
 	memcpy(dst, src, len);
 }
 
+/* Helpers for reference counting the struct net_device behind the bond slaves.
+ * These can be used to propagate the net_device_ops from the bond to the
+ * slaves while not holding rcu_read_lock() or the rtnl_mutex.
+ */
+struct bonding_slave_dev {
+	struct net_device *ndev;
+	struct list_head list;
+};
+
+static inline void bond_put_slaves(struct list_head *slaves)
+{
+	struct bonding_slave_dev *s, *tmp;
+
+	list_for_each_entry_safe(s, tmp, slaves, list) {
+		dev_put(s->ndev);
+		list_del(&s->list);
+		kfree(s);
+	}
+}
+
+static inline int bond_get_slaves(struct bonding *bond,
+				  struct list_head *slaves,
+				  int *num_slaves)
+{
+	struct list_head *iter;
+	struct slave *slave;
+	int err = 0;
+
+	INIT_LIST_HEAD(slaves);
+	*num_slaves = 0;
+
+	rcu_read_lock();
+
+	bond_for_each_slave_rcu(bond, slave, iter) {
+		struct bonding_slave_dev *s;
+
+		s = kzalloc(sizeof(*s), GFP_ATOMIC);
+		if (!s) {
+			rcu_read_unlock();
+			bond_put_slaves(slaves);
+			break;
+		}
+
+		s->ndev = slave->dev;
+		dev_hold(s->ndev);
+		list_add_tail(&s->list, slaves);
+		(*num_slaves)++;
+	}
+
+	rcu_read_unlock();
+
+	return err;
+}
+
 #define BOND_PRI_RESELECT_ALWAYS	0
 #define BOND_PRI_RESELECT_BETTER	1
 #define BOND_PRI_RESELECT_FAILURE	2
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 net-next 16/16] net: mark ndo_get_stats64 as being able to sleep
  2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
                   ` (14 preceding siblings ...)
  2021-01-08 16:31 ` [PATCH v5 net-next 15/16] net: bonding: " Vladimir Oltean
@ 2021-01-08 16:31 ` Vladimir Oltean
  15 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 16:31 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Now that all callers have been converted to not use atomic context when
calling dev_get_stats, it is time to update the documentation and put a
notice in the function that it expects process context.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
None.

Changes in v4:
None.

Changes in v3:
None.

Changes in v2:
Updated the documentation.

 Documentation/networking/netdevices.rst | 8 ++++++--
 Documentation/networking/statistics.rst | 9 ++++-----
 net/core/dev.c                          | 2 ++
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
index 5a85fcc80c76..944599722c76 100644
--- a/Documentation/networking/netdevices.rst
+++ b/Documentation/networking/netdevices.rst
@@ -64,8 +64,12 @@ ndo_do_ioctl:
 	Context: process
 
 ndo_get_stats:
-	Synchronization: dev_base_lock rwlock.
-	Context: nominally process, but don't sleep inside an rwlock
+	Synchronization:
+		none. netif_lists_lock(net) might be held, but not guaranteed.
+		It is illegal to hold rtnl_lock() in this method, since it will
+		cause a lock inversion with netif_lists_lock and a deadlock.
+	Context:
+		process
 
 ndo_start_xmit:
 	Synchronization: __netif_tx_lock spinlock.
diff --git a/Documentation/networking/statistics.rst b/Documentation/networking/statistics.rst
index 234abedc29b2..ad3e353df0dd 100644
--- a/Documentation/networking/statistics.rst
+++ b/Documentation/networking/statistics.rst
@@ -155,11 +155,10 @@ Drivers must ensure best possible compliance with
 Please note for example that detailed error statistics must be
 added into the general `rx_error` / `tx_error` counters.
 
-The `.ndo_get_stats64` callback can not sleep because of accesses
-via `/proc/net/dev`. If driver may sleep when retrieving the statistics
-from the device it should do so periodically asynchronously and only return
-a recent copy from `.ndo_get_stats64`. Ethtool interrupt coalescing interface
-allows setting the frequency of refreshing statistics, if needed.
+Drivers may sleep when retrieving the statistics from the device, or they might
+read the counters periodically and only return in `.ndo_get_stats64` a recent
+copy collected asynchronously. In the latter case, the ethtool interrupt
+coalescing interface allows setting the frequency of refreshing statistics.
 
 Retrieving ethtool statistics is a multi-syscall process, drivers are advised
 to keep the number of statistics constant to avoid race conditions with
diff --git a/net/core/dev.c b/net/core/dev.c
index 30facac95d5e..afd0e226efd4 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -10409,6 +10409,8 @@ int __must_check dev_get_stats(struct net_device *dev,
 	const struct net_device_ops *ops = dev->netdev_ops;
 	int err = 0;
 
+	might_sleep();
+
 	if (ops->ndo_get_stats64) {
 		memset(storage, 0, sizeof(*storage));
 		err = ops->ndo_get_stats64(dev, storage);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 net-next 11/16] net: propagate errors from dev_get_stats
  2021-01-08 16:31 ` [PATCH v5 net-next 11/16] net: " Vladimir Oltean
@ 2021-01-08 17:22   ` Vladimir Oltean
  2021-01-11 10:55   ` Dan Carpenter
  1 sibling, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-08 17:22 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski
  Cc: netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

On Fri, Jan 08, 2021 at 06:31:54PM +0200, Vladimir Oltean wrote:
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> 
> dev_get_stats can now return error codes. Take the remaining call sites
> where those errors can be propagated, which are all trivial, and convert
> them to look at that error code and stop processing.
> 
> The effects of simulating a kernel error (returning -ENOMEM) upon
> existing programs or kernel interfaces:
> 
> - cat /proc/net/dev prints up until the interface that failed, and there
>   it returns:
> cat: read error: Cannot allocate memory
> 
> - ifstat simply returns this and prints nothing:
> Error: Buffer too small for object.
> Dump terminated
> 
> - ip -s -s link show behaves the same as ifstat.

Note that at first I did not understand why ifstat and "ip -s -s link
show" return this message. But in the meantime I figured that it was due
to rtnetlink still returning -EMSGSIZE. That is fixed in this patch
series, but I forgot to update the commit message to reflect it. The
current output from these two commands is:

$ ifstat
RTNETLINK answers: Cannot allocate memory
Dump terminated
$ ip -s -s link show
RTNETLINK answers: Cannot allocate memory
Dump terminated

> 
> - ifconfig prints only the info for the interfaces whose statistics did
>   not fail.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Changes in v5:
> - Actually propagate errors from bonding and net_failover from within
>   this patch.
> - Properly propagating the dev_get_stats() error code from
>   rtnl_fill_stats now, and not -EMSGSIZE.
> 
> Changes in v4:
> Patch is new (Eric's suggestion).

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 net-next 15/16] net: bonding: ensure .ndo_get_stats64 can sleep
  2021-01-08 16:31 ` [PATCH v5 net-next 15/16] net: bonding: " Vladimir Oltean
@ 2021-01-09  1:43   ` Jakub Kicinski
  0 siblings, 0 replies; 21+ messages in thread
From: Jakub Kicinski @ 2021-01-09  1:43 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: David S . Miller, netdev, Andrew Lunn, Florian Fainelli,
	Cong Wang, Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel, Jay Vosburgh, Veaceslav Falico, Andy Gospodarek,
	Arnd Bergmann, Taehee Yoo, Jiri Pirko, Florian Westphal,
	Nikolay Aleksandrov, Pravin B Shelar, Sridhar Samudrala,
	Saeed Mahameed

Sparse says that you...

On Fri,  8 Jan 2021 18:31:58 +0200 Vladimir Oltean wrote:
> +	rcu_read_lock();
> +
> +	bond_for_each_slave_rcu(bond, slave, iter) {
> +		struct bonding_slave_dev *s;
> +
> +		s = kzalloc(sizeof(*s), GFP_ATOMIC);
> +		if (!s) {
> +			rcu_read_unlock();

..unlock..

> +			bond_put_slaves(slaves);
> +			break;
> +		}
> +
> +		s->ndev = slave->dev;
> +		dev_hold(s->ndev);
> +		list_add_tail(&s->list, slaves);
> +		(*num_slaves)++;
> +	}
> +
> +	rcu_read_unlock();

..twice:

drivers/net/bonding/bond_main.c: note: in included file (...):
include/linux/rcupdate.h:700:9: warning: context imbalance in 'bond_get_stats' - unexpected unlock

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 net-next 11/16] net: propagate errors from dev_get_stats
  2021-01-08 16:31 ` [PATCH v5 net-next 11/16] net: " Vladimir Oltean
  2021-01-08 17:22   ` Vladimir Oltean
@ 2021-01-11 10:55   ` Dan Carpenter
  2021-01-11 11:03     ` Vladimir Oltean
  1 sibling, 1 reply; 21+ messages in thread
From: Dan Carpenter @ 2021-01-11 10:55 UTC (permalink / raw)
  To: kbuild, Vladimir Oltean, David S . Miller, Jakub Kicinski
  Cc: lkp, kbuild-all, netdev, Andrew Lunn, Florian Fainelli,
	Cong Wang, Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel

[-- Attachment #1: Type: text/plain, Size: 12239 bytes --]

Hi Vladimir,

I love your patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Vladimir-Oltean/Make-ndo_get_stats64-sleepable/20210109-003617
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 58334e7537278793c86baa88b70c48b0d50b00ae
config: i386-randconfig-m021-20210108 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
net/core/rtnetlink.c:1821 rtnl_fill_ifinfo() warn: missing error code 'err'

vim +/err +1821 net/core/rtnetlink.c

79e1ad148c844f5c Jiri Benc            2017-11-02  1704  static int rtnl_fill_ifinfo(struct sk_buff *skb,
79e1ad148c844f5c Jiri Benc            2017-11-02  1705  			    struct net_device *dev, struct net *src_net,
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1706  			    int type, u32 pid, u32 seq, u32 change,
3d3ea5af5c0b382b Vlad Yasevich        2017-05-27  1707  			    unsigned int flags, u32 ext_filter_mask,
38e01b30563a5b5a Nicolas Dichtel      2018-01-25  1708  			    u32 event, int *new_nsid, int new_ifindex,
d4e4fdf9e4a27c87 Guillaume Nault      2019-10-23  1709  			    int tgt_netnsid, gfp_t gfp)
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1710  {
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1711  	struct ifinfomsg *ifm;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1712  	struct nlmsghdr *nlh;
5d346342f59ffa31 Vladimir Oltean      2021-01-08  1713  	int err = -EMSGSIZE;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1714  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1715  	ASSERT_RTNL();
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1716  	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1717  	if (nlh == NULL)
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1718  		return -EMSGSIZE;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1719  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1720  	ifm = nlmsg_data(nlh);
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1721  	ifm->ifi_family = AF_UNSPEC;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1722  	ifm->__ifi_pad = 0;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1723  	ifm->ifi_type = dev->type;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1724  	ifm->ifi_index = dev->ifindex;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1725  	ifm->ifi_flags = dev_get_flags(dev);
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1726  	ifm->ifi_change = change;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1727  
7e4a8d5a93f649a1 Christian Brauner    2018-09-04  1728  	if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid))
79e1ad148c844f5c Jiri Benc            2017-11-02  1729  		goto nla_put_failure;
79e1ad148c844f5c Jiri Benc            2017-11-02  1730  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1731  	if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1732  	    nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1733  	    nla_put_u8(skb, IFLA_OPERSTATE,
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1734  		       netif_running(dev) ? dev->operstate : IF_OPER_DOWN) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1735  	    nla_put_u8(skb, IFLA_LINKMODE, dev->link_mode) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1736  	    nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
3e7a50ceb11ea75c Stephen Hemminger    2018-07-27  1737  	    nla_put_u32(skb, IFLA_MIN_MTU, dev->min_mtu) ||
3e7a50ceb11ea75c Stephen Hemminger    2018-07-27  1738  	    nla_put_u32(skb, IFLA_MAX_MTU, dev->max_mtu) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1739  	    nla_put_u32(skb, IFLA_GROUP, dev->group) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1740  	    nla_put_u32(skb, IFLA_PROMISCUITY, dev->promiscuity) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1741  	    nla_put_u32(skb, IFLA_NUM_TX_QUEUES, dev->num_tx_queues) ||
c70ce028e834f8e5 Eric Dumazet         2016-03-21  1742  	    nla_put_u32(skb, IFLA_GSO_MAX_SEGS, dev->gso_max_segs) ||
c70ce028e834f8e5 Eric Dumazet         2016-03-21  1743  	    nla_put_u32(skb, IFLA_GSO_MAX_SIZE, dev->gso_max_size) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1744  #ifdef CONFIG_RPS
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1745  	    nla_put_u32(skb, IFLA_NUM_RX_QUEUES, dev->num_rx_queues) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1746  #endif
79110a0426d8179a Florian Westphal     2017-09-26  1747  	    put_master_ifindex(skb, dev) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1748  	    nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1749  	    (dev->qdisc &&
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1750  	     nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
6c5570016b972d9b Florian Westphal     2017-10-02  1751  	    nla_put_ifalias(skb, dev) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1752  	    nla_put_u32(skb, IFLA_CARRIER_CHANGES,
b2d3bcfa26a7a8de David Decotigny      2018-01-18  1753  			atomic_read(&dev->carrier_up_count) +
b2d3bcfa26a7a8de David Decotigny      2018-01-18  1754  			atomic_read(&dev->carrier_down_count)) ||
b2d3bcfa26a7a8de David Decotigny      2018-01-18  1755  	    nla_put_u32(skb, IFLA_CARRIER_UP_COUNT,
b2d3bcfa26a7a8de David Decotigny      2018-01-18  1756  			atomic_read(&dev->carrier_up_count)) ||
b2d3bcfa26a7a8de David Decotigny      2018-01-18  1757  	    nla_put_u32(skb, IFLA_CARRIER_DOWN_COUNT,
b2d3bcfa26a7a8de David Decotigny      2018-01-18  1758  			atomic_read(&dev->carrier_down_count)))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1759  		goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1760  
829eb208e80d6db9 Roopa Prabhu         2020-07-31  1761  	if (rtnl_fill_proto_down(skb, dev))
829eb208e80d6db9 Roopa Prabhu         2020-07-31  1762  		goto nla_put_failure;
829eb208e80d6db9 Roopa Prabhu         2020-07-31  1763  
3d3ea5af5c0b382b Vlad Yasevich        2017-05-27  1764  	if (event != IFLA_EVENT_NONE) {
3d3ea5af5c0b382b Vlad Yasevich        2017-05-27  1765  		if (nla_put_u32(skb, IFLA_EVENT, event))
3d3ea5af5c0b382b Vlad Yasevich        2017-05-27  1766  			goto nla_put_failure;
3d3ea5af5c0b382b Vlad Yasevich        2017-05-27  1767  	}
3d3ea5af5c0b382b Vlad Yasevich        2017-05-27  1768  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1769  	if (rtnl_fill_link_ifmap(skb, dev))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1770  		goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1771  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1772  	if (dev->addr_len) {
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1773  		if (nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr) ||
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1774  		    nla_put(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1775  			goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1776  	}
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1777  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1778  	if (rtnl_phys_port_id_fill(skb, dev))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1779  		goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1780  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1781  	if (rtnl_phys_port_name_fill(skb, dev))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1782  		goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1783  
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1784  	if (rtnl_phys_switch_id_fill(skb, dev))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1785  		goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1786  
5d346342f59ffa31 Vladimir Oltean      2021-01-08  1787  	err = rtnl_fill_stats(skb, dev);
5d346342f59ffa31 Vladimir Oltean      2021-01-08  1788  	if (err)
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1789  		goto nla_put_failure;
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1790  
250fc3dfdbd3e8b5 Florian Westphal     2017-09-26  1791  	if (rtnl_fill_vf(skb, dev, ext_filter_mask))
b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1792  		goto nla_put_failure;

No error codes any more on the rest of the gotos in this function.

b22b941b2c253a20 Hannes Frederic Sowa 2015-11-17  1793  
c53864fd60227de0 David Gibson         2014-04-24  1794  	if (rtnl_port_fill(skb, dev, ext_filter_mask))
57b610805ce92dbd Scott Feldman        2010-05-17  1795  		goto nla_put_failure;
57b610805ce92dbd Scott Feldman        2010-05-17  1796  
d1fdd9138682e0f2 Brenden Blanco       2016-07-19  1797  	if (rtnl_xdp_fill(skb, dev))
d1fdd9138682e0f2 Brenden Blanco       2016-07-19  1798  		goto nla_put_failure;
d1fdd9138682e0f2 Brenden Blanco       2016-07-19  1799  
ba7d49b1f0f8e5f2 Jiri Pirko           2014-01-22  1800  	if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) {
38f7b870d4a6a5d3 Patrick McHardy      2007-06-13  1801  		if (rtnl_link_fill(skb, dev) < 0)
38f7b870d4a6a5d3 Patrick McHardy      2007-06-13  1802  			goto nla_put_failure;
38f7b870d4a6a5d3 Patrick McHardy      2007-06-13  1803  	}
38f7b870d4a6a5d3 Patrick McHardy      2007-06-13  1804  
d4e4fdf9e4a27c87 Guillaume Nault      2019-10-23  1805  	if (rtnl_fill_link_netnsid(skb, dev, src_net, gfp))
d37512a277dfb2ce Nicolas Dichtel      2015-01-15  1806  		goto nla_put_failure;
d37512a277dfb2ce Nicolas Dichtel      2015-01-15  1807  
6621dd29eb9b5e67 Nicolas Dichtel      2017-10-03  1808  	if (new_nsid &&
6621dd29eb9b5e67 Nicolas Dichtel      2017-10-03  1809  	    nla_put_s32(skb, IFLA_NEW_NETNSID, *new_nsid) < 0)
6621dd29eb9b5e67 Nicolas Dichtel      2017-10-03  1810  		goto nla_put_failure;
38e01b30563a5b5a Nicolas Dichtel      2018-01-25  1811  	if (new_ifindex &&
38e01b30563a5b5a Nicolas Dichtel      2018-01-25  1812  	    nla_put_s32(skb, IFLA_NEW_IFINDEX, new_ifindex) < 0)
38e01b30563a5b5a Nicolas Dichtel      2018-01-25  1813  		goto nla_put_failure;
38e01b30563a5b5a Nicolas Dichtel      2018-01-25  1814  
f74877a5457d34d6 Michal Kubecek       2019-12-11  1815  	if (memchr_inv(dev->perm_addr, '\0', dev->addr_len) &&
f74877a5457d34d6 Michal Kubecek       2019-12-11  1816  	    nla_put(skb, IFLA_PERM_ADDRESS, dev->addr_len, dev->perm_addr))
f74877a5457d34d6 Michal Kubecek       2019-12-11  1817  		goto nla_put_failure;
6621dd29eb9b5e67 Nicolas Dichtel      2017-10-03  1818  
5fa85a09390c4a52 Florian Westphal     2017-10-16  1819  	rcu_read_lock();
070cbf5be7774dcf Florian Westphal     2017-10-16  1820  	if (rtnl_fill_link_af(skb, dev, ext_filter_mask))
5fa85a09390c4a52 Florian Westphal     2017-10-16 @1821  		goto nla_put_failure_rcu;
5fa85a09390c4a52 Florian Westphal     2017-10-16  1822  	rcu_read_unlock();
f8ff182c716c6f11 Thomas Graf          2010-11-16  1823  
88f4fb0c7496a13b Jiri Pirko           2019-09-30  1824  	if (rtnl_fill_prop_list(skb, dev))
88f4fb0c7496a13b Jiri Pirko           2019-09-30  1825  		goto nla_put_failure;
88f4fb0c7496a13b Jiri Pirko           2019-09-30  1826  
053c095a82cf7730 Johannes Berg        2015-01-16  1827  	nlmsg_end(skb, nlh);
053c095a82cf7730 Johannes Berg        2015-01-16  1828  	return 0;
b60c5115f4abf0b9 Thomas Graf          2006-08-04  1829  
5fa85a09390c4a52 Florian Westphal     2017-10-16  1830  nla_put_failure_rcu:
5fa85a09390c4a52 Florian Westphal     2017-10-16  1831  	rcu_read_unlock();
b60c5115f4abf0b9 Thomas Graf          2006-08-04  1832  nla_put_failure:
26932566a42d46ae Patrick McHardy      2007-01-31  1833  	nlmsg_cancel(skb, nlh);
5d346342f59ffa31 Vladimir Oltean      2021-01-08  1834  	return err;
^1da177e4c3f4152 Linus Torvalds       2005-04-16  1835  }
^1da177e4c3f4152 Linus Torvalds       2005-04-16  1836  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 30176 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 net-next 11/16] net: propagate errors from dev_get_stats
  2021-01-11 10:55   ` Dan Carpenter
@ 2021-01-11 11:03     ` Vladimir Oltean
  0 siblings, 0 replies; 21+ messages in thread
From: Vladimir Oltean @ 2021-01-11 11:03 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: kbuild, David S . Miller, Jakub Kicinski, lkp, kbuild-all,
	netdev, Andrew Lunn, Florian Fainelli, Cong Wang,
	Stephen Hemminger, Eric Dumazet, George McCollister,
	Oleksij Rempel

Hi Dan,

On Mon, Jan 11, 2021 at 01:55:16PM +0300, Dan Carpenter wrote:
> Hi Vladimir,
>
> New smatch warnings:
> net/core/rtnetlink.c:1821 rtnl_fill_ifinfo() warn: missing error code 'err'
>
> vim +/err +1821 net/core/rtnetlink.c
>
> static int rtnl_fill_ifinfo(struct sk_buff *skb,
> 			    struct net_device *dev, struct net *src_net,
> 			    int type, u32 pid, u32 seq, u32 change,
> 			    unsigned int flags, u32 ext_filter_mask,
> 			    u32 event, int *new_nsid, int new_ifindex,
> 			    int tgt_netnsid, gfp_t gfp)
> {
> 	struct ifinfomsg *ifm;
> 	struct nlmsghdr *nlh;
> 	int err = -EMSGSIZE;
>
...
>
> 	err = rtnl_fill_stats(skb, dev);
> 	if (err)
> 		goto nla_put_failure;
>
> 	if (rtnl_fill_vf(skb, dev, ext_filter_mask))
> 		goto nla_put_failure;
>
> No error codes any more on the rest of the gotos in this function.
>
>
> 	if (rtnl_port_fill(skb, dev, ext_filter_mask))
> 		goto nla_put_failure;
>
> 	if (rtnl_xdp_fill(skb, dev))
> 		goto nla_put_failure;
>
...
>
> 	nlmsg_end(skb, nlh);
> 	return 0;
>
> nla_put_failure_rcu:
> 	rcu_read_unlock();
> nla_put_failure:
> 	nlmsg_cancel(skb, nlh);
> 	return err;
> }

Thank you for this report. It is a valid issue. It has also been fixed
in v6:
https://patchwork.kernel.org/project/netdevbpf/patch/20210109172624.2028156-12-olteanv@gmail.com/
From the changelog:

Changes in v6:
- Fixed rtnetlink incorrectly returning 0 in rtnl_fill_ifinfo on
  nla_put_failure and causing "ip a" to not show any interfaces.

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2021-01-11 11:04 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-08 16:31 [PATCH v5 net-next 00/16] Make .ndo_get_stats64 sleepable Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 01/16] net: mark dev_base_lock for deprecation Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 02/16] net: introduce a mutex for the netns interface lists Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 03/16] net: procfs: hold netif_lists_lock when retrieving device statistics Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 04/16] net: sysfs: don't hold dev_base_lock while " Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 05/16] s390/appldata_net_sum: hold the netdev lists lock when " Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 06/16] parisc/led: " Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 07/16] net: remove return value from dev_get_stats Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 08/16] net: allow ndo_get_stats64 to return an int error code Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 09/16] scsi: fcoe: propagate errors from dev_get_stats Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 10/16] net: openvswitch: " Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 11/16] net: " Vladimir Oltean
2021-01-08 17:22   ` Vladimir Oltean
2021-01-11 10:55   ` Dan Carpenter
2021-01-11 11:03     ` Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 12/16] net: terminate " Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 13/16] net: openvswitch: ensure dev_get_stats can sleep Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 14/16] net: net_failover: ensure .ndo_get_stats64 " Vladimir Oltean
2021-01-08 16:31 ` [PATCH v5 net-next 15/16] net: bonding: " Vladimir Oltean
2021-01-09  1:43   ` Jakub Kicinski
2021-01-08 16:31 ` [PATCH v5 net-next 16/16] net: mark ndo_get_stats64 as being able to sleep Vladimir Oltean

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).