Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutex/spinlock/condvar #990

Draft
wants to merge 10 commits into
base: rust-next
Choose a base branch
from
Draft

Conversation

wedsonaf
Copy link

No description provided.

rust/kernel/sync/lock.rs Outdated Show resolved Hide resolved
rust/kernel/sync/lock/spinlock.rs Outdated Show resolved Hide resolved
rust/kernel/sync/condvar.rs Show resolved Hide resolved
rust/kernel/sync/condvar.rs Outdated Show resolved Hide resolved
/// ```
///
/// [`struct mutex`]: ../../../../include/linux/mutex.h
pub type Mutex<T> = super::Lock<T, MutexBackend>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that I do not like with a type alias is that in the error messages it gets replaced by its definition, so this piece of code:

fn do_something_with_mutex(mtx: &Mutex<usize>) { ... }

let spinlock = Box::pin_init(new_spinlock!(42, "demo::spinlock")).unwrap();
do_something_with_mutex(&*spinlock);

Would fail with the following error:

  |
4 |     do_something_with_mutex(&*spinlock);
  |     ----------------------- ^^^^^^^^^^ expected `&Lock<usize, MutexBackend>`, found `&Lock<usize, SpinLockBackend>`
  |     |
  |     arguments to this function are incorrect
  |
   = note: expected reference `&Lock<usize, MutexBackend>`
              found reference `&Lock<usize, SpinLockBackend>`

Which is not really nice IMO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't like it either. (And we talked about this effect in the context of Arc in some past meeting.)

However, the split of lock backend/frontend really simplifies how we implement other locks: we don't need to reimplement the struct with the C lock + unsafe cell for data + phantom pinned data, Deref implementations, etc. We already have two locks in this series, and we'll have more soon (e.g., RawSpinLock).

One thing we could perhaps do is to create a new type with a Deref implementation, but it goes against the recommendation in its documentation:

Deref should only be implemented for smart pointers to avoid confusion.

So I think for now we should take this small hit on usability when the wrong type is supplied. Unless, of course, you have better ideas on how to improve this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better approach would be to introduce an attribute #[own_name] to Rust that would prevent the name resolution.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it.

Here's another argument in favor of #[own_name], the backends mentioned in the error currently are private -- there is no way to refer to them outside of the kernel create. So it would be much better to actually hide them.

Copy link
Member

@y86-dev y86-dev Mar 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started the discussion over at the rust zulip and they seem positive about adding this change in general to type aliases.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @y86-dev . If they make this change, we may be able to make Arc<T> be ARef<WithRef<T>> as some of us have discussed in the past.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds to me like a potential improvement over C++ too: at least current compilers also ignore the aliases sometimes (but sometimes they do print both the alias and the resolved type, so they are better in those cases, including @y86-dev's case above).

And improvements around this sort of thing are usually very welcome (e.g. old C++ compilers infamously printed fully expanded standard types, including defaulted type parameters).

Added to #355.

rust/kernel/sync/lock/spinlock.rs Outdated Show resolved Hide resolved
rust/kernel/sync/lock/spinlock.rs Show resolved Hide resolved
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}

impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this method is named unlocked and has a signature of fn unlocked<F: FnOnce(), U>(&mut self, f: F) -> U.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting that we do it this way? Or are you saying others have done something similar?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, I missed "in parking_lot (and lock_api)" at the end of my comment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now this is not public and only used by CondVar, which doesn't require a return value, so I'd rather keep it this way because it is simpler. We can, of course, change it later if the need arises or if we want to make it public for wider use.

rust/kernel/sync/lock/mutex.rs Outdated Show resolved Hide resolved
}

unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState) {
match guard_state {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, dynamic... Do you think it makes sense to have guards of different types for irq save?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runtime check is optimized away in the straightforward cases.

When we get to rw locks we'll need different guards. We can revisit this then -- that's the path we took in the rust tree: we started with this dynamic check then we converted when we added support for differently-typed guards.

@wedsonaf wedsonaf force-pushed the mutex branch 2 times, most recently from 643c4dd to e3bd837 Compare April 5, 2023 18:52
@ojeda ojeda force-pushed the rust-next branch 3 times, most recently from 107d2b0 to 871d3de Compare April 10, 2023 21:56
/// remain valid for read indefinitely.
unsafe fn init(
ptr: *mut Self::State,
name: *const core::ffi::c_char,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason that we convert it here to FFI types, instead of leaving it stay longer till the implementor?

@nbdd0121
Copy link
Member

I am experimenting a new design that splits irq save to a new backend: nbdd0121@6e4c596 (more iterations to come)

@nbdd0121
Copy link
Member

Updated version: nbdd0121@01ae567

They are generic Rust implementations of a lock and a lock guard that
contain code that is common to all locks. Different backends will be
introduced in subsequent commits.

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Suggested-by: Gary Guo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
This is the `struct mutex` lock backend and allows Rust code to use the
kernel mutex idiomatically.

Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Waiman Long <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
Rust cannot call C macros, so it has its own macro to create a new lock
class when a spin lock is initialised. This new function allows Rust
code to pass the lock class it generates to the C implementation.

Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Waiman Long <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Reviewed-by: Boqun Feng <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
This is the `spinlock_t` lock backend and allows Rust code to use the
kernel spinlock idiomatically.

Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Waiman Long <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
This is an owned reference to an object that is always ref-counted. This
is meant to be used in wrappers for C types that have their own ref
counting functions, for example, tasks, files, inodes, dentries, etc.

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Reviewed-by: Gary Guo <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
It is an abstraction for C's `struct task_struct`. It implements
`AlwaysRefCounted`, so the refcount of the wrapped object is managed
safely on the Rust side.

Cc: Ingo Molnar <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
This allows Rust code to get a reference to the current task without
having to increment the refcount, but still guaranteeing memory safety.

Cc: Ingo Molnar <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
This allows us to have data protected by a lock despite not being
wrapped by it. Access is granted by providing evidence that the lock is
held by the caller.

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Reviewed-by: Benno Lossin <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Miguel Ojeda <[email protected]>
It releases the lock, executes some function provided by the caller,
then reacquires the lock. This is preparation for the implementation of
condvars, which will sleep after between unlocking and relocking.

We need an explicit `relock` method for primitives like `SpinLock` that
have an irqsave variant: we use the guard state to determine if the lock
was originally acquired with the regular `lock` function or
`lock_irqsave`.

Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
This is the traditional condition variable or monitor synchronisation
primitive. It is implemented with C's `wait_queue_head_t`.

It allows users to release a lock and go to sleep while guaranteeing
that notifications won't be missed. This is achieved by enqueuing a wait
entry before releasing the lock.

Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Waiman Long <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

7 participants