]>
Commit | Line | Data |
---|---|---|
433ee529 GKH |
1 | From e82b0b3828451c1cd331d9f304c6078fcd43b62e Mon Sep 17 00:00:00 2001 |
2 | From: Lukas Wunner <lukas@wunner.de> | |
3 | Date: Thu, 8 Nov 2018 08:06:10 +0100 | |
4 | Subject: spi: bcm2835: Fix race on DMA termination | |
5 | MIME-Version: 1.0 | |
6 | Content-Type: text/plain; charset=UTF-8 | |
7 | Content-Transfer-Encoding: 8bit | |
8 | ||
9 | From: Lukas Wunner <lukas@wunner.de> | |
10 | ||
11 | commit e82b0b3828451c1cd331d9f304c6078fcd43b62e upstream. | |
12 | ||
13 | If a DMA transfer finishes orderly right when spi_transfer_one_message() | |
14 | determines that it has timed out, the callbacks bcm2835_spi_dma_done() | |
15 | and bcm2835_spi_handle_err() race to call dmaengine_terminate_all(), | |
16 | potentially leading to double termination. | |
17 | ||
18 | Prevent by atomically changing the dma_pending flag before calling | |
19 | dmaengine_terminate_all(). | |
20 | ||
21 | Signed-off-by: Lukas Wunner <lukas@wunner.de> | |
22 | Fixes: 3ecd37edaa2a ("spi: bcm2835: enable dma modes for transfers meeting certain conditions") | |
23 | Cc: stable@vger.kernel.org # v4.2+ | |
24 | Cc: Mathias Duckeck <m.duckeck@kunbus.de> | |
25 | Cc: Frank Pavlic <f.pavlic@kunbus.de> | |
26 | Cc: Martin Sperl <kernel@martin.sperl.org> | |
27 | Cc: Noralf Trønnes <noralf@tronnes.org> | |
28 | Signed-off-by: Mark Brown <broonie@kernel.org> | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
30 | ||
31 | --- | |
32 | drivers/spi/spi-bcm2835.c | 10 ++++------ | |
33 | 1 file changed, 4 insertions(+), 6 deletions(-) | |
34 | ||
35 | --- a/drivers/spi/spi-bcm2835.c | |
36 | +++ b/drivers/spi/spi-bcm2835.c | |
37 | @@ -233,10 +233,9 @@ static void bcm2835_spi_dma_done(void *d | |
38 | * is called the tx-dma must have finished - can't get to this | |
39 | * situation otherwise... | |
40 | */ | |
41 | - dmaengine_terminate_all(master->dma_tx); | |
42 | - | |
43 | - /* mark as no longer pending */ | |
44 | - bs->dma_pending = 0; | |
45 | + if (cmpxchg(&bs->dma_pending, true, false)) { | |
46 | + dmaengine_terminate_all(master->dma_tx); | |
47 | + } | |
48 | ||
49 | /* and mark as completed */; | |
50 | complete(&master->xfer_completion); | |
51 | @@ -617,10 +616,9 @@ static void bcm2835_spi_handle_err(struc | |
52 | struct bcm2835_spi *bs = spi_master_get_devdata(master); | |
53 | ||
54 | /* if an error occurred and we have an active dma, then terminate */ | |
55 | - if (bs->dma_pending) { | |
56 | + if (cmpxchg(&bs->dma_pending, true, false)) { | |
57 | dmaengine_terminate_all(master->dma_tx); | |
58 | dmaengine_terminate_all(master->dma_rx); | |
59 | - bs->dma_pending = 0; | |
60 | } | |
61 | /* and reset */ | |
62 | bcm2835_spi_reset_hw(master); |