Skip to content

Commit cf4a3e9

Browse files
Darksonnjannau
authored andcommitted
rust: maple_tree: add MapleTreeAlloc
To support allocation trees, we introduce a new type MapleTreeAlloc for the case where the tree is created using MT_FLAGS_ALLOC_RANGE. To ensure that you can only call mtree_alloc_range on an allocation tree, we restrict thta method to the new MapleTreeAlloc type. However, all methods on MapleTree remain accessible to MapleTreeAlloc as allocation trees can use the other methods without issues. Link: https://lkml.kernel.org/r/20250902-maple-tree-v3-3-fb5c8958fb1e@google.com Signed-off-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Danilo Krummrich <dakr@kernel.org> Cc: Andreas Hindborg <a.hindborg@kernel.org> Cc: Andrew Ballance <andrewjballance@gmail.com> Cc: Björn Roy Baron <bjorn3_gh@protonmail.com> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Gary Guo <gary@garyguo.net> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Cc: Miguel Ojeda <ojeda@kernel.org> Cc: Trevor Gross <tmgross@umich.edu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent dd491bd commit cf4a3e9

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

rust/kernel/maple_tree.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ pub struct MapleTree<T: ForeignOwnable> {
3232
_p: PhantomData<T>,
3333
}
3434

35+
/// A maple tree with `MT_FLAGS_ALLOC_RANGE` set.
36+
///
37+
/// All methods on [`MapleTree`] are also accessible on this type.
38+
#[pin_data]
39+
#[repr(transparent)]
40+
pub struct MapleTreeAlloc<T: ForeignOwnable> {
41+
#[pin]
42+
tree: MapleTree<T>,
43+
}
44+
45+
// Make MapleTree methods usable on MapleTreeAlloc.
46+
impl<T: ForeignOwnable> core::ops::Deref for MapleTreeAlloc<T> {
47+
type Target = MapleTree<T>;
48+
49+
#[inline]
50+
fn deref(&self) -> &MapleTree<T> {
51+
&self.tree
52+
}
53+
}
54+
3555
#[inline]
3656
fn to_maple_range(range: impl RangeBounds<usize>) -> Option<(usize, usize)> {
3757
let first = match range.start_bound() {
@@ -358,6 +378,107 @@ impl<'tree, T: ForeignOwnable> MapleGuard<'tree, T> {
358378
}
359379
}
360380

381+
impl<T: ForeignOwnable> MapleTreeAlloc<T> {
382+
/// Create a new allocation tree.
383+
pub fn new() -> impl PinInit<Self> {
384+
let tree = pin_init!(MapleTree {
385+
// SAFETY: This initializes a maple tree into a pinned slot. The maple tree will be
386+
// destroyed in Drop before the memory location becomes invalid.
387+
tree <- Opaque::ffi_init(|slot| unsafe {
388+
bindings::mt_init_flags(slot, bindings::MT_FLAGS_ALLOC_RANGE)
389+
}),
390+
_p: PhantomData,
391+
});
392+
393+
pin_init!(MapleTreeAlloc { tree <- tree })
394+
}
395+
396+
/// Insert an entry with the given size somewhere in the given range.
397+
///
398+
/// The maple tree will search for a location in the given range where there is space to insert
399+
/// the new range. If there is not enough available space, then an error will be returned.
400+
///
401+
/// The index of the new range is returned.
402+
///
403+
/// # Examples
404+
///
405+
/// ```
406+
/// use kernel::maple_tree::{MapleTreeAlloc, AllocErrorKind};
407+
///
408+
/// let tree = KBox::pin_init(MapleTreeAlloc::<KBox<i32>>::new(), GFP_KERNEL)?;
409+
///
410+
/// let ten = KBox::new(10, GFP_KERNEL)?;
411+
/// let twenty = KBox::new(20, GFP_KERNEL)?;
412+
/// let thirty = KBox::new(30, GFP_KERNEL)?;
413+
/// let hundred = KBox::new(100, GFP_KERNEL)?;
414+
///
415+
/// // Allocate three ranges.
416+
/// let idx1 = tree.alloc_range(100, ten, ..1000, GFP_KERNEL)?;
417+
/// let idx2 = tree.alloc_range(100, twenty, ..1000, GFP_KERNEL)?;
418+
/// let idx3 = tree.alloc_range(100, thirty, ..1000, GFP_KERNEL)?;
419+
///
420+
/// assert_eq!(idx1, 0);
421+
/// assert_eq!(idx2, 100);
422+
/// assert_eq!(idx3, 200);
423+
///
424+
/// // This will fail because the remaining space is too small.
425+
/// assert_eq!(
426+
/// tree.alloc_range(800, hundred, ..1000, GFP_KERNEL).unwrap_err().cause,
427+
/// AllocErrorKind::Busy,
428+
/// );
429+
/// # Ok::<_, Error>(())
430+
/// ```
431+
pub fn alloc_range<R>(
432+
&self,
433+
size: usize,
434+
value: T,
435+
range: R,
436+
gfp: Flags,
437+
) -> Result<usize, AllocError<T>>
438+
where
439+
R: RangeBounds<usize>,
440+
{
441+
let Some((min, max)) = to_maple_range(range) else {
442+
return Err(AllocError {
443+
value,
444+
cause: AllocErrorKind::InvalidRequest,
445+
});
446+
};
447+
448+
let ptr = T::into_foreign(value);
449+
let mut index = 0;
450+
451+
// SAFETY: The tree is valid, and we are passing a pointer to an owned instance of `T`.
452+
let res = to_result(unsafe {
453+
bindings::mtree_alloc_range(
454+
self.tree.tree.get(),
455+
&mut index,
456+
ptr,
457+
size,
458+
min,
459+
max,
460+
gfp.as_raw(),
461+
)
462+
});
463+
464+
if let Err(err) = res {
465+
// SAFETY: As `mtree_alloc_range` failed, it is safe to take back ownership.
466+
let value = unsafe { T::from_foreign(ptr) };
467+
468+
let cause = if err == ENOMEM {
469+
AllocErrorKind::AllocError(kernel::alloc::AllocError)
470+
} else if err == EBUSY {
471+
AllocErrorKind::Busy
472+
} else {
473+
AllocErrorKind::InvalidRequest
474+
};
475+
Err(AllocError { value, cause })
476+
} else {
477+
Ok(index)
478+
}
479+
}
480+
}
481+
361482
/// A helper type used for navigating a [`MapleTree`].
362483
///
363484
/// # Invariants
@@ -487,3 +608,40 @@ impl<T> From<InsertError<T>> for Error {
487608
Error::from(insert_err.cause)
488609
}
489610
}
611+
612+
/// Error type for failure to insert a new value.
613+
pub struct AllocError<T> {
614+
/// The value that could not be inserted.
615+
pub value: T,
616+
/// The reason for the failure to insert.
617+
pub cause: AllocErrorKind,
618+
}
619+
620+
/// The reason for the failure to insert.
621+
#[derive(PartialEq, Eq, Copy, Clone)]
622+
pub enum AllocErrorKind {
623+
/// There is not enough space for the requested allocation.
624+
Busy,
625+
/// Failure to allocate memory.
626+
AllocError(kernel::alloc::AllocError),
627+
/// The insertion request was invalid.
628+
InvalidRequest,
629+
}
630+
631+
impl From<AllocErrorKind> for Error {
632+
#[inline]
633+
fn from(kind: AllocErrorKind) -> Error {
634+
match kind {
635+
AllocErrorKind::Busy => EBUSY,
636+
AllocErrorKind::AllocError(kernel::alloc::AllocError) => ENOMEM,
637+
AllocErrorKind::InvalidRequest => EINVAL,
638+
}
639+
}
640+
}
641+
642+
impl<T> From<AllocError<T>> for Error {
643+
#[inline]
644+
fn from(insert_err: AllocError<T>) -> Error {
645+
Error::from(insert_err.cause)
646+
}
647+
}

0 commit comments

Comments
 (0)