forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rust: list: add macro for implementing ListItem
Adds a macro for safely implementing the ListItem trait. As part of the implementation of the macro, we also provide a HasListLinks trait similar to the workqueue's HasWorkItem trait. The HasListLinks trait is only necessary if you are implementing ListItem using the impl_list_item macro. Reviewed-by: Benno Lossin <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Miguel Ojeda <[email protected]>
- Loading branch information
Showing
2 changed files
with
146 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
// Copyright (C) 2024 Google LLC. | ||
|
||
//! Helpers for implementing list traits safely. | ||
use crate::list::ListLinks; | ||
|
||
/// Declares that this type has a `ListLinks<ID>` field at a fixed offset. | ||
/// | ||
/// This trait is only used to help implement `ListItem` safely. If `ListItem` is implemented | ||
/// manually, then this trait is not needed. Use the [`impl_has_list_links!`] macro to implement | ||
/// this trait. | ||
/// | ||
/// # Safety | ||
/// | ||
/// All values of this type must have a `ListLinks<ID>` field at the given offset. | ||
/// | ||
/// The behavior of `raw_get_list_links` must not be changed. | ||
pub unsafe trait HasListLinks<const ID: u64 = 0> { | ||
/// The offset of the `ListLinks` field. | ||
const OFFSET: usize; | ||
|
||
/// Returns a pointer to the [`ListLinks<T, ID>`] field. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The provided pointer must point at a valid struct of type `Self`. | ||
/// | ||
/// [`ListLinks<T, ID>`]: ListLinks | ||
// We don't really need this method, but it's necessary for the implementation of | ||
// `impl_has_list_links!` to be correct. | ||
#[inline] | ||
unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut ListLinks<ID> { | ||
// SAFETY: The caller promises that the pointer is valid. The implementer promises that the | ||
// `OFFSET` constant is correct. | ||
unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut ListLinks<ID> } | ||
} | ||
} | ||
|
||
/// Implements the [`HasListLinks`] trait for the given type. | ||
#[macro_export] | ||
macro_rules! impl_has_list_links { | ||
($(impl$(<$($implarg:ident),*>)? | ||
HasListLinks$(<$id:tt>)? | ||
for $self:ident $(<$($selfarg:ty),*>)? | ||
{ self$(.$field:ident)* } | ||
)*) => {$( | ||
// SAFETY: The implementation of `raw_get_list_links` only compiles if the field has the | ||
// right type. | ||
// | ||
// The behavior of `raw_get_list_links` is not changed since the `addr_of_mut!` macro is | ||
// equivalent to the pointer offset operation in the trait definition. | ||
unsafe impl$(<$($implarg),*>)? $crate::list::HasListLinks$(<$id>)? for | ||
$self $(<$($selfarg),*>)? | ||
{ | ||
const OFFSET: usize = ::core::mem::offset_of!(Self, $($field).*) as usize; | ||
|
||
#[inline] | ||
unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$id>)? { | ||
// SAFETY: The caller promises that the pointer is not dangling. We know that this | ||
// expression doesn't follow any pointers, as the `offset_of!` invocation above | ||
// would otherwise not compile. | ||
unsafe { ::core::ptr::addr_of_mut!((*ptr)$(.$field)*) } | ||
} | ||
} | ||
)*}; | ||
} | ||
pub use impl_has_list_links; | ||
|
||
/// Implements the [`ListItem`] trait for the given type. | ||
/// | ||
/// Requires that the type implements [`HasListLinks`]. Use the [`impl_has_list_links!`] macro to | ||
/// implement that trait. | ||
/// | ||
/// [`ListItem`]: crate::list::ListItem | ||
#[macro_export] | ||
macro_rules! impl_list_item { | ||
( | ||
$(impl$({$($generics:tt)*})? ListItem<$num:tt> for $t:ty { | ||
using ListLinks; | ||
})* | ||
) => {$( | ||
// SAFETY: See GUARANTEES comment on each method. | ||
unsafe impl$(<$($generics)*>)? $crate::list::ListItem<$num> for $t { | ||
// GUARANTEES: | ||
// * This returns the same pointer as `prepare_to_insert` because `prepare_to_insert` | ||
// is implemented in terms of `view_links`. | ||
// * By the type invariants of `ListLinks`, the `ListLinks` has two null pointers when | ||
// this value is not in a list. | ||
unsafe fn view_links(me: *const Self) -> *mut $crate::list::ListLinks<$num> { | ||
// SAFETY: The caller guarantees that `me` points at a valid value of type `Self`. | ||
unsafe { | ||
<Self as $crate::list::HasListLinks<$num>>::raw_get_list_links(me.cast_mut()) | ||
} | ||
} | ||
|
||
// GUARANTEES: | ||
// * `me` originates from the most recent call to `prepare_to_insert`, which just added | ||
// `offset` to the pointer passed to `prepare_to_insert`. This method subtracts | ||
// `offset` from `me` so it returns the pointer originally passed to | ||
// `prepare_to_insert`. | ||
// * The pointer remains valid until the next call to `post_remove` because the caller | ||
// of the most recent call to `prepare_to_insert` promised to retain ownership of the | ||
// `ListArc` containing `Self` until the next call to `post_remove`. The value cannot | ||
// be destroyed while a `ListArc` reference exists. | ||
unsafe fn view_value(me: *mut $crate::list::ListLinks<$num>) -> *const Self { | ||
let offset = <Self as $crate::list::HasListLinks<$num>>::OFFSET; | ||
// SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it | ||
// points at the field at offset `offset` in a value of type `Self`. Thus, | ||
// subtracting `offset` from `me` is still in-bounds of the allocation. | ||
unsafe { (me as *const u8).sub(offset) as *const Self } | ||
} | ||
|
||
// GUARANTEES: | ||
// This implementation of `ListItem` will not give out exclusive access to the same | ||
// `ListLinks` several times because calls to `prepare_to_insert` and `post_remove` | ||
// must alternate and exclusive access is given up when `post_remove` is called. | ||
// | ||
// Other invocations of `impl_list_item!` also cannot give out exclusive access to the | ||
// same `ListLinks` because you can only implement `ListItem` once for each value of | ||
// `ID`, and the `ListLinks` fields only work with the specified `ID`. | ||
unsafe fn prepare_to_insert(me: *const Self) -> *mut $crate::list::ListLinks<$num> { | ||
// SAFETY: The caller promises that `me` points at a valid value. | ||
unsafe { <Self as $crate::list::ListItem<$num>>::view_links(me) } | ||
} | ||
|
||
// GUARANTEES: | ||
// * `me` originates from the most recent call to `prepare_to_insert`, which just added | ||
// `offset` to the pointer passed to `prepare_to_insert`. This method subtracts | ||
// `offset` from `me` so it returns the pointer originally passed to | ||
// `prepare_to_insert`. | ||
unsafe fn post_remove(me: *mut $crate::list::ListLinks<$num>) -> *const Self { | ||
let offset = <Self as $crate::list::HasListLinks<$num>>::OFFSET; | ||
// SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it | ||
// points at the field at offset `offset` in a value of type `Self`. Thus, | ||
// subtracting `offset` from `me` is still in-bounds of the allocation. | ||
unsafe { (me as *const u8).sub(offset) as *const Self } | ||
} | ||
} | ||
)*}; | ||
} | ||
pub use impl_list_item; |