From 3e81b59208fa4697c6c3b6eefb92892b47f8b1e7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 6 Aug 2014 14:59:03 +0200 Subject: [PATCH] spi: sh-msiof: Fix leaking of unused DMA descriptors If dmaengine_prep_slave_sg() or dmaengine_submit() fail, we may leak unused DMA descriptors. As per Documentation/dmaengine.txt, once a DMA descriptor has been obtained, it must be submitted. Hence: - First prepare and submit all DMA descriptors, - Prepare the SPI controller for DMA, - Start DMA by calling dma_async_issue_pending(), - Make sure to call dmaengine_terminate_all() on all descriptors that haven't completed. Reported-by: Laurent Pinchart Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 81 ++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 2a4354dcd661..887c2084130f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -636,6 +636,26 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, dma_cookie_t cookie; int ret; + /* First prepare and submit the DMA request(s), as this may fail */ + if (rx) { + ier_bits |= IER_RDREQE | IER_RDMAE; + desc_rx = dmaengine_prep_slave_single(p->master->dma_rx, + p->rx_dma_addr, len, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) { + ret = -EAGAIN; + goto no_dma_rx; + } + + desc_rx->callback = sh_msiof_dma_complete; + desc_rx->callback_param = p; + cookie = dmaengine_submit(desc_rx); + if (dma_submit_error(cookie)) { + ret = cookie; + goto no_dma_rx; + } + } + if (tx) { ier_bits |= IER_TDREQE | IER_TDMAE; dma_sync_single_for_device(p->master->dma_tx->device->dev, @@ -643,17 +663,23 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, desc_tx = dmaengine_prep_slave_single(p->master->dma_tx, p->tx_dma_addr, len, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_tx) - return -EAGAIN; - } + if (!desc_tx) { + ret = -EAGAIN; + goto no_dma_tx; + } - if (rx) { - ier_bits |= IER_RDREQE | IER_RDMAE; - desc_rx = dmaengine_prep_slave_single(p->master->dma_rx, - p->rx_dma_addr, len, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc_rx) - return -EAGAIN; + if (rx) { + /* No callback */ + desc_tx->callback = NULL; + } else { + desc_tx->callback = sh_msiof_dma_complete; + desc_tx->callback_param = p; + } + cookie = dmaengine_submit(desc_tx); + if (dma_submit_error(cookie)) { + ret = cookie; + goto no_dma_tx; + } } /* 1 stage FIFO watermarks for DMA */ @@ -666,37 +692,16 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, reinit_completion(&p->done); - if (rx) { - desc_rx->callback = sh_msiof_dma_complete; - desc_rx->callback_param = p; - cookie = dmaengine_submit(desc_rx); - if (dma_submit_error(cookie)) { - ret = cookie; - goto stop_ier; - } + /* Now start DMA */ + if (tx) dma_async_issue_pending(p->master->dma_rx); - } - - if (tx) { - if (rx) { - /* No callback */ - desc_tx->callback = NULL; - } else { - desc_tx->callback = sh_msiof_dma_complete; - desc_tx->callback_param = p; - } - cookie = dmaengine_submit(desc_tx); - if (dma_submit_error(cookie)) { - ret = cookie; - goto stop_rx; - } + if (rx) dma_async_issue_pending(p->master->dma_tx); - } ret = sh_msiof_spi_start(p, rx); if (ret) { dev_err(&p->pdev->dev, "failed to start hardware\n"); - goto stop_tx; + goto stop_dma; } /* wait for tx fifo to be emptied / rx fifo to be filled */ @@ -726,14 +731,14 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, stop_reset: sh_msiof_reset_str(p); sh_msiof_spi_stop(p, rx); -stop_tx: +stop_dma: if (tx) dmaengine_terminate_all(p->master->dma_tx); -stop_rx: +no_dma_tx: if (rx) dmaengine_terminate_all(p->master->dma_rx); -stop_ier: sh_msiof_write(p, IER, 0); +no_dma_rx: return ret; }