3.16.84-rc1 review patch.  If anyone has any objections, please let me know.

------------------

From: "wuxu.wu" <wuxu...@huawei.com>

commit 19b61392c5a852b4e8a0bf35aecb969983c5932d upstream.

dw_spi_irq() and dw_spi_transfer_one concurrent calls.

I find a panic in dw_writer(): txw = *(u8 *)(dws->tx), when dw->tx==null,
dw->len==4, and dw->tx_end==1.

When tpm driver's message overtime dw_spi_irq() and dw_spi_transfer_one
may concurrent visit dw_spi, so I think dw_spi structure lack of protection.

Otherwise dw_spi_transfer_one set dw rx/tx buffer and then open irq,
store dw rx/tx instructions and other cores handle irq load dw rx/tx
instructions may out of order.

        [ 1025.321302] Call trace:
        ...
        [ 1025.321319]  __crash_kexec+0x98/0x148
        [ 1025.321323]  panic+0x17c/0x314
        [ 1025.321329]  die+0x29c/0x2e8
        [ 1025.321334]  die_kernel_fault+0x68/0x78
        [ 1025.321337]  __do_kernel_fault+0x90/0xb0
        [ 1025.321346]  do_page_fault+0x88/0x500
        [ 1025.321347]  do_translation_fault+0xa8/0xb8
        [ 1025.321349]  do_mem_abort+0x68/0x118
        [ 1025.321351]  el1_da+0x20/0x8c
        [ 1025.321362]  dw_writer+0xc8/0xd0
        [ 1025.321364]  interrupt_transfer+0x60/0x110
        [ 1025.321365]  dw_spi_irq+0x48/0x70
        ...

Signed-off-by: wuxu.wu <wuxu...@huawei.com>
Link: 
https://lore.kernel.org/r/1577849981-31489-1-git-send-email-wuxu...@huawei.com
Signed-off-by: Mark Brown <broo...@kernel.org>
[iwamatsu: Backported to 3.16: adjut context]
Signed-off-by: Nobuhiro Iwamatsu (CIP) <nobuhiro1.iwama...@toshiba.co.jp>
Signed-off-by: Ben Hutchings <b...@decadent.org.uk>
---
 drivers/spi/spi-dw.c | 14 ++++++++++++--
 drivers/spi/spi-dw.h |  1 +
 2 files changed, 13 insertions(+), 2 deletions(-)

--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -182,9 +182,11 @@ static inline u32 rx_max(struct dw_spi *
 
 static void dw_writer(struct dw_spi *dws)
 {
-       u32 max = tx_max(dws);
+       u32 max;
        u16 txw = 0;
 
+       spin_lock(&dws->buf_lock);
+       max = tx_max(dws);
        while (max--) {
                /* Set the tx word if the transfer's original "tx" is not null 
*/
                if (dws->tx_end - dws->len) {
@@ -196,13 +198,16 @@ static void dw_writer(struct dw_spi *dws
                dw_writew(dws, DW_SPI_DR, txw);
                dws->tx += dws->n_bytes;
        }
+       spin_unlock(&dws->buf_lock);
 }
 
 static void dw_reader(struct dw_spi *dws)
 {
-       u32 max = rx_max(dws);
+       u32 max;
        u16 rxw;
 
+       spin_lock(&dws->buf_lock);
+       max = rx_max(dws);
        while (max--) {
                rxw = dw_readw(dws, DW_SPI_DR);
                /* Care rx only if the transfer's original "rx" is not null */
@@ -214,6 +219,7 @@ static void dw_reader(struct dw_spi *dws
                }
                dws->rx += dws->n_bytes;
        }
+       spin_unlock(&dws->buf_lock);
 }
 
 static void *next_transfer(struct dw_spi *dws)
@@ -368,6 +374,7 @@ static void pump_transfers(unsigned long
        struct spi_transfer *previous = NULL;
        struct spi_device *spi = NULL;
        struct chip_data *chip = NULL;
+       unsigned long flags;
        u8 bits = 0;
        u8 imask = 0;
        u8 cs_change = 0;
@@ -406,6 +413,7 @@ static void pump_transfers(unsigned long
        dws->dma_width = chip->dma_width;
        dws->cs_control = chip->cs_control;
 
+       spin_lock_irqsave(&dws->buf_lock, flags);
        dws->rx_dma = transfer->rx_dma;
        dws->tx_dma = transfer->tx_dma;
        dws->tx = (void *)transfer->tx_buf;
@@ -415,6 +423,7 @@ static void pump_transfers(unsigned long
        dws->len = dws->cur_transfer->len;
        if (chip != dws->prev_chip)
                cs_change = 1;
+       spin_unlock_irqrestore(&dws->buf_lock, flags);
 
        cr0 = chip->cr0;
 
@@ -651,6 +660,7 @@ int dw_spi_add_host(struct device *dev,
        dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
        snprintf(dws->name, sizeof(dws->name), "dw_spi%d",
                        dws->bus_num);
+       spin_lock_init(&dws->buf_lock);
 
        ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dws->name, dws);
        if (ret < 0) {
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -116,6 +116,7 @@ struct dw_spi {
        size_t                  len;
        void                    *tx;
        void                    *tx_end;
+       spinlock_t              buf_lock;
        void                    *rx;
        void                    *rx_end;
        int                     dma_mapped;

Reply via email to