From nobody Wed Feb 11 06:31:22 2026 Received: from out-184.mta0.migadu.com (out-184.mta0.migadu.com [91.218.175.184]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28A9333B6E1 for ; Sat, 7 Feb 2026 20:35:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770496534; cv=none; b=JvePbgTcno453YI21x+qOHNS+BAW6MLEdDAu6mJmcejMthgWhnk+vx/TYaHAw54EyfDlguyqK2+//+JDFJ7SQKRkznXrBXUlKfPIBmWkDlCGviVhUoLtb7ATnufU4V2yC3s7wYION+G2hlE/vhEPVAJPro/6121tVymiyzOaNqw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770496534; c=relaxed/simple; bh=u8MaCdx5Ffdv34SvM82nE/qSB0uqS/ObMt4tOKeUV6g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=C/hsDNzEThw41aJTHyXGQTfWHUW+5RveUq2Iy1Q+/+yBLtrHMBuRGy1Ewnf3ibAkiomYfxXCVJve6gC9DkBVEBLaH/R1N+ENmk4n9MXvjvsEXecbz8cK3mv/+bUW7u/Aq09kfX6f7uxi/VasF7QWaFCvDhrhSB94smP8NMIDerk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=o0X+INwO; arc=none smtp.client-ip=91.218.175.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="o0X+INwO" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1770496532; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0w5b3oibjT3PZzzlV9ueN9mGZUZHCcfvmELH4WHHfmA=; b=o0X+INwOB5VEJFe2a+o17oIsC+zJEli33Wklr3bD64/6UxScV0UlnLnAse3sU+563bCAfX XNYC/qjRXrZubbNZhW8dgUsJoL4XpOPKfb0nl9eiq5Kvi+mf8bk2lu0YnrjExI5P77yJiJ pM7PGD8B5+r1YiRHIs9uDssMVkMJNcg= From: wen.yang@linux.dev To: Joel Granados Cc: linux-kernel@vger.kernel.org, Wen Yang Subject: [RFC PATCH v2 1/2] sysctl: introduce SYSCTL_ENTRY() helper macro Date: Sun, 8 Feb 2026 04:35:16 +0800 Message-Id: <139638bcf35a5306019d9399a0127f946d037f4c.1770496163.git.wen.yang@linux.dev> In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Wen Yang 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/psot4oeauxi3yyj2w4ajm3tfgtcsvao4rhv5sgd5s6ymmjg= ojk@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) -> explic= it maxlen - Smart defaults: - Auto address-of: SYSCTL_ENTRY(my_var) -> .data =3D &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 =3D 256; /* Before */ { .procname =3D "my_int", .data =3D &my_int, .maxlen =3D sizeof(int), .mode =3D 0644, .proc_handler =3D proc_dointvec, } /* After */ SYSCTL_TBL_ENTRY("my_int", my_int, 0644) No functional change intended. Suggested-by: Joel Granados Signed-off-by: Wen Yang --- 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; =20 +/* 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) >=3D 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 =3D __SYSCTL_PROCNAME(NAME), \ + .data =3D __SYSCTL_DATA(VAR), \ + .maxlen =3D __SYSCTL_MAXLEN(VAR, MAXLEN), \ + .mode =3D __SYSCTL_MODE(MODE), \ + .proc_handler =3D __SYSCTL_PROC_HANDLER(HANDLER), \ + .poll =3D NULL, \ + .extra1 =3D __SYSCTL_EXTRA(MIN), \ + .extra2 =3D __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 h= andler */ +#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 handl= er */ +#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 w= ith 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 con= trol */ +#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_ARG= S__) + struct ctl_node { struct rb_node node; struct ctl_table_header *header; --=20 2.25.1