18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * kmod stress test driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free
88c2ecf20Sopenharmony_ci * Software Foundation; either version 2 of the License, or at your option any
98c2ecf20Sopenharmony_ci * later version; or, when distributed separately from the Linux kernel or
108c2ecf20Sopenharmony_ci * when incorporated into other software packages, subject to the following
118c2ecf20Sopenharmony_ci * license:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
148c2ecf20Sopenharmony_ci * under the terms of copyleft-next (version 0.3.1 or later) as published
158c2ecf20Sopenharmony_ci * at http://copyleft-next.org/.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * This driver provides an interface to trigger and test the kernel's
218c2ecf20Sopenharmony_ci * module loader through a series of configurations and a few triggers.
228c2ecf20Sopenharmony_ci * To test this driver use the following script as root:
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * tools/testing/selftests/kmod/kmod.sh --help
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/kernel.h>
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/kmod.h>
308c2ecf20Sopenharmony_ci#include <linux/printk.h>
318c2ecf20Sopenharmony_ci#include <linux/kthread.h>
328c2ecf20Sopenharmony_ci#include <linux/sched.h>
338c2ecf20Sopenharmony_ci#include <linux/fs.h>
348c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
358c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
368c2ecf20Sopenharmony_ci#include <linux/slab.h>
378c2ecf20Sopenharmony_ci#include <linux/device.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define TEST_START_NUM_THREADS	50
408c2ecf20Sopenharmony_ci#define TEST_START_DRIVER	"test_module"
418c2ecf20Sopenharmony_ci#define TEST_START_TEST_FS	"xfs"
428c2ecf20Sopenharmony_ci#define TEST_START_TEST_CASE	TEST_KMOD_DRIVER
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic bool force_init_test = false;
468c2ecf20Sopenharmony_cimodule_param(force_init_test, bool_enable_only, 0644);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_init_test,
488c2ecf20Sopenharmony_ci		 "Force kicking a test immediately after driver loads");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * For device allocation / registration
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(reg_dev_mutex);
548c2ecf20Sopenharmony_cistatic LIST_HEAD(reg_test_devs);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * num_test_devs actually represents the *next* ID of the next
588c2ecf20Sopenharmony_ci * device we will allow to create.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic int num_test_devs;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/**
638c2ecf20Sopenharmony_ci * enum kmod_test_case - linker table test case
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci * If you add a  test case, please be sure to review if you need to se
668c2ecf20Sopenharmony_ci * @need_mod_put for your tests case.
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * @TEST_KMOD_DRIVER: stress tests request_module()
698c2ecf20Sopenharmony_ci * @TEST_KMOD_FS_TYPE: stress tests get_fs_type()
708c2ecf20Sopenharmony_ci */
718c2ecf20Sopenharmony_cienum kmod_test_case {
728c2ecf20Sopenharmony_ci	__TEST_KMOD_INVALID = 0,
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	TEST_KMOD_DRIVER,
758c2ecf20Sopenharmony_ci	TEST_KMOD_FS_TYPE,
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	__TEST_KMOD_MAX,
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct test_config {
818c2ecf20Sopenharmony_ci	char *test_driver;
828c2ecf20Sopenharmony_ci	char *test_fs;
838c2ecf20Sopenharmony_ci	unsigned int num_threads;
848c2ecf20Sopenharmony_ci	enum kmod_test_case test_case;
858c2ecf20Sopenharmony_ci	int test_result;
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistruct kmod_test_device;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/**
918c2ecf20Sopenharmony_ci * kmod_test_device_info - thread info
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * @ret_sync: return value if request_module() is used, sync request for
948c2ecf20Sopenharmony_ci * 	@TEST_KMOD_DRIVER
958c2ecf20Sopenharmony_ci * @fs_sync: return value of get_fs_type() for @TEST_KMOD_FS_TYPE
968c2ecf20Sopenharmony_ci * @thread_idx: thread ID
978c2ecf20Sopenharmony_ci * @test_dev: test device test is being performed under
988c2ecf20Sopenharmony_ci * @need_mod_put: Some tests (get_fs_type() is one) requires putting the module
998c2ecf20Sopenharmony_ci *	(module_put(fs_sync->owner)) when done, otherwise you will not be able
1008c2ecf20Sopenharmony_ci *	to unload the respective modules and re-test. We use this to keep
1018c2ecf20Sopenharmony_ci *	accounting of when we need this and to help out in case we need to
1028c2ecf20Sopenharmony_ci *	error out and deal with module_put() on error.
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_cistruct kmod_test_device_info {
1058c2ecf20Sopenharmony_ci	int ret_sync;
1068c2ecf20Sopenharmony_ci	struct file_system_type *fs_sync;
1078c2ecf20Sopenharmony_ci	struct task_struct *task_sync;
1088c2ecf20Sopenharmony_ci	unsigned int thread_idx;
1098c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev;
1108c2ecf20Sopenharmony_ci	bool need_mod_put;
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/**
1148c2ecf20Sopenharmony_ci * kmod_test_device - test device to help test kmod
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci * @dev_idx: unique ID for test device
1178c2ecf20Sopenharmony_ci * @config: configuration for the test
1188c2ecf20Sopenharmony_ci * @misc_dev: we use a misc device under the hood
1198c2ecf20Sopenharmony_ci * @dev: pointer to misc_dev's own struct device
1208c2ecf20Sopenharmony_ci * @config_mutex: protects configuration of test
1218c2ecf20Sopenharmony_ci * @trigger_mutex: the test trigger can only be fired once at a time
1228c2ecf20Sopenharmony_ci * @thread_lock: protects @done count, and the @info per each thread
1238c2ecf20Sopenharmony_ci * @done: number of threads which have completed or failed
1248c2ecf20Sopenharmony_ci * @test_is_oom: when we run out of memory, use this to halt moving forward
1258c2ecf20Sopenharmony_ci * @kthreads_done: completion used to signal when all work is done
1268c2ecf20Sopenharmony_ci * @list: needed to be part of the reg_test_devs
1278c2ecf20Sopenharmony_ci * @info: array of info for each thread
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_cistruct kmod_test_device {
1308c2ecf20Sopenharmony_ci	int dev_idx;
1318c2ecf20Sopenharmony_ci	struct test_config config;
1328c2ecf20Sopenharmony_ci	struct miscdevice misc_dev;
1338c2ecf20Sopenharmony_ci	struct device *dev;
1348c2ecf20Sopenharmony_ci	struct mutex config_mutex;
1358c2ecf20Sopenharmony_ci	struct mutex trigger_mutex;
1368c2ecf20Sopenharmony_ci	struct mutex thread_mutex;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	unsigned int done;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	bool test_is_oom;
1418c2ecf20Sopenharmony_ci	struct completion kthreads_done;
1428c2ecf20Sopenharmony_ci	struct list_head list;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	struct kmod_test_device_info *info;
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic const char *test_case_str(enum kmod_test_case test_case)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	switch (test_case) {
1508c2ecf20Sopenharmony_ci	case TEST_KMOD_DRIVER:
1518c2ecf20Sopenharmony_ci		return "TEST_KMOD_DRIVER";
1528c2ecf20Sopenharmony_ci	case TEST_KMOD_FS_TYPE:
1538c2ecf20Sopenharmony_ci		return "TEST_KMOD_FS_TYPE";
1548c2ecf20Sopenharmony_ci	default:
1558c2ecf20Sopenharmony_ci		return "invalid";
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic struct miscdevice *dev_to_misc_dev(struct device *dev)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	return dev_get_drvdata(dev);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic struct kmod_test_device *misc_dev_to_test_dev(struct miscdevice *misc_dev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return container_of(misc_dev, struct kmod_test_device, misc_dev);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct kmod_test_device *dev_to_test_dev(struct device *dev)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct miscdevice *misc_dev;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	misc_dev = dev_to_misc_dev(dev);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return misc_dev_to_test_dev(misc_dev);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* Must run with thread_mutex held */
1798c2ecf20Sopenharmony_cistatic void kmod_test_done_check(struct kmod_test_device *test_dev,
1808c2ecf20Sopenharmony_ci				 unsigned int idx)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	test_dev->done++;
1858c2ecf20Sopenharmony_ci	dev_dbg(test_dev->dev, "Done thread count: %u\n", test_dev->done);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (test_dev->done == config->num_threads) {
1888c2ecf20Sopenharmony_ci		dev_info(test_dev->dev, "Done: %u threads have all run now\n",
1898c2ecf20Sopenharmony_ci			 test_dev->done);
1908c2ecf20Sopenharmony_ci		dev_info(test_dev->dev, "Last thread to run: %u\n", idx);
1918c2ecf20Sopenharmony_ci		complete(&test_dev->kthreads_done);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic void test_kmod_put_module(struct kmod_test_device_info *info)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = info->test_dev;
1988c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (!info->need_mod_put)
2018c2ecf20Sopenharmony_ci		return;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	switch (config->test_case) {
2048c2ecf20Sopenharmony_ci	case TEST_KMOD_DRIVER:
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	case TEST_KMOD_FS_TYPE:
2078c2ecf20Sopenharmony_ci		if (info->fs_sync && info->fs_sync->owner)
2088c2ecf20Sopenharmony_ci			module_put(info->fs_sync->owner);
2098c2ecf20Sopenharmony_ci		break;
2108c2ecf20Sopenharmony_ci	default:
2118c2ecf20Sopenharmony_ci		BUG();
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	info->need_mod_put = true;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int run_request(void *data)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct kmod_test_device_info *info = data;
2208c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = info->test_dev;
2218c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	switch (config->test_case) {
2248c2ecf20Sopenharmony_ci	case TEST_KMOD_DRIVER:
2258c2ecf20Sopenharmony_ci		info->ret_sync = request_module("%s", config->test_driver);
2268c2ecf20Sopenharmony_ci		break;
2278c2ecf20Sopenharmony_ci	case TEST_KMOD_FS_TYPE:
2288c2ecf20Sopenharmony_ci		info->fs_sync = get_fs_type(config->test_fs);
2298c2ecf20Sopenharmony_ci		info->need_mod_put = true;
2308c2ecf20Sopenharmony_ci		break;
2318c2ecf20Sopenharmony_ci	default:
2328c2ecf20Sopenharmony_ci		/* __trigger_config_run() already checked for test sanity */
2338c2ecf20Sopenharmony_ci		BUG();
2348c2ecf20Sopenharmony_ci		return -EINVAL;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	dev_dbg(test_dev->dev, "Ran thread %u\n", info->thread_idx);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	test_kmod_put_module(info);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->thread_mutex);
2428c2ecf20Sopenharmony_ci	info->task_sync = NULL;
2438c2ecf20Sopenharmony_ci	kmod_test_done_check(test_dev, info->thread_idx);
2448c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->thread_mutex);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int tally_work_test(struct kmod_test_device_info *info)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = info->test_dev;
2528c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
2538c2ecf20Sopenharmony_ci	int err_ret = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	switch (config->test_case) {
2568c2ecf20Sopenharmony_ci	case TEST_KMOD_DRIVER:
2578c2ecf20Sopenharmony_ci		/*
2588c2ecf20Sopenharmony_ci		 * Only capture errors, if one is found that's
2598c2ecf20Sopenharmony_ci		 * enough, for now.
2608c2ecf20Sopenharmony_ci		 */
2618c2ecf20Sopenharmony_ci		if (info->ret_sync != 0)
2628c2ecf20Sopenharmony_ci			err_ret = info->ret_sync;
2638c2ecf20Sopenharmony_ci		dev_info(test_dev->dev,
2648c2ecf20Sopenharmony_ci			 "Sync thread %d return status: %d\n",
2658c2ecf20Sopenharmony_ci			 info->thread_idx, info->ret_sync);
2668c2ecf20Sopenharmony_ci		break;
2678c2ecf20Sopenharmony_ci	case TEST_KMOD_FS_TYPE:
2688c2ecf20Sopenharmony_ci		/* For now we make this simple */
2698c2ecf20Sopenharmony_ci		if (!info->fs_sync)
2708c2ecf20Sopenharmony_ci			err_ret = -EINVAL;
2718c2ecf20Sopenharmony_ci		dev_info(test_dev->dev, "Sync thread %u fs: %s\n",
2728c2ecf20Sopenharmony_ci			 info->thread_idx, info->fs_sync ? config->test_fs :
2738c2ecf20Sopenharmony_ci			 "NULL");
2748c2ecf20Sopenharmony_ci		break;
2758c2ecf20Sopenharmony_ci	default:
2768c2ecf20Sopenharmony_ci		BUG();
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return err_ret;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * XXX: add result option to display if all errors did not match.
2848c2ecf20Sopenharmony_ci * For now we just keep any error code if one was found.
2858c2ecf20Sopenharmony_ci *
2868c2ecf20Sopenharmony_ci * If this ran it means *all* tasks were created fine and we
2878c2ecf20Sopenharmony_ci * are now just collecting results.
2888c2ecf20Sopenharmony_ci *
2898c2ecf20Sopenharmony_ci * Only propagate errors, do not override with a subsequent sucess case.
2908c2ecf20Sopenharmony_ci */
2918c2ecf20Sopenharmony_cistatic void tally_up_work(struct kmod_test_device *test_dev)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
2948c2ecf20Sopenharmony_ci	struct kmod_test_device_info *info;
2958c2ecf20Sopenharmony_ci	unsigned int idx;
2968c2ecf20Sopenharmony_ci	int err_ret = 0;
2978c2ecf20Sopenharmony_ci	int ret = 0;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->thread_mutex);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Results:\n");
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	for (idx=0; idx < config->num_threads; idx++) {
3048c2ecf20Sopenharmony_ci		info = &test_dev->info[idx];
3058c2ecf20Sopenharmony_ci		ret = tally_work_test(info);
3068c2ecf20Sopenharmony_ci		if (ret)
3078c2ecf20Sopenharmony_ci			err_ret = ret;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/*
3118c2ecf20Sopenharmony_ci	 * Note: request_module() returns 256 for a module not found even
3128c2ecf20Sopenharmony_ci	 * though modprobe itself returns 1.
3138c2ecf20Sopenharmony_ci	 */
3148c2ecf20Sopenharmony_ci	config->test_result = err_ret;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->thread_mutex);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int try_one_request(struct kmod_test_device *test_dev, unsigned int idx)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct kmod_test_device_info *info = &test_dev->info[idx];
3228c2ecf20Sopenharmony_ci	int fail_ret = -ENOMEM;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->thread_mutex);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	info->thread_idx = idx;
3278c2ecf20Sopenharmony_ci	info->test_dev = test_dev;
3288c2ecf20Sopenharmony_ci	info->task_sync = kthread_run(run_request, info, "%s-%u",
3298c2ecf20Sopenharmony_ci				      KBUILD_MODNAME, idx);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (!info->task_sync || IS_ERR(info->task_sync)) {
3328c2ecf20Sopenharmony_ci		test_dev->test_is_oom = true;
3338c2ecf20Sopenharmony_ci		dev_err(test_dev->dev, "Setting up thread %u failed\n", idx);
3348c2ecf20Sopenharmony_ci		info->task_sync = NULL;
3358c2ecf20Sopenharmony_ci		goto err_out;
3368c2ecf20Sopenharmony_ci	} else
3378c2ecf20Sopenharmony_ci		dev_dbg(test_dev->dev, "Kicked off thread %u\n", idx);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->thread_mutex);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cierr_out:
3448c2ecf20Sopenharmony_ci	info->ret_sync = fail_ret;
3458c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->thread_mutex);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return fail_ret;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void test_dev_kmod_stop_tests(struct kmod_test_device *test_dev)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
3538c2ecf20Sopenharmony_ci	struct kmod_test_device_info *info;
3548c2ecf20Sopenharmony_ci	unsigned int i;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Ending request_module() tests\n");
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->thread_mutex);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	for (i=0; i < config->num_threads; i++) {
3618c2ecf20Sopenharmony_ci		info = &test_dev->info[i];
3628c2ecf20Sopenharmony_ci		if (info->task_sync && !IS_ERR(info->task_sync)) {
3638c2ecf20Sopenharmony_ci			dev_info(test_dev->dev,
3648c2ecf20Sopenharmony_ci				 "Stopping still-running thread %i\n", i);
3658c2ecf20Sopenharmony_ci			kthread_stop(info->task_sync);
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		/*
3698c2ecf20Sopenharmony_ci		 * info->task_sync is well protected, it can only be
3708c2ecf20Sopenharmony_ci		 * NULL or a pointer to a struct. If its NULL we either
3718c2ecf20Sopenharmony_ci		 * never ran, or we did and we completed the work. Completed
3728c2ecf20Sopenharmony_ci		 * tasks *always* put the module for us. This is a sanity
3738c2ecf20Sopenharmony_ci		 * check -- just in case.
3748c2ecf20Sopenharmony_ci		 */
3758c2ecf20Sopenharmony_ci		if (info->task_sync && info->need_mod_put)
3768c2ecf20Sopenharmony_ci			test_kmod_put_module(info);
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->thread_mutex);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci/*
3838c2ecf20Sopenharmony_ci * Only wait *iff* we did not run into any errors during all of our thread
3848c2ecf20Sopenharmony_ci * set up. If run into any issues we stop threads and just bail out with
3858c2ecf20Sopenharmony_ci * an error to the trigger. This also means we don't need any tally work
3868c2ecf20Sopenharmony_ci * for any threads which fail.
3878c2ecf20Sopenharmony_ci */
3888c2ecf20Sopenharmony_cistatic int try_requests(struct kmod_test_device *test_dev)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
3918c2ecf20Sopenharmony_ci	unsigned int idx;
3928c2ecf20Sopenharmony_ci	int ret;
3938c2ecf20Sopenharmony_ci	bool any_error = false;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	for (idx=0; idx < config->num_threads; idx++) {
3968c2ecf20Sopenharmony_ci		if (test_dev->test_is_oom) {
3978c2ecf20Sopenharmony_ci			any_error = true;
3988c2ecf20Sopenharmony_ci			break;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		ret = try_one_request(test_dev, idx);
4028c2ecf20Sopenharmony_ci		if (ret) {
4038c2ecf20Sopenharmony_ci			any_error = true;
4048c2ecf20Sopenharmony_ci			break;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (!any_error) {
4098c2ecf20Sopenharmony_ci		test_dev->test_is_oom = false;
4108c2ecf20Sopenharmony_ci		dev_info(test_dev->dev,
4118c2ecf20Sopenharmony_ci			 "No errors were found while initializing threads\n");
4128c2ecf20Sopenharmony_ci		wait_for_completion(&test_dev->kthreads_done);
4138c2ecf20Sopenharmony_ci		tally_up_work(test_dev);
4148c2ecf20Sopenharmony_ci	} else {
4158c2ecf20Sopenharmony_ci		test_dev->test_is_oom = true;
4168c2ecf20Sopenharmony_ci		dev_info(test_dev->dev,
4178c2ecf20Sopenharmony_ci			 "At least one thread failed to start, stop all work\n");
4188c2ecf20Sopenharmony_ci		test_dev_kmod_stop_tests(test_dev);
4198c2ecf20Sopenharmony_ci		return -ENOMEM;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int run_test_driver(struct kmod_test_device *test_dev)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Test case: %s (%u)\n",
4308c2ecf20Sopenharmony_ci		 test_case_str(config->test_case),
4318c2ecf20Sopenharmony_ci		 config->test_case);
4328c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Test driver to load: %s\n",
4338c2ecf20Sopenharmony_ci		 config->test_driver);
4348c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Number of threads to run: %u\n",
4358c2ecf20Sopenharmony_ci		 config->num_threads);
4368c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Thread IDs will range from 0 - %u\n",
4378c2ecf20Sopenharmony_ci		 config->num_threads - 1);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return try_requests(test_dev);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int run_test_fs_type(struct kmod_test_device *test_dev)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Test case: %s (%u)\n",
4478c2ecf20Sopenharmony_ci		 test_case_str(config->test_case),
4488c2ecf20Sopenharmony_ci		 config->test_case);
4498c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Test filesystem to load: %s\n",
4508c2ecf20Sopenharmony_ci		 config->test_fs);
4518c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Number of threads to run: %u\n",
4528c2ecf20Sopenharmony_ci		 config->num_threads);
4538c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "Thread IDs will range from 0 - %u\n",
4548c2ecf20Sopenharmony_ci		 config->num_threads - 1);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return try_requests(test_dev);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic ssize_t config_show(struct device *dev,
4608c2ecf20Sopenharmony_ci			   struct device_attribute *attr,
4618c2ecf20Sopenharmony_ci			   char *buf)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
4648c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
4658c2ecf20Sopenharmony_ci	int len = 0;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	len += snprintf(buf, PAGE_SIZE,
4708c2ecf20Sopenharmony_ci			"Custom trigger configuration for: %s\n",
4718c2ecf20Sopenharmony_ci			dev_name(dev));
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	len += snprintf(buf+len, PAGE_SIZE - len,
4748c2ecf20Sopenharmony_ci			"Number of threads:\t%u\n",
4758c2ecf20Sopenharmony_ci			config->num_threads);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	len += snprintf(buf+len, PAGE_SIZE - len,
4788c2ecf20Sopenharmony_ci			"Test_case:\t%s (%u)\n",
4798c2ecf20Sopenharmony_ci			test_case_str(config->test_case),
4808c2ecf20Sopenharmony_ci			config->test_case);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (config->test_driver)
4838c2ecf20Sopenharmony_ci		len += snprintf(buf+len, PAGE_SIZE - len,
4848c2ecf20Sopenharmony_ci				"driver:\t%s\n",
4858c2ecf20Sopenharmony_ci				config->test_driver);
4868c2ecf20Sopenharmony_ci	else
4878c2ecf20Sopenharmony_ci		len += snprintf(buf+len, PAGE_SIZE - len,
4888c2ecf20Sopenharmony_ci				"driver:\tEMPTY\n");
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (config->test_fs)
4918c2ecf20Sopenharmony_ci		len += snprintf(buf+len, PAGE_SIZE - len,
4928c2ecf20Sopenharmony_ci				"fs:\t%s\n",
4938c2ecf20Sopenharmony_ci				config->test_fs);
4948c2ecf20Sopenharmony_ci	else
4958c2ecf20Sopenharmony_ci		len += snprintf(buf+len, PAGE_SIZE - len,
4968c2ecf20Sopenharmony_ci				"fs:\tEMPTY\n");
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	return len;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(config);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/*
5058c2ecf20Sopenharmony_ci * This ensures we don't allow kicking threads through if our configuration
5068c2ecf20Sopenharmony_ci * is faulty.
5078c2ecf20Sopenharmony_ci */
5088c2ecf20Sopenharmony_cistatic int __trigger_config_run(struct kmod_test_device *test_dev)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	test_dev->done = 0;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	switch (config->test_case) {
5158c2ecf20Sopenharmony_ci	case TEST_KMOD_DRIVER:
5168c2ecf20Sopenharmony_ci		return run_test_driver(test_dev);
5178c2ecf20Sopenharmony_ci	case TEST_KMOD_FS_TYPE:
5188c2ecf20Sopenharmony_ci		return run_test_fs_type(test_dev);
5198c2ecf20Sopenharmony_ci	default:
5208c2ecf20Sopenharmony_ci		dev_warn(test_dev->dev,
5218c2ecf20Sopenharmony_ci			 "Invalid test case requested: %u\n",
5228c2ecf20Sopenharmony_ci			 config->test_case);
5238c2ecf20Sopenharmony_ci		return -EINVAL;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic int trigger_config_run(struct kmod_test_device *test_dev)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
5308c2ecf20Sopenharmony_ci	int ret;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->trigger_mutex);
5338c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	ret = __trigger_config_run(test_dev);
5368c2ecf20Sopenharmony_ci	if (ret < 0)
5378c2ecf20Sopenharmony_ci		goto out;
5388c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "General test result: %d\n",
5398c2ecf20Sopenharmony_ci		 config->test_result);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/*
5428c2ecf20Sopenharmony_ci	 * We must return 0 after a trigger even unless something went
5438c2ecf20Sopenharmony_ci	 * wrong with the setup of the test. If the test setup went fine
5448c2ecf20Sopenharmony_ci	 * then userspace must just check the result of config->test_result.
5458c2ecf20Sopenharmony_ci	 * One issue with relying on the return from a call in the kernel
5468c2ecf20Sopenharmony_ci	 * is if the kernel returns a possitive value using this trigger
5478c2ecf20Sopenharmony_ci	 * will not return the value to userspace, it would be lost.
5488c2ecf20Sopenharmony_ci	 *
5498c2ecf20Sopenharmony_ci	 * By not relying on capturing the return value of tests we are using
5508c2ecf20Sopenharmony_ci	 * through the trigger it also us to run tests with set -e and only
5518c2ecf20Sopenharmony_ci	 * fail when something went wrong with the driver upon trigger
5528c2ecf20Sopenharmony_ci	 * requests.
5538c2ecf20Sopenharmony_ci	 */
5548c2ecf20Sopenharmony_ci	ret = 0;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ciout:
5578c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
5588c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->trigger_mutex);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	return ret;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic ssize_t
5648c2ecf20Sopenharmony_citrigger_config_store(struct device *dev,
5658c2ecf20Sopenharmony_ci		     struct device_attribute *attr,
5668c2ecf20Sopenharmony_ci		     const char *buf, size_t count)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
5698c2ecf20Sopenharmony_ci	int ret;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (test_dev->test_is_oom)
5728c2ecf20Sopenharmony_ci		return -ENOMEM;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* For all intents and purposes we don't care what userspace
5758c2ecf20Sopenharmony_ci	 * sent this trigger, we care only that we were triggered.
5768c2ecf20Sopenharmony_ci	 * We treat the return value only for caputuring issues with
5778c2ecf20Sopenharmony_ci	 * the test setup. At this point all the test variables should
5788c2ecf20Sopenharmony_ci	 * have been allocated so typically this should never fail.
5798c2ecf20Sopenharmony_ci	 */
5808c2ecf20Sopenharmony_ci	ret = trigger_config_run(test_dev);
5818c2ecf20Sopenharmony_ci	if (unlikely(ret < 0))
5828c2ecf20Sopenharmony_ci		goto out;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/*
5858c2ecf20Sopenharmony_ci	 * Note: any return > 0 will be treated as success
5868c2ecf20Sopenharmony_ci	 * and the error value will not be available to userspace.
5878c2ecf20Sopenharmony_ci	 * Do not rely on trying to send to userspace a test value
5888c2ecf20Sopenharmony_ci	 * return value as possitive return errors will be lost.
5898c2ecf20Sopenharmony_ci	 */
5908c2ecf20Sopenharmony_ci	if (WARN_ON(ret > 0))
5918c2ecf20Sopenharmony_ci		return -EINVAL;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ret = count;
5948c2ecf20Sopenharmony_ciout:
5958c2ecf20Sopenharmony_ci	return ret;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(trigger_config);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci/*
6008c2ecf20Sopenharmony_ci * XXX: move to kstrncpy() once merged.
6018c2ecf20Sopenharmony_ci *
6028c2ecf20Sopenharmony_ci * Users should use kfree_const() when freeing these.
6038c2ecf20Sopenharmony_ci */
6048c2ecf20Sopenharmony_cistatic int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	*dst = kstrndup(name, count, gfp);
6078c2ecf20Sopenharmony_ci	if (!*dst)
6088c2ecf20Sopenharmony_ci		return -ENOSPC;
6098c2ecf20Sopenharmony_ci	return count;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int config_copy_test_driver_name(struct test_config *config,
6138c2ecf20Sopenharmony_ci				    const char *name,
6148c2ecf20Sopenharmony_ci				    size_t count)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	return __kstrncpy(&config->test_driver, name, count, GFP_KERNEL);
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int config_copy_test_fs(struct test_config *config, const char *name,
6218c2ecf20Sopenharmony_ci			       size_t count)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	return __kstrncpy(&config->test_fs, name, count, GFP_KERNEL);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic void __kmod_config_free(struct test_config *config)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	if (!config)
6298c2ecf20Sopenharmony_ci		return;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	kfree_const(config->test_driver);
6328c2ecf20Sopenharmony_ci	config->test_driver = NULL;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	kfree_const(config->test_fs);
6358c2ecf20Sopenharmony_ci	config->test_fs = NULL;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void kmod_config_free(struct kmod_test_device *test_dev)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct test_config *config;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (!test_dev)
6438c2ecf20Sopenharmony_ci		return;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	config = &test_dev->config;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
6488c2ecf20Sopenharmony_ci	__kmod_config_free(config);
6498c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic ssize_t config_test_driver_store(struct device *dev,
6538c2ecf20Sopenharmony_ci					struct device_attribute *attr,
6548c2ecf20Sopenharmony_ci					const char *buf, size_t count)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
6578c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
6588c2ecf20Sopenharmony_ci	int copied;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	kfree_const(config->test_driver);
6638c2ecf20Sopenharmony_ci	config->test_driver = NULL;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	copied = config_copy_test_driver_name(config, buf, count);
6668c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	return copied;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci/*
6728c2ecf20Sopenharmony_ci * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
6738c2ecf20Sopenharmony_ci */
6748c2ecf20Sopenharmony_cistatic ssize_t config_test_show_str(struct mutex *config_mutex,
6758c2ecf20Sopenharmony_ci				    char *dst,
6768c2ecf20Sopenharmony_ci				    char *src)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	int len;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	mutex_lock(config_mutex);
6818c2ecf20Sopenharmony_ci	len = snprintf(dst, PAGE_SIZE, "%s\n", src);
6828c2ecf20Sopenharmony_ci	mutex_unlock(config_mutex);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	return len;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic ssize_t config_test_driver_show(struct device *dev,
6888c2ecf20Sopenharmony_ci					struct device_attribute *attr,
6898c2ecf20Sopenharmony_ci					char *buf)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
6928c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	return config_test_show_str(&test_dev->config_mutex, buf,
6958c2ecf20Sopenharmony_ci				    config->test_driver);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(config_test_driver);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic ssize_t config_test_fs_store(struct device *dev,
7008c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
7018c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
7048c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
7058c2ecf20Sopenharmony_ci	int copied;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	kfree_const(config->test_fs);
7108c2ecf20Sopenharmony_ci	config->test_fs = NULL;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	copied = config_copy_test_fs(config, buf, count);
7138c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return copied;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_cistatic ssize_t config_test_fs_show(struct device *dev,
7198c2ecf20Sopenharmony_ci				   struct device_attribute *attr,
7208c2ecf20Sopenharmony_ci				   char *buf)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
7238c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return config_test_show_str(&test_dev->config_mutex, buf,
7268c2ecf20Sopenharmony_ci				    config->test_fs);
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(config_test_fs);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic int trigger_config_run_type(struct kmod_test_device *test_dev,
7318c2ecf20Sopenharmony_ci				   enum kmod_test_case test_case,
7328c2ecf20Sopenharmony_ci				   const char *test_str)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	int copied = 0;
7358c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	switch (test_case) {
7408c2ecf20Sopenharmony_ci	case TEST_KMOD_DRIVER:
7418c2ecf20Sopenharmony_ci		kfree_const(config->test_driver);
7428c2ecf20Sopenharmony_ci		config->test_driver = NULL;
7438c2ecf20Sopenharmony_ci		copied = config_copy_test_driver_name(config, test_str,
7448c2ecf20Sopenharmony_ci						      strlen(test_str));
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	case TEST_KMOD_FS_TYPE:
7478c2ecf20Sopenharmony_ci		kfree_const(config->test_fs);
7488c2ecf20Sopenharmony_ci		config->test_fs = NULL;
7498c2ecf20Sopenharmony_ci		copied = config_copy_test_fs(config, test_str,
7508c2ecf20Sopenharmony_ci					     strlen(test_str));
7518c2ecf20Sopenharmony_ci		break;
7528c2ecf20Sopenharmony_ci	default:
7538c2ecf20Sopenharmony_ci		mutex_unlock(&test_dev->config_mutex);
7548c2ecf20Sopenharmony_ci		return -EINVAL;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	config->test_case = test_case;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (copied <= 0 || copied != strlen(test_str)) {
7628c2ecf20Sopenharmony_ci		test_dev->test_is_oom = true;
7638c2ecf20Sopenharmony_ci		return -ENOMEM;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	test_dev->test_is_oom = false;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return trigger_config_run(test_dev);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic void free_test_dev_info(struct kmod_test_device *test_dev)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	vfree(test_dev->info);
7748c2ecf20Sopenharmony_ci	test_dev->info = NULL;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cistatic int kmod_config_sync_info(struct kmod_test_device *test_dev)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	free_test_dev_info(test_dev);
7828c2ecf20Sopenharmony_ci	test_dev->info =
7838c2ecf20Sopenharmony_ci		vzalloc(array_size(sizeof(struct kmod_test_device_info),
7848c2ecf20Sopenharmony_ci				   config->num_threads));
7858c2ecf20Sopenharmony_ci	if (!test_dev->info)
7868c2ecf20Sopenharmony_ci		return -ENOMEM;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	return 0;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci/*
7928c2ecf20Sopenharmony_ci * Old kernels may not have this, if you want to port this code to
7938c2ecf20Sopenharmony_ci * test it on older kernels.
7948c2ecf20Sopenharmony_ci */
7958c2ecf20Sopenharmony_ci#ifdef get_kmod_umh_limit
7968c2ecf20Sopenharmony_cistatic unsigned int kmod_init_test_thread_limit(void)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	return get_kmod_umh_limit();
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci#else
8018c2ecf20Sopenharmony_cistatic unsigned int kmod_init_test_thread_limit(void)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	return TEST_START_NUM_THREADS;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci#endif
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic int __kmod_config_init(struct kmod_test_device *test_dev)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
8108c2ecf20Sopenharmony_ci	int ret = -ENOMEM, copied;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	__kmod_config_free(config);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	copied = config_copy_test_driver_name(config, TEST_START_DRIVER,
8158c2ecf20Sopenharmony_ci					      strlen(TEST_START_DRIVER));
8168c2ecf20Sopenharmony_ci	if (copied != strlen(TEST_START_DRIVER))
8178c2ecf20Sopenharmony_ci		goto err_out;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	copied = config_copy_test_fs(config, TEST_START_TEST_FS,
8208c2ecf20Sopenharmony_ci				     strlen(TEST_START_TEST_FS));
8218c2ecf20Sopenharmony_ci	if (copied != strlen(TEST_START_TEST_FS))
8228c2ecf20Sopenharmony_ci		goto err_out;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	config->num_threads = kmod_init_test_thread_limit();
8258c2ecf20Sopenharmony_ci	config->test_result = 0;
8268c2ecf20Sopenharmony_ci	config->test_case = TEST_START_TEST_CASE;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	ret = kmod_config_sync_info(test_dev);
8298c2ecf20Sopenharmony_ci	if (ret)
8308c2ecf20Sopenharmony_ci		goto err_out;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	test_dev->test_is_oom = false;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	return 0;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cierr_out:
8378c2ecf20Sopenharmony_ci	test_dev->test_is_oom = true;
8388c2ecf20Sopenharmony_ci	WARN_ON(test_dev->test_is_oom);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	__kmod_config_free(config);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	return ret;
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic ssize_t reset_store(struct device *dev,
8468c2ecf20Sopenharmony_ci			   struct device_attribute *attr,
8478c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
8508c2ecf20Sopenharmony_ci	int ret;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->trigger_mutex);
8538c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	ret = __kmod_config_init(test_dev);
8568c2ecf20Sopenharmony_ci	if (ret < 0) {
8578c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8588c2ecf20Sopenharmony_ci		dev_err(dev, "could not alloc settings for config trigger: %d\n",
8598c2ecf20Sopenharmony_ci		       ret);
8608c2ecf20Sopenharmony_ci		goto out;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	dev_info(dev, "reset\n");
8648c2ecf20Sopenharmony_ci	ret = count;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ciout:
8678c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
8688c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->trigger_mutex);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	return ret;
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(reset);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic int test_dev_config_update_uint_sync(struct kmod_test_device *test_dev,
8758c2ecf20Sopenharmony_ci					    const char *buf, size_t size,
8768c2ecf20Sopenharmony_ci					    unsigned int *config,
8778c2ecf20Sopenharmony_ci					    int (*test_sync)(struct kmod_test_device *test_dev))
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	int ret;
8808c2ecf20Sopenharmony_ci	unsigned int val;
8818c2ecf20Sopenharmony_ci	unsigned int old_val;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	ret = kstrtouint(buf, 10, &val);
8848c2ecf20Sopenharmony_ci	if (ret)
8858c2ecf20Sopenharmony_ci		return ret;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	old_val = *config;
8908c2ecf20Sopenharmony_ci	*(unsigned int *)config = val;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	ret = test_sync(test_dev);
8938c2ecf20Sopenharmony_ci	if (ret) {
8948c2ecf20Sopenharmony_ci		*(unsigned int *)config = old_val;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		ret = test_sync(test_dev);
8978c2ecf20Sopenharmony_ci		WARN_ON(ret);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		mutex_unlock(&test_dev->config_mutex);
9008c2ecf20Sopenharmony_ci		return -EINVAL;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
9048c2ecf20Sopenharmony_ci	/* Always return full write size even if we didn't consume all */
9058c2ecf20Sopenharmony_ci	return size;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic int test_dev_config_update_uint_range(struct kmod_test_device *test_dev,
9098c2ecf20Sopenharmony_ci					     const char *buf, size_t size,
9108c2ecf20Sopenharmony_ci					     unsigned int *config,
9118c2ecf20Sopenharmony_ci					     unsigned int min,
9128c2ecf20Sopenharmony_ci					     unsigned int max)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	unsigned int val;
9158c2ecf20Sopenharmony_ci	int ret;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	ret = kstrtouint(buf, 10, &val);
9188c2ecf20Sopenharmony_ci	if (ret)
9198c2ecf20Sopenharmony_ci		return ret;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (val < min || val > max)
9228c2ecf20Sopenharmony_ci		return -EINVAL;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
9258c2ecf20Sopenharmony_ci	*config = val;
9268c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	/* Always return full write size even if we didn't consume all */
9298c2ecf20Sopenharmony_ci	return size;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic int test_dev_config_update_int(struct kmod_test_device *test_dev,
9338c2ecf20Sopenharmony_ci				      const char *buf, size_t size,
9348c2ecf20Sopenharmony_ci				      int *config)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	int val;
9378c2ecf20Sopenharmony_ci	int ret;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	ret = kstrtoint(buf, 10, &val);
9408c2ecf20Sopenharmony_ci	if (ret)
9418c2ecf20Sopenharmony_ci		return ret;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
9448c2ecf20Sopenharmony_ci	*config = val;
9458c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
9468c2ecf20Sopenharmony_ci	/* Always return full write size even if we didn't consume all */
9478c2ecf20Sopenharmony_ci	return size;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic ssize_t test_dev_config_show_int(struct kmod_test_device *test_dev,
9518c2ecf20Sopenharmony_ci					char *buf,
9528c2ecf20Sopenharmony_ci					int config)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	int val;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
9578c2ecf20Sopenharmony_ci	val = config;
9588c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic ssize_t test_dev_config_show_uint(struct kmod_test_device *test_dev,
9648c2ecf20Sopenharmony_ci					 char *buf,
9658c2ecf20Sopenharmony_ci					 unsigned int config)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	unsigned int val;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
9708c2ecf20Sopenharmony_ci	val = config;
9718c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", val);
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic ssize_t test_result_store(struct device *dev,
9778c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
9788c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
9818c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return test_dev_config_update_int(test_dev, buf, count,
9848c2ecf20Sopenharmony_ci					  &config->test_result);
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic ssize_t config_num_threads_store(struct device *dev,
9888c2ecf20Sopenharmony_ci					struct device_attribute *attr,
9898c2ecf20Sopenharmony_ci					const char *buf, size_t count)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
9928c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	return test_dev_config_update_uint_sync(test_dev, buf, count,
9958c2ecf20Sopenharmony_ci						&config->num_threads,
9968c2ecf20Sopenharmony_ci						kmod_config_sync_info);
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cistatic ssize_t config_num_threads_show(struct device *dev,
10008c2ecf20Sopenharmony_ci				       struct device_attribute *attr,
10018c2ecf20Sopenharmony_ci				       char *buf)
10028c2ecf20Sopenharmony_ci{
10038c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
10048c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	return test_dev_config_show_int(test_dev, buf, config->num_threads);
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(config_num_threads);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistatic ssize_t config_test_case_store(struct device *dev,
10118c2ecf20Sopenharmony_ci				      struct device_attribute *attr,
10128c2ecf20Sopenharmony_ci				      const char *buf, size_t count)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
10158c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	return test_dev_config_update_uint_range(test_dev, buf, count,
10188c2ecf20Sopenharmony_ci						 &config->test_case,
10198c2ecf20Sopenharmony_ci						 __TEST_KMOD_INVALID + 1,
10208c2ecf20Sopenharmony_ci						 __TEST_KMOD_MAX - 1);
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cistatic ssize_t config_test_case_show(struct device *dev,
10248c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
10258c2ecf20Sopenharmony_ci				     char *buf)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
10288c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	return test_dev_config_show_uint(test_dev, buf, config->test_case);
10318c2ecf20Sopenharmony_ci}
10328c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(config_test_case);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_cistatic ssize_t test_result_show(struct device *dev,
10358c2ecf20Sopenharmony_ci				struct device_attribute *attr,
10368c2ecf20Sopenharmony_ci				char *buf)
10378c2ecf20Sopenharmony_ci{
10388c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = dev_to_test_dev(dev);
10398c2ecf20Sopenharmony_ci	struct test_config *config = &test_dev->config;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	return test_dev_config_show_int(test_dev, buf, config->test_result);
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(test_result);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci#define TEST_KMOD_DEV_ATTR(name)		&dev_attr_##name.attr
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic struct attribute *test_dev_attrs[] = {
10488c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(trigger_config),
10498c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(config),
10508c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(reset),
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(config_test_driver),
10538c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(config_test_fs),
10548c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(config_num_threads),
10558c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(config_test_case),
10568c2ecf20Sopenharmony_ci	TEST_KMOD_DEV_ATTR(test_result),
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	NULL,
10598c2ecf20Sopenharmony_ci};
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(test_dev);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic int kmod_config_init(struct kmod_test_device *test_dev)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	int ret;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
10688c2ecf20Sopenharmony_ci	ret = __kmod_config_init(test_dev);
10698c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	return ret;
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic struct kmod_test_device *alloc_test_dev_kmod(int idx)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	int ret;
10778c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev;
10788c2ecf20Sopenharmony_ci	struct miscdevice *misc_dev;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	test_dev = vzalloc(sizeof(struct kmod_test_device));
10818c2ecf20Sopenharmony_ci	if (!test_dev)
10828c2ecf20Sopenharmony_ci		goto err_out;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	mutex_init(&test_dev->config_mutex);
10858c2ecf20Sopenharmony_ci	mutex_init(&test_dev->trigger_mutex);
10868c2ecf20Sopenharmony_ci	mutex_init(&test_dev->thread_mutex);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	init_completion(&test_dev->kthreads_done);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	ret = kmod_config_init(test_dev);
10918c2ecf20Sopenharmony_ci	if (ret < 0) {
10928c2ecf20Sopenharmony_ci		pr_err("Cannot alloc kmod_config_init()\n");
10938c2ecf20Sopenharmony_ci		goto err_out_free;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	test_dev->dev_idx = idx;
10978c2ecf20Sopenharmony_ci	misc_dev = &test_dev->misc_dev;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	misc_dev->minor = MISC_DYNAMIC_MINOR;
11008c2ecf20Sopenharmony_ci	misc_dev->name = kasprintf(GFP_KERNEL, "test_kmod%d", idx);
11018c2ecf20Sopenharmony_ci	if (!misc_dev->name) {
11028c2ecf20Sopenharmony_ci		pr_err("Cannot alloc misc_dev->name\n");
11038c2ecf20Sopenharmony_ci		goto err_out_free_config;
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci	misc_dev->groups = test_dev_groups;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	return test_dev;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cierr_out_free_config:
11108c2ecf20Sopenharmony_ci	free_test_dev_info(test_dev);
11118c2ecf20Sopenharmony_ci	kmod_config_free(test_dev);
11128c2ecf20Sopenharmony_cierr_out_free:
11138c2ecf20Sopenharmony_ci	vfree(test_dev);
11148c2ecf20Sopenharmony_ci	test_dev = NULL;
11158c2ecf20Sopenharmony_cierr_out:
11168c2ecf20Sopenharmony_ci	return NULL;
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_cistatic void free_test_dev_kmod(struct kmod_test_device *test_dev)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	if (test_dev) {
11228c2ecf20Sopenharmony_ci		kfree_const(test_dev->misc_dev.name);
11238c2ecf20Sopenharmony_ci		test_dev->misc_dev.name = NULL;
11248c2ecf20Sopenharmony_ci		free_test_dev_info(test_dev);
11258c2ecf20Sopenharmony_ci		kmod_config_free(test_dev);
11268c2ecf20Sopenharmony_ci		vfree(test_dev);
11278c2ecf20Sopenharmony_ci		test_dev = NULL;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic struct kmod_test_device *register_test_dev_kmod(void)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev = NULL;
11348c2ecf20Sopenharmony_ci	int ret;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	mutex_lock(&reg_dev_mutex);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	/* int should suffice for number of devices, test for wrap */
11398c2ecf20Sopenharmony_ci	if (num_test_devs + 1 == INT_MAX) {
11408c2ecf20Sopenharmony_ci		pr_err("reached limit of number of test devices\n");
11418c2ecf20Sopenharmony_ci		goto out;
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	test_dev = alloc_test_dev_kmod(num_test_devs);
11458c2ecf20Sopenharmony_ci	if (!test_dev)
11468c2ecf20Sopenharmony_ci		goto out;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	ret = misc_register(&test_dev->misc_dev);
11498c2ecf20Sopenharmony_ci	if (ret) {
11508c2ecf20Sopenharmony_ci		pr_err("could not register misc device: %d\n", ret);
11518c2ecf20Sopenharmony_ci		free_test_dev_kmod(test_dev);
11528c2ecf20Sopenharmony_ci		test_dev = NULL;
11538c2ecf20Sopenharmony_ci		goto out;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	test_dev->dev = test_dev->misc_dev.this_device;
11578c2ecf20Sopenharmony_ci	list_add_tail(&test_dev->list, &reg_test_devs);
11588c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "interface ready\n");
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	num_test_devs++;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ciout:
11638c2ecf20Sopenharmony_ci	mutex_unlock(&reg_dev_mutex);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	return test_dev;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_cistatic int __init test_kmod_init(void)
11708c2ecf20Sopenharmony_ci{
11718c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev;
11728c2ecf20Sopenharmony_ci	int ret;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	test_dev = register_test_dev_kmod();
11758c2ecf20Sopenharmony_ci	if (!test_dev) {
11768c2ecf20Sopenharmony_ci		pr_err("Cannot add first test kmod device\n");
11778c2ecf20Sopenharmony_ci		return -ENODEV;
11788c2ecf20Sopenharmony_ci	}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	/*
11818c2ecf20Sopenharmony_ci	 * With some work we might be able to gracefully enable
11828c2ecf20Sopenharmony_ci	 * testing with this driver built-in, for now this seems
11838c2ecf20Sopenharmony_ci	 * rather risky. For those willing to try have at it,
11848c2ecf20Sopenharmony_ci	 * and enable the below. Good luck! If that works, try
11858c2ecf20Sopenharmony_ci	 * lowering the init level for more fun.
11868c2ecf20Sopenharmony_ci	 */
11878c2ecf20Sopenharmony_ci	if (force_init_test) {
11888c2ecf20Sopenharmony_ci		ret = trigger_config_run_type(test_dev,
11898c2ecf20Sopenharmony_ci					      TEST_KMOD_DRIVER, "tun");
11908c2ecf20Sopenharmony_ci		if (WARN_ON(ret))
11918c2ecf20Sopenharmony_ci			return ret;
11928c2ecf20Sopenharmony_ci		ret = trigger_config_run_type(test_dev,
11938c2ecf20Sopenharmony_ci					      TEST_KMOD_FS_TYPE, "btrfs");
11948c2ecf20Sopenharmony_ci		if (WARN_ON(ret))
11958c2ecf20Sopenharmony_ci			return ret;
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	return 0;
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_cilate_initcall(test_kmod_init);
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_cistatic
12038c2ecf20Sopenharmony_civoid unregister_test_dev_kmod(struct kmod_test_device *test_dev)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->trigger_mutex);
12068c2ecf20Sopenharmony_ci	mutex_lock(&test_dev->config_mutex);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	test_dev_kmod_stop_tests(test_dev);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	dev_info(test_dev->dev, "removing interface\n");
12118c2ecf20Sopenharmony_ci	misc_deregister(&test_dev->misc_dev);
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->config_mutex);
12148c2ecf20Sopenharmony_ci	mutex_unlock(&test_dev->trigger_mutex);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	free_test_dev_kmod(test_dev);
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic void __exit test_kmod_exit(void)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	struct kmod_test_device *test_dev, *tmp;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	mutex_lock(&reg_dev_mutex);
12248c2ecf20Sopenharmony_ci	list_for_each_entry_safe(test_dev, tmp, &reg_test_devs, list) {
12258c2ecf20Sopenharmony_ci		list_del(&test_dev->list);
12268c2ecf20Sopenharmony_ci		unregister_test_dev_kmod(test_dev);
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci	mutex_unlock(&reg_dev_mutex);
12298c2ecf20Sopenharmony_ci}
12308c2ecf20Sopenharmony_cimodule_exit(test_kmod_exit);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
12338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1234