All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization
@ 2026-03-25 18:39 wen.yang
  2026-03-25 18:39 ` [RFC PATCH v5 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: wen.yang @ 2026-03-25 18:39 UTC (permalink / raw)
  To: Joel Granados, Kees Cook; +Cc: linux-fsdevel, linux-kernel, Wen Yang

From: Wen Yang <wen.yang@linux.dev>

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/202603050724.SZxrEyyu-lkp@intel.com/

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(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC PATCH v5 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros
  2026-03-25 18:39 [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization wen.yang
@ 2026-03-25 18:39 ` wen.yang
  2026-03-25 18:39 ` [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
  2026-04-07  7:28 ` [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization Joel Granados
  2 siblings, 0 replies; 5+ messages in thread
From: wen.yang @ 2026-03-25 18:39 UTC (permalink / raw)
  To: Joel Granados, Kees Cook; +Cc: linux-fsdevel, linux-kernel, Wen Yang

From: Wen Yang <wen.yang@linux.dev>

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.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 include/linux/sysctl.h | 307 +++++++++++++++++++++++++++++++++++++++++
 kernel/sysctl.c        |   2 +
 2 files changed, 309 insertions(+)

diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 2886fbceb5d6..f5aa7bc319da 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -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;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9d3a666ffde1..121e743e7709 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -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 */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
  2026-03-25 18:39 [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization wen.yang
  2026-03-25 18:39 ` [RFC PATCH v5 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
@ 2026-03-25 18:39 ` wen.yang
  2026-03-30 19:52   ` kernel test robot
  2026-04-07  7:28 ` [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization Joel Granados
  2 siblings, 1 reply; 5+ messages in thread
From: wen.yang @ 2026-03-25 18:39 UTC (permalink / raw)
  To: Joel Granados, Kees Cook; +Cc: linux-fsdevel, linux-kernel, Wen Yang

From: Wen Yang <wen.yang@linux.dev>

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.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/sysctl-test.c | 237 +++++++++++++++++++++++++------------------
 1 file changed, 138 insertions(+), 99 deletions(-)

diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 92f94ea28957..3b37bb0516e3 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -15,20 +15,14 @@
  */
 static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
 {
-	struct ctl_table null_data_table = {
-		.procname = "foo",
-		/*
-		 * Here we are testing that proc_dointvec behaves correctly when
-		 * we give it a NULL .data field. Normally this would point to a
-		 * piece of memory where the value would be stored.
-		 */
-		.data		= NULL,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	/*
+	 * Here we are testing that proc_dointvec behaves correctly when
+	 * we give it a NULL .data field. Normally this would point to a
+	 * piece of memory where the value would be stored.
+	 */
+	struct ctl_table null_data_table = 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),
 	{}
 };
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
  2026-03-25 18:39 ` [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
@ 2026-03-30 19:52   ` kernel test robot
  0 siblings, 0 replies; 5+ messages in thread
From: kernel test robot @ 2026-03-30 19:52 UTC (permalink / raw)
  To: wen.yang; +Cc: llvm, oe-kbuild-all

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/202603310301.Ogh6kR6N-lkp@intel.com/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/202603310301.Ogh6kR6N-lkp@intel.com/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 <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603310301.Ogh6kR6N-lkp@intel.com/

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	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization
  2026-03-25 18:39 [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization wen.yang
  2026-03-25 18:39 ` [RFC PATCH v5 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
  2026-03-25 18:39 ` [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
@ 2026-04-07  7:28 ` Joel Granados
  2 siblings, 0 replies; 5+ messages in thread
From: Joel Granados @ 2026-04-07  7:28 UTC (permalink / raw)
  To: Kees Cook; +Cc: Kees Cook, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3217 bytes --]

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

On Thu, Mar 26, 2026 at 02:39:14AM +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
> 
> 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/202603050724.SZxrEyyu-lkp@intel.com/
> 
> 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(-)
> 
> -- 
> 2.25.1
> 

-- 

Joel Granados

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-04-07  7:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-25 18:39 [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization wen.yang
2026-03-25 18:39 ` [RFC PATCH v5 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
2026-03-25 18:39 ` [RFC PATCH v5 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
2026-03-30 19:52   ` kernel test robot
2026-04-07  7:28 ` [RFC PATCH v5 0/2] sysctl: add CTLTBL_ENTRY_XXX() macros for ctl_table initialization Joel Granados

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.