diff options
Diffstat (limited to 'drivers/dma/dmatest.c')
-rw-r--r-- | drivers/dma/dmatest.c | 129 |
1 files changed, 49 insertions, 80 deletions
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index ed9636bfb54a..3603f1ea5b28 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c | |||
@@ -35,7 +35,7 @@ MODULE_PARM_DESC(threads_per_chan, | |||
35 | 35 | ||
36 | static unsigned int max_channels; | 36 | static unsigned int max_channels; |
37 | module_param(max_channels, uint, S_IRUGO); | 37 | module_param(max_channels, uint, S_IRUGO); |
38 | MODULE_PARM_DESC(nr_channels, | 38 | MODULE_PARM_DESC(max_channels, |
39 | "Maximum number of channels to use (default: all)"); | 39 | "Maximum number of channels to use (default: all)"); |
40 | 40 | ||
41 | /* | 41 | /* |
@@ -71,7 +71,7 @@ struct dmatest_chan { | |||
71 | 71 | ||
72 | /* | 72 | /* |
73 | * These are protected by dma_list_mutex since they're only used by | 73 | * These are protected by dma_list_mutex since they're only used by |
74 | * the DMA client event callback | 74 | * the DMA filter function callback |
75 | */ | 75 | */ |
76 | static LIST_HEAD(dmatest_channels); | 76 | static LIST_HEAD(dmatest_channels); |
77 | static unsigned int nr_channels; | 77 | static unsigned int nr_channels; |
@@ -80,7 +80,7 @@ static bool dmatest_match_channel(struct dma_chan *chan) | |||
80 | { | 80 | { |
81 | if (test_channel[0] == '\0') | 81 | if (test_channel[0] == '\0') |
82 | return true; | 82 | return true; |
83 | return strcmp(dev_name(&chan->dev), test_channel) == 0; | 83 | return strcmp(dma_chan_name(chan), test_channel) == 0; |
84 | } | 84 | } |
85 | 85 | ||
86 | static bool dmatest_match_device(struct dma_device *device) | 86 | static bool dmatest_match_device(struct dma_device *device) |
@@ -215,7 +215,6 @@ static int dmatest_func(void *data) | |||
215 | 215 | ||
216 | smp_rmb(); | 216 | smp_rmb(); |
217 | chan = thread->chan; | 217 | chan = thread->chan; |
218 | dma_chan_get(chan); | ||
219 | 218 | ||
220 | while (!kthread_should_stop()) { | 219 | while (!kthread_should_stop()) { |
221 | total_tests++; | 220 | total_tests++; |
@@ -293,7 +292,6 @@ static int dmatest_func(void *data) | |||
293 | } | 292 | } |
294 | 293 | ||
295 | ret = 0; | 294 | ret = 0; |
296 | dma_chan_put(chan); | ||
297 | kfree(thread->dstbuf); | 295 | kfree(thread->dstbuf); |
298 | err_dstbuf: | 296 | err_dstbuf: |
299 | kfree(thread->srcbuf); | 297 | kfree(thread->srcbuf); |
@@ -319,21 +317,16 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) | |||
319 | kfree(dtc); | 317 | kfree(dtc); |
320 | } | 318 | } |
321 | 319 | ||
322 | static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) | 320 | static int dmatest_add_channel(struct dma_chan *chan) |
323 | { | 321 | { |
324 | struct dmatest_chan *dtc; | 322 | struct dmatest_chan *dtc; |
325 | struct dmatest_thread *thread; | 323 | struct dmatest_thread *thread; |
326 | unsigned int i; | 324 | unsigned int i; |
327 | 325 | ||
328 | /* Have we already been told about this channel? */ | ||
329 | list_for_each_entry(dtc, &dmatest_channels, node) | ||
330 | if (dtc->chan == chan) | ||
331 | return DMA_DUP; | ||
332 | |||
333 | dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); | 326 | dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); |
334 | if (!dtc) { | 327 | if (!dtc) { |
335 | pr_warning("dmatest: No memory for %s\n", dev_name(&chan->dev)); | 328 | pr_warning("dmatest: No memory for %s\n", dma_chan_name(chan)); |
336 | return DMA_NAK; | 329 | return -ENOMEM; |
337 | } | 330 | } |
338 | 331 | ||
339 | dtc->chan = chan; | 332 | dtc->chan = chan; |
@@ -343,16 +336,16 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) | |||
343 | thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); | 336 | thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); |
344 | if (!thread) { | 337 | if (!thread) { |
345 | pr_warning("dmatest: No memory for %s-test%u\n", | 338 | pr_warning("dmatest: No memory for %s-test%u\n", |
346 | dev_name(&chan->dev), i); | 339 | dma_chan_name(chan), i); |
347 | break; | 340 | break; |
348 | } | 341 | } |
349 | thread->chan = dtc->chan; | 342 | thread->chan = dtc->chan; |
350 | smp_wmb(); | 343 | smp_wmb(); |
351 | thread->task = kthread_run(dmatest_func, thread, "%s-test%u", | 344 | thread->task = kthread_run(dmatest_func, thread, "%s-test%u", |
352 | dev_name(&chan->dev), i); | 345 | dma_chan_name(chan), i); |
353 | if (IS_ERR(thread->task)) { | 346 | if (IS_ERR(thread->task)) { |
354 | pr_warning("dmatest: Failed to run thread %s-test%u\n", | 347 | pr_warning("dmatest: Failed to run thread %s-test%u\n", |
355 | dev_name(&chan->dev), i); | 348 | dma_chan_name(chan), i); |
356 | kfree(thread); | 349 | kfree(thread); |
357 | break; | 350 | break; |
358 | } | 351 | } |
@@ -362,86 +355,62 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) | |||
362 | list_add_tail(&thread->node, &dtc->threads); | 355 | list_add_tail(&thread->node, &dtc->threads); |
363 | } | 356 | } |
364 | 357 | ||
365 | pr_info("dmatest: Started %u threads using %s\n", i, dev_name(&chan->dev)); | 358 | pr_info("dmatest: Started %u threads using %s\n", i, dma_chan_name(chan)); |
366 | 359 | ||
367 | list_add_tail(&dtc->node, &dmatest_channels); | 360 | list_add_tail(&dtc->node, &dmatest_channels); |
368 | nr_channels++; | 361 | nr_channels++; |
369 | 362 | ||
370 | return DMA_ACK; | 363 | return 0; |
371 | } | ||
372 | |||
373 | static enum dma_state_client dmatest_remove_channel(struct dma_chan *chan) | ||
374 | { | ||
375 | struct dmatest_chan *dtc, *_dtc; | ||
376 | |||
377 | list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) { | ||
378 | if (dtc->chan == chan) { | ||
379 | list_del(&dtc->node); | ||
380 | dmatest_cleanup_channel(dtc); | ||
381 | pr_debug("dmatest: lost channel %s\n", | ||
382 | dev_name(&chan->dev)); | ||
383 | return DMA_ACK; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | return DMA_DUP; | ||
388 | } | 364 | } |
389 | 365 | ||
390 | /* | 366 | static bool filter(struct dma_chan *chan, void *param) |
391 | * Start testing threads as new channels are assigned to us, and kill | ||
392 | * them when the channels go away. | ||
393 | * | ||
394 | * When we unregister the client, all channels are removed so this | ||
395 | * will also take care of cleaning things up when the module is | ||
396 | * unloaded. | ||
397 | */ | ||
398 | static enum dma_state_client | ||
399 | dmatest_event(struct dma_client *client, struct dma_chan *chan, | ||
400 | enum dma_state state) | ||
401 | { | 367 | { |
402 | enum dma_state_client ack = DMA_NAK; | 368 | if (!dmatest_match_channel(chan) || !dmatest_match_device(chan->device)) |
403 | 369 | return false; | |
404 | switch (state) { | 370 | else |
405 | case DMA_RESOURCE_AVAILABLE: | 371 | return true; |
406 | if (!dmatest_match_channel(chan) | ||
407 | || !dmatest_match_device(chan->device)) | ||
408 | ack = DMA_DUP; | ||
409 | else if (max_channels && nr_channels >= max_channels) | ||
410 | ack = DMA_NAK; | ||
411 | else | ||
412 | ack = dmatest_add_channel(chan); | ||
413 | break; | ||
414 | |||
415 | case DMA_RESOURCE_REMOVED: | ||
416 | ack = dmatest_remove_channel(chan); | ||
417 | break; | ||
418 | |||
419 | default: | ||
420 | pr_info("dmatest: Unhandled event %u (%s)\n", | ||
421 | state, dev_name(&chan->dev)); | ||
422 | break; | ||
423 | } | ||
424 | |||
425 | return ack; | ||
426 | } | 372 | } |
427 | 373 | ||
428 | static struct dma_client dmatest_client = { | ||
429 | .event_callback = dmatest_event, | ||
430 | }; | ||
431 | |||
432 | static int __init dmatest_init(void) | 374 | static int __init dmatest_init(void) |
433 | { | 375 | { |
434 | dma_cap_set(DMA_MEMCPY, dmatest_client.cap_mask); | 376 | dma_cap_mask_t mask; |
435 | dma_async_client_register(&dmatest_client); | 377 | struct dma_chan *chan; |
436 | dma_async_client_chan_request(&dmatest_client); | 378 | int err = 0; |
379 | |||
380 | dma_cap_zero(mask); | ||
381 | dma_cap_set(DMA_MEMCPY, mask); | ||
382 | for (;;) { | ||
383 | chan = dma_request_channel(mask, filter, NULL); | ||
384 | if (chan) { | ||
385 | err = dmatest_add_channel(chan); | ||
386 | if (err == 0) | ||
387 | continue; | ||
388 | else { | ||
389 | dma_release_channel(chan); | ||
390 | break; /* add_channel failed, punt */ | ||
391 | } | ||
392 | } else | ||
393 | break; /* no more channels available */ | ||
394 | if (max_channels && nr_channels >= max_channels) | ||
395 | break; /* we have all we need */ | ||
396 | } | ||
437 | 397 | ||
438 | return 0; | 398 | return err; |
439 | } | 399 | } |
440 | module_init(dmatest_init); | 400 | /* when compiled-in wait for drivers to load first */ |
401 | late_initcall(dmatest_init); | ||
441 | 402 | ||
442 | static void __exit dmatest_exit(void) | 403 | static void __exit dmatest_exit(void) |
443 | { | 404 | { |
444 | dma_async_client_unregister(&dmatest_client); | 405 | struct dmatest_chan *dtc, *_dtc; |
406 | |||
407 | list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) { | ||
408 | list_del(&dtc->node); | ||
409 | dmatest_cleanup_channel(dtc); | ||
410 | pr_debug("dmatest: dropped channel %s\n", | ||
411 | dma_chan_name(dtc->chan)); | ||
412 | dma_release_channel(dtc->chan); | ||
413 | } | ||
445 | } | 414 | } |
446 | module_exit(dmatest_exit); | 415 | module_exit(dmatest_exit); |
447 | 416 | ||