162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * proc sysctl test driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * This module provides an interface to the proc sysctl interfaces. This 1062306a36Sopenharmony_ci * driver requires CONFIG_PROC_SYSCTL. It will not normally be loaded by the 1162306a36Sopenharmony_ci * system unless explicitly requested by name. You can also build this driver 1262306a36Sopenharmony_ci * into your kernel. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/printk.h> 2162306a36Sopenharmony_ci#include <linux/fs.h> 2262306a36Sopenharmony_ci#include <linux/miscdevice.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/async.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/vmalloc.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int i_zero; 3062306a36Sopenharmony_cistatic int i_one_hundred = 100; 3162306a36Sopenharmony_cistatic int match_int_ok = 1; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic struct { 3562306a36Sopenharmony_ci struct ctl_table_header *test_h_setup_node; 3662306a36Sopenharmony_ci struct ctl_table_header *test_h_mnt; 3762306a36Sopenharmony_ci struct ctl_table_header *test_h_mnterror; 3862306a36Sopenharmony_ci} sysctl_test_headers; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct test_sysctl_data { 4162306a36Sopenharmony_ci int int_0001; 4262306a36Sopenharmony_ci int int_0002; 4362306a36Sopenharmony_ci int int_0003[4]; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci int boot_int; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci unsigned int uint_0001; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci char string_0001[65]; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define SYSCTL_TEST_BITMAP_SIZE 65536 5262306a36Sopenharmony_ci unsigned long *bitmap_0001; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct test_sysctl_data test_data = { 5662306a36Sopenharmony_ci .int_0001 = 60, 5762306a36Sopenharmony_ci .int_0002 = 1, 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci .int_0003[0] = 0, 6062306a36Sopenharmony_ci .int_0003[1] = 1, 6162306a36Sopenharmony_ci .int_0003[2] = 2, 6262306a36Sopenharmony_ci .int_0003[3] = 3, 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci .boot_int = 0, 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci .uint_0001 = 314, 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci .string_0001 = "(none)", 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* These are all under /proc/sys/debug/test_sysctl/ */ 7262306a36Sopenharmony_cistatic struct ctl_table test_table[] = { 7362306a36Sopenharmony_ci { 7462306a36Sopenharmony_ci .procname = "int_0001", 7562306a36Sopenharmony_ci .data = &test_data.int_0001, 7662306a36Sopenharmony_ci .maxlen = sizeof(int), 7762306a36Sopenharmony_ci .mode = 0644, 7862306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 7962306a36Sopenharmony_ci .extra1 = &i_zero, 8062306a36Sopenharmony_ci .extra2 = &i_one_hundred, 8162306a36Sopenharmony_ci }, 8262306a36Sopenharmony_ci { 8362306a36Sopenharmony_ci .procname = "int_0002", 8462306a36Sopenharmony_ci .data = &test_data.int_0002, 8562306a36Sopenharmony_ci .maxlen = sizeof(int), 8662306a36Sopenharmony_ci .mode = 0644, 8762306a36Sopenharmony_ci .proc_handler = proc_dointvec, 8862306a36Sopenharmony_ci }, 8962306a36Sopenharmony_ci { 9062306a36Sopenharmony_ci .procname = "int_0003", 9162306a36Sopenharmony_ci .data = &test_data.int_0003, 9262306a36Sopenharmony_ci .maxlen = sizeof(test_data.int_0003), 9362306a36Sopenharmony_ci .mode = 0644, 9462306a36Sopenharmony_ci .proc_handler = proc_dointvec, 9562306a36Sopenharmony_ci }, 9662306a36Sopenharmony_ci { 9762306a36Sopenharmony_ci .procname = "match_int", 9862306a36Sopenharmony_ci .data = &match_int_ok, 9962306a36Sopenharmony_ci .maxlen = sizeof(match_int_ok), 10062306a36Sopenharmony_ci .mode = 0444, 10162306a36Sopenharmony_ci .proc_handler = proc_dointvec, 10262306a36Sopenharmony_ci }, 10362306a36Sopenharmony_ci { 10462306a36Sopenharmony_ci .procname = "boot_int", 10562306a36Sopenharmony_ci .data = &test_data.boot_int, 10662306a36Sopenharmony_ci .maxlen = sizeof(test_data.boot_int), 10762306a36Sopenharmony_ci .mode = 0644, 10862306a36Sopenharmony_ci .proc_handler = proc_dointvec, 10962306a36Sopenharmony_ci .extra1 = SYSCTL_ZERO, 11062306a36Sopenharmony_ci .extra2 = SYSCTL_ONE, 11162306a36Sopenharmony_ci }, 11262306a36Sopenharmony_ci { 11362306a36Sopenharmony_ci .procname = "uint_0001", 11462306a36Sopenharmony_ci .data = &test_data.uint_0001, 11562306a36Sopenharmony_ci .maxlen = sizeof(unsigned int), 11662306a36Sopenharmony_ci .mode = 0644, 11762306a36Sopenharmony_ci .proc_handler = proc_douintvec, 11862306a36Sopenharmony_ci }, 11962306a36Sopenharmony_ci { 12062306a36Sopenharmony_ci .procname = "string_0001", 12162306a36Sopenharmony_ci .data = &test_data.string_0001, 12262306a36Sopenharmony_ci .maxlen = sizeof(test_data.string_0001), 12362306a36Sopenharmony_ci .mode = 0644, 12462306a36Sopenharmony_ci .proc_handler = proc_dostring, 12562306a36Sopenharmony_ci }, 12662306a36Sopenharmony_ci { 12762306a36Sopenharmony_ci .procname = "bitmap_0001", 12862306a36Sopenharmony_ci .data = &test_data.bitmap_0001, 12962306a36Sopenharmony_ci .maxlen = SYSCTL_TEST_BITMAP_SIZE, 13062306a36Sopenharmony_ci .mode = 0644, 13162306a36Sopenharmony_ci .proc_handler = proc_do_large_bitmap, 13262306a36Sopenharmony_ci }, 13362306a36Sopenharmony_ci { } 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void test_sysctl_calc_match_int_ok(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci struct { 14162306a36Sopenharmony_ci int defined; 14262306a36Sopenharmony_ci int wanted; 14362306a36Sopenharmony_ci } match_int[] = { 14462306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_ZERO, .wanted = 0}, 14562306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_ONE, .wanted = 1}, 14662306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_TWO, .wanted = 2}, 14762306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_THREE, .wanted = 3}, 14862306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_FOUR, .wanted = 4}, 14962306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100}, 15062306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_TWO_HUNDRED, .wanted = 200}, 15162306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000}, 15262306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000}, 15362306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_INT_MAX, .wanted = INT_MAX}, 15462306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_MAXOLDUID, .wanted = 65535}, 15562306a36Sopenharmony_ci {.defined = *(int *)SYSCTL_NEG_ONE, .wanted = -1}, 15662306a36Sopenharmony_ci }; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(match_int); i++) 15962306a36Sopenharmony_ci if (match_int[i].defined != match_int[i].wanted) 16062306a36Sopenharmony_ci match_int_ok = 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int test_sysctl_setup_node_tests(void) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci test_sysctl_calc_match_int_ok(); 16662306a36Sopenharmony_ci test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); 16762306a36Sopenharmony_ci if (!test_data.bitmap_0001) 16862306a36Sopenharmony_ci return -ENOMEM; 16962306a36Sopenharmony_ci sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table); 17062306a36Sopenharmony_ci if (!sysctl_test_headers.test_h_setup_node) { 17162306a36Sopenharmony_ci kfree(test_data.bitmap_0001); 17262306a36Sopenharmony_ci return -ENOMEM; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* Used to test that unregister actually removes the directory */ 17962306a36Sopenharmony_cistatic struct ctl_table test_table_unregister[] = { 18062306a36Sopenharmony_ci { 18162306a36Sopenharmony_ci .procname = "unregister_error", 18262306a36Sopenharmony_ci .data = &test_data.int_0001, 18362306a36Sopenharmony_ci .maxlen = sizeof(int), 18462306a36Sopenharmony_ci .mode = 0644, 18562306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci {} 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int test_sysctl_run_unregister_nested(void) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ctl_table_header *unregister; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci unregister = register_sysctl("debug/test_sysctl/unregister_error", 19562306a36Sopenharmony_ci test_table_unregister); 19662306a36Sopenharmony_ci if (!unregister) 19762306a36Sopenharmony_ci return -ENOMEM; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci unregister_sysctl_table(unregister); 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int test_sysctl_run_register_mount_point(void) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci sysctl_test_headers.test_h_mnt 20662306a36Sopenharmony_ci = register_sysctl_mount_point("debug/test_sysctl/mnt"); 20762306a36Sopenharmony_ci if (!sysctl_test_headers.test_h_mnt) 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci sysctl_test_headers.test_h_mnterror 21162306a36Sopenharmony_ci = register_sysctl("debug/test_sysctl/mnt/mnt_error", 21262306a36Sopenharmony_ci test_table_unregister); 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Don't check the result.: 21562306a36Sopenharmony_ci * If it fails (expected behavior), return 0. 21662306a36Sopenharmony_ci * If successful (missbehavior of register mount point), we want to see 21762306a36Sopenharmony_ci * mnt_error when we run the sysctl test script 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int __init test_sysctl_init(void) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci int err; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci err = test_sysctl_setup_node_tests(); 22862306a36Sopenharmony_ci if (err) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci err = test_sysctl_run_unregister_nested(); 23262306a36Sopenharmony_ci if (err) 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci err = test_sysctl_run_register_mount_point(); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ciout: 23862306a36Sopenharmony_ci return err; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_cimodule_init(test_sysctl_init); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic void __exit test_sysctl_exit(void) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci kfree(test_data.bitmap_0001); 24562306a36Sopenharmony_ci if (sysctl_test_headers.test_h_setup_node) 24662306a36Sopenharmony_ci unregister_sysctl_table(sysctl_test_headers.test_h_setup_node); 24762306a36Sopenharmony_ci if (sysctl_test_headers.test_h_mnt) 24862306a36Sopenharmony_ci unregister_sysctl_table(sysctl_test_headers.test_h_mnt); 24962306a36Sopenharmony_ci if (sysctl_test_headers.test_h_mnterror) 25062306a36Sopenharmony_ci unregister_sysctl_table(sysctl_test_headers.test_h_mnterror); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cimodule_exit(test_sysctl_exit); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciMODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>"); 25662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 257