diff options
author | Steve Hodgson <shodgson@solarflare.com> | 2010-02-03 04:30:17 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-03 22:12:43 -0500 |
commit | 8b2103add08b79abd3ac7015b4bac744c0af534e (patch) | |
tree | 018ee0b97a17a702dd163d17cf13483ae1906408 /drivers/net/sfc | |
parent | 5297a98d5dd6de86fe1e2ffc9ea60cdf59b71443 (diff) |
sfc: Handle firmware assertion failure while resetting
This allows the driver to recover if the MC firmware has crashed due
to an assertion failure.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r-- | drivers/net/sfc/mcdi.c | 63 | ||||
-rw-r--r-- | drivers/net/sfc/siena.c | 6 |
2 files changed, 44 insertions, 25 deletions
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c index 9f035b9f0350..e9f0e5ee4519 100644 --- a/drivers/net/sfc/mcdi.c +++ b/drivers/net/sfc/mcdi.c | |||
@@ -896,29 +896,27 @@ fail: | |||
896 | return rc; | 896 | return rc; |
897 | } | 897 | } |
898 | 898 | ||
899 | int efx_mcdi_handle_assertion(struct efx_nic *efx) | 899 | static int efx_mcdi_read_assertion(struct efx_nic *efx) |
900 | { | 900 | { |
901 | union { | 901 | u8 inbuf[MC_CMD_GET_ASSERTS_IN_LEN]; |
902 | u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN]; | 902 | u8 outbuf[MC_CMD_GET_ASSERTS_OUT_LEN]; |
903 | u8 reboot[MC_CMD_REBOOT_IN_LEN]; | ||
904 | } inbuf; | ||
905 | u8 assertion[MC_CMD_GET_ASSERTS_OUT_LEN]; | ||
906 | unsigned int flags, index, ofst; | 903 | unsigned int flags, index, ofst; |
907 | const char *reason; | 904 | const char *reason; |
908 | size_t outlen; | 905 | size_t outlen; |
909 | int retry; | 906 | int retry; |
910 | int rc; | 907 | int rc; |
911 | 908 | ||
912 | /* Check if the MC is in the assertion handler, retrying twice. Once | 909 | /* Attempt to read any stored assertion state before we reboot |
910 | * the mcfw out of the assertion handler. Retry twice, once | ||
913 | * because a boot-time assertion might cause this command to fail | 911 | * because a boot-time assertion might cause this command to fail |
914 | * with EINTR. And once again because GET_ASSERTS can race with | 912 | * with EINTR. And once again because GET_ASSERTS can race with |
915 | * MC_CMD_REBOOT running on the other port. */ | 913 | * MC_CMD_REBOOT running on the other port. */ |
916 | retry = 2; | 914 | retry = 2; |
917 | do { | 915 | do { |
918 | MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0); | 916 | MCDI_SET_DWORD(inbuf, GET_ASSERTS_IN_CLEAR, 1); |
919 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, | 917 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, |
920 | inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN, | 918 | inbuf, MC_CMD_GET_ASSERTS_IN_LEN, |
921 | assertion, sizeof(assertion), &outlen); | 919 | outbuf, sizeof(outbuf), &outlen); |
922 | } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); | 920 | } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); |
923 | 921 | ||
924 | if (rc) | 922 | if (rc) |
@@ -926,21 +924,11 @@ int efx_mcdi_handle_assertion(struct efx_nic *efx) | |||
926 | if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) | 924 | if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) |
927 | return -EINVAL; | 925 | return -EINVAL; |
928 | 926 | ||
929 | flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS); | 927 | /* Print out any recorded assertion state */ |
928 | flags = MCDI_DWORD(outbuf, GET_ASSERTS_OUT_GLOBAL_FLAGS); | ||
930 | if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) | 929 | if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) |
931 | return 0; | 930 | return 0; |
932 | 931 | ||
933 | /* Reset the hardware atomically such that only one port with succeed. | ||
934 | * This command will succeed if a reboot is no longer required (because | ||
935 | * the other port did it first), but fail with EIO if it succeeds. | ||
936 | */ | ||
937 | BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); | ||
938 | MCDI_SET_DWORD(inbuf.reboot, REBOOT_IN_FLAGS, | ||
939 | MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); | ||
940 | efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf.reboot, MC_CMD_REBOOT_IN_LEN, | ||
941 | NULL, 0, NULL); | ||
942 | |||
943 | /* Print out the assertion */ | ||
944 | reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) | 932 | reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) |
945 | ? "system-level assertion" | 933 | ? "system-level assertion" |
946 | : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) | 934 | : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) |
@@ -949,20 +937,45 @@ int efx_mcdi_handle_assertion(struct efx_nic *efx) | |||
949 | ? "watchdog reset" | 937 | ? "watchdog reset" |
950 | : "unknown assertion"; | 938 | : "unknown assertion"; |
951 | EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, | 939 | EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, |
952 | MCDI_DWORD(assertion, GET_ASSERTS_OUT_SAVED_PC_OFFS), | 940 | MCDI_DWORD(outbuf, GET_ASSERTS_OUT_SAVED_PC_OFFS), |
953 | MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS)); | 941 | MCDI_DWORD(outbuf, GET_ASSERTS_OUT_THREAD_OFFS)); |
954 | 942 | ||
955 | /* Print out the registers */ | 943 | /* Print out the registers */ |
956 | ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; | 944 | ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; |
957 | for (index = 1; index < 32; index++) { | 945 | for (index = 1; index < 32; index++) { |
958 | EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, | 946 | EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, |
959 | MCDI_DWORD2(assertion, ofst)); | 947 | MCDI_DWORD2(outbuf, ofst)); |
960 | ofst += sizeof(efx_dword_t); | 948 | ofst += sizeof(efx_dword_t); |
961 | } | 949 | } |
962 | 950 | ||
963 | return 0; | 951 | return 0; |
964 | } | 952 | } |
965 | 953 | ||
954 | static void efx_mcdi_exit_assertion(struct efx_nic *efx) | ||
955 | { | ||
956 | u8 inbuf[MC_CMD_REBOOT_IN_LEN]; | ||
957 | |||
958 | /* Atomically reboot the mcfw out of the assertion handler */ | ||
959 | BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); | ||
960 | MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, | ||
961 | MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); | ||
962 | efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN, | ||
963 | NULL, 0, NULL); | ||
964 | } | ||
965 | |||
966 | int efx_mcdi_handle_assertion(struct efx_nic *efx) | ||
967 | { | ||
968 | int rc; | ||
969 | |||
970 | rc = efx_mcdi_read_assertion(efx); | ||
971 | if (rc) | ||
972 | return rc; | ||
973 | |||
974 | efx_mcdi_exit_assertion(efx); | ||
975 | |||
976 | return 0; | ||
977 | } | ||
978 | |||
966 | void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) | 979 | void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) |
967 | { | 980 | { |
968 | u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; | 981 | u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; |
diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c index f8c6771e66d8..0e4c13abf087 100644 --- a/drivers/net/sfc/siena.c +++ b/drivers/net/sfc/siena.c | |||
@@ -181,6 +181,12 @@ static int siena_test_registers(struct efx_nic *efx) | |||
181 | 181 | ||
182 | static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) | 182 | static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) |
183 | { | 183 | { |
184 | int rc; | ||
185 | |||
186 | /* Recover from a failed assertion pre-reset */ | ||
187 | rc = efx_mcdi_handle_assertion(efx); | ||
188 | if (rc) | ||
189 | return rc; | ||
184 | 190 | ||
185 | if (method == RESET_TYPE_WORLD) | 191 | if (method == RESET_TYPE_WORLD) |
186 | return efx_mcdi_reset_mc(efx); | 192 | return efx_mcdi_reset_mc(efx); |