public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization
@ 2026-03-17 17:36 wen.yang
  2026-03-17 17:36 ` [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: wen.yang @ 2026-03-17 17:36 UTC (permalink / raw)
  To: Joel Granados; +Cc: linux-kernel, Wen Yang

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

The main motivation for this series is to avoid treewide patch series.
Historically, changes to how struct ctl_table entries are initialized
(e.g. removing the child field, const-qualifying ctl_table) required
touching hundreds of files across all subsystems. Such series require
the attention of many maintainers, sometimes need to be pulled into
mainline in a special way, and create lots of unnecessary churn.

By introducing a family of CTLTBL_ENTRY_XXX() helper macros in
<linux/sysctl.h>, any future structural changes to struct ctl_table can
be handled in one place — the macro definition — without requiring a
treewide sweep of all callers. Individual subsystem maintainers can also
migrate their sysctl tables to use the macros at their own pace.

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

This series:
 1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h,
    allowing callers to initialize struct ctl_table entries indirectly
    via the macro instead of assigning each field directly. The macros
    use _Generic() for automatic type detection and proc_handler
    selection, provide smart defaults (auto address-of, auto maxlen via
    sizeof), and include compile-time validation of name, mode, and
    handler.
 2. Converts kernel/sysctl-test.c to use the new macros as a
    demonstration, replacing direct struct ctl_table field assignments
    with CTLTBL_ENTRY_XXX() calls. Four new tests are added to cover the
    previously untested variants (CTLTBL_ENTRY_V, VN, VNM, VNMH), bringing
    the total to 16 passing tests.

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

-- 
2.25.1


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

* [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros
  2026-03-17 17:36 [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization wen.yang
@ 2026-03-17 17:36 ` wen.yang
  2026-03-17 17:36 ` [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
  2026-03-19 14:12 ` [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization Joel Granados
  2 siblings, 0 replies; 7+ messages in thread
From: wen.yang @ 2026-03-17 17:36 UTC (permalink / raw)
  To: Joel Granados; +Cc: linux-kernel, Wen Yang

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

Add a family of CTLTBL_ENTRY_XXX() macros to simplify struct ctl_table
initialization. Each macro variant covers a different use case, using
_Generic() for automatic type detection and proc_handler selection,
with sensible defaults to reduce boilerplate and potential errors.

The following macro variants are introduced:

  CTLTBL_ENTRY_V(__var)
    - Uses variable name as procname, mode 0444,
      and auto-selected handler based on variable type

  CTLTBL_ENTRY_VM(__var, __mode)
    - Uses variable name as procname, custom mode,
      and auto-selected handler based on variable type

  CTLTBL_ENTRY_VMR(__var, __mode, __min, __max)
    - Uses variable name as procname, custom mode,
      and auto-selected range-checking handler based on variable type

  CTLTBL_ENTRY_VN(__var, __name)
    - Custom procname, mode 0444,
      and auto-selected handler based on variable type

  CTLTBL_ENTRY_VNM(__var, __name, __mode)
    - Custom procname and mode,
      and auto-selected handler based on variable type

  CTLTBL_ENTRY_VNMH(__var, __name, __mode, __handler)
    - Custom procname, mode, and proc_handler

  CTLTBL_ENTRY_VNMR(__var, __name, __mode, __min, __max)
    - Auto-selected range-checking handler based on variable type

  CTLTBL_ENTRY_VNMHR(__var, __name, __mode, __handler, __min, __max)
    - Custom handler with extra1/extra2 range bounds

  CTLTBL_ENTRY_VNMHRL(__var, __name, __mode, __handler, __min, __max, __maxlen)
    - Full control including explicit maxlen override

All variants share the following features:
- Automatic type detection via _Generic(): supports int, unsigned int,
  long, unsigned long; selects the appropriate proc_dointvec,
  proc_douintvec, or proc_doulongvec_minmax handler automatically
- Auto address-of: the macro takes the address of the variable, so
  callers do not need to pass &var explicitly
- Auto maxlen: uses sizeof(var) when maxlen is not explicitly provided
- SYSCTL_NULL marker: use SYSCTL_NULL as the variable argument for
  entries where .data should be NULL
- Compile-time validation: NAME must be a string literal; MODE and
  HANDLER are checked for type compatibility

No functional change intended.

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

diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 2886fbceb5d6..840a3fba5fa9 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -175,6 +175,300 @@ struct ctl_table {
 	void *extra2;
 } __randomize_layout;
 
+/**
+ * struct _sysctl_null_type - sentinel type for variable-less entries
+ */
+struct _sysctl_null_type { char __dummy; };
+
+/**
+ * _sysctl_null_marker - unique instance of the sentinel type
+ *
+ * Define once per link unit:
+ *   const struct _sysctl_null_type _sysctl_null_marker;
+ */
+extern const struct _sysctl_null_type _sysctl_null_marker;
+
+/**
+ * SYSCTL_NULL - pass as __var for entries with no associated variable
+ */
+#define SYSCTL_NULL (_sysctl_null_marker)
+
+/**
+ * __CTL_AUTO_HANDLER - select proc_handler based on the variable type
+ * @__var: kernel variable (value, not address), or SYSCTL_NULL
+ */
+#define __CTL_AUTO_HANDLER(__var)					\
+	_Generic((__var),						\
+			int : proc_dointvec,				\
+			unsigned int : proc_douintvec,			\
+			long : proc_doulongvec_minmax,			\
+			unsigned long : proc_doulongvec_minmax,		\
+			default :					\
+				  0xdeadbeaf)
+
+/**
+ * __CTL_AUTO_HANDLER_RANGE - select proc_handler for a range-constrained entry
+ * @__var: kernel variable (value, not address), or SYSCTL_NULL
+ */
+#define __CTL_AUTO_HANDLER_RANGE(__var)					\
+		_Generic((__var),					\
+			int : proc_dointvec_minmax,			\
+			unsigned int : proc_douintvec_minmax,		\
+			long : proc_doulongvec_minmax,			\
+			unsigned long : proc_doulongvec_minmax,		\
+			default :					\
+				 0xdeadbeaf)
+
+/**
+ * __CTL_PROCNAME - validate and return the procname string
+ *
+ * "" __name "" enforces a string-literal argument at compile time.
+ *
+ * @__name: procname string literal
+ */
+#define __CTL_PROCNAME(__name)    ("" __name "")
+
+/**
+ * __CTL_DATA - assert __var is addressable; return its address
+ *
+ * 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] 7+ messages in thread

* [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
  2026-03-17 17:36 [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization wen.yang
  2026-03-17 17:36 ` [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
@ 2026-03-17 17:36 ` wen.yang
  2026-03-19 14:15   ` Joel Granados
  2026-03-19 14:12 ` [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization Joel Granados
  2 siblings, 1 reply; 7+ messages in thread
From: wen.yang @ 2026-03-17 17:36 UTC (permalink / raw)
  To: Joel Granados; +Cc: linux-kernel, Wen Yang

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

Convert all struct ctl_table initializations in kernel/sysctl-test.c
from direct field assignment to indirect initialization via the
CTLTBL_ENTRY_XXX() macros introduced in the previous patch. This
demonstrates the macros in real usage and validates their correctness
against the existing test suite.

The following macro variants are exercised:

- CTLTBL_ENTRY_VNMR(var, name, mode, min, max)
  Used for the majority of test cases that initialize a standard int
  entry with range checking. The macro automatically selects the
  range-checking handler (proc_dointvec_minmax for int) based on the
  variable type and computes maxlen via sizeof():

    Before:
      struct ctl_table table = {
          .procname     = "foo",
          .data         = &data,
          .maxlen       = sizeof(int),
          .mode         = 0644,
          .proc_handler = proc_dointvec,
          .extra1       = SYSCTL_ZERO,
          .extra2       = SYSCTL_ONE_HUNDRED,
      };

    After:
      struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
              SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);

- CTLTBL_ENTRY_VNMHR(var, name, mode, handler, min, max)
  Used for the NULL data test case, passing SYSCTL_NULL as the data
  argument and proc_dointvec as the explicit handler:

    Before:
      struct ctl_table null_data_table = {
          .procname     = "foo",
          .data         = NULL,
          .maxlen       = sizeof(int),
          .mode         = 0644,
          .proc_handler = proc_dointvec,
          .extra1       = SYSCTL_ZERO,
          .extra2       = SYSCTL_ONE_HUNDRED,
      };

    After:
      struct ctl_table null_data_table = CTLTBL_ENTRY_VNMHR(
              SYSCTL_NULL, "foo", 0644, proc_dointvec,
              SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);

- CTLTBL_ENTRY_VNMHRL(var, name, mode, handler, min, max, maxlen)
  Used for the maxlen=0 test case, passing an explicit maxlen of 0 to
  override the default sizeof()-based value:

    Before:
      struct ctl_table data_maxlen_unset_table = {
          .procname     = "foo",
          .data         = &data,
          .maxlen       = 0,
          .mode         = 0644,
          .proc_handler = proc_dointvec,
          .extra1       = SYSCTL_ZERO,
          .extra2       = SYSCTL_ONE_HUNDRED,
      };

    After:
      struct ctl_table data_maxlen_unset_table = CTLTBL_ENTRY_VNMHRL(
              data, "foo", 0644, proc_dointvec,
              SYSCTL_ZERO, SYSCTL_ONE_HUNDRED, 0);

New tests are added to cover the remaining macro variants:

- CTLTBL_ENTRY_V(var)
  Verifies that the macro initializes .procname to the variable name,
  .mode to 0444, .data to &var, .maxlen to sizeof(var), and selects
  proc_dointvec as the handler for an int variable.

- CTLTBL_ENTRY_VM(var, mode)
  Verifies that the macro initializes .procname to the variable name,
  .mode to 0644, .data to &var, .maxlen to sizeof(var), and selects
  proc_dointvec as the handler for an int variable.

- CTLTBL_ENTRY_VMR(var, mode, min, max)
  Verifies that the macro initializes .procname to the variable name,
  .mode to 0644, .data to &var, .maxlen to sizeof(var),
  and auto-selecting proc_dointvec_minmax as the handler.

- CTLTBL_ENTRY_VN(var, name)
  Verifies that the macro initializes .procname to the custom name while
  keeping .mode at 0444 and auto-selecting the handler.

- CTLTBL_ENTRY_VNM(name, var, mode)
  Verifies that the macro initializes .procname and .mode as specified
  while auto-selecting the handler.

- CTLTBL_ENTRY_VNMH(var, name, mode, handler)
  Verifies that the macro initializes all fields including an explicitly
  provided proc_handler.

All tests pass:

[13:53:49] ================ sysctl_test (16 subtests) =================
[13:53:49] [PASSED] sysctl_test_api_dointvec_null_tbl_data
[13:53:49] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
[13:53:49] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
[13:53:49] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
[13:53:49] [PASSED] sysctl_test_dointvec_read_happy_single_positive
[13:53:49] [PASSED] sysctl_test_dointvec_read_happy_single_negative
[13:53:49] [PASSED] sysctl_test_dointvec_write_happy_single_positive
[13:53:49] [PASSED] sysctl_test_dointvec_write_happy_single_negative
[13:53:49] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
[13:53:49] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_v
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vm
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vmr
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vn
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vnm
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vnmh
[13:53:49] =================== [PASSED] sysctl_test ===================
[13:53:49] ============================================================
[13:53:49] Testing complete. Ran 16 tests: passed: 16

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

diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 92f94ea28957..410e6ce32ee1 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,109 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
 	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
 }
 
+/*
+ * Test CTLTBL_ENTRY_V: variable name used as procname, mode 0444,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_v(struct kunit *test)
+{
+	int foo = 123;
+	struct ctl_table table = CTLTBL_ENTRY_V(foo);
+
+	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+	KUNIT_EXPECT_EQ(test, (umode_t)0444, table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, &foo, table.data);
+	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+			table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VM: variable name used as procname, custom mode,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vm(struct kunit *test)
+{
+	int foo = 123;
+	struct ctl_table table = CTLTBL_ENTRY_VM(foo, 0644);
+
+	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+	KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, &foo, table.data);
+	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+			table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VMR: variable name used as procname, custom mode,
+ * auto-selected range checkint handler (proc_dointvec_minmax for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vmr(struct kunit *test)
+{
+	int foo = 123;
+	struct ctl_table table = CTLTBL_ENTRY_VMR(foo, 0644, SYSCTL_ZERO,
+						  SYSCTL_ONE_HUNDRED);
+
+	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+	KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, &foo, table.data);
+	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec_minmax,
+			table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VN: custom procname, mode 0444,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vn(struct kunit *test)
+{
+	int data = 0;
+	struct ctl_table table = CTLTBL_ENTRY_VN(data, "foo");
+
+	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+	KUNIT_EXPECT_EQ(test, (umode_t)0444, table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
+	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+			table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VNM: custom procname and mode,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vnm(struct kunit *test)
+{
+	int data = 0;
+	struct ctl_table table = CTLTBL_ENTRY_VNM(data, "foo", 0644);
+
+	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+	KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
+	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+			table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VNMH: custom procname, mode and explicit handler.
+ */
+static void sysctl_test_api_ctltbl_entry_vnmh(struct kunit *test)
+{
+	int data = 0;
+	struct ctl_table table = CTLTBL_ENTRY_VNMH(data, "foo", 0644,
+			proc_dointvec);
+
+	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+	KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+	KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
+	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+			table.proc_handler);
+}
+
 static struct kunit_case sysctl_test_cases[] = {
 	KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
 	KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
@@ -378,6 +414,12 @@ static struct kunit_case sysctl_test_cases[] = {
 	KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
 	KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
 	KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
+	KUNIT_CASE(sysctl_test_api_ctltbl_entry_v),
+	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vm),
+	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vmr),
+	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vn),
+	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnm),
+	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnmh),
 	{}
 };
 
-- 
2.25.1


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

* Re: [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization
  2026-03-17 17:36 [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization wen.yang
  2026-03-17 17:36 ` [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
  2026-03-17 17:36 ` [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
@ 2026-03-19 14:12 ` Joel Granados
  2026-03-19 14:32   ` Joel Granados
  2 siblings, 1 reply; 7+ messages in thread
From: Joel Granados @ 2026-03-19 14:12 UTC (permalink / raw)
  To: wen.yang; +Cc: linux-kernel

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

Hey Wen

Comments inline

On Wed, Mar 18, 2026 at 01:36:13AM +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
> 
> The main motivation for this series is to avoid treewide patch series.
> Historically, changes to how struct ctl_table entries are initialized
> (e.g. removing the child field, const-qualifying ctl_table) required
> touching hundreds of files across all subsystems. Such series require
> the attention of many maintainers, sometimes need to be pulled into
> mainline in a special way, and create lots of unnecessary churn.
LGTM: Clear motivation.

> 
> By introducing a family of CTLTBL_ENTRY_XXX() helper macros in
> <linux/sysctl.h>, any future structural changes to struct ctl_table can
> be handled in one place — the macro definition — without requiring a
> treewide sweep of all callers. Individual subsystem maintainers can also
> migrate their sysctl tables to use the macros at their own pace.
I think this paragraph is redundant; I would remove it. And just add
"This will all go away with the CTLTBL_ENTRY_XXX helper macros" as the
last sentence in the first paragraph.

Migration strategy
==================
You touched on this when you mentioned that the maintainers can migrate
to the new macro at their pace. In my experience this will never happen
if you give the responsibility to the subsys maintainers (They are busy
with other matters). I think the more successful strategy is to lead the
conversion from sysctl.

> 
> Based on discussion and suggestions from:
> [1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
> [2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/
> 
> This series:
>  1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h,
>     allowing callers to initialize struct ctl_table entries indirectly
>     via the macro instead of assigning each field directly. The macros
>     use _Generic() for automatic type detection and proc_handler
>     selection, provide smart defaults (auto address-of, auto maxlen via
>     sizeof), and include compile-time validation of name, mode, and
>     handler.
>  2. Converts kernel/sysctl-test.c to use the new macros as a
>     demonstration, replacing direct struct ctl_table field assignments
>     with CTLTBL_ENTRY_XXX() calls. Four new tests are added to cover the
>     previously untested variants (CTLTBL_ENTRY_V, VN, VNM, VNMH), bringing
>     the total to 16 passing tests.
> 
> ---
> 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 | 294 +++++++++++++++++++++++++++++++++++++++++
>  kernel/sysctl-test.c   | 240 +++++++++++++++++++--------------
>  kernel/sysctl.c        |   2 +
>  3 files changed, 437 insertions(+), 99 deletions(-)
> 
> -- 
> 2.25.1
> 

Best

-- 

Joel Granados

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

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

* Re: [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
  2026-03-17 17:36 ` [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
@ 2026-03-19 14:15   ` Joel Granados
  0 siblings, 0 replies; 7+ messages in thread
From: Joel Granados @ 2026-03-19 14:15 UTC (permalink / raw)
  To: wen.yang; +Cc: linux-kernel

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

On Wed, Mar 18, 2026 at 01:36:15AM +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
> 
> Convert all struct ctl_table initializations in kernel/sysctl-test.c
> from direct field assignment to indirect initialization via the
> CTLTBL_ENTRY_XXX() macros introduced in the previous patch. This
> demonstrates the macros in real usage and validates their correctness
> against the existing test suite.
> 
> The following macro variants are exercised:

...

> + * auto-selected handler (proc_dointvec for int).
> + */
> +static void sysctl_test_api_ctltbl_entry_vnm(struct kunit *test)
> +{
> +	int data = 0;
> +	struct ctl_table table = CTLTBL_ENTRY_VNM(data, "foo", 0644);
> +
> +	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
> +	KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
> +	KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
> +	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
> +	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
> +			table.proc_handler);
> +}
> +
> +/*
> + * Test CTLTBL_ENTRY_VNMH: custom procname, mode and explicit handler.
> + */
> +static void sysctl_test_api_ctltbl_entry_vnmh(struct kunit *test)
> +{
> +	int data = 0;
> +	struct ctl_table table = CTLTBL_ENTRY_VNMH(data, "foo", 0644,
> +			proc_dointvec);
> +
> +	KUNIT_EXPECT_STREQ(test, "foo", table.procname);
> +	KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
> +	KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
> +	KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
> +	KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
> +			table.proc_handler);
> +}
> +

Have you checked if this approach supports u8, bool and char (string).
I see that in all your tests you use an integer as data; Does it make
sense to add a u8, bool and char test case to the list?

>  static struct kunit_case sysctl_test_cases[] = {
>  	KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
>  	KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
> @@ -378,6 +414,12 @@ static struct kunit_case sysctl_test_cases[] = {
>  	KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
>  	KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
>  	KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
> +	KUNIT_CASE(sysctl_test_api_ctltbl_entry_v),
> +	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vm),
> +	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vmr),
> +	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vn),
> +	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnm),
> +	KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnmh),
>  	{}
>  };
>  
> -- 
> 2.25.1
> 

-- 

Joel Granados

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

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

* Re: [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization
  2026-03-19 14:12 ` [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization Joel Granados
@ 2026-03-19 14:32   ` Joel Granados
  2026-03-25 18:21     ` Wen Yang
  0 siblings, 1 reply; 7+ messages in thread
From: Joel Granados @ 2026-03-19 14:32 UTC (permalink / raw)
  To: wen.yang; +Cc: linux-kernel

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

On Thu, Mar 19, 2026 at 03:12:04PM +0100, Joel Granados wrote:
> Hey Wen
> 
> Comments inline
> 
> On Wed, Mar 18, 2026 at 01:36:13AM +0800, wen.yang@linux.dev wrote:
> > From: Wen Yang <wen.yang@linux.dev>
> > 
> > The main motivation for this series is to avoid treewide patch series.
> > Historically, changes to how struct ctl_table entries are initialized
> > (e.g. removing the child field, const-qualifying ctl_table) required
> > touching hundreds of files across all subsystems. Such series require
> > the attention of many maintainers, sometimes need to be pulled into
> > mainline in a special way, and create lots of unnecessary churn.
> LGTM: Clear motivation.
> 
> > 
> > By introducing a family of CTLTBL_ENTRY_XXX() helper macros in
> > <linux/sysctl.h>, any future structural changes to struct ctl_table can
> > be handled in one place — the macro definition — without requiring a
> > treewide sweep of all callers. Individual subsystem maintainers can also
> > migrate their sysctl tables to use the macros at their own pace.
> I think this paragraph is redundant; I would remove it. And just add
> "This will all go away with the CTLTBL_ENTRY_XXX helper macros" as the
> last sentence in the first paragraph.
> 
> Migration strategy
> ==================
> You touched on this when you mentioned that the maintainers can migrate
> to the new macro at their pace. In my experience this will never happen
> if you give the responsibility to the subsys maintainers (They are busy
> with other matters). I think the more successful strategy is to lead the
> conversion from sysctl.
> 
> > 
> > Based on discussion and suggestions from:
> > [1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
> > [2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/
> > 
> > This series:
> >  1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h,
> >     allowing callers to initialize struct ctl_table entries indirectly
> >     via the macro instead of assigning each field directly. The macros
> >     use _Generic() for automatic type detection and proc_handler
> >     selection, provide smart defaults (auto address-of, auto maxlen via
> >     sizeof), and include compile-time validation of name, mode, and
> >     handler.
> >  2. Converts kernel/sysctl-test.c to use the new macros as a
> >     demonstration, replacing direct struct ctl_table field assignments
> >     with CTLTBL_ENTRY_XXX() calls. Four new tests are added to cover the
> >     previously untested variants (CTLTBL_ENTRY_V, VN, VNM, VNMH), bringing
> >     the total to 16 passing tests.
> > 
> > ---
> > 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 | 294 +++++++++++++++++++++++++++++++++++++++++
> >  kernel/sysctl-test.c   | 240 +++++++++++++++++++--------------
> >  kernel/sysctl.c        |   2 +
> >  3 files changed, 437 insertions(+), 99 deletions(-)
> > 
> > -- 
> > 2.25.1
> > 
> 
> Best
> 
> -- 
> 
> Joel Granados

I think that after you address the last round of feedback, the series
will be ready for a broader audience.

For your next version I would keep it as an RFC but include a more
targeted audience. Add the Kees Cook <kees@kernel.org> to the "To:" and
add linux-fsdevel@vger.kernel.org to the "Cc". This will hopefully get
some feedback and we can take it from there. I can nudge the new RFC if
it does not get any initial traction.

Thx again for taking this

Best

-- 

Joel Granados

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

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

* Re: [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization
  2026-03-19 14:32   ` Joel Granados
@ 2026-03-25 18:21     ` Wen Yang
  0 siblings, 0 replies; 7+ messages in thread
From: Wen Yang @ 2026-03-25 18:21 UTC (permalink / raw)
  To: Joel Granados; +Cc: linux-kernel



On 3/19/26 22:32, Joel Granados wrote:
> On Thu, Mar 19, 2026 at 03:12:04PM +0100, Joel Granados wrote:
>> Hey Wen
>>
>> Comments inline
>>
>> On Wed, Mar 18, 2026 at 01:36:13AM +0800, wen.yang@linux.dev wrote:
>>> From: Wen Yang <wen.yang@linux.dev>
>>>
>>> The main motivation for this series is to avoid treewide patch series.
>>> Historically, changes to how struct ctl_table entries are initialized
>>> (e.g. removing the child field, const-qualifying ctl_table) required
>>> touching hundreds of files across all subsystems. Such series require
>>> the attention of many maintainers, sometimes need to be pulled into
>>> mainline in a special way, and create lots of unnecessary churn.
>> LGTM: Clear motivation.
>>
>>>
>>> By introducing a family of CTLTBL_ENTRY_XXX() helper macros in
>>> <linux/sysctl.h>, any future structural changes to struct ctl_table can
>>> be handled in one place — the macro definition — without requiring a
>>> treewide sweep of all callers. Individual subsystem maintainers can also
>>> migrate their sysctl tables to use the macros at their own pace.
>> I think this paragraph is redundant; I would remove it. And just add
>> "This will all go away with the CTLTBL_ENTRY_XXX helper macros" as the
>> last sentence in the first paragraph.
>>
>> Migration strategy
>> ==================
>> You touched on this when you mentioned that the maintainers can migrate
>> to the new macro at their pace. In my experience this will never happen
>> if you give the responsibility to the subsys maintainers (They are busy
>> with other matters). I think the more successful strategy is to lead the
>> conversion from sysctl.
>>
>>>
>>> Based on discussion and suggestions from:
>>> [1] https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
>>> [2] https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/
>>>
>>> This series:
>>>   1. Introduces the CTLTBL_ENTRY_XXX() macros in include/linux/sysctl.h,
>>>      allowing callers to initialize struct ctl_table entries indirectly
>>>      via the macro instead of assigning each field directly. The macros
>>>      use _Generic() for automatic type detection and proc_handler
>>>      selection, provide smart defaults (auto address-of, auto maxlen via
>>>      sizeof), and include compile-time validation of name, mode, and
>>>      handler.
>>>   2. Converts kernel/sysctl-test.c to use the new macros as a
>>>      demonstration, replacing direct struct ctl_table field assignments
>>>      with CTLTBL_ENTRY_XXX() calls. Four new tests are added to cover the
>>>      previously untested variants (CTLTBL_ENTRY_V, VN, VNM, VNMH), bringing
>>>      the total to 16 passing tests.
>>>
>>> ---
>>> 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 | 294 +++++++++++++++++++++++++++++++++++++++++
>>>   kernel/sysctl-test.c   | 240 +++++++++++++++++++--------------
>>>   kernel/sysctl.c        |   2 +
>>>   3 files changed, 437 insertions(+), 99 deletions(-)
>>>
>>> -- 
>>> 2.25.1
>>>
>>
>> Best
>>
>> -- 
>>
>> Joel Granados
> 

> 
> For your next version I would keep it as an RFC but include a more
> targeted audience. Add the Kees Cook <kees@kernel.org> to the "To:" and
> add linux-fsdevel@vger.kernel.org to the "Cc". This will hopefully get
> some feedback and we can take it from there. I can nudge the new RFC if
> it does not get any initial traction.
> 

Thank you for your kind instruction.
We will revise it based on your suggestions and send v5 soon.

--
Best wishes,
Wen




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

end of thread, other threads:[~2026-03-25 18:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-17 17:36 [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization wen.yang
2026-03-17 17:36 ` [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
2026-03-17 17:36 ` [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
2026-03-19 14:15   ` Joel Granados
2026-03-19 14:12 ` [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization Joel Granados
2026-03-19 14:32   ` Joel Granados
2026-03-25 18:21     ` Wen Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox