xref: /linux/lib/test_sysctl.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
16cad1ecdSLuis Chamberlain // SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
29308f2f9SLuis R. Rodriguez /*
39308f2f9SLuis R. Rodriguez  * proc sysctl test driver
49308f2f9SLuis R. Rodriguez  *
59308f2f9SLuis R. Rodriguez  * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
69308f2f9SLuis R. Rodriguez  */
79308f2f9SLuis R. Rodriguez 
89308f2f9SLuis R. Rodriguez /*
92d046981SRandy Dunlap  * This module provides an interface to the proc sysctl interfaces.  This
109308f2f9SLuis R. Rodriguez  * driver requires CONFIG_PROC_SYSCTL. It will not normally be loaded by the
119308f2f9SLuis R. Rodriguez  * system unless explicitly requested by name. You can also build this driver
129308f2f9SLuis R. Rodriguez  * into your kernel.
139308f2f9SLuis R. Rodriguez  */
149308f2f9SLuis R. Rodriguez 
159308f2f9SLuis R. Rodriguez #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
169308f2f9SLuis R. Rodriguez 
179308f2f9SLuis R. Rodriguez #include <linux/init.h>
189308f2f9SLuis R. Rodriguez #include <linux/list.h>
199308f2f9SLuis R. Rodriguez #include <linux/module.h>
209308f2f9SLuis R. Rodriguez #include <linux/printk.h>
219308f2f9SLuis R. Rodriguez #include <linux/fs.h>
229308f2f9SLuis R. Rodriguez #include <linux/miscdevice.h>
239308f2f9SLuis R. Rodriguez #include <linux/slab.h>
249308f2f9SLuis R. Rodriguez #include <linux/uaccess.h>
259308f2f9SLuis R. Rodriguez #include <linux/async.h>
269308f2f9SLuis R. Rodriguez #include <linux/delay.h>
279308f2f9SLuis R. Rodriguez #include <linux/vmalloc.h>
289308f2f9SLuis R. Rodriguez 
299308f2f9SLuis R. Rodriguez static int i_zero;
309308f2f9SLuis R. Rodriguez static int i_one_hundred = 100;
3157b19468STonghao Zhang static int match_int_ok = 1;
329308f2f9SLuis R. Rodriguez 
33f2e7a626SJoel Granados 
34f2e7a626SJoel Granados static struct {
35f2e7a626SJoel Granados 	struct ctl_table_header *test_h_setup_node;
36f2e7a626SJoel Granados 	struct ctl_table_header *test_h_mnt;
37f2e7a626SJoel Granados 	struct ctl_table_header *test_h_mnterror;
3877774077SJoel Granados 	struct ctl_table_header *empty_add;
3977774077SJoel Granados 	struct ctl_table_header *empty;
40f2e7a626SJoel Granados } sysctl_test_headers;
41f2e7a626SJoel Granados 
429308f2f9SLuis R. Rodriguez struct test_sysctl_data {
439308f2f9SLuis R. Rodriguez 	int int_0001;
44eb965edaSLuis R. Rodriguez 	int int_0002;
457c43a657SLuis R. Rodriguez 	int int_0003[4];
46eb965edaSLuis R. Rodriguez 
474f2f682dSVlastimil Babka 	int boot_int;
484f2f682dSVlastimil Babka 
492920fad3SLuis R. Rodriguez 	unsigned int uint_0001;
502920fad3SLuis R. Rodriguez 
519308f2f9SLuis R. Rodriguez 	char string_0001[65];
522ea622b8SEric Sandeen 
532ea622b8SEric Sandeen #define SYSCTL_TEST_BITMAP_SIZE	65536
542ea622b8SEric Sandeen 	unsigned long *bitmap_0001;
559308f2f9SLuis R. Rodriguez };
569308f2f9SLuis R. Rodriguez 
579308f2f9SLuis R. Rodriguez static struct test_sysctl_data test_data = {
589308f2f9SLuis R. Rodriguez 	.int_0001 = 60,
59eb965edaSLuis R. Rodriguez 	.int_0002 = 1,
60eb965edaSLuis R. Rodriguez 
617c43a657SLuis R. Rodriguez 	.int_0003[0] = 0,
627c43a657SLuis R. Rodriguez 	.int_0003[1] = 1,
637c43a657SLuis R. Rodriguez 	.int_0003[2] = 2,
647c43a657SLuis R. Rodriguez 	.int_0003[3] = 3,
657c43a657SLuis R. Rodriguez 
664f2f682dSVlastimil Babka 	.boot_int = 0,
674f2f682dSVlastimil Babka 
682920fad3SLuis R. Rodriguez 	.uint_0001 = 314,
692920fad3SLuis R. Rodriguez 
709308f2f9SLuis R. Rodriguez 	.string_0001 = "(none)",
719308f2f9SLuis R. Rodriguez };
729308f2f9SLuis R. Rodriguez 
739308f2f9SLuis R. Rodriguez /* These are all under /proc/sys/debug/test_sysctl/ */
749308f2f9SLuis R. Rodriguez static struct ctl_table test_table[] = {
759308f2f9SLuis R. Rodriguez 	{
769308f2f9SLuis R. Rodriguez 		.procname	= "int_0001",
779308f2f9SLuis R. Rodriguez 		.data		= &test_data.int_0001,
789308f2f9SLuis R. Rodriguez 		.maxlen		= sizeof(int),
799308f2f9SLuis R. Rodriguez 		.mode		= 0644,
809308f2f9SLuis R. Rodriguez 		.proc_handler	= proc_dointvec_minmax,
819308f2f9SLuis R. Rodriguez 		.extra1		= &i_zero,
829308f2f9SLuis R. Rodriguez 		.extra2         = &i_one_hundred,
839308f2f9SLuis R. Rodriguez 	},
849308f2f9SLuis R. Rodriguez 	{
85eb965edaSLuis R. Rodriguez 		.procname	= "int_0002",
86eb965edaSLuis R. Rodriguez 		.data		= &test_data.int_0002,
87eb965edaSLuis R. Rodriguez 		.maxlen		= sizeof(int),
88eb965edaSLuis R. Rodriguez 		.mode		= 0644,
89eb965edaSLuis R. Rodriguez 		.proc_handler	= proc_dointvec,
90eb965edaSLuis R. Rodriguez 	},
91eb965edaSLuis R. Rodriguez 	{
927c43a657SLuis R. Rodriguez 		.procname	= "int_0003",
937c43a657SLuis R. Rodriguez 		.data		= &test_data.int_0003,
947c43a657SLuis R. Rodriguez 		.maxlen		= sizeof(test_data.int_0003),
957c43a657SLuis R. Rodriguez 		.mode		= 0644,
967c43a657SLuis R. Rodriguez 		.proc_handler	= proc_dointvec,
977c43a657SLuis R. Rodriguez 	},
987c43a657SLuis R. Rodriguez 	{
9957b19468STonghao Zhang 		.procname	= "match_int",
10057b19468STonghao Zhang 		.data		= &match_int_ok,
10157b19468STonghao Zhang 		.maxlen		= sizeof(match_int_ok),
10257b19468STonghao Zhang 		.mode		= 0444,
10357b19468STonghao Zhang 		.proc_handler	= proc_dointvec,
10457b19468STonghao Zhang 	},
10557b19468STonghao Zhang 	{
1064f2f682dSVlastimil Babka 		.procname	= "boot_int",
1074f2f682dSVlastimil Babka 		.data		= &test_data.boot_int,
1084f2f682dSVlastimil Babka 		.maxlen		= sizeof(test_data.boot_int),
1094f2f682dSVlastimil Babka 		.mode		= 0644,
1104f2f682dSVlastimil Babka 		.proc_handler	= proc_dointvec,
1114f2f682dSVlastimil Babka 		.extra1		= SYSCTL_ZERO,
1124f2f682dSVlastimil Babka 		.extra2         = SYSCTL_ONE,
1134f2f682dSVlastimil Babka 	},
1144f2f682dSVlastimil Babka 	{
1152920fad3SLuis R. Rodriguez 		.procname	= "uint_0001",
1162920fad3SLuis R. Rodriguez 		.data		= &test_data.uint_0001,
1172920fad3SLuis R. Rodriguez 		.maxlen		= sizeof(unsigned int),
1182920fad3SLuis R. Rodriguez 		.mode		= 0644,
1192920fad3SLuis R. Rodriguez 		.proc_handler	= proc_douintvec,
1202920fad3SLuis R. Rodriguez 	},
1212920fad3SLuis R. Rodriguez 	{
1229308f2f9SLuis R. Rodriguez 		.procname	= "string_0001",
1239308f2f9SLuis R. Rodriguez 		.data		= &test_data.string_0001,
1249308f2f9SLuis R. Rodriguez 		.maxlen		= sizeof(test_data.string_0001),
1259308f2f9SLuis R. Rodriguez 		.mode		= 0644,
1269308f2f9SLuis R. Rodriguez 		.proc_handler	= proc_dostring,
1279308f2f9SLuis R. Rodriguez 	},
1282ea622b8SEric Sandeen 	{
1292ea622b8SEric Sandeen 		.procname	= "bitmap_0001",
1302ea622b8SEric Sandeen 		.data		= &test_data.bitmap_0001,
1312ea622b8SEric Sandeen 		.maxlen		= SYSCTL_TEST_BITMAP_SIZE,
1322ea622b8SEric Sandeen 		.mode		= 0644,
1332ea622b8SEric Sandeen 		.proc_handler	= proc_do_large_bitmap,
1342ea622b8SEric Sandeen 	},
1359308f2f9SLuis R. Rodriguez };
1369308f2f9SLuis R. Rodriguez 
test_sysctl_calc_match_int_ok(void)137e009bd5eSJoel Granados static void test_sysctl_calc_match_int_ok(void)
1389308f2f9SLuis R. Rodriguez {
13957b19468STonghao Zhang 	int i;
14057b19468STonghao Zhang 
14157b19468STonghao Zhang 	struct {
14257b19468STonghao Zhang 		int defined;
14357b19468STonghao Zhang 		int wanted;
14457b19468STonghao Zhang 	} match_int[] = {
14557b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ZERO,	.wanted = 0},
14657b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ONE,		.wanted = 1},
14757b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_TWO,		.wanted = 2},
14857b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_THREE,	.wanted = 3},
14957b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_FOUR,	.wanted = 4},
15057b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100},
15157b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_TWO_HUNDRED,	.wanted = 200},
15257b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000},
15357b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000},
15457b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_INT_MAX,	.wanted = INT_MAX},
15557b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_MAXOLDUID,	.wanted = 65535},
15657b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_NEG_ONE,	.wanted = -1},
15757b19468STonghao Zhang 	};
15857b19468STonghao Zhang 
15957b19468STonghao Zhang 	for (i = 0; i < ARRAY_SIZE(match_int); i++)
16057b19468STonghao Zhang 		if (match_int[i].defined != match_int[i].wanted)
16157b19468STonghao Zhang 			match_int_ok = 0;
162e009bd5eSJoel Granados }
16357b19468STonghao Zhang 
test_sysctl_setup_node_tests(void)164e009bd5eSJoel Granados static int test_sysctl_setup_node_tests(void)
165e009bd5eSJoel Granados {
166e009bd5eSJoel Granados 	test_sysctl_calc_match_int_ok();
1672ea622b8SEric Sandeen 	test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL);
1682ea622b8SEric Sandeen 	if (!test_data.bitmap_0001)
1699308f2f9SLuis R. Rodriguez 		return -ENOMEM;
170f2e7a626SJoel Granados 	sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table);
171f2e7a626SJoel Granados 	if (!sysctl_test_headers.test_h_setup_node) {
1722ea622b8SEric Sandeen 		kfree(test_data.bitmap_0001);
1732ea622b8SEric Sandeen 		return -ENOMEM;
1742ea622b8SEric Sandeen 	}
175e009bd5eSJoel Granados 
1769308f2f9SLuis R. Rodriguez 	return 0;
1779308f2f9SLuis R. Rodriguez }
178e009bd5eSJoel Granados 
17935576438SJoel Granados /* Used to test that unregister actually removes the directory */
18035576438SJoel Granados static struct ctl_table test_table_unregister[] = {
18135576438SJoel Granados 	{
18235576438SJoel Granados 		.procname	= "unregister_error",
18335576438SJoel Granados 		.data		= &test_data.int_0001,
18435576438SJoel Granados 		.maxlen		= sizeof(int),
18535576438SJoel Granados 		.mode		= 0644,
18635576438SJoel Granados 		.proc_handler	= proc_dointvec_minmax,
18735576438SJoel Granados 	},
18835576438SJoel Granados };
18935576438SJoel Granados 
test_sysctl_run_unregister_nested(void)19035576438SJoel Granados static int test_sysctl_run_unregister_nested(void)
19135576438SJoel Granados {
19235576438SJoel Granados 	struct ctl_table_header *unregister;
19335576438SJoel Granados 
19435576438SJoel Granados 	unregister = register_sysctl("debug/test_sysctl/unregister_error",
19535576438SJoel Granados 				   test_table_unregister);
19635576438SJoel Granados 	if (!unregister)
19735576438SJoel Granados 		return -ENOMEM;
19835576438SJoel Granados 
19935576438SJoel Granados 	unregister_sysctl_table(unregister);
20035576438SJoel Granados 	return 0;
20135576438SJoel Granados }
20235576438SJoel Granados 
test_sysctl_run_register_mount_point(void)203f2e7a626SJoel Granados static int test_sysctl_run_register_mount_point(void)
204f2e7a626SJoel Granados {
205f2e7a626SJoel Granados 	sysctl_test_headers.test_h_mnt
206f2e7a626SJoel Granados 		= register_sysctl_mount_point("debug/test_sysctl/mnt");
207f2e7a626SJoel Granados 	if (!sysctl_test_headers.test_h_mnt)
208f2e7a626SJoel Granados 		return -ENOMEM;
209f2e7a626SJoel Granados 
210f2e7a626SJoel Granados 	sysctl_test_headers.test_h_mnterror
211f2e7a626SJoel Granados 		= register_sysctl("debug/test_sysctl/mnt/mnt_error",
212f2e7a626SJoel Granados 				  test_table_unregister);
213f2e7a626SJoel Granados 	/*
214f2e7a626SJoel Granados 	 * Don't check the result.:
215f2e7a626SJoel Granados 	 * If it fails (expected behavior), return 0.
216f2e7a626SJoel Granados 	 * If successful (missbehavior of register mount point), we want to see
217f2e7a626SJoel Granados 	 * mnt_error when we run the sysctl test script
218f2e7a626SJoel Granados 	 */
219f2e7a626SJoel Granados 
220f2e7a626SJoel Granados 	return 0;
221f2e7a626SJoel Granados }
222f2e7a626SJoel Granados 
22377774077SJoel Granados static struct ctl_table test_table_empty[] = { };
22477774077SJoel Granados 
test_sysctl_run_register_empty(void)22577774077SJoel Granados static int test_sysctl_run_register_empty(void)
22677774077SJoel Granados {
22777774077SJoel Granados 	/* Tets that an empty dir can be created */
22877774077SJoel Granados 	sysctl_test_headers.empty_add
22977774077SJoel Granados 		= register_sysctl("debug/test_sysctl/empty_add", test_table_empty);
23077774077SJoel Granados 	if (!sysctl_test_headers.empty_add)
23177774077SJoel Granados 		return -ENOMEM;
23277774077SJoel Granados 
23377774077SJoel Granados 	/* Test that register on top of an empty dir works */
23477774077SJoel Granados 	sysctl_test_headers.empty
23577774077SJoel Granados 		= register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty);
23677774077SJoel Granados 	if (!sysctl_test_headers.empty)
23777774077SJoel Granados 		return -ENOMEM;
23877774077SJoel Granados 
23977774077SJoel Granados 	return 0;
24077774077SJoel Granados }
24177774077SJoel Granados 
test_sysctl_init(void)242e009bd5eSJoel Granados static int __init test_sysctl_init(void)
243e009bd5eSJoel Granados {
244e009bd5eSJoel Granados 	int err;
245e009bd5eSJoel Granados 
246e009bd5eSJoel Granados 	err = test_sysctl_setup_node_tests();
24735576438SJoel Granados 	if (err)
24835576438SJoel Granados 		goto out;
249e009bd5eSJoel Granados 
25035576438SJoel Granados 	err = test_sysctl_run_unregister_nested();
251f2e7a626SJoel Granados 	if (err)
252f2e7a626SJoel Granados 		goto out;
253f2e7a626SJoel Granados 
254f2e7a626SJoel Granados 	err = test_sysctl_run_register_mount_point();
25577774077SJoel Granados 	if (err)
25677774077SJoel Granados 		goto out;
25777774077SJoel Granados 
25877774077SJoel Granados 	err = test_sysctl_run_register_empty();
25935576438SJoel Granados 
26035576438SJoel Granados out:
261e009bd5eSJoel Granados 	return err;
262e009bd5eSJoel Granados }
2632f56f845SMasami Hiramatsu module_init(test_sysctl_init);
2649308f2f9SLuis R. Rodriguez 
test_sysctl_exit(void)2659308f2f9SLuis R. Rodriguez static void __exit test_sysctl_exit(void)
2669308f2f9SLuis R. Rodriguez {
2672ea622b8SEric Sandeen 	kfree(test_data.bitmap_0001);
268f2e7a626SJoel Granados 	if (sysctl_test_headers.test_h_setup_node)
269f2e7a626SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.test_h_setup_node);
270f2e7a626SJoel Granados 	if (sysctl_test_headers.test_h_mnt)
271f2e7a626SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.test_h_mnt);
272f2e7a626SJoel Granados 	if (sysctl_test_headers.test_h_mnterror)
273f2e7a626SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.test_h_mnterror);
27477774077SJoel Granados 	if (sysctl_test_headers.empty)
27577774077SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.empty);
27677774077SJoel Granados 	if (sysctl_test_headers.empty_add)
27777774077SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.empty_add);
2789308f2f9SLuis R. Rodriguez }
2799308f2f9SLuis R. Rodriguez 
2809308f2f9SLuis R. Rodriguez module_exit(test_sysctl_exit);
2819308f2f9SLuis R. Rodriguez 
2829308f2f9SLuis R. Rodriguez MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
283*30347491SJeff Johnson MODULE_DESCRIPTION("proc sysctl test driver");
2849308f2f9SLuis R. Rodriguez MODULE_LICENSE("GPL");
285