Skip to content

Commit 40c5329

Browse files
Darksonnojeda
authored andcommitted
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 <benno.lossin@proton.me> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://lore.kernel.org/r/20240814-linked-list-v5-5-f5f5e8075da0@google.com Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent 1417629 commit 40c5329

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

rust/kernel/list.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use crate::init::PinInit;
88
use crate::types::Opaque;
99
use core::ptr;
1010

11+
mod impl_list_item_mod;
12+
pub use self::impl_list_item_mod::{impl_has_list_links, impl_list_item, HasListLinks};
13+
1114
mod arc;
1215
pub use self::arc::{impl_list_arc_safe, AtomicTracker, ListArc, ListArcSafe, TryNewListArc};
1316

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
// Copyright (C) 2024 Google LLC.
4+
5+
//! Helpers for implementing list traits safely.
6+
7+
use crate::list::ListLinks;
8+
9+
/// Declares that this type has a `ListLinks<ID>` field at a fixed offset.
10+
///
11+
/// This trait is only used to help implement `ListItem` safely. If `ListItem` is implemented
12+
/// manually, then this trait is not needed. Use the [`impl_has_list_links!`] macro to implement
13+
/// this trait.
14+
///
15+
/// # Safety
16+
///
17+
/// All values of this type must have a `ListLinks<ID>` field at the given offset.
18+
///
19+
/// The behavior of `raw_get_list_links` must not be changed.
20+
pub unsafe trait HasListLinks<const ID: u64 = 0> {
21+
/// The offset of the `ListLinks` field.
22+
const OFFSET: usize;
23+
24+
/// Returns a pointer to the [`ListLinks<T, ID>`] field.
25+
///
26+
/// # Safety
27+
///
28+
/// The provided pointer must point at a valid struct of type `Self`.
29+
///
30+
/// [`ListLinks<T, ID>`]: ListLinks
31+
// We don't really need this method, but it's necessary for the implementation of
32+
// `impl_has_list_links!` to be correct.
33+
#[inline]
34+
unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut ListLinks<ID> {
35+
// SAFETY: The caller promises that the pointer is valid. The implementer promises that the
36+
// `OFFSET` constant is correct.
37+
unsafe { (ptr as *mut u8).add(Self::OFFSET) as *mut ListLinks<ID> }
38+
}
39+
}
40+
41+
/// Implements the [`HasListLinks`] trait for the given type.
42+
#[macro_export]
43+
macro_rules! impl_has_list_links {
44+
($(impl$(<$($implarg:ident),*>)?
45+
HasListLinks$(<$id:tt>)?
46+
for $self:ident $(<$($selfarg:ty),*>)?
47+
{ self$(.$field:ident)* }
48+
)*) => {$(
49+
// SAFETY: The implementation of `raw_get_list_links` only compiles if the field has the
50+
// right type.
51+
//
52+
// The behavior of `raw_get_list_links` is not changed since the `addr_of_mut!` macro is
53+
// equivalent to the pointer offset operation in the trait definition.
54+
unsafe impl$(<$($implarg),*>)? $crate::list::HasListLinks$(<$id>)? for
55+
$self $(<$($selfarg),*>)?
56+
{
57+
const OFFSET: usize = ::core::mem::offset_of!(Self, $($field).*) as usize;
58+
59+
#[inline]
60+
unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$id>)? {
61+
// SAFETY: The caller promises that the pointer is not dangling. We know that this
62+
// expression doesn't follow any pointers, as the `offset_of!` invocation above
63+
// would otherwise not compile.
64+
unsafe { ::core::ptr::addr_of_mut!((*ptr)$(.$field)*) }
65+
}
66+
}
67+
)*};
68+
}
69+
pub use impl_has_list_links;
70+
71+
/// Implements the [`ListItem`] trait for the given type.
72+
///
73+
/// Requires that the type implements [`HasListLinks`]. Use the [`impl_has_list_links!`] macro to
74+
/// implement that trait.
75+
///
76+
/// [`ListItem`]: crate::list::ListItem
77+
#[macro_export]
78+
macro_rules! impl_list_item {
79+
(
80+
$(impl$({$($generics:tt)*})? ListItem<$num:tt> for $t:ty {
81+
using ListLinks;
82+
})*
83+
) => {$(
84+
// SAFETY: See GUARANTEES comment on each method.
85+
unsafe impl$(<$($generics)*>)? $crate::list::ListItem<$num> for $t {
86+
// GUARANTEES:
87+
// * This returns the same pointer as `prepare_to_insert` because `prepare_to_insert`
88+
// is implemented in terms of `view_links`.
89+
// * By the type invariants of `ListLinks`, the `ListLinks` has two null pointers when
90+
// this value is not in a list.
91+
unsafe fn view_links(me: *const Self) -> *mut $crate::list::ListLinks<$num> {
92+
// SAFETY: The caller guarantees that `me` points at a valid value of type `Self`.
93+
unsafe {
94+
<Self as $crate::list::HasListLinks<$num>>::raw_get_list_links(me.cast_mut())
95+
}
96+
}
97+
98+
// GUARANTEES:
99+
// * `me` originates from the most recent call to `prepare_to_insert`, which just added
100+
// `offset` to the pointer passed to `prepare_to_insert`. This method subtracts
101+
// `offset` from `me` so it returns the pointer originally passed to
102+
// `prepare_to_insert`.
103+
// * The pointer remains valid until the next call to `post_remove` because the caller
104+
// of the most recent call to `prepare_to_insert` promised to retain ownership of the
105+
// `ListArc` containing `Self` until the next call to `post_remove`. The value cannot
106+
// be destroyed while a `ListArc` reference exists.
107+
unsafe fn view_value(me: *mut $crate::list::ListLinks<$num>) -> *const Self {
108+
let offset = <Self as $crate::list::HasListLinks<$num>>::OFFSET;
109+
// SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it
110+
// points at the field at offset `offset` in a value of type `Self`. Thus,
111+
// subtracting `offset` from `me` is still in-bounds of the allocation.
112+
unsafe { (me as *const u8).sub(offset) as *const Self }
113+
}
114+
115+
// GUARANTEES:
116+
// This implementation of `ListItem` will not give out exclusive access to the same
117+
// `ListLinks` several times because calls to `prepare_to_insert` and `post_remove`
118+
// must alternate and exclusive access is given up when `post_remove` is called.
119+
//
120+
// Other invocations of `impl_list_item!` also cannot give out exclusive access to the
121+
// same `ListLinks` because you can only implement `ListItem` once for each value of
122+
// `ID`, and the `ListLinks` fields only work with the specified `ID`.
123+
unsafe fn prepare_to_insert(me: *const Self) -> *mut $crate::list::ListLinks<$num> {
124+
// SAFETY: The caller promises that `me` points at a valid value.
125+
unsafe { <Self as $crate::list::ListItem<$num>>::view_links(me) }
126+
}
127+
128+
// GUARANTEES:
129+
// * `me` originates from the most recent call to `prepare_to_insert`, which just added
130+
// `offset` to the pointer passed to `prepare_to_insert`. This method subtracts
131+
// `offset` from `me` so it returns the pointer originally passed to
132+
// `prepare_to_insert`.
133+
unsafe fn post_remove(me: *mut $crate::list::ListLinks<$num>) -> *const Self {
134+
let offset = <Self as $crate::list::HasListLinks<$num>>::OFFSET;
135+
// SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it
136+
// points at the field at offset `offset` in a value of type `Self`. Thus,
137+
// subtracting `offset` from `me` is still in-bounds of the allocation.
138+
unsafe { (me as *const u8).sub(offset) as *const Self }
139+
}
140+
}
141+
)*};
142+
}
143+
pub use impl_list_item;

0 commit comments

Comments
 (0)