diff mbox

[09/11] dmaengine: cppi41: Fix a race between PM runtime and channel abort

Message ID 20170109160656.3470-10-abailon@baylibre.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Alexandre Bailon Jan. 9, 2017, 4:06 p.m. UTC
cppi41_dma_issue_pending() may be called while the device is runtime
suspended. In that case, the descritpor will be push to the pending
list and then be queued to hardware queue.
But if cppi41_stop_chan() is called before the device got time to
resume, then the descriptor will remain in the pending list and be
queued to hardware queue after the teardown.
During the channel stop, check if there is a pendding descriptor
and if so, remove it.

Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
---
 drivers/dma/cppi41.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

Comments

Sergei Shtylyov Jan. 10, 2017, 5:55 p.m. UTC | #1
On 01/09/2017 07:06 PM, Alexandre Bailon wrote:

> cppi41_dma_issue_pending() may be called while the device is runtime
> suspended. In that case, the descritpor will be push to the pending

   "Descriptor" and "pushed".

> list and then be queued to hardware queue.
> But if cppi41_stop_chan() is called before the device got time to
> resume, then the descriptor will remain in the pending list and be
> queued to hardware queue after the teardown.
> During the channel stop, check if there is a pendding descriptor

    Pending.

> and if so, remove it.
>
> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
[...]

MBR, Sergei

--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c
index 4318e53..e8470b1 100644
--- a/drivers/dma/cppi41.c
+++ b/drivers/dma/cppi41.c
@@ -751,10 +751,17 @@  static int cppi41_stop_chan(struct dma_chan *chan)
 {
 	struct cppi41_channel *c = to_cpp41_chan(chan);
 	struct cppi41_dd *cdd = c->cdd;
+	unsigned long flags;
 	u32 desc_num;
 	u32 desc_phys;
 	int ret;
 
+	/* Remove pending descriptor that haven't been pushed to queue */
+	spin_lock_irqsave(&cdd->lock, flags);
+	if (!list_empty(&c->node))
+		list_del_init(&c->node);
+	spin_unlock_irqrestore(&cdd->lock, flags);
+
 	desc_phys = lower_32_bits(c->desc_phys);
 	desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
 	if (!cdd->chan_busy[desc_num])
@@ -812,6 +819,7 @@  static int cppi41_add_chans(struct device *dev, struct cppi41_dd *cdd)
 		cchan->desc_phys = cdd->descs_phys;
 		cchan->desc_phys += i * sizeof(struct cppi41_desc);
 		cchan->chan.device = &cdd->ddev;
+		INIT_LIST_HEAD(&cchan->node);
 		list_add_tail(&cchan->chan.device_node, &cdd->ddev.channels);
 	}
 	cdd->first_td_desc = n_chans;
@@ -1301,7 +1309,7 @@  static int __maybe_unused cppi41_runtime_resume(struct device *dev)
 	spin_lock_irqsave(&cdd->lock, flags);
 	list_for_each_entry_safe(c, _c, &cdd->pending, node) {
 		push_desc_queue(c);
-		list_del(&c->node);
+		list_del_init(&c->node);
 	}
 	spin_unlock_irqrestore(&cdd->lock, flags);