From b3c03126c463bc843211fd43d80531539f2f18ab Mon Sep 17 00:00:00 2001 From: Hans Christian Schmitz Date: Tue, 3 Jun 2025 13:07:40 +0200 Subject: [PATCH 1/2] Fix derived `Hash` for type with custom `PartialEq` Hash all empty spans to the same value to align with the `PartialEq` implementation. The derived `Hash` was causing clippy to throw an error. See https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq --- talc/src/span.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/talc/src/span.rs b/talc/src/span.rs index 5a66db0..17c3cd2 100644 --- a/talc/src/span.rs +++ b/talc/src/span.rs @@ -1,4 +1,8 @@ -use core::ops::Range; +use core::{ + hash::{Hash, Hasher}, + ops::Range, + ptr::null_mut, +}; use crate::ptr_utils::*; @@ -11,7 +15,7 @@ use crate::ptr_utils::*; /// the specific values of `base` and `acme` are considered meaningless. /// * Empty spans contain nothing and overlap with nothing. /// * Empty spans are contained by any sized span. -#[derive(Clone, Copy, Hash)] +#[derive(Clone, Copy)] pub struct Span { base: *mut u8, acme: *mut u8, @@ -120,11 +124,24 @@ impl From<*const [T; N]> for Span { impl PartialEq for Span { fn eq(&self, other: &Self) -> bool { - self.is_empty() && other.is_empty() || self.base == other.base && self.acme == other.acme + self.is_empty() && other.is_empty() + || core::ptr::eq(self.base, other.base) && core::ptr::eq(self.acme, other.acme) } } impl Eq for Span {} +impl Hash for Span { + fn hash(&self, hasher: &mut H) { + if self.is_empty() { + null_mut::().hash(hasher); + null_mut::().hash(hasher); + } else { + self.base.hash(hasher); + self.acme.hash(hasher); + } + } +} + impl Span { /// Returns whether `base >= acme`. #[inline] From be81838265fb2319aaaba01f5c01dd46457255f4 Mon Sep 17 00:00:00 2001 From: Hans Christian Schmitz Date: Tue, 3 Jun 2025 13:07:54 +0200 Subject: [PATCH 2/2] Support fallible malloc attempts without OOM handling When allocating with Talc for a balloon device allocations should be multiples of 4KiB in size and must be aligned to 4KiB. To reduce per-allocation overhead we want to group such allocations as much as possible into large chunks. Allocation of such almost as large as possible chunks below a maximum size can be accomplished by attempting allocations in decreasing powers of two below the maximum size. These allocation attempts would however trigger the OOM handler which is undesirable. The balloon driver is also responsible for OOM handling in my case and since we are already within balloon driver code, making these allocation attempts, the driver is already locked. The OOM handler therefore cannot (and should not) deflate the balloon, because the driver state is locked. Thus we want to be able to make allocation attempts without triggering the OOM handler. --- talc/src/talc.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/talc/src/talc.rs b/talc/src/talc.rs index 1770217..a2d2021 100644 --- a/talc/src/talc.rs +++ b/talc/src/talc.rs @@ -322,9 +322,31 @@ impl Talc { } /// Allocate a contiguous region of memory according to `layout`, if possible. + /// If the allocation is not currently possible, attempt to recover via the + /// specified [`OomHandler`]. + /// See [`Self::malloc_without_oom_handler`] for allocating without automatic OOM recovery. + /// /// # Safety /// `layout.size()` must be nonzero. pub unsafe fn malloc(&mut self, layout: Layout) -> Result, ()> { + self.malloc_impl(layout, true) + } + + /// Allocate a contiguous region of memory according to `layout`, if possible. + /// If the allocation is not currently possible, do not attempt to recover via + /// the specified [`OomHandler`] and always return `Err(())`. + /// + /// # Safety + /// `layout.size()` must be nonzero. + pub unsafe fn malloc_without_oom_handler(&mut self, layout: Layout) -> Result, ()> { + self.malloc_impl(layout, false) + } + + unsafe fn malloc_impl( + &mut self, + layout: Layout, + handle_oom: bool, + ) -> Result, ()> { debug_assert!(layout.size() != 0); self.scan_for_errors(); @@ -332,7 +354,13 @@ impl Talc { // this returns None if there are no heaps or allocatable memory match self.get_sufficient_chunk(layout) { Some(payload) => break payload, - None => _ = O::handle_oom(self, layout)?, + None => { + if handle_oom { + _ = O::handle_oom(self, layout)? + } else { + return Err(()); + } + } } };