Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

spi: pl022: Fix race in giveback() leading to driver lock-up

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>
Cc: stable@vger.kernel.org

authored by

Alexander Sverdlin and committed by
Mark Brown
cd6fa8d2 c517d838

+1 -1
+1 -1
drivers/spi/spi-pl022.c
··· 534 534 pl022->cur_msg = NULL; 535 535 pl022->cur_transfer = NULL; 536 536 pl022->cur_chip = NULL; 537 - spi_finalize_current_message(pl022->master); 538 537 539 538 /* disable the SPI/SSP operation */ 540 539 writew((readw(SSP_CR1(pl022->virtbase)) & 541 540 (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); 542 541 542 + spi_finalize_current_message(pl022->master); 543 543 } 544 544 545 545 /**