diff --git a/libports/src/lib/libc/malloc.cc b/libports/src/lib/libc/malloc.cc
index 620cb44f49..f2a6308db3 100644
--- a/libports/src/lib/libc/malloc.cc
+++ b/libports/src/lib/libc/malloc.cc
@@ -1,6 +1,7 @@
/*
* \brief Simplistic malloc and free implementation
* \author Norman Feske
+ * \author Sebastian Sumpf
* \date 2006-07-21
*/
@@ -14,6 +15,7 @@
/* Genode includes */
#include
#include
+#include
#include
#include
@@ -22,25 +24,146 @@
typedef unsigned long Block_header;
+namespace Genode {
+
+ class Slab_alloc : public Slab
+ {
+ size_t _calculate_block_size(size_t object_size)
+ {
+ size_t block_size = 8 * (object_size + sizeof(Slab_entry)) + sizeof(Slab_block);
+ return align_addr(block_size, 12);
+ }
+
+ public:
+
+ Slab_alloc(size_t object_size, Allocator *backing_store)
+ : Slab(object_size, _calculate_block_size(object_size), 0, backing_store)
+ { }
+
+ inline void *alloc()
+ {
+ void *result;
+ return (Slab::alloc(slab_size(), &result) ? result : 0);
+ }
+ };
+}
+
+
+/**
+ * Allocator that uses slabs for small objects sizes
+ */
+class Malloc : public Genode::Allocator
+{
+ private:
+
+ enum {
+ SLAB_START = 2, /* 4 Byte (log2) */
+ SLAB_STOP = 11, /* 2048 Byte (log2) */
+ NUM_SLABS = (SLAB_STOP - SLAB_START) + 1
+ };
+
+ Genode::Allocator *_backing_store; /* back-end allocator */
+ Genode::Slab_alloc *_allocator[NUM_SLABS]; /* slab allocators */
+ Genode::Lock _lock;
+
+ unsigned long _slab_log2(unsigned long size)
+ {
+ unsigned msb = Genode::log2(size);
+ /* size is greater than msb */
+ if (size > (1U << msb))
+ msb++;
+
+ /* use smallest slab */
+ if (size < (1U << SLAB_START))
+ msb = SLAB_START;
+
+ return msb;
+ }
+
+ public:
+
+ Malloc(Genode::Allocator *backing_store) : _backing_store(backing_store)
+ {
+ for (unsigned i = SLAB_START; i <= SLAB_STOP; i++) {
+ _allocator[i - SLAB_START] = new (backing_store)
+ Genode::Slab_alloc(1U << i, backing_store);
+ }
+ }
+
+ /**
+ * Allocator interface
+ */
+
+ bool alloc(size_t size, void **out_addr)
+ {
+ Genode::Lock::Guard lock_guard(_lock);
+
+ /* enforce size to be a multiple of 4 bytes */
+ size = (size + 3) & ~3;
+
+ /*
+ * We store the size of the allocation at the very
+ * beginning of the allocated block and return
+ * the subsequent address. This way, we can retrieve
+ * the size information when freeing the block.
+ */
+ unsigned long real_size = size + sizeof(Block_header);
+ unsigned long msb = _slab_log2(real_size);
+ void *addr = 0;
+
+ /* use backing store if requested memory is larger than largest slab */
+ if (msb > SLAB_STOP) {
+
+ if (!(_backing_store->alloc(real_size, &addr)))
+ return false;
+ }
+ else
+ if (!(addr = _allocator[msb - SLAB_START]->alloc()))
+ return false;
+
+ *(Block_header *)addr = real_size;
+ *out_addr = (Block_header *)addr + 1;
+ return true;
+ }
+
+ void free(void *ptr, size_t /* size */)
+ {
+ Genode::Lock::Guard lock_guard(_lock);
+
+ unsigned long *addr = ((unsigned long *)ptr) - 1;
+ unsigned long real_size = *addr;
+
+ if (real_size > (1U << SLAB_STOP))
+ _backing_store->free(addr, real_size);
+ else {
+ unsigned long msb = _slab_log2(real_size);
+ _allocator[msb - SLAB_START]->free(addr);
+ }
+ }
+
+ size_t overhead(size_t size)
+ {
+ size += sizeof(Block_header);
+
+ if (size > (1U << SLAB_STOP))
+ return _backing_store->overhead(size);
+
+ return _allocator[_slab_log2(size) - SLAB_START]->overhead(size);
+ }
+};
+
+
+static Genode::Allocator *allocator()
+{
+ static Malloc _m(Genode::env()->heap());
+ return &_m;
+}
+
extern "C" void *malloc(unsigned size)
{
- /* enforce size to be a multiple of 4 bytes */
- size = (size + 3) & ~3;
-
- /*
- * We store the size of the allocation at the very
- * beginning of the allocated block and return
- * the subsequent address. This way, we can retrieve
- * the size information when freeing the block.
- */
- unsigned long real_size = size + sizeof(Block_header);
- void *addr = 0;
- if (!Genode::env()->heap()->alloc(real_size, &addr))
- return 0;
-
- *(Block_header *)addr = real_size;
- return (Block_header *)addr + 1;
+ void *addr;
+ return allocator()->alloc(size, &addr) ? addr : 0;
}
@@ -56,8 +179,7 @@ extern "C" void free(void *ptr)
{
if (!ptr) return;
- unsigned long *addr = ((unsigned long *)ptr) - 1;
- Genode::env()->heap()->free(addr, *addr);
+ allocator()->free(ptr, 0);
}