2023-06-01 14:05:53

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 0/8] rust: workqueue: add bindings for the workqueue

This patchset contains bindings for the kernel workqueue.

One of the primary goals behind the design used in this patch is that we
must support embedding the `work_struct` as a field in user-provided
types, because this allows you to submit things to the workqueue without
having to allocate, making the submission infallible. If we didn't have
to support this, then the patch would be much simpler. One of the main
things that make it complicated is that we must ensure that the function
pointer in the `work_struct` is compatible with the struct it is
contained within.

The original version of the workqueue bindings was written by Wedson,
but I have rewritten much of it so that it uses the pin-init
infrastructure and can be used with containers other than `Arc`.

Changes since v1:

Most of this patchset was rewritten based on Gary's example for how
field projections can be used with the workqueue.

I have also added some examples of how the workqueue bindings will be
used. You can find those in the last patch of this patchset.

v1: https://lore.kernel.org/all/[email protected]/

Alice Ryhl (5):
rust: workqueue: add low-level workqueue bindings
rust: workqueue: add helper for defining work_struct fields
rust: workqueue: implement `WorkItemPointer` for pointer types
rust: workqueue: add `try_spawn` helper method
rust: workqueue: add examples

Wedson Almeida Filho (3):
rust: add offset_of! macro
rust: sync: add `Arc::{from_raw, into_raw}`
rust: workqueue: define built-in queues

rust/bindings/bindings_helper.h | 1 +
rust/helpers.c | 8 +
rust/kernel/lib.rs | 37 ++
rust/kernel/sync/arc.rs | 42 ++-
rust/kernel/workqueue.rs | 631 ++++++++++++++++++++++++++++++++
scripts/Makefile.build | 2 +-
6 files changed, 719 insertions(+), 2 deletions(-)
create mode 100644 rust/kernel/workqueue.rs


base-commit: d09a61024f6b78c6a08892fc916cdafd87b50365
--
2.41.0.rc0.172.g3f132b7071-goog



2023-06-01 14:07:42

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 7/8] rust: workqueue: add `try_spawn` helper method

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 <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Signed-off-by: Alice Ryhl <[email protected]>
---
rust/kernel/workqueue.rs | 43 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)

diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index f06a2f036d8b..c302e8b8624b 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -29,6 +29,7 @@
//! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)

use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
+use alloc::alloc::AllocError;
use alloc::boxed::Box;
use core::marker::{PhantomData, PhantomPinned};
use core::pin::Pin;
@@ -87,6 +88,44 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput
})
}
}
+
+ /// 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<T: 'static + Send + FnOnce()>(&self, func: T) -> Result<(), AllocError> {
+ let init = pin_init!(ClosureWork {
+ work <- Work::new(),
+ func: Some(func),
+ });
+
+ self.enqueue(Box::pin_init(init).map_err(|_| AllocError)?);
+ Ok(())
+ }
+}
+
+/// A helper type used in `try_spawn`.
+#[pin_data]
+struct ClosureWork<T> {
+ #[pin]
+ work: Work<ClosureWork<T>>,
+ func: Option<T>,
+}
+
+impl<T> ClosureWork<T> {
+ fn project(self: Pin<&mut Self>) -> &mut Option<T> {
+ // SAFETY: The `func` field is not structurally pinned.
+ unsafe { &mut self.get_unchecked_mut().func }
+ }
+}
+
+impl<T: FnOnce()> WorkItem for ClosureWork<T> {
+ type Pointer = Pin<Box<Self>>;
+
+ fn run(mut this: Pin<Box<Self>>) {
+ if let Some(func) = this.as_mut().project().take() {
+ (func)()
+ }
+ }
}

/// A raw work item.
@@ -325,6 +364,10 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_typ
)*};
}

+impl_has_work! {
+ impl<T> HasWork<Self> for ClosureWork<T> { self.work }
+}
+
unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
where
T: WorkItem<ID, Pointer = Self>,
--
2.41.0.rc0.172.g3f132b7071-goog


2023-06-01 14:15:24

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 8/8] rust: workqueue: add examples

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 <[email protected]>
---
rust/kernel/workqueue.rs | 104 +++++++++++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)

diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index c302e8b8624b..cefcf43ff40e 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -26,6 +26,110 @@
//! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
//! that implements `WorkItem`.
//!
+//! ## Example
+//!
+//! This example defines a struct that holds an integer and can be scheduled 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};
+//!
+//! #[pin_data]
+//! struct MyStruct {
+//! value: i32,
+//! #[pin]
+//! work: Work<MyStruct>,
+//! }
+//!
+//! impl_has_work! {
+//! impl HasWork<Self> for MyStruct { self.work }
+//! }
+//!
+//! impl MyStruct {
+//! fn new(value: i32) -> Result<Arc<Self>> {
+//! Arc::pin_init(pin_init!(MyStruct {
+//! value,
+//! work <- Work::new(),
+//! }))
+//! }
+//! }
+//!
+//! impl WorkItem for MyStruct {
+//! type Pointer = Arc<MyStruct>;
+//!
+//! fn run(this: Arc<MyStruct>) {
+//! pr_info!("The value is: {}", this.value);
+//! }
+//! }
+//!
+//! /// This method will enqueue the struct for execution on the system workqueue, where its value
+//! /// will be printed.
+//! fn print_later(val: Arc<MyStruct>) {
+//! let _ = workqueue::system().enqueue(val);
+//! }
+//! ```
+//!
+//! The following example shows how multiple `work_struct` fields can be used:
+//!
+//! ```
+//! use kernel::prelude::*;
+//! use kernel::sync::Arc;
+//! use kernel::workqueue::{self, Work, WorkItem};
+//!
+//! #[pin_data]
+//! struct MyStruct {
+//! value_1: i32,
+//! value_2: i32,
+//! #[pin]
+//! work_1: Work<MyStruct, 1>,
+//! #[pin]
+//! work_2: Work<MyStruct, 2>,
+//! }
+//!
+//! impl_has_work! {
+//! impl HasWork<Self, 1> for MyStruct { self.work_1 }
+//! impl HasWork<Self, 2> for MyStruct { self.work_2 }
+//! }
+//!
+//! impl MyStruct {
+//! fn new(value_1: i32, value_2: i32) -> Result<Arc<Self>> {
+//! Arc::pin_init(pin_init!(MyStruct {
+//! value_1,
+//! value_2,
+//! work_1 <- Work::new(),
+//! work_2 <- Work::new(),
+//! }))
+//! }
+//! }
+//!
+//! impl WorkItem<1> for MyStruct {
+//! type Pointer = Arc<MyStruct>;
+//!
+//! fn run(this: Arc<MyStruct>) {
+//! pr_info!("The value is: {}", this.value_1);
+//! }
+//! }
+//!
+//! impl WorkItem<2> for MyStruct {
+//! type Pointer = Arc<MyStruct>;
+//!
+//! fn run(this: Arc<MyStruct>) {
+//! pr_info!("The second value is: {}", this.value_2);
+//! }
+//! }
+//!
+//! fn print_1_later(val: Arc<MyStruct>) {
+//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val);
+//! }
+//!
+//! fn print_2_later(val: Arc<MyStruct>) {
+//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
+//! }
+//! ```
+//!
//! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)

use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
--
2.41.0.rc0.172.g3f132b7071-goog


2023-06-01 14:15:25

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

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<T, ID>`. If a type
implements this trait, then the type declares that, at the given offset,
there is a field of type `Work<T, ID>`. 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<T>` on a type.
The macro expands to something that only compiles if the specified field
really has the type `Work<T>`. It is used like this:

```
struct MyWorkItem {
work_field: Work<MyWorkItem, 1>,
}

impl_has_work! {
impl HasWork<MyWorkItem, 1> 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 <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Signed-off-by: Alice Ryhl <[email protected]>
---
rust/helpers.c | 8 ++
rust/kernel/workqueue.rs | 219 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 226 insertions(+), 1 deletion(-)

diff --git a/rust/helpers.c b/rust/helpers.c
index 81e80261d597..7f0c2fe2fbeb 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/sched/signal.h>
#include <linux/wait.h>
+#include <linux/workqueue.h>

__noreturn void rust_helper_BUG(void)
{
@@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
}
EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);

+void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
+ bool on_stack)
+{
+ __INIT_WORK(work, func, on_stack);
+}
+EXPORT_SYMBOL_GPL(rust_helper___INIT_WORK);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index e37820f253f6..dbf0aab29a85 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -2,9 +2,34 @@

//! 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 struct.) Since these IDs are
+//! generic, they are used only at compile-time, so they shouldn't exist in the final binary.
+//!
+//! # The raw API
+//!
+//! The raw API consists of the `RawWorkItem` trait, where the work item needs 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. Furthermore, it also includes
+//! a trait called `WorkItemPointer`, which is usually not used directly by 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 enqueued to a workqueue.
+//! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
+//! that implements `WorkItem`.
+//!
//! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)

-use crate::{bindings, types::Opaque};
+use crate::{bindings, prelude::*, types::Opaque};
+use core::marker::{PhantomData, PhantomPinned};

/// A kernel work queue.
///
@@ -106,6 +131,198 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
F: FnOnce(*mut bindings::work_struct) -> bool;
}

+/// Defines the method that should be called directly when a work item is executed.
+///
+/// Typically you would implement [`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 the [`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` initialized with the [`run`]
+/// method of this trait as the function pointer.
+///
+/// [`__enqueue`]: RawWorkItem::__enqueue
+/// [`run`]: WorkItemPointer::run
+pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
+ /// 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 still be valid.
+ unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
+}
+
+/// Defines the method that should be called when this work item is executed.
+///
+/// This trait is used when the `work_struct` field is defined using the [`Work`] helper.
+pub trait WorkItem<const ID: u64 = 0> {
+ /// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or
+ /// `Pin<Box<Self>>`.
+ type Pointer: WorkItemPointer<ID>;
+
+ /// 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 work 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 [`WorkItem`] that uses it.
+#[repr(transparent)]
+pub struct Work<T: ?Sized, const ID: u64 = 0> {
+ work: Opaque<bindings::work_struct>,
+ _pin: PhantomPinned,
+ _inner: PhantomData<T>,
+}
+
+// SAFETY: Kernel work items are usable from any thread.
+//
+// We do not need to constrain `T` since the work item does not actually contain a `T`.
+unsafe impl<T: ?Sized, const ID: u64> Send for Work<T, ID> {}
+// SAFETY: Kernel work items are usable from any thread.
+//
+// We do not need to constrain `T` since the work item does not actually contain a `T`.
+unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}
+
+impl<T: ?Sized, const ID: u64> Work<T, ID> {
+ /// Creates a new instance of [`Work`].
+ #[inline]
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new() -> impl PinInit<Self>
+ where
+ T: WorkItem<ID>,
+ {
+ // SAFETY: The `WorkItemPointer` implementation promises that `run` can be used as the work
+ // item function.
+ unsafe {
+ kernel::init::pin_init_from_closure(move |slot| {
+ bindings::__INIT_WORK(Self::raw_get(slot), Some(T::Pointer::run), false);
+ Ok(())
+ })
+ }
+ }
+
+ /// Get a pointer to the inner `work_struct`.
+ ///
+ /// # Safety
+ ///
+ /// The provided pointer must not be dangling and must be properly aligned. (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<T, ID>`] field.
+///
+/// # Safety
+///
+/// The [`OFFSET`] constant must be the offset of a field in Self of type [`Work<T, ID>`]. The methods on
+/// this trait must have exactly the behavior that the definitions given below have.
+///
+/// [`Work<T, ID>`]: Work
+/// [`OFFSET`]: HasWork::OFFSET
+pub unsafe trait HasWork<T, const ID: u64 = 0> {
+ /// The offset of the [`Work<T, ID>`] field.
+ ///
+ /// [`Work<T, ID>`]: Work
+ const OFFSET: usize;
+
+ /// Returns the offset of the [`Work<T, ID>`] field.
+ ///
+ /// This method exists because the [`OFFSET`] constant cannot be accessed if the type is not Sized.
+ ///
+ /// [`Work<T, ID>`]: Work
+ /// [`OFFSET`]: HasWork::OFFSET
+ #[inline]
+ fn get_work_offset(&self) -> usize {
+ Self::OFFSET
+ }
+
+ /// Returns a pointer to the [`Work<T, ID>`] field.
+ ///
+ /// # Safety
+ ///
+ /// The provided pointer must point at a valid struct of type `Self`.
+ ///
+ /// [`Work<T, ID>`]: Work
+ #[inline]
+ unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<T, ID> {
+ // SAFETY: The caller promises that the pointer is valid.
+ unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut Work<T, ID> }
+ }
+
+ /// Returns a pointer to the struct containing the [`Work<T, ID>`] field.
+ ///
+ /// # Safety
+ ///
+ /// The pointer must point at a [`Work<T, ID>`] field in a struct of type `Self`.
+ ///
+ /// [`Work<T, ID>`]: Work
+ #[inline]
+ unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *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<T, ID>`] trait.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::sync::Arc;
+///
+/// struct MyStruct {
+/// work_field: Work<MyStruct, 17>,
+/// }
+///
+/// impl_has_work! {
+/// impl HasWork<MyStruct, 17> for MyStruct { self.work_field }
+/// }
+/// ```
+///
+/// [`HasWork<T, ID>`]: 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 }
+ )*) => {$(
+ // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right
+ // type.
+ unsafe impl$(<$($implarg),*>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self $(<$($selfarg),*>)? {
+ const OFFSET: usize = $crate::offset_of!(Self, $field) as usize;
+
+ #[inline]
+ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
+ // SAFETY: The caller promises that the pointer is not dangling.
+ 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 multi-threaded. There are
--
2.41.0.rc0.172.g3f132b7071-goog


2023-06-01 14:15:39

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 6/8] rust: workqueue: implement `WorkItemPointer` for pointer types

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<Box<T>>` type is for objects
where the caller has exclusive ownership of the object.

Co-developed-by: Gary Guo <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Signed-off-by: Alice Ryhl <[email protected]>
---
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 dbf0aab29a85..f06a2f036d8b 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -28,8 +28,10 @@
//!
//! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)

-use crate::{bindings, prelude::*, types::Opaque};
+use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
+use alloc::boxed::Box;
use core::marker::{PhantomData, PhantomPinned};
+use core::pin::Pin;

/// A kernel work queue.
///
@@ -323,6 +325,99 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_typ
)*};
}

+unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
+where
+ T: WorkItem<ID, Pointer = Self>,
+ T: HasWork<T, ID>,
+{
+ unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
+ // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
+ let ptr = ptr as *mut Work<T, ID>;
+ // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
+ let ptr = unsafe { T::work_container_of(ptr) };
+ // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
+ let arc = unsafe { Arc::from_raw(ptr) };
+
+ T::run(arc)
+ }
+}
+
+unsafe impl<T, const ID: u64> RawWorkItem<ID> for Arc<T>
+where
+ T: WorkItem<ID, Pointer = Self>,
+ T: HasWork<T, ID>,
+{
+ type EnqueueOutput = Result<(), Self>;
+
+ unsafe fn __enqueue<F>(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 pointer is a raw pointer.
+ let ptr = Arc::into_raw(self) as *mut T;
+
+ // SAFETY: Pointers into an `Arc` point at a valid value.
+ let work_ptr = unsafe { T::raw_get_work(ptr) };
+ // SAFETY: `raw_get_work` returns a pointer to a valid value.
+ let work_ptr = unsafe { Work::raw_get(work_ptr) };
+
+ if queue_work_on(work_ptr) {
+ Ok(())
+ } else {
+ // SAFETY: The work queue has not taken ownership of the pointer.
+ Err(unsafe { Arc::from_raw(ptr) })
+ }
+ }
+}
+
+unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<Box<T>>
+where
+ T: WorkItem<ID, Pointer = Self>,
+ T: HasWork<T, ID>,
+{
+ unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
+ // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
+ let ptr = ptr as *mut Work<T, ID>;
+ // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
+ let ptr = unsafe { T::work_container_of(ptr) };
+ // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
+ let boxed = unsafe { Box::from_raw(ptr) };
+ // SAFETY: The box was already pinned when it was enqueued.
+ let pinned = unsafe { Pin::new_unchecked(boxed) };
+
+ T::run(pinned)
+ }
+}
+
+unsafe impl<T, const ID: u64> RawWorkItem<ID> for Pin<Box<T>>
+where
+ T: WorkItem<ID, Pointer = Self>,
+ T: HasWork<T, ID>,
+{
+ type EnqueueOutput = ();
+
+ unsafe fn __enqueue<F>(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 = unsafe { Pin::into_inner_unchecked(self) };
+ let ptr = Box::into_raw(boxed);
+
+ // SAFETY: Pointers into a `Box` point at a valid value.
+ let work_ptr = unsafe { T::raw_get_work(ptr) };
+ // SAFETY: `raw_get_work` returns a pointer to a valid value.
+ let work_ptr = 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 multi-threaded. There are
--
2.41.0.rc0.172.g3f132b7071-goog


2023-06-01 14:17:35

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 4/8] rust: workqueue: define built-in queues

From: Wedson Almeida Filho <[email protected]>

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 <[email protected]>
Co-developed-by: Alice Ryhl <[email protected]>
Signed-off-by: Alice Ryhl <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
---
rust/kernel/workqueue.rs | 65 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)

diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index 9c630840039b..e37820f253f6 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -105,3 +105,68 @@ unsafe fn __enqueue<F>(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 multi-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 which 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 running 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, and 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 freezable.
+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" variants 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_freezable_power_efficient_wq`).
+///
+/// It is similar to the one returned by [`system_power_efficient`] except that is freezable.
+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) }
+}
--
2.41.0.rc0.172.g3f132b7071-goog


Subject: Re: [PATCH v2 7/8] rust: workqueue: add `try_spawn` helper method

On 6/1/23 10:49, Alice Ryhl wrote:
> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> ---
> [...]

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

On 6/1/23 10:49, Alice Ryhl wrote:
> 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<T, ID>`. If a type
> implements this trait, then the type declares that, at the given offset,
> there is a field of type `Work<T, ID>`. 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<T>` on a type.
> The macro expands to something that only compiles if the specified field
> really has the type `Work<T>`. It is used like this:
>
> ```
> struct MyWorkItem {
> work_field: Work<MyWorkItem, 1>,
> }
>
> impl_has_work! {
> impl HasWork<MyWorkItem, 1> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> ---
> [...]

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

Subject: Re: [PATCH v2 6/8] rust: workqueue: implement `WorkItemPointer` for pointer types

On 6/1/23 10:49, Alice Ryhl wrote:
> 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<Box<T>>` type is for objects
> where the caller has exclusive ownership of the object.
>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> ---
> [...]

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples

On 6/1/23 10:49, Alice Ryhl wrote:
> 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 <[email protected]>
> ---
> [...]

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

2023-06-01 17:51:41

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] rust: workqueue: define built-in queues

On Thu, 1 Jun 2023 13:49:42 +0000
Alice Ryhl <[email protected]> wrote:

> From: Wedson Almeida Filho <[email protected]>
>
> 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 <[email protected]>
> Co-developed-by: Alice Ryhl <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

This looks fine to me, so:

Reviewed-by: Gary Guo <[email protected]>

Just one question about style: would people prefer:

kernel::workqueue::system().enqueue(...)

or

use kernel::workqueue::Queue;
Queue::system().enqueue(...)

?

> ---
> rust/kernel/workqueue.rs | 65 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index 9c630840039b..e37820f253f6 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -105,3 +105,68 @@ unsafe fn __enqueue<F>(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 multi-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 which 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 running 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, and 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 freezable.
> +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" variants 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_freezable_power_efficient_wq`).
> +///
> +/// It is similar to the one returned by [`system_power_efficient`] except that is freezable.
> +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) }
> +}


2023-06-01 18:04:42

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples

On Thu, 1 Jun 2023 13:49:46 +0000
Alice Ryhl <[email protected]> wrote:

> 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 <[email protected]>
> ---
> rust/kernel/workqueue.rs | 104 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 104 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index c302e8b8624b..cefcf43ff40e 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -26,6 +26,110 @@
> //! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> //! that implements `WorkItem`.
> //!
> +//! ## Example
> +//!
> +//! This example defines a struct that holds an integer and can be scheduled 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};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value: i32,
> +//! #[pin]
> +//! work: Work<MyStruct>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self> for MyStruct { self.work }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value,
> +//! work <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value);
> +//! }
> +//! }
> +//!
> +//! /// This method will enqueue the struct for execution on the system workqueue, where its value
> +//! /// will be printed.
> +//! fn print_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue(val);
> +//! }
> +//! ```
> +//!
> +//! The following example shows how multiple `work_struct` fields can be used:
> +//!
> +//! ```
> +//! use kernel::prelude::*;
> +//! use kernel::sync::Arc;
> +//! use kernel::workqueue::{self, Work, WorkItem};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value_1: i32,
> +//! value_2: i32,
> +//! #[pin]
> +//! work_1: Work<MyStruct, 1>,
> +//! #[pin]
> +//! work_2: Work<MyStruct, 2>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self, 1> for MyStruct { self.work_1 }
> +//! impl HasWork<Self, 2> for MyStruct { self.work_2 }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value_1: i32, value_2: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value_1,
> +//! value_2,
> +//! work_1 <- Work::new(),
> +//! work_2 <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<1> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value_1);
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<2> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The second value is: {}", this.value_2);
> +//! }
> +//! }
> +//!
> +//! fn print_1_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val);

Nothing bad about explicit, but I just want to confirm that you could
write

let _ = workqueue::system().enqueue::<_, 1>(val);

here, right?

Reviewed-by: Gary Guo <[email protected]>

> +//! }
> +//!
> +//! fn print_2_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
> +//! }
> +//! ```
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> use crate::{bindings, prelude::*, sync::Arc, types::Opaque};


Subject: Re: [PATCH v2 4/8] rust: workqueue: define built-in queues

On 6/1/23 14:30, Gary Guo wrote:
> On Thu, 1 Jun 2023 13:49:42 +0000
> Alice Ryhl <[email protected]> wrote:
>
>> From: Wedson Almeida Filho <[email protected]>
>>
>> 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 <[email protected]>
>> Co-developed-by: Alice Ryhl <[email protected]>
>> Signed-off-by: Alice Ryhl <[email protected]>
>> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
>
> This looks fine to me, so:
>
> Reviewed-by: Gary Guo <[email protected]>
>
> Just one question about style: would people prefer:
>
> kernel::workqueue::system().enqueue(...)
>
> or
>
> use kernel::workqueue::Queue;
> Queue::system().enqueue(...)
>
> ?

I can compare the first with `std::thread::spawn` and the second
with enqueuing an executor with a future. Both makes sense to me so
I can't decide.

> [...]

2023-06-01 19:20:45

by Boqun Feng

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

On Thu, Jun 01, 2023 at 01:49:43PM +0000, Alice Ryhl wrote:
> 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<T, ID>`. If a type
> implements this trait, then the type declares that, at the given offset,
> there is a field of type `Work<T, ID>`. 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<T>` on a type.
> The macro expands to something that only compiles if the specified field
> really has the type `Work<T>`. It is used like this:
>
> ```
> struct MyWorkItem {
> work_field: Work<MyWorkItem, 1>,
> }
>
> impl_has_work! {
> impl HasWork<MyWorkItem, 1> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> ---
> rust/helpers.c | 8 ++
> rust/kernel/workqueue.rs | 219 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 226 insertions(+), 1 deletion(-)
>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 81e80261d597..7f0c2fe2fbeb 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -26,6 +26,7 @@
> #include <linux/spinlock.h>
> #include <linux/sched/signal.h>
> #include <linux/wait.h>
> +#include <linux/workqueue.h>
>
> __noreturn void rust_helper_BUG(void)
> {
> @@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
> }
> EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
>
> +void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
> + bool on_stack)
> +{
> + __INIT_WORK(work, func, on_stack);

Note here all the work items in Rust will share the same lockdep class.
That could be problematic: the lockdep classes for work are for
detecting deadlocks in the following scenario:

step 1: queue a work "foo", whose work function is:

mutex_lock(&bar);
do_something(...);
mutex_unlock(&bar);

step 2: in another thread do:

mutex_lock(&bar);
flush_work(foo); // wait until work "foo" is finished.

if this case, if step 2 get the lock "bar" first, it's a deadlock.

With the current implementation, all the work items share the same
lockdep class, so the following will be treated as deadlock:

<in work "work1">
mutex_lock(&bar);
do_something(...);
mutex_unlock(&bar);

<in another thread>
mutex_lock(&bar);
flush_work(work2); // flush work2 intead of work1.

which is a false positive. We at least need some changes in C side to
make it work:

https://lore.kernel.org/rust-for-linux/[email protected]/

however, that still has the disadvantage that all Rust work items have
the same name for the lockdep classes.. maybe we should extend that for
an extra "name" parameter. And then it's not necessary to be a macro.

Regards,
Boqun

> +}
> +EXPORT_SYMBOL_GPL(rust_helper___INIT_WORK);
> +
> /*
> * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
> * as the Rust `usize` type, so we can use it in contexts where Rust
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index e37820f253f6..dbf0aab29a85 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -2,9 +2,34 @@
>
> //! 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 struct.) Since these IDs are
> +//! generic, they are used only at compile-time, so they shouldn't exist in the final binary.
> +//!
> +//! # The raw API
> +//!
> +//! The raw API consists of the `RawWorkItem` trait, where the work item needs 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. Furthermore, it also includes
> +//! a trait called `WorkItemPointer`, which is usually not used directly by 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 enqueued to a workqueue.
> +//! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> +//! that implements `WorkItem`.
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> -use crate::{bindings, types::Opaque};
> +use crate::{bindings, prelude::*, types::Opaque};
> +use core::marker::{PhantomData, PhantomPinned};
>
> /// A kernel work queue.
> ///
> @@ -106,6 +131,198 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
> F: FnOnce(*mut bindings::work_struct) -> bool;
> }
>
> +/// Defines the method that should be called directly when a work item is executed.
> +///
> +/// Typically you would implement [`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 the [`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` initialized with the [`run`]
> +/// method of this trait as the function pointer.
> +///
> +/// [`__enqueue`]: RawWorkItem::__enqueue
> +/// [`run`]: WorkItemPointer::run
> +pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
> + /// 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 still be valid.
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
> +}
> +
> +/// Defines the method that should be called when this work item is executed.
> +///
> +/// This trait is used when the `work_struct` field is defined using the [`Work`] helper.
> +pub trait WorkItem<const ID: u64 = 0> {
> + /// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or
> + /// `Pin<Box<Self>>`.
> + type Pointer: WorkItemPointer<ID>;
> +
> + /// 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 work 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 [`WorkItem`] that uses it.
> +#[repr(transparent)]
> +pub struct Work<T: ?Sized, const ID: u64 = 0> {
> + work: Opaque<bindings::work_struct>,
> + _pin: PhantomPinned,
> + _inner: PhantomData<T>,
> +}
> +
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Send for Work<T, ID> {}
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}
> +
> +impl<T: ?Sized, const ID: u64> Work<T, ID> {
> + /// Creates a new instance of [`Work`].
> + #[inline]
> + #[allow(clippy::new_ret_no_self)]
> + pub fn new() -> impl PinInit<Self>
> + where
> + T: WorkItem<ID>,
> + {
> + // SAFETY: The `WorkItemPointer` implementation promises that `run` can be used as the work
> + // item function.
> + unsafe {
> + kernel::init::pin_init_from_closure(move |slot| {
> + bindings::__INIT_WORK(Self::raw_get(slot), Some(T::Pointer::run), false);
> + Ok(())
> + })
> + }
> + }
> +
> + /// Get a pointer to the inner `work_struct`.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must not be dangling and must be properly aligned. (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<T, ID>`] field.
> +///
> +/// # Safety
> +///
> +/// The [`OFFSET`] constant must be the offset of a field in Self of type [`Work<T, ID>`]. The methods on
> +/// this trait must have exactly the behavior that the definitions given below have.
> +///
> +/// [`Work<T, ID>`]: Work
> +/// [`OFFSET`]: HasWork::OFFSET
> +pub unsafe trait HasWork<T, const ID: u64 = 0> {
> + /// The offset of the [`Work<T, ID>`] field.
> + ///
> + /// [`Work<T, ID>`]: Work
> + const OFFSET: usize;
> +
> + /// Returns the offset of the [`Work<T, ID>`] field.
> + ///
> + /// This method exists because the [`OFFSET`] constant cannot be accessed if the type is not Sized.
> + ///
> + /// [`Work<T, ID>`]: Work
> + /// [`OFFSET`]: HasWork::OFFSET
> + #[inline]
> + fn get_work_offset(&self) -> usize {
> + Self::OFFSET
> + }
> +
> + /// Returns a pointer to the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must point at a valid struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<T, ID> {
> + // SAFETY: The caller promises that the pointer is valid.
> + unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut Work<T, ID> }
> + }
> +
> + /// Returns a pointer to the struct containing the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The pointer must point at a [`Work<T, ID>`] field in a struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *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<T, ID>`] trait.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::sync::Arc;
> +///
> +/// struct MyStruct {
> +/// work_field: Work<MyStruct, 17>,
> +/// }
> +///
> +/// impl_has_work! {
> +/// impl HasWork<MyStruct, 17> for MyStruct { self.work_field }
> +/// }
> +/// ```
> +///
> +/// [`HasWork<T, ID>`]: 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 }
> + )*) => {$(
> + // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right
> + // type.
> + unsafe impl$(<$($implarg),*>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self $(<$($selfarg),*>)? {
> + const OFFSET: usize = $crate::offset_of!(Self, $field) as usize;
> +
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
> + // SAFETY: The caller promises that the pointer is not dangling.
> + 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 multi-threaded. There are
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>

2023-06-01 21:13:39

by Boqun Feng

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

On Thu, Jun 01, 2023 at 01:49:43PM +0000, Alice Ryhl wrote:
> 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<T, ID>`. If a type
> implements this trait, then the type declares that, at the given offset,
> there is a field of type `Work<T, ID>`. 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<T>` on a type.
> The macro expands to something that only compiles if the specified field
> really has the type `Work<T>`. It is used like this:
>
> ```
> struct MyWorkItem {
> work_field: Work<MyWorkItem, 1>,
> }
>
> impl_has_work! {
> impl HasWork<MyWorkItem, 1> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> ---
> rust/helpers.c | 8 ++
> rust/kernel/workqueue.rs | 219 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 226 insertions(+), 1 deletion(-)
>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 81e80261d597..7f0c2fe2fbeb 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -26,6 +26,7 @@
> #include <linux/spinlock.h>
> #include <linux/sched/signal.h>
> #include <linux/wait.h>
> +#include <linux/workqueue.h>
>
> __noreturn void rust_helper_BUG(void)
> {
> @@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
> }
> EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
>
> +void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
> + bool on_stack)
> +{
> + __INIT_WORK(work, func, on_stack);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper___INIT_WORK);
> +
> /*
> * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
> * as the Rust `usize` type, so we can use it in contexts where Rust
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index e37820f253f6..dbf0aab29a85 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -2,9 +2,34 @@
>
> //! 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 struct.) Since these IDs are
> +//! generic, they are used only at compile-time, so they shouldn't exist in the final binary.
> +//!
> +//! # The raw API
> +//!
> +//! The raw API consists of the `RawWorkItem` trait, where the work item needs 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. Furthermore, it also includes
> +//! a trait called `WorkItemPointer`, which is usually not used directly by 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 enqueued to a workqueue.
> +//! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> +//! that implements `WorkItem`.
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> -use crate::{bindings, types::Opaque};
> +use crate::{bindings, prelude::*, types::Opaque};
> +use core::marker::{PhantomData, PhantomPinned};
>
> /// A kernel work queue.
> ///
> @@ -106,6 +131,198 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
> F: FnOnce(*mut bindings::work_struct) -> bool;
> }
>
> +/// Defines the method that should be called directly when a work item is executed.
> +///
> +/// Typically you would implement [`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 the [`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` initialized with the [`run`]
> +/// method of this trait as the function pointer.
> +///
> +/// [`__enqueue`]: RawWorkItem::__enqueue
> +/// [`run`]: WorkItemPointer::run
> +pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
> + /// 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 still be valid.
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
> +}
> +
> +/// Defines the method that should be called when this work item is executed.
> +///
> +/// This trait is used when the `work_struct` field is defined using the [`Work`] helper.
> +pub trait WorkItem<const ID: u64 = 0> {
> + /// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or
> + /// `Pin<Box<Self>>`.
> + type Pointer: WorkItemPointer<ID>;

This being an associate type makes me wonder how do we want to support
the following (totally made-up by me, but I think it makes sense)?:

Say we have a struct

pub struct Foo {
work: Work<Foo>,
data: Data,
}

impl Foo {
pub fn do_sth(&self) {
...
}
}

and we want to queue both Pin<Box<Foo>> and Arc<Foo> as work items, but
the following doesn't work:

// Pin<Box<Foo>> can be queued.
impl WorkItem for Foo {
type Pointer = Pin<Box<Foo>>;
fn run(ptr: Self::Pointer) {
ptr.do_sth();
}
}

// Arc<Foo> can also be queued.
impl WorkItem for Foo {
type Pointer = Arc<Foo>;
fn run(ptr: Self::Pointer) {
ptr.do_sth();
}
}



Of course, we can use new type idiom, but that's not really great, and
we may have more smart pointer types in the future.

Am I missing something here?

Regards,
Boqun

> +
> + /// 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 work 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 [`WorkItem`] that uses it.
> +#[repr(transparent)]
> +pub struct Work<T: ?Sized, const ID: u64 = 0> {
> + work: Opaque<bindings::work_struct>,
> + _pin: PhantomPinned,
> + _inner: PhantomData<T>,
> +}
> +
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Send for Work<T, ID> {}
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}
> +
> +impl<T: ?Sized, const ID: u64> Work<T, ID> {
> + /// Creates a new instance of [`Work`].
> + #[inline]
> + #[allow(clippy::new_ret_no_self)]
> + pub fn new() -> impl PinInit<Self>
> + where
> + T: WorkItem<ID>,
> + {
> + // SAFETY: The `WorkItemPointer` implementation promises that `run` can be used as the work
> + // item function.
> + unsafe {
> + kernel::init::pin_init_from_closure(move |slot| {
> + bindings::__INIT_WORK(Self::raw_get(slot), Some(T::Pointer::run), false);
> + Ok(())
> + })
> + }
> + }
> +
> + /// Get a pointer to the inner `work_struct`.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must not be dangling and must be properly aligned. (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<T, ID>`] field.
> +///
> +/// # Safety
> +///
> +/// The [`OFFSET`] constant must be the offset of a field in Self of type [`Work<T, ID>`]. The methods on
> +/// this trait must have exactly the behavior that the definitions given below have.
> +///
> +/// [`Work<T, ID>`]: Work
> +/// [`OFFSET`]: HasWork::OFFSET
> +pub unsafe trait HasWork<T, const ID: u64 = 0> {
> + /// The offset of the [`Work<T, ID>`] field.
> + ///
> + /// [`Work<T, ID>`]: Work
> + const OFFSET: usize;
> +
> + /// Returns the offset of the [`Work<T, ID>`] field.
> + ///
> + /// This method exists because the [`OFFSET`] constant cannot be accessed if the type is not Sized.
> + ///
> + /// [`Work<T, ID>`]: Work
> + /// [`OFFSET`]: HasWork::OFFSET
> + #[inline]
> + fn get_work_offset(&self) -> usize {
> + Self::OFFSET
> + }
> +
> + /// Returns a pointer to the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must point at a valid struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<T, ID> {
> + // SAFETY: The caller promises that the pointer is valid.
> + unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut Work<T, ID> }
> + }
> +
> + /// Returns a pointer to the struct containing the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The pointer must point at a [`Work<T, ID>`] field in a struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *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<T, ID>`] trait.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::sync::Arc;
> +///
> +/// struct MyStruct {
> +/// work_field: Work<MyStruct, 17>,
> +/// }
> +///
> +/// impl_has_work! {
> +/// impl HasWork<MyStruct, 17> for MyStruct { self.work_field }
> +/// }
> +/// ```
> +///
> +/// [`HasWork<T, ID>`]: 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 }
> + )*) => {$(
> + // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right
> + // type.
> + unsafe impl$(<$($implarg),*>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self $(<$($selfarg),*>)? {
> + const OFFSET: usize = $crate::offset_of!(Self, $field) as usize;
> +
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
> + // SAFETY: The caller promises that the pointer is not dangling.
> + 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 multi-threaded. There are
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>

2023-06-02 08:56:12

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

Boqun Feng <[email protected]> writes:
> On Thu, Jun 01, 2023 at 01:49:43PM +0000, Alice Ryhl wrote:
>> diff --git a/rust/helpers.c b/rust/helpers.c
>> index 81e80261d597..7f0c2fe2fbeb 100644
>> --- a/rust/helpers.c
>> +++ b/rust/helpers.c
>> @@ -26,6 +26,7 @@
>> #include <linux/spinlock.h>
>> #include <linux/sched/signal.h>
>> #include <linux/wait.h>
>> +#include <linux/workqueue.h>
>>
>> __noreturn void rust_helper_BUG(void)
>> {
>> @@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
>> }
>> EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
>>
>> +void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
>> + bool on_stack)
>> +{
>> + __INIT_WORK(work, func, on_stack);
>
> Note here all the work items in Rust will share the same lockdep class.
> That could be problematic: the lockdep classes for work are for
> detecting deadlocks in the following scenario:
>
> step 1: queue a work "foo", whose work function is:
>
> mutex_lock(&bar);
> do_something(...);
> mutex_unlock(&bar);
>
> step 2: in another thread do:
>
> mutex_lock(&bar);
> flush_work(foo); // wait until work "foo" is finished.
>
> if this case, if step 2 get the lock "bar" first, it's a deadlock.
>
> With the current implementation, all the work items share the same
> lockdep class, so the following will be treated as deadlock:
>
> <in work "work1">
> mutex_lock(&bar);
> do_something(...);
> mutex_unlock(&bar);
>
> <in another thread>
> mutex_lock(&bar);
> flush_work(work2); // flush work2 intead of work1.
>
> which is a false positive. We at least need some changes in C side to
> make it work:
>
> https://lore.kernel.org/rust-for-linux/[email protected]/
>
> however, that still has the disadvantage that all Rust work items have
> the same name for the lockdep classes.. maybe we should extend that for
> an extra "name" parameter. And then it's not necessary to be a macro.

Yeah, I did know about this issue, but I didn't know what the best way
to fix it is. What solution would you like me to use?

Alice

2023-06-02 08:56:53

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] rust: workqueue: define built-in queues

Gary Guo <[email protected]> writes:
> On Thu, 1 Jun 2023 13:49:42 +0000
> Alice Ryhl <[email protected]> wrote:
>> From: Wedson Almeida Filho <[email protected]>
>>
>> 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 <[email protected]>
>> Co-developed-by: Alice Ryhl <[email protected]>
>> Signed-off-by: Alice Ryhl <[email protected]>
>> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
>
> This looks fine to me, so:
>
> Reviewed-by: Gary Guo <[email protected]>
>
> Just one question about style: would people prefer:
>
> kernel::workqueue::system().enqueue(...)
>
> or
>
> use kernel::workqueue::Queue;
> Queue::system().enqueue(...)
>
> ?

I prefer the first version.

Alice

2023-06-02 10:07:23

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

Boqun Feng <[email protected]> writes:
> On Thu, Jun 01, 2023 at 01:49:43PM +0000, Alice Ryhl wrote:
>> +/// Defines the method that should be called directly when a work item is executed.
>> +///
>> +/// Typically you would implement [`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 the [`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` initialized with the [`run`]
>> +/// method of this trait as the function pointer.
>> +///
>> +/// [`__enqueue`]: RawWorkItem::__enqueue
>> +/// [`run`]: WorkItemPointer::run
>> +pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
>> + /// 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 still be valid.
>> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
>> +}
>> +
>> +/// Defines the method that should be called when this work item is executed.
>> +///
>> +/// This trait is used when the `work_struct` field is defined using the [`Work`] helper.
>> +pub trait WorkItem<const ID: u64 = 0> {
>> + /// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or
>> + /// `Pin<Box<Self>>`.
>> + type Pointer: WorkItemPointer<ID>;
>
> This being an associate type makes me wonder how do we want to support
> the following (totally made-up by me, but I think it makes sense)?:
>
> Say we have a struct
>
> pub struct Foo {
> work: Work<Foo>,
> data: Data,
> }
>
> impl Foo {
> pub fn do_sth(&self) {
> ...
> }
> }
>
> and we want to queue both Pin<Box<Foo>> and Arc<Foo> as work items, but
> the following doesn't work:
>
> // Pin<Box<Foo>> can be queued.
> impl WorkItem for Foo {
> type Pointer = Pin<Box<Foo>>;
> fn run(ptr: Self::Pointer) {
> ptr.do_sth();
> }
> }
>
> // Arc<Foo> can also be queued.
> impl WorkItem for Foo {
> type Pointer = Arc<Foo>;
> fn run(ptr: Self::Pointer) {
> ptr.do_sth();
> }
> }
>
> Of course, we can use new type idiom, but that's not really great, and
> we may have more smart pointer types in the future.
>
> Am I missing something here?

Basically, you're asking "is it possible to use the same `work_struct`
field for several different pointer types"?

When creating the function pointer to store in the `work_struct`, the
function pointer _must_ know what the pointer type is. Otherwise it
cannot call the right `WorkItem::run` method or perform the correct
`container_of` operation. (E.g. an `Arc` embeds a `refcount_t` before
the struct, but a `Box` does not.)

With an associated type there is no problem with that. Associated types
force you to make a choice, which means that the `work_struct` knows
what the pointer type is when you create it. Supporting what you suggest
means that we must be able to change the function pointer stored in the
`work_struct` after initializing it.

This is rather tricky because you can call `enqueue` from several
threads in parallel; just setting the function pointer before you call
`queue_work_on` would be a data race. I suppose you could do it by
implementing our own `queue_work_on` that sets the function pointer
_after_ successfully setting the `WORK_STRUCT_PENDING_BIT`, but I don't
think this patchset should do that.

Alice

2023-06-02 10:08:26

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples

Gary Guo <[email protected]> writes:
> On Thu, 1 Jun 2023 13:49:46 +0000
> Alice Ryhl <[email protected]> wrote:
>> 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 <[email protected]>
>> ---
>> rust/kernel/workqueue.rs | 104 +++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 104 insertions(+)
>>
>> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
>> index c302e8b8624b..cefcf43ff40e 100644
>> --- a/rust/kernel/workqueue.rs
>> +++ b/rust/kernel/workqueue.rs
>> @@ -26,6 +26,110 @@
>> //! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
>> //! that implements `WorkItem`.
>> //!
>> +//! ## Example
>> +//!
>> +//! This example defines a struct that holds an integer and can be scheduled 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};
>> +//!
>> +//! #[pin_data]
>> +//! struct MyStruct {
>> +//! value: i32,
>> +//! #[pin]
>> +//! work: Work<MyStruct>,
>> +//! }
>> +//!
>> +//! impl_has_work! {
>> +//! impl HasWork<Self> for MyStruct { self.work }
>> +//! }
>> +//!
>> +//! impl MyStruct {
>> +//! fn new(value: i32) -> Result<Arc<Self>> {
>> +//! Arc::pin_init(pin_init!(MyStruct {
>> +//! value,
>> +//! work <- Work::new(),
>> +//! }))
>> +//! }
>> +//! }
>> +//!
>> +//! impl WorkItem for MyStruct {
>> +//! type Pointer = Arc<MyStruct>;
>> +//!
>> +//! fn run(this: Arc<MyStruct>) {
>> +//! pr_info!("The value is: {}", this.value);
>> +//! }
>> +//! }
>> +//!
>> +//! /// This method will enqueue the struct for execution on the system workqueue, where its value
>> +//! /// will be printed.
>> +//! fn print_later(val: Arc<MyStruct>) {
>> +//! let _ = workqueue::system().enqueue(val);
>> +//! }
>> +//! ```
>> +//!
>> +//! The following example shows how multiple `work_struct` fields can be used:
>> +//!
>> +//! ```
>> +//! use kernel::prelude::*;
>> +//! use kernel::sync::Arc;
>> +//! use kernel::workqueue::{self, Work, WorkItem};
>> +//!
>> +//! #[pin_data]
>> +//! struct MyStruct {
>> +//! value_1: i32,
>> +//! value_2: i32,
>> +//! #[pin]
>> +//! work_1: Work<MyStruct, 1>,
>> +//! #[pin]
>> +//! work_2: Work<MyStruct, 2>,
>> +//! }
>> +//!
>> +//! impl_has_work! {
>> +//! impl HasWork<Self, 1> for MyStruct { self.work_1 }
>> +//! impl HasWork<Self, 2> for MyStruct { self.work_2 }
>> +//! }
>> +//!
>> +//! impl MyStruct {
>> +//! fn new(value_1: i32, value_2: i32) -> Result<Arc<Self>> {
>> +//! Arc::pin_init(pin_init!(MyStruct {
>> +//! value_1,
>> +//! value_2,
>> +//! work_1 <- Work::new(),
>> +//! work_2 <- Work::new(),
>> +//! }))
>> +//! }
>> +//! }
>> +//!
>> +//! impl WorkItem<1> for MyStruct {
>> +//! type Pointer = Arc<MyStruct>;
>> +//!
>> +//! fn run(this: Arc<MyStruct>) {
>> +//! pr_info!("The value is: {}", this.value_1);
>> +//! }
>> +//! }
>> +//!
>> +//! impl WorkItem<2> for MyStruct {
>> +//! type Pointer = Arc<MyStruct>;
>> +//!
>> +//! fn run(this: Arc<MyStruct>) {
>> +//! pr_info!("The second value is: {}", this.value_2);
>> +//! }
>> +//! }
>> +//!
>> +//! fn print_1_later(val: Arc<MyStruct>) {
>> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val);
>
> Nothing bad about explicit, but I just want to confirm that you could
> write
>
> let _ = workqueue::system().enqueue::<_, 1>(val);
>
> here, right?

Yes, you can also do that.

2023-06-02 14:46:17

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] rust: workqueue: implement `WorkItemPointer` for pointer types


Alice Ryhl <[email protected]> writes:

> 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<Box<T>>` type is for objects
> where the caller has exclusive ownership of the object.
>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>

Reviewed-by: Andreas Hindborg (Samsung) <[email protected]>

> ---
> 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 dbf0aab29a85..f06a2f036d8b 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -28,8 +28,10 @@
> //!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> -use crate::{bindings, prelude::*, types::Opaque};
> +use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
> +use alloc::boxed::Box;
> use core::marker::{PhantomData, PhantomPinned};
> +use core::pin::Pin;
>
> /// A kernel work queue.
> ///
> @@ -323,6 +325,99 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_typ
> )*};
> }
>
> +unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
> + // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
> + let ptr = ptr as *mut Work<T, ID>;
> + // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
> + let ptr = unsafe { T::work_container_of(ptr) };
> + // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
> + let arc = unsafe { Arc::from_raw(ptr) };
> +
> + T::run(arc)
> + }
> +}
> +
> +unsafe impl<T, const ID: u64> RawWorkItem<ID> for Arc<T>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + type EnqueueOutput = Result<(), Self>;
> +
> + unsafe fn __enqueue<F>(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 pointer is a raw pointer.
> + let ptr = Arc::into_raw(self) as *mut T;
> +
> + // SAFETY: Pointers into an `Arc` point at a valid value.
> + let work_ptr = unsafe { T::raw_get_work(ptr) };
> + // SAFETY: `raw_get_work` returns a pointer to a valid value.
> + let work_ptr = unsafe { Work::raw_get(work_ptr) };
> +
> + if queue_work_on(work_ptr) {
> + Ok(())
> + } else {
> + // SAFETY: The work queue has not taken ownership of the pointer.
> + Err(unsafe { Arc::from_raw(ptr) })
> + }
> + }
> +}
> +
> +unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<Box<T>>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
> + // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
> + let ptr = ptr as *mut Work<T, ID>;
> + // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
> + let ptr = unsafe { T::work_container_of(ptr) };
> + // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
> + let boxed = unsafe { Box::from_raw(ptr) };
> + // SAFETY: The box was already pinned when it was enqueued.
> + let pinned = unsafe { Pin::new_unchecked(boxed) };
> +
> + T::run(pinned)
> + }
> +}
> +
> +unsafe impl<T, const ID: u64> RawWorkItem<ID> for Pin<Box<T>>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + type EnqueueOutput = ();
> +
> + unsafe fn __enqueue<F>(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 = unsafe { Pin::into_inner_unchecked(self) };
> + let ptr = Box::into_raw(boxed);
> +
> + // SAFETY: Pointers into a `Box` point at a valid value.
> + let work_ptr = unsafe { T::raw_get_work(ptr) };
> + // SAFETY: `raw_get_work` returns a pointer to a valid value.
> + let work_ptr = 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 multi-threaded. There are


2023-06-02 14:50:10

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields


Alice Ryhl <[email protected]> writes:

> 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<T, ID>`. If a type
> implements this trait, then the type declares that, at the given offset,
> there is a field of type `Work<T, ID>`. 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<T>` on a type.
> The macro expands to something that only compiles if the specified field
> really has the type `Work<T>`. It is used like this:
>
> ```
> struct MyWorkItem {
> work_field: Work<MyWorkItem, 1>,
> }
>
> impl_has_work! {
> impl HasWork<MyWorkItem, 1> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>

Reviewed-by: Andreas Hindborg (Samsung) <[email protected]>

> ---
> rust/helpers.c | 8 ++
> rust/kernel/workqueue.rs | 219 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 226 insertions(+), 1 deletion(-)
>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 81e80261d597..7f0c2fe2fbeb 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -26,6 +26,7 @@
> #include <linux/spinlock.h>
> #include <linux/sched/signal.h>
> #include <linux/wait.h>
> +#include <linux/workqueue.h>
>
> __noreturn void rust_helper_BUG(void)
> {
> @@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
> }
> EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
>
> +void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
> + bool on_stack)
> +{
> + __INIT_WORK(work, func, on_stack);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper___INIT_WORK);
> +
> /*
> * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
> * as the Rust `usize` type, so we can use it in contexts where Rust
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index e37820f253f6..dbf0aab29a85 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -2,9 +2,34 @@
>
> //! 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 struct.) Since these IDs are
> +//! generic, they are used only at compile-time, so they shouldn't exist in the final binary.
> +//!
> +//! # The raw API
> +//!
> +//! The raw API consists of the `RawWorkItem` trait, where the work item needs 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. Furthermore, it also includes
> +//! a trait called `WorkItemPointer`, which is usually not used directly by 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 enqueued to a workqueue.
> +//! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> +//! that implements `WorkItem`.
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> -use crate::{bindings, types::Opaque};
> +use crate::{bindings, prelude::*, types::Opaque};
> +use core::marker::{PhantomData, PhantomPinned};
>
> /// A kernel work queue.
> ///
> @@ -106,6 +131,198 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
> F: FnOnce(*mut bindings::work_struct) -> bool;
> }
>
> +/// Defines the method that should be called directly when a work item is executed.
> +///
> +/// Typically you would implement [`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 the [`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` initialized with the [`run`]
> +/// method of this trait as the function pointer.
> +///
> +/// [`__enqueue`]: RawWorkItem::__enqueue
> +/// [`run`]: WorkItemPointer::run
> +pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
> + /// 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 still be valid.
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
> +}
> +
> +/// Defines the method that should be called when this work item is executed.
> +///
> +/// This trait is used when the `work_struct` field is defined using the [`Work`] helper.
> +pub trait WorkItem<const ID: u64 = 0> {
> + /// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or
> + /// `Pin<Box<Self>>`.
> + type Pointer: WorkItemPointer<ID>;
> +
> + /// 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 work 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 [`WorkItem`] that uses it.
> +#[repr(transparent)]
> +pub struct Work<T: ?Sized, const ID: u64 = 0> {
> + work: Opaque<bindings::work_struct>,
> + _pin: PhantomPinned,
> + _inner: PhantomData<T>,
> +}
> +
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Send for Work<T, ID> {}
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}
> +
> +impl<T: ?Sized, const ID: u64> Work<T, ID> {
> + /// Creates a new instance of [`Work`].
> + #[inline]
> + #[allow(clippy::new_ret_no_self)]
> + pub fn new() -> impl PinInit<Self>
> + where
> + T: WorkItem<ID>,
> + {
> + // SAFETY: The `WorkItemPointer` implementation promises that `run` can be used as the work
> + // item function.
> + unsafe {
> + kernel::init::pin_init_from_closure(move |slot| {
> + bindings::__INIT_WORK(Self::raw_get(slot), Some(T::Pointer::run), false);
> + Ok(())
> + })
> + }
> + }
> +
> + /// Get a pointer to the inner `work_struct`.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must not be dangling and must be properly aligned. (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<T, ID>`] field.
> +///
> +/// # Safety
> +///
> +/// The [`OFFSET`] constant must be the offset of a field in Self of type [`Work<T, ID>`]. The methods on
> +/// this trait must have exactly the behavior that the definitions given below have.
> +///
> +/// [`Work<T, ID>`]: Work
> +/// [`OFFSET`]: HasWork::OFFSET
> +pub unsafe trait HasWork<T, const ID: u64 = 0> {
> + /// The offset of the [`Work<T, ID>`] field.
> + ///
> + /// [`Work<T, ID>`]: Work
> + const OFFSET: usize;
> +
> + /// Returns the offset of the [`Work<T, ID>`] field.
> + ///
> + /// This method exists because the [`OFFSET`] constant cannot be accessed if the type is not Sized.
> + ///
> + /// [`Work<T, ID>`]: Work
> + /// [`OFFSET`]: HasWork::OFFSET
> + #[inline]
> + fn get_work_offset(&self) -> usize {
> + Self::OFFSET
> + }
> +
> + /// Returns a pointer to the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must point at a valid struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<T, ID> {
> + // SAFETY: The caller promises that the pointer is valid.
> + unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut Work<T, ID> }
> + }
> +
> + /// Returns a pointer to the struct containing the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The pointer must point at a [`Work<T, ID>`] field in a struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *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<T, ID>`] trait.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::sync::Arc;
> +///
> +/// struct MyStruct {
> +/// work_field: Work<MyStruct, 17>,
> +/// }
> +///
> +/// impl_has_work! {
> +/// impl HasWork<MyStruct, 17> for MyStruct { self.work_field }
> +/// }
> +/// ```
> +///
> +/// [`HasWork<T, ID>`]: 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 }
> + )*) => {$(
> + // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right
> + // type.
> + unsafe impl$(<$($implarg),*>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self $(<$($selfarg),*>)? {
> + const OFFSET: usize = $crate::offset_of!(Self, $field) as usize;
> +
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
> + // SAFETY: The caller promises that the pointer is not dangling.
> + 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 multi-threaded. There are


2023-06-02 14:53:43

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples


Alice Ryhl <[email protected]> writes:

> 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 <[email protected]>

Reviewed-by: Andreas Hindborg (Samsung) <[email protected]>

> ---
> rust/kernel/workqueue.rs | 104 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 104 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index c302e8b8624b..cefcf43ff40e 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -26,6 +26,110 @@
> //! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> //! that implements `WorkItem`.
> //!
> +//! ## Example
> +//!
> +//! This example defines a struct that holds an integer and can be scheduled 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};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value: i32,
> +//! #[pin]
> +//! work: Work<MyStruct>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self> for MyStruct { self.work }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value,
> +//! work <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value);
> +//! }
> +//! }
> +//!
> +//! /// This method will enqueue the struct for execution on the system workqueue, where its value
> +//! /// will be printed.
> +//! fn print_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue(val);
> +//! }
> +//! ```
> +//!
> +//! The following example shows how multiple `work_struct` fields can be used:
> +//!
> +//! ```
> +//! use kernel::prelude::*;
> +//! use kernel::sync::Arc;
> +//! use kernel::workqueue::{self, Work, WorkItem};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value_1: i32,
> +//! value_2: i32,
> +//! #[pin]
> +//! work_1: Work<MyStruct, 1>,
> +//! #[pin]
> +//! work_2: Work<MyStruct, 2>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self, 1> for MyStruct { self.work_1 }
> +//! impl HasWork<Self, 2> for MyStruct { self.work_2 }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value_1: i32, value_2: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value_1,
> +//! value_2,
> +//! work_1 <- Work::new(),
> +//! work_2 <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<1> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value_1);
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<2> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The second value is: {}", this.value_2);
> +//! }
> +//! }
> +//!
> +//! fn print_1_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val);
> +//! }
> +//!
> +//! fn print_2_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
> +//! }
> +//! ```
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> use crate::{bindings, prelude::*, sync::Arc, types::Opaque};


2023-06-02 15:01:19

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples


Alice Ryhl <[email protected]> writes:

> 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 <[email protected]>

Reviewed-by: Andreas Hindborg (Samsung) <[email protected]>

> ---
> rust/kernel/workqueue.rs | 104 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 104 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index c302e8b8624b..cefcf43ff40e 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -26,6 +26,110 @@
> //! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> //! that implements `WorkItem`.
> //!
> +//! ## Example
> +//!
> +//! This example defines a struct that holds an integer and can be scheduled 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};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value: i32,
> +//! #[pin]
> +//! work: Work<MyStruct>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self> for MyStruct { self.work }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value,
> +//! work <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value);
> +//! }
> +//! }
> +//!
> +//! /// This method will enqueue the struct for execution on the system workqueue, where its value
> +//! /// will be printed.
> +//! fn print_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue(val);
> +//! }
> +//! ```
> +//!
> +//! The following example shows how multiple `work_struct` fields can be used:
> +//!
> +//! ```
> +//! use kernel::prelude::*;
> +//! use kernel::sync::Arc;
> +//! use kernel::workqueue::{self, Work, WorkItem};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value_1: i32,
> +//! value_2: i32,
> +//! #[pin]
> +//! work_1: Work<MyStruct, 1>,
> +//! #[pin]
> +//! work_2: Work<MyStruct, 2>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self, 1> for MyStruct { self.work_1 }
> +//! impl HasWork<Self, 2> for MyStruct { self.work_2 }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value_1: i32, value_2: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value_1,
> +//! value_2,
> +//! work_1 <- Work::new(),
> +//! work_2 <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<1> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value_1);
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<2> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The second value is: {}", this.value_2);
> +//! }
> +//! }
> +//!
> +//! fn print_1_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val);
> +//! }
> +//!
> +//! fn print_2_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
> +//! }
> +//! ```
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> use crate::{bindings, prelude::*, sync::Arc, types::Opaque};


2023-06-02 15:16:03

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] rust: workqueue: define built-in queues


Alice Ryhl <[email protected]> writes:

> From: Wedson Almeida Filho <[email protected]>
>
> 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 <[email protected]>
> Co-developed-by: Alice Ryhl <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

I would suggest adding the description to freezable:

"A freezable wq participates in the freeze phase of the system suspend
operations. Work items on the wq are drained and no new work item starts
execution until thawed."

But otherwise ????

Reviewed-by: Andreas Hindborg (Samsung) <[email protected]>

> ---
> rust/kernel/workqueue.rs | 65 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index 9c630840039b..e37820f253f6 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -105,3 +105,68 @@ unsafe fn __enqueue<F>(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 multi-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 which 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 running 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, and 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 freezable.
> +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" variants 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_freezable_power_efficient_wq`).
> +///
> +/// It is similar to the one returned by [`system_power_efficient`] except that is freezable.
> +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) }
> +}


2023-06-02 15:21:07

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 7/8] rust: workqueue: add `try_spawn` helper method


Alice Ryhl <[email protected]> writes:

> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>

Reviewed-by: Andreas Hindborg (Samsung) <[email protected]>

> ---
> rust/kernel/workqueue.rs | 43 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 43 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index f06a2f036d8b..c302e8b8624b 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -29,6 +29,7 @@
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
> +use alloc::alloc::AllocError;
> use alloc::boxed::Box;
> use core::marker::{PhantomData, PhantomPinned};
> use core::pin::Pin;
> @@ -87,6 +88,44 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput
> })
> }
> }
> +
> + /// 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<T: 'static + Send + FnOnce()>(&self, func: T) -> Result<(), AllocError> {
> + let init = pin_init!(ClosureWork {
> + work <- Work::new(),
> + func: Some(func),
> + });
> +
> + self.enqueue(Box::pin_init(init).map_err(|_| AllocError)?);
> + Ok(())
> + }
> +}
> +
> +/// A helper type used in `try_spawn`.
> +#[pin_data]
> +struct ClosureWork<T> {
> + #[pin]
> + work: Work<ClosureWork<T>>,
> + func: Option<T>,
> +}
> +
> +impl<T> ClosureWork<T> {
> + fn project(self: Pin<&mut Self>) -> &mut Option<T> {
> + // SAFETY: The `func` field is not structurally pinned.
> + unsafe { &mut self.get_unchecked_mut().func }
> + }
> +}
> +
> +impl<T: FnOnce()> WorkItem for ClosureWork<T> {
> + type Pointer = Pin<Box<Self>>;
> +
> + fn run(mut this: Pin<Box<Self>>) {
> + if let Some(func) = this.as_mut().project().take() {
> + (func)()
> + }
> + }
> }
>
> /// A raw work item.
> @@ -325,6 +364,10 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_typ
> )*};
> }
>
> +impl_has_work! {
> + impl<T> HasWork<Self> for ClosureWork<T> { self.work }
> +}
> +
> unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
> where
> T: WorkItem<ID, Pointer = Self>,


2023-06-02 16:41:03

by Boqun Feng

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

On Fri, Jun 02, 2023 at 08:38:56AM +0000, Alice Ryhl wrote:
> Boqun Feng <[email protected]> writes:
> > On Thu, Jun 01, 2023 at 01:49:43PM +0000, Alice Ryhl wrote:
> >> diff --git a/rust/helpers.c b/rust/helpers.c
> >> index 81e80261d597..7f0c2fe2fbeb 100644
> >> --- a/rust/helpers.c
> >> +++ b/rust/helpers.c
> >> @@ -26,6 +26,7 @@
> >> #include <linux/spinlock.h>
> >> #include <linux/sched/signal.h>
> >> #include <linux/wait.h>
> >> +#include <linux/workqueue.h>
> >>
> >> __noreturn void rust_helper_BUG(void)
> >> {
> >> @@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
> >> }
> >> EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
> >>
> >> +void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
> >> + bool on_stack)
> >> +{
> >> + __INIT_WORK(work, func, on_stack);
> >
> > Note here all the work items in Rust will share the same lockdep class.
> > That could be problematic: the lockdep classes for work are for
> > detecting deadlocks in the following scenario:
> >
> > step 1: queue a work "foo", whose work function is:
> >
> > mutex_lock(&bar);
> > do_something(...);
> > mutex_unlock(&bar);
> >
> > step 2: in another thread do:
> >
> > mutex_lock(&bar);
> > flush_work(foo); // wait until work "foo" is finished.
> >
> > if this case, if step 2 get the lock "bar" first, it's a deadlock.
> >
> > With the current implementation, all the work items share the same
> > lockdep class, so the following will be treated as deadlock:
> >
> > <in work "work1">
> > mutex_lock(&bar);
> > do_something(...);
> > mutex_unlock(&bar);
> >
> > <in another thread>
> > mutex_lock(&bar);
> > flush_work(work2); // flush work2 intead of work1.
> >
> > which is a false positive. We at least need some changes in C side to
> > make it work:
> >
> > https://lore.kernel.org/rust-for-linux/[email protected]/
> >
> > however, that still has the disadvantage that all Rust work items have
> > the same name for the lockdep classes.. maybe we should extend that for
> > an extra "name" parameter. And then it's not necessary to be a macro.
>
> Yeah, I did know about this issue, but I didn't know what the best way
> to fix it is. What solution would you like me to use?
>

Having a init_work_with_key() function in C side:

void init_work_with_key(struct work_struct *work, bool onstack,
const char *name, struct lock_class_key *key)
{
__init_work(work, onstack); \
work->data = (atomic_long_t) WORK_DATA_INIT(); \
lockdep_init_map(&work->lockdep_map, name, key, 0); \
INIT_LIST_HEAD(&work->entry); \
work->func = func; \
}

, and provide class key and name from Rust side.

Thoughts?

Regards,
Boqun

> Alice

2023-06-11 15:52:31

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] rust: workqueue: define built-in queues

On 01.06.23 15:49, Alice Ryhl wrote:
> From: Wedson Almeida Filho <[email protected]>
>
> 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 <[email protected]>
> Co-developed-by: Alice Ryhl <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

Reviewed-by: Benno Lossin <[email protected]>

--
Cheers,
Benno

> ---
> rust/kernel/workqueue.rs | 65 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index 9c630840039b..e37820f253f6 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -105,3 +105,68 @@ unsafe fn __enqueue<F>(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 multi-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 which 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 running 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, and 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 freezable.
> +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" variants 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_freezable_power_efficient_wq`).
> +///
> +/// It is similar to the one returned by [`system_power_efficient`] except that is freezable.
> +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) }
> +}
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>


2023-06-11 16:12:05

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] rust: workqueue: implement `WorkItemPointer` for pointer types

On 01.06.23 15:49, Alice Ryhl wrote:
> 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<Box<T>>` type is for objects
> where the caller has exclusive ownership of the object.
>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>

I have a small nit below.

Reviewed-by: Benno Lossin <[email protected]>

> ---
> 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 dbf0aab29a85..f06a2f036d8b 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -28,8 +28,10 @@
> //!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> -use crate::{bindings, prelude::*, types::Opaque};
> +use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
> +use alloc::boxed::Box;
> use core::marker::{PhantomData, PhantomPinned};
> +use core::pin::Pin;
>
> /// A kernel work queue.
> ///
> @@ -323,6 +325,99 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_typ
> )*};
> }
>
> +unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
> + // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
> + let ptr = ptr as *mut Work<T, ID>;
> + // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
> + let ptr = unsafe { T::work_container_of(ptr) };
> + // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
> + let arc = unsafe { Arc::from_raw(ptr) };
> +
> + T::run(arc)
> + }
> +}
> +
> +unsafe impl<T, const ID: u64> RawWorkItem<ID> for Arc<T>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + type EnqueueOutput = Result<(), Self>;
> +
> + unsafe fn __enqueue<F>(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 pointer is a raw pointer.
> + let ptr = Arc::into_raw(self) as *mut T;

I personally would prefer `cast_mut()` here.

--
Cheers,
Benno

> +
> + // SAFETY: Pointers into an `Arc` point at a valid value.
> + let work_ptr = unsafe { T::raw_get_work(ptr) };
> + // SAFETY: `raw_get_work` returns a pointer to a valid value.
> + let work_ptr = unsafe { Work::raw_get(work_ptr) };
> +
> + if queue_work_on(work_ptr) {
> + Ok(())
> + } else {
> + // SAFETY: The work queue has not taken ownership of the pointer.
> + Err(unsafe { Arc::from_raw(ptr) })
> + }
> + }
> +}
> +
> +unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<Box<T>>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct) {
> + // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
> + let ptr = ptr as *mut Work<T, ID>;
> + // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
> + let ptr = unsafe { T::work_container_of(ptr) };
> + // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
> + let boxed = unsafe { Box::from_raw(ptr) };
> + // SAFETY: The box was already pinned when it was enqueued.
> + let pinned = unsafe { Pin::new_unchecked(boxed) };
> +
> + T::run(pinned)
> + }
> +}
> +
> +unsafe impl<T, const ID: u64> RawWorkItem<ID> for Pin<Box<T>>
> +where
> + T: WorkItem<ID, Pointer = Self>,
> + T: HasWork<T, ID>,
> +{
> + type EnqueueOutput = ();
> +
> + unsafe fn __enqueue<F>(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 = unsafe { Pin::into_inner_unchecked(self) };
> + let ptr = Box::into_raw(boxed);
> +
> + // SAFETY: Pointers into a `Box` point at a valid value.
> + let work_ptr = unsafe { T::raw_get_work(ptr) };
> + // SAFETY: `raw_get_work` returns a pointer to a valid value.
> + let work_ptr = 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 multi-threaded. There are
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>


2023-06-11 17:00:35

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples

On 01.06.23 15:49, Alice Ryhl wrote:
> 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 <[email protected]>

Reviewed-by: Benno Lossin <[email protected]>

I really like that you added these examples!
Is there some particular reason you decided to not use
0 as the first index in the second example? (you can keep
it this way)

--
Cheers,
Benno

> ---
> rust/kernel/workqueue.rs | 104 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 104 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index c302e8b8624b..cefcf43ff40e 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -26,6 +26,110 @@
> //! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> //! that implements `WorkItem`.
> //!
> +//! ## Example
> +//!
> +//! This example defines a struct that holds an integer and can be scheduled 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};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value: i32,
> +//! #[pin]
> +//! work: Work<MyStruct>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self> for MyStruct { self.work }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value,
> +//! work <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value);
> +//! }
> +//! }
> +//!
> +//! /// This method will enqueue the struct for execution on the system workqueue, where its value
> +//! /// will be printed.
> +//! fn print_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue(val);
> +//! }
> +//! ```
> +//!
> +//! The following example shows how multiple `work_struct` fields can be used:
> +//!
> +//! ```
> +//! use kernel::prelude::*;
> +//! use kernel::sync::Arc;
> +//! use kernel::workqueue::{self, Work, WorkItem};
> +//!
> +//! #[pin_data]
> +//! struct MyStruct {
> +//! value_1: i32,
> +//! value_2: i32,
> +//! #[pin]
> +//! work_1: Work<MyStruct, 1>,
> +//! #[pin]
> +//! work_2: Work<MyStruct, 2>,
> +//! }
> +//!
> +//! impl_has_work! {
> +//! impl HasWork<Self, 1> for MyStruct { self.work_1 }
> +//! impl HasWork<Self, 2> for MyStruct { self.work_2 }
> +//! }
> +//!
> +//! impl MyStruct {
> +//! fn new(value_1: i32, value_2: i32) -> Result<Arc<Self>> {
> +//! Arc::pin_init(pin_init!(MyStruct {
> +//! value_1,
> +//! value_2,
> +//! work_1 <- Work::new(),
> +//! work_2 <- Work::new(),
> +//! }))
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<1> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The value is: {}", this.value_1);
> +//! }
> +//! }
> +//!
> +//! impl WorkItem<2> for MyStruct {
> +//! type Pointer = Arc<MyStruct>;
> +//!
> +//! fn run(this: Arc<MyStruct>) {
> +//! pr_info!("The second value is: {}", this.value_2);
> +//! }
> +//! }
> +//!
> +//! fn print_1_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val);
> +//! }
> +//!
> +//! fn print_2_later(val: Arc<MyStruct>) {
> +//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
> +//! }
> +//! ```
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>


2023-06-11 17:20:55

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

On 01.06.23 15:49, Alice Ryhl wrote:
> 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<T, ID>`. If a type
> implements this trait, then the type declares that, at the given offset,
> there is a field of type `Work<T, ID>`. 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<T>` on a type.
> The macro expands to something that only compiles if the specified field
> really has the type `Work<T>`. It is used like this:
>
> ```
> struct MyWorkItem {
> work_field: Work<MyWorkItem, 1>,
> }
>
> impl_has_work! {
> impl HasWork<MyWorkItem, 1> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>

I really like this new design! It splits the relevant components in a much
more natural way and is easier to understand. I added some comments below,
they only affect documentation though.

Reviewed-by: Benno Lossin <[email protected]>

> ---
> rust/helpers.c | 8 ++
> rust/kernel/workqueue.rs | 219 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 226 insertions(+), 1 deletion(-)
>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 81e80261d597..7f0c2fe2fbeb 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -26,6 +26,7 @@
> #include <linux/spinlock.h>
> #include <linux/sched/signal.h>
> #include <linux/wait.h>
> +#include <linux/workqueue.h>
>
> __noreturn void rust_helper_BUG(void)
> {
> @@ -128,6 +129,13 @@ void rust_helper_put_task_struct(struct task_struct *t)
> }
> EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
>
> +void rust_helper___INIT_WORK(struct work_struct *work, work_func_t func,
> + bool on_stack)
> +{
> + __INIT_WORK(work, func, on_stack);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper___INIT_WORK);
> +
> /*
> * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
> * as the Rust `usize` type, so we can use it in contexts where Rust
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index e37820f253f6..dbf0aab29a85 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -2,9 +2,34 @@
>
> //! 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 struct.) Since these IDs are
> +//! generic, they are used only at compile-time, so they shouldn't exist in the final binary.
> +//!
> +//! # The raw API
> +//!
> +//! The raw API consists of the `RawWorkItem` trait, where the work item needs 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. Furthermore, it also includes
> +//! a trait called `WorkItemPointer`, which is usually not used directly by 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 enqueued to a workqueue.
> +//! * The `WorkItemPointer` trait is implemented for the pointer type that points at a something
> +//! that implements `WorkItem`.
> +//!
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> -use crate::{bindings, types::Opaque};
> +use crate::{bindings, prelude::*, types::Opaque};
> +use core::marker::{PhantomData, PhantomPinned};
>
> /// A kernel work queue.
> ///
> @@ -106,6 +131,198 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
> F: FnOnce(*mut bindings::work_struct) -> bool;
> }
>
> +/// Defines the method that should be called directly when a work item is executed.
> +///
> +/// Typically you would implement [`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 the [`WorkItem`] trait.

I think it makes sense to say that `Pin<Box<T>>` and `Arc<T>` implement
this trait (adding the comment in the next commit). Just to make it less
likely that people try to implement this trait instead of WorkItem. Also,
you could mention that this trait is meant for smart pointers.

> +///
> +/// 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` initialized with the [`run`]
> +/// method of this trait as the function pointer.
> +///
> +/// [`__enqueue`]: RawWorkItem::__enqueue
> +/// [`run`]: WorkItemPointer::run
> +pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {
> + /// 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 still be valid.
> + unsafe extern "C" fn run(ptr: *mut bindings::work_struct);
> +}
> +
> +/// Defines the method that should be called when this work item is executed.
> +///
> +/// This trait is used when the `work_struct` field is defined using the [`Work`] helper.
> +pub trait WorkItem<const ID: u64 = 0> {
> + /// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or
> + /// `Pin<Box<Self>>`.
> + type Pointer: WorkItemPointer<ID>;
> +
> + /// 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 work 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 [`WorkItem`] that uses it.
> +#[repr(transparent)]
> +pub struct Work<T: ?Sized, const ID: u64 = 0> {
> + work: Opaque<bindings::work_struct>,
> + _pin: PhantomPinned,
> + _inner: PhantomData<T>,
> +}
> +
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Send for Work<T, ID> {}
> +// SAFETY: Kernel work items are usable from any thread.
> +//
> +// We do not need to constrain `T` since the work item does not actually contain a `T`.
> +unsafe impl<T: ?Sized, const ID: u64> Sync for Work<T, ID> {}
> +
> +impl<T: ?Sized, const ID: u64> Work<T, ID> {
> + /// Creates a new instance of [`Work`].
> + #[inline]
> + #[allow(clippy::new_ret_no_self)]
> + pub fn new() -> impl PinInit<Self>
> + where
> + T: WorkItem<ID>,
> + {
> + // SAFETY: The `WorkItemPointer` implementation promises that `run` can be used as the work
> + // item function.
> + unsafe {
> + kernel::init::pin_init_from_closure(move |slot| {
> + bindings::__INIT_WORK(Self::raw_get(slot), Some(T::Pointer::run), false);
> + Ok(())
> + })
> + }
> + }
> +
> + /// Get a pointer to the inner `work_struct`.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must not be dangling and must be properly aligned. (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<T, ID>`] field.
> +///

Please mention the macro here as well, maybe also copy the example
from the commit message into this section.

> +/// # Safety
> +///
> +/// The [`OFFSET`] constant must be the offset of a field in Self of type [`Work<T, ID>`]. The methods on
> +/// this trait must have exactly the behavior that the definitions given below have.

I think you should just say "implementers are not allowed to implement
the functions defined by this trait." I see no reason to allow that, since
all functions are fully determined by the `OFFSET` constant.

--
Cheers,
Benno

> +///
> +/// [`Work<T, ID>`]: Work
> +/// [`OFFSET`]: HasWork::OFFSET
> +pub unsafe trait HasWork<T, const ID: u64 = 0> {
> + /// The offset of the [`Work<T, ID>`] field.
> + ///
> + /// [`Work<T, ID>`]: Work
> + const OFFSET: usize;
> +
> + /// Returns the offset of the [`Work<T, ID>`] field.
> + ///
> + /// This method exists because the [`OFFSET`] constant cannot be accessed if the type is not Sized.
> + ///
> + /// [`Work<T, ID>`]: Work
> + /// [`OFFSET`]: HasWork::OFFSET
> + #[inline]
> + fn get_work_offset(&self) -> usize {
> + Self::OFFSET
> + }
> +
> + /// Returns a pointer to the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The provided pointer must point at a valid struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<T, ID> {
> + // SAFETY: The caller promises that the pointer is valid.
> + unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut Work<T, ID> }
> + }
> +
> + /// Returns a pointer to the struct containing the [`Work<T, ID>`] field.
> + ///
> + /// # Safety
> + ///
> + /// The pointer must point at a [`Work<T, ID>`] field in a struct of type `Self`.
> + ///
> + /// [`Work<T, ID>`]: Work
> + #[inline]
> + unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *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<T, ID>`] trait.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::sync::Arc;
> +///
> +/// struct MyStruct {
> +/// work_field: Work<MyStruct, 17>,
> +/// }
> +///
> +/// impl_has_work! {
> +/// impl HasWork<MyStruct, 17> for MyStruct { self.work_field }
> +/// }
> +/// ```
> +///
> +/// [`HasWork<T, ID>`]: 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 }
> + )*) => {$(
> + // SAFETY: The implementation of `raw_get_work` only compiles if the field has the right
> + // type.
> + unsafe impl$(<$($implarg),*>)? $crate::workqueue::HasWork<$work_type $(, $id)?> for $self $(<$($selfarg),*>)? {
> + const OFFSET: usize = $crate::offset_of!(Self, $field) as usize;
> +
> + #[inline]
> + unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_type $(, $id)?> {
> + // SAFETY: The caller promises that the pointer is not dangling.
> + 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 multi-threaded. There are
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>


2023-06-11 17:20:57

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 7/8] rust: workqueue: add `try_spawn` helper method

On 01.06.23 15:49, Alice Ryhl wrote:
> 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 <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>

Reviewed-by: Benno Lossin <[email protected]>

--
Cheers,
Benno

> ---
> rust/kernel/workqueue.rs | 43 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 43 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index f06a2f036d8b..c302e8b8624b 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -29,6 +29,7 @@
> //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h)
>
> use crate::{bindings, prelude::*, sync::Arc, types::Opaque};
> +use alloc::alloc::AllocError;
> use alloc::boxed::Box;
> use core::marker::{PhantomData, PhantomPinned};
> use core::pin::Pin;
> @@ -87,6 +88,44 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput
> })
> }
> }
> +
> + /// 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<T: 'static + Send + FnOnce()>(&self, func: T) -> Result<(), AllocError> {
> + let init = pin_init!(ClosureWork {
> + work <- Work::new(),
> + func: Some(func),
> + });
> +
> + self.enqueue(Box::pin_init(init).map_err(|_| AllocError)?);
> + Ok(())
> + }
> +}
> +
> +/// A helper type used in `try_spawn`.
> +#[pin_data]
> +struct ClosureWork<T> {
> + #[pin]
> + work: Work<ClosureWork<T>>,
> + func: Option<T>,
> +}
> +
> +impl<T> ClosureWork<T> {
> + fn project(self: Pin<&mut Self>) -> &mut Option<T> {
> + // SAFETY: The `func` field is not structurally pinned.
> + unsafe { &mut self.get_unchecked_mut().func }
> + }
> +}
> +
> +impl<T: FnOnce()> WorkItem for ClosureWork<T> {
> + type Pointer = Pin<Box<Self>>;
> +
> + fn run(mut this: Pin<Box<Self>>) {
> + if let Some(func) = this.as_mut().project().take() {
> + (func)()
> + }
> + }
> }
>
> /// A raw work item.
> @@ -325,6 +364,10 @@ unsafe fn raw_get_work(ptr: *mut Self) -> *mut $crate::workqueue::Work<$work_typ
> )*};
> }
>
> +impl_has_work! {
> + impl<T> HasWork<Self> for ClosureWork<T> { self.work }
> +}
> +
> unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T>
> where
> T: WorkItem<ID, Pointer = Self>,
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>


2023-06-27 08:58:07

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] rust: workqueue: add helper for defining work_struct fields

Benno Lossin <[email protected]> writes:
> On 01.06.23 15:49, Alice Ryhl wrote:
>> +/// # Safety
>> +///
>> +/// The [`OFFSET`] constant must be the offset of a field in Self of type [`Work<T, ID>`]. The methods on
>> +/// this trait must have exactly the behavior that the definitions given below have.
>
> I think you should just say "implementers are not allowed to implement
> the functions defined by this trait." I see no reason to allow that, since
> all functions are fully determined by the `OFFSET` constant.

The macro overrides one of the methods, so that wont work.
(It overrides the method to check that the field has the claimed type.)

I'll apply the rest of your suggestions.

Alice


2023-06-27 09:03:09

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] rust: workqueue: add examples

Benno Lossin <[email protected]> writes:
> On 01.06.23 15:49, Alice Ryhl wrote:
>> 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 <[email protected]>
>
> Reviewed-by: Benno Lossin <[email protected]>
>
> I really like that you added these examples!
> Is there some particular reason you decided to not use
> 0 as the first index in the second example? (you can keep
> it this way)

In my head, it felt more natural for the first field to be called 1 and
the second field to be called 2. But it doesn't really matter which
numbers you use.

Alice