Merge branch 'mt7530-dsa-subdriver-fix-vlan-egress-and-handling-of-all-link-local...
[sfrench/cifs-2.6.git] / lib / test_sysctl.c
1 // SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
2 /*
3  * proc sysctl test driver
4  *
5  * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
6  */
7
8 /*
9  * This module provides an interface to the proc sysctl interfaces.  This
10  * driver requires CONFIG_PROC_SYSCTL. It will not normally be loaded by the
11  * system unless explicitly requested by name. You can also build this driver
12  * into your kernel.
13  */
14
15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16
17 #include <linux/init.h>
18 #include <linux/list.h>
19 #include <linux/module.h>
20 #include <linux/printk.h>
21 #include <linux/fs.h>
22 #include <linux/miscdevice.h>
23 #include <linux/slab.h>
24 #include <linux/uaccess.h>
25 #include <linux/async.h>
26 #include <linux/delay.h>
27 #include <linux/vmalloc.h>
28
29 static int i_zero;
30 static int i_one_hundred = 100;
31 static int match_int_ok = 1;
32
33
34 static struct {
35         struct ctl_table_header *test_h_setup_node;
36         struct ctl_table_header *test_h_mnt;
37         struct ctl_table_header *test_h_mnterror;
38         struct ctl_table_header *empty_add;
39         struct ctl_table_header *empty;
40 } sysctl_test_headers;
41
42 struct test_sysctl_data {
43         int int_0001;
44         int int_0002;
45         int int_0003[4];
46
47         int boot_int;
48
49         unsigned int uint_0001;
50
51         char string_0001[65];
52
53 #define SYSCTL_TEST_BITMAP_SIZE 65536
54         unsigned long *bitmap_0001;
55 };
56
57 static struct test_sysctl_data test_data = {
58         .int_0001 = 60,
59         .int_0002 = 1,
60
61         .int_0003[0] = 0,
62         .int_0003[1] = 1,
63         .int_0003[2] = 2,
64         .int_0003[3] = 3,
65
66         .boot_int = 0,
67
68         .uint_0001 = 314,
69
70         .string_0001 = "(none)",
71 };
72
73 /* These are all under /proc/sys/debug/test_sysctl/ */
74 static struct ctl_table test_table[] = {
75         {
76                 .procname       = "int_0001",
77                 .data           = &test_data.int_0001,
78                 .maxlen         = sizeof(int),
79                 .mode           = 0644,
80                 .proc_handler   = proc_dointvec_minmax,
81                 .extra1         = &i_zero,
82                 .extra2         = &i_one_hundred,
83         },
84         {
85                 .procname       = "int_0002",
86                 .data           = &test_data.int_0002,
87                 .maxlen         = sizeof(int),
88                 .mode           = 0644,
89                 .proc_handler   = proc_dointvec,
90         },
91         {
92                 .procname       = "int_0003",
93                 .data           = &test_data.int_0003,
94                 .maxlen         = sizeof(test_data.int_0003),
95                 .mode           = 0644,
96                 .proc_handler   = proc_dointvec,
97         },
98         {
99                 .procname       = "match_int",
100                 .data           = &match_int_ok,
101                 .maxlen         = sizeof(match_int_ok),
102                 .mode           = 0444,
103                 .proc_handler   = proc_dointvec,
104         },
105         {
106                 .procname       = "boot_int",
107                 .data           = &test_data.boot_int,
108                 .maxlen         = sizeof(test_data.boot_int),
109                 .mode           = 0644,
110                 .proc_handler   = proc_dointvec,
111                 .extra1         = SYSCTL_ZERO,
112                 .extra2         = SYSCTL_ONE,
113         },
114         {
115                 .procname       = "uint_0001",
116                 .data           = &test_data.uint_0001,
117                 .maxlen         = sizeof(unsigned int),
118                 .mode           = 0644,
119                 .proc_handler   = proc_douintvec,
120         },
121         {
122                 .procname       = "string_0001",
123                 .data           = &test_data.string_0001,
124                 .maxlen         = sizeof(test_data.string_0001),
125                 .mode           = 0644,
126                 .proc_handler   = proc_dostring,
127         },
128         {
129                 .procname       = "bitmap_0001",
130                 .data           = &test_data.bitmap_0001,
131                 .maxlen         = SYSCTL_TEST_BITMAP_SIZE,
132                 .mode           = 0644,
133                 .proc_handler   = proc_do_large_bitmap,
134         },
135 };
136
137 static void test_sysctl_calc_match_int_ok(void)
138 {
139         int i;
140
141         struct {
142                 int defined;
143                 int wanted;
144         } match_int[] = {
145                 {.defined = *(int *)SYSCTL_ZERO,        .wanted = 0},
146                 {.defined = *(int *)SYSCTL_ONE,         .wanted = 1},
147                 {.defined = *(int *)SYSCTL_TWO,         .wanted = 2},
148                 {.defined = *(int *)SYSCTL_THREE,       .wanted = 3},
149                 {.defined = *(int *)SYSCTL_FOUR,        .wanted = 4},
150                 {.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100},
151                 {.defined = *(int *)SYSCTL_TWO_HUNDRED, .wanted = 200},
152                 {.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000},
153                 {.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000},
154                 {.defined = *(int *)SYSCTL_INT_MAX,     .wanted = INT_MAX},
155                 {.defined = *(int *)SYSCTL_MAXOLDUID,   .wanted = 65535},
156                 {.defined = *(int *)SYSCTL_NEG_ONE,     .wanted = -1},
157         };
158
159         for (i = 0; i < ARRAY_SIZE(match_int); i++)
160                 if (match_int[i].defined != match_int[i].wanted)
161                         match_int_ok = 0;
162 }
163
164 static int test_sysctl_setup_node_tests(void)
165 {
166         test_sysctl_calc_match_int_ok();
167         test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL);
168         if (!test_data.bitmap_0001)
169                 return -ENOMEM;
170         sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table);
171         if (!sysctl_test_headers.test_h_setup_node) {
172                 kfree(test_data.bitmap_0001);
173                 return -ENOMEM;
174         }
175
176         return 0;
177 }
178
179 /* Used to test that unregister actually removes the directory */
180 static struct ctl_table test_table_unregister[] = {
181         {
182                 .procname       = "unregister_error",
183                 .data           = &test_data.int_0001,
184                 .maxlen         = sizeof(int),
185                 .mode           = 0644,
186                 .proc_handler   = proc_dointvec_minmax,
187         },
188 };
189
190 static int test_sysctl_run_unregister_nested(void)
191 {
192         struct ctl_table_header *unregister;
193
194         unregister = register_sysctl("debug/test_sysctl/unregister_error",
195                                    test_table_unregister);
196         if (!unregister)
197                 return -ENOMEM;
198
199         unregister_sysctl_table(unregister);
200         return 0;
201 }
202
203 static int test_sysctl_run_register_mount_point(void)
204 {
205         sysctl_test_headers.test_h_mnt
206                 = register_sysctl_mount_point("debug/test_sysctl/mnt");
207         if (!sysctl_test_headers.test_h_mnt)
208                 return -ENOMEM;
209
210         sysctl_test_headers.test_h_mnterror
211                 = register_sysctl("debug/test_sysctl/mnt/mnt_error",
212                                   test_table_unregister);
213         /*
214          * Don't check the result.:
215          * If it fails (expected behavior), return 0.
216          * If successful (missbehavior of register mount point), we want to see
217          * mnt_error when we run the sysctl test script
218          */
219
220         return 0;
221 }
222
223 static struct ctl_table test_table_empty[] = { };
224
225 static int test_sysctl_run_register_empty(void)
226 {
227         /* Tets that an empty dir can be created */
228         sysctl_test_headers.empty_add
229                 = register_sysctl("debug/test_sysctl/empty_add", test_table_empty);
230         if (!sysctl_test_headers.empty_add)
231                 return -ENOMEM;
232
233         /* Test that register on top of an empty dir works */
234         sysctl_test_headers.empty
235                 = register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty);
236         if (!sysctl_test_headers.empty)
237                 return -ENOMEM;
238
239         return 0;
240 }
241
242 static int __init test_sysctl_init(void)
243 {
244         int err;
245
246         err = test_sysctl_setup_node_tests();
247         if (err)
248                 goto out;
249
250         err = test_sysctl_run_unregister_nested();
251         if (err)
252                 goto out;
253
254         err = test_sysctl_run_register_mount_point();
255         if (err)
256                 goto out;
257
258         err = test_sysctl_run_register_empty();
259
260 out:
261         return err;
262 }
263 module_init(test_sysctl_init);
264
265 static void __exit test_sysctl_exit(void)
266 {
267         kfree(test_data.bitmap_0001);
268         if (sysctl_test_headers.test_h_setup_node)
269                 unregister_sysctl_table(sysctl_test_headers.test_h_setup_node);
270         if (sysctl_test_headers.test_h_mnt)
271                 unregister_sysctl_table(sysctl_test_headers.test_h_mnt);
272         if (sysctl_test_headers.test_h_mnterror)
273                 unregister_sysctl_table(sysctl_test_headers.test_h_mnterror);
274         if (sysctl_test_headers.empty)
275                 unregister_sysctl_table(sysctl_test_headers.empty);
276         if (sysctl_test_headers.empty_add)
277                 unregister_sysctl_table(sysctl_test_headers.empty_add);
278 }
279
280 module_exit(test_sysctl_exit);
281
282 MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
283 MODULE_LICENSE("GPL");