diff options
Diffstat (limited to 'drivers/dma/dmatest.c')
-rw-r--r-- | drivers/dma/dmatest.c | 887 |
1 files changed, 751 insertions, 136 deletions
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index a2c8904b63ea..d8ce4ecfef18 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * DMA Engine test module | 2 | * DMA Engine test module |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Atmel Corporation | 4 | * Copyright (C) 2007 Atmel Corporation |
5 | * Copyright (C) 2013 Intel Corporation | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -18,6 +19,10 @@ | |||
18 | #include <linux/random.h> | 19 | #include <linux/random.h> |
19 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
20 | #include <linux/wait.h> | 21 | #include <linux/wait.h> |
22 | #include <linux/ctype.h> | ||
23 | #include <linux/debugfs.h> | ||
24 | #include <linux/uaccess.h> | ||
25 | #include <linux/seq_file.h> | ||
21 | 26 | ||
22 | static unsigned int test_buf_size = 16384; | 27 | static unsigned int test_buf_size = 16384; |
23 | module_param(test_buf_size, uint, S_IRUGO); | 28 | module_param(test_buf_size, uint, S_IRUGO); |
@@ -61,6 +66,9 @@ module_param(timeout, uint, S_IRUGO); | |||
61 | MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), " | 66 | MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), " |
62 | "Pass -1 for infinite timeout"); | 67 | "Pass -1 for infinite timeout"); |
63 | 68 | ||
69 | /* Maximum amount of mismatched bytes in buffer to print */ | ||
70 | #define MAX_ERROR_COUNT 32 | ||
71 | |||
64 | /* | 72 | /* |
65 | * Initialization patterns. All bytes in the source buffer has bit 7 | 73 | * Initialization patterns. All bytes in the source buffer has bit 7 |
66 | * set, all bytes in the destination buffer has bit 7 cleared. | 74 | * set, all bytes in the destination buffer has bit 7 cleared. |
@@ -78,13 +86,65 @@ MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), " | |||
78 | #define PATTERN_OVERWRITE 0x20 | 86 | #define PATTERN_OVERWRITE 0x20 |
79 | #define PATTERN_COUNT_MASK 0x1f | 87 | #define PATTERN_COUNT_MASK 0x1f |
80 | 88 | ||
89 | enum dmatest_error_type { | ||
90 | DMATEST_ET_OK, | ||
91 | DMATEST_ET_MAP_SRC, | ||
92 | DMATEST_ET_MAP_DST, | ||
93 | DMATEST_ET_PREP, | ||
94 | DMATEST_ET_SUBMIT, | ||
95 | DMATEST_ET_TIMEOUT, | ||
96 | DMATEST_ET_DMA_ERROR, | ||
97 | DMATEST_ET_DMA_IN_PROGRESS, | ||
98 | DMATEST_ET_VERIFY, | ||
99 | DMATEST_ET_VERIFY_BUF, | ||
100 | }; | ||
101 | |||
102 | struct dmatest_verify_buffer { | ||
103 | unsigned int index; | ||
104 | u8 expected; | ||
105 | u8 actual; | ||
106 | }; | ||
107 | |||
108 | struct dmatest_verify_result { | ||
109 | unsigned int error_count; | ||
110 | struct dmatest_verify_buffer data[MAX_ERROR_COUNT]; | ||
111 | u8 pattern; | ||
112 | bool is_srcbuf; | ||
113 | }; | ||
114 | |||
115 | struct dmatest_thread_result { | ||
116 | struct list_head node; | ||
117 | unsigned int n; | ||
118 | unsigned int src_off; | ||
119 | unsigned int dst_off; | ||
120 | unsigned int len; | ||
121 | enum dmatest_error_type type; | ||
122 | union { | ||
123 | unsigned long data; | ||
124 | dma_cookie_t cookie; | ||
125 | enum dma_status status; | ||
126 | int error; | ||
127 | struct dmatest_verify_result *vr; | ||
128 | }; | ||
129 | }; | ||
130 | |||
131 | struct dmatest_result { | ||
132 | struct list_head node; | ||
133 | char *name; | ||
134 | struct list_head results; | ||
135 | }; | ||
136 | |||
137 | struct dmatest_info; | ||
138 | |||
81 | struct dmatest_thread { | 139 | struct dmatest_thread { |
82 | struct list_head node; | 140 | struct list_head node; |
141 | struct dmatest_info *info; | ||
83 | struct task_struct *task; | 142 | struct task_struct *task; |
84 | struct dma_chan *chan; | 143 | struct dma_chan *chan; |
85 | u8 **srcs; | 144 | u8 **srcs; |
86 | u8 **dsts; | 145 | u8 **dsts; |
87 | enum dma_transaction_type type; | 146 | enum dma_transaction_type type; |
147 | bool done; | ||
88 | }; | 148 | }; |
89 | 149 | ||
90 | struct dmatest_chan { | 150 | struct dmatest_chan { |
@@ -93,25 +153,69 @@ struct dmatest_chan { | |||
93 | struct list_head threads; | 153 | struct list_head threads; |
94 | }; | 154 | }; |
95 | 155 | ||
96 | /* | 156 | /** |
97 | * These are protected by dma_list_mutex since they're only used by | 157 | * struct dmatest_params - test parameters. |
98 | * the DMA filter function callback | 158 | * @buf_size: size of the memcpy test buffer |
159 | * @channel: bus ID of the channel to test | ||
160 | * @device: bus ID of the DMA Engine to test | ||
161 | * @threads_per_chan: number of threads to start per channel | ||
162 | * @max_channels: maximum number of channels to use | ||
163 | * @iterations: iterations before stopping test | ||
164 | * @xor_sources: number of xor source buffers | ||
165 | * @pq_sources: number of p+q source buffers | ||
166 | * @timeout: transfer timeout in msec, -1 for infinite timeout | ||
99 | */ | 167 | */ |
100 | static LIST_HEAD(dmatest_channels); | 168 | struct dmatest_params { |
101 | static unsigned int nr_channels; | 169 | unsigned int buf_size; |
170 | char channel[20]; | ||
171 | char device[20]; | ||
172 | unsigned int threads_per_chan; | ||
173 | unsigned int max_channels; | ||
174 | unsigned int iterations; | ||
175 | unsigned int xor_sources; | ||
176 | unsigned int pq_sources; | ||
177 | int timeout; | ||
178 | }; | ||
102 | 179 | ||
103 | static bool dmatest_match_channel(struct dma_chan *chan) | 180 | /** |
181 | * struct dmatest_info - test information. | ||
182 | * @params: test parameters | ||
183 | * @lock: access protection to the fields of this structure | ||
184 | */ | ||
185 | struct dmatest_info { | ||
186 | /* Test parameters */ | ||
187 | struct dmatest_params params; | ||
188 | |||
189 | /* Internal state */ | ||
190 | struct list_head channels; | ||
191 | unsigned int nr_channels; | ||
192 | struct mutex lock; | ||
193 | |||
194 | /* debugfs related stuff */ | ||
195 | struct dentry *root; | ||
196 | struct dmatest_params dbgfs_params; | ||
197 | |||
198 | /* Test results */ | ||
199 | struct list_head results; | ||
200 | struct mutex results_lock; | ||
201 | }; | ||
202 | |||
203 | static struct dmatest_info test_info; | ||
204 | |||
205 | static bool dmatest_match_channel(struct dmatest_params *params, | ||
206 | struct dma_chan *chan) | ||
104 | { | 207 | { |
105 | if (test_channel[0] == '\0') | 208 | if (params->channel[0] == '\0') |
106 | return true; | 209 | return true; |
107 | return strcmp(dma_chan_name(chan), test_channel) == 0; | 210 | return strcmp(dma_chan_name(chan), params->channel) == 0; |
108 | } | 211 | } |
109 | 212 | ||
110 | static bool dmatest_match_device(struct dma_device *device) | 213 | static bool dmatest_match_device(struct dmatest_params *params, |
214 | struct dma_device *device) | ||
111 | { | 215 | { |
112 | if (test_device[0] == '\0') | 216 | if (params->device[0] == '\0') |
113 | return true; | 217 | return true; |
114 | return strcmp(dev_name(device->dev), test_device) == 0; | 218 | return strcmp(dev_name(device->dev), params->device) == 0; |
115 | } | 219 | } |
116 | 220 | ||
117 | static unsigned long dmatest_random(void) | 221 | static unsigned long dmatest_random(void) |
@@ -122,7 +226,8 @@ static unsigned long dmatest_random(void) | |||
122 | return buf; | 226 | return buf; |
123 | } | 227 | } |
124 | 228 | ||
125 | static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len) | 229 | static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len, |
230 | unsigned int buf_size) | ||
126 | { | 231 | { |
127 | unsigned int i; | 232 | unsigned int i; |
128 | u8 *buf; | 233 | u8 *buf; |
@@ -133,13 +238,14 @@ static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len) | |||
133 | for ( ; i < start + len; i++) | 238 | for ( ; i < start + len; i++) |
134 | buf[i] = PATTERN_SRC | PATTERN_COPY | 239 | buf[i] = PATTERN_SRC | PATTERN_COPY |
135 | | (~i & PATTERN_COUNT_MASK); | 240 | | (~i & PATTERN_COUNT_MASK); |
136 | for ( ; i < test_buf_size; i++) | 241 | for ( ; i < buf_size; i++) |
137 | buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); | 242 | buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); |
138 | buf++; | 243 | buf++; |
139 | } | 244 | } |
140 | } | 245 | } |
141 | 246 | ||
142 | static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len) | 247 | static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len, |
248 | unsigned int buf_size) | ||
143 | { | 249 | { |
144 | unsigned int i; | 250 | unsigned int i; |
145 | u8 *buf; | 251 | u8 *buf; |
@@ -150,40 +256,14 @@ static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len) | |||
150 | for ( ; i < start + len; i++) | 256 | for ( ; i < start + len; i++) |
151 | buf[i] = PATTERN_DST | PATTERN_OVERWRITE | 257 | buf[i] = PATTERN_DST | PATTERN_OVERWRITE |
152 | | (~i & PATTERN_COUNT_MASK); | 258 | | (~i & PATTERN_COUNT_MASK); |
153 | for ( ; i < test_buf_size; i++) | 259 | for ( ; i < buf_size; i++) |
154 | buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); | 260 | buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); |
155 | } | 261 | } |
156 | } | 262 | } |
157 | 263 | ||
158 | static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index, | 264 | static unsigned int dmatest_verify(struct dmatest_verify_result *vr, u8 **bufs, |
159 | unsigned int counter, bool is_srcbuf) | 265 | unsigned int start, unsigned int end, unsigned int counter, |
160 | { | 266 | u8 pattern, bool is_srcbuf) |
161 | u8 diff = actual ^ pattern; | ||
162 | u8 expected = pattern | (~counter & PATTERN_COUNT_MASK); | ||
163 | const char *thread_name = current->comm; | ||
164 | |||
165 | if (is_srcbuf) | ||
166 | pr_warning("%s: srcbuf[0x%x] overwritten!" | ||
167 | " Expected %02x, got %02x\n", | ||
168 | thread_name, index, expected, actual); | ||
169 | else if ((pattern & PATTERN_COPY) | ||
170 | && (diff & (PATTERN_COPY | PATTERN_OVERWRITE))) | ||
171 | pr_warning("%s: dstbuf[0x%x] not copied!" | ||
172 | " Expected %02x, got %02x\n", | ||
173 | thread_name, index, expected, actual); | ||
174 | else if (diff & PATTERN_SRC) | ||
175 | pr_warning("%s: dstbuf[0x%x] was copied!" | ||
176 | " Expected %02x, got %02x\n", | ||
177 | thread_name, index, expected, actual); | ||
178 | else | ||
179 | pr_warning("%s: dstbuf[0x%x] mismatch!" | ||
180 | " Expected %02x, got %02x\n", | ||
181 | thread_name, index, expected, actual); | ||
182 | } | ||
183 | |||
184 | static unsigned int dmatest_verify(u8 **bufs, unsigned int start, | ||
185 | unsigned int end, unsigned int counter, u8 pattern, | ||
186 | bool is_srcbuf) | ||
187 | { | 267 | { |
188 | unsigned int i; | 268 | unsigned int i; |
189 | unsigned int error_count = 0; | 269 | unsigned int error_count = 0; |
@@ -191,6 +271,7 @@ static unsigned int dmatest_verify(u8 **bufs, unsigned int start, | |||
191 | u8 expected; | 271 | u8 expected; |
192 | u8 *buf; | 272 | u8 *buf; |
193 | unsigned int counter_orig = counter; | 273 | unsigned int counter_orig = counter; |
274 | struct dmatest_verify_buffer *vb; | ||
194 | 275 | ||
195 | for (; (buf = *bufs); bufs++) { | 276 | for (; (buf = *bufs); bufs++) { |
196 | counter = counter_orig; | 277 | counter = counter_orig; |
@@ -198,18 +279,21 @@ static unsigned int dmatest_verify(u8 **bufs, unsigned int start, | |||
198 | actual = buf[i]; | 279 | actual = buf[i]; |
199 | expected = pattern | (~counter & PATTERN_COUNT_MASK); | 280 | expected = pattern | (~counter & PATTERN_COUNT_MASK); |
200 | if (actual != expected) { | 281 | if (actual != expected) { |
201 | if (error_count < 32) | 282 | if (error_count < MAX_ERROR_COUNT && vr) { |
202 | dmatest_mismatch(actual, pattern, i, | 283 | vb = &vr->data[error_count]; |
203 | counter, is_srcbuf); | 284 | vb->index = i; |
285 | vb->expected = expected; | ||
286 | vb->actual = actual; | ||
287 | } | ||
204 | error_count++; | 288 | error_count++; |
205 | } | 289 | } |
206 | counter++; | 290 | counter++; |
207 | } | 291 | } |
208 | } | 292 | } |
209 | 293 | ||
210 | if (error_count > 32) | 294 | if (error_count > MAX_ERROR_COUNT) |
211 | pr_warning("%s: %u errors suppressed\n", | 295 | pr_warning("%s: %u errors suppressed\n", |
212 | current->comm, error_count - 32); | 296 | current->comm, error_count - MAX_ERROR_COUNT); |
213 | 297 | ||
214 | return error_count; | 298 | return error_count; |
215 | } | 299 | } |
@@ -249,6 +333,170 @@ static unsigned int min_odd(unsigned int x, unsigned int y) | |||
249 | return val % 2 ? val : val - 1; | 333 | return val % 2 ? val : val - 1; |
250 | } | 334 | } |
251 | 335 | ||
336 | static char *verify_result_get_one(struct dmatest_verify_result *vr, | ||
337 | unsigned int i) | ||
338 | { | ||
339 | struct dmatest_verify_buffer *vb = &vr->data[i]; | ||
340 | u8 diff = vb->actual ^ vr->pattern; | ||
341 | static char buf[512]; | ||
342 | char *msg; | ||
343 | |||
344 | if (vr->is_srcbuf) | ||
345 | msg = "srcbuf overwritten!"; | ||
346 | else if ((vr->pattern & PATTERN_COPY) | ||
347 | && (diff & (PATTERN_COPY | PATTERN_OVERWRITE))) | ||
348 | msg = "dstbuf not copied!"; | ||
349 | else if (diff & PATTERN_SRC) | ||
350 | msg = "dstbuf was copied!"; | ||
351 | else | ||
352 | msg = "dstbuf mismatch!"; | ||
353 | |||
354 | snprintf(buf, sizeof(buf) - 1, "%s [0x%x] Expected %02x, got %02x", msg, | ||
355 | vb->index, vb->expected, vb->actual); | ||
356 | |||
357 | return buf; | ||
358 | } | ||
359 | |||
360 | static char *thread_result_get(const char *name, | ||
361 | struct dmatest_thread_result *tr) | ||
362 | { | ||
363 | static const char * const messages[] = { | ||
364 | [DMATEST_ET_OK] = "No errors", | ||
365 | [DMATEST_ET_MAP_SRC] = "src mapping error", | ||
366 | [DMATEST_ET_MAP_DST] = "dst mapping error", | ||
367 | [DMATEST_ET_PREP] = "prep error", | ||
368 | [DMATEST_ET_SUBMIT] = "submit error", | ||
369 | [DMATEST_ET_TIMEOUT] = "test timed out", | ||
370 | [DMATEST_ET_DMA_ERROR] = | ||
371 | "got completion callback (DMA_ERROR)", | ||
372 | [DMATEST_ET_DMA_IN_PROGRESS] = | ||
373 | "got completion callback (DMA_IN_PROGRESS)", | ||
374 | [DMATEST_ET_VERIFY] = "errors", | ||
375 | [DMATEST_ET_VERIFY_BUF] = "verify errors", | ||
376 | }; | ||
377 | static char buf[512]; | ||
378 | |||
379 | snprintf(buf, sizeof(buf) - 1, | ||
380 | "%s: #%u: %s with src_off=0x%x ""dst_off=0x%x len=0x%x (%lu)", | ||
381 | name, tr->n, messages[tr->type], tr->src_off, tr->dst_off, | ||
382 | tr->len, tr->data); | ||
383 | |||
384 | return buf; | ||
385 | } | ||
386 | |||
387 | static int thread_result_add(struct dmatest_info *info, | ||
388 | struct dmatest_result *r, enum dmatest_error_type type, | ||
389 | unsigned int n, unsigned int src_off, unsigned int dst_off, | ||
390 | unsigned int len, unsigned long data) | ||
391 | { | ||
392 | struct dmatest_thread_result *tr; | ||
393 | |||
394 | tr = kzalloc(sizeof(*tr), GFP_KERNEL); | ||
395 | if (!tr) | ||
396 | return -ENOMEM; | ||
397 | |||
398 | tr->type = type; | ||
399 | tr->n = n; | ||
400 | tr->src_off = src_off; | ||
401 | tr->dst_off = dst_off; | ||
402 | tr->len = len; | ||
403 | tr->data = data; | ||
404 | |||
405 | mutex_lock(&info->results_lock); | ||
406 | list_add_tail(&tr->node, &r->results); | ||
407 | mutex_unlock(&info->results_lock); | ||
408 | |||
409 | pr_warn("%s\n", thread_result_get(r->name, tr)); | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static unsigned int verify_result_add(struct dmatest_info *info, | ||
414 | struct dmatest_result *r, unsigned int n, | ||
415 | unsigned int src_off, unsigned int dst_off, unsigned int len, | ||
416 | u8 **bufs, int whence, unsigned int counter, u8 pattern, | ||
417 | bool is_srcbuf) | ||
418 | { | ||
419 | struct dmatest_verify_result *vr; | ||
420 | unsigned int error_count; | ||
421 | unsigned int buf_off = is_srcbuf ? src_off : dst_off; | ||
422 | unsigned int start, end; | ||
423 | |||
424 | if (whence < 0) { | ||
425 | start = 0; | ||
426 | end = buf_off; | ||
427 | } else if (whence > 0) { | ||
428 | start = buf_off + len; | ||
429 | end = info->params.buf_size; | ||
430 | } else { | ||
431 | start = buf_off; | ||
432 | end = buf_off + len; | ||
433 | } | ||
434 | |||
435 | vr = kmalloc(sizeof(*vr), GFP_KERNEL); | ||
436 | if (!vr) { | ||
437 | pr_warn("dmatest: No memory to store verify result\n"); | ||
438 | return dmatest_verify(NULL, bufs, start, end, counter, pattern, | ||
439 | is_srcbuf); | ||
440 | } | ||
441 | |||
442 | vr->pattern = pattern; | ||
443 | vr->is_srcbuf = is_srcbuf; | ||
444 | |||
445 | error_count = dmatest_verify(vr, bufs, start, end, counter, pattern, | ||
446 | is_srcbuf); | ||
447 | if (error_count) { | ||
448 | vr->error_count = error_count; | ||
449 | thread_result_add(info, r, DMATEST_ET_VERIFY_BUF, n, src_off, | ||
450 | dst_off, len, (unsigned long)vr); | ||
451 | return error_count; | ||
452 | } | ||
453 | |||
454 | kfree(vr); | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static void result_free(struct dmatest_info *info, const char *name) | ||
459 | { | ||
460 | struct dmatest_result *r, *_r; | ||
461 | |||
462 | mutex_lock(&info->results_lock); | ||
463 | list_for_each_entry_safe(r, _r, &info->results, node) { | ||
464 | struct dmatest_thread_result *tr, *_tr; | ||
465 | |||
466 | if (name && strcmp(r->name, name)) | ||
467 | continue; | ||
468 | |||
469 | list_for_each_entry_safe(tr, _tr, &r->results, node) { | ||
470 | if (tr->type == DMATEST_ET_VERIFY_BUF) | ||
471 | kfree(tr->vr); | ||
472 | list_del(&tr->node); | ||
473 | kfree(tr); | ||
474 | } | ||
475 | |||
476 | kfree(r->name); | ||
477 | list_del(&r->node); | ||
478 | kfree(r); | ||
479 | } | ||
480 | |||
481 | mutex_unlock(&info->results_lock); | ||
482 | } | ||
483 | |||
484 | static struct dmatest_result *result_init(struct dmatest_info *info, | ||
485 | const char *name) | ||
486 | { | ||
487 | struct dmatest_result *r; | ||
488 | |||
489 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
490 | if (r) { | ||
491 | r->name = kstrdup(name, GFP_KERNEL); | ||
492 | INIT_LIST_HEAD(&r->results); | ||
493 | mutex_lock(&info->results_lock); | ||
494 | list_add_tail(&r->node, &info->results); | ||
495 | mutex_unlock(&info->results_lock); | ||
496 | } | ||
497 | return r; | ||
498 | } | ||
499 | |||
252 | /* | 500 | /* |
253 | * This function repeatedly tests DMA transfers of various lengths and | 501 | * This function repeatedly tests DMA transfers of various lengths and |
254 | * offsets for a given operation type until it is told to exit by | 502 | * offsets for a given operation type until it is told to exit by |
@@ -268,6 +516,8 @@ static int dmatest_func(void *data) | |||
268 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_wait); | 516 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_wait); |
269 | struct dmatest_thread *thread = data; | 517 | struct dmatest_thread *thread = data; |
270 | struct dmatest_done done = { .wait = &done_wait }; | 518 | struct dmatest_done done = { .wait = &done_wait }; |
519 | struct dmatest_info *info; | ||
520 | struct dmatest_params *params; | ||
271 | struct dma_chan *chan; | 521 | struct dma_chan *chan; |
272 | struct dma_device *dev; | 522 | struct dma_device *dev; |
273 | const char *thread_name; | 523 | const char *thread_name; |
@@ -278,11 +528,12 @@ static int dmatest_func(void *data) | |||
278 | dma_cookie_t cookie; | 528 | dma_cookie_t cookie; |
279 | enum dma_status status; | 529 | enum dma_status status; |
280 | enum dma_ctrl_flags flags; | 530 | enum dma_ctrl_flags flags; |
281 | u8 pq_coefs[pq_sources + 1]; | 531 | u8 *pq_coefs = NULL; |
282 | int ret; | 532 | int ret; |
283 | int src_cnt; | 533 | int src_cnt; |
284 | int dst_cnt; | 534 | int dst_cnt; |
285 | int i; | 535 | int i; |
536 | struct dmatest_result *result; | ||
286 | 537 | ||
287 | thread_name = current->comm; | 538 | thread_name = current->comm; |
288 | set_freezable(); | 539 | set_freezable(); |
@@ -290,28 +541,39 @@ static int dmatest_func(void *data) | |||
290 | ret = -ENOMEM; | 541 | ret = -ENOMEM; |
291 | 542 | ||
292 | smp_rmb(); | 543 | smp_rmb(); |
544 | info = thread->info; | ||
545 | params = &info->params; | ||
293 | chan = thread->chan; | 546 | chan = thread->chan; |
294 | dev = chan->device; | 547 | dev = chan->device; |
295 | if (thread->type == DMA_MEMCPY) | 548 | if (thread->type == DMA_MEMCPY) |
296 | src_cnt = dst_cnt = 1; | 549 | src_cnt = dst_cnt = 1; |
297 | else if (thread->type == DMA_XOR) { | 550 | else if (thread->type == DMA_XOR) { |
298 | /* force odd to ensure dst = src */ | 551 | /* force odd to ensure dst = src */ |
299 | src_cnt = min_odd(xor_sources | 1, dev->max_xor); | 552 | src_cnt = min_odd(params->xor_sources | 1, dev->max_xor); |
300 | dst_cnt = 1; | 553 | dst_cnt = 1; |
301 | } else if (thread->type == DMA_PQ) { | 554 | } else if (thread->type == DMA_PQ) { |
302 | /* force odd to ensure dst = src */ | 555 | /* force odd to ensure dst = src */ |
303 | src_cnt = min_odd(pq_sources | 1, dma_maxpq(dev, 0)); | 556 | src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0)); |
304 | dst_cnt = 2; | 557 | dst_cnt = 2; |
558 | |||
559 | pq_coefs = kmalloc(params->pq_sources+1, GFP_KERNEL); | ||
560 | if (!pq_coefs) | ||
561 | goto err_thread_type; | ||
562 | |||
305 | for (i = 0; i < src_cnt; i++) | 563 | for (i = 0; i < src_cnt; i++) |
306 | pq_coefs[i] = 1; | 564 | pq_coefs[i] = 1; |
307 | } else | 565 | } else |
566 | goto err_thread_type; | ||
567 | |||
568 | result = result_init(info, thread_name); | ||
569 | if (!result) | ||
308 | goto err_srcs; | 570 | goto err_srcs; |
309 | 571 | ||
310 | thread->srcs = kcalloc(src_cnt+1, sizeof(u8 *), GFP_KERNEL); | 572 | thread->srcs = kcalloc(src_cnt+1, sizeof(u8 *), GFP_KERNEL); |
311 | if (!thread->srcs) | 573 | if (!thread->srcs) |
312 | goto err_srcs; | 574 | goto err_srcs; |
313 | for (i = 0; i < src_cnt; i++) { | 575 | for (i = 0; i < src_cnt; i++) { |
314 | thread->srcs[i] = kmalloc(test_buf_size, GFP_KERNEL); | 576 | thread->srcs[i] = kmalloc(params->buf_size, GFP_KERNEL); |
315 | if (!thread->srcs[i]) | 577 | if (!thread->srcs[i]) |
316 | goto err_srcbuf; | 578 | goto err_srcbuf; |
317 | } | 579 | } |
@@ -321,7 +583,7 @@ static int dmatest_func(void *data) | |||
321 | if (!thread->dsts) | 583 | if (!thread->dsts) |
322 | goto err_dsts; | 584 | goto err_dsts; |
323 | for (i = 0; i < dst_cnt; i++) { | 585 | for (i = 0; i < dst_cnt; i++) { |
324 | thread->dsts[i] = kmalloc(test_buf_size, GFP_KERNEL); | 586 | thread->dsts[i] = kmalloc(params->buf_size, GFP_KERNEL); |
325 | if (!thread->dsts[i]) | 587 | if (!thread->dsts[i]) |
326 | goto err_dstbuf; | 588 | goto err_dstbuf; |
327 | } | 589 | } |
@@ -337,7 +599,7 @@ static int dmatest_func(void *data) | |||
337 | | DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SRC_UNMAP_SINGLE; | 599 | | DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SRC_UNMAP_SINGLE; |
338 | 600 | ||
339 | while (!kthread_should_stop() | 601 | while (!kthread_should_stop() |
340 | && !(iterations && total_tests >= iterations)) { | 602 | && !(params->iterations && total_tests >= params->iterations)) { |
341 | struct dma_async_tx_descriptor *tx = NULL; | 603 | struct dma_async_tx_descriptor *tx = NULL; |
342 | dma_addr_t dma_srcs[src_cnt]; | 604 | dma_addr_t dma_srcs[src_cnt]; |
343 | dma_addr_t dma_dsts[dst_cnt]; | 605 | dma_addr_t dma_dsts[dst_cnt]; |
@@ -353,24 +615,24 @@ static int dmatest_func(void *data) | |||
353 | else if (thread->type == DMA_PQ) | 615 | else if (thread->type == DMA_PQ) |
354 | align = dev->pq_align; | 616 | align = dev->pq_align; |
355 | 617 | ||
356 | if (1 << align > test_buf_size) { | 618 | if (1 << align > params->buf_size) { |
357 | pr_err("%u-byte buffer too small for %d-byte alignment\n", | 619 | pr_err("%u-byte buffer too small for %d-byte alignment\n", |
358 | test_buf_size, 1 << align); | 620 | params->buf_size, 1 << align); |
359 | break; | 621 | break; |
360 | } | 622 | } |
361 | 623 | ||
362 | len = dmatest_random() % test_buf_size + 1; | 624 | len = dmatest_random() % params->buf_size + 1; |
363 | len = (len >> align) << align; | 625 | len = (len >> align) << align; |
364 | if (!len) | 626 | if (!len) |
365 | len = 1 << align; | 627 | len = 1 << align; |
366 | src_off = dmatest_random() % (test_buf_size - len + 1); | 628 | src_off = dmatest_random() % (params->buf_size - len + 1); |
367 | dst_off = dmatest_random() % (test_buf_size - len + 1); | 629 | dst_off = dmatest_random() % (params->buf_size - len + 1); |
368 | 630 | ||
369 | src_off = (src_off >> align) << align; | 631 | src_off = (src_off >> align) << align; |
370 | dst_off = (dst_off >> align) << align; | 632 | dst_off = (dst_off >> align) << align; |
371 | 633 | ||
372 | dmatest_init_srcs(thread->srcs, src_off, len); | 634 | dmatest_init_srcs(thread->srcs, src_off, len, params->buf_size); |
373 | dmatest_init_dsts(thread->dsts, dst_off, len); | 635 | dmatest_init_dsts(thread->dsts, dst_off, len, params->buf_size); |
374 | 636 | ||
375 | for (i = 0; i < src_cnt; i++) { | 637 | for (i = 0; i < src_cnt; i++) { |
376 | u8 *buf = thread->srcs[i] + src_off; | 638 | u8 *buf = thread->srcs[i] + src_off; |
@@ -380,10 +642,10 @@ static int dmatest_func(void *data) | |||
380 | ret = dma_mapping_error(dev->dev, dma_srcs[i]); | 642 | ret = dma_mapping_error(dev->dev, dma_srcs[i]); |
381 | if (ret) { | 643 | if (ret) { |
382 | unmap_src(dev->dev, dma_srcs, len, i); | 644 | unmap_src(dev->dev, dma_srcs, len, i); |
383 | pr_warn("%s: #%u: mapping error %d with " | 645 | thread_result_add(info, result, |
384 | "src_off=0x%x len=0x%x\n", | 646 | DMATEST_ET_MAP_SRC, |
385 | thread_name, total_tests - 1, ret, | 647 | total_tests, src_off, dst_off, |
386 | src_off, len); | 648 | len, ret); |
387 | failed_tests++; | 649 | failed_tests++; |
388 | continue; | 650 | continue; |
389 | } | 651 | } |
@@ -391,16 +653,17 @@ static int dmatest_func(void *data) | |||
391 | /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */ | 653 | /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */ |
392 | for (i = 0; i < dst_cnt; i++) { | 654 | for (i = 0; i < dst_cnt; i++) { |
393 | dma_dsts[i] = dma_map_single(dev->dev, thread->dsts[i], | 655 | dma_dsts[i] = dma_map_single(dev->dev, thread->dsts[i], |
394 | test_buf_size, | 656 | params->buf_size, |
395 | DMA_BIDIRECTIONAL); | 657 | DMA_BIDIRECTIONAL); |
396 | ret = dma_mapping_error(dev->dev, dma_dsts[i]); | 658 | ret = dma_mapping_error(dev->dev, dma_dsts[i]); |
397 | if (ret) { | 659 | if (ret) { |
398 | unmap_src(dev->dev, dma_srcs, len, src_cnt); | 660 | unmap_src(dev->dev, dma_srcs, len, src_cnt); |
399 | unmap_dst(dev->dev, dma_dsts, test_buf_size, i); | 661 | unmap_dst(dev->dev, dma_dsts, params->buf_size, |
400 | pr_warn("%s: #%u: mapping error %d with " | 662 | i); |
401 | "dst_off=0x%x len=0x%x\n", | 663 | thread_result_add(info, result, |
402 | thread_name, total_tests - 1, ret, | 664 | DMATEST_ET_MAP_DST, |
403 | dst_off, test_buf_size); | 665 | total_tests, src_off, dst_off, |
666 | len, ret); | ||
404 | failed_tests++; | 667 | failed_tests++; |
405 | continue; | 668 | continue; |
406 | } | 669 | } |
@@ -428,11 +691,11 @@ static int dmatest_func(void *data) | |||
428 | 691 | ||
429 | if (!tx) { | 692 | if (!tx) { |
430 | unmap_src(dev->dev, dma_srcs, len, src_cnt); | 693 | unmap_src(dev->dev, dma_srcs, len, src_cnt); |
431 | unmap_dst(dev->dev, dma_dsts, test_buf_size, dst_cnt); | 694 | unmap_dst(dev->dev, dma_dsts, params->buf_size, |
432 | pr_warning("%s: #%u: prep error with src_off=0x%x " | 695 | dst_cnt); |
433 | "dst_off=0x%x len=0x%x\n", | 696 | thread_result_add(info, result, DMATEST_ET_PREP, |
434 | thread_name, total_tests - 1, | 697 | total_tests, src_off, dst_off, |
435 | src_off, dst_off, len); | 698 | len, 0); |
436 | msleep(100); | 699 | msleep(100); |
437 | failed_tests++; | 700 | failed_tests++; |
438 | continue; | 701 | continue; |
@@ -444,18 +707,18 @@ static int dmatest_func(void *data) | |||
444 | cookie = tx->tx_submit(tx); | 707 | cookie = tx->tx_submit(tx); |
445 | 708 | ||
446 | if (dma_submit_error(cookie)) { | 709 | if (dma_submit_error(cookie)) { |
447 | pr_warning("%s: #%u: submit error %d with src_off=0x%x " | 710 | thread_result_add(info, result, DMATEST_ET_SUBMIT, |
448 | "dst_off=0x%x len=0x%x\n", | 711 | total_tests, src_off, dst_off, |
449 | thread_name, total_tests - 1, cookie, | 712 | len, cookie); |
450 | src_off, dst_off, len); | ||
451 | msleep(100); | 713 | msleep(100); |
452 | failed_tests++; | 714 | failed_tests++; |
453 | continue; | 715 | continue; |
454 | } | 716 | } |
455 | dma_async_issue_pending(chan); | 717 | dma_async_issue_pending(chan); |
456 | 718 | ||
457 | wait_event_freezable_timeout(done_wait, done.done, | 719 | wait_event_freezable_timeout(done_wait, |
458 | msecs_to_jiffies(timeout)); | 720 | done.done || kthread_should_stop(), |
721 | msecs_to_jiffies(params->timeout)); | ||
459 | 722 | ||
460 | status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); | 723 | status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); |
461 | 724 | ||
@@ -468,56 +731,57 @@ static int dmatest_func(void *data) | |||
468 | * free it this time?" dancing. For now, just | 731 | * free it this time?" dancing. For now, just |
469 | * leave it dangling. | 732 | * leave it dangling. |
470 | */ | 733 | */ |
471 | pr_warning("%s: #%u: test timed out\n", | 734 | thread_result_add(info, result, DMATEST_ET_TIMEOUT, |
472 | thread_name, total_tests - 1); | 735 | total_tests, src_off, dst_off, |
736 | len, 0); | ||
473 | failed_tests++; | 737 | failed_tests++; |
474 | continue; | 738 | continue; |
475 | } else if (status != DMA_SUCCESS) { | 739 | } else if (status != DMA_SUCCESS) { |
476 | pr_warning("%s: #%u: got completion callback," | 740 | enum dmatest_error_type type = (status == DMA_ERROR) ? |
477 | " but status is \'%s\'\n", | 741 | DMATEST_ET_DMA_ERROR : DMATEST_ET_DMA_IN_PROGRESS; |
478 | thread_name, total_tests - 1, | 742 | thread_result_add(info, result, type, |
479 | status == DMA_ERROR ? "error" : "in progress"); | 743 | total_tests, src_off, dst_off, |
744 | len, status); | ||
480 | failed_tests++; | 745 | failed_tests++; |
481 | continue; | 746 | continue; |
482 | } | 747 | } |
483 | 748 | ||
484 | /* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */ | 749 | /* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */ |
485 | unmap_dst(dev->dev, dma_dsts, test_buf_size, dst_cnt); | 750 | unmap_dst(dev->dev, dma_dsts, params->buf_size, dst_cnt); |
486 | 751 | ||
487 | error_count = 0; | 752 | error_count = 0; |
488 | 753 | ||
489 | pr_debug("%s: verifying source buffer...\n", thread_name); | 754 | pr_debug("%s: verifying source buffer...\n", thread_name); |
490 | error_count += dmatest_verify(thread->srcs, 0, src_off, | 755 | error_count += verify_result_add(info, result, total_tests, |
756 | src_off, dst_off, len, thread->srcs, -1, | ||
491 | 0, PATTERN_SRC, true); | 757 | 0, PATTERN_SRC, true); |
492 | error_count += dmatest_verify(thread->srcs, src_off, | 758 | error_count += verify_result_add(info, result, total_tests, |
493 | src_off + len, src_off, | 759 | src_off, dst_off, len, thread->srcs, 0, |
494 | PATTERN_SRC | PATTERN_COPY, true); | 760 | src_off, PATTERN_SRC | PATTERN_COPY, true); |
495 | error_count += dmatest_verify(thread->srcs, src_off + len, | 761 | error_count += verify_result_add(info, result, total_tests, |
496 | test_buf_size, src_off + len, | 762 | src_off, dst_off, len, thread->srcs, 1, |
497 | PATTERN_SRC, true); | 763 | src_off + len, PATTERN_SRC, true); |
498 | 764 | ||
499 | pr_debug("%s: verifying dest buffer...\n", | 765 | pr_debug("%s: verifying dest buffer...\n", thread_name); |
500 | thread->task->comm); | 766 | error_count += verify_result_add(info, result, total_tests, |
501 | error_count += dmatest_verify(thread->dsts, 0, dst_off, | 767 | src_off, dst_off, len, thread->dsts, -1, |
502 | 0, PATTERN_DST, false); | 768 | 0, PATTERN_DST, false); |
503 | error_count += dmatest_verify(thread->dsts, dst_off, | 769 | error_count += verify_result_add(info, result, total_tests, |
504 | dst_off + len, src_off, | 770 | src_off, dst_off, len, thread->dsts, 0, |
505 | PATTERN_SRC | PATTERN_COPY, false); | 771 | src_off, PATTERN_SRC | PATTERN_COPY, false); |
506 | error_count += dmatest_verify(thread->dsts, dst_off + len, | 772 | error_count += verify_result_add(info, result, total_tests, |
507 | test_buf_size, dst_off + len, | 773 | src_off, dst_off, len, thread->dsts, 1, |
508 | PATTERN_DST, false); | 774 | dst_off + len, PATTERN_DST, false); |
509 | 775 | ||
510 | if (error_count) { | 776 | if (error_count) { |
511 | pr_warning("%s: #%u: %u errors with " | 777 | thread_result_add(info, result, DMATEST_ET_VERIFY, |
512 | "src_off=0x%x dst_off=0x%x len=0x%x\n", | 778 | total_tests, src_off, dst_off, |
513 | thread_name, total_tests - 1, error_count, | 779 | len, error_count); |
514 | src_off, dst_off, len); | ||
515 | failed_tests++; | 780 | failed_tests++; |
516 | } else { | 781 | } else { |
517 | pr_debug("%s: #%u: No errors with " | 782 | thread_result_add(info, result, DMATEST_ET_OK, |
518 | "src_off=0x%x dst_off=0x%x len=0x%x\n", | 783 | total_tests, src_off, dst_off, |
519 | thread_name, total_tests - 1, | 784 | len, 0); |
520 | src_off, dst_off, len); | ||
521 | } | 785 | } |
522 | } | 786 | } |
523 | 787 | ||
@@ -532,6 +796,8 @@ err_dsts: | |||
532 | err_srcbuf: | 796 | err_srcbuf: |
533 | kfree(thread->srcs); | 797 | kfree(thread->srcs); |
534 | err_srcs: | 798 | err_srcs: |
799 | kfree(pq_coefs); | ||
800 | err_thread_type: | ||
535 | pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", | 801 | pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", |
536 | thread_name, total_tests, failed_tests, ret); | 802 | thread_name, total_tests, failed_tests, ret); |
537 | 803 | ||
@@ -539,7 +805,9 @@ err_srcs: | |||
539 | if (ret) | 805 | if (ret) |
540 | dmaengine_terminate_all(chan); | 806 | dmaengine_terminate_all(chan); |
541 | 807 | ||
542 | if (iterations > 0) | 808 | thread->done = true; |
809 | |||
810 | if (params->iterations > 0) | ||
543 | while (!kthread_should_stop()) { | 811 | while (!kthread_should_stop()) { |
544 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); | 812 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); |
545 | interruptible_sleep_on(&wait_dmatest_exit); | 813 | interruptible_sleep_on(&wait_dmatest_exit); |
@@ -568,8 +836,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) | |||
568 | kfree(dtc); | 836 | kfree(dtc); |
569 | } | 837 | } |
570 | 838 | ||
571 | static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_type type) | 839 | static int dmatest_add_threads(struct dmatest_info *info, |
840 | struct dmatest_chan *dtc, enum dma_transaction_type type) | ||
572 | { | 841 | { |
842 | struct dmatest_params *params = &info->params; | ||
573 | struct dmatest_thread *thread; | 843 | struct dmatest_thread *thread; |
574 | struct dma_chan *chan = dtc->chan; | 844 | struct dma_chan *chan = dtc->chan; |
575 | char *op; | 845 | char *op; |
@@ -584,7 +854,7 @@ static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_ty | |||
584 | else | 854 | else |
585 | return -EINVAL; | 855 | return -EINVAL; |
586 | 856 | ||
587 | for (i = 0; i < threads_per_chan; i++) { | 857 | for (i = 0; i < params->threads_per_chan; i++) { |
588 | thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); | 858 | thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); |
589 | if (!thread) { | 859 | if (!thread) { |
590 | pr_warning("dmatest: No memory for %s-%s%u\n", | 860 | pr_warning("dmatest: No memory for %s-%s%u\n", |
@@ -592,6 +862,7 @@ static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_ty | |||
592 | 862 | ||
593 | break; | 863 | break; |
594 | } | 864 | } |
865 | thread->info = info; | ||
595 | thread->chan = dtc->chan; | 866 | thread->chan = dtc->chan; |
596 | thread->type = type; | 867 | thread->type = type; |
597 | smp_wmb(); | 868 | smp_wmb(); |
@@ -612,7 +883,8 @@ static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_ty | |||
612 | return i; | 883 | return i; |
613 | } | 884 | } |
614 | 885 | ||
615 | static int dmatest_add_channel(struct dma_chan *chan) | 886 | static int dmatest_add_channel(struct dmatest_info *info, |
887 | struct dma_chan *chan) | ||
616 | { | 888 | { |
617 | struct dmatest_chan *dtc; | 889 | struct dmatest_chan *dtc; |
618 | struct dma_device *dma_dev = chan->device; | 890 | struct dma_device *dma_dev = chan->device; |
@@ -629,75 +901,418 @@ static int dmatest_add_channel(struct dma_chan *chan) | |||
629 | INIT_LIST_HEAD(&dtc->threads); | 901 | INIT_LIST_HEAD(&dtc->threads); |
630 | 902 | ||
631 | if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { | 903 | if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { |
632 | cnt = dmatest_add_threads(dtc, DMA_MEMCPY); | 904 | cnt = dmatest_add_threads(info, dtc, DMA_MEMCPY); |
633 | thread_count += cnt > 0 ? cnt : 0; | 905 | thread_count += cnt > 0 ? cnt : 0; |
634 | } | 906 | } |
635 | if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { | 907 | if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { |
636 | cnt = dmatest_add_threads(dtc, DMA_XOR); | 908 | cnt = dmatest_add_threads(info, dtc, DMA_XOR); |
637 | thread_count += cnt > 0 ? cnt : 0; | 909 | thread_count += cnt > 0 ? cnt : 0; |
638 | } | 910 | } |
639 | if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { | 911 | if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { |
640 | cnt = dmatest_add_threads(dtc, DMA_PQ); | 912 | cnt = dmatest_add_threads(info, dtc, DMA_PQ); |
641 | thread_count += cnt > 0 ? cnt : 0; | 913 | thread_count += cnt > 0 ? cnt : 0; |
642 | } | 914 | } |
643 | 915 | ||
644 | pr_info("dmatest: Started %u threads using %s\n", | 916 | pr_info("dmatest: Started %u threads using %s\n", |
645 | thread_count, dma_chan_name(chan)); | 917 | thread_count, dma_chan_name(chan)); |
646 | 918 | ||
647 | list_add_tail(&dtc->node, &dmatest_channels); | 919 | list_add_tail(&dtc->node, &info->channels); |
648 | nr_channels++; | 920 | info->nr_channels++; |
649 | 921 | ||
650 | return 0; | 922 | return 0; |
651 | } | 923 | } |
652 | 924 | ||
653 | static bool filter(struct dma_chan *chan, void *param) | 925 | static bool filter(struct dma_chan *chan, void *param) |
654 | { | 926 | { |
655 | if (!dmatest_match_channel(chan) || !dmatest_match_device(chan->device)) | 927 | struct dmatest_params *params = param; |
928 | |||
929 | if (!dmatest_match_channel(params, chan) || | ||
930 | !dmatest_match_device(params, chan->device)) | ||
656 | return false; | 931 | return false; |
657 | else | 932 | else |
658 | return true; | 933 | return true; |
659 | } | 934 | } |
660 | 935 | ||
661 | static int __init dmatest_init(void) | 936 | static int __run_threaded_test(struct dmatest_info *info) |
662 | { | 937 | { |
663 | dma_cap_mask_t mask; | 938 | dma_cap_mask_t mask; |
664 | struct dma_chan *chan; | 939 | struct dma_chan *chan; |
940 | struct dmatest_params *params = &info->params; | ||
665 | int err = 0; | 941 | int err = 0; |
666 | 942 | ||
667 | dma_cap_zero(mask); | 943 | dma_cap_zero(mask); |
668 | dma_cap_set(DMA_MEMCPY, mask); | 944 | dma_cap_set(DMA_MEMCPY, mask); |
669 | for (;;) { | 945 | for (;;) { |
670 | chan = dma_request_channel(mask, filter, NULL); | 946 | chan = dma_request_channel(mask, filter, params); |
671 | if (chan) { | 947 | if (chan) { |
672 | err = dmatest_add_channel(chan); | 948 | err = dmatest_add_channel(info, chan); |
673 | if (err) { | 949 | if (err) { |
674 | dma_release_channel(chan); | 950 | dma_release_channel(chan); |
675 | break; /* add_channel failed, punt */ | 951 | break; /* add_channel failed, punt */ |
676 | } | 952 | } |
677 | } else | 953 | } else |
678 | break; /* no more channels available */ | 954 | break; /* no more channels available */ |
679 | if (max_channels && nr_channels >= max_channels) | 955 | if (params->max_channels && |
956 | info->nr_channels >= params->max_channels) | ||
680 | break; /* we have all we need */ | 957 | break; /* we have all we need */ |
681 | } | 958 | } |
682 | |||
683 | return err; | 959 | return err; |
684 | } | 960 | } |
685 | /* when compiled-in wait for drivers to load first */ | ||
686 | late_initcall(dmatest_init); | ||
687 | 961 | ||
688 | static void __exit dmatest_exit(void) | 962 | #ifndef MODULE |
963 | static int run_threaded_test(struct dmatest_info *info) | ||
964 | { | ||
965 | int ret; | ||
966 | |||
967 | mutex_lock(&info->lock); | ||
968 | ret = __run_threaded_test(info); | ||
969 | mutex_unlock(&info->lock); | ||
970 | return ret; | ||
971 | } | ||
972 | #endif | ||
973 | |||
974 | static void __stop_threaded_test(struct dmatest_info *info) | ||
689 | { | 975 | { |
690 | struct dmatest_chan *dtc, *_dtc; | 976 | struct dmatest_chan *dtc, *_dtc; |
691 | struct dma_chan *chan; | 977 | struct dma_chan *chan; |
692 | 978 | ||
693 | list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) { | 979 | list_for_each_entry_safe(dtc, _dtc, &info->channels, node) { |
694 | list_del(&dtc->node); | 980 | list_del(&dtc->node); |
695 | chan = dtc->chan; | 981 | chan = dtc->chan; |
696 | dmatest_cleanup_channel(dtc); | 982 | dmatest_cleanup_channel(dtc); |
697 | pr_debug("dmatest: dropped channel %s\n", | 983 | pr_debug("dmatest: dropped channel %s\n", dma_chan_name(chan)); |
698 | dma_chan_name(chan)); | ||
699 | dma_release_channel(chan); | 984 | dma_release_channel(chan); |
700 | } | 985 | } |
986 | |||
987 | info->nr_channels = 0; | ||
988 | } | ||
989 | |||
990 | static void stop_threaded_test(struct dmatest_info *info) | ||
991 | { | ||
992 | mutex_lock(&info->lock); | ||
993 | __stop_threaded_test(info); | ||
994 | mutex_unlock(&info->lock); | ||
995 | } | ||
996 | |||
997 | static int __restart_threaded_test(struct dmatest_info *info, bool run) | ||
998 | { | ||
999 | struct dmatest_params *params = &info->params; | ||
1000 | int ret; | ||
1001 | |||
1002 | /* Stop any running test first */ | ||
1003 | __stop_threaded_test(info); | ||
1004 | |||
1005 | if (run == false) | ||
1006 | return 0; | ||
1007 | |||
1008 | /* Clear results from previous run */ | ||
1009 | result_free(info, NULL); | ||
1010 | |||
1011 | /* Copy test parameters */ | ||
1012 | memcpy(params, &info->dbgfs_params, sizeof(*params)); | ||
1013 | |||
1014 | /* Run test with new parameters */ | ||
1015 | ret = __run_threaded_test(info); | ||
1016 | if (ret) { | ||
1017 | __stop_threaded_test(info); | ||
1018 | pr_err("dmatest: Can't run test\n"); | ||
1019 | } | ||
1020 | |||
1021 | return ret; | ||
1022 | } | ||
1023 | |||
1024 | static ssize_t dtf_write_string(void *to, size_t available, loff_t *ppos, | ||
1025 | const void __user *from, size_t count) | ||
1026 | { | ||
1027 | char tmp[20]; | ||
1028 | ssize_t len; | ||
1029 | |||
1030 | len = simple_write_to_buffer(tmp, sizeof(tmp) - 1, ppos, from, count); | ||
1031 | if (len >= 0) { | ||
1032 | tmp[len] = '\0'; | ||
1033 | strlcpy(to, strim(tmp), available); | ||
1034 | } | ||
1035 | |||
1036 | return len; | ||
1037 | } | ||
1038 | |||
1039 | static ssize_t dtf_read_channel(struct file *file, char __user *buf, | ||
1040 | size_t count, loff_t *ppos) | ||
1041 | { | ||
1042 | struct dmatest_info *info = file->private_data; | ||
1043 | return simple_read_from_buffer(buf, count, ppos, | ||
1044 | info->dbgfs_params.channel, | ||
1045 | strlen(info->dbgfs_params.channel)); | ||
1046 | } | ||
1047 | |||
1048 | static ssize_t dtf_write_channel(struct file *file, const char __user *buf, | ||
1049 | size_t size, loff_t *ppos) | ||
1050 | { | ||
1051 | struct dmatest_info *info = file->private_data; | ||
1052 | return dtf_write_string(info->dbgfs_params.channel, | ||
1053 | sizeof(info->dbgfs_params.channel), | ||
1054 | ppos, buf, size); | ||
1055 | } | ||
1056 | |||
1057 | static const struct file_operations dtf_channel_fops = { | ||
1058 | .read = dtf_read_channel, | ||
1059 | .write = dtf_write_channel, | ||
1060 | .open = simple_open, | ||
1061 | .llseek = default_llseek, | ||
1062 | }; | ||
1063 | |||
1064 | static ssize_t dtf_read_device(struct file *file, char __user *buf, | ||
1065 | size_t count, loff_t *ppos) | ||
1066 | { | ||
1067 | struct dmatest_info *info = file->private_data; | ||
1068 | return simple_read_from_buffer(buf, count, ppos, | ||
1069 | info->dbgfs_params.device, | ||
1070 | strlen(info->dbgfs_params.device)); | ||
1071 | } | ||
1072 | |||
1073 | static ssize_t dtf_write_device(struct file *file, const char __user *buf, | ||
1074 | size_t size, loff_t *ppos) | ||
1075 | { | ||
1076 | struct dmatest_info *info = file->private_data; | ||
1077 | return dtf_write_string(info->dbgfs_params.device, | ||
1078 | sizeof(info->dbgfs_params.device), | ||
1079 | ppos, buf, size); | ||
1080 | } | ||
1081 | |||
1082 | static const struct file_operations dtf_device_fops = { | ||
1083 | .read = dtf_read_device, | ||
1084 | .write = dtf_write_device, | ||
1085 | .open = simple_open, | ||
1086 | .llseek = default_llseek, | ||
1087 | }; | ||
1088 | |||
1089 | static ssize_t dtf_read_run(struct file *file, char __user *user_buf, | ||
1090 | size_t count, loff_t *ppos) | ||
1091 | { | ||
1092 | struct dmatest_info *info = file->private_data; | ||
1093 | char buf[3]; | ||
1094 | struct dmatest_chan *dtc; | ||
1095 | bool alive = false; | ||
1096 | |||
1097 | mutex_lock(&info->lock); | ||
1098 | list_for_each_entry(dtc, &info->channels, node) { | ||
1099 | struct dmatest_thread *thread; | ||
1100 | |||
1101 | list_for_each_entry(thread, &dtc->threads, node) { | ||
1102 | if (!thread->done) { | ||
1103 | alive = true; | ||
1104 | break; | ||
1105 | } | ||
1106 | } | ||
1107 | } | ||
1108 | |||
1109 | if (alive) { | ||
1110 | buf[0] = 'Y'; | ||
1111 | } else { | ||
1112 | __stop_threaded_test(info); | ||
1113 | buf[0] = 'N'; | ||
1114 | } | ||
1115 | |||
1116 | mutex_unlock(&info->lock); | ||
1117 | buf[1] = '\n'; | ||
1118 | buf[2] = 0x00; | ||
1119 | return simple_read_from_buffer(user_buf, count, ppos, buf, 2); | ||
1120 | } | ||
1121 | |||
1122 | static ssize_t dtf_write_run(struct file *file, const char __user *user_buf, | ||
1123 | size_t count, loff_t *ppos) | ||
1124 | { | ||
1125 | struct dmatest_info *info = file->private_data; | ||
1126 | char buf[16]; | ||
1127 | bool bv; | ||
1128 | int ret = 0; | ||
1129 | |||
1130 | if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1)))) | ||
1131 | return -EFAULT; | ||
1132 | |||
1133 | if (strtobool(buf, &bv) == 0) { | ||
1134 | mutex_lock(&info->lock); | ||
1135 | ret = __restart_threaded_test(info, bv); | ||
1136 | mutex_unlock(&info->lock); | ||
1137 | } | ||
1138 | |||
1139 | return ret ? ret : count; | ||
1140 | } | ||
1141 | |||
1142 | static const struct file_operations dtf_run_fops = { | ||
1143 | .read = dtf_read_run, | ||
1144 | .write = dtf_write_run, | ||
1145 | .open = simple_open, | ||
1146 | .llseek = default_llseek, | ||
1147 | }; | ||
1148 | |||
1149 | static int dtf_results_show(struct seq_file *sf, void *data) | ||
1150 | { | ||
1151 | struct dmatest_info *info = sf->private; | ||
1152 | struct dmatest_result *result; | ||
1153 | struct dmatest_thread_result *tr; | ||
1154 | unsigned int i; | ||
1155 | |||
1156 | mutex_lock(&info->results_lock); | ||
1157 | list_for_each_entry(result, &info->results, node) { | ||
1158 | list_for_each_entry(tr, &result->results, node) { | ||
1159 | seq_printf(sf, "%s\n", | ||
1160 | thread_result_get(result->name, tr)); | ||
1161 | if (tr->type == DMATEST_ET_VERIFY_BUF) { | ||
1162 | for (i = 0; i < tr->vr->error_count; i++) { | ||
1163 | seq_printf(sf, "\t%s\n", | ||
1164 | verify_result_get_one(tr->vr, i)); | ||
1165 | } | ||
1166 | } | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | mutex_unlock(&info->results_lock); | ||
1171 | return 0; | ||
1172 | } | ||
1173 | |||
1174 | static int dtf_results_open(struct inode *inode, struct file *file) | ||
1175 | { | ||
1176 | return single_open(file, dtf_results_show, inode->i_private); | ||
1177 | } | ||
1178 | |||
1179 | static const struct file_operations dtf_results_fops = { | ||
1180 | .open = dtf_results_open, | ||
1181 | .read = seq_read, | ||
1182 | .llseek = seq_lseek, | ||
1183 | .release = single_release, | ||
1184 | }; | ||
1185 | |||
1186 | static int dmatest_register_dbgfs(struct dmatest_info *info) | ||
1187 | { | ||
1188 | struct dentry *d; | ||
1189 | struct dmatest_params *params = &info->dbgfs_params; | ||
1190 | int ret = -ENOMEM; | ||
1191 | |||
1192 | d = debugfs_create_dir("dmatest", NULL); | ||
1193 | if (IS_ERR(d)) | ||
1194 | return PTR_ERR(d); | ||
1195 | if (!d) | ||
1196 | goto err_root; | ||
1197 | |||
1198 | info->root = d; | ||
1199 | |||
1200 | /* Copy initial values */ | ||
1201 | memcpy(params, &info->params, sizeof(*params)); | ||
1202 | |||
1203 | /* Test parameters */ | ||
1204 | |||
1205 | d = debugfs_create_u32("test_buf_size", S_IWUSR | S_IRUGO, info->root, | ||
1206 | (u32 *)¶ms->buf_size); | ||
1207 | if (IS_ERR_OR_NULL(d)) | ||
1208 | goto err_node; | ||
1209 | |||
1210 | d = debugfs_create_file("channel", S_IRUGO | S_IWUSR, info->root, | ||
1211 | info, &dtf_channel_fops); | ||
1212 | if (IS_ERR_OR_NULL(d)) | ||
1213 | goto err_node; | ||
1214 | |||
1215 | d = debugfs_create_file("device", S_IRUGO | S_IWUSR, info->root, | ||
1216 | info, &dtf_device_fops); | ||
1217 | if (IS_ERR_OR_NULL(d)) | ||
1218 | goto err_node; | ||
1219 | |||
1220 | d = debugfs_create_u32("threads_per_chan", S_IWUSR | S_IRUGO, info->root, | ||
1221 | (u32 *)¶ms->threads_per_chan); | ||
1222 | if (IS_ERR_OR_NULL(d)) | ||
1223 | goto err_node; | ||
1224 | |||
1225 | d = debugfs_create_u32("max_channels", S_IWUSR | S_IRUGO, info->root, | ||
1226 | (u32 *)¶ms->max_channels); | ||
1227 | if (IS_ERR_OR_NULL(d)) | ||
1228 | goto err_node; | ||
1229 | |||
1230 | d = debugfs_create_u32("iterations", S_IWUSR | S_IRUGO, info->root, | ||
1231 | (u32 *)¶ms->iterations); | ||
1232 | if (IS_ERR_OR_NULL(d)) | ||
1233 | goto err_node; | ||
1234 | |||
1235 | d = debugfs_create_u32("xor_sources", S_IWUSR | S_IRUGO, info->root, | ||
1236 | (u32 *)¶ms->xor_sources); | ||
1237 | if (IS_ERR_OR_NULL(d)) | ||
1238 | goto err_node; | ||
1239 | |||
1240 | d = debugfs_create_u32("pq_sources", S_IWUSR | S_IRUGO, info->root, | ||
1241 | (u32 *)¶ms->pq_sources); | ||
1242 | if (IS_ERR_OR_NULL(d)) | ||
1243 | goto err_node; | ||
1244 | |||
1245 | d = debugfs_create_u32("timeout", S_IWUSR | S_IRUGO, info->root, | ||
1246 | (u32 *)¶ms->timeout); | ||
1247 | if (IS_ERR_OR_NULL(d)) | ||
1248 | goto err_node; | ||
1249 | |||
1250 | /* Run or stop threaded test */ | ||
1251 | d = debugfs_create_file("run", S_IWUSR | S_IRUGO, info->root, | ||
1252 | info, &dtf_run_fops); | ||
1253 | if (IS_ERR_OR_NULL(d)) | ||
1254 | goto err_node; | ||
1255 | |||
1256 | /* Results of test in progress */ | ||
1257 | d = debugfs_create_file("results", S_IRUGO, info->root, info, | ||
1258 | &dtf_results_fops); | ||
1259 | if (IS_ERR_OR_NULL(d)) | ||
1260 | goto err_node; | ||
1261 | |||
1262 | return 0; | ||
1263 | |||
1264 | err_node: | ||
1265 | debugfs_remove_recursive(info->root); | ||
1266 | err_root: | ||
1267 | pr_err("dmatest: Failed to initialize debugfs\n"); | ||
1268 | return ret; | ||
1269 | } | ||
1270 | |||
1271 | static int __init dmatest_init(void) | ||
1272 | { | ||
1273 | struct dmatest_info *info = &test_info; | ||
1274 | struct dmatest_params *params = &info->params; | ||
1275 | int ret; | ||
1276 | |||
1277 | memset(info, 0, sizeof(*info)); | ||
1278 | |||
1279 | mutex_init(&info->lock); | ||
1280 | INIT_LIST_HEAD(&info->channels); | ||
1281 | |||
1282 | mutex_init(&info->results_lock); | ||
1283 | INIT_LIST_HEAD(&info->results); | ||
1284 | |||
1285 | /* Set default parameters */ | ||
1286 | params->buf_size = test_buf_size; | ||
1287 | strlcpy(params->channel, test_channel, sizeof(params->channel)); | ||
1288 | strlcpy(params->device, test_device, sizeof(params->device)); | ||
1289 | params->threads_per_chan = threads_per_chan; | ||
1290 | params->max_channels = max_channels; | ||
1291 | params->iterations = iterations; | ||
1292 | params->xor_sources = xor_sources; | ||
1293 | params->pq_sources = pq_sources; | ||
1294 | params->timeout = timeout; | ||
1295 | |||
1296 | ret = dmatest_register_dbgfs(info); | ||
1297 | if (ret) | ||
1298 | return ret; | ||
1299 | |||
1300 | #ifdef MODULE | ||
1301 | return 0; | ||
1302 | #else | ||
1303 | return run_threaded_test(info); | ||
1304 | #endif | ||
1305 | } | ||
1306 | /* when compiled-in wait for drivers to load first */ | ||
1307 | late_initcall(dmatest_init); | ||
1308 | |||
1309 | static void __exit dmatest_exit(void) | ||
1310 | { | ||
1311 | struct dmatest_info *info = &test_info; | ||
1312 | |||
1313 | debugfs_remove_recursive(info->root); | ||
1314 | stop_threaded_test(info); | ||
1315 | result_free(info, NULL); | ||
701 | } | 1316 | } |
702 | module_exit(dmatest_exit); | 1317 | module_exit(dmatest_exit); |
703 | 1318 | ||