From: Michael Chan Date: Tue, 11 Jul 2017 17:05:34 +0000 (-0400) Subject: bnxt_en: Fix race conditions in .ndo_get_stats64(). X-Git-Tag: v4.1.12-124.31.3~239 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=16b201b3a4b2368fe87d22bf5b00bcf20b3ae387;p=users%2Fjedix%2Flinux-maple.git bnxt_en: Fix race conditions in .ndo_get_stats64(). .ndo_get_stats64() may not be protected by RTNL and can race with .ndo_stop() or other ethtool operations that can free the statistics memory. Fix it by setting a new flag BNXT_STATE_READ_STATS and then proceeding to read statistics memory only if the state is OPEN. The close path that frees the memory clears the OPEN state and then waits for the BNXT_STATE_READ_STATS to clear before proceeding to free the statistics memory. Fixes: c0c050c58d84 ("bnxt_en: New Broadcom ethernet driver.") Signed-off-by: Michael Chan Signed-off-by: David S. Miller (cherry picked from commit f9b76ebd49f97458857568918c305a17fa7c6567) Signed-off-by: aru kolappan Orabug: 29129625 Reviewed-by: Jack Vogel Signed-off-by: aru kolappan Signed-off-by: Brian Maly --- diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f29444395173..5a15d28d4353 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6812,7 +6812,11 @@ static int bnxt_open(struct net_device *dev) return __bnxt_open_nic(bp, true, true); } - +static bool bnxt_drv_busy(struct bnxt *bp) +{ + return (test_bit(BNXT_STATE_IN_SP_TASK, &bp->state) || + test_bit(BNXT_STATE_READ_STATS, &bp->state)); +} static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) @@ -6822,7 +6826,7 @@ static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init, clear_bit(BNXT_STATE_OPEN, &bp->state); smp_mb__after_atomic(); - while (test_bit(BNXT_STATE_IN_SP_TASK, &bp->state)) + while (bnxt_drv_busy(bp)) msleep(20); /* Flush rings and and disable interrupts */ @@ -6901,8 +6905,15 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) memset(stats, 0, sizeof(struct rtnl_link_stats64)); - if (!bp->bnapi) + set_bit(BNXT_STATE_READ_STATS, &bp->state); + /* Make sure bnxt_close_nic() sees that we are reading stats before + * we check the BNXT_STATE_OPEN flag. + */ + smp_mb__after_atomic(); + if (!test_bit(BNXT_STATE_OPEN, &bp->state)) { + clear_bit(BNXT_STATE_READ_STATS, &bp->state); return stats; + } /* TODO check if we need to synchronize with bnxt_close path */ for (i = 0; i < bp->cp_nr_rings; i++) { @@ -6949,7 +6960,7 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) stats->tx_fifo_errors = le64_to_cpu(tx->tx_fifo_underruns); stats->tx_errors = le64_to_cpu(tx->tx_err); } - + clear_bit(BNXT_STATE_READ_STATS, &bp->state); return stats; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 09a70ea409ae..31deb72b2952 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1167,6 +1167,7 @@ struct bnxt { unsigned long state; #define BNXT_STATE_OPEN 0 #define BNXT_STATE_IN_SP_TASK 1 +#define BNXT_STATE_READ_STATS 2 struct bnxt_irq *irq_tbl; int total_irqs;