From nobody Mon Feb 9 13:02:53 2026 Received: from mail-ot1-f41.google.com (mail-ot1-f41.google.com [209.85.210.41]) (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 2C8D95B5AB for ; Wed, 28 Jan 2026 23:21:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769642464; cv=none; b=A3aI5DnFjYsPSlARTRub+Xp5IyKsY9SJIxoKrCxO6bLA6hYae4Rxe9Y+BdJLEm694+0l6OKBFQOPjKmNIqKwGoW2UQqDYl82BWM1aMz1FryENX0TXyeu2dBRDCmpYWKW2PxFdgtLqLZqq/sThWHSSr1S3yRya1TzjNYN/z+l06U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769642464; c=relaxed/simple; bh=/hRPwTjZmIfPfF6SXM76s/bDbQMZnDCoJvb+xB1fsC4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Kwbr6wzzRWGAEX8H7n2EthFVSEZQT/7k65CcpBfQZgkQF9QBMeJVlR3FAEc0cG6zk+nW11ezaJgvrkzXeGlMXNCFgHQtlxAdt3eF/c6sD2kpdtFLqZBTX7e3ENfjUDcoAvCcRSDnp38sXlsPBBNjBuDMb2Yionaw9Q0tALcoum0= 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=OSwMD3I+; arc=none smtp.client-ip=209.85.210.41 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="OSwMD3I+" Received: by mail-ot1-f41.google.com with SMTP id 46e09a7af769-7d195166b2cso217645a34.3 for ; Wed, 28 Jan 2026 15:21:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769642461; x=1770247261; 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=OSwMD3I+4TaljjLEeOpfWhDV6TjbHWxSSXgIPpR5rAwACY9DP0rC0jklYeXw/z0Jqa 2O4C4rodt0gvSN02DORKNFYEoxITOLiddp/hpNGoLm7wrHZNGi5HE3Wk1vRZ1qn9X5PP EI61TAjj1FTyGkk5VyZdVBecNpfS4jkKn9iyyBoB+Mfp+UAQOhwFP0zmMWyWt/pXo+Og mCqod8aunjIyD8r/S0xLUVu6IFZQNLWkJq948W6MbVf/iIrKOXYqWJ6GI7JwBapf0kO6 SDY6BzypwbScTLPkb8+v/wNPIQU7gcKWgVw2GVK5smpIr228mf1pUTvX4g8D/T36Eoza Wa4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769642461; x=1770247261; 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=DnvZbngTFCd4pB5QPTkxv1DBjnTFEk+Jexf4oUN/1P4+ub8AFXBzdYY/PxvU9x2lrI zszdvmOXCYkNtXLhHfJ85F5/c5MkirxWPI+6pMhuUnN8O3YhpoL1TIUeWmGJR8GjJHVE U91OoaCEXhD4eB9uGZd90OAm7eXqetZWstIUp/maUKr6qVc+5vwP4JLXyCZvSFFHS0Z4 qvwIRlF6TM/Ddo9pYtU96S7K16v8bPCu6Th86PZhhRI8A4O7OMPYVqK6J+FqksLJnm+N dGE+z+k62h/gWokatE5W6lxbSnn3E43nUl/3Vo7X+kgvHVYpYHpcdvgwSygIBlOICEqv M/vQ== X-Gm-Message-State: AOJu0YxUUd9/HnzSaZISqmXuOMxO3eGmkDnUqC5tNfpuJBb9BBNX6xvz KIMR8bAXnDd1TWem2m/W4l7SKsns/OHstk6hamE5TeiStnl3+UDHTre6FCM39Q== X-Gm-Gg: AZuq6aLa5Omwxk2DZtcsSfthYdF+RbnpHsD5GXQiCbR4wbC3atLm1OFt9DlKxtWiWfo KlqGEh9ZhzkYzH0xZn5naoevbHMgFrXHPd/o4nW+2XmoU2y5o4c9Z+OZ/eWXa5I5oU1vmOFZpqH 4GA7wpvy7UA18VO6WL/PRat0D57U3V4yc6FKT4/H8+CGPbYnBD4izXQu5gkwndKyjdHWaAPCsCN 8gZK2jXWVIzC+Illm1Hs7+00fYlSxvV5r3h7WSaDdKagAKD2RJy4CGebpH9lXvowfKbGiZ3OUJs tstNN8qOHCY3FzOjo0b8rOr1n6EZ5xoqJGptP2kVMPDH1CeAAczwefJ6GutR409M1G7JoiR2by2 iQeU/iwW800j7JhR3bQ/COe2412c0nXiSW4DH/ShPHQ7OPWWz8EGl9jyL1WqAqfNASkgn8ej/J8 +kJChSuWajgpCOR63MCe5hFdBwxM2JwCcIlw4sLAa5elIrKZ1I1X456Qi/DKq0SUMYQsfDOsiGp owLQZIA1D7TpAs= 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: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 b= e 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 retu= rn valid pointers and +/// must be true inverses of each other; that is, they must satisfy the fo= llowing invariants: - +/// `field_container_of(raw_get_field(ptr)) =3D=3D ptr` for any `ptr: *mut= Self`. - +/// `raw_get_field(field_container_of(ptr)) =3D=3D 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 _: () =3D { +/// 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}; =20 #[doc(no_inline)] -pub use macros::{export, fmt, kunit_tests, module, vtable}; +pub use macros::{export, fmt, kunit_tests, module, vtable, HasField}; =20 pub use pin_init::{init, pin_data, pin_init, pinned_drop, InPlaceWrite, In= it, PinInit, Zeroable}; =20 +pub use super::field::{Field, HasField}; + pub use super::{build_assert, build_error}; =20 // `super::std_vendor` is hidden, which makes the macro inline for some re= ason. 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, Generi= cs, Ident, Result, Type, +}; + +fn impl_has_field(base: &Ident, field: &Ident, ty: &Type, generics: &Gener= ics) -> TokenStream { + let (impl_generics, type_generics, where_clause) =3D generics.split_fo= r_impl(); + + quote!( + // SAFETY: The implementation of `raw_get_field()` only compiles i= f 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 =3D vec![]; + + if let Fields::Named(fields) =3D &st.fields { + for field in &fields.named { + let found =3D field + .attrs + .iter() + .find(|attr| attr.path().is_ident("field")); + + if found.is_some() { + if let Some(name) =3D &field.ident { + impls.push(impl_has_field(ident, name, &field.ty, gene= rics)); + } + } + } + + 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 =3D input.span(); + let data =3D &input.data; + let ident =3D &input.ident; + let generics =3D &input.generics; + + if let Data::Struct(st) =3D data { + let impls =3D 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 @@ =20 mod concat_idents; mod export; +mod field; mod fmt; mod helpers; mod kunit; @@ -486,3 +487,13 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStre= am) -> 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() +} --=20 2.50.1 (Apple Git-155)