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