| From cd6fa8d2ca53cac3226fdcffcf763be390abae32 Mon Sep 17 00:00:00 2001 |
| From: Alexander Sverdlin <alexander.sverdlin@nokia.com> |
| Date: Fri, 27 Feb 2015 16:30:21 +0100 |
| Subject: spi: pl022: Fix race in giveback() leading to driver lock-up |
| |
| From: Alexander Sverdlin <alexander.sverdlin@nokia.com> |
| |
| commit cd6fa8d2ca53cac3226fdcffcf763be390abae32 upstream. |
| |
| Commit fd316941c ("spi/pl022: disable port when unused") introduced a race, |
| which leads to possible driver lock up (easily reproducible on SMP). |
| |
| The problem happens in giveback() function where the completion of the transfer |
| is signalled to SPI subsystem and then the HW SPI controller is disabled. Another |
| transfer might be setup in between, which brings driver in locked-up state. |
| |
| Exact event sequence on SMP: |
| |
| core0 core1 |
| |
| => pump_transfers() |
| /* message->state == STATE_DONE */ |
| => giveback() |
| => spi_finalize_current_message() |
| |
| => pl022_unprepare_transfer_hardware() |
| => pl022_transfer_one_message |
| => flush() |
| => do_interrupt_dma_transfer() |
| => set_up_next_transfer() |
| /* Enable SSP, turn on interrupts */ |
| writew((readw(SSP_CR1(pl022->virtbase)) | |
| SSP_CR1_MASK_SSE), SSP_CR1(pl022->virtbase)); |
| |
| ... |
| |
| => pl022_interrupt_handler() |
| => readwriter() |
| |
| /* disable the SPI/SSP operation */ |
| => writew((readw(SSP_CR1(pl022->virtbase)) & |
| (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); |
| |
| Lockup! SPI controller is disabled and the data will never be received. Whole |
| SPI subsystem is waiting for transfer ACK and blocked. |
| |
| So, only signal transfer completion after disabling the controller. |
| |
| Fixes: fd316941c (spi/pl022: disable port when unused) |
| Signed-off-by: Alexander Sverdlin <alexander.sverdlin@nokia.com> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/spi/spi-pl022.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/drivers/spi/spi-pl022.c |
| +++ b/drivers/spi/spi-pl022.c |
| @@ -503,12 +503,12 @@ static void giveback(struct pl022 *pl022 |
| pl022->cur_msg = NULL; |
| pl022->cur_transfer = NULL; |
| pl022->cur_chip = NULL; |
| - spi_finalize_current_message(pl022->master); |
| |
| /* disable the SPI/SSP operation */ |
| writew((readw(SSP_CR1(pl022->virtbase)) & |
| (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); |
| |
| + spi_finalize_current_message(pl022->master); |
| } |
| |
| /** |