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:
committed by
Nathaniel Wesley Filardo
parent
7eb8769950
commit
70c3e00df7
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
57
src/backend/backend_concept.h
Normal file
57
src/backend/backend_concept.h
Normal 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
|
||||
Reference in New Issue
Block a user