Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9b76ebd authored by Michael Chan's avatar Michael Chan Committed by David S. Miller
Browse files

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: c0c050c5 ("bnxt_en: New Broadcom ethernet driver.")
Signed-off-by: default avatarMichael Chan <michael.chan@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent de92cd6c
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -6279,6 +6279,12 @@ 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));
}

int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
{
	int rc = 0;
@@ -6297,7 +6303,7 @@ int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_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 */
@@ -6358,8 +6364,15 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
	u32 i;
	struct bnxt *bp = netdev_priv(dev);

	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;
	}

	/* TODO check if we need to synchronize with bnxt_close path */
	for (i = 0; i < bp->cp_nr_rings; i++) {
@@ -6406,6 +6419,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);
}

static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask)
+1 −0
Original line number Diff line number Diff line
@@ -1117,6 +1117,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;