Skip to contentSkip to conversation

sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization

RFC PATCH In review [email protected] 3 participants 10 messages Mar 25 – Nov 2

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v5 cover sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:

    https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
 kernel/sysctl-test.c   | 237 ++++++++++++++++++-------------
 kernel/sysctl.c        |   2 +
 3 files changed, 447 insertions(+), 99 deletions(-)
Signature · 1 line
2.25.1
#1 lore raw

RFC PATCH v5 1/2 sysctl: introduce CTLTBL_ENTRY_XXX() helper macros

From: Wen Yang <[email protected]>

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. This will all go away with the CTLTBL_ENTRY_XXX helper macros.

Add a family of CTLTBL_ENTRY_XXX() macros that reduce the boilerplate of initialising struct ctl_table. The suffix letters encode the accepted arguments: V derives .procname from the variable name, N takes an explicit name, M an explicit mode, H an explicit handler, R enables range checking via extra1/extra2, and L overrides .maxlen. Unspecified fields default to: .procname = #var, .data = &var, .maxlen = sizeof(var), and .proc_handler selected by _Generic() from the variable type.

Auto-dispatch maps int/unsigned int to proc_dointvec/proc_douintvec, long/unsigned long to proc_doulongvec_minmax, bool to proc_dobool, and u8 to proc_dou8vec_minmax. Range variants (R suffix) substitute the corresponding _minmax handler. char[] cannot be dispatched via _Generic(); use CTLTBL_ENTRY_VNMH() with proc_dostring explicitly. Pass SYSCTL_NULL as the variable argument for entries where .data must be NULL.

No functional change.

---
 include/linux/sysctl.h | 307 +++++++++++++++++++++++++++++++++++++++++
 kernel/sysctl.c        |   2 +
 2 files changed, 309 insertions(+)
include/linux/sysctl.h +307 -0
index 2886fbceb5d6..f5aa7bc319da 100644
@@ -175,6 +175,313 @@ 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 - sentinel instance used by SYSCTL_NULL
+ *
+ * Passed via SYSCTL_NULL to mark entries with no associated variable.
+ * Detected by _Generic() dispatch in CTLTBL_ENTRY_XXX().
+ */
+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
+ *
+ * Supported types: int, unsigned int, long, unsigned long, bool, u8.
+ * char arrays (strings) are not supported; use CTLTBL_ENTRY_VNMH() with
+ * proc_dostring instead.
+ *
+ * @__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,		\
+			bool         : proc_dobool,			\
+			u8           : proc_dou8vec_minmax,		\
+			default      :					\
+					0xdeadbeaf)
+
+/**
+ * __CTL_AUTO_HANDLER_RANGE - select proc_handler for a range-constrained entry
+ *
+ * Supported types: int, unsigned int, long, unsigned long, u8.
+ * bool is also accepted but proc_dobool ignores extra1/extra2 range bounds.
+ *
+ * @__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,		\
+			bool         : proc_dobool,			\
+			u8           : proc_dou8vec_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
+ *
+ * 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
+ *
+ * @__handler: proc_handler
+ */
+#define __CTL_HANDLER(__handler) \
+	(0 ? (typeof(proc_handler) *)0 : (__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 v5 2/2 sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()

In reply to [email protected]

From: Wen Yang <[email protected]>

Replace open-coded struct ctl_table initialisers with the CTLTBL_ENTRY_VNMR(), CTLTBL_ENTRY_VNMHR(), and CTLTBL_ENTRY_VNMHRL() macros introduced in the previous patch.

Add a parameterized KUnit test case exercising all CTLTBL_ENTRY_XXX() variants (V, VM, VMR, VN, VNM, VNMH) for int, u8, and bool types. char[] (proc_dostring) requires CTLTBL_ENTRY_VNMH() with an explicit handler since _Generic cannot dispatch on array types.

---
 kernel/sysctl-test.c | 237 +++++++++++++++++++++++++------------------
 1 file changed, 138 insertions(+), 99 deletions(-)
kernel/sysctl-test.c +138 -99
index 92f94ea28957..3b37bb0516e3 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,111 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
 	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
 }
 
+/*
+ * Test each CTLTBL_ENTRY_XXX() variant.  File-scope variables are required
+ * so that &var is a valid constant expression in a static initializer.
+ */
+static int  ctltbl_int;
+static u8   ctltbl_u8;
+static u8   ctltbl_u8_min, ctltbl_u8_max = 200;
+static bool ctltbl_bool;
+static char ctltbl_str[64];
+
+struct ctltbl_param {
+	const char       *desc;
+	struct ctl_table  table;    /* built by CTLTBL_ENTRY_XXX() */
+	struct ctl_table  expected; /* expected field values */
+};
+
+static const struct ctltbl_param ctltbl_cases[] = {
+	/* auto handler — int */
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_V_int",
+		.table = CTLTBL_ENTRY_V(ctltbl_int),
+		.expected = { .procname = "ctltbl_int", .mode = 0444,
+			      .data = &ctltbl_int, .maxlen = sizeof(int),
+			      .proc_handler = proc_dointvec },
+	},
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_VM_int",
+		.table = CTLTBL_ENTRY_VM(ctltbl_int, 0644),
+		.expected = { .procname = "ctltbl_int", .mode = 0644,
+			      .data = &ctltbl_int, .maxlen = sizeof(int),
+			      .proc_handler = proc_dointvec },
+	},
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_VMR_int",
+		.table = CTLTBL_ENTRY_VMR(ctltbl_int, 0644,
+					  SYSCTL_ZERO, SYSCTL_ONE_HUNDRED),
+		.expected = { .procname = "ctltbl_int", .mode = 0644,
+			      .data = &ctltbl_int, .maxlen = sizeof(int),
+			      .proc_handler = proc_dointvec_minmax },
+	},
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_VN_int",
+		.table = CTLTBL_ENTRY_VN(ctltbl_int, "my-sysctl"),
+		.expected = { .procname = "my-sysctl", .mode = 0444,
+			      .data = &ctltbl_int, .maxlen = sizeof(int),
+			      .proc_handler = proc_dointvec },
+	},
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_VNM_int",
+		.table = CTLTBL_ENTRY_VNM(ctltbl_int, "my-sysctl", 0644),
+		.expected = { .procname = "my-sysctl", .mode = 0644,
+			      .data = &ctltbl_int, .maxlen = sizeof(int),
+			      .proc_handler = proc_dointvec },
+	},
+	/* auto handler — u8 */
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_V_u8",
+		.table = CTLTBL_ENTRY_V(ctltbl_u8),
+		.expected = { .procname = "ctltbl_u8", .mode = 0444,
+			      .data = &ctltbl_u8, .maxlen = sizeof(u8),
+			      .proc_handler = proc_dou8vec_minmax },
+	},
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_VMR_u8",
+		.table = CTLTBL_ENTRY_VMR(ctltbl_u8, 0644,
+					  &ctltbl_u8_min, &ctltbl_u8_max),
+		.expected = { .procname = "ctltbl_u8", .mode = 0644,
+			      .data = &ctltbl_u8, .maxlen = sizeof(u8),
+			      .proc_handler = proc_dou8vec_minmax },
+	},
+	/* auto handler — bool */
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_V_bool",
+		.table = CTLTBL_ENTRY_V(ctltbl_bool),
+		.expected = { .procname = "ctltbl_bool", .mode = 0444,
+			      .data = &ctltbl_bool, .maxlen = sizeof(bool),
+			      .proc_handler = proc_dobool },
+	},
+	/* VNMH — char[] cannot be auto-dispatched by _Generic (each char[N]
+	 * is a distinct type); explicit handler is required for strings.
+	 * .maxlen must be sizeof(array), not sizeof(char *). */
+	{
+		.desc  = "sysctl_test_api_ctltbl_entry_VNMH_string",
+		.table = CTLTBL_ENTRY_VNMH(ctltbl_str, "foo", 0644,
+					   proc_dostring),
+		.expected = { .procname = "foo", .mode = 0644,
+			      .data = ctltbl_str, .maxlen = sizeof(ctltbl_str),
+			      .proc_handler = proc_dostring },
+	},
+};
+
+KUNIT_ARRAY_PARAM_DESC(ctltbl, ctltbl_cases, desc);
+
+static void sysctl_test_api_ctltbl_entry(struct kunit *test)
+{
+	const struct ctltbl_param *p = test->param_value;
+
+	KUNIT_EXPECT_STREQ(test, p->expected.procname, p->table.procname);
+	KUNIT_EXPECT_EQ(test, p->expected.mode, p->table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, p->expected.data, p->table.data);
+	KUNIT_EXPECT_EQ(test, p->expected.maxlen, p->table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, p->expected.proc_handler,
+			p->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 +416,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_PARAM(sysctl_test_api_ctltbl_entry, ctltbl_gen_params),
 	{}
 };
 
Signature · 1 line
2.25.1
#3 lore raw

Re: [RFC PATCH v5 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 kees/for-next/pstore kees/for-next/kspp linus/master v7.0-rc6 next-20260327] [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/20260330-010040
base:   https://git.kernel.org/pub/scm/linux/kernel/git/sysctl/sysctl.git sysctl-next
patch link:    https://lore.kernel.org/r/d2589e4a49044b0d857ee1b9da495ca73d19ae57.1774463505.git.wen.yang%40linux.dev
patch subject: [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20260331/[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/20260331/[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:386:12: warning: implicit conversion from 'size_t' (aka 'unsigned long') to 'int' changes value from 18446744073709551615 to -1 [-Wconstant-conversion]
     386 |                 .table = CTLTBL_ENTRY_VNMH(ctltbl_str, "foo", 0644,
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     387 |                                            proc_dostring),
         |                                            ~~~~~~~~~~~~~~
   include/linux/sysctl.h:413:2: note: expanded from macro 'CTLTBL_ENTRY_VNMH'
     413 |         __CTLTBL_ENTRY(__var, __name, __mode, __handler, NULL, NULL, -1)
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:316:18: note: expanded from macro '__CTLTBL_ENTRY'
     312 | #define __CTLTBL_ENTRY(_var, _name, _mode, _handler, _min, _max, _maxlen) \
         |                                                                           ~
     313 | { \
     314 |         .procname     = __CTL_PROCNAME(_name),                          \
     315 |         .data         = __CTL_DATA(_var),                               \
     316 |         .maxlen       = __CTL_MAXLEN(_var, _maxlen),                    \
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:277:5: note: expanded from macro '__CTL_MAXLEN'
     277 |                                 __SYSCTL_MAXLEN_VAR(__var, __maxlen)    \
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:264:21: note: expanded from macro '__SYSCTL_MAXLEN_VAR'
     264 |         ((__maxlen) >= 0 ? (size_t)(__maxlen) : (size_t)sizeof(__var))
         |                            ^~~~~~~~~~~~~~~~~~
   kernel/sysctl-test.c:376:12: warning: implicit conversion from 'size_t' (aka 'unsigned long') to 'int' changes value from 18446744073709551615 to -1 [-Wconstant-conversion]
     376 |                 .table = CTLTBL_ENTRY_V(ctltbl_bool),
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:347:2: note: expanded from macro 'CTLTBL_ENTRY_V'
     347 |         __CTLTBL_ENTRY(__var, #__var, 0444,                             \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     348 |                 __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:316:18: note: expanded from macro '__CTLTBL_ENTRY'
     312 | #define __CTLTBL_ENTRY(_var, _name, _mode, _handler, _min, _max, _maxlen) \
         |                                                                           ~
     313 | { \
     314 |         .procname     = __CTL_PROCNAME(_name),                          \
     315 |         .data         = __CTL_DATA(_var),                               \
     316 |         .maxlen       = __CTL_MAXLEN(_var, _maxlen),                    \
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:277:5: note: expanded from macro '__CTL_MAXLEN'
     277 |                                 __SYSCTL_MAXLEN_VAR(__var, __maxlen)    \
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:264:21: note: expanded from macro '__SYSCTL_MAXLEN_VAR'
     264 |         ((__maxlen) >= 0 ? (size_t)(__maxlen) : (size_t)sizeof(__var))
         |                            ^~~~~~~~~~~~~~~~~~
   kernel/sysctl-test.c:367:12: warning: implicit conversion from 'size_t' (aka 'unsigned long') to 'int' changes value from 18446744073709551615 to -1 [-Wconstant-conversion]
     367 |                 .table = CTLTBL_ENTRY_VMR(ctltbl_u8, 0644,
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     368 |                                           &ctltbl_u8_min, &ctltbl_u8_max),
         |                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:373:2: note: expanded from macro 'CTLTBL_ENTRY_VMR'
     373 |         __CTLTBL_ENTRY(__var, #__var, __mode,                           \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     374 |                 __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1)
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:316:18: note: expanded from macro '__CTLTBL_ENTRY'
     312 | #define __CTLTBL_ENTRY(_var, _name, _mode, _handler, _min, _max, _maxlen) \
         |                                                                           ~
     313 | { \
     314 |         .procname     = __CTL_PROCNAME(_name),                          \
     315 |         .data         = __CTL_DATA(_var),                               \
     316 |         .maxlen       = __CTL_MAXLEN(_var, _maxlen),                    \
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:277:5: note: expanded from macro '__CTL_MAXLEN'
     277 |                                 __SYSCTL_MAXLEN_VAR(__var, __maxlen)    \
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:264:21: note: expanded from macro '__SYSCTL_MAXLEN_VAR'
     264 |         ((__maxlen) >= 0 ? (size_t)(__maxlen) : (size_t)sizeof(__var))
         |                            ^~~~~~~~~~~~~~~~~~
   kernel/sysctl-test.c:360:12: warning: implicit conversion from 'size_t' (aka 'unsigned long') to 'int' changes value from 18446744073709551615 to -1 [-Wconstant-conversion]
     360 |                 .table = CTLTBL_ENTRY_V(ctltbl_u8),
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:347:2: note: expanded from macro 'CTLTBL_ENTRY_V'
     347 |         __CTLTBL_ENTRY(__var, #__var, 0444,                             \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     348 |                 __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:316:18: note: expanded from macro '__CTLTBL_ENTRY'
     312 | #define __CTLTBL_ENTRY(_var, _name, _mode, _handler, _min, _max, _maxlen) \
         |                                                                           ~
     313 | { \
     314 |         .procname     = __CTL_PROCNAME(_name),                          \
     315 |         .data         = __CTL_DATA(_var),                               \
     316 |         .maxlen       = __CTL_MAXLEN(_var, _maxlen),                    \
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:277:5: note: expanded from macro '__CTL_MAXLEN'
     277 |                                 __SYSCTL_MAXLEN_VAR(__var, __maxlen)    \
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:264:21: note: expanded from macro '__SYSCTL_MAXLEN_VAR'
     264 |         ((__maxlen) >= 0 ? (size_t)(__maxlen) : (size_t)sizeof(__var))
         |                            ^~~~~~~~~~~~~~~~~~
   kernel/sysctl-test.c:352:12: warning: implicit conversion from 'size_t' (aka 'unsigned long') to 'int' changes value from 18446744073709551615 to -1 [-Wconstant-conversion]
     352 |                 .table = CTLTBL_ENTRY_VNM(ctltbl_int, "my-sysctl", 0644),
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:398:2: note: expanded from macro 'CTLTBL_ENTRY_VNM'
     398 |         __CTLTBL_ENTRY(__var, __name, __mode,                           \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     399 |                 __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/sysctl.h:316:18: note: expanded from macro '__CTLTBL_ENTRY'
     312 | #define __CTLTBL_ENTRY(_var, _name, _mode, _handler, _min, _max, _maxlen) \
         |                                                                           ~

vim +386 kernel/sysctl-test.c

318
319 static const struct ctltbl_param ctltbl_cases[] = {
320 /* auto handler — int */
321 {
322 .desc = "sysctl_test_api_ctltbl_entry_V_int",
323 .table = CTLTBL_ENTRY_V(ctltbl_int),
324 .expected = { .procname = "ctltbl_int", .mode = 0444,
325 .data = &ctltbl_int, .maxlen = sizeof(int),
326 .proc_handler = proc_dointvec },
327 },
328 {
329 .desc = "sysctl_test_api_ctltbl_entry_VM_int",
330 .table = CTLTBL_ENTRY_VM(ctltbl_int, 0644),
331 .expected = { .procname = "ctltbl_int", .mode = 0644,
332 .data = &ctltbl_int, .maxlen = sizeof(int),
333 .proc_handler = proc_dointvec },
334 },
335 {
336 .desc = "sysctl_test_api_ctltbl_entry_VMR_int",
337 .table = CTLTBL_ENTRY_VMR(ctltbl_int, 0644,
338 SYSCTL_ZERO, SYSCTL_ONE_HUNDRED),
339 .expected = { .procname = "ctltbl_int", .mode = 0644,
340 .data = &ctltbl_int, .maxlen = sizeof(int),
341 .proc_handler = proc_dointvec_minmax },
342 },
343 {
344 .desc = "sysctl_test_api_ctltbl_entry_VN_int",
345 .table = CTLTBL_ENTRY_VN(ctltbl_int, "my-sysctl"),
346 .expected = { .procname = "my-sysctl", .mode = 0444,
347 .data = &ctltbl_int, .maxlen = sizeof(int),
348 .proc_handler = proc_dointvec },
349 },
350 {
351 .desc = "sysctl_test_api_ctltbl_entry_VNM_int",
352 .table = CTLTBL_ENTRY_VNM(ctltbl_int, "my-sysctl", 0644),
353 .expected = { .procname = "my-sysctl", .mode = 0644,
354 .data = &ctltbl_int, .maxlen = sizeof(int),
355 .proc_handler = proc_dointvec },
356 },
357 /* auto handler — u8 */
358 {
359 .desc = "sysctl_test_api_ctltbl_entry_V_u8",
360 .table = CTLTBL_ENTRY_V(ctltbl_u8),
361 .expected = { .procname = "ctltbl_u8", .mode = 0444,
362 .data = &ctltbl_u8, .maxlen = sizeof(u8),
363 .proc_handler = proc_dou8vec_minmax },
364 },
365 {
366 .desc = "sysctl_test_api_ctltbl_entry_VMR_u8",
367 .table = CTLTBL_ENTRY_VMR(ctltbl_u8, 0644,
368 &ctltbl_u8_min, &ctltbl_u8_max),
369 .expected = { .procname = "ctltbl_u8", .mode = 0644,
370 .data = &ctltbl_u8, .maxlen = sizeof(u8),
371 .proc_handler = proc_dou8vec_minmax },
372 },
373 /* auto handler — bool */
374 {
375 .desc = "sysctl_test_api_ctltbl_entry_V_bool",
376 .table = CTLTBL_ENTRY_V(ctltbl_bool),
377 .expected = { .procname = "ctltbl_bool", .mode = 0444,
378 .data = &ctltbl_bool, .maxlen = sizeof(bool),
379 .proc_handler = proc_dobool },
380 },
381 /* VNMH — char[] cannot be auto-dispatched by _Generic (each char[N]
382 * is a distinct type); explicit handler is required for strings.
383 * .maxlen must be sizeof(array), not sizeof(char *). */
384 {
385 .desc = "sysctl_test_api_ctltbl_entry_VNMH_string",

 > 386			.table = CTLTBL_ENTRY_VNMH(ctltbl_str, "foo", 0644,

387 proc_dostring),
388 .expected = { .procname = "foo", .mode = 0644,
389 .data = ctltbl_str, .maxlen = sizeof(ctltbl_str),
390 .proc_handler = proc_dostring },
391 },
392 };
393

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
maintainer
#4 lore raw
In reply to [email protected]

Hey Kees

I have been trying to avoid having to do a treewide change everytime that the sysctl ctl_table struct are changed, it creates unnecessary churn and requires a "review" when there is really no need for it.

This series tries to move the definitions of the struct elements into a macro within sysctl. I was wondering if you see any red flags that should be address before this moves forward.

Best

Quoted 60 lines · On Thu, Mar 26, 2026 at 02:39:14AM +0800, [email protected] wrote:

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:

    https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
 kernel/sysctl-test.c   | 237 ++++++++++++++++++-------------
 kernel/sysctl.c        |   2 +
 3 files changed, 447 insertions(+), 99 deletions(-)
Signature · 1 line
2.25.1
Signature · 1 line
Joel Granados
author
#5 lore raw
In reply to [email protected]

Hi Kees, H jioel,

Gentle ping on this series. Please let me know if there are any concerns or if further changes are needed.

The series is available at:
https://lore.kernel.org/all/x5stdwwsfp2h22zyexn5ycbxnkyc4ioxscninxsa6q7lf6djqr@jkuppwwqubkp/

Thanks for your time,
Wen

Quoted 57 lines · On 3/26/26 02:39, [email protected] wrote:

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:

     https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
  kernel/sysctl-test.c   | 237 ++++++++++++++++++-------------
  kernel/sysctl.c        |   2 +
  3 files changed, 447 insertions(+), 99 deletions(-)
maintainer
#6 lore raw

Hey Wen

Sorry for not getting back to you sooner.

IMO, this is approach is the way to go to bring back the control of the sysctl variables into the sysctl subsys files. Would like to get another set of eyes on it before committing to it though.

I think it has not gotten too much attention because it does not touch any other subsystem. Would you consider adding one subsys to the series? Something like implementing it for the users in arch/ or fs/ or drivers/.

Also, this might be a signal to park this temporarily and let it simmer until more ppl are annoyed at making changes to the sysctl API.

Best

Quoted 70 lines · Wen Yang

Hi Kees, H jioel,

Gentle ping on this series. Please let me know if there are any concerns or if further changes are needed.

The series is available at:
https://lore.kernel.org/all/x5stdwwsfp2h22zyexn5ycbxnkyc4ioxscninxsa6q7lf6djqr@jkuppwwqubkp/

Thanks for your time,
Wen

Quoted 57 lines · On 3/26/26 02:39, [email protected] wrote:

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:

     https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
  kernel/sysctl-test.c   | 237 ++++++++++++++++++-------------
  kernel/sysctl.c        |   2 +
  3 files changed, 447 insertions(+), 99 deletions(-)
Signature · 1 line
Joel Granados
author
#7 lore raw
Quoted 13 lines · Joel Granados

Hey Wen

Sorry for not getting back to you sooner.

IMO, this is approach is the way to go to bring back the control of the sysctl variables into the sysctl subsys files. Would like to get another set of eyes on it before committing to it though.

I think it has not gotten too much attention because it does not touch any other subsystem. Would you consider adding one subsys to the series? Something like implementing it for the users in arch/ or fs/ or drivers/.

Quoted 5 lines

Also, this might be a signal to park this temporarily and let it simmer until more ppl are annoyed at making changes to the sysctl API.

Best

Hi Joel,

Apologies for the long delay, and thank you for your helpful feedback.

I've been tied up with other commitments, but can now return to this

work and prepare v3 based on your suggestions.

Given the time that has passed, I'd appreciate any updated advice or

related developments I should take into account.

Thanks again for your time and patience.

Signature · 2 lines
Best regards, 
                                                           Wen
Quoted 72 lines · Wen Yang
Quoted 70 lines

Hi Kees, H jioel,

Gentle ping on this series. Please let me know if there are any concerns or if further changes are needed.

The series is available at:
https://lore.kernel.org/all/x5stdwwsfp2h22zyexn5ycbxnkyc4ioxscninxsa6q7lf6djqr@jkuppwwqubkp/

Thanks for your time,
Wen

Quoted 57 lines · On 3/26/26 02:39, [email protected] wrote:

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:
https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
   kernel/sysctl-test.c   | 237 ++++++++++++++++++-------------
   kernel/sysctl.c        |   2 +
   3 files changed, 447 insertions(+), 99 deletions(-)
maintainer
#8 lore raw
Quoted 28 lines · Wen Yang
Quoted 13 lines · Joel Granados

Hey Wen

Sorry for not getting back to you sooner.

IMO, this is approach is the way to go to bring back the control of the sysctl variables into the sysctl subsys files. Would like to get another set of eyes on it before committing to it though.

I think it has not gotten too much attention because it does not touch any other subsystem. Would you consider adding one subsys to the series? Something like implementing it for the users in arch/ or fs/ or drivers/.

Quoted 5 lines

Also, this might be a signal to park this temporarily and let it simmer until more ppl are annoyed at making changes to the sysctl API.

Best

Hi Joel,

Apologies for the long delay, and thank you for your helpful feedback.

I've been tied up with other commitments, but can now return to this

Quoted 7 lines

work and prepare v3 based on your suggestions.

Given the time that has passed, I'd appreciate any updated advice or

related developments I should take into account.

There is now more clarity that the use of MACROS like we had discussed for this series is probably not the way to go. In [1] it is clear that the overuse of macros will probably be rejected once it gets upstream. So for now I would let this implementation go.

Best

[1] https://lore.kernel.org/all/CAHk-=wjUmLrJd4KM4xvJHUN4P0r8oSu5LyvG4JipnhxizBrE9Q@mail.gmail.com

Quoted 82 lines

Thanks again for your time and patience.

Signature · 1 line
Best regards,                                                            Wen
Quoted 72 lines · Wen Yang
Quoted 70 lines

Hi Kees, H jioel,

Gentle ping on this series. Please let me know if there are any concerns or if further changes are needed.

The series is available at:
https://lore.kernel.org/all/x5stdwwsfp2h22zyexn5ycbxnkyc4ioxscninxsa6q7lf6djqr@jkuppwwqubkp/

Thanks for your time,
Wen

Quoted 57 lines · On 3/26/26 02:39, [email protected] wrote:

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:
https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
   kernel/sysctl-test.c   | 237 ++++++++++++++++++-------------
   kernel/sysctl.c        |   2 +
   3 files changed, 447 insertions(+), 99 deletions(-)
author
#9 lore raw
Quoted 33 lines · Joel Granados
Quoted 32 lines · Wen Yang
Quoted 13 lines · Joel Granados

Hey Wen

Sorry for not getting back to you sooner.

IMO, this is approach is the way to go to bring back the control of the sysctl variables into the sysctl subsys files. Would like to get another set of eyes on it before committing to it though.

I think it has not gotten too much attention because it does not touch any other subsystem. Would you consider adding one subsys to the series? Something like implementing it for the users in arch/ or fs/ or drivers/.

Quoted 5 lines

Also, this might be a signal to park this temporarily and let it simmer until more ppl are annoyed at making changes to the sysctl API.

Best

Hi Joel,

Apologies for the long delay, and thank you for your helpful feedback.

I've been tied up with other commitments, but can now return to this

work and prepare v3 based on your suggestions.

Quoted 9 lines
Quoted 3 lines

Given the time that has passed, I'd appreciate any updated advice or

related developments I should take into account.

There is now more clarity that the use of MACROS like we had discussed for this series is probably not the way to go. In [1] it is clear that the overuse of macros will probably be rejected once it gets upstream. So for now I would let this implementation go.

Hi Joel,

It's okay, we'll keep trying other approaches.
Since it has existed for so many years, it's indeed hard to deal with,
but we won't give up. As we increasingly adopt Linux in autonomous driving, we will pay closer attention to information security issues of this kind. We would greatly appreciate your continued guidance. Could you take a look at this RFC series and share your thoughts on whether the approach makes sense?

https://lore.kernel.org/all/[email protected]/

Thanks,
Wen

Quoted 85 lines

Best

[1] https://lore.kernel.org/all/CAHk-=wjUmLrJd4KM4xvJHUN4P0r8oSu5LyvG4JipnhxizBrE9Q@mail.gmail.com

Quoted 82 lines

Thanks again for your time and patience.

Signature · 1 line
Best regards,                                                            Wen
Quoted 72 lines · Wen Yang
Quoted 70 lines

Hi Kees, H jioel,

Gentle ping on this series. Please let me know if there are any concerns or if further changes are needed.

The series is available at:
https://lore.kernel.org/all/x5stdwwsfp2h22zyexn5ycbxnkyc4ioxscninxsa6q7lf6djqr@jkuppwwqubkp/

Thanks for your time,
Wen

Quoted 57 lines · On 3/26/26 02:39, [email protected] wrote:

From: Wen Yang <[email protected]>

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. With CTLTBL_ENTRY_XXX(), future structural changes to struct ctl_table need only update the macro definitions.

Conversion of existing call sites will proceed incrementally from kernel/sysctl.c outward, rather than as a treewide sweep.

This series:
1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h, using _Generic() for automatic proc_handler selection, auto address-of, auto maxlen via sizeof(), and compile-time validation. Supported types: int, unsigned int, long, unsigned long, bool, u8.
2. Converts kernel/sysctl-test.c as a demonstration, adding a parameterized KUnit test covering all macro variants (V, VM, VMR, VN, VNM, VNMH) across int, u8, bool, and char[] types.

Based on discussion and suggestions from:
[1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html
[2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/

---
Changes in v5:
- Extend __CTL_AUTO_HANDLER to support bool and u8
- Replace individual test functions with a single parameterized KUnit test (KUNIT_CASE_PARAM) covering all variants and types
- Use struct ctl_table as the expected-value container in the param struct, eliminating custom expected_* fields

Changes in v4:
- Fix Wpointer-type-mismatch warnings detected by lkp:
https://lore.kernel.org/oe-kbuild-all/[email protected]/

Changes in v3:
- Replace the unique macro with "capital letter approach"
- Reduce the name further
https://lore.kernel.org/all/rn4rsazh7kxf5byq65vw2phyqgzvwm3scczu3l5h2r4aqit2r6@znlpb24z2zuo/

Changes in v2:
- Add lvalue check, handler type check, etc.
https://lore.kernel.org/all/xptwb3uwbzposd4xf7khj52ifv4tchcjdgllhv7aabi6d7wgef@2msurl564v53/

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 | 307 +++++++++++++++++++++++++++++++++++++++++
kernel/sysctl-test.c | 237 ++++++++++++++++++-------------
kernel/sysctl.c | 2 +
3 files changed, 447 insertions(+), 99 deletions(-)