| From e82b0b3828451c1cd331d9f304c6078fcd43b62e Mon Sep 17 00:00:00 2001 |
| From: Lukas Wunner <lukas@wunner.de> |
| Date: Thu, 8 Nov 2018 08:06:10 +0100 |
| Subject: spi: bcm2835: Fix race on DMA termination |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Lukas Wunner <lukas@wunner.de> |
| |
| commit e82b0b3828451c1cd331d9f304c6078fcd43b62e upstream. |
| |
| If a DMA transfer finishes orderly right when spi_transfer_one_message() |
| determines that it has timed out, the callbacks bcm2835_spi_dma_done() |
| and bcm2835_spi_handle_err() race to call dmaengine_terminate_all(), |
| potentially leading to double termination. |
| |
| Prevent by atomically changing the dma_pending flag before calling |
| dmaengine_terminate_all(). |
| |
| Signed-off-by: Lukas Wunner <lukas@wunner.de> |
| Fixes: 3ecd37edaa2a ("spi: bcm2835: enable dma modes for transfers meeting certain conditions") |
| Cc: stable@vger.kernel.org # v4.2+ |
| Cc: Mathias Duckeck <m.duckeck@kunbus.de> |
| Cc: Frank Pavlic <f.pavlic@kunbus.de> |
| Cc: Martin Sperl <kernel@martin.sperl.org> |
| Cc: Noralf TrΓΈnnes <noralf@tronnes.org> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/spi/spi-bcm2835.c | 10 ++++------ |
| 1 file changed, 4 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/spi/spi-bcm2835.c |
| +++ b/drivers/spi/spi-bcm2835.c |
| @@ -233,10 +233,9 @@ static void bcm2835_spi_dma_done(void *d |
| * is called the tx-dma must have finished - can't get to this |
| * situation otherwise... |
| */ |
| - dmaengine_terminate_all(master->dma_tx); |
| - |
| - /* mark as no longer pending */ |
| - bs->dma_pending = 0; |
| + if (cmpxchg(&bs->dma_pending, true, false)) { |
| + dmaengine_terminate_all(master->dma_tx); |
| + } |
| |
| /* and mark as completed */; |
| complete(&master->xfer_completion); |
| @@ -617,10 +616,9 @@ static void bcm2835_spi_handle_err(struc |
| struct bcm2835_spi *bs = spi_master_get_devdata(master); |
| |
| /* if an error occurred and we have an active dma, then terminate */ |
| - if (bs->dma_pending) { |
| + if (cmpxchg(&bs->dma_pending, true, false)) { |
| dmaengine_terminate_all(master->dma_tx); |
| dmaengine_terminate_all(master->dma_rx); |
| - bs->dma_pending = 0; |
| } |
| /* and reset */ |
| bcm2835_spi_reset_hw(master); |