xref: /openbmc/linux/lib/test_kmod.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
16cad1ecdSLuis Chamberlain // SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
2d9c6a72dSLuis R. Rodriguez /*
3d9c6a72dSLuis R. Rodriguez  * kmod stress test driver
4d9c6a72dSLuis R. Rodriguez  *
5d9c6a72dSLuis R. Rodriguez  * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
6d9c6a72dSLuis R. Rodriguez  */
7d9c6a72dSLuis R. Rodriguez #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8d9c6a72dSLuis R. Rodriguez 
9d9c6a72dSLuis R. Rodriguez /*
10d9c6a72dSLuis R. Rodriguez  * This driver provides an interface to trigger and test the kernel's
11d9c6a72dSLuis R. Rodriguez  * module loader through a series of configurations and a few triggers.
12d9c6a72dSLuis R. Rodriguez  * To test this driver use the following script as root:
13d9c6a72dSLuis R. Rodriguez  *
14d9c6a72dSLuis R. Rodriguez  * tools/testing/selftests/kmod/kmod.sh --help
15d9c6a72dSLuis R. Rodriguez  */
16d9c6a72dSLuis R. Rodriguez 
17d9c6a72dSLuis R. Rodriguez #include <linux/kernel.h>
18d9c6a72dSLuis R. Rodriguez #include <linux/module.h>
19d9c6a72dSLuis R. Rodriguez #include <linux/kmod.h>
20d9c6a72dSLuis R. Rodriguez #include <linux/printk.h>
21d9c6a72dSLuis R. Rodriguez #include <linux/kthread.h>
22d9c6a72dSLuis R. Rodriguez #include <linux/sched.h>
23d9c6a72dSLuis R. Rodriguez #include <linux/fs.h>
24d9c6a72dSLuis R. Rodriguez #include <linux/miscdevice.h>
25d9c6a72dSLuis R. Rodriguez #include <linux/vmalloc.h>
26d9c6a72dSLuis R. Rodriguez #include <linux/slab.h>
27d9c6a72dSLuis R. Rodriguez #include <linux/device.h>
28d9c6a72dSLuis R. Rodriguez 
29d9c6a72dSLuis R. Rodriguez #define TEST_START_NUM_THREADS	50
30d9c6a72dSLuis R. Rodriguez #define TEST_START_DRIVER	"test_module"
31d9c6a72dSLuis R. Rodriguez #define TEST_START_TEST_FS	"xfs"
32d9c6a72dSLuis R. Rodriguez #define TEST_START_TEST_CASE	TEST_KMOD_DRIVER
33d9c6a72dSLuis R. Rodriguez 
34d9c6a72dSLuis R. Rodriguez 
35d9c6a72dSLuis R. Rodriguez static bool force_init_test = false;
36d9c6a72dSLuis R. Rodriguez module_param(force_init_test, bool_enable_only, 0644);
37d9c6a72dSLuis R. Rodriguez MODULE_PARM_DESC(force_init_test,
38d9c6a72dSLuis R. Rodriguez 		 "Force kicking a test immediately after driver loads");
39d9c6a72dSLuis R. Rodriguez 
40d9c6a72dSLuis R. Rodriguez /*
41d9c6a72dSLuis R. Rodriguez  * For device allocation / registration
42d9c6a72dSLuis R. Rodriguez  */
43d9c6a72dSLuis R. Rodriguez static DEFINE_MUTEX(reg_dev_mutex);
44d9c6a72dSLuis R. Rodriguez static LIST_HEAD(reg_test_devs);
45d9c6a72dSLuis R. Rodriguez 
46d9c6a72dSLuis R. Rodriguez /*
47d9c6a72dSLuis R. Rodriguez  * num_test_devs actually represents the *next* ID of the next
48d9c6a72dSLuis R. Rodriguez  * device we will allow to create.
49d9c6a72dSLuis R. Rodriguez  */
50d9c6a72dSLuis R. Rodriguez static int num_test_devs;
51d9c6a72dSLuis R. Rodriguez 
52d9c6a72dSLuis R. Rodriguez /**
53d9c6a72dSLuis R. Rodriguez  * enum kmod_test_case - linker table test case
54d9c6a72dSLuis R. Rodriguez  * @TEST_KMOD_DRIVER: stress tests request_module()
55d9c6a72dSLuis R. Rodriguez  * @TEST_KMOD_FS_TYPE: stress tests get_fs_type()
56*c093a74dSRandy Dunlap  *
57*c093a74dSRandy Dunlap  * If you add a  test case, please be sure to review if you need to set
58*c093a74dSRandy Dunlap  * @need_mod_put for your tests case.
59d9c6a72dSLuis R. Rodriguez  */
60d9c6a72dSLuis R. Rodriguez enum kmod_test_case {
61d9c6a72dSLuis R. Rodriguez 	__TEST_KMOD_INVALID = 0,
62d9c6a72dSLuis R. Rodriguez 
63d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DRIVER,
64d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_FS_TYPE,
65d9c6a72dSLuis R. Rodriguez 
66d9c6a72dSLuis R. Rodriguez 	__TEST_KMOD_MAX,
67d9c6a72dSLuis R. Rodriguez };
68d9c6a72dSLuis R. Rodriguez 
69d9c6a72dSLuis R. Rodriguez struct test_config {
70d9c6a72dSLuis R. Rodriguez 	char *test_driver;
71d9c6a72dSLuis R. Rodriguez 	char *test_fs;
72d9c6a72dSLuis R. Rodriguez 	unsigned int num_threads;
73d9c6a72dSLuis R. Rodriguez 	enum kmod_test_case test_case;
74d9c6a72dSLuis R. Rodriguez 	int test_result;
75d9c6a72dSLuis R. Rodriguez };
76d9c6a72dSLuis R. Rodriguez 
77d9c6a72dSLuis R. Rodriguez struct kmod_test_device;
78d9c6a72dSLuis R. Rodriguez 
79d9c6a72dSLuis R. Rodriguez /**
80*c093a74dSRandy Dunlap  * struct kmod_test_device_info - thread info
81d9c6a72dSLuis R. Rodriguez  *
82d9c6a72dSLuis R. Rodriguez  * @ret_sync: return value if request_module() is used, sync request for
83d9c6a72dSLuis R. Rodriguez  * 	@TEST_KMOD_DRIVER
84d9c6a72dSLuis R. Rodriguez  * @fs_sync: return value of get_fs_type() for @TEST_KMOD_FS_TYPE
85d9c6a72dSLuis R. Rodriguez  * @thread_idx: thread ID
86d9c6a72dSLuis R. Rodriguez  * @test_dev: test device test is being performed under
87d9c6a72dSLuis R. Rodriguez  * @need_mod_put: Some tests (get_fs_type() is one) requires putting the module
88d9c6a72dSLuis R. Rodriguez  *	(module_put(fs_sync->owner)) when done, otherwise you will not be able
89d9c6a72dSLuis R. Rodriguez  *	to unload the respective modules and re-test. We use this to keep
90d9c6a72dSLuis R. Rodriguez  *	accounting of when we need this and to help out in case we need to
91d9c6a72dSLuis R. Rodriguez  *	error out and deal with module_put() on error.
92d9c6a72dSLuis R. Rodriguez  */
93d9c6a72dSLuis R. Rodriguez struct kmod_test_device_info {
94d9c6a72dSLuis R. Rodriguez 	int ret_sync;
95d9c6a72dSLuis R. Rodriguez 	struct file_system_type *fs_sync;
96d9c6a72dSLuis R. Rodriguez 	struct task_struct *task_sync;
97d9c6a72dSLuis R. Rodriguez 	unsigned int thread_idx;
98d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev;
99d9c6a72dSLuis R. Rodriguez 	bool need_mod_put;
100d9c6a72dSLuis R. Rodriguez };
101d9c6a72dSLuis R. Rodriguez 
102d9c6a72dSLuis R. Rodriguez /**
103*c093a74dSRandy Dunlap  * struct kmod_test_device - test device to help test kmod
104d9c6a72dSLuis R. Rodriguez  *
105d9c6a72dSLuis R. Rodriguez  * @dev_idx: unique ID for test device
106d9c6a72dSLuis R. Rodriguez  * @config: configuration for the test
107d9c6a72dSLuis R. Rodriguez  * @misc_dev: we use a misc device under the hood
108d9c6a72dSLuis R. Rodriguez  * @dev: pointer to misc_dev's own struct device
109d9c6a72dSLuis R. Rodriguez  * @config_mutex: protects configuration of test
110d9c6a72dSLuis R. Rodriguez  * @trigger_mutex: the test trigger can only be fired once at a time
111d9c6a72dSLuis R. Rodriguez  * @thread_lock: protects @done count, and the @info per each thread
112d9c6a72dSLuis R. Rodriguez  * @done: number of threads which have completed or failed
113d9c6a72dSLuis R. Rodriguez  * @test_is_oom: when we run out of memory, use this to halt moving forward
114d9c6a72dSLuis R. Rodriguez  * @kthreads_done: completion used to signal when all work is done
115d9c6a72dSLuis R. Rodriguez  * @list: needed to be part of the reg_test_devs
116d9c6a72dSLuis R. Rodriguez  * @info: array of info for each thread
117d9c6a72dSLuis R. Rodriguez  */
118d9c6a72dSLuis R. Rodriguez struct kmod_test_device {
119d9c6a72dSLuis R. Rodriguez 	int dev_idx;
120d9c6a72dSLuis R. Rodriguez 	struct test_config config;
121d9c6a72dSLuis R. Rodriguez 	struct miscdevice misc_dev;
122d9c6a72dSLuis R. Rodriguez 	struct device *dev;
123d9c6a72dSLuis R. Rodriguez 	struct mutex config_mutex;
124d9c6a72dSLuis R. Rodriguez 	struct mutex trigger_mutex;
125d9c6a72dSLuis R. Rodriguez 	struct mutex thread_mutex;
126d9c6a72dSLuis R. Rodriguez 
127d9c6a72dSLuis R. Rodriguez 	unsigned int done;
128d9c6a72dSLuis R. Rodriguez 
129d9c6a72dSLuis R. Rodriguez 	bool test_is_oom;
130d9c6a72dSLuis R. Rodriguez 	struct completion kthreads_done;
131d9c6a72dSLuis R. Rodriguez 	struct list_head list;
132d9c6a72dSLuis R. Rodriguez 
133d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device_info *info;
134d9c6a72dSLuis R. Rodriguez };
135d9c6a72dSLuis R. Rodriguez 
test_case_str(enum kmod_test_case test_case)136d9c6a72dSLuis R. Rodriguez static const char *test_case_str(enum kmod_test_case test_case)
137d9c6a72dSLuis R. Rodriguez {
138d9c6a72dSLuis R. Rodriguez 	switch (test_case) {
139d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_DRIVER:
140d9c6a72dSLuis R. Rodriguez 		return "TEST_KMOD_DRIVER";
141d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_FS_TYPE:
142d9c6a72dSLuis R. Rodriguez 		return "TEST_KMOD_FS_TYPE";
143d9c6a72dSLuis R. Rodriguez 	default:
144d9c6a72dSLuis R. Rodriguez 		return "invalid";
145d9c6a72dSLuis R. Rodriguez 	}
146d9c6a72dSLuis R. Rodriguez }
147d9c6a72dSLuis R. Rodriguez 
dev_to_misc_dev(struct device * dev)148d9c6a72dSLuis R. Rodriguez static struct miscdevice *dev_to_misc_dev(struct device *dev)
149d9c6a72dSLuis R. Rodriguez {
150d9c6a72dSLuis R. Rodriguez 	return dev_get_drvdata(dev);
151d9c6a72dSLuis R. Rodriguez }
152d9c6a72dSLuis R. Rodriguez 
misc_dev_to_test_dev(struct miscdevice * misc_dev)153d9c6a72dSLuis R. Rodriguez static struct kmod_test_device *misc_dev_to_test_dev(struct miscdevice *misc_dev)
154d9c6a72dSLuis R. Rodriguez {
155d9c6a72dSLuis R. Rodriguez 	return container_of(misc_dev, struct kmod_test_device, misc_dev);
156d9c6a72dSLuis R. Rodriguez }
157d9c6a72dSLuis R. Rodriguez 
dev_to_test_dev(struct device * dev)158d9c6a72dSLuis R. Rodriguez static struct kmod_test_device *dev_to_test_dev(struct device *dev)
159d9c6a72dSLuis R. Rodriguez {
160d9c6a72dSLuis R. Rodriguez 	struct miscdevice *misc_dev;
161d9c6a72dSLuis R. Rodriguez 
162d9c6a72dSLuis R. Rodriguez 	misc_dev = dev_to_misc_dev(dev);
163d9c6a72dSLuis R. Rodriguez 
164d9c6a72dSLuis R. Rodriguez 	return misc_dev_to_test_dev(misc_dev);
165d9c6a72dSLuis R. Rodriguez }
166d9c6a72dSLuis R. Rodriguez 
167d9c6a72dSLuis R. Rodriguez /* Must run with thread_mutex held */
kmod_test_done_check(struct kmod_test_device * test_dev,unsigned int idx)168d9c6a72dSLuis R. Rodriguez static void kmod_test_done_check(struct kmod_test_device *test_dev,
169d9c6a72dSLuis R. Rodriguez 				 unsigned int idx)
170d9c6a72dSLuis R. Rodriguez {
171d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
172d9c6a72dSLuis R. Rodriguez 
173d9c6a72dSLuis R. Rodriguez 	test_dev->done++;
174d9c6a72dSLuis R. Rodriguez 	dev_dbg(test_dev->dev, "Done thread count: %u\n", test_dev->done);
175d9c6a72dSLuis R. Rodriguez 
176d9c6a72dSLuis R. Rodriguez 	if (test_dev->done == config->num_threads) {
177d9c6a72dSLuis R. Rodriguez 		dev_info(test_dev->dev, "Done: %u threads have all run now\n",
178d9c6a72dSLuis R. Rodriguez 			 test_dev->done);
179d9c6a72dSLuis R. Rodriguez 		dev_info(test_dev->dev, "Last thread to run: %u\n", idx);
180d9c6a72dSLuis R. Rodriguez 		complete(&test_dev->kthreads_done);
181d9c6a72dSLuis R. Rodriguez 	}
182d9c6a72dSLuis R. Rodriguez }
183d9c6a72dSLuis R. Rodriguez 
test_kmod_put_module(struct kmod_test_device_info * info)184d9c6a72dSLuis R. Rodriguez static void test_kmod_put_module(struct kmod_test_device_info *info)
185d9c6a72dSLuis R. Rodriguez {
186d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = info->test_dev;
187d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
188d9c6a72dSLuis R. Rodriguez 
189d9c6a72dSLuis R. Rodriguez 	if (!info->need_mod_put)
190d9c6a72dSLuis R. Rodriguez 		return;
191d9c6a72dSLuis R. Rodriguez 
192d9c6a72dSLuis R. Rodriguez 	switch (config->test_case) {
193d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_DRIVER:
194d9c6a72dSLuis R. Rodriguez 		break;
195d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_FS_TYPE:
1968f0259c2SDan Carpenter 		if (info->fs_sync && info->fs_sync->owner)
197d9c6a72dSLuis R. Rodriguez 			module_put(info->fs_sync->owner);
198d9c6a72dSLuis R. Rodriguez 		break;
199d9c6a72dSLuis R. Rodriguez 	default:
200d9c6a72dSLuis R. Rodriguez 		BUG();
201d9c6a72dSLuis R. Rodriguez 	}
202d9c6a72dSLuis R. Rodriguez 
203d9c6a72dSLuis R. Rodriguez 	info->need_mod_put = true;
204d9c6a72dSLuis R. Rodriguez }
205d9c6a72dSLuis R. Rodriguez 
run_request(void * data)206d9c6a72dSLuis R. Rodriguez static int run_request(void *data)
207d9c6a72dSLuis R. Rodriguez {
208d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device_info *info = data;
209d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = info->test_dev;
210d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
211d9c6a72dSLuis R. Rodriguez 
212d9c6a72dSLuis R. Rodriguez 	switch (config->test_case) {
213d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_DRIVER:
214d9c6a72dSLuis R. Rodriguez 		info->ret_sync = request_module("%s", config->test_driver);
215d9c6a72dSLuis R. Rodriguez 		break;
216d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_FS_TYPE:
217d9c6a72dSLuis R. Rodriguez 		info->fs_sync = get_fs_type(config->test_fs);
218d9c6a72dSLuis R. Rodriguez 		info->need_mod_put = true;
219d9c6a72dSLuis R. Rodriguez 		break;
220d9c6a72dSLuis R. Rodriguez 	default:
221d9c6a72dSLuis R. Rodriguez 		/* __trigger_config_run() already checked for test sanity */
222d9c6a72dSLuis R. Rodriguez 		BUG();
223d9c6a72dSLuis R. Rodriguez 		return -EINVAL;
224d9c6a72dSLuis R. Rodriguez 	}
225d9c6a72dSLuis R. Rodriguez 
226d9c6a72dSLuis R. Rodriguez 	dev_dbg(test_dev->dev, "Ran thread %u\n", info->thread_idx);
227d9c6a72dSLuis R. Rodriguez 
228d9c6a72dSLuis R. Rodriguez 	test_kmod_put_module(info);
229d9c6a72dSLuis R. Rodriguez 
230d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->thread_mutex);
231d9c6a72dSLuis R. Rodriguez 	info->task_sync = NULL;
232d9c6a72dSLuis R. Rodriguez 	kmod_test_done_check(test_dev, info->thread_idx);
233d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->thread_mutex);
234d9c6a72dSLuis R. Rodriguez 
235d9c6a72dSLuis R. Rodriguez 	return 0;
236d9c6a72dSLuis R. Rodriguez }
237d9c6a72dSLuis R. Rodriguez 
tally_work_test(struct kmod_test_device_info * info)238d9c6a72dSLuis R. Rodriguez static int tally_work_test(struct kmod_test_device_info *info)
239d9c6a72dSLuis R. Rodriguez {
240d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = info->test_dev;
241d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
242d9c6a72dSLuis R. Rodriguez 	int err_ret = 0;
243d9c6a72dSLuis R. Rodriguez 
244d9c6a72dSLuis R. Rodriguez 	switch (config->test_case) {
245d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_DRIVER:
246d9c6a72dSLuis R. Rodriguez 		/*
247d9c6a72dSLuis R. Rodriguez 		 * Only capture errors, if one is found that's
248d9c6a72dSLuis R. Rodriguez 		 * enough, for now.
249d9c6a72dSLuis R. Rodriguez 		 */
250d9c6a72dSLuis R. Rodriguez 		if (info->ret_sync != 0)
251d9c6a72dSLuis R. Rodriguez 			err_ret = info->ret_sync;
252d9c6a72dSLuis R. Rodriguez 		dev_info(test_dev->dev,
253d9c6a72dSLuis R. Rodriguez 			 "Sync thread %d return status: %d\n",
254d9c6a72dSLuis R. Rodriguez 			 info->thread_idx, info->ret_sync);
255d9c6a72dSLuis R. Rodriguez 		break;
256d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_FS_TYPE:
257d9c6a72dSLuis R. Rodriguez 		/* For now we make this simple */
258d9c6a72dSLuis R. Rodriguez 		if (!info->fs_sync)
259d9c6a72dSLuis R. Rodriguez 			err_ret = -EINVAL;
260d9c6a72dSLuis R. Rodriguez 		dev_info(test_dev->dev, "Sync thread %u fs: %s\n",
261d9c6a72dSLuis R. Rodriguez 			 info->thread_idx, info->fs_sync ? config->test_fs :
262d9c6a72dSLuis R. Rodriguez 			 "NULL");
263d9c6a72dSLuis R. Rodriguez 		break;
264d9c6a72dSLuis R. Rodriguez 	default:
265d9c6a72dSLuis R. Rodriguez 		BUG();
266d9c6a72dSLuis R. Rodriguez 	}
267d9c6a72dSLuis R. Rodriguez 
268d9c6a72dSLuis R. Rodriguez 	return err_ret;
269d9c6a72dSLuis R. Rodriguez }
270d9c6a72dSLuis R. Rodriguez 
271d9c6a72dSLuis R. Rodriguez /*
272d9c6a72dSLuis R. Rodriguez  * XXX: add result option to display if all errors did not match.
273d9c6a72dSLuis R. Rodriguez  * For now we just keep any error code if one was found.
274d9c6a72dSLuis R. Rodriguez  *
275d9c6a72dSLuis R. Rodriguez  * If this ran it means *all* tasks were created fine and we
276d9c6a72dSLuis R. Rodriguez  * are now just collecting results.
277d9c6a72dSLuis R. Rodriguez  *
27853b0fe36SZhen Lei  * Only propagate errors, do not override with a subsequent success case.
279d9c6a72dSLuis R. Rodriguez  */
tally_up_work(struct kmod_test_device * test_dev)280d9c6a72dSLuis R. Rodriguez static void tally_up_work(struct kmod_test_device *test_dev)
281d9c6a72dSLuis R. Rodriguez {
282d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
283d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device_info *info;
284d9c6a72dSLuis R. Rodriguez 	unsigned int idx;
285d9c6a72dSLuis R. Rodriguez 	int err_ret = 0;
286d9c6a72dSLuis R. Rodriguez 	int ret = 0;
287d9c6a72dSLuis R. Rodriguez 
288d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->thread_mutex);
289d9c6a72dSLuis R. Rodriguez 
290d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Results:\n");
291d9c6a72dSLuis R. Rodriguez 
292d9c6a72dSLuis R. Rodriguez 	for (idx=0; idx < config->num_threads; idx++) {
293d9c6a72dSLuis R. Rodriguez 		info = &test_dev->info[idx];
294d9c6a72dSLuis R. Rodriguez 		ret = tally_work_test(info);
295d9c6a72dSLuis R. Rodriguez 		if (ret)
296d9c6a72dSLuis R. Rodriguez 			err_ret = ret;
297d9c6a72dSLuis R. Rodriguez 	}
298d9c6a72dSLuis R. Rodriguez 
299d9c6a72dSLuis R. Rodriguez 	/*
300d9c6a72dSLuis R. Rodriguez 	 * Note: request_module() returns 256 for a module not found even
301d9c6a72dSLuis R. Rodriguez 	 * though modprobe itself returns 1.
302d9c6a72dSLuis R. Rodriguez 	 */
303d9c6a72dSLuis R. Rodriguez 	config->test_result = err_ret;
304d9c6a72dSLuis R. Rodriguez 
305d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->thread_mutex);
306d9c6a72dSLuis R. Rodriguez }
307d9c6a72dSLuis R. Rodriguez 
try_one_request(struct kmod_test_device * test_dev,unsigned int idx)308d9c6a72dSLuis R. Rodriguez static int try_one_request(struct kmod_test_device *test_dev, unsigned int idx)
309d9c6a72dSLuis R. Rodriguez {
310d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device_info *info = &test_dev->info[idx];
311d9c6a72dSLuis R. Rodriguez 	int fail_ret = -ENOMEM;
312d9c6a72dSLuis R. Rodriguez 
313d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->thread_mutex);
314d9c6a72dSLuis R. Rodriguez 
315d9c6a72dSLuis R. Rodriguez 	info->thread_idx = idx;
316d9c6a72dSLuis R. Rodriguez 	info->test_dev = test_dev;
317d9c6a72dSLuis R. Rodriguez 	info->task_sync = kthread_run(run_request, info, "%s-%u",
318d9c6a72dSLuis R. Rodriguez 				      KBUILD_MODNAME, idx);
319d9c6a72dSLuis R. Rodriguez 
320d9c6a72dSLuis R. Rodriguez 	if (!info->task_sync || IS_ERR(info->task_sync)) {
321d9c6a72dSLuis R. Rodriguez 		test_dev->test_is_oom = true;
322d9c6a72dSLuis R. Rodriguez 		dev_err(test_dev->dev, "Setting up thread %u failed\n", idx);
323d9c6a72dSLuis R. Rodriguez 		info->task_sync = NULL;
324d9c6a72dSLuis R. Rodriguez 		goto err_out;
325d9c6a72dSLuis R. Rodriguez 	} else
326d9c6a72dSLuis R. Rodriguez 		dev_dbg(test_dev->dev, "Kicked off thread %u\n", idx);
327d9c6a72dSLuis R. Rodriguez 
328d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->thread_mutex);
329d9c6a72dSLuis R. Rodriguez 
330d9c6a72dSLuis R. Rodriguez 	return 0;
331d9c6a72dSLuis R. Rodriguez 
332d9c6a72dSLuis R. Rodriguez err_out:
333d9c6a72dSLuis R. Rodriguez 	info->ret_sync = fail_ret;
334d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->thread_mutex);
335d9c6a72dSLuis R. Rodriguez 
336d9c6a72dSLuis R. Rodriguez 	return fail_ret;
337d9c6a72dSLuis R. Rodriguez }
338d9c6a72dSLuis R. Rodriguez 
test_dev_kmod_stop_tests(struct kmod_test_device * test_dev)339d9c6a72dSLuis R. Rodriguez static void test_dev_kmod_stop_tests(struct kmod_test_device *test_dev)
340d9c6a72dSLuis R. Rodriguez {
341d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
342d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device_info *info;
343d9c6a72dSLuis R. Rodriguez 	unsigned int i;
344d9c6a72dSLuis R. Rodriguez 
345d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Ending request_module() tests\n");
346d9c6a72dSLuis R. Rodriguez 
347d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->thread_mutex);
348d9c6a72dSLuis R. Rodriguez 
349d9c6a72dSLuis R. Rodriguez 	for (i=0; i < config->num_threads; i++) {
350d9c6a72dSLuis R. Rodriguez 		info = &test_dev->info[i];
351d9c6a72dSLuis R. Rodriguez 		if (info->task_sync && !IS_ERR(info->task_sync)) {
352d9c6a72dSLuis R. Rodriguez 			dev_info(test_dev->dev,
353d9c6a72dSLuis R. Rodriguez 				 "Stopping still-running thread %i\n", i);
354d9c6a72dSLuis R. Rodriguez 			kthread_stop(info->task_sync);
355d9c6a72dSLuis R. Rodriguez 		}
356d9c6a72dSLuis R. Rodriguez 
357d9c6a72dSLuis R. Rodriguez 		/*
358d9c6a72dSLuis R. Rodriguez 		 * info->task_sync is well protected, it can only be
359d9c6a72dSLuis R. Rodriguez 		 * NULL or a pointer to a struct. If its NULL we either
360d9c6a72dSLuis R. Rodriguez 		 * never ran, or we did and we completed the work. Completed
361d9c6a72dSLuis R. Rodriguez 		 * tasks *always* put the module for us. This is a sanity
362d9c6a72dSLuis R. Rodriguez 		 * check -- just in case.
363d9c6a72dSLuis R. Rodriguez 		 */
364d9c6a72dSLuis R. Rodriguez 		if (info->task_sync && info->need_mod_put)
365d9c6a72dSLuis R. Rodriguez 			test_kmod_put_module(info);
366d9c6a72dSLuis R. Rodriguez 	}
367d9c6a72dSLuis R. Rodriguez 
368d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->thread_mutex);
369d9c6a72dSLuis R. Rodriguez }
370d9c6a72dSLuis R. Rodriguez 
371d9c6a72dSLuis R. Rodriguez /*
372d9c6a72dSLuis R. Rodriguez  * Only wait *iff* we did not run into any errors during all of our thread
373d9c6a72dSLuis R. Rodriguez  * set up. If run into any issues we stop threads and just bail out with
374d9c6a72dSLuis R. Rodriguez  * an error to the trigger. This also means we don't need any tally work
375d9c6a72dSLuis R. Rodriguez  * for any threads which fail.
376d9c6a72dSLuis R. Rodriguez  */
try_requests(struct kmod_test_device * test_dev)377d9c6a72dSLuis R. Rodriguez static int try_requests(struct kmod_test_device *test_dev)
378d9c6a72dSLuis R. Rodriguez {
379d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
380d9c6a72dSLuis R. Rodriguez 	unsigned int idx;
381d9c6a72dSLuis R. Rodriguez 	int ret;
382d9c6a72dSLuis R. Rodriguez 	bool any_error = false;
383d9c6a72dSLuis R. Rodriguez 
384d9c6a72dSLuis R. Rodriguez 	for (idx=0; idx < config->num_threads; idx++) {
385d9c6a72dSLuis R. Rodriguez 		if (test_dev->test_is_oom) {
386d9c6a72dSLuis R. Rodriguez 			any_error = true;
387d9c6a72dSLuis R. Rodriguez 			break;
388d9c6a72dSLuis R. Rodriguez 		}
389d9c6a72dSLuis R. Rodriguez 
390d9c6a72dSLuis R. Rodriguez 		ret = try_one_request(test_dev, idx);
391d9c6a72dSLuis R. Rodriguez 		if (ret) {
392d9c6a72dSLuis R. Rodriguez 			any_error = true;
393d9c6a72dSLuis R. Rodriguez 			break;
394d9c6a72dSLuis R. Rodriguez 		}
395d9c6a72dSLuis R. Rodriguez 	}
396d9c6a72dSLuis R. Rodriguez 
397d9c6a72dSLuis R. Rodriguez 	if (!any_error) {
398d9c6a72dSLuis R. Rodriguez 		test_dev->test_is_oom = false;
399d9c6a72dSLuis R. Rodriguez 		dev_info(test_dev->dev,
400d9c6a72dSLuis R. Rodriguez 			 "No errors were found while initializing threads\n");
401d9c6a72dSLuis R. Rodriguez 		wait_for_completion(&test_dev->kthreads_done);
402d9c6a72dSLuis R. Rodriguez 		tally_up_work(test_dev);
403d9c6a72dSLuis R. Rodriguez 	} else {
404d9c6a72dSLuis R. Rodriguez 		test_dev->test_is_oom = true;
405d9c6a72dSLuis R. Rodriguez 		dev_info(test_dev->dev,
406d9c6a72dSLuis R. Rodriguez 			 "At least one thread failed to start, stop all work\n");
407d9c6a72dSLuis R. Rodriguez 		test_dev_kmod_stop_tests(test_dev);
408d9c6a72dSLuis R. Rodriguez 		return -ENOMEM;
409d9c6a72dSLuis R. Rodriguez 	}
410d9c6a72dSLuis R. Rodriguez 
411d9c6a72dSLuis R. Rodriguez 	return 0;
412d9c6a72dSLuis R. Rodriguez }
413d9c6a72dSLuis R. Rodriguez 
run_test_driver(struct kmod_test_device * test_dev)414d9c6a72dSLuis R. Rodriguez static int run_test_driver(struct kmod_test_device *test_dev)
415d9c6a72dSLuis R. Rodriguez {
416d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
417d9c6a72dSLuis R. Rodriguez 
418d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Test case: %s (%u)\n",
419d9c6a72dSLuis R. Rodriguez 		 test_case_str(config->test_case),
420d9c6a72dSLuis R. Rodriguez 		 config->test_case);
421d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Test driver to load: %s\n",
422d9c6a72dSLuis R. Rodriguez 		 config->test_driver);
423d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Number of threads to run: %u\n",
424d9c6a72dSLuis R. Rodriguez 		 config->num_threads);
425d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Thread IDs will range from 0 - %u\n",
426d9c6a72dSLuis R. Rodriguez 		 config->num_threads - 1);
427d9c6a72dSLuis R. Rodriguez 
428d9c6a72dSLuis R. Rodriguez 	return try_requests(test_dev);
429d9c6a72dSLuis R. Rodriguez }
430d9c6a72dSLuis R. Rodriguez 
run_test_fs_type(struct kmod_test_device * test_dev)431d9c6a72dSLuis R. Rodriguez static int run_test_fs_type(struct kmod_test_device *test_dev)
432d9c6a72dSLuis R. Rodriguez {
433d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
434d9c6a72dSLuis R. Rodriguez 
435d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Test case: %s (%u)\n",
436d9c6a72dSLuis R. Rodriguez 		 test_case_str(config->test_case),
437d9c6a72dSLuis R. Rodriguez 		 config->test_case);
438d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Test filesystem to load: %s\n",
439d9c6a72dSLuis R. Rodriguez 		 config->test_fs);
440d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Number of threads to run: %u\n",
441d9c6a72dSLuis R. Rodriguez 		 config->num_threads);
442d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "Thread IDs will range from 0 - %u\n",
443d9c6a72dSLuis R. Rodriguez 		 config->num_threads - 1);
444d9c6a72dSLuis R. Rodriguez 
445d9c6a72dSLuis R. Rodriguez 	return try_requests(test_dev);
446d9c6a72dSLuis R. Rodriguez }
447d9c6a72dSLuis R. Rodriguez 
config_show(struct device * dev,struct device_attribute * attr,char * buf)448d9c6a72dSLuis R. Rodriguez static ssize_t config_show(struct device *dev,
449d9c6a72dSLuis R. Rodriguez 			   struct device_attribute *attr,
450d9c6a72dSLuis R. Rodriguez 			   char *buf)
451d9c6a72dSLuis R. Rodriguez {
452d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
453d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
454d9c6a72dSLuis R. Rodriguez 	int len = 0;
455d9c6a72dSLuis R. Rodriguez 
456d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
457d9c6a72dSLuis R. Rodriguez 
458d9c6a72dSLuis R. Rodriguez 	len += snprintf(buf, PAGE_SIZE,
459d9c6a72dSLuis R. Rodriguez 			"Custom trigger configuration for: %s\n",
460d9c6a72dSLuis R. Rodriguez 			dev_name(dev));
461d9c6a72dSLuis R. Rodriguez 
462d9c6a72dSLuis R. Rodriguez 	len += snprintf(buf+len, PAGE_SIZE - len,
463d9c6a72dSLuis R. Rodriguez 			"Number of threads:\t%u\n",
464d9c6a72dSLuis R. Rodriguez 			config->num_threads);
465d9c6a72dSLuis R. Rodriguez 
466d9c6a72dSLuis R. Rodriguez 	len += snprintf(buf+len, PAGE_SIZE - len,
467d9c6a72dSLuis R. Rodriguez 			"Test_case:\t%s (%u)\n",
468d9c6a72dSLuis R. Rodriguez 			test_case_str(config->test_case),
469d9c6a72dSLuis R. Rodriguez 			config->test_case);
470d9c6a72dSLuis R. Rodriguez 
471d9c6a72dSLuis R. Rodriguez 	if (config->test_driver)
472d9c6a72dSLuis R. Rodriguez 		len += snprintf(buf+len, PAGE_SIZE - len,
473d9c6a72dSLuis R. Rodriguez 				"driver:\t%s\n",
474d9c6a72dSLuis R. Rodriguez 				config->test_driver);
475d9c6a72dSLuis R. Rodriguez 	else
476d9c6a72dSLuis R. Rodriguez 		len += snprintf(buf+len, PAGE_SIZE - len,
477a4afe8cdSColin Ian King 				"driver:\tEMPTY\n");
478d9c6a72dSLuis R. Rodriguez 
479d9c6a72dSLuis R. Rodriguez 	if (config->test_fs)
480d9c6a72dSLuis R. Rodriguez 		len += snprintf(buf+len, PAGE_SIZE - len,
481d9c6a72dSLuis R. Rodriguez 				"fs:\t%s\n",
482d9c6a72dSLuis R. Rodriguez 				config->test_fs);
483d9c6a72dSLuis R. Rodriguez 	else
484d9c6a72dSLuis R. Rodriguez 		len += snprintf(buf+len, PAGE_SIZE - len,
485a4afe8cdSColin Ian King 				"fs:\tEMPTY\n");
486d9c6a72dSLuis R. Rodriguez 
487d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
488d9c6a72dSLuis R. Rodriguez 
489d9c6a72dSLuis R. Rodriguez 	return len;
490d9c6a72dSLuis R. Rodriguez }
491d9c6a72dSLuis R. Rodriguez static DEVICE_ATTR_RO(config);
492d9c6a72dSLuis R. Rodriguez 
493d9c6a72dSLuis R. Rodriguez /*
494d9c6a72dSLuis R. Rodriguez  * This ensures we don't allow kicking threads through if our configuration
495d9c6a72dSLuis R. Rodriguez  * is faulty.
496d9c6a72dSLuis R. Rodriguez  */
__trigger_config_run(struct kmod_test_device * test_dev)497d9c6a72dSLuis R. Rodriguez static int __trigger_config_run(struct kmod_test_device *test_dev)
498d9c6a72dSLuis R. Rodriguez {
499d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
500d9c6a72dSLuis R. Rodriguez 
501d9c6a72dSLuis R. Rodriguez 	test_dev->done = 0;
502d9c6a72dSLuis R. Rodriguez 
503d9c6a72dSLuis R. Rodriguez 	switch (config->test_case) {
504d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_DRIVER:
505d9c6a72dSLuis R. Rodriguez 		return run_test_driver(test_dev);
506d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_FS_TYPE:
507d9c6a72dSLuis R. Rodriguez 		return run_test_fs_type(test_dev);
508d9c6a72dSLuis R. Rodriguez 	default:
509d9c6a72dSLuis R. Rodriguez 		dev_warn(test_dev->dev,
510d9c6a72dSLuis R. Rodriguez 			 "Invalid test case requested: %u\n",
511d9c6a72dSLuis R. Rodriguez 			 config->test_case);
512d9c6a72dSLuis R. Rodriguez 		return -EINVAL;
513d9c6a72dSLuis R. Rodriguez 	}
514d9c6a72dSLuis R. Rodriguez }
515d9c6a72dSLuis R. Rodriguez 
trigger_config_run(struct kmod_test_device * test_dev)516d9c6a72dSLuis R. Rodriguez static int trigger_config_run(struct kmod_test_device *test_dev)
517d9c6a72dSLuis R. Rodriguez {
518d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
519d9c6a72dSLuis R. Rodriguez 	int ret;
520d9c6a72dSLuis R. Rodriguez 
521d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->trigger_mutex);
522d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
523d9c6a72dSLuis R. Rodriguez 
524d9c6a72dSLuis R. Rodriguez 	ret = __trigger_config_run(test_dev);
525d9c6a72dSLuis R. Rodriguez 	if (ret < 0)
526d9c6a72dSLuis R. Rodriguez 		goto out;
527d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "General test result: %d\n",
528d9c6a72dSLuis R. Rodriguez 		 config->test_result);
529d9c6a72dSLuis R. Rodriguez 
530d9c6a72dSLuis R. Rodriguez 	/*
531d9c6a72dSLuis R. Rodriguez 	 * We must return 0 after a trigger even unless something went
532d9c6a72dSLuis R. Rodriguez 	 * wrong with the setup of the test. If the test setup went fine
533d9c6a72dSLuis R. Rodriguez 	 * then userspace must just check the result of config->test_result.
534d9c6a72dSLuis R. Rodriguez 	 * One issue with relying on the return from a call in the kernel
53553b0fe36SZhen Lei 	 * is if the kernel returns a positive value using this trigger
536d9c6a72dSLuis R. Rodriguez 	 * will not return the value to userspace, it would be lost.
537d9c6a72dSLuis R. Rodriguez 	 *
538d9c6a72dSLuis R. Rodriguez 	 * By not relying on capturing the return value of tests we are using
539d9c6a72dSLuis R. Rodriguez 	 * through the trigger it also us to run tests with set -e and only
540d9c6a72dSLuis R. Rodriguez 	 * fail when something went wrong with the driver upon trigger
541d9c6a72dSLuis R. Rodriguez 	 * requests.
542d9c6a72dSLuis R. Rodriguez 	 */
543d9c6a72dSLuis R. Rodriguez 	ret = 0;
544d9c6a72dSLuis R. Rodriguez 
545d9c6a72dSLuis R. Rodriguez out:
546d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
547d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->trigger_mutex);
548d9c6a72dSLuis R. Rodriguez 
549d9c6a72dSLuis R. Rodriguez 	return ret;
550d9c6a72dSLuis R. Rodriguez }
551d9c6a72dSLuis R. Rodriguez 
552d9c6a72dSLuis R. Rodriguez static ssize_t
trigger_config_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)553d9c6a72dSLuis R. Rodriguez trigger_config_store(struct device *dev,
554d9c6a72dSLuis R. Rodriguez 		     struct device_attribute *attr,
555d9c6a72dSLuis R. Rodriguez 		     const char *buf, size_t count)
556d9c6a72dSLuis R. Rodriguez {
557d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
558d9c6a72dSLuis R. Rodriguez 	int ret;
559d9c6a72dSLuis R. Rodriguez 
560d9c6a72dSLuis R. Rodriguez 	if (test_dev->test_is_oom)
561d9c6a72dSLuis R. Rodriguez 		return -ENOMEM;
562d9c6a72dSLuis R. Rodriguez 
563d9c6a72dSLuis R. Rodriguez 	/* For all intents and purposes we don't care what userspace
564d9c6a72dSLuis R. Rodriguez 	 * sent this trigger, we care only that we were triggered.
565d9c6a72dSLuis R. Rodriguez 	 * We treat the return value only for caputuring issues with
566d9c6a72dSLuis R. Rodriguez 	 * the test setup. At this point all the test variables should
567d9c6a72dSLuis R. Rodriguez 	 * have been allocated so typically this should never fail.
568d9c6a72dSLuis R. Rodriguez 	 */
569d9c6a72dSLuis R. Rodriguez 	ret = trigger_config_run(test_dev);
570d9c6a72dSLuis R. Rodriguez 	if (unlikely(ret < 0))
571d9c6a72dSLuis R. Rodriguez 		goto out;
572d9c6a72dSLuis R. Rodriguez 
573d9c6a72dSLuis R. Rodriguez 	/*
574d9c6a72dSLuis R. Rodriguez 	 * Note: any return > 0 will be treated as success
575d9c6a72dSLuis R. Rodriguez 	 * and the error value will not be available to userspace.
576d9c6a72dSLuis R. Rodriguez 	 * Do not rely on trying to send to userspace a test value
57753b0fe36SZhen Lei 	 * return value as positive return errors will be lost.
578d9c6a72dSLuis R. Rodriguez 	 */
579d9c6a72dSLuis R. Rodriguez 	if (WARN_ON(ret > 0))
580d9c6a72dSLuis R. Rodriguez 		return -EINVAL;
581d9c6a72dSLuis R. Rodriguez 
582d9c6a72dSLuis R. Rodriguez 	ret = count;
583d9c6a72dSLuis R. Rodriguez out:
584d9c6a72dSLuis R. Rodriguez 	return ret;
585d9c6a72dSLuis R. Rodriguez }
586d9c6a72dSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_config);
587d9c6a72dSLuis R. Rodriguez 
588d9c6a72dSLuis R. Rodriguez /*
589d9c6a72dSLuis R. Rodriguez  * XXX: move to kstrncpy() once merged.
590d9c6a72dSLuis R. Rodriguez  *
591d9c6a72dSLuis R. Rodriguez  * Users should use kfree_const() when freeing these.
592d9c6a72dSLuis R. Rodriguez  */
__kstrncpy(char ** dst,const char * name,size_t count,gfp_t gfp)593d9c6a72dSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
594d9c6a72dSLuis R. Rodriguez {
595d9c6a72dSLuis R. Rodriguez 	*dst = kstrndup(name, count, gfp);
596d9c6a72dSLuis R. Rodriguez 	if (!*dst)
597d9c6a72dSLuis R. Rodriguez 		return -ENOSPC;
598d9c6a72dSLuis R. Rodriguez 	return count;
599d9c6a72dSLuis R. Rodriguez }
600d9c6a72dSLuis R. Rodriguez 
config_copy_test_driver_name(struct test_config * config,const char * name,size_t count)601d9c6a72dSLuis R. Rodriguez static int config_copy_test_driver_name(struct test_config *config,
602d9c6a72dSLuis R. Rodriguez 				    const char *name,
603d9c6a72dSLuis R. Rodriguez 				    size_t count)
604d9c6a72dSLuis R. Rodriguez {
605d9c6a72dSLuis R. Rodriguez 	return __kstrncpy(&config->test_driver, name, count, GFP_KERNEL);
606d9c6a72dSLuis R. Rodriguez }
607d9c6a72dSLuis R. Rodriguez 
608d9c6a72dSLuis R. Rodriguez 
config_copy_test_fs(struct test_config * config,const char * name,size_t count)609d9c6a72dSLuis R. Rodriguez static int config_copy_test_fs(struct test_config *config, const char *name,
610d9c6a72dSLuis R. Rodriguez 			       size_t count)
611d9c6a72dSLuis R. Rodriguez {
612d9c6a72dSLuis R. Rodriguez 	return __kstrncpy(&config->test_fs, name, count, GFP_KERNEL);
613d9c6a72dSLuis R. Rodriguez }
614d9c6a72dSLuis R. Rodriguez 
__kmod_config_free(struct test_config * config)615d9c6a72dSLuis R. Rodriguez static void __kmod_config_free(struct test_config *config)
616d9c6a72dSLuis R. Rodriguez {
617d9c6a72dSLuis R. Rodriguez 	if (!config)
618d9c6a72dSLuis R. Rodriguez 		return;
619d9c6a72dSLuis R. Rodriguez 
620d9c6a72dSLuis R. Rodriguez 	kfree_const(config->test_driver);
621d9c6a72dSLuis R. Rodriguez 	config->test_driver = NULL;
622d9c6a72dSLuis R. Rodriguez 
623d9c6a72dSLuis R. Rodriguez 	kfree_const(config->test_fs);
624db7ddeabSDan Carpenter 	config->test_fs = NULL;
625d9c6a72dSLuis R. Rodriguez }
626d9c6a72dSLuis R. Rodriguez 
kmod_config_free(struct kmod_test_device * test_dev)627d9c6a72dSLuis R. Rodriguez static void kmod_config_free(struct kmod_test_device *test_dev)
628d9c6a72dSLuis R. Rodriguez {
629d9c6a72dSLuis R. Rodriguez 	struct test_config *config;
630d9c6a72dSLuis R. Rodriguez 
631d9c6a72dSLuis R. Rodriguez 	if (!test_dev)
632d9c6a72dSLuis R. Rodriguez 		return;
633d9c6a72dSLuis R. Rodriguez 
634d9c6a72dSLuis R. Rodriguez 	config = &test_dev->config;
635d9c6a72dSLuis R. Rodriguez 
636d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
637d9c6a72dSLuis R. Rodriguez 	__kmod_config_free(config);
638d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
639d9c6a72dSLuis R. Rodriguez }
640d9c6a72dSLuis R. Rodriguez 
config_test_driver_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)641d9c6a72dSLuis R. Rodriguez static ssize_t config_test_driver_store(struct device *dev,
642d9c6a72dSLuis R. Rodriguez 					struct device_attribute *attr,
643d9c6a72dSLuis R. Rodriguez 					const char *buf, size_t count)
644d9c6a72dSLuis R. Rodriguez {
645d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
646d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
647d9c6a72dSLuis R. Rodriguez 	int copied;
648d9c6a72dSLuis R. Rodriguez 
649d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
650d9c6a72dSLuis R. Rodriguez 
651d9c6a72dSLuis R. Rodriguez 	kfree_const(config->test_driver);
652d9c6a72dSLuis R. Rodriguez 	config->test_driver = NULL;
653d9c6a72dSLuis R. Rodriguez 
654d9c6a72dSLuis R. Rodriguez 	copied = config_copy_test_driver_name(config, buf, count);
655d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
656d9c6a72dSLuis R. Rodriguez 
657d9c6a72dSLuis R. Rodriguez 	return copied;
658d9c6a72dSLuis R. Rodriguez }
659d9c6a72dSLuis R. Rodriguez 
660d9c6a72dSLuis R. Rodriguez /*
661d9c6a72dSLuis R. Rodriguez  * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
662d9c6a72dSLuis R. Rodriguez  */
config_test_show_str(struct mutex * config_mutex,char * dst,char * src)663d9c6a72dSLuis R. Rodriguez static ssize_t config_test_show_str(struct mutex *config_mutex,
664d9c6a72dSLuis R. Rodriguez 				    char *dst,
665d9c6a72dSLuis R. Rodriguez 				    char *src)
666d9c6a72dSLuis R. Rodriguez {
667d9c6a72dSLuis R. Rodriguez 	int len;
668d9c6a72dSLuis R. Rodriguez 
669d9c6a72dSLuis R. Rodriguez 	mutex_lock(config_mutex);
670d9c6a72dSLuis R. Rodriguez 	len = snprintf(dst, PAGE_SIZE, "%s\n", src);
671d9c6a72dSLuis R. Rodriguez 	mutex_unlock(config_mutex);
672d9c6a72dSLuis R. Rodriguez 
673d9c6a72dSLuis R. Rodriguez 	return len;
674d9c6a72dSLuis R. Rodriguez }
675d9c6a72dSLuis R. Rodriguez 
config_test_driver_show(struct device * dev,struct device_attribute * attr,char * buf)676d9c6a72dSLuis R. Rodriguez static ssize_t config_test_driver_show(struct device *dev,
677d9c6a72dSLuis R. Rodriguez 					struct device_attribute *attr,
678d9c6a72dSLuis R. Rodriguez 					char *buf)
679d9c6a72dSLuis R. Rodriguez {
680d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
681d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
682d9c6a72dSLuis R. Rodriguez 
683d9c6a72dSLuis R. Rodriguez 	return config_test_show_str(&test_dev->config_mutex, buf,
684d9c6a72dSLuis R. Rodriguez 				    config->test_driver);
685d9c6a72dSLuis R. Rodriguez }
686b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_test_driver);
687d9c6a72dSLuis R. Rodriguez 
config_test_fs_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)688d9c6a72dSLuis R. Rodriguez static ssize_t config_test_fs_store(struct device *dev,
689d9c6a72dSLuis R. Rodriguez 				    struct device_attribute *attr,
690d9c6a72dSLuis R. Rodriguez 				    const char *buf, size_t count)
691d9c6a72dSLuis R. Rodriguez {
692d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
693d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
694d9c6a72dSLuis R. Rodriguez 	int copied;
695d9c6a72dSLuis R. Rodriguez 
696d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
697d9c6a72dSLuis R. Rodriguez 
698d9c6a72dSLuis R. Rodriguez 	kfree_const(config->test_fs);
699d9c6a72dSLuis R. Rodriguez 	config->test_fs = NULL;
700d9c6a72dSLuis R. Rodriguez 
701d9c6a72dSLuis R. Rodriguez 	copied = config_copy_test_fs(config, buf, count);
702d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
703d9c6a72dSLuis R. Rodriguez 
704d9c6a72dSLuis R. Rodriguez 	return copied;
705d9c6a72dSLuis R. Rodriguez }
706d9c6a72dSLuis R. Rodriguez 
config_test_fs_show(struct device * dev,struct device_attribute * attr,char * buf)707d9c6a72dSLuis R. Rodriguez static ssize_t config_test_fs_show(struct device *dev,
708d9c6a72dSLuis R. Rodriguez 				   struct device_attribute *attr,
709d9c6a72dSLuis R. Rodriguez 				   char *buf)
710d9c6a72dSLuis R. Rodriguez {
711d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
712d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
713d9c6a72dSLuis R. Rodriguez 
714d9c6a72dSLuis R. Rodriguez 	return config_test_show_str(&test_dev->config_mutex, buf,
715d9c6a72dSLuis R. Rodriguez 				    config->test_fs);
716d9c6a72dSLuis R. Rodriguez }
717b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_test_fs);
718d9c6a72dSLuis R. Rodriguez 
trigger_config_run_type(struct kmod_test_device * test_dev,enum kmod_test_case test_case,const char * test_str)719d9c6a72dSLuis R. Rodriguez static int trigger_config_run_type(struct kmod_test_device *test_dev,
720d9c6a72dSLuis R. Rodriguez 				   enum kmod_test_case test_case,
721d9c6a72dSLuis R. Rodriguez 				   const char *test_str)
722d9c6a72dSLuis R. Rodriguez {
723d9c6a72dSLuis R. Rodriguez 	int copied = 0;
724d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
725d9c6a72dSLuis R. Rodriguez 
726d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
727d9c6a72dSLuis R. Rodriguez 
728d9c6a72dSLuis R. Rodriguez 	switch (test_case) {
729d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_DRIVER:
730d9c6a72dSLuis R. Rodriguez 		kfree_const(config->test_driver);
731d9c6a72dSLuis R. Rodriguez 		config->test_driver = NULL;
732d9c6a72dSLuis R. Rodriguez 		copied = config_copy_test_driver_name(config, test_str,
733d9c6a72dSLuis R. Rodriguez 						      strlen(test_str));
734d9c6a72dSLuis R. Rodriguez 		break;
735d9c6a72dSLuis R. Rodriguez 	case TEST_KMOD_FS_TYPE:
736d9c6a72dSLuis R. Rodriguez 		kfree_const(config->test_fs);
7370776d123STiezhu Yang 		config->test_fs = NULL;
738d9c6a72dSLuis R. Rodriguez 		copied = config_copy_test_fs(config, test_str,
739d9c6a72dSLuis R. Rodriguez 					     strlen(test_str));
7404e98ebe5SDan Carpenter 		break;
741d9c6a72dSLuis R. Rodriguez 	default:
742d9c6a72dSLuis R. Rodriguez 		mutex_unlock(&test_dev->config_mutex);
743d9c6a72dSLuis R. Rodriguez 		return -EINVAL;
744d9c6a72dSLuis R. Rodriguez 	}
745d9c6a72dSLuis R. Rodriguez 
746d9c6a72dSLuis R. Rodriguez 	config->test_case = test_case;
747d9c6a72dSLuis R. Rodriguez 
748d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
749d9c6a72dSLuis R. Rodriguez 
750d9c6a72dSLuis R. Rodriguez 	if (copied <= 0 || copied != strlen(test_str)) {
751d9c6a72dSLuis R. Rodriguez 		test_dev->test_is_oom = true;
752d9c6a72dSLuis R. Rodriguez 		return -ENOMEM;
753d9c6a72dSLuis R. Rodriguez 	}
754d9c6a72dSLuis R. Rodriguez 
755d9c6a72dSLuis R. Rodriguez 	test_dev->test_is_oom = false;
756d9c6a72dSLuis R. Rodriguez 
757d9c6a72dSLuis R. Rodriguez 	return trigger_config_run(test_dev);
758d9c6a72dSLuis R. Rodriguez }
759d9c6a72dSLuis R. Rodriguez 
free_test_dev_info(struct kmod_test_device * test_dev)760d9c6a72dSLuis R. Rodriguez static void free_test_dev_info(struct kmod_test_device *test_dev)
761d9c6a72dSLuis R. Rodriguez {
762d9c6a72dSLuis R. Rodriguez 	vfree(test_dev->info);
763d9c6a72dSLuis R. Rodriguez 	test_dev->info = NULL;
764d9c6a72dSLuis R. Rodriguez }
765d9c6a72dSLuis R. Rodriguez 
kmod_config_sync_info(struct kmod_test_device * test_dev)766d9c6a72dSLuis R. Rodriguez static int kmod_config_sync_info(struct kmod_test_device *test_dev)
767d9c6a72dSLuis R. Rodriguez {
768d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
769d9c6a72dSLuis R. Rodriguez 
770d9c6a72dSLuis R. Rodriguez 	free_test_dev_info(test_dev);
771fad953ceSKees Cook 	test_dev->info =
772fad953ceSKees Cook 		vzalloc(array_size(sizeof(struct kmod_test_device_info),
773fad953ceSKees Cook 				   config->num_threads));
774dc2bf000SMarkus Elfring 	if (!test_dev->info)
775d9c6a72dSLuis R. Rodriguez 		return -ENOMEM;
776d9c6a72dSLuis R. Rodriguez 
777d9c6a72dSLuis R. Rodriguez 	return 0;
778d9c6a72dSLuis R. Rodriguez }
779d9c6a72dSLuis R. Rodriguez 
780d9c6a72dSLuis R. Rodriguez /*
781d9c6a72dSLuis R. Rodriguez  * Old kernels may not have this, if you want to port this code to
782d9c6a72dSLuis R. Rodriguez  * test it on older kernels.
783d9c6a72dSLuis R. Rodriguez  */
784d9c6a72dSLuis R. Rodriguez #ifdef get_kmod_umh_limit
kmod_init_test_thread_limit(void)785d9c6a72dSLuis R. Rodriguez static unsigned int kmod_init_test_thread_limit(void)
786d9c6a72dSLuis R. Rodriguez {
787d9c6a72dSLuis R. Rodriguez 	return get_kmod_umh_limit();
788d9c6a72dSLuis R. Rodriguez }
789d9c6a72dSLuis R. Rodriguez #else
kmod_init_test_thread_limit(void)790d9c6a72dSLuis R. Rodriguez static unsigned int kmod_init_test_thread_limit(void)
791d9c6a72dSLuis R. Rodriguez {
792d9c6a72dSLuis R. Rodriguez 	return TEST_START_NUM_THREADS;
793d9c6a72dSLuis R. Rodriguez }
794d9c6a72dSLuis R. Rodriguez #endif
795d9c6a72dSLuis R. Rodriguez 
__kmod_config_init(struct kmod_test_device * test_dev)796d9c6a72dSLuis R. Rodriguez static int __kmod_config_init(struct kmod_test_device *test_dev)
797d9c6a72dSLuis R. Rodriguez {
798d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
799d9c6a72dSLuis R. Rodriguez 	int ret = -ENOMEM, copied;
800d9c6a72dSLuis R. Rodriguez 
801d9c6a72dSLuis R. Rodriguez 	__kmod_config_free(config);
802d9c6a72dSLuis R. Rodriguez 
803d9c6a72dSLuis R. Rodriguez 	copied = config_copy_test_driver_name(config, TEST_START_DRIVER,
804d9c6a72dSLuis R. Rodriguez 					      strlen(TEST_START_DRIVER));
805d9c6a72dSLuis R. Rodriguez 	if (copied != strlen(TEST_START_DRIVER))
806d9c6a72dSLuis R. Rodriguez 		goto err_out;
807d9c6a72dSLuis R. Rodriguez 
808d9c6a72dSLuis R. Rodriguez 	copied = config_copy_test_fs(config, TEST_START_TEST_FS,
809d9c6a72dSLuis R. Rodriguez 				     strlen(TEST_START_TEST_FS));
810d9c6a72dSLuis R. Rodriguez 	if (copied != strlen(TEST_START_TEST_FS))
811d9c6a72dSLuis R. Rodriguez 		goto err_out;
812d9c6a72dSLuis R. Rodriguez 
813d9c6a72dSLuis R. Rodriguez 	config->num_threads = kmod_init_test_thread_limit();
814d9c6a72dSLuis R. Rodriguez 	config->test_result = 0;
815d9c6a72dSLuis R. Rodriguez 	config->test_case = TEST_START_TEST_CASE;
816d9c6a72dSLuis R. Rodriguez 
817d9c6a72dSLuis R. Rodriguez 	ret = kmod_config_sync_info(test_dev);
818d9c6a72dSLuis R. Rodriguez 	if (ret)
819d9c6a72dSLuis R. Rodriguez 		goto err_out;
820d9c6a72dSLuis R. Rodriguez 
821d9c6a72dSLuis R. Rodriguez 	test_dev->test_is_oom = false;
822d9c6a72dSLuis R. Rodriguez 
823d9c6a72dSLuis R. Rodriguez 	return 0;
824d9c6a72dSLuis R. Rodriguez 
825d9c6a72dSLuis R. Rodriguez err_out:
826d9c6a72dSLuis R. Rodriguez 	test_dev->test_is_oom = true;
827d9c6a72dSLuis R. Rodriguez 	WARN_ON(test_dev->test_is_oom);
828d9c6a72dSLuis R. Rodriguez 
829d9c6a72dSLuis R. Rodriguez 	__kmod_config_free(config);
830d9c6a72dSLuis R. Rodriguez 
831d9c6a72dSLuis R. Rodriguez 	return ret;
832d9c6a72dSLuis R. Rodriguez }
833d9c6a72dSLuis R. Rodriguez 
reset_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)834d9c6a72dSLuis R. Rodriguez static ssize_t reset_store(struct device *dev,
835d9c6a72dSLuis R. Rodriguez 			   struct device_attribute *attr,
836d9c6a72dSLuis R. Rodriguez 			   const char *buf, size_t count)
837d9c6a72dSLuis R. Rodriguez {
838d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
839d9c6a72dSLuis R. Rodriguez 	int ret;
840d9c6a72dSLuis R. Rodriguez 
841d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->trigger_mutex);
842d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
843d9c6a72dSLuis R. Rodriguez 
844d9c6a72dSLuis R. Rodriguez 	ret = __kmod_config_init(test_dev);
845d9c6a72dSLuis R. Rodriguez 	if (ret < 0) {
846d9c6a72dSLuis R. Rodriguez 		ret = -ENOMEM;
847d9c6a72dSLuis R. Rodriguez 		dev_err(dev, "could not alloc settings for config trigger: %d\n",
848d9c6a72dSLuis R. Rodriguez 		       ret);
849d9c6a72dSLuis R. Rodriguez 		goto out;
850d9c6a72dSLuis R. Rodriguez 	}
851d9c6a72dSLuis R. Rodriguez 
852d9c6a72dSLuis R. Rodriguez 	dev_info(dev, "reset\n");
853d9c6a72dSLuis R. Rodriguez 	ret = count;
854d9c6a72dSLuis R. Rodriguez 
855d9c6a72dSLuis R. Rodriguez out:
856d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
857d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->trigger_mutex);
858d9c6a72dSLuis R. Rodriguez 
859d9c6a72dSLuis R. Rodriguez 	return ret;
860d9c6a72dSLuis R. Rodriguez }
861d9c6a72dSLuis R. Rodriguez static DEVICE_ATTR_WO(reset);
862d9c6a72dSLuis R. Rodriguez 
test_dev_config_update_uint_sync(struct kmod_test_device * test_dev,const char * buf,size_t size,unsigned int * config,int (* test_sync)(struct kmod_test_device * test_dev))863d9c6a72dSLuis R. Rodriguez static int test_dev_config_update_uint_sync(struct kmod_test_device *test_dev,
864d9c6a72dSLuis R. Rodriguez 					    const char *buf, size_t size,
865d9c6a72dSLuis R. Rodriguez 					    unsigned int *config,
866d9c6a72dSLuis R. Rodriguez 					    int (*test_sync)(struct kmod_test_device *test_dev))
867d9c6a72dSLuis R. Rodriguez {
868d9c6a72dSLuis R. Rodriguez 	int ret;
869506dfc99SAlexey Dobriyan 	unsigned int val;
870d9c6a72dSLuis R. Rodriguez 	unsigned int old_val;
871d9c6a72dSLuis R. Rodriguez 
872506dfc99SAlexey Dobriyan 	ret = kstrtouint(buf, 10, &val);
873d9c6a72dSLuis R. Rodriguez 	if (ret)
874d9c6a72dSLuis R. Rodriguez 		return ret;
875d9c6a72dSLuis R. Rodriguez 
876d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
877d9c6a72dSLuis R. Rodriguez 
878d9c6a72dSLuis R. Rodriguez 	old_val = *config;
879506dfc99SAlexey Dobriyan 	*(unsigned int *)config = val;
880d9c6a72dSLuis R. Rodriguez 
881d9c6a72dSLuis R. Rodriguez 	ret = test_sync(test_dev);
882d9c6a72dSLuis R. Rodriguez 	if (ret) {
883d9c6a72dSLuis R. Rodriguez 		*(unsigned int *)config = old_val;
884d9c6a72dSLuis R. Rodriguez 
885d9c6a72dSLuis R. Rodriguez 		ret = test_sync(test_dev);
886d9c6a72dSLuis R. Rodriguez 		WARN_ON(ret);
887d9c6a72dSLuis R. Rodriguez 
888d9c6a72dSLuis R. Rodriguez 		mutex_unlock(&test_dev->config_mutex);
889d9c6a72dSLuis R. Rodriguez 		return -EINVAL;
890d9c6a72dSLuis R. Rodriguez 	}
891d9c6a72dSLuis R. Rodriguez 
892d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
893d9c6a72dSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
894d9c6a72dSLuis R. Rodriguez 	return size;
895d9c6a72dSLuis R. Rodriguez }
896d9c6a72dSLuis R. Rodriguez 
test_dev_config_update_uint_range(struct kmod_test_device * test_dev,const char * buf,size_t size,unsigned int * config,unsigned int min,unsigned int max)897d9c6a72dSLuis R. Rodriguez static int test_dev_config_update_uint_range(struct kmod_test_device *test_dev,
898d9c6a72dSLuis R. Rodriguez 					     const char *buf, size_t size,
899d9c6a72dSLuis R. Rodriguez 					     unsigned int *config,
900d9c6a72dSLuis R. Rodriguez 					     unsigned int min,
901d9c6a72dSLuis R. Rodriguez 					     unsigned int max)
902d9c6a72dSLuis R. Rodriguez {
903506dfc99SAlexey Dobriyan 	unsigned int val;
904d9c6a72dSLuis R. Rodriguez 	int ret;
905d9c6a72dSLuis R. Rodriguez 
906506dfc99SAlexey Dobriyan 	ret = kstrtouint(buf, 10, &val);
907d9c6a72dSLuis R. Rodriguez 	if (ret)
908d9c6a72dSLuis R. Rodriguez 		return ret;
909d9c6a72dSLuis R. Rodriguez 
910506dfc99SAlexey Dobriyan 	if (val < min || val > max)
911d9c6a72dSLuis R. Rodriguez 		return -EINVAL;
912d9c6a72dSLuis R. Rodriguez 
913d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
914506dfc99SAlexey Dobriyan 	*config = val;
915d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
916d9c6a72dSLuis R. Rodriguez 
917d9c6a72dSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
918d9c6a72dSLuis R. Rodriguez 	return size;
919d9c6a72dSLuis R. Rodriguez }
920d9c6a72dSLuis R. Rodriguez 
test_dev_config_update_int(struct kmod_test_device * test_dev,const char * buf,size_t size,int * config)921d9c6a72dSLuis R. Rodriguez static int test_dev_config_update_int(struct kmod_test_device *test_dev,
922d9c6a72dSLuis R. Rodriguez 				      const char *buf, size_t size,
923d9c6a72dSLuis R. Rodriguez 				      int *config)
924d9c6a72dSLuis R. Rodriguez {
925506dfc99SAlexey Dobriyan 	int val;
926d9c6a72dSLuis R. Rodriguez 	int ret;
927d9c6a72dSLuis R. Rodriguez 
928506dfc99SAlexey Dobriyan 	ret = kstrtoint(buf, 10, &val);
929d9c6a72dSLuis R. Rodriguez 	if (ret)
930d9c6a72dSLuis R. Rodriguez 		return ret;
931d9c6a72dSLuis R. Rodriguez 
932d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
933506dfc99SAlexey Dobriyan 	*config = val;
934d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
935d9c6a72dSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
936d9c6a72dSLuis R. Rodriguez 	return size;
937d9c6a72dSLuis R. Rodriguez }
938d9c6a72dSLuis R. Rodriguez 
test_dev_config_show_int(struct kmod_test_device * test_dev,char * buf,int config)939d9c6a72dSLuis R. Rodriguez static ssize_t test_dev_config_show_int(struct kmod_test_device *test_dev,
940d9c6a72dSLuis R. Rodriguez 					char *buf,
941d9c6a72dSLuis R. Rodriguez 					int config)
942d9c6a72dSLuis R. Rodriguez {
943d9c6a72dSLuis R. Rodriguez 	int val;
944d9c6a72dSLuis R. Rodriguez 
945d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
946d9c6a72dSLuis R. Rodriguez 	val = config;
947d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
948d9c6a72dSLuis R. Rodriguez 
949d9c6a72dSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
950d9c6a72dSLuis R. Rodriguez }
951d9c6a72dSLuis R. Rodriguez 
test_dev_config_show_uint(struct kmod_test_device * test_dev,char * buf,unsigned int config)952d9c6a72dSLuis R. Rodriguez static ssize_t test_dev_config_show_uint(struct kmod_test_device *test_dev,
953d9c6a72dSLuis R. Rodriguez 					 char *buf,
954d9c6a72dSLuis R. Rodriguez 					 unsigned int config)
955d9c6a72dSLuis R. Rodriguez {
956d9c6a72dSLuis R. Rodriguez 	unsigned int val;
957d9c6a72dSLuis R. Rodriguez 
958d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
959d9c6a72dSLuis R. Rodriguez 	val = config;
960d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
961d9c6a72dSLuis R. Rodriguez 
962d9c6a72dSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%u\n", val);
963d9c6a72dSLuis R. Rodriguez }
964d9c6a72dSLuis R. Rodriguez 
test_result_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)965d9c6a72dSLuis R. Rodriguez static ssize_t test_result_store(struct device *dev,
966d9c6a72dSLuis R. Rodriguez 				 struct device_attribute *attr,
967d9c6a72dSLuis R. Rodriguez 				 const char *buf, size_t count)
968d9c6a72dSLuis R. Rodriguez {
969d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
970d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
971d9c6a72dSLuis R. Rodriguez 
972d9c6a72dSLuis R. Rodriguez 	return test_dev_config_update_int(test_dev, buf, count,
973d9c6a72dSLuis R. Rodriguez 					  &config->test_result);
974d9c6a72dSLuis R. Rodriguez }
975d9c6a72dSLuis R. Rodriguez 
config_num_threads_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)976d9c6a72dSLuis R. Rodriguez static ssize_t config_num_threads_store(struct device *dev,
977d9c6a72dSLuis R. Rodriguez 					struct device_attribute *attr,
978d9c6a72dSLuis R. Rodriguez 					const char *buf, size_t count)
979d9c6a72dSLuis R. Rodriguez {
980d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
981d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
982d9c6a72dSLuis R. Rodriguez 
983d9c6a72dSLuis R. Rodriguez 	return test_dev_config_update_uint_sync(test_dev, buf, count,
984d9c6a72dSLuis R. Rodriguez 						&config->num_threads,
985d9c6a72dSLuis R. Rodriguez 						kmod_config_sync_info);
986d9c6a72dSLuis R. Rodriguez }
987d9c6a72dSLuis R. Rodriguez 
config_num_threads_show(struct device * dev,struct device_attribute * attr,char * buf)988d9c6a72dSLuis R. Rodriguez static ssize_t config_num_threads_show(struct device *dev,
989d9c6a72dSLuis R. Rodriguez 				       struct device_attribute *attr,
990d9c6a72dSLuis R. Rodriguez 				       char *buf)
991d9c6a72dSLuis R. Rodriguez {
992d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
993d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
994d9c6a72dSLuis R. Rodriguez 
995d9c6a72dSLuis R. Rodriguez 	return test_dev_config_show_int(test_dev, buf, config->num_threads);
996d9c6a72dSLuis R. Rodriguez }
997b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_threads);
998d9c6a72dSLuis R. Rodriguez 
config_test_case_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)999d9c6a72dSLuis R. Rodriguez static ssize_t config_test_case_store(struct device *dev,
1000d9c6a72dSLuis R. Rodriguez 				      struct device_attribute *attr,
1001d9c6a72dSLuis R. Rodriguez 				      const char *buf, size_t count)
1002d9c6a72dSLuis R. Rodriguez {
1003d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
1004d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
1005d9c6a72dSLuis R. Rodriguez 
1006d9c6a72dSLuis R. Rodriguez 	return test_dev_config_update_uint_range(test_dev, buf, count,
1007d9c6a72dSLuis R. Rodriguez 						 &config->test_case,
1008d9c6a72dSLuis R. Rodriguez 						 __TEST_KMOD_INVALID + 1,
1009d9c6a72dSLuis R. Rodriguez 						 __TEST_KMOD_MAX - 1);
1010d9c6a72dSLuis R. Rodriguez }
1011d9c6a72dSLuis R. Rodriguez 
config_test_case_show(struct device * dev,struct device_attribute * attr,char * buf)1012d9c6a72dSLuis R. Rodriguez static ssize_t config_test_case_show(struct device *dev,
1013d9c6a72dSLuis R. Rodriguez 				     struct device_attribute *attr,
1014d9c6a72dSLuis R. Rodriguez 				     char *buf)
1015d9c6a72dSLuis R. Rodriguez {
1016d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
1017d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
1018d9c6a72dSLuis R. Rodriguez 
1019d9c6a72dSLuis R. Rodriguez 	return test_dev_config_show_uint(test_dev, buf, config->test_case);
1020d9c6a72dSLuis R. Rodriguez }
1021b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_test_case);
1022d9c6a72dSLuis R. Rodriguez 
test_result_show(struct device * dev,struct device_attribute * attr,char * buf)1023d9c6a72dSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev,
1024d9c6a72dSLuis R. Rodriguez 				struct device_attribute *attr,
1025d9c6a72dSLuis R. Rodriguez 				char *buf)
1026d9c6a72dSLuis R. Rodriguez {
1027d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
1028d9c6a72dSLuis R. Rodriguez 	struct test_config *config = &test_dev->config;
1029d9c6a72dSLuis R. Rodriguez 
1030d9c6a72dSLuis R. Rodriguez 	return test_dev_config_show_int(test_dev, buf, config->test_result);
1031d9c6a72dSLuis R. Rodriguez }
1032b6b996b6SJoe Perches static DEVICE_ATTR_RW(test_result);
1033d9c6a72dSLuis R. Rodriguez 
1034d9c6a72dSLuis R. Rodriguez #define TEST_KMOD_DEV_ATTR(name)		&dev_attr_##name.attr
1035d9c6a72dSLuis R. Rodriguez 
1036d9c6a72dSLuis R. Rodriguez static struct attribute *test_dev_attrs[] = {
1037d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(trigger_config),
1038d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(config),
1039d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(reset),
1040d9c6a72dSLuis R. Rodriguez 
1041d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(config_test_driver),
1042d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(config_test_fs),
1043d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(config_num_threads),
1044d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(config_test_case),
1045d9c6a72dSLuis R. Rodriguez 	TEST_KMOD_DEV_ATTR(test_result),
1046d9c6a72dSLuis R. Rodriguez 
1047d9c6a72dSLuis R. Rodriguez 	NULL,
1048d9c6a72dSLuis R. Rodriguez };
1049d9c6a72dSLuis R. Rodriguez 
1050d9c6a72dSLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev);
1051d9c6a72dSLuis R. Rodriguez 
kmod_config_init(struct kmod_test_device * test_dev)1052d9c6a72dSLuis R. Rodriguez static int kmod_config_init(struct kmod_test_device *test_dev)
1053d9c6a72dSLuis R. Rodriguez {
1054d9c6a72dSLuis R. Rodriguez 	int ret;
1055d9c6a72dSLuis R. Rodriguez 
1056d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
1057d9c6a72dSLuis R. Rodriguez 	ret = __kmod_config_init(test_dev);
1058d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
1059d9c6a72dSLuis R. Rodriguez 
1060d9c6a72dSLuis R. Rodriguez 	return ret;
1061d9c6a72dSLuis R. Rodriguez }
1062d9c6a72dSLuis R. Rodriguez 
alloc_test_dev_kmod(int idx)1063d9c6a72dSLuis R. Rodriguez static struct kmod_test_device *alloc_test_dev_kmod(int idx)
1064d9c6a72dSLuis R. Rodriguez {
1065d9c6a72dSLuis R. Rodriguez 	int ret;
1066d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev;
1067d9c6a72dSLuis R. Rodriguez 	struct miscdevice *misc_dev;
1068d9c6a72dSLuis R. Rodriguez 
1069d9c6a72dSLuis R. Rodriguez 	test_dev = vzalloc(sizeof(struct kmod_test_device));
1070dc2bf000SMarkus Elfring 	if (!test_dev)
1071d9c6a72dSLuis R. Rodriguez 		goto err_out;
1072d9c6a72dSLuis R. Rodriguez 
1073d9c6a72dSLuis R. Rodriguez 	mutex_init(&test_dev->config_mutex);
1074d9c6a72dSLuis R. Rodriguez 	mutex_init(&test_dev->trigger_mutex);
1075d9c6a72dSLuis R. Rodriguez 	mutex_init(&test_dev->thread_mutex);
1076d9c6a72dSLuis R. Rodriguez 
1077d9c6a72dSLuis R. Rodriguez 	init_completion(&test_dev->kthreads_done);
1078d9c6a72dSLuis R. Rodriguez 
1079d9c6a72dSLuis R. Rodriguez 	ret = kmod_config_init(test_dev);
1080d9c6a72dSLuis R. Rodriguez 	if (ret < 0) {
1081d9c6a72dSLuis R. Rodriguez 		pr_err("Cannot alloc kmod_config_init()\n");
1082d9c6a72dSLuis R. Rodriguez 		goto err_out_free;
1083d9c6a72dSLuis R. Rodriguez 	}
1084d9c6a72dSLuis R. Rodriguez 
1085d9c6a72dSLuis R. Rodriguez 	test_dev->dev_idx = idx;
1086d9c6a72dSLuis R. Rodriguez 	misc_dev = &test_dev->misc_dev;
1087d9c6a72dSLuis R. Rodriguez 
1088d9c6a72dSLuis R. Rodriguez 	misc_dev->minor = MISC_DYNAMIC_MINOR;
1089d9c6a72dSLuis R. Rodriguez 	misc_dev->name = kasprintf(GFP_KERNEL, "test_kmod%d", idx);
1090d9c6a72dSLuis R. Rodriguez 	if (!misc_dev->name) {
1091d9c6a72dSLuis R. Rodriguez 		pr_err("Cannot alloc misc_dev->name\n");
1092d9c6a72dSLuis R. Rodriguez 		goto err_out_free_config;
1093d9c6a72dSLuis R. Rodriguez 	}
1094d9c6a72dSLuis R. Rodriguez 	misc_dev->groups = test_dev_groups;
1095d9c6a72dSLuis R. Rodriguez 
1096d9c6a72dSLuis R. Rodriguez 	return test_dev;
1097d9c6a72dSLuis R. Rodriguez 
1098d9c6a72dSLuis R. Rodriguez err_out_free_config:
1099d9c6a72dSLuis R. Rodriguez 	free_test_dev_info(test_dev);
1100d9c6a72dSLuis R. Rodriguez 	kmod_config_free(test_dev);
1101d9c6a72dSLuis R. Rodriguez err_out_free:
1102d9c6a72dSLuis R. Rodriguez 	vfree(test_dev);
1103d9c6a72dSLuis R. Rodriguez 	test_dev = NULL;
1104d9c6a72dSLuis R. Rodriguez err_out:
1105d9c6a72dSLuis R. Rodriguez 	return NULL;
1106d9c6a72dSLuis R. Rodriguez }
1107d9c6a72dSLuis R. Rodriguez 
free_test_dev_kmod(struct kmod_test_device * test_dev)1108d9c6a72dSLuis R. Rodriguez static void free_test_dev_kmod(struct kmod_test_device *test_dev)
1109d9c6a72dSLuis R. Rodriguez {
1110d9c6a72dSLuis R. Rodriguez 	if (test_dev) {
1111d9c6a72dSLuis R. Rodriguez 		kfree_const(test_dev->misc_dev.name);
1112d9c6a72dSLuis R. Rodriguez 		test_dev->misc_dev.name = NULL;
1113d9c6a72dSLuis R. Rodriguez 		free_test_dev_info(test_dev);
1114d9c6a72dSLuis R. Rodriguez 		kmod_config_free(test_dev);
1115d9c6a72dSLuis R. Rodriguez 		vfree(test_dev);
1116d9c6a72dSLuis R. Rodriguez 		test_dev = NULL;
1117d9c6a72dSLuis R. Rodriguez 	}
1118d9c6a72dSLuis R. Rodriguez }
1119d9c6a72dSLuis R. Rodriguez 
register_test_dev_kmod(void)1120d9c6a72dSLuis R. Rodriguez static struct kmod_test_device *register_test_dev_kmod(void)
1121d9c6a72dSLuis R. Rodriguez {
1122d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev = NULL;
1123d9c6a72dSLuis R. Rodriguez 	int ret;
1124d9c6a72dSLuis R. Rodriguez 
11259c567713SDan Carpenter 	mutex_lock(&reg_dev_mutex);
1126d9c6a72dSLuis R. Rodriguez 
1127d9c6a72dSLuis R. Rodriguez 	/* int should suffice for number of devices, test for wrap */
1128ac68b1b3SLuis R. Rodriguez 	if (num_test_devs + 1 == INT_MAX) {
1129d9c6a72dSLuis R. Rodriguez 		pr_err("reached limit of number of test devices\n");
1130d9c6a72dSLuis R. Rodriguez 		goto out;
1131d9c6a72dSLuis R. Rodriguez 	}
1132d9c6a72dSLuis R. Rodriguez 
1133d9c6a72dSLuis R. Rodriguez 	test_dev = alloc_test_dev_kmod(num_test_devs);
1134d9c6a72dSLuis R. Rodriguez 	if (!test_dev)
1135d9c6a72dSLuis R. Rodriguez 		goto out;
1136d9c6a72dSLuis R. Rodriguez 
1137d9c6a72dSLuis R. Rodriguez 	ret = misc_register(&test_dev->misc_dev);
1138d9c6a72dSLuis R. Rodriguez 	if (ret) {
1139d9c6a72dSLuis R. Rodriguez 		pr_err("could not register misc device: %d\n", ret);
1140d9c6a72dSLuis R. Rodriguez 		free_test_dev_kmod(test_dev);
1141dc0ce6ccSDan Carpenter 		test_dev = NULL;
1142d9c6a72dSLuis R. Rodriguez 		goto out;
1143d9c6a72dSLuis R. Rodriguez 	}
1144d9c6a72dSLuis R. Rodriguez 
1145d9c6a72dSLuis R. Rodriguez 	test_dev->dev = test_dev->misc_dev.this_device;
1146d9c6a72dSLuis R. Rodriguez 	list_add_tail(&test_dev->list, &reg_test_devs);
1147d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "interface ready\n");
1148d9c6a72dSLuis R. Rodriguez 
1149d9c6a72dSLuis R. Rodriguez 	num_test_devs++;
1150d9c6a72dSLuis R. Rodriguez 
1151d9c6a72dSLuis R. Rodriguez out:
1152d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&reg_dev_mutex);
1153d9c6a72dSLuis R. Rodriguez 
1154d9c6a72dSLuis R. Rodriguez 	return test_dev;
1155d9c6a72dSLuis R. Rodriguez 
1156d9c6a72dSLuis R. Rodriguez }
1157d9c6a72dSLuis R. Rodriguez 
test_kmod_init(void)1158d9c6a72dSLuis R. Rodriguez static int __init test_kmod_init(void)
1159d9c6a72dSLuis R. Rodriguez {
1160d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev;
1161d9c6a72dSLuis R. Rodriguez 	int ret;
1162d9c6a72dSLuis R. Rodriguez 
1163d9c6a72dSLuis R. Rodriguez 	test_dev = register_test_dev_kmod();
1164d9c6a72dSLuis R. Rodriguez 	if (!test_dev) {
1165d9c6a72dSLuis R. Rodriguez 		pr_err("Cannot add first test kmod device\n");
1166d9c6a72dSLuis R. Rodriguez 		return -ENODEV;
1167d9c6a72dSLuis R. Rodriguez 	}
1168d9c6a72dSLuis R. Rodriguez 
1169d9c6a72dSLuis R. Rodriguez 	/*
1170d9c6a72dSLuis R. Rodriguez 	 * With some work we might be able to gracefully enable
1171d9c6a72dSLuis R. Rodriguez 	 * testing with this driver built-in, for now this seems
1172d9c6a72dSLuis R. Rodriguez 	 * rather risky. For those willing to try have at it,
1173d9c6a72dSLuis R. Rodriguez 	 * and enable the below. Good luck! If that works, try
1174d9c6a72dSLuis R. Rodriguez 	 * lowering the init level for more fun.
1175d9c6a72dSLuis R. Rodriguez 	 */
1176d9c6a72dSLuis R. Rodriguez 	if (force_init_test) {
1177d9c6a72dSLuis R. Rodriguez 		ret = trigger_config_run_type(test_dev,
1178d9c6a72dSLuis R. Rodriguez 					      TEST_KMOD_DRIVER, "tun");
1179d9c6a72dSLuis R. Rodriguez 		if (WARN_ON(ret))
1180d9c6a72dSLuis R. Rodriguez 			return ret;
1181d9c6a72dSLuis R. Rodriguez 		ret = trigger_config_run_type(test_dev,
1182d9c6a72dSLuis R. Rodriguez 					      TEST_KMOD_FS_TYPE, "btrfs");
1183d9c6a72dSLuis R. Rodriguez 		if (WARN_ON(ret))
1184d9c6a72dSLuis R. Rodriguez 			return ret;
1185d9c6a72dSLuis R. Rodriguez 	}
1186d9c6a72dSLuis R. Rodriguez 
1187d9c6a72dSLuis R. Rodriguez 	return 0;
1188d9c6a72dSLuis R. Rodriguez }
1189d9c6a72dSLuis R. Rodriguez late_initcall(test_kmod_init);
1190d9c6a72dSLuis R. Rodriguez 
1191d9c6a72dSLuis R. Rodriguez static
unregister_test_dev_kmod(struct kmod_test_device * test_dev)1192d9c6a72dSLuis R. Rodriguez void unregister_test_dev_kmod(struct kmod_test_device *test_dev)
1193d9c6a72dSLuis R. Rodriguez {
1194d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->trigger_mutex);
1195d9c6a72dSLuis R. Rodriguez 	mutex_lock(&test_dev->config_mutex);
1196d9c6a72dSLuis R. Rodriguez 
1197d9c6a72dSLuis R. Rodriguez 	test_dev_kmod_stop_tests(test_dev);
1198d9c6a72dSLuis R. Rodriguez 
1199d9c6a72dSLuis R. Rodriguez 	dev_info(test_dev->dev, "removing interface\n");
1200d9c6a72dSLuis R. Rodriguez 	misc_deregister(&test_dev->misc_dev);
1201d9c6a72dSLuis R. Rodriguez 
1202d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->config_mutex);
1203d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&test_dev->trigger_mutex);
1204d9c6a72dSLuis R. Rodriguez 
1205d9c6a72dSLuis R. Rodriguez 	free_test_dev_kmod(test_dev);
1206d9c6a72dSLuis R. Rodriguez }
1207d9c6a72dSLuis R. Rodriguez 
test_kmod_exit(void)1208d9c6a72dSLuis R. Rodriguez static void __exit test_kmod_exit(void)
1209d9c6a72dSLuis R. Rodriguez {
1210d9c6a72dSLuis R. Rodriguez 	struct kmod_test_device *test_dev, *tmp;
1211d9c6a72dSLuis R. Rodriguez 
1212d9c6a72dSLuis R. Rodriguez 	mutex_lock(&reg_dev_mutex);
1213d9c6a72dSLuis R. Rodriguez 	list_for_each_entry_safe(test_dev, tmp, &reg_test_devs, list) {
1214d9c6a72dSLuis R. Rodriguez 		list_del(&test_dev->list);
1215d9c6a72dSLuis R. Rodriguez 		unregister_test_dev_kmod(test_dev);
1216d9c6a72dSLuis R. Rodriguez 	}
1217d9c6a72dSLuis R. Rodriguez 	mutex_unlock(&reg_dev_mutex);
1218d9c6a72dSLuis R. Rodriguez }
1219d9c6a72dSLuis R. Rodriguez module_exit(test_kmod_exit);
1220d9c6a72dSLuis R. Rodriguez 
1221d9c6a72dSLuis R. Rodriguez MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
1222d9c6a72dSLuis R. Rodriguez MODULE_LICENSE("GPL");
1223