Skip to contentSkip to conversation

sysctl: refactor ctl_table initialization

RFC PATCH Superseded [email protected] 2 participants 4 messages Mar 2 – Mar 4 Proc sysctl

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index 2886fbceb5d6..a115cfff0cad 100644
@@ -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
index 9d3a666ffde1..121e743e7709 100644
@@ -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
#2 lore raw

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
index 92f94ea28957..410e6ce32ee1 100644
@@ -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
#3 lore raw

Re: [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