diff mbox

[04/10] dmatest: restore ability to start test at module load and init

Message ID 1383950489.2047.4.camel@dwillia2-mobl1.amr.corp.intel.com (mailing list archive)
State Accepted
Commit a310d037b8d06755c62bb4878c00d19490af5550
Delegated to: Dan Williams
Headers show

Commit Message

Dan Williams Nov. 8, 2013, 10:41 p.m. UTC
Subject: 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>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

---
 Documentation/dmatest.txt |   18 ++-
 drivers/dma/dmatest.c     |  265 ++++++++++++++++++---------------------------
 2 files changed, 116 insertions(+), 167 deletions(-)

On Fri, 2013-11-08 at 08:52 +0100, Linus Walleij wrote: 
> Nice!

> Acked-by.

> 

> Maybe we could put in the syntax for cmdline starting from e.g.

> the bootloader when compiled-in as well?

> 

> I guess it's something like:

> 

> dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1

> dmatest.run=1


Yup, I went ahead and added that to the dmatest.txt file.

Thanks!

--
Dan

Comments

Andy Shevchenko Nov. 12, 2013, 11:49 a.m. UTC | #1
On Fri, 2013-11-08 at 22:41 +0000, Williams, Dan J wrote:
> Subject: 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: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  Documentation/dmatest.txt |   18 ++-
>  drivers/dma/dmatest.c     |  265 ++++++++++++++++++---------------------------
>  2 files changed, 116 insertions(+), 167 deletions(-)
> 
> On Fri, 2013-11-08 at 08:52 +0100, Linus Walleij wrote: 
> > Nice!
> > Acked-by.
> > 
> > Maybe we could put in the syntax for cmdline starting from e.g.
> > the bootloader when compiled-in as well?
> > 
> > I guess it's something like:
> > 
> > dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1
> > dmatest.run=1
> 
> Yup, I went ahead and added that to the dmatest.txt file.
> 
> Thanks!
> 
> --
> Dan
> 
> diff --git a/Documentation/dmatest.txt b/Documentation/dmatest.txt
> index 45b8c95f1a21..e6e16a7f3706 100644
> --- a/Documentation/dmatest.txt
> +++ b/Documentation/dmatest.txt
> @@ -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
> diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
> index 15199edcc366..c5048671daf7 100644
> --- a/drivers/dma/dmatest.c
> +++ b/drivers/dma/dmatest.c
> @@ -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;
> +	bool			did_init;
> +} test_info = {
> +	.channels = LIST_HEAD_INIT(test_info.channels),
> +	.lock = __MUTEX_INITIALIZER(test_info.lock),
> +};
>  
> -	/* debugfs related stuff */
> -	struct dentry		*root;
> +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)");
>  
> -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,
>  		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)
> -{
> -	mutex_lock(&info->lock);
> -	__stop_threaded_test(info);
> -	mutex_unlock(&info->lock);
> -}
> -
> -static int __restart_threaded_test(struct dmatest_info *info, bool run)
> +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);
> -	buf[1] = '\n';
> -	buf[2] = 0x00;
> -	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
> +
> +	return param_get_bool(val, kp);
>  }
>  
> -static ssize_t dtf_write_run(struct file *file, const char __user *user_buf,
> -		size_t count, loff_t *ppos)
> +static int dmatest_run_set(const char *val, const struct kernel_param *kp)
>  {
> -	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);
> +	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;
>  	}
>  
> -	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;
> +	if (is_threaded_test_run(info))
> +		ret = -EBUSY;
> +	else if (dmatest_run)
> +		ret = restart_threaded_test(info, dmatest_run);
>  
> -	info->root = d;
> -
> -	/* Run or stop threaded test */
> -	debugfs_create_file("run", S_IWUSR | S_IRUGO, info->root, info,
> -			    &dtf_run_fops);
> -
> -	return 0;
> +	mutex_unlock(&info->lock);
>  
> -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;
> -
> -	memset(info, 0, sizeof(*info));
> +	int ret = 0;
>  
> -	mutex_init(&info->lock);
> -	INIT_LIST_HEAD(&info->channels);
> +	if (dmatest_run) {
> +		mutex_lock(&info->lock);
> +		ret = run_threaded_test(info);
> +		mutex_unlock(&info->lock);
> +	}
>  
> -	ret = dmatest_register_dbgfs(info);
> -	if (ret)
> -		return ret;
> +	/* module parameters are stable, inittime tests are started,
> +	 * let userspace take over 'run' control
> +	 */
> +	info->did_init = true;
>  
> -#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);
>
diff mbox

Patch

diff --git a/Documentation/dmatest.txt b/Documentation/dmatest.txt
index 45b8c95f1a21..e6e16a7f3706 100644
--- a/Documentation/dmatest.txt
+++ b/Documentation/dmatest.txt
@@ -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
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 15199edcc366..c5048671daf7 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -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;
+	bool			did_init;
+} test_info = {
+	.channels = LIST_HEAD_INIT(test_info.channels),
+	.lock = __MUTEX_INITIALIZER(test_info.lock),
+};
 
-	/* debugfs related stuff */
-	struct dentry		*root;
+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)");
 
-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,
 		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)
-{
-	mutex_lock(&info->lock);
-	__stop_threaded_test(info);
-	mutex_unlock(&info->lock);
-}
-
-static int __restart_threaded_test(struct dmatest_info *info, bool run)
+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);
-	buf[1] = '\n';
-	buf[2] = 0x00;
-	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+
+	return param_get_bool(val, kp);
 }
 
-static ssize_t dtf_write_run(struct file *file, const char __user *user_buf,
-		size_t count, loff_t *ppos)
+static int dmatest_run_set(const char *val, const struct kernel_param *kp)
 {
-	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);
+	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;
 	}
 
-	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;
+	if (is_threaded_test_run(info))
+		ret = -EBUSY;
+	else if (dmatest_run)
+		ret = restart_threaded_test(info, dmatest_run);
 
-	info->root = d;
-
-	/* Run or stop threaded test */
-	debugfs_create_file("run", S_IWUSR | S_IRUGO, info->root, info,
-			    &dtf_run_fops);
-
-	return 0;
+	mutex_unlock(&info->lock);
 
-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;
-
-	memset(info, 0, sizeof(*info));
+	int ret = 0;
 
-	mutex_init(&info->lock);
-	INIT_LIST_HEAD(&info->channels);
+	if (dmatest_run) {
+		mutex_lock(&info->lock);
+		ret = run_threaded_test(info);
+		mutex_unlock(&info->lock);
+	}
 
-	ret = dmatest_register_dbgfs(info);
-	if (ret)
-		return ret;
+	/* module parameters are stable, inittime tests are started,
+	 * let userspace take over 'run' control
+	 */
+	info->did_init = true;
 
-#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);