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