From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oo1-f54.google.com (mail-oo1-f54.google.com [209.85.161.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19A3321D5AA for ; Wed, 28 Jan 2026 23:46:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.54 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769643995; cv=none; b=LQDHxaOiCTfL7Rt31Q/nGTaPZl8ZG8qhWBjQOK2ZmtOV2LxqyNHhMoLec1msZ1Ndpom2cvUTZsdu1sq/upcGjIs+xUauLR2q6WyLjJinp9hNyjulYLfKK9rqB6TqNVLctZKRrSi1PdTHxBxBGQcAK0c6H9WZxR1HZmrUadeLsZA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769643995; c=relaxed/simple; bh=/hRPwTjZmIfPfF6SXM76s/bDbQMZnDCoJvb+xB1fsC4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eOP8zgpAnwT24Jz6iGBNZh8C9EFxZ2ly4QcY8iiC/TEH7YbGdzCHgFUoe64hWzisBaAGROe2hwI6ZenmD1I/CVdNllRt2ao3Pznroc5ROWql0KTeqiaR02zMNdNoKI7jOfVQZ+itQcph9jyrfkZ5/zUc03CYHHJb9qWb9u3LuTs= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HG0K+ktx; arc=none smtp.client-ip=209.85.161.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HG0K+ktx" Received: by mail-oo1-f54.google.com with SMTP id 006d021491bc7-662fe3ff6f6so248519eaf.0 for ; Wed, 28 Jan 2026 15:46:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769643993; x=1770248793; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:feedback-id:from:to:cc:subject :date:message-id:reply-to; bh=NNS0YAOwbVOFsEM5UfR8k15PlxPXeRQf3X2TS9uzn6I=; b=HG0K+ktxvfgAsDGCY6OrkBI/pkcNKJ4p29Ij6z7kLKxm2H3vlUJN4iijc292dKtv3R awosEH5cpnFUlE0ALt3zhdA1ovUT8O0OZVwwWyMqND1io4x+9/MyvYtfbIAhDGMwo/AS teltxuF4ivRhfmCKI+2TsKzxG3UrflAqSL38isALIh+gpTzIonTitMGjM7JjgUwWJ+Cy Sg4DQRwGSjQYn1xtkZ4t25OIUDLfY0icKOgEbtr/VbPlHyP+stVGinF+cGI0sRsXAeRF hEbXyB1ydfKCnfcvNygIdieePxt2azCQK/yiWlV/64w4w5LkVHmCnuTIMa3RLyqaXmhB y7yQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769643993; x=1770248793; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:feedback-id:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=NNS0YAOwbVOFsEM5UfR8k15PlxPXeRQf3X2TS9uzn6I=; b=YXh6a6bKLTl7sO/zM6v0pGlVjiLqWfChYs1k4KANetziN9jU4aYyWlxLY61BD0GWdK k9Gw+0m/hAgJwliMZX7mtFHfugHj+F0jO/db5IP5WK4YV+CUeHgJgfg7COmvR/9ldk9v oROnC7TppEWenWxHg9dXQD8inN9lvs6CbOGKtnXRGzs+jONOg7lRU6E0OJHn1LnrJYs+ +j0L/ntEiSgEhWXAQmefE9i5hUJQUHMY+1LnW+S+yz+O2mbfRRTxACT0jZYi92KISDs4 pj1EVOd+x2SxPo7ev1M/0yhclABQnBMgmQ/FE2Af1b7aQBn7IHT+8Z1VUcKQPI+1TkuF qnQQ== X-Forwarded-Encrypted: i=1; AJvYcCVgWQoMyYSi6FfRsaG0lSD5U7QTxvP04tRrdjwb95HvjNZL+nmb6cwN1GVsil/QLysHEH6hJ94jphOEMn5mfQ==@vger.kernel.org X-Gm-Message-State: AOJu0YyCUOk6V9+DgCirBkoigyxUXxoS2qoFQ/LtTeiBiGFKow2RwciP uvFvi4AlXaxuhymIqu90o6rNYhNrd9C/rCeNY0phQqbk2VxuEDiwag7G70980g== X-Gm-Gg: AZuq6aLfP4onUVPwIRh5XFqF5hvNpXsLHJoshqPSY72XIHD1oz6ViHCGtAj9hXkievx zUJNspdSzHd9Exg1c8ib1xfjelzY9ctUJwwluavurkRnrVUB55aPL5jGIIEN3sU+8QBW81631rE TRIOBiFHsmZ/PNsURO1NEs/y7We5vD99qLs/7/xDPaxhcf1Yddo5Qvp4zfHwFRIq9MHphTMKmuD SN368ChquXAnNQeb1EEnv+/K4wwSGq6cnr3Dxzm2h4pTNsCQsGJASTpIH1aDyHn37+6OM+9XCyu ur6kjSrdUFBjLT1Eaj8MOchp1EHqW7IbCRD8SFRfQ0TZTC2VmJjd6WahOyx20RcRM1ruyg/wUnT +ElSmbPfIOUrwUO+tImqW8voH05RbjdIdFhtuCSl4+giJ3guhIAjGjtjOfDLIPgfUnNBtDQUXY1 e9xxIyO4JWvcQyzRcL6HAS+uYHtKeArvdiSX5jj5wVJxdAbyjZhQGHPIq2wX0eN5pJjWHlVaCpJ nOxCrAAnm/7InQ= X-Received: by 2002:ad4:5de1:0:b0:894:6e23:3c3f with SMTP id 6a1803df08f44-894cc81e09amr106899066d6.20.1769637215910; Wed, 28 Jan 2026 13:53:35 -0800 (PST) Received: from fauth-a1-smtp.messagingengine.com (fauth-a1-smtp.messagingengine.com. [103.168.172.200]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-894d3740f6dsm25243756d6.37.2026.01.28.13.53.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Jan 2026 13:53:35 -0800 (PST) Received: from phl-compute-07.internal (phl-compute-07.internal [10.202.2.47]) by mailfauth.phl.internal (Postfix) with ESMTP id A8058F4006D; Wed, 28 Jan 2026 16:53:34 -0500 (EST) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-07.internal (MEProxy); Wed, 28 Jan 2026 16:53:34 -0500 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgdduieeggeehucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhephffvvefufffkofgjfhgggfestdekredtredttdenucfhrhhomhepuehoqhhunhcu hfgvnhhguceosghoqhhunhdrfhgvnhhgsehgmhgrihhlrdgtohhmqeenucggtffrrghtth gvrhhnpefhkeetffffleefffelueduhfeufedtffdvuedtkeejhfeivdelkedvffejvdeu keenucffohhmrghinhepphgrthhhrdhishenucevlhhushhtvghrufhiiigvpedtnecurf grrhgrmhepmhgrihhlfhhrohhmpegsohhquhhnodhmvghsmhhtphgruhhthhhpvghrshho nhgrlhhithihqdeiledvgeehtdeigedqudejjeekheehhedvqdgsohhquhhnrdhfvghngh eppehgmhgrihhlrdgtohhmsehfihigmhgvrdhnrghmvgdpnhgspghrtghpthhtohepgedv pdhmohguvgepshhmthhpohhuthdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvh hgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehruhhsthdqfhhorhdqlhhinhhu giesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehrtghusehvghgvrhdrkh gvrhhnvghlrdhorhhgpdhrtghpthhtohepghhrvghgkhhhsehlihhnuhigfhhouhhnuggr thhiohhnrdhorhhgpdhrtghpthhtoheprghrvhgvsegrnhgurhhoihgurdgtohhmpdhrtg hpthhtohepthhkjhhoshesrghnughrohhiugdrtghomhdprhgtphhtthhopegsrhgruhhn vghrsehkvghrnhgvlhdrohhrghdprhgtphhtthhopegtmhhllhgrmhgrshesghhoohhglh gvrdgtohhmpdhrtghpthhtoheprghlihgtvghrhihhlhesghhoohhglhgvrdgtohhm X-ME-Proxy: Feedback-ID: iad51458e:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 28 Jan 2026 16:53:34 -0500 (EST) From: Boqun Feng To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, rcu@vger.kernel.org Cc: Greg Kroah-Hartman , =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= , Todd Kjos , Christian Brauner , Carlos Llamas , Alice Ryhl , Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , FUJITA Tomonori , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , "Yury Norov (NVIDIA)" , Vitaly Wool , Tamir Duberstein , Viresh Kumar , Daniel Almeida , Mitchell Levy , David Gow , Peter Novak , =?UTF-8?q?Jos=C3=A9=20Exp=C3=B3sito?= Subject: [RFC PATCH 1/7] rust: types: Introduce HasField trait and derive macro Date: Wed, 28 Jan 2026 13:53:24 -0800 Message-ID: <20260128215330.58410-2-boqun.feng@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260128215330.58410-1-boqun.feng@gmail.com> References: <20260128215330.58410-1-boqun.feng@gmail.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit In order to unify all the `Has*` infrastrutures, a generic `HasField` trait is added along with a derive macro `#[derive(HasField)]` which allows generate implementation of `HasField` automatically, e.g. #[derive(HasField)] struct Base { a: i32; b: i32; #[field] f1: Field, #[field] f2: Field, } two implementations `impl HasField>` and `impl HasField>` will be generated with `&raw mut` and `container_of!()`. This simplifies the usage of the current `Has*` traits, namely `HasWork` and `HasHrTimer`, and eases the introduction of more `Field` type in the future. Signed-off-by: Boqun Feng --- rust/kernel/field.rs | 73 ++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + rust/kernel/prelude.rs | 4 +- rust/macros/field.rs | 85 ++++++++++++++++++++++++++++++++++++++++++ rust/macros/lib.rs | 11 ++++++ 5 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/field.rs create mode 100644 rust/macros/field.rs diff --git a/rust/kernel/field.rs b/rust/kernel/field.rs new file mode 100644 index 000000000000..347387731d71 --- /dev/null +++ b/rust/kernel/field.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Field types to describe a field inside a struct. + +/// A field. +/// +/// The generic type `T` is usually the type that contains the field. For some field types, it +/// needs to be generic over the type containing it, because it needs to be initialized with +/// container-type-specific callbacks. For other types, simply implement [`Field`] for all `T` +/// to indicate there is no restriction. +pub trait Field: Sized {} + +/// A struct `T` that has a field `F`. +/// +/// # Safety +/// +/// The methods [`raw_get_field()`] and [`field_container_of()`] must return valid pointers and +/// must be true inverses of each other; that is, they must satisfy the following invariants: - +/// `field_container_of(raw_get_field(ptr)) == ptr` for any `ptr: *mut Self`. - +/// `raw_get_field(field_container_of(ptr)) == ptr` for any `ptr: *mut Field`. +/// +/// Use [`macros::HasField`] to generate the impls automatically. +/// +/// # Examples +/// +/// ``` +/// # use core::marker::PhantomData; +/// use kernel::{ +/// macros::HasField, +/// field::{ +/// Field, +/// HasField, // +/// }, // +/// }; +/// +/// struct Work { +/// _x: isize, +/// _inner: PhantomData, +/// } +/// +/// // Declare that `Work` is a `Field`. +/// impl Field for Work {} +/// +/// #[derive(HasField)] +/// struct B { +/// #[field] +/// w: Work, +/// a: i32, +/// } +/// +/// const _: () = { +/// const fn assert_has_field>>() { } +/// assert_has_field::(); +/// }; +/// ``` +/// +/// [`raw_get_field()`]: HasField::raw_get_field +/// [`field_container_of()`]: HasField::field_container_of +pub unsafe trait HasField> { + /// Returns a pointer to the [`Field`] field. + /// + /// # Safety + /// + /// The provided pointer must point at a valid struct of type `Self`. + unsafe fn raw_get_field(ptr: *mut Self) -> *mut F; + + /// Returns a pointer to the struct containing [`Field`] field. + /// + /// # Safety + /// + /// The pointer must point at a [`Field`] field in a struct of type `Self`. + unsafe fn field_container_of(ptr: *mut F) -> *mut Self; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f812cf120042..36259aac1843 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -93,6 +93,7 @@ pub mod drm; pub mod error; pub mod faux; +pub mod field; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; pub mod fmt; diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 2877e3f7b6d3..3668ef42046b 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -25,10 +25,12 @@ pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec, Vec}; #[doc(no_inline)] -pub use macros::{export, fmt, kunit_tests, module, vtable}; +pub use macros::{export, fmt, kunit_tests, module, vtable, HasField}; pub use pin_init::{init, pin_data, pin_init, pinned_drop, InPlaceWrite, Init, PinInit, Zeroable}; +pub use super::field::{Field, HasField}; + pub use super::{build_assert, build_error}; // `super::std_vendor` is hidden, which makes the macro inline for some reason. diff --git a/rust/macros/field.rs b/rust/macros/field.rs new file mode 100644 index 000000000000..3d32e5089f27 --- /dev/null +++ b/rust/macros/field.rs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{ + spanned::Spanned, Data, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Result, Type, +}; + +fn impl_has_field(base: &Ident, field: &Ident, ty: &Type, generics: &Generics) -> TokenStream { + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + + quote!( + // SAFETY: The implementation of `raw_get_field()` only compiles if the field has the + // right type. + unsafe impl #impl_generics + HasField<#base #type_generics, #ty> + for #base #type_generics + #where_clause { + #[inline(always)] + unsafe fn raw_get_field(ptr: *mut Self) -> *mut #ty { + // SAFETY: Per function safety requirement, the pointer is valid. + unsafe { &raw mut (*ptr).#field } + } + + #[inline(always)] + unsafe fn field_container_of(ptr: *mut #ty) -> *mut Self { + // SAFETY: Per function safety requirement, the pointer is valid, and it points + // to the right field of the struct. + unsafe { kernel::container_of!(ptr, Self, #field) } + } + } + ) +} +fn handle_struct( + ident: &Ident, + generics: &Generics, + st: &DataStruct, + span: Span, +) -> Result { + let mut impls = vec![]; + + if let Fields::Named(fields) = &st.fields { + for field in &fields.named { + let found = field + .attrs + .iter() + .find(|attr| attr.path().is_ident("field")); + + if found.is_some() { + if let Some(name) = &field.ident { + impls.push(impl_has_field(ident, name, &field.ty, generics)); + } + } + } + + Ok(quote!( + #(#impls)* + )) + } else { + Err(Error::new( + span, + "`#[derive(HasField)]` only supports structs with named fields", + )) + } +} + +pub(crate) fn has_field(input: DeriveInput) -> Result { + let span = input.span(); + let data = &input.data; + let ident = &input.ident; + let generics = &input.generics; + + if let Data::Struct(st) = data { + let impls = handle_struct(ident, generics, st, span)?; + + Ok(quote!( + #impls + )) + } else { + Err(Error::new_spanned( + input, + "`#[derive(HasField)]` only supports structs", + )) + } +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 85b7938c08e5..4fccca0e11af 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -13,6 +13,7 @@ mod concat_idents; mod export; +mod field; mod fmt; mod helpers; mod kunit; @@ -486,3 +487,13 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|e| e.into_compile_error()) .into() } + +/// Derives the implementation for `HasField`. +/// +/// See the documentation of `HasField` for more information. +#[proc_macro_derive(HasField, attributes(field))] +pub fn has_field(input: TokenStream) -> TokenStream { + field::has_field(parse_macro_input!(input)) + .unwrap_or_else(|e| e.into_compile_error()) + .into() +} -- 2.50.1 (Apple Git-155)