From nobody Mon Apr 6 23:08:44 2026 Received: from out-178.mta1.migadu.com (out-178.mta1.migadu.com [95.215.58.178]) (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 0C4C137BE8C for ; Tue, 17 Mar 2026 17:36:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773769004; cv=none; b=uxottxPCZzottr29b2AY1yveQK45zku3/tHoXeUYAPZ0b9yCgeIxqZ7k47yB44IOaZiPn1FWZ5XZm4yIMfnPo0Qbdys+kImfPcCemmEmZwLmB300vRUcvFzVVCDS6A+637xgBpEDg2JJSoabyATHu61ZpUgPtPf+GX+kGGrPMxA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773769004; c=relaxed/simple; bh=bIndQJRNAxddMhaermrvYNN7zlWGn9AxT/L6IUGMeK4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YuYaxVBF7ZPZjhXOgjefRDg6W7Kc2ovTTp1FL4kzjigsqpxmSMyE1Sx5VGYvLpsEoRifnXsmR2aiNh28yiCdcUGC6g/RlcJTh2w2Qhcdawg/XVHeWBkCQSxKmixn14cszyknkbhSOCBz4eNPcVWduEUCfDitnmJXSyoNnNkjg+k= 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=k9bu/itb; arc=none smtp.client-ip=95.215.58.178 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="k9bu/itb" 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=1773769001; 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=CfDSMHGi4fzlBuDQKW2K1VzjExBrp7AO7mpoNhfjXow=; b=k9bu/itb2am3lDyhEdSQwA3gvAZoTTCm0VayDiBoXRoDRLJa6rcH2N3WsuGmxIbqINmuuL ZKGGv8TrHgvwsmvFDOWDoGHffec3D2OjjTL5knbSUWTJc37eqX2AXMkri5oAjEkZSzrLND az6uenCqH323iXYqU1X03prH71tze8Q= From: wen.yang@linux.dev To: Joel Granados Cc: linux-kernel@vger.kernel.org, Wen Yang Subject: [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros Date: Wed, 18 Mar 2026 01:36:14 +0800 Message-Id: <8a57127a4bf818313264c57a99d5577c1d263d0a.1773767153.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 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, __max= len) - 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 Signed-off-by: Wen Yang --- 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; =20 +/** + * 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) >=3D 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 =3D __CTL_PROCNAME(_name), \ + .data =3D __CTL_DATA(_var), \ + .maxlen =3D __CTL_MAXLEN(_var, _maxlen), \ + .mode =3D __CTL_MODE(_mode), \ + .proc_handler =3D __CTL_HANDLER(_handler), \ + .poll =3D NULL, \ + .extra1 =3D __CTL_EXTRA(_min), \ + .extra2 =3D __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 =3D=3D stringified variable = name + * + * Proto: (T __var) + * + * .procname =3D #__var, .mode =3D 0444, .extra1 =3D .extra2 =3D 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 =3D=3D #__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 =3D=3D #__= 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 =3D=3D typeof(__= var) + * @__max: pointer to maximum value; typeof(*__max) must =3D=3D 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 =3D=3D typeof(__= var) + * @__max: pointer to maximum value; typeof(*__max) must =3D=3D 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 =3D NULL, .maxlen =3D 0, .extra1 =3D NULL, .extra2 =3D 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[] =3D { 0, 1, LONG_MAX }; EXPORT_SYMBOL_GPL(sysctl_long_vals); =20 +const struct _sysctl_null_type _sysctl_null_marker; + #if defined(CONFIG_SYSCTL) =20 /* Constants used for minimum and maximum */ --=20 2.25.1