diff --git a/repos/ports/src/virtualbox/rt.cc b/repos/ports/src/virtualbox/rt.cc
index fdf2698779..ab52b09fa3 100644
--- a/repos/ports/src/virtualbox/rt.cc
+++ b/repos/ports/src/virtualbox/rt.cc
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2013 Genode Labs GmbH
+ * Copyright (C) 2013-2016 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@@ -16,6 +16,7 @@
#include
#include
+
/* VirtualBox includes */
#include
#include
@@ -31,34 +32,183 @@ class Avl_ds : public Genode::Avl_node
Genode::Ram_dataspace_capability _ds;
Genode::addr_t _virt;
+ Genode::addr_t const _size;
+ Genode::addr_t _used_size;
+
+ bool _inuse = true;
+
+ static Genode::addr_t _mem_allocated;
+ static Genode::addr_t _mem_unused;
+
+ static Genode::Avl_tree _unused_ds;
+ static Genode::Avl_tree _runtime_ds;
public:
- Avl_ds(Genode::Ram_dataspace_capability ds, void * virt)
- : _ds(ds), _virt(reinterpret_cast(virt))
- { }
+ static Genode::addr_t hit;
+ static Genode::addr_t hit_coarse;
- ~Avl_ds() {
- Genode::env()->ram_session()->free(_ds);
+ Avl_ds(Genode::Ram_dataspace_capability ds, void * virt,
+ Genode::addr_t size)
+ :
+ _ds(ds), _virt(reinterpret_cast(virt)),
+ _size(size), _used_size(_size)
+ {
+ _mem_allocated += _size;
+ _runtime_ds.insert(this);
}
- bool higher(Avl_ds *e) { return e->_virt > _virt; }
+ ~Avl_ds() {
+ Assert (!_inuse);
- Avl_ds *find(Genode::addr_t virt)
+ _unused_ds.remove(this);
+ _mem_unused -= _size;
+ _mem_allocated -= _size;
+
+ Genode::env()->ram_session()->free(_ds);
+ PWRN("free up %lu %lu/%lu hit=%lu/%lu avail=%zu",
+ _size, _mem_allocated, _mem_unused, hit, hit_coarse,
+ Genode::env()->ram_session()->avail());
+ }
+
+ void unused()
+ {
+ _inuse = false;
+
+ _runtime_ds.remove(this);
+
+ _mem_unused += _size;
+ _unused_ds.insert(this);
+ }
+
+ void used(Genode::addr_t size)
+ {
+ _inuse = true;
+
+ _unused_ds.remove(this);
+ _used_size = size;
+ _mem_unused -= _size;
+ _runtime_ds.insert(this);
+ }
+
+ bool higher(Avl_ds *e) {
+ return _inuse ? e->_virt > _virt : e->_size > _size; }
+
+ Avl_ds *find_virt(Genode::addr_t virt)
{
if (virt == _virt) return this;
Avl_ds *obj = this->child(virt > _virt);
- return obj ? obj->find(virt) : 0;
+ return obj ? obj->find_virt(virt) : 0;
}
+ Avl_ds *find_coarse_match(Genode::addr_t size_min, Genode::addr_t size_max)
+ {
+ if (size_min <= _size && _size <= size_max) return this;
+ Avl_ds *obj = this->child(size_max > _size);
+ return obj ? obj->find_coarse_match(size_min, size_max) : 0;
+ }
+
+ Avl_ds *find_size(Genode::addr_t size, bool equal = true)
+ {
+ if (equal ? size == _size : size <= _size) return this;
+ Avl_ds *obj = this->child(size > _size);
+ return obj ? obj->find_size(size, equal) : 0;
+ }
+
+ static Avl_ds *find_match(Genode::addr_t size, bool coarse = false)
+ {
+ Avl_ds * head = _unused_ds.first();
+ if (!head)
+ return head;
+
+ return coarse ? head->find_coarse_match(size, size * 2)
+ : head->find_size(size);
+ }
+
+ Genode::addr_t ds_virt() const { return _virt; }
+
+ static void memory_freeup(Genode::addr_t const cb)
+ {
+ /* free up memory if we hit some chosen limits */
+ enum {
+ MEMORY_MAX = 64 * 1024 * 1024,
+ MEMORY_CACHED = 16 * 1024 * 1024,
+ };
+
+ size_t cbx = cb * 4;
+ while (_unused_ds.first() && cbx &&
+ (_mem_allocated + cb > MEMORY_MAX ||
+ _mem_unused + cb > MEMORY_CACHED ||
+ Genode::env()->ram_session()->avail() < cb * 2
+ )
+ )
+ {
+ Avl_ds * ds_free = _unused_ds.first()->find_size(cbx, false);
+ if (!ds_free) {
+ cbx = cbx / 2;
+ continue;
+ }
+
+ destroy(Genode::env()->heap(), ds_free);
+ }
+ }
+
+ static void free_memory(void * pv, size_t cb)
+ {
+ if (cb % 0x1000)
+ cb = (cb & ~0xFFFUL) + 0x1000UL;
+
+ Avl_ds * ds_obj = Avl_ds::_runtime_ds.first();
+ if (ds_obj)
+ ds_obj = ds_obj->find_virt(reinterpret_cast(pv));
+
+ if (ds_obj && ds_obj->_used_size == cb)
+ ds_obj->unused();
+ else {
+ PERR("%s unknown memory region %p(%lx)+%zx(%lx)",
+ __func__, pv, ds_obj ? ds_obj->ds_virt() : 0,
+ cb, ds_obj ? ds_obj->_size : 0);
+ }
+ }
};
-static Genode::Avl_tree runtime_ds;
+Genode::Avl_tree Avl_ds::_runtime_ds;
+Genode::Avl_tree Avl_ds::_unused_ds;
+static Genode::Lock lock_ds;
+
+Genode::addr_t Avl_ds::hit = 0;
+Genode::addr_t Avl_ds::hit_coarse = 0;
+Genode::addr_t Avl_ds::_mem_allocated = 0;
+Genode::addr_t Avl_ds::_mem_unused = 0;
static void *alloc_mem(size_t cb, const char *pszTag, bool executable = false)
{
using namespace Genode;
+ if (!cb)
+ return nullptr;
+
+ if (cb % 0x1000)
+ cb = (cb & ~0xFFFUL) + 0x1000UL;
+
+ Lock::Guard guard(lock_ds);
+
+ if (Avl_ds * ds_free = Avl_ds::find_match(cb)) {
+ ds_free->used(cb);
+
+ Avl_ds::hit++;
+ return reinterpret_cast(ds_free->ds_virt());
+ } else
+ if (Avl_ds * ds_free = Avl_ds::find_match(cb, true)) {
+ ds_free->used(cb);
+
+ Avl_ds::hit_coarse++;
+ return reinterpret_cast(ds_free->ds_virt());
+ }
+
+ /* check for memory freeup, give hint about required memory (cb) */
+ Avl_ds::memory_freeup(cb);
+
try {
Ram_dataspace_capability ds = env()->ram_session()->alloc(cb);
Assert(ds.valid());
@@ -72,15 +222,13 @@ static void *alloc_mem(size_t cb, const char *pszTag, bool executable = false)
any_addr, any_local_addr,
executable);
- if (!local_addr)
- PERR("%s size=0x%zx, tag=%s -> %p", __func__, cb, pszTag, local_addr);
Assert(local_addr);
- runtime_ds.insert(new (env()->heap()) Avl_ds(ds, local_addr));
+ new (env()->heap()) Avl_ds(ds, local_addr, cb);
return local_addr;
} catch (...) {
- Assert(!"Could not allocate RTMem* memory ");
+ PERR("Could not allocate RTMem* memory of size=%zx", cb);
return nullptr;
}
}
@@ -101,7 +249,10 @@ void *RTMemPageAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW
* The RAM dataspace freshly allocated by 'RTMemExecAllocTag' is zeroed
* already.
*/
- return alloc_mem(cb, pszTag);
+ void * addr = alloc_mem(cb, pszTag);
+ if (addr)
+ Genode::memset(addr, 0, cb);
+ return addr;
}
@@ -113,17 +264,9 @@ void *RTMemPageAllocTag(size_t cb, const char *pszTag) RT_NO_THROW
void RTMemPageFree(void *pv, size_t cb) RT_NO_THROW
{
- Avl_ds * ds_obj = runtime_ds.first();
- if (ds_obj)
- ds_obj = ds_obj->find(reinterpret_cast(pv));
-
- if (ds_obj) {
- runtime_ds.remove(ds_obj);
- destroy(Genode::env()->heap(), ds_obj);
- }
- else
- PERR("%s unknown memory region %p+%zx", __func__, pv, cb);
+ Genode::Lock::Guard guard(lock_ds);
+ Avl_ds::free_memory(pv, cb);
}
#include