From patchwork Sat Jun 24 09:24:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112439 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6306538vqr; Sat, 24 Jun 2023 03:11:48 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5P7TYRMGdGipRcxL6aPc7Yi8ufCEcAiijY9/QDeMIXp06AkVxNDknFlQ85d1YlpsC1GT6o X-Received: by 2002:a05:622a:d:b0:3fd:dab5:942d with SMTP id x13-20020a05622a000d00b003fddab5942dmr29250533qtw.53.1687601507834; Sat, 24 Jun 2023 03:11:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687601507; cv=none; d=google.com; s=arc-20160816; b=GIreFdb0dxVN82kbEUg7+J4qt+TaogGB5NR2+eh/YcDn0+N14FBIiq01N2fwJzq0nj hzzjz6q2FztuXtks/VeS7qWRe2vJnz5qxQzXGhZnNEN9rPws/u4aJR11IV8whILkUeq0 zd8OFSpmXV6b8nFkWv4QNrHhpsmPdA2xi2r6sGDvaNRLsMr6emPlHQtEJyI7c6wDVrEy UX/lfFipaFlMTGgTYS1Z0+iSwK9PFboQUK7xiHiWw9awPiKwxBa6zlEh5kNsRwEp+zRD kXhKGzjJ6s/yh1rqkY44X6PDsbHAMx5XB+wGJLxmuKUs8Jx7Dc3HAl4QQmu23bH8ZsDf wB9A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:message-id:subject:cc:from:to:dkim-signature:date; bh=59XOq8B2rCdfLJSTN1omKI9iBhysqGeBftTNdO85xB4=; fh=MjAOSe9N0TSmaKf+f9PeCP42XQuaAqCEgZCvUulmBUE=; b=na+SuGhGYnee+OGBlftSA4ZtMircwtwp8wBh2nHmyikNPhupQH5KUuCj+9ThCFue3o PxeOXbySA1Dht7ImmupLXmrJgxmVDN2JSjkdpo2+k4JJKD9zPaIzwvXA0mCXmWSQwmSj j7Wjut5a3WLt5Btw/W1KVA0DohS+wXjVqhKwNk2s+V9Kdw0zPWB0ZDgIZflwpBXNWbY0 +JdKRYS6koWD8r/PMH9x0UIEeeXMju77M084W5stw3WRQ2GG3+nPtI1Ye+3JLBc3TpZT rQ5rK9pstNuZpgR722ZXkVH4Hh0rN4aR2vsV+H/bnSIUJleSJjTXaW2Hb3sRwJSIAkrz M2ig== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=dNkEWve8; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id d4-20020a056a0024c400b006590bb60328si1046195pfv.173.2023.06.24.03.11.34; Sat, 24 Jun 2023 03:11:47 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=dNkEWve8; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232984AbjFXJZP (ORCPT + 99 others); Sat, 24 Jun 2023 05:25:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231514AbjFXJZM (ORCPT ); Sat, 24 Jun 2023 05:25:12 -0400 Received: from mail-40131.protonmail.ch (mail-40131.protonmail.ch [185.70.40.131]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1F42010FD; Sat, 24 Jun 2023 02:25:06 -0700 (PDT) Date: Sat, 24 Jun 2023 09:24:55 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1687598703; x=1687857903; bh=59XOq8B2rCdfLJSTN1omKI9iBhysqGeBftTNdO85xB4=; h=Date:To:From:Cc:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=dNkEWve8JU2Qz+j2G+Y3ujxGakQQHYRMlAmL07B3+9Ypdj7x0eg4YQm6bjH5oVL64 OpPrBE3Fjs6S3x/Zbp7OKy263ur9y9HVxeXs2eykwqpz2PNNcL2d4gLwFlol7/7UQE t5MpCQL5UB3BzLaVyRkb+AnZlnhpY1Q7Fyluibt/XBDsEY+ZwyXZOIyhMc1SBl0F8D SZYvoM8C5zDZSxZIyH112lSkGg8KurcGtC1fUz9UygG2HUSm5V//HBTuK97e4DZqU/ /a+Es9cPOy7aD/Kw0AhbjYpvXvt9g1jTBNxJ2iCyIsJ3TpOB18UHoIIH0jxzEaTDGa egBjOrk6IZebQ== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev Subject: [PATCH 1/7] rust: init: consolidate init macros Message-ID: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769578438549564547?= X-GMAIL-MSGID: =?utf-8?q?1769578438549564547?= Merges the implementations of `try_init!` and `try_pin_init!`. These two macros are very similar, but use different traits. The new macro `__init_internal!` that is now the implementation for both takes these traits as parameters. This change does not affect any users, as no public API has been changed, but it should simplify maintaining the init macros. Signed-off-by: Benno Lossin Reviewed-by: Björn Roy Baron --- rust/kernel/init.rs | 388 +++---------------------------------- rust/kernel/init/macros.rs | 237 +++++++++++++++++++++- 2 files changed, 259 insertions(+), 366 deletions(-) base-commit: d2e3115d717197cb2bc020dd1f06b06538474ac3 -- 2.41.0 diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index b4332a4ec1f4..d9a91950cba2 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -540,11 +540,14 @@ macro_rules! pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - $crate::try_pin_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error(::core::convert::Infallible), + @data(PinData, use_data), + @has_data(HasPinData, __pin_data), + @construct_closure(pin_init_from_closure), ) }; } @@ -593,205 +596,29 @@ macro_rules! try_pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - $crate::try_pin_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)? ), @fields($($fields)*), @error($crate::error::Error), + @data(PinData, use_data), + @has_data(HasPinData, __pin_data), + @construct_closure(pin_init_from_closure), ) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - $crate::try_pin_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)? ), @fields($($fields)*), @error($err), + @data(PinData, use_data), + @has_data(HasPinData, __pin_data), + @construct_closure(pin_init_from_closure), ) }; - ( - @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), - @fields($($fields:tt)*), - @error($err:ty), - ) => {{ - // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return - // type and shadow it later when we insert the arbitrary user code. That way there will be - // no possibility of returning without `unsafe`. - struct __InitOk; - // Get the pin data from the supplied type. - let data = unsafe { - use $crate::init::__internal::HasPinData; - $t$(::<$($generics),*>)?::__pin_data() - }; - // Ensure that `data` really is of type `PinData` and help with type inference: - let init = $crate::init::__internal::PinData::make_closure::<_, __InitOk, $err>( - data, - move |slot| { - { - // Shadow the structure so it cannot be used to return early. - struct __InitOk; - // Create the `this` so it can be referenced by the user inside of the - // expressions creating the individual fields. - $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)? - // Initialize every field. - $crate::try_pin_init!(init_slot: - @data(data), - @slot(slot), - @munch_fields($($fields)*,), - ); - // We use unreachable code to ensure that all fields have been mentioned exactly - // once, this struct initializer will still be type-checked and complain with a - // very natural error message if a field is forgotten/mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expression)] - if false { - $crate::try_pin_init!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - } - // Forget all guards, since initialization was a success. - $crate::try_pin_init!(forget_guards: - @munch_fields($($fields)*,), - ); - } - Ok(__InitOk) - } - ); - let init = move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - let init = unsafe { $crate::init::pin_init_from_closure::<_, $err>(init) }; - init - }}; - (init_slot: - @data($data:ident), - @slot($slot:ident), - @munch_fields($(,)?), - ) => { - // Endpoint of munching, no fields are left. - }; - (init_slot: - @data($data:ident), - @slot($slot:ident), - // In-place initialization syntax. - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - let $field = $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`. - unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), $field)? }; - // Create the drop guard. - // - // We only give access to `&DropGuard`, so it cannot be forgotten via safe code. - // - // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::try_pin_init!(init_slot: - @data($data), - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (init_slot: - @data($data:ident), - @slot($slot:ident), - // Direct value init, this is safe for every field. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - $(let $field = $val;)? - // Initialize the field. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; - // Create the drop guard: - // - // We only give access to `&DropGuard`, so it cannot be accidentally forgotten. - // - // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::try_pin_init!(init_slot: - @data($data), - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($(,)?), - @acc($($acc:tt)*), - ) => { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the `if false` branch, this will never get executed. We abuse `slot` to - // get the correct type inference here: - unsafe { - ::core::ptr::write($slot, $t { - $($acc)* - }); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::try_pin_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::try_pin_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; - (forget_guards: - @munch_fields($(,)?), - ) => { - // Munching finished. - }; - (forget_guards: - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_pin_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; - (forget_guards: - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_pin_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; } /// Construct an in-place initializer for `struct`s. @@ -816,11 +643,14 @@ macro_rules! init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - $crate::try_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error(::core::convert::Infallible), + @data(InitData, /*no use_data*/), + @has_data(HasInitData, __init_data), + @construct_closure(init_from_closure), ) } } @@ -863,199 +693,29 @@ macro_rules! try_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - $crate::try_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error($crate::error::Error), + @data(InitData, /*no use_data*/), + @has_data(HasInitData, __init_data), + @construct_closure(init_from_closure), ) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - $crate::try_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error($err), + @data(InitData, /*no use_data*/), + @has_data(HasInitData, __init_data), + @construct_closure(init_from_closure), ) }; - ( - @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), - @fields($($fields:tt)*), - @error($err:ty), - ) => {{ - // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return - // type and shadow it later when we insert the arbitrary user code. That way there will be - // no possibility of returning without `unsafe`. - struct __InitOk; - // Get the init data from the supplied type. - let data = unsafe { - use $crate::init::__internal::HasInitData; - $t$(::<$($generics),*>)?::__init_data() - }; - // Ensure that `data` really is of type `InitData` and help with type inference: - let init = $crate::init::__internal::InitData::make_closure::<_, __InitOk, $err>( - data, - move |slot| { - { - // Shadow the structure so it cannot be used to return early. - struct __InitOk; - // Create the `this` so it can be referenced by the user inside of the - // expressions creating the individual fields. - $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)? - // Initialize every field. - $crate::try_init!(init_slot: - @slot(slot), - @munch_fields($($fields)*,), - ); - // We use unreachable code to ensure that all fields have been mentioned exactly - // once, this struct initializer will still be type-checked and complain with a - // very natural error message if a field is forgotten/mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expression)] - if false { - $crate::try_init!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - } - // Forget all guards, since initialization was a success. - $crate::try_init!(forget_guards: - @munch_fields($($fields)*,), - ); - } - Ok(__InitOk) - } - ); - let init = move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - let init = unsafe { $crate::init::init_from_closure::<_, $err>(init) }; - init - }}; - (init_slot: - @slot($slot:ident), - @munch_fields( $(,)?), - ) => { - // Endpoint of munching, no fields are left. - }; - (init_slot: - @slot($slot:ident), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - let $field = $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - unsafe { - $crate::init::Init::__init($field, ::core::ptr::addr_of_mut!((*$slot).$field))?; - } - // Create the drop guard. - // - // We only give access to `&DropGuard`, so it cannot be accidentally forgotten. - // - // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::try_init!(init_slot: - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (init_slot: - @slot($slot:ident), - // Direct value init. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - $(let $field = $val;)? - // Call the initializer. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; - // Create the drop guard. - // - // We only give access to `&DropGuard`, so it cannot be accidentally forgotten. - // - // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::try_init!(init_slot: - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields( $(,)?), - @acc($($acc:tt)*), - ) => { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the `if false` branch, this will never get executed. We abuse `slot` to - // get the correct type inference here: - unsafe { - ::core::ptr::write($slot, $t { - $($acc)* - }); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::try_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)*$field: ::core::panic!(),), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::try_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)*$field: ::core::panic!(),), - ); - }; - (forget_guards: - @munch_fields($(,)?), - ) => { - // Munching finished. - }; - (forget_guards: - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; - (forget_guards: - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; } /// A pin-initializer for the type `T`. diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index 00aa4e956c0a..fbaebd34f218 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! This module provides the macros that actually implement the proc-macros `pin_data` and -//! `pinned_drop`. +//! `pinned_drop`. It also contains `__init_internal` the implementation of the `{try_}{pin_}init!` +//! macros. //! //! These macros should never be called directly, since they expect their input to be -//! in a certain format which is internal. Use the proc-macros instead. +//! in a certain format which is internal. If used incorrectly, these macros can lead to UB even in +//! safe code! Use the public facing macros instead. //! //! This architecture has been chosen because the kernel does not yet have access to `syn` which //! would make matters a lot easier for implementing these as proc-macros. @@ -980,3 +982,234 @@ impl<$($impl_generics)*> $pin_data<$($ty_generics)*> } }; } + +/// The internal init macro. Do not call manually! +/// +/// This is called by the `{try_}{pin_}init!` macros with various inputs. +/// +/// This macro has multiple internal call configurations, these are always the very first ident: +/// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros. +/// - `init_slot`: recursively creates the code that initializes all fields in `slot`. +/// - `make_initializer`: recursively create the struct initializer that guarantees that every +/// field has been initialized exactly once. +/// - `forget_guards`: recursively forget the drop guards for every field. +#[doc(hidden)] +#[macro_export] +macro_rules! __init_internal { + ( + @this($($this:ident)?), + @typ($t:ident $(::<$($generics:ty),*>)?), + @fields($($fields:tt)*), + @error($err:ty), + // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` + // case. + @data($data:ident, $($use_data:ident)?), + // `HasPinData` or `HasInitData`. + @has_data($has_data:ident, $get_data:ident), + // `pin_init_from_closure` or `init_from_closure`. + @construct_closure($construct_closure:ident), + ) => {{ + // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return + // type and shadow it later when we insert the arbitrary user code. That way there will be + // no possibility of returning without `unsafe`. + struct __InitOk; + // Get the data about fields from the supplied type. + let data = unsafe { + use $crate::init::__internal::$has_data; + $t$(::<$($generics),*>)?::$get_data() + }; + // Ensure that `data` really is of type `$data` and help with type inference: + let init = $crate::init::__internal::$data::make_closure::<_, __InitOk, $err>( + data, + move |slot| { + { + // Shadow the structure so it cannot be used to return early. + struct __InitOk; + // Create the `this` so it can be referenced by the user inside of the + // expressions creating the individual fields. + $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)? + // Initialize every field. + $crate::__init_internal!(init_slot($($use_data)?): + @data(data), + @slot(slot), + @munch_fields($($fields)*,), + ); + // We use unreachable code to ensure that all fields have been mentioned exactly + // once, this struct initializer will still be type-checked and complain with a + // very natural error message if a field is forgotten/mentioned more than once. + #[allow(unreachable_code, clippy::diverging_sub_expression)] + if false { + $crate::__init_internal!(make_initializer: + @slot(slot), + @type_name($t), + @munch_fields($($fields)*,), + @acc(), + ); + } + // Forget all guards, since initialization was a success. + $crate::__init_internal!(forget_guards: + @munch_fields($($fields)*,), + ); + } + Ok(__InitOk) + } + ); + let init = move |slot| -> ::core::result::Result<(), $err> { + init(slot).map(|__InitOk| ()) + }; + let init = unsafe { $crate::init::$construct_closure::<_, $err>(init) }; + init + }}; + (init_slot($($use_data:ident)?): + @data($data:ident), + @slot($slot:ident), + @munch_fields($(,)?), + ) => { + // Endpoint of munching, no fields are left. + }; + (init_slot($use_data:ident): // use_data is present, so we use the `data` to init fields. + @data($data:ident), + @slot($slot:ident), + // In-place initialization syntax. + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + ) => { + let $field = $val; + // Call the initializer. + // + // SAFETY: `slot` is valid, because we are inside of an initializer closure, we + // return when an error/panic occurs. + // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`. + unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), $field)? }; + // Create the drop guard. + // + // We only give access to `&DropGuard`, so it cannot be forgotten via safe code. + // + // SAFETY: We forget the guard later when initialization has succeeded. + let $field = &unsafe { + $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) + }; + + $crate::__init_internal!(init_slot($use_data): + @data($data), + @slot($slot), + @munch_fields($($rest)*), + ); + }; + (init_slot(): // no use_data, so we use `Init::__init` directly. + @data($data:ident), + @slot($slot:ident), + // In-place initialization syntax. + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + ) => { + let $field = $val; + // Call the initializer. + // + // SAFETY: `slot` is valid, because we are inside of an initializer closure, we + // return when an error/panic occurs. + unsafe { $crate::init::Init::__init($field, ::core::ptr::addr_of_mut!((*$slot).$field))? }; + // Create the drop guard. + // + // We only give access to `&DropGuard`, so it cannot be forgotten via safe code. + // + // SAFETY: We forget the guard later when initialization has succeeded. + let $field = &unsafe { + $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) + }; + + $crate::__init_internal!(init_slot(): + @data($data), + @slot($slot), + @munch_fields($($rest)*), + ); + }; + (init_slot($($use_data:ident)?): + @data($data:ident), + @slot($slot:ident), + // Init by-value. + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + ) => { + $(let $field = $val;)? + // Initialize the field. + // + // SAFETY: The memory at `slot` is uninitialized. + unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; + // Create the drop guard: + // + // We only give access to `&DropGuard`, so it cannot be accidentally forgotten. + // + // SAFETY: We forget the guard later when initialization has succeeded. + let $field = &unsafe { + $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) + }; + + $crate::__init_internal!(init_slot($($use_data)?): + @data($data), + @slot($slot), + @munch_fields($($rest)*), + ); + }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields($(,)?), + @acc($($acc:tt)*), + ) => { + // Endpoint, nothing more to munch, create the initializer. + // Since we are in the `if false` branch, this will never get executed. We abuse `slot` to + // get the correct type inference here: + unsafe { + ::core::ptr::write($slot, $t { + $($acc)* + }); + } + }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + @acc($($acc:tt)*), + ) => { + $crate::__init_internal!(make_initializer: + @slot($slot), + @type_name($t), + @munch_fields($($rest)*), + @acc($($acc)* $field: ::core::panic!(),), + ); + }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + @acc($($acc:tt)*), + ) => { + $crate::__init_internal!(make_initializer: + @slot($slot), + @type_name($t), + @munch_fields($($rest)*), + @acc($($acc)* $field: ::core::panic!(),), + ); + }; + (forget_guards: + @munch_fields($(,)?), + ) => { + // Munching finished. + }; + (forget_guards: + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + ) => { + unsafe { $crate::init::__internal::DropGuard::forget($field) }; + + $crate::__init_internal!(forget_guards: + @munch_fields($($rest)*), + ); + }; + (forget_guards: + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + ) => { + unsafe { $crate::init::__internal::DropGuard::forget($field) }; + + $crate::__init_internal!(forget_guards: + @munch_fields($($rest)*), + ); + }; +} From patchwork Sat Jun 24 09:25:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112435 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6293480vqr; Sat, 24 Jun 2023 02:33:29 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6Cpu8oBjSJl3O6lH45f2YQB2Y/n6YqVfQBv+AlOe7BVhHDh5TYcoK2kRDrJ3i1Ev2wv2Gz X-Received: by 2002:a17:90a:1da:b0:262:cc10:5a67 with SMTP id 26-20020a17090a01da00b00262cc105a67mr765633pjd.14.1687599209156; Sat, 24 Jun 2023 02:33:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687599209; cv=none; d=google.com; s=arc-20160816; b=G7siHCw7SFeQKUKTw3WsDBMoM7h6Hx4j9HObsvs5DKhzY1/JHNIsQ4OwQR3MAhSUU+ I1qK9FkkMC5Xk0hla4tKnkdQW7/0wO5YR9Wj6bR9tA7MHjh1EeYjV25pfdKK2rfFbSNn gMBnRZiwhLg1jWt7jixfbw90JnPS7L3qmtI73XH8bG4q4p+Vi/Lbpc6t/PiTsbEB8hYZ JD7KMHucK6qxwq10ZfPDzBpRmLMzIOsJLIJtlhEZkd42V9wGNyRDh14dBVJAbCcE3oIN VIfIfJnk+DmaWLe62N39ITNkzYKBg/Hz7XFx8vS1/wrC+O3PqPDUHnPGRsD4NqGCNHOA wXeg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=8VcczR7TSrM7NM+PW6yKJnJo9tNhZ0vALWp60pbLLGg=; fh=Y3hGqDvLhFUfCxW08frpp1lth+cS6Xq25ZUxZll0Zv0=; b=kIDx9iTVmM1yeqjdwCDCE5fJfrBmDsX5yoXwmkCMjdT3cqBFES3PEHHdzDmFNEUXRV OmfUqXJh7XqaQVoc/6+26P8QRV0GvBoE7G/n5MrWKVvHB+r/oq2do52mg9U9mjZbo9AM Lkl9EE01U2VjutD8EqaeleWyZKhOo9vi2Syq5YzyilDN5oGlMZJrUyAvw3CBVnN5OyV7 YyezCefhyzZcQvo1TSA2iyrK2deN1voMC5qobl83Yf3xvjIbYcdTkEGBkuFiP6aMfHmf fuUrepa/lTmi3KX8Uoj1VTL6DEGZMnnsV0Adtr9be6yRpWId1ZDZo1xOQ4SnBgT6RGac pcRQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=CQ8JgzRY; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id cs23-20020a17090af51700b0025df9f1f7a1si1232724pjb.94.2023.06.24.02.33.16; Sat, 24 Jun 2023 02:33:29 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=CQ8JgzRY; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233000AbjFXJZ1 (ORCPT + 99 others); Sat, 24 Jun 2023 05:25:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52316 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231752AbjFXJZX (ORCPT ); Sat, 24 Jun 2023 05:25:23 -0400 Received: from mail-4316.protonmail.ch (mail-4316.protonmail.ch [185.70.43.16]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B347B1BF2; Sat, 24 Jun 2023 02:25:18 -0700 (PDT) Date: Sat, 24 Jun 2023 09:25:03 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1687598716; x=1687857916; bh=8VcczR7TSrM7NM+PW6yKJnJo9tNhZ0vALWp60pbLLGg=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=CQ8JgzRY5eg8ZFcZ8U/kQhsNREbJ5BKQRnOYiPvVf/nAkIyKAYf6M0555lN4BsGux BgbRBJRRq6SPFVx3OK1+8IOj8efQfXqs7HoBvUSAjSsMsXf+amhcMnj7ZZmwHT1SCW dw9NwCnI3mIOpdzgtoInHRNvvfVFCSrW9lkgSGG15BWohm61FZ0I9gy/wjXnPiXODD ceMgI69ITB2prVNu2aW4W5EaPhoVI7OzGIp0JPResWODeinoRFa2W4ydfKxbShz6wQ PMbmYti9KDxP1GN5xvtVFWuodPTXqd5JVEugWP77yQkVSRTkMkk9xudkH0LYHXLibS QxSC+2lhM4/Tg== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Asahi Lina Subject: [PATCH 2/7] rust: add derive macro for `Zeroable` Message-ID: <20230624092330.157338-2-benno.lossin@proton.me> In-Reply-To: <20230624092330.157338-1-benno.lossin@proton.me> References: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769576028285514149?= X-GMAIL-MSGID: =?utf-8?q?1769576028285514149?= Add a derive proc-macro for the `Zeroable` trait. The macro supports structs where every field implements the `Zeroable` trait. This way `unsafe` implementations can be avoided. The macro is split into two parts: - a proc-macro to parse generics into impl and ty generics, - a declarative macro that expands to the impl block. Suggested-by: Asahi Lina Signed-off-by: Benno Lossin Reviewed-by: Björn Roy Baron Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl --- rust/kernel/init/macros.rs | 28 ++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- rust/macros/lib.rs | 20 ++++++++++++++++++++ rust/macros/quote.rs | 6 ++++++ rust/macros/zeroable.rs | 25 +++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 rust/macros/zeroable.rs -- 2.41.0 diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index fbaebd34f218..e8165ff53a94 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -1213,3 +1213,31 @@ macro_rules! __init_internal { ); }; } + +#[doc(hidden)] +#[macro_export] +macro_rules! __derive_zeroable { + (parse_input: + @sig( + $(#[$($struct_attr:tt)*])* + $vis:vis struct $name:ident + $(where $($whr:tt)*)? + ), + @impl_generics($($impl_generics:tt)*), + @ty_generics($($ty_generics:tt)*), + @body({ + $( + $(#[$($field_attr:tt)*])* + $field:ident : $field_ty:ty + ),* $(,)? + }), + ) => { + // SAFETY: every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> + where + $($field_ty: $crate::Zeroable,)* + $($($whr)*)? + {} + }; +} diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index c28587d68ebc..ae21600970b3 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -18,7 +18,7 @@ pub use alloc::{boxed::Box, vec::Vec}; #[doc(no_inline)] -pub use macros::{module, pin_data, pinned_drop, vtable}; +pub use macros::{module, pin_data, pinned_drop, vtable, Zeroable}; pub use super::build_assert; diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 3fc74cb4ea19..9f056a5c780a 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -10,6 +10,7 @@ mod pin_data; mod pinned_drop; mod vtable; +mod zeroable; use proc_macro::TokenStream; @@ -246,3 +247,22 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { pinned_drop::pinned_drop(args, input) } + +/// Derives the [`Zeroable`] trait for the given struct. +/// +/// This can only be used for structs where every field implements the [`Zeroable`] trait. +/// +/// # Examples +/// +/// ```rust +/// #[derive(Zeroable)] +/// pub struct DriverData { +/// id: i64, +/// buf_ptr: *mut u8, +/// len: usize, +/// } +/// ``` +#[proc_macro_derive(Zeroable)] +pub fn derive_zeroable(input: TokenStream) -> TokenStream { + zeroable::derive(input) +} diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs index dddbb4e6f4cb..b76c198a4ed5 100644 --- a/rust/macros/quote.rs +++ b/rust/macros/quote.rs @@ -124,6 +124,12 @@ macro_rules! quote_spanned { )); quote_spanned!(@proc $v $span $($tt)*); }; + (@proc $v:ident $span:ident ; $($tt:tt)*) => { + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone) + )); + quote_spanned!(@proc $v $span $($tt)*); + }; (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => { $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span))); quote_spanned!(@proc $v $span $($tt)*); diff --git a/rust/macros/zeroable.rs b/rust/macros/zeroable.rs new file mode 100644 index 000000000000..cddb866c44ef --- /dev/null +++ b/rust/macros/zeroable.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::helpers::{parse_generics, Generics}; +use proc_macro::TokenStream; + +pub(crate) fn derive(input: TokenStream) -> TokenStream { + let ( + Generics { + impl_generics, + ty_generics, + }, + mut rest, + ) = parse_generics(input); + // This should be the body of the struct `{...}`. + let last = rest.pop(); + quote! { + ::kernel::__derive_zeroable!( + parse_input: + @sig(#(#rest)*), + @impl_generics(#(#impl_generics)*), + @ty_generics(#(#ty_generics)*), + @body(#last), + ); + } +} From patchwork Sat Jun 24 09:25:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112436 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6300425vqr; Sat, 24 Jun 2023 02:56:50 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5yJCvZpp7PFo70FHi0oj9yVE9iRjAVa3hjJ4mN+7r/ZkhwNabqZRmXQnxTGPbjP6tDVVvr X-Received: by 2002:a54:4383:0:b0:3a1:b4aa:551a with SMTP id u3-20020a544383000000b003a1b4aa551amr2903009oiv.59.1687600610012; Sat, 24 Jun 2023 02:56:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687600609; cv=none; d=google.com; s=arc-20160816; b=MNxarrMI/OYeBi9JAwPoXWT8nANjQRkAEDXJ9t0AjYHYorquOodcQqqkQxCUdJEwy/ xIidjTJVAHHGgK54hDDDbXzz1r+pFl1rkCj7wGGYjlrYKzuBKLt4j3ASTxJ2uFxNqJwZ blOmRFIDzVnmDCfZIoaqTnoP62zat0sCgimHiUgD2hFnT2hLn7pFcFIkvISLwllPGjmY 7R5X0K/3KkjjFbZGe1rMqNNns6p8JYKAfHaNsneO2jNagUVOnY5WWgvHOZBpahhOLBDm 0MJ90vH4u04MvXeAgOcjhnJUMriZS0EWqFcK/tb7FFhWauzuTUmPxjJwEJKvtmUyaTGI lfwg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=F442EJD8L22HB05UPRQHv+35aOiE4fN5MpCVafEtbng=; fh=Y3hGqDvLhFUfCxW08frpp1lth+cS6Xq25ZUxZll0Zv0=; b=eJj8M2CVBFot0mAHJ0GfUGIqUegs9k2ZVG3t0Eivxuso0VetaMpVjJjmF477uMrIRn MMgJfUKfhoIEzqHwIMoX2zCRk7trprITy2aC1x4V/bUIFvew1vjTm7nosnSDEn3yEUiw uVpWxa6DxsO6IiWDbDKBhJgOmhlUqQVnszlKhQIShCzvlMPUWWCqyd5lNNHv75X6vRKv U0j7PlDvyk8asiQF4jvi5BpHptcOB4ID9bN/PzqOOy+DRVTCnbE27uDIjfaxoek6CJcb X9qJS+j/bub2UHB8usEYieIwwEAhlGjo/YOu1kLpG0kdm+UxCnxalYCPSDLCYk2ZDP07 BWwA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=RZe4r4hG; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id j22-20020a170902759600b001b551fb08f2si1005159pll.168.2023.06.24.02.56.27; Sat, 24 Jun 2023 02:56:49 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=RZe4r4hG; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232807AbjFXJZh (ORCPT + 99 others); Sat, 24 Jun 2023 05:25:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52502 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233008AbjFXJZa (ORCPT ); Sat, 24 Jun 2023 05:25:30 -0400 Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E79312128 for ; Sat, 24 Jun 2023 02:25:23 -0700 (PDT) Date: Sat, 24 Jun 2023 09:25:10 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1687598721; x=1687857921; bh=F442EJD8L22HB05UPRQHv+35aOiE4fN5MpCVafEtbng=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=RZe4r4hGwC3X2Yf8meMtLPnpHuNbbt3hvvQm5OhFulqj51Wfeh9O2oJymyLFULk3U 0zzW9fXpxc5suiaGFJv2NhGQEqWE5FybAA5U47qT6r46PPw465AADoJE/fQitdHMzS T4XA7V/Va3kcasO3EnMB/N6iiC6llValzVIO+HPOrE4irfHExbaV5RE9JmJ8wi9r7x 9Zr2iQZ4Wns1P+JV44Pc3upL/Z7Z2TrFKiORmrB8sDfgLaixFynMw/c/fGpG3dzC+9 /pTPm10InbpJYHhyZ62yHXRYl92iHZgIl63yH+E2LwToIfkaWyTt95DsuSVMKOm1xs OxF863zALlKCg== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Asahi Lina Subject: [PATCH 3/7] rust: init: make guards in the init macros hygienic Message-ID: <20230624092330.157338-3-benno.lossin@proton.me> In-Reply-To: <20230624092330.157338-1-benno.lossin@proton.me> References: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769577497216415761?= X-GMAIL-MSGID: =?utf-8?q?1769577497216415761?= Use hygienic identifiers for the guards instead of the field names. This makes the init macros feel more like normal struct initializers, since assigning identifiers with the name of a field does not create conflicts. Also change the internals of the guards, no need to make the `forget` function `unsafe`, since users cannot access the guards anyways. Now the guards are carried directly on the stack and have no extra `Cell` field that marks if they have been forgotten or not, instead they are just forgotten via `mem::forget`. Suggested-by: Asahi Lina Signed-off-by: Benno Lossin Reviewed-by: Björn Roy Baron Reviewed-by: Gary Guo --- rust/kernel/init.rs | 1 - rust/kernel/init/__internal.rs | 25 +++------------ rust/kernel/init/macros.rs | 56 ++++++++++++---------------------- 3 files changed, 23 insertions(+), 59 deletions(-) -- 2.41.0 diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index d9a91950cba2..ecf6a4bd0ce4 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -206,7 +206,6 @@ use alloc::boxed::Box; use core::{ alloc::AllocError, - cell::Cell, convert::Infallible, marker::PhantomData, mem::MaybeUninit, diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs index 44751fb62b51..7abd1fb65e41 100644 --- a/rust/kernel/init/__internal.rs +++ b/rust/kernel/init/__internal.rs @@ -174,7 +174,6 @@ pub fn init(self: Pin<&mut Self>, init: impl PinInit) -> Result { ptr: *mut T, - do_drop: Cell, } impl DropGuard { @@ -190,32 +189,16 @@ impl DropGuard { /// - will not be dropped by any other means. #[inline] pub unsafe fn new(ptr: *mut T) -> Self { - Self { - ptr, - do_drop: Cell::new(true), - } - } - - /// Prevents this guard from dropping the supplied pointer. - /// - /// # Safety - /// - /// This function is unsafe in order to prevent safe code from forgetting this guard. It should - /// only be called by the macros in this module. - #[inline] - pub unsafe fn forget(&self) { - self.do_drop.set(false); + Self { ptr } } } impl Drop for DropGuard { #[inline] fn drop(&mut self) { - if self.do_drop.get() { - // SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function - // ensuring that this operation is safe. - unsafe { ptr::drop_in_place(self.ptr) } - } + // SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function + // ensuring that this operation is safe. + unsafe { ptr::drop_in_place(self.ptr) } } } diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index e8165ff53a94..df4281743175 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -992,7 +992,6 @@ impl<$($impl_generics)*> $pin_data<$($ty_generics)*> /// - `init_slot`: recursively creates the code that initializes all fields in `slot`. /// - `make_initializer`: recursively create the struct initializer that guarantees that every /// field has been initialized exactly once. -/// - `forget_guards`: recursively forget the drop guards for every field. #[doc(hidden)] #[macro_export] macro_rules! __init_internal { @@ -1032,6 +1031,7 @@ macro_rules! __init_internal { $crate::__init_internal!(init_slot($($use_data)?): @data(data), @slot(slot), + @guards(), @munch_fields($($fields)*,), ); // We use unreachable code to ensure that all fields have been mentioned exactly @@ -1046,10 +1046,6 @@ macro_rules! __init_internal { @acc(), ); } - // Forget all guards, since initialization was a success. - $crate::__init_internal!(forget_guards: - @munch_fields($($fields)*,), - ); } Ok(__InitOk) } @@ -1063,13 +1059,17 @@ macro_rules! __init_internal { (init_slot($($use_data:ident)?): @data($data:ident), @slot($slot:ident), + @guards($($guards:ident,)*), @munch_fields($(,)?), ) => { - // Endpoint of munching, no fields are left. + // Endpoint of munching, no fields are left. If execution reaches this point, all fields + // have been initialized. Therefore we can now dismiss the guards by forgetting them. + $(::core::mem::forget($guards);)* }; (init_slot($use_data:ident): // use_data is present, so we use the `data` to init fields. @data($data:ident), @slot($slot:ident), + @guards($($guards:ident,)*), // In-place initialization syntax. @munch_fields($field:ident <- $val:expr, $($rest:tt)*), ) => { @@ -1080,24 +1080,26 @@ macro_rules! __init_internal { // return when an error/panic occurs. // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`. unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), $field)? }; - // Create the drop guard. + // Create the drop guard: // - // We only give access to `&DropGuard`, so it cannot be forgotten via safe code. + // We rely on macro hygiene to make it impossible for users to access this local variable. // // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { + let guard = unsafe { $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) }; $crate::__init_internal!(init_slot($use_data): @data($data), @slot($slot), + @guards(guard, $($guards,)*), @munch_fields($($rest)*), ); }; (init_slot(): // no use_data, so we use `Init::__init` directly. @data($data:ident), @slot($slot:ident), + @guards($($guards:ident,)*), // In-place initialization syntax. @munch_fields($field:ident <- $val:expr, $($rest:tt)*), ) => { @@ -1107,24 +1109,26 @@ macro_rules! __init_internal { // SAFETY: `slot` is valid, because we are inside of an initializer closure, we // return when an error/panic occurs. unsafe { $crate::init::Init::__init($field, ::core::ptr::addr_of_mut!((*$slot).$field))? }; - // Create the drop guard. + // Create the drop guard: // - // We only give access to `&DropGuard`, so it cannot be forgotten via safe code. + // We rely on macro hygiene to make it impossible for users to access this local variable. // // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { + let guard = unsafe { $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) }; $crate::__init_internal!(init_slot(): @data($data), @slot($slot), + @guards(guard, $($guards,)*), @munch_fields($($rest)*), ); }; (init_slot($($use_data:ident)?): @data($data:ident), @slot($slot:ident), + @guards($($guards:ident,)*), // Init by-value. @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), ) => { @@ -1135,16 +1139,17 @@ macro_rules! __init_internal { unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; // Create the drop guard: // - // We only give access to `&DropGuard`, so it cannot be accidentally forgotten. + // We rely on macro hygiene to make it impossible for users to access this local variable. // // SAFETY: We forget the guard later when initialization has succeeded. - let $field = &unsafe { + let guard = unsafe { $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) }; $crate::__init_internal!(init_slot($($use_data)?): @data($data), @slot($slot), + @guards(guard, $($guards,)*), @munch_fields($($rest)*), ); }; @@ -1189,29 +1194,6 @@ macro_rules! __init_internal { @acc($($acc)* $field: ::core::panic!(),), ); }; - (forget_guards: - @munch_fields($(,)?), - ) => { - // Munching finished. - }; - (forget_guards: - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::__init_internal!(forget_guards: - @munch_fields($($rest)*), - ); - }; - (forget_guards: - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::__init_internal!(forget_guards: - @munch_fields($($rest)*), - ); - }; } #[doc(hidden)] From patchwork Sat Jun 24 09:25:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112437 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6302900vqr; Sat, 24 Jun 2023 03:02:42 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5ZryCCuzpBeMCnVhMpkSgIyzgIw/fIH/0KFMITZc3U9ukUgmj1TeNhJP9oKaVi6MzhDh4/ X-Received: by 2002:a05:6a00:3a10:b0:668:8596:7524 with SMTP id fj16-20020a056a003a1000b0066885967524mr15013294pfb.20.1687600962129; Sat, 24 Jun 2023 03:02:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687600962; cv=none; d=google.com; s=arc-20160816; b=FKt7rpLnpULcLINw7PPZI9BvkRBDbOV6dshLPhuYzvT8PHdvzm7IZ9i33irpToAk/N GQJRe3PQ8dzLQadgZnTv5Cs5XYue/wSfJJrp7CgHUwQ5azMwN5lykLZzffyfQ1Oeo/Ux pYWWm0Hh2SfbfWfP5BoRstMxCIAvsTMcTFv32XjUnaVRLvyfMnzV2bZ4lfLIv74tnc82 MhRSQG+MZqSWP2ZPvK9CWSDKz4Whz5COE+XAl79KARqD5Zk9hrhvYhg+uFiOtJ4gbxHL 7iJU/m2SifQfIm+Ox9vhnwdd6eXRZ9YQ9RZMDzwqakpAvPjVZws+rwZZi6sayjkAJ/uq 7+zA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=MwlsAZeth2fh5s/kKCjOwcaQoR9NfdiNJqcnIsJP+QU=; fh=MjAOSe9N0TSmaKf+f9PeCP42XQuaAqCEgZCvUulmBUE=; b=lsWiZ2+U1Gj9lG/c6Oy3tdlWW/cH54mkoYdoUn3MR8gxiyQiHU/mpcJXc/+C6BOQ4l OZNM/d2qcOsNrKt4hGVRHcE8B2pgdEmd2n9R+doob+goJgdXDuvE3twWB4zPEVAsLRU6 H7WEr09XwTDrdEENW13kQQ1duHpHKXr0DhhQqNK+AtN01mv42ZNqk3Ka35Ys/LIWjK2X W2iqR3YTIHOZh7fbmaMcPSVochMCZTS+OAe5CYDJn/xkVcMy2KHgixpbtbTC5lmq3dpF 8GxfxeYfjU9G799oHEjoIFtM5BMLEZWCpr74wARkYc1TK2pcftsuD2Ta5Ys+pGZWgZCF m5PQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=xkiqz2ubxjcrjarzt6h2fs2eda.protonmail header.b=RX1QAFc2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id ce12-20020a056a002a0c00b0065b4e2b52c5si951778pfb.196.2023.06.24.03.02.28; Sat, 24 Jun 2023 03:02:41 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=xkiqz2ubxjcrjarzt6h2fs2eda.protonmail header.b=RX1QAFc2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233043AbjFXJZu (ORCPT + 99 others); Sat, 24 Jun 2023 05:25:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52346 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232713AbjFXJZk (ORCPT ); Sat, 24 Jun 2023 05:25:40 -0400 Received: from mail-40133.protonmail.ch (mail-40133.protonmail.ch [185.70.40.133]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BCEDB1FF0 for ; Sat, 24 Jun 2023 02:25:28 -0700 (PDT) Date: Sat, 24 Jun 2023 09:25:16 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=xkiqz2ubxjcrjarzt6h2fs2eda.protonmail; t=1687598726; x=1687857926; bh=MwlsAZeth2fh5s/kKCjOwcaQoR9NfdiNJqcnIsJP+QU=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=RX1QAFc2D7lJnNB8YIGMjffGX2FEKkrq6fmEfZflSEwkkwMLXzhsXN8ZLqMJFL9oW eaHYEK9ECgnIrEKj2hNjevL6+hEB1EsPWMmOxWnQQQE6XF/MEg/tpFrFaAMHfdB3ST YJhWTH9QJVe7q8dh8ykOd37SmZ6Za20e/jR7O8ztupEyl9TvSTMWqWr5+w7pB4Rd4h 8shd5T+lOMdD3IVD+VzW+zQ18TluwVoBqC8G9mZq4EtC2pMhy6AueT+OcOFRd04TW9 fpMkLLW3wmqrHR0wVHlpFOxMqX0pnYLjz0S0aR4lf7FsUsMOFju/QgLG68H3psY5b6 MVGAuvLNTqwuw== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev Subject: [PATCH 4/7] rust: init: wrap type checking struct initializers in a closure Message-ID: <20230624092330.157338-4-benno.lossin@proton.me> In-Reply-To: <20230624092330.157338-1-benno.lossin@proton.me> References: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769577866421593024?= X-GMAIL-MSGID: =?utf-8?q?1769577866421593024?= In the implementation of the init macros there is a `if false` statement that type checks the initializer to ensure every field is initialized. Since the next patch has a stack variable to store the struct, the function might allocate too much memory on debug builds. Putting the struct into a closure ensures that even in debug builds no stack overflow error is caused. In release builds this was not a problem since the code was optimized away due to the `if false`. Signed-off-by: Benno Lossin --- rust/kernel/init/macros.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) -- 2.41.0 diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index df4281743175..1e0c4aca055a 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -1037,14 +1037,18 @@ macro_rules! __init_internal { // We use unreachable code to ensure that all fields have been mentioned exactly // once, this struct initializer will still be type-checked and complain with a // very natural error message if a field is forgotten/mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expression)] + #[allow(unreachable_code, + clippy::diverging_sub_expression, + clippy::redundant_closure_call)] if false { - $crate::__init_internal!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); + (|| { + $crate::__init_internal!(make_initializer: + @slot(slot), + @type_name($t), + @munch_fields($($fields)*,), + @acc(), + ); + })(); } } Ok(__InitOk) From patchwork Sat Jun 24 09:25:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112438 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6306074vqr; Sat, 24 Jun 2023 03:10:36 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6G5pVYVILcdCxjEGgeKqUZUEid+q0UdJgbjQ/a5gL5nw5zJgYSNAF4M4G/r1p2z38BADNu X-Received: by 2002:a17:90a:1a03:b0:262:c2fa:b077 with SMTP id 3-20020a17090a1a0300b00262c2fab077mr1566993pjk.18.1687601436348; Sat, 24 Jun 2023 03:10:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687601436; cv=none; d=google.com; s=arc-20160816; b=UiZbgUzJejfLR7dX/YHygfe/vkz/K+m1g8F6tz7yo1PHrm6T5y4+ljLNoNW95FGJ/F zge4pSjB5htPKhf9L3o3uayed/qhXNsw2aneO9lot5KAxq5NGfh1uC47EQf99ng7E4SY EcRfLCWEygvJiFKHSc3SkuH+0+ZczrQuGEDPN9GUzJXnhsDbZjG28Y3eV++1FVklyqY/ rqhqwsEBLKUSpVVw/vAKdWxoko8+OF4CicUmHa0p3HTY4oIEFOdy8/8g3tO0nVkqS7Qm WEjYFB64MazzUAMlhtwmNNesMqZ6ChuGqsecSM8WT6JCzCUmqUR4xakD1GsPE5/MdUOY JsUQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=e1sXKJCCxLmpy0ulsQW4r9ABVvH3CCi6eWCJaK/m/J8=; fh=Y3hGqDvLhFUfCxW08frpp1lth+cS6Xq25ZUxZll0Zv0=; b=CYnP3+tH7+AFjxHDTezEYFkDsIZNDhY3MD99oGAkQkNvtKNOSIz6Tr9T6GgtlWF7dT 7wCrm4T0SZOvr7NOGvHOKovfhhp7Jj8fz93B9tR/+AhWoQjYfQxEDOp2SQgy1IE5y7Mt p13YWHWaYLza0vMQQLTeZJBpovh31/X5EaN4pLq91sl0AK0QsZNbzfjg/nHs9qbG0GGl EyUsZ49EsDsIOm7VwMQqI0cgqrB+IkCmq/p0AlsuQpaXBo7GPfcD2zgh1DnoQHgz5ozX Zqu0L6iOF7ssHb8hsovmCLpRx1f0eXK5abwyBT+Py/E0BrI1whfne/MF6zdXt2gblBpg EG3w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=AKUodPJC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id lx16-20020a17090b4b1000b0025ea25605ddsi4156889pjb.40.2023.06.24.03.10.23; Sat, 24 Jun 2023 03:10:36 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=AKUodPJC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233026AbjFXJZk (ORCPT + 99 others); Sat, 24 Jun 2023 05:25:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52346 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233017AbjFXJZe (ORCPT ); Sat, 24 Jun 2023 05:25:34 -0400 Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D93271FFD; Sat, 24 Jun 2023 02:25:24 -0700 (PDT) Date: Sat, 24 Jun 2023 09:25:19 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1687598722; x=1687857922; bh=e1sXKJCCxLmpy0ulsQW4r9ABVvH3CCi6eWCJaK/m/J8=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=AKUodPJCL5jU2Ry/mdoLY25kCZxnIwD6WpjGc0d7R7r7oYlwoG6Nt92wA7U4IIAfZ 5pUf2OhLYtWf0YJT+EgsmNvlmhNpoxym4E899jND9VoT8J1D5qoKUjCCC1W47wEs1I XmmhfK/A0DNBAIqzOasd6dNwk34hbXF/Peuo+P4PNikryOwoY4zoYvB5044X9VWRVo xZYN9E85PbGzC4fdIw+8cnjUgcLDHfEcbiV+QwTpZPUHGsXEAhd0kWCwUP3czUumDn UyqoOIRJ4OxpPjUWLN7cv6WLm0IyNf8ODSHFzLviR7rr2PWnijAMw6BzZZhlsxWAzT E0oxTs4W/HCmQ== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Asahi Lina Subject: [PATCH 5/7] rust: init: add `..Zeroable::zeroed()` syntax for zeroing all missing fields Message-ID: <20230624092330.157338-5-benno.lossin@proton.me> In-Reply-To: <20230624092330.157338-1-benno.lossin@proton.me> References: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769578363774609468?= X-GMAIL-MSGID: =?utf-8?q?1769578363774609468?= Add the struct update syntax to the init macros, but only for `..Zeroable::zeroed()`. Adding this at the end of the struct initializer allows one to omit fields from the initializer, these fields will be initialized with 0x00 set to every byte. Only types that implement the `Zeroable` trait can utilize this. Suggested-by: Asahi Lina Signed-off-by: Benno Lossin Reviewed-by: Alice Ryhl --- rust/kernel/init.rs | 16 +++++- rust/kernel/init/macros.rs | 114 ++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 2 deletions(-) -- 2.41.0 diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index ecf6a4bd0ce4..44bc3e77419a 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -508,14 +508,18 @@ macro_rules! stack_try_pin_init { /// - Fields that you want to initialize in-place have to use `<-` instead of `:`. /// - In front of the initializer you can write `&this in` to have access to a [`NonNull`] /// pointer named `this` inside of the initializer. +/// - Using struct update syntax one can place `..Zeroable::zeroed()` at the very end of the +/// struct, this initializes every field with 0 and then runs all initializers specified in the +/// body. This can only be done if [`Zeroable`] is implemented for the struct. /// /// For instance: /// /// ```rust /// # use kernel::pin_init; -/// # use macros::pin_data; +/// # use macros::{pin_data, Zeroable}; /// # use core::{ptr::addr_of_mut, marker::PhantomPinned}; /// #[pin_data] +/// #[derive(Zeroable)] /// struct Buf { /// // `ptr` points into `buf`. /// ptr: *mut u8, @@ -528,6 +532,10 @@ macro_rules! stack_try_pin_init { /// ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() }, /// pin: PhantomPinned, /// }); +/// pin_init!(Buf { +/// buf: [1; 64], +/// ..Zeroable::zeroed(), +/// }); /// ``` /// /// [`try_pin_init!`]: kernel::try_pin_init @@ -547,6 +555,7 @@ macro_rules! pin_init { @data(PinData, use_data), @has_data(HasPinData, __pin_data), @construct_closure(pin_init_from_closure), + @munch_fields($($fields)*), ) }; } @@ -603,6 +612,7 @@ macro_rules! try_pin_init { @data(PinData, use_data), @has_data(HasPinData, __pin_data), @construct_closure(pin_init_from_closure), + @munch_fields($($fields)*), ) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { @@ -616,6 +626,7 @@ macro_rules! try_pin_init { @data(PinData, use_data), @has_data(HasPinData, __pin_data), @construct_closure(pin_init_from_closure), + @munch_fields($($fields)*), ) }; } @@ -650,6 +661,7 @@ macro_rules! init { @data(InitData, /*no use_data*/), @has_data(HasInitData, __init_data), @construct_closure(init_from_closure), + @munch_fields($($fields)*), ) } } @@ -700,6 +712,7 @@ macro_rules! try_init { @data(InitData, /*no use_data*/), @has_data(HasInitData, __init_data), @construct_closure(init_from_closure), + @munch_fields($($fields)*), ) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { @@ -713,6 +726,7 @@ macro_rules! try_init { @data(InitData, /*no use_data*/), @has_data(HasInitData, __init_data), @construct_closure(init_from_closure), + @munch_fields($($fields)*), ) }; } diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index 1e0c4aca055a..5dcb2e513f26 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -989,6 +989,7 @@ impl<$($impl_generics)*> $pin_data<$($ty_generics)*> /// /// This macro has multiple internal call configurations, these are always the very first ident: /// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros. +/// - `with_update_parsed`: when the `..Zeroable::zeroed()` syntax has been handled. /// - `init_slot`: recursively creates the code that initializes all fields in `slot`. /// - `make_initializer`: recursively create the struct initializer that guarantees that every /// field has been initialized exactly once. @@ -1007,6 +1008,82 @@ macro_rules! __init_internal { @has_data($has_data:ident, $get_data:ident), // `pin_init_from_closure` or `init_from_closure`. @construct_closure($construct_closure:ident), + @munch_fields(), + ) => { + $crate::__init_internal!(with_update_parsed: + @this($($this)?), + @typ($t $(::<$($generics),*>)? ), + @fields($($fields)*), + @error($err), + @data($data, $($use_data)?), + @has_data($has_data, $get_data), + @construct_closure($construct_closure), + @zeroed(), // nothing means default behavior. + ) + }; + ( + @this($($this:ident)?), + @typ($t:ident $(::<$($generics:ty),*>)?), + @fields($($fields:tt)*), + @error($err:ty), + // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` + // case. + @data($data:ident, $($use_data:ident)?), + // `HasPinData` or `HasInitData`. + @has_data($has_data:ident, $get_data:ident), + // `pin_init_from_closure` or `init_from_closure`. + @construct_closure($construct_closure:ident), + @munch_fields(..Zeroable::zeroed()), + ) => { + $crate::__init_internal!(with_update_parsed: + @this($($this)?), + @typ($t $(::<$($generics),*>)? ), + @fields($($fields)*), + @error($err), + @data($data, $($use_data)?), + @has_data($has_data, $get_data), + @construct_closure($construct_closure), + @zeroed(()), // `()` means zero all fields not mentioned. + ) + }; + ( + @this($($this:ident)?), + @typ($t:ident $(::<$($generics:ty),*>)?), + @fields($($fields:tt)*), + @error($err:ty), + // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` + // case. + @data($data:ident, $($use_data:ident)?), + // `HasPinData` or `HasInitData`. + @has_data($has_data:ident, $get_data:ident), + // `pin_init_from_closure` or `init_from_closure`. + @construct_closure($construct_closure:ident), + @munch_fields($ignore:tt $($rest:tt)*), + ) => { + $crate::__init_internal!( + @this($($this)?), + @typ($t $(::<$($generics),*>)? ), + @fields($($fields)*), + @error($err), + @data($data, $($use_data)?), + @has_data($has_data, $get_data), + @construct_closure($construct_closure), + @munch_fields($($rest)*), + ) + }; + (with_update_parsed: + @this($($this:ident)?), + @typ($t:ident $(::<$($generics:ty),*>)?), + @fields($($fields:tt)*), + @error($err:ty), + // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` + // case. + @data($data:ident, $($use_data:ident)?), + // `HasPinData` or `HasInitData`. + @has_data($has_data:ident, $get_data:ident), + // `pin_init_from_closure` or `init_from_closure`. + @construct_closure($construct_closure:ident), + @zeroed($($init_zeroed:expr)?), ) => {{ // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return // type and shadow it later when we insert the arbitrary user code. That way there will be @@ -1024,6 +1101,17 @@ macro_rules! __init_internal { { // Shadow the structure so it cannot be used to return early. struct __InitOk; + // If `$init_zeroed` is present we should zero the slot now and not emit an + // error when fields are missing (since they will be zeroed). We also have to + // check that the type actually implements `Zeroable`. + $( + fn is_zeroable(ptr: *mut T) {} + // Ensure that the struct is indeed `Zeroable`. + is_zeroable(slot); + // SAFETY: The type implements `Zeroable` by the check above. + unsafe { ::core::ptr::write_bytes(slot, 0, 1) }; + $init_zeroed // this will be `()` if set. + )? // Create the `this` so it can be referenced by the user inside of the // expressions creating the individual fields. $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)? @@ -1064,7 +1152,7 @@ macro_rules! __init_internal { @data($data:ident), @slot($slot:ident), @guards($($guards:ident,)*), - @munch_fields($(,)?), + @munch_fields($(..Zeroable::zeroed())? $(,)?), ) => { // Endpoint of munching, no fields are left. If execution reaches this point, all fields // have been initialized. Therefore we can now dismiss the guards by forgetting them. @@ -1157,6 +1245,30 @@ macro_rules! __init_internal { @munch_fields($($rest)*), ); }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields(..Zeroable::zeroed() $(,)?), + @acc($($acc:tt)*), + ) => { + // Endpoint, nothing more to munch, create the initializer. Since the users specified + // `..Zeroable::zeroed()`, the slot will already have been zeroed and all field that have + // not been overwritten are thus zero and initialized. We still check that all fields are + // actually accessible by using the struct update syntax ourselves. + // Since we are in the `if false` branch, this will never get executed. We abuse `slot` to + // get the correct type inference here: + unsafe { + let mut zeroed = ::core::mem::zeroed(); + // We have to use type inference her to make zeroed have the correct type. This does + // not get executed, so it has no effect. + ::core::ptr::write($slot, zeroed); + zeroed = ::core::mem::zeroed(); + ::core::ptr::write($slot, $t { + $($acc)* + ..zeroed + }); + } + }; (make_initializer: @slot($slot:ident), @type_name($t:ident), From patchwork Sat Jun 24 09:25:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112434 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6292982vqr; Sat, 24 Jun 2023 02:31:53 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5DAHVmeuiLG0SHJcgylEhxG7Mclsg1URcQz7+DUfo+LwXP4v3Vfqx2AI/5YLMJ/SogEJOp X-Received: by 2002:a05:6a20:8f0b:b0:107:1805:feea with SMTP id b11-20020a056a208f0b00b001071805feeamr31678385pzk.37.1687599113438; Sat, 24 Jun 2023 02:31:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687599113; cv=none; d=google.com; s=arc-20160816; b=iKd0e0CRfx5FUwvKT4THedt84M94l63jkh9nozvdMqljsn+sTJWInMuOxB/gSI5Q1V Yj2UFsQ0khc3HGCrd5D/TouSQ75GFScUautcXE4Pe6a8YfH2DBvVcxyW9BGE4HPyBBPJ qHHVKhyo1XdLrLvuEqOvXhw9zeKAURhW//3YfTeRJNsxpHQPndWHsB8hhW3nAOSmsPj9 W64PE3Bp9JnLCeOWvzvj/FgotZbHS4thPMC5bOexoGfkN+7Wg8+6MEdnUkNwd56SZa1O NzeRnQJF+q7Uu8Qxeo1D2Phikv4PIM/O1UKidS8aBsY4dLsyBGxx1UtebWPLwuzAFehj 2dCQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=gLSKxHqYjJDu1EPRJrKPDpLlPpYDJtCGZbauNM8TqoI=; fh=Y3hGqDvLhFUfCxW08frpp1lth+cS6Xq25ZUxZll0Zv0=; b=eXEg9IQzl1oanOwbFXYwkM/dyjS/LrrbHDsynedhsg1gtkl8mGj2T4DzVZ7TEtYF0A 0nQvdNrQp4ke2tkl692oE7xnLhFSvbWs69WV40L2bCxDTz5difK8Slbci8UsS9bEwzWD 9PGX8rycYYHWMi/TJ8SqGHVw1U5I2oHQnV6NEey3Xg7UPNs6mdQ3wjwnD1WB+FGQk551 PbPHT6LLFaMuop3fOE25rwvU8JmRk4rrwbTpryohbVPJGtrqPDw1Ksw7Xm7lWd6mKa6+ dmVur+oObWRXYtt8zMrtPrUgc75G6oUTzojM2qssZfMsqtRMM9ELsutEkoepArjWKfCp hSUg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=PzF6EJxG; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id fi4-20020a056a00398400b006665df4f1f0si984100pfb.136.2023.06.24.02.31.40; Sat, 24 Jun 2023 02:31:53 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=PzF6EJxG; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233092AbjFXJ0S (ORCPT + 99 others); Sat, 24 Jun 2023 05:26:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52750 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233090AbjFXJ0H (ORCPT ); Sat, 24 Jun 2023 05:26:07 -0400 Received: from mail-40133.protonmail.ch (mail-40133.protonmail.ch [185.70.40.133]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7286A2726 for ; Sat, 24 Jun 2023 02:25:41 -0700 (PDT) Date: Sat, 24 Jun 2023 09:25:34 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1687598739; x=1687857939; bh=gLSKxHqYjJDu1EPRJrKPDpLlPpYDJtCGZbauNM8TqoI=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=PzF6EJxG1JQsX9+/c2QvRBGF5Cbkpnue8an6zQc9ZiqUYDUbqRR6uVPFTdYoxa/Bm XHmJxP2+dwnwyHf3xCVMBvycPXyKDfrpaN8Bj5tmkrE7wWjqNdcgT0DqgpmPB6vDEW OGo9gZ1pYvS64q4AwSnH2I155EI8c4TqhNeZjHkl+FjUKDJtx/S5blWgJzPDYobl3p kcKJ6g3/OTVZOVXUBEFG84CoFPxfj3wPjJYttS6dDdVYjlT29F0Ggd9yxgSjmoIx7G s37c8geiKJZtxmImSKsQ7OZ8+PKwY5V37EkLz4u2s8DNqh6gwCSt8tlyZ/9iSIZknp dohrYK9kH30KA== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Asahi Lina Subject: [PATCH 6/7] rust: init: Add functions to create array initializers Message-ID: <20230624092330.157338-6-benno.lossin@proton.me> In-Reply-To: <20230624092330.157338-1-benno.lossin@proton.me> References: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769575927841982358?= X-GMAIL-MSGID: =?utf-8?q?1769575927841982358?= Add two functions `pin_init_array_from_fn` and `init_array_from_fn` that take a function that generates initializers for `T` from usize, the added functions then return an initializer for `[T; N]` where every element is initialized by an element returned from the generator function. Suggested-by: Asahi Lina Signed-off-by: Benno Lossin Reviewed-by: Björn Roy Baron --- rust/kernel/init.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) -- 2.41.0 diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 44bc3e77419a..c9ea4bf71987 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -867,6 +867,96 @@ pub fn uninit() -> impl Init, E> { unsafe { init_from_closure(|_| Ok(())) } } +/// Initializes an array by initializing each element via the provided initializer. +/// +/// # Examples +/// +/// ```rust +/// let array: Box<[usize; 1000_000_000]>= Box::init(init_array_from_fn(|i| i)).unwrap(); +/// println!("{array:?}"); +/// ``` +pub fn init_array_from_fn( + mut make_init: impl FnMut(usize) -> I, +) -> impl Init<[T; N], E> +where + I: Init, +{ + let init = move |slot: *mut [T; N]| { + let slot = slot.cast::(); + for i in 0..N { + let init = make_init(i); + // SAFETY: since 0 <= `i` < N, it is still in bounds of `[T; N]`. + let ptr = unsafe { slot.add(i) }; + // SAFETY: The pointer is derived from `slot` and thus satisfies the `__init` + // requirements. + match unsafe { init.__init(ptr) } { + Ok(()) => {} + Err(e) => { + // We now free every element that has been initialized before: + for j in 0..i { + let ptr = unsafe { slot.add(j) }; + // SAFETY: The value was initialized in a previous iteration of the loop + // and since we return `Err` below, the caller will consider the memory at + // `slot` as uninitialized. + unsafe { ptr::drop_in_place(ptr) }; + } + return Err(e); + } + } + } + Ok(()) + }; + // SAFETY: The initializer above initializes every element of the array. On failure it drops + // any initialized elements and returns `Err`. + unsafe { init_from_closure(init) } +} + +/// Initializes an array by initializing each element via the provided initializer. +/// +/// # Examples +/// +/// ```rust +/// let array: Arc<[Mutex; 1000_000_000]>= +/// Arc::pin_init(init_array_from_fn(|i| new_mutex!(i))).unwrap(); +/// println!("{array:?}"); +/// ``` +pub fn pin_init_array_from_fn( + mut make_init: impl FnMut(usize) -> I, +) -> impl PinInit<[T; N], E> +where + I: PinInit, +{ + let init = move |slot: *mut [T; N]| { + let slot = slot.cast::(); + for i in 0..N { + let init = make_init(i); + // SAFETY: since 0 <= `i` < N, it is still in bounds of `[T; N]`. + let ptr = unsafe { slot.add(i) }; + // SAFETY: The pointer is derived from `slot` and thus satisfies the `__pinned_init` + // requirements. + match unsafe { init.__pinned_init(ptr) } { + Ok(()) => {} + Err(e) => { + // We now have to free every element that has been initialized before, since we + // have to abide by the drop guarantee. + for j in 0..i { + let ptr = unsafe { slot.add(j) }; + // SAFETY: The value was initialized in a previous iteration of the loop + // and since we return `Err` below, the caller will consider the memory at + // `slot` as uninitialized. + unsafe { ptr::drop_in_place(ptr) }; + } + return Err(e); + } + } + } + Ok(()) + }; + // SAFETY: The initializer above initializes every element of the array. On failure it drops + // any initialized elements and returns `Err`. + unsafe { pin_init_from_closure(init) } +} + // SAFETY: Every type can be initialized by-value. unsafe impl Init for T { unsafe fn __init(self, slot: *mut T) -> Result<(), E> { From patchwork Sat Jun 24 09:25:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benno Lossin X-Patchwork-Id: 112433 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6292920vqr; Sat, 24 Jun 2023 02:31:40 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6GFWVpgCdDE7BnpsP/oNRX7CBG2QVVeor/If634WSOlep1CClc8VGHRYe48m6S7mVoBlSC X-Received: by 2002:a17:902:c10c:b0:1b5:4709:fa0e with SMTP id 12-20020a170902c10c00b001b54709fa0emr1550380pli.10.1687599099957; Sat, 24 Jun 2023 02:31:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687599099; cv=none; d=google.com; s=arc-20160816; b=A7ufkskjBlciNzsXGHGftHQd4XYdJngfX+2VrqBUWw8Qi64WJty7CooawSZXZqtZZm Xc/55BBqfK89tcX244K1OZsAL1BwVkG1XLQlPCnsArUmFOng12xZBIn5hwIu4W8H0uYW /KYbo4RLnq6wqCZIH77fP1OJQ8pdHb4oiUh3j62+uiKKST+q7C8gLlSgk0kaWtIxg/74 +tf6gEvoJLN0TdWwmfW3oRs7QIhjPKIEZ6q96VmJg5vY7ILB+xzDKv+FTk3/ou95gksO Wx+pmYCVJLkTW6kC60i8fona+U85564HoDfUeMU7wg113nFFSfkqxYsu0kFaszs1mOGV vlGg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=4u3eXFuoov+G4R/MtGOXrWncucnn87dIfxR6A2h1gls=; fh=Y3hGqDvLhFUfCxW08frpp1lth+cS6Xq25ZUxZll0Zv0=; b=YwwPq/t2lZc8ztxsHMvBMSKIHtcT7ES5SZFMM3BrwovRYYUa6aosCVFL5Ap5ZGNXtW wMsnp5DC5uxZC0j+JnfB0K9r+KtvmhOfaKVsK1aztZKbzKb2M83BFmvonLVPRClxv4Kh ISGkUuPow3CIkRpZ4amAqFEXbkGqo1p2wYGyVo2rjYiSnd6enklygvQ3j9ADMKm5+86r /COASSClMWiP+9a0l8neFWjoEhHNbPFc3YA2iv/ajhKgouX9Z5xDi1M5m9T0m632Z9yL tV5vExw5QlZNTU8Ep8B9eKq8ENvwqNyzcA1RVooMHebSNo/Oq1TXWBDWBQSWFN73Tt4B lJAg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=cKuF3PVs; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id l10-20020a170903244a00b001ae3cb42bfdsi1004426pls.634.2023.06.24.02.31.26; Sat, 24 Jun 2023 02:31:39 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=cKuF3PVs; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232760AbjFXJ0e (ORCPT + 99 others); Sat, 24 Jun 2023 05:26:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52932 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233056AbjFXJ0S (ORCPT ); Sat, 24 Jun 2023 05:26:18 -0400 Received: from mail-40131.protonmail.ch (mail-40131.protonmail.ch [185.70.40.131]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07939211E; Sat, 24 Jun 2023 02:25:47 -0700 (PDT) Date: Sat, 24 Jun 2023 09:25:39 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1687598744; x=1687857944; bh=4u3eXFuoov+G4R/MtGOXrWncucnn87dIfxR6A2h1gls=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=cKuF3PVseiBIPHVbekskRtA5tfj9oT8WATPSFesjshLVN3uHAqe8yeQWu5+REZVbq 2VmxbaYsYxdSXMnT1divFecszBbKOM8cL7STDOfT6J6xrZEu4rmlnNqRRx9sayKMdM GaK1SWGq8KHyufHHrVisp9NsfQmBouc3Svanwd7pfCeGNgmcVrRHBP3uS5bBP7R/US WX3JK9BY910N5z290B1ItfCwfmlFA1Eud+tCvOJdsiRucDSado5DQNXJ0DK4qXBeTt t7wv0M5Bt8ofR77pwJpLAUPNVJyxSNtze6fWfSixZLKWRemVfXKDRKifHWQYBwAzDR EHkoN7Sry55tg== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Asahi Lina Subject: [PATCH 7/7] rust: init: add support for arbitrary paths in init macros Message-ID: <20230624092330.157338-7-benno.lossin@proton.me> In-Reply-To: <20230624092330.157338-1-benno.lossin@proton.me> References: <20230624092330.157338-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769575914221333756?= X-GMAIL-MSGID: =?utf-8?q?1769575914221333756?= Previously only `ident` and generic types were supported in the `{try_}{pin_}init!` macros. This patch allows arbitrary path fragments, so for example `Foo::Bar` but also very complex paths such as `::Bar::<0, i32>`. Internally this is accomplished by using `path` fragments. Due to some peculiar declarative macro limitations, we have to "forget" certain additional parsing information in the token trees. This is achieved by the new `retokenize` proc macro. It does not modify the input, but just strips this information. For example, if a declarative macro takes `$t:path` as its input, it cannot sensibly propagate this to a macro that takes `$($p:tt)*` as its input, since the `$t` token will only be considered one `tt` token for the second macro. If we first pipe the tokens through `retokenize`, then it parses as expected. Suggested-by: Asahi Lina Signed-off-by: Benno Lossin Reviewed-by: Björn Roy Baron --- rust/kernel/init/__internal.rs | 2 ++ rust/kernel/init/macros.rs | 42 +++++++++++++++++++--------------- rust/macros/lib.rs | 17 +++++++++++++- 3 files changed, 41 insertions(+), 20 deletions(-) -- 2.41.0 diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs index 7abd1fb65e41..e36a706a4a1b 100644 --- a/rust/kernel/init/__internal.rs +++ b/rust/kernel/init/__internal.rs @@ -9,6 +9,8 @@ use super::*; +pub use ::macros::retokenize; + /// See the [nomicon] for what subtyping is. See also [this table]. /// /// [nomicon]: https://doc.rust-lang.org/nomicon/subtyping.html diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index 5dcb2e513f26..6a82be675808 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -998,7 +998,7 @@ impl<$($impl_generics)*> $pin_data<$($ty_generics)*> macro_rules! __init_internal { ( @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), + @typ($t:path), @fields($($fields:tt)*), @error($err:ty), // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` @@ -1012,7 +1012,7 @@ macro_rules! __init_internal { ) => { $crate::__init_internal!(with_update_parsed: @this($($this)?), - @typ($t $(::<$($generics),*>)? ), + @typ($t), @fields($($fields)*), @error($err), @data($data, $($use_data)?), @@ -1023,7 +1023,7 @@ macro_rules! __init_internal { }; ( @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), + @typ($t:path), @fields($($fields:tt)*), @error($err:ty), // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` @@ -1037,7 +1037,7 @@ macro_rules! __init_internal { ) => { $crate::__init_internal!(with_update_parsed: @this($($this)?), - @typ($t $(::<$($generics),*>)? ), + @typ($t), @fields($($fields)*), @error($err), @data($data, $($use_data)?), @@ -1048,7 +1048,7 @@ macro_rules! __init_internal { }; ( @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), + @typ($t:path), @fields($($fields:tt)*), @error($err:ty), // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` @@ -1062,7 +1062,7 @@ macro_rules! __init_internal { ) => { $crate::__init_internal!( @this($($this)?), - @typ($t $(::<$($generics),*>)? ), + @typ($t), @fields($($fields)*), @error($err), @data($data, $($use_data)?), @@ -1073,7 +1073,7 @@ macro_rules! __init_internal { }; (with_update_parsed: @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), + @typ($t:path), @fields($($fields:tt)*), @error($err:ty), // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` @@ -1092,7 +1092,7 @@ macro_rules! __init_internal { // Get the data about fields from the supplied type. let data = unsafe { use $crate::init::__internal::$has_data; - $t$(::<$($generics),*>)?::$get_data() + $crate::init::__internal::retokenize!($t::$get_data()) }; // Ensure that `data` really is of type `$data` and help with type inference: let init = $crate::init::__internal::$data::make_closure::<_, __InitOk, $err>( @@ -1247,7 +1247,7 @@ fn is_zeroable(ptr: *mut T) {} }; (make_initializer: @slot($slot:ident), - @type_name($t:ident), + @type_name($t:path), @munch_fields(..Zeroable::zeroed() $(,)?), @acc($($acc:tt)*), ) => { @@ -1263,15 +1263,17 @@ fn is_zeroable(ptr: *mut T) {} // not get executed, so it has no effect. ::core::ptr::write($slot, zeroed); zeroed = ::core::mem::zeroed(); - ::core::ptr::write($slot, $t { - $($acc)* - ..zeroed - }); + $crate::init::__internal::retokenize!( + ::core::ptr::write($slot, $t { + $($acc)* + ..zeroed + }); + ); } }; (make_initializer: @slot($slot:ident), - @type_name($t:ident), + @type_name($t:path), @munch_fields($(,)?), @acc($($acc:tt)*), ) => { @@ -1279,14 +1281,16 @@ fn is_zeroable(ptr: *mut T) {} // Since we are in the `if false` branch, this will never get executed. We abuse `slot` to // get the correct type inference here: unsafe { - ::core::ptr::write($slot, $t { - $($acc)* - }); + $crate::init::__internal::retokenize!( + ::core::ptr::write($slot, $t { + $($acc)* + }); + ); } }; (make_initializer: @slot($slot:ident), - @type_name($t:ident), + @type_name($t:path), @munch_fields($field:ident <- $val:expr, $($rest:tt)*), @acc($($acc:tt)*), ) => { @@ -1299,7 +1303,7 @@ fn is_zeroable(ptr: *mut T) {} }; (make_initializer: @slot($slot:ident), - @type_name($t:ident), + @type_name($t:path), @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), @acc($($acc:tt)*), ) => { diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 9f056a5c780a..d329ab622fd4 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -12,7 +12,7 @@ mod vtable; mod zeroable; -use proc_macro::TokenStream; +use proc_macro::{Group, TokenStream, TokenTree}; /// Declares a kernel module. /// @@ -266,3 +266,18 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { pub fn derive_zeroable(input: TokenStream) -> TokenStream { zeroable::derive(input) } + +/// Does not modify the given TokenStream, but removes any declarative macro information. +#[proc_macro] +pub fn retokenize(input: TokenStream) -> TokenStream { + fn id(tt: TokenTree) -> TokenTree { + match tt { + TokenTree::Group(g) => TokenTree::Group(Group::new( + g.delimiter(), + g.stream().into_iter().map(id).collect(), + )), + x => x, + } + } + input.into_iter().map(id).collect() +}