Message ID | 20231206-alice-file-v2-4-af617c0d9d94@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | File abstractions needed by Rust Binder | expand |
On 12/6/23 12:59, Alice Ryhl wrote: > + /// Commits the reservation. > + /// > + /// The previously reserved file descriptor is bound to `file`. This method consumes the > + /// [`FileDescriptorReservation`], so it will not be usable after this call. > + pub fn fd_install(self, file: ARef<File>) { > + // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is > + // guaranteed to have an owned ref count by its type invariants. There is no mention of the requirement that `current` has not changed (you do mention it on the `_not_send` field, but I think it should also be in the safety comment here). > + unsafe { bindings::fd_install(self.fd, file.0.get()) }; > + > + // `fd_install` consumes both the file descriptor and the file reference, so we cannot run > + // the destructors. > + core::mem::forget(self); > + core::mem::forget(file); > + } > +} > + > +impl Drop for FileDescriptorReservation { > + fn drop(&mut self) { > + // SAFETY: `self.fd` was returned by a previous call to `get_unused_fd_flags`. Ditto. > + unsafe { bindings::put_unused_fd(self.fd) }; > + } > +} > + > /// Represents the `EBADF` error code. > /// > /// Used for methods that can only fail with `EBADF`. > diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs > index fdb778e65d79..a4584d6b26c0 100644 > --- a/rust/kernel/types.rs > +++ b/rust/kernel/types.rs > @@ -387,3 +387,13 @@ pub enum Either<L, R> { > /// Constructs an instance of [`Either`] containing a value of type `R`. > Right(R), > } > + > +/// 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 when a type stores task-local information for example file descriptors. > +pub type NotThreadSafe = PhantomData<*mut ()>; This should be in its own commit. Then you can also change the usages of `PhantomData<*mut ()>` in `Guard` and `TaskRef`. It would be nice to use `NotThreadSafe` as the value instead of `PhantomData`, since it is a bit confusing... I think we might be able to also have a constant with the same name that is just `pub const NotThreadSafe: NotThreadSafe = PhantomData;`.
On Fri, Dec 8, 2023 at 8:37 AM Benno Lossin <benno.lossin@proton.me> wrote: > > +/// 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 when a type stores task-local information for example file descriptors. > > +pub type NotThreadSafe = PhantomData<*mut ()>; > > This should be in its own commit. Will do. > Then you can also change the usages of `PhantomData<*mut ()>` in > `Guard` and `TaskRef`. Will do. > It would be nice to use `NotThreadSafe` as the value instead of > `PhantomData`, since it is a bit confusing... That doesn't compile, unfortunately. > I think we might be able to also have a constant with the same name > that is just `pub const NotThreadSafe: NotThreadSafe = PhantomData;`. I can try to get this to work, but I worry that they will shadow each other. Alice
Benno Lossin <benno.lossin@proton.me> writes: > On 12/6/23 12:59, Alice Ryhl wrote: > > + /// Commits the reservation. > > + /// > > + /// The previously reserved file descriptor is bound to `file`. This method consumes the > > + /// [`FileDescriptorReservation`], so it will not be usable after this call. > > + pub fn fd_install(self, file: ARef<File>) { > > + // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is > > + // guaranteed to have an owned ref count by its type invariants. > > There is no mention of the requirement that `current` has not changed > (you do mention it on the `_not_send` field, but I think it should also > be in the safety comment here). > > [...] > > +impl Drop for FileDescriptorReservation { > > + fn drop(&mut self) { > > + // SAFETY: `self.fd` was returned by a previous call to `get_unused_fd_flags`. > > Ditto. I'll update this. > > +/// 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 when a type stores task-local information for example file descriptors. > > +pub type NotThreadSafe = PhantomData<*mut ()>; > > This should be in its own commit. > > Then you can also change the usages of `PhantomData<*mut ()>` in > `Guard` and `TaskRef`. > > It would be nice to use `NotThreadSafe` as the value instead of > `PhantomData`, since it is a bit confusing... > I think we might be able to also have a constant with the same name > that is just `pub const NotThreadSafe: NotThreadSafe = PhantomData;`. I was able to get this to work with a `const`, so I will use that. Alice
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index a88140794a8d..2d036d4636a0 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -9,9 +9,9 @@ bindings, cred::Credential, error::{code::*, Error, Result}, - types::{ARef, AlwaysRefCounted, Opaque}, + types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque}, }; -use core::ptr; +use core::{marker::PhantomData, ptr}; /// Flags associated with a [`File`]. pub mod flags { @@ -193,6 +193,70 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) { } } +/// A file descriptor reservation. +/// +/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it, +/// then we commit or drop the reservation. The first step may fail (e.g., the current process ran +/// out of available slots), but commit and drop never fail (and are mutually exclusive). +/// +/// Dropping the reservation happens in the destructor of this type. +/// +/// # Invariants +/// +/// The fd stored in this struct must correspond to a reserved file descriptor of the current task. +pub struct FileDescriptorReservation { + fd: u32, + /// Prevent values of this type from being moved to a different task. + /// + /// The `fd_install` and `put_unused_fd` functions assume that the value of `current` is + /// unchanged since the call to `get_unused_fd_flags`. By adding this marker to this type, we + /// prevent it from being moved across task boundaries, which ensures that `current` does not + /// change while this value exists. + _not_send: NotThreadSafe, +} + +impl FileDescriptorReservation { + /// Creates a new file descriptor reservation. + pub fn get_unused_fd_flags(flags: u32) -> Result<Self> { + // SAFETY: FFI call, there are no safety requirements on `flags`. + let fd: i32 = unsafe { bindings::get_unused_fd_flags(flags) }; + if fd < 0 { + return Err(Error::from_errno(fd)); + } + Ok(Self { + fd: fd as u32, + _not_send: PhantomData, + }) + } + + /// Returns the file descriptor number that was reserved. + pub fn reserved_fd(&self) -> u32 { + self.fd + } + + /// Commits the reservation. + /// + /// The previously reserved file descriptor is bound to `file`. This method consumes the + /// [`FileDescriptorReservation`], so it will not be usable after this call. + pub fn fd_install(self, file: ARef<File>) { + // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is + // guaranteed to have an owned ref count by its type invariants. + unsafe { bindings::fd_install(self.fd, file.0.get()) }; + + // `fd_install` consumes both the file descriptor and the file reference, so we cannot run + // the destructors. + core::mem::forget(self); + core::mem::forget(file); + } +} + +impl Drop for FileDescriptorReservation { + fn drop(&mut self) { + // SAFETY: `self.fd` was returned by a previous call to `get_unused_fd_flags`. + unsafe { bindings::put_unused_fd(self.fd) }; + } +} + /// Represents the `EBADF` error code. /// /// Used for methods that can only fail with `EBADF`. diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index fdb778e65d79..a4584d6b26c0 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -387,3 +387,13 @@ pub enum Either<L, R> { /// Constructs an instance of [`Either`] containing a value of type `R`. Right(R), } + +/// 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 when a type stores task-local information for example file descriptors. +pub type NotThreadSafe = PhantomData<*mut ()>;