From 59d6157441aad35f24d32462a7e5dc1e64c53263 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 10 Sep 2013 15:16:35 +0200 Subject: [PATCH] l4lx: ballooning by using new parent interface Implement a ballooning mechanism in L4Linux similar to solutions like XEN's balloon driver. Therefore the new parent interface extensions for requesting and yielding resources are used. L4Linux registers a yield signal context at its parent. Whenever the parent triggers a yield, the balloon driver blows up, which means it requests all pages available, and then frees the corresponding backend memory. --- ports-foc/include/genode/balloon.h | 36 ++++++ ports-foc/run/l4linux_dynamic.run | 59 ++------- ports-foc/src/drivers/Makefile | 1 + ports-foc/src/drivers/genode_balloon.c | 112 ++++++++++++++++++ ports-foc/src/lib/l4lx/include/dataspace.h | 105 +++++++--------- ports-foc/src/lib/l4lx/include/platform_env.h | 111 +++++++++++++++++ ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc | 23 +--- ports-foc/src/lib/l4lx/l4x_pagefault.cc | 102 +++++++++++++++- ports-foc/src/server/balloon/main.cc | 90 ++++++++++++++ ports-foc/src/server/balloon/target.mk | 3 + 10 files changed, 507 insertions(+), 135 deletions(-) create mode 100644 ports-foc/include/genode/balloon.h create mode 100644 ports-foc/src/drivers/genode_balloon.c create mode 100644 ports-foc/src/lib/l4lx/include/platform_env.h create mode 100644 ports-foc/src/server/balloon/main.cc create mode 100644 ports-foc/src/server/balloon/target.mk diff --git a/ports-foc/include/genode/balloon.h b/ports-foc/include/genode/balloon.h new file mode 100644 index 0000000000..69e0977866 --- /dev/null +++ b/ports-foc/include/genode/balloon.h @@ -0,0 +1,36 @@ +/* + * \brief Genode C API balloon functions + * \author Stefan Kalkowski + * \date 2013-09-19 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__GENODE__BALLOON_H_ +#define _INCLUDE__GENODE__BALLOON_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +L4_CV l4_cap_idx_t genode_balloon_irq_cap(void); + +L4_CV void genode_balloon_free_chunk(unsigned long addr); + +L4_CV void genode_balloon_free_done(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _INCLUDE__GENODE__BALLOON_H_ */ diff --git a/ports-foc/run/l4linux_dynamic.run b/ports-foc/run/l4linux_dynamic.run index e47009a757..9b3c08abba 100644 --- a/ports-foc/run/l4linux_dynamic.run +++ b/ports-foc/run/l4linux_dynamic.run @@ -112,7 +112,7 @@ set config { - + @@ -163,64 +163,23 @@ set config { - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - - + + diff --git a/ports-foc/src/drivers/Makefile b/ports-foc/src/drivers/Makefile index b4e9b029cf..d43339d8c9 100644 --- a/ports-foc/src/drivers/Makefile +++ b/ports-foc/src/drivers/Makefile @@ -1,4 +1,5 @@ obj-y += genode_serial.o +obj-y += genode_balloon.o obj-$(CONFIG_RTC_CLASS) += genode_rtc.o obj-$(CONFIG_FB) += genode_fb.o obj-$(CONFIG_BLOCK) += genode_block.o diff --git a/ports-foc/src/drivers/genode_balloon.c b/ports-foc/src/drivers/genode_balloon.c new file mode 100644 index 0000000000..11b1a091e9 --- /dev/null +++ b/ports-foc/src/drivers/genode_balloon.c @@ -0,0 +1,112 @@ + +/* + * \brief Balloon driver to use Genode's dynamic memory balancing + * \author Stefan Kalkowski + * \date 2013-09-19 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Linux includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define GFP_BALLOON \ + (__GFP_IO | __GFP_FS | __GFP_HARDWALL | __GFP_HIGHMEM | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC) + +enum { CHUNK_CACHE_SIZE = 16384 }; +static void* chunk_cache[CHUNK_CACHE_SIZE]; + + +static void free_avail_pages(unsigned long data) +{ + void *pages; + unsigned i = 0; + + LOG_printf("free_avail_pages\n"); + for (; i < CHUNK_CACHE_SIZE; i++) { + pages = alloc_pages_exact(SZ_1M, GFP_BALLOON); + if (!pages) + break; + chunk_cache[i] = pages; + } + + BUG_ON(i == CHUNK_CACHE_SIZE); + + /* Ensure that ballooned highmem pages don't have kmaps. */ + kmap_flush_unused(); + flush_tlb_all(); + + for (; i > 0;) { + genode_balloon_free_chunk((unsigned long)chunk_cache[--i]); + free_pages_exact(chunk_cache[i], SZ_1M); + } + + LOG_printf("free_avail_pages done\n"); + genode_balloon_free_done(); +} + +DECLARE_TASKLET(free_avail, free_avail_pages, 0); + +static irqreturn_t event_interrupt(int irq, void *data) +{ + tasklet_schedule(&free_avail); + return IRQ_HANDLED; +} + + +static struct platform_device genode_balloon_device = { + .name = "balloon-genode", +}; + + +static int __init balloon_init(void) +{ + int ret = 0; + unsigned irq; + l4_cap_idx_t irq_cap; + + /* + * touch the memory eager otherwise we run into trouble + * when memory is empty and we balloon + */ + memset(&chunk_cache, 0, sizeof(chunk_cache)); + + /** + * Obtain an IRQ for the device. + */ + irq_cap = genode_balloon_irq_cap(); + if ((irq = l4x_register_irq(irq_cap)) < 0) + return -ENOMEM; + if ((ret = request_irq(irq, event_interrupt, 0, + "Genode balloon", &genode_balloon_device))) { + printk(KERN_WARNING "%s: request_irq failed: %d\n", __func__, ret); + return ret; + } + + ret = platform_device_register(&genode_balloon_device); + return ret; +} + +subsys_initcall(balloon_init); + +MODULE_LICENSE("GPL"); diff --git a/ports-foc/src/lib/l4lx/include/dataspace.h b/ports-foc/src/lib/l4lx/include/dataspace.h index b3708f67d3..f88f1147c7 100644 --- a/ports-foc/src/lib/l4lx/include/dataspace.h +++ b/ports-foc/src/lib/l4lx/include/dataspace.h @@ -20,6 +20,7 @@ #include #include #include +#include namespace Fiasco { #include @@ -56,7 +57,8 @@ namespace L4lx { Fiasco::l4_cap_idx_t ref() { return _ref; } virtual Genode::Dataspace_capability cap() = 0; - virtual bool map(Genode::size_t offset) = 0; + virtual void map(Genode::size_t offset, bool greedy = false) = 0; + virtual bool free(Genode::size_t offset) = 0; /************************ ** Avl_node interface ** @@ -89,8 +91,9 @@ namespace L4lx { Genode::cap_idx_alloc()->alloc_range(1)->kcap()) : Dataspace(name, size, ref), _cap(ds) {} - Genode::Dataspace_capability cap() { return _cap; } - bool map(Genode::size_t offset) { return true; } + Genode::Dataspace_capability cap() { return _cap; } + void map(Genode::size_t offset, bool greedy) { } + bool free(Genode::size_t offset) { return false; } }; @@ -98,72 +101,54 @@ namespace L4lx { { private: - class Chunk : public Genode::Avl_node - { - private: - - Genode::size_t _offset; - Genode::size_t _size; - Genode::Dataspace_capability _cap; - - public: - - Chunk(Genode::size_t off, Genode::size_t size, - Genode::Dataspace_capability cap) - : _offset(off), _size(size), _cap(cap) {} - - Genode::size_t offset() { return _offset; } - Genode::size_t size() { return _size; } - Genode::Dataspace_capability cap() { return _cap; } - - bool higher(Chunk *n) { return n->_offset > _offset; } - - Chunk *find_by_offset(Genode::size_t off) - { - if (off >= _offset && off < _offset+_size) return this; - - Chunk *n = Genode::Avl_node::child(off > _offset); - return n ? n->find_by_offset(off) : 0; - } - }; - - Genode::Rm_connection _rm; - Genode::Avl_tree _chunks; - Genode::size_t _chunk_size; - Genode::size_t _chunk_size_log2; + Genode::Rm_connection _rm_con; + Genode::Expanding_rm_session_client _rm; + Genode::Ram_dataspace_capability *_chunks; public: + enum { + CHUNK_SIZE_LOG2 = 20, + CHUNK_SIZE = 1 << CHUNK_SIZE_LOG2, + }; + Chunked_dataspace(const char* name, Genode::size_t size, - Fiasco::l4_cap_idx_t ref, - Genode::size_t chunk_size) - : Dataspace(name, size, ref), _rm(0, size), _chunk_size(chunk_size), - _chunk_size_log2(Genode::log2(_chunk_size)) {} - - Genode::Dataspace_capability cap() { return _rm.dataspace(); } - - bool map(Genode::size_t off) + Fiasco::l4_cap_idx_t ref) + : Dataspace(name, size, ref), _rm_con(0, size), _rm(_rm_con.cap()) { - off = Genode::align_addr((off-(_chunk_size-1)), _chunk_size_log2); + _chunks = (Genode::Ram_dataspace_capability*) Genode::env()->heap()->alloc( + sizeof(Genode::Ram_dataspace_capability) * (size/CHUNK_SIZE)); + } - Chunk* c = _chunks.first() ? _chunks.first()->find_by_offset(off) : 0; - if (c) return true; + Genode::Dataspace_capability cap() { return _rm_con.dataspace(); } - try { - Genode::Dataspace_capability cap = - Genode::env()->ram_session()->alloc(_chunk_size); - _chunks.insert(new (Genode::env()->heap()) - Chunk(off, _chunk_size, cap)); - _rm.attach(cap, 0, 0, true, off); - return true; - } catch(Genode::Ram_session::Quota_exceeded) { - PWRN("Could not allocate new dataspace chunk"); - } catch(Genode::Rm_session::Attach_failed) { - PWRN("Attach of chunk dataspace of size %zx to %p failed", - _chunk_size, (void*) off); + void map(Genode::size_t off, bool greedy) + { + off = Genode::align_addr((off-(CHUNK_SIZE-1)), CHUNK_SIZE_LOG2); + int i = off / CHUNK_SIZE; + if (_chunks[i].valid()) return; + + Genode::size_t ram_avail = Genode::env()->ram_session()->avail(); + if (greedy && ram_avail < 4*CHUNK_SIZE) { + char buf[128]; + Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd", + 4*CHUNK_SIZE - ram_avail); + Genode::env()->parent()->resource_request(buf); } - return false; + + _chunks[i] = Genode::env()->ram_session()->alloc(CHUNK_SIZE); + _rm.attach(_chunks[i], 0, 0, true, off, false); + } + + bool free(Genode::size_t off) + { + off = Genode::align_addr((off-(CHUNK_SIZE-1)), CHUNK_SIZE_LOG2); + int i = off / CHUNK_SIZE; + if (!_chunks[i].valid()) return false; + Genode::env()->ram_session()->free(_chunks[i]); + _chunks[i] = Genode::Ram_dataspace_capability(); + return true; } }; diff --git a/ports-foc/src/lib/l4lx/include/platform_env.h b/ports-foc/src/lib/l4lx/include/platform_env.h new file mode 100644 index 0000000000..ee1cec1f5e --- /dev/null +++ b/ports-foc/src/lib/l4lx/include/platform_env.h @@ -0,0 +1,111 @@ +/* + * \brief Platform environment of Genode process + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-28 + * + * This file is a generic variant of the platform environment, which is + * suitable for platforms such as L4ka::Pistachio and L4/Fiasco. On other + * platforms, it may be replaced by a platform-specific version residing + * in the corresponding 'base-' repository. + */ + +/* + * Copyright (C) 2006-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM_ENV_H_ +#define _PLATFORM_ENV_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +namespace Genode { + struct Expanding_rm_session_client; +} + + +/** + * Repeatedly try to execute a function 'func' + * + * If the function 'func' throws an exception of type 'EXC', the 'handler' + * is called and the function call is retried. + * + * \param EXC exception type to handle + * \param func functor to execute + * \param handler exception handler executed if 'func' raised an exception + * of type 'EXC' + * \param attempts number of attempts to execute 'func' before giving up + * and reflecting the exception 'EXC' to the caller. If not + * specified, attempt infinitely. + */ +template +auto retry(FUNC func, HANDLER handler, unsigned attempts = ~0U) -> decltype(func()) +{ + for (unsigned i = 0; attempts == ~0U || i < attempts; i++) + try { return func(); } + catch (EXC) { handler(); } + + throw EXC(); +} + + +/** + * Client object for a session that may get its session quota upgraded + */ +template +struct Upgradeable_client : CLIENT +{ + typedef Genode::Capability Capability; + + Capability _cap; + + Upgradeable_client(Capability cap) : CLIENT(cap), _cap(cap) { } + + void upgrade_ram(Genode::size_t quota) + { + PINF("upgrading quota donation for Env::%s (%zd bytes)", + CLIENT::Rpc_interface::service_name(), quota); + + char buf[128]; + Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd", quota); + + Genode::env()->parent()->upgrade(_cap, buf); + } +}; + + +struct Genode::Expanding_rm_session_client : Upgradeable_client +{ + Expanding_rm_session_client(Rm_session_capability cap) + : Upgradeable_client(cap) { } + + Local_addr attach(Dataspace_capability ds, size_t size, off_t offset, + bool use_local_addr, Local_addr local_addr, + bool executable) + { + return retry( + [&] () { + return Rm_session_client::attach(ds, size, offset, + use_local_addr, + local_addr, + executable); }, + [&] () { upgrade_ram(8*1024); }); + } + + Pager_capability add_client(Thread_capability thread) + { + return retry( + [&] () { return Rm_session_client::add_client(thread); }, + [&] () { upgrade_ram(8*1024); }); + } +}; + +#endif /* _PLATFORM_ENV_H_ */ diff --git a/ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc b/ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc index 69487b2222..004f147518 100644 --- a/ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc +++ b/ports-foc/src/lib/l4lx/l4_re_c_mem_alloc.cc @@ -14,7 +14,6 @@ /* Genode includes */ #include #include -#include #include /* L4lx includes */ @@ -31,36 +30,18 @@ static const bool DEBUG = false; extern "C" { - static const unsigned long _chunk_size() - { - enum { DEFAULT_CHUNK_SIZE = 16*1024*1024 }; - - Genode::Number_of_bytes result = DEFAULT_CHUNK_SIZE; - - try { - Genode::config()->xml_node().sub_node("ram") - .attribute("chunk_size") - .value(&result); - } catch(...) { } - - return result; - } - - long l4re_ma_alloc(unsigned long size, l4re_ds_t const mem, unsigned long flags) { - static const unsigned long chunk_size = _chunk_size(); - using namespace L4lx; if (DEBUG) PDBG("size=%lx mem=%lx flags=%lx", size, mem, flags); Dataspace *ds; - if (size > chunk_size) { + if (Genode::log2(size) >= Chunked_dataspace::CHUNK_SIZE_LOG2) { ds = new (Genode::env()->heap()) - Chunked_dataspace("lx_memory", size, mem, chunk_size); + Chunked_dataspace("lx_memory", size, mem); } else { Genode::Dataspace_capability cap = Genode::env()->ram_session()->alloc(size); diff --git a/ports-foc/src/lib/l4lx/l4x_pagefault.cc b/ports-foc/src/lib/l4lx/l4x_pagefault.cc index 260399f912..142322aa33 100644 --- a/ports-foc/src/lib/l4lx/l4x_pagefault.cc +++ b/ports-foc/src/lib/l4lx/l4x_pagefault.cc @@ -1,13 +1,66 @@ #include #include +#include +#include + namespace Fiasco { +#include +#include #include #include } -extern "C" L4_CV int l4x_forward_pf(Fiasco::l4_umword_t addr, - Fiasco::l4_umword_t pc, int extra_write) +static bool ballooning = false; +static Genode::Lock balloon_lock; + +namespace { + + class Signal_thread : public Genode::Thread<8192> + { + private: + + Fiasco::l4_cap_idx_t _cap; + Genode::Lock *_sync; + + protected: + + void entry() + { + using namespace Fiasco; + using namespace Genode; + + Signal_receiver receiver; + Signal_context rx; + Signal_context_capability cap(receiver.manage(&rx)); + Genode::env()->parent()->yield_sigh(cap); + _sync->unlock(); + + while (true) { + receiver.wait_for_signal(); + Genode::env()->parent()->yield_request(); + { + Genode::Lock::Guard guard(balloon_lock); + ballooning = true; + if (l4_error(l4_irq_trigger(_cap)) != -1) + PWRN("IRQ net trigger failed\n"); + } + } + } + + public: + + Signal_thread(Fiasco::l4_cap_idx_t cap, Genode::Lock *sync) + : Genode::Thread<8192>("net-signal-thread"), _cap(cap), _sync(sync) { + start(); } + }; +} + + +extern "C" { + +L4_CV int l4x_forward_pf(Fiasco::l4_umword_t addr, + Fiasco::l4_umword_t pc, int extra_write) { using namespace Fiasco; @@ -15,8 +68,16 @@ extern "C" L4_CV int l4x_forward_pf(Fiasco::l4_umword_t addr, Genode::addr_t ds_start_addr = addr; L4lx::Region *r = L4lx::Env::env()->rm()->find_region(&ds_start_addr, &size); L4lx::Dataspace *ds = r ? r->ds() : 0; - if (ds && !ds->map(addr - r->addr())) - return 0; + + while (ds) { + try { + ds->map(addr - r->addr(), !ballooning); + break; + } catch(Genode::Rm_session::Attach_failed) { + PWRN("Attach of chunk dataspace of failed"); + return 0; + } + } if (!extra_write) l4_touch_ro((void*)l4_trunc_page(addr), L4_LOG2_PAGESIZE); @@ -26,3 +87,36 @@ extern "C" L4_CV int l4x_forward_pf(Fiasco::l4_umword_t addr, } +Fiasco::l4_cap_idx_t genode_balloon_irq_cap() +{ + Linux::Irq_guard guard; + + static Genode::Native_capability cap = L4lx::vcpu_connection()->alloc_irq(); + static Genode::Lock lock(Genode::Lock::LOCKED); + static Signal_thread th(cap.dst(), &lock); + lock.lock(); + return cap.dst(); +} + + +bool genode_balloon_free_chunk(unsigned long addr) +{ + Linux::Irq_guard guard; + + Genode::addr_t ds_start_addr = addr; + Genode::size_t size = L4_PAGESIZE; + L4lx::Region *r = L4lx::Env::env()->rm()->find_region(&ds_start_addr, &size); + L4lx::Dataspace *ds = r ? r->ds() : 0; + return ds ? ds->free(addr - r->addr()) : false; +} + + +void genode_balloon_free_done() +{ + Linux::Irq_guard ig; + Genode::Lock::Guard guard(balloon_lock); + ballooning = false; + Genode::env()->parent()->yield_response(); +} + +} diff --git a/ports-foc/src/server/balloon/main.cc b/ports-foc/src/server/balloon/main.cc new file mode 100644 index 0000000000..877fadc68f --- /dev/null +++ b/ports-foc/src/server/balloon/main.cc @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + +class Session_component; +static List session_list; + + +class Session_component : public Rpc_object, + public List::Element +{ + private: + + Signal_context_capability _handler; + + public: + + Session_component() { + session_list.insert(this); } + + ~Session_component() { + session_list.remove(this); }; + + int increase_quota(Ram_session_capability ram_session, size_t amount) + { + PDBG("increase ram_quota of client by %zx", amount); + while (true) ; + return 0; + } + + void balloon_handler(Signal_context_capability handler) { + _handler = handler; } + + Signal_context_capability handler() { return _handler; } +}; + + +class Root : public Genode::Root_component +{ + protected: + + Session_component *_create_session(const char *args) + { + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota").ulong_value(0); + + if (ram_quota < sizeof(Session_component)) + throw Root::Quota_exceeded(); + + return new (md_alloc()) Session_component(); + } + + public: + + Root(Rpc_entrypoint *session_ep, + Allocator *md_alloc) + : Root_component(session_ep, md_alloc) { } +}; + + +int main() { + + enum { STACK_SIZE = 1024*sizeof(Genode::addr_t) }; + + static Timer::Connection timer; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "balloon_ep"); + static ::Root root(&ep, env()->heap()); + env()->parent()->announce(ep.manage(&root)); + + while (true) { + Session_component *c = session_list.first(); + while (c) { + if (c->handler().valid()) { + PINF("request memory from client!"); + Signal_transmitter transmitter(c->handler()); + transmitter.submit(); + } + c = c->next(); + } + timer.msleep(10000); + } + return 0; +} diff --git a/ports-foc/src/server/balloon/target.mk b/ports-foc/src/server/balloon/target.mk new file mode 100644 index 0000000000..56950d2012 --- /dev/null +++ b/ports-foc/src/server/balloon/target.mk @@ -0,0 +1,3 @@ +TARGET = balloon +LIBS = base +SRC_CC = main.cc