]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
dmaengine: pl330: Fix hang on dmaengine_terminate_all on certain boards
authorKrzysztof Kozlowski <k.kozlowski@samsung.com>
Thu, 21 May 2015 00:34:09 +0000 (09:34 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Jun 2015 00:03:27 +0000 (17:03 -0700)
commit 81cc6edc08705ac0146fe6ac14a0982a31ce6f3d upstream.

The pl330 device could hang infinitely on certain boards when DMA
channels are terminated.

It was caused by lack of runtime resume when executing
pl330_terminate_all() which calls the _stop() function. _stop() accesses
device register and can loop infinitely while checking for device state.

The hang was confirmed by Dinh Nguyen on Altera SOCFPGA Cyclone V
board during boot. It can be also triggered with:

$ echo 1 > /sys/module/dmatest/parameters/iterations
$ echo dma1chan0 > /sys/module/dmatest/parameters/channel
$ echo 1 > /sys/module/dmatest/parameters/run
$ sleep 1
$ cat /sys/module/dmatest/parameters/run

Reported-by: Dinh Nguyen <dinguyen@opensource.altera.com>
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Fixes: ae43b3289186 ("ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12")
Tested-by: Dinh Nguyen <dinguyen@opensource.altera.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/dma/pl330.c

index 0e1f56772855d8fc7f1194099a4c113a6b1c8ddf..a2771a8d43774ccfef418f8a6dc0a68d4fb462aa 100644 (file)
@@ -2127,6 +2127,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
        struct pl330_dmac *pl330 = pch->dmac;
        LIST_HEAD(list);
 
+       pm_runtime_get_sync(pl330->ddma.dev);
        spin_lock_irqsave(&pch->lock, flags);
        spin_lock(&pl330->lock);
        _stop(pch->thread);
@@ -2151,6 +2152,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
        list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
        list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
        spin_unlock_irqrestore(&pch->lock, flags);
+       pm_runtime_mark_last_busy(pl330->ddma.dev);
+       pm_runtime_put_autosuspend(pl330->ddma.dev);
 
        return 0;
 }