Browse Source

Merge tag 'dmaengine-fix-4.15-rc4' of git://git.infradead.org/users/vkoul/slave-dma

Pull dmaengine fixes from Vinod Koul:
 "This time consisting of fixes in a bunch of drivers and the dmatest
  module:

   - Fix for disable clk on error path in fsl-edma driver
   - Disable clk fail fix in jz4740 driver
   - Fix long pending bug in dmatest driver for dangling pointer
   - Fix potential NULL pointer dereference in at_hdmac driver
   - Error handling path in ioat driver"

* tag 'dmaengine-fix-4.15-rc4' of git://git.infradead.org/users/vkoul/slave-dma:
  dmaengine: fsl-edma: disable clks on all error paths
  dmaengine: jz4740: disable/unprepare clk if probe fails
  dmaengine: dmatest: move callback wait queue to thread context
  dmaengine: at_hdmac: fix potential NULL pointer dereference in atc_prep_dma_interleaved
  dmaengine: ioat: Fix error handling path
Linus Torvalds 7 years ago
parent
commit
c43727908f
5 changed files with 52 additions and 41 deletions
  1. 3 1
      drivers/dma/at_hdmac.c
  2. 3 1
      drivers/dma/dma-jz4740.c
  3. 31 24
      drivers/dma/dmatest.c
  4. 14 14
      drivers/dma/fsl-edma.c
  5. 1 1
      drivers/dma/ioat/init.c

+ 3 - 1
drivers/dma/at_hdmac.c

@@ -708,7 +708,7 @@ atc_prep_dma_interleaved(struct dma_chan *chan,
 			 unsigned long flags)
 			 unsigned long flags)
 {
 {
 	struct at_dma_chan	*atchan = to_at_dma_chan(chan);
 	struct at_dma_chan	*atchan = to_at_dma_chan(chan);
-	struct data_chunk	*first = xt->sgl;
+	struct data_chunk	*first;
 	struct at_desc		*desc = NULL;
 	struct at_desc		*desc = NULL;
 	size_t			xfer_count;
 	size_t			xfer_count;
 	unsigned int		dwidth;
 	unsigned int		dwidth;
@@ -720,6 +720,8 @@ atc_prep_dma_interleaved(struct dma_chan *chan,
 	if (unlikely(!xt || xt->numf != 1 || !xt->frame_size))
 	if (unlikely(!xt || xt->numf != 1 || !xt->frame_size))
 		return NULL;
 		return NULL;
 
 
+	first = xt->sgl;
+
 	dev_info(chan2dev(chan),
 	dev_info(chan2dev(chan),
 		 "%s: src=%pad, dest=%pad, numf=%d, frame_size=%d, flags=0x%lx\n",
 		 "%s: src=%pad, dest=%pad, numf=%d, frame_size=%d, flags=0x%lx\n",
 		__func__, &xt->src_start, &xt->dst_start, xt->numf,
 		__func__, &xt->src_start, &xt->dst_start, xt->numf,

+ 3 - 1
drivers/dma/dma-jz4740.c

@@ -555,7 +555,7 @@ static int jz4740_dma_probe(struct platform_device *pdev)
 
 
 	ret = dma_async_device_register(dd);
 	ret = dma_async_device_register(dd);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_clk;
 
 
 	irq = platform_get_irq(pdev, 0);
 	irq = platform_get_irq(pdev, 0);
 	ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev);
 	ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev);
@@ -568,6 +568,8 @@ static int jz4740_dma_probe(struct platform_device *pdev)
 
 
 err_unregister:
 err_unregister:
 	dma_async_device_unregister(dd);
 	dma_async_device_unregister(dd);
+err_clk:
+	clk_disable_unprepare(dmadev->clk);
 	return ret;
 	return ret;
 }
 }
 
 

+ 31 - 24
drivers/dma/dmatest.c

@@ -155,6 +155,12 @@ MODULE_PARM_DESC(run, "Run the test (default: false)");
 #define PATTERN_COUNT_MASK	0x1f
 #define PATTERN_COUNT_MASK	0x1f
 #define PATTERN_MEMSET_IDX	0x01
 #define PATTERN_MEMSET_IDX	0x01
 
 
+/* poor man's completion - we want to use wait_event_freezable() on it */
+struct dmatest_done {
+	bool			done;
+	wait_queue_head_t	*wait;
+};
+
 struct dmatest_thread {
 struct dmatest_thread {
 	struct list_head	node;
 	struct list_head	node;
 	struct dmatest_info	*info;
 	struct dmatest_info	*info;
@@ -165,6 +171,8 @@ struct dmatest_thread {
 	u8			**dsts;
 	u8			**dsts;
 	u8			**udsts;
 	u8			**udsts;
 	enum dma_transaction_type type;
 	enum dma_transaction_type type;
+	wait_queue_head_t done_wait;
+	struct dmatest_done test_done;
 	bool			done;
 	bool			done;
 };
 };
 
 
@@ -342,18 +350,25 @@ static unsigned int dmatest_verify(u8 **bufs, unsigned int start,
 	return error_count;
 	return error_count;
 }
 }
 
 
-/* poor man's completion - we want to use wait_event_freezable() on it */
-struct dmatest_done {
-	bool			done;
-	wait_queue_head_t	*wait;
-};
 
 
 static void dmatest_callback(void *arg)
 static void dmatest_callback(void *arg)
 {
 {
 	struct dmatest_done *done = arg;
 	struct dmatest_done *done = arg;
-
-	done->done = true;
-	wake_up_all(done->wait);
+	struct dmatest_thread *thread =
+		container_of(arg, struct dmatest_thread, done_wait);
+	if (!thread->done) {
+		done->done = true;
+		wake_up_all(done->wait);
+	} else {
+		/*
+		 * If thread->done, it means that this callback occurred
+		 * after the parent thread has cleaned up. This can
+		 * happen in the case that driver doesn't implement
+		 * the terminate_all() functionality and a dma operation
+		 * did not occur within the timeout period
+		 */
+		WARN(1, "dmatest: Kernel memory may be corrupted!!\n");
+	}
 }
 }
 
 
 static unsigned int min_odd(unsigned int x, unsigned int y)
 static unsigned int min_odd(unsigned int x, unsigned int y)
@@ -424,9 +439,8 @@ static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len)
  */
  */
 static int dmatest_func(void *data)
 static int dmatest_func(void *data)
 {
 {
-	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_wait);
 	struct dmatest_thread	*thread = data;
 	struct dmatest_thread	*thread = data;
-	struct dmatest_done	done = { .wait = &done_wait };
+	struct dmatest_done	*done = &thread->test_done;
 	struct dmatest_info	*info;
 	struct dmatest_info	*info;
 	struct dmatest_params	*params;
 	struct dmatest_params	*params;
 	struct dma_chan		*chan;
 	struct dma_chan		*chan;
@@ -673,9 +687,9 @@ static int dmatest_func(void *data)
 			continue;
 			continue;
 		}
 		}
 
 
-		done.done = false;
+		done->done = false;
 		tx->callback = dmatest_callback;
 		tx->callback = dmatest_callback;
-		tx->callback_param = &done;
+		tx->callback_param = done;
 		cookie = tx->tx_submit(tx);
 		cookie = tx->tx_submit(tx);
 
 
 		if (dma_submit_error(cookie)) {
 		if (dma_submit_error(cookie)) {
@@ -688,21 +702,12 @@ static int dmatest_func(void *data)
 		}
 		}
 		dma_async_issue_pending(chan);
 		dma_async_issue_pending(chan);
 
 
-		wait_event_freezable_timeout(done_wait, done.done,
+		wait_event_freezable_timeout(thread->done_wait, done->done,
 					     msecs_to_jiffies(params->timeout));
 					     msecs_to_jiffies(params->timeout));
 
 
 		status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
 		status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
 
 
-		if (!done.done) {
-			/*
-			 * We're leaving the timed out dma operation with
-			 * dangling pointer to done_wait.  To make this
-			 * correct, we'll need to allocate wait_done for
-			 * each test iteration and perform "who's gonna
-			 * free it this time?" dancing.  For now, just
-			 * leave it dangling.
-			 */
-			WARN(1, "dmatest: Kernel stack may be corrupted!!\n");
+		if (!done->done) {
 			dmaengine_unmap_put(um);
 			dmaengine_unmap_put(um);
 			result("test timed out", total_tests, src_off, dst_off,
 			result("test timed out", total_tests, src_off, dst_off,
 			       len, 0);
 			       len, 0);
@@ -789,7 +794,7 @@ err_thread_type:
 		dmatest_KBs(runtime, total_len), ret);
 		dmatest_KBs(runtime, total_len), ret);
 
 
 	/* terminate all transfers on specified channels */
 	/* terminate all transfers on specified channels */
-	if (ret)
+	if (ret || failed_tests)
 		dmaengine_terminate_all(chan);
 		dmaengine_terminate_all(chan);
 
 
 	thread->done = true;
 	thread->done = true;
@@ -849,6 +854,8 @@ static int dmatest_add_threads(struct dmatest_info *info,
 		thread->info = info;
 		thread->info = info;
 		thread->chan = dtc->chan;
 		thread->chan = dtc->chan;
 		thread->type = type;
 		thread->type = type;
+		thread->test_done.wait = &thread->done_wait;
+		init_waitqueue_head(&thread->done_wait);
 		smp_wmb();
 		smp_wmb();
 		thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
 		thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
 				dma_chan_name(chan), op, i);
 				dma_chan_name(chan), op, i);

+ 14 - 14
drivers/dma/fsl-edma.c

@@ -863,11 +863,11 @@ static void fsl_edma_irq_exit(
 	}
 	}
 }
 }
 
 
-static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma)
+static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks)
 {
 {
 	int i;
 	int i;
 
 
-	for (i = 0; i < DMAMUX_NR; i++)
+	for (i = 0; i < nr_clocks; i++)
 		clk_disable_unprepare(fsl_edma->muxclk[i]);
 		clk_disable_unprepare(fsl_edma->muxclk[i]);
 }
 }
 
 
@@ -904,25 +904,25 @@ static int fsl_edma_probe(struct platform_device *pdev)
 
 
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
 		fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res);
 		fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res);
-		if (IS_ERR(fsl_edma->muxbase[i]))
+		if (IS_ERR(fsl_edma->muxbase[i])) {
+			/* on error: disable all previously enabled clks */
+			fsl_disable_clocks(fsl_edma, i);
 			return PTR_ERR(fsl_edma->muxbase[i]);
 			return PTR_ERR(fsl_edma->muxbase[i]);
+		}
 
 
 		sprintf(clkname, "dmamux%d", i);
 		sprintf(clkname, "dmamux%d", i);
 		fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname);
 		fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname);
 		if (IS_ERR(fsl_edma->muxclk[i])) {
 		if (IS_ERR(fsl_edma->muxclk[i])) {
 			dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
 			dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
+			/* on error: disable all previously enabled clks */
+			fsl_disable_clocks(fsl_edma, i);
 			return PTR_ERR(fsl_edma->muxclk[i]);
 			return PTR_ERR(fsl_edma->muxclk[i]);
 		}
 		}
 
 
 		ret = clk_prepare_enable(fsl_edma->muxclk[i]);
 		ret = clk_prepare_enable(fsl_edma->muxclk[i]);
-		if (ret) {
-			/* disable only clks which were enabled on error */
-			for (; i >= 0; i--)
-				clk_disable_unprepare(fsl_edma->muxclk[i]);
-
-			dev_err(&pdev->dev, "DMAMUX clk block failed.\n");
-			return ret;
-		}
+		if (ret)
+			/* on error: disable all previously enabled clks */
+			fsl_disable_clocks(fsl_edma, i);
 
 
 	}
 	}
 
 
@@ -976,7 +976,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev,
 		dev_err(&pdev->dev,
 			"Can't register Freescale eDMA engine. (%d)\n", ret);
 			"Can't register Freescale eDMA engine. (%d)\n", ret);
-		fsl_disable_clocks(fsl_edma);
+		fsl_disable_clocks(fsl_edma, DMAMUX_NR);
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -985,7 +985,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev,
 		dev_err(&pdev->dev,
 			"Can't register Freescale eDMA of_dma. (%d)\n", ret);
 			"Can't register Freescale eDMA of_dma. (%d)\n", ret);
 		dma_async_device_unregister(&fsl_edma->dma_dev);
 		dma_async_device_unregister(&fsl_edma->dma_dev);
-		fsl_disable_clocks(fsl_edma);
+		fsl_disable_clocks(fsl_edma, DMAMUX_NR);
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -1015,7 +1015,7 @@ static int fsl_edma_remove(struct platform_device *pdev)
 	fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
 	fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
 	of_dma_controller_free(np);
 	of_dma_controller_free(np);
 	dma_async_device_unregister(&fsl_edma->dma_dev);
 	dma_async_device_unregister(&fsl_edma->dma_dev);
-	fsl_disable_clocks(fsl_edma);
+	fsl_disable_clocks(fsl_edma, DMAMUX_NR);
 
 
 	return 0;
 	return 0;
 }
 }

+ 1 - 1
drivers/dma/ioat/init.c

@@ -390,7 +390,7 @@ static int ioat_dma_self_test(struct ioatdma_device *ioat_dma)
 	if (memcmp(src, dest, IOAT_TEST_SIZE)) {
 	if (memcmp(src, dest, IOAT_TEST_SIZE)) {
 		dev_err(dev, "Self-test copy failed compare, disabling\n");
 		dev_err(dev, "Self-test copy failed compare, disabling\n");
 		err = -ENODEV;
 		err = -ENODEV;
-		goto free_resources;
+		goto unmap_dma;
 	}
 	}
 
 
 unmap_dma:
 unmap_dma: