diff options
-rw-r--r-- | drivers/dma/dmatest.c | 55 |
1 files changed, 31 insertions, 24 deletions
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 47edc7fbf91f..ec5f9d2bc820 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c | |||
@@ -155,6 +155,12 @@ MODULE_PARM_DESC(run, "Run the test (default: false)"); | |||
155 | #define PATTERN_COUNT_MASK 0x1f | 155 | #define PATTERN_COUNT_MASK 0x1f |
156 | #define PATTERN_MEMSET_IDX 0x01 | 156 | #define PATTERN_MEMSET_IDX 0x01 |
157 | 157 | ||
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 | |||
158 | struct dmatest_thread { | 164 | struct dmatest_thread { |
159 | struct list_head node; | 165 | struct list_head node; |
160 | struct dmatest_info *info; | 166 | struct dmatest_info *info; |
@@ -165,6 +171,8 @@ struct dmatest_thread { | |||
165 | u8 **dsts; | 171 | u8 **dsts; |
166 | u8 **udsts; | 172 | u8 **udsts; |
167 | enum dma_transaction_type type; | 173 | enum dma_transaction_type type; |
174 | wait_queue_head_t done_wait; | ||
175 | struct dmatest_done test_done; | ||
168 | bool done; | 176 | bool done; |
169 | }; | 177 | }; |
170 | 178 | ||
@@ -342,18 +350,25 @@ static unsigned int dmatest_verify(u8 **bufs, unsigned int start, | |||
342 | return error_count; | 350 | return error_count; |
343 | } | 351 | } |
344 | 352 | ||
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 | }; | ||
350 | 353 | ||
351 | static void dmatest_callback(void *arg) | 354 | static void dmatest_callback(void *arg) |
352 | { | 355 | { |
353 | struct dmatest_done *done = arg; | 356 | struct dmatest_done *done = arg; |
354 | 357 | struct dmatest_thread *thread = | |
355 | done->done = true; | 358 | container_of(arg, struct dmatest_thread, done_wait); |
356 | wake_up_all(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 | } | ||
357 | } | 372 | } |
358 | 373 | ||
359 | static unsigned int min_odd(unsigned int x, unsigned int y) | 374 | 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 | */ | 439 | */ |
425 | static int dmatest_func(void *data) | 440 | static int dmatest_func(void *data) |
426 | { | 441 | { |
427 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_wait); | ||
428 | struct dmatest_thread *thread = data; | 442 | struct dmatest_thread *thread = data; |
429 | struct dmatest_done done = { .wait = &done_wait }; | 443 | struct dmatest_done *done = &thread->test_done; |
430 | struct dmatest_info *info; | 444 | struct dmatest_info *info; |
431 | struct dmatest_params *params; | 445 | struct dmatest_params *params; |
432 | struct dma_chan *chan; | 446 | struct dma_chan *chan; |
@@ -673,9 +687,9 @@ static int dmatest_func(void *data) | |||
673 | continue; | 687 | continue; |
674 | } | 688 | } |
675 | 689 | ||
676 | done.done = false; | 690 | done->done = false; |
677 | tx->callback = dmatest_callback; | 691 | tx->callback = dmatest_callback; |
678 | tx->callback_param = &done; | 692 | tx->callback_param = done; |
679 | cookie = tx->tx_submit(tx); | 693 | cookie = tx->tx_submit(tx); |
680 | 694 | ||
681 | if (dma_submit_error(cookie)) { | 695 | if (dma_submit_error(cookie)) { |
@@ -688,21 +702,12 @@ static int dmatest_func(void *data) | |||
688 | } | 702 | } |
689 | dma_async_issue_pending(chan); | 703 | dma_async_issue_pending(chan); |
690 | 704 | ||
691 | wait_event_freezable_timeout(done_wait, done.done, | 705 | wait_event_freezable_timeout(thread->done_wait, done->done, |
692 | msecs_to_jiffies(params->timeout)); | 706 | msecs_to_jiffies(params->timeout)); |
693 | 707 | ||
694 | status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); | 708 | status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); |
695 | 709 | ||
696 | if (!done.done) { | 710 | 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"); | ||
706 | dmaengine_unmap_put(um); | 711 | dmaengine_unmap_put(um); |
707 | result("test timed out", total_tests, src_off, dst_off, | 712 | result("test timed out", total_tests, src_off, dst_off, |
708 | len, 0); | 713 | len, 0); |
@@ -789,7 +794,7 @@ err_thread_type: | |||
789 | dmatest_KBs(runtime, total_len), ret); | 794 | dmatest_KBs(runtime, total_len), ret); |
790 | 795 | ||
791 | /* terminate all transfers on specified channels */ | 796 | /* terminate all transfers on specified channels */ |
792 | if (ret) | 797 | if (ret || failed_tests) |
793 | dmaengine_terminate_all(chan); | 798 | dmaengine_terminate_all(chan); |
794 | 799 | ||
795 | thread->done = true; | 800 | thread->done = true; |
@@ -849,6 +854,8 @@ static int dmatest_add_threads(struct dmatest_info *info, | |||
849 | thread->info = info; | 854 | thread->info = info; |
850 | thread->chan = dtc->chan; | 855 | thread->chan = dtc->chan; |
851 | thread->type = type; | 856 | thread->type = type; |
857 | thread->test_done.wait = &thread->done_wait; | ||
858 | init_waitqueue_head(&thread->done_wait); | ||
852 | smp_wmb(); | 859 | smp_wmb(); |
853 | thread->task = kthread_create(dmatest_func, thread, "%s-%s%u", | 860 | thread->task = kthread_create(dmatest_func, thread, "%s-%s%u", |
854 | dma_chan_name(chan), op, i); | 861 | dma_chan_name(chan), op, i); |