Skip to content

Commit

Permalink
Merge pull request torvalds#615 from wedsonaf/id-array
Browse files Browse the repository at this point in the history
rust: add `IdArray` and `IdTable`
  • Loading branch information
wedsonaf authored Jan 13, 2022
2 parents 6a0100f + c3f395b commit 26e876d
Show file tree
Hide file tree
Showing 2 changed files with 280 additions and 1 deletion.
277 changes: 276 additions & 1 deletion rust/kernel/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::{str::CStr, sync::Ref, Error, KernelModule, Result, ScopeGuard, ThisModule};
use alloc::{boxed::Box, vec::Vec};
use core::{cell::UnsafeCell, mem::MaybeUninit, ops::Deref, pin::Pin};
use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ops::Deref, pin::Pin};

/// A subsystem (e.g., PCI, Platform, Amba, etc.) that allows drivers to be written for it.
pub trait DriverOps {
Expand Down Expand Up @@ -162,6 +162,281 @@ impl<T: DriverOps> Drop for Registration<T> {
}
}

/// Conversion from a device id to a raw device id.
///
/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
/// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
///
/// # Safety
///
/// Implementers must ensure that:
/// * [`RawDeviceId::ZERO`] is actually a zeroed-out version of the raw device id.
/// * [`RawDeviceId::to_rawid`] stores `offset` in the context/data field of the raw device id so
/// that buses can recover the pointer to the data.
pub unsafe trait RawDeviceId {
/// The raw type that holds the device id.
///
/// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
type RawType: Copy;

/// A zeroed-out representation of the raw device id.
///
/// Id tables created from [`Self`] use [`Self::ZERO`] as the sentinel to indicate the end of
/// the table.
const ZERO: Self::RawType;

/// Converts an id into a raw id.
///
/// `offset` is the offset from the memory location where the raw device id is stored to the
/// location where its associated context information is stored. Implementations must store
/// this in the appropriate context/data field of the raw type.
fn to_rawid(&self, offset: isize) -> Self::RawType;
}

/// A zero-terminated device id array, followed by context data.
#[repr(C)]
pub struct IdArray<T: RawDeviceId, U, const N: usize> {
ids: [T::RawType; N],
sentinel: T::RawType,
id_infos: [Option<U>; N],
}

impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
/// Creates a new instance of the array.
///
/// The contents are derived from the given identifiers and context information.
pub const fn new(ids: [T; N], infos: [Option<U>; N]) -> Self
where
T: ~const RawDeviceId + Copy,
{
let mut array = Self {
ids: [T::ZERO; N],
sentinel: T::ZERO,
id_infos: infos,
};
let mut i = 0usize;
while i < N {
// SAFETY: Both pointers are within `array` (or one byte beyond), consequently they are
// derived from the same allocated object. We are using a `u8` pointer, whose size 1,
// so the pointers are necessarily 1-byte aligned.
let offset = unsafe {
(&array.id_infos[i] as *const _ as *const u8)
.offset_from(&array.ids[i] as *const _ as _)
};
array.ids[i] = ids[i].to_rawid(offset);
i += 1;
}
array
}

/// Returns an `IdTable` backed by `self`.
///
/// This is used to essentially erase the array size.
pub const fn as_table(&self) -> IdTable<'_, T, U> {
IdTable {
first: &self.ids[0],
_p: PhantomData,
}
}
}

/// A device id table.
///
/// The table is guaranteed to be zero-terminated and to be followed by an array of context data of
/// type `Option<U>`.
pub struct IdTable<'a, T: RawDeviceId, U> {
first: &'a T::RawType,
_p: PhantomData<&'a U>,
}

impl<T: RawDeviceId, U> const AsRef<T::RawType> for IdTable<'_, T, U> {
fn as_ref(&self) -> &T::RawType {
self.first
}
}

/// Counts the number of parenthesis-delimited, comma-separated items.
///
/// # Examples
///
/// ```
/// # use kernel::count_paren_items;
///
/// assert_eq!(0, count_paren_items!());
/// assert_eq!(1, count_paren_items!((A)));
/// assert_eq!(1, count_paren_items!((A),));
/// assert_eq!(2, count_paren_items!((A), (B)));
/// assert_eq!(2, count_paren_items!((A), (B),));
/// assert_eq!(3, count_paren_items!((A), (B), (C)));
/// assert_eq!(3, count_paren_items!((A), (B), (C),));
/// ```
#[macro_export]
macro_rules! count_paren_items {
(($($item:tt)*), $($remaining:tt)*) => { 1 + $crate::count_paren_items!($($remaining)*) };
(($($item:tt)*)) => { 1 };
() => { 0 };
}

/// Converts a comma-separated list of pairs into an array with the first element. That is, it
/// discards the second element of the pair.
///
/// Additionally, it automatically introduces a type if the first element is warpped in curly
/// braces, for example, if it's `{v: 10}`, it becomes `X { v: 10 }`; this is to avoid repeating
/// the type.
///
/// # Examples
///
/// ```
/// # use kernel::first_item;
///
/// #[derive(PartialEq, Debug)]
/// struct X {
/// v: u32,
/// }
///
/// assert_eq!([] as [X; 0], first_item!(X, ));
/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y)));
/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y),));
/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y)));
/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y),));
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y)));
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y),));
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y)));
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y),));
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y)));
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y),));
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 30}, Y)));
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 30}, Y),));
/// ```
#[macro_export]
macro_rules! first_item {
($id_type:ty, $(({$($first:tt)*}, $second:expr)),* $(,)?) => {
{
type IdType = $id_type;
[$(IdType{$($first)*},)*]
}
};
($id_type:ty, $(($first:expr, $second:expr)),* $(,)?) => { [$($first,)*] };
}

/// Converts a comma-separated list of pairs into an array with the second element. That is, it
/// discards the first element of the pair.
///
/// # Examples
///
/// ```
/// # use kernel::second_item;
///
/// assert_eq!([] as [u32; 0], second_item!());
/// assert_eq!([10u32], second_item!((X, 10u32)));
/// assert_eq!([10u32], second_item!((X, 10u32),));
/// assert_eq!([10u32], second_item!(({X}, 10u32)));
/// assert_eq!([10u32], second_item!(({X}, 10u32),));
/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20)));
/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20),));
/// assert_eq!([10u32, 20], second_item!(({X}, 10u32), ({X}, 20)));
/// assert_eq!([10u32, 20], second_item!(({X}, 10u32), ({X}, 20),));
/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30)));
/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30),));
/// assert_eq!([10u32, 20, 30], second_item!(({X}, 10u32), ({X}, 20), ({X}, 30)));
/// assert_eq!([10u32, 20, 30], second_item!(({X}, 10u32), ({X}, 20), ({X}, 30),));
/// ```
#[macro_export]
macro_rules! second_item {
($(({$($first:tt)*}, $second:expr)),* $(,)?) => { [$($second,)*] };
($(($first:expr, $second:expr)),* $(,)?) => { [$($second,)*] };
}

/// Defines a new constant [`IdArray`] with a concise syntax.
///
/// It is meant to be used by buses and subsystems to create a similar macro with their device id
/// type already specified, i.e., with fewer parameters to the end user.
///
/// # Examples
///
/// ```
/// #![feature(const_trait_impl)]
/// # use kernel::{define_id_array, driver::RawDeviceId};
///
/// #[derive(Copy, Clone)]
/// struct Id(u32);
///
/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw
/// // device id pair.
/// unsafe impl const RawDeviceId for Id {
/// type RawType = (u64, isize);
/// const ZERO: Self::RawType = (0, 0);
/// fn to_rawid(&self, offset: isize) -> Self::RawType {
/// (self.0 as u64 + 1, offset)
/// }
/// }
///
/// define_id_array!(A1, Id, (), []);
/// define_id_array!(A2, Id, &'static [u8], [(Id(10), None)]);
/// define_id_array!(A3, Id, &'static [u8], [(Id(10), Some(b"id1")), ]);
/// define_id_array!(A4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]);
/// define_id_array!(A5, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2")), ]);
/// define_id_array!(A6, Id, &'static [u8], [(Id(10), None), (Id(20), Some(b"id2")), ]);
/// define_id_array!(A7, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), None), ]);
/// define_id_array!(A8, Id, &'static [u8], [(Id(10), None), (Id(20), None), ]);
/// ```
#[macro_export]
macro_rules! define_id_array {
($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) => {
const $table_name:
$crate::driver::IdArray<$id_type, $data_type, { $crate::count_paren_items!($($t)*) }> =
$crate::driver::IdArray::new(
$crate::first_item!($id_type, $($t)*), $crate::second_item!($($t)*));
};
}

// TODO: Remove `ignore` tag from example once we go to 1.58.x.
/// Defines a new constant [`IdTable`] with a concise syntax.
///
/// It is meant to be used by buses and subsystems to create a similar macro with their device id
/// type already specified, i.e., with fewer parameters to the end user.
///
/// # Examples
///
/// ```ignore
/// #![feature(const_trait_impl)]
/// # use kernel::{define_id_table, driver::RawDeviceId};
///
/// #[derive(Copy, Clone)]
/// struct Id(u32);
///
/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw
/// // device id pair.
/// unsafe impl const RawDeviceId for Id {
/// type RawType = (u64, isize);
/// const ZERO: Self::RawType = (0, 0);
/// fn to_rawid(&self, offset: isize) -> Self::RawType {
/// (self.0 as u64 + 1, offset)
/// }
/// }
///
/// define_id_table!(T1, Id, &'static [u8], [(Id(10), None)]);
/// define_id_table!(T2, Id, &'static [u8], [(Id(10), Some(b"id1")), ]);
/// define_id_table!(T3, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]);
/// define_id_table!(T4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2")), ]);
/// define_id_table!(T5, Id, &'static [u8], [(Id(10), None), (Id(20), Some(b"id2")), ]);
/// define_id_table!(T6, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), None), ]);
/// define_id_table!(T7, Id, &'static [u8], [(Id(10), None), (Id(20), None), ]);
/// ```
#[macro_export]
macro_rules! define_id_table {
($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) => {
const $table_name: Option<$crate::driver::IdTable<'static, $id_type, $data_type>> = {
$crate::define_id_array!(ARRAY, $id_type, $data_type, [ $($t)* ]);
Some(ARRAY.as_table())
};
};
}

/// Custom code within device removal.
pub trait DeviceRemoval {
/// Cleans resources up when the device is removed.
Expand Down
4 changes: 4 additions & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
concat_idents,
const_fn_trait_bound,
const_mut_refs,
const_precise_live_drops, // TODO: Remove once we go to 1.58.x.
const_ptr_offset_from,
const_refs_to_cell,
const_trait_impl,
doc_cfg,
generic_associated_types,
maybe_uninit_extra,
Expand Down

0 comments on commit 26e876d

Please sign in to comment.