dmaengine: k3dma: Avoid null pointer traversal
authorJohn Stultz <john.stultz@linaro.org>
Wed, 18 Dec 2019 19:09:06 +0000 (19:09 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Jan 2020 18:45:54 +0000 (19:45 +0100)
[ Upstream commit 2f42e05b942fe2fbfb9bbc6e34e1dd8c3ce4f3a4 ]

In some cases we seem to submit two transactions in a row, which
causes us to lose track of the first. If we then cancel the
request, we may still get an interrupt, which traverses a null
ds_run value.

So try to avoid starting a new transaction if the ds_run value
is set.

While this patch avoids the null pointer crash, I've had some
reports of the k3dma driver still getting confused, which
suggests the ds_run/ds_done value handling still isn't quite
right. However, I've not run into an issue recently with it
so I think this patch is worth pushing upstream to avoid the
crash.

Signed-off-by: John Stultz <john.stultz@linaro.org>
[add ss tag]
Link: https://lore.kernel.org/r/20191218190906.6641-1-john.stultz@linaro.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/dma/k3dma.c

index 219ae3b545dbab035d20ce628c7a025159f9e9bb..803045c92f3ba5dd723e236fee0ae4bed235260b 100644 (file)
@@ -222,9 +222,11 @@ static irqreturn_t k3_dma_int_handler(int irq, void *dev_id)
                        c = p->vchan;
                        if (c && (tc1 & BIT(i))) {
                                spin_lock_irqsave(&c->vc.lock, flags);
-                               vchan_cookie_complete(&p->ds_run->vd);
-                               p->ds_done = p->ds_run;
-                               p->ds_run = NULL;
+                               if (p->ds_run != NULL) {
+                                       vchan_cookie_complete(&p->ds_run->vd);
+                                       p->ds_done = p->ds_run;
+                                       p->ds_run = NULL;
+                               }
                                spin_unlock_irqrestore(&c->vc.lock, flags);
                        }
                        if (c && (tc2 & BIT(i))) {
@@ -264,6 +266,10 @@ static int k3_dma_start_txd(struct k3_dma_chan *c)
        if (BIT(c->phy->idx) & k3_dma_get_chan_stat(d))
                return -EAGAIN;
 
+       /* Avoid losing track of  ds_run if a transaction is in flight */
+       if (c->phy->ds_run)
+               return -EAGAIN;
+
        if (vd) {
                struct k3_dma_desc_sw *ds =
                        container_of(vd, struct k3_dma_desc_sw, vd);