Refactor MetaSlab / MetaCommon. (#501)
MetaCommon is now gone. The back end must provide a SlabMetadata, which must be a subtype of MetaSlab (i.e. MetaSlab or a subclass of MetaSlab). It may add additional state here. The MetaEntry is now templated on the concrete subclass of MetaSlab that the back-end uses. The MetaEntry still stores this as a `uintptr_t` to allow easier toggling of the boundary bit but the interfaces are all in terms of stable types now. Also some tidying of names (SharedStateHandle is now called Backend). In a follow-on PR, we can then remove the chunk field from the BackendMetadata in the non-CHERI back end and allow back ends that don't require extra state to use MetaSlab directly. Other cleanups: - Remove backend/metatypes, define the types that the front end expects in mem/metaslab. The back end may extend them but these types define part of the contract between the front and back ends. - Remove FrontendMetaEntry and fold its methods into MetaEntry. - For example purposes, the default back end now extends MetaEntry. This also ensures that nothing in the front end depends on the specific type of MetaEntry. - Some things now have more sensible names. The meta entry now operates in one of three modes: - When owned by the front end, it stores a pointer to a remote, a pointer to some MetaSlab subclass, and a sizeclass. - When owned by the back end, it stores two back-end defined values that must fit in the bits of `uintptr_t` that are not reserved for the MetaEntry itself. - When not owned by either, it can be queried as if owned by the front end. The red-black tree has been refactored to allow the holder to be a wrapper type, removing all of the Holder* and Holder& uses and treating it uniformly as a value type that can be used to access the contents. The chunk field is fone from the slab medatada. This will need to be added back in the CHERI back ends, but it's a back-end policy. The back end can choose to use it or not, depending on whether it can safely convert between an Alloc-bounded pointer and a Chunk-bounded pointer. The term 'metaslab' originated in snmalloc 1 to mean a slab of slabs. In the snmalloc2 branch it was repurposed to mean metadata about a slab. To make this clearer, all uses of metaslab are now gone and have been renamed to slab metadata. The frontend metadata classes are all prefixed Frontend and some extra invariants are checked with `static_assert`.
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
#include "../mem/allocconfig.h"
|
||||
#include "../mem/metadata.h"
|
||||
#include "../pal/pal.h"
|
||||
#include "commitrange.h"
|
||||
#include "commonconfig.h"
|
||||
#include "empty_range.h"
|
||||
#include "globalrange.h"
|
||||
#include "largebuddyrange.h"
|
||||
#include "metatypes.h"
|
||||
#include "pagemap.h"
|
||||
#include "pagemapregisterrange.h"
|
||||
#include "palrange.h"
|
||||
@@ -34,23 +34,88 @@ namespace snmalloc
|
||||
* This class implements the standard backend for handling allocations.
|
||||
* It abstracts page table management and address space management.
|
||||
*/
|
||||
template<
|
||||
SNMALLOC_CONCEPT(ConceptPAL) PAL,
|
||||
bool fixed_range,
|
||||
typename PageMapEntry = MetaEntry>
|
||||
template<SNMALLOC_CONCEPT(ConceptPAL) PAL, bool fixed_range>
|
||||
class BackendAllocator : public CommonConfig
|
||||
{
|
||||
public:
|
||||
using Pal = PAL;
|
||||
|
||||
using SlabMetadata = FrontendSlabMetadata;
|
||||
|
||||
class Pagemap
|
||||
{
|
||||
friend class BackendAllocator;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Export the type stored in the pagemap.
|
||||
* The following class could be replaced by:
|
||||
*
|
||||
* ```
|
||||
* using Entry = FrontendMetaEntry<SlabMetadata>;
|
||||
* ```
|
||||
*
|
||||
* The full form here provides an example of how to extend the pagemap
|
||||
* entries. It also guarantees that the front end never directly
|
||||
* constructs meta entries, it only ever reads them or modifies them in
|
||||
* place.
|
||||
*/
|
||||
class Entry : public FrontendMetaEntry<SlabMetadata>
|
||||
{
|
||||
/**
|
||||
* The private initialising constructor is usable only by this back end.
|
||||
*/
|
||||
friend class BackendAllocator;
|
||||
|
||||
/**
|
||||
* The private default constructor is usable only by the pagemap.
|
||||
*/
|
||||
friend class FlatPagemap<MIN_CHUNK_BITS, Entry, PAL, fixed_range>;
|
||||
|
||||
/**
|
||||
* The only constructor that creates newly initialised meta entries.
|
||||
* This is callable only by the back end. The front end may copy,
|
||||
* query, and update these entries, but it may not create them
|
||||
* directly. This contract allows the back end to store any arbitrary
|
||||
* metadata in meta entries when they are first constructed.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH
|
||||
Entry(SlabMetadata* meta, uintptr_t ras)
|
||||
: FrontendMetaEntry<SlabMetadata>(meta, ras)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Default constructor. This must be callable from the pagemap.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH Entry() = default;
|
||||
|
||||
/**
|
||||
* Copy assignment is used only by the pagemap.
|
||||
*/
|
||||
Entry& operator=(const Entry& other)
|
||||
{
|
||||
FrontendMetaEntry<SlabMetadata>::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
SNMALLOC_REQUIRE_CONSTINIT
|
||||
static inline FlatPagemap<MIN_CHUNK_BITS, PageMapEntry, PAL, fixed_range>
|
||||
static inline FlatPagemap<MIN_CHUNK_BITS, Entry, PAL, fixed_range>
|
||||
concretePagemap;
|
||||
|
||||
/**
|
||||
* Set the metadata associated with a chunk.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH
|
||||
static void set_metaentry(address_t p, size_t size, const Entry& t)
|
||||
{
|
||||
for (address_t a = p; a < p + size; a += MIN_CHUNK_SIZE)
|
||||
{
|
||||
concretePagemap.set(a, t);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the metadata associated with a chunk.
|
||||
@@ -59,7 +124,7 @@ namespace snmalloc
|
||||
* to access a location that is not backed by a chunk.
|
||||
*/
|
||||
template<bool potentially_out_of_range = false>
|
||||
SNMALLOC_FAST_PATH static const MetaEntry& get_metaentry(address_t p)
|
||||
SNMALLOC_FAST_PATH static const auto& get_metaentry(address_t p)
|
||||
{
|
||||
return concretePagemap.template get<potentially_out_of_range>(p);
|
||||
}
|
||||
@@ -71,23 +136,11 @@ namespace snmalloc
|
||||
* to access a location that is not backed by a chunk.
|
||||
*/
|
||||
template<bool potentially_out_of_range = false>
|
||||
SNMALLOC_FAST_PATH static MetaEntry& get_metaentry_mut(address_t p)
|
||||
SNMALLOC_FAST_PATH static auto& get_metaentry_mut(address_t p)
|
||||
{
|
||||
return concretePagemap.template get_mut<potentially_out_of_range>(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the metadata associated with a chunk.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH
|
||||
static void set_metaentry(address_t p, size_t size, const MetaEntry& t)
|
||||
{
|
||||
for (address_t a = p; a < p + size; a += MIN_CHUNK_SIZE)
|
||||
{
|
||||
concretePagemap.set(a, t);
|
||||
}
|
||||
}
|
||||
|
||||
static void register_range(address_t p, size_t sz)
|
||||
{
|
||||
concretePagemap.register_range(p, sz);
|
||||
@@ -246,26 +299,23 @@ namespace snmalloc
|
||||
|
||||
/**
|
||||
* Returns a chunk of memory with alignment and size of `size`, and a
|
||||
* metaslab block.
|
||||
* block containing metadata about the slab.
|
||||
*
|
||||
* It additionally set the meta-data for this chunk of memory to
|
||||
* be
|
||||
* (remote, sizeclass, metaslab)
|
||||
* where metaslab, is the second element of the pair return.
|
||||
* (remote, sizeclass, slab_metadata)
|
||||
* where slab_metadata, is the second element of the pair return.
|
||||
*/
|
||||
static std::pair<capptr::Chunk<void>, Metaslab*>
|
||||
static std::pair<capptr::Chunk<void>, SlabMetadata*>
|
||||
alloc_chunk(LocalState& local_state, size_t size, uintptr_t ras)
|
||||
{
|
||||
SNMALLOC_ASSERT(bits::is_pow2(size));
|
||||
SNMALLOC_ASSERT(size >= MIN_CHUNK_SIZE);
|
||||
|
||||
SNMALLOC_ASSERT((ras & MetaEntry::REMOTE_BACKEND_MARKER) == 0);
|
||||
ras &= ~MetaEntry::REMOTE_BACKEND_MARKER;
|
||||
|
||||
auto meta_cap =
|
||||
local_state.get_meta_range()->alloc_range(PAGEMAP_METADATA_STRUCT_SIZE);
|
||||
local_state.get_meta_range()->alloc_range(sizeof(SlabMetadata));
|
||||
|
||||
auto meta = meta_cap.template as_reinterpret<Metaslab>().unsafe_ptr();
|
||||
auto meta = meta_cap.template as_reinterpret<SlabMetadata>().unsafe_ptr();
|
||||
|
||||
if (meta == nullptr)
|
||||
{
|
||||
@@ -281,7 +331,7 @@ namespace snmalloc
|
||||
if (p == nullptr)
|
||||
{
|
||||
local_state.get_meta_range()->dealloc_range(
|
||||
meta_cap, PAGEMAP_METADATA_STRUCT_SIZE);
|
||||
meta_cap, sizeof(SlabMetadata));
|
||||
errno = ENOMEM;
|
||||
#ifdef SNMALLOC_TRACING
|
||||
message<1024>("Out of memory");
|
||||
@@ -289,32 +339,44 @@ namespace snmalloc
|
||||
return {p, nullptr};
|
||||
}
|
||||
|
||||
meta->meta_common.chunk = p;
|
||||
|
||||
MetaEntry t(&meta->meta_common, ras);
|
||||
typename Pagemap::Entry t(meta, ras);
|
||||
Pagemap::set_metaentry(address_cast(p), size, t);
|
||||
|
||||
p = Aal::capptr_bound<void, capptr::bounds::Chunk>(p, size);
|
||||
return {p, meta};
|
||||
}
|
||||
|
||||
static void
|
||||
dealloc_chunk(LocalState& local_state, MetaCommon& meta_common, size_t size)
|
||||
static void dealloc_chunk(
|
||||
LocalState& local_state,
|
||||
SlabMetadata& slab_metadata,
|
||||
capptr::Alloc<void> alloc,
|
||||
size_t size)
|
||||
{
|
||||
auto chunk = meta_common.chunk;
|
||||
|
||||
/*
|
||||
* The backend takes possession of these chunks now, by disassociating
|
||||
* any existing remote allocator and metadata structure. If
|
||||
* interrogated, the sizeclass reported by the MetaEntry is 0, which has
|
||||
* size 0.
|
||||
* interrogated, the sizeclass reported by the FrontendMetaEntry is 0,
|
||||
* which has size 0.
|
||||
*/
|
||||
MetaEntry t(nullptr, MetaEntry::REMOTE_BACKEND_MARKER);
|
||||
Pagemap::set_metaentry(address_cast(chunk), size, t);
|
||||
typename Pagemap::Entry t(nullptr, 0);
|
||||
t.claim_for_backend();
|
||||
SNMALLOC_ASSERT_MSG(
|
||||
Pagemap::get_metaentry(address_cast(alloc)).get_slab_metadata() ==
|
||||
&slab_metadata,
|
||||
"Slab metadata {} passed for address {} does not match the meta entry "
|
||||
"{} that is used for that address",
|
||||
&slab_metadata,
|
||||
address_cast(alloc),
|
||||
Pagemap::get_metaentry(address_cast(alloc)).get_slab_metadata());
|
||||
Pagemap::set_metaentry(address_cast(alloc), size, t);
|
||||
|
||||
local_state.get_meta_range()->dealloc_range(
|
||||
capptr::Chunk<void>(&meta_common), PAGEMAP_METADATA_STRUCT_SIZE);
|
||||
capptr::Chunk<void>(&slab_metadata), sizeof(SlabMetadata));
|
||||
|
||||
// On non-CHERI platforms, we don't need to re-derive to get a pointer to
|
||||
// the chunk. On CHERI platforms this will need to be stored in the
|
||||
// SlabMetadata or similar.
|
||||
capptr::Chunk<void> chunk{alloc.unsafe_ptr()};
|
||||
local_state.object_range->dealloc_range(chunk, size);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user