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:
parent
0adff80066
commit
a310d037b8
|
@ -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...
|
||||
|
||||
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:
|
||||
% modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 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 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
|
||||
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.
|
||||
|
||||
% while [ $(cat /sys/kernel/debug/dmatest/run) = "Y" ]
|
||||
% while [ $(cat /sys/module/dmatest/parameters/run) = "Y" ]
|
||||
> do
|
||||
> echo -n "."
|
||||
> sleep 1
|
||||
|
|
|
@ -21,10 +21,6 @@
|
|||
#include <linux/random.h>
|
||||
#include <linux/slab.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;
|
||||
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), "
|
||||
"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.
|
||||
* @buf_size: size of the memcpy test buffer
|
||||
|
@ -138,7 +95,7 @@ struct dmatest_params {
|
|||
* @params: test parameters
|
||||
* @lock: access protection to the fields of this structure
|
||||
*/
|
||||
struct dmatest_info {
|
||||
static struct dmatest_info {
|
||||
/* Test parameters */
|
||||
struct dmatest_params params;
|
||||
|
||||
|
@ -146,12 +103,58 @@ struct dmatest_info {
|
|||
struct list_head channels;
|
||||
unsigned int nr_channels;
|
||||
struct mutex lock;
|
||||
|
||||
/* debugfs related stuff */
|
||||
struct dentry *root;
|
||||
bool did_init;
|
||||
} test_info = {
|
||||
.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,
|
||||
struct dma_chan *chan)
|
||||
|
@ -731,13 +734,24 @@ static bool filter(struct dma_chan *chan, void *param)
|
|||
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;
|
||||
struct dma_chan *chan;
|
||||
struct dmatest_params *params = &info->params;
|
||||
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_set(DMA_MEMCPY, mask);
|
||||
for (;;) {
|
||||
|
@ -757,19 +771,8 @@ static int __run_threaded_test(struct dmatest_info *info)
|
|||
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 dma_chan *chan;
|
||||
|
@ -785,39 +788,22 @@ static void __stop_threaded_test(struct dmatest_info *info)
|
|||
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;
|
||||
|
||||
/* Stop any running test first */
|
||||
__stop_threaded_test(info);
|
||||
|
||||
if (run == false)
|
||||
/* we might be called early to set run=, defer running until all
|
||||
* parameters have been evaluated
|
||||
*/
|
||||
if (!info->did_init)
|
||||
return 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;
|
||||
/* Stop any running test first */
|
||||
stop_threaded_test(info);
|
||||
|
||||
/* 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;
|
||||
|
||||
|
@ -833,101 +819,61 @@ static bool __is_threaded_test_run(struct dmatest_info *info)
|
|||
return false;
|
||||
}
|
||||
|
||||
static ssize_t dtf_read_run(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
static int dmatest_run_get(char *val, const struct kernel_param *kp)
|
||||
{
|
||||
struct dmatest_info *info = file->private_data;
|
||||
char buf[3];
|
||||
struct dmatest_info *info = &test_info;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
if (__is_threaded_test_run(info)) {
|
||||
buf[0] = 'Y';
|
||||
if (is_threaded_test_run(info)) {
|
||||
dmatest_run = true;
|
||||
} else {
|
||||
__stop_threaded_test(info);
|
||||
buf[0] = 'N';
|
||||
stop_threaded_test(info);
|
||||
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);
|
||||
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,
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init dmatest_init(void)
|
||||
{
|
||||
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);
|
||||
INIT_LIST_HEAD(&info->channels);
|
||||
/* module parameters are stable, inittime tests are started,
|
||||
* let userspace take over 'run' control
|
||||
*/
|
||||
info->did_init = true;
|
||||
|
||||
ret = dmatest_register_dbgfs(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef MODULE
|
||||
return 0;
|
||||
#else
|
||||
return run_threaded_test(info);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
/* when compiled-in wait for drivers to load first */
|
||||
late_initcall(dmatest_init);
|
||||
|
@ -936,8 +882,9 @@ static void __exit dmatest_exit(void)
|
|||
{
|
||||
struct dmatest_info *info = &test_info;
|
||||
|
||||
debugfs_remove_recursive(info->root);
|
||||
mutex_lock(&info->lock);
|
||||
stop_threaded_test(info);
|
||||
mutex_unlock(&info->lock);
|
||||
}
|
||||
module_exit(dmatest_exit);
|
||||
|
||||
|
|
Loading…
Reference in New Issue