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

Commit 766ca0fa authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller
Browse files

sfc: Rework MAC, PHY and board event handling



From: Steve Hodgson <shodgson@solarflare.com>

MAC, PHY and board events may be separately enabled and signalled.
Our current arrangement of chaining the polling functions can result
in events being missed.  Change them to be more independent.

Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 04cc8cac
Loading
Loading
Loading
Loading
+42 −21
Original line number Original line Diff line number Diff line
@@ -612,10 +612,9 @@ void efx_reconfigure_port(struct efx_nic *efx)
/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all()
/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all()
 * we don't efx_reconfigure_port() if the port is disabled. Care is taken
 * we don't efx_reconfigure_port() if the port is disabled. Care is taken
 * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */
 * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */
static void efx_reconfigure_work(struct work_struct *data)
static void efx_phy_work(struct work_struct *data)
{
{
	struct efx_nic *efx = container_of(data, struct efx_nic,
	struct efx_nic *efx = container_of(data, struct efx_nic, phy_work);
					   reconfigure_work);


	mutex_lock(&efx->mac_lock);
	mutex_lock(&efx->mac_lock);
	if (efx->port_enabled)
	if (efx->port_enabled)
@@ -623,6 +622,16 @@ static void efx_reconfigure_work(struct work_struct *data)
	mutex_unlock(&efx->mac_lock);
	mutex_unlock(&efx->mac_lock);
}
}


static void efx_mac_work(struct work_struct *data)
{
	struct efx_nic *efx = container_of(data, struct efx_nic, mac_work);

	mutex_lock(&efx->mac_lock);
	if (efx->port_enabled)
		efx->mac_op->irq(efx);
	mutex_unlock(&efx->mac_lock);
}

static int efx_probe_port(struct efx_nic *efx)
static int efx_probe_port(struct efx_nic *efx)
{
{
	int rc;
	int rc;
@@ -688,7 +697,7 @@ static int efx_init_port(struct efx_nic *efx)


/* Allow efx_reconfigure_port() to be scheduled, and close the window
/* Allow efx_reconfigure_port() to be scheduled, and close the window
 * between efx_stop_port and efx_flush_all whereby a previously scheduled
 * between efx_stop_port and efx_flush_all whereby a previously scheduled
 * efx_reconfigure_port() may have been cancelled */
 * efx_phy_work()/efx_mac_work() may have been cancelled */
static void efx_start_port(struct efx_nic *efx)
static void efx_start_port(struct efx_nic *efx)
{
{
	EFX_LOG(efx, "start port\n");
	EFX_LOG(efx, "start port\n");
@@ -697,13 +706,14 @@ static void efx_start_port(struct efx_nic *efx)
	mutex_lock(&efx->mac_lock);
	mutex_lock(&efx->mac_lock);
	efx->port_enabled = true;
	efx->port_enabled = true;
	__efx_reconfigure_port(efx);
	__efx_reconfigure_port(efx);
	efx->mac_op->irq(efx);
	mutex_unlock(&efx->mac_lock);
	mutex_unlock(&efx->mac_lock);
}
}


/* Prevent efx_reconfigure_work and efx_monitor() from executing, and
/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing,
 * efx_set_multicast_list() from scheduling efx_reconfigure_work.
 * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work
 * efx_reconfigure_work can still be scheduled via NAPI processing
 * and efx_mac_work may still be scheduled via NAPI processing until
 * until efx_flush_all() is called */
 * efx_flush_all() is called */
static void efx_stop_port(struct efx_nic *efx)
static void efx_stop_port(struct efx_nic *efx)
{
{
	EFX_LOG(efx, "stop port\n");
	EFX_LOG(efx, "stop port\n");
@@ -1094,7 +1104,8 @@ static void efx_flush_all(struct efx_nic *efx)
		cancel_delayed_work_sync(&rx_queue->work);
		cancel_delayed_work_sync(&rx_queue->work);


	/* Stop scheduled port reconfigurations */
	/* Stop scheduled port reconfigurations */
	cancel_work_sync(&efx->reconfigure_work);
	cancel_work_sync(&efx->mac_work);
	cancel_work_sync(&efx->phy_work);


}
}


@@ -1131,7 +1142,7 @@ static void efx_stop_all(struct efx_nic *efx)
	 * window to loose phy events */
	 * window to loose phy events */
	efx_stop_port(efx);
	efx_stop_port(efx);


	/* Flush reconfigure_work, refill_workqueue, monitor_work */
	/* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */
	efx_flush_all(efx);
	efx_flush_all(efx);


	/* Isolate the MAC from the TX and RX engines, so that queue
	/* Isolate the MAC from the TX and RX engines, so that queue
@@ -1203,24 +1214,31 @@ static void efx_monitor(struct work_struct *data)
{
{
	struct efx_nic *efx = container_of(data, struct efx_nic,
	struct efx_nic *efx = container_of(data, struct efx_nic,
					   monitor_work.work);
					   monitor_work.work);
	int rc;


	EFX_TRACE(efx, "hardware monitor executing on CPU %d\n",
	EFX_TRACE(efx, "hardware monitor executing on CPU %d\n",
		  raw_smp_processor_id());
		  raw_smp_processor_id());



	/* If the mac_lock is already held then it is likely a port
	/* If the mac_lock is already held then it is likely a port
	 * reconfiguration is already in place, which will likely do
	 * reconfiguration is already in place, which will likely do
	 * most of the work of check_hw() anyway. */
	 * most of the work of check_hw() anyway. */
	if (!mutex_trylock(&efx->mac_lock)) {
	if (!mutex_trylock(&efx->mac_lock))
		queue_delayed_work(efx->workqueue, &efx->monitor_work,
		goto out_requeue;
				   efx_monitor_interval);
	if (!efx->port_enabled)
		return;
		goto out_unlock;
	rc = efx->board_info.monitor(efx);
	if (rc) {
		EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
			(rc == -ERANGE) ? "reported fault" : "failed");
		efx->phy_mode |= PHY_MODE_LOW_POWER;
		falcon_sim_phy_event(efx);
	}
	}
	efx->phy_op->poll(efx);
	efx->mac_op->poll(efx);


	if (efx->port_enabled)
out_unlock:
		efx->mac_op->check_hw(efx);
	mutex_unlock(&efx->mac_lock);
	mutex_unlock(&efx->mac_lock);

out_requeue:
	queue_delayed_work(efx->workqueue, &efx->monitor_work,
	queue_delayed_work(efx->workqueue, &efx->monitor_work,
			   efx_monitor_interval);
			   efx_monitor_interval);
}
}
@@ -1477,7 +1495,7 @@ static void efx_set_multicast_list(struct net_device *net_dev)
		return;
		return;


	if (changed)
	if (changed)
		queue_work(efx->workqueue, &efx->reconfigure_work);
		queue_work(efx->workqueue, &efx->phy_work);


	/* Create and activate new global multicast hash table */
	/* Create and activate new global multicast hash table */
	falcon_set_multicast_hash(efx);
	falcon_set_multicast_hash(efx);
@@ -1800,12 +1818,14 @@ void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {}


static struct efx_mac_operations efx_dummy_mac_operations = {
static struct efx_mac_operations efx_dummy_mac_operations = {
	.reconfigure	= efx_port_dummy_op_void,
	.reconfigure	= efx_port_dummy_op_void,
	.poll		= efx_port_dummy_op_void,
	.irq		= efx_port_dummy_op_void,
};
};


static struct efx_phy_operations efx_dummy_phy_operations = {
static struct efx_phy_operations efx_dummy_phy_operations = {
	.init		 = efx_port_dummy_op_int,
	.init		 = efx_port_dummy_op_int,
	.reconfigure	 = efx_port_dummy_op_void,
	.reconfigure	 = efx_port_dummy_op_void,
	.check_hw        = efx_port_dummy_op_int,
	.poll		 = efx_port_dummy_op_void,
	.fini		 = efx_port_dummy_op_void,
	.fini		 = efx_port_dummy_op_void,
	.clear_interrupt = efx_port_dummy_op_void,
	.clear_interrupt = efx_port_dummy_op_void,
};
};
@@ -1857,7 +1877,8 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
	efx->mac_op = &efx_dummy_mac_operations;
	efx->mac_op = &efx_dummy_mac_operations;
	efx->phy_op = &efx_dummy_phy_operations;
	efx->phy_op = &efx_dummy_phy_operations;
	efx->mii.dev = net_dev;
	efx->mii.dev = net_dev;
	INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work);
	INIT_WORK(&efx->phy_work, efx_phy_work);
	INIT_WORK(&efx->mac_work, efx_mac_work);
	atomic_set(&efx->netif_stop_count, 1);
	atomic_set(&efx->netif_stop_count, 1);


	for (i = 0; i < EFX_MAX_CHANNELS; i++) {
	for (i = 0; i < EFX_MAX_CHANNELS; i++) {
+9 −11
Original line number Original line Diff line number Diff line
@@ -910,22 +910,20 @@ static void falcon_handle_global_event(struct efx_channel *channel,
				       efx_qword_t *event)
				       efx_qword_t *event)
{
{
	struct efx_nic *efx = channel->efx;
	struct efx_nic *efx = channel->efx;
	bool is_phy_event = false, handled = false;
	bool handled = false;


	/* Check for interrupt on either port.  Some boards have a
	 * single PHY wired to the interrupt line for port 1. */
	if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) ||
	if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) ||
	    EFX_QWORD_FIELD(*event, G_PHY1_INTR) ||
	    EFX_QWORD_FIELD(*event, G_PHY1_INTR) ||
	    EFX_QWORD_FIELD(*event, XG_PHY_INTR))
	    EFX_QWORD_FIELD(*event, XG_PHY_INTR) ||
		is_phy_event = true;
	    EFX_QWORD_FIELD(*event, XFP_PHY_INTR)) {
		efx->phy_op->clear_interrupt(efx);
		queue_work(efx->workqueue, &efx->phy_work);
		handled = true;
	}


	if ((falcon_rev(efx) >= FALCON_REV_B0) &&
	if ((falcon_rev(efx) >= FALCON_REV_B0) &&
	    EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0))
	    EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) {
		is_phy_event = true;
		queue_work(efx->workqueue, &efx->mac_work);

	if (is_phy_event) {
		efx->phy_op->clear_interrupt(efx);
		queue_work(efx->workqueue, &efx->reconfigure_work);
		handled = true;
		handled = true;
	}
	}


+2 −6
Original line number Original line Diff line number Diff line
@@ -221,13 +221,9 @@ static void falcon_update_stats_gmac(struct efx_nic *efx)
	mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64;
	mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64;
}
}


static int falcon_check_gmac(struct efx_nic *efx)
{
	return efx->phy_op->check_hw(efx);
}

struct efx_mac_operations falcon_gmac_operations = {
struct efx_mac_operations falcon_gmac_operations = {
	.reconfigure	= falcon_reconfigure_gmac,
	.reconfigure	= falcon_reconfigure_gmac,
	.update_stats	= falcon_update_stats_gmac,
	.update_stats	= falcon_update_stats_gmac,
	.check_hw	= falcon_check_gmac,
	.irq		= efx_port_dummy_op_void,
	.poll		= efx_port_dummy_op_void,
};
};
+2 −0
Original line number Original line Diff line number Diff line
@@ -1051,6 +1051,8 @@
#define XG_MNT_INTR_B0_WIDTH 1
#define XG_MNT_INTR_B0_WIDTH 1
#define RX_RECOVERY_A1_LBN 11
#define RX_RECOVERY_A1_LBN 11
#define RX_RECOVERY_A1_WIDTH 1
#define RX_RECOVERY_A1_WIDTH 1
#define XFP_PHY_INTR_LBN 10
#define XFP_PHY_INTR_WIDTH 1
#define XG_PHY_INTR_LBN 9
#define XG_PHY_INTR_LBN 9
#define XG_PHY_INTR_WIDTH 1
#define XG_PHY_INTR_WIDTH 1
#define G_PHY1_INTR_LBN 8
#define G_PHY1_INTR_LBN 8
+22 −20
Original line number Original line Diff line number Diff line
@@ -342,33 +342,35 @@ static void falcon_update_stats_xmac(struct efx_nic *efx)
		 mac_stats->rx_control * 64);
		 mac_stats->rx_control * 64);
}
}


static int falcon_check_xmac(struct efx_nic *efx)
static void falcon_xmac_irq(struct efx_nic *efx)
{
{
	bool xaui_link_ok;
	/* The XGMII link has a transient fault, which indicates either:
	int rc;
	 *   - there's a transient xgmii fault
	 *   - falcon's end of the xaui link may need a kick
	 *   - the wire-side link may have gone down, but the lasi/poll()
	 *     hasn't noticed yet.
	 *
	 * We only want to even bother polling XAUI if we're confident it's
	 * not (1) or (3). In both cases, the only reliable way to spot this
	 * is to wait a bit. We do this here by forcing the mac link state
	 * to down, and waiting for the mac poll to come round and check
	 */
	efx->mac_up = false;
}


	if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
static void falcon_poll_xmac(struct efx_nic *efx)
	    efx_phy_mode_disabled(efx->phy_mode))
{
		return 0;
	if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up)
		return;


	falcon_mask_status_intr(efx, false);
	falcon_mask_status_intr(efx, false);
	xaui_link_ok = falcon_xaui_link_ok(efx);
	falcon_check_xaui_link_up(efx, 1);

	if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok)
		falcon_reset_xaui(efx);

	/* Call the PHY check_hw routine */
	rc = efx->phy_op->check_hw(efx);

	/* Unmask interrupt if everything was (and still is) ok */
	if (xaui_link_ok && efx->link_up)
	falcon_mask_status_intr(efx, true);
	falcon_mask_status_intr(efx, true);

	return rc;
}
}


struct efx_mac_operations falcon_xmac_operations = {
struct efx_mac_operations falcon_xmac_operations = {
	.reconfigure	= falcon_reconfigure_xmac,
	.reconfigure	= falcon_reconfigure_xmac,
	.update_stats	= falcon_update_stats_xmac,
	.update_stats	= falcon_update_stats_xmac,
	.check_hw	= falcon_check_xmac,
	.irq		= falcon_xmac_irq,
	.poll		= falcon_poll_xmac,
};
};
Loading