Skip to content

Commit

Permalink
dmaengine: dmatest: Add support for multi channel testing
Browse files Browse the repository at this point in the history
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: Seraj Alijan <seraj.alijan@sondrel.com>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
  • Loading branch information
Seraj Alijan authored and vinodkoul committed Dec 17, 2018
1 parent 3f3c755 commit d53513d
Showing 1 changed file with 177 additions and 19 deletions.
196 changes: 177 additions & 19 deletions drivers/dma/dmatest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -139,6 +134,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

Expand Down Expand Up @@ -179,6 +196,7 @@ struct dmatest_thread {
wait_queue_head_t done_wait;
struct dmatest_done test_done;
bool done;
bool pending;
};

struct dmatest_chan {
Expand Down Expand Up @@ -206,6 +224,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;
Expand Down Expand Up @@ -476,6 +510,7 @@ static int dmatest_func(void *data)
ret = -ENOMEM;

smp_rmb();
thread->pending = false;
info = thread->info;
params = &info->params;
chan = thread->chan;
Expand Down Expand Up @@ -884,7 +919,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;
Expand Down Expand Up @@ -930,7 +965,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);
Expand Down Expand Up @@ -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;

Expand All @@ -998,6 +1033,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;
Expand All @@ -1014,19 +1067,15 @@ 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
*/
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)
Expand All @@ -1037,7 +1086,8 @@ static int dmatest_run_get(char *val, const struct kernel_param *kp)
if (is_threaded_test_run(info)) {
dmatest_run = true;
} else {
stop_threaded_test(info);
if (!is_threaded_test_pending(info))
stop_threaded_test(info);
dmatest_run = false;
}
mutex_unlock(&info->lock);
Expand All @@ -1055,26 +1105,134 @@ static int dmatest_run_set(const char *val, const struct kernel_param *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;
}

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;
}
}
}

if (is_threaded_test_run(info))
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;
else if (dmatest_run)
restart_threaded_test(info, dmatest_run);
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;
struct dmatest_params *params = &info->params;

if (dmatest_run) {
mutex_lock(&info->lock);
run_threaded_test(info);
add_threaded_test(info);
run_pending_tests(info);
mutex_unlock(&info->lock);
}

Expand Down

0 comments on commit d53513d

Please sign in to comment.