ealanos: Rewrote parts of memory allocator to be no longer depending on Bit_alloc, as this proved to consume too much memory per superblock.

This commit is contained in:
Michael Mueller
2025-05-05 18:14:44 +02:00
parent dc51de546b
commit bba40b9423
3 changed files with 153 additions and 87 deletions

View File

@@ -52,34 +52,50 @@ class Ealan::Memory::Core_heap
{
Tukija::uint8_t mem_regions = 0;
_tip = const_cast<Tip *>(Tip::tip());
Tip::Memory_region &region = _tip->memory_for_domain(domain_id, &mem_regions);
Range_allocator::Range range = {.start = reinterpret_cast<addr_t>(region.start), .end = reinterpret_cast<addr_t>(region.end)};
try {
Tip::Memory_region &region = _tip->memory_for_domain(domain_id, &mem_regions);
Range_allocator::Range range = {.start = reinterpret_cast<addr_t>(region.start), .end = reinterpret_cast<addr_t>(region.end)};
Ram_dataspace_capability ds_cap = _pd.try_alloc_from_range(size, Genode::CACHED, range).convert<Ram_dataspace_capability>(
[&](Ram_dataspace_capability cap) { return cap; },
[&](Ram_allocator::Alloc_error) { return Ram_dataspace_capability(); });
Ram_dataspace_capability ds_cap = _pd.try_alloc_from_range(size, Genode::CACHED, range).convert<Ram_dataspace_capability>([&](Ram_dataspace_capability cap)
{ return cap; },
[&](Ram_allocator::Alloc_error err)
{
Genode::error("Failed to allocate phyiscal memory in domain ", domain_id, ":", err);
return Ram_dataspace_capability(); });
if (!ds_cap.valid())
if (!ds_cap.valid()) {
Genode::warning("Failed to allocate physical memory for hyperblock");
return nullptr;
}
Region_map::Attr attr{};
attr.writeable = true;
void *hb = _rm.attach(ds_cap, attr).convert<void *>(
[&](Region_map::Range r) { return reinterpret_cast<void *>(r.start); },
[&](Region_map::Attach_error) { return nullptr; });
if (!hb) {
Genode::warning("Failed to obtain regionmap for hyperblock");
return nullptr;
}
Hyperblock *hyperblock = new (hb) Hyperblock();
hyperblock->cap = ds_cap;
return hyperblock;
} catch (Tukija::Tip::Domain_has_no_memory_regions) {
Genode::error("Domain ", domain_id, " has no memory regions.");
return nullptr;
Region_map::Attr attr{};
attr.writeable = true;
void *hb = _rm.attach(ds_cap, attr).convert<void *>(
[&](Region_map::Range r) { return reinterpret_cast<void *>(r.start); },
[&](Region_map::Attach_error) { return nullptr; });
if (!hb)
return nullptr;
Hyperblock *hyperblock = new (hb) Hyperblock();
hyperblock->cap = ds_cap;
return hyperblock;
}
}
Sb *_allocate_superblock(unsigned domain_id, Genode::size_t sz_class)
{
Hyperblock *hb = _allocate_hyperblock(domain_id, MAX * 2);
if (!hb) {
Genode::warning("Failed to allocate superblock for size class ", sz_class, " in domain ", domain_id);
return nullptr;
}
return new (static_cast<void *>(hb)) Superblock<MAX * 2, MIN>(sz_class);
}
@@ -87,7 +103,11 @@ class Ealan::Memory::Core_heap
Core_heap &operator=(Core_heap &);
public:
Core_heap(Pd_session &pd, Region_map &rm) : _pd(pd), _rm(rm) {}
Core_heap(Pd_session &pd, Region_map &rm) : _pd(pd), _rm(rm) {
Genode::log("Size of superblock array is ", sizeof(_superblocks));
Genode::log("Individual superblock size is: ", sizeof(Sb));
Genode::log("Size of individual ist of superblocks: ", sizeof(Ealan::util::MPSCQueue<Sb>));
}
~Core_heap()
{
@@ -106,7 +126,10 @@ class Ealan::Memory::Core_heap
void *aligned_alloc(Genode::size_t size, unsigned domain_id, Genode::size_t alignment)
{
if (size > MAX) {
void *ptr = nullptr;
if (size > MAX)
{
/* directly allocate a hyperblock */
Hyperblock *hb = _allocate_hyperblock(domain_id, size+sizeof(Hyperblock*) + sizeof(Ram_dataspace_capability));
hb->_next = reinterpret_cast<Hyperblock*>(magic_num);
@@ -118,18 +141,26 @@ class Ealan::Memory::Core_heap
if (!sb) {
sb = _allocate_superblock(domain_id, sz_class);
if (!sb)
return nullptr;
_superblocks[sz_class / MIN - 1][domain_id].push_back(sb);
} else if (sb->free_blocks() == 0) {
for (; sb && sb->free_blocks() == 0; sb = static_cast<Sb*>(sb->next()))
;
if (!sb) {
Sb *new_sb = _allocate_superblock(domain_id, sz_class);
_superblocks[sz_class / MIN - 1][domain_id].push_back(new_sb);
sb = new_sb;
}
}
for (; sb != nullptr ; sb = static_cast<Sb*>(sb->next())) {
ptr = sb->aligned_alloc(alignment);
if (ptr)
return ptr;
}
if (!sb) {
sb = _allocate_superblock(domain_id, sz_class);
if (!sb)
return nullptr;
_superblocks[sz_class / MIN - 1][domain_id].push_back(sb);
return sb->aligned_alloc(alignment);
}
return sb->aligned_alloc(alignment);
return ptr;
}
void *aligned_alloc(Genode::size_t size, Genode::size_t alignment)
@@ -152,6 +183,10 @@ class Ealan::Memory::Core_heap
void free(void *ptr, Genode::size_t alignment = 0)
{
if (!ptr) {
Genode::warning("Tried to free nullptr");
return;
}
void *p = reinterpret_cast<void *>((reinterpret_cast<addr_t>(ptr) - sizeof(Hyperblock *) - sizeof(Ram_dataspace_capability)));
Hyperblock *hb = reinterpret_cast<Hyperblock *>(p);
@@ -166,8 +201,16 @@ class Ealan::Memory::Core_heap
Block *b = Block::metadata(p);
if (!b) {
Genode::warning("Invalid block. Not freeing.");
return;
}
Sb *sb = static_cast<Sb*>(b->_superblock);
sb->free(ptr);
if (!sb) {
Genode::warning("Corrupt or invalid memory block.");
return;
}
sb->free(p);
}
void reserve_superblocks(size_t count, unsigned domain_id, size_t sz_class)

View File

@@ -43,6 +43,9 @@ class Ealan::Memory::Hamstraaja : public Genode::Allocator
Heap &_location_to_heap(Affinity::Location loc) const
{
size_t pos = loc.xpos() * loc.height() + loc.ypos();
if (!_core_heaps[pos]) {
Genode::error("No heap for location ", loc);
}
return *_core_heaps[pos];
}
@@ -51,12 +54,16 @@ class Ealan::Memory::Hamstraaja : public Genode::Allocator
return Thread::myself()->affinity();
}
Hamstraaja &operator=(Hamstraaja &copy);
Hamstraaja(Hamstraaja const &copy);
public:
Hamstraaja(Genode::Pd_session &pd, Genode::Region_map &rm) : _pd(pd), _rm(rm)
{
size_t num_cpus = Cip::cip()->habitat_affinity.total();
for (size_t cpu = 0; cpu < num_cpus; cpu++) {
_core_heaps[cpu] = new (_backend) Core_heap<MIN, MAX>(_pd, _rm);
Genode::log("Size of CoreHeap for size ", MAX * 2, " with ", MIN, " blocks is: ", sizeof(Core_heap<MIN, MAX>));
}
}
@@ -92,7 +99,7 @@ class Ealan::Memory::Hamstraaja : public Genode::Allocator
void *alloc(size_t size, unsigned domain_id)
{
_quota_used += overhead(size) + size;
return _location_to_heap(_my_location()).alloc(size, domain_id);
return _location_to_heap(_my_location()).aligned_alloc(size, 0, domain_id);
}
/**
@@ -104,6 +111,7 @@ class Ealan::Memory::Hamstraaja : public Genode::Allocator
void *alloc(size_t size)
{
_quota_used += overhead(size) + size;
//Genode::log("Allocating ", size, " bytes.");
return _location_to_heap(_my_location()).alloc(size);
}
@@ -116,6 +124,10 @@ class Ealan::Memory::Hamstraaja : public Genode::Allocator
*/
void free(void *ptr, size_t alignment, size_t size)
{
if (!ptr) {
Genode::warning("Tried to free nullptr");
return;
}
_quota_used -= overhead(size) + size;
return _location_to_heap(_my_location()).free(ptr, alignment);
}
@@ -154,7 +166,7 @@ class Ealan::Memory::Hamstraaja : public Genode::Allocator
}
}
void free(void *ptr, size_t size) override { free(ptr, 0, size); }
void free(void *ptr, size_t size=0) override { free(ptr, 0, size); }
size_t consumed() const override { return _quota_used; }
size_t overhead(size_t size) const override
{

View File

@@ -14,6 +14,7 @@
#include <ealanos/util/bit_alloc.h>
#include <base/stdint.h>
#include <base/ram_allocator.h>
namespace Ealan::Memory {
class Block;
@@ -44,37 +45,29 @@ class Ealan::Memory::Hyperblock
* @brief A block of memory
*
*/
class Ealan::Memory::Block
struct Ealan::Memory::Block
{
public:
void *_superblock{nullptr}; /* Pointer to the superblock, this block was allocated from. */
Genode::size_t _alignment{0};
void *_block_start; /* Placeholder for marking the start of the usable area of this block. */
void *_superblock{nullptr}; /* Pointer to the superblock, this block was allocated from. */
char _padding[56];
/**
* @brief Construct a new Block object
*
* @param sb - Pointer to the superblock this block was allocated from
*/
Block(void *sb, Genode::size_t align) : _superblock(sb), _alignment(align), _block_start{nullptr} {}
/**
* @brief Return a pointer to the metadata of this block
*
* @param ptr - application-facing pointer for which to request the metadata
* @return Block* - pointer to the Block datastructure containing the metadata
*/
static Block *metadata(void *ptr) {
return reinterpret_cast<Block *>(reinterpret_cast<Genode::addr_t>(ptr) - sizeof(Block));
}
/**
* @brief Creates a new block at the address pointed to by p
*
* @param p - the address at which the block shall be created
* @return void* - pointer to the new block
*/
void *operator new(Genode::size_t, void *p) { return p; }
/**
* @brief Return a pointer to the metadata of this block
*
* @param ptr - application-facing pointer for which to request the metadata
* @return Block* - pointer to the Block datastructure containing the metadata
*/
static Block *metadata(void *ptr) {
return reinterpret_cast<Block *>(reinterpret_cast<Genode::addr_t>(ptr) - sizeof(Genode::size_t) - sizeof(void *));
}
bool reserve(void *sb) {
void *expect{nullptr};
return __atomic_compare_exchange_n(&_superblock, &expect, sb, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
void free() {
__atomic_store_n(&_superblock, nullptr, __ATOMIC_RELEASE);
}
};
/**
@@ -91,13 +84,22 @@ template <int SIZE, unsigned BASE>
class Ealan::Memory::Superblock : public Hyperblock
{
private:
Genode::size_t _size_class{0}; /* actual size of the blocks in this superblock */
Bit_alloc<0, SIZE/BASE> _alloc; /* Bit allocator to allocate blocks from this superblock */
Block _block_start[]; /* Marker for the end of the metadata portion */
Genode::size_t _size_class;
alignas(64) Genode::addr_t _start{0}; /* Start address of the blocks */
public:
Superblock(Genode::size_t sz) : _size_class(sz), _alloc(SIZE / _size_class)
Superblock(Genode::size_t sz) : _size_class(sz)
{
if (_size_class > SIZE) {
Genode::error("Size class ", _size_class, " is bigger than superblock size ", SIZE);
}
/*Genode::log("Superblock SIZE=", SIZE, " BASE=", BASE, " this at ", this);
Genode::log("Block metadata size is ", sizeof(Block));
Genode::log("Size class of superblock is ", _size_class);
Block *end = reinterpret_cast<Block *>(reinterpret_cast<Genode::addr_t>(this) + SIZE);
Genode::log("Superblock ends at ", end);
Genode::log("Capacity is ", capacity());
Genode::log("-------------------");*/
}
/**
@@ -106,14 +108,19 @@ class Ealan::Memory::Superblock : public Hyperblock
* @return void* - pointer to the allocated block
*/
void *alloc() {
long idx = static_cast<long>(_alloc.alloc());
if (!idx)
return nullptr;
Genode::addr_t block_addr = reinterpret_cast<Genode::addr_t>(_block_start) + idx * _size_class;
Block *block = new (reinterpret_cast<void *>(block_addr)) Block(this, 0);
return &block->_block_start;
Block *block = reinterpret_cast<Block *>(&_start);
Block *end = reinterpret_cast<Block *>(reinterpret_cast<Genode::addr_t>(this) + SIZE - 64);
while (block < end) {
if (block->_superblock == nullptr) {
if (block->reserve(this))
{
return reinterpret_cast<void*>(reinterpret_cast<Genode::addr_t>(block)+64);
}
}
Genode::addr_t next = reinterpret_cast<Genode::addr_t>(block) + sizeof(Block) + _size_class;
block = reinterpret_cast<Block *>(next);
}
return nullptr;
}
/**
@@ -122,13 +129,17 @@ class Ealan::Memory::Superblock : public Hyperblock
* @param alignment - adress alignment to use (must be at least 8)
* @return void* - pointer to this block aligned to alignment bytes boundary
*/
void *aligned_alloc(Genode::size_t alignment = 64) {
void *aligned_alloc(Genode::size_t alignment = 0) {
void *ptr = alloc();
Genode::addr_t base_ptr = reinterpret_cast<Genode::addr_t>(ptr) - sizeof(Genode::size_t) - sizeof(void *);
reinterpret_cast<Block *>(base_ptr)->_alignment = alignment;
if (!ptr)
return nullptr;
return reinterpret_cast<void *>(reinterpret_cast<Genode::addr_t>(ptr) + alignment);
}
Genode::size_t capacity() {
return (reinterpret_cast<Genode::addr_t>(this) + SIZE - reinterpret_cast<Genode::addr_t>(&_start)) / (sizeof(Block) + _size_class);
}
/**
* @brief Free the block pointed to by ptr.
*
@@ -141,23 +152,23 @@ class Ealan::Memory::Superblock : public Hyperblock
* @param ptr - Pointer to memory block to free
*/
void free(void *ptr) {
Genode::size_t idx = (reinterpret_cast<Genode::size_t>(ptr) - reinterpret_cast<Genode::size_t>(_block_start)) / _size_class;
_alloc.release(idx);
}
if (!ptr)
return;
/**
* @brief Return number of blocks that can be allocated from this superblock
*
* @return Genode::size_t - Number of available blocks
*/
Genode::size_t free_blocks() { return _alloc.left(); }
Block *b = reinterpret_cast<Block *>(reinterpret_cast<Genode::addr_t>(ptr) - 64);
Block *end = reinterpret_cast<Block *>(reinterpret_cast<Genode::addr_t>(this) + SIZE);
if (b > --end)
return;
b->free();
}
/**
* @brief Return the address of the first memory block in this superblock
*
* @return Block* - address of the first block
*/
Block *start() { return _block_start; }
Block *start() { return reinterpret_cast<Block*>(&_start); }
/**
* @brief Placement new used to create a superblock at address pointed to by p.