From 28172739f0a276eb8d6ca917b3974c2edb036da3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Jul 2010 14:58:56 -0700 Subject: net: fix 64 bit counters on 32 bit arches There is a small possibility that a reader gets incorrect values on 32 bit arches. SNMP applications could catch incorrect counters when a 32bit high part is changed by another stats consumer/provider. One way to solve this is to add a rtnl_link_stats64 param to all ndo_get_stats64() methods, and also add such a parameter to dev_get_stats(). Rule is that we are not allowed to use dev->stats64 as a temporary storage for 64bit stats, but a caller provided area (usually on stack) Old drivers (only providing get_stats() method) need no changes. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 64 ++++++++++++++--------------- drivers/net/ixgbe/ixgbe_ethtool.c | 8 ++-- drivers/net/loopback.c | 4 +- drivers/net/macvlan.c | 6 +-- drivers/net/sfc/efx.c | 3 +- drivers/net/sfc/ethtool.c | 3 +- drivers/parisc/led.c | 3 +- drivers/scsi/fcoe/fcoe.c | 3 +- drivers/staging/batman-adv/hard-interface.c | 3 +- drivers/usb/gadget/rndis.c | 3 +- 10 files changed, 52 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index a95a41b74b4e..9bb9bfa225b6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3804,51 +3804,49 @@ static int bond_close(struct net_device *bond_dev) return 0; } -static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev) +static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev, + struct rtnl_link_stats64 *stats) { struct bonding *bond = netdev_priv(bond_dev); - struct rtnl_link_stats64 *stats = &bond_dev->stats64; - struct rtnl_link_stats64 local_stats; + struct rtnl_link_stats64 temp; struct slave *slave; int i; - memset(&local_stats, 0, sizeof(local_stats)); + memset(stats, 0, sizeof(*stats)); read_lock_bh(&bond->lock); bond_for_each_slave(bond, slave, i) { const struct rtnl_link_stats64 *sstats = - dev_get_stats(slave->dev); - - local_stats.rx_packets += sstats->rx_packets; - local_stats.rx_bytes += sstats->rx_bytes; - local_stats.rx_errors += sstats->rx_errors; - local_stats.rx_dropped += sstats->rx_dropped; - - local_stats.tx_packets += sstats->tx_packets; - local_stats.tx_bytes += sstats->tx_bytes; - local_stats.tx_errors += sstats->tx_errors; - local_stats.tx_dropped += sstats->tx_dropped; - - local_stats.multicast += sstats->multicast; - local_stats.collisions += sstats->collisions; - - local_stats.rx_length_errors += sstats->rx_length_errors; - local_stats.rx_over_errors += sstats->rx_over_errors; - local_stats.rx_crc_errors += sstats->rx_crc_errors; - local_stats.rx_frame_errors += sstats->rx_frame_errors; - local_stats.rx_fifo_errors += sstats->rx_fifo_errors; - local_stats.rx_missed_errors += sstats->rx_missed_errors; - - local_stats.tx_aborted_errors += sstats->tx_aborted_errors; - local_stats.tx_carrier_errors += sstats->tx_carrier_errors; - local_stats.tx_fifo_errors += sstats->tx_fifo_errors; - local_stats.tx_heartbeat_errors += sstats->tx_heartbeat_errors; - local_stats.tx_window_errors += sstats->tx_window_errors; + dev_get_stats(slave->dev, &temp); + + stats->rx_packets += sstats->rx_packets; + stats->rx_bytes += sstats->rx_bytes; + stats->rx_errors += sstats->rx_errors; + stats->rx_dropped += sstats->rx_dropped; + + stats->tx_packets += sstats->tx_packets; + stats->tx_bytes += sstats->tx_bytes; + stats->tx_errors += sstats->tx_errors; + stats->tx_dropped += sstats->tx_dropped; + + stats->multicast += sstats->multicast; + stats->collisions += sstats->collisions; + + stats->rx_length_errors += sstats->rx_length_errors; + stats->rx_over_errors += sstats->rx_over_errors; + stats->rx_crc_errors += sstats->rx_crc_errors; + stats->rx_frame_errors += sstats->rx_frame_errors; + stats->rx_fifo_errors += sstats->rx_fifo_errors; + stats->rx_missed_errors += sstats->rx_missed_errors; + + stats->tx_aborted_errors += sstats->tx_aborted_errors; + stats->tx_carrier_errors += sstats->tx_carrier_errors; + stats->tx_fifo_errors += sstats->tx_fifo_errors; + stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; + stats->tx_window_errors += sstats->tx_window_errors; } - memcpy(stats, &local_stats, sizeof(struct net_device_stats)); - read_unlock_bh(&bond->lock); return stats; diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index b35ef36741ef..da54b38bb480 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -55,7 +55,7 @@ struct ixgbe_stats { offsetof(struct ixgbe_adapter, m) #define IXGBE_NETDEV_STAT(m) NETDEV_STATS, \ sizeof(((struct net_device *)0)->m), \ - offsetof(struct net_device, m) + offsetof(struct net_device, m) - offsetof(struct net_device, stats) static struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_packets", IXGBE_NETDEV_STAT(stats.rx_packets)}, @@ -998,16 +998,18 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); u64 *queue_stat; int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64); + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *net_stats; int j, k; int i; char *p = NULL; ixgbe_update_stats(adapter); - dev_get_stats(netdev); + net_stats = dev_get_stats(netdev, &temp); for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) { switch (ixgbe_gstrings_stats[i].type) { case NETDEV_STATS: - p = (char *) netdev + + p = (char *) net_stats + ixgbe_gstrings_stats[i].stat_offset; break; case IXGBE_STATS: diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 4dd0510d7a99..9a0996795321 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -98,10 +98,10 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { const struct pcpu_lstats __percpu *pcpu_lstats; - struct rtnl_link_stats64 *stats = &dev->stats64; u64 bytes = 0; u64 packets = 0; u64 drops = 0; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index e6d626e78515..6112f1498940 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -431,12 +431,12 @@ static void macvlan_uninit(struct net_device *dev) free_percpu(vlan->rx_stats); } -static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { - struct rtnl_link_stats64 *stats = &dev->stats64; struct macvlan_dev *vlan = netdev_priv(dev); - dev_txq_stats_fold(dev, &dev->stats); + dev_txq_stats_fold(dev, (struct net_device_stats *)stats); if (vlan->rx_stats) { struct macvlan_rx_stats *p, accum = {0}; diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 35b3f2922e5c..ba674c5ca29e 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1533,11 +1533,10 @@ static int efx_net_stop(struct net_device *net_dev) } /* Context: process, dev_base_lock or RTNL held, non-blocking. */ -static struct rtnl_link_stats64 *efx_net_stats(struct net_device *net_dev) +static struct rtnl_link_stats64 *efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats) { struct efx_nic *efx = netdev_priv(net_dev); struct efx_mac_stats *mac_stats = &efx->mac_stats; - struct rtnl_link_stats64 *stats = &net_dev->stats64; spin_lock_bh(&efx->stats_lock); efx->type->update_stats(efx); diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 3b8b0a062749..fd19d6ab97a2 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -469,12 +469,13 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, struct efx_mac_stats *mac_stats = &efx->mac_stats; struct efx_ethtool_stat *stat; struct efx_channel *channel; + struct rtnl_link_stats64 temp; int i; EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); /* Update MAC and NIC statistics */ - dev_get_stats(net_dev); + dev_get_stats(net_dev, &temp); /* Fill detailed statistics buffer */ for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index 188bc8496a26..18dff43b8bd2 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c @@ -355,12 +355,13 @@ static __inline__ int led_get_net_activity(void) rcu_read_lock(); for_each_netdev_rcu(&init_net, dev) { const struct net_device_stats *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); + stats = dev_get_stats(dev, &temp); rx_total += stats->rx_packets; tx_total += stats->tx_packets; } diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 44a07593de56..1a429ed6da9d 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -2653,6 +2653,7 @@ static void fcoe_get_lesb(struct fc_lport *lport, u32 lfc, vlfc, mdac; struct fcoe_dev_stats *devst; struct fcoe_fc_els_lesb *lesb; + struct rtnl_link_stats64 temp; struct net_device *netdev = fcoe_netdev(lport); lfc = 0; @@ -2669,7 +2670,7 @@ static 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)->rx_crc_errors); + lesb->lesb_fcs_error = htonl(dev_get_stats(netdev, &temp)->rx_crc_errors); } /** diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c index 5ede9c255094..96c86c873011 100644 --- a/drivers/staging/batman-adv/hard-interface.c +++ b/drivers/staging/batman-adv/hard-interface.c @@ -440,6 +440,7 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, struct batman_packet *batman_packet; struct batman_if *batman_if; struct net_device_stats *stats; + struct rtnl_link_stats64 temp; int ret; skb = skb_share_check(skb, GFP_ATOMIC); @@ -468,7 +469,7 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, if (batman_if->if_status != IF_ACTIVE) goto err_free; - stats = (struct net_device_stats *)dev_get_stats(skb->dev); + stats = (struct net_device_stats *)dev_get_stats(skb->dev, &temp); if (stats) { stats->rx_packets++; stats->rx_bytes += skb->len; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index fb69b01c8f3a..020fa5a25fda 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -171,6 +171,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, 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; @@ -194,7 +195,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, resp->InformationBufferOffset = cpu_to_le32 (16); net = rndis_per_dev_params[configNr].dev; - stats = dev_get_stats(net); + stats = dev_get_stats(net, &temp); switch (OID) { -- cgit v1.2.3