Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e81b592 authored by Geert Uytterhoeven's avatar Geert Uytterhoeven Committed by Mark Brown
Browse files

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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 7d1311b9
Loading
Loading
Loading
Loading
+38 −33
Original line number Diff line number Diff line
@@ -636,48 +636,38 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
	dma_cookie_t cookie;
	int ret;

	if (tx) {
		ier_bits |= IER_TDREQE | IER_TDMAE;
		dma_sync_single_for_device(p->master->dma_tx->device->dev,
					   p->tx_dma_addr, len, DMA_TO_DEVICE);
		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;
	}

	/* 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)
			return -EAGAIN;
		if (!desc_rx) {
			ret = -EAGAIN;
			goto no_dma_rx;
		}

	/* 1 stage FIFO watermarks for DMA */
	sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);

	/* setup msiof transfer mode registers (32-bit words) */
	sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);

	sh_msiof_write(p, IER, ier_bits);

	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;
			goto no_dma_rx;
		}
		dma_async_issue_pending(p->master->dma_rx);
	}

	if (tx) {
		ier_bits |= IER_TDREQE | IER_TDMAE;
		dma_sync_single_for_device(p->master->dma_tx->device->dev,
					   p->tx_dma_addr, len, DMA_TO_DEVICE);
		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) {
			ret = -EAGAIN;
			goto no_dma_tx;
		}

		if (rx) {
			/* No callback */
			desc_tx->callback = NULL;
@@ -688,15 +678,30 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
		cookie = dmaengine_submit(desc_tx);
		if (dma_submit_error(cookie)) {
			ret = cookie;
			goto stop_rx;
			goto no_dma_tx;
		}
		dma_async_issue_pending(p->master->dma_tx);
	}

	/* 1 stage FIFO watermarks for DMA */
	sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);

	/* setup msiof transfer mode registers (32-bit words) */
	sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);

	sh_msiof_write(p, IER, ier_bits);

	reinit_completion(&p->done);

	/* Now start DMA */
	if (tx)
		dma_async_issue_pending(p->master->dma_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;
}