From 5731d7b35e5b87157a9b9973cc2eff70c50aec58 Mon Sep 17 00:00:00 2001 From: Robert Stonehouse Date: Wed, 9 Oct 2013 11:52:43 +0100 Subject: sfc: Refactor efx_mcdi_poll() by introducing efx_mcdi_poll_once() Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/mcdi.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers/net/ethernet/sfc/mcdi.c') diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 366c8e3e378..9f26e46ee28 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -50,6 +50,7 @@ struct efx_mcdi_async_param { static void efx_mcdi_timeout_async(unsigned long context); static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, bool *was_attached_out); +static bool efx_mcdi_poll_once(struct efx_nic *efx); static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) { @@ -237,6 +238,21 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx) } } +static bool efx_mcdi_poll_once(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + rmb(); + if (!efx->type->mcdi_poll_response(efx)) + return false; + + spin_lock_bh(&mcdi->iface_lock); + efx_mcdi_read_response_header(efx); + spin_unlock_bh(&mcdi->iface_lock); + + return true; +} + static int efx_mcdi_poll(struct efx_nic *efx) { struct efx_mcdi_iface *mcdi = efx_mcdi(efx); @@ -272,18 +288,13 @@ static int efx_mcdi_poll(struct efx_nic *efx) time = jiffies; - rmb(); - if (efx->type->mcdi_poll_response(efx)) + if (efx_mcdi_poll_once(efx)) break; if (time_after(time, finish)) return -ETIMEDOUT; } - spin_lock_bh(&mcdi->iface_lock); - efx_mcdi_read_response_header(efx); - spin_unlock_bh(&mcdi->iface_lock); - /* Return rc=0 like wait_event_timeout() */ return 0; } -- cgit v1.2.3-70-g09d2 From 6b294b8efedaa7cf7507154148e2c79766ad6f96 Mon Sep 17 00:00:00 2001 From: Robert Stonehouse Date: Wed, 9 Oct 2013 11:52:48 +0100 Subject: sfc: Poll for MCDI completion once before timeout occurs There is an as-yet unexplained bug that sometimes prevents (or delays) the driver seeing the completion event for a completed MCDI request on the SFC9120. The requested configuration change will have happened but the driver assumes it to have failed, and this can result in further failures. We can mitigate this by polling for completion after unsuccessfully waiting for an event. Fixes: 8127d661e77f ('sfc: Add support for Solarflare SFC9100 family') Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/mcdi.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/sfc/mcdi.c') diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 9f26e46ee28..4b0bd8a1514 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -630,6 +630,16 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, rc = efx_mcdi_await_completion(efx); if (rc != 0) { + netif_err(efx, hw, efx->net_dev, + "MC command 0x%x inlen %d mode %d timed out\n", + cmd, (int)inlen, mcdi->mode); + + if (mcdi->mode == MCDI_MODE_EVENTS && efx_mcdi_poll_once(efx)) { + netif_err(efx, hw, efx->net_dev, + "MCDI request was completed without an event\n"); + rc = 0; + } + /* Close the race with efx_mcdi_ev_cpl() executing just too late * and completing a request we've just cancelled, by ensuring * that the seqno check therein fails. @@ -638,11 +648,9 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, ++mcdi->seqno; ++mcdi->credits; spin_unlock_bh(&mcdi->iface_lock); + } - netif_err(efx, hw, efx->net_dev, - "MC command 0x%x inlen %d mode %d timed out\n", - cmd, (int)inlen, mcdi->mode); - } else { + if (rc == 0) { size_t hdr_len, data_len; /* At the very least we need a memory barrier here to ensure -- cgit v1.2.3-70-g09d2