Commit c4372790 authored by Linus Torvalds's avatar Linus Torvalds

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
parents b9f5fb18 2610acf4
...@@ -708,7 +708,7 @@ atc_prep_dma_interleaved(struct dma_chan *chan, ...@@ -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, ...@@ -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,
......
...@@ -555,7 +555,7 @@ static int jz4740_dma_probe(struct platform_device *pdev) ...@@ -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) ...@@ -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;
} }
......
...@@ -155,6 +155,12 @@ MODULE_PARM_DESC(run, "Run the test (default: false)"); ...@@ -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 { ...@@ -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, ...@@ -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;
struct dmatest_thread *thread =
done->done = true; container_of(arg, struct dmatest_thread, done_wait);
wake_up_all(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) ...@@ -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) ...@@ -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) ...@@ -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) { 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");
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 @@ static int dmatest_func(void *data) ...@@ -789,7 +794,7 @@ static int dmatest_func(void *data)
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, ...@@ -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);
......
...@@ -863,11 +863,11 @@ static void fsl_edma_irq_exit( ...@@ -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) ...@@ -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) { if (ret)
/* disable only clks which were enabled on error */ /* on error: disable all previously enabled clks */
for (; i >= 0; i--) fsl_disable_clocks(fsl_edma, i);
clk_disable_unprepare(fsl_edma->muxclk[i]);
dev_err(&pdev->dev, "DMAMUX clk block failed.\n");
return ret;
}
} }
...@@ -976,7 +976,7 @@ static int fsl_edma_probe(struct platform_device *pdev) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
...@@ -390,7 +390,7 @@ static int ioat_dma_self_test(struct ioatdma_device *ioat_dma) ...@@ -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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment