Files
snmalloc/src/pal/pal.h
Matthew Parkinson 5287000453 Buddy (#468)
# Small changes before rewrite

* Additional bit in remote allocator to prevent type confusion with the backend.
* Move Chunk allocator to backend.
* Improvements to RedBlack tree
* Expose message from Pal

# Complete backend rewrite

This provides two key changes:

* We use buddy allocators to allow memory to reconsolidated
* The backend is factored into a series of small operations that
    allocate and deallocate memory.

The backend now uses "Ranges", there are two ranges that don't require a
parent range:
* EmptyRange - Never returns any memory
* PalRange - Returns memory from the platform.

All other ranges require a parent range to supply memory to them.  Some
ranges support both allocation and deallocation, and some just
deallocation.  For instance,  CommitRange supports both, and maps
requests to the parent range, but will Commit and Decommit the memory.

As the ranges perform only a single task, they are generally small and
easy to follow.  The two exceptions to this are the two BuddyRanges
(Large and Small).  Large is for CHUNK_SIZE and above blocks, while
Small is for below CHUNK_SIZE blocks.  Both are implemented with a buddy
allocator, but the SmallBuddyRange uses in place meta-data, while the
LargeBuddyRange uses the pagemap for its meta-data.  This means the
LargeBuddyRange can keep the majority of memory it is managing
decommitted.

The Backend glues together the various ranges to support the appropriate
way to manage memory on the platform.
2022-03-11 18:16:06 +00:00

178 lines
5.3 KiB
C++

#pragma once
#include "../ds/concept.h"
#include "pal_concept.h"
#include "pal_consts.h"
// If simultating OE, then we need the underlying platform
#if defined(OPEN_ENCLAVE)
# include "pal_open_enclave.h"
#endif
#if !defined(OPEN_ENCLAVE) || defined(OPEN_ENCLAVE_SIMULATION)
# include "pal_apple.h"
# include "pal_dragonfly.h"
# include "pal_freebsd.h"
# include "pal_freebsd_kernel.h"
# include "pal_haiku.h"
# include "pal_linux.h"
# include "pal_netbsd.h"
# include "pal_openbsd.h"
# include "pal_solaris.h"
# include "pal_windows.h"
#endif
#include "pal_noalloc.h"
#include "pal_plain.h"
namespace snmalloc
{
#if !defined(OPEN_ENCLAVE) || defined(OPEN_ENCLAVE_SIMULATION)
using DefaultPal =
# if defined(_WIN32)
PALWindows;
# elif defined(__APPLE__)
PALApple<>;
# elif defined(__linux__)
PALLinux;
# elif defined(FreeBSD_KERNEL)
PALFreeBSDKernel;
# elif defined(__FreeBSD__)
PALFreeBSD;
# elif defined(__HAIKU__)
PALHaiku;
# elif defined(__NetBSD__)
PALNetBSD;
# elif defined(__OpenBSD__)
PALOpenBSD;
# elif defined(__sun)
PALSolaris;
# elif defined(__DragonFly__)
PALDragonfly;
# else
# error Unsupported platform
# endif
#endif
using Pal =
#if defined(SNMALLOC_MEMORY_PROVIDER)
PALPlainMixin<SNMALLOC_MEMORY_PROVIDER>;
#elif defined(OPEN_ENCLAVE)
PALOpenEnclave;
#else
DefaultPal;
#endif
[[noreturn]] SNMALLOC_SLOW_PATH inline void error(const char* const str)
{
Pal::error(str);
}
// Used to keep Superslab metadata committed.
static constexpr size_t OS_PAGE_SIZE = Pal::page_size;
/**
* Perform platform-specific adjustment of return pointers.
*
* This is here, rather than in every PAL proper, merely to minimize
* disruption to PALs for platforms that do not support StrictProvenance AALs.
*/
template<
typename PAL = Pal,
typename AAL = Aal,
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) B>
static inline typename std::enable_if_t<
!aal_supports<StrictProvenance, AAL>,
CapPtr<T, capptr::user_address_control_type<B>>>
capptr_to_user_address_control(CapPtr<T, B> p)
{
return CapPtr<T, capptr::user_address_control_type<B>>(p.unsafe_ptr());
}
template<
typename PAL = Pal,
typename AAL = Aal,
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) B>
static SNMALLOC_FAST_PATH typename std::enable_if_t<
aal_supports<StrictProvenance, AAL>,
CapPtr<T, capptr::user_address_control_type<B>>>
capptr_to_user_address_control(CapPtr<T, B> p)
{
return PAL::capptr_to_user_address_control(p);
}
/**
* A convenience wrapper that avoids the need to litter unsafe accesses with
* every call to PAL::zero.
*
* We do this here rather than plumb CapPtr further just to minimize
* disruption and avoid code bloat. This wrapper ought to compile down to
* nothing if SROA is doing its job.
*/
template<
typename PAL,
bool page_aligned = false,
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) B>
static SNMALLOC_FAST_PATH void pal_zero(CapPtr<T, B> p, size_t sz)
{
static_assert(
!page_aligned || B::spatial >= capptr::dimension::Spatial::Chunk);
PAL::template zero<page_aligned>(p.unsafe_ptr(), sz);
}
static_assert(
bits::is_pow2(OS_PAGE_SIZE), "OS_PAGE_SIZE must be a power of two");
static_assert(
OS_PAGE_SIZE % Aal::smallest_page_size == 0,
"The smallest architectural page size must divide OS_PAGE_SIZE");
// Some system headers (e.g. Linux' sys/user.h, FreeBSD's machine/param.h)
// define `PAGE_SIZE` as a macro, while others (e.g. macOS 11's
// mach/machine/vm_param.h) define `PAGE_SIZE` as an extern. We don't use
// `PAGE_SIZE` as our variable name, to avoid conflicts, but if we do see a
// macro definition evaluates to a constant then check that our value matches
// the platform's expected value.
#ifdef PAGE_SIZE
static_assert(
# if __has_builtin(__builtin_constant_p)
!__builtin_constant_p(PAGE_SIZE) || (PAGE_SIZE == OS_PAGE_SIZE),
# else
true,
# endif
"Page size from system header does not match snmalloc config page size.");
#endif
/**
* Report a fatal error via a PAL-specific error reporting mechanism. This
* takes a format string and a set of arguments. The format string indicates
* the remaining arguments with "{}". This could be extended later to
* support indexing fairly easily, if we ever want to localise these error
* messages.
*
* The following are supported as arguments:
*
* - Characters (`char`), printed verbatim.
* - Strings (anything convertible to `std::string_view`), typically string
* literals because nothing on this path should be performing heap
* allocations. Printed verbatim.
* - Raw pointers (void*), printed as hex strings.
* - Integers (convertible to `size_t`), printed as hex strings.
*
* These types should be sufficient for allocator-related error messages.
*/
template<size_t BufferSize, typename... Args>
[[noreturn]] inline void report_fatal_error(Args... args)
{
MessageBuilder<BufferSize> msg{std::forward<Args>(args)...};
Pal::error(msg.get_message());
}
template<size_t BufferSize, typename... Args>
inline void message(Args... args)
{
MessageBuilder<BufferSize> msg{std::forward<Args>(args)...};
Pal::message(msg.get_message());
}
} // namespace snmalloc