提交 a310d037 编写于 作者: D Dan Williams

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: NLinus Walleij <linus.walleij@linaro.org>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: NAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: NDan Williams <dan.j.williams@intel.com>
上级 0adff800
...@@ -15,17 +15,19 @@ be built as module or inside kernel. Let's consider those cases. ...@@ -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. ...@@ -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 @@ ...@@ -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); ...@@ -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 { ...@@ -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 { ...@@ -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;
} test_info = {
.channels = LIST_HEAD_INIT(test_info.channels),
.lock = __MUTEX_INITIALIZER(test_info.lock),
};
/* debugfs related stuff */ static int dmatest_run_set(const char *val, const struct kernel_param *kp);
struct dentry *root; 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)");
static struct dmatest_info test_info; /* 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) ...@@ -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) ...@@ -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);
ret = __run_threaded_test(info);
mutex_unlock(&info->lock);
return ret;
}
#endif
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;
struct dma_chan *chan; struct dma_chan *chan;
...@@ -785,39 +788,22 @@ static void __stop_threaded_test(struct dmatest_info *info) ...@@ -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);
__stop_threaded_test(info);
mutex_unlock(&info->lock);
}
static int __restart_threaded_test(struct dmatest_info *info, bool run)
{ {
struct dmatest_params *params = &info->params; /* we might be called early to set run=, defer running until all
* parameters have been evaluated
/* Stop any running test first */ */
__stop_threaded_test(info); if (!info->did_init)
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) ...@@ -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); mutex_unlock(&info->lock);
buf[1] = '\n';
buf[2] = 0x00; return param_get_bool(val, kp);
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, static int dmatest_run_set(const 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[16]; int ret;
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_lock(&info->lock);
ret = param_set_bool(val, kp);
if (ret) {
mutex_unlock(&info->lock); mutex_unlock(&info->lock);
return ret;
} }
return ret ? ret : count; if (is_threaded_test_run(info))
} ret = -EBUSY;
else if (dmatest_run)
static const struct file_operations dtf_run_fops = { ret = restart_threaded_test(info, dmatest_run);
.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; mutex_unlock(&info->lock);
/* Run or stop threaded test */
debugfs_create_file("run", S_IWUSR | S_IRUGO, info->root, info,
&dtf_run_fops);
return 0;
err_root: return ret;
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));
mutex_init(&info->lock); if (dmatest_run) {
INIT_LIST_HEAD(&info->channels); mutex_lock(&info->lock);
ret = run_threaded_test(info);
mutex_unlock(&info->lock);
}
ret = dmatest_register_dbgfs(info); /* module parameters are stable, inittime tests are started,
if (ret) * let userspace take over 'run' control
return ret; */
info->did_init = true;
#ifdef MODULE return ret;
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) ...@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册