Files
snmalloc/src/mem/chunkmap.h
Nathaniel Filardo 263e9562c0 NFC: Feed Pagemap its primitive allocator as template arg
This will let us use Pagemaps further down the dependency stack (specifically,
we're going to want a Pagemap inside the AddressSpaceManager) by letting us
manually tie the knot rather than rely on GlobalVirtual and
default_memory_provider() being defined by the time we want a Pagemap.
2021-03-16 09:29:19 +00:00

204 lines
6.2 KiB
C++

#pragma once
using namespace std;
#include "../ds/address.h"
#include "largealloc.h"
#include "mediumslab.h"
#include "pagemap.h"
#include "slab.h"
namespace snmalloc
{
enum ChunkMapSuperslabKind : uint8_t
{
CMNotOurs = 0,
CMSuperslab = 1,
CMMediumslab = 2,
/*
* Values 3 (inclusive) through SUPERSLAB_BITS (exclusive) are as yet
* unused.
*
* Values SUPERSLAB_BITS (inclusive) through 64 (exclusive, as it would
* represent the entire address space) are used for log2(size) at the
* heads of large allocations. See SuperslabMap::set_large_size.
*/
CMLargeMin = SUPERSLAB_BITS,
CMLargeMax = 63,
/*
* Values 64 (inclusive) through 64 + SUPERSLAB_BITS (exclusive) are unused
*/
/*
* Values 64 + SUPERSLAB_BITS (inclusive) through 128 (exclusive) are used
* for entries within a large allocation. A value of x at pagemap entry p
* indicates that there are at least 2^(x-64) (inclusive) and at most
* 2^(x+1-64) (exclusive) page map entries between p and the start of the
* allocation. See ChunkMap::set_large_size and external_address's
* handling of large reallocation redirections.
*/
CMLargeRangeMin = 64 + SUPERSLAB_BITS,
CMLargeRangeMax = 127,
/*
* Values 128 (inclusive) through 255 (inclusive) are as yet unused.
*/
};
/*
* Ensure that ChunkMapSuperslabKind values are actually disjoint, i.e.,
* that large allocations don't land on CMMediumslab.
*/
static_assert(
SUPERSLAB_BITS > CMMediumslab, "Large allocations may be too small");
#ifndef SNMALLOC_MAX_FLATPAGEMAP_SIZE
/*
* Unless otherwise specified, use a flat pagemap for the chunkmap (1 byte per
* Superslab-sized and -aligned region of the address space) if either of the
* following hold:
*
* - the platform supports LazyCommit and the flat structure would occupy 256
* MiB or less. 256 MiB is more than adequate for 32-bit architectures and
* is the size of the flat pagemap for a 48-bit AS with the default chunk
* size or the USE_LARGE_CHUNKS chunksize (that is, configurations other
* than USE_SMALL_CHUNKS).
*
* - the platform does not support LazyCommit but the flat structure would
* occupy less than PAGEMAP_NODE_SIZE (i.e., the backing store for an
* internal tree node in the non-flat pagemap).
*/
# define SNMALLOC_MAX_FLATPAGEMAP_SIZE \
(pal_supports<LazyCommit> ? 256ULL * 1024 * 1024 : PAGEMAP_NODE_SIZE)
#endif
static constexpr bool CHUNKMAP_USE_FLATPAGEMAP =
SNMALLOC_MAX_FLATPAGEMAP_SIZE >=
sizeof(FlatPagemap<SUPERSLAB_BITS, uint8_t>);
using ChunkmapPagemap = std::conditional_t<
CHUNKMAP_USE_FLATPAGEMAP,
FlatPagemap<SUPERSLAB_BITS, uint8_t>,
Pagemap<SUPERSLAB_BITS, uint8_t, 0, DefaultPrimAlloc>>;
struct ForChunkmap
{};
using GlobalChunkmap = GlobalPagemapTemplate<ChunkmapPagemap, ForChunkmap>;
/**
* Optionally exported function that accesses the global chunkmap pagemap
* provided by a shared library.
*/
extern "C" void*
snmalloc_chunkmap_global_get(snmalloc::PagemapConfig const**);
/**
* Class that defines an interface to the pagemap. This is provided to
* `Allocator` as a template argument and so can be replaced by a compatible
* implementation (for example, to move pagemap updates to a different
* protection domain).
*/
template<typename PagemapProvider = GlobalChunkmap>
struct DefaultChunkMap
{
/**
* Get the pagemap entry corresponding to a specific address.
*
* Despite the type, the return value is an enum ChunkMapSuperslabKind
* or one of the reserved values described therewith.
*/
static uint8_t get(address_t p)
{
return PagemapProvider::pagemap().get(p);
}
/**
* Get the pagemap entry corresponding to a specific address.
*/
static uint8_t get(void* p)
{
return get(address_cast(p));
}
/**
* Set a pagemap entry indicating that there is a superslab at the
* specified index.
*/
static void set_slab(Superslab* slab)
{
set(slab, static_cast<size_t>(CMSuperslab));
}
/**
* Add a pagemap entry indicating that a medium slab has been allocated.
*/
static void set_slab(Mediumslab* slab)
{
set(slab, static_cast<size_t>(CMMediumslab));
}
/**
* Remove an entry from the pagemap corresponding to a superslab.
*/
static void clear_slab(Superslab* slab)
{
SNMALLOC_ASSERT(get(slab) == CMSuperslab);
set(slab, static_cast<size_t>(CMNotOurs));
}
/**
* Remove an entry corresponding to a medium slab.
*/
static void clear_slab(Mediumslab* slab)
{
SNMALLOC_ASSERT(get(slab) == CMMediumslab);
set(slab, static_cast<size_t>(CMNotOurs));
}
/**
* Update the pagemap to reflect a large allocation, of `size` bytes from
* address `p`.
*/
static void set_large_size(void* p, size_t size)
{
size_t size_bits = bits::next_pow2_bits(size);
set(p, static_cast<uint8_t>(size_bits));
// Set redirect slide
auto ss = address_cast(p) + SUPERSLAB_SIZE;
for (size_t i = 0; i < size_bits - SUPERSLAB_BITS; i++)
{
size_t run = bits::one_at_bit(i);
PagemapProvider::pagemap().set_range(
ss, static_cast<uint8_t>(CMLargeRangeMin + i), run);
ss = ss + SUPERSLAB_SIZE * run;
}
}
/**
* Update the pagemap to remove a large allocation, of `size` bytes from
* address `p`.
*/
static void clear_large_size(void* vp, size_t size)
{
auto p = address_cast(vp);
size_t rounded_size = bits::next_pow2(size);
SNMALLOC_ASSERT(get(p) == bits::next_pow2_bits(size));
auto count = rounded_size >> SUPERSLAB_BITS;
PagemapProvider::pagemap().set_range(p, CMNotOurs, count);
}
private:
/**
* Helper function to set a pagemap entry. This is not part of the public
* interface and exists to make it easy to reuse the code in the public
* methods in other pagemap adaptors.
*/
static void set(void* p, uint8_t x)
{
PagemapProvider::pagemap().set(address_cast(p), x);
}
};
#ifndef SNMALLOC_DEFAULT_CHUNKMAP
# define SNMALLOC_DEFAULT_CHUNKMAP snmalloc::DefaultChunkMap<>
#endif
} // namespace snmalloc