diff mbox series

[v8,1/8] rust: types: add `NotThreadSafe`

Message ID 20240725-alice-file-v8-1-55a2e80deaa8@google.com (mailing list archive)
State New
Headers show
Series File abstractions needed by Rust Binder | expand

Commit Message

Alice Ryhl July 25, 2024, 2:27 p.m. UTC
This introduces a new marker type for types that shouldn't be thread
safe. By adding a field of this type to a struct, it becomes non-Send
and non-Sync, which means that it cannot be accessed in any way from
threads other than the one it was created on.

This is useful for APIs that require globals such as `current` to remain
constant while the value exists.

We update two existing users in the Kernel to use this helper:

 * `Task::current()` - moving the return type of this value to a
   different thread would not be safe as you can no longer be guaranteed
   that the `current` pointer remains valid.
 * Lock guards. Mutexes and spinlocks should be unlocked on the same
   thread as where they were locked, so we enforce this using the Send
   trait.

There are also additional users in later patches of this patchset. See
[1] and [2] for the discussion that led to the introduction of this
patch.

Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [1]
Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [2]
Suggested-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Trevor Gross <tmgross@umich.edu>
Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
Reviewed-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/sync/lock.rs | 13 +++++++++----
 rust/kernel/task.rs      | 10 ++++++----
 rust/kernel/types.rs     | 21 +++++++++++++++++++++
 3 files changed, 36 insertions(+), 8 deletions(-)

Comments

Peter Zijlstra July 25, 2024, 2:37 p.m. UTC | #1
On Thu, Jul 25, 2024 at 02:27:34PM +0000, Alice Ryhl wrote:
> This introduces a new marker type for types that shouldn't be thread
> safe. By adding a field of this type to a struct, it becomes non-Send
> and non-Sync, which means that it cannot be accessed in any way from
> threads other than the one it was created on.
> 
> This is useful for APIs that require globals such as `current` to remain
> constant while the value exists.
> 
> We update two existing users in the Kernel to use this helper:
> 
>  * `Task::current()` - moving the return type of this value to a
>    different thread would not be safe as you can no longer be guaranteed
>    that the `current` pointer remains valid.
>  * Lock guards. Mutexes and spinlocks should be unlocked on the same
>    thread as where they were locked, so we enforce this using the Send
>    trait.
> 
> There are also additional users in later patches of this patchset. See
> [1] and [2] for the discussion that led to the introduction of this
> patch.
> 
> Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [1]
> Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [2]
> Suggested-by: Benno Lossin <benno.lossin@proton.me>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Reviewed-by: Trevor Gross <tmgross@umich.edu>
> Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
> Reviewed-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  rust/kernel/sync/lock.rs | 13 +++++++++----
>  rust/kernel/task.rs      | 10 ++++++----
>  rust/kernel/types.rs     | 21 +++++++++++++++++++++
>  3 files changed, 36 insertions(+), 8 deletions(-)
> 
> diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
> index f6c34ca4d819..d6e9bab114b8 100644
> --- a/rust/kernel/sync/lock.rs
> +++ b/rust/kernel/sync/lock.rs
> @@ -6,8 +6,13 @@
>  //! spinlocks, raw spinlocks) to be provided with minimal effort.
>  
>  use super::LockClassKey;
> -use crate::{init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard};
> -use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned};
> +use crate::{
> +    init::PinInit,
> +    pin_init,
> +    str::CStr,
> +    types::{NotThreadSafe, Opaque, ScopeGuard},
> +};
> +use core::{cell::UnsafeCell, marker::PhantomPinned};
>  use macros::pin_data;
>  
>  pub mod mutex;
> @@ -139,7 +144,7 @@ pub fn lock(&self) -> Guard<'_, T, B> {
>  pub struct Guard<'a, T: ?Sized, B: Backend> {
>      pub(crate) lock: &'a Lock<T, B>,
>      pub(crate) state: B::GuardState,
> -    _not_send: PhantomData<*mut ()>,
> +    _not_send: NotThreadSafe,
>  }
>  
>  // SAFETY: `Guard` is sync when the data protected by the lock is also sync.
> @@ -191,7 +196,7 @@ pub(crate) unsafe fn new(lock: &'a Lock<T, B>, state: B::GuardState) -> Self {
>          Self {
>              lock,
>              state,
> -            _not_send: PhantomData,
> +            _not_send: NotThreadSafe,
>          }
>      }
>  }
> diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
> index 55dff7e088bf..278c623de0c6 100644
> --- a/rust/kernel/task.rs
> +++ b/rust/kernel/task.rs
> @@ -4,10 +4,12 @@
>  //!
>  //! C header: [`include/linux/sched.h`](srctree/include/linux/sched.h).
>  
> -use crate::types::Opaque;
> +use crate::{
> +    bindings,
> +    types::{NotThreadSafe, Opaque},
> +};
>  use core::{
>      ffi::{c_int, c_long, c_uint},
> -    marker::PhantomData,
>      ops::Deref,
>      ptr,
>  };
> @@ -106,7 +108,7 @@ impl Task {
>      pub unsafe fn current() -> impl Deref<Target = Task> {
>          struct TaskRef<'a> {
>              task: &'a Task,
> -            _not_send: PhantomData<*mut ()>,
> +            _not_send: NotThreadSafe,
>          }
>  
>          impl Deref for TaskRef<'_> {
> @@ -125,7 +127,7 @@ fn deref(&self) -> &Self::Target {
>              // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread
>              // (where it could potentially outlive the caller).
>              task: unsafe { &*ptr.cast() },
> -            _not_send: PhantomData,
> +            _not_send: NotThreadSafe,
>          }
>      }

As per always for not being able to read rust; how does this extend to
get_task_struct()? Once you've taken a reference on current, you should
be free to pass it along to whomever.
Alice Ryhl July 25, 2024, 3:09 p.m. UTC | #2
On Thu, Jul 25, 2024 at 4:37 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Jul 25, 2024 at 02:27:34PM +0000, Alice Ryhl wrote:
> > This introduces a new marker type for types that shouldn't be thread
> > safe. By adding a field of this type to a struct, it becomes non-Send
> > and non-Sync, which means that it cannot be accessed in any way from
> > threads other than the one it was created on.
> >
> > This is useful for APIs that require globals such as `current` to remain
> > constant while the value exists.
> >
> > We update two existing users in the Kernel to use this helper:
> >
> >  * `Task::current()` - moving the return type of this value to a
> >    different thread would not be safe as you can no longer be guaranteed
> >    that the `current` pointer remains valid.
> >  * Lock guards. Mutexes and spinlocks should be unlocked on the same
> >    thread as where they were locked, so we enforce this using the Send
> >    trait.
> >
> > There are also additional users in later patches of this patchset. See
> > [1] and [2] for the discussion that led to the introduction of this
> > patch.
> >
> > Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [1]
> > Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [2]
> > Suggested-by: Benno Lossin <benno.lossin@proton.me>
> > Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> > Reviewed-by: Trevor Gross <tmgross@umich.edu>
> > Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
> > Reviewed-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > ---
> >  rust/kernel/sync/lock.rs | 13 +++++++++----
> >  rust/kernel/task.rs      | 10 ++++++----
> >  rust/kernel/types.rs     | 21 +++++++++++++++++++++
> >  3 files changed, 36 insertions(+), 8 deletions(-)
> >
> > diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
> > index f6c34ca4d819..d6e9bab114b8 100644
> > --- a/rust/kernel/sync/lock.rs
> > +++ b/rust/kernel/sync/lock.rs
> > @@ -6,8 +6,13 @@
> >  //! spinlocks, raw spinlocks) to be provided with minimal effort.
> >
> >  use super::LockClassKey;
> > -use crate::{init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard};
> > -use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned};
> > +use crate::{
> > +    init::PinInit,
> > +    pin_init,
> > +    str::CStr,
> > +    types::{NotThreadSafe, Opaque, ScopeGuard},
> > +};
> > +use core::{cell::UnsafeCell, marker::PhantomPinned};
> >  use macros::pin_data;
> >
> >  pub mod mutex;
> > @@ -139,7 +144,7 @@ pub fn lock(&self) -> Guard<'_, T, B> {
> >  pub struct Guard<'a, T: ?Sized, B: Backend> {
> >      pub(crate) lock: &'a Lock<T, B>,
> >      pub(crate) state: B::GuardState,
> > -    _not_send: PhantomData<*mut ()>,
> > +    _not_send: NotThreadSafe,
> >  }
> >
> >  // SAFETY: `Guard` is sync when the data protected by the lock is also sync.
> > @@ -191,7 +196,7 @@ pub(crate) unsafe fn new(lock: &'a Lock<T, B>, state: B::GuardState) -> Self {
> >          Self {
> >              lock,
> >              state,
> > -            _not_send: PhantomData,
> > +            _not_send: NotThreadSafe,
> >          }
> >      }
> >  }
> > diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
> > index 55dff7e088bf..278c623de0c6 100644
> > --- a/rust/kernel/task.rs
> > +++ b/rust/kernel/task.rs
> > @@ -4,10 +4,12 @@
> >  //!
> >  //! C header: [`include/linux/sched.h`](srctree/include/linux/sched.h).
> >
> > -use crate::types::Opaque;
> > +use crate::{
> > +    bindings,
> > +    types::{NotThreadSafe, Opaque},
> > +};
> >  use core::{
> >      ffi::{c_int, c_long, c_uint},
> > -    marker::PhantomData,
> >      ops::Deref,
> >      ptr,
> >  };
> > @@ -106,7 +108,7 @@ impl Task {
> >      pub unsafe fn current() -> impl Deref<Target = Task> {
> >          struct TaskRef<'a> {
> >              task: &'a Task,
> > -            _not_send: PhantomData<*mut ()>,
> > +            _not_send: NotThreadSafe,
> >          }
> >
> >          impl Deref for TaskRef<'_> {
> > @@ -125,7 +127,7 @@ fn deref(&self) -> &Self::Target {
> >              // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread
> >              // (where it could potentially outlive the caller).
> >              task: unsafe { &*ptr.cast() },
> > -            _not_send: PhantomData,
> > +            _not_send: NotThreadSafe,
> >          }
> >      }
>
> As per always for not being able to read rust; how does this extend to
> get_task_struct()? Once you've taken a reference on current, you should
> be free to pass it along to whomever.

Once you take a reference on current, it becomes thread-safe. This is
because taking a reference creates a value of type ARef<Task> rather
than TaskRef, and ARef<Task> is considered thread-safe.

Alice
Peter Zijlstra July 25, 2024, 3:30 p.m. UTC | #3
On Thu, Jul 25, 2024 at 05:09:14PM +0200, Alice Ryhl wrote:

> > As per always for not being able to read rust; how does this extend to
> > get_task_struct()? Once you've taken a reference on current, you should
> > be free to pass it along to whomever.
> 
> Once you take a reference on current, it becomes thread-safe. This is
> because taking a reference creates a value of type ARef<Task> rather
> than TaskRef, and ARef<Task> is considered thread-safe.

Ignoring comments, there isn't a single mention of ARef there. Where
does it come from?
Alice Ryhl July 25, 2024, 3:32 p.m. UTC | #4
On Thu, Jul 25, 2024 at 5:30 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Jul 25, 2024 at 05:09:14PM +0200, Alice Ryhl wrote:
>
> > > As per always for not being able to read rust; how does this extend to
> > > get_task_struct()? Once you've taken a reference on current, you should
> > > be free to pass it along to whomever.
> >
> > Once you take a reference on current, it becomes thread-safe. This is
> > because taking a reference creates a value of type ARef<Task> rather
> > than TaskRef, and ARef<Task> is considered thread-safe.
>
> Ignoring comments, there isn't a single mention of ARef there. Where
> does it come from?

That's because the conversion to ARef<Task> is a two-step process. The
TaskRef type just provides a conversion from &TaskRef to &Task via the
`impl Deref` block. There is a conversion from &Task to ARef<Task>
elsewhere, and this conversion is where the call to get_task_struct()
happens.

Alice
Gary Guo Aug. 7, 2024, 10:59 a.m. UTC | #5
On Thu, 25 Jul 2024 14:27:34 +0000
Alice Ryhl <aliceryhl@google.com> wrote:

> This introduces a new marker type for types that shouldn't be thread
> safe. By adding a field of this type to a struct, it becomes non-Send
> and non-Sync, which means that it cannot be accessed in any way from
> threads other than the one it was created on.
> 
> This is useful for APIs that require globals such as `current` to remain
> constant while the value exists.
> 
> We update two existing users in the Kernel to use this helper:
> 
>  * `Task::current()` - moving the return type of this value to a
>    different thread would not be safe as you can no longer be guaranteed
>    that the `current` pointer remains valid.
>  * Lock guards. Mutexes and spinlocks should be unlocked on the same
>    thread as where they were locked, so we enforce this using the Send
>    trait.
> 
> There are also additional users in later patches of this patchset. See
> [1] and [2] for the discussion that led to the introduction of this
> patch.
> 
> Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [1]
> Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [2]
> Suggested-by: Benno Lossin <benno.lossin@proton.me>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Reviewed-by: Trevor Gross <tmgross@umich.edu>
> Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
> Reviewed-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
>  rust/kernel/sync/lock.rs | 13 +++++++++----
>  rust/kernel/task.rs      | 10 ++++++----
>  rust/kernel/types.rs     | 21 +++++++++++++++++++++
>  3 files changed, 36 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index f6c34ca4d819..d6e9bab114b8 100644
--- a/rust/kernel/sync/lock.rs
+++ b/rust/kernel/sync/lock.rs
@@ -6,8 +6,13 @@ 
 //! spinlocks, raw spinlocks) to be provided with minimal effort.
 
 use super::LockClassKey;
-use crate::{init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard};
-use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned};
+use crate::{
+    init::PinInit,
+    pin_init,
+    str::CStr,
+    types::{NotThreadSafe, Opaque, ScopeGuard},
+};
+use core::{cell::UnsafeCell, marker::PhantomPinned};
 use macros::pin_data;
 
 pub mod mutex;
@@ -139,7 +144,7 @@  pub fn lock(&self) -> Guard<'_, T, B> {
 pub struct Guard<'a, T: ?Sized, B: Backend> {
     pub(crate) lock: &'a Lock<T, B>,
     pub(crate) state: B::GuardState,
-    _not_send: PhantomData<*mut ()>,
+    _not_send: NotThreadSafe,
 }
 
 // SAFETY: `Guard` is sync when the data protected by the lock is also sync.
@@ -191,7 +196,7 @@  pub(crate) unsafe fn new(lock: &'a Lock<T, B>, state: B::GuardState) -> Self {
         Self {
             lock,
             state,
-            _not_send: PhantomData,
+            _not_send: NotThreadSafe,
         }
     }
 }
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index 55dff7e088bf..278c623de0c6 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -4,10 +4,12 @@ 
 //!
 //! C header: [`include/linux/sched.h`](srctree/include/linux/sched.h).
 
-use crate::types::Opaque;
+use crate::{
+    bindings,
+    types::{NotThreadSafe, Opaque},
+};
 use core::{
     ffi::{c_int, c_long, c_uint},
-    marker::PhantomData,
     ops::Deref,
     ptr,
 };
@@ -106,7 +108,7 @@  impl Task {
     pub unsafe fn current() -> impl Deref<Target = Task> {
         struct TaskRef<'a> {
             task: &'a Task,
-            _not_send: PhantomData<*mut ()>,
+            _not_send: NotThreadSafe,
         }
 
         impl Deref for TaskRef<'_> {
@@ -125,7 +127,7 @@  fn deref(&self) -> &Self::Target {
             // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread
             // (where it could potentially outlive the caller).
             task: unsafe { &*ptr.cast() },
-            _not_send: PhantomData,
+            _not_send: NotThreadSafe,
         }
     }
 
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index bd189d646adb..bb115d730ebb 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -473,3 +473,24 @@  unsafe impl AsBytes for str {}
 // does not have any uninitialized portions either.
 unsafe impl<T: AsBytes> AsBytes for [T] {}
 unsafe impl<T: AsBytes, const N: usize> AsBytes for [T; N] {}
+
+/// Zero-sized type to mark types not [`Send`].
+///
+/// Add this type as a field to your struct if your type should not be sent to a different task.
+/// Since [`Send`] is an auto trait, adding a single field that is `!Send` will ensure that the
+/// whole type is `!Send`.
+///
+/// If a type is `!Send` it is impossible to give control over an instance of the type to another
+/// task. This is useful to include in types that store or reference task-local information. A file
+/// descriptor is an example of such task-local information.
+///
+/// This type also makes the type `!Sync`, which prevents immutable access to the value from
+/// several threads in parallel.
+pub type NotThreadSafe = PhantomData<*mut ()>;
+
+/// Used to construct instances of type [`NotThreadSafe`] similar to how `PhantomData` is
+/// constructed.
+///
+/// [`NotThreadSafe`]: type@NotThreadSafe
+#[allow(non_upper_case_globals)]
+pub const NotThreadSafe: NotThreadSafe = PhantomData;