AddressSpace: use Backend to access Pagemap

And do so by type, rather than by value.  While here, introduce a C++20 concept
for this Backend-offered proxy and adjust the template parameters appropriately.

This will be useful for the process sandbox code, which needs to mediate stores
to the pagemap, but can provide a read-only view.
This commit is contained in:
Nathaniel Wesley Filardo
2021-08-20 21:19:18 +01:00
committed by Nathaniel Wesley Filardo
parent 7eb8769950
commit 70c3e00df7
8 changed files with 168 additions and 93 deletions

View File

@@ -42,8 +42,8 @@ namespace snmalloc
* part of satisfying the request will be registered with the provided
* arena_map for use in subsequent amplification.
*/
template<bool committed, typename Pagemap>
CapPtr<void, CBChunk> reserve(size_t size, Pagemap& pagemap)
template<bool committed, SNMALLOC_CONCEPT(ConceptBackendMetaRange) Pagemap>
CapPtr<void, CBChunk> reserve(size_t size)
{
#ifdef SNMALLOC_TRACING
std::cout << "ASM reserve request:" << size << std::endl;
@@ -63,7 +63,7 @@ namespace snmalloc
{
auto base = CapPtr<void, CBChunk>(
PAL::template reserve_aligned<committed>(size));
pagemap.register_range(address_cast(base), size);
Pagemap::register_range(address_cast(base), size);
return base;
}
}
@@ -71,7 +71,7 @@ namespace snmalloc
CapPtr<void, CBChunk> res;
{
FlagLock lock(spin_lock);
res = core.template reserve<PAL>(size, pagemap);
res = core.template reserve<PAL, Pagemap>(size);
if (res == nullptr)
{
// Allocation failed ask OS for more memory
@@ -133,12 +133,12 @@ namespace snmalloc
return nullptr;
}
pagemap.register_range(address_cast(block), block_size);
Pagemap::register_range(address_cast(block), block_size);
core.template add_range<PAL>(block, block_size, pagemap);
core.template add_range<PAL, Pagemap>(block, block_size);
// still holding lock so guaranteed to succeed.
res = core.template reserve<PAL>(size, pagemap);
res = core.template reserve<PAL, Pagemap>(size);
}
}
@@ -156,8 +156,8 @@ namespace snmalloc
* This is useful for allowing the space required for alignment to be
* used, by smaller objects.
*/
template<bool committed, typename Pagemap>
CapPtr<void, CBChunk> reserve_with_left_over(size_t size, Pagemap& pagemap)
template<bool committed, SNMALLOC_CONCEPT(ConceptBackendMetaRange) Pagemap>
CapPtr<void, CBChunk> reserve_with_left_over(size_t size)
{
SNMALLOC_ASSERT(size >= sizeof(void*));
@@ -165,15 +165,15 @@ namespace snmalloc
size_t rsize = bits::next_pow2(size);
auto res = reserve<false>(rsize, pagemap);
auto res = reserve<false, Pagemap>(rsize);
if (res != nullptr)
{
if (rsize > size)
{
FlagLock lock(spin_lock);
core.template add_range<PAL>(
pointer_offset(res, size), rsize - size, pagemap);
core.template add_range<PAL, Pagemap>(
pointer_offset(res, size), rsize - size);
}
if constexpr (committed)
@@ -193,11 +193,11 @@ namespace snmalloc
* Add a range of memory to the address space.
* Divides blocks into power of two sizes with natural alignment
*/
template<typename Pagemap>
void add_range(CapPtr<void, CBChunk> base, size_t length, Pagemap& pagemap)
template<SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
void add_range(CapPtr<void, CBChunk> base, size_t length)
{
FlagLock lock(spin_lock);
core.add_range<PAL>(base, length, pagemap);
core.add_range<PAL, Pagemap>(base, length);
}
};
} // namespace snmalloc

View File

@@ -4,6 +4,7 @@
#include "../mem/allocconfig.h"
#include "../mem/metaslab.h"
#include "../pal/pal.h"
#include "backend_concept.h"
#include <array>
#ifdef SNMALLOC_TRACING
@@ -67,12 +68,11 @@ namespace snmalloc
* to store the next pointer for the list of unused address space of a
* particular size.
*/
template<typename Pagemap>
template<SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
void set_next(
size_t align_bits,
CapPtr<FreeChunk, CBChunk> base,
CapPtr<FreeChunk, CBChunk> next,
Pagemap& pagemap)
CapPtr<FreeChunk, CBChunk> next)
{
if (align_bits >= MIN_CHUNK_BITS)
{
@@ -84,7 +84,7 @@ namespace snmalloc
// external_pointer, for example) will not attempt to follow this
// "Metaslab" pointer.
MetaEntry t(reinterpret_cast<Metaslab*>(next.unsafe_ptr()), nullptr, 0);
pagemap.set(address_cast(base), t);
Pagemap::set_meta_data(address_cast(base), 1, t);
return;
}
@@ -100,13 +100,14 @@ namespace snmalloc
* to store the next pointer for the list of unused address space of a
* particular size.
*/
template<typename Pagemap>
CapPtr<FreeChunk, CBChunk> get_next(
size_t align_bits, CapPtr<FreeChunk, CBChunk> base, Pagemap& pagemap)
template<SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
CapPtr<FreeChunk, CBChunk>
get_next(size_t align_bits, CapPtr<FreeChunk, CBChunk> base)
{
if (align_bits >= MIN_CHUNK_BITS)
{
const MetaEntry& t = pagemap.template get<false>(address_cast(base));
const MetaEntry& t =
Pagemap::template get_meta_data<false>(address_cast(base));
return CapPtr<FreeChunk, CBChunk>(
reinterpret_cast<FreeChunk*>(t.get_metaslab()));
}
@@ -117,14 +118,15 @@ namespace snmalloc
/**
* Adds a block to `ranges`.
*/
template<SNMALLOC_CONCEPT(ConceptPAL) PAL, typename Pagemap>
void add_block(
size_t align_bits, CapPtr<FreeChunk, CBChunk> base, Pagemap& pagemap)
template<
SNMALLOC_CONCEPT(ConceptPAL) PAL,
SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
void add_block(size_t align_bits, CapPtr<FreeChunk, CBChunk> base)
{
check_block(base, align_bits);
SNMALLOC_ASSERT(align_bits < 64);
set_next(align_bits, base, ranges[align_bits], pagemap);
set_next<Pagemap>(align_bits, base, ranges[align_bits]);
ranges[align_bits] = base.as_static<FreeChunk>();
}
@@ -132,8 +134,10 @@ namespace snmalloc
* Find a block of the correct size. May split larger blocks
* to satisfy this request.
*/
template<SNMALLOC_CONCEPT(ConceptPAL) PAL, typename Pagemap>
CapPtr<void, CBChunk> remove_block(size_t align_bits, Pagemap& pagemap)
template<
SNMALLOC_CONCEPT(ConceptPAL) PAL,
SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
CapPtr<void, CBChunk> remove_block(size_t align_bits)
{
CapPtr<FreeChunk, CBChunk> first = ranges[align_bits];
if (first == nullptr)
@@ -146,7 +150,7 @@ namespace snmalloc
// Look for larger block and split up recursively
CapPtr<void, CBChunk> bigger =
remove_block<PAL>(align_bits + 1, pagemap);
remove_block<PAL, Pagemap>(align_bits + 1);
if (bigger != nullptr)
{
// This block is going to be broken up into sub CHUNK_SIZE blocks
@@ -160,10 +164,9 @@ namespace snmalloc
size_t left_over_size = bits::one_at_bit(align_bits);
auto left_over = pointer_offset(bigger, left_over_size);
add_block<PAL>(
add_block<PAL, Pagemap>(
align_bits,
Aal::capptr_bound<FreeChunk, CBChunk>(left_over, left_over_size),
pagemap);
Aal::capptr_bound<FreeChunk, CBChunk>(left_over, left_over_size));
check_block(left_over.as_static<FreeChunk>(), align_bits);
}
check_block(bigger.as_static<FreeChunk>(), align_bits + 1);
@@ -171,7 +174,7 @@ namespace snmalloc
}
check_block(first, align_bits);
ranges[align_bits] = get_next(align_bits, first, pagemap);
ranges[align_bits] = get_next<Pagemap>(align_bits, first);
return first.as_void();
}
@@ -180,8 +183,10 @@ namespace snmalloc
* Add a range of memory to the address space.
* Divides blocks into power of two sizes with natural alignment
*/
template<SNMALLOC_CONCEPT(ConceptPAL) PAL, typename Pagemap>
void add_range(CapPtr<void, CBChunk> base, size_t length, Pagemap& pagemap)
template<
SNMALLOC_CONCEPT(ConceptPAL) PAL,
SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
void add_range(CapPtr<void, CBChunk> base, size_t length)
{
// For start and end that are not chunk sized, we need to
// commit the pages to track the allocations.
@@ -206,7 +211,7 @@ namespace snmalloc
auto b = base.as_static<FreeChunk>();
check_block(b, align_bits);
add_block<PAL>(align_bits, b, pagemap);
add_block<PAL, Pagemap>(align_bits, b);
base = pointer_offset(base, align);
length -= align;
@@ -238,8 +243,10 @@ namespace snmalloc
* part of satisfying the request will be registered with the provided
* arena_map for use in subsequent amplification.
*/
template<SNMALLOC_CONCEPT(ConceptPAL) PAL, typename Pagemap>
CapPtr<void, CBChunk> reserve(size_t size, Pagemap& pagemap)
template<
SNMALLOC_CONCEPT(ConceptPAL) PAL,
SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
CapPtr<void, CBChunk> reserve(size_t size)
{
#ifdef SNMALLOC_TRACING
std::cout << "ASM Core reserve request:" << size << std::endl;
@@ -248,7 +255,7 @@ namespace snmalloc
SNMALLOC_ASSERT(bits::is_pow2(size));
SNMALLOC_ASSERT(size >= sizeof(void*));
return remove_block<PAL>(bits::next_pow2_bits(size), pagemap);
return remove_block<PAL, Pagemap>(bits::next_pow2_bits(size));
}
/**
@@ -258,8 +265,10 @@ namespace snmalloc
* This is useful for allowing the space required for alignment to be
* used, by smaller objects.
*/
template<SNMALLOC_CONCEPT(ConceptPAL) PAL, typename Pagemap>
CapPtr<void, CBChunk> reserve_with_left_over(size_t size, Pagemap& pagemap)
template<
SNMALLOC_CONCEPT(ConceptPAL) PAL,
SNMALLOC_CONCEPT(ConceptBackendMeta) Pagemap>
CapPtr<void, CBChunk> reserve_with_left_over(size_t size)
{
SNMALLOC_ASSERT(size >= sizeof(void*));
@@ -267,13 +276,13 @@ namespace snmalloc
size_t rsize = bits::next_pow2(size);
auto res = reserve<PAL>(rsize, pagemap);
auto res = reserve<PAL, Pagemap>(rsize);
if (res != nullptr)
{
if (rsize > size)
{
add_range<PAL>(pointer_offset(res, size), rsize - size, pagemap);
add_range<PAL, Pagemap>(pointer_offset(res, size), rsize - size);
}
}
return res;

View File

@@ -62,6 +62,38 @@ namespace snmalloc
static inline FlatPagemap<MIN_CHUNK_BITS, PageMapEntry, PAL, fixed_range>
pagemap;
struct Pagemap
{
/**
* Get the metadata associated with a chunk.
*
* Set template parameter to true if it not an error
* 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_meta_data(address_t p)
{
return pagemap.template get<potentially_out_of_range>(p);
}
/**
* Set the metadata associated with a chunk.
*/
SNMALLOC_FAST_PATH
static void set_meta_data(address_t p, size_t size, MetaEntry t)
{
for (address_t a = p; a < p + size; a += MIN_CHUNK_SIZE)
{
pagemap.set(a, t);
}
}
static void register_range(address_t p, size_t sz)
{
pagemap.register_range(p, sz);
}
};
public:
template<bool fixed_range_ = fixed_range>
static std::enable_if_t<!fixed_range_> init()
@@ -77,8 +109,8 @@ namespace snmalloc
static_assert(fixed_range_ == fixed_range, "Don't set SFINAE parameter!");
auto [heap_base, heap_length] = pagemap.init(base, length);
address_space.add_range(
CapPtr<void, CBChunk>(heap_base), heap_length, pagemap);
address_space.template add_range<Pagemap>(
CapPtr<void, CBChunk>(heap_base), heap_length);
}
private:
@@ -136,14 +168,14 @@ namespace snmalloc
auto& local = local_state->local_address_space;
#endif
p = local.template reserve_with_left_over<PAL>(size, pagemap);
p = local.template reserve_with_left_over<PAL, Pagemap>(size);
if (p != nullptr)
{
return p;
}
auto refill_size = LOCAL_CACHE_BLOCK;
auto refill = global.template reserve<false>(refill_size, pagemap);
auto refill = global.template reserve<false, Pagemap>(refill_size);
if (refill == nullptr)
return nullptr;
@@ -155,10 +187,10 @@ namespace snmalloc
}
#endif
PAL::template notify_using<NoZero>(refill.unsafe_ptr(), refill_size);
local.template add_range<PAL>(refill, refill_size, pagemap);
local.template add_range<PAL, Pagemap>(refill, refill_size);
// This should succeed
return local.template reserve_with_left_over<PAL>(size, pagemap);
return local.template reserve_with_left_over<PAL, Pagemap>(size);
}
#ifdef SNMALLOC_CHECK_CLIENT
@@ -169,7 +201,7 @@ namespace snmalloc
size_t rsize = bits::max(OS_PAGE_SIZE, bits::next_pow2(size));
size_t size_request = rsize * 64;
p = global.template reserve<false>(size_request, pagemap);
p = global.template reserve<false, Pagemap>(size_request);
if (p == nullptr)
return nullptr;
@@ -185,7 +217,7 @@ namespace snmalloc
SNMALLOC_ASSERT(!is_meta);
#endif
p = global.template reserve_with_left_over<true>(size, pagemap);
p = global.template reserve_with_left_over<true, Pagemap>(size);
return p;
}
@@ -249,37 +281,8 @@ namespace snmalloc
}
MetaEntry t(meta, remote, sizeclass);
for (address_t a = address_cast(p);
a < address_cast(pointer_offset(p, size));
a += MIN_CHUNK_SIZE)
{
pagemap.set(a, t);
}
Pagemap::set_meta_data(address_cast(p), size, t);
return {p, meta};
}
/**
* Get the metadata associated with a chunk.
*
* Set template parameter to true if it not an error
* to access a location that is not backed by a chunk.
*/
template<bool potentially_out_of_range = false>
static const MetaEntry& get_meta_data(address_t p)
{
return pagemap.template get<potentially_out_of_range>(p);
}
/**
* Set the metadata associated with a chunk.
*/
static void set_meta_data(address_t p, size_t size, MetaEntry t)
{
for (address_t a = p; a < p + size; a += MIN_CHUNK_SIZE)
{
pagemap.set(a, t);
}
}
};
} // namespace snmalloc

View File

@@ -0,0 +1,57 @@
#pragma once
#ifdef __cpp_concepts
# include <cstddef>
# include "../ds/concept.h"
namespace snmalloc
{
class MetaEntry;
/**
* The core of the static pagemap accessor interface: {get,set}_metadata.
*
* get_metadata takes a bool-ean template parameter indicating whether it may
* be accessing memory that is not known to be committed.
*/
template<typename Meta>
concept ConceptBackendMeta =
requires(address_t addr, size_t sz, MetaEntry t)
{
{ Meta::set_meta_data(addr, sz, t) } -> ConceptSame<void>;
{ Meta::template get_meta_data<true>(addr) }
-> ConceptSame<const MetaEntry&>;
{ Meta::template get_meta_data<false>(addr) }
-> ConceptSame<const MetaEntry&>;
};
/**
* The pagemap can also be told to commit backing storage for a range of
* addresses. This is broken out to a separate concept so that we can
* annotate which functions expect to do this vs. which merely use the core
* interface above. In practice, use ConceptBackendMetaRange (without the
* underscore) below, which combines this and the core concept, above.
*/
template<typename Meta>
concept ConceptBackendMeta_Range =
requires(address_t addr, size_t sz)
{
{ Meta::register_range(addr, sz) } -> ConceptSame<void>;
};
/**
* The full pagemap accessor interface, with all of {get,set}_metadata and
* register_range. Use this to annotate callers that need the full interface
* and use ConceptBackendMeta for callers that merely need {get,set}_metadata,
* but note that the difference is just for humans and not compilers (since
* concept checking is lower bounding and does not constrain the templatized
* code to use only those affordances given by the concept).
*/
template<typename Meta>
concept ConceptBackendMetaRange =
ConceptBackendMeta<Meta> && ConceptBackendMeta_Range<Meta>;
} // namespace snmalloc
#endif