Merge tag 'topic/device-context-2025-04-17' into nova-next

Introduce "Bound" device context

Introduce the "Bound" device context, such that it can be ensured to only
ever pass a bound device to APIs that require this precondition. [1]

This tag exists to share the patches from [1] between multiple trees.

[1] https://lore.kernel.org/lkml/20250413173758.12068-1-dakr@kernel.org/
This commit is contained in:
Danilo Krummrich
2025-04-19 10:10:40 +02:00
5 changed files with 121 additions and 65 deletions

View File

@@ -9,7 +9,7 @@
str::CStr,
types::{ARef, Opaque},
};
use core::{fmt, ptr};
use core::{fmt, marker::PhantomData, ptr};
#[cfg(CONFIG_PRINTK)]
use crate::c_str;
@@ -42,7 +42,7 @@
/// `bindings::device::release` is valid to be called from any thread, hence `ARef<Device>` can be
/// dropped from any thread.
#[repr(transparent)]
pub struct Device(Opaque<bindings::device>);
pub struct Device<Ctx: DeviceContext = Normal>(Opaque<bindings::device>, PhantomData<Ctx>);
impl Device {
/// Creates a new reference-counted abstraction instance of an existing `struct device` pointer.
@@ -59,7 +59,9 @@ pub unsafe fn get_device(ptr: *mut bindings::device) -> ARef<Self> {
// SAFETY: By the safety requirements ptr is valid
unsafe { Self::as_ref(ptr) }.into()
}
}
impl<Ctx: DeviceContext> Device<Ctx> {
/// Obtain the raw `struct device *`.
pub(crate) fn as_raw(&self) -> *mut bindings::device {
self.0.get()
@@ -189,6 +191,11 @@ pub fn property_present(&self, name: &CStr) -> bool {
}
}
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
// argument.
kernel::impl_device_context_deref!(unsafe { Device });
kernel::impl_device_context_into_aref!(Device);
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
fn inc_ref(&self) {
@@ -225,16 +232,95 @@ pub trait DeviceContext: private::Sealed {}
/// any of the bus callbacks, such as `probe()`.
pub struct Core;
/// The [`Bound`] context is the context of a bus specific device reference when it is guaranteed to
/// be bound for the duration of its lifetime.
pub struct Bound;
mod private {
pub trait Sealed {}
impl Sealed for super::Bound {}
impl Sealed for super::Core {}
impl Sealed for super::Normal {}
}
impl DeviceContext for Bound {}
impl DeviceContext for Core {}
impl DeviceContext for Normal {}
/// # Safety
///
/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
/// generic argument of `$device`.
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_device_context_deref {
(unsafe { $device:ident, $src:ty => $dst:ty }) => {
impl ::core::ops::Deref for $device<$src> {
type Target = $device<$dst>;
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
// CAST: `$device<$src>` and `$device<$dst>` transparently wrap the same type by the
// safety requirement of the macro.
let ptr = ptr.cast::<Self::Target>();
// SAFETY: `ptr` was derived from `&self`.
unsafe { &*ptr }
}
}
};
}
/// Implement [`core::ops::Deref`] traits for allowed [`DeviceContext`] conversions of a (bus
/// specific) device.
///
/// # Safety
///
/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
/// generic argument of `$device`.
#[macro_export]
macro_rules! impl_device_context_deref {
(unsafe { $device:ident }) => {
// SAFETY: This macro has the exact same safety requirement as
// `__impl_device_context_deref!`.
::kernel::__impl_device_context_deref!(unsafe {
$device,
$crate::device::Core => $crate::device::Bound
});
// SAFETY: This macro has the exact same safety requirement as
// `__impl_device_context_deref!`.
::kernel::__impl_device_context_deref!(unsafe {
$device,
$crate::device::Bound => $crate::device::Normal
});
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_device_context_into_aref {
($src:ty, $device:tt) => {
impl ::core::convert::From<&$device<$src>> for $crate::types::ARef<$device> {
fn from(dev: &$device<$src>) -> Self {
(&**dev).into()
}
}
};
}
/// Implement [`core::convert::From`], such that all `&Device<Ctx>` can be converted to an
/// `ARef<Device>`.
#[macro_export]
macro_rules! impl_device_context_into_aref {
($device:tt) => {
::kernel::__impl_device_context_into_aref!($crate::device::Core, $device);
::kernel::__impl_device_context_into_aref!($crate::device::Bound, $device);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! dev_printk {

View File

@@ -8,7 +8,7 @@
use crate::{
alloc::Flags,
bindings,
device::Device,
device::{Bound, Device},
error::{Error, Result},
ffi::c_void,
prelude::*,
@@ -45,7 +45,7 @@ struct DevresInner<T> {
/// # Example
///
/// ```no_run
/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::{Io, IoRaw}};
/// # use kernel::{bindings, c_str, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}};
/// # use core::ops::Deref;
///
/// // See also [`pci::Bar`] for a real example.
@@ -83,13 +83,10 @@ struct DevresInner<T> {
/// unsafe { Io::from_raw(&self.0) }
/// }
/// }
/// # fn no_run() -> Result<(), Error> {
/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
/// # let dev = unsafe { Device::get_device(core::ptr::null_mut()) };
///
/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> {
/// // SAFETY: Invalid usage for example purposes.
/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
/// let devres = Devres::new(dev, iomem, GFP_KERNEL)?;
///
/// let res = devres.try_access().ok_or(ENXIO)?;
/// res.write8(0x42, 0x0);
@@ -99,7 +96,7 @@ struct DevresInner<T> {
pub struct Devres<T>(Arc<DevresInner<T>>);
impl<T> DevresInner<T> {
fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
fn new(dev: &Device<Bound>, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
let inner = Arc::pin_init(
pin_init!( DevresInner {
dev: dev.into(),
@@ -171,7 +168,7 @@ fn remove_action(this: &Arc<Self>) {
impl<T> Devres<T> {
/// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the
/// returned `Devres` instance' `data` will be revoked once the device is detached.
pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
pub fn new(dev: &Device<Bound>, data: T, flags: Flags) -> Result<Self> {
let inner = DevresInner::new(dev, data, flags)?;
Ok(Devres(inner))
@@ -179,7 +176,7 @@ pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
/// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data`
/// is owned by devres and will be revoked / dropped, once the device is detached.
pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result {
pub fn new_foreign_owned(dev: &Device<Bound>, data: T, flags: Flags) -> Result {
let _ = DevresInner::new(dev, data, flags)?;
Ok(())

View File

@@ -6,7 +6,7 @@
use crate::{
bindings, build_assert,
device::Device,
device::{Bound, Device},
error::code::*,
error::Result,
transmute::{AsBytes, FromBytes},
@@ -22,10 +22,10 @@
/// # Examples
///
/// ```
/// use kernel::device::Device;
/// # use kernel::device::{Bound, Device};
/// use kernel::dma::{attrs::*, CoherentAllocation};
///
/// # fn test(dev: &Device) -> Result {
/// # fn test(dev: &Device<Bound>) -> Result {
/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN;
/// let c: CoherentAllocation<u64> =
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?;
@@ -143,16 +143,16 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
/// # Examples
///
/// ```
/// use kernel::device::Device;
/// # use kernel::device::{Bound, Device};
/// use kernel::dma::{attrs::*, CoherentAllocation};
///
/// # fn test(dev: &Device) -> Result {
/// # fn test(dev: &Device<Bound>) -> Result {
/// let c: CoherentAllocation<u64> =
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
/// # Ok::<(), Error>(()) }
/// ```
pub fn alloc_attrs(
dev: &Device,
dev: &Device<Bound>,
count: usize,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
@@ -194,7 +194,7 @@ pub fn alloc_attrs(
/// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the
/// `dma_attrs` is 0 by default.
pub fn alloc_coherent(
dev: &Device,
dev: &Device<Bound>,
count: usize,
gfp_flags: kernel::alloc::Flags,
) -> Result<CoherentAllocation<T>> {

View File

@@ -360,11 +360,13 @@ fn deref(&self) -> &Self::Target {
}
}
impl Device {
impl<Ctx: device::DeviceContext> Device<Ctx> {
fn as_raw(&self) -> *mut bindings::pci_dev {
self.0.get()
}
}
impl Device {
/// Returns the PCI vendor ID.
pub fn vendor_id(&self) -> u16 {
// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
@@ -388,7 +390,9 @@ pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
// - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`.
Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
}
}
impl Device<device::Bound> {
/// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks
/// can be performed on compile time for offsets (plus the requested type size) < SIZE.
pub fn iomap_region_sized<const SIZE: usize>(
@@ -422,25 +426,10 @@ pub fn set_master(&self) {
}
}
impl Deref for Device<device::Core> {
type Target = Device;
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
// CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::pci_dev>`.
let ptr = ptr.cast::<Device>();
// SAFETY: `ptr` was derived from `&self`.
unsafe { &*ptr }
}
}
impl From<&Device<device::Core>> for ARef<Device> {
fn from(dev: &Device<device::Core>) -> Self {
(&**dev).into()
}
}
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
// argument.
kernel::impl_device_context_deref!(unsafe { Device });
kernel::impl_device_context_into_aref!(Device);
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
@@ -455,8 +444,8 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
}
}
impl AsRef<device::Device> for Device {
fn as_ref(&self) -> &device::Device {
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
fn as_ref(&self) -> &device::Device<Ctx> {
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
// `struct pci_dev`.
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };

View File

@@ -10,13 +10,12 @@
of,
prelude::*,
str::CStr,
types::{ARef, ForeignOwnable, Opaque},
types::{ForeignOwnable, Opaque},
ThisModule,
};
use core::{
marker::PhantomData,
ops::Deref,
ptr::{addr_of_mut, NonNull},
};
@@ -184,31 +183,16 @@ pub struct Device<Ctx: device::DeviceContext = device::Normal>(
PhantomData<Ctx>,
);
impl Device {
impl<Ctx: device::DeviceContext> Device<Ctx> {
fn as_raw(&self) -> *mut bindings::platform_device {
self.0.get()
}
}
impl Deref for Device<device::Core> {
type Target = Device;
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
// CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::platform_device>`.
let ptr = ptr.cast::<Device>();
// SAFETY: `ptr` was derived from `&self`.
unsafe { &*ptr }
}
}
impl From<&Device<device::Core>> for ARef<Device> {
fn from(dev: &Device<device::Core>) -> Self {
(&**dev).into()
}
}
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
// argument.
kernel::impl_device_context_deref!(unsafe { Device });
kernel::impl_device_context_into_aref!(Device);
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
@@ -223,8 +207,8 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
}
}
impl AsRef<device::Device> for Device {
fn as_ref(&self) -> &device::Device {
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
fn as_ref(&self) -> &device::Device<Ctx> {
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
// `struct platform_device`.
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };