Loading Documentation/driver-api/dmaengine/dmatest.rst +103 −6 Original line number Diff line number Diff line Loading @@ -26,28 +26,43 @@ Part 2 - When dmatest is built as a module Example of usage:: % modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 run=1 % modprobe dmatest timeout=2000 iterations=1 channel=dma0chan0 run=1 ...or:: % modprobe dmatest % echo dma0chan0 > /sys/module/dmatest/parameters/channel % echo 2000 > /sys/module/dmatest/parameters/timeout % echo 1 > /sys/module/dmatest/parameters/iterations % echo dma0chan0 > /sys/module/dmatest/parameters/channel % echo 1 > /sys/module/dmatest/parameters/run ...or on the kernel command line:: dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1 dmatest.run=1 dmatest.timeout=2000 dmatest.iterations=1 dmatest.channel=dma0chan0 dmatest.run=1 Example of multi-channel test usage: % modprobe dmatest % echo 2000 > /sys/module/dmatest/parameters/timeout % echo 1 > /sys/module/dmatest/parameters/iterations % echo dma0chan0 > /sys/module/dmatest/parameters/channel % echo dma0chan1 > /sys/module/dmatest/parameters/channel % echo dma0chan2 > /sys/module/dmatest/parameters/channel % echo 1 > /sys/module/dmatest/parameters/run Note: the channel parameter should always be the last parameter set prior to running the test (setting run=1), this is because upon setting the channel parameter, that specific channel is requested using the dmaengine and a thread is created with the existing parameters. This thread is set as pending and will be executed once run is set to 1. Any parameters set after the thread is created are not applied. .. hint:: available channel list could be extracted by running the following command:: % ls -1 /sys/class/dma/ Once started a message like "dmatest: Started 1 threads using dma0chan0" is emitted. After that only test failure messages are reported until the test stops. Once started a message like " dmatest: Added 1 threads using dma0chan0" is emitted. A thread for that specific channel is created and is now pending, the pending thread is started once run is to 1. Note that running a new test will not stop any in progress test. Loading Loading @@ -112,3 +127,85 @@ Example:: The details of a data miscompare error are also emitted, but do not follow the above format. Part 5 - Handling channel allocation ==================================== Allocating Channels ------------------- Channels are required to be configured prior to starting the test run. Attempting to run the test without configuring the channels will fail. Example:: % echo 1 > /sys/module/dmatest/parameters/run dmatest: Could not start test, no channels configured Channels are registered using the "channel" parameter. Channels can be requested by their name, once requested, the channel is registered and a pending thread is added to the test list. Example:: % echo dma0chan2 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan2 More channels can be added by repeating the example above. Reading back the channel parameter will return the name of last channel that was added successfully. Example:: % echo dma0chan1 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan1 % echo dma0chan2 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan2 % cat /sys/module/dmatest/parameters/channel dma0chan2 Another method of requesting channels is to request a channel with an empty string, Doing so will request all channels available to be tested: Example:: % echo "" > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan0 dmatest: Added 1 threads using dma0chan3 dmatest: Added 1 threads using dma0chan4 dmatest: Added 1 threads using dma0chan5 dmatest: Added 1 threads using dma0chan6 dmatest: Added 1 threads using dma0chan7 dmatest: Added 1 threads using dma0chan8 At any point during the test configuration, reading the "test_list" parameter will print the list of currently pending tests. Example:: % cat /sys/module/dmatest/parameters/test_list dmatest: 1 threads using dma0chan0 dmatest: 1 threads using dma0chan3 dmatest: 1 threads using dma0chan4 dmatest: 1 threads using dma0chan5 dmatest: 1 threads using dma0chan6 dmatest: 1 threads using dma0chan7 dmatest: 1 threads using dma0chan8 Note: Channels will have to be configured for each test run as channel configurations do not carry across to the next test run. Releasing Channels ------------------- Channels can be freed by setting run to 0. Example:: % echo dma0chan1 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan1 % cat /sys/class/dma/dma0chan1/in_use 1 % echo 0 > /sys/module/dmatest/parameters/run % cat /sys/class/dma/dma0chan1/in_use 0 Channels allocated by previous test runs are automatically freed when a new channel is requested after completing a successful test run. drivers/dma/dmatest.c +246 −53 Original line number Diff line number Diff line Loading @@ -27,11 +27,6 @@ static unsigned int test_buf_size = 16384; module_param(test_buf_size, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); static char test_channel[20]; module_param_string(channel, test_channel, sizeof(test_channel), S_IRUGO | S_IWUSR); MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); static char test_device[32]; module_param_string(device, test_device, sizeof(test_device), S_IRUGO | S_IWUSR); Loading Loading @@ -84,6 +79,14 @@ static bool verbose; module_param(verbose, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)"); static int alignment = -1; module_param(alignment, int, 0644); MODULE_PARM_DESC(alignment, "Custom data address alignment taken as 2^(alignment) (default: not used (-1))"); static unsigned int transfer_size; module_param(transfer_size, uint, 0644); MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))"); /** * struct dmatest_params - test parameters. * @buf_size: size of the memcpy test buffer Loading @@ -108,6 +111,8 @@ struct dmatest_params { int timeout; bool noverify; bool norandom; int alignment; unsigned int transfer_size; }; /** Loading Loading @@ -139,6 +144,28 @@ static bool dmatest_run; module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(run, "Run the test (default: false)"); static int dmatest_chan_set(const char *val, const struct kernel_param *kp); static int dmatest_chan_get(char *val, const struct kernel_param *kp); static const struct kernel_param_ops multi_chan_ops = { .set = dmatest_chan_set, .get = dmatest_chan_get, }; static char test_channel[20]; static struct kparam_string newchan_kps = { .string = test_channel, .maxlen = 20, }; module_param_cb(channel, &multi_chan_ops, &newchan_kps, 0644); MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); static int dmatest_test_list_get(char *val, const struct kernel_param *kp); static const struct kernel_param_ops test_list_ops = { .get = dmatest_test_list_get, }; module_param_cb(test_list, &test_list_ops, NULL, 0444); MODULE_PARM_DESC(test_list, "Print current test list"); /* Maximum amount of mismatched bytes in buffer to print */ #define MAX_ERROR_COUNT 32 Loading @@ -160,6 +187,13 @@ MODULE_PARM_DESC(run, "Run the test (default: false)"); #define PATTERN_COUNT_MASK 0x1f #define PATTERN_MEMSET_IDX 0x01 /* Fixed point arithmetic ops */ #define FIXPT_SHIFT 8 #define FIXPNT_MASK 0xFF #define FIXPT_TO_INT(a) ((a) >> FIXPT_SHIFT) #define INT_TO_FIXPT(a) ((a) << FIXPT_SHIFT) #define FIXPT_GET_FRAC(a) ((((a) & FIXPNT_MASK) * 100) >> FIXPT_SHIFT) /* poor man's completion - we want to use wait_event_freezable() on it */ struct dmatest_done { bool done; Loading @@ -179,6 +213,7 @@ struct dmatest_thread { wait_queue_head_t done_wait; struct dmatest_done test_done; bool done; bool pending; }; struct dmatest_chan { Loading Loading @@ -206,6 +241,22 @@ static bool is_threaded_test_run(struct dmatest_info *info) return false; } static bool is_threaded_test_pending(struct dmatest_info *info) { struct dmatest_chan *dtc; list_for_each_entry(dtc, &info->channels, node) { struct dmatest_thread *thread; list_for_each_entry(thread, &dtc->threads, node) { if (thread->pending) return true; } } return false; } static int dmatest_wait_get(char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; Loading Loading @@ -419,13 +470,15 @@ static unsigned long long dmatest_persec(s64 runtime, unsigned int val) } per_sec *= val; per_sec = INT_TO_FIXPT(per_sec); do_div(per_sec, runtime); return per_sec; } static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len) { return dmatest_persec(runtime, len >> 10); return FIXPT_TO_INT(dmatest_persec(runtime, len >> 10)); } /* Loading Loading @@ -466,6 +519,7 @@ static int dmatest_func(void *data) ktime_t comparetime = 0; s64 runtime = 0; unsigned long long total_len = 0; unsigned long long iops = 0; u8 align = 0; bool is_memset = false; dma_addr_t *srcs; Loading @@ -476,27 +530,32 @@ static int dmatest_func(void *data) ret = -ENOMEM; smp_rmb(); thread->pending = false; info = thread->info; params = &info->params; chan = thread->chan; dev = chan->device; if (thread->type == DMA_MEMCPY) { align = dev->copy_align; align = params->alignment < 0 ? dev->copy_align : params->alignment; src_cnt = dst_cnt = 1; } else if (thread->type == DMA_MEMSET) { align = dev->fill_align; align = params->alignment < 0 ? dev->fill_align : params->alignment; src_cnt = dst_cnt = 1; is_memset = true; } else if (thread->type == DMA_XOR) { /* force odd to ensure dst = src */ src_cnt = min_odd(params->xor_sources | 1, dev->max_xor); dst_cnt = 1; align = dev->xor_align; align = params->alignment < 0 ? dev->xor_align : params->alignment; } else if (thread->type == DMA_PQ) { /* force odd to ensure dst = src */ src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0)); dst_cnt = 2; align = dev->pq_align; align = params->alignment < 0 ? dev->pq_align : params->alignment; pq_coefs = kmalloc(params->pq_sources + 1, GFP_KERNEL); if (!pq_coefs) Loading @@ -507,9 +566,22 @@ static int dmatest_func(void *data) } else goto err_thread_type; /* Check if buffer count fits into map count variable (u8) */ if ((src_cnt + dst_cnt) >= 255) { pr_err("too many buffers (%d of 255 supported)\n", src_cnt + dst_cnt); goto err_free_coefs; } if (1 << align > params->buf_size) { pr_err("%u-byte buffer too small for %d-byte alignment\n", params->buf_size, 1 << align); goto err_free_coefs; } thread->srcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL); if (!thread->srcs) goto err_srcs; goto err_free_coefs; thread->usrcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL); if (!thread->usrcs) Loading Loading @@ -576,28 +648,25 @@ static int dmatest_func(void *data) total_tests++; /* Check if buffer count fits into map count variable (u8) */ if ((src_cnt + dst_cnt) >= 255) { pr_err("too many buffers (%d of 255 supported)\n", src_cnt + dst_cnt); break; } if (1 << align > params->buf_size) { pr_err("%u-byte buffer too small for %d-byte alignment\n", params->buf_size, 1 << align); if (params->transfer_size) { if (params->transfer_size >= params->buf_size) { pr_err("%u-byte transfer size must be lower than %u-buffer size\n", params->transfer_size, params->buf_size); break; } if (params->norandom) len = params->transfer_size; } else if (params->norandom) { len = params->buf_size; else } else { len = dmatest_random() % params->buf_size + 1; } /* Do not alter transfer size explicitly defined by user */ if (!params->transfer_size) { len = (len >> align) << align; if (!len) len = 1 << align; } total_len += len; if (params->norandom) { Loading Loading @@ -721,14 +790,14 @@ static int dmatest_func(void *data) status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); if (!done->done) { dmaengine_unmap_put(um); if (!done->done) { result("test timed out", total_tests, src_off, dst_off, len, 0); failed_tests++; continue; } else if (status != DMA_COMPLETE) { dmaengine_unmap_put(um); result(status == DMA_ERROR ? "completion error status" : "completion busy status", total_tests, src_off, Loading @@ -737,8 +806,6 @@ static int dmatest_func(void *data) continue; } dmaengine_unmap_put(um); if (params->noverify) { verbose_result("test passed", total_tests, src_off, dst_off, len, 0); Loading Loading @@ -802,17 +869,18 @@ static int dmatest_func(void *data) kfree(thread->usrcs); err_usrcs: kfree(thread->srcs); err_srcs: err_free_coefs: kfree(pq_coefs); err_thread_type: pr_info("%s: summary %u tests, %u failures %llu iops %llu KB/s (%d)\n", iops = dmatest_persec(runtime, total_tests); pr_info("%s: summary %u tests, %u failures %llu.%02llu iops %llu KB/s (%d)\n", current->comm, total_tests, failed_tests, dmatest_persec(runtime, total_tests), FIXPT_TO_INT(iops), FIXPT_GET_FRAC(iops), dmatest_KBs(runtime, total_len), ret); /* terminate all transfers on specified channels */ if (ret || failed_tests) dmaengine_terminate_all(chan); dmaengine_terminate_sync(chan); thread->done = true; wake_up(&thread_wait); Loading @@ -836,7 +904,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) } /* terminate all transfers on specified channels */ dmaengine_terminate_all(dtc->chan); dmaengine_terminate_sync(dtc->chan); kfree(dtc); } Loading Loading @@ -886,7 +954,7 @@ static int dmatest_add_threads(struct dmatest_info *info, /* srcbuf and dstbuf are allocated by the thread itself */ get_task_struct(thread->task); list_add_tail(&thread->node, &dtc->threads); wake_up_process(thread->task); thread->pending = true; } return i; Loading Loading @@ -932,7 +1000,7 @@ static int dmatest_add_channel(struct dmatest_info *info, thread_count += cnt > 0 ? cnt : 0; } pr_info("Started %u threads using %s\n", pr_info("Added %u threads using %s\n", thread_count, dma_chan_name(chan)); list_add_tail(&dtc->node, &info->channels); Loading Loading @@ -977,7 +1045,7 @@ static void request_channels(struct dmatest_info *info, } } static void run_threaded_test(struct dmatest_info *info) static void add_threaded_test(struct dmatest_info *info) { struct dmatest_params *params = &info->params; Loading @@ -993,6 +1061,8 @@ static void run_threaded_test(struct dmatest_info *info) params->timeout = timeout; params->noverify = noverify; params->norandom = norandom; params->alignment = alignment; params->transfer_size = transfer_size; request_channels(info, DMA_MEMCPY); request_channels(info, DMA_MEMSET); Loading @@ -1000,6 +1070,24 @@ static void run_threaded_test(struct dmatest_info *info) request_channels(info, DMA_PQ); } static void run_pending_tests(struct dmatest_info *info) { struct dmatest_chan *dtc; unsigned int thread_count = 0; list_for_each_entry(dtc, &info->channels, node) { struct dmatest_thread *thread; thread_count = 0; list_for_each_entry(thread, &dtc->threads, node) { wake_up_process(thread->task); thread_count++; } pr_info("Started %u threads using %s\n", thread_count, dma_chan_name(dtc->chan)); } } static void stop_threaded_test(struct dmatest_info *info) { struct dmatest_chan *dtc, *_dtc; Loading @@ -1016,7 +1104,7 @@ static void stop_threaded_test(struct dmatest_info *info) info->nr_channels = 0; } static void restart_threaded_test(struct dmatest_info *info, bool run) static void start_threaded_tests(struct dmatest_info *info) { /* we might be called early to set run=, defer running until all * parameters have been evaluated Loading @@ -1024,11 +1112,7 @@ static void restart_threaded_test(struct dmatest_info *info, bool run) if (!info->did_init) return; /* Stop any running test first */ stop_threaded_test(info); /* Run test with new parameters */ run_threaded_test(info); run_pending_tests(info); } static int dmatest_run_get(char *val, const struct kernel_param *kp) Loading @@ -1039,6 +1123,7 @@ static int dmatest_run_get(char *val, const struct kernel_param *kp) if (is_threaded_test_run(info)) { dmatest_run = true; } else { if (!is_threaded_test_pending(info)) stop_threaded_test(info); dmatest_run = false; } Loading @@ -1056,19 +1141,126 @@ static int dmatest_run_set(const char *val, const struct kernel_param *kp) ret = param_set_bool(val, kp); if (ret) { mutex_unlock(&info->lock); return ret; } else if (dmatest_run) { if (is_threaded_test_pending(info)) start_threaded_tests(info); else pr_info("Could not start test, no channels configured\n"); } else { stop_threaded_test(info); } mutex_unlock(&info->lock); return ret; } if (is_threaded_test_run(info)) static int dmatest_chan_set(const char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; struct dmatest_chan *dtc; char chan_reset_val[20]; int ret = 0; mutex_lock(&info->lock); ret = param_set_copystring(val, kp); if (ret) { mutex_unlock(&info->lock); return ret; } /*Clear any previously run threads */ if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) stop_threaded_test(info); /* Reject channels that are already registered */ if (is_threaded_test_pending(info)) { list_for_each_entry(dtc, &info->channels, node) { if (strcmp(dma_chan_name(dtc->chan), strim(test_channel)) == 0) { dtc = list_last_entry(&info->channels, struct dmatest_chan, node); strlcpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); ret = -EBUSY; else if (dmatest_run) restart_threaded_test(info, dmatest_run); goto add_chan_err; } } } add_threaded_test(info); /* Check if channel was added successfully */ dtc = list_last_entry(&info->channels, struct dmatest_chan, node); if (dtc->chan) { /* * if new channel was not successfully added, revert the * "test_channel" string to the name of the last successfully * added channel. exception for when users issues empty string * to channel parameter. */ if ((strcmp(dma_chan_name(dtc->chan), strim(test_channel)) != 0) && (strcmp("", strim(test_channel)) != 0)) { ret = -EINVAL; strlcpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); goto add_chan_err; } } else { /* Clear test_channel if no channels were added successfully */ strlcpy(chan_reset_val, "", sizeof(chan_reset_val)); ret = -EBUSY; goto add_chan_err; } mutex_unlock(&info->lock); return ret; add_chan_err: param_set_copystring(chan_reset_val, kp); mutex_unlock(&info->lock); return ret; } static int dmatest_chan_get(char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; mutex_lock(&info->lock); if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) { stop_threaded_test(info); strlcpy(test_channel, "", sizeof(test_channel)); } mutex_unlock(&info->lock); return param_get_string(val, kp); } static int dmatest_test_list_get(char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; struct dmatest_chan *dtc; unsigned int thread_count = 0; list_for_each_entry(dtc, &info->channels, node) { struct dmatest_thread *thread; thread_count = 0; list_for_each_entry(thread, &dtc->threads, node) { thread_count++; } pr_info("%u threads using %s\n", thread_count, dma_chan_name(dtc->chan)); } return 0; } static int __init dmatest_init(void) { struct dmatest_info *info = &test_info; Loading @@ -1076,7 +1268,8 @@ static int __init dmatest_init(void) if (dmatest_run) { mutex_lock(&info->lock); run_threaded_test(info); add_threaded_test(info); run_pending_tests(info); mutex_unlock(&info->lock); } Loading Loading
Documentation/driver-api/dmaengine/dmatest.rst +103 −6 Original line number Diff line number Diff line Loading @@ -26,28 +26,43 @@ Part 2 - When dmatest is built as a module Example of usage:: % modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 run=1 % modprobe dmatest timeout=2000 iterations=1 channel=dma0chan0 run=1 ...or:: % modprobe dmatest % echo dma0chan0 > /sys/module/dmatest/parameters/channel % echo 2000 > /sys/module/dmatest/parameters/timeout % echo 1 > /sys/module/dmatest/parameters/iterations % echo dma0chan0 > /sys/module/dmatest/parameters/channel % echo 1 > /sys/module/dmatest/parameters/run ...or on the kernel command line:: dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1 dmatest.run=1 dmatest.timeout=2000 dmatest.iterations=1 dmatest.channel=dma0chan0 dmatest.run=1 Example of multi-channel test usage: % modprobe dmatest % echo 2000 > /sys/module/dmatest/parameters/timeout % echo 1 > /sys/module/dmatest/parameters/iterations % echo dma0chan0 > /sys/module/dmatest/parameters/channel % echo dma0chan1 > /sys/module/dmatest/parameters/channel % echo dma0chan2 > /sys/module/dmatest/parameters/channel % echo 1 > /sys/module/dmatest/parameters/run Note: the channel parameter should always be the last parameter set prior to running the test (setting run=1), this is because upon setting the channel parameter, that specific channel is requested using the dmaengine and a thread is created with the existing parameters. This thread is set as pending and will be executed once run is set to 1. Any parameters set after the thread is created are not applied. .. hint:: available channel list could be extracted by running the following command:: % ls -1 /sys/class/dma/ Once started a message like "dmatest: Started 1 threads using dma0chan0" is emitted. After that only test failure messages are reported until the test stops. Once started a message like " dmatest: Added 1 threads using dma0chan0" is emitted. A thread for that specific channel is created and is now pending, the pending thread is started once run is to 1. Note that running a new test will not stop any in progress test. Loading Loading @@ -112,3 +127,85 @@ Example:: The details of a data miscompare error are also emitted, but do not follow the above format. Part 5 - Handling channel allocation ==================================== Allocating Channels ------------------- Channels are required to be configured prior to starting the test run. Attempting to run the test without configuring the channels will fail. Example:: % echo 1 > /sys/module/dmatest/parameters/run dmatest: Could not start test, no channels configured Channels are registered using the "channel" parameter. Channels can be requested by their name, once requested, the channel is registered and a pending thread is added to the test list. Example:: % echo dma0chan2 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan2 More channels can be added by repeating the example above. Reading back the channel parameter will return the name of last channel that was added successfully. Example:: % echo dma0chan1 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan1 % echo dma0chan2 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan2 % cat /sys/module/dmatest/parameters/channel dma0chan2 Another method of requesting channels is to request a channel with an empty string, Doing so will request all channels available to be tested: Example:: % echo "" > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan0 dmatest: Added 1 threads using dma0chan3 dmatest: Added 1 threads using dma0chan4 dmatest: Added 1 threads using dma0chan5 dmatest: Added 1 threads using dma0chan6 dmatest: Added 1 threads using dma0chan7 dmatest: Added 1 threads using dma0chan8 At any point during the test configuration, reading the "test_list" parameter will print the list of currently pending tests. Example:: % cat /sys/module/dmatest/parameters/test_list dmatest: 1 threads using dma0chan0 dmatest: 1 threads using dma0chan3 dmatest: 1 threads using dma0chan4 dmatest: 1 threads using dma0chan5 dmatest: 1 threads using dma0chan6 dmatest: 1 threads using dma0chan7 dmatest: 1 threads using dma0chan8 Note: Channels will have to be configured for each test run as channel configurations do not carry across to the next test run. Releasing Channels ------------------- Channels can be freed by setting run to 0. Example:: % echo dma0chan1 > /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan1 % cat /sys/class/dma/dma0chan1/in_use 1 % echo 0 > /sys/module/dmatest/parameters/run % cat /sys/class/dma/dma0chan1/in_use 0 Channels allocated by previous test runs are automatically freed when a new channel is requested after completing a successful test run.
drivers/dma/dmatest.c +246 −53 Original line number Diff line number Diff line Loading @@ -27,11 +27,6 @@ static unsigned int test_buf_size = 16384; module_param(test_buf_size, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); static char test_channel[20]; module_param_string(channel, test_channel, sizeof(test_channel), S_IRUGO | S_IWUSR); MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); static char test_device[32]; module_param_string(device, test_device, sizeof(test_device), S_IRUGO | S_IWUSR); Loading Loading @@ -84,6 +79,14 @@ static bool verbose; module_param(verbose, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)"); static int alignment = -1; module_param(alignment, int, 0644); MODULE_PARM_DESC(alignment, "Custom data address alignment taken as 2^(alignment) (default: not used (-1))"); static unsigned int transfer_size; module_param(transfer_size, uint, 0644); MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))"); /** * struct dmatest_params - test parameters. * @buf_size: size of the memcpy test buffer Loading @@ -108,6 +111,8 @@ struct dmatest_params { int timeout; bool noverify; bool norandom; int alignment; unsigned int transfer_size; }; /** Loading Loading @@ -139,6 +144,28 @@ static bool dmatest_run; module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(run, "Run the test (default: false)"); static int dmatest_chan_set(const char *val, const struct kernel_param *kp); static int dmatest_chan_get(char *val, const struct kernel_param *kp); static const struct kernel_param_ops multi_chan_ops = { .set = dmatest_chan_set, .get = dmatest_chan_get, }; static char test_channel[20]; static struct kparam_string newchan_kps = { .string = test_channel, .maxlen = 20, }; module_param_cb(channel, &multi_chan_ops, &newchan_kps, 0644); MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); static int dmatest_test_list_get(char *val, const struct kernel_param *kp); static const struct kernel_param_ops test_list_ops = { .get = dmatest_test_list_get, }; module_param_cb(test_list, &test_list_ops, NULL, 0444); MODULE_PARM_DESC(test_list, "Print current test list"); /* Maximum amount of mismatched bytes in buffer to print */ #define MAX_ERROR_COUNT 32 Loading @@ -160,6 +187,13 @@ MODULE_PARM_DESC(run, "Run the test (default: false)"); #define PATTERN_COUNT_MASK 0x1f #define PATTERN_MEMSET_IDX 0x01 /* Fixed point arithmetic ops */ #define FIXPT_SHIFT 8 #define FIXPNT_MASK 0xFF #define FIXPT_TO_INT(a) ((a) >> FIXPT_SHIFT) #define INT_TO_FIXPT(a) ((a) << FIXPT_SHIFT) #define FIXPT_GET_FRAC(a) ((((a) & FIXPNT_MASK) * 100) >> FIXPT_SHIFT) /* poor man's completion - we want to use wait_event_freezable() on it */ struct dmatest_done { bool done; Loading @@ -179,6 +213,7 @@ struct dmatest_thread { wait_queue_head_t done_wait; struct dmatest_done test_done; bool done; bool pending; }; struct dmatest_chan { Loading Loading @@ -206,6 +241,22 @@ static bool is_threaded_test_run(struct dmatest_info *info) return false; } static bool is_threaded_test_pending(struct dmatest_info *info) { struct dmatest_chan *dtc; list_for_each_entry(dtc, &info->channels, node) { struct dmatest_thread *thread; list_for_each_entry(thread, &dtc->threads, node) { if (thread->pending) return true; } } return false; } static int dmatest_wait_get(char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; Loading Loading @@ -419,13 +470,15 @@ static unsigned long long dmatest_persec(s64 runtime, unsigned int val) } per_sec *= val; per_sec = INT_TO_FIXPT(per_sec); do_div(per_sec, runtime); return per_sec; } static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len) { return dmatest_persec(runtime, len >> 10); return FIXPT_TO_INT(dmatest_persec(runtime, len >> 10)); } /* Loading Loading @@ -466,6 +519,7 @@ static int dmatest_func(void *data) ktime_t comparetime = 0; s64 runtime = 0; unsigned long long total_len = 0; unsigned long long iops = 0; u8 align = 0; bool is_memset = false; dma_addr_t *srcs; Loading @@ -476,27 +530,32 @@ static int dmatest_func(void *data) ret = -ENOMEM; smp_rmb(); thread->pending = false; info = thread->info; params = &info->params; chan = thread->chan; dev = chan->device; if (thread->type == DMA_MEMCPY) { align = dev->copy_align; align = params->alignment < 0 ? dev->copy_align : params->alignment; src_cnt = dst_cnt = 1; } else if (thread->type == DMA_MEMSET) { align = dev->fill_align; align = params->alignment < 0 ? dev->fill_align : params->alignment; src_cnt = dst_cnt = 1; is_memset = true; } else if (thread->type == DMA_XOR) { /* force odd to ensure dst = src */ src_cnt = min_odd(params->xor_sources | 1, dev->max_xor); dst_cnt = 1; align = dev->xor_align; align = params->alignment < 0 ? dev->xor_align : params->alignment; } else if (thread->type == DMA_PQ) { /* force odd to ensure dst = src */ src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0)); dst_cnt = 2; align = dev->pq_align; align = params->alignment < 0 ? dev->pq_align : params->alignment; pq_coefs = kmalloc(params->pq_sources + 1, GFP_KERNEL); if (!pq_coefs) Loading @@ -507,9 +566,22 @@ static int dmatest_func(void *data) } else goto err_thread_type; /* Check if buffer count fits into map count variable (u8) */ if ((src_cnt + dst_cnt) >= 255) { pr_err("too many buffers (%d of 255 supported)\n", src_cnt + dst_cnt); goto err_free_coefs; } if (1 << align > params->buf_size) { pr_err("%u-byte buffer too small for %d-byte alignment\n", params->buf_size, 1 << align); goto err_free_coefs; } thread->srcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL); if (!thread->srcs) goto err_srcs; goto err_free_coefs; thread->usrcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL); if (!thread->usrcs) Loading Loading @@ -576,28 +648,25 @@ static int dmatest_func(void *data) total_tests++; /* Check if buffer count fits into map count variable (u8) */ if ((src_cnt + dst_cnt) >= 255) { pr_err("too many buffers (%d of 255 supported)\n", src_cnt + dst_cnt); break; } if (1 << align > params->buf_size) { pr_err("%u-byte buffer too small for %d-byte alignment\n", params->buf_size, 1 << align); if (params->transfer_size) { if (params->transfer_size >= params->buf_size) { pr_err("%u-byte transfer size must be lower than %u-buffer size\n", params->transfer_size, params->buf_size); break; } if (params->norandom) len = params->transfer_size; } else if (params->norandom) { len = params->buf_size; else } else { len = dmatest_random() % params->buf_size + 1; } /* Do not alter transfer size explicitly defined by user */ if (!params->transfer_size) { len = (len >> align) << align; if (!len) len = 1 << align; } total_len += len; if (params->norandom) { Loading Loading @@ -721,14 +790,14 @@ static int dmatest_func(void *data) status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); if (!done->done) { dmaengine_unmap_put(um); if (!done->done) { result("test timed out", total_tests, src_off, dst_off, len, 0); failed_tests++; continue; } else if (status != DMA_COMPLETE) { dmaengine_unmap_put(um); result(status == DMA_ERROR ? "completion error status" : "completion busy status", total_tests, src_off, Loading @@ -737,8 +806,6 @@ static int dmatest_func(void *data) continue; } dmaengine_unmap_put(um); if (params->noverify) { verbose_result("test passed", total_tests, src_off, dst_off, len, 0); Loading Loading @@ -802,17 +869,18 @@ static int dmatest_func(void *data) kfree(thread->usrcs); err_usrcs: kfree(thread->srcs); err_srcs: err_free_coefs: kfree(pq_coefs); err_thread_type: pr_info("%s: summary %u tests, %u failures %llu iops %llu KB/s (%d)\n", iops = dmatest_persec(runtime, total_tests); pr_info("%s: summary %u tests, %u failures %llu.%02llu iops %llu KB/s (%d)\n", current->comm, total_tests, failed_tests, dmatest_persec(runtime, total_tests), FIXPT_TO_INT(iops), FIXPT_GET_FRAC(iops), dmatest_KBs(runtime, total_len), ret); /* terminate all transfers on specified channels */ if (ret || failed_tests) dmaengine_terminate_all(chan); dmaengine_terminate_sync(chan); thread->done = true; wake_up(&thread_wait); Loading @@ -836,7 +904,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) } /* terminate all transfers on specified channels */ dmaengine_terminate_all(dtc->chan); dmaengine_terminate_sync(dtc->chan); kfree(dtc); } Loading Loading @@ -886,7 +954,7 @@ static int dmatest_add_threads(struct dmatest_info *info, /* srcbuf and dstbuf are allocated by the thread itself */ get_task_struct(thread->task); list_add_tail(&thread->node, &dtc->threads); wake_up_process(thread->task); thread->pending = true; } return i; Loading Loading @@ -932,7 +1000,7 @@ static int dmatest_add_channel(struct dmatest_info *info, thread_count += cnt > 0 ? cnt : 0; } pr_info("Started %u threads using %s\n", pr_info("Added %u threads using %s\n", thread_count, dma_chan_name(chan)); list_add_tail(&dtc->node, &info->channels); Loading Loading @@ -977,7 +1045,7 @@ static void request_channels(struct dmatest_info *info, } } static void run_threaded_test(struct dmatest_info *info) static void add_threaded_test(struct dmatest_info *info) { struct dmatest_params *params = &info->params; Loading @@ -993,6 +1061,8 @@ static void run_threaded_test(struct dmatest_info *info) params->timeout = timeout; params->noverify = noverify; params->norandom = norandom; params->alignment = alignment; params->transfer_size = transfer_size; request_channels(info, DMA_MEMCPY); request_channels(info, DMA_MEMSET); Loading @@ -1000,6 +1070,24 @@ static void run_threaded_test(struct dmatest_info *info) request_channels(info, DMA_PQ); } static void run_pending_tests(struct dmatest_info *info) { struct dmatest_chan *dtc; unsigned int thread_count = 0; list_for_each_entry(dtc, &info->channels, node) { struct dmatest_thread *thread; thread_count = 0; list_for_each_entry(thread, &dtc->threads, node) { wake_up_process(thread->task); thread_count++; } pr_info("Started %u threads using %s\n", thread_count, dma_chan_name(dtc->chan)); } } static void stop_threaded_test(struct dmatest_info *info) { struct dmatest_chan *dtc, *_dtc; Loading @@ -1016,7 +1104,7 @@ static void stop_threaded_test(struct dmatest_info *info) info->nr_channels = 0; } static void restart_threaded_test(struct dmatest_info *info, bool run) static void start_threaded_tests(struct dmatest_info *info) { /* we might be called early to set run=, defer running until all * parameters have been evaluated Loading @@ -1024,11 +1112,7 @@ static void restart_threaded_test(struct dmatest_info *info, bool run) if (!info->did_init) return; /* Stop any running test first */ stop_threaded_test(info); /* Run test with new parameters */ run_threaded_test(info); run_pending_tests(info); } static int dmatest_run_get(char *val, const struct kernel_param *kp) Loading @@ -1039,6 +1123,7 @@ static int dmatest_run_get(char *val, const struct kernel_param *kp) if (is_threaded_test_run(info)) { dmatest_run = true; } else { if (!is_threaded_test_pending(info)) stop_threaded_test(info); dmatest_run = false; } Loading @@ -1056,19 +1141,126 @@ static int dmatest_run_set(const char *val, const struct kernel_param *kp) ret = param_set_bool(val, kp); if (ret) { mutex_unlock(&info->lock); return ret; } else if (dmatest_run) { if (is_threaded_test_pending(info)) start_threaded_tests(info); else pr_info("Could not start test, no channels configured\n"); } else { stop_threaded_test(info); } mutex_unlock(&info->lock); return ret; } if (is_threaded_test_run(info)) static int dmatest_chan_set(const char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; struct dmatest_chan *dtc; char chan_reset_val[20]; int ret = 0; mutex_lock(&info->lock); ret = param_set_copystring(val, kp); if (ret) { mutex_unlock(&info->lock); return ret; } /*Clear any previously run threads */ if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) stop_threaded_test(info); /* Reject channels that are already registered */ if (is_threaded_test_pending(info)) { list_for_each_entry(dtc, &info->channels, node) { if (strcmp(dma_chan_name(dtc->chan), strim(test_channel)) == 0) { dtc = list_last_entry(&info->channels, struct dmatest_chan, node); strlcpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); ret = -EBUSY; else if (dmatest_run) restart_threaded_test(info, dmatest_run); goto add_chan_err; } } } add_threaded_test(info); /* Check if channel was added successfully */ dtc = list_last_entry(&info->channels, struct dmatest_chan, node); if (dtc->chan) { /* * if new channel was not successfully added, revert the * "test_channel" string to the name of the last successfully * added channel. exception for when users issues empty string * to channel parameter. */ if ((strcmp(dma_chan_name(dtc->chan), strim(test_channel)) != 0) && (strcmp("", strim(test_channel)) != 0)) { ret = -EINVAL; strlcpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); goto add_chan_err; } } else { /* Clear test_channel if no channels were added successfully */ strlcpy(chan_reset_val, "", sizeof(chan_reset_val)); ret = -EBUSY; goto add_chan_err; } mutex_unlock(&info->lock); return ret; add_chan_err: param_set_copystring(chan_reset_val, kp); mutex_unlock(&info->lock); return ret; } static int dmatest_chan_get(char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; mutex_lock(&info->lock); if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) { stop_threaded_test(info); strlcpy(test_channel, "", sizeof(test_channel)); } mutex_unlock(&info->lock); return param_get_string(val, kp); } static int dmatest_test_list_get(char *val, const struct kernel_param *kp) { struct dmatest_info *info = &test_info; struct dmatest_chan *dtc; unsigned int thread_count = 0; list_for_each_entry(dtc, &info->channels, node) { struct dmatest_thread *thread; thread_count = 0; list_for_each_entry(thread, &dtc->threads, node) { thread_count++; } pr_info("%u threads using %s\n", thread_count, dma_chan_name(dtc->chan)); } return 0; } static int __init dmatest_init(void) { struct dmatest_info *info = &test_info; Loading @@ -1076,7 +1268,8 @@ static int __init dmatest_init(void) if (dmatest_run) { mutex_lock(&info->lock); run_threaded_test(info); add_threaded_test(info); run_pending_tests(info); mutex_unlock(&info->lock); } Loading