From f0361f8c0153a11fb91e86ede3b0f76d13e65f4f Mon Sep 17 00:00:00 2001 From: Robert Norton Date: Mon, 11 Apr 2022 13:29:06 +0100 Subject: [PATCH] Pagemap move (#504) * Move PageMap interface into pagemap.h and rename to BasicPagemap. Refactoring suggested by David. This allows custom backends to reuse or extend the BasicPagemap. It has template parameters for the PAL, concrete page map and page map entry types as well as the Backend (so that it can be friends). BackendAllocator provides an exmple page map entry type. --- src/snmalloc/backend/backend.h | 153 ++++++++----------------- src/snmalloc/backend_helpers/pagemap.h | 101 ++++++++++++++++ 2 files changed, 146 insertions(+), 108 deletions(-) diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index 5bf16a6..cbff62b 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -24,135 +24,72 @@ namespace snmalloc class BackendAllocator : public CommonConfig { public: + class PageMapEntry; using Pal = PAL; - using SlabMetadata = FrontendSlabMetadata; - class Pagemap + private: + using ConcretePagemap = + FlatPagemap; + + public: + /** + * Example of type stored in the pagemap. + * The following class could be replaced by: + * + * ``` + * using PageMapEntry = FrontendMetaEntry; + * ``` + * + * 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 PageMapEntry : public FrontendMetaEntry { + /** + * The private initialising constructor is usable only by this back end. + */ friend class BackendAllocator; - public: /** - * Export the type stored in the pagemap. - * The following class could be replaced by: - * - * ``` - * using Entry = FrontendMetaEntry; - * ``` - * - * 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. + * The private default constructor is usable only by the pagemap. */ - class Entry : public FrontendMetaEntry - { - /** - * 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; - - /** - * 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(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::operator=(other); - return *this; - } - }; - - private: - SNMALLOC_REQUIRE_CONSTINIT - static inline FlatPagemap - concretePagemap; + friend ConcretePagemap; /** - * Set the metadata associated with a chunk. + * 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 - 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); - } - } + PageMapEntry(SlabMetadata* meta, uintptr_t ras) + : FrontendMetaEntry(meta, ras) + {} - public: /** - * 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. + * Copy assignment is used only by the pagemap. */ - template - SNMALLOC_FAST_PATH static const auto& get_metaentry(address_t p) + PageMapEntry& operator=(const PageMapEntry& other) { - return concretePagemap.template get(p); + FrontendMetaEntry::operator=(other); + return *this; } /** - * 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. + * Default constructor. This must be callable from the pagemap. */ - template - SNMALLOC_FAST_PATH static auto& get_metaentry_mut(address_t p) - { - return concretePagemap.template get_mut(p); - } - - static void register_range(address_t p, size_t sz) - { - concretePagemap.register_range(p, sz); - } - - /** - * Return the bounds of the memory this back-end manages as a pair of - * addresses (start then end). This is available iff this is a - * fixed-range Backend. - */ - template - static SNMALLOC_FAST_PATH - std::enable_if_t> - get_bounds() - { - static_assert( - fixed_range_ == fixed_range, "Don't set SFINAE parameter!"); - - return concretePagemap.get_bounds(); - } - - static bool is_initialised() - { - return concretePagemap.is_initialised(); - } + SNMALLOC_FAST_PATH PageMapEntry() = default; }; + using Pagemap = BasicPagemap< + BackendAllocator, + PAL, + ConcretePagemap, + PageMapEntry, + fixed_range>; #if defined(_WIN32) || defined(__CHERI_PURE_CAPABILITY__) static constexpr bool CONSOLIDATE_PAL_ALLOCS = false; diff --git a/src/snmalloc/backend_helpers/pagemap.h b/src/snmalloc/backend_helpers/pagemap.h index ec7bbdf..15ff51c 100644 --- a/src/snmalloc/backend_helpers/pagemap.h +++ b/src/snmalloc/backend_helpers/pagemap.h @@ -325,4 +325,105 @@ namespace snmalloc body[p >> SHIFT] = t; } }; + + /** + * This is a generic implementation of the backend's interface to the page + * map. It takes a concrete page map implementation (probably FlatPageMap + * above) and entry type. It is friends with the backend passed in as a + * template parameter so that the backend can initialise the concrete page map + * and use set_metaentry which no one else should use. + */ + template< + typename Backend, + typename PAL, + typename ConcreteMap, + typename PageMapEntry, + bool fixed_range> + class BasicPagemap + { + public: + /** + * Export the type stored in the pagemap. + */ + using Entry = PageMapEntry; + + private: + friend Backend; + + /** + * Instance of the concrete pagemap, accessible to the backend so that + * it can call the init method whose type dependent on fixed_range. + */ + SNMALLOC_REQUIRE_CONSTINIT + static inline ConcreteMap 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. + * + * Set template parameter to true if it not an error + * to access a location that is not backed by a chunk. + */ + template + SNMALLOC_FAST_PATH static const auto& get_metaentry(address_t p) + { + return concretePagemap.template get(p); + } + + /** + * 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 + SNMALLOC_FAST_PATH static auto& get_metaentry_mut(address_t p) + { + return concretePagemap.template get_mut(p); + } + + /** + * Register a range in the pagemap as in-use, requiring it to allow writing + * to the underlying memory. + */ + static void register_range(address_t p, size_t sz) + { + concretePagemap.register_range(p, sz); + } + + /** + * Return the bounds of the memory this back-end manages as a pair of + * addresses (start then end). This is available iff this is a + * fixed-range Backend. + */ + template + static SNMALLOC_FAST_PATH + std::enable_if_t> + get_bounds() + { + static_assert(fixed_range_ == fixed_range, "Don't set SFINAE parameter!"); + + return concretePagemap.get_bounds(); + } + + /** + * Return whether the pagemap is initialised, ready for access. + */ + static bool is_initialised() + { + return concretePagemap.is_initialised(); + } + }; } // namespace snmalloc