diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2011-06-24 15:46:31 -0400 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2011-06-24 19:43:48 -0400 |
commit | a7d529ae2158b5300e4aa16c21f1828bc864449b (patch) | |
tree | 8a4442da3d52682945579ce76a9c075cf41a110b | |
parent | 4017dbdc14af1903dc9fcba4d08b89c02325069d (diff) |
sfc: Allow resets to be upgraded; use atomic ops for safety
Currently an attempt to schedule any reset is ignored if a reset
is already pending. This ignores the relative scopes - if the
requested reset is greater in scope then the scheduled reset should
be upgraded accordingly.
There are also some race conditions which could lead to a reset
request being lost. Deal with them by using atomic operations on a
bitmask. This also makes tests on reset_pending easier to get right.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
-rw-r--r-- | drivers/net/sfc/efx.c | 38 | ||||
-rw-r--r-- | drivers/net/sfc/enum.h | 3 | ||||
-rw-r--r-- | drivers/net/sfc/falcon.c | 2 | ||||
-rw-r--r-- | drivers/net/sfc/net_driver.h | 4 |
4 files changed, 22 insertions, 25 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index c914729f9554..9b4cfdb09516 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c | |||
@@ -229,8 +229,7 @@ static int efx_process_channel(struct efx_channel *channel, int budget) | |||
229 | struct efx_nic *efx = channel->efx; | 229 | struct efx_nic *efx = channel->efx; |
230 | int spent; | 230 | int spent; |
231 | 231 | ||
232 | if (unlikely(efx->reset_pending != RESET_TYPE_NONE || | 232 | if (unlikely(efx->reset_pending || !channel->enabled)) |
233 | !channel->enabled)) | ||
234 | return 0; | 233 | return 0; |
235 | 234 | ||
236 | spent = efx_nic_process_eventq(channel, budget); | 235 | spent = efx_nic_process_eventq(channel, budget); |
@@ -1461,7 +1460,7 @@ static void efx_start_all(struct efx_nic *efx) | |||
1461 | * reset_pending [modified from an atomic context], we instead guarantee | 1460 | * reset_pending [modified from an atomic context], we instead guarantee |
1462 | * that efx_mcdi_mode_poll() isn't reverted erroneously */ | 1461 | * that efx_mcdi_mode_poll() isn't reverted erroneously */ |
1463 | efx_mcdi_mode_event(efx); | 1462 | efx_mcdi_mode_event(efx); |
1464 | if (efx->reset_pending != RESET_TYPE_NONE) | 1463 | if (efx->reset_pending) |
1465 | efx_mcdi_mode_poll(efx); | 1464 | efx_mcdi_mode_poll(efx); |
1466 | 1465 | ||
1467 | /* Start the hardware monitor if there is one. Otherwise (we're link | 1466 | /* Start the hardware monitor if there is one. Otherwise (we're link |
@@ -2118,8 +2117,10 @@ int efx_reset(struct efx_nic *efx, enum reset_type method) | |||
2118 | goto out; | 2117 | goto out; |
2119 | } | 2118 | } |
2120 | 2119 | ||
2121 | /* Allow resets to be rescheduled. */ | 2120 | /* Clear flags for the scopes we covered. We assume the NIC and |
2122 | efx->reset_pending = RESET_TYPE_NONE; | 2121 | * driver are now quiescent so that there is no race here. |
2122 | */ | ||
2123 | efx->reset_pending &= -(1 << (method + 1)); | ||
2123 | 2124 | ||
2124 | /* Reinitialise bus-mastering, which may have been turned off before | 2125 | /* Reinitialise bus-mastering, which may have been turned off before |
2125 | * the reset was scheduled. This is still appropriate, even in the | 2126 | * the reset was scheduled. This is still appropriate, even in the |
@@ -2154,12 +2155,13 @@ out: | |||
2154 | static void efx_reset_work(struct work_struct *data) | 2155 | static void efx_reset_work(struct work_struct *data) |
2155 | { | 2156 | { |
2156 | struct efx_nic *efx = container_of(data, struct efx_nic, reset_work); | 2157 | struct efx_nic *efx = container_of(data, struct efx_nic, reset_work); |
2158 | unsigned long pending = ACCESS_ONCE(efx->reset_pending); | ||
2157 | 2159 | ||
2158 | if (efx->reset_pending == RESET_TYPE_NONE) | 2160 | if (!pending) |
2159 | return; | 2161 | return; |
2160 | 2162 | ||
2161 | /* If we're not RUNNING then don't reset. Leave the reset_pending | 2163 | /* If we're not RUNNING then don't reset. Leave the reset_pending |
2162 | * flag set so that efx_pci_probe_main will be retried */ | 2164 | * flags set so that efx_pci_probe_main will be retried */ |
2163 | if (efx->state != STATE_RUNNING) { | 2165 | if (efx->state != STATE_RUNNING) { |
2164 | netif_info(efx, drv, efx->net_dev, | 2166 | netif_info(efx, drv, efx->net_dev, |
2165 | "scheduled reset quenched. NIC not RUNNING\n"); | 2167 | "scheduled reset quenched. NIC not RUNNING\n"); |
@@ -2167,7 +2169,7 @@ static void efx_reset_work(struct work_struct *data) | |||
2167 | } | 2169 | } |
2168 | 2170 | ||
2169 | rtnl_lock(); | 2171 | rtnl_lock(); |
2170 | (void)efx_reset(efx, efx->reset_pending); | 2172 | (void)efx_reset(efx, fls(pending) - 1); |
2171 | rtnl_unlock(); | 2173 | rtnl_unlock(); |
2172 | } | 2174 | } |
2173 | 2175 | ||
@@ -2175,12 +2177,6 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) | |||
2175 | { | 2177 | { |
2176 | enum reset_type method; | 2178 | enum reset_type method; |
2177 | 2179 | ||
2178 | if (efx->reset_pending != RESET_TYPE_NONE) { | ||
2179 | netif_info(efx, drv, efx->net_dev, | ||
2180 | "quenching already scheduled reset\n"); | ||
2181 | return; | ||
2182 | } | ||
2183 | |||
2184 | switch (type) { | 2180 | switch (type) { |
2185 | case RESET_TYPE_INVISIBLE: | 2181 | case RESET_TYPE_INVISIBLE: |
2186 | case RESET_TYPE_ALL: | 2182 | case RESET_TYPE_ALL: |
@@ -2208,7 +2204,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) | |||
2208 | netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n", | 2204 | netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n", |
2209 | RESET_TYPE(method)); | 2205 | RESET_TYPE(method)); |
2210 | 2206 | ||
2211 | efx->reset_pending = method; | 2207 | set_bit(method, &efx->reset_pending); |
2212 | 2208 | ||
2213 | /* efx_process_channel() will no longer read events once a | 2209 | /* efx_process_channel() will no longer read events once a |
2214 | * reset is scheduled. So switch back to poll'd MCDI completions. */ | 2210 | * reset is scheduled. So switch back to poll'd MCDI completions. */ |
@@ -2288,7 +2284,6 @@ static int efx_init_struct(struct efx_nic *efx, const struct efx_nic_type *type, | |||
2288 | efx->pci_dev = pci_dev; | 2284 | efx->pci_dev = pci_dev; |
2289 | efx->msg_enable = debug; | 2285 | efx->msg_enable = debug; |
2290 | efx->state = STATE_INIT; | 2286 | efx->state = STATE_INIT; |
2291 | efx->reset_pending = RESET_TYPE_NONE; | ||
2292 | strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); | 2287 | strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); |
2293 | 2288 | ||
2294 | efx->net_dev = net_dev; | 2289 | efx->net_dev = net_dev; |
@@ -2510,7 +2505,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, | |||
2510 | cancel_work_sync(&efx->reset_work); | 2505 | cancel_work_sync(&efx->reset_work); |
2511 | 2506 | ||
2512 | if (rc == 0) { | 2507 | if (rc == 0) { |
2513 | if (efx->reset_pending != RESET_TYPE_NONE) { | 2508 | if (efx->reset_pending) { |
2514 | /* If there was a scheduled reset during | 2509 | /* If there was a scheduled reset during |
2515 | * probe, the NIC is probably hosed anyway */ | 2510 | * probe, the NIC is probably hosed anyway */ |
2516 | efx_pci_remove_main(efx); | 2511 | efx_pci_remove_main(efx); |
@@ -2521,11 +2516,12 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, | |||
2521 | } | 2516 | } |
2522 | 2517 | ||
2523 | /* Retry if a recoverably reset event has been scheduled */ | 2518 | /* Retry if a recoverably reset event has been scheduled */ |
2524 | if ((efx->reset_pending != RESET_TYPE_INVISIBLE) && | 2519 | if (efx->reset_pending & |
2525 | (efx->reset_pending != RESET_TYPE_ALL)) | 2520 | ~(1 << RESET_TYPE_INVISIBLE | 1 << RESET_TYPE_ALL) || |
2521 | !efx->reset_pending) | ||
2526 | goto fail3; | 2522 | goto fail3; |
2527 | 2523 | ||
2528 | efx->reset_pending = RESET_TYPE_NONE; | 2524 | efx->reset_pending = 0; |
2529 | } | 2525 | } |
2530 | 2526 | ||
2531 | if (rc) { | 2527 | if (rc) { |
@@ -2609,7 +2605,7 @@ static int efx_pm_poweroff(struct device *dev) | |||
2609 | 2605 | ||
2610 | efx->type->fini(efx); | 2606 | efx->type->fini(efx); |
2611 | 2607 | ||
2612 | efx->reset_pending = RESET_TYPE_NONE; | 2608 | efx->reset_pending = 0; |
2613 | 2609 | ||
2614 | pci_save_state(pci_dev); | 2610 | pci_save_state(pci_dev); |
2615 | return pci_set_power_state(pci_dev, PCI_D3hot); | 2611 | return pci_set_power_state(pci_dev, PCI_D3hot); |
diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h index 384cfe3b1be1..d725a8fbe1a6 100644 --- a/drivers/net/sfc/enum.h +++ b/drivers/net/sfc/enum.h | |||
@@ -134,6 +134,8 @@ enum efx_loopback_mode { | |||
134 | * other valuesspecify reasons, which efx_schedule_reset() will choose | 134 | * other valuesspecify reasons, which efx_schedule_reset() will choose |
135 | * a method for. | 135 | * a method for. |
136 | * | 136 | * |
137 | * Reset methods are numbered in order of increasing scope. | ||
138 | * | ||
137 | * @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts | 139 | * @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts |
138 | * @RESET_TYPE_ALL: reset everything but PCI core blocks | 140 | * @RESET_TYPE_ALL: reset everything but PCI core blocks |
139 | * @RESET_TYPE_WORLD: reset everything, save & restore PCI config | 141 | * @RESET_TYPE_WORLD: reset everything, save & restore PCI config |
@@ -147,7 +149,6 @@ enum efx_loopback_mode { | |||
147 | * @RESET_TYPE_MC_FAILURE: MC reboot/assertion | 149 | * @RESET_TYPE_MC_FAILURE: MC reboot/assertion |
148 | */ | 150 | */ |
149 | enum reset_type { | 151 | enum reset_type { |
150 | RESET_TYPE_NONE = -1, | ||
151 | RESET_TYPE_INVISIBLE = 0, | 152 | RESET_TYPE_INVISIBLE = 0, |
152 | RESET_TYPE_ALL = 1, | 153 | RESET_TYPE_ALL = 1, |
153 | RESET_TYPE_WORLD = 2, | 154 | RESET_TYPE_WORLD = 2, |
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 60176e873d62..a4c7830ec9b0 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c | |||
@@ -536,7 +536,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) | |||
536 | efx_oword_t reg; | 536 | efx_oword_t reg; |
537 | int link_speed, isolate; | 537 | int link_speed, isolate; |
538 | 538 | ||
539 | isolate = (efx->reset_pending != RESET_TYPE_NONE); | 539 | isolate = !!ACCESS_ONCE(efx->reset_pending); |
540 | 540 | ||
541 | switch (link_state->speed) { | 541 | switch (link_state->speed) { |
542 | case 10000: link_speed = 3; break; | 542 | case 10000: link_speed = 3; break; |
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index e8d5f03a89fe..c422eb2ce60a 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h | |||
@@ -645,7 +645,7 @@ struct efx_filter_state; | |||
645 | * @irq_rx_moderation: IRQ moderation time for RX event queues | 645 | * @irq_rx_moderation: IRQ moderation time for RX event queues |
646 | * @msg_enable: Log message enable flags | 646 | * @msg_enable: Log message enable flags |
647 | * @state: Device state flag. Serialised by the rtnl_lock. | 647 | * @state: Device state flag. Serialised by the rtnl_lock. |
648 | * @reset_pending: Pending reset method (normally RESET_TYPE_NONE) | 648 | * @reset_pending: Bitmask for pending resets |
649 | * @tx_queue: TX DMA queues | 649 | * @tx_queue: TX DMA queues |
650 | * @rx_queue: RX DMA queues | 650 | * @rx_queue: RX DMA queues |
651 | * @channel: Channels | 651 | * @channel: Channels |
@@ -728,7 +728,7 @@ struct efx_nic { | |||
728 | u32 msg_enable; | 728 | u32 msg_enable; |
729 | 729 | ||
730 | enum nic_state state; | 730 | enum nic_state state; |
731 | enum reset_type reset_pending; | 731 | unsigned long reset_pending; |
732 | 732 | ||
733 | struct efx_channel *channel[EFX_MAX_CHANNELS]; | 733 | struct efx_channel *channel[EFX_MAX_CHANNELS]; |
734 | char channel_name[EFX_MAX_CHANNELS][IFNAMSIZ + 6]; | 734 | char channel_name[EFX_MAX_CHANNELS][IFNAMSIZ + 6]; |