From nobody Thu Jun 11 21:24:55 2026 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (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 4512A395240 for ; Wed, 3 Jun 2026 14:08:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780495719; cv=none; b=FO2Qky+WN3gq1P9k5QVyrru0gPXtMk0ToxroHxZ35dZtN+X7gK0smfsr3IRdg9JoxUN9zkTgr0Ww1rNXKnaneAAuMG2fovouoj3NMbymz7tlgmSHYpq0nDOqNT8gnVMMcqvQDP4P3vpBUDbq6aVgyP6AxJmR4arr5w8juX68Pfw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780495719; c=relaxed/simple; bh=UZBzC4mkOBLTvp3u5L/CRvBTZs/ZqdIzznnBi48JzTw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=tHTJOX2RUrLVsaPo/Maar19sC1OYoungIPqUJFJCFz1KAQLKQxxLm7PDcNDS4MYtOI9mSqkHKmfjLqXvVgyHjbgDrrbqFbYykJY7RddeHbFrUvuXtkIl8p1nb/FAQaSDh9Yaw9troshMagwhd3Kmt8ftSTBdgs4l5KrOarvDA8Y= 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=ib9wYdgP; arc=none smtp.client-ip=209.85.128.44 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="ib9wYdgP" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-490b8ac62baso5247885e9.0 for ; Wed, 03 Jun 2026 07:08:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780495716; x=1781100516; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:from:to:cc:subject:date:message-id:reply-to; bh=bQjixubEe0JOxXIG6jNUIWub0O0mCeLRwASvqRJwK1Q=; b=ib9wYdgPc7YtpE9TChknf1Ls0wokFBXp18RGN/w5/3aVGBJqnqOv3MHj2I3Vx3y5Fc 66um7bdNkf0fwsYpQ7VzErtPmGi1083Jx/rreycP21BfrFhZKhSc5kAB97ZwFVnXznku dzKjckI3efZV++72oGMsu+opvGknQXBj1TMCyb8q1OqEsFApdrV1Ca9Bx2yWYDrU7v2s vVglLPFAC5jWFMI1JpQ6sZ0i9kmV/D5Wt+GMm4ky2nSUOmZ8g/LzcIcoBDNBBi1hiThM eHlKlBTQh1mTzjjbz3h76nxq9dgK+I12KSfjEbkn3iKoycqp+xbpcVguOIjAr3YYC/pg 6Frg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780495716; x=1781100516; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=bQjixubEe0JOxXIG6jNUIWub0O0mCeLRwASvqRJwK1Q=; b=dT+kH5GPhst8tliDfzZanDws0xVzp2g9hAZhCN4DtGdTCpSzW5azhUE9AK3K+3uzhy A+Hc3BTq8uAVDfLWKaoIOrf2Mvq543cLEtK6ck/sdvya8KBbh+BdXZySJNJXF2NYMat6 URU0lfOwnNwJOrXEH1EuMjFSepQ2oJgIEzCLsBxhOrNHOF2ICwJR+DlohCMXIN2kJ0pC Ff+ch/JXSUYdEhjprpOi2oWbE4iDXeStPC/xBdEPCtWgPp02iPB1kmXRurqLG0KLWBr9 Z2/fxVk5Z/kfLb+quNnTS/IiXm/gMr87gvpUnvUYNVp0J54E9EDRhhygrBmP4p1W5l/x 8a1w== X-Gm-Message-State: AOJu0Yw9WOt5JDaASRymuuuHkPUuzAFm+WGHBvWd2cdgqvjMUD2ZBIws dUHiBeH2txvvki9jXfli4xkrOPD4FZs7QkR7DLAtto/rAxaPw9YytbUNEGK/cPXx X-Gm-Gg: Acq92OGqw6EAgdgKFbDp1HNpj0aTKumqk6ercVeys8vzsdsQJ8NY2IpofxYYeA3Ah1L +j9sw2hFPvoznbBaqF6SFqoNFii9EosQdJVOIKYcIWHNyuzyhhCuGon2ExxnLp5RjO+fImJAD+7 yLT9xDZIZqWV+SJx0AFfwUZKaCfhAs6iNcvIBtaIl9U37+OhYVjeB7/3IBVKzIgoI+b0BvcFCm9 5Mkx+Iu1q8VyAEwlvTt8vb8ZABgnbYE+x9mMDOd9htysaXICqIy0WlUTL1A+GoK0BzuAmumxOhE tA17eJmi/pTjUwnIK0U0fq5t7dqQ0GsyYwIEvvFhmdwQHS6iXEqMElt2ARCFSyZSIgFyqH8AQhz Pre3mMghuWCt7CiNa4NLyGdxoICN7Qma3sAtpsxHVlg4lFG2+SCcZIsKcEdkugiKg81smwj1JWJ I4V05ojc1fx/oPTOeZ9ezX1up1DEWEXeeAkcV9E2an/w== X-Received: by 2002:a05:600c:4f05:b0:490:9dc3:3483 with SMTP id 5b1f17b1804b1-490b610dc80mr41468315e9.2.1780495715232; Wed, 03 Jun 2026 07:08:35 -0700 (PDT) Received: from [172.16.16.15] ([195.100.225.50]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-490b0daefbbsm241404105e9.0.2026.06.03.07.08.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 03 Jun 2026 07:08:34 -0700 (PDT) From: Malte Wechter Date: Wed, 03 Jun 2026 16:08:29 +0200 Subject: [PATCH v2] rust: add procedural macro for declaring configfs attributes Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260603-configfs-syn-v2-1-cb58489c2647@gmail.com> X-B4-Tracking: v=1; b=H4sIAAAAAAAC/1XMQQ6CMBCF4auQWVszUwXElfcwLGqdwiTSmtY0E sLdrbhy+b/kfQskjsIJztUCkbMkCb6E3lVgR+MHVnIvDRp1g0dqlQ3eyeCSSrNX1BFjSwdE3UK 5PCM7eW/ctS89SnqFOG96pu/6g2qN/1AmRaqx9e2EprPs9GWYjDz2NkzQr+v6AQnWhlyoAAAA X-Change-ID: 20260417-configfs-syn-191e07130027 To: Andreas Hindborg , Breno Leitao , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Jens Axboe Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-block@vger.kernel.org, Malte Wechter X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780495713; l=21678; i=maltewechter@gmail.com; s=20260417; h=from:subject:message-id; bh=UZBzC4mkOBLTvp3u5L/CRvBTZs/ZqdIzznnBi48JzTw=; b=819Gqg6BDuM4DeUdBrWNLHy/EN8+U2IUQ98JFrQICMoF/kYW/zj39bnN283uiElCNDXnpC2Lz DoA+DDd+hP0CtIpO/yJl2fWRoYr1PKMXZlDtg+SGkXNN6dLrtsAkKXj X-Developer-Key: i=maltewechter@gmail.com; a=ed25519; pk=07WplWXZnwyLTMZOHNCIGcpoEutcMXU/JDY6f9VtxSY= Implement `configfs_attrs!` as a procedural macro using `syn`, this improves readability and maintainability. Remove the old macro and replace all uses with the new macro. Add the new macro implementation file to MAINTAINERS. Signed-off-by: Malte Wechter --- Changes in v2: - Add a try_parse helper function to macros/helpers.rs - Fix bug where 'child' configuration gets dropped if trailing comma is mis= sing (sashiko) - Link to v1: https://lore.kernel.org/r/20260520-configfs-syn-v1-1-6c5b80a9= cef2@gmail.com --- MAINTAINERS | 1 + drivers/block/rnull/configfs.rs | 2 +- rust/kernel/configfs.rs | 251 ------------------------------------= ---- rust/macros/configfs_attrs.rs | 182 +++++++++++++++++++++++++++++ rust/macros/helpers.rs | 15 +++ rust/macros/lib.rs | 85 ++++++++++++++ samples/rust/rust_configfs.rs | 2 +- 7 files changed, 285 insertions(+), 253 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd163..45f7a1ec93b45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6464,6 +6464,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/= a.hindborg/linux.git config F: fs/configfs/ F: include/linux/configfs.h F: rust/kernel/configfs.rs +F: rust/macros/configfs_attrs.rs F: samples/configfs/ F: samples/rust/rust_configfs.rs =20 diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 7c2eb5c0b7228..f28ec69d79846 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -4,8 +4,8 @@ use kernel::{ block::mq::gen_disk::{GenDisk, GenDiskBuilder}, configfs::{self, AttributeOperations}, - configfs_attrs, fmt::{self, Write as _}, + macros::configfs_attrs, new_mutex, page::PAGE_SIZE, prelude::*, diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs index 2339c6467325d..7a91e36677f52 100644 --- a/rust/kernel/configfs.rs +++ b/rust/kernel/configfs.rs @@ -791,254 +791,3 @@ fn as_ptr(&self) -> *const bindings::config_item_type= { self.item_type.get() } } - -/// Define a list of configfs attributes statically. -/// -/// Invoking the macro in the following manner: -/// -/// ```ignore -/// let item_type =3D configfs_attrs! { -/// container: configfs::Subsystem, -/// data: Configuration, -/// child: Child, -/// attributes: [ -/// message: 0, -/// bar: 1, -/// ], -/// }; -/// ``` -/// -/// Expands the following output: -/// -/// ```ignore -/// let item_type =3D { -/// static CONFIGURATION_MESSAGE_ATTR: kernel::configfs::Attribute< -/// 0, -/// Configuration, -/// Configuration, -/// > =3D unsafe { -/// kernel::configfs::Attribute::new({ -/// const S: &str =3D "message\u{0}"; -/// const C: &kernel::str::CStr =3D match kernel::str::CStr::f= rom_bytes_with_nul( -/// S.as_bytes() -/// ) { -/// Ok(v) =3D> v, -/// Err(_) =3D> { -/// core::panicking::panic_fmt(core::const_format_args= !( -/// "string contains interior NUL" -/// )); -/// } -/// }; -/// C -/// }) -/// }; -/// -/// static CONFIGURATION_BAR_ATTR: kernel::configfs::Attribute< -/// 1, -/// Configuration, -/// Configuration -/// > =3D unsafe { -/// kernel::configfs::Attribute::new({ -/// const S: &str =3D "bar\u{0}"; -/// const C: &kernel::str::CStr =3D match kernel::str::CStr::f= rom_bytes_with_nul( -/// S.as_bytes() -/// ) { -/// Ok(v) =3D> v, -/// Err(_) =3D> { -/// core::panicking::panic_fmt(core::const_format_args= !( -/// "string contains interior NUL" -/// )); -/// } -/// }; -/// C -/// }) -/// }; -/// -/// const N: usize =3D (1usize + (1usize + 0usize)) + 1usize; -/// -/// static CONFIGURATION_ATTRS: kernel::configfs::AttributeList =3D -/// unsafe { kernel::configfs::AttributeList::new() }; -/// -/// { -/// const N: usize =3D 0usize; -/// unsafe { CONFIGURATION_ATTRS.add::(&CONFIGURATION_MES= SAGE_ATTR) }; -/// } -/// -/// { -/// const N: usize =3D (1usize + 0usize); -/// unsafe { CONFIGURATION_ATTRS.add::(&CONFIGURATION_BAR= _ATTR) }; -/// } -/// -/// static CONFIGURATION_TPE: -/// kernel::configfs::ItemType ,C= onfiguration> -/// =3D kernel::configfs::ItemType::< -/// configfs::Subsystem, -/// Configuration -/// >::new_with_child_ctor::( -/// &THIS_MODULE, -/// &CONFIGURATION_ATTRS -/// ); -/// -/// &CONFIGURATION_TPE -/// } -/// ``` -#[macro_export] -macro_rules! configfs_attrs { - ( - container: $container:ty, - data: $data:ty, - attributes: [ - $($name:ident: $attr:literal),* $(,)? - ] $(,)? - ) =3D> { - $crate::configfs_attrs!( - count: - @container($container), - @data($data), - @child(), - @no_child(x), - @attrs($($name $attr)*), - @eat($($name $attr,)*), - @assign(), - @cnt(0usize), - ) - }; - ( - container: $container:ty, - data: $data:ty, - child: $child:ty, - attributes: [ - $($name:ident: $attr:literal),* $(,)? - ] $(,)? - ) =3D> { - $crate::configfs_attrs!( - count: - @container($container), - @data($data), - @child($child), - @no_child(), - @attrs($($name $attr)*), - @eat($($name $attr,)*), - @assign(), - @cnt(0usize), - ) - }; - (count: - @container($container:ty), - @data($data:ty), - @child($($child:ty)?), - @no_child($($no_child:ident)?), - @attrs($($aname:ident $aattr:literal)*), - @eat($name:ident $attr:literal, $($rname:ident $rattr:literal,)*), - @assign($($assign:block)*), - @cnt($cnt:expr), - ) =3D> { - $crate::configfs_attrs!( - count: - @container($container), - @data($data), - @child($($child)?), - @no_child($($no_child)?), - @attrs($($aname $aattr)*), - @eat($($rname $rattr,)*), - @assign($($assign)* { - const N: usize =3D $cnt; - // The following macro text expands to a call to `Attribut= e::add`. - - // SAFETY: By design of this macro, the name of the variab= le we - // invoke the `add` method on below, is not visible outsid= e of - // the macro expansion. The macro does not operate concurr= ently - // on this variable, and thus we have exclusive access to = the - // variable. - unsafe { - $crate::macros::paste!( - [< $data:upper _ATTRS >] - .add::(&[< $data:upper _ $name:up= per _ATTR >]) - ) - }; - }), - @cnt(1usize + $cnt), - ) - }; - (count: - @container($container:ty), - @data($data:ty), - @child($($child:ty)?), - @no_child($($no_child:ident)?), - @attrs($($aname:ident $aattr:literal)*), - @eat(), - @assign($($assign:block)*), - @cnt($cnt:expr), - ) =3D> - { - $crate::configfs_attrs!( - final: - @container($container), - @data($data), - @child($($child)?), - @no_child($($no_child)?), - @attrs($($aname $aattr)*), - @assign($($assign)*), - @cnt($cnt), - ) - }; - (final: - @container($container:ty), - @data($data:ty), - @child($($child:ty)?), - @no_child($($no_child:ident)?), - @attrs($($name:ident $attr:literal)*), - @assign($($assign:block)*), - @cnt($cnt:expr), - ) =3D> - { - $crate::macros::paste!{ - { - $( - // SAFETY: We are expanding `configfs_attrs`. - static [< $data:upper _ $name:upper _ATTR >]: - $crate::configfs::Attribute<$attr, $data, $data> = =3D - unsafe { - $crate::configfs::Attribute::new( - $crate::c_str!(::core::stringify!($nam= e)), - ) - }; - )* - - - // We need space for a null terminator. - const N: usize =3D $cnt + 1usize; - - // SAFETY: We are expanding `configfs_attrs`. - static [< $data:upper _ATTRS >]: - $crate::configfs::AttributeList =3D - unsafe { $crate::configfs::AttributeList::new() }; - - $($assign)* - - $( - const [<$no_child:upper>]: bool =3D true; - - static [< $data:upper _TPE >] : $crate::configfs::Item= Type<$container, $data> =3D - $crate::configfs::ItemType::<$container, $data>::n= ew::( - &THIS_MODULE, &[<$ data:upper _ATTRS >] - ); - )? - - $( - static [< $data:upper _TPE >]: - $crate::configfs::ItemType<$container, $data> =3D - $crate::configfs::ItemType::<$container, $data= >:: - new_with_child_ctor::( - &THIS_MODULE, &[<$ data:upper _ATTRS >] - ); - )? - - & [< $data:upper _TPE >] - } - } - }; - -} - -pub use crate::configfs_attrs; diff --git a/rust/macros/configfs_attrs.rs b/rust/macros/configfs_attrs.rs new file mode 100644 index 0000000000000..a7fc75cdebcc0 --- /dev/null +++ b/rust/macros/configfs_attrs.rs @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 + +use quote::{ + quote, // + ToTokens, +}; + +use proc_macro2::{ + Span, // +}; + +use syn::{ + bracketed, + parse::{ + Parse, + ParseStream, // + }, + punctuated::Punctuated, + spanned::Spanned, + Ident, + LitInt, + Token, + Type, // +}; + +use crate::helpers::try_parse; + +pub(crate) struct ConfigfsAttrs { + container: Type, + data: Type, + child: Option, + attributes: Vec<(Ident, LitInt)>, +} + +fn parse_attribute_field(stream: ParseStream<'_>) -> syn::Result<(Ident, L= itInt)> { + let id =3D stream.parse::()?; + let _colon =3D stream.parse::()?; + let v =3D stream.parse::()?; + Ok((id, v)) +} + +fn parse_named_field(stream: ParseStream<'_>, name: &str) -> syn::Result { + let name_id =3D stream.parse::()?; + if name_id !=3D name { + return Err(parse_field_error(name_id.span(), &name_id, name)); + } + let _colon =3D stream.parse::()?; + let v =3D stream.parse::()?; + stream.parse::()?; + Ok(v) +} + +fn parse_attributes(stream: ParseStream<'_>) -> syn::Result> { + let attribute_id =3D stream.parse::()?; + if attribute_id !=3D "attributes" { + return Err(syn::Error::new( + attribute_id.span(), + format!( + "unexpected identifier: {}, expected: 'attributes'", + attribute_id + ), + )); + } + stream.parse::()?; + let attr_stream; + let _bracket =3D bracketed!(attr_stream in stream); + let attributes =3D Punctuated::<(Ident, LitInt), Token![,]>::parse_ter= minated_with( + &attr_stream, + parse_attribute_field, + )?; + let attributes =3D attributes.into_iter().collect::>(); + + stream.parse::>()?; + Ok(attributes) +} + +fn parse_field_error(span: Span, name: &Ident, expected_name: &str) -> syn= ::Error { + syn::Error::new( + span, + format!("Unexpected field name, got: {name} expected: {expected_na= me}"), + ) +} + +impl Parse for ConfigfsAttrs { + fn parse(input: ParseStream<'_>) -> syn::Result { + let container =3D try_parse(input, |s| parse_named_field(s, "conta= iner"))?; + let data =3D try_parse(input, |s| parse_named_field(s, "data"))?; + let child =3D try_parse(input, |s| parse_named_field(s, "child")).= ok(); + let attributes =3D parse_attributes(input)?; + + Ok(ConfigfsAttrs { + container, + data, + child, + attributes, + }) + } +} + +fn make_static_ident(ty: &T, suffix: &str) -> syn::Ident { + let raw_id =3D quote! { #ty }.to_string(); + + // Sanitizing syn::Type::Path, this is safe since it is + // only used as the identifier. + let normalized =3D raw_id + .split("::") + .map(|s| String::from(s.trim())) + .reduce(|a, b| format!("{a}_{b}")) + .expect("Cannot be empty") + .to_uppercase() + .replace(|c: char| !c.is_alphanumeric(), "_"); + + Ident::new(&format!("{}_{}", normalized, suffix), ty.span()) +} + +pub(crate) fn configfs_attrs(cfs_attrs: ConfigfsAttrs) -> proc_macro2::Tok= enStream { + let (container_ty, data_ty) =3D (&cfs_attrs.container, &cfs_attrs.data= ); + + let data_tp_ident =3D make_static_ident(data_ty, "TPE"); + let data_attr_ident =3D make_static_ident(data_ty, "ATTR_LIST"); + + let n =3D cfs_attrs.attributes.len() + 1; + + let attr_list =3D quote! { + static #data_attr_ident: kernel::configfs::AttributeList<#n, #data= _ty> =3D + // SAFETY: We are expanding `configfs_attrs`. + unsafe { kernel::configfs::AttributeList::new() }; + }; + + let mut attrs =3D Vec::new(); + for (attr_idx, (name, id)) in cfs_attrs.attributes.iter().enumerate() { + let name_with_attr =3D make_static_ident(name, "ATTR"); + + let id: u64 =3D match id.base10_parse::() { + Ok(v) =3D> v, + Err(_) =3D> { + return syn::Error::new(id.span(), "Could not parse attribu= te ID as a u64") + .to_compile_error(); + } + }; + + attrs.push(quote! { + static #name_with_attr: kernel::configfs::Attribute<#id, #data_ty,= #data_ty> =3D + // SAFETY: We are expanding `configfs_attrs`. + unsafe { + kernel::configfs::Attribute::new(kernel::c_str!(::core::stri= ngify!(#name))) + }; + + // SAFETY: By design of this macro, the name of the variable we + // invoke the `add` method on below, is not visible outside of + // the macro expansion. The macro does not operate concurrently + // on this variable, and thus we have exclusive access to the + // variable. + unsafe { #data_attr_ident.add::<#attr_idx, #id, _>(&#name_with_a= ttr) } + }); + } + + let has_child_code =3D if let Some(child) =3D cfs_attrs.child { + quote! { new_with_child_ctor::<#n, #child>} + } else { + quote! { new::<#n> } + }; + + let data_type =3D quote! { + { + static #data_tp_ident: + kernel::configfs::ItemType<#container_ty, #data_ty> =3D + kernel::configfs::ItemType::<#container_ty, #data_ty>::#ha= s_child_code( + &THIS_MODULE, &#data_attr_ident + ); + &#data_tp_ident + } + }; + + quote! { + { + #attr_list + #(#attrs)* + #data_type + } + } +} diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index d18fbf4daa0a5..fdab8804e1ba9 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -4,6 +4,7 @@ use quote::ToTokens; use syn::{ parse::{ + discouraged::Speculative, Parse, ParseStream, // }, @@ -54,6 +55,20 @@ pub(crate) fn file() -> String { } } =20 +pub(crate) fn try_parse( + input: ParseStream<'_>, + parser: impl FnOnce(ParseStream<'_>) -> Result, +) -> Result { + let fork =3D input.fork(); + match parser(&fork) { + Ok(value) =3D> { + input.advance_to(&fork); + Ok(value) + } + Err(e) =3D> Err(e), + } +} + /// Obtain all `#[cfg]` attributes. pub(crate) fn gather_cfg_attrs(attr: &[Attribute]) -> impl Iterator + '_ { attr.iter().filter(|a| a.path().is_ident("cfg")) diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 2cfd59e0f9e7c..745ba83c828b9 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -15,6 +15,8 @@ #![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))] =20 mod concat_idents; +#[cfg(CONFIG_CONFIGFS_FS)] +mod configfs_attrs; mod export; mod fmt; mod helpers; @@ -489,3 +491,86 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStre= am) -> TokenStream { .unwrap_or_else(|e| e.into_compile_error()) .into() } + +/// Define a list of configfs attributes statically. +/// +/// # Examples +/// +/// ```ignore +/// let item_type =3D configfs_attrs! { +/// container: configfs::Subsystem, +/// data: Configuration, +/// child: Child, +/// attributes: [ +/// message: 0, +/// bar: 1, +/// ], +/// }; +///``` +/// +/// Expands the following output: +/// let item_type =3D { +/// static CONFIGURATION_ATTR_LIST: kernel::configfs::AttributeLis= t< +/// 3usize, +/// Configuration, +/// > =3D unsafe { kernel::configfs::AttributeList::new() }; +/// static MESSAGE_ATTR: kernel::configfs::Attribute< +/// 0u64, +/// Configuration, +/// Configuration, +/// > =3D unsafe { +/// kernel::configfs::Attribute::new({ +/// const S: &str =3D "message\u{0}"; +/// const C: &kernel::str::CStr =3D match kernel::str::CSt= r::from_bytes_with_nul( +/// S.as_bytes(), +/// ) { +/// Ok(v) =3D> v, +/// Err(_) =3D> { +/// ::core::panicking::panic_fmt( +/// format_args!("string contains interior NUL= "), +/// ); +/// } +/// }; +/// C +/// }) +/// }; +/// unsafe { CONFIGURATION_ATTR_LIST.add::<0usize, 0u64, _>(&MESSA= GE_ATTR) } +/// static BAR_ATTR: kernel::configfs::Attribute< +/// 1u64, +/// Configuration, +/// Configuration, +/// > =3D unsafe { +/// kernel::configfs::Attribute::new({ +/// const S: &str =3D "bar\u{0}"; +/// const C: &kernel::str::CStr =3D match kernel::str::CSt= r::from_bytes_with_nul( +/// S.as_bytes(), +/// ) { +/// Ok(v) =3D> v, +/// Err(_) =3D> { +/// ::core::panicking::panic_fmt( +/// format_args!("string contains interior NUL= "), +/// ); +/// } +/// }; +/// C +/// }) +/// }; +/// unsafe { CONFIGURATION_ATTR_LIST.add::<1usize, 1u64, _>(&BAR_A= TTR) } +/// { +/// static CONFIGURATION_TPE: kernel::configfs::ItemType< +/// Subsystem, +/// Configuration, +/// > =3D kernel::configfs::ItemType::< +/// Subsystem, +/// Configuration, +/// >::new_with_child_ctor::<3usize, Child>(&THIS_MODULE, &CON= FIGURATION_ATTR_LIST); +/// &CONFIGURATION_TPE +/// } +/// }; +/// +#[cfg(CONFIG_CONFIGFS_FS)] +#[proc_macro] +pub fn configfs_attrs(input: TokenStream) -> TokenStream { + configfs_attrs::configfs_attrs(parse_macro_input!(input as configfs_at= trs::ConfigfsAttrs)) + .into() +} diff --git a/samples/rust/rust_configfs.rs b/samples/rust/rust_configfs.rs index a1bd9db6010da..876462f7789d1 100644 --- a/samples/rust/rust_configfs.rs +++ b/samples/rust/rust_configfs.rs @@ -4,7 +4,7 @@ =20 use kernel::alloc::flags; use kernel::configfs; -use kernel::configfs::configfs_attrs; +use kernel::macros::configfs_attrs; use kernel::new_mutex; use kernel::page::PAGE_SIZE; use kernel::prelude::*; --- base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731 change-id: 20260417-configfs-syn-191e07130027 Best regards, --=20 Malte Wechter