Merge branch 'topic/dmatest' into for-linus
This commit is contained in:
commit
466e601a68
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -108,6 +111,8 @@ struct dmatest_params {
|
|||
int timeout;
|
||||
bool noverify;
|
||||
bool norandom;
|
||||
int alignment;
|
||||
unsigned int transfer_size;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
break;
|
||||
}
|
||||
|
||||
if (params->norandom)
|
||||
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;
|
||||
}
|
||||
len = params->transfer_size;
|
||||
} else if (params->norandom) {
|
||||
len = params->buf_size;
|
||||
else
|
||||
} else {
|
||||
len = dmatest_random() % params->buf_size + 1;
|
||||
}
|
||||
|
||||
len = (len >> align) << align;
|
||||
if (!len)
|
||||
len = 1 << align;
|
||||
|
||||
/* 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) {
|
||||
|
@ -721,14 +790,14 @@ static int dmatest_func(void *data)
|
|||
|
||||
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
|
||||
|
||||
dmaengine_unmap_put(um);
|
||||
|
||||
if (!done->done) {
|
||||
dmaengine_unmap_put(um);
|
||||
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,
|
||||
|
@ -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);
|
||||
|
@ -802,17 +869,18 @@ err_srcbuf:
|
|||
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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -1039,7 +1123,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);
|
||||
|
@ -1057,18 +1142,125 @@ 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);
|
||||
}
|
||||
|
||||
if (is_threaded_test_run(info))
|
||||
ret = -EBUSY;
|
||||
else if (dmatest_run)
|
||||
restart_threaded_test(info, dmatest_run);
|
||||
|
||||
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;
|
||||
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;
|
||||
|
@ -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…
Reference in New Issue