Skip to content

Commit 26e876d

Browse files
authored
Merge pull request torvalds#615 from wedsonaf/id-array
rust: add `IdArray` and `IdTable`
2 parents 6a0100f + c3f395b commit 26e876d

File tree

2 files changed

+280
-1
lines changed

2 files changed

+280
-1
lines changed

rust/kernel/driver.rs

Lines changed: 276 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
use crate::{str::CStr, sync::Ref, Error, KernelModule, Result, ScopeGuard, ThisModule};
99
use alloc::{boxed::Box, vec::Vec};
10-
use core::{cell::UnsafeCell, mem::MaybeUninit, ops::Deref, pin::Pin};
10+
use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ops::Deref, pin::Pin};
1111

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

165+
/// Conversion from a device id to a raw device id.
166+
///
167+
/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
168+
/// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
169+
///
170+
/// # Safety
171+
///
172+
/// Implementers must ensure that:
173+
/// * [`RawDeviceId::ZERO`] is actually a zeroed-out version of the raw device id.
174+
/// * [`RawDeviceId::to_rawid`] stores `offset` in the context/data field of the raw device id so
175+
/// that buses can recover the pointer to the data.
176+
pub unsafe trait RawDeviceId {
177+
/// The raw type that holds the device id.
178+
///
179+
/// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
180+
type RawType: Copy;
181+
182+
/// A zeroed-out representation of the raw device id.
183+
///
184+
/// Id tables created from [`Self`] use [`Self::ZERO`] as the sentinel to indicate the end of
185+
/// the table.
186+
const ZERO: Self::RawType;
187+
188+
/// Converts an id into a raw id.
189+
///
190+
/// `offset` is the offset from the memory location where the raw device id is stored to the
191+
/// location where its associated context information is stored. Implementations must store
192+
/// this in the appropriate context/data field of the raw type.
193+
fn to_rawid(&self, offset: isize) -> Self::RawType;
194+
}
195+
196+
/// A zero-terminated device id array, followed by context data.
197+
#[repr(C)]
198+
pub struct IdArray<T: RawDeviceId, U, const N: usize> {
199+
ids: [T::RawType; N],
200+
sentinel: T::RawType,
201+
id_infos: [Option<U>; N],
202+
}
203+
204+
impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
205+
/// Creates a new instance of the array.
206+
///
207+
/// The contents are derived from the given identifiers and context information.
208+
pub const fn new(ids: [T; N], infos: [Option<U>; N]) -> Self
209+
where
210+
T: ~const RawDeviceId + Copy,
211+
{
212+
let mut array = Self {
213+
ids: [T::ZERO; N],
214+
sentinel: T::ZERO,
215+
id_infos: infos,
216+
};
217+
let mut i = 0usize;
218+
while i < N {
219+
// SAFETY: Both pointers are within `array` (or one byte beyond), consequently they are
220+
// derived from the same allocated object. We are using a `u8` pointer, whose size 1,
221+
// so the pointers are necessarily 1-byte aligned.
222+
let offset = unsafe {
223+
(&array.id_infos[i] as *const _ as *const u8)
224+
.offset_from(&array.ids[i] as *const _ as _)
225+
};
226+
array.ids[i] = ids[i].to_rawid(offset);
227+
i += 1;
228+
}
229+
array
230+
}
231+
232+
/// Returns an `IdTable` backed by `self`.
233+
///
234+
/// This is used to essentially erase the array size.
235+
pub const fn as_table(&self) -> IdTable<'_, T, U> {
236+
IdTable {
237+
first: &self.ids[0],
238+
_p: PhantomData,
239+
}
240+
}
241+
}
242+
243+
/// A device id table.
244+
///
245+
/// The table is guaranteed to be zero-terminated and to be followed by an array of context data of
246+
/// type `Option<U>`.
247+
pub struct IdTable<'a, T: RawDeviceId, U> {
248+
first: &'a T::RawType,
249+
_p: PhantomData<&'a U>,
250+
}
251+
252+
impl<T: RawDeviceId, U> const AsRef<T::RawType> for IdTable<'_, T, U> {
253+
fn as_ref(&self) -> &T::RawType {
254+
self.first
255+
}
256+
}
257+
258+
/// Counts the number of parenthesis-delimited, comma-separated items.
259+
///
260+
/// # Examples
261+
///
262+
/// ```
263+
/// # use kernel::count_paren_items;
264+
///
265+
/// assert_eq!(0, count_paren_items!());
266+
/// assert_eq!(1, count_paren_items!((A)));
267+
/// assert_eq!(1, count_paren_items!((A),));
268+
/// assert_eq!(2, count_paren_items!((A), (B)));
269+
/// assert_eq!(2, count_paren_items!((A), (B),));
270+
/// assert_eq!(3, count_paren_items!((A), (B), (C)));
271+
/// assert_eq!(3, count_paren_items!((A), (B), (C),));
272+
/// ```
273+
#[macro_export]
274+
macro_rules! count_paren_items {
275+
(($($item:tt)*), $($remaining:tt)*) => { 1 + $crate::count_paren_items!($($remaining)*) };
276+
(($($item:tt)*)) => { 1 };
277+
() => { 0 };
278+
}
279+
280+
/// Converts a comma-separated list of pairs into an array with the first element. That is, it
281+
/// discards the second element of the pair.
282+
///
283+
/// Additionally, it automatically introduces a type if the first element is warpped in curly
284+
/// braces, for example, if it's `{v: 10}`, it becomes `X { v: 10 }`; this is to avoid repeating
285+
/// the type.
286+
///
287+
/// # Examples
288+
///
289+
/// ```
290+
/// # use kernel::first_item;
291+
///
292+
/// #[derive(PartialEq, Debug)]
293+
/// struct X {
294+
/// v: u32,
295+
/// }
296+
///
297+
/// assert_eq!([] as [X; 0], first_item!(X, ));
298+
/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y)));
299+
/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y),));
300+
/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y)));
301+
/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y),));
302+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y)));
303+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y),));
304+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y)));
305+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y),));
306+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
307+
/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y)));
308+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
309+
/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y),));
310+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
311+
/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 30}, Y)));
312+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
313+
/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 30}, Y),));
314+
/// ```
315+
#[macro_export]
316+
macro_rules! first_item {
317+
($id_type:ty, $(({$($first:tt)*}, $second:expr)),* $(,)?) => {
318+
{
319+
type IdType = $id_type;
320+
[$(IdType{$($first)*},)*]
321+
}
322+
};
323+
($id_type:ty, $(($first:expr, $second:expr)),* $(,)?) => { [$($first,)*] };
324+
}
325+
326+
/// Converts a comma-separated list of pairs into an array with the second element. That is, it
327+
/// discards the first element of the pair.
328+
///
329+
/// # Examples
330+
///
331+
/// ```
332+
/// # use kernel::second_item;
333+
///
334+
/// assert_eq!([] as [u32; 0], second_item!());
335+
/// assert_eq!([10u32], second_item!((X, 10u32)));
336+
/// assert_eq!([10u32], second_item!((X, 10u32),));
337+
/// assert_eq!([10u32], second_item!(({X}, 10u32)));
338+
/// assert_eq!([10u32], second_item!(({X}, 10u32),));
339+
/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20)));
340+
/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20),));
341+
/// assert_eq!([10u32, 20], second_item!(({X}, 10u32), ({X}, 20)));
342+
/// assert_eq!([10u32, 20], second_item!(({X}, 10u32), ({X}, 20),));
343+
/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30)));
344+
/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30),));
345+
/// assert_eq!([10u32, 20, 30], second_item!(({X}, 10u32), ({X}, 20), ({X}, 30)));
346+
/// assert_eq!([10u32, 20, 30], second_item!(({X}, 10u32), ({X}, 20), ({X}, 30),));
347+
/// ```
348+
#[macro_export]
349+
macro_rules! second_item {
350+
($(({$($first:tt)*}, $second:expr)),* $(,)?) => { [$($second,)*] };
351+
($(($first:expr, $second:expr)),* $(,)?) => { [$($second,)*] };
352+
}
353+
354+
/// Defines a new constant [`IdArray`] with a concise syntax.
355+
///
356+
/// It is meant to be used by buses and subsystems to create a similar macro with their device id
357+
/// type already specified, i.e., with fewer parameters to the end user.
358+
///
359+
/// # Examples
360+
///
361+
/// ```
362+
/// #![feature(const_trait_impl)]
363+
/// # use kernel::{define_id_array, driver::RawDeviceId};
364+
///
365+
/// #[derive(Copy, Clone)]
366+
/// struct Id(u32);
367+
///
368+
/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw
369+
/// // device id pair.
370+
/// unsafe impl const RawDeviceId for Id {
371+
/// type RawType = (u64, isize);
372+
/// const ZERO: Self::RawType = (0, 0);
373+
/// fn to_rawid(&self, offset: isize) -> Self::RawType {
374+
/// (self.0 as u64 + 1, offset)
375+
/// }
376+
/// }
377+
///
378+
/// define_id_array!(A1, Id, (), []);
379+
/// define_id_array!(A2, Id, &'static [u8], [(Id(10), None)]);
380+
/// define_id_array!(A3, Id, &'static [u8], [(Id(10), Some(b"id1")), ]);
381+
/// define_id_array!(A4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]);
382+
/// define_id_array!(A5, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2")), ]);
383+
/// define_id_array!(A6, Id, &'static [u8], [(Id(10), None), (Id(20), Some(b"id2")), ]);
384+
/// define_id_array!(A7, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), None), ]);
385+
/// define_id_array!(A8, Id, &'static [u8], [(Id(10), None), (Id(20), None), ]);
386+
/// ```
387+
#[macro_export]
388+
macro_rules! define_id_array {
389+
($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) => {
390+
const $table_name:
391+
$crate::driver::IdArray<$id_type, $data_type, { $crate::count_paren_items!($($t)*) }> =
392+
$crate::driver::IdArray::new(
393+
$crate::first_item!($id_type, $($t)*), $crate::second_item!($($t)*));
394+
};
395+
}
396+
397+
// TODO: Remove `ignore` tag from example once we go to 1.58.x.
398+
/// Defines a new constant [`IdTable`] with a concise syntax.
399+
///
400+
/// It is meant to be used by buses and subsystems to create a similar macro with their device id
401+
/// type already specified, i.e., with fewer parameters to the end user.
402+
///
403+
/// # Examples
404+
///
405+
/// ```ignore
406+
/// #![feature(const_trait_impl)]
407+
/// # use kernel::{define_id_table, driver::RawDeviceId};
408+
///
409+
/// #[derive(Copy, Clone)]
410+
/// struct Id(u32);
411+
///
412+
/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw
413+
/// // device id pair.
414+
/// unsafe impl const RawDeviceId for Id {
415+
/// type RawType = (u64, isize);
416+
/// const ZERO: Self::RawType = (0, 0);
417+
/// fn to_rawid(&self, offset: isize) -> Self::RawType {
418+
/// (self.0 as u64 + 1, offset)
419+
/// }
420+
/// }
421+
///
422+
/// define_id_table!(T1, Id, &'static [u8], [(Id(10), None)]);
423+
/// define_id_table!(T2, Id, &'static [u8], [(Id(10), Some(b"id1")), ]);
424+
/// define_id_table!(T3, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]);
425+
/// define_id_table!(T4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2")), ]);
426+
/// define_id_table!(T5, Id, &'static [u8], [(Id(10), None), (Id(20), Some(b"id2")), ]);
427+
/// define_id_table!(T6, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), None), ]);
428+
/// define_id_table!(T7, Id, &'static [u8], [(Id(10), None), (Id(20), None), ]);
429+
/// ```
430+
#[macro_export]
431+
macro_rules! define_id_table {
432+
($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) => {
433+
const $table_name: Option<$crate::driver::IdTable<'static, $id_type, $data_type>> = {
434+
$crate::define_id_array!(ARRAY, $id_type, $data_type, [ $($t)* ]);
435+
Some(ARRAY.as_table())
436+
};
437+
};
438+
}
439+
165440
/// Custom code within device removal.
166441
pub trait DeviceRemoval {
167442
/// Cleans resources up when the device is removed.

rust/kernel/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
concat_idents,
1919
const_fn_trait_bound,
2020
const_mut_refs,
21+
const_precise_live_drops, // TODO: Remove once we go to 1.58.x.
22+
const_ptr_offset_from,
23+
const_refs_to_cell,
24+
const_trait_impl,
2125
doc_cfg,
2226
generic_associated_types,
2327
maybe_uninit_extra,

0 commit comments

Comments
 (0)