This patch introduces rust abstraction for io-uring sqe, cmd. IoUringSqe
abstracts io_uring_sqe and it has cmd_data(). and IoUringCmd is
abstraction for io_uring_cmd. From this, user can get cmd_op, flags,
pdu and also sqe.
Signed-off-by: Sidong Yang <sidong.yang@furiosa.ai>
---
rust/kernel/io_uring.rs | 183 ++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
2 files changed, 184 insertions(+)
create mode 100644 rust/kernel/io_uring.rs
diff --git a/rust/kernel/io_uring.rs b/rust/kernel/io_uring.rs
new file mode 100644
index 000000000000..0acdf3878247
--- /dev/null
+++ b/rust/kernel/io_uring.rs
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Furiosa AI.
+
+//! IoUring command and submission queue entry abstractions.
+//!
+//! C headers: [`include/linux/io_uring/cmd.h`](srctree/include/linux/io_uring/cmd.h) and
+//! [`include/linux/io_uring/io_uring.h`](srctree/include/linux/io_uring/io_uring.h)
+
+use core::{mem::MaybeUninit, pin::Pin, ptr::addr_of_mut};
+
+use crate::{fs::File, types::Opaque};
+
+/// A Rust abstraction for the Linux kernel's `io_uring_cmd` structure.
+///
+/// This structure is a safe, opaque wrapper around the raw C `io_uring_cmd`
+/// binding from the Linux kernel. It represents a command structure used
+/// in io_uring operations within the kernel.
+///
+/// # Type Safety
+///
+/// The `#[repr(transparent)]` attribute ensures that this wrapper has
+/// the same memory layout as the underlying `io_uring_cmd` structure,
+/// allowing it to be safely transmuted between the two representations.
+///
+/// # Fields
+///
+/// * `inner` - An opaque wrapper containing the actual `io_uring_cmd` data.
+/// The `Opaque` type prevents direct access to the internal
+/// structure fields, ensuring memory safety and encapsulation.
+///
+/// # Usage
+///
+/// This type is used internally by the io_uring subsystem to manage
+/// asynchronous I/O commands. It is typically accessed through a pinned
+/// mutable reference: `Pin<&mut IoUringCmd>`. The pinning ensures that
+/// the structure remains at a fixed memory location, which is required
+/// for safe interaction with the kernel's io_uring infrastructure.
+///
+/// Users typically receive this type as an argument in the `file_operations::uring_cmd()`
+/// callback function, where they can access and manipulate the io_uring command
+/// data for custom file operations.
+///
+/// This type should not be constructed or manipulated directly by
+/// kernel module developers.
+#[repr(transparent)]
+pub struct IoUringCmd {
+ inner: Opaque<bindings::io_uring_cmd>,
+}
+
+impl IoUringCmd {
+ /// Returns the cmd_op with associated with the io_uring_cmd.
+ #[inline]
+ pub fn cmd_op(&self) -> u32 {
+ // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid
+ unsafe { (*self.inner.get()).cmd_op }
+ }
+
+ /// Returns the flags with associated with the io_uring_cmd.
+ #[inline]
+ pub fn flags(&self) -> u32 {
+ // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid
+ unsafe { (*self.inner.get()).flags }
+ }
+
+ /// Returns the ref pdu for free use.
+ #[inline]
+ pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> {
+ // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid
+ let inner = unsafe { &mut *self.inner.get() };
+ let ptr = addr_of_mut!(inner.pdu) as *mut MaybeUninit<[u8; 32]>;
+
+ // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid
+ unsafe { &mut *ptr }
+ }
+
+ /// Constructs a new `IoUringCmd` from a raw `io_uring_cmd`
+ ///
+ /// # Safety
+ ///
+ /// The caller must guarantee that:
+ /// - The pointer `ptr` is not null and points to a valid `bindings::io_uring_cmd`.
+ /// - The memory pointed to by `ptr` remains valid for the duration of the returned reference's lifetime `'a`.
+ /// - The memory will not be moved or freed while the returned `Pin<&mut IoUringCmd>` is alive.
+ #[inline]
+ pub unsafe fn from_raw<'a>(ptr: *mut bindings::io_uring_cmd) -> Pin<&'a mut IoUringCmd> {
+ // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the
+ // duration of 'a. The cast is okay because `IoUringCmd` is `repr(transparent)` and has the
+ // same memory layout as `bindings::io_uring_cmd`. The returned `Pin` ensures that the object
+ // cannot be moved, which is required because the kernel may hold pointers to this memory
+ // location and moving it would invalidate those pointers.
+ unsafe { Pin::new_unchecked(&mut *ptr.cast()) }
+ }
+
+ /// Returns the file that referenced by uring cmd self.
+ #[inline]
+ pub fn file(&self) -> &File {
+ // SAFETY: The call guarantees that the `self.inner` is not dangling and stays valid
+ let file = unsafe { (*self.inner.get()).file };
+ // SAFETY: The call guarantees that `file` points valid file.
+ unsafe { File::from_raw_file(file) }
+ }
+
+ /// Returns a reference to the uring cmd's SQE.
+ #[inline]
+ pub fn sqe(&self) -> &IoUringSqe {
+ // SAFETY: The call guarantees that the `self.inner` is not dangling and stays valid
+ let sqe = unsafe { (*self.inner.get()).sqe };
+ // SAFETY: The call guarantees that the `sqe` points valid io_uring_sqe.
+ unsafe { IoUringSqe::from_raw(sqe) }
+ }
+
+ /// Called by consumers of io_uring_cmd, if they originally returned -EIOCBQUEUED upon receiving the command
+ #[inline]
+ pub fn done(self: Pin<&mut IoUringCmd>, ret: isize, res2: u64, issue_flags: u32) {
+ // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid
+ unsafe {
+ bindings::io_uring_cmd_done(self.inner.get(), ret, res2, issue_flags);
+ }
+ }
+}
+
+/// A Rust abstraction for the Linux kernel's `io_uring_sqe` structure.
+///
+/// This structure is a safe, opaque wrapper around the raw C `io_uring_sqe`
+/// binding from the Linux kernel. It represents a Submission Queue Entry
+/// used in io_uring operations within the kernel.
+///
+/// # Type Safety
+///
+/// The `#[repr(transparent)]` attribute ensures that this wrapper has
+/// the same memory layout as the underlying `io_uring_sqe` structure,
+/// allowing it to be safely transmuted between the two representations.
+///
+/// # Fields
+///
+/// * `inner` - An opaque wrapper containing the actual `io_uring_sqe` data.
+/// The `Opaque` type prevents direct access to the internal
+/// structure fields, ensuring memory safety and encapsulation.
+///
+/// # Usage
+///
+/// This type represents a submission queue entry that describes an I/O
+/// operation to be executed by the io_uring subsystem. It contains
+/// information such as the operation type, file descriptor, buffer
+/// pointers, and other operation-specific data.
+///
+/// Users can obtain this type from `IoUringCmd::sqe()` method, which
+/// extracts the submission queue entry associated with a command.
+///
+/// This type should not be constructed or manipulated directly by
+/// kernel module developers.
+#[repr(transparent)]
+pub struct IoUringSqe {
+ inner: Opaque<bindings::io_uring_sqe>,
+}
+
+impl<'a> IoUringSqe {
+ /// Returns the cmd_data with associated with the io_uring_sqe.
+ /// This function returns 16 byte array. We don't support IORING_SETUP_SQE128 for now.
+ pub fn cmd_data(&'a self) -> &'a [Opaque<u8>] {
+ // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid
+ let cmd = unsafe { (*self.inner.get()).__bindgen_anon_6.cmd.as_ref() };
+
+ // SAFETY: The call guarantees that `cmd` is not dangling and stays valid
+ unsafe { core::slice::from_raw_parts(cmd.as_ptr() as *const Opaque<u8>, 16) }
+ }
+
+ /// Constructs a new `IoUringSqe` from a raw `io_uring_sqe`
+ ///
+ /// # Safety
+ ///
+ /// The caller must guarantee that:
+ /// - The pointer `ptr` is not null and points to a valid `bindings::io_uring_sqe`.
+ /// - The memory pointed to by `ptr` remains valid for the duration of the returned reference's lifetime `'a`.
+ #[inline]
+ pub unsafe fn from_raw(ptr: *const bindings::io_uring_sqe) -> &'a IoUringSqe {
+ // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the
+ // duration of 'a. The cast is okay because `IoUringSqe` is `repr(transparent)` and has the
+ // same memory layout as `bindings::io_uring_sqe`.
+ unsafe { &*ptr.cast() }
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 6b4774b2b1c3..fb310e78d51d 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -80,6 +80,7 @@
pub mod fs;
pub mod init;
pub mod io;
+pub mod io_uring;
pub mod ioctl;
pub mod jump_label;
#[cfg(CONFIG_KUNIT)]
--
2.43.0
Hi Sidong, > On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > This patch introduces rust abstraction for io-uring sqe, cmd. IoUringSqe > abstracts io_uring_sqe and it has cmd_data(). and IoUringCmd is > abstraction for io_uring_cmd. From this, user can get cmd_op, flags, > pdu and also sqe. IMHO you need to expand this substantially. Instead of a very brief discussion of *what* you're doing, you need to explain *why* you're doing this and how this patch fits with the overall plan that you have in mind. Also, for the sake of reviewers, try to at least describe the role of all the types you've mentioned. > > Signed-off-by: Sidong Yang <sidong.yang@furiosa.ai> > --- > rust/kernel/io_uring.rs | 183 ++++++++++++++++++++++++++++++++++++++++ > rust/kernel/lib.rs | 1 + > 2 files changed, 184 insertions(+) > create mode 100644 rust/kernel/io_uring.rs > > diff --git a/rust/kernel/io_uring.rs b/rust/kernel/io_uring.rs > new file mode 100644 > index 000000000000..0acdf3878247 > --- /dev/null > +++ b/rust/kernel/io_uring.rs > @@ -0,0 +1,183 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +// Copyright (C) 2025 Furiosa AI. > + Perhaps this instead [0]. > +//! IoUring command and submission queue entry abstractions. Maybe expand this just a little bit as well. > +//! > +//! C headers: [`include/linux/io_uring/cmd.h`](srctree/include/linux/io_uring/cmd.h) and > +//! [`include/linux/io_uring/io_uring.h`](srctree/include/linux/io_uring/io_uring.h) > + > +use core::{mem::MaybeUninit, pin::Pin, ptr::addr_of_mut}; > + > +use crate::{fs::File, types::Opaque}; > + > +/// A Rust abstraction for the Linux kernel's `io_uring_cmd` structure. Is there a link for io_uring_cmd that you can use here? > +/// > +/// This structure is a safe, opaque wrapper around the raw C `io_uring_cmd` > +/// binding from the Linux kernel. It represents a command structure used > +/// in io_uring operations within the kernel. Perhaps backticks on “io_uring” ? > +/// > +/// # Type Safety > +/// > +/// The `#[repr(transparent)]` attribute ensures that this wrapper has > +/// the same memory layout as the underlying `io_uring_cmd` structure, > +/// allowing it to be safely transmuted between the two representations. > +/// > +/// # Fields > +/// > +/// * `inner` - An opaque wrapper containing the actual `io_uring_cmd` data. > +/// The `Opaque` type prevents direct access to the internal > +/// structure fields, ensuring memory safety and encapsulation. Place this on top of the field itself please. Also, I don’t think you need this at all because you don't need to explain the Opaque type, as it's extensively used in the kernel crate. > +/// > +/// # Usage I don’t think you need this. > +/// > +/// This type is used internally by the io_uring subsystem to manage > +/// asynchronous I/O commands. It is typically accessed through a pinned > +/// mutable reference: `Pin<&mut IoUringCmd>`. The pinning ensures that > +/// the structure remains at a fixed memory location, which is required > +/// for safe interaction with the kernel's io_uring infrastructure. I don’t think you need anything other than: > +/// This type is used internally by the io_uring subsystem to manage > +/// asynchronous I/O commands. Specifically, you don’t need to say this: > The pinning ensures that > +/// the structure remains at a fixed memory location, > +/// > +/// Users typically receive this type as an argument in the `file_operations::uring_cmd()` > +/// callback function, where they can access and manipulate the io_uring command > +/// data for custom file operations. > +/// > +/// This type should not be constructed or manipulated directly by > +/// kernel module developers. Well, this is pub, so the reality is that it can be manipulated directly through whatever public API it offers. > +#[repr(transparent)] > +pub struct IoUringCmd { > + inner: Opaque<bindings::io_uring_cmd>, > +} > + > +impl IoUringCmd { > + /// Returns the cmd_op with associated with the io_uring_cmd. Backticks > + #[inline] > + pub fn cmd_op(&self) -> u32 { > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid Not sure I understand what you’re trying to say here. Perhaps add an invariant saying that `self.inner` always points to a valid `bindings::io_uring_cmd` and mention that here instead. > + unsafe { (*self.inner.get()).cmd_op } > + } > + > + /// Returns the flags with associated with the io_uring_cmd. > + #[inline] > + pub fn flags(&self) -> u32 { > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > + unsafe { (*self.inner.get()).flags } > + } > + > + /// Returns the ref pdu for free use. I have no idea what “ref pdu” is. You need to describe these acronyms. > + #[inline] > + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { Why MaybeUninit? Also, this is a question for others, but I don’t think that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > + let inner = unsafe { &mut *self.inner.get() }; > + let ptr = addr_of_mut!(inner.pdu) as *mut MaybeUninit<[u8; 32]>; &raw mut > + > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > + unsafe { &mut *ptr } > + } > + > + /// Constructs a new `IoUringCmd` from a raw `io_uring_cmd` [`IoUringCmd`] By the way, always build the docs and see if they look nice. > + /// > + /// # Safety > + /// > + /// The caller must guarantee that: > + /// - The pointer `ptr` is not null and points to a valid `bindings::io_uring_cmd`. > + /// - The memory pointed to by `ptr` remains valid for the duration of the returned reference's lifetime `'a`. > + /// - The memory will not be moved or freed while the returned `Pin<&mut IoUringCmd>` is alive. This returns a wrapper over a mutable reference. You must mention Rust’s aliasing rules here. > + #[inline] > + pub unsafe fn from_raw<'a>(ptr: *mut bindings::io_uring_cmd) -> Pin<&'a mut IoUringCmd> { > + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the > + // duration of 'a. The cast is okay because `IoUringCmd` is `repr(transparent)` and has the > + // same memory layout as `bindings::io_uring_cmd`. The returned `Pin` ensures that the object > + // cannot be moved, which is required because the kernel may hold pointers to this memory > + // location and moving it would invalidate those pointers. Please break this into multiple paragraphs. > + unsafe { Pin::new_unchecked(&mut *ptr.cast()) } > + } > + > + /// Returns the file that referenced by uring cmd self. > + #[inline] > + pub fn file(&self) -> &File { > + // SAFETY: The call guarantees that the `self.inner` is not dangling and stays valid > + let file = unsafe { (*self.inner.get()).file }; > + // SAFETY: The call guarantees that `file` points valid file. > + unsafe { File::from_raw_file(file) } > + } > + > + /// Returns a reference to the uring cmd's SQE. Please define what `SQE` means. Add links if possible. > + #[inline] > + pub fn sqe(&self) -> &IoUringSqe { > + // SAFETY: The call guarantees that the `self.inner` is not dangling and stays valid > + let sqe = unsafe { (*self.inner.get()).sqe }; > + // SAFETY: The call guarantees that the `sqe` points valid io_uring_sqe. Backticks > + unsafe { IoUringSqe::from_raw(sqe) } > + } > + > + /// Called by consumers of io_uring_cmd, if they originally returned -EIOCBQUEUED upon receiving the command Backticks > + #[inline] > + pub fn done(self: Pin<&mut IoUringCmd>, ret: isize, res2: u64, issue_flags: u32) { The arguments are cryptic here. Please let us know what they do. > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > + unsafe { > + bindings::io_uring_cmd_done(self.inner.get(), ret, res2, issue_flags); > + } > + } > +} > + > +/// A Rust abstraction for the Linux kernel's `io_uring_sqe` structure. > +/// > +/// This structure is a safe, opaque wrapper around the raw C `io_uring_sqe` > +/// binding from the Linux kernel. It represents a Submission Queue Entry Ah, SQE == Submission Queue Entry. Is there a link for this? > +/// used in io_uring operations within the kernel. > +/// > +/// # Type Safety > +/// > +/// The `#[repr(transparent)]` attribute ensures that this wrapper has > +/// the same memory layout as the underlying `io_uring_sqe` structure, > +/// allowing it to be safely transmuted between the two representations. > +/// > +/// # Fields > +/// > +/// * `inner` - An opaque wrapper containing the actual `io_uring_sqe` data. > +/// The `Opaque` type prevents direct access to the internal > +/// structure fields, ensuring memory safety and encapsulation. > +/// > +/// # Usage > +/// > +/// This type represents a submission queue entry that describes an I/O > +/// operation to be executed by the io_uring subsystem. It contains > +/// information such as the operation type, file descriptor, buffer > +/// pointers, and other operation-specific data. This description is very good :) > +/// > +/// Users can obtain this type from `IoUringCmd::sqe()` method, which > +/// extracts the submission queue entry associated with a command. [`IoUringCmd::sqe`] > +/// > +/// This type should not be constructed or manipulated directly by > +/// kernel module developers. Again, this is pub and can be freely manipulated through whatever public API it offers. > +#[repr(transparent)] > +pub struct IoUringSqe { > + inner: Opaque<bindings::io_uring_sqe>, > +} > + > +impl<'a> IoUringSqe { Why is this ‘a here? > + /// Returns the cmd_data with associated with the io_uring_sqe. > + /// This function returns 16 byte array. We don't support IORING_SETUP_SQE128 for now. > + pub fn cmd_data(&'a self) -> &'a [Opaque<u8>] { This is automatically placed by the compiler. See the lifetime elision rules [1]. Also why does this return a reference to an array of Opaque<u8>? You can return a &[u8] here if you can prove that this reference is legal given Rust's aliasing rules. If you can't, then you have to look at what the DMA allocator code is doing and use that as an example, i.e.: have a look at the dma_read and dma_write macros and mark the function that returns the slice as "unsafe". > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > + let cmd = unsafe { (*self.inner.get()).__bindgen_anon_6.cmd.as_ref() }; What do you mean by “the call” ? Same in all other places where this sentence is used. > + > + // SAFETY: The call guarantees that `cmd` is not dangling and stays valid > + unsafe { core::slice::from_raw_parts(cmd.as_ptr() as *const Opaque<u8>, 16) } > + } > + > + /// Constructs a new `IoUringSqe` from a raw `io_uring_sqe` > + /// > + /// # Safety > + /// > + /// The caller must guarantee that: > + /// - The pointer `ptr` is not null and points to a valid `bindings::io_uring_sqe`. > + /// - The memory pointed to by `ptr` remains valid for the duration of the returned reference's lifetime `'a`. Must mention Rust’s aliasing rules here. > + #[inline] > + pub unsafe fn from_raw(ptr: *const bindings::io_uring_sqe) -> &'a IoUringSqe { > + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the > + // duration of 'a. The cast is okay because `IoUringSqe` is `repr(transparent)` and has the > + // same memory layout as `bindings::io_uring_sqe`. > + unsafe { &*ptr.cast() } > + } > +} > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index 6b4774b2b1c3..fb310e78d51d 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -80,6 +80,7 @@ > pub mod fs; > pub mod init; > pub mod io; > +pub mod io_uring; > pub mod ioctl; > pub mod jump_label; > #[cfg(CONFIG_KUNIT)] > -- > 2.43.0 > > [0] https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Properties/copyrightText/ [1] https://doc.rust-lang.org/reference/lifetime-elision.html
On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: >> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: >> + #[inline] >> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > > Why MaybeUninit? Also, this is a question for others, but I don’t think > that `u8`s can ever be uninitialized as all byte values are valid for `u8`. `u8` can be uninitialized. Uninitialized doesn't just mean "can take any bit pattern", but also "is known to the compiler as being uninitialized". The docs of `MaybeUninit` explain it like this: Moreover, uninitialized memory is special in that it does not have a fixed value (“fixed” meaning “it won’t change without being written to”). Reading the same uninitialized byte multiple times can give different results. But the return type probably should be `&mut [MaybeUninit<u8>; 32]` instead. >> + #[inline] >> + pub unsafe fn from_raw<'a>(ptr: *mut bindings::io_uring_cmd) -> Pin<&'a mut IoUringCmd> { >> + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the >> + // duration of 'a. The cast is okay because `IoUringCmd` is `repr(transparent)` and has the >> + // same memory layout as `bindings::io_uring_cmd`. The returned `Pin` ensures that the object >> + // cannot be moved, which is required because the kernel may hold pointers to this memory >> + // location and moving it would invalidate those pointers. > > Please break this into multiple paragraphs. We usually use bullet point lists for this. --- Cheers, Benno
Hi Benno, > On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: > > On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: >>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: >>> + #[inline] >>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { >> >> Why MaybeUninit? Also, this is a question for others, but I don’t think >> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > > `u8` can be uninitialized. Uninitialized doesn't just mean "can take any > bit pattern", but also "is known to the compiler as being > uninitialized". The docs of `MaybeUninit` explain it like this: > > Moreover, uninitialized memory is special in that it does not have a > fixed value (“fixed” meaning “it won’t change without being written > to”). Reading the same uninitialized byte multiple times can give > different results. > > But the return type probably should be `&mut [MaybeUninit<u8>; 32]` > instead. Right, but I guess the question then is why would we ever need to use MaybeUninit here anyways. It's a reference to a C array. Just treat that as initialized. — Daniel
On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: > Hi Benno, > >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: >> >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: >>>> + #[inline] >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { >>> >>> Why MaybeUninit? Also, this is a question for others, but I don’t think >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. >> >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any >> bit pattern", but also "is known to the compiler as being >> uninitialized". The docs of `MaybeUninit` explain it like this: >> >> Moreover, uninitialized memory is special in that it does not have a >> fixed value (“fixed” meaning “it won’t change without being written >> to”). Reading the same uninitialized byte multiple times can give >> different results. >> >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` >> instead. > > > Right, but I guess the question then is why would we ever need to use > MaybeUninit here anyways. > > It's a reference to a C array. Just treat that as initialized. AFAIK C uninitialized memory also is considered uninitialized in Rust. So if this array is not properly initialized on the C side, this would be the correct type. If it is initialized, then just use `&mut [u8; 32]`. --- Cheers, Benno
On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: > On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: > > Hi Benno, > > > >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: > >> > >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: > >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > >>>> + #[inline] > >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > >>> > >>> Why MaybeUninit? Also, this is a question for others, but I don´t think > >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > >> > >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any > >> bit pattern", but also "is known to the compiler as being > >> uninitialized". The docs of `MaybeUninit` explain it like this: > >> > >> Moreover, uninitialized memory is special in that it does not have a > >> fixed value ("fixed" meaning "it won´t change without being written > >> to"). Reading the same uninitialized byte multiple times can give > >> different results. > >> > >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` > >> instead. > > > > > > Right, but I guess the question then is why would we ever need to use > > MaybeUninit here anyways. > > > > It's a reference to a C array. Just treat that as initialized. > > AFAIK C uninitialized memory also is considered uninitialized in Rust. > So if this array is not properly initialized on the C side, this would > be the correct type. If it is initialized, then just use `&mut [u8; 32]`. pdu field is memory chunk for driver can use it freely. The driver usually saves a private data and read or modify it on the other context. using just `&mut [u8;32]` would be simple and easy to use. > > --- > Cheers, > Benno
On Fri, Aug 8, 2025 at 2:56 AM Sidong Yang <sidong.yang@furiosa.ai> wrote: > > On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: > > On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: > > > Hi Benno, > > > > > >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: > > >> > > >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: > > >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > >>>> + #[inline] > > >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > > >>> > > >>> Why MaybeUninit? Also, this is a question for others, but I don´t think > > >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > > >> > > >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any > > >> bit pattern", but also "is known to the compiler as being > > >> uninitialized". The docs of `MaybeUninit` explain it like this: > > >> > > >> Moreover, uninitialized memory is special in that it does not have a > > >> fixed value ("fixed" meaning "it won´t change without being written > > >> to"). Reading the same uninitialized byte multiple times can give > > >> different results. > > >> > > >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` > > >> instead. > > > > > > > > > Right, but I guess the question then is why would we ever need to use > > > MaybeUninit here anyways. > > > > > > It's a reference to a C array. Just treat that as initialized. > > > > AFAIK C uninitialized memory also is considered uninitialized in Rust. > > So if this array is not properly initialized on the C side, this would > > be the correct type. If it is initialized, then just use `&mut [u8; 32]`. > > pdu field is memory chunk for driver can use it freely. The driver usually > saves a private data and read or modify it on the other context. using > just `&mut [u8;32]` would be simple and easy to use. MaybeUninit is correct. The io_uring/uring_cmd layer doesn't initialize the pdu field. struct io_uring_cmd is overlaid with struct io_kiocb's cmd field. struct io_kiocb's are allocated using kmem_cache_alloc(_bulk)() in __io_alloc_req_refill(). io_preinit_req() is called to initialize each struct io_kiocb but doesn't initialize the cmd field. As Sidong said, it's uninitialized memory for the ->uring_cmd() implementation to use however it wants for the duration of the command. Best, Caleb
On Fri, Aug 08, 2025 at 09:55:22AM -0400, Caleb Sander Mateos wrote: > On Fri, Aug 8, 2025 at 2:56 AM Sidong Yang <sidong.yang@furiosa.ai> wrote: > > > > On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: > > > On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: > > > > Hi Benno, > > > > > > > >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: > > > >> > > > >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: > > > >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > > >>>> + #[inline] > > > >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > > > >>> > > > >>> Why MaybeUninit? Also, this is a question for others, but I don´t think > > > >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > > > >> > > > >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any > > > >> bit pattern", but also "is known to the compiler as being > > > >> uninitialized". The docs of `MaybeUninit` explain it like this: > > > >> > > > >> Moreover, uninitialized memory is special in that it does not have a > > > >> fixed value ("fixed" meaning "it won´t change without being written > > > >> to"). Reading the same uninitialized byte multiple times can give > > > >> different results. > > > >> > > > >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` > > > >> instead. > > > > > > > > > > > > Right, but I guess the question then is why would we ever need to use > > > > MaybeUninit here anyways. > > > > > > > > It's a reference to a C array. Just treat that as initialized. > > > > > > AFAIK C uninitialized memory also is considered uninitialized in Rust. > > > So if this array is not properly initialized on the C side, this would > > > be the correct type. If it is initialized, then just use `&mut [u8; 32]`. > > > > pdu field is memory chunk for driver can use it freely. The driver usually > > saves a private data and read or modify it on the other context. using > > just `&mut [u8;32]` would be simple and easy to use. > > MaybeUninit is correct. The io_uring/uring_cmd layer doesn't > initialize the pdu field. struct io_uring_cmd is overlaid with struct > io_kiocb's cmd field. struct io_kiocb's are allocated using > kmem_cache_alloc(_bulk)() in __io_alloc_req_refill(). io_preinit_req() > is called to initialize each struct io_kiocb but doesn't initialize > the cmd field. As Sidong said, it's uninitialized memory for the > ->uring_cmd() implementation to use however it wants for the duration > of the command. Agreed. Thanks. Thanks, Sidong > > Best, > Caleb
On Fri Aug 8, 2025 at 8:56 AM CEST, Sidong Yang wrote: > On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: >> On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: >> > Hi Benno, >> > >> >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: >> >> >> >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: >> >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: >> >>>> + #[inline] >> >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { >> >>> >> >>> Why MaybeUninit? Also, this is a question for others, but I don´t think >> >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. >> >> >> >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any >> >> bit pattern", but also "is known to the compiler as being >> >> uninitialized". The docs of `MaybeUninit` explain it like this: >> >> >> >> Moreover, uninitialized memory is special in that it does not have a >> >> fixed value ("fixed" meaning "it won´t change without being written >> >> to"). Reading the same uninitialized byte multiple times can give >> >> different results. >> >> >> >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` >> >> instead. >> > >> > >> > Right, but I guess the question then is why would we ever need to use >> > MaybeUninit here anyways. >> > >> > It's a reference to a C array. Just treat that as initialized. >> >> AFAIK C uninitialized memory also is considered uninitialized in Rust. >> So if this array is not properly initialized on the C side, this would >> be the correct type. If it is initialized, then just use `&mut [u8; 32]`. > > pdu field is memory chunk for driver can use it freely. The driver usually > saves a private data and read or modify it on the other context. using > just `&mut [u8;32]` would be simple and easy to use. Private data is usually handled using `ForeignOwnable` in Rust. What kind of data would be stored there? If it's a pointer, then `&mut [u8; 32]` would not be the correct choice. --- Cheers, Benno
On Fri, Aug 08, 2025 at 10:49:14AM +0200, Benno Lossin wrote: > On Fri Aug 8, 2025 at 8:56 AM CEST, Sidong Yang wrote: > > On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: > >> On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: > >> > Hi Benno, > >> > > >> >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: > >> >> > >> >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: > >> >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > >> >>>> + #[inline] > >> >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > >> >>> > >> >>> Why MaybeUninit? Also, this is a question for others, but I don´t think > >> >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > >> >> > >> >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any > >> >> bit pattern", but also "is known to the compiler as being > >> >> uninitialized". The docs of `MaybeUninit` explain it like this: > >> >> > >> >> Moreover, uninitialized memory is special in that it does not have a > >> >> fixed value ("fixed" meaning "it won´t change without being written > >> >> to"). Reading the same uninitialized byte multiple times can give > >> >> different results. > >> >> > >> >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` > >> >> instead. > >> > > >> > > >> > Right, but I guess the question then is why would we ever need to use > >> > MaybeUninit here anyways. > >> > > >> > It's a reference to a C array. Just treat that as initialized. > >> > >> AFAIK C uninitialized memory also is considered uninitialized in Rust. > >> So if this array is not properly initialized on the C side, this would > >> be the correct type. If it is initialized, then just use `&mut [u8; 32]`. > > > > pdu field is memory chunk for driver can use it freely. The driver usually > > saves a private data and read or modify it on the other context. using > > just `&mut [u8;32]` would be simple and easy to use. > > Private data is usually handled using `ForeignOwnable` in Rust. What > kind of data would be stored there? If it's a pointer, then `&mut [u8; > 32]` would not be the correct choice. Most driver uses `io_uring_cmd_to_pdu` macro that casts address of pdu to private data type. It seems that all driver use this macro has it's own struct type. How about make 2 function for pdu? like store_pdu(), borrow_pdu(). > > --- > Cheers, > Benno
On Fri Aug 8, 2025 at 11:43 AM CEST, Sidong Yang wrote: > On Fri, Aug 08, 2025 at 10:49:14AM +0200, Benno Lossin wrote: >> On Fri Aug 8, 2025 at 8:56 AM CEST, Sidong Yang wrote: >> > On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: >> >> On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: >> >> > Hi Benno, >> >> > >> >> >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: >> >> >> >> >> >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: >> >> >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: >> >> >>>> + #[inline] >> >> >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { >> >> >>> >> >> >>> Why MaybeUninit? Also, this is a question for others, but I don´t think >> >> >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. >> >> >> >> >> >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any >> >> >> bit pattern", but also "is known to the compiler as being >> >> >> uninitialized". The docs of `MaybeUninit` explain it like this: >> >> >> >> >> >> Moreover, uninitialized memory is special in that it does not have a >> >> >> fixed value ("fixed" meaning "it won´t change without being written >> >> >> to"). Reading the same uninitialized byte multiple times can give >> >> >> different results. >> >> >> >> >> >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` >> >> >> instead. >> >> > >> >> > >> >> > Right, but I guess the question then is why would we ever need to use >> >> > MaybeUninit here anyways. >> >> > >> >> > It's a reference to a C array. Just treat that as initialized. >> >> >> >> AFAIK C uninitialized memory also is considered uninitialized in Rust. >> >> So if this array is not properly initialized on the C side, this would >> >> be the correct type. If it is initialized, then just use `&mut [u8; 32]`. >> > >> > pdu field is memory chunk for driver can use it freely. The driver usually >> > saves a private data and read or modify it on the other context. using >> > just `&mut [u8;32]` would be simple and easy to use. >> >> Private data is usually handled using `ForeignOwnable` in Rust. What >> kind of data would be stored there? If it's a pointer, then `&mut [u8; >> 32]` would not be the correct choice. > > Most driver uses `io_uring_cmd_to_pdu` macro that casts address of pdu to > private data type. It seems that all driver use this macro has it's own > struct type. How about make 2 function for pdu? like store_pdu(), borrow_pdu(). We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` has been called before. Is there any way we can just ensure that pdu is always initialized? Like a callback that's called once, before the value is used at all? --- Cheers, Benno
On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: > On Fri Aug 8, 2025 at 11:43 AM CEST, Sidong Yang wrote: > > On Fri, Aug 08, 2025 at 10:49:14AM +0200, Benno Lossin wrote: > >> On Fri Aug 8, 2025 at 8:56 AM CEST, Sidong Yang wrote: > >> > On Wed, Aug 06, 2025 at 03:38:24PM +0200, Benno Lossin wrote: > >> >> On Wed Aug 6, 2025 at 2:38 PM CEST, Daniel Almeida wrote: > >> >> > Hi Benno, > >> >> > > >> >> >> On 2 Aug 2025, at 07:52, Benno Lossin <lossin@kernel.org> wrote: > >> >> >> > >> >> >> On Fri Aug 1, 2025 at 3:48 PM CEST, Daniel Almeida wrote: > >> >> >>>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > >> >> >>>> + #[inline] > >> >> >>>> + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > >> >> >>> > >> >> >>> Why MaybeUninit? Also, this is a question for others, but I don´t think > >> >> >>> that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > >> >> >> > >> >> >> `u8` can be uninitialized. Uninitialized doesn't just mean "can take any > >> >> >> bit pattern", but also "is known to the compiler as being > >> >> >> uninitialized". The docs of `MaybeUninit` explain it like this: > >> >> >> > >> >> >> Moreover, uninitialized memory is special in that it does not have a > >> >> >> fixed value ("fixed" meaning "it won´t change without being written > >> >> >> to"). Reading the same uninitialized byte multiple times can give > >> >> >> different results. > >> >> >> > >> >> >> But the return type probably should be `&mut [MaybeUninit<u8>; 32]` > >> >> >> instead. > >> >> > > >> >> > > >> >> > Right, but I guess the question then is why would we ever need to use > >> >> > MaybeUninit here anyways. > >> >> > > >> >> > It's a reference to a C array. Just treat that as initialized. > >> >> > >> >> AFAIK C uninitialized memory also is considered uninitialized in Rust. > >> >> So if this array is not properly initialized on the C side, this would > >> >> be the correct type. If it is initialized, then just use `&mut [u8; 32]`. > >> > > >> > pdu field is memory chunk for driver can use it freely. The driver usually > >> > saves a private data and read or modify it on the other context. using > >> > just `&mut [u8;32]` would be simple and easy to use. > >> > >> Private data is usually handled using `ForeignOwnable` in Rust. What > >> kind of data would be stored there? If it's a pointer, then `&mut [u8; > >> 32]` would not be the correct choice. > > > > Most driver uses `io_uring_cmd_to_pdu` macro that casts address of pdu to > > private data type. It seems that all driver use this macro has it's own > > struct type. How about make 2 function for pdu? like store_pdu(), borrow_pdu(). > > We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` > has been called before. Is there any way we can just ensure that pdu is > always initialized? Like a callback that's called once, before the value > is used at all? I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is simple and best. Only driver knows it's initialized. There is no way to check whether it's initialized with reading the pdu. The best way is to return `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After init, driver knows it's guranteed that it's initialized so it could call `assume_init_mut()`. And casting to other struct is another problem. The driver is responsible for determining how to interpret the PDU, whether by using it directly as a byte array or by performing an unsafe cast to another struct. Thanks, Sidong > > --- > Cheers, > Benno
On Sat Aug 9, 2025 at 2:51 PM CEST, Sidong Yang wrote: > On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: >> We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` >> has been called before. Is there any way we can just ensure that pdu is >> always initialized? Like a callback that's called once, before the value >> is used at all? > > I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is > simple and best. Only driver knows it's initialized. There is no way to > check whether it's initialized with reading the pdu. The best way is to return > `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After > init, driver knows it's guranteed that it's initialized so it could call > `assume_init_mut()`. And casting to other struct is another problem. The driver > is responsible for determining how to interpret the PDU, whether by using it > directly as a byte array or by performing an unsafe cast to another struct. But then drivers will have to use `unsafe` & possibly cast the slice to a struct? I think that's bad design since we try to avoid unsafe code in drivers as much as possible. Couldn't we try to ensure from the abstraction side that any time you create such an object, the driver needs to provide the pdu data? Or we could make it implement `Default` and then set it to that before handing it to the driver. --- Cheers, Benno
On Sat, Aug 09, 2025 at 10:22:06PM +0200, Benno Lossin wrote: > On Sat Aug 9, 2025 at 2:51 PM CEST, Sidong Yang wrote: > > On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: > >> We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` > >> has been called before. Is there any way we can just ensure that pdu is > >> always initialized? Like a callback that's called once, before the value > >> is used at all? > > > > I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is > > simple and best. Only driver knows it's initialized. There is no way to > > check whether it's initialized with reading the pdu. The best way is to return > > `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After > > init, driver knows it's guranteed that it's initialized so it could call > > `assume_init_mut()`. And casting to other struct is another problem. The driver > > is responsible for determining how to interpret the PDU, whether by using it > > directly as a byte array or by performing an unsafe cast to another struct. > > But then drivers will have to use `unsafe` & possibly cast the slice to > a struct? I think that's bad design since we try to avoid unsafe code in > drivers as much as possible. Couldn't we try to ensure from the > abstraction side that any time you create such an object, the driver > needs to provide the pdu data? Or we could make it implement `Default` > and then set it to that before handing it to the driver. pdu data is [u8; 32] memory space that driver can borrow. this has two kind of issues. The one is that the array is not initialized and another one is it's array type that driver should cast it to private data structure unsafely. The first one could be resolved with returning `&mut MaybeUninit<>`. And the second one, casting issue, is remaining. It seems that we need new unsafe trait like below: /// Pdu should be... repr C or transparent, sizeof <= 20 unsafe trait Pdu: Sized {} /// Returning to casted Pdu type T pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> I think it is like bytemuck::Pod trait. Pod meaning plain old data. Thanks, Sidong > > --- > Cheers, > Benno
> On 10 Aug 2025, at 10:50, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > On Sat, Aug 09, 2025 at 10:22:06PM +0200, Benno Lossin wrote: >> On Sat Aug 9, 2025 at 2:51 PM CEST, Sidong Yang wrote: >>> On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: >>>> We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` >>>> has been called before. Is there any way we can just ensure that pdu is >>>> always initialized? Like a callback that's called once, before the value >>>> is used at all? >>> >>> I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is >>> simple and best. Only driver knows it's initialized. There is no way to >>> check whether it's initialized with reading the pdu. The best way is to return >>> `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After >>> init, driver knows it's guranteed that it's initialized so it could call >>> `assume_init_mut()`. And casting to other struct is another problem. The driver >>> is responsible for determining how to interpret the PDU, whether by using it >>> directly as a byte array or by performing an unsafe cast to another struct. >> >> But then drivers will have to use `unsafe` & possibly cast the slice to >> a struct? I think that's bad design since we try to avoid unsafe code in >> drivers as much as possible. Couldn't we try to ensure from the >> abstraction side that any time you create such an object, the driver >> needs to provide the pdu data? Or we could make it implement `Default` >> and then set it to that before handing it to the driver. > > pdu data is [u8; 32] memory space that driver can borrow. this has two kind of > issues. The one is that the array is not initialized and another one is it's > array type that driver should cast it to private data structure unsafely. > The first one could be resolved with returning `&mut MaybeUninit<>`. And the > second one, casting issue, is remaining. > > It seems that we need new unsafe trait like below: > > /// Pdu should be... repr C or transparent, sizeof <= 20 > unsafe trait Pdu: Sized {} > > /// Returning to casted Pdu type T > pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> Wait, you receive an uninitialized array, and you’re supposed to cast it to T, is that correct? Because that does not fit the signature above. > > I think it is like bytemuck::Pod trait. Pod meaning plain old data. > > Thanks, > Sidong > > >> >> --- >> Cheers, >> Benno I'm not really sure how this solves the transmute/cast problem. Is the trait you're referring to supposed to have any member functions? Or is it just a marker trait? I wonder if we can fit the existing "kernel::FromBytes" for this problem. — Daniel
On Sun, Aug 10, 2025 at 11:27:12AM -0300, Daniel Almeida wrote: > > > > On 10 Aug 2025, at 10:50, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > > > On Sat, Aug 09, 2025 at 10:22:06PM +0200, Benno Lossin wrote: > >> On Sat Aug 9, 2025 at 2:51 PM CEST, Sidong Yang wrote: > >>> On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: > >>>> We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` > >>>> has been called before. Is there any way we can just ensure that pdu is > >>>> always initialized? Like a callback that's called once, before the value > >>>> is used at all? > >>> > >>> I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is > >>> simple and best. Only driver knows it's initialized. There is no way to > >>> check whether it's initialized with reading the pdu. The best way is to return > >>> `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After > >>> init, driver knows it's guranteed that it's initialized so it could call > >>> `assume_init_mut()`. And casting to other struct is another problem. The driver > >>> is responsible for determining how to interpret the PDU, whether by using it > >>> directly as a byte array or by performing an unsafe cast to another struct. > >> > >> But then drivers will have to use `unsafe` & possibly cast the slice to > >> a struct? I think that's bad design since we try to avoid unsafe code in > >> drivers as much as possible. Couldn't we try to ensure from the > >> abstraction side that any time you create such an object, the driver > >> needs to provide the pdu data? Or we could make it implement `Default` > >> and then set it to that before handing it to the driver. > > > > pdu data is [u8; 32] memory space that driver can borrow. this has two kind of > > issues. The one is that the array is not initialized and another one is it's > > array type that driver should cast it to private data structure unsafely. > > The first one could be resolved with returning `&mut MaybeUninit<>`. And the > > second one, casting issue, is remaining. > > > > It seems that we need new unsafe trait like below: > > > > /// Pdu should be... repr C or transparent, sizeof <= 20 > > unsafe trait Pdu: Sized {} > > > > /// Returning to casted Pdu type T > > pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> > > Wait, you receive an uninitialized array, and you´re supposed to cast it to > T, is that correct? Because that does not fit the signature above. Sorry if my intent wasn´t clear. More example below: // in rust/kernel/io_uring.rs unsafe trait Pdu: Sized {} pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> { let inner = unsafe { &mut *self.inner.get() }; let ptr = &raw mut inner.pdu as *mut MaybeUninit<T>; // the cast here unsafe { &mut *ptr } } // in driver code #[repr(C)] struct MyPdu { value: u64 } unsafe impl Pdu for MyPdu {} // initialize ioucmd.pdu().write(MyPdu { value: 1 }); // read or modify let mypdu = unsafe { ioucmd.pdu().assume_init_mut() }; Thanks, Sidong > > > > > I think it is like bytemuck::Pod trait. Pod meaning plain old data. > > > > Thanks, > > Sidong > > > > > >> > >> --- > >> Cheers, > >> Benno > > > I'm not really sure how this solves the transmute/cast problem. Is the trait > you're referring to supposed to have any member functions? Or is it just a > marker trait? > > I wonder if we can fit the existing "kernel::FromBytes" for this problem. > > - Daniel >
On Sun Aug 10, 2025 at 4:46 PM CEST, Sidong Yang wrote: > On Sun, Aug 10, 2025 at 11:27:12AM -0300, Daniel Almeida wrote: >> > On 10 Aug 2025, at 10:50, Sidong Yang <sidong.yang@furiosa.ai> wrote: >> > >> > On Sat, Aug 09, 2025 at 10:22:06PM +0200, Benno Lossin wrote: >> >> On Sat Aug 9, 2025 at 2:51 PM CEST, Sidong Yang wrote: >> >>> On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: >> >>>> We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` >> >>>> has been called before. Is there any way we can just ensure that pdu is >> >>>> always initialized? Like a callback that's called once, before the value >> >>>> is used at all? >> >>> >> >>> I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is >> >>> simple and best. Only driver knows it's initialized. There is no way to >> >>> check whether it's initialized with reading the pdu. The best way is to return >> >>> `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After >> >>> init, driver knows it's guranteed that it's initialized so it could call >> >>> `assume_init_mut()`. And casting to other struct is another problem. The driver >> >>> is responsible for determining how to interpret the PDU, whether by using it >> >>> directly as a byte array or by performing an unsafe cast to another struct. >> >> >> >> But then drivers will have to use `unsafe` & possibly cast the slice to >> >> a struct? I think that's bad design since we try to avoid unsafe code in >> >> drivers as much as possible. Couldn't we try to ensure from the >> >> abstraction side that any time you create such an object, the driver >> >> needs to provide the pdu data? Or we could make it implement `Default` >> >> and then set it to that before handing it to the driver. >> > >> > pdu data is [u8; 32] memory space that driver can borrow. this has two kind of >> > issues. The one is that the array is not initialized and another one is it's >> > array type that driver should cast it to private data structure unsafely. >> > The first one could be resolved with returning `&mut MaybeUninit<>`. And the >> > second one, casting issue, is remaining. >> > >> > It seems that we need new unsafe trait like below: >> > >> > /// Pdu should be... repr C or transparent, sizeof <= 20 >> > unsafe trait Pdu: Sized {} >> > >> > /// Returning to casted Pdu type T >> > pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> >> >> Wait, you receive an uninitialized array, and you´re supposed to cast it to >> T, is that correct? Because that does not fit the signature above. > > Sorry if my intent wasn´t clear. More example below: > > // in rust/kernel/io_uring.rs > unsafe trait Pdu: Sized {} > pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> { > let inner = unsafe { &mut *self.inner.get() }; > let ptr = &raw mut inner.pdu as *mut MaybeUninit<T>; // the cast here > unsafe { &mut *ptr } > } > > // in driver code > #[repr(C)] struct MyPdu { value: u64 } > unsafe impl Pdu for MyPdu {} > > // initialize > ioucmd.pdu().write(MyPdu { value: 1 }); > > // read or modify > let mypdu = unsafe { ioucmd.pdu().assume_init_mut() }; This is the kind of code I'd like to avoid, since it plans to use `unsafe` in driver code (the `unsafe impl` above is also a problem, but we can solve that with a derive macro). Where are the entrypoints for `IoUringCmd` for driver code? I imagine that there is some kind of a driver callback (like `probe`, `open` etc) that contains an `Pin<&mut IoUringCmd>` as an argument, right? When is it created, can we control that & just write some default value to the pdu field? --- Cheers, Benno
On Sun, Aug 10, 2025 at 10:06:21PM +0200, Benno Lossin wrote: > On Sun Aug 10, 2025 at 4:46 PM CEST, Sidong Yang wrote: > > On Sun, Aug 10, 2025 at 11:27:12AM -0300, Daniel Almeida wrote: > >> > On 10 Aug 2025, at 10:50, Sidong Yang <sidong.yang@furiosa.ai> wrote: > >> > > >> > On Sat, Aug 09, 2025 at 10:22:06PM +0200, Benno Lossin wrote: > >> >> On Sat Aug 9, 2025 at 2:51 PM CEST, Sidong Yang wrote: > >> >>> On Sat, Aug 09, 2025 at 12:18:49PM +0200, Benno Lossin wrote: > >> >>>> We'd need to ensure that `borrow_pdu` can only be called if `store_pdu` > >> >>>> has been called before. Is there any way we can just ensure that pdu is > >> >>>> always initialized? Like a callback that's called once, before the value > >> >>>> is used at all? > >> >>> > >> >>> I've thought about this. As Celab said, returning `&mut MaybeUninit<[u8;32]> is > >> >>> simple and best. Only driver knows it's initialized. There is no way to > >> >>> check whether it's initialized with reading the pdu. The best way is to return > >> >>> `&mut MaybeUninit<[u8;32]>` and driver initializes it in first time. After > >> >>> init, driver knows it's guranteed that it's initialized so it could call > >> >>> `assume_init_mut()`. And casting to other struct is another problem. The driver > >> >>> is responsible for determining how to interpret the PDU, whether by using it > >> >>> directly as a byte array or by performing an unsafe cast to another struct. > >> >> > >> >> But then drivers will have to use `unsafe` & possibly cast the slice to > >> >> a struct? I think that's bad design since we try to avoid unsafe code in > >> >> drivers as much as possible. Couldn't we try to ensure from the > >> >> abstraction side that any time you create such an object, the driver > >> >> needs to provide the pdu data? Or we could make it implement `Default` > >> >> and then set it to that before handing it to the driver. > >> > > >> > pdu data is [u8; 32] memory space that driver can borrow. this has two kind of > >> > issues. The one is that the array is not initialized and another one is it's > >> > array type that driver should cast it to private data structure unsafely. > >> > The first one could be resolved with returning `&mut MaybeUninit<>`. And the > >> > second one, casting issue, is remaining. > >> > > >> > It seems that we need new unsafe trait like below: > >> > > >> > /// Pdu should be... repr C or transparent, sizeof <= 20 > >> > unsafe trait Pdu: Sized {} > >> > > >> > /// Returning to casted Pdu type T > >> > pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> > >> > >> Wait, you receive an uninitialized array, and you´re supposed to cast it to > >> T, is that correct? Because that does not fit the signature above. > > > > Sorry if my intent wasn´t clear. More example below: > > > > // in rust/kernel/io_uring.rs > > unsafe trait Pdu: Sized {} > > pub fn pdu<T: Pdu>(&mut self) -> &mut MaybeUninit<T> { > > let inner = unsafe { &mut *self.inner.get() }; > > let ptr = &raw mut inner.pdu as *mut MaybeUninit<T>; // the cast here > > unsafe { &mut *ptr } > > } > > > > // in driver code > > #[repr(C)] struct MyPdu { value: u64 } > > unsafe impl Pdu for MyPdu {} > > > > // initialize > > ioucmd.pdu().write(MyPdu { value: 1 }); > > > > // read or modify > > let mypdu = unsafe { ioucmd.pdu().assume_init_mut() }; > > This is the kind of code I'd like to avoid, since it plans to use > `unsafe` in driver code (the `unsafe impl` above is also a problem, but > we can solve that with a derive macro). > > Where are the entrypoints for `IoUringCmd` for driver code? I imagine > that there is some kind of a driver callback (like `probe`, `open` etc) > that contains an `Pin<&mut IoUringCmd>` as an argument, right? When is > it created, can we control that & just write some default value to the > pdu field? There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` would be create in the callback function. But the callback function could be called repeatedly with same `io_uring_cmd` instance as far as I know. But in c side, there is initialization step `io_uring_cmd_prep()`. How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte as flag in pdu for checking initialized also we should provide 31 bytes except a byte for the flag. Thanks, Sidong > > --- > Cheers, > Benno
> > There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` > would be create in the callback function. But the callback function could be > called repeatedly with same `io_uring_cmd` instance as far as I know. > > But in c side, there is initialization step `io_uring_cmd_prep()`. > How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte > as flag in pdu for checking initialized also we should provide 31 bytes except > a byte for the flag. > That was a follow-up question of mine. Can’t we enforce zero-initialization in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. Hopefully this can be done as you've described above, but I don't want to over extend my opinion on something I know nothing about. — Daniel
On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: > > > > > There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` > > would be create in the callback function. But the callback function could be > > called repeatedly with same `io_uring_cmd` instance as far as I know. > > > > But in c side, there is initialization step `io_uring_cmd_prep()`. > > How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte > > as flag in pdu for checking initialized also we should provide 31 bytes except > > a byte for the flag. > > > > That was a follow-up question of mine. Can´t we enforce zero-initialization > in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. > > Hopefully this can be done as you've described above, but I don't want to over > extend my opinion on something I know nothing about. I need to add a commit that initialize pdu in prep step in next version. I'd like to get a comment from io_uring maintainer Jens. Thanks. If we could initialize (filling zero) in prep step, How about casting issue? Driver still needs to cast array to its private struct in unsafe? Thanks, Sidong > > - Daniel
On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: > On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: >> > There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` >> > would be create in the callback function. But the callback function could be >> > called repeatedly with same `io_uring_cmd` instance as far as I know. >> > >> > But in c side, there is initialization step `io_uring_cmd_prep()`. >> > How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte >> > as flag in pdu for checking initialized also we should provide 31 bytes except >> > a byte for the flag. >> > >> >> That was a follow-up question of mine. Can´t we enforce zero-initialization >> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. >> >> Hopefully this can be done as you've described above, but I don't want to over >> extend my opinion on something I know nothing about. > > I need to add a commit that initialize pdu in prep step in next version. > I'd like to get a comment from io_uring maintainer Jens. Thanks. > > If we could initialize (filling zero) in prep step, How about casting issue? > Driver still needs to cast array to its private struct in unsafe? We still would have the casting issue. Can't we do the following: * Add a new associated type to `MiscDevice` called `IoUringPdu` that has to implement `Default` and have a size of at most 32 bytes. * make `IoUringCmd` generic * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` * initialize the private data to be `IoUringPdu::default()` when we create the `IoUringCmd` object. * provide a `fn pdu(&mut self) -> &mut Pdu` on `IoUringPdu<Pdu>`. Any thoughts? If we don't want to add a new associated type to `MiscDevice` (because not everyone has to declare the `IoUringCmd` data), I have a small trait dance that we can do to avoid that: pub trait IoUringMiscDevice: MiscDevice { type IoUringPdu: Default; // missing the 32 byte constraint } and then in MiscDevice we still add this function: fn uring_cmd( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _io_uring_cmd: Pin<&mut IoUringCmd<Self::IoUringPdu>>, _issue_flags: u32, ) -> Result<i32> where Self: IoUringMiscDevice, { build_error!(VTABLE_DEFAULT_ERROR) } It can only be called when the user also implements `IoUringMiscDevice`. --- Cheers, Benno
> On 12 Aug 2025, at 05:34, Benno Lossin <lossin@kernel.org> wrote: > > On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: >> On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: >>>> There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` >>>> would be create in the callback function. But the callback function could be >>>> called repeatedly with same `io_uring_cmd` instance as far as I know. >>>> >>>> But in c side, there is initialization step `io_uring_cmd_prep()`. >>>> How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte >>>> as flag in pdu for checking initialized also we should provide 31 bytes except >>>> a byte for the flag. >>>> >>> >>> That was a follow-up question of mine. Can´t we enforce zero-initialization >>> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. >>> >>> Hopefully this can be done as you've described above, but I don't want to over >>> extend my opinion on something I know nothing about. >> >> I need to add a commit that initialize pdu in prep step in next version. >> I'd like to get a comment from io_uring maintainer Jens. Thanks. >> >> If we could initialize (filling zero) in prep step, How about casting issue? >> Driver still needs to cast array to its private struct in unsafe? > > We still would have the casting issue. > > Can't we do the following: > > * Add a new associated type to `MiscDevice` called `IoUringPdu` that > has to implement `Default` and have a size of at most 32 bytes. > * make `IoUringCmd` generic > * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` > * initialize the private data to be `IoUringPdu::default()` when we > create the `IoUringCmd` object. > * provide a `fn pdu(&mut self) -> &mut Pdu` on `IoUringPdu<Pdu>`. > > Any thoughts? If we don't want to add a new associated type to > `MiscDevice` (because not everyone has to declare the `IoUringCmd` > data), I have a small trait dance that we can do to avoid that: Benno, IIUC, and note that I'm not proficient with io_uring in general: I think we have to accept that we will need to parse types from and to byte arrays, and that is inherently unsafe. It is no different from what is going on in UserSliceReader/UserSliceWriter, and IMHO, we should copy that in as much as it makes sense. I think that the only difference is that all uAPI types de-facto satisfy all the requirements for FromBytes/AsBytes, as we've discussed previously, whereas here, drivers have to prove that their types can implement the trait. By the way, Sidong, is this byte array shared with userspace? i.e.: is there any copy_to/from_user() taking place here? -- Daniel
On Tue, Aug 12, 2025 at 11:38:51AM -0300, Daniel Almeida wrote: > > > > On 12 Aug 2025, at 05:34, Benno Lossin <lossin@kernel.org> wrote: > > > > On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: > >> On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: > >>>> There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` > >>>> would be create in the callback function. But the callback function could be > >>>> called repeatedly with same `io_uring_cmd` instance as far as I know. > >>>> > >>>> But in c side, there is initialization step `io_uring_cmd_prep()`. > >>>> How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte > >>>> as flag in pdu for checking initialized also we should provide 31 bytes except > >>>> a byte for the flag. > >>>> > >>> > >>> That was a follow-up question of mine. Can´t we enforce zero-initialization > >>> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. > >>> > >>> Hopefully this can be done as you've described above, but I don't want to over > >>> extend my opinion on something I know nothing about. > >> > >> I need to add a commit that initialize pdu in prep step in next version. > >> I'd like to get a comment from io_uring maintainer Jens. Thanks. > >> > >> If we could initialize (filling zero) in prep step, How about casting issue? > >> Driver still needs to cast array to its private struct in unsafe? > > > > We still would have the casting issue. > > > > Can't we do the following: > > > > * Add a new associated type to `MiscDevice` called `IoUringPdu` that > > has to implement `Default` and have a size of at most 32 bytes. > > * make `IoUringCmd` generic > > * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` > > * initialize the private data to be `IoUringPdu::default()` when we > > create the `IoUringCmd` object. > > * provide a `fn pdu(&mut self) -> &mut Pdu` on `IoUringPdu<Pdu>`. > > > > Any thoughts? If we don't want to add a new associated type to > > `MiscDevice` (because not everyone has to declare the `IoUringCmd` > > data), I have a small trait dance that we can do to avoid that: > > > Benno, > > IIUC, and note that I'm not proficient with io_uring in general: > > I think we have to accept that we will need to parse types from and to byte > arrays, and that is inherently unsafe. It is no different from what is going on > in UserSliceReader/UserSliceWriter, and IMHO, we should copy that in as much as > it makes sense. > > I think that the only difference is that all uAPI types de-facto satisfy all > the requirements for FromBytes/AsBytes, as we've discussed previously, whereas > here, drivers have to prove that their types can implement the trait. > > > By the way, Sidong, is this byte array shared with userspace? i.e.: is there > any copy_to/from_user() taking place here? No. pdu array allocated from kernel. I'll use `core::ptr::copy_nonoverlapping`. Thanks, Sidong > > -- Daniel
On Tue, Aug 12, 2025 at 10:34:56AM +0200, Benno Lossin wrote: > On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: > > On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: > >> > There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` > >> > would be create in the callback function. But the callback function could be > >> > called repeatedly with same `io_uring_cmd` instance as far as I know. > >> > > >> > But in c side, there is initialization step `io_uring_cmd_prep()`. > >> > How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte > >> > as flag in pdu for checking initialized also we should provide 31 bytes except > >> > a byte for the flag. > >> > > >> > >> That was a follow-up question of mine. Can´t we enforce zero-initialization > >> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. > >> > >> Hopefully this can be done as you've described above, but I don't want to over > >> extend my opinion on something I know nothing about. > > > > I need to add a commit that initialize pdu in prep step in next version. > > I'd like to get a comment from io_uring maintainer Jens. Thanks. > > > > If we could initialize (filling zero) in prep step, How about casting issue? > > Driver still needs to cast array to its private struct in unsafe? > > We still would have the casting issue. > > Can't we do the following: > > * Add a new associated type to `MiscDevice` called `IoUringPdu` that > has to implement `Default` and have a size of at most 32 bytes. > * make `IoUringCmd` generic > * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` > * initialize the private data to be `IoUringPdu::default()` when we > create the `IoUringCmd` object. `uring_cmd` could be called multiple times. So we can't initialize in that time. I don't understand that how can we cast [u8; 32] to `IoUringPdu` safely. It seems that casting can't help to use unsafe. I think best way is that just return zerod `&mut [u8; 32]` and each driver implements safe serde logic for its private data. > * provide a `fn pdu(&mut self) -> &mut Pdu` on `IoUringPdu<Pdu>`. > > Any thoughts? If we don't want to add a new associated type to > `MiscDevice` (because not everyone has to declare the `IoUringCmd` > data), I have a small trait dance that we can do to avoid that: > > pub trait IoUringMiscDevice: MiscDevice { > type IoUringPdu: Default; // missing the 32 byte constraint > } > > and then in MiscDevice we still add this function: > > fn uring_cmd( > _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, > _io_uring_cmd: Pin<&mut IoUringCmd<Self::IoUringPdu>>, > _issue_flags: u32, > ) -> Result<i32> > where > Self: IoUringMiscDevice, > { > build_error!(VTABLE_DEFAULT_ERROR) > } > > It can only be called when the user also implements `IoUringMiscDevice`. > > --- > Cheers, > Benno
> On 12 Aug 2025, at 09:19, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > On Tue, Aug 12, 2025 at 10:34:56AM +0200, Benno Lossin wrote: >> On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: >>> On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: >>>>> There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` >>>>> would be create in the callback function. But the callback function could be >>>>> called repeatedly with same `io_uring_cmd` instance as far as I know. >>>>> >>>>> But in c side, there is initialization step `io_uring_cmd_prep()`. >>>>> How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte >>>>> as flag in pdu for checking initialized also we should provide 31 bytes except >>>>> a byte for the flag. >>>>> >>>> >>>> That was a follow-up question of mine. Can´t we enforce zero-initialization >>>> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. >>>> >>>> Hopefully this can be done as you've described above, but I don't want to over >>>> extend my opinion on something I know nothing about. >>> >>> I need to add a commit that initialize pdu in prep step in next version. >>> I'd like to get a comment from io_uring maintainer Jens. Thanks. >>> >>> If we could initialize (filling zero) in prep step, How about casting issue? >>> Driver still needs to cast array to its private struct in unsafe? >> >> We still would have the casting issue. >> >> Can't we do the following: >> >> * Add a new associated type to `MiscDevice` called `IoUringPdu` that >> has to implement `Default` and have a size of at most 32 bytes. >> * make `IoUringCmd` generic >> * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` >> * initialize the private data to be `IoUringPdu::default()` when we >> create the `IoUringCmd` object. > > `uring_cmd` could be called multiple times. So we can't initialize > in that time. I don't understand that how can we cast [u8; 32] to > `IoUringPdu` safely. It seems that casting can't help to use unsafe. > I think best way is that just return zerod `&mut [u8; 32]` and > each driver implements safe serde logic for its private data. > Again, can’t we use FromBytes for this?
On Tue, Aug 12, 2025 at 09:43:56AM -0300, Daniel Almeida wrote: > > > > On 12 Aug 2025, at 09:19, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > > > On Tue, Aug 12, 2025 at 10:34:56AM +0200, Benno Lossin wrote: > >> On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: > >>> On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: > >>>>> There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` > >>>>> would be create in the callback function. But the callback function could be > >>>>> called repeatedly with same `io_uring_cmd` instance as far as I know. > >>>>> > >>>>> But in c side, there is initialization step `io_uring_cmd_prep()`. > >>>>> How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte > >>>>> as flag in pdu for checking initialized also we should provide 31 bytes except > >>>>> a byte for the flag. > >>>>> > >>>> > >>>> That was a follow-up question of mine. Can´t we enforce zero-initialization > >>>> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. > >>>> > >>>> Hopefully this can be done as you've described above, but I don't want to over > >>>> extend my opinion on something I know nothing about. > >>> > >>> I need to add a commit that initialize pdu in prep step in next version. > >>> I'd like to get a comment from io_uring maintainer Jens. Thanks. > >>> > >>> If we could initialize (filling zero) in prep step, How about casting issue? > >>> Driver still needs to cast array to its private struct in unsafe? > >> > >> We still would have the casting issue. > >> > >> Can't we do the following: > >> > >> * Add a new associated type to `MiscDevice` called `IoUringPdu` that > >> has to implement `Default` and have a size of at most 32 bytes. > >> * make `IoUringCmd` generic > >> * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` > >> * initialize the private data to be `IoUringPdu::default()` when we > >> create the `IoUringCmd` object. > > > > `uring_cmd` could be called multiple times. So we can't initialize > > in that time. I don't understand that how can we cast [u8; 32] to > > `IoUringPdu` safely. It seems that casting can't help to use unsafe. > > I think best way is that just return zerod `&mut [u8; 32]` and > > each driver implements safe serde logic for its private data. > > > > Again, can´t we use FromBytes for this? Agreed, we need FromBytes for read_pdu and AsBytes for write_pdu. I'll reference dma code for next version. Thanks, Sidong > >
> On 12 Aug 2025, at 10:56, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > On Tue, Aug 12, 2025 at 09:43:56AM -0300, Daniel Almeida wrote: >> >> >>> On 12 Aug 2025, at 09:19, Sidong Yang <sidong.yang@furiosa.ai> wrote: >>> >>> On Tue, Aug 12, 2025 at 10:34:56AM +0200, Benno Lossin wrote: >>>> On Mon Aug 11, 2025 at 4:50 PM CEST, Sidong Yang wrote: >>>>> On Mon, Aug 11, 2025 at 09:44:22AM -0300, Daniel Almeida wrote: >>>>>>> There is `uring_cmd` callback in `file_operation` at c side. `Pin<&mut IoUringCmd>` >>>>>>> would be create in the callback function. But the callback function could be >>>>>>> called repeatedly with same `io_uring_cmd` instance as far as I know. >>>>>>> >>>>>>> But in c side, there is initialization step `io_uring_cmd_prep()`. >>>>>>> How about fill zero pdu in `io_uring_cmd_prep()`? And we could assign a byte >>>>>>> as flag in pdu for checking initialized also we should provide 31 bytes except >>>>>>> a byte for the flag. >>>>>>> >>>>>> >>>>>> That was a follow-up question of mine. Can´t we enforce zero-initialization >>>>>> in C to get rid of this MaybeUninit? Uninitialized data is just bad in general. >>>>>> >>>>>> Hopefully this can be done as you've described above, but I don't want to over >>>>>> extend my opinion on something I know nothing about. >>>>> >>>>> I need to add a commit that initialize pdu in prep step in next version. >>>>> I'd like to get a comment from io_uring maintainer Jens. Thanks. >>>>> >>>>> If we could initialize (filling zero) in prep step, How about casting issue? >>>>> Driver still needs to cast array to its private struct in unsafe? >>>> >>>> We still would have the casting issue. >>>> >>>> Can't we do the following: >>>> >>>> * Add a new associated type to `MiscDevice` called `IoUringPdu` that >>>> has to implement `Default` and have a size of at most 32 bytes. >>>> * make `IoUringCmd` generic >>>> * make `MiscDevice::uring_cmd` take `Pin<&mut IoUringCmd<Self::IoUringPdu>>` >>>> * initialize the private data to be `IoUringPdu::default()` when we >>>> create the `IoUringCmd` object. >>> >>> `uring_cmd` could be called multiple times. So we can't initialize >>> in that time. I don't understand that how can we cast [u8; 32] to >>> `IoUringPdu` safely. It seems that casting can't help to use unsafe. >>> I think best way is that just return zerod `&mut [u8; 32]` and Also, I agree about zeroing out the array, let’s try to not have this MaybeUninit thing here if possible. >>> each driver implements safe serde logic for its private data. >>> >> >> Again, can´t we use FromBytes for this? > > Agreed, we need FromBytes for read_pdu and AsBytes for write_pdu. I'll reference > dma code for next version. > > Thanks, > Sidong >> >>
On Fri, Aug 01, 2025 at 10:48:40AM -0300, Daniel Almeida wrote: Hi Daniel, > Hi Sidong, > > > On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > > > This patch introduces rust abstraction for io-uring sqe, cmd. IoUringSqe > > abstracts io_uring_sqe and it has cmd_data(). and IoUringCmd is > > abstraction for io_uring_cmd. From this, user can get cmd_op, flags, > > pdu and also sqe. > > IMHO you need to expand this substantially. > > Instead of a very brief discussion of *what* you're doing, you need to explain > *why* you're doing this and how this patch fits with the overall plan that you > have in mind. It seems that it's hard to explain *why* deeply. But I'll try it. > > Also, for the sake of reviewers, try to at least describe the role of all the > types you've mentioned. Okay, I'll add some detailed descrption for all types like IoUringCmd. > > > > > Signed-off-by: Sidong Yang <sidong.yang@furiosa.ai> > > --- > > rust/kernel/io_uring.rs | 183 ++++++++++++++++++++++++++++++++++++++++ > > rust/kernel/lib.rs | 1 + > > 2 files changed, 184 insertions(+) > > create mode 100644 rust/kernel/io_uring.rs > > > > diff --git a/rust/kernel/io_uring.rs b/rust/kernel/io_uring.rs > > new file mode 100644 > > index 000000000000..0acdf3878247 > > --- /dev/null > > +++ b/rust/kernel/io_uring.rs > > @@ -0,0 +1,183 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +// Copyright (C) 2025 Furiosa AI. > > + > > Perhaps this instead [0]. Thanks, I'll change it with the format. > > > > +//! IoUring command and submission queue entry abstractions. > > Maybe expand this just a little bit as well. Okay. > > > +//! > > +//! C headers: [`include/linux/io_uring/cmd.h`](srctree/include/linux/io_uring/cmd.h) and > > +//! [`include/linux/io_uring/io_uring.h`](srctree/include/linux/io_uring/io_uring.h) > > + > > +use core::{mem::MaybeUninit, pin::Pin, ptr::addr_of_mut}; > > + > > +use crate::{fs::File, types::Opaque}; > > + > > +/// A Rust abstraction for the Linux kernel's `io_uring_cmd` structure. > > Is there a link for io_uring_cmd that you can use here? Yes, I'll add a link for the struct. > > > +/// > > +/// This structure is a safe, opaque wrapper around the raw C `io_uring_cmd` > > +/// binding from the Linux kernel. It represents a command structure used > > +/// in io_uring operations within the kernel. > > Perhaps backticks on "io_uring" ? Thanks. > > > +/// > > +/// # Type Safety > > +/// > > +/// The `#[repr(transparent)]` attribute ensures that this wrapper has > > +/// the same memory layout as the underlying `io_uring_cmd` structure, > > +/// allowing it to be safely transmuted between the two representations. > > +/// > > +/// # Fields > > +/// > > +/// * `inner` - An opaque wrapper containing the actual `io_uring_cmd` data. > > +/// The `Opaque` type prevents direct access to the internal > > +/// structure fields, ensuring memory safety and encapsulation. > > Place this on top of the field itself please. Also, I don´t think you need > this at all because you don't need to explain the Opaque type, as it's > extensively used in the kernel crate. Okay, I'll move this comments to code with the field. > > > +/// > > +/// # Usage > > I don´t think you need this. Okay. > > > +/// > > +/// This type is used internally by the io_uring subsystem to manage > > +/// asynchronous I/O commands. It is typically accessed through a pinned > > +/// mutable reference: `Pin<&mut IoUringCmd>`. The pinning ensures that > > +/// the structure remains at a fixed memory location, which is required > > +/// for safe interaction with the kernel's io_uring infrastructure. > > I don´t think you need anything other than: Thanks, It's too verbose. I'll rewrite this. > > > +/// This type is used internally by the io_uring subsystem to manage > > +/// asynchronous I/O commands. > > Specifically, you don´t need to say this: > > > The pinning ensures that > > +/// the structure remains at a fixed memory location, > > > > > +/// > > +/// Users typically receive this type as an argument in the `file_operations::uring_cmd()` > > +/// callback function, where they can access and manipulate the io_uring command > > +/// data for custom file operations. > > +/// > > +/// This type should not be constructed or manipulated directly by > > +/// kernel module developers. > > Well, this is pub, so the reality is that it can be manipulated directly > through whatever public API it offers. > Agreed, We should provide safe pub fns that it doesn't corrupt the inner state. > > +#[repr(transparent)] > > +pub struct IoUringCmd { > > + inner: Opaque<bindings::io_uring_cmd>, > > +} > > + > > +impl IoUringCmd { > > + /// Returns the cmd_op with associated with the io_uring_cmd. > > Backticks Thanks. > > > + #[inline] > > + pub fn cmd_op(&self) -> u32 { > > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > > Not sure I understand what you´re trying to say here. Perhaps add an > invariant saying that `self.inner` always points to a valid > `bindings::io_uring_cmd` and mention that here instead. Thanks, I'll add INVARIANT comment for self.inner and reference it in this comment. > > > + unsafe { (*self.inner.get()).cmd_op } > > + } > > + > > + /// Returns the flags with associated with the io_uring_cmd. > > + #[inline] > > + pub fn flags(&self) -> u32 { > > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > > + unsafe { (*self.inner.get()).flags } > > + } > > + > > + /// Returns the ref pdu for free use. > > I have no idea what "ref pdu" is. You need to describe these acronyms. Okay. Thanks. > > > + #[inline] > > + pub fn pdu(&mut self) -> &mut MaybeUninit<[u8; 32]> { > > Why MaybeUninit? Also, this is a question for others, but I don´t think > that `u8`s can ever be uninitialized as all byte values are valid for `u8`. > > > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > > + let inner = unsafe { &mut *self.inner.get() }; > > + let ptr = addr_of_mut!(inner.pdu) as *mut MaybeUninit<[u8; 32]>; > > &raw mut I didn't know about this. Thanks. > > > + > > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > > + unsafe { &mut *ptr } > > + } > > + > > + /// Constructs a new `IoUringCmd` from a raw `io_uring_cmd` > > [`IoUringCmd`] > > By the way, always build the docs and see if they look nice. Thanks. > > > + /// > > + /// # Safety > > + /// > > + /// The caller must guarantee that: > > + /// - The pointer `ptr` is not null and points to a valid `bindings::io_uring_cmd`. > > + /// - The memory pointed to by `ptr` remains valid for the duration of the returned reference's lifetime `'a`. > > + /// - The memory will not be moved or freed while the returned `Pin<&mut IoUringCmd>` is alive. > > This returns a wrapper over a mutable reference. You must mention Rust´s aliasing rules here. Okay. Thanks. > > > + #[inline] > > + pub unsafe fn from_raw<'a>(ptr: *mut bindings::io_uring_cmd) -> Pin<&'a mut IoUringCmd> { > > + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the > > + // duration of 'a. The cast is okay because `IoUringCmd` is `repr(transparent)` and has the > > + // same memory layout as `bindings::io_uring_cmd`. The returned `Pin` ensures that the object > > + // cannot be moved, which is required because the kernel may hold pointers to this memory > > + // location and moving it would invalidate those pointers. > > Please break this into multiple paragraphs. Thanks. > > > + unsafe { Pin::new_unchecked(&mut *ptr.cast()) } > > + } > > + > > + /// Returns the file that referenced by uring cmd self. > > + #[inline] > > + pub fn file(&self) -> &File { > > + // SAFETY: The call guarantees that the `self.inner` is not dangling and stays valid > > + let file = unsafe { (*self.inner.get()).file }; > > + // SAFETY: The call guarantees that `file` points valid file. > > + unsafe { File::from_raw_file(file) } > > + } > > + > > + /// Returns a reference to the uring cmd's SQE. > > Please define what `SQE` means. Add links if possible. Okay. Thanks. > > > + #[inline] > > + pub fn sqe(&self) -> &IoUringSqe { > > + // SAFETY: The call guarantees that the `self.inner` is not dangling and stays valid > > + let sqe = unsafe { (*self.inner.get()).sqe }; > > + // SAFETY: The call guarantees that the `sqe` points valid io_uring_sqe. > > Backticks Thanks. > > > + unsafe { IoUringSqe::from_raw(sqe) } > > + } > > + > > + /// Called by consumers of io_uring_cmd, if they originally returned -EIOCBQUEUED upon receiving the command > > Backticks Thanks. > > > + #[inline] > > + pub fn done(self: Pin<&mut IoUringCmd>, ret: isize, res2: u64, issue_flags: u32) { > > The arguments are cryptic here. Please let us know what they do. Thanks I'll add description for each arguments. > > > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > > + unsafe { > > + bindings::io_uring_cmd_done(self.inner.get(), ret, res2, issue_flags); > > + } > > + } > > +} > > + > > +/// A Rust abstraction for the Linux kernel's `io_uring_sqe` structure. > > +/// > > +/// This structure is a safe, opaque wrapper around the raw C `io_uring_sqe` > > +/// binding from the Linux kernel. It represents a Submission Queue Entry > > Ah, SQE == Submission Queue Entry. Is there a link for this? I'll add a link for this. > > > +/// used in io_uring operations within the kernel. > > +/// > > +/// # Type Safety > > +/// > > +/// The `#[repr(transparent)]` attribute ensures that this wrapper has > > +/// the same memory layout as the underlying `io_uring_sqe` structure, > > +/// allowing it to be safely transmuted between the two representations. > > +/// > > +/// # Fields > > +/// > > +/// * `inner` - An opaque wrapper containing the actual `io_uring_sqe` data. > > +/// The `Opaque` type prevents direct access to the internal > > +/// structure fields, ensuring memory safety and encapsulation. > > +/// > > +/// # Usage > > +/// > > +/// This type represents a submission queue entry that describes an I/O > > +/// operation to be executed by the io_uring subsystem. It contains > > +/// information such as the operation type, file descriptor, buffer > > +/// pointers, and other operation-specific data. > > This description is very good :) Thanks. :) > > > +/// > > +/// Users can obtain this type from `IoUringCmd::sqe()` method, which > > +/// extracts the submission queue entry associated with a command. > > [`IoUringCmd::sqe`] Thanks. > > > +/// > > +/// This type should not be constructed or manipulated directly by > > +/// kernel module developers. > > Again, this is pub and can be freely manipulated through whatever > public API it offers. Okay. > > > +#[repr(transparent)] > > +pub struct IoUringSqe { > > + inner: Opaque<bindings::io_uring_sqe>, > > +} > > + > > +impl<'a> IoUringSqe { > > Why is this `a here? It seems that I can delete 'a here. > > > + /// Returns the cmd_data with associated with the io_uring_sqe. > > + /// This function returns 16 byte array. We don't support IORING_SETUP_SQE128 for now. > > + pub fn cmd_data(&'a self) -> &'a [Opaque<u8>] { > > This is automatically placed by the compiler. See the lifetime elision rules > [1]. > Thanks. > Also why does this return a reference to an array of Opaque<u8>? > > You can return a &[u8] here if you can prove that this reference is legal given > Rust's aliasing rules. If you can't, then you have to look at what the DMA > allocator code is doing and use that as an example, i.e.: have a look at the > dma_read and dma_write macros and mark the function that returns the slice as > "unsafe". > Okay. It's better to use &[u8]. > > + // SAFETY: The call guarantees that `self.inner` is not dangling and stays valid > > + let cmd = unsafe { (*self.inner.get()).__bindgen_anon_6.cmd.as_ref() }; > > What do you mean by "the call" ? Same in all other places where this sentence is used. Thanks, it should be dereferecing than call. > > + > > + // SAFETY: The call guarantees that `cmd` is not dangling and stays valid > > + unsafe { core::slice::from_raw_parts(cmd.as_ptr() as *const Opaque<u8>, 16) } > > + } > > + > > + /// Constructs a new `IoUringSqe` from a raw `io_uring_sqe` > > + /// > > + /// # Safety > > + /// > > + /// The caller must guarantee that: > > + /// - The pointer `ptr` is not null and points to a valid `bindings::io_uring_sqe`. > > + /// - The memory pointed to by `ptr` remains valid for the duration of the returned reference's lifetime `'a`. > > Must mention Rust´s aliasing rules here. Okay. Thanks. > > > + #[inline] > > + pub unsafe fn from_raw(ptr: *const bindings::io_uring_sqe) -> &'a IoUringSqe { > > + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the > > + // duration of 'a. The cast is okay because `IoUringSqe` is `repr(transparent)` and has the > > + // same memory layout as `bindings::io_uring_sqe`. > > + unsafe { &*ptr.cast() } > > + } > > +} > > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > > index 6b4774b2b1c3..fb310e78d51d 100644 > > --- a/rust/kernel/lib.rs > > +++ b/rust/kernel/lib.rs > > @@ -80,6 +80,7 @@ > > pub mod fs; > > pub mod init; > > pub mod io; > > +pub mod io_uring; > > pub mod ioctl; > > pub mod jump_label; > > #[cfg(CONFIG_KUNIT)] > > -- > > 2.43.0 > > > > > > [0] https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Properties/copyrightText/ > [1] https://doc.rust-lang.org/reference/lifetime-elision.html
Hi Sidon, > On 5 Aug 2025, at 00:39, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > On Fri, Aug 01, 2025 at 10:48:40AM -0300, Daniel Almeida wrote: > > Hi Daniel, > >> Hi Sidong, >> >>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: >>> >>> This patch introduces rust abstraction for io-uring sqe, cmd. IoUringSqe >>> abstracts io_uring_sqe and it has cmd_data(). and IoUringCmd is >>> abstraction for io_uring_cmd. From this, user can get cmd_op, flags, >>> pdu and also sqe. >> >> IMHO you need to expand this substantially. >> >> Instead of a very brief discussion of *what* you're doing, you need to explain >> *why* you're doing this and how this patch fits with the overall plan that you >> have in mind. > > It seems that it's hard to explain *why* deeply. But I'll try it. Just to be clear, you don’t need to go deep enough in the sense that you’re basically rewriting the documentation that is already available in C, but you do need to provide an overview of how things fit together, otherwise we're left to connect the dots. Have a look at the I2C series [0]. That is all you need to do IMHO. I’d use that as an example. [0]: https://lore.kernel.org/rust-for-linux/2D1DE1BC-13FB-4563-BE11-232C755B5117@collabora.com/T/#t — Daniel
On Tue, Aug 05, 2025 at 10:02:18AM -0300, Daniel Almeida wrote: > Hi Sidon, > > > On 5 Aug 2025, at 00:39, Sidong Yang <sidong.yang@furiosa.ai> wrote: > > > > On Fri, Aug 01, 2025 at 10:48:40AM -0300, Daniel Almeida wrote: > > > > Hi Daniel, > > > >> Hi Sidong, > >> > >>> On 27 Jul 2025, at 12:03, Sidong Yang <sidong.yang@furiosa.ai> wrote: > >>> > >>> This patch introduces rust abstraction for io-uring sqe, cmd. IoUringSqe > >>> abstracts io_uring_sqe and it has cmd_data(). and IoUringCmd is > >>> abstraction for io_uring_cmd. From this, user can get cmd_op, flags, > >>> pdu and also sqe. > >> > >> IMHO you need to expand this substantially. > >> > >> Instead of a very brief discussion of *what* you're doing, you need to explain > >> *why* you're doing this and how this patch fits with the overall plan that you > >> have in mind. > > > > It seems that it's hard to explain *why* deeply. But I'll try it. > > Just to be clear, you don´t need to go deep enough in the sense that > you´re basically rewriting the documentation that is already available in > C, but you do need to provide an overview of how things fit together, otherwise > we're left to connect the dots. > > Have a look at the I2C series [0]. That is all you need to do IMHO. > > I´d use that as an example. > > [0]: https://lore.kernel.org/rust-for-linux/2D1DE1BC-13FB-4563-BE11-232C755B5117@collabora.com/T/#t Thanks, I'll rewrite the cover letter and commit messages that reference the example. Thanks, Sidong > > - Daniel
© 2016 - 2025 Red Hat, Inc.