From dac1ba6a6caded330a0212e803de000800414bc7 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sun, 29 Aug 2021 18:57:42 +0100 Subject: [PATCH] Metaslab: SlabLink field rather than parent class Introduce Metaslab::from_link(SlabLink*) to encapsulate the "container of" dance. Note that Metaslab was not a standard layout type prior to this change (since both SlabLink and Metaslab defined non-static data members), and so the reinterpret_cast<>s replaced here with ::from_link() were UB, but everyone lays out classes as one expects so it was fine in practice. Most of the uses of ::from_link() are already guarded by checks that the link pointer is not nullptr, but in src/mem/corealloc.h:/debug_is_empty_impl we shift to testing the link pointer explicitly before converting to the metaslab. Despite that Metaslab is now standard layout, we still don't fall back to the inter-convertibility of a standard layout class and its first[*] data member since we're going to want to put a common initial sequence across Metaslab and ChunkRecord and the SlabLink isn't likely to be in it. --- src/mem/corealloc.h | 13 +++++++------ src/mem/metaslab.h | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/mem/corealloc.h b/src/mem/corealloc.h index c708127..87c6aa7 100644 --- a/src/mem/corealloc.h +++ b/src/mem/corealloc.h @@ -304,7 +304,7 @@ namespace snmalloc while (curr != nullptr) { auto nxt = curr->get_next(); - auto meta = reinterpret_cast(curr); + auto meta = Metaslab::from_link(curr); if (meta->needed() == 0) { prev->pop(); @@ -348,7 +348,7 @@ namespace snmalloc // Wake slab up. meta->set_not_sleeping(sizeclass); - alloc_classes[sizeclass].insert(meta); + alloc_classes[sizeclass].insert(&meta->link); alloc_classes[sizeclass].length++; #ifdef SNMALLOC_TRACING @@ -593,7 +593,7 @@ namespace snmalloc auto& sl = alloc_classes[sizeclass]; if (likely(!(sl.is_empty()))) { - auto meta = reinterpret_cast(sl.pop()); + auto meta = Metaslab::from_link(sl.pop()); // Drop length of sl, and empty count if it was empty. alloc_classes[sizeclass].length--; if (meta->needed() == 0) @@ -736,17 +736,18 @@ namespace snmalloc auto test = [&result](auto& queue) { if (!queue.is_empty()) { - auto curr = reinterpret_cast(queue.get_next()); + auto curr = queue.get_next(); while (curr != nullptr) { - if (curr->needed() != 0) + auto currmeta = Metaslab::from_link(curr); + if (currmeta->needed() != 0) { if (result != nullptr) *result = false; else error("debug_is_empty: found non-empty allocator"); } - curr = reinterpret_cast(curr->get_next()); + curr = currmeta->link.get_next(); } } }; diff --git a/src/mem/metaslab.h b/src/mem/metaslab.h index 41f73b1..56499ec 100644 --- a/src/mem/metaslab.h +++ b/src/mem/metaslab.h @@ -16,10 +16,19 @@ namespace snmalloc // The Metaslab represent the status of a single slab. // This can be either a short or a standard slab. - class alignas(CACHELINE_SIZE) Metaslab : public SlabLink + class alignas(CACHELINE_SIZE) Metaslab { public: - constexpr Metaslab() : SlabLink(true) {} + // TODO: Annotate with CHERI subobject unbound for pointer arithmetic + SlabLink link; + + constexpr Metaslab() : link(true) {} + + /** + * Metaslab::link points at another link field. To get the actual Metaslab, + * use this encapsulation of the container-of logic. + */ + static Metaslab* from_link(SlabLink* ptr); /** * Data-structure for building the free list for this slab. @@ -160,6 +169,12 @@ namespace snmalloc } }; + Metaslab* Metaslab::from_link(SlabLink* lptr) + { + return pointer_offset_signed( + lptr, -static_cast(offsetof(Metaslab, link))); + } + struct RemoteAllocator; /**