sysctl: refactor ctl_table initialization
v3 cover sysctl: refactor ctl_table initialization
From: Wen Yang <[email protected]>
The main motivation for this series is to avoid treewide patch series. Historically, changes to how struct ctl_table entries are initialized (e.g. removing the child field, const-qualifying ctl_table) required touching hundreds of files across all subsystems. Such series require the attention of many maintainers, sometimes need to be pulled into mainline in a special way, and create lots of unnecessary churn.
By introducing a family of CTLTBL_ENTRY_XXX() helper macros in <linux/sysctl.h>, any future structural changes to struct ctl_table can be handled in one place — the macro definition — without requiring a treewide sweep of all callers. Individual subsystem maintainers can also migrate their sysctl tables to use the macros at their own pace, without any coordinated effort.
This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, allowing callers to initialize struct ctl_table entries indirectly via the macro instead of assigning each field directly. The macros use _Generic() for automatic type detection and proc_handler selection, provide smart defaults (auto address-of, auto maxlen via sizeof), and include compile-time validation of name, mode, and handler.
2. Converts kernel/sysctl-test.c to use the new macros as a demonstration, replacing direct struct ctl_table field assignments with CTLTBL_ENTRY_XXX() calls. Four new tests are added to cover the previously untested variants (CTLTBL_ENTRY_V, VN, VNM, VNMH), bringing the total to 16 passing tests.
Thanks Joel's suggestions and guidance:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/
Wen Yang (2):
sysctl: introduce CTLTBL_ENTRY_XXX() helper macros
sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
include/linux/sysctl.h | 302 +++++++++++++++++++++++++++++++++++++++++ kernel/sysctl-test.c | 240 ++++++++++++++++++-------------- kernel/sysctl.c | 2 + 3 files changed, 445 insertions(+), 99 deletions(-)
Signature · 1 line
2.25.1
RFC PATCH v3 1/2 sysctl: introduce CTLTBL_ENTRY_XXX() helper macros
From: Wen Yang <[email protected]>
Add a family of CTLTBL_ENTRY_XXX() macros to simplify struct ctl_table initialization. Each macro variant covers a different use case, using _Generic() for automatic type detection and proc_handler selection, with sensible defaults to reduce boilerplate and potential errors.
The following macro variants are introduced:
CTLTBL_ENTRY_V(__var)
- Uses variable name as procname, mode 0444, and auto-selected handler based on variable type
CTLTBL_ENTRY_VM(__var, __mode)
- Uses variable name as procname, custom mode, and auto-selected handler based on variable type
CTLTBL_ENTRY_VMR(__var, __mode, __min, __max)
- Uses variable name as procname, custom mode,
and auto-selected range-checking handler based on variable type
CTLTBL_ENTRY_VN(__var, __name)
- Custom procname, mode 0444,
and auto-selected handler based on variable type
CTLTBL_ENTRY_VNM(__var, __name, __mode)
- Custom procname and mode,
and auto-selected handler based on variable type
CTLTBL_ENTRY_VNMH(__var, __name, __mode, __handler)
- Custom procname, mode, and proc_handler
CTLTBL_ENTRY_VNMR(__var, __name, __mode, __min, __max)
- Auto-selected range-checking handler based on variable type
CTLTBL_ENTRY_VNMHR(__var, __name, __mode, __handler, __min, __max)
- Custom handler with extra1/extra2 range bounds
CTLTBL_ENTRY_VNMHRL(__var, __name, __mode, __handler, __min, __max, __maxlen)
- Full control including explicit maxlen override
All variants share the following features:
- Automatic type detection via _Generic(): supports int, unsigned int, long, unsigned long; selects the appropriate proc_dointvec, proc_douintvec, or proc_doulongvec_minmax handler automatically
- Auto address-of: the macro takes the address of the variable, so callers do not need to pass &var explicitly
- Auto maxlen: uses sizeof(var) when maxlen is not explicitly provided
- SYSCTL_NULL marker: use SYSCTL_NULL as the variable argument for entries where .data should be NULL
- Compile-time validation: NAME must be a string literal; MODE and HANDLER are checked for type compatibility
No functional change intended.
--- include/linux/sysctl.h | 302 +++++++++++++++++++++++++++++++++++++++++ kernel/sysctl.c | 2 + 2 files changed, 304 insertions(+)
include/linux/sysctl.h +302 -0
@@ -175,6 +175,308 @@ struct ctl_table {void *extra2; } __randomize_layout; +/** +* struct _sysctl_null_type - sentinel type for variable-less entries +*/ +struct _sysctl_null_type {char __dummy; }; + +/** +* _sysctl_null_marker - unique instance of the sentinel type +* +* Define once per link unit: +* const struct _sysctl_null_type _sysctl_null_marker; +*/ +extern const struct _sysctl_null_type _sysctl_null_marker; + +/** +* SYSCTL_NULL - pass as __var for entries with no associated variable +*/ +#define SYSCTL_NULL (_sysctl_null_marker) + +/** +* __CTL_AUTO_HANDLER - select proc_handler based on the variable type +* @__var: kernel variable (value, not address), or SYSCTL_NULL +*/ +#define __CTL_AUTO_HANDLER (__var) \ + _Generic((__var), \ + int : proc_dointvec, \ + unsigned int : proc_douintvec, \ + long : proc_doulongvec_minmax, \ + unsigned long : proc_doulongvec_minmax, \ + default : \ + 0xdeadbeaf) + +/** +* __CTL_AUTO_HANDLER_RANGE - select proc_handler for a range-constrained entry +* @__var: kernel variable (value, not address), or SYSCTL_NULL +*/ +#define __CTL_AUTO_HANDLER_RANGE (__var) \ + _Generic((__var), \ + int : proc_dointvec_minmax, \ + unsigned int : proc_douintvec_minmax, \ + long : proc_doulongvec_minmax, \ + unsigned long : proc_doulongvec_minmax, \ + default : \ + 0xdeadbeaf) + +/** +* __CTL_PROCNAME - validate and return the procname string +* +* "" __name "" enforces a string-literal argument at compile time. +* +* @__name: procname string literal +*/ +#define __CTL_PROCNAME (__name) ("" __name "") + +/** +* __CTL_DATA - assert __var is addressable; return its address +* +* Only lvalue-ness is checked; any addressable type is accepted. +* +* SYSCTL_NULL -> (void *)NULL +* lvalue __var -> (void *)&(__var) +* +* @__var: kernel variable (without &), or SYSCTL_NULL +*/ +#define __CTL_DATA (__var) \ + _Generic((__var), \ + struct _sysctl_null_type : (void *)NULL, \ + default : \ + (void *)&(__var)) + +/* Compute maxlen for NULL entries: use explicit MAXLEN if >0, else 0 */ +#define __SYSCTL_MAXLEN_NULL (__maxlen) \ + ((__maxlen) > 0 ? (size_t)(__maxlen) : (size_t)0) + +/* Compute maxlen: use explicit MAXLEN if >0, else sizeof(VAR) */ +#define __SYSCTL_MAXLEN_VAR (__var, __maxlen) \ + ((__maxlen) >= 0 ? (size_t)(__maxlen) : (size_t)sizeof(__var)) + +/** +* __CTL_MAXLEN - compute the .maxlen value +* +* @__var: kernel variable (without &), or SYSCTL_NULL +* @__maxlen: explicit override, or -1 for auto-sizing +*/ +#define __CTL_MAXLEN (__var, __maxlen) \ + _Generic((__var), \ + struct _sysctl_null_type : \ + __SYSCTL_MAXLEN_NULL(__maxlen), \ + default : \ + __SYSCTL_MAXLEN_VAR(__var, __maxlen) \ + ) + +/** +* __CTL_MODE - validate and return file permission bits +* +* @__mode: file permission bits (e.g. 0644) +*/ +#define __CTL_MODE (__mode) (0 ? (umode_t)0 : (__mode)) + +/** +* __CTL_HANDLER - validate and return proc_handler +* +* exact function-type match via sizeof trick. +* including wrong parameter types or qualifiers. +* @__handler: proc_handler +*/ +#define __CTL_HANDLER (__handler) \ + (0 ? \ + ((void)sizeof(char[__builtin_types_compatible_p( \ + __typeof__(__handler), proc_handler) ? \ + 1 : -1]), NULL) : \ + __handler) + +/* Validate PTR is pointer-compatible and return it as void* */ +#define __CTL_EXTRA (PTR ) \ + (0 ? (void *)0 : (PTR)) + +/** +* Internal primitive (single source of truth) +* +* All public macros delegate here. +* +* @_var: kernel variable (without &), or SYSCTL_NULL +* @_name: procname string literal +* @_mode: file permission bits +* @_handler: proc_handler-compatible function pointer +* @_min: lower bound pointer, or NULL +* @_max: upper bound pointer, or NULL +* @_maxlen: explicit .maxlen, or -1 for auto-sizing +*/ +#define __CTLTBL_ENTRY (_var, _name, _mode, _handler, _min, _max, _maxlen) \ +{ \ + .procname = __CTL_PROCNAME(_name), \ + .data = __CTL_DATA(_var), \ + .maxlen = __CTL_MAXLEN(_var, _maxlen), \ + .mode = __CTL_MODE(_mode), \ + .proc_handler = __CTL_HANDLER(_handler), \ + .poll = NULL, \ + .extra1 = __CTL_EXTRA(_min), \ + .extra2 = __CTL_EXTRA(_max), \ +} + +/** +* Public API +* +* Naming convention: +* V - Variable is auto-named via #__var +* N - explicit Name string +* M - explicit Mode +* H - explicit Handler +* R - Range checking (min/max, selects _minmax handler) +* L - explicit Length (.maxlen override) +*/ + +/** +* CTLTBL_ENTRY_V - read-only entry; procname == stringified variable name +* +* Proto: (T __var) +* +* .procname = #__var, .mode = 0444, .extra1 = .extra2 = NULL. +* proc_handler and .maxlen inferred from typeof(__var). +* +* @__var: kernel variable (without &); must be an addressable lvalue +*/ +#define CTLTBL_ENTRY_V (__var) \ + __CTLTBL_ENTRY(__var, #__var, 0444, \ + __CTL_AUTO_HANDLER(__var), NULL, NULL, -1) + +/** +* CTLTBL_ENTRY_VM - entry with custom mode; procname == #__var +* +* Proto: (T __var, umode_t __mode) +* +* @__var: kernel variable (without &) +* @__mode: file permission bits +*/ +#define CTLTBL_ENTRY_VM (__var, __mode) \ + __CTLTBL_ENTRY(__var, #__var, __mode, \ + __CTL_AUTO_HANDLER(__var), NULL, NULL, -1) + +/** +* CTLTBL_ENTRY_VMR - custom mode with range checking; procname == #__var +* +* Proto: (T __var, umode_t __mode, T *__min, T *__max) +* +* @__var: kernel variable (without &) +* @__mode: file permission bits +* @__min: pointer to minimum value; typeof(*__min) must == typeof(__var) +* @__max: pointer to maximum value; typeof(*__max) must == typeof(__var) +*/ +#define CTLTBL_ENTRY_VMR (__var, __mode, __min, __max) \ + __CTLTBL_ENTRY(__var, #__var, __mode, \ + __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1) + +/** +* CTLTBL_ENTRY_VN - read-only entry with explicit procname +* +* Proto: (T __var, const char *__name) +* +* @__var: kernel variable (without &) +* @__name: procname string literal +*/ +#define CTLTBL_ENTRY_VN (__var, __name) \ + __CTLTBL_ENTRY(__var, __name, 0444, \ + __CTL_AUTO_HANDLER(__var), NULL, NULL, -1) + +/** +* CTLTBL_ENTRY_VNM - entry with explicit procname and mode +* +* Proto: (T __var, const char *__name, umode_t __mode) +* +* @__var: kernel variable (without &) +* @__name: procname string literal +* @__mode: file permission bits +*/ +#define CTLTBL_ENTRY_VNM (__var, __name, __mode) \ + __CTLTBL_ENTRY(__var, __name, __mode, \ + __CTL_AUTO_HANDLER(__var), NULL, NULL, -1) + +/** +* CTLTBL_ENTRY_VNMH - entry with explicit procname, mode and handler +* +* Proto: (T __var, const char *__name, umode_t __mode, +* proc_handler *__handler) +* +* @__var: kernel variable (without &), or SYSCTL_NULL +* @__name: procname string literal +* @__mode: file permission bits +* @__handler: proc_handler-compatible function pointer +*/ +#define CTLTBL_ENTRY_VNMH (__var, __name, __mode, __handler) \ + __CTLTBL_ENTRY(__var, __name, __mode, __handler, NULL, NULL, -1) + +/** +* CTLTBL_ENTRY_VNMR - entry with procname, mode and range checking +* +* Proto: (T __var, const char *__name, umode_t __mode, +* T *__min, T *__max) +* +* @__var: kernel variable (without &) +* @__name: procname string literal +* @__mode: file permission bits +* @__min: pointer to minimum value; typeof(*__min) must == typeof(__var) +* @__max: pointer to maximum value; typeof(*__max) must == typeof(__var) +*/ +#define CTLTBL_ENTRY_VNMR (__var, __name, __mode, __min, __max) \ + __CTLTBL_ENTRY(__var, __name, __mode, \ + __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1) + +/** +* CTLTBL_ENTRY_VNMHR - entry with explicit handler and range +* +* Proto: (T __var, const char *__name, umode_t __mode, +* proc_handler *__handler, T *__min, T *__max) +* +* @__var: kernel variable (without &), or SYSCTL_NULL +* @__name: procname string literal +* @__mode: file permission bits +* @__handler: proc_handler-compatible function pointer +* @__min: pointer to minimum value for .extra1 +* @__max: pointer to maximum value for .extra2 +*/ +#define CTLTBL_ENTRY_VNMHR (__var, __name, __mode, __handler, __min, __max) \ + __CTLTBL_ENTRY(__var, __name, __mode, __handler, __min, __max, -1) + +/** +* CTLTBL_ENTRY_VNMHRL - fully explicit entry +* +* Proto: (T __var, const char *__name, umode_t __mode, +* proc_handler *__handler, T *__min, T *__max, +* size_t __maxlen) +* +* Pass -1 for __maxlen to use sizeof(__var) / 0 (auto). +* Pass 0 to set .maxlen to zero explicitly. +* +* @__var: kernel variable (without &), or SYSCTL_NULL +* @__name: procname string literal +* @__mode: file permission bits +* @__handler: proc_handler-compatible function pointer +* @__min: pointer to minimum value for .extra1 +* @__max: pointer to maximum value for .extra2 +* @__maxlen: explicit value for .maxlen +*/ +#define CTLTBL_ENTRY_VNMHRL (__var, __name, __mode, __handler, \ + __min, __max, __maxlen) \ + __CTLTBL_ENTRY(__var, __name, __mode, __handler, __min, __max, __maxlen) + +/** +* CTLTBL_ENTRY_NMH - custom-handler entry with no associated variable +* +* Proto: (const char *__name, umode_t __mode, +* proc_handler *__handler) +* +* Shorthand for CTLTBL_ENTRY_VNMH(SYSCTL_NULL, ...). +* .data = NULL, .maxlen = 0, .extra1 = NULL, .extra2 = NULL. +* +* @__name: procname string literal +* @__mode: file permission bits +* @__handler: proc_handler-compatible function pointer +*/ +#define CTLTBL_ENTRY_NMH (__name, __mode, __handler) \ + CTLTBL_ENTRY_VNMH(SYSCTL_NULL, __name, __mode, __handler) +struct ctl_node {struct rb_node node;struct ctl_table_header *header;
kernel/sysctl.c +2 -0
@@ -29,6 +29,8 @@ EXPORT_SYMBOL(sysctl_vals);const unsigned long sysctl_long_vals[] = {0 ,1 ,LONG_MAX };EXPORT_SYMBOL_GPL (sysctl_long_vals); +const struct _sysctl_null_type _sysctl_null_marker; +#if defined(CONFIG_SYSCTL )/* Constants used for minimum and maximum */
Signature · 1 line
2.25.1
RFC PATCH v3 2/2 sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
In reply to [email protected]From: Wen Yang <[email protected]>
Convert all struct ctl_table initializations in kernel/sysctl-test.c from direct field assignment to indirect initialization via the CTLTBL_ENTRY_XXX() macros introduced in the previous patch. This demonstrates the macros in real usage and validates their correctness against the existing test suite.
The following macro variants are exercised:
- CTLTBL_ENTRY_VNMR(var, name, mode, min, max)
Used for the majority of test cases that initialize a standard int entry with range checking. The macro automatically selects the range-checking handler (proc_dointvec_minmax for int) based on the variable type and computes maxlen via sizeof():
Before:
struct ctl_table table = {
.procname = "foo",
.data = &data,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
};
After:
struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
- CTLTBL_ENTRY_VNMHR(var, name, mode, handler, min, max) Used for the NULL data test case, passing SYSCTL_NULL as the data argument and proc_dointvec as the explicit handler:
Before:
struct ctl_table null_data_table = {
.procname = "foo",
.data = NULL,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
};
After:
struct ctl_table null_data_table = CTLTBL_ENTRY_VNMHR(
SYSCTL_NULL, "foo", 0644, proc_dointvec, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
- CTLTBL_ENTRY_VNMHRL(var, name, mode, handler, min, max, maxlen) Used for the maxlen=0 test case, passing an explicit maxlen of 0 to override the default sizeof()-based value:
Before:
struct ctl_table data_maxlen_unset_table = {
.procname = "foo",
.data = &data,
.maxlen = 0,
.mode = 0644,
.proc_handler = proc_dointvec,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
};
After:
struct ctl_table data_maxlen_unset_table = CTLTBL_ENTRY_VNMHRL(
data, "foo", 0644, proc_dointvec, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED, 0);
New tests are added to cover the remaining macro variants:
- CTLTBL_ENTRY_V(var)
Verifies that the macro initializes .procname to the variable name, .mode to 0444, .data to &var, .maxlen to sizeof(var), and selects proc_dointvec as the handler for an int variable.
- CTLTBL_ENTRY_VM(var, mode)
Verifies that the macro initializes .procname to the variable name, .mode to 0644, .data to &var, .maxlen to sizeof(var), and selects proc_dointvec as the handler for an int variable.
- CTLTBL_ENTRY_VMR(var, mode, min, max)
Verifies that the macro initializes .procname to the variable name, .mode to 0644, .data to &var, .maxlen to sizeof(var), and auto-selecting proc_dointvec_minmax as the handler.
- CTLTBL_ENTRY_VN(var, name)
Verifies that the macro initializes .procname to the custom name while keeping .mode at 0444 and auto-selecting the handler.
- CTLTBL_ENTRY_VNM(name, var, mode)
Verifies that the macro initializes .procname and .mode as specified while auto-selecting the handler.
- CTLTBL_ENTRY_VNMH(var, name, mode, handler)
Verifies that the macro initializes all fields including an explicitly provided proc_handler.
All tests pass:
[13:53:49] ================ sysctl_test (16 subtests) ================= [13:53:49] [PASSED] sysctl_test_api_dointvec_null_tbl_data [13:53:49] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset [13:53:49] [PASSED] sysctl_test_api_dointvec_table_len_is_zero [13:53:49] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set [13:53:49] [PASSED] sysctl_test_dointvec_read_happy_single_positive [13:53:49] [PASSED] sysctl_test_dointvec_read_happy_single_negative [13:53:49] [PASSED] sysctl_test_dointvec_write_happy_single_positive [13:53:49] [PASSED] sysctl_test_dointvec_write_happy_single_negative [13:53:49] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min [13:53:49] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max [13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_v
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vm [13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vmr [13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vn [13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vnm [13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vnmh [13:53:49] =================== [PASSED] sysctl_test =================== [13:53:49] ============================================================ [13:53:49] Testing complete. Ran 16 tests: passed: 16
--- kernel/sysctl-test.c | 240 +++++++++++++++++++++++++------------------ 1 file changed, 141 insertions(+), 99 deletions(-)
kernel/sysctl-test.c +141 -99
@@ -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 =CTLTBL_ENTRY_VNMHR ( +SYSCTL_NULL ,"foo" ,0644 , proc_dointvec, +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 =CTLTBL_ENTRY_VNMHRL ( + data,"foo" ,0644 , proc_dointvec, +SYSCTL_ZERO ,SYSCTL_ONE_HUNDRED ,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 =CTLTBL_ENTRY_VNMR (data,"foo" , +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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 =CTLTBL_ENTRY_VNMR (data,"foo" ,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 );
@@ -367,6 +300,109 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(KUNIT_EXPECT_EQ (test,0 , *((int *)table.data)); } +/* +* Test CTLTBL_ENTRY_V: variable name used as procname, mode 0444, +* auto-selected handler (proc_dointvec for int). +*/ +static void sysctl_test_api_ctltbl_entry_v (struct kunit *test) +{ +int foo =123 ; +struct ctl_table table =CTLTBL_ENTRY_V (foo); + +KUNIT_EXPECT_STREQ (test,"foo" , table.procname); +KUNIT_EXPECT_EQ (test, (umode_t )0444 , table.mode); +KUNIT_EXPECT_PTR_EQ (test, &foo, table.data); +KUNIT_EXPECT_EQ (test,sizeof (int ), table.maxlen); +KUNIT_EXPECT_PTR_EQ (test, (proc_handler *)proc_dointvec, + table.proc_handler); +} + +/* +* Test CTLTBL_ENTRY_VM: variable name used as procname, custom mode, +* auto-selected handler (proc_dointvec for int). +*/ +static void sysctl_test_api_ctltbl_entry_vm (struct kunit *test) +{ +int foo =123 ; +struct ctl_table table =CTLTBL_ENTRY_VM (foo,0644 ); + +KUNIT_EXPECT_STREQ (test,"foo" , table.procname); +KUNIT_EXPECT_EQ (test, (umode_t )0644 , table.mode); +KUNIT_EXPECT_PTR_EQ (test, &foo, table.data); +KUNIT_EXPECT_EQ (test,sizeof (int ), table.maxlen); +KUNIT_EXPECT_PTR_EQ (test, (proc_handler *)proc_dointvec, + table.proc_handler); +} + +/* +* Test CTLTBL_ENTRY_VMR: variable name used as procname, custom mode, +* auto-selected range checkint handler (proc_dointvec_minmax for int). +*/ +static void sysctl_test_api_ctltbl_entry_vmr (struct kunit *test) +{ +int foo =123 ; +struct ctl_table table =CTLTBL_ENTRY_VMR (foo,0644 ,SYSCTL_ZERO , +SYSCTL_ONE_HUNDRED ); + +KUNIT_EXPECT_STREQ (test,"foo" , table.procname); +KUNIT_EXPECT_EQ (test, (umode_t )0644 , table.mode); +KUNIT_EXPECT_PTR_EQ (test, &foo, table.data); +KUNIT_EXPECT_EQ (test,sizeof (int ), table.maxlen); +KUNIT_EXPECT_PTR_EQ (test, (proc_handler *)proc_dointvec_minmax, + table.proc_handler); +} + +/* +* Test CTLTBL_ENTRY_VN: custom procname, mode 0444, +* auto-selected handler (proc_dointvec for int). +*/ +static void sysctl_test_api_ctltbl_entry_vn (struct kunit *test) +{ +int data =0 ; +struct ctl_table table =CTLTBL_ENTRY_VN (data,"foo" ); + +KUNIT_EXPECT_STREQ (test,"foo" , table.procname); +KUNIT_EXPECT_EQ (test, (umode_t )0444 , table.mode); +KUNIT_EXPECT_PTR_EQ (test, &data, table.data); +KUNIT_EXPECT_EQ (test,sizeof (int ), table.maxlen); +KUNIT_EXPECT_PTR_EQ (test, (proc_handler *)proc_dointvec, + table.proc_handler); +} + +/* +* Test CTLTBL_ENTRY_VNM: custom procname and mode, +* auto-selected handler (proc_dointvec for int). +*/ +static void sysctl_test_api_ctltbl_entry_vnm (struct kunit *test) +{ +int data =0 ; +struct ctl_table table =CTLTBL_ENTRY_VNM (data,"foo" ,0644 ); + +KUNIT_EXPECT_STREQ (test,"foo" , table.procname); +KUNIT_EXPECT_EQ (test, (umode_t )0644 , table.mode); +KUNIT_EXPECT_PTR_EQ (test, &data, table.data); +KUNIT_EXPECT_EQ (test,sizeof (int ), table.maxlen); +KUNIT_EXPECT_PTR_EQ (test, (proc_handler *)proc_dointvec, + table.proc_handler); +} + +/* +* Test CTLTBL_ENTRY_VNMH: custom procname, mode and explicit handler. +*/ +static void sysctl_test_api_ctltbl_entry_vnmh (struct kunit *test) +{ +int data =0 ; +struct ctl_table table =CTLTBL_ENTRY_VNMH (data,"foo" ,0644 , + proc_dointvec); + +KUNIT_EXPECT_STREQ (test,"foo" , table.procname); +KUNIT_EXPECT_EQ (test, (umode_t )0644 , table.mode); +KUNIT_EXPECT_PTR_EQ (test, &data, table.data); +KUNIT_EXPECT_EQ (test,sizeof (int ), table.maxlen); +KUNIT_EXPECT_PTR_EQ (test, (proc_handler *)proc_dointvec, + table.proc_handler); +} +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),
@@ -378,6 +414,12 @@ 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_ctltbl_entry_v), +KUNIT_CASE (sysctl_test_api_ctltbl_entry_vm), +KUNIT_CASE (sysctl_test_api_ctltbl_entry_vmr), +KUNIT_CASE (sysctl_test_api_ctltbl_entry_vn), +KUNIT_CASE (sysctl_test_api_ctltbl_entry_vnm), + KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnmh), {} };
Signature · 1 line
2.25.1
kernel test robot
build botRe: [RFC PATCH v3 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
kernel test robot noticed the following build warnings:
Hi,
[This is a private test report for your RFC patch.] kernel test robot noticed the following build warnings:
[auto build test WARNING on sysctl/sysctl-next]
[also build test WARNING on linus/master v7.0-rc2 next-20260304] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/wen-yang-linux-dev/sysctl-introduce-CTLTBL_ENTRY_XXX-helper-macros/20260302-143709 base: https://git.kernel.org/pub/scm/linux/kernel/git/sysctl/sysctl.git sysctl-next patch link: https://lore.kernel.org/r/ae37f783d33aeab5769551df4d500467eb0676e5.1772432502.git.wen.yang%40linux.dev patch subject: [RFC PATCH v3 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20260305/[email protected]/config) compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260305/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> kernel/sysctl-test.c:23:37: warning: pointer type mismatch ('void *' and 'int (*)(const struct ctl_table *, int, void *, size_t *, loff_t *)' (aka 'int (*)(const struct ctl_table *, int, void *, unsigned long *, long long *)')) [-Wpointer-type-mismatch]
23 | struct ctl_table null_data_table = CTLTBL_ENTRY_VNMHR(
| ^~~~~~~~~~~~~~~~~~~
24 | SYSCTL_NULL, "foo", 0644, proc_dointvec,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 | SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:440:2: note: expanded from macro 'CTLTBL_ENTRY_VNMHR'
440 | __CTLTBL_ENTRY(__var, __name, __mode, __handler, __min, __max, -1)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:313:18: note: expanded from macro '__CTLTBL_ENTRY'
313 | .proc_handler = __CTL_HANDLER(_handler), \
| ^~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:284:5: note: expanded from macro '__CTL_HANDLER'
284 | (0 ? \
| ^
285 | ((void)sizeof(char[__builtin_types_compatible_p( \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286 | __typeof__(__handler), proc_handler) ? \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287 | 1 : -1]), NULL) : \
| ~~~~~~~~~~~~~~~
288 | __handler)
| ~~~~~~~~~
kernel/sysctl-test.c:67:45: warning: pointer type mismatch ('void *' and 'int (*)(const struct ctl_table *, int, void *, size_t *, loff_t *)' (aka 'int (*)(const struct ctl_table *, int, void *, unsigned long *, long long *)')) [-Wpointer-type-mismatch]
67 | struct ctl_table data_maxlen_unset_table = CTLTBL_ENTRY_VNMHRL(
| ^~~~~~~~~~~~~~~~~~~~
68 | data, "foo", 0644, proc_dointvec,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
69 | SYSCTL_ZERO, SYSCTL_ONE_HUNDRED, 0);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:462:2: note: expanded from macro 'CTLTBL_ENTRY_VNMHRL'
462 | __CTLTBL_ENTRY(__var, __name, __mode, __handler, __min, __max, __maxlen)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:313:18: note: expanded from macro '__CTLTBL_ENTRY'
313 | .proc_handler = __CTL_HANDLER(_handler), \
| ^~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:284:5: note: expanded from macro '__CTL_HANDLER'
284 | (0 ? \
| ^
285 | ((void)sizeof(char[__builtin_types_compatible_p( \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286 | __typeof__(__handler), proc_handler) ? \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287 | 1 : -1]), NULL) : \
| ~~~~~~~~~~~~~~~
288 | __handler)
| ~~~~~~~~~
kernel/sysctl-test.c:105:27: warning: pointer type mismatch ('void *' and 'int (*)(const struct ctl_table *, int, void *, size_t *, loff_t *)' (aka 'int (*)(const struct ctl_table *, int, void *, unsigned long *, long long *)')) [-Wpointer-type-mismatch]
105 | struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106 | 0644, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:423:2: note: expanded from macro 'CTLTBL_ENTRY_VNMR'
423 | __CTLTBL_ENTRY(__var, __name, __mode, \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424 | __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:313:18: note: expanded from macro '__CTLTBL_ENTRY'
313 | .proc_handler = __CTL_HANDLER(_handler), \
| ^~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:284:5: note: expanded from macro '__CTL_HANDLER'
284 | (0 ? \
| ^
285 | ((void)sizeof(char[__builtin_types_compatible_p( \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286 | __typeof__(__handler), proc_handler) ? \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287 | 1 : -1]), NULL) : \
| ~~~~~~~~~~~~~~~
288 | __handler)
| ~~~~~~~~~
kernel/sysctl-test.c:132:27: warning: pointer type mismatch ('void *' and 'int (*)(const struct ctl_table *, int, void *, size_t *, loff_t *)' (aka 'int (*)(const struct ctl_table *, int, void *, unsigned long *, long long *)')) [-Wpointer-type-mismatch]
132 | struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
133 | SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:423:2: note: expanded from macro 'CTLTBL_ENTRY_VNMR'
423 | __CTLTBL_ENTRY(__var, __name, __mode, \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424 | __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:313:18: note: expanded from macro '__CTLTBL_ENTRY'
313 | .proc_handler = __CTL_HANDLER(_handler), \
| ^~~~~~~~~~~~~~~~~~~~~~~
include/linux/sysctl.h:284:5: note: expanded from macro '__CTL_HANDLER'
284 | (0 ? \
| ^
285 | ((void)sizeof(char[__builtin_types_compatible_p( \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286 | __typeof__(__handler), proc_handler) ? \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287 | 1 : -1]), NULL) : \
| ~~~~~~~~~~~~~~~
288 | __handler)
| ~~~~~~~~~
kernel/sysctl-test.c:160:27: warning: pointer type mismatch ('void *' and 'int (*)(const struct ctl_table *, int, void *, size_t *, loff_t *)' (aka 'int (*)(const struct ctl_table *, int, void *, unsigned long *, long long *)')) [-Wpointer-type-mismatch]
160 | struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161 | SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~vim +23 kernel/sysctl-test.c
11
12 /*
13 * Test that proc_dointvec will not try to use a NULL .data field even when the
14 * length is non-zero.
15 */
16 static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
17 {
18 /*
19 * Here we are testing that proc_dointvec behaves correctly when
20 * we give it a NULL .data field. Normally this would point to a
21 * piece of memory where the value would be stored.
22 */
> 23 struct ctl_table null_data_table = CTLTBL_ENTRY_VNMHR(
24 SYSCTL_NULL, "foo", 0644, proc_dointvec,
25 SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
26 /*
27 * proc_dointvec expects a buffer in user space, so we allocate one. We
28 * also need to cast it to __user so sparse doesn't get mad.
29 */
30 void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
31 GFP_USER);
32 size_t len;
33 loff_t pos;
34
35 /*
36 * We don't care what the starting length is since proc_dointvec should
37 * not try to read because .data is NULL.
38 */
39 len = 1234;
40 KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
41 KUNIT_PROC_READ, buffer, &len,
42 &pos));
43 KUNIT_EXPECT_EQ(test, 0, len);
44
45 /*
46 * See above.
47 */
48 len = 1234;
49 KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
50 KUNIT_PROC_WRITE, buffer, &len,
51 &pos));
52 KUNIT_EXPECT_EQ(test, 0, len);
53 }
54 Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki