diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/mcdi.c')
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi.c | 55 |
1 files changed, 44 insertions, 11 deletions
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 7bd4b14bf3b3..5239cf9bdc56 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c | |||
@@ -52,12 +52,7 @@ static void efx_mcdi_timeout_async(unsigned long context); | |||
52 | static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, | 52 | static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, |
53 | bool *was_attached_out); | 53 | bool *was_attached_out); |
54 | static bool efx_mcdi_poll_once(struct efx_nic *efx); | 54 | static bool efx_mcdi_poll_once(struct efx_nic *efx); |
55 | 55 | static void efx_mcdi_abandon(struct efx_nic *efx); | |
56 | static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) | ||
57 | { | ||
58 | EFX_BUG_ON_PARANOID(!efx->mcdi); | ||
59 | return &efx->mcdi->iface; | ||
60 | } | ||
61 | 56 | ||
62 | int efx_mcdi_init(struct efx_nic *efx) | 57 | int efx_mcdi_init(struct efx_nic *efx) |
63 | { | 58 | { |
@@ -558,6 +553,8 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, | |||
558 | rc = 0; | 553 | rc = 0; |
559 | } | 554 | } |
560 | 555 | ||
556 | efx_mcdi_abandon(efx); | ||
557 | |||
561 | /* Close the race with efx_mcdi_ev_cpl() executing just too late | 558 | /* Close the race with efx_mcdi_ev_cpl() executing just too late |
562 | * and completing a request we've just cancelled, by ensuring | 559 | * and completing a request we've just cancelled, by ensuring |
563 | * that the seqno check therein fails. | 560 | * that the seqno check therein fails. |
@@ -672,6 +669,9 @@ int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, | |||
672 | if (efx->mc_bist_for_other_fn) | 669 | if (efx->mc_bist_for_other_fn) |
673 | return -ENETDOWN; | 670 | return -ENETDOWN; |
674 | 671 | ||
672 | if (mcdi->mode == MCDI_MODE_FAIL) | ||
673 | return -ENETDOWN; | ||
674 | |||
675 | efx_mcdi_acquire_sync(mcdi); | 675 | efx_mcdi_acquire_sync(mcdi); |
676 | efx_mcdi_send_request(efx, cmd, inbuf, inlen); | 676 | efx_mcdi_send_request(efx, cmd, inbuf, inlen); |
677 | return 0; | 677 | return 0; |
@@ -812,7 +812,11 @@ void efx_mcdi_mode_poll(struct efx_nic *efx) | |||
812 | return; | 812 | return; |
813 | 813 | ||
814 | mcdi = efx_mcdi(efx); | 814 | mcdi = efx_mcdi(efx); |
815 | if (mcdi->mode == MCDI_MODE_POLL) | 815 | /* If already in polling mode, nothing to do. |
816 | * If in fail-fast state, don't switch to polled completion. | ||
817 | * FLR recovery will do that later. | ||
818 | */ | ||
819 | if (mcdi->mode == MCDI_MODE_POLL || mcdi->mode == MCDI_MODE_FAIL) | ||
816 | return; | 820 | return; |
817 | 821 | ||
818 | /* We can switch from event completion to polled completion, because | 822 | /* We can switch from event completion to polled completion, because |
@@ -841,8 +845,8 @@ void efx_mcdi_flush_async(struct efx_nic *efx) | |||
841 | 845 | ||
842 | mcdi = efx_mcdi(efx); | 846 | mcdi = efx_mcdi(efx); |
843 | 847 | ||
844 | /* We must be in polling mode so no more requests can be queued */ | 848 | /* We must be in poll or fail mode so no more requests can be queued */ |
845 | BUG_ON(mcdi->mode != MCDI_MODE_POLL); | 849 | BUG_ON(mcdi->mode == MCDI_MODE_EVENTS); |
846 | 850 | ||
847 | del_timer_sync(&mcdi->async_timer); | 851 | del_timer_sync(&mcdi->async_timer); |
848 | 852 | ||
@@ -875,8 +879,11 @@ void efx_mcdi_mode_event(struct efx_nic *efx) | |||
875 | return; | 879 | return; |
876 | 880 | ||
877 | mcdi = efx_mcdi(efx); | 881 | mcdi = efx_mcdi(efx); |
878 | 882 | /* If already in event completion mode, nothing to do. | |
879 | if (mcdi->mode == MCDI_MODE_EVENTS) | 883 | * If in fail-fast state, don't switch to event completion. FLR |
884 | * recovery will do that later. | ||
885 | */ | ||
886 | if (mcdi->mode == MCDI_MODE_EVENTS || mcdi->mode == MCDI_MODE_FAIL) | ||
880 | return; | 887 | return; |
881 | 888 | ||
882 | /* We can't switch from polled to event completion in the middle of a | 889 | /* We can't switch from polled to event completion in the middle of a |
@@ -966,6 +973,19 @@ static void efx_mcdi_ev_bist(struct efx_nic *efx) | |||
966 | spin_unlock(&mcdi->iface_lock); | 973 | spin_unlock(&mcdi->iface_lock); |
967 | } | 974 | } |
968 | 975 | ||
976 | /* MCDI timeouts seen, so make all MCDI calls fail-fast and issue an FLR to try | ||
977 | * to recover. | ||
978 | */ | ||
979 | static void efx_mcdi_abandon(struct efx_nic *efx) | ||
980 | { | ||
981 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
982 | |||
983 | if (xchg(&mcdi->mode, MCDI_MODE_FAIL) == MCDI_MODE_FAIL) | ||
984 | return; /* it had already been done */ | ||
985 | netif_dbg(efx, hw, efx->net_dev, "MCDI is timing out; trying to recover\n"); | ||
986 | efx_schedule_reset(efx, RESET_TYPE_MCDI_TIMEOUT); | ||
987 | } | ||
988 | |||
969 | /* Called from falcon_process_eventq for MCDI events */ | 989 | /* Called from falcon_process_eventq for MCDI events */ |
970 | void efx_mcdi_process_event(struct efx_channel *channel, | 990 | void efx_mcdi_process_event(struct efx_channel *channel, |
971 | efx_qword_t *event) | 991 | efx_qword_t *event) |
@@ -1512,6 +1532,19 @@ int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method) | |||
1512 | { | 1532 | { |
1513 | int rc; | 1533 | int rc; |
1514 | 1534 | ||
1535 | /* If MCDI is down, we can't handle_assertion */ | ||
1536 | if (method == RESET_TYPE_MCDI_TIMEOUT) { | ||
1537 | rc = pci_reset_function(efx->pci_dev); | ||
1538 | if (rc) | ||
1539 | return rc; | ||
1540 | /* Re-enable polled MCDI completion */ | ||
1541 | if (efx->mcdi) { | ||
1542 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
1543 | mcdi->mode = MCDI_MODE_POLL; | ||
1544 | } | ||
1545 | return 0; | ||
1546 | } | ||
1547 | |||
1515 | /* Recover from a failed assertion pre-reset */ | 1548 | /* Recover from a failed assertion pre-reset */ |
1516 | rc = efx_mcdi_handle_assertion(efx); | 1549 | rc = efx_mcdi_handle_assertion(efx); |
1517 | if (rc) | 1550 | if (rc) |