dmatest: restore ability to start test at module load and init
1/ move 'run' control to a module parameter so we can do: modprobe dmatest run=1. With this moved the rest of the debugfs boilerplate can go. 2/ Fix parameter initialization. Previously the test was being started without taking the parameters into account in the built-in case. Also killed off the '__' version of some routines. The new rule is just hold the lock when calling a *threaded_test() routine. Acked-by: Linus Walleij <linus.walleij@linaro.org> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
@ -15,17 +15,19 @@ be built as module or inside kernel. Let's consider those cases.
|
|||||||
|
|
||||||
Part 2 - When dmatest is built as a module...
|
Part 2 - When dmatest is built as a module...
|
||||||
|
|
||||||
After mounting debugfs and loading the module, the /sys/kernel/debug/dmatest
|
|
||||||
folder with a file named 'run' nodes will be created. 'run' controls run and
|
|
||||||
stop phases of the test.
|
|
||||||
|
|
||||||
Note that in this case test will not run on load automatically.
|
|
||||||
|
|
||||||
Example of usage:
|
Example of usage:
|
||||||
|
% modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 run=1
|
||||||
|
|
||||||
|
...or:
|
||||||
|
% modprobe dmatest
|
||||||
% echo dma0chan0 > /sys/module/dmatest/parameters/channel
|
% echo dma0chan0 > /sys/module/dmatest/parameters/channel
|
||||||
% echo 2000 > /sys/module/dmatest/parameters/timeout
|
% echo 2000 > /sys/module/dmatest/parameters/timeout
|
||||||
% echo 1 > /sys/module/dmatest/parameters/iterations
|
% echo 1 > /sys/module/dmatest/parameters/iterations
|
||||||
% echo 1 > /sys/kernel/debug/dmatest/run
|
% 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
|
||||||
|
|
||||||
Hint: available channel list could be extracted by running the following
|
Hint: available channel list could be extracted by running the following
|
||||||
command:
|
command:
|
||||||
@ -42,7 +44,7 @@ The following command should return actual state of the test.
|
|||||||
|
|
||||||
To wait for test done the user may perform a busy loop that checks the state.
|
To wait for test done the user may perform a busy loop that checks the state.
|
||||||
|
|
||||||
% while [ $(cat /sys/kernel/debug/dmatest/run) = "Y" ]
|
% while [ $(cat /sys/module/dmatest/parameters/run) = "Y" ]
|
||||||
> do
|
> do
|
||||||
> echo -n "."
|
> echo -n "."
|
||||||
> sleep 1
|
> sleep 1
|
||||||
|
@ -21,10 +21,6 @@
|
|||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/ctype.h>
|
|
||||||
#include <linux/debugfs.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
|
|
||||||
static unsigned int test_buf_size = 16384;
|
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);
|
||||||
@ -70,45 +66,6 @@ module_param(timeout, uint, S_IRUGO | S_IWUSR);
|
|||||||
MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
|
MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
|
||||||
"Pass -1 for infinite timeout");
|
"Pass -1 for infinite timeout");
|
||||||
|
|
||||||
/* Maximum amount of mismatched bytes in buffer to print */
|
|
||||||
#define MAX_ERROR_COUNT 32
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialization patterns. All bytes in the source buffer has bit 7
|
|
||||||
* set, all bytes in the destination buffer has bit 7 cleared.
|
|
||||||
*
|
|
||||||
* Bit 6 is set for all bytes which are to be copied by the DMA
|
|
||||||
* engine. Bit 5 is set for all bytes which are to be overwritten by
|
|
||||||
* the DMA engine.
|
|
||||||
*
|
|
||||||
* The remaining bits are the inverse of a counter which increments by
|
|
||||||
* one for each byte address.
|
|
||||||
*/
|
|
||||||
#define PATTERN_SRC 0x80
|
|
||||||
#define PATTERN_DST 0x00
|
|
||||||
#define PATTERN_COPY 0x40
|
|
||||||
#define PATTERN_OVERWRITE 0x20
|
|
||||||
#define PATTERN_COUNT_MASK 0x1f
|
|
||||||
|
|
||||||
struct dmatest_info;
|
|
||||||
|
|
||||||
struct dmatest_thread {
|
|
||||||
struct list_head node;
|
|
||||||
struct dmatest_info *info;
|
|
||||||
struct task_struct *task;
|
|
||||||
struct dma_chan *chan;
|
|
||||||
u8 **srcs;
|
|
||||||
u8 **dsts;
|
|
||||||
enum dma_transaction_type type;
|
|
||||||
bool done;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dmatest_chan {
|
|
||||||
struct list_head node;
|
|
||||||
struct dma_chan *chan;
|
|
||||||
struct list_head threads;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct dmatest_params - test parameters.
|
* struct dmatest_params - test parameters.
|
||||||
* @buf_size: size of the memcpy test buffer
|
* @buf_size: size of the memcpy test buffer
|
||||||
@ -138,7 +95,7 @@ struct dmatest_params {
|
|||||||
* @params: test parameters
|
* @params: test parameters
|
||||||
* @lock: access protection to the fields of this structure
|
* @lock: access protection to the fields of this structure
|
||||||
*/
|
*/
|
||||||
struct dmatest_info {
|
static struct dmatest_info {
|
||||||
/* Test parameters */
|
/* Test parameters */
|
||||||
struct dmatest_params params;
|
struct dmatest_params params;
|
||||||
|
|
||||||
@ -146,12 +103,58 @@ struct dmatest_info {
|
|||||||
struct list_head channels;
|
struct list_head channels;
|
||||||
unsigned int nr_channels;
|
unsigned int nr_channels;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
bool did_init;
|
||||||
/* debugfs related stuff */
|
} test_info = {
|
||||||
struct dentry *root;
|
.channels = LIST_HEAD_INIT(test_info.channels),
|
||||||
|
.lock = __MUTEX_INITIALIZER(test_info.lock),
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct dmatest_info test_info;
|
static int dmatest_run_set(const char *val, const struct kernel_param *kp);
|
||||||
|
static int dmatest_run_get(char *val, const struct kernel_param *kp);
|
||||||
|
static struct kernel_param_ops run_ops = {
|
||||||
|
.set = dmatest_run_set,
|
||||||
|
.get = dmatest_run_get,
|
||||||
|
};
|
||||||
|
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)");
|
||||||
|
|
||||||
|
/* Maximum amount of mismatched bytes in buffer to print */
|
||||||
|
#define MAX_ERROR_COUNT 32
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialization patterns. All bytes in the source buffer has bit 7
|
||||||
|
* set, all bytes in the destination buffer has bit 7 cleared.
|
||||||
|
*
|
||||||
|
* Bit 6 is set for all bytes which are to be copied by the DMA
|
||||||
|
* engine. Bit 5 is set for all bytes which are to be overwritten by
|
||||||
|
* the DMA engine.
|
||||||
|
*
|
||||||
|
* The remaining bits are the inverse of a counter which increments by
|
||||||
|
* one for each byte address.
|
||||||
|
*/
|
||||||
|
#define PATTERN_SRC 0x80
|
||||||
|
#define PATTERN_DST 0x00
|
||||||
|
#define PATTERN_COPY 0x40
|
||||||
|
#define PATTERN_OVERWRITE 0x20
|
||||||
|
#define PATTERN_COUNT_MASK 0x1f
|
||||||
|
|
||||||
|
struct dmatest_thread {
|
||||||
|
struct list_head node;
|
||||||
|
struct dmatest_info *info;
|
||||||
|
struct task_struct *task;
|
||||||
|
struct dma_chan *chan;
|
||||||
|
u8 **srcs;
|
||||||
|
u8 **dsts;
|
||||||
|
enum dma_transaction_type type;
|
||||||
|
bool done;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dmatest_chan {
|
||||||
|
struct list_head node;
|
||||||
|
struct dma_chan *chan;
|
||||||
|
struct list_head threads;
|
||||||
|
};
|
||||||
|
|
||||||
static bool dmatest_match_channel(struct dmatest_params *params,
|
static bool dmatest_match_channel(struct dmatest_params *params,
|
||||||
struct dma_chan *chan)
|
struct dma_chan *chan)
|
||||||
@ -731,13 +734,24 @@ static bool filter(struct dma_chan *chan, void *param)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __run_threaded_test(struct dmatest_info *info)
|
static int run_threaded_test(struct dmatest_info *info)
|
||||||
{
|
{
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
struct dma_chan *chan;
|
struct dma_chan *chan;
|
||||||
struct dmatest_params *params = &info->params;
|
struct dmatest_params *params = &info->params;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
/* Copy test parameters */
|
||||||
|
params->buf_size = test_buf_size;
|
||||||
|
strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
|
||||||
|
strlcpy(params->device, strim(test_device), sizeof(params->device));
|
||||||
|
params->threads_per_chan = threads_per_chan;
|
||||||
|
params->max_channels = max_channels;
|
||||||
|
params->iterations = iterations;
|
||||||
|
params->xor_sources = xor_sources;
|
||||||
|
params->pq_sources = pq_sources;
|
||||||
|
params->timeout = timeout;
|
||||||
|
|
||||||
dma_cap_zero(mask);
|
dma_cap_zero(mask);
|
||||||
dma_cap_set(DMA_MEMCPY, mask);
|
dma_cap_set(DMA_MEMCPY, mask);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -757,19 +771,8 @@ static int __run_threaded_test(struct dmatest_info *info)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef MODULE
|
|
||||||
static int run_threaded_test(struct dmatest_info *info)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mutex_lock(&info->lock);
|
static void stop_threaded_test(struct dmatest_info *info)
|
||||||
ret = __run_threaded_test(info);
|
|
||||||
mutex_unlock(&info->lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void __stop_threaded_test(struct dmatest_info *info)
|
|
||||||
{
|
{
|
||||||
struct dmatest_chan *dtc, *_dtc;
|
struct dmatest_chan *dtc, *_dtc;
|
||||||
struct dma_chan *chan;
|
struct dma_chan *chan;
|
||||||
@ -785,39 +788,22 @@ static void __stop_threaded_test(struct dmatest_info *info)
|
|||||||
info->nr_channels = 0;
|
info->nr_channels = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop_threaded_test(struct dmatest_info *info)
|
static int restart_threaded_test(struct dmatest_info *info, bool run)
|
||||||
{
|
{
|
||||||
mutex_lock(&info->lock);
|
/* we might be called early to set run=, defer running until all
|
||||||
__stop_threaded_test(info);
|
* parameters have been evaluated
|
||||||
mutex_unlock(&info->lock);
|
*/
|
||||||
}
|
if (!info->did_init)
|
||||||
|
|
||||||
static int __restart_threaded_test(struct dmatest_info *info, bool run)
|
|
||||||
{
|
|
||||||
struct dmatest_params *params = &info->params;
|
|
||||||
|
|
||||||
/* Stop any running test first */
|
|
||||||
__stop_threaded_test(info);
|
|
||||||
|
|
||||||
if (run == false)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Copy test parameters */
|
/* Stop any running test first */
|
||||||
params->buf_size = test_buf_size;
|
stop_threaded_test(info);
|
||||||
strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
|
|
||||||
strlcpy(params->device, strim(test_device), sizeof(params->device));
|
|
||||||
params->threads_per_chan = threads_per_chan;
|
|
||||||
params->max_channels = max_channels;
|
|
||||||
params->iterations = iterations;
|
|
||||||
params->xor_sources = xor_sources;
|
|
||||||
params->pq_sources = pq_sources;
|
|
||||||
params->timeout = timeout;
|
|
||||||
|
|
||||||
/* Run test with new parameters */
|
/* Run test with new parameters */
|
||||||
return __run_threaded_test(info);
|
return run_threaded_test(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool __is_threaded_test_run(struct dmatest_info *info)
|
static bool is_threaded_test_run(struct dmatest_info *info)
|
||||||
{
|
{
|
||||||
struct dmatest_chan *dtc;
|
struct dmatest_chan *dtc;
|
||||||
|
|
||||||
@ -833,101 +819,61 @@ static bool __is_threaded_test_run(struct dmatest_info *info)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t dtf_read_run(struct file *file, char __user *user_buf,
|
static int dmatest_run_get(char *val, const struct kernel_param *kp)
|
||||||
size_t count, loff_t *ppos)
|
|
||||||
{
|
{
|
||||||
struct dmatest_info *info = file->private_data;
|
struct dmatest_info *info = &test_info;
|
||||||
char buf[3];
|
|
||||||
|
|
||||||
mutex_lock(&info->lock);
|
mutex_lock(&info->lock);
|
||||||
|
if (is_threaded_test_run(info)) {
|
||||||
if (__is_threaded_test_run(info)) {
|
dmatest_run = true;
|
||||||
buf[0] = 'Y';
|
|
||||||
} else {
|
} else {
|
||||||
__stop_threaded_test(info);
|
stop_threaded_test(info);
|
||||||
buf[0] = 'N';
|
dmatest_run = false;
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&info->lock);
|
||||||
|
|
||||||
|
return param_get_bool(val, kp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dmatest_run_set(const char *val, const struct kernel_param *kp)
|
||||||
|
{
|
||||||
|
struct dmatest_info *info = &test_info;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&info->lock);
|
||||||
|
ret = param_set_bool(val, kp);
|
||||||
|
if (ret) {
|
||||||
|
mutex_unlock(&info->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_threaded_test_run(info))
|
||||||
|
ret = -EBUSY;
|
||||||
|
else if (dmatest_run)
|
||||||
|
ret = restart_threaded_test(info, dmatest_run);
|
||||||
|
|
||||||
mutex_unlock(&info->lock);
|
mutex_unlock(&info->lock);
|
||||||
buf[1] = '\n';
|
|
||||||
buf[2] = 0x00;
|
|
||||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t dtf_write_run(struct file *file, const char __user *user_buf,
|
return ret;
|
||||||
size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
struct dmatest_info *info = file->private_data;
|
|
||||||
char buf[16];
|
|
||||||
bool bv;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1))))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (strtobool(buf, &bv) == 0) {
|
|
||||||
mutex_lock(&info->lock);
|
|
||||||
|
|
||||||
if (__is_threaded_test_run(info))
|
|
||||||
ret = -EBUSY;
|
|
||||||
else
|
|
||||||
ret = __restart_threaded_test(info, bv);
|
|
||||||
|
|
||||||
mutex_unlock(&info->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret ? ret : count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations dtf_run_fops = {
|
|
||||||
.read = dtf_read_run,
|
|
||||||
.write = dtf_write_run,
|
|
||||||
.open = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int dmatest_register_dbgfs(struct dmatest_info *info)
|
|
||||||
{
|
|
||||||
struct dentry *d;
|
|
||||||
|
|
||||||
d = debugfs_create_dir("dmatest", NULL);
|
|
||||||
if (IS_ERR(d))
|
|
||||||
return PTR_ERR(d);
|
|
||||||
if (!d)
|
|
||||||
goto err_root;
|
|
||||||
|
|
||||||
info->root = d;
|
|
||||||
|
|
||||||
/* Run or stop threaded test */
|
|
||||||
debugfs_create_file("run", S_IWUSR | S_IRUGO, info->root, info,
|
|
||||||
&dtf_run_fops);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_root:
|
|
||||||
pr_err("Failed to initialize debugfs\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init dmatest_init(void)
|
static int __init dmatest_init(void)
|
||||||
{
|
{
|
||||||
struct dmatest_info *info = &test_info;
|
struct dmatest_info *info = &test_info;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
memset(info, 0, sizeof(*info));
|
if (dmatest_run) {
|
||||||
|
mutex_lock(&info->lock);
|
||||||
|
ret = run_threaded_test(info);
|
||||||
|
mutex_unlock(&info->lock);
|
||||||
|
}
|
||||||
|
|
||||||
mutex_init(&info->lock);
|
/* module parameters are stable, inittime tests are started,
|
||||||
INIT_LIST_HEAD(&info->channels);
|
* let userspace take over 'run' control
|
||||||
|
*/
|
||||||
|
info->did_init = true;
|
||||||
|
|
||||||
ret = dmatest_register_dbgfs(info);
|
return ret;
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
#ifdef MODULE
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return run_threaded_test(info);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
/* when compiled-in wait for drivers to load first */
|
/* when compiled-in wait for drivers to load first */
|
||||||
late_initcall(dmatest_init);
|
late_initcall(dmatest_init);
|
||||||
@ -936,8 +882,9 @@ static void __exit dmatest_exit(void)
|
|||||||
{
|
{
|
||||||
struct dmatest_info *info = &test_info;
|
struct dmatest_info *info = &test_info;
|
||||||
|
|
||||||
debugfs_remove_recursive(info->root);
|
mutex_lock(&info->lock);
|
||||||
stop_threaded_test(info);
|
stop_threaded_test(info);
|
||||||
|
mutex_unlock(&info->lock);
|
||||||
}
|
}
|
||||||
module_exit(dmatest_exit);
|
module_exit(dmatest_exit);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user