|
@@ -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);
|