Commit 55edc6e6 authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller
Browse files

sfc: Split MAC stats DMA initiation and completion



From: Steve Hodgson <shodgson@solarflare.com>

Currently we initiate MAC stats DMA and busy-wait for completion when
stats are requested.  We can improve on this with a periodic timer to
initiate and poll for stats, and opportunistically poll when stats are
requested.

Since efx_nic::stats_disable_count and efx_stats_{disable,enable}()
are Falcon-specific, rename them and move them accordingly.

Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1dfc5cea
Loading
Loading
Loading
Loading
+13 −35
Original line number Diff line number Diff line
@@ -637,6 +637,7 @@ void __efx_reconfigure_port(struct efx_nic *efx)
		netif_addr_unlock_bh(efx->net_dev);
	}

	falcon_stop_nic_stats(efx);
	falcon_deconfigure_mac_wrapper(efx);

	/* Reconfigure the PHY, disabling transmit in mac level loopback. */
@@ -651,6 +652,8 @@ void __efx_reconfigure_port(struct efx_nic *efx)

	efx->mac_op->reconfigure(efx);

	falcon_start_nic_stats(efx);

	/* Inform kernel of loss/gain of carrier */
	efx_link_status_changed(efx);
	return;
@@ -749,7 +752,6 @@ static int efx_init_port(struct efx_nic *efx)
	efx->mac_op->reconfigure(efx);

	efx->port_initialized = true;
	efx_stats_enable(efx);

	mutex_unlock(&efx->mac_lock);
	return 0;
@@ -802,7 +804,6 @@ static void efx_fini_port(struct efx_nic *efx)
	if (!efx->port_initialized)
		return;

	efx_stats_disable(efx);
	efx->phy_op->fini(efx);
	efx->port_initialized = false;

@@ -1158,6 +1159,8 @@ static void efx_start_all(struct efx_nic *efx)
	if (efx->state == STATE_RUNNING)
		queue_delayed_work(efx->workqueue, &efx->monitor_work,
				   efx_monitor_interval);

	falcon_start_nic_stats(efx);
}

/* Flush all delayed work. Should only be called when no more delayed work
@@ -1195,6 +1198,8 @@ static void efx_stop_all(struct efx_nic *efx)
	if (!efx->port_enabled)
		return;

	falcon_stop_nic_stats(efx);

	/* Disable interrupts and wait for ISR to complete */
	falcon_disable_interrupts(efx);
	if (efx->legacy_irq)
@@ -1438,20 +1443,6 @@ static int efx_net_stop(struct net_device *net_dev)
	return 0;
}

void efx_stats_disable(struct efx_nic *efx)
{
	spin_lock(&efx->stats_lock);
	++efx->stats_disable_count;
	spin_unlock(&efx->stats_lock);
}

void efx_stats_enable(struct efx_nic *efx)
{
	spin_lock(&efx->stats_lock);
	--efx->stats_disable_count;
	spin_unlock(&efx->stats_lock);
}

/* Context: process, dev_base_lock or RTNL held, non-blocking. */
static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
{
@@ -1459,17 +1450,9 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
	struct efx_mac_stats *mac_stats = &efx->mac_stats;
	struct net_device_stats *stats = &net_dev->stats;

	/* Update stats if possible, but do not wait if another thread
	 * is updating them or if MAC stats fetches are temporarily
	 * disabled; slightly stale stats are acceptable.
	 */
	if (!spin_trylock(&efx->stats_lock))
		return stats;
	if (!efx->stats_disable_count) {
		efx->mac_op->update_stats(efx);
	spin_lock_bh(&efx->stats_lock);
	falcon_update_nic_stats(efx);
	}
	spin_unlock(&efx->stats_lock);
	spin_unlock_bh(&efx->stats_lock);

	stats->rx_packets = mac_stats->rx_packets;
	stats->tx_packets = mac_stats->tx_packets;
@@ -1726,7 +1709,6 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method,
{
	EFX_ASSERT_RESET_SERIALISED(efx);

	efx_stats_disable(efx);
	efx_stop_all(efx);
	mutex_lock(&efx->mac_lock);
	mutex_lock(&efx->spi_lock);
@@ -1776,10 +1758,8 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method,
	mutex_unlock(&efx->spi_lock);
	mutex_unlock(&efx->mac_lock);

	if (ok) {
	if (ok)
		efx_start_all(efx);
		efx_stats_enable(efx);
	}
	return rc;
}

@@ -1977,7 +1957,6 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
	efx->rx_checksum_enabled = true;
	spin_lock_init(&efx->netif_stop_lock);
	spin_lock_init(&efx->stats_lock);
	efx->stats_disable_count = 1;
	mutex_init(&efx->mac_lock);
	efx->mac_op = &efx_dummy_mac_operations;
	efx->phy_op = &efx_dummy_phy_operations;
@@ -2219,9 +2198,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
		goto fail4;
	}

	/* Switch to the running state before we expose the device to
	 * the OS.  This is to ensure that the initial gathering of
	 * MAC stats succeeds. */
	/* Switch to the running state before we expose the device to the OS,
	 * so that dev_open()|efx_start_all() will actually start the device */
	efx->state = STATE_RUNNING;

	rc = efx_register_netdev(efx);
+0 −2
Original line number Diff line number Diff line
@@ -60,8 +60,6 @@ extern void efx_process_channel_now(struct efx_channel *channel);
#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1)

/* Ports */
extern void efx_stats_disable(struct efx_nic *efx);
extern void efx_stats_enable(struct efx_nic *efx);
extern void efx_reconfigure_port(struct efx_nic *efx);
extern void __efx_reconfigure_port(struct efx_nic *efx);

+101 −30
Original line number Diff line number Diff line
@@ -36,8 +36,6 @@
 **************************************************************************
 */

static int disable_dma_stats;

/* This is set to 16 for a good reason.  In summary, if larger than
 * 16, the descriptor cache holds more than a default socket
 * buffer's worth of packets (for UDP we can only have at most one
@@ -1890,7 +1888,7 @@ static int falcon_reset_macs(struct efx_nic *efx)

	/* MAC stats will fail whilst the TX fifo is draining. Serialise
	 * the drain sequence with the statistics fetch */
	efx_stats_disable(efx);
	falcon_stop_nic_stats(efx);

	efx_reado(efx, &reg, FR_AB_MAC_CTRL);
	EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, 1);
@@ -1920,13 +1918,13 @@ static int falcon_reset_macs(struct efx_nic *efx)
		udelay(10);
	}

	efx_stats_enable(efx);

	/* If we've reset the EM block and the link is up, then
	 * we'll have to kick the XAUI link so the PHY can recover */
	if (efx->link_state.up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx))
		falcon_reset_xaui(efx);

	falcon_start_nic_stats(efx);

	return 0;
}

@@ -2010,25 +2008,19 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
	efx_writeo(efx, &reg, FR_AZ_RX_CFG);
}

int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset)
static void falcon_stats_request(struct efx_nic *efx)
{
	struct falcon_nic_data *nic_data = efx->nic_data;
	efx_oword_t reg;
	u32 *dma_done;
	int i;

	if (disable_dma_stats)
		return 0;
	WARN_ON(nic_data->stats_pending);
	WARN_ON(nic_data->stats_disable_count);

	/* Statistics fetch will fail if the MAC is in TX drain */
	if (falcon_rev(efx) >= FALCON_REV_B0) {
		efx_oword_t temp;
		efx_reado(efx, &temp, FR_AB_MAC_CTRL);
		if (EFX_OWORD_FIELD(temp, FRF_BB_TXFIFO_DRAIN_EN))
			return 0;
	}
	if (nic_data->stats_dma_done == NULL)
		return;	/* no mac selected */

	dma_done = (efx->stats_buffer.addr + done_offset);
	*dma_done = FALCON_STATS_NOT_DONE;
	*nic_data->stats_dma_done = FALCON_STATS_NOT_DONE;
	nic_data->stats_pending = true;
	wmb(); /* ensure done flag is clear */

	/* Initiate DMA transfer of stats */
@@ -2038,17 +2030,37 @@ int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset)
			     efx->stats_buffer.dma_addr);
	efx_writeo(efx, &reg, FR_AB_MAC_STAT_DMA);

	/* Wait for transfer to complete */
	for (i = 0; i < 400; i++) {
		if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) {
			rmb(); /* Ensure the stats are valid. */
			return 0;
		}
		udelay(10);
	mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2));
}

static void falcon_stats_complete(struct efx_nic *efx)
{
	struct falcon_nic_data *nic_data = efx->nic_data;

	if (!nic_data->stats_pending)
		return;

	nic_data->stats_pending = 0;
	if (*nic_data->stats_dma_done == FALCON_STATS_DONE) {
		rmb(); /* read the done flag before the stats */
		efx->mac_op->update_stats(efx);
	} else {
		EFX_ERR(efx, "timed out waiting for statistics\n");
	return -ETIMEDOUT;
	}
}

static void falcon_stats_timer_func(unsigned long context)
{
	struct efx_nic *efx = (struct efx_nic *)context;
	struct falcon_nic_data *nic_data = efx->nic_data;

	spin_lock(&efx->stats_lock);

	falcon_stats_complete(efx);
	if (nic_data->stats_disable_count == 0)
		falcon_stats_request(efx);

	spin_unlock(&efx->stats_lock);
}

/**************************************************************************
@@ -2206,10 +2218,12 @@ static void falcon_clock_mac(struct efx_nic *efx)
int falcon_switch_mac(struct efx_nic *efx)
{
	struct efx_mac_operations *old_mac_op = efx->mac_op;
	struct falcon_nic_data *nic_data = efx->nic_data;
	unsigned int stats_done_offset;
	int rc = 0;

	/* Don't try to fetch MAC stats while we're switching MACs */
	efx_stats_disable(efx);
	falcon_stop_nic_stats(efx);

	/* Internal loopbacks override the phy speed setting */
	if (efx->loopback_mode == LOOPBACK_GMAC) {
@@ -2224,6 +2238,12 @@ int falcon_switch_mac(struct efx_nic *efx)
	efx->mac_op = (EFX_IS10G(efx) ?
		       &falcon_xmac_operations : &falcon_gmac_operations);

	if (EFX_IS10G(efx))
		stats_done_offset = XgDmaDone_offset;
	else
		stats_done_offset = GDmaDone_offset;
	nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset;

	if (old_mac_op == efx->mac_op)
		goto out;

@@ -2235,7 +2255,7 @@ int falcon_switch_mac(struct efx_nic *efx)

	rc = falcon_reset_macs(efx);
out:
	efx_stats_enable(efx);
	falcon_start_nic_stats(efx);
	return rc;
}

@@ -2900,6 +2920,10 @@ int falcon_probe_nic(struct efx_nic *efx)
		goto fail6;
	}

	nic_data->stats_disable_count = 1;
	setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func,
		    (unsigned long)efx);

	return 0;

 fail6:
@@ -3125,11 +3149,58 @@ void falcon_remove_nic(struct efx_nic *efx)

void falcon_update_nic_stats(struct efx_nic *efx)
{
	struct falcon_nic_data *nic_data = efx->nic_data;
	efx_oword_t cnt;

	if (nic_data->stats_disable_count)
		return;

	efx_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP);
	efx->n_rx_nodesc_drop_cnt +=
		EFX_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT);

	if (nic_data->stats_pending &&
	    *nic_data->stats_dma_done == FALCON_STATS_DONE) {
		nic_data->stats_pending = false;
		rmb(); /* read the done flag before the stats */
		efx->mac_op->update_stats(efx);
	}
}

void falcon_start_nic_stats(struct efx_nic *efx)
{
	struct falcon_nic_data *nic_data = efx->nic_data;

	spin_lock_bh(&efx->stats_lock);
	if (--nic_data->stats_disable_count == 0)
		falcon_stats_request(efx);
	spin_unlock_bh(&efx->stats_lock);
}

void falcon_stop_nic_stats(struct efx_nic *efx)
{
	struct falcon_nic_data *nic_data = efx->nic_data;
	int i;

	might_sleep();

	spin_lock_bh(&efx->stats_lock);
	++nic_data->stats_disable_count;
	spin_unlock_bh(&efx->stats_lock);

	del_timer_sync(&nic_data->stats_timer);

	/* Wait enough time for the most recent transfer to
	 * complete. */
	for (i = 0; i < 4 && nic_data->stats_pending; i++) {
		if (*nic_data->stats_dma_done == FALCON_STATS_DONE)
			break;
		msleep(1);
	}

	spin_lock_bh(&efx->stats_lock);
	falcon_stats_complete(efx);
	spin_unlock_bh(&efx->stats_lock);
}

/**************************************************************************
+10 −2
Original line number Diff line number Diff line
@@ -75,10 +75,18 @@ struct falcon_board {
 * struct falcon_nic_data - Falcon NIC state
 * @pci_dev2: Secondary function of Falcon A
 * @board: Board state and functions
 * @stats_disable_count: Nest count for disabling statistics fetches
 * @stats_pending: Is there a pending DMA of MAC statistics.
 * @stats_timer: A timer for regularly fetching MAC statistics.
 * @stats_dma_done: Pointer to the flag which indicates DMA completion.
 */
struct falcon_nic_data {
	struct pci_dev *pci_dev2;
	struct falcon_board board;
	unsigned int stats_disable_count;
	bool stats_pending;
	struct timer_list stats_timer;
	u32 *stats_dma_done;
};

static inline struct falcon_board *falcon_board(struct efx_nic *efx)
@@ -128,8 +136,6 @@ extern void falcon_remove_port(struct efx_nic *efx);
/* MAC/PHY */
extern int falcon_switch_mac(struct efx_nic *efx);
extern bool falcon_xaui_link_ok(struct efx_nic *efx);
extern int falcon_dma_stats(struct efx_nic *efx,
			    unsigned int done_offset);
extern void falcon_drain_tx_fifo(struct efx_nic *efx);
extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx);
@@ -154,6 +160,8 @@ extern int falcon_flush_queues(struct efx_nic *efx);
extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method);
extern void falcon_remove_nic(struct efx_nic *efx);
extern void falcon_update_nic_stats(struct efx_nic *efx);
extern void falcon_start_nic_stats(struct efx_nic *efx);
extern void falcon_stop_nic_stats(struct efx_nic *efx);
extern void falcon_set_multicast_hash(struct efx_nic *efx);
extern int falcon_reset_xaui(struct efx_nic *efx);

+5 −5
Original line number Diff line number Diff line
@@ -347,14 +347,14 @@ static ssize_t set_phy_flash_cfg(struct device *dev,
		 * MAC stats accordingly. */
		efx->phy_mode = new_mode;
		if (new_mode & PHY_MODE_SPECIAL)
			efx_stats_disable(efx);
			falcon_stop_nic_stats(efx);
		if (falcon_board(efx)->type->id == FALCON_BOARD_SFE4001)
			err = sfe4001_poweron(efx);
		else
			err = sfn4111t_reset(efx);
		efx_reconfigure_port(efx);
		if (!(new_mode & PHY_MODE_SPECIAL))
			efx_stats_enable(efx);
			falcon_start_nic_stats(efx);
	}
	rtnl_unlock();

@@ -441,7 +441,7 @@ static int sfe4001_init(struct efx_nic *efx)
	if (efx->phy_mode & PHY_MODE_SPECIAL) {
		/* PHY won't generate a 156.25 MHz clock and MAC stats fetch
		 * will fail. */
		efx_stats_disable(efx);
		falcon_stop_nic_stats(efx);
	}
	rc = sfe4001_poweron(efx);
	if (rc)
@@ -504,7 +504,7 @@ static void sfn4111t_init_phy(struct efx_nic *efx)
			return;

		efx->phy_mode = PHY_MODE_SPECIAL;
		efx_stats_disable(efx);
		falcon_stop_nic_stats(efx);
	}

	sfn4111t_reset(efx);
@@ -531,7 +531,7 @@ static int sfn4111t_init(struct efx_nic *efx)
	if (efx->phy_mode & PHY_MODE_SPECIAL)
		/* PHY may not generate a 156.25 MHz clock and MAC
		 * stats fetch will fail. */
		efx_stats_disable(efx);
		falcon_stop_nic_stats(efx);

	return 0;

Loading