lkml.org 
[lkml]   [2016]   [Apr]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.5 053/124] bonding: fix bond_get_stats()
    Date
    4.5-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Eric Dumazet <edumazet@google.com>

    [ Upstream commit fe30937b65354c7fec244caebbdaae68e28ca797 ]

    bond_get_stats() can be called from rtnetlink (with RTNL held)
    or from /proc/net/dev seq handler (with RCU held)

    The logic added in commit 5f0c5f73e5ef ("bonding: make global bonding
    stats more reliable") kind of assumed only one cpu could run there.

    If multiple threads are reading /proc/net/dev, stats can be really
    messed up after a while.

    A second problem is that some fields are 32bit, so we need to properly
    handle the wrap around problem.

    Given that RTNL is not always held, we need to use
    bond_for_each_slave_rcu().

    Fixes: 5f0c5f73e5ef ("bonding: make global bonding stats more reliable")
    Signed-off-by: Eric Dumazet <edumazet@google.com>
    Cc: Andy Gospodarek <gospo@cumulusnetworks.com>
    Cc: Jay Vosburgh <j.vosburgh@gmail.com>
    Cc: Veaceslav Falico <vfalico@gmail.com>
    Reviewed-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/net/bonding/bond_main.c | 63 +++++++++++++++++++++-------------------
    include/net/bonding.h | 1
    2 files changed, 35 insertions(+), 29 deletions(-)

    --- a/drivers/net/bonding/bond_main.c
    +++ b/drivers/net/bonding/bond_main.c
    @@ -3308,6 +3308,30 @@ static int bond_close(struct net_device
    return 0;
    }

    +/* fold stats, assuming all rtnl_link_stats64 fields are u64, but
    + * that some drivers can provide 32bit values only.
    + */
    +static void bond_fold_stats(struct rtnl_link_stats64 *_res,
    + const struct rtnl_link_stats64 *_new,
    + const struct rtnl_link_stats64 *_old)
    +{
    + const u64 *new = (const u64 *)_new;
    + const u64 *old = (const u64 *)_old;
    + u64 *res = (u64 *)_res;
    + int i;
    +
    + for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) {
    + u64 nv = new[i];
    + u64 ov = old[i];
    +
    + /* detects if this particular field is 32bit only */
    + if (((nv | ov) >> 32) == 0)
    + res[i] += (u32)nv - (u32)ov;
    + else
    + res[i] += nv - ov;
    + }
    +}
    +
    static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
    struct rtnl_link_stats64 *stats)
    {
    @@ -3316,43 +3340,23 @@ static struct rtnl_link_stats64 *bond_ge
    struct list_head *iter;
    struct slave *slave;

    + spin_lock(&bond->stats_lock);
    memcpy(stats, &bond->bond_stats, sizeof(*stats));

    - bond_for_each_slave(bond, slave, iter) {
    - const struct rtnl_link_stats64 *sstats =
    + rcu_read_lock();
    + bond_for_each_slave_rcu(bond, slave, iter) {
    + const struct rtnl_link_stats64 *new =
    dev_get_stats(slave->dev, &temp);
    - struct rtnl_link_stats64 *pstats = &slave->slave_stats;

    - stats->rx_packets += sstats->rx_packets - pstats->rx_packets;
    - stats->rx_bytes += sstats->rx_bytes - pstats->rx_bytes;
    - stats->rx_errors += sstats->rx_errors - pstats->rx_errors;
    - stats->rx_dropped += sstats->rx_dropped - pstats->rx_dropped;
    -
    - stats->tx_packets += sstats->tx_packets - pstats->tx_packets;;
    - stats->tx_bytes += sstats->tx_bytes - pstats->tx_bytes;
    - stats->tx_errors += sstats->tx_errors - pstats->tx_errors;
    - stats->tx_dropped += sstats->tx_dropped - pstats->tx_dropped;
    -
    - stats->multicast += sstats->multicast - pstats->multicast;
    - stats->collisions += sstats->collisions - pstats->collisions;
    -
    - stats->rx_length_errors += sstats->rx_length_errors - pstats->rx_length_errors;
    - stats->rx_over_errors += sstats->rx_over_errors - pstats->rx_over_errors;
    - stats->rx_crc_errors += sstats->rx_crc_errors - pstats->rx_crc_errors;
    - stats->rx_frame_errors += sstats->rx_frame_errors - pstats->rx_frame_errors;
    - stats->rx_fifo_errors += sstats->rx_fifo_errors - pstats->rx_fifo_errors;
    - stats->rx_missed_errors += sstats->rx_missed_errors - pstats->rx_missed_errors;
    -
    - stats->tx_aborted_errors += sstats->tx_aborted_errors - pstats->tx_aborted_errors;
    - stats->tx_carrier_errors += sstats->tx_carrier_errors - pstats->tx_carrier_errors;
    - stats->tx_fifo_errors += sstats->tx_fifo_errors - pstats->tx_fifo_errors;
    - stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors - pstats->tx_heartbeat_errors;
    - stats->tx_window_errors += sstats->tx_window_errors - pstats->tx_window_errors;
    + bond_fold_stats(stats, new, &slave->slave_stats);

    /* save off the slave stats for the next run */
    - memcpy(pstats, sstats, sizeof(*sstats));
    + memcpy(&slave->slave_stats, new, sizeof(*new));
    }
    + rcu_read_unlock();
    +
    memcpy(&bond->bond_stats, stats, sizeof(*stats));
    + spin_unlock(&bond->stats_lock);

    return stats;
    }
    @@ -4166,6 +4170,7 @@ void bond_setup(struct net_device *bond_
    struct bonding *bond = netdev_priv(bond_dev);

    spin_lock_init(&bond->mode_lock);
    + spin_lock_init(&bond->stats_lock);
    bond->params = bonding_defaults;

    /* Initialize pointers */
    --- a/include/net/bonding.h
    +++ b/include/net/bonding.h
    @@ -215,6 +215,7 @@ struct bonding {
    * ALB mode (6) - to sync the use and modifications of its hash table
    */
    spinlock_t mode_lock;
    + spinlock_t stats_lock;
    u8 send_peer_notif;
    u8 igmp_retrans;
    #ifdef CONFIG_PROC_FS

    \
     
     \ /
      Last update: 2016-04-18 05:41    [W:2.143 / U:0.924 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site