From patchwork Fri Mar 21 09:17:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Hindborg X-Patchwork-Id: 14025051 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 90A0F1F03F4; Fri, 21 Mar 2025 09:18:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742548716; cv=none; b=Y6Fx0kfjezFtdL1NM8tKIFHB9EbI/D//VX6JKrItb7DCJENHU3ZHdVX+c7mEMB/odjIgDre2KlsybQDSVuIY7WedkJdOuimV8a2OPn6k1aC9esvk9bwOp1f31gzYUGXV8B7PanYogxtOKNen2FmDT7mldTAFbmeAXK8COkmy6Ag= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742548716; c=relaxed/simple; bh=sCW19nzrlwKLY8toSkWFitagnE2dtqdJ0NHcRxGxCRI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QwNCENlMfltzzgoCGvNbzK6bTRerO3xasKhhuMTv8CgljUeZHqtLquXyqgycENPuufStdvAogC6Kmn2kiufz0LFcKHayV/MulTHz1v3aZvY4hbOvf6G4AMOyHwzZL+HHbv5MOWD4n+WH7LWUj9Po7gWOSzedYJpg1jcTDQ4RK8M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=MvshrXsL; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="MvshrXsL" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 27C2EC4CEE3; Fri, 21 Mar 2025 09:18:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1742548716; bh=sCW19nzrlwKLY8toSkWFitagnE2dtqdJ0NHcRxGxCRI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=MvshrXsL5J8yEJ6pLRK7lWXDlQpuRiiBUfEKw4ftMP12zVD5aP5yTHdij+MAWeWP1 SQ0N4F4eCYkX8E8JlyVe/XERcI4k7Nw692OHblCm3ua3tXQluCBY8eIbeSrBuRjTiU cdrX+8bSpELqMTCXrKg5fYoK77dnko4LpMvE1BKhe2Ad2FljnBJY/ezHA3KzGUs1ZO blcZwsJYfoPKcnbiS2Rd9H+zjOKD5z0uevhP0VimV5+GHcwf8sboFzWaHY1qRL5Nrp w2lGWomBA1aBSoUSl85GcuTzgi4inPAUw798dLjobgj8E/VABmdT9/+HMjbybPNI5E p38DMWXPVmxmw== From: Andreas Hindborg Date: Fri, 21 Mar 2025 10:17:34 +0100 Subject: [PATCH v9 1/3] rust: str: add radix prefixed integer parsing functions Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250321-module-params-v3-v9-1-28b905f2e345@kernel.org> References: <20250321-module-params-v3-v9-0-28b905f2e345@kernel.org> In-Reply-To: <20250321-module-params-v3-v9-0-28b905f2e345@kernel.org> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Masahiro Yamada , Nathan Chancellor , Nicolas Schier , Luis Chamberlain , Danilo Krummrich Cc: Trevor Gross , Adam Bratschi-Kaye , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org, Petr Pavlu , Sami Tolvanen , Daniel Gomez , Simona Vetter , Greg KH , Fiona Behrens , Daniel Almeida , linux-modules@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7742; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=sCW19nzrlwKLY8toSkWFitagnE2dtqdJ0NHcRxGxCRI=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBn3S7DDzfCvCVd1geFru40zNvER27+CDGh87Rid 8ZQ8d4kCZeJAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ90uwwAKCRDhuBo+eShj dzx5EAC4aQCfDTDnt/kSlqXQSLzCwxWqffOGemTbhdAQ8ny5H/SLSl5olD7LU4utIz19/0aQolP yBcT7cZj4rmxPddHlSH8DCxJus8SmEvlLRelVEzqlYhk64ZZYzSRc1a6Msov0juWXiOxinuq00O 26MkNAqdHwqWi6uZ7Q7stqkTakvEe83oxnsJesTvA0BWhm5oRoabdCI4LSwX26hDYRSeXQJq88A 3WoDVLE1mLBX/TaRmWi/FqQKIDMIDg08j36OpkUYQkj8ryZuGL5A9I2qThmfd3F1FynN1eVsIBN dg+Mz0HkrqJppgNjEFu2irsfCPDqdu5O/NeQQgVc/6bAf19tAo3oT/8FSIhRnzusht1XX+EpVJM 9z/HVyoArLZB2J9fgwN/eM6AO/HDeMrUHz220HpOJ9E9gniHJP8VFmbAv9V2DqJPX2QqSFPbZ+p zY88MW1MHl92XKGWa9tN5NquDBHjn99e9jGjpJR2sZXAK9CveVewsAQY1tBiuQAkWhXD5DT3kLe l15DKHBpmbEoU1X888SsMP4r770aYODREUyJmm3+5T1BE1Yr3MB7PLllfFWAvzK8CnwMx6A09+C NUhD+/V7q4xFfpok6L4KmIJSmeV48xFcqBKC9easniXE2nmH7E1XADg8WfL2mEPHAoF1RgTdLFW BQTS6Xw7Eo/sEOg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the trait `ParseInt` for parsing string representations of integers where the string representations are optionally prefixed by a radix specifier. Implement the trait for the primitive integer types. Tested-by: Daniel Almeida Reviewed-by: Daniel Almeida Signed-off-by: Andreas Hindborg --- rust/kernel/str.rs | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 878111cb77bc..0bfa2c1cf942 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -946,3 +946,173 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { macro_rules! fmt { ($($f:tt)*) => ( core::format_args!($($f)*) ) } + +pub mod parse_int { + //! Integer parsing functions for parsing signed and unsigned integers + //! potentially prefixed with `0x`, `0o`, or `0b`. + + use crate::prelude::*; + use crate::str::BStr; + use core::ops::Deref; + + // Make `FromStrRadix` a public type with a private name. This seals + // `ParseInt`, that is, prevents downstream users from implementing the + // trait. + mod private { + use crate::str::BStr; + + /// Trait that allows parsing a [`&BStr`] to an integer with a radix. + /// + /// # Safety + /// + /// The member functions of this trait must be implemented according to + /// their documentation. + /// + /// [`&BStr`]: kernel::str::BStr + // This is required because the `from_str_radix` function on the primitive + // integer types is not part of any trait. + pub unsafe trait FromStrRadix: Sized { + /// The minimum value this integer type can assume. + const MIN: Self; + + /// Parse `src` to `Self` using radix `radix`. + fn from_str_radix(src: &BStr, radix: u32) -> Result; + + /// Return the absolute value of Self::MIN. + fn abs_min() -> u64; + + /// Perform bitwise 2's complement on `self`. + /// + /// Note: This function does not make sense for unsigned integers. + fn complement(self) -> Self; + } + } + + /// Extract the radix from an integer literal optionally prefixed with + /// one of `0x`, `0X`, `0o`, `0O`, `0b`, `0B`, `0`. + fn strip_radix(src: &BStr) -> (u32, &BStr) { + match src.deref() { + [b'0', b'x' | b'X', rest @ ..] => (16, rest.as_ref()), + [b'0', b'o' | b'O', rest @ ..] => (8, rest.as_ref()), + [b'0', b'b' | b'B', rest @ ..] => (2, rest.as_ref()), + // NOTE: We are including the leading zero to be able to parse + // literal 0 here. If we removed it as a radix prefix, we would not + // be able to parse `0`. + [b'0', ..] => (8, src), + _ => (10, src), + } + } + + /// Trait for parsing string representations of integers. + /// + /// Strings beginning with `0x`, `0o`, or `0b` are parsed as hex, octal, or + /// binary respectively. Strings beginning with `0` otherwise are parsed as + /// octal. Anything else is parsed as decimal. A leading `+` or `-` is also + /// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be + /// successfully parsed. + /// + /// [`kstrtol()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtol + /// [`kstrtoul()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtoul + /// + /// # Example + /// ``` + /// use kernel::str::parse_int::ParseInt; + /// use kernel::b_str; + /// + /// assert_eq!(Ok(0), u8::from_str(b_str!("0"))); + /// + /// assert_eq!(Ok(0xa2u8), u8::from_str(b_str!("0xa2"))); + /// assert_eq!(Ok(-0xa2i32), i32::from_str(b_str!("-0xa2"))); + /// + /// assert_eq!(Ok(-0o57i8), i8::from_str(b_str!("-0o57"))); + /// assert_eq!(Ok(0o57i8), i8::from_str(b_str!("057"))); + /// + /// assert_eq!(Ok(0b1001i16), i16::from_str(b_str!("0b1001"))); + /// assert_eq!(Ok(-0b1001i16), i16::from_str(b_str!("-0b1001"))); + /// + /// assert_eq!(Ok(127), i8::from_str(b_str!("127"))); + /// assert!(i8::from_str(b_str!("128")).is_err()); + /// assert_eq!(Ok(-128), i8::from_str(b_str!("-128"))); + /// assert!(i8::from_str(b_str!("-129")).is_err()); + /// assert_eq!(Ok(255), u8::from_str(b_str!("255"))); + /// assert!(u8::from_str(b_str!("256")).is_err()); + /// ``` + pub trait ParseInt: private::FromStrRadix + TryFrom { + /// Parse a string according to the description in [`Self`]. + fn from_str(src: &BStr) -> Result { + match src.deref() { + [b'-', rest @ ..] => { + let (radix, digits) = strip_radix(rest.as_ref()); + // 2's complement values range from -2^(b-1) to 2^(b-1)-1. + // So if we want to parse negative numbers as positive and + // later multiply by -1, we have to parse into a larger + // integer. We choose u64 as sufficiently large. NOTE: 128 + // bit integers are not available on all platforms, hence + // the choice of 64 bit. + let val = u64::from_str_radix( + core::str::from_utf8(digits).map_err(|_| EINVAL)?, + radix, + ) + .map_err(|_| EINVAL)?; + + if val > Self::abs_min() { + return Err(EINVAL); + } + + if val == Self::abs_min() { + return Ok(Self::MIN); + } + + // SAFETY: We checked that `val` will fit in `Self` above. + let val: Self = unsafe { val.try_into().unwrap_unchecked() }; + + Ok(val.complement()) + } + _ => { + let (radix, digits) = strip_radix(src); + Self::from_str_radix(digits, radix).map_err(|_| EINVAL) + } + } + } + } + + macro_rules! impl_parse_int { + ($ty:ty) => { + // SAFETY: We implement the trait according to the documentation. + unsafe impl private::FromStrRadix for $ty { + const MIN: Self = <$ty>::MIN; + + fn from_str_radix(src: &BStr, radix: u32) -> Result { + <$ty>::from_str_radix(core::str::from_utf8(src).map_err(|_| EINVAL)?, radix) + .map_err(|_| EINVAL) + } + + fn abs_min() -> u64 { + #[allow(unused_comparisons)] + if Self::MIN < 0 { + 1u64 << (Self::BITS - 1) + } else { + 0 + } + } + + fn complement(self) -> Self { + (!self).wrapping_add((1 as $ty)) + } + } + + impl ParseInt for $ty {} + }; + } + + impl_parse_int!(i8); + impl_parse_int!(u8); + impl_parse_int!(i16); + impl_parse_int!(u16); + impl_parse_int!(i32); + impl_parse_int!(u32); + impl_parse_int!(i64); + impl_parse_int!(u64); + impl_parse_int!(isize); + impl_parse_int!(usize); +} From patchwork Fri Mar 21 09:17:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Hindborg X-Patchwork-Id: 14025049 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 6CE22374EA; Fri, 21 Mar 2025 09:18:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742548705; cv=none; b=cMzgtkwCSoFOk44Dk4LCdJ/0CU+KJoW/vzyXNYAvVN57cycukeNl9062dGrZ2bG0V3twW1VybQrI+kZzzXQ5OxwzNXm3gnVBL4VX9xJfLYdsdFJ38ZltT9Z2eXlKRpaZ0FpOBw4yTJSjnoGSP+15gmF/hMxsxBftDowVEX+wOeA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742548705; c=relaxed/simple; bh=8Wr4jyxHjDbVCnTv61t4HQkxhcAj2YaxOL3Eh4nef/Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QzXLLRsROmh61x0k7nryuVq6pccXU/VK33RjqevKTwgzFLy+xW5ZhPZAzU5rZjmJxdzWq0Ow6gucK8tbCHgwQMibbU3OKDFe3pMA6THwdaf0GQvhHbqPhFFPnwQvKjjYcUY0y+Hm+WdFCw2c2pVYu5GtL95lGZf4sL5EsWrxd1k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hFWdD9EQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hFWdD9EQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 93198C4CEE3; Fri, 21 Mar 2025 09:18:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1742548704; bh=8Wr4jyxHjDbVCnTv61t4HQkxhcAj2YaxOL3Eh4nef/Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hFWdD9EQrMCd8zBXaj5OF1hrGhMUmvYNZSg4gAifmUNPTtQRAb8wV+WDMe8Rr2P+f NEwGLX+T4N7jAP1A3+OKP646yCRyjWqiuheJLYzwUd8SWUg4VrHkQrhGhSa5Gb/9KR aFoYDmRM8pLkdeMfplzeU+Orc9sjxtGaoPayPoM51UArYv0Nx6h6+OPZhsdr2LRF+H B74V+PN2smyZwW/JW+qPE2d4N0eh3I5sHG6z768Apu+pJ3hMaLtRLaunm+EiOdUW+n K3Dsj9oezOC0N+3M0E1BUPECd8rYFeA+/DxVhhj5BGlG38rm0qfej5GuvtYyqPeSCW iSoWiVEIV+3/w== From: Andreas Hindborg Date: Fri, 21 Mar 2025 10:17:35 +0100 Subject: [PATCH v9 2/3] rust: add parameter support to the `module!` macro Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250321-module-params-v3-v9-2-28b905f2e345@kernel.org> References: <20250321-module-params-v3-v9-0-28b905f2e345@kernel.org> In-Reply-To: <20250321-module-params-v3-v9-0-28b905f2e345@kernel.org> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Masahiro Yamada , Nathan Chancellor , Nicolas Schier , Luis Chamberlain , Danilo Krummrich Cc: Trevor Gross , Adam Bratschi-Kaye , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org, Petr Pavlu , Sami Tolvanen , Daniel Gomez , Simona Vetter , Greg KH , Fiona Behrens , Daniel Almeida , linux-modules@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=24437; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=8Wr4jyxHjDbVCnTv61t4HQkxhcAj2YaxOL3Eh4nef/Y=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBn3S7ErBSNvzgapqHw841qJLVWR6xIO5Y+nJu6F VzAkzhQj1+JAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ90uxAAKCRDhuBo+eShj d9eiD/wNNUfVtC50B1NzuBLFYALmsbRPopba7Pxy7otlAS+6mq0dcQA28+UUwWUWlGe7JGRQC8X Qq4aPReDUtl5tD67+/63YAQ9QHqpnors6I5oF3jAhKuADeB2vi92qooGZ8UP3Yir2Y+q1WPSF2o gcJmhBoFYSWcMDRGT4O8Cab3+llV2MClDUBs48T0I2j2kil9eJm/cboD6WRiWMnpE6VRI3mK23X JhrbtmusKYxbn+/9fbFHkriZhCS9CkIllXQMV+aqiX/d1RV3zlC3ykVJq+ZbzhZcpRE3saoC5Xm tEQ4Q8kM7igA6DEi0oW4fec9XbAMYhBwF6El4n0s7wFxRKOtYknDeGmyg/FRJJL4krjAbV9fTMs OtUiUWWNj8hdvL386ABqtMsazCSq7nYQkcek25wM1FxiiB9C95VaMt2KZR1Lr4tNVBBHYoKhBI4 EhpheD/TZGd0kMfq7L1V3NVn4PrEo9Pj3A9LTrMmrlemBIPjYa036KTVEgSnw3iVDEyNt1nyMdF bLDh5dE+k+bJ1GilDro8+3avhndCd/F3IQZMalBpWMvtNbkmnrNq2qQ5aNqqho1UFHPl/uGksvw SA5vKNsgXt/x3oT64ES9gFSrxHnwf+qBQVcPMxaQNMRC1rcpd/KLlWmP3xIQq8ghNhAkUNp6uzN ZzYjd7Ulosqe14g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for module parameters to the `module!` macro. Implement read only support for integer types without `sysfs` support. Acked-by: Petr Pavlu # from modules perspective Tested-by: Daniel Gomez Signed-off-by: Andreas Hindborg --- rust/kernel/lib.rs | 1 + rust/kernel/module_param.rs | 221 +++++++++++++++++++++++++++++++++++++++++++ rust/macros/helpers.rs | 25 +++++ rust/macros/lib.rs | 31 ++++++ rust/macros/module.rs | 195 ++++++++++++++++++++++++++++++++++---- samples/rust/rust_minimal.rs | 10 ++ 6 files changed, 463 insertions(+), 20 deletions(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 001374a96d66..c1dd27297cd2 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -59,6 +59,7 @@ pub mod kunit; pub mod list; pub mod miscdevice; +pub mod module_param; #[cfg(CONFIG_NET)] pub mod net; pub mod of; diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs new file mode 100644 index 000000000000..0f153aa64dae --- /dev/null +++ b/rust/kernel/module_param.rs @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Support for module parameters. +//! +//! C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h) + +use crate::prelude::*; +use crate::str::BStr; + +/// Newtype to make `bindings::kernel_param` [`Sync`]. +#[repr(transparent)] +#[doc(hidden)] +pub struct RacyKernelParam(pub ::kernel::bindings::kernel_param); + +// SAFETY: C kernel handles serializing access to this type. We never access it +// from Rust module. +unsafe impl Sync for RacyKernelParam {} + +/// Types that can be used for module parameters. +pub trait ModuleParam: Sized { + /// The [`ModuleParam`] will be used by the kernel module through this type. + /// + /// This may differ from `Self` if, for example, `Self` needs to track + /// ownership without exposing it or allocate extra space for other possible + /// parameter values. + // This is required to support string parameters in the future. + type Value: ?Sized; + + /// Parse a parameter argument into the parameter value. + /// + /// `Err(_)` should be returned when parsing of the argument fails. + /// + /// Parameters passed at boot time will be set before [`kmalloc`] is + /// available (even if the module is loaded at a later time). However, in + /// this case, the argument buffer will be valid for the entire lifetime of + /// the kernel. So implementations of this method which need to allocate + /// should first check that the allocator is available (with + /// [`crate::bindings::slab_is_available`]) and when it is not available + /// provide an alternative implementation which doesn't allocate. In cases + /// where the allocator is not available it is safe to save references to + /// `arg` in `Self`, but in other cases a copy should be made. + /// + /// [`kmalloc`]: srctree/include/linux/slab.h + fn try_from_param_arg(arg: &'static BStr) -> Result; +} + +/// Set the module parameter from a string. +/// +/// Used to set the parameter value at kernel initialization, when loading +/// the module or when set through `sysfs`. +/// +/// See `struct kernel_param_ops.set`. +/// +/// # Safety +/// +/// - If `val` is non-null then it must point to a valid null-terminated string. +/// The `arg` field of `param` must be an instance of `T`. +/// - `param.arg` must be a pointer to valid `*mut T` as set up by the +/// [`module!`] macro. +/// +/// # Invariants +/// +/// Currently, we only support read-only parameters that are not readable +/// from `sysfs`. Thus, this function is only called at kernel +/// initialization time, or at module load time, and we have exclusive +/// access to the parameter for the duration of the function. +/// +/// [`module!`]: macros::module +unsafe extern "C" fn set_param( + val: *const kernel::ffi::c_char, + param: *const crate::bindings::kernel_param, +) -> core::ffi::c_int +where + T: ModuleParam, +{ + // NOTE: If we start supporting arguments without values, val _is_ allowed + // to be null here. + if val.is_null() { + // TODO: Use pr_warn_once available. + crate::pr_warn!("Null pointer passed to `module_param::set_param`"); + return EINVAL.to_errno(); + } + + // SAFETY: By function safety requirement, val is non-null and + // null-terminated. By C API contract, `val` is live and valid for reads + // for the duration of this function. + let arg = unsafe { CStr::from_char_ptr(val) }; + + crate::error::from_result(|| { + let new_value = T::try_from_param_arg(arg)?; + + // SAFETY: `param` is guaranteed to be valid by C API contract + // and `arg` is guaranteed to point to an instance of `T`. + let old_value = unsafe { (*param).__bindgen_anon_1.arg as *mut T }; + + // SAFETY: `old_value` is valid for writes, as we have exclusive + // access. `old_value` is pointing to an initialized static, and + // so it is properly initialized. + unsafe { core::ptr::replace(old_value, new_value) }; + Ok(0) + }) +} + +/// Drop the parameter. +/// +/// Called when unloading a module. +/// +/// # Safety +/// +/// The `arg` field of `param` must be an initialized instance of `T`. +unsafe extern "C" fn free(arg: *mut core::ffi::c_void) +where + T: ModuleParam, +{ + // SAFETY: By function safety requirement, `arg` is an initialized + // instance of `T`. By C API contract, `arg` will not be used after + // this function returns. + unsafe { core::ptr::drop_in_place(arg as *mut T) }; +} + +macro_rules! impl_int_module_param { + ($ty:ident) => { + impl ModuleParam for $ty { + type Value = $ty; + + fn try_from_param_arg(arg: &'static BStr) -> Result { + <$ty as crate::str::parse_int::ParseInt>::from_str(arg) + } + } + }; +} + +impl_int_module_param!(i8); +impl_int_module_param!(u8); +impl_int_module_param!(i16); +impl_int_module_param!(u16); +impl_int_module_param!(i32); +impl_int_module_param!(u32); +impl_int_module_param!(i64); +impl_int_module_param!(u64); +impl_int_module_param!(isize); +impl_int_module_param!(usize); + +/// A wrapper for kernel parameters. +/// +/// This type is instantiated by the [`module!`] macro when module parameters are +/// defined. You should never need to instantiate this type directly. +/// +/// Note: This type is `pub` because it is used by module crates to access +/// parameter values. +#[repr(transparent)] +pub struct ModuleParamAccess { + data: core::cell::UnsafeCell, +} + +// SAFETY: We only create shared references to the contents of this container, +// so if `T` is `Sync`, so is `ModuleParamAccess`. +unsafe impl Sync for ModuleParamAccess {} + +impl ModuleParamAccess { + #[doc(hidden)] + pub const fn new(value: T) -> Self { + Self { + data: core::cell::UnsafeCell::new(value), + } + } + + /// Get a shared reference to the parameter value. + // Note: When sysfs access to parameters are enabled, we have to pass in a + // held lock guard here. + pub fn get(&self) -> &T { + // SAFETY: As we only support read only parameters with no sysfs + // exposure, the kernel will not touch the parameter data after module + // initialization. + unsafe { &*self.data.get() } + } + + /// Get a mutable pointer to the parameter value. + pub const fn as_mut_ptr(&self) -> *mut T { + self.data.get() + } +} + +#[doc(hidden)] +#[macro_export] +/// Generate a static [`kernel_param_ops`](srctree/include/linux/moduleparam.h) struct. +/// +/// # Examples +/// +/// ```ignore +/// make_param_ops!( +/// /// Documentation for new param ops. +/// PARAM_OPS_MYTYPE, // Name for the static. +/// MyType // A type which implements [`ModuleParam`]. +/// ); +/// ``` +macro_rules! make_param_ops { + ($ops:ident, $ty:ty) => { + /// + /// Static [`kernel_param_ops`](srctree/include/linux/moduleparam.h) + /// struct generated by `make_param_ops` + #[doc = concat!("for [`", stringify!($ty), "`].")] + pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops { + flags: 0, + set: Some(set_param::<$ty>), + get: None, + free: Some(free::<$ty>), + }; + }; +} + +make_param_ops!(PARAM_OPS_I8, i8); +make_param_ops!(PARAM_OPS_U8, u8); +make_param_ops!(PARAM_OPS_I16, i16); +make_param_ops!(PARAM_OPS_U16, u16); +make_param_ops!(PARAM_OPS_I32, i32); +make_param_ops!(PARAM_OPS_U32, u32); +make_param_ops!(PARAM_OPS_I64, i64); +make_param_ops!(PARAM_OPS_U64, u64); +make_param_ops!(PARAM_OPS_ISIZE, isize); +make_param_ops!(PARAM_OPS_USIZE, usize); diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index a3ee27e29a6f..16d300ad3d3b 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -10,6 +10,17 @@ pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option { } } +pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option { + let peek = it.clone().next(); + match peek { + Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => { + let _ = it.next(); + Some(punct.as_char()) + } + _ => None, + } +} + pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option { if let Some(TokenTree::Literal(literal)) = it.next() { Some(literal.to_string()) @@ -86,3 +97,17 @@ pub(crate) fn function_name(input: TokenStream) -> Option { } None } + +/// Parse a token stream of the form `expected_name: "value",` and return the +/// string in the position of "value". +/// +/// # Panics +/// +/// - On parse error. +pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String { + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let string = expect_string(it); + assert_eq!(expect_punct(it), ','); + string +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 9acaa68c974e..7a3a54fcf88e 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -23,6 +23,30 @@ /// The `type` argument should be a type which implements the [`Module`] /// trait. Also accepts various forms of kernel metadata. /// +/// The `params` field describe module parameters. Each entry has the form +/// +/// ```ignore +/// parameter_name: type { +/// default: default_value, +/// description: "Description", +/// } +/// ``` +/// +/// `type` may be one of +/// +/// - [`i8`] +/// - [`u8`] +/// - [`i8`] +/// - [`u8`] +/// - [`i16`] +/// - [`u16`] +/// - [`i32`] +/// - [`u32`] +/// - [`i64`] +/// - [`u64`] +/// - [`isize`] +/// - [`usize`] +/// /// C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h) /// /// [`Module`]: ../kernel/trait.Module.html @@ -39,6 +63,12 @@ /// description: "My very own kernel module!", /// license: "GPL", /// alias: ["alternate_module_name"], +/// params: { +/// my_parameter: i64 { +/// default: 1, +/// description: "This parameter has a default of 1", +/// }, +/// }, /// } /// /// struct MyModule(i32); @@ -47,6 +77,7 @@ /// fn init(_module: &'static ThisModule) -> Result { /// let foo: i32 = 42; /// pr_info!("I contain: {}\n", foo); +/// pr_info!("i32 param is: {}\n", module_parameters::my_parameter.read()); /// Ok(Self(foo)) /// } /// } diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 46f20682a7a9..2937a0ae6d62 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -26,6 +26,7 @@ struct ModInfoBuilder<'a> { module: &'a str, counter: usize, buffer: String, + param_buffer: String, } impl<'a> ModInfoBuilder<'a> { @@ -34,10 +35,11 @@ fn new(module: &'a str) -> Self { module, counter: 0, buffer: String::new(), + param_buffer: String::new(), } } - fn emit_base(&mut self, field: &str, content: &str, builtin: bool) { + fn emit_base(&mut self, field: &str, content: &str, builtin: bool, param: bool) { let string = if builtin { // Built-in modules prefix their modinfo strings by `module.`. format!( @@ -51,8 +53,14 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) { format!("{field}={content}\0", field = field, content = content) }; + let buffer = if param { + &mut self.param_buffer + } else { + &mut self.buffer + }; + write!( - &mut self.buffer, + buffer, " {cfg} #[doc(hidden)] @@ -75,20 +83,116 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) { self.counter += 1; } - fn emit_only_builtin(&mut self, field: &str, content: &str) { - self.emit_base(field, content, true) + fn emit_only_builtin(&mut self, field: &str, content: &str, param: bool) { + self.emit_base(field, content, true, param) } - fn emit_only_loadable(&mut self, field: &str, content: &str) { - self.emit_base(field, content, false) + fn emit_only_loadable(&mut self, field: &str, content: &str, param: bool) { + self.emit_base(field, content, false, param) } fn emit(&mut self, field: &str, content: &str) { - self.emit_only_builtin(field, content); - self.emit_only_loadable(field, content); + self.emit_internal(field, content, false); + } + + fn emit_internal(&mut self, field: &str, content: &str, param: bool) { + self.emit_only_builtin(field, content, param); + self.emit_only_loadable(field, content, param); + } + + fn emit_param(&mut self, field: &str, param: &str, content: &str) { + let content = format!("{param}:{content}", param = param, content = content); + self.emit_internal(field, &content, true); + } + + fn emit_params(&mut self, info: &ModuleInfo) { + let Some(params) = &info.params else { + return; + }; + + for param in params { + let ops = param_ops_path(¶m.ptype); + + // Note: The spelling of these fields is dictated by the user space + // tool `modinfo`. + self.emit_param("parmtype", ¶m.name, ¶m.ptype); + self.emit_param("parm", ¶m.name, ¶m.description); + + write!( + self.param_buffer, + " + pub(crate) static {param_name}: + ::kernel::module_param::ModuleParamAccess<{param_type}> = + ::kernel::module_param::ModuleParamAccess::new({param_default}); + + #[link_section = \"__param\"] + #[used] + static __{module_name}_{param_name}_struct: + ::kernel::module_param::RacyKernelParam = + ::kernel::module_param::RacyKernelParam(::kernel::bindings::kernel_param {{ + name: if cfg!(MODULE) {{ + ::kernel::c_str!(\"{param_name}\").as_bytes_with_nul() + }} else {{ + ::kernel::c_str!(\"{module_name}.{param_name}\").as_bytes_with_nul() + }}.as_ptr(), + // SAFETY: `__this_module` is constructed by the kernel at load time + // and will not be freed until the module is unloaded. + #[cfg(MODULE)] + mod_: unsafe {{ + (&::kernel::bindings::__this_module + as *const ::kernel::bindings::module) + .cast_mut() + }}, + #[cfg(not(MODULE))] + mod_: ::core::ptr::null_mut(), + ops: &{ops} as *const ::kernel::bindings::kernel_param_ops, + perm: 0, // Will not appear in sysfs + level: -1, + flags: 0, + __bindgen_anon_1: + ::kernel::bindings::kernel_param__bindgen_ty_1 {{ + arg: {param_name}.as_mut_ptr().cast() + }}, + }}); + ", + module_name = info.name, + param_type = param.ptype, + param_default = param.default, + param_name = param.name, + ops = ops, + ) + .unwrap(); + } + } +} + +fn param_ops_path(param_type: &str) -> &'static str { + match param_type { + "i8" => "::kernel::module_param::PARAM_OPS_I8", + "u8" => "::kernel::module_param::PARAM_OPS_U8", + "i16" => "::kernel::module_param::PARAM_OPS_I16", + "u16" => "::kernel::module_param::PARAM_OPS_U16", + "i32" => "::kernel::module_param::PARAM_OPS_I32", + "u32" => "::kernel::module_param::PARAM_OPS_U32", + "i64" => "::kernel::module_param::PARAM_OPS_I64", + "u64" => "::kernel::module_param::PARAM_OPS_U64", + "isize" => "::kernel::module_param::PARAM_OPS_ISIZE", + "usize" => "::kernel::module_param::PARAM_OPS_USIZE", + t => panic!("Unsupported parameter type {}", t), } } +fn expect_param_default(param_it: &mut token_stream::IntoIter) -> String { + assert_eq!(expect_ident(param_it), "default"); + assert_eq!(expect_punct(param_it), ':'); + let sign = try_sign(param_it); + let default = try_literal(param_it).expect("Expected default param value"); + assert_eq!(expect_punct(param_it), ','); + let mut value = sign.map(String::from).unwrap_or_default(); + value.push_str(&default); + value +} + #[derive(Debug, Default)] struct ModuleInfo { type_: String, @@ -99,6 +203,50 @@ struct ModuleInfo { description: Option, alias: Option>, firmware: Option>, + params: Option>, +} + +#[derive(Debug)] +struct Parameter { + name: String, + ptype: String, + default: String, + description: String, +} + +fn expect_params(it: &mut token_stream::IntoIter) -> Vec { + let params = expect_group(it); + assert_eq!(params.delimiter(), Delimiter::Brace); + let mut it = params.stream().into_iter(); + let mut parsed = Vec::new(); + + loop { + let param_name = match it.next() { + Some(TokenTree::Ident(ident)) => ident.to_string(), + Some(_) => panic!("Expected Ident or end"), + None => break, + }; + + assert_eq!(expect_punct(&mut it), ':'); + let param_type = expect_ident(&mut it); + let group = expect_group(&mut it); + assert_eq!(group.delimiter(), Delimiter::Brace); + assert_eq!(expect_punct(&mut it), ','); + + let mut param_it = group.stream().into_iter(); + let param_default = expect_param_default(&mut param_it); + let param_description = expect_string_field(&mut param_it, "description"); + expect_end(&mut param_it); + + parsed.push(Parameter { + name: param_name, + ptype: param_type, + default: param_default, + description: param_description, + }) + } + + parsed } impl ModuleInfo { @@ -114,6 +262,7 @@ fn parse(it: &mut token_stream::IntoIter) -> Self { "license", "alias", "firmware", + "params", ]; const REQUIRED_KEYS: &[&str] = &["type", "name", "license"]; let mut seen_keys = Vec::new(); @@ -143,6 +292,7 @@ fn parse(it: &mut token_stream::IntoIter) -> Self { "license" => info.license = expect_string_ascii(it), "alias" => info.alias = Some(expect_string_array(it)), "firmware" => info.firmware = Some(expect_string_array(it)), + "params" => info.params = Some(expect_params(it)), _ => panic!( "Unknown key \"{}\". Valid keys are: {:?}.", key, EXPECTED_KEYS @@ -186,33 +336,35 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { let info = ModuleInfo::parse(&mut it); let mut modinfo = ModInfoBuilder::new(info.name.as_ref()); - if let Some(author) = info.author { - modinfo.emit("author", &author); + if let Some(author) = &info.author { + modinfo.emit("author", author); } - if let Some(authors) = info.authors { + if let Some(authors) = &info.authors { for author in authors { - modinfo.emit("author", &author); + modinfo.emit("author", author); } } - if let Some(description) = info.description { - modinfo.emit("description", &description); + if let Some(description) = &info.description { + modinfo.emit("description", description); } modinfo.emit("license", &info.license); - if let Some(aliases) = info.alias { + if let Some(aliases) = &info.alias { for alias in aliases { - modinfo.emit("alias", &alias); + modinfo.emit("alias", alias); } } - if let Some(firmware) = info.firmware { + if let Some(firmware) = &info.firmware { for fw in firmware { - modinfo.emit("firmware", &fw); + modinfo.emit("firmware", fw); } } // Built-in modules also export the `file` modinfo string. let file = std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable"); - modinfo.emit_only_builtin("file", &file); + modinfo.emit_only_builtin("file", &file, false); + + modinfo.emit_params(&info); format!( " @@ -370,14 +522,17 @@ unsafe fn __exit() {{ __MOD.assume_init_drop(); }} }} - {modinfo} }} }} + mod module_parameters {{ + {params} + }} ", type_ = info.type_, name = info.name, modinfo = modinfo.buffer, + params = modinfo.param_buffer, initcall_section = ".initcall6.init" ) .parse() diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 1fc7a1be6b6d..c04cc07b3249 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -10,6 +10,12 @@ authors: ["Rust for Linux Contributors"], description: "Rust minimal sample", license: "GPL", + params: { + test_parameter: i64 { + default: 1, + description: "This parameter has a default of 1", + }, + }, } struct RustMinimal { @@ -20,6 +26,10 @@ impl kernel::Module for RustMinimal { fn init(_module: &'static ThisModule) -> Result { pr_info!("Rust minimal sample (init)\n"); pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); + pr_info!( + "test_parameter: {}\n", + *module_parameters::test_parameter.get() + ); let mut numbers = KVec::new(); numbers.push(72, GFP_KERNEL)?; From patchwork Fri Mar 21 09:17:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Hindborg X-Patchwork-Id: 14025052 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 654481EB5F4; Fri, 21 Mar 2025 09:18:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742548722; cv=none; b=gwOFu1LySYh7TCxsHr0GCQkw6tKNh3yno7XF77ScFSyv5YCA05fnI9hm2u0Ga473qNilPg/3hP3De6ZIER8/LJKlLUd2Mu+9SRiLJU8dTCdSpNXzEtcOrKMUKg/CqypJOmTQHzRV8INqN/mgF+ldx19c9RN4wCHqDgtSuZIItcU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742548722; c=relaxed/simple; bh=wc7GsbS3qt1jWIEplofQPOfdWK8wEAPOvoSH0Kax8jM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GpLZwP10FlNr2WqkU2in17AU+0VQH5ldYoadjQpjrH9IQe1qd5qHcoII8A+8ZBbamxGbMu5ynrIU/tPMX61UeJ5QrTd/xhNj+oyV9t6kt7lu8peohyLdWmpiqOf3IQnn++Cnev/hRSLpRE8HRfjCIPfoRwlINiOUVkI3fYhko5Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bnEmDsG4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bnEmDsG4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AACC9C4CEE8; Fri, 21 Mar 2025 09:18:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1742548721; bh=wc7GsbS3qt1jWIEplofQPOfdWK8wEAPOvoSH0Kax8jM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=bnEmDsG4A9yeTbbp4PRxZLSiccJvCgkZieX7EoSOmgtR5oqeQlgXrfuK1O6pYKxTJ YDcZwRA3nJNWun844kqDyfVy6Yhs/x5/OfduWOFBmrXOcmLMokSGzFlf6hE0ZY8QKf 1fIcWCYoEetJZ/zh/FXxozGWHNsq6gnixLv0vGc5hPfNv2UN7wQGM6cG5rw6Vlwri+ okr8BX2LuKh2TCu2qWo7+zTvafgtJcGQrT0m83+gVaXSAhOXqbtk+wpbsPQ2Tta/Gk xMhb0WifVDDfAS6LjqVLGvy5hVM5I9s+w9etYsNF/5x9DSYYYlR/weO6FxuOmzXotD UaUV0xRep9EuQ== From: Andreas Hindborg Date: Fri, 21 Mar 2025 10:17:36 +0100 Subject: [PATCH v9 3/3] modules: add rust modules files to MAINTAINERS Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250321-module-params-v3-v9-3-28b905f2e345@kernel.org> References: <20250321-module-params-v3-v9-0-28b905f2e345@kernel.org> In-Reply-To: <20250321-module-params-v3-v9-0-28b905f2e345@kernel.org> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Masahiro Yamada , Nathan Chancellor , Nicolas Schier , Luis Chamberlain , Danilo Krummrich Cc: Trevor Gross , Adam Bratschi-Kaye , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org, Petr Pavlu , Sami Tolvanen , Daniel Gomez , Simona Vetter , Greg KH , Fiona Behrens , Daniel Almeida , linux-modules@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=775; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=wc7GsbS3qt1jWIEplofQPOfdWK8wEAPOvoSH0Kax8jM=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBn3S7FszSHexMdVAg4ncDmcBnpX0wowDdFhJXAd 11UhFvoJA+JAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ90uxQAKCRDhuBo+eShj d6QKEAC41SM5t9JXRgTwU5y42PWjmXeCeVBNkFWVY4sx2mP+2M0k1fmYVjXzT5E1hoLS7M8s69A cOkXUj4J7TNr1jqPNAUK1sMUUT4enBU3lNYzoH9BgTBm3yg5buhFFHquCMufd02UI4ZakI+pQeU 2kaWGTTyx8ibT6Kq45Tc7cm1bS888qYMmXlzwKMSnWouKFG1G//2Ql8HGvMz3xVHfmPvaL8zed2 rK8HZRwZBzDBAksy+GdBFS6G9fkXylDcyVPrM+8Y01Nj75wKyCjHaPdSdEZp5yaKobMbuE2/3v4 3hxzmZm2waSG+iLHvhgfBTZA+2pQCX4Dv9UJlAS5EDcauX3F2+S62y9T7af9ZTbr5bpMMslDxBR c4/1PZuu2/kaoBQ9YiAs+yh5NYaarPFL9LQFirrOj0Hi1w4rFoE+Y9Ceu8XDTNjACO4TP7WVZgO 1CGO54Xkw0X1o9ZRX+ygVe9N0ipdOK7Q8p3LBhPAYEaP/YXCrMGfG3YwXyVsbZ8Rn9Pzkeu6Zl+ T42kv2B7KRDeGBSUrctcQoh2BgH6kpxF8DBzHFwz+4jmx1FjsNpeNI6nHx4XyDIRUMsTj3V676U TUb1QGU/W2ODotR/IBr4lCt/SJSWc5VlpnsuICXhnAjuP4Xx7wC0EAealVWEg/TjigwWmay6ib2 HH8IQ6wRa2EXYWQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The module subsystem people agreed to maintain rust support for modules [1]. Thus, add entries for relevant files to modules entry in MAINTAINERS. Link: https://lore.kernel.org/all/0d9e596a-5316-4e00-862b-fd77552ae4b5@suse.com/ [1] Acked-by: Daniel Gomez Signed-off-by: Andreas Hindborg --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index cbf84690c495..94f5d9e8e7ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16007,6 +16007,8 @@ F: include/linux/kmod.h F: include/linux/module*.h F: kernel/module/ F: lib/test_kmod.c +F: rust/kernel/module_param.rs +F: rust/macros/module.rs F: scripts/module* F: tools/testing/selftests/kmod/