-
Notifications
You must be signed in to change notification settings - Fork 435
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
base: rust-next
Are you sure you want to change the base?
Conversation
/// ``` | ||
/// | ||
/// [`struct mutex`]: ../../../../include/linux/mutex.h | ||
pub type Mutex<T> = super::Lock<T, MutexBackend>; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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()) { |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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/spinlock.rs
Outdated
} | ||
|
||
unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState) { | ||
match guard_state { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
643c4dd
to
e3bd837
Compare
107d2b0
to
871d3de
Compare
/// remain valid for read indefinitely. | ||
unsafe fn init( | ||
ptr: *mut Self::State, | ||
name: *const core::ffi::c_char, |
There was a problem hiding this comment.
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?
I am experimenting a new design that splits irq save to a new backend: nbdd0121@6e4c596 (more iterations to come) |
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]>
c9b5ce6
to
ce1c54f
Compare
9ee7197
to
6ce162a
Compare
No description provided.