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