lkml.org 
[lkml]   [2018]   [Dec]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3.16 204/328] spi: rspi: Fix leaking of unused DMA descriptors
    3.16.62-rc1 review patch.  If anyone has any objections, please let me know.

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

    From: Geert Uytterhoeven <geert+renesas@glider.be>

    commit 3819bc8752367eae0d72fa1c473dc88ea45631a7 upstream.

    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 <laurent.pinchart@ideasonboard.com>
    Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
    Signed-off-by: Mark Brown <broonie@linaro.org>
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    ---
    drivers/spi/spi-rspi.c | 94 ++++++++++++++++++++++++++----------------
    1 file changed, 58 insertions(+), 36 deletions(-)

    --- a/drivers/spi/spi-rspi.c
    +++ b/drivers/spi/spi-rspi.c
    @@ -472,25 +472,52 @@ static int rspi_dma_transfer(struct rspi
    dma_cookie_t cookie;
    int ret;

    - if (tx) {
    - desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
    - tx->sgl, tx->nents, DMA_TO_DEVICE,
    - DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    - if (!desc_tx)
    - goto no_dma;
    -
    - irq_mask |= SPCR_SPTIE;
    - }
    + /* First prepare and submit the DMA request(s), as this may fail */
    if (rx) {
    desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx,
    rx->sgl, rx->nents, DMA_FROM_DEVICE,
    DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    - if (!desc_rx)
    - goto no_dma;
    + if (!desc_rx) {
    + ret = -EAGAIN;
    + goto no_dma_rx;
    + }
    +
    + desc_rx->callback = rspi_dma_complete;
    + desc_rx->callback_param = rspi;
    + cookie = dmaengine_submit(desc_rx);
    + if (dma_submit_error(cookie)) {
    + ret = cookie;
    + goto no_dma_rx;
    + }

    irq_mask |= SPCR_SPRIE;
    }

    + if (tx) {
    + desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
    + tx->sgl, tx->nents, DMA_TO_DEVICE,
    + DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    + if (!desc_tx) {
    + ret = -EAGAIN;
    + goto no_dma_tx;
    + }
    +
    + if (rx) {
    + /* No callback */
    + desc_tx->callback = NULL;
    + } else {
    + desc_tx->callback = rspi_dma_complete;
    + desc_tx->callback_param = rspi;
    + }
    + cookie = dmaengine_submit(desc_tx);
    + if (dma_submit_error(cookie)) {
    + ret = cookie;
    + goto no_dma_tx;
    + }
    +
    + irq_mask |= SPCR_SPTIE;
    + }
    +
    /*
    * DMAC needs SPxIE, but if SPxIE is set, the IRQ routine will be
    * called. So, this driver disables the IRQ while DMA transfer.
    @@ -503,34 +530,24 @@ static int rspi_dma_transfer(struct rspi
    rspi_enable_irq(rspi, irq_mask);
    rspi->dma_callbacked = 0;

    - if (rx) {
    - desc_rx->callback = rspi_dma_complete;
    - desc_rx->callback_param = rspi;
    - cookie = dmaengine_submit(desc_rx);
    - if (dma_submit_error(cookie))
    - return cookie;
    + /* Now start DMA */
    + if (rx)
    dma_async_issue_pending(rspi->master->dma_rx);
    - }
    - if (tx) {
    - if (rx) {
    - /* No callback */
    - desc_tx->callback = NULL;
    - } else {
    - desc_tx->callback = rspi_dma_complete;
    - desc_tx->callback_param = rspi;
    - }
    - cookie = dmaengine_submit(desc_tx);
    - if (dma_submit_error(cookie))
    - return cookie;
    + if (tx)
    dma_async_issue_pending(rspi->master->dma_tx);
    - }

    ret = wait_event_interruptible_timeout(rspi->wait,
    rspi->dma_callbacked, HZ);
    if (ret > 0 && rspi->dma_callbacked)
    ret = 0;
    - else if (!ret)
    + else if (!ret) {
    + dev_err(&rspi->master->dev, "DMA timeout\n");
    ret = -ETIMEDOUT;
    + if (tx)
    + dmaengine_terminate_all(rspi->master->dma_tx);
    + if (rx)
    + dmaengine_terminate_all(rspi->master->dma_rx);
    + }

    rspi_disable_irq(rspi, irq_mask);

    @@ -541,11 +558,16 @@ static int rspi_dma_transfer(struct rspi

    return ret;

    -no_dma:
    - pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
    - dev_driver_string(&rspi->master->dev),
    - dev_name(&rspi->master->dev));
    - return -EAGAIN;
    +no_dma_tx:
    + if (rx)
    + dmaengine_terminate_all(rspi->master->dma_rx);
    +no_dma_rx:
    + if (ret == -EAGAIN) {
    + pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
    + dev_driver_string(&rspi->master->dev),
    + dev_name(&rspi->master->dev));
    + }
    + return ret;
    }

    static void rspi_receive_init(const struct rspi_data *rspi)
    \
     
     \ /
      Last update: 2018-12-09 23:17    [W:2.835 / U:0.148 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site