Commit d53513d5 authored by Seraj Alijan's avatar Seraj Alijan Committed by Vinod Koul

dmaengine: dmatest: Add support for multi channel testing

Add support for running tests on multiple channels simultaneously as the
driver currently limits to 1 channel per test run. This will add support
for stress testing DMA controllers with multi channel capabilities.

This is done by adding a callback function to the "channel" parameter
that registers the requested channel prior to the "run" parameter being
set to 1. Each time the "channel" parameter is populated with a new
dma channel, a new test is appended to the thread queue. Once the "run"
parameter is set to 1, the test will kick start all pending threads.
Signed-off-by: default avatarSeraj Alijan <seraj.alijan@sondrel.com>
Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 3f3c7554
...@@ -27,11 +27,6 @@ static unsigned int test_buf_size = 16384; ...@@ -27,11 +27,6 @@ static unsigned int test_buf_size = 16384;
module_param(test_buf_size, uint, S_IRUGO | S_IWUSR); module_param(test_buf_size, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); 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]; static char test_device[32];
module_param_string(device, test_device, sizeof(test_device), module_param_string(device, test_device, sizeof(test_device),
S_IRUGO | S_IWUSR); S_IRUGO | S_IWUSR);
...@@ -139,6 +134,28 @@ static bool dmatest_run; ...@@ -139,6 +134,28 @@ static bool dmatest_run;
module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR); module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(run, "Run the test (default: false)"); 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 */ /* Maximum amount of mismatched bytes in buffer to print */
#define MAX_ERROR_COUNT 32 #define MAX_ERROR_COUNT 32
...@@ -179,6 +196,7 @@ struct dmatest_thread { ...@@ -179,6 +196,7 @@ struct dmatest_thread {
wait_queue_head_t done_wait; wait_queue_head_t done_wait;
struct dmatest_done test_done; struct dmatest_done test_done;
bool done; bool done;
bool pending;
}; };
struct dmatest_chan { struct dmatest_chan {
...@@ -206,6 +224,22 @@ static bool is_threaded_test_run(struct dmatest_info *info) ...@@ -206,6 +224,22 @@ static bool is_threaded_test_run(struct dmatest_info *info)
return false; 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) static int dmatest_wait_get(char *val, const struct kernel_param *kp)
{ {
struct dmatest_info *info = &test_info; struct dmatest_info *info = &test_info;
...@@ -476,6 +510,7 @@ static int dmatest_func(void *data) ...@@ -476,6 +510,7 @@ static int dmatest_func(void *data)
ret = -ENOMEM; ret = -ENOMEM;
smp_rmb(); smp_rmb();
thread->pending = false;
info = thread->info; info = thread->info;
params = &info->params; params = &info->params;
chan = thread->chan; chan = thread->chan;
...@@ -884,7 +919,7 @@ static int dmatest_add_threads(struct dmatest_info *info, ...@@ -884,7 +919,7 @@ static int dmatest_add_threads(struct dmatest_info *info,
/* srcbuf and dstbuf are allocated by the thread itself */ /* srcbuf and dstbuf are allocated by the thread itself */
get_task_struct(thread->task); get_task_struct(thread->task);
list_add_tail(&thread->node, &dtc->threads); list_add_tail(&thread->node, &dtc->threads);
wake_up_process(thread->task); thread->pending = true;
} }
return i; return i;
...@@ -930,7 +965,7 @@ static int dmatest_add_channel(struct dmatest_info *info, ...@@ -930,7 +965,7 @@ static int dmatest_add_channel(struct dmatest_info *info,
thread_count += cnt > 0 ? cnt : 0; 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)); thread_count, dma_chan_name(chan));
list_add_tail(&dtc->node, &info->channels); list_add_tail(&dtc->node, &info->channels);
...@@ -975,7 +1010,7 @@ static void request_channels(struct dmatest_info *info, ...@@ -975,7 +1010,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; struct dmatest_params *params = &info->params;
...@@ -998,6 +1033,24 @@ static void run_threaded_test(struct dmatest_info *info) ...@@ -998,6 +1033,24 @@ static void run_threaded_test(struct dmatest_info *info)
request_channels(info, DMA_PQ); 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) static void stop_threaded_test(struct dmatest_info *info)
{ {
struct dmatest_chan *dtc, *_dtc; struct dmatest_chan *dtc, *_dtc;
...@@ -1014,7 +1067,7 @@ static void stop_threaded_test(struct dmatest_info *info) ...@@ -1014,7 +1067,7 @@ static void stop_threaded_test(struct dmatest_info *info)
info->nr_channels = 0; 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 /* we might be called early to set run=, defer running until all
* parameters have been evaluated * parameters have been evaluated
...@@ -1022,11 +1075,7 @@ static void restart_threaded_test(struct dmatest_info *info, bool run) ...@@ -1022,11 +1075,7 @@ static void restart_threaded_test(struct dmatest_info *info, bool run)
if (!info->did_init) if (!info->did_init)
return; return;
/* Stop any running test first */ run_pending_tests(info);
stop_threaded_test(info);
/* Run test with new parameters */
run_threaded_test(info);
} }
static int dmatest_run_get(char *val, const struct kernel_param *kp) static int dmatest_run_get(char *val, const struct kernel_param *kp)
...@@ -1037,6 +1086,7 @@ static int dmatest_run_get(char *val, const struct kernel_param *kp) ...@@ -1037,6 +1086,7 @@ static int dmatest_run_get(char *val, const struct kernel_param *kp)
if (is_threaded_test_run(info)) { if (is_threaded_test_run(info)) {
dmatest_run = true; dmatest_run = true;
} else { } else {
if (!is_threaded_test_pending(info))
stop_threaded_test(info); stop_threaded_test(info);
dmatest_run = false; dmatest_run = false;
} }
...@@ -1055,18 +1105,125 @@ static int dmatest_run_set(const char *val, const struct kernel_param *kp) ...@@ -1055,18 +1105,125 @@ static int dmatest_run_set(const char *val, const struct kernel_param *kp)
if (ret) { if (ret) {
mutex_unlock(&info->lock); mutex_unlock(&info->lock);
return ret; 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);
} }
if (is_threaded_test_run(info)) mutex_unlock(&info->lock);
return ret;
}
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;
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; ret = -EBUSY;
else if (dmatest_run) goto add_chan_err;
restart_threaded_test(info, dmatest_run); }
mutex_unlock(&info->lock);
return ret;
add_chan_err:
param_set_copystring(chan_reset_val, kp);
mutex_unlock(&info->lock); mutex_unlock(&info->lock);
return ret; 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) static int __init dmatest_init(void)
{ {
struct dmatest_info *info = &test_info; struct dmatest_info *info = &test_info;
...@@ -1074,7 +1231,8 @@ static int __init dmatest_init(void) ...@@ -1074,7 +1231,8 @@ static int __init dmatest_init(void)
if (dmatest_run) { if (dmatest_run) {
mutex_lock(&info->lock); mutex_lock(&info->lock);
run_threaded_test(info); add_threaded_test(info);
run_pending_tests(info);
mutex_unlock(&info->lock); mutex_unlock(&info->lock);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment