Commit 78c1f0a0 authored by Steve Hodgson's avatar Steve Hodgson Committed by David S. Miller

sfc: Generalise link state monitoring

Use the efx_nic_type::monitor operation or event handling as
appropriate.
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d3245b28
...@@ -1174,10 +1174,18 @@ static void efx_start_all(struct efx_nic *efx) ...@@ -1174,10 +1174,18 @@ static void efx_start_all(struct efx_nic *efx)
falcon_enable_interrupts(efx); falcon_enable_interrupts(efx);
/* Start the hardware monitor (if there is one) if we're in RUNNING */ /* Start the hardware monitor if there is one. Otherwise (we're link
if (efx->state == STATE_RUNNING && efx->type->monitor != NULL) * event driven), we have to poll the PHY because after an event queue
* flush, we could have a missed a link state change */
if (efx->type->monitor != NULL) {
queue_delayed_work(efx->workqueue, &efx->monitor_work, queue_delayed_work(efx->workqueue, &efx->monitor_work,
efx_monitor_interval); efx_monitor_interval);
} else {
mutex_lock(&efx->mac_lock);
if (efx->phy_op->poll(efx))
efx_link_status_changed(efx);
mutex_unlock(&efx->mac_lock);
}
efx->type->start_stats(efx); efx->type->start_stats(efx);
} }
...@@ -1421,6 +1429,10 @@ static int efx_net_open(struct net_device *net_dev) ...@@ -1421,6 +1429,10 @@ static int efx_net_open(struct net_device *net_dev)
if (efx->phy_mode & PHY_MODE_SPECIAL) if (efx->phy_mode & PHY_MODE_SPECIAL)
return -EBUSY; return -EBUSY;
/* Notify the kernel of the link state polled during driver load,
* before the monitor starts running */
efx_link_status_changed(efx);
efx_start_all(efx); efx_start_all(efx);
return 0; return 0;
} }
......
...@@ -563,14 +563,49 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, ...@@ -563,14 +563,49 @@ efx_test_loopback(struct efx_tx_queue *tx_queue,
return 0; return 0;
} }
/* Wait for link up. On Falcon, we would prefer to rely on efx_monitor, but
* any contention on the mac lock (via e.g. efx_mac_mcast_work) causes it
* to delay and retry. Therefore, it's safer to just poll directly. Wait
* for link up and any faults to dissipate. */
static int efx_wait_for_link(struct efx_nic *efx)
{
struct efx_link_state *link_state = &efx->link_state;
int count;
bool link_up;
for (count = 0; count < 40; count++) {
schedule_timeout_uninterruptible(HZ / 10);
if (efx->type->monitor != NULL) {
mutex_lock(&efx->mac_lock);
efx->type->monitor(efx);
mutex_unlock(&efx->mac_lock);
} else {
struct efx_channel *channel = &efx->channel[0];
if (channel->work_pending)
efx_process_channel_now(channel);
}
mutex_lock(&efx->mac_lock);
link_up = link_state->up;
if (link_up)
link_up = !efx->mac_op->check_fault(efx);
mutex_unlock(&efx->mac_lock);
if (link_up)
return 0;
}
return -ETIMEDOUT;
}
static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests,
unsigned int loopback_modes) unsigned int loopback_modes)
{ {
enum efx_loopback_mode mode; enum efx_loopback_mode mode;
struct efx_loopback_state *state; struct efx_loopback_state *state;
struct efx_tx_queue *tx_queue; struct efx_tx_queue *tx_queue;
bool link_up; int rc = 0;
int count, rc = 0;
/* Set the port loopback_selftest member. From this point on /* Set the port loopback_selftest member. From this point on
* all received packets will be dropped. Mark the state as * all received packets will be dropped. Mark the state as
...@@ -589,43 +624,23 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, ...@@ -589,43 +624,23 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests,
/* Move the port into the specified loopback mode. */ /* Move the port into the specified loopback mode. */
state->flush = true; state->flush = true;
mutex_lock(&efx->mac_lock);
efx->loopback_mode = mode; efx->loopback_mode = mode;
efx_reconfigure_port(efx); rc = __efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
/* Wait for the PHY to signal the link is up. Interrupts if (rc) {
* are enabled for PHY's using LASI, otherwise we poll() EFX_ERR(efx, "unable to move into %s loopback\n",
* quickly */ LOOPBACK_MODE(efx));
count = 0; goto out;
do { }
struct efx_channel *channel = &efx->channel[0];
efx->phy_op->poll(efx);
schedule_timeout_uninterruptible(HZ / 10);
if (channel->work_pending)
efx_process_channel_now(channel);
/* Wait for PHY events to be processed */
flush_workqueue(efx->workqueue);
rmb();
/* We need both the PHY and MAC-PHY links to be OK */
link_up = efx->link_state.up;
if (link_up)
link_up = !efx->mac_op->check_fault(efx);
} while ((++count < 20) && !link_up);
/* The link should now be up. If it isn't, there is no point rc = efx_wait_for_link(efx);
* in attempting a loopback test */ if (rc) {
if (!link_up) {
EFX_ERR(efx, "loopback %s never came up\n", EFX_ERR(efx, "loopback %s never came up\n",
LOOPBACK_MODE(efx)); LOOPBACK_MODE(efx));
rc = -EIO;
goto out; goto out;
} }
EFX_LOG(efx, "link came up in %s loopback in %d iterations\n",
LOOPBACK_MODE(efx), count);
/* Test every TX queue */ /* Test every TX queue */
efx_for_each_tx_queue(tx_queue, efx) { efx_for_each_tx_queue(tx_queue, efx) {
state->offload_csum = (tx_queue->queue == state->offload_csum = (tx_queue->queue ==
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment