[PATCH v4] rust: drm: Drop the use of Opaque for ioctl arguments

Boqun Feng boqun.feng at gmail.com
Thu Jun 26 03:42:35 UTC 2025


Hi Beata,

On Wed, Jun 25, 2025 at 10:13:33AM +0200, Beata Michalska wrote:
> With the Opaque<T>, the expectations are that Rust should not
> make any assumptions on the layout or invariants of the wrapped
> C types. That runs rather counter to ioctl arguments, which must
> adhere to certain data-layout constraits. By using Opaque<T>,
> ioctl handlers are forced to use unsafe code where none is acually
> needed. This adds needless complexity and maintenance overhead,
> brining no safety benefits.
> Drop the use of Opaque for ioctl arguments as that is not the best
> fit here.
> 
> Signed-off-by: Beata Michalska <beata.michalska at arm.com>
> Acked-by: Danilo Krummrich <dakr at kernel.org>
> ---

Appreciate it if you could put a change log here mentioning changes
between each version.

>  drivers/gpu/drm/nova/file.rs | 23 ++++++--------
>  drivers/gpu/drm/nova/nova.rs |  1 -
>  drivers/gpu/drm/nova/uapi.rs | 61 ------------------------------------
>  rust/kernel/drm/ioctl.rs     | 11 ++++---
>  4 files changed, 16 insertions(+), 80 deletions(-)
>  delete mode 100644 drivers/gpu/drm/nova/uapi.rs
> 
> diff --git a/drivers/gpu/drm/nova/file.rs b/drivers/gpu/drm/nova/file.rs
> index 7e59a34b830d..7e7d4e2de2fb 100644
> --- a/drivers/gpu/drm/nova/file.rs
> +++ b/drivers/gpu/drm/nova/file.rs
> @@ -2,13 +2,11 @@
>  
>  use crate::driver::{NovaDevice, NovaDriver};
>  use crate::gem::NovaObject;
> -use crate::uapi::{GemCreate, GemInfo, Getparam};
>  use kernel::{
>      alloc::flags::*,
>      drm::{self, gem::BaseObject},
>      pci,
>      prelude::*,
> -    types::Opaque,
>      uapi,
>  };
>  
> @@ -26,20 +24,19 @@ impl File {
>      /// IOCTL: get_param: Query GPU / driver metadata.
>      pub(crate) fn get_param(
>          dev: &NovaDevice,
> -        getparam: &Opaque<uapi::drm_nova_getparam>,
> +        getparam: &mut uapi::drm_nova_getparam,
>          _file: &drm::File<File>,
>      ) -> Result<u32> {
>          let adev = &dev.adev;
>          let parent = adev.parent().ok_or(ENOENT)?;
>          let pdev: &pci::Device = parent.try_into()?;
> -        let getparam: &Getparam = getparam.into();
>  
> -        let value = match getparam.param() as u32 {
> +        let value = match getparam.param as u32 {
>              uapi::NOVA_GETPARAM_VRAM_BAR_SIZE => pdev.resource_len(1)?,
>              _ => return Err(EINVAL),
>          };
>  
> -        getparam.set_value(value);
> +        getparam.value = value;
>  
>          Ok(0)
>      }
> @@ -47,13 +44,12 @@ pub(crate) fn get_param(
>      /// IOCTL: gem_create: Create a new DRM GEM object.
>      pub(crate) fn gem_create(
>          dev: &NovaDevice,
> -        req: &Opaque<uapi::drm_nova_gem_create>,
> +        req: &mut uapi::drm_nova_gem_create,
>          file: &drm::File<File>,
>      ) -> Result<u32> {
> -        let req: &GemCreate = req.into();
> -        let obj = NovaObject::new(dev, req.size().try_into()?)?;
> +        let obj = NovaObject::new(dev, req.size.try_into()?)?;
>  
> -        req.set_handle(obj.create_handle(file)?);
> +        req.handle = obj.create_handle(file)?;
>  
>          Ok(0)
>      }
> @@ -61,13 +57,12 @@ pub(crate) fn gem_create(
>      /// IOCTL: gem_info: Query GEM metadata.
>      pub(crate) fn gem_info(
>          _dev: &NovaDevice,
> -        req: &Opaque<uapi::drm_nova_gem_info>,
> +        req: &mut uapi::drm_nova_gem_info,
>          file: &drm::File<File>,
>      ) -> Result<u32> {
> -        let req: &GemInfo = req.into();
> -        let bo = NovaObject::lookup_handle(file, req.handle())?;
> +        let bo = NovaObject::lookup_handle(file, req.handle)?;
>  
> -        req.set_size(bo.size().try_into()?);
> +        req.size = bo.size().try_into()?;
>  
>          Ok(0)
>      }
> diff --git a/drivers/gpu/drm/nova/nova.rs b/drivers/gpu/drm/nova/nova.rs
> index 902876aa14d1..730598defe04 100644
> --- a/drivers/gpu/drm/nova/nova.rs
> +++ b/drivers/gpu/drm/nova/nova.rs
> @@ -5,7 +5,6 @@
>  mod driver;
>  mod file;
>  mod gem;
> -mod uapi;
>  
>  use crate::driver::NovaDriver;
>  
> diff --git a/drivers/gpu/drm/nova/uapi.rs b/drivers/gpu/drm/nova/uapi.rs
> deleted file mode 100644
> index eb228a58d423..000000000000
> --- a/drivers/gpu/drm/nova/uapi.rs
> +++ /dev/null
> @@ -1,61 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -
> -use kernel::uapi;
> -
> -// TODO Work out some common infrastructure to avoid boilerplate code for uAPI abstractions.
> -
> -macro_rules! define_uapi_abstraction {
> -    ($name:ident <= $inner:ty) => {
> -        #[repr(transparent)]
> -        pub struct $name(::kernel::types::Opaque<$inner>);
> -
> -        impl ::core::convert::From<&::kernel::types::Opaque<$inner>> for &$name {
> -            fn from(value: &::kernel::types::Opaque<$inner>) -> Self {
> -                // SAFETY: `Self` is a transparent wrapper of `$inner`.
> -                unsafe { ::core::mem::transmute(value) }
> -            }
> -        }
> -    };
> -}
> -
> -define_uapi_abstraction!(Getparam <= uapi::drm_nova_getparam);
> -
> -impl Getparam {
> -    pub fn param(&self) -> u64 {
> -        // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`.
> -        unsafe { (*self.0.get()).param }
> -    }
> -
> -    pub fn set_value(&self, v: u64) {
> -        // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`.
> -        unsafe { (*self.0.get()).value = v };
> -    }
> -}
> -
> -define_uapi_abstraction!(GemCreate <= uapi::drm_nova_gem_create);
> -
> -impl GemCreate {
> -    pub fn size(&self) -> u64 {
> -        // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`.
> -        unsafe { (*self.0.get()).size }
> -    }
> -
> -    pub fn set_handle(&self, handle: u32) {
> -        // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`.
> -        unsafe { (*self.0.get()).handle = handle };
> -    }
> -}
> -
> -define_uapi_abstraction!(GemInfo <= uapi::drm_nova_gem_info);
> -
> -impl GemInfo {
> -    pub fn handle(&self) -> u32 {
> -        // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`.
> -        unsafe { (*self.0.get()).handle }
> -    }
> -
> -    pub fn set_size(&self, size: u64) {
> -        // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`.
> -        unsafe { (*self.0.get()).size = size };
> -    }
> -}
> diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs
> index 445639404fb7..3425a835f9cd 100644
> --- a/rust/kernel/drm/ioctl.rs
> +++ b/rust/kernel/drm/ioctl.rs
> @@ -83,7 +83,7 @@ pub mod internal {
>  ///
>  /// ```ignore
>  /// fn foo(device: &kernel::drm::Device<Self>,
> -///        data: &Opaque<uapi::argument_type>,
> +///        data: &mut uapi::argument_type,
>  ///        file: &kernel::drm::File<Self::File>,
>  /// ) -> Result<u32>
>  /// ```
> @@ -138,9 +138,12 @@ pub mod internal {
>                              // SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we
>                              // asserted above matches the size of this type, and all bit patterns of
>                              // UAPI structs must be valid.
> -                            let data = unsafe {
> -                                &*(raw_data as *const $crate::types::Opaque<$crate::uapi::$struct>)
> -                            };
> +                            // The `ioctl` argument is exclusively owned by the handler
> +                            // and guaranteed by the C implementation (`drm_ioctl()`) to remain
> +                            // valid for the entire lifetime of the reference taken here.
> +                            // There is no concurrent access or aliasing; no other references
> +                            // to this object exist during this call.
> +                            let data = unsafe { &mut *(raw_data as *mut $crate::uapi::$struct) };

"ptr as ptr" should be avoided, see:

	https://lore.kernel.org/rust-for-linux/20250615-ptr-as-ptr-v12-1-f43b024581e8@gmail.com/

so probably

    let data = unsafe { &mut *(raw_data.cast()) };

?

With that fixed, feel free to add:

Reviewed-by: Boqun Feng <boqun.feng at gmail.com>

Regards,
Boqun

>                              // SAFETY: This is just the DRM file structure
>                              let file = unsafe { $crate::drm::File::as_ref(raw_file) };
>  
> -- 
> 2.25.1
> 


More information about the dri-devel mailing list