From: wen.yang@linux.dev
To: Joel Granados <joel.granados@kernel.org>
Cc: linux-kernel@vger.kernel.org, Wen Yang <wen.yang@linux.dev>
Subject: [RFC PATCH v2 1/2] sysctl: introduce SYSCTL_ENTRY() helper macro
Date: Sun, 8 Feb 2026 04:35:16 +0800 [thread overview]
Message-ID: <139638bcf35a5306019d9399a0127f946d037f4c.1770496163.git.wen.yang@linux.dev> (raw)
In-Reply-To: <cover.1770496163.git.wen.yang@linux.dev>
From: Wen Yang <wen.yang@linux.dev>
Add SYSCTL_ENTRY() macro to simplify struct ctl_table initialization.
This macro provides automatic type detection, handler selection, and
sensible defaults, reducing boilerplate and potential errors.
Based on discussion and suggestions from:
https://sysctl-dev-rtd.readthedocs.io/en/latest/notes/ctltable_entry_macro.html#table-entry-macro
https://lore.kernel.org/all/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjgojk@p3vrj3qluban/
Features:
- Automatic type detection and handler selection via _Generic()
- Supports int, unsigned int, long, unsigned long
- Auto-selects range-checking handlers when min/max provided
- Flexible calling conventions (1-7 arguments):
- SYSCTL_TBL_ENTRY(var) -> readonly, auto-handler
- SYSCTL_TBL_ENTRY(name, var) -> custom name
- SYSCTL_TBL_ENTRY(name, var, mode) -> custom mode
- SYSCTL_TBL_ENTRY(name, var, mode, handler) -> custom handler
- SYSCTL_TBL_ENTRY(name, var, mode, min, max) -> with range
- SYSCTL_TBL_ENTRY(name, var, mode, handler, min, max) -> full control
- SYSCTL_TBL_ENTRY(name, var, mode, handler, min, max, maxlen) -> explicit maxlen
- Smart defaults:
- Auto address-of: SYSCTL_ENTRY(my_var) -> .data = &my_var
- Auto maxlen: uses sizeof(var) when not specified
- SYSCTL_NULL marker for entries without data
- Compile-time validation:
- NAME must be string literal
- MODE, HANDLER validated via type compatibility checks
- VAR must be lvalue
Example usage:
static int my_int = 256;
/* Before */
{
.procname = "my_int",
.data = &my_int,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
}
/* After */
SYSCTL_TBL_ENTRY("my_int", my_int, 0644)
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 | 131 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 131 insertions(+)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 2886fbceb5d6..3d10e2e9d6dc 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -175,6 +175,137 @@ struct ctl_table {
void *extra2;
} __randomize_layout;
+/* Special marker type to represent NULL data in sysctl entries */
+struct __sysctl_null_type { char __dummy; };
+extern const struct __sysctl_null_type __sysctl_null_marker;
+
+/* Use SYSCTL_NULL to indicate a sysctl entry without associated data */
+#define SYSCTL_NULL (__sysctl_null_marker)
+
+/* Validate NAME is a string literal and return it (fails on non-literals) */
+#define __SYSCTL_PROCNAME(NAME) \
+ ("" NAME "")
+
+/* Generate data pointer: NULL for SYSCTL_NULL, &VAR for actual variables */
+#define __SYSCTL_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))
+
+/* Compute maxlen: auto-detect based on entry type (NULL vs variable) */
+#define __SYSCTL_MAXLEN(VAR, MAXLEN) \
+ _Generic((VAR), \
+ struct __sysctl_null_type : __SYSCTL_MAXLEN_NULL(MAXLEN), \
+ default : \
+ __SYSCTL_MAXLEN_VAR(VAR, MAXLEN) \
+ )
+
+/* Validate MODE is compatible with umode_t and return it */
+#define __SYSCTL_MODE(MODE) \
+ (0 ? (umode_t)0 : (MODE))
+
+/* Validate HANDLER matches proc_handler signature and return it */
+#define __SYSCTL_PROC_HANDLER(HANDLER) \
+ (0 ? (proc_handler *)0 : (HANDLER))
+
+/* Auto-select appropriate proc_handler based on VAR's type */
+#define __SYSCTL_AUTO_HANDLER(VAR) \
+ _Generic((VAR), \
+ int : (proc_handler *)proc_dointvec, \
+ unsigned int : (proc_handler *)proc_douintvec, \
+ long : (proc_handler *)proc_dointvec, \
+ unsigned long : (proc_handler *)proc_douintvec, \
+ default : \
+ (proc_handler *)NULL \
+ )
+
+/* Auto-select range-checking proc_handler based on VAR's type */
+#define __SYSCTL_AUTO_HANDLER_MINMAX(VAR) \
+ _Generic((VAR), \
+ int : (proc_handler *)proc_dointvec_minmax, \
+ unsigned int : (proc_handler *)proc_douintvec_minmax, \
+ long : (proc_handler *)proc_doulongvec_minmax, \
+ unsigned long : (proc_handler *)proc_doulongvec_minmax, \
+ default : \
+ (proc_handler *)NULL \
+ )
+
+/* Validate PTR is pointer-compatible and return it as void* */
+#define __SYSCTL_EXTRA(PTR) \
+ (0 ? (void *)0 : (PTR))
+
+/* Initialize a ctl_table entry with all parameters */
+#define __SYSCTL_TBL_ENTRY(NAME, VAR, MODE, HANDLER, MIN, MAX, MAXLEN) \
+ { \
+ .procname = __SYSCTL_PROCNAME(NAME), \
+ .data = __SYSCTL_DATA(VAR), \
+ .maxlen = __SYSCTL_MAXLEN(VAR, MAXLEN), \
+ .mode = __SYSCTL_MODE(MODE), \
+ .proc_handler = __SYSCTL_PROC_HANDLER(HANDLER), \
+ .poll = NULL, \
+ .extra1 = __SYSCTL_EXTRA(MIN), \
+ .extra2 = __SYSCTL_EXTRA(MAX), \
+ }
+
+/* Count the number of variadic arguments (supports 1-7 arguments) */
+#define __SYSCTL_NARG(...) \
+ __SYSCTL_NARG_(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
+
+/* Helper for __SYSCTL_NARG - maps arguments to their count */
+#define __SYSCTL_NARG_(_1, _2, _3, _4, _5, _6, _7, N, ...) N
+
+/* Concatenate two tokens */
+#define __SYSCTL_CONCAT(A, B) A##B
+
+/* Select macro variant based on argument count */
+#define __SYSCTL_SELECT(NAME, N) __SYSCTL_CONCAT(NAME, N)
+
+/* SYSCTL_TBL_ENTRY(var) - use variable name as procname, readonly, auto handler */
+#define __SYSCTL_TBL_ENTRY_1(VAR) \
+ __SYSCTL_TBL_ENTRY(#VAR, VAR, 0444, \
+ __SYSCTL_AUTO_HANDLER(VAR), NULL, NULL, -1)
+
+/* SYSCTL_TBL_ENTRY(name, var) - custom name, readonly, auto handler */
+#define __SYSCTL_TBL_ENTRY_2(NAME, VAR) \
+ __SYSCTL_TBL_ENTRY(NAME, VAR, 0444, \
+ __SYSCTL_AUTO_HANDLER(VAR), NULL, NULL, -1)
+
+/* SYSCTL_TBL_ENTRY(name, var, mode) - custom name and mode, auto handler */
+#define __SYSCTL_TBL_ENTRY_3(NAME, VAR, MODE) \
+ __SYSCTL_TBL_ENTRY(NAME, VAR, MODE, \
+ __SYSCTL_AUTO_HANDLER(VAR), NULL, NULL, -1)
+
+/* SYSCTL_TBL_ENTRY(name, var, mode, handler) - custom handler */
+#define __SYSCTL_TBL_ENTRY_4(NAME, VAR, MODE, HANDLER) \
+ __SYSCTL_TBL_ENTRY(NAME, VAR, MODE, HANDLER, NULL, NULL, -1)
+
+/* SYSCTL_TBL_ENTRY(name, var, mode, min, max) - auto range-checking handler */
+#define __SYSCTL_TBL_ENTRY_5(NAME, VAR, MODE, MIN, MAX) \
+ __SYSCTL_TBL_ENTRY(NAME, VAR, MODE, \
+ __SYSCTL_AUTO_HANDLER_MINMAX(VAR), MIN, MAX, -1)
+
+/* SYSCTL_TBL_ENTRY(name, var, mode, handler, min, max) - custom handler with range */
+#define __SYSCTL_TBL_ENTRY_6(NAME, VAR, MODE, HANDLER, MIN, MAX) \
+ __SYSCTL_TBL_ENTRY(NAME, VAR, MODE, HANDLER, MIN, MAX, -1)
+
+/* SYSCTL_TBL_ENTRY(name, var, mode, handler, min, max, maxlen) - full control */
+#define __SYSCTL_TBL_ENTRY_7(NAME, VAR, MODE, HANDLER, MIN, MAX, MAXLEN) \
+ __SYSCTL_TBL_ENTRY(NAME, VAR, MODE, HANDLER, MIN, MAX, MAXLEN)
+
+/* Define a sysctl table entry with automatic type detection and parameter handling */
+#define SYSCTL_TBL_ENTRY(...) \
+ __SYSCTL_SELECT(__SYSCTL_TBL_ENTRY_, __SYSCTL_NARG(__VA_ARGS__))(__VA_ARGS__)
+
struct ctl_node {
struct rb_node node;
struct ctl_table_header *header;
--
2.25.1
next prev parent reply other threads:[~2026-02-07 20:35 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-07 20:35 [RFC PATCH v2 0/2] sysctl: refactor ctl_table creation and change extra{1,2} type wen.yang
2026-02-07 20:35 ` wen.yang [this message]
2026-02-19 10:33 ` [RFC PATCH v2 1/2] sysctl: introduce SYSCTL_ENTRY() helper macro Joel Granados
2026-02-07 20:35 ` [RFC PATCH v2 2/2] sysctl: convert kernel/sysctl-test.c to use SYSCTL_TBL_ENTRY() wen.yang
2026-02-19 8:59 ` [RFC PATCH v2 0/2] sysctl: refactor ctl_table creation and change extra{1,2} type Joel Granados
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=139638bcf35a5306019d9399a0127f946d037f4c.1770496163.git.wen.yang@linux.dev \
--to=wen.yang@linux.dev \
--cc=joel.granados@kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®