Skip to content

Commit 6f6a23a

Browse files
Adam WallisVinod Koul
authored andcommitted
dmaengine: dmatest: move callback wait queue to thread context
Commit adfa543 ("dmatest: don't use set_freezable_with_signal()") introduced a bug (that is in fact documented by the patch commit text) that leaves behind a dangling pointer. Since the done_wait structure is allocated on the stack, future invocations to the DMATEST can produce undesirable results (e.g., corrupted spinlocks). Commit a9df21e ("dmaengine: dmatest: warn user when dma test times out") attempted to WARN the user that the stack was likely corrupted but did not fix the actual issue. This patch fixes the issue by pushing the wait queue and callback structs into the the thread structure. If a failure occurs due to time, dmaengine_terminate_all will force the callback to safely call wake_up_all() without possibility of using a freed pointer. Cc: [email protected] Bug: https://bugzilla.kernel.org/show_bug.cgi?id=197605 Fixes: adfa543 ("dmatest: don't use set_freezable_with_signal()") Reviewed-by: Sinan Kaya <[email protected]> Suggested-by: Shunyong Yang <[email protected]> Signed-off-by: Adam Wallis <[email protected]> Signed-off-by: Vinod Koul <[email protected]>
1 parent 62a277d commit 6f6a23a

File tree

1 file changed

+31
-24
lines changed

1 file changed

+31
-24
lines changed

drivers/dma/dmatest.c

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ MODULE_PARM_DESC(run, "Run the test (default: false)");
155155
#define PATTERN_COUNT_MASK 0x1f
156156
#define PATTERN_MEMSET_IDX 0x01
157157

158+
/* poor man's completion - we want to use wait_event_freezable() on it */
159+
struct dmatest_done {
160+
bool done;
161+
wait_queue_head_t *wait;
162+
};
163+
158164
struct dmatest_thread {
159165
struct list_head node;
160166
struct dmatest_info *info;
@@ -165,6 +171,8 @@ struct dmatest_thread {
165171
u8 **dsts;
166172
u8 **udsts;
167173
enum dma_transaction_type type;
174+
wait_queue_head_t done_wait;
175+
struct dmatest_done test_done;
168176
bool done;
169177
};
170178

@@ -342,18 +350,25 @@ static unsigned int dmatest_verify(u8 **bufs, unsigned int start,
342350
return error_count;
343351
}
344352

345-
/* poor man's completion - we want to use wait_event_freezable() on it */
346-
struct dmatest_done {
347-
bool done;
348-
wait_queue_head_t *wait;
349-
};
350353

351354
static void dmatest_callback(void *arg)
352355
{
353356
struct dmatest_done *done = arg;
354-
355-
done->done = true;
356-
wake_up_all(done->wait);
357+
struct dmatest_thread *thread =
358+
container_of(arg, struct dmatest_thread, done_wait);
359+
if (!thread->done) {
360+
done->done = true;
361+
wake_up_all(done->wait);
362+
} else {
363+
/*
364+
* If thread->done, it means that this callback occurred
365+
* after the parent thread has cleaned up. This can
366+
* happen in the case that driver doesn't implement
367+
* the terminate_all() functionality and a dma operation
368+
* did not occur within the timeout period
369+
*/
370+
WARN(1, "dmatest: Kernel memory may be corrupted!!\n");
371+
}
357372
}
358373

359374
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)
424439
*/
425440
static int dmatest_func(void *data)
426441
{
427-
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_wait);
428442
struct dmatest_thread *thread = data;
429-
struct dmatest_done done = { .wait = &done_wait };
443+
struct dmatest_done *done = &thread->test_done;
430444
struct dmatest_info *info;
431445
struct dmatest_params *params;
432446
struct dma_chan *chan;
@@ -673,9 +687,9 @@ static int dmatest_func(void *data)
673687
continue;
674688
}
675689

676-
done.done = false;
690+
done->done = false;
677691
tx->callback = dmatest_callback;
678-
tx->callback_param = &done;
692+
tx->callback_param = done;
679693
cookie = tx->tx_submit(tx);
680694

681695
if (dma_submit_error(cookie)) {
@@ -688,21 +702,12 @@ static int dmatest_func(void *data)
688702
}
689703
dma_async_issue_pending(chan);
690704

691-
wait_event_freezable_timeout(done_wait, done.done,
705+
wait_event_freezable_timeout(thread->done_wait, done->done,
692706
msecs_to_jiffies(params->timeout));
693707

694708
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
695709

696-
if (!done.done) {
697-
/*
698-
* We're leaving the timed out dma operation with
699-
* dangling pointer to done_wait. To make this
700-
* correct, we'll need to allocate wait_done for
701-
* each test iteration and perform "who's gonna
702-
* free it this time?" dancing. For now, just
703-
* leave it dangling.
704-
*/
705-
WARN(1, "dmatest: Kernel stack may be corrupted!!\n");
710+
if (!done->done) {
706711
dmaengine_unmap_put(um);
707712
result("test timed out", total_tests, src_off, dst_off,
708713
len, 0);
@@ -789,7 +794,7 @@ static int dmatest_func(void *data)
789794
dmatest_KBs(runtime, total_len), ret);
790795

791796
/* terminate all transfers on specified channels */
792-
if (ret)
797+
if (ret || failed_tests)
793798
dmaengine_terminate_all(chan);
794799

795800
thread->done = true;
@@ -849,6 +854,8 @@ static int dmatest_add_threads(struct dmatest_info *info,
849854
thread->info = info;
850855
thread->chan = dtc->chan;
851856
thread->type = type;
857+
thread->test_done.wait = &thread->done_wait;
858+
init_waitqueue_head(&thread->done_wait);
852859
smp_wmb();
853860
thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
854861
dma_chan_name(chan), op, i);

0 commit comments

Comments
 (0)