From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8113DC83F27 for ; Mon, 28 Aug 2023 10:50:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232215AbjH1KuU (ORCPT ); Mon, 28 Aug 2023 06:50:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44182 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231972AbjH1Ktd (ORCPT ); Mon, 28 Aug 2023 06:49:33 -0400 Received: from mail-ej1-x64a.google.com (mail-ej1-x64a.google.com [IPv6:2a00:1450:4864:20::64a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 71A4D119 for ; Mon, 28 Aug 2023 03:49:12 -0700 (PDT) Received: by mail-ej1-x64a.google.com with SMTP id a640c23a62f3a-94a355c9028so230133166b.3 for ; Mon, 28 Aug 2023 03:49:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219744; x=1693824544; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=pDP42zhivXOqcs6bhwO68yvQPwPyC5hRmGTeBYgZbjo=; b=7V18r85iP6wYbDoB1Q2CxxH0UUwhkNHMc2j+CCaD4bNBnGp3Upadsvuh6Gin4Ft9b+ fxS1b8qCXpGTZKY5L7jD1GmZtORRAJvp7jnsgIGYKmSM0Gq88hmWzLdOGOMujD7umUjz AZZEKUdn5EAMSJBlDg9fqIuSF34asIai6XpTpwKWnBbYNjswJD+5hsw/oIP9T+2V90mb +D6p0YLVxp7QeIWUWIckbRYAEgjZCMPEtceRVo1reEYt6OfY/EqTJUkDRcdgaFTTq1o+ hnAGxlBJk+Or3zqDRxBqcE9PKSiv21q/uhAvZGh6S8hW3ayWBxyfWgZ9vZMV0ISyxkPn jXIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219744; x=1693824544; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=pDP42zhivXOqcs6bhwO68yvQPwPyC5hRmGTeBYgZbjo=; b=T7BQly02OiVQ8XojkkBW2ruM3CWcKSiGA6v3uG1YFBunLCWDnZ6R/4GaXnL+lPM5ls Q+tVDCrBw9y8C2P2B6jmudl6b6lh8KquFZmgkc8G0Jzz9+seFs15f/1GfBUJU5lGApZJ 0T/DoBww8rbNpwKv5XtNkE5fjK37FqSSMmKY3JUV2fCuTjui+THwbjkiufrP1jU3B8MB SLppUNHfnMGdbRzp59jptnqbWsMQYng5+IenQwTFv5OSmA1xjH90eFQQu9mMAAnUIxR6 29qT+RYq6mBdDoO0+gK0l8BoleuneBEyemaltgosU8aR1Of1nBP/kKZXqOBUnN1WuMKF 0ruw== X-Gm-Message-State: AOJu0YwI5qSBRPqglgKqsARhe5FF4u9VP6z0Piw9W1G6qeEzKjbClXkg tvyDqV+QK7My7DT2uAIW6BYU8k+m3/RnQ3g= X-Google-Smtp-Source: AGHT+IEdOjPh0Uew8IdWm/tyy/0oO+3TUd+SIs77H1knmFnd3hNZemCnPFGJq1OlHiFl5RLAX9VYSyKw8p7K1Ew= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a17:907:3546:b0:9a1:c474:8e5f with SMTP id aa6-20020a170907354600b009a1c4748e5fmr352725ejc.8.1693219744499; Mon, 28 Aug 2023 03:49:04 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:01 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-2-aliceryhl@google.com> Subject: [PATCH v4 1/7] rust: sync: add `Arc::{from_raw, into_raw}` From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev, Wedson Almeida Filho , Martin Rodriguez Reboredo , Andreas Hindborg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho These methods can be used to turn an `Arc` into a raw pointer and back, in a way that preserves the metadata for fat pointers. This is done using the unstable ptr_metadata feature [1]. However, it could also be done using the unstable pointer_byte_offsets feature [2], which is likely to have a shorter path to stabilization than ptr_metadata. Link: https://github.com/rust-lang/rust/issues/81513 [1] Link: https://github.com/rust-lang/rust/issues/96283 [2] Signed-off-by: Wedson Almeida Filho Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Benno Lossin Reviewed-by: Andreas Hindborg Reviewed-by: Boqun Feng --- v3 -> v4: * Update doc comment for `Arc::from_raw`. * Add Reviewed-by for Martin, Benno, and Andreas. rust/kernel/lib.rs | 1 + rust/kernel/sync/arc.rs | 46 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index d59041ff5ff2..abeef423d3a8 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -16,6 +16,7 @@ #![feature(coerce_unsized)] #![feature(dispatch_from_dyn)] #![feature(new_uninit)] +#![feature(ptr_metadata)] #![feature(receiver_trait)] #![feature(unsize)] =20 diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 172f563976a9..cae2ff44920b 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -24,13 +24,13 @@ }; use alloc::boxed::Box; use core::{ - alloc::AllocError, + alloc::{AllocError, Layout}, fmt, marker::{PhantomData, Unsize}, mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, pin::Pin, - ptr::NonNull, + ptr::{NonNull, Pointee}, }; use macros::pin_data; =20 @@ -212,6 +212,48 @@ unsafe fn from_inner(inner: NonNull>) -> S= elf { } } =20 + /// Convert the [`Arc`] into a raw pointer. + /// + /// The raw pointer has ownership of the refcount that this Arc object= owned. + pub fn into_raw(self) -> *const T { + let ptr =3D self.ptr.as_ptr(); + core::mem::forget(self); + // SAFETY: The pointer is valid. + unsafe { core::ptr::addr_of!((*ptr).data) } + } + + /// Recreates an [`Arc`] instance previously deconstructed via [`Arc::= into_raw`]. + /// + /// # Safety + /// + /// `ptr` must have been returned by a previous call to [`Arc::into_ra= w`]. Additionally, it + /// must not be called more than once for each previous call to [`Arc:= :into_raw`]. + pub unsafe fn from_raw(ptr: *const T) -> Self { + let refcount_layout =3D Layout::new::(); + // SAFETY: The caller guarantees that the pointer is valid. + let val_layout =3D Layout::for_value(unsafe { &*ptr }); + // SAFETY: We're computing the layout of a real struct that existe= d when compiling this + // binary, so its layout is not so large that it can trigger arith= metic overflow. + let val_offset =3D unsafe { refcount_layout.extend(val_layout).unw= rap_unchecked().1 }; + + let metadata: ::Metadata =3D core::ptr::metadata(ptr= ); + // SAFETY: The metadata of `T` and `ArcInner` is the same becau= se `ArcInner` is a struct + // with `T` as its last field. + // + // This is documented at: + // . + let metadata: as Pointee>::Metadata =3D + unsafe { core::mem::transmute_copy(&metadata) }; + // SAFETY: The pointer is in-bounds of an allocation both before a= nd after offsetting the + // pointer, since it originates from a previous call to `Arc::into= _raw` and is still valid. + let ptr =3D unsafe { (ptr as *mut u8).sub(val_offset) as *mut () }; + let ptr =3D core::ptr::from_raw_parts_mut(ptr, metadata); + + // SAFETY: By the safety requirements we know that `ptr` came from= `Arc::into_raw`, so the + // reference count held then will be owned by the new `Arc` object. + unsafe { Self::from_inner(NonNull::new_unchecked(ptr)) } + } + /// Returns an [`ArcBorrow`] from the given [`Arc`]. /// /// This is useful when the argument of a function call is an [`ArcBor= row`] (e.g., in a method --=20 2.42.0.rc1.204.g551eb34607-goog From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5B9D0C83F23 for ; Mon, 28 Aug 2023 10:50:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232181AbjH1KuS (ORCPT ); Mon, 28 Aug 2023 06:50:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44210 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232066AbjH1Kte (ORCPT ); Mon, 28 Aug 2023 06:49:34 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB52D131 for ; Mon, 28 Aug 2023 03:49:12 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-594e8207103so27372917b3.2 for ; Mon, 28 Aug 2023 03:49:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219747; x=1693824547; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=AaqyLPzL5DfmrhwAzqi3/i2YCtejTjC09pg/QV+14WA=; b=U4MaVzwkZosewmXyZ3UYIlthrKUrCJUozk7U43qdxMkort93XeYnkkKoRVl7isRISs LEg67ep1ZhF52aC6Jj9g96aQ1zPkqyMA8j5ZjsTB/it64/5A3/1Z8r2mJL95yaDxx7e1 cv79JGhr+eXXgX24qbAtiKGAdI5c5g06Ny61nUirdbDy7mWOyxQZwrJJdMw+3a81MfoZ SqMrrqZReqLdzF4q2nLWR+PQo5WD9dDfiPur5zgu91Lzns0GIB6GBkBZABNowPHZ/qVc a1HuKvP0BNfd0/uEz/hiWOzljI2BXTEIEY+K/yMTPNdDmzE2W0e6Zc3xLFG1qglDxN20 aG3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219747; x=1693824547; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=AaqyLPzL5DfmrhwAzqi3/i2YCtejTjC09pg/QV+14WA=; b=Pgc86QbVaUhu8SOB/VPv80idTKV3d7VZTdFM8VMCN0g3eJ8I2qACZIVkmGGjRvdlYx /pA1ADQd3oeEI+2h2znhwRMky43gEqlt4MMiC4f8mkB0MkO/G46zRJSEl9FO/Zhh2UY+ HXJglegGtVnVQB9d/kAlWb6mtIQt2ZRtbDhDDIYCX2ygbl8k6jdn9cAEMC0ZAdIUQ8Il VTiNAJ96uAxAQaVa8jHaNaSd+mNfHMcL3TOOLqlfFuVWYAPPfC+n7yk4F+/XYoW0nKXK hfXZWTQ4jc8gcTro4cWlZ72M3L2Mpcvru/liyzzqEgz+vem9MjFN2dIp9h+FVYTl36+o eN6g== X-Gm-Message-State: AOJu0YwOxgXwkxtSmsPtipAYkEh2W7avj+sizYEzO31fD/HDDOBy8P7L sdIgM/PNzX3hPIneLlN1edPn7UhE+JMgkEY= X-Google-Smtp-Source: AGHT+IGCZXrcEJml/uFdu2lKN+b3E5c2vxmWIO3DGJceLMsq469NqNCWUWJz9ys0whWTlSmJ/Ji6FpUUxRoqt7M= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a81:ac08:0:b0:583:9ace:cf41 with SMTP id k8-20020a81ac08000000b005839acecf41mr759201ywh.0.1693219746901; Mon, 28 Aug 2023 03:49:06 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:02 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-3-aliceryhl@google.com> Subject: [PATCH v4 2/7] rust: workqueue: add low-level workqueue bindings From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev, Martin Rodriguez Reboredo , Andreas Hindborg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Define basic low-level bindings to a kernel workqueue. The API defined here can only be used unsafely. Later commits will provide safe wrappers. Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Andreas Hindborg (Samsung) Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- v3 -> v4: * Update safety comment for Send and Sync impls. * Add Reviewed-by for Boqun. rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 1 + rust/kernel/workqueue.rs | 109 ++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 rust/kernel/workqueue.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 058954961bfc..ca8ac8d8decb 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include =20 /* `bindgen` gets confused at certain things. */ const size_t BINDINGS_ARCH_SLAB_MINALIGN =3D ARCH_SLAB_MINALIGN; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index abeef423d3a8..b56aaea3de7a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -44,6 +44,7 @@ pub mod sync; pub mod task; pub mod types; +pub mod workqueue; =20 #[doc(hidden)] pub use bindings; diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs new file mode 100644 index 000000000000..19e5c50127af --- /dev/null +++ b/rust/kernel/workqueue.rs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Work queues. +//! +//! C header: [`include/linux/workqueue.h`](../../../../include/linux/work= queue.h) + +use crate::{bindings, types::Opaque}; + +/// A kernel work queue. +/// +/// Wraps the kernel's C `struct workqueue_struct`. +/// +/// It allows work items to be queued to run on thread pools managed by th= e kernel. Several are +/// always available, for example, `system`, `system_highpri`, `system_lon= g`, etc. +#[repr(transparent)] +pub struct Queue(Opaque); + +// SAFETY: Accesses to workqueues used by [`Queue`] are thread-safe. +unsafe impl Send for Queue {} +// SAFETY: Accesses to workqueues used by [`Queue`] are thread-safe. +unsafe impl Sync for Queue {} + +impl Queue { + /// Use the provided `struct workqueue_struct` with Rust. + /// + /// # Safety + /// + /// The caller must ensure that the provided raw pointer is not dangli= ng, that it points at a + /// valid workqueue, and that it remains valid until the end of 'a. + pub unsafe fn from_raw<'a>(ptr: *const bindings::workqueue_struct) -> = &'a Queue { + // SAFETY: The `Queue` type is `#[repr(transparent)]`, so the poin= ter cast is valid. The + // caller promises that the pointer is not dangling. + unsafe { &*(ptr as *const Queue) } + } + + /// Enqueues a work item. + /// + /// This may fail if the work item is already enqueued in a workqueue. + /// + /// The work item will be submitted using `WORK_CPU_UNBOUND`. + pub fn enqueue(&self, w: W) -> W::EnqueueOutput + where + W: RawWorkItem + Send + 'static, + { + let queue_ptr =3D self.0.get(); + + // SAFETY: We only return `false` if the `work_struct` is already = in a workqueue. The other + // `__enqueue` requirements are not relevant since `W` is `Send` a= nd static. + // + // The call to `bindings::queue_work_on` will dereference the prov= ided raw pointer, which + // is ok because `__enqueue` guarantees that the pointer is valid = for the duration of this + // closure. + // + // Furthermore, if the C workqueue code accesses the pointer after= this call to + // `__enqueue`, then the work item was successfully enqueued, and = `bindings::queue_work_on` + // will have returned true. In this case, `__enqueue` promises tha= t the raw pointer will + // stay valid until we call the function pointer in the `work_stru= ct`, so the access is ok. + unsafe { + w.__enqueue(move |work_ptr| { + bindings::queue_work_on(bindings::WORK_CPU_UNBOUND as _, q= ueue_ptr, work_ptr) + }) + } + } +} + +/// A raw work item. +/// +/// This is the low-level trait that is designed for being as general as p= ossible. +/// +/// The `ID` parameter to this trait exists so that a single type can prov= ide multiple +/// implementations of this trait. For example, if a struct has multiple `= work_struct` fields, then +/// you will implement this trait once for each field, using a different i= d for each field. The +/// actual value of the id is not important as long as you use different i= ds for different fields +/// of the same struct. (Fields of different structs need not use differen= t ids.) +/// +/// Note that the id is used only to select the right method to call durin= g compilation. It wont be +/// part of the final executable. +/// +/// # Safety +/// +/// Implementers must ensure that any pointers passed to a `queue_work_on`= closure by `__enqueue` +/// remain valid for the duration specified in the guarantees section of t= he documentation for +/// `__enqueue`. +pub unsafe trait RawWorkItem { + /// The return type of [`Queue::enqueue`]. + type EnqueueOutput; + + /// Enqueues this work item on a queue using the provided `queue_work_= on` method. + /// + /// # Guarantees + /// + /// If this method calls the provided closure, then the raw pointer is= guaranteed to point at a + /// valid `work_struct` for the duration of the call to the closure. I= f the closure returns + /// true, then it is further guaranteed that the pointer remains valid= until someone calls the + /// function pointer stored in the `work_struct`. + /// + /// # Safety + /// + /// The provided closure may only return `false` if the `work_struct` = is already in a workqueue. + /// + /// If the work item type is annotated with any lifetimes, then you mu= st not call the function + /// pointer after any such lifetime expires. (Never calling the functi= on pointer is okay.) + /// + /// If the work item type is not [`Send`], then the function pointer m= ust be called on the same + /// thread as the call to `__enqueue`. + unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool; +} --=20 2.42.0.rc1.204.g551eb34607-goog From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9087AC83F26 for ; Mon, 28 Aug 2023 10:50:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232240AbjH1KuW (ORCPT ); Mon, 28 Aug 2023 06:50:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44218 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232096AbjH1Ktg (ORCPT ); Mon, 28 Aug 2023 06:49:36 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8A68018B for ; Mon, 28 Aug 2023 03:49:14 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-594e5e2e608so29328927b3.2 for ; Mon, 28 Aug 2023 03:49:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219749; x=1693824549; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=BorrAImMTABb8airVo2n4M+IWhza6B9m8B72c2I7bnc=; b=ZVr65IWuMuDz+VSStAp2mlRisUS2ja6h5TVTTR1ji4Xkh+9bmL91ZjH8VgN2w8LaEM KjKqoAv2exExmfAPJumi00HL0RRi8RgongOvndUyj4XzM9qdoez5qMH6xVbO4C4/HJCi q+QEuSlod0R8PM/avacBeLfg/7fYwnoTvRDiAm71ZKOCKzdEJFsWWfLe3xTXxLXzEHkt vGJ8NgWLxbeGJPjE8lAmnkuELtS523rJNiGy2Llmxq/qjbhIJ3HAGe3fPf0agKOEtLHy ABBM34aHiN8xK54rNT1/0myzkws2BKrh4hH8uMkMfrwAvrAPAoVrzbvX9KE8Vkv+8hN6 JLBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219749; x=1693824549; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=BorrAImMTABb8airVo2n4M+IWhza6B9m8B72c2I7bnc=; b=OUsSMOakvUOisNr17UjesBmRZR8JpLFRP29aHAsWYBf+uAfxJIvim1EnpxMsFTUMXL X8OpSPub8eXNkW5qqjK8+6bb8OWsWjTheyfxTWfVVfqWOAEKXfRepiOmlaTh5gfJ9TOK VNGDjNlDKcWfk0Izlgr+/mw5o/udxwGaPUk/knidG9OWtfyADGeKn1zbCgHx0B77f5Px uw4PbmnH8aEnsfnyydAqo9d0FZemhM5VsxA87K/tstZQsQoaAq+p4pRRmu0mgN8IMtcl 0Jqkd+8WYB7GDw6tUdJoiYVd0W6UH4/gLkfo+GKX3aLGChZyGsINHmq+JltXJ06QKfVa rYvA== X-Gm-Message-State: AOJu0Ywe7WIvL/l2jbdMvI5L5Pqt2lXIzhkYjTKRmPuHBiSlHgWHfiYX ugAEiHfPlmDtqFGy1VtcNcFEDZZzeS+TL9Q= X-Google-Smtp-Source: AGHT+IEAMgoHOoYGZ6vEg7ci/vFHdanR86AmFmfUcjSX2ixfcNpn+UlmAxB/P2cmfzNNFBCIKAJ65ZsIL4wQwR8= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a5b:68e:0:b0:d77:cd42:913a with SMTP id j14-20020a5b068e000000b00d77cd42913amr654954ybq.9.1693219749478; Mon, 28 Aug 2023 03:49:09 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:03 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-4-aliceryhl@google.com> Subject: [PATCH v4 3/7] rust: workqueue: define built-in queues From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev, Wedson Almeida Filho , Martin Rodriguez Reboredo , Andreas Hindborg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wedson Almeida Filho We provide these methods because it lets us access these queues from Rust without using unsafe code. These methods return `&'static Queue`. References annotated with the 'static lifetime are used when the referent will stay alive forever. That is ok for these queues because they are global variables and cannot be destroyed. Signed-off-by: Wedson Almeida Filho Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Gary Guo Reviewed-by: Andreas Hindborg (Samsung) Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- v3 -> v4: * No changes. rust/kernel/workqueue.rs | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 19e5c50127af..6dbc5b9b3da9 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -107,3 +107,74 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self= ::EnqueueOutput where F: FnOnce(*mut bindings::work_struct) -> bool; } + +/// Returns the system work queue (`system_wq`). +/// +/// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU mult= i-threaded. There are +/// users which expect relatively short queue flush time. +/// +/// Callers shouldn't queue work items which can run for too long. +pub fn system() -> &'static Queue { + // SAFETY: `system_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_wq) } +} + +/// Returns the system high-priority work queue (`system_highpri_wq`). +/// +/// It is similar to the one returned by [`system`] but for work items whi= ch require higher +/// scheduling priority. +pub fn system_highpri() -> &'static Queue { + // SAFETY: `system_highpri_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_highpri_wq) } +} + +/// Returns the system work queue for potentially long-running work items = (`system_long_wq`). +/// +/// It is similar to the one returned by [`system`] but may host long runn= ing work items. Queue +/// flushing might take relatively long. +pub fn system_long() -> &'static Queue { + // SAFETY: `system_long_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_long_wq) } +} + +/// Returns the system unbound work queue (`system_unbound_wq`). +/// +/// Workers are not bound to any specific CPU, not concurrency managed, an= d all queued work items +/// are executed immediately as long as `max_active` limit is not reached = and resources are +/// available. +pub fn system_unbound() -> &'static Queue { + // SAFETY: `system_unbound_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_unbound_wq) } +} + +/// Returns the system freezable work queue (`system_freezable_wq`). +/// +/// It is equivalent to the one returned by [`system`] except that it's fr= eezable. +/// +/// A freezable workqueue participates in the freeze phase of the system s= uspend operations. Work +/// items on the workqueue are drained and no new work item starts executi= on until thawed. +pub fn system_freezable() -> &'static Queue { + // SAFETY: `system_freezable_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_freezable_wq) } +} + +/// Returns the system power-efficient work queue (`system_power_efficient= _wq`). +/// +/// It is inclined towards saving power and is converted to "unbound" vari= ants if the +/// `workqueue.power_efficient` kernel parameter is specified; otherwise, = it is similar to the one +/// returned by [`system`]. +pub fn system_power_efficient() -> &'static Queue { + // SAFETY: `system_power_efficient_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_power_efficient_wq) } +} + +/// Returns the system freezable power-efficient work queue (`system_freez= able_power_efficient_wq`). +/// +/// It is similar to the one returned by [`system_power_efficient`] except= that is freezable. +/// +/// A freezable workqueue participates in the freeze phase of the system s= uspend operations. Work +/// items on the workqueue are drained and no new work item starts executi= on until thawed. +pub fn system_freezable_power_efficient() -> &'static Queue { + // SAFETY: `system_freezable_power_efficient_wq` is a C global, always= available. + unsafe { Queue::from_raw(bindings::system_freezable_power_efficient_wq= ) } +} --=20 2.42.0.rc1.204.g551eb34607-goog From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00994C83F1D for ; Mon, 28 Aug 2023 10:50:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232120AbjH1KuK (ORCPT ); Mon, 28 Aug 2023 06:50:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44238 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232107AbjH1Kth (ORCPT ); Mon, 28 Aug 2023 06:49:37 -0400 Received: from mail-ed1-x549.google.com (mail-ed1-x549.google.com [IPv6:2a00:1450:4864:20::549]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6ED8E191 for ; Mon, 28 Aug 2023 03:49:15 -0700 (PDT) Received: by mail-ed1-x549.google.com with SMTP id 4fb4d7f45d1cf-5256fdb3e20so2313222a12.3 for ; Mon, 28 Aug 2023 03:49:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219752; x=1693824552; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=0XJdlnyIi47TCCt+KqTt48ehUlv6JLaTMbUL33NsVck=; b=YNvTVbM0tG6yKGAvcIsOwna9dAWZAPN9ErPdOEZYfV1AEpVv2CPQMzmcyfWIvNsZzr ZyKN3AeosmGkVhw+k+V0r7p6SwsemWRPQjjwwuMNI5uT7RV+wKWOcVstaI86fFaG0O7i Ibe21ZQNoyUlD/VlWo9cnNdWuOH25OY5P7lnTpSRbozEAQMWJE3QXaIO0BL9EvSxkUqJ fmq7xI6w3oY1Tnkgw11FD0gVC14PUO+AyzvVnbwxR+7/X1eLoUWqn4iVUI7/WGsKbXTc ipRx+AiL4tuDWKp90mkr49zKZfSLxSHZVT+9Ar5ccHUHzHFKwnxZSkiVJabnCeFkawgo +0RQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219752; x=1693824552; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=0XJdlnyIi47TCCt+KqTt48ehUlv6JLaTMbUL33NsVck=; b=LWl6Hs5kdGm1lKpBMUi+jzTzZ0Jbez2ZuOAEZVFd8q0mt53DQvmF++1Cy5cpMOv60H ZxJDCwc8yM8qcQmyg4Soiu+oEnfiTpYMZPKmyiwimvjHmxAv9hc1sWWZt1WdtA4Qn54p 5hpBVAg7KbxLz+zrfuBMH+Ht2Xn/pbeN9yIF/UJ5R2GchYbmxJPRyeAUXV15KalX237U b2Jgr+dEXiqVuwUhhUI02MuLagZsq33nT3OfovqCeBTBw8o5FIkIhbtGMVw2nNOdNelu f2wfNEFxevtM0+GNHGHfL1EJBaO/EFSadvxW/p0GDT/RktjYDa+1NNUijQHlPWsqHAxP QLzQ== X-Gm-Message-State: AOJu0YwY/cJMUZzgqmLiUnYfWU5HwBwTh+f0PMnXNYbSdixmGrK+VQ/p UphR+LuF5DdeD43XmJEXBmmGSBHN2eiVrjI= X-Google-Smtp-Source: AGHT+IEWj/SM3lFZSJcEmgneHhzK3Sph7jjCA+NIh+h9N/GO8AMcitH7S3lViIuD67WtBwEqrZKWf1Mk9/GJveY= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a50:d59d:0:b0:521:822e:2f34 with SMTP id v29-20020a50d59d000000b00521822e2f34mr471533edi.8.1693219752091; Mon, 28 Aug 2023 03:49:12 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:04 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-5-aliceryhl@google.com> Subject: [PATCH v4 4/7] rust: workqueue: add helper for defining work_struct fields From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The main challenge with defining `work_struct` fields is making sure that the function pointer stored in the `work_struct` is appropriate for the work item type it is embedded in. It needs to know the offset of the `work_struct` field being used (even if there are several!) so that it can do a `container_of`, and it needs to know the type of the work item so that it can call into the right user-provided code. All of this needs to happen in a way that provides a safe API to the user, so that users of the workqueue cannot mix up the function pointers. There are three important pieces that are relevant when doing this: * The pointer type. * The work item struct. This is what the pointer points at. * The `work_struct` field. This is a field of the work item struct. This patch introduces a separate trait for each piece. The pointer type is given a `WorkItemPointer` trait, which pointer types need to implement to be usable with the workqueue. This trait will be implemented for `Arc` and `Box` in a later patch in this patchset. Implementing this trait is unsafe because this is where the `container_of` operation happens, but user-code will not need to implement it themselves. The work item struct should then implement the `WorkItem` trait. This trait is where user-code specifies what they want to happen when a work item is executed. It also specifies what the correct pointer type is. Finally, to make the work item struct know the offset of its `work_struct` field, we use a trait called `HasWork`. If a type implements this trait, then the type declares that, at the given offset, there is a field of type `Work`. The trait is marked unsafe because the OFFSET constant must be correct, but we provide an `impl_has_work!` macro that can safely implement `HasWork` on a type. The macro expands to something that only compiles if the specified field really has the type `Work`. It is used like this: ``` struct MyWorkItem { work_field: Work, } impl_has_work! { impl HasWork for MyWorkItem { self.work_field } } ``` Note that since the `Work` type is annotated with an id, you can have several `work_struct` fields by using a different id for each one. Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Alice Ryhl Reviewed-by: Andreas Hindborg Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng Reviewed-by: Martin Rodriguez Reboredo --- v3 -> v4: * The helper was changed to take a name argument, and is implemented directly. * `Work::new` now takes a name argument, and a `new_work!` macro was introduced to help call it. * Use `core::mem::offset_of` rather than a custom implementation. * Fix imports in examples. * Dropped Reviewed-bys due to changes. rust/helpers.c | 13 ++ rust/kernel/lib.rs | 1 + rust/kernel/workqueue.rs | 257 ++++++++++++++++++++++++++++++++++++++- scripts/Makefile.build | 2 +- 4 files changed, 271 insertions(+), 2 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index ebd69490127b..45cf9702d4e4 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -29,6 +29,7 @@ #include #include #include +#include =20 __noreturn void rust_helper_BUG(void) { @@ -137,6 +138,18 @@ void rust_helper_put_task_struct(struct task_struct *t) } EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); =20 +void rust_helper_init_work_with_key(struct work_struct *work, work_func_t = func, + bool onstack, const char *name, + struct lock_class_key *key) +{ + __init_work(work, onstack); + work->data =3D (atomic_long_t)WORK_DATA_INIT(); + lockdep_init_map(&work->lockdep_map, name, key, 0); + INIT_LIST_HEAD(&work->entry); + work->func =3D func; +} +EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indi= ces. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b56aaea3de7a..c537d2edb4c8 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -16,6 +16,7 @@ #![feature(coerce_unsized)] #![feature(dispatch_from_dyn)] #![feature(new_uninit)] +#![feature(offset_of)] #![feature(ptr_metadata)] #![feature(receiver_trait)] #![feature(unsize)] diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 82e3fb19fdaf..da37bfa97211 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -262,7 +262,7 @@ $(obj)/%.lst: $(src)/%.c FORCE # Compile Rust sources (.rs) # ------------------------------------------------------------------------= --- =20 -rust_allowed_features :=3D new_uninit +rust_allowed_features :=3D new_uninit,offset_of =20 # `--out-dir` is required to avoid temporaries being created by `rustc` in= the # current working directory, which may be not accessible in the out-of-tree diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 6dbc5b9b3da9..251541f1cd68 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -2,9 +2,42 @@ =20 //! Work queues. //! +//! This file has two components: The raw work item API, and the safe work= item API. +//! +//! One pattern that is used in both APIs is the `ID` const generic, which= exists to allow a single +//! type to define multiple `work_struct` fields. This is done by choosing= an id for each field, +//! and using that id to specify which field you wish to use. (The actual = value doesn't matter, as +//! long as you use different values for different fields of the same stru= ct.) Since these IDs are +//! generic, they are used only at compile-time, so they shouldn't exist i= n the final binary. +//! +//! # The raw API +//! +//! The raw API consists of the `RawWorkItem` trait, where the work item n= eeds to provide an +//! arbitrary function that knows how to enqueue the work item. It should = usually not be used +//! directly, but if you want to, you can use it without using the pieces = from the safe API. +//! +//! # The safe API +//! +//! The safe API is used via the `Work` struct and `WorkItem` traits. Furt= hermore, it also includes +//! a trait called `WorkItemPointer`, which is usually not used directly b= y the user. +//! +//! * The `Work` struct is the Rust wrapper for the C `work_struct` type. +//! * The `WorkItem` trait is implemented for structs that can be enqueue= d to a workqueue. +//! * The `WorkItemPointer` trait is implemented for the pointer type tha= t points at a something +//! that implements `WorkItem`. +//! //! C header: [`include/linux/workqueue.h`](../../../../include/linux/work= queue.h) =20 -use crate::{bindings, types::Opaque}; +use crate::{bindings, prelude::*, sync::LockClassKey, types::Opaque}; +use core::marker::PhantomData; + +/// Creates a [`Work`] initialiser with the given name and a newly-created= lock class. +#[macro_export] +macro_rules! new_work { + ($($name:literal)?) =3D> { + $crate::workqueue::Work::new($crate::optional_name!($($name)?), $c= rate::static_lock_class!()) + }; +} =20 /// A kernel work queue. /// @@ -108,6 +141,228 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Sel= f::EnqueueOutput F: FnOnce(*mut bindings::work_struct) -> bool; } =20 +/// Defines the method that should be called directly when a work item is = executed. +/// +/// This trait is implemented by `Pin>` and `Arc`, and is mainly= intended to be +/// implemented for smart pointer types. For your own structs, you would i= mplement [`WorkItem`] +/// instead. The `run` method on this trait will usually just perform the = appropriate +/// `container_of` translation and then call into the `run` method from th= e [`WorkItem`] trait. +/// +/// This trait is used when the `work_struct` field is defined using the [= `Work`] helper. +/// +/// # Safety +/// +/// Implementers must ensure that [`__enqueue`] uses a `work_struct` initi= alized with the [`run`] +/// method of this trait as the function pointer. +/// +/// [`__enqueue`]: RawWorkItem::__enqueue +/// [`run`]: WorkItemPointer::run +pub unsafe trait WorkItemPointer: RawWorkItem { + /// Run this work item. + /// + /// # Safety + /// + /// The provided `work_struct` pointer must originate from a previous = call to `__enqueue` where + /// the `queue_work_on` closure returned true, and the pointer must st= ill be valid. + unsafe extern "C" fn run(ptr: *mut bindings::work_struct); +} + +/// Defines the method that should be called when this work item is execut= ed. +/// +/// This trait is used when the `work_struct` field is defined using the [= `Work`] helper. +pub trait WorkItem { + /// The pointer type that this struct is wrapped in. This will typical= ly be `Arc` or + /// `Pin>`. + type Pointer: WorkItemPointer; + + /// The method that should be called when this work item is executed. + fn run(this: Self::Pointer); +} + +/// Links for a work item. +/// +/// This struct contains a function pointer to the `run` function from the= [`WorkItemPointer`] +/// trait, and defines the linked list pointers necessary to enqueue a wor= k item in a workqueue. +/// +/// Wraps the kernel's C `struct work_struct`. +/// +/// This is a helper type used to associate a `work_struct` with the [`Wor= kItem`] that uses it. +#[repr(transparent)] +pub struct Work { + work: Opaque, + _inner: PhantomData, +} + +// SAFETY: Kernel work items are usable from any thread. +// +// We do not need to constrain `T` since the work item does not actually c= ontain a `T`. +unsafe impl Send for Work {} +// SAFETY: Kernel work items are usable from any thread. +// +// We do not need to constrain `T` since the work item does not actually c= ontain a `T`. +unsafe impl Sync for Work {} + +impl Work { + /// Creates a new instance of [`Work`]. + #[inline] + #[allow(clippy::new_ret_no_self)] + pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl Pi= nInit + where + T: WorkItem, + { + // SAFETY: The `WorkItemPointer` implementation promises that `run= ` can be used as the work + // item function. + unsafe { + kernel::init::pin_init_from_closure(move |slot| { + let slot =3D Self::raw_get(slot); + bindings::init_work_with_key( + slot, + Some(T::Pointer::run), + false, + name.as_char_ptr(), + key.as_ptr(), + ); + Ok(()) + }) + } + } + + /// Get a pointer to the inner `work_struct`. + /// + /// # Safety + /// + /// The provided pointer must not be dangling and must be properly ali= gned. (But the memory + /// need not be initialized.) + #[inline] + pub unsafe fn raw_get(ptr: *const Self) -> *mut bindings::work_struct { + // SAFETY: The caller promises that the pointer is aligned and not= dangling. + // + // A pointer cast would also be ok due to `#[repr(transparent)]`. = We use `addr_of!` so that + // the compiler does not complain that the `work` field is unused. + unsafe { Opaque::raw_get(core::ptr::addr_of!((*ptr).work)) } + } +} + +/// Declares that a type has a [`Work`] field. +/// +/// The intended way of using this trait is via the [`impl_has_work!`] mac= ro. You can use the macro +/// like this: +/// +/// ```no_run +/// use kernel::impl_has_work; +/// use kernel::prelude::*; +/// use kernel::workqueue::Work; +/// +/// struct MyWorkItem { +/// work_field: Work, +/// } +/// +/// impl_has_work! { +/// impl HasWork for MyWorkItem { self.work_field } +/// } +/// ``` +/// +/// Note that since the `Work` type is annotated with an id, you can have = several `work_struct` +/// fields by using a different id for each one. +/// +/// # Safety +/// +/// The [`OFFSET`] constant must be the offset of a field in Self of type = [`Work`]. The methods on +/// this trait must have exactly the behavior that the definitions given b= elow have. +/// +/// [`Work`]: Work +/// [`impl_has_work!`]: crate::impl_has_work +/// [`OFFSET`]: HasWork::OFFSET +pub unsafe trait HasWork { + /// The offset of the [`Work`] field. + /// + /// [`Work`]: Work + const OFFSET: usize; + + /// Returns the offset of the [`Work`] field. + /// + /// This method exists because the [`OFFSET`] constant cannot be acces= sed if the type is not Sized. + /// + /// [`Work`]: Work + /// [`OFFSET`]: HasWork::OFFSET + #[inline] + fn get_work_offset(&self) -> usize { + Self::OFFSET + } + + /// Returns a pointer to the [`Work`] field. + /// + /// # Safety + /// + /// The provided pointer must point at a valid struct of type `Self`. + /// + /// [`Work`]: Work + #[inline] + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work { + // SAFETY: The caller promises that the pointer is valid. + unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut Work } + } + + /// Returns a pointer to the struct containing the [`Work`] fie= ld. + /// + /// # Safety + /// + /// The pointer must point at a [`Work`] field in a struct of t= ype `Self`. + /// + /// [`Work`]: Work + #[inline] + unsafe fn work_container_of(ptr: *mut Work) -> *mut Self + where + Self: Sized, + { + // SAFETY: The caller promises that the pointer points at a field = of the right type in the + // right kind of struct. + unsafe { (ptr as *mut u8).sub(Self::OFFSET) as *mut Self } + } +} + +/// Used to safely implement the [`HasWork`] trait. +/// +/// # Examples +/// +/// ``` +/// use kernel::impl_has_work; +/// use kernel::sync::Arc; +/// use kernel::workqueue::{self, Work}; +/// +/// struct MyStruct { +/// work_field: Work, +/// } +/// +/// impl_has_work! { +/// impl HasWork for MyStruct { self.work_field } +/// } +/// ``` +/// +/// [`HasWork`]: HasWork +#[macro_export] +macro_rules! impl_has_work { + ($(impl$(<$($implarg:ident),*>)? + HasWork<$work_type:ty $(, $id:tt)?> + for $self:ident $(<$($selfarg:ident),*>)? + { self.$field:ident } + )*) =3D> {$( + // SAFETY: The implementation of `raw_get_work` only compiles if t= he field has the right + // type. + unsafe impl$(<$($implarg),*>)? $crate::workqueue::HasWork<$work_ty= pe $(, $id)?> for $self $(<$($selfarg),*>)? { + const OFFSET: usize =3D ::core::mem::offset_of!(Self, $field) = as usize; + + #[inline] + unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workque= ue::Work<$work_type $(, $id)?> { + // SAFETY: The caller promises that the pointer is not dan= gling. + unsafe { + ::core::ptr::addr_of_mut!((*ptr).$field) + } + } + } + )*}; +} + /// Returns the system work queue (`system_wq`). /// /// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU mult= i-threaded. There are --=20 2.42.0.rc1.204.g551eb34607-goog From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5BD3CC83F22 for ; Mon, 28 Aug 2023 10:50:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232166AbjH1KuQ (ORCPT ); Mon, 28 Aug 2023 06:50:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60868 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232132AbjH1Kto (ORCPT ); Mon, 28 Aug 2023 06:49:44 -0400 Received: from mail-ed1-x54a.google.com (mail-ed1-x54a.google.com [IPv6:2a00:1450:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A0DBE1AE for ; Mon, 28 Aug 2023 03:49:19 -0700 (PDT) Received: by mail-ed1-x54a.google.com with SMTP id 4fb4d7f45d1cf-50bf847b267so2372074a12.3 for ; Mon, 28 Aug 2023 03:49:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219754; x=1693824554; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=EpAYUAyxWxnw3jUtIWPiJWYZLbp2zAaEuj7yHabOWqc=; b=XzTJ/q1sd8jr6ASWsMnGU20/8iQtEMTzUJFSZU++Q4JKU51YXFSxOeNCJEVl/khJm6 AtD492RaXxEUPcNkxt4gJxVCji38QYzs+hnYmZEZdVYlluWZN+RSttUG1W0ZLBwu7Pwg R8X9uAen9oQqaO7cnNtgqh2kVCgvNPhvAxTzvmjB7zRya6BssAHCx1P8gc78gVETHMbs i1SqFq6Iea/ZQYTnF3XT2IsCjCmAg2IcFnuygH3AK73NAXjFLv+PrIIa/z+xEeQJOioF wdZbT+RNvUbtmL5tXRzrJFwxji5i1BGa+xVkTXRjXoxHihu++OWQ8oE8BuJQP3Fdfs/z PcPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219754; x=1693824554; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=EpAYUAyxWxnw3jUtIWPiJWYZLbp2zAaEuj7yHabOWqc=; b=kpHFz8XVlwVVrPIogrHjrIqVSXIKEzCUrD1pvOaxQTuaCku9azYumVI4KjQ1Av64t5 xbJgMZzTYU+hCLtXl8ROU2zAh2nqetxpcrbEZhiWaVAZGpU727ZVzPZenUN8lLF0MnlE 0dzIHLNoOOztvoBVLbAKMgnSa6Uzpfjp7l0t3WxT3GGEEMXvXxXC+L1HErO4nlHfqwsZ zoMonQYZx41I+T69/b4+z+598AYpZ+Q5RykNVRugezqbMBSgORfAP4CRePUDW4aXrz9x 1cMysf7rG1wSX1o3ghd8DSXK74QWnNhwvWQgYWDi/exJRo4eSgS8YWuspO47t5zLKJCX MzNA== X-Gm-Message-State: AOJu0YxuJTunz8Dff+vuXbPVzLHlHBIWRub7wkRCcMdENp7lXu1s2Zn9 E7bZOAhIApCinCs+LjD71M3PRndINV7JG6s= X-Google-Smtp-Source: AGHT+IEQrG+gVweyEWQVJLcIHo0Q1u7H8GGDQBphbzbzjWfrMy8pyrBtRjUaysHe+JWs6smqdp5iXyOp9w/9lug= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a50:d696:0:b0:522:1d2e:e553 with SMTP id r22-20020a50d696000000b005221d2ee553mr432512edi.8.1693219754366; Mon, 28 Aug 2023 03:49:14 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:05 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-6-aliceryhl@google.com> Subject: [PATCH v4 5/7] rust: workqueue: implement `WorkItemPointer` for pointer types From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev, Martin Rodriguez Reboredo , Andreas Hindborg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This implements the `WorkItemPointer` trait for the pointer types that you are likely to use the workqueue with. The `Arc` type is for reference counted objects, and the `Pin>` type is for objects where the caller has exclusive ownership of the object. Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Andreas Hindborg (Samsung) Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- v3 -> v4: * No changes. rust/kernel/workqueue.rs | 97 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 251541f1cd68..c1334c48b96b 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -28,8 +28,10 @@ //! //! C header: [`include/linux/workqueue.h`](../../../../include/linux/work= queue.h) =20 -use crate::{bindings, prelude::*, sync::LockClassKey, types::Opaque}; +use crate::{bindings, prelude::*, sync::Arc, sync::LockClassKey, types::Op= aque}; +use alloc::boxed::Box; use core::marker::PhantomData; +use core::pin::Pin; =20 /// Creates a [`Work`] initialiser with the given name and a newly-created= lock class. #[macro_export] @@ -363,6 +365,99 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate:= :workqueue::Work<$work_typ )*}; } =20 +unsafe impl WorkItemPointer for Arc +where + T: WorkItem, + T: HasWork, +{ + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { + // SAFETY: The `__enqueue` method always uses a `work_struct` stor= ed in a `Work`. + let ptr =3D ptr as *mut Work; + // SAFETY: This computes the pointer that `__enqueue` got from `Ar= c::into_raw`. + let ptr =3D unsafe { T::work_container_of(ptr) }; + // SAFETY: This pointer comes from `Arc::into_raw` and we've been = given back ownership. + let arc =3D unsafe { Arc::from_raw(ptr) }; + + T::run(arc) + } +} + +unsafe impl RawWorkItem for Arc +where + T: WorkItem, + T: HasWork, +{ + type EnqueueOutput =3D Result<(), Self>; + + unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool, + { + // Casting between const and mut is not a problem as long as the p= ointer is a raw pointer. + let ptr =3D Arc::into_raw(self).cast_mut(); + + // SAFETY: Pointers into an `Arc` point at a valid value. + let work_ptr =3D unsafe { T::raw_get_work(ptr) }; + // SAFETY: `raw_get_work` returns a pointer to a valid value. + let work_ptr =3D unsafe { Work::raw_get(work_ptr) }; + + if queue_work_on(work_ptr) { + Ok(()) + } else { + // SAFETY: The work queue has not taken ownership of the point= er. + Err(unsafe { Arc::from_raw(ptr) }) + } + } +} + +unsafe impl WorkItemPointer for Pin> +where + T: WorkItem, + T: HasWork, +{ + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { + // SAFETY: The `__enqueue` method always uses a `work_struct` stor= ed in a `Work`. + let ptr =3D ptr as *mut Work; + // SAFETY: This computes the pointer that `__enqueue` got from `Ar= c::into_raw`. + let ptr =3D unsafe { T::work_container_of(ptr) }; + // SAFETY: This pointer comes from `Arc::into_raw` and we've been = given back ownership. + let boxed =3D unsafe { Box::from_raw(ptr) }; + // SAFETY: The box was already pinned when it was enqueued. + let pinned =3D unsafe { Pin::new_unchecked(boxed) }; + + T::run(pinned) + } +} + +unsafe impl RawWorkItem for Pin> +where + T: WorkItem, + T: HasWork, +{ + type EnqueueOutput =3D (); + + unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool, + { + // SAFETY: We're not going to move `self` or any of its fields, so= its okay to temporarily + // remove the `Pin` wrapper. + let boxed =3D unsafe { Pin::into_inner_unchecked(self) }; + let ptr =3D Box::into_raw(boxed); + + // SAFETY: Pointers into a `Box` point at a valid value. + let work_ptr =3D unsafe { T::raw_get_work(ptr) }; + // SAFETY: `raw_get_work` returns a pointer to a valid value. + let work_ptr =3D unsafe { Work::raw_get(work_ptr) }; + + if !queue_work_on(work_ptr) { + // SAFETY: This method requires exclusive ownership of the box= , so it cannot be in a + // workqueue. + unsafe { ::core::hint::unreachable_unchecked() } + } + } +} + /// Returns the system work queue (`system_wq`). /// /// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU mult= i-threaded. There are --=20 2.42.0.rc1.204.g551eb34607-goog From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D55BCC71153 for ; Mon, 28 Aug 2023 10:50:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232269AbjH1KuZ (ORCPT ); Mon, 28 Aug 2023 06:50:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60934 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232138AbjH1Kto (ORCPT ); Mon, 28 Aug 2023 06:49:44 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DE25F1B5 for ; Mon, 28 Aug 2023 03:49:20 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-d7475f45d31so3662265276.0 for ; Mon, 28 Aug 2023 03:49:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219757; x=1693824557; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=v7MHrwaqRQcEpiQUM63plXyMym72xBHnTiBqMbx7wZw=; b=AMPWN1b/UHqT75qKL7kIoe84TxjmLd12ffSET1Hw3zrmkOFwbRpVrKJO0Cmu7vKTqw jfM29FhtKEndG/pyh+fyXScGgQ140AUwIHMKysjW4X30MuZL6l1pHcgbC8sMj6pGTcQg 2Ri1T9Ff9aeccFLlJ7RFWqFTXHKVbYFBdd+Z/df0DE/2fxuDdKIYf27868XQW/VMM1TO zbX6u8EEd7oD6xVBxHDRTMcxMHou4uyuSZBG7YFDP7dVo5k0nGnqDwM3WA9RLTHJp56j 4lJ4E+8lhveVrs7QXiPHz6Sord4FCuGSmK8HAEvJRIQb9QAYu1p22PwiEw3zNHdwAPIp DUJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219757; x=1693824557; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=v7MHrwaqRQcEpiQUM63plXyMym72xBHnTiBqMbx7wZw=; b=WG7ZgwUMuTssVYZcHZhdH6/F4RFdU5iR791cQsR26T1fcnH/I4ZYqP4ZkONIEHZol6 9yy+7BYAO8yeS0LaZPF60rtbAg1Cf2vjqU/PEqJE7AUWZSIouTwXaVKUVdOBy9ehfMeU 1Wz6jynx5h1yywG3N+qz3ER3O8d5ewE9NsPqvBSkdm8vodgATZl1ZYkiATY0xp35Fhts Q4gHVgl6wguM/5qHI3N6L7caFEDpc4KTT6Vds//D5yuDuNJELf3JQ1MSyyEXPGJPtsW3 vjs0JnRM1MfncBOCoPeAqrgXV+4WfIZTSWcgC83V4BAlCYuxmyrMOq1TSJj0G6w9OfTS pKCg== X-Gm-Message-State: AOJu0Yzvl91CcGRzqr4MdbbZcncwDDbVF7DluKaJYh7uquoi/LghTmdW Qi51tIOqqXIKua3o/Pj8U+CuvJq4QXxjXUc= X-Google-Smtp-Source: AGHT+IG68V4CQ3Yh0kd1RgiLluj29FsB8LO+ZlUEBBe9LgdPfKf4r19liQXmPNdRsJc4iZq4rzAmSeqTtzQT/Do= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a25:d6c7:0:b0:d4b:df05:3500 with SMTP id n190-20020a25d6c7000000b00d4bdf053500mr875605ybg.11.1693219756887; Mon, 28 Aug 2023 03:49:16 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:06 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-7-aliceryhl@google.com> Subject: [PATCH v4 6/7] rust: workqueue: add `try_spawn` helper method From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev, Martin Rodriguez Reboredo , Andreas Hindborg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This adds a convenience method that lets you spawn a closure for execution on a workqueue. This will be the most convenient way to use workqueues, but it is fallible because it needs to allocate memory. Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Andreas Hindborg (Samsung) Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- v3 -> v4: * Use new `new_work!` macro. rust/kernel/workqueue.rs | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index c1334c48b96b..40ccc53f6e93 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -29,6 +29,7 @@ //! C header: [`include/linux/workqueue.h`](../../../../include/linux/work= queue.h) =20 use crate::{bindings, prelude::*, sync::Arc, sync::LockClassKey, types::Op= aque}; +use alloc::alloc::AllocError; use alloc::boxed::Box; use core::marker::PhantomData; use core::pin::Pin; @@ -96,6 +97,44 @@ pub fn enqueue(&self, w: W) -> W::Enqu= eueOutput }) } } + + /// Tries to spawn the given function or closure as a work item. + /// + /// This method can fail because it allocates memory to store the work= item. + pub fn try_spawn(&self, func: T) -> Resu= lt<(), AllocError> { + let init =3D pin_init!(ClosureWork { + work <- new_work!("Queue::try_spawn"), + func: Some(func), + }); + + self.enqueue(Box::pin_init(init).map_err(|_| AllocError)?); + Ok(()) + } +} + +/// A helper type used in `try_spawn`. +#[pin_data] +struct ClosureWork { + #[pin] + work: Work>, + func: Option, +} + +impl ClosureWork { + fn project(self: Pin<&mut Self>) -> &mut Option { + // SAFETY: The `func` field is not structurally pinned. + unsafe { &mut self.get_unchecked_mut().func } + } +} + +impl WorkItem for ClosureWork { + type Pointer =3D Pin>; + + fn run(mut this: Pin>) { + if let Some(func) =3D this.as_mut().project().take() { + (func)() + } + } } =20 /// A raw work item. @@ -365,6 +404,10 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate:= :workqueue::Work<$work_typ )*}; } =20 +impl_has_work! { + impl HasWork for ClosureWork { self.work } +} + unsafe impl WorkItemPointer for Arc where T: WorkItem, --=20 2.42.0.rc1.204.g551eb34607-goog From nobody Thu Dec 18 08:38:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4484C83F11 for ; Mon, 28 Aug 2023 10:50:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232254AbjH1KuY (ORCPT ); Mon, 28 Aug 2023 06:50:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60946 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232143AbjH1Ktq (ORCPT ); Mon, 28 Aug 2023 06:49:46 -0400 Received: from mail-ej1-x649.google.com (mail-ej1-x649.google.com [IPv6:2a00:1450:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BBCFCCC4 for ; Mon, 28 Aug 2023 03:49:22 -0700 (PDT) Received: by mail-ej1-x649.google.com with SMTP id a640c23a62f3a-99bca0b9234so257291566b.2 for ; Mon, 28 Aug 2023 03:49:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1693219759; x=1693824559; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=OAlJg4PuI7woDTN+Wm0bsiKVHLOIDSV75p/+lEqw6kA=; b=XnFwXu5YDpx5+Ab0UAmbjNTDztHVXo9CpSwyE2EFlfbXimUQ5lTwlHIAnFZ3t4o49C k/jepCs7z98GXEWCJcoe5gcXzqGfQav18Rx1BMZSWghaROBGzXVbHA50JxL2MZHpaptC yPxz4zg56rnMJr92kMj43rgQCUc96IXT4tO9fEbl4OH7M0JITbMUWr10opQHVb8RQuAT ij4nHODutJ3XLY2UeJiaNc1P2vjVtvyuGbWQV8THNGvw9JgExkGgjjPocqY3aXyQWCpM r0IfP83Cr06VOIw5+26XpEag2RfKq/l0D6uEOOfBEdfrD40nfEP/mlSB9xDvl3x5uggH 3q9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693219759; x=1693824559; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=OAlJg4PuI7woDTN+Wm0bsiKVHLOIDSV75p/+lEqw6kA=; b=aCL8s4ljjHoM33Jrq8qSWoCJ0u6dF+ynRz8ZMa91OwFbMAyuTkh+iudOYA8TJNu9he NVKfzBuCBqEknK0yMmG6gMi5ELMTjzWnJcnvAkILmyCGJAmOL1axaYxjhMtCPqd89LNL b1dXnNnma6zry13GEeju1VD1Y9r0ruGtkfK/2SkaMmS5kELTo40CmeMub2rPA/2yDndy HxPeiPNxB8TpRMrIt52/aX+PpKmuIbc/UQat5HxnhnZ3iXyWlzDXNS2Kxf/KVJbpv00W 0ch0bNJD+3o8+YeDhTvRQnhSrt08v8Zy5jQbgNs/rwms7iFaaHYqvhE2bDeQyCuIAecJ 5l8A== X-Gm-Message-State: AOJu0YzivJ806+JEClXYr6wts1AynRQ3cUs+16HU25aQKBQaw9RQvruw KJFuWl3vGTs+tKAkBLbNdYwNGMdFe23rjf8= X-Google-Smtp-Source: AGHT+IGlSInn2X6uUdlEjdFRXR4sN14V7sjkE1qqya78L0m3DBmtXEEaU3Orgvxl9fEF3KQmjDsnQRr0V0Fh5Xk= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:31:98fb:c0a8:6c8]) (user=aliceryhl job=sendgmr) by 2002:a17:906:8aaf:b0:9a1:ebcf:bcbc with SMTP id mu47-20020a1709068aaf00b009a1ebcfbcbcmr373856ejc.4.1693219759144; Mon, 28 Aug 2023 03:49:19 -0700 (PDT) Date: Mon, 28 Aug 2023 10:48:07 +0000 In-Reply-To: <20230828104807.1581592-1-aliceryhl@google.com> Mime-Version: 1.0 References: <20230828104807.1581592-1-aliceryhl@google.com> X-Mailer: git-send-email 2.42.0.rc1.204.g551eb34607-goog Message-ID: <20230828104807.1581592-8-aliceryhl@google.com> Subject: [PATCH v4 7/7] rust: workqueue: add examples From: Alice Ryhl To: rust-for-linux@vger.kernel.org, Tejun Heo , Miguel Ojeda Cc: Lai Jiangshan , Wedson Almeida Filho , Alex Gaynor , Boqun Feng , Gary Guo , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , Benno Lossin , Alice Ryhl , linux-kernel@vger.kernel.org, patches@lists.linux.dev, Martin Rodriguez Reboredo , Andreas Hindborg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This adds two examples of how to use the workqueue. The first example shows how to use it when you only have one `work_struct` field, and the second example shows how to use it when you have multiple `work_struct` fields. Signed-off-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Gary Guo Reviewed-by: Andreas Hindborg (Samsung) Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- v3 -> v4: * Use `new_work!` macro. * Fix example imports. rust/kernel/workqueue.rs | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 40ccc53f6e93..b67fb1ba168e 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -26,6 +26,112 @@ //! * The `WorkItemPointer` trait is implemented for the pointer type tha= t points at a something //! that implements `WorkItem`. //! +//! ## Example +//! +//! This example defines a struct that holds an integer and can be schedul= ed on the workqueue. When +//! the struct is executed, it will print the integer. Since there is only= one `work_struct` field, +//! we do not need to specify ids for the fields. +//! +//! ``` +//! use kernel::prelude::*; +//! use kernel::sync::Arc; +//! use kernel::workqueue::{self, Work, WorkItem}; +//! use kernel::{impl_has_work, new_work}; +//! +//! #[pin_data] +//! struct MyStruct { +//! value: i32, +//! #[pin] +//! work: Work, +//! } +//! +//! impl_has_work! { +//! impl HasWork for MyStruct { self.work } +//! } +//! +//! impl MyStruct { +//! fn new(value: i32) -> Result> { +//! Arc::pin_init(pin_init!(MyStruct { +//! value, +//! work <- new_work!("MyStruct::work"), +//! })) +//! } +//! } +//! +//! impl WorkItem for MyStruct { +//! type Pointer =3D Arc; +//! +//! fn run(this: Arc) { +//! pr_info!("The value is: {}", this.value); +//! } +//! } +//! +//! /// This method will enqueue the struct for execution on the system wo= rkqueue, where its value +//! /// will be printed. +//! fn print_later(val: Arc) { +//! let _ =3D workqueue::system().enqueue(val); +//! } +//! ``` +//! +//! The following example shows how multiple `work_struct` fields can be u= sed: +//! +//! ``` +//! use kernel::prelude::*; +//! use kernel::sync::Arc; +//! use kernel::workqueue::{self, Work, WorkItem}; +//! use kernel::{impl_has_work, new_work}; +//! +//! #[pin_data] +//! struct MyStruct { +//! value_1: i32, +//! value_2: i32, +//! #[pin] +//! work_1: Work, +//! #[pin] +//! work_2: Work, +//! } +//! +//! impl_has_work! { +//! impl HasWork for MyStruct { self.work_1 } +//! impl HasWork for MyStruct { self.work_2 } +//! } +//! +//! impl MyStruct { +//! fn new(value_1: i32, value_2: i32) -> Result> { +//! Arc::pin_init(pin_init!(MyStruct { +//! value_1, +//! value_2, +//! work_1 <- new_work!("MyStruct::work_1"), +//! work_2 <- new_work!("MyStruct::work_2"), +//! })) +//! } +//! } +//! +//! impl WorkItem<1> for MyStruct { +//! type Pointer =3D Arc; +//! +//! fn run(this: Arc) { +//! pr_info!("The value is: {}", this.value_1); +//! } +//! } +//! +//! impl WorkItem<2> for MyStruct { +//! type Pointer =3D Arc; +//! +//! fn run(this: Arc) { +//! pr_info!("The second value is: {}", this.value_2); +//! } +//! } +//! +//! fn print_1_later(val: Arc) { +//! let _ =3D workqueue::system().enqueue::, 1>(val); +//! } +//! +//! fn print_2_later(val: Arc) { +//! let _ =3D workqueue::system().enqueue::, 2>(val); +//! } +//! ``` +//! //! C header: [`include/linux/workqueue.h`](../../../../include/linux/work= queue.h) =20 use crate::{bindings, prelude::*, sync::Arc, sync::LockClassKey, types::Op= aque}; --=20 2.42.0.rc1.204.g551eb34607-goog