* [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init
2026-01-13 17:40 [RFC PATCH 0/4] sysctl: refactor ctl_table creation and change extra{1,2} type wen.yang
@ 2026-01-13 17:40 ` wen.yang
2026-01-21 14:30 ` Joel Granados
2026-01-22 9:56 ` Joel Granados
2026-01-13 17:40 ` [RFC PATCH 2/4] sysctl: add helper functions to extract table->extra1/extra2 wen.yang
` (2 subsequent siblings)
3 siblings, 2 replies; 8+ messages in thread
From: wen.yang @ 2026-01-13 17:40 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
Introduce SYSCTL_ENTRY() and SYSCTL_RANGE_ENTRY() (built on __SYSCTL_ENTRY())
to consolidate common struct ctl_table initializers in one place.
This is a preparatory refactor in support of future tree-wide sysctl
cleanups by making the how to build a ctl_table entry logic uniform.
Also add SYSCTL_NULL for explicitly passing a NULL .data pointer through
the new macros, and convert a few in-tree users (kernel/sysctl.c) to
validate the new helpers.
No functional change intended.
Selftest were also made:
[19:31:40] Starting KUnit Kernel (1/1)...
[19:31:40] ============================================================
Running tests with:
$ .kunit/linux kunit.filter_glob=sysctl_test kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[19:31:40] ================ sysctl_test (10 subtests) =================
[19:31:40] [PASSED] sysctl_test_api_dointvec_null_tbl_data
[19:31:40] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
[19:31:40] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
[19:31:40] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
[19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_positive
[19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_negative
[19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_positive
[19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_negative
[19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
[19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
[19:31:40] =================== [PASSED] sysctl_test ===================
[19:31:40] ============================================================
[19:31:40] Testing complete. Ran 10 tests: passed: 10
[19:31:40] Elapsed time: 26.755s total, 0.002s configuring, 26.632s building, 0.102s running
Suggested-by: Joel Granados <joel.granados@kernel.org>
Link: https://lore.kernel.org/all/7mevcgca7xsd5gckrap22byjinhrgqnelu4y7hzo2r5t2cq7w4@nyaa42khwqez/#t
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/sysctl.h | 18 ++++++
kernel/sysctl-test.c | 131 ++++++++++-------------------------------
kernel/sysctl.c | 43 ++------------
3 files changed, 56 insertions(+), 136 deletions(-)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 2886fbceb5d6..683422bc3e7f 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -53,6 +53,7 @@ struct ctl_dir;
#define SYSCTL_MAXOLDUID ((void *)&sysctl_vals[10])
#define SYSCTL_NEG_ONE ((void *)&sysctl_vals[11])
+#define SYSCTL_NULL ((void *)NULL)
extern const int sysctl_vals[];
#define SYSCTL_LONG_ZERO ((void *)&sysctl_long_vals[0])
@@ -175,6 +176,23 @@ struct ctl_table {
void *extra2;
} __randomize_layout;
+#define __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, HANDLER, SMIN, SMAX)\
+ { \
+ .procname = NAME, \
+ .data = DATA, \
+ .maxlen = sizeof(TYPE), \
+ .mode = MODE, \
+ .proc_handler = HANDLER, \
+ .extra1 = SMIN, \
+ .extra2 = SMAX, \
+ }
+
+#define SYSCTL_ENTRY(NAME, DATA, TYPE, MODE) \
+ __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec, NULL, NULL)
+
+#define SYSCTL_RANGE_ENTRY(NAME, DATA, TYPE, MODE, SMIN, SMAX) \
+ __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec_minmax, SMIN, SMAX)
+
struct ctl_node {
struct rb_node node;
struct ctl_table_header *header;
diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 92f94ea28957..7fb0e7f1e62f 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -15,20 +15,14 @@
*/
static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
{
- struct ctl_table null_data_table = {
- .procname = "foo",
- /*
- * Here we are testing that proc_dointvec behaves correctly when
- * we give it a NULL .data field. Normally this would point to a
- * piece of memory where the value would be stored.
- */
- .data = NULL,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ /*
+ * Here we are testing that proc_dointvec behaves correctly when
+ * we give it a NULL .data field. Normally this would point to a
+ * piece of memory where the value would be stored.
+ */
+ struct ctl_table null_data_table = SYSCTL_RANGE_ENTRY("foo", \
+ SYSCTL_NULL, int, 0644, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
+
/*
* proc_dointvec expects a buffer in user space, so we allocate one. We
* also need to cast it to __user so sparse doesn't get mad.
@@ -66,19 +60,14 @@ static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
{
int data = 0;
- struct ctl_table data_maxlen_unset_table = {
- .procname = "foo",
- .data = &data,
- /*
- * So .data is no longer NULL, but we tell proc_dointvec its
- * length is 0, so it still shouldn't try to use it.
- */
- .maxlen = 0,
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+
+ /*
+ * So .data is no longer NULL, but we tell proc_dointvec its
+ * length is 0, so it still shouldn't try to use it.
+ */
+ struct ctl_table data_maxlen_unset_table = SYSCTL_RANGE_ENTRY("foo", \
+ &data, int, 0644, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
+ data_maxlen_unset_table.maxlen = 0;
void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
GFP_USER);
size_t len;
@@ -113,15 +102,8 @@ static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
GFP_USER);
/*
@@ -147,15 +129,8 @@ static void sysctl_test_api_dointvec_table_read_but_position_set(
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
GFP_USER);
/*
@@ -182,15 +157,8 @@ static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t len = 4;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, len, GFP_USER);
@@ -213,15 +181,8 @@ static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t len = 5;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, len, GFP_USER);
@@ -242,15 +203,8 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
char input[] = "9";
size_t len = sizeof(input) - 1;
loff_t pos = 0;
@@ -272,15 +226,8 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
{
int data = 0;
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
char input[] = "-9";
size_t len = sizeof(input) - 1;
loff_t pos = 0;
@@ -304,15 +251,8 @@ static void sysctl_test_api_dointvec_write_single_less_int_min(
struct kunit *test)
{
int data = 0;
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t max_len = 32, len = max_len;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
@@ -342,15 +282,8 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
struct kunit *test)
{
int data = 0;
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t max_len = 32, len = max_len;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9d3a666ffde1..2e769550b268 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1373,47 +1373,16 @@ int proc_do_static_key(const struct ctl_table *table, int dir,
static const struct ctl_table sysctl_subsys_table[] = {
#ifdef CONFIG_PROC_SYSCTL
- {
- .procname = "sysctl_writes_strict",
- .data = &sysctl_writes_strict,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_NEG_ONE,
- .extra2 = SYSCTL_ONE,
- },
+ SYSCTL_RANGE_ENTRY("sysctl_writes_strict", &sysctl_writes_strict, int, \
+ 0644, SYSCTL_NEG_ONE, SYSCTL_ONE),
#endif
- {
- .procname = "ngroups_max",
- .data = (void *)&ngroups_max,
- .maxlen = sizeof (int),
- .mode = 0444,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "cap_last_cap",
- .data = (void *)&cap_last_cap,
- .maxlen = sizeof(int),
- .mode = 0444,
- .proc_handler = proc_dointvec,
- },
+ SYSCTL_ENTRY("ngroups_max", (void *)&ngroups_max, int, 0444),
+ SYSCTL_ENTRY("cap_last_cap", (void *)&cap_last_cap, int, 0444),
#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW
- {
- .procname = "unaligned-trap",
- .data = &unaligned_enabled,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
+ SYSCTL_ENTRY("unaligned-trap", &unaligned_enabled, int, 0644),
#endif
#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN
- {
- .procname = "ignore-unaligned-usertrap",
- .data = &no_unaligned_warning,
- .maxlen = sizeof (int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
+ SYSCTL_ENTRY("ignore-unaligned-usertrap", &no_unaligned_warning, int, 0644),
#endif
};
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init
2026-01-13 17:40 ` [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init wen.yang
@ 2026-01-21 14:30 ` Joel Granados
2026-01-22 9:56 ` Joel Granados
1 sibling, 0 replies; 8+ messages in thread
From: Joel Granados @ 2026-01-21 14:30 UTC (permalink / raw)
To: wen.yang; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 14181 bytes --]
Hey Wen
Going to comment only on this patch. As I want to concentrate in getting
the macro just right. After that we can continue with further changes.
With this in mind, you might just want to drop the other commits for
now... up to you.
On Wed, Jan 14, 2026 at 01:40:30AM +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> Introduce SYSCTL_ENTRY() and SYSCTL_RANGE_ENTRY() (built on __SYSCTL_ENTRY())
> to consolidate common struct ctl_table initializers in one place.
> This is a preparatory refactor in support of future tree-wide sysctl
> cleanups by making the how to build a ctl_table entry logic uniform.
>
> Also add SYSCTL_NULL for explicitly passing a NULL .data pointer through
> the new macros, and convert a few in-tree users (kernel/sysctl.c) to
> validate the new helpers.
>
> No functional change intended.
>
> Selftest were also made:
>
> [19:31:40] Starting KUnit Kernel (1/1)...
> [19:31:40] ============================================================
> Running tests with:
> $ .kunit/linux kunit.filter_glob=sysctl_test kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
> [19:31:40] ================ sysctl_test (10 subtests) =================
> [19:31:40] [PASSED] sysctl_test_api_dointvec_null_tbl_data
> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
> [19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_positive
> [19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_negative
> [19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_positive
> [19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_negative
> [19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
> [19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
> [19:31:40] =================== [PASSED] sysctl_test ===================
> [19:31:40] ============================================================
> [19:31:40] Testing complete. Ran 10 tests: passed: 10
> [19:31:40] Elapsed time: 26.755s total, 0.002s configuring, 26.632s building, 0.102s running
Thx for getting the ball rolling by the way. And it is a very good idea
to include the tests early on. Can you pls create two commits from this
one: one for the introduction of the macro and the other for the changes
in the kunit sysctl tests.
>
> Suggested-by: Joel Granados <joel.granados@kernel.org>
> Link: https://lore.kernel.org/all/7mevcgca7xsd5gckrap22byjinhrgqnelu4y7hzo2r5t2cq7w4@nyaa42khwqez/#t
> Signed-off-by: Wen Yang <wen.yang@linux.dev>
> ---
> include/linux/sysctl.h | 18 ++++++
> kernel/sysctl-test.c | 131 ++++++++++-------------------------------
> kernel/sysctl.c | 43 ++------------
> 3 files changed, 56 insertions(+), 136 deletions(-)
>
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 2886fbceb5d6..683422bc3e7f 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -53,6 +53,7 @@ struct ctl_dir;
> #define SYSCTL_MAXOLDUID ((void *)&sysctl_vals[10])
> #define SYSCTL_NEG_ONE ((void *)&sysctl_vals[11])
>
> +#define SYSCTL_NULL ((void *)NULL)
> extern const int sysctl_vals[];
>
> #define SYSCTL_LONG_ZERO ((void *)&sysctl_long_vals[0])
> @@ -175,6 +176,23 @@ struct ctl_table {
> void *extra2;
> } __randomize_layout;
>
> +#define __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, HANDLER, SMIN, SMAX)\
> + { \
> + .procname = NAME, \
> + .data = DATA, \
> + .maxlen = sizeof(TYPE), \
> + .mode = MODE, \
> + .proc_handler = HANDLER, \
> + .extra1 = SMIN, \
> + .extra2 = SMAX, \
> + }
> +
> +#define SYSCTL_ENTRY(NAME, DATA, TYPE, MODE) \
> + __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec, NULL, NULL)
> +
> +#define SYSCTL_RANGE_ENTRY(NAME, DATA, TYPE, MODE, SMIN, SMAX) \
> + __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec_minmax, SMIN, SMAX)
> +
I included type in my initial prototype, but I'm wondering if we can get
away with removing TYPE and figuring out what proc_doXXXXXvec function
we call based on DATA (maybe __same_type? or __builtin_types_compatible_p?
or something else?) This would make the MACRO even easier to use as it
would just be "SYSCTL_ENTRY(NAME, DATA, MODE)"
The user can always pass a DATA that is a different type from the TYPE
and if they are compatible, the compiler will probably not scream. We
have probably removed most of these, but I would like to have a compiler
warning if it sees that the type does not agree with the handler
type.
> struct ctl_node {
> struct rb_node node;
> struct ctl_table_header *header;
> diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
> index 92f94ea28957..7fb0e7f1e62f 100644
> --- a/kernel/sysctl-test.c
> +++ b/kernel/sysctl-test.c
> @@ -15,20 +15,14 @@
> */
> static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
> {
> - struct ctl_table null_data_table = {
> - .procname = "foo",
> - /*
> - * Here we are testing that proc_dointvec behaves correctly when
> - * we give it a NULL .data field. Normally this would point to a
> - * piece of memory where the value would be stored.
> - */
> - .data = NULL,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + /*
> + * Here we are testing that proc_dointvec behaves correctly when
> + * we give it a NULL .data field. Normally this would point to a
> + * piece of memory where the value would be stored.
> + */
> + struct ctl_table null_data_table = SYSCTL_RANGE_ENTRY("foo", \
> + SYSCTL_NULL, int, 0644, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> +
> /*
> * proc_dointvec expects a buffer in user space, so we allocate one. We
> * also need to cast it to __user so sparse doesn't get mad.
> @@ -66,19 +60,14 @@ static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
> static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
> {
> int data = 0;
> - struct ctl_table data_maxlen_unset_table = {
> - .procname = "foo",
> - .data = &data,
> - /*
> - * So .data is no longer NULL, but we tell proc_dointvec its
> - * length is 0, so it still shouldn't try to use it.
> - */
> - .maxlen = 0,
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> +
> + /*
> + * So .data is no longer NULL, but we tell proc_dointvec its
> + * length is 0, so it still shouldn't try to use it.
> + */
> + struct ctl_table data_maxlen_unset_table = SYSCTL_RANGE_ENTRY("foo", \
> + &data, int, 0644, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> + data_maxlen_unset_table.maxlen = 0;
> void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
> GFP_USER);
> size_t len;
> @@ -113,15 +102,8 @@ static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
> {
> int data = 0;
> /* Good table. */
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
> GFP_USER);
> /*
> @@ -147,15 +129,8 @@ static void sysctl_test_api_dointvec_table_read_but_position_set(
> {
> int data = 0;
> /* Good table. */
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
> GFP_USER);
> /*
> @@ -182,15 +157,8 @@ static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
> {
> int data = 0;
> /* Good table. */
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> size_t len = 4;
> loff_t pos = 0;
> char *buffer = kunit_kzalloc(test, len, GFP_USER);
> @@ -213,15 +181,8 @@ static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
> {
> int data = 0;
> /* Good table. */
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> size_t len = 5;
> loff_t pos = 0;
> char *buffer = kunit_kzalloc(test, len, GFP_USER);
> @@ -242,15 +203,8 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
> {
> int data = 0;
> /* Good table. */
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> char input[] = "9";
> size_t len = sizeof(input) - 1;
> loff_t pos = 0;
> @@ -272,15 +226,8 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
> static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
> {
> int data = 0;
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644, \
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> char input[] = "-9";
> size_t len = sizeof(input) - 1;
> loff_t pos = 0;
> @@ -304,15 +251,8 @@ static void sysctl_test_api_dointvec_write_single_less_int_min(
> struct kunit *test)
> {
> int data = 0;
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> size_t max_len = 32, len = max_len;
> loff_t pos = 0;
> char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
> @@ -342,15 +282,8 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
> struct kunit *test)
> {
> int data = 0;
> - struct ctl_table table = {
> - .procname = "foo",
> - .data = &data,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - .extra1 = SYSCTL_ZERO,
> - .extra2 = SYSCTL_ONE_HUNDRED,
> - };
> + struct ctl_table table = SYSCTL_RANGE_ENTRY("foo", &data, int, 0644,\
> + SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
> size_t max_len = 32, len = max_len;
> loff_t pos = 0;
> char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index 9d3a666ffde1..2e769550b268 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -1373,47 +1373,16 @@ int proc_do_static_key(const struct ctl_table *table, int dir,
>
> static const struct ctl_table sysctl_subsys_table[] = {
> #ifdef CONFIG_PROC_SYSCTL
> - {
> - .procname = "sysctl_writes_strict",
> - .data = &sysctl_writes_strict,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec_minmax,
> - .extra1 = SYSCTL_NEG_ONE,
> - .extra2 = SYSCTL_ONE,
> - },
> + SYSCTL_RANGE_ENTRY("sysctl_writes_strict", &sysctl_writes_strict, int, \
> + 0644, SYSCTL_NEG_ONE, SYSCTL_ONE),
> #endif
> - {
> - .procname = "ngroups_max",
> - .data = (void *)&ngroups_max,
> - .maxlen = sizeof (int),
> - .mode = 0444,
> - .proc_handler = proc_dointvec,
> - },
> - {
> - .procname = "cap_last_cap",
> - .data = (void *)&cap_last_cap,
> - .maxlen = sizeof(int),
> - .mode = 0444,
> - .proc_handler = proc_dointvec,
> - },
> + SYSCTL_ENTRY("ngroups_max", (void *)&ngroups_max, int, 0444),
> + SYSCTL_ENTRY("cap_last_cap", (void *)&cap_last_cap, int, 0444),
> #ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW
> - {
> - .procname = "unaligned-trap",
> - .data = &unaligned_enabled,
> - .maxlen = sizeof(int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - },
> + SYSCTL_ENTRY("unaligned-trap", &unaligned_enabled, int, 0644),
> #endif
> #ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN
> - {
> - .procname = "ignore-unaligned-usertrap",
> - .data = &no_unaligned_warning,
> - .maxlen = sizeof (int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - },
> + SYSCTL_ENTRY("ignore-unaligned-usertrap", &no_unaligned_warning, int, 0644),
> #endif
Again, very nice that you included the testing. Does it cover all the
ways of using the API? What I mean is, do we test for 1. When the data
is NULL ptr, 2. for when there is only one (instead of both) "extra" pointer
thx
Best
--
Joel Granados
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init
2026-01-13 17:40 ` [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init wen.yang
2026-01-21 14:30 ` Joel Granados
@ 2026-01-22 9:56 ` Joel Granados
2026-01-25 20:08 ` Wen Yang
1 sibling, 1 reply; 8+ messages in thread
From: Joel Granados @ 2026-01-22 9:56 UTC (permalink / raw)
To: wen.yang; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 4704 bytes --]
On Wed, Jan 14, 2026 at 01:40:30AM +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> Introduce SYSCTL_ENTRY() and SYSCTL_RANGE_ENTRY() (built on __SYSCTL_ENTRY())
> to consolidate common struct ctl_table initializers in one place.
> This is a preparatory refactor in support of future tree-wide sysctl
> cleanups by making the how to build a ctl_table entry logic uniform.
>
> Also add SYSCTL_NULL for explicitly passing a NULL .data pointer through
> the new macros, and convert a few in-tree users (kernel/sysctl.c) to
> validate the new helpers.
>
> No functional change intended.
>
> Selftest were also made:
>
> [19:31:40] Starting KUnit Kernel (1/1)...
> [19:31:40] ============================================================
> Running tests with:
> $ .kunit/linux kunit.filter_glob=sysctl_test kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
> [19:31:40] ================ sysctl_test (10 subtests) =================
> [19:31:40] [PASSED] sysctl_test_api_dointvec_null_tbl_data
> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
> [19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_positive
> [19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_negative
> [19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_positive
> [19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_negative
> [19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
> [19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
> [19:31:40] =================== [PASSED] sysctl_test ===================
> [19:31:40] ============================================================
> [19:31:40] Testing complete. Ran 10 tests: passed: 10
> [19:31:40] Elapsed time: 26.755s total, 0.002s configuring, 26.632s building, 0.102s running
>
> Suggested-by: Joel Granados <joel.granados@kernel.org>
> Link: https://lore.kernel.org/all/7mevcgca7xsd5gckrap22byjinhrgqnelu4y7hzo2r5t2cq7w4@nyaa42khwqez/#t
> Signed-off-by: Wen Yang <wen.yang@linux.dev>
> ---
> include/linux/sysctl.h | 18 ++++++
> kernel/sysctl-test.c | 131 ++++++++++-------------------------------
> kernel/sysctl.c | 43 ++------------
> 3 files changed, 56 insertions(+), 136 deletions(-)
>
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 2886fbceb5d6..683422bc3e7f 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -53,6 +53,7 @@ struct ctl_dir;
> #define SYSCTL_MAXOLDUID ((void *)&sysctl_vals[10])
> #define SYSCTL_NEG_ONE ((void *)&sysctl_vals[11])
>
> +#define SYSCTL_NULL ((void *)NULL)
> extern const int sysctl_vals[];
>
> #define SYSCTL_LONG_ZERO ((void *)&sysctl_long_vals[0])
> @@ -175,6 +176,23 @@ struct ctl_table {
> void *extra2;
> } __randomize_layout;
>
> +#define __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, HANDLER, SMIN, SMAX)\
> + { \
> + .procname = NAME, \
> + .data = DATA, \
I would like to see at least one lvalue check for the macro parameters.
Something like this
.data = (*(typeof(DATA)*)&(DATA))
In such a way that the compiler fails if an expression is passed.
We can handle these cases (if they exist) in some other way
> + .maxlen = sizeof(TYPE), \
> + .mode = MODE, \
> + .proc_handler = HANDLER, \
> + .extra1 = SMIN, \
> + .extra2 = SMAX, \
> + }
> +
> +#define SYSCTL_ENTRY(NAME, DATA, TYPE, MODE) \
> + __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec, NULL, NULL)
> +
> +#define SYSCTL_RANGE_ENTRY(NAME, DATA, TYPE, MODE, SMIN, SMAX) \
I'm leaning more towards SYSCTL_ENTRY_RANGE or SYSCTL_ENTRY_R.
I am also keeping notes here if you are interested
https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
Best
> + __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec_minmax, SMIN, SMAX)
> +
> struct ctl_node {
> struct rb_node node;
> struct ctl_table_header *header;
> diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
> index 92f94ea28957..7fb0e7f1e62f 100644
> --- a/kernel/sysctl-test.c
> +++ b/kernel/sysctl-test.c
> @@ -15,20 +15,14 @@
....
> - {
> - .procname = "ignore-unaligned-usertrap",
> - .data = &no_unaligned_warning,
> - .maxlen = sizeof (int),
> - .mode = 0644,
> - .proc_handler = proc_dointvec,
> - },
> + SYSCTL_ENTRY("ignore-unaligned-usertrap", &no_unaligned_warning, int, 0644),
> #endif
> };
>
> --
> 2.25.1
>
--
Joel Granados
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init
2026-01-22 9:56 ` Joel Granados
@ 2026-01-25 20:08 ` Wen Yang
0 siblings, 0 replies; 8+ messages in thread
From: Wen Yang @ 2026-01-25 20:08 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel
On 1/22/26 17:56, Joel Granados wrote:
> On Wed, Jan 14, 2026 at 01:40:30AM +0800, wen.yang@linux.dev wrote:
>> From: Wen Yang <wen.yang@linux.dev>
>>
>> Introduce SYSCTL_ENTRY() and SYSCTL_RANGE_ENTRY() (built on __SYSCTL_ENTRY())
>> to consolidate common struct ctl_table initializers in one place.
>> This is a preparatory refactor in support of future tree-wide sysctl
>> cleanups by making the how to build a ctl_table entry logic uniform.
>>
>> Also add SYSCTL_NULL for explicitly passing a NULL .data pointer through
>> the new macros, and convert a few in-tree users (kernel/sysctl.c) to
>> validate the new helpers.
>>
>> No functional change intended.
>>
>> Selftest were also made:
>>
>> [19:31:40] Starting KUnit Kernel (1/1)...
>> [19:31:40] ============================================================
>> Running tests with:
>> $ .kunit/linux kunit.filter_glob=sysctl_test kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
>> [19:31:40] ================ sysctl_test (10 subtests) =================
>> [19:31:40] [PASSED] sysctl_test_api_dointvec_null_tbl_data
>> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
>> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
>> [19:31:40] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
>> [19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_positive
>> [19:31:40] [PASSED] sysctl_test_dointvec_read_happy_single_negative
>> [19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_positive
>> [19:31:40] [PASSED] sysctl_test_dointvec_write_happy_single_negative
>> [19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
>> [19:31:40] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
>> [19:31:40] =================== [PASSED] sysctl_test ===================
>> [19:31:40] ============================================================
>> [19:31:40] Testing complete. Ran 10 tests: passed: 10
>> [19:31:40] Elapsed time: 26.755s total, 0.002s configuring, 26.632s building, 0.102s running
>>
>> Suggested-by: Joel Granados <joel.granados@kernel.org>
>> Link: https://lore.kernel.org/all/7mevcgca7xsd5gckrap22byjinhrgqnelu4y7hzo2r5t2cq7w4@nyaa42khwqez/#t
>> Signed-off-by: Wen Yang <wen.yang@linux.dev>
>> ---
>> include/linux/sysctl.h | 18 ++++++
>> kernel/sysctl-test.c | 131 ++++++++++-------------------------------
>> kernel/sysctl.c | 43 ++------------
>> 3 files changed, 56 insertions(+), 136 deletions(-)
>>
>> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
>> index 2886fbceb5d6..683422bc3e7f 100644
>> --- a/include/linux/sysctl.h
>> +++ b/include/linux/sysctl.h
>> @@ -53,6 +53,7 @@ struct ctl_dir;
>> #define SYSCTL_MAXOLDUID ((void *)&sysctl_vals[10])
>> #define SYSCTL_NEG_ONE ((void *)&sysctl_vals[11])
>>
>> +#define SYSCTL_NULL ((void *)NULL)
>> extern const int sysctl_vals[];
>>
>> #define SYSCTL_LONG_ZERO ((void *)&sysctl_long_vals[0])
>> @@ -175,6 +176,23 @@ struct ctl_table {
>> void *extra2;
>> } __randomize_layout;
>>
>> +#define __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, HANDLER, SMIN, SMAX)\
>> + { \
>> + .procname = NAME, \
>> + .data = DATA, \
> I would like to see at least one lvalue check for the macro parameters.
> Something like this
>
> .data = (*(typeof(DATA)*)&(DATA))
>
> In such a way that the compiler fails if an expression is passed.
> We can handle these cases (if they exist) in some other way
>
>> + .maxlen = sizeof(TYPE), \
>> + .mode = MODE, \
>> + .proc_handler = HANDLER, \
>> + .extra1 = SMIN, \
>> + .extra2 = SMAX, \
>> + }
>> +
>> +#define SYSCTL_ENTRY(NAME, DATA, TYPE, MODE) \
>> + __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec, NULL, NULL)
>> +
>> +#define SYSCTL_RANGE_ENTRY(NAME, DATA, TYPE, MODE, SMIN, SMAX) \
> I'm leaning more towards SYSCTL_ENTRY_RANGE or SYSCTL_ENTRY_R.
>
> I am also keeping notes here if you are interested
> https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
>
Great, thank you for your detailed comments.
These suggestions are indeed the best, implementable, robust, and highly
readable.
I will soon make these changes according to them soon, probably within a
day or two.
--
Best wishes,
Wen
>
>
>> + __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, proc_do##TYPE##vec_minmax, SMIN, SMAX)
>> +
>> struct ctl_node {
>> struct rb_node node;
>> struct ctl_table_header *header;
>> diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
>> index 92f94ea28957..7fb0e7f1e62f 100644
>> --- a/kernel/sysctl-test.c
>> +++ b/kernel/sysctl-test.c
>> @@ -15,20 +15,14 @@
> ....
>> - {
>> - .procname = "ignore-unaligned-usertrap",
>> - .data = &no_unaligned_warning,
>> - .maxlen = sizeof (int),
>> - .mode = 0644,
>> - .proc_handler = proc_dointvec,
>> - },
>> + SYSCTL_ENTRY("ignore-unaligned-usertrap", &no_unaligned_warning, int, 0644),
>> #endif
>> };
>>
>> --
>> 2.25.1
>>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH 2/4] sysctl: add helper functions to extract table->extra1/extra2
2026-01-13 17:40 [RFC PATCH 0/4] sysctl: refactor ctl_table creation and change extra{1,2} type wen.yang
2026-01-13 17:40 ` [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init wen.yang
@ 2026-01-13 17:40 ` wen.yang
2026-01-13 17:40 ` [RFC PATCH 3/4] sysctl: support encoding values directly in the table entry wen.yang
2026-01-13 17:40 ` [RFC PATCH 4/4] scripts/coccinelle: add sysctl_table_init.cocci wen.yang
3 siblings, 0 replies; 8+ messages in thread
From: wen.yang @ 2026-01-13 17:40 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
Add some sysctl helper functions to avoid direct access to
table->extra1/extra2.
No functional change intended.
Selftest were also made:
[14:52:13] ================ sysctl_test (10 subtests) =================
[14:52:13] [PASSED] sysctl_test_api_dointvec_null_tbl_data
[14:52:13] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
[14:52:13] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
[14:52:13] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
[14:52:13] [PASSED] sysctl_test_dointvec_read_happy_single_positive
[14:52:13] [PASSED] sysctl_test_dointvec_read_happy_single_negative
[14:52:13] [PASSED] sysctl_test_dointvec_write_happy_single_positive
[14:52:13] [PASSED] sysctl_test_dointvec_write_happy_single_negative
[14:52:13] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
[14:52:13] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
[14:52:13] =================== [PASSED] sysctl_test ===================
[14:52:13] ============================================================
[14:52:13] Testing complete. Ran 10 tests: passed: 10
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/sysctl.h | 10 ++++++++++
kernel/sysctl.c | 14 ++++----------
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 683422bc3e7f..9690740885ab 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -164,6 +164,16 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll)
#define DEFINE_CTL_TABLE_POLL(name) \
struct ctl_table_poll name = __CTL_TABLE_POLL_INITIALIZER(name)
+#define SYSCTL_IN_RANGE(tbl, val, type) \
+ ((tbl) && \
+ (!(tbl)->extra1 || (*(type *)(tbl)->extra1 <= (type)(val))) && \
+ (!(tbl)->extra2 || (*(type *)(tbl)->extra2 >= (type)(val))))
+
+#define SYSCTL_IN_RANGE_INT(tbl, val) SYSCTL_IN_RANGE(tbl, val, int)
+#define SYSCTL_IN_RANGE_LONG(tbl, val) SYSCTL_IN_RANGE(tbl, val, long)
+#define SYSCTL_IN_RANGE_UINT(tbl, val) SYSCTL_IN_RANGE(tbl, val, unsigned int)
+#define SYSCTL_IN_RANGE_ULONG(tbl, val) SYSCTL_IN_RANGE(tbl, val, unsigned long)
+
/* A sysctl table is an array of struct ctl_table: */
struct ctl_table {
const char *procname; /* Text ID for /proc/sys */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2e769550b268..990e692f626b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -428,10 +428,7 @@ int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
ret = user_to_kern(u_ptr, &tmp_k);
if (ret)
return ret;
- if ((tbl->extra1 &&
- *(uint *)tbl->extra1 > tmp_k) ||
- (tbl->extra2 &&
- *(uint *)tbl->extra2 < tmp_k))
+ if (!SYSCTL_IN_RANGE_UINT(tbl, tmp_k))
return -ERANGE;
WRITE_ONCE(*k_ptr, tmp_k);
} else
@@ -531,8 +528,7 @@ int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
ret = user_to_kern(negp, u_ptr, &tmp_k);
if (ret)
return ret;
- if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
- (tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
+ if (!SYSCTL_IN_RANGE_INT(tbl, tmp_k))
return -EINVAL;
WRITE_ONCE(*k_ptr, tmp_k);
} else
@@ -969,7 +965,7 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int dir,
unsigned long convmul,
unsigned long convdiv)
{
- unsigned long *i, *min, *max;
+ unsigned long *i ;
int vleft, first = 1, err = 0;
size_t left;
char *p;
@@ -981,8 +977,6 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int dir,
}
i = table->data;
- min = table->extra1;
- max = table->extra2;
vleft = table->maxlen / sizeof(unsigned long);
left = *lenp;
@@ -1014,7 +1008,7 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int dir,
}
val = convmul * val / convdiv;
- if ((min && val < *min) || (max && val > *max)) {
+ if (!SYSCTL_IN_RANGE_ULONG(table, val)) {
err = -EINVAL;
break;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [RFC PATCH 3/4] sysctl: support encoding values directly in the table entry
2026-01-13 17:40 [RFC PATCH 0/4] sysctl: refactor ctl_table creation and change extra{1,2} type wen.yang
2026-01-13 17:40 ` [RFC PATCH 1/4] sysctl: add SYSCTL_ENTRY/SYSCTL_RANGE_ENTRY helpers for ctl_table init wen.yang
2026-01-13 17:40 ` [RFC PATCH 2/4] sysctl: add helper functions to extract table->extra1/extra2 wen.yang
@ 2026-01-13 17:40 ` wen.yang
2026-01-13 17:40 ` [RFC PATCH 4/4] scripts/coccinelle: add sysctl_table_init.cocci wen.yang
3 siblings, 0 replies; 8+ messages in thread
From: wen.yang @ 2026-01-13 17:40 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
Eric points out: "by turning .extra1 and .extra2 into longs instead of
keeping them as pointers and needing constants to be pointed at somewhere
.. The only people I can see who find a significant benefit by
consolidating all of the constants into one place are people who know how
to stomp kernel memory."
This patch supports encoding values directly in table entries.
This patch also adds a kunit test case:
[15:09:24] ================ sysctl_test (11 subtests) =================
[15:09:24] [PASSED] sysctl_test_api_dointvec_null_tbl_data
[15:09:24] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
[15:09:24] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
[15:09:24] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
[15:09:24] [PASSED] sysctl_test_dointvec_read_happy_single_positive
[15:09:24] [PASSED] sysctl_test_dointvec_read_happy_single_negative
[15:09:24] [PASSED] sysctl_test_dointvec_write_happy_single_positive
[15:09:24] [PASSED] sysctl_test_dointvec_write_happy_single_negative
[15:09:24] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
[15:09:24] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
[15:09:24] [PASSED] sysctl_test_api_dointvec_write_range_check
[15:09:24] =================== [PASSED] sysctl_test ===================
[15:09:24] ============================================================
[15:09:24] Testing complete. Ran 11 tests: passed: 11
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/sysctl.h | 60 +++++++++++++++--
kernel/sysctl-test.c | 142 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 195 insertions(+), 7 deletions(-)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 9690740885ab..19be4f3f700b 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -165,9 +165,14 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll)
struct ctl_table_poll name = __CTL_TABLE_POLL_INITIALIZER(name)
#define SYSCTL_IN_RANGE(tbl, val, type) \
- ((tbl) && \
- (!(tbl)->extra1 || (*(type *)(tbl)->extra1 <= (type)(val))) && \
- (!(tbl)->extra2 || (*(type *)(tbl)->extra2 >= (type)(val))))
+ ((tbl) && ({ \
+ type v = (type)(val); \
+ !((tbl)->min_is_value ? (tbl)->min > (unsigned long)v \
+ : (tbl)->extra1 && *(type*)(tbl)->extra1 > v) \
+ && !((tbl)->max_is_value ? (tbl)->max < (unsigned long)v \
+ : (tbl)->extra2 && *(type*)(tbl)->extra2 < v); \
+ }) \
+ )
#define SYSCTL_IN_RANGE_INT(tbl, val) SYSCTL_IN_RANGE(tbl, val, int)
#define SYSCTL_IN_RANGE_LONG(tbl, val) SYSCTL_IN_RANGE(tbl, val, long)
@@ -180,12 +185,52 @@ struct ctl_table {
void *data;
int maxlen;
umode_t mode;
+
+ /**
+ * Range bound type flags
+ * - min_is_value: 1 = min field is direct value, 0 = extra1 is pointer
+ * - max_is_value: 1 = max field is direct value, 0 = extra2 is pointer
+ */
+ union {
+ struct {
+ unsigned char min_is_value : 1; /* bit 0: min type */
+ unsigned char max_is_value : 1; /* bit 1: max type */
+ unsigned char reserved : 6; /* bits 2-7: reserved */
+ };
+ unsigned char flags;
+ };
+
proc_handler *proc_handler; /* Callback for text formatting */
struct ctl_table_poll *poll;
- void *extra1;
- void *extra2;
+
+ /* Range bounds: either pointers or direct values */
+ union {
+ struct {
+ void *extra1; /* min as pointer */
+ void *extra2; /* max as pointer */
+ };
+ struct {
+ unsigned long min; /* min as direct value */
+ unsigned long max; /* max as direct value */
+ };
+ };
} __randomize_layout;
+/* Pointer detection using _Generic */
+#define IS_PTR(x) _Generic((x), \
+ char*:1, short*:1, int*:1, long*:1, \
+ unsigned char*:1, unsigned short*:1, unsigned int*:1, unsigned long*:1, \
+ const char*:1, const int*:1, const long*:1, \
+ void*:1, const void*:1, default:0)
+
+/* Auto-detect range flags: 0=ptr_ptr, 1=val_ptr, 2=ptr_val, 3=val_val */
+#define RANGE_FLAGS(smin, smax) ((!IS_PTR(smin)) | ((!IS_PTR(smax)) << 1))
+
+/* Type-specific checks */
+#define CHECK_INT(tbl, val) CHECK(tbl, val, int)
+#define CHECK_UINT(tbl, val) CHECK(tbl, val, unsigned int)
+#define CHECK_LONG(tbl, val) CHECK(tbl, val, long)
+
#define __SYSCTL_ENTRY(NAME, DATA, TYPE, MODE, HANDLER, SMIN, SMAX)\
{ \
.procname = NAME, \
@@ -193,8 +238,9 @@ struct ctl_table {
.maxlen = sizeof(TYPE), \
.mode = MODE, \
.proc_handler = HANDLER, \
- .extra1 = SMIN, \
- .extra2 = SMAX, \
+ .flags = RANGE_FLAGS(SMIN, SMAX), \
+ .min = (unsigned long)(SMIN), \
+ .max = (unsigned long)(SMAX), \
}
#define SYSCTL_ENTRY(NAME, DATA, TYPE, MODE) \
diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 7fb0e7f1e62f..b55bc84e56c2 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -300,6 +300,147 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
}
+/*
+ * Test that writing values within the specified range succeeds,
+ * and ensures out-of-range writes are correctly rejected.
+ */
+static void sysctl_test_api_dointvec_write_range_check(struct kunit *test)
+{
+ int data = 0;
+ size_t len;
+ loff_t pos;
+ char *buffer;
+ char __user *user_buffer;
+
+ /* Prepare bound variables for pointer modes */
+ int min_bounds[] = {10, 0, 15, 0};
+ int max_bounds[] = {90, 75, 0, 0};
+
+ /* Initialize test tables using macro */
+ struct ctl_table test_cases[] = {
+ /* PTR_PTR: both pointers */
+ SYSCTL_RANGE_ENTRY("ptr_ptr", &data, int, 0644,
+ &min_bounds[0], &max_bounds[0]),
+ /* VAL_PTR: min is value, max is pointer */
+ SYSCTL_RANGE_ENTRY("val_ptr", &data, int, 0644,
+ 25, &max_bounds[1]),
+ /* PTR_VAL: min is pointer, max is value */
+ SYSCTL_RANGE_ENTRY("ptr_val", &data, int, 0644,
+ &min_bounds[2], 85),
+ /* VAL_VAL: both values */
+ SYSCTL_RANGE_ENTRY("val_val", &data, int, 0644,
+ 20, 80),
+ };
+
+ /* Test parameters for each mode */
+ const struct {
+ int min, max;
+ const char *valid;
+ const char *below_min;
+ const char *above_max;
+ } test_params[] = {
+ {10, 90, "50", "5", "95"}, /* PTR_PTR */
+ {25, 75, "50", "20", "80"}, /* VAL_PTR */
+ {15, 85, "50", "10", "90"}, /* PTR_VAL */
+ {20, 80, "50", "15", "85"}, /* VAL_VAL */
+ };
+
+ for (int i = 0; i < 4; i++) {
+ struct ctl_table *table = &test_cases[i];
+ const typeof(test_params[0]) *param = &test_params[i];
+
+ /* Verify flags auto-detection */
+ KUNIT_EXPECT_EQ(test, table->flags, i);
+ KUNIT_EXPECT_EQ(test, (int)table->min_is_value, (i & 1) != 0);
+ KUNIT_EXPECT_EQ(test, (int)table->max_is_value, (i & 2) != 0);
+
+ /* Verify union access */
+ if (table->min_is_value) {
+ KUNIT_EXPECT_EQ(test, table->min, (unsigned long)param->min);
+ } else {
+ KUNIT_EXPECT_PTR_EQ(test, table->extra1, &min_bounds[i]);
+ KUNIT_EXPECT_EQ(test, *(int *)table->extra1, param->min);
+ }
+
+ if (table->max_is_value) {
+ KUNIT_EXPECT_EQ(test, table->max, (unsigned long)param->max);
+ } else {
+ KUNIT_EXPECT_PTR_EQ(test, table->extra2, &max_bounds[i]);
+ KUNIT_EXPECT_EQ(test, *(int *)table->extra2, param->max);
+ }
+
+ /* Test valid value in range */
+ data = 0;
+ len = strlen(param->valid);
+ pos = 0;
+ buffer = kunit_kzalloc(test, len, GFP_USER);
+ user_buffer = (char __user *)buffer;
+ memcpy(buffer, param->valid, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, strlen(param->valid), len);
+ KUNIT_EXPECT_EQ(test, strlen(param->valid), pos);
+ KUNIT_EXPECT_EQ(test, 50, data);
+
+ /* Test min boundary */
+ data = 0;
+ char min_str[16];
+ snprintf(min_str, sizeof(min_str), "%d", param->min);
+ len = strlen(min_str);
+ pos = 0;
+ buffer = kunit_kzalloc(test, len, GFP_USER);
+ user_buffer = (char __user *)buffer;
+ memcpy(buffer, min_str, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, strlen(min_str), len);
+ KUNIT_EXPECT_EQ(test, strlen(min_str), pos);
+ KUNIT_EXPECT_EQ(test, param->min, data);
+
+ /* Test max boundary */
+ data = 0;
+ char max_str[16];
+ snprintf(max_str, sizeof(max_str), "%d", param->max);
+ len = strlen(max_str);
+ pos = 0;
+ buffer = kunit_kzalloc(test, len, GFP_USER);
+ user_buffer = (char __user *)buffer;
+ memcpy(buffer, max_str, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, strlen(max_str), len);
+ KUNIT_EXPECT_EQ(test, strlen(max_str), pos);
+ KUNIT_EXPECT_EQ(test, param->max, data);
+
+ /* Test below min - should fail */
+ data = 0;
+ len = strlen(param->below_min);
+ pos = 0;
+ buffer = kunit_kzalloc(test, len, GFP_USER);
+ user_buffer = (char __user *)buffer;
+ memcpy(buffer, param->below_min, len);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, data);
+
+ /* Test above max - should fail */
+ data = 0;
+ len = strlen(param->above_max);
+ pos = 0;
+ buffer = kunit_kzalloc(test, len, GFP_USER);
+ user_buffer = (char __user *)buffer;
+ memcpy(buffer, param->above_max, len);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, data);
+ }
+}
+
static struct kunit_case sysctl_test_cases[] = {
KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
@@ -311,6 +452,7 @@ static struct kunit_case sysctl_test_cases[] = {
KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_range_check),
{}
};
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [RFC PATCH 4/4] scripts/coccinelle: add sysctl_table_init.cocci
2026-01-13 17:40 [RFC PATCH 0/4] sysctl: refactor ctl_table creation and change extra{1,2} type wen.yang
` (2 preceding siblings ...)
2026-01-13 17:40 ` [RFC PATCH 3/4] sysctl: support encoding values directly in the table entry wen.yang
@ 2026-01-13 17:40 ` wen.yang
3 siblings, 0 replies; 8+ messages in thread
From: wen.yang @ 2026-01-13 17:40 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
This coccinelle script transforms traditional sysctl table
initializations into the new macro-based format using
SYSCTL_ENTRY and SYSCTL_RANGE_ENTRY.
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
.../coccinelle/api/sysctl_table_init.cocci | 519 ++++++++++++++++++
1 file changed, 519 insertions(+)
create mode 100644 scripts/coccinelle/api/sysctl_table_init.cocci
diff --git a/scripts/coccinelle/api/sysctl_table_init.cocci b/scripts/coccinelle/api/sysctl_table_init.cocci
new file mode 100644
index 000000000000..e2a1ce46329d
--- /dev/null
+++ b/scripts/coccinelle/api/sysctl_table_init.cocci
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/// Convert sysctl table initializations to use SYSCTL_ENTRY and SYSCTL_RANGE_ENTRY macros
+///
+// Confidence: Medium
+// Options: --no-includes --include-headers
+
+// Virtual rules for different modes
+virtual patch
+virtual context
+virtual report
+virtual org
+
+// =============================================================================
+// STRUCT DECLARATIONS with extra1/extra2 - Type-specific rules
+// =============================================================================
+
+// int type
+@rule_struct_range_int depends on patch@
+expression E1, E2, E3, E4;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(int),
+- .mode = E2,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- };
++ struct ctl_table I = SYSCTL_RANGE_ENTRY(E1, &D, int, E2, E3, E4);
+
+// unsigned int -> uint
+@rule_struct_range_uint depends on patch@
+expression E1, E2, E3, E4;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned int),
+- .mode = E2,
+- .proc_handler = proc_douintvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- };
++ struct ctl_table I = SYSCTL_RANGE_ENTRY(E1, &D, uint, E2, E3, E4);
+
+// unsigned -> uint
+@rule_struct_range_unsigned depends on patch@
+expression E1, E2, E3, E4;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned),
+- .mode = E2,
+- .proc_handler = proc_douintvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- };
++ struct ctl_table I = SYSCTL_RANGE_ENTRY(E1, &D, uint, E2, E3, E4);
+
+// long type
+@rule_struct_range_long depends on patch@
+expression E1, E2, E3, E4;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(long),
+- .mode = E2,
+- .proc_handler = proc_dolongvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- };
++ struct ctl_table I = SYSCTL_RANGE_ENTRY(E1, &D, long, E2, E3, E4);
+
+// unsigned long -> ulong
+@rule_struct_range_ulong depends on patch@
+expression E1, E2, E3, E4;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned long),
+- .mode = E2,
+- .proc_handler = proc_doulongvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- };
++ struct ctl_table I = SYSCTL_RANGE_ENTRY(E1, &D, ulong, E2, E3, E4);
+
+// unsigned char -> u8
+@rule_struct_range_u8 depends on patch@
+expression E1, E2, E3, E4;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned char),
+- .mode = E2,
+- .proc_handler = proc_dou8vec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- };
++ struct ctl_table I = SYSCTL_RANGE_ENTRY(E1, &D, u8, E2, E3, E4);
+
+// =============================================================================
+// STRUCT DECLARATIONS without extra1/extra2 - Type-specific rules
+// Only match standard proc_handler: proc_dointvec, proc_douintvec, etc.
+// =============================================================================
+
+// int type
+@rule_struct_simple_int depends on patch@
+expression E1, E2;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(int),
+- .mode = E2,
+- .proc_handler = proc_dointvec
+- };
++ struct ctl_table I = SYSCTL_ENTRY(E1, &D, int, E2);
+
+// unsigned int -> uint
+@rule_struct_simple_uint depends on patch@
+expression E1, E2;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned int),
+- .mode = E2,
+- .proc_handler = proc_douintvec
+- };
++ struct ctl_table I = SYSCTL_ENTRY(E1, &D, uint, E2);
+
+// unsigned -> uint
+@rule_struct_simple_unsigned depends on patch@
+expression E1, E2;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned),
+- .mode = E2,
+- .proc_handler = proc_douintvec
+- };
++ struct ctl_table I = SYSCTL_ENTRY(E1, &D, uint, E2);
+
+// long type
+@rule_struct_simple_long depends on patch@
+expression E1, E2;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(long),
+- .mode = E2,
+- .proc_handler = proc_dolongvec
+- };
++ struct ctl_table I = SYSCTL_ENTRY(E1, &D, long, E2);
+
+// unsigned long -> ulong
+@rule_struct_simple_ulong depends on patch@
+expression E1, E2;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned long),
+- .mode = E2,
+- .proc_handler = proc_doulongvec
+- };
++ struct ctl_table I = SYSCTL_ENTRY(E1, &D, ulong, E2);
+
+// unsigned char -> u8
+@rule_struct_simple_u8 depends on patch@
+expression E1, E2;
+identifier D, I;
+@@
+- struct ctl_table I = {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned char),
+- .mode = E2,
+- .proc_handler = proc_dou8vec
+- };
++ struct ctl_table I = SYSCTL_ENTRY(E1, &D, u8, E2);
+
+// =============================================================================
+// ARRAY ELEMENTS with extra1/extra2 - Type-specific rules
+// =============================================================================
+
+// int type
+@rule_array_range_int depends on patch@
+expression E1, E2, E3, E4;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(int),
+- .mode = E2,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- }
++ SYSCTL_RANGE_ENTRY(E1, &D, int, E2, E3, E4)
+
+// unsigned int -> uint
+@rule_array_range_uint depends on patch@
+expression E1, E2, E3, E4;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned int),
+- .mode = E2,
+- .proc_handler = proc_douintvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- }
++ SYSCTL_RANGE_ENTRY(E1, &D, uint, E2, E3, E4)
+
+// unsigned -> uint
+@rule_array_range_unsigned depends on patch@
+expression E1, E2, E3, E4;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned),
+- .mode = E2,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- }
++ SYSCTL_RANGE_ENTRY(E1, &D, uint, E2, E3, E4)
+
+// long type
+@rule_array_range_long depends on patch@
+expression E1, E2, E3, E4;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(long),
+- .mode = E2,
+- .proc_handler = proc_dolongvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- }
++ SYSCTL_RANGE_ENTRY(E1, &D, long, E2, E3, E4)
+
+// unsigned long -> ulong
+@rule_array_range_ulong depends on patch@
+expression E1, E2, E3, E4;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned long),
+- .mode = E2,
+- .proc_handler = proc_doulongvec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- }
++ SYSCTL_RANGE_ENTRY(E1, &D, ulong, E2, E3, E4)
+
+// unsigned char -> u8
+@rule_array_range_u8 depends on patch@
+expression E1, E2, E3, E4;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned char),
+- .mode = E2,
+- .proc_handler = proc_dou8vec_minmax,
+- .extra1 = E3,
+- .extra2 = E4
+- }
++ SYSCTL_RANGE_ENTRY(E1, &D, u8, E2, E3, E4)
+
+// =============================================================================
+// ARRAY ELEMENTS without extra1/extra2 - Type-specific rules
+// =============================================================================
+
+// int type
+@rule_array_simple_int depends on patch@
+expression E1, E2;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(int),
+- .mode = E2,
+- .proc_handler = proc_dointvec
+- }
++ SYSCTL_ENTRY(E1, &D, int, E2)
+
+// unsigned int -> uint
+@rule_array_simple_uint depends on patch@
+expression E1, E2;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned int),
+- .mode = E2,
+- .proc_handler = proc_douintvec
+- }
++ SYSCTL_ENTRY(E1, &D, uint, E2)
+
+// unsigned -> uint
+@rule_array_simple_unsigned depends on patch@
+expression E1, E2;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned),
+- .mode = E2,
+- .proc_handler = proc_dointvec
+- }
++ SYSCTL_ENTRY(E1, &D, uint, E2)
+
+// long type
+@rule_array_simple_long depends on patch@
+expression E1, E2;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(long),
+- .mode = E2,
+- .proc_handler = proc_dolongvec
+- }
++ SYSCTL_ENTRY(E1, &D, long, E2)
+
+// unsigned long -> ulong
+@rule_array_simple_ulong depends on patch@
+expression E1, E2;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned long),
+- .mode = E2,
+- .proc_handler = proc_doulongvec
+- }
++ SYSCTL_ENTRY(E1, &D, ulong, E2)
+
+// unsigned char -> u8
+@rule_array_simple_u8 depends on patch@
+expression E1, E2;
+identifier D;
+@@
+- {
+- .procname = E1,
+- .data = &D,
+- .maxlen = sizeof(unsigned char),
+- .mode = E2,
+- .proc_handler = proc_dou8vec
+- }
++ SYSCTL_ENTRY(E1, &D, u8, E2)
+
+// =============================================================================
+// CONTEXT MODE - Generic rule to show all matches
+// =============================================================================
+
+@rule_context_range depends on context@
+expression E1, E2, E3, E4;
+identifier D;
+identifier H = {proc_dointvec_minmax, proc_douintvec_minmax, proc_dolongvec_minmax, proc_doulongvec_minmax, proc_dou8vec_minmax};
+type T;
+@@
+* {
+* .procname = E1,
+* .data = &D,
+* .maxlen = sizeof(T),
+* .mode = E2,
+* .proc_handler = H,
+* .extra1 = E3,
+* .extra2 = E4
+* }
+
+@rule_context_simple depends on context@
+expression E1, E2;
+identifier D;
+identifier H = {proc_dointvec, proc_douintvec, proc_dolongvec, proc_doulongvec, proc_dou8vec};
+type T;
+@@
+* {
+* .procname = E1,
+* .data = &D,
+* .maxlen = sizeof(T),
+* .mode = E2,
+* .proc_handler = H
+* }
+
+// =============================================================================
+// REPORT MODE - Generic rule to report all matches
+// =============================================================================
+
+@rule_report_range depends on report@
+expression E1, E2, E3, E4;
+identifier D;
+identifier H = {proc_dointvec_minmax, proc_douintvec_minmax, proc_dolongvec_minmax, proc_doulongvec_minmax, proc_dou8vec_minmax};
+type T;
+position p;
+@@
+{
+ .procname@p = E1,
+ .data = &D,
+ .maxlen = sizeof(T),
+ .mode = E2,
+ .proc_handler = H,
+ .extra1 = E3,
+ .extra2 = E4
+}
+
+@script:python depends on report@
+p << rule_report_range.p;
+@@
+msg = "INFO: ctl_table initialization can use SYSCTL_RANGE_ENTRY"
+coccilib.report.print_report(p[0], msg)
+
+@rule_report_simple depends on report@
+expression E1, E2;
+identifier D;
+identifier H = {proc_dointvec, proc_douintvec, proc_dolongvec, proc_doulongvec, proc_dou8vec};
+type T;
+position p;
+@@
+{
+ .procname@p = E1,
+ .data = &D,
+ .maxlen = sizeof(T),
+ .mode = E2,
+ .proc_handler = H
+}
+
+@script:python depends on report@
+p << rule_report_simple.p;
+@@
+msg = "INFO: ctl_table initialization can use SYSCTL_ENTRY"
+coccilib.report.print_report(p[0], msg)
+
+// =============================================================================
+// ORG MODE - Generic rule for org-mode output
+// =============================================================================
+
+@rule_org_range depends on org@
+expression E1, E2, E3, E4;
+identifier D;
+identifier H = {proc_dointvec_minmax, proc_douintvec_minmax, proc_dolongvec_minmax, proc_doulongvec_minmax, proc_dou8vec_minmax};
+type T;
+position p;
+@@
+{
+ .procname@p = E1,
+ .data = &D,
+ .maxlen = sizeof(T),
+ .mode = E2,
+ .proc_handler = H,
+ .extra1 = E3,
+ .extra2 = E4
+}
+
+@script:python depends on org@
+p << rule_org_range.p;
+@@
+msg = "INFO: ctl_table initialization can use SYSCTL_RANGE_ENTRY"
+coccilib.org.print_todo(p[0], msg)
+
+@rule_org_simple depends on org@
+expression E1, E2;
+identifier D;
+identifier H = {proc_dointvec, proc_douintvec, proc_dolongvec, proc_doulongvec, proc_dou8vec};
+type T;
+position p;
+@@
+{
+ .procname@p = E1,
+ .data = &D,
+ .maxlen = sizeof(T),
+ .mode = E2,
+ .proc_handler = H
+}
+
+@script:python depends on org@
+p << rule_org_simple.p;
+@@
+msg = "INFO: ctl_table initialization can use SYSCTL_ENTRY"
+coccilib.org.print_todo(p[0], msg)
+
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread