Refactor MetaSlab / MetaCommon. (#501)

MetaCommon is now gone.  The back end must provide a SlabMetadata,
which must be a subtype of MetaSlab (i.e. MetaSlab or a subclass of
MetaSlab).  It may add additional state here.

The MetaEntry is now templated on the concrete subclass of MetaSlab that
the back-end uses.  The MetaEntry still stores this as a `uintptr_t` to
allow easier toggling of the boundary bit but the interfaces are all in
terms of stable types now.

Also some tidying of names (SharedStateHandle is now called Backend).

In a follow-on PR, we can then remove the chunk field from the
BackendMetadata in the non-CHERI back end and allow back ends that don't
require extra state to use MetaSlab directly.

Other cleanups:

 - Remove backend/metatypes, define the types that the front end expects
   in mem/metaslab.  The back end may extend them but these types define
   part of the contract between the front and back ends.
 - Remove FrontendMetaEntry and fold its methods into MetaEntry.
 - For example purposes, the default back end now extends MetaEntry.
   This also ensures that nothing in the front end depends on the
   specific type of MetaEntry.
 - Some things now have more sensible names.

The meta entry now operates in one of three modes:

 - When owned by the front end, it stores a pointer to a remote, a
   pointer to some MetaSlab subclass, and a sizeclass.
 - When owned by the back end, it stores two back-end defined values
   that must fit in the bits of `uintptr_t` that are not reserved for
   the MetaEntry itself.
 - When not owned by either, it can be queried as if owned by the front
   end.

The red-black tree has been refactored to allow the holder to be a
wrapper type, removing all of the Holder* and Holder& uses and treating
it uniformly as a value type that can be used to access the contents.

The chunk field is fone from the slab medatada.
This will need to be added back in the CHERI back ends, but it's a
back-end policy.  The back end can choose to use it or not, depending on
whether it can safely convert between an Alloc-bounded pointer and a
Chunk-bounded pointer.

The term 'metaslab' originated in snmalloc 1 to mean a slab of slabs.
In the snmalloc2 branch it was repurposed to mean metadata about a
slab.  To make this clearer, all uses of metaslab are now gone and have
been renamed to slab metadata.  The frontend metadata classes are all
prefixed Frontend and some extra invariants are checked with
`static_assert`.
This commit is contained in:
David Chisnall
2022-04-01 17:32:53 +01:00
committed by GitHub
parent ede7dbb3ef
commit 65ee6b2a2f
16 changed files with 1295 additions and 858 deletions

View File

@@ -146,8 +146,8 @@ int main()
*
* - RemoteAllocator::dequeue domesticating the stub's next pointer (p)
*
* - Metaslab::alloc_free_list, domesticating the successor object in the
* newly minted freelist::Iter (i.e., the thing that would be allocated
* - FrontendMetaData::alloc_free_list, domesticating the successor object
* in the newly minted freelist::Iter (i.e., the thing that would be allocated
* after q).
*/
static constexpr size_t expected_count =

View File

@@ -13,7 +13,7 @@
#include "ds/redblacktree.h"
#include "snmalloc.h"
struct Wrapper
struct NodeRef
{
// The redblack tree is going to be used inside the pagemap,
// and the redblack tree cannot use all the bits. Applying an offset
@@ -21,7 +21,33 @@ struct Wrapper
// the representation.
static constexpr size_t offset = 10000;
size_t value = offset << 1;
size_t* ptr;
constexpr NodeRef(size_t* p) : ptr(p) {}
constexpr NodeRef() : ptr(nullptr) {}
constexpr NodeRef(const NodeRef& other) : ptr(other.ptr) {}
constexpr NodeRef(NodeRef&& other) : ptr(other.ptr) {}
bool operator!=(const NodeRef& other) const
{
return ptr != other.ptr;
}
NodeRef& operator=(const NodeRef& other)
{
ptr = other.ptr;
return *this;
}
void set(uint16_t val)
{
*ptr = ((size_t(val) + offset) << 1) + (*ptr & 1);
}
explicit operator uint16_t()
{
return uint16_t((*ptr >> 1) - offset);
}
explicit operator size_t*()
{
return ptr;
}
};
// Simple representation that is like the pagemap.
@@ -29,8 +55,8 @@ struct Wrapper
// We shift the fields up to make room for the colour.
struct node
{
Wrapper left;
Wrapper right;
size_t left;
size_t right;
};
inline static node array[2048];
@@ -38,40 +64,41 @@ inline static node array[2048];
class Rep
{
public:
using key = size_t;
using key = uint16_t;
static constexpr key null = 0;
static constexpr size_t root{NodeRef::offset << 1};
using Holder = Wrapper;
using Contents = size_t;
using Handle = NodeRef;
using Contents = uint16_t;
static void set(Holder* ptr, Contents r)
static void set(Handle ptr, Contents r)
{
ptr->value = ((r + Wrapper::offset) << 1) + (ptr->value & 1);
ptr.set(r);
}
static Contents get(Holder* ptr)
static Contents get(Handle ptr)
{
return (ptr->value >> 1) - Wrapper::offset;
return static_cast<Contents>(ptr);
}
static Holder& ref(bool direction, key k)
static Handle ref(bool direction, key k)
{
if (direction)
return array[k].left;
return {&array[k].left};
else
return array[k].right;
return {&array[k].right};
}
static bool is_red(key k)
{
return (array[k].left.value & 1) == 1;
return (array[k].left & 1) == 1;
}
static void set_red(key k, bool new_is_red)
{
if (new_is_red != is_red(k))
array[k].left.value ^= 1;
array[k].left ^= 1;
}
static bool compare(key k1, key k2)
@@ -88,6 +115,16 @@ public:
{
return k;
}
static size_t* printable(NodeRef k)
{
return static_cast<size_t*>(k);
}
static const char* name()
{
return "TestRep";
}
};
template<bool TRACE>
@@ -112,9 +149,9 @@ void test(size_t size, unsigned int seed)
for (auto j = batch; j > 0; j--)
{
auto index = 1 + rand.next() % size;
if (tree.insert_elem(index))
if (tree.insert_elem(Rep::key(index)))
{
entries.push_back(index);
entries.push_back(Rep::key(index));
}
}
}
@@ -189,4 +226,4 @@ int main(int argc, char** argv)
// Trace particular example
test<true>(size, seed);
return 0;
}
}