From 4a1b545770d440a2965018092bb943b464985e11 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 9 Oct 2012 13:45:33 +0200 Subject: [PATCH] Move 'Child' API implementation to library --- base-codezero/src/core/target.inc | 2 +- base-fiasco/src/core/target.inc | 2 +- base-foc/src/core/target.inc | 2 +- base-host/src/core/target.inc | 2 +- base-hw/src/core/target.mk | 2 +- base-linux/src/core/target.mk | 2 +- base-mb/src/core/target.mk | 2 +- base-nova/src/core/target.inc | 2 +- base-okl4/src/core/target.inc | 4 +- base-pistachio/src/core/target.inc | 2 +- base/include/base/child.h | 399 +----------------- base/lib/mk/child.mk | 4 + base/src/base/child/child.cc | 440 ++++++++++++++++++++ base/src/test/rm_fault/target.mk | 2 +- demo/lib/mk/launchpad.mk | 2 +- gems/src/server/d3m/target.mk | 2 +- os/src/drivers/acpi/x86/target.mk | 2 +- os/src/init/target.mk | 2 +- os/src/server/loader/target.mk | 2 +- os/src/test/bomb/target.mk | 2 +- os/src/test/dynamic_config/master/target.mk | 2 +- ports/src/app/gdb_monitor/target.mk | 3 +- ports/src/noux/minimal/target.mk | 2 +- ports/src/noux/net/target.mk | 2 +- 24 files changed, 479 insertions(+), 409 deletions(-) create mode 100644 base/lib/mk/child.mk create mode 100644 base/src/base/child/child.cc diff --git a/base-codezero/src/core/target.inc b/base-codezero/src/core/target.inc index abda2b1bed..99879e7840 100644 --- a/base-codezero/src/core/target.inc +++ b/base-codezero/src/core/target.inc @@ -1,5 +1,5 @@ TARGET = core -LIBS = cxx ipc heap core_printf process pager lock \ +LIBS = cxx ipc heap core_printf child pager lock \ raw_signal raw_server GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base-fiasco/src/core/target.inc b/base-fiasco/src/core/target.inc index 393f30f127..dd56cdfdfc 100644 --- a/base-fiasco/src/core/target.inc +++ b/base-fiasco/src/core/target.inc @@ -1,6 +1,6 @@ TARGET = core REQUIRES = fiasco -LIBS = cxx ipc heap core_printf process pager lock raw_signal raw_server +LIBS = cxx ipc heap core_printf child pager lock raw_signal raw_server GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base-foc/src/core/target.inc b/base-foc/src/core/target.inc index 9b82b739da..14c1aca6fa 100644 --- a/base-foc/src/core/target.inc +++ b/base-foc/src/core/target.inc @@ -1,6 +1,6 @@ TARGET = core REQUIRES = foc -LIBS = cxx ipc heap core_printf process pager lock raw_signal raw_server +LIBS = cxx ipc heap core_printf child pager lock raw_signal raw_server LD_TEXT_ADDR = 0x500000 diff --git a/base-host/src/core/target.inc b/base-host/src/core/target.inc index 4e227348a3..a9a62a9395 100644 --- a/base-host/src/core/target.inc +++ b/base-host/src/core/target.inc @@ -1,5 +1,5 @@ TARGET = core -LIBS = cxx ipc heap core_printf process pager lock \ +LIBS = cxx ipc heap core_printf child pager lock \ raw_signal raw_server GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base-hw/src/core/target.mk b/base-hw/src/core/target.mk index d6ec95498c..c46076bedf 100644 --- a/base-hw/src/core/target.mk +++ b/base-hw/src/core/target.mk @@ -11,7 +11,7 @@ TARGET = core STARTUP_LIB = startup_core # add library dependencies -LIBS += cxx raw_ipc heap process pager lock console signal raw_server \ +LIBS += cxx raw_ipc heap child pager lock console signal raw_server \ syscall startup_core core_support # add include paths diff --git a/base-linux/src/core/target.mk b/base-linux/src/core/target.mk index 948d1a8b81..84e259b182 100644 --- a/base-linux/src/core/target.mk +++ b/base-linux/src/core/target.mk @@ -1,6 +1,6 @@ TARGET = core REQUIRES = linux -LIBS = cxx ipc heap core_printf process lock raw_server syscall rpath +LIBS = cxx ipc heap core_printf child lock raw_server syscall rpath GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base-mb/src/core/target.mk b/base-mb/src/core/target.mk index 88de7e1097..348c5788c4 100755 --- a/base-mb/src/core/target.mk +++ b/base-mb/src/core/target.mk @@ -1,5 +1,5 @@ TARGET = core -LIBS = kernel_core cxx ipc heap printf_microblaze process pager lock \ +LIBS = kernel_core cxx ipc heap printf_microblaze child pager lock \ raw_signal raw_server STARTUP_LIB = kernel_core diff --git a/base-nova/src/core/target.inc b/base-nova/src/core/target.inc index 090c27aa55..242beb0e11 100644 --- a/base-nova/src/core/target.inc +++ b/base-nova/src/core/target.inc @@ -1,5 +1,5 @@ TARGET = core -LIBS = cxx ipc heap core_printf process pager lock \ +LIBS = cxx ipc heap core_printf child pager lock \ raw_signal raw_server GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base-okl4/src/core/target.inc b/base-okl4/src/core/target.inc index 739e1aaf9d..9a89ee3a49 100644 --- a/base-okl4/src/core/target.inc +++ b/base-okl4/src/core/target.inc @@ -1,7 +1,7 @@ TARGET = core REQUIRES = okl4 -LIBS = cxx ipc heap core_printf process pager lock \ - raw_signal raw_server bootinfo +LIBS = cxx ipc heap core_printf child pager lock \ + raw_signal raw_server bootinfo child GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base-pistachio/src/core/target.inc b/base-pistachio/src/core/target.inc index eed6e1e14a..ad5b6b5d92 100644 --- a/base-pistachio/src/core/target.inc +++ b/base-pistachio/src/core/target.inc @@ -1,6 +1,6 @@ TARGET = core REQUIRES = pistachio -LIBS = cxx ipc heap core_printf process pager lock \ +LIBS = cxx ipc heap core_printf child pager lock \ raw_signal raw_server kip hexdump GEN_CORE_DIR = $(BASE_DIR)/src/core diff --git a/base/include/base/child.h b/base/include/base/child.h index 3728554a23..8eba59b2d7 100644 --- a/base/include/base/child.h +++ b/base/include/base/child.h @@ -125,151 +125,7 @@ namespace Genode { { private: - /** - * Representation of an open session - */ - class Session : public Object_pool::Entry, - public List::Element - { - private: - - enum { IDENT_LEN = 16 }; - - /** - * Session capability at the server - */ - Session_capability _cap; - - /** - * Service interface that was used to create the session - */ - Service *_service; - - /** - * Server implementing the session - * - * Even though we can normally determine the server of the - * session via '_service->server()', this does not apply - * when destructing a server. During destruction, we use - * the 'Server' pointer as opaque key for revoking active - * sessions of the server. So we keep a copy independent of - * the 'Service' object. - */ - Server *_server; - - /** - * Total of quota associated with this session - */ - size_t _donated_ram_quota; - - /** - * Name of session, used for debugging - */ - char _ident[IDENT_LEN]; - - public: - - /** - * Constructor - * - * \param session session capability - * \param service service that implements the session - * \param ram_quota initial quota donation associated with - * the session - * \param ident optional session identifier, used for - * debugging - */ - Session(Session_capability session, Service *service, - size_t ram_quota, const char *ident = "") - : - Object_pool::Entry(session), _cap(session), - _service(service), _server(service->server()), - _donated_ram_quota(ram_quota) { - strncpy(_ident, ident, sizeof(_ident)); } - - /** - * Default constructor creates invalid session - */ - Session() : _service(0), _donated_ram_quota(0) { } - - /** - * Extend amount of ram attached to the session - */ - void upgrade_ram_quota(size_t ram_quota) { - _donated_ram_quota += ram_quota; } - - /** - * Accessors - */ - Session_capability cap() const { return _cap; } - size_t donated_ram_quota() const { return _donated_ram_quota; } - bool valid() const { return _service != 0; } - Service *service() const { return _service; } - Server *server() const { return _server; } - const char *ident() const { return _ident; } - }; - - - /** - * Guard for transferring quota donation - * - * This class is used to provide transactional semantics of quota - * transfers. Establishing a new session involves several steps, in - * particular subsequent quota transfers. If one intermediate step - * fails, we need to revert all quota transfers that already took - * place. When instantated at a local scope, a 'Transfer' object - * guards a quota transfer. If the scope is left without prior an - * explicit acknowledgement of the transfer (for example via an - * exception), the destructor the 'Transfer' object reverts the - * transfer in flight. - */ - class Transfer { - - bool _ack; - size_t _quantum; - Ram_session_capability _from; - Ram_session_capability _to; - - public: - - /** - * Constructor - * - * \param quantim number of bytes to transfer - * \param from donator RAM session - * \param to receiver RAM session - */ - Transfer(size_t quantum, - Ram_session_capability from, - Ram_session_capability to) - : _ack(false), _quantum(quantum), _from(from), _to(to) - { - if (_from.valid() && _to.valid() && - Ram_session_client(_from).transfer_quota(_to, quantum)) { - PWRN("not enough quota for a donation of %zd bytes", quantum); - throw Quota_exceeded(); - } - } - - /** - * Destructor - * - * The destructor will be called when leaving the scope of - * the 'session' function. If the scope is left because of - * an error (e.g., an exception), the donation will be - * reverted. - */ - ~Transfer() - { - if (!_ack && _from.valid() && _to.valid()) - Ram_session_client(_to).transfer_quota(_from, _quantum); - } - - /** - * Acknowledge quota donation - */ - void acknowledge() { _ack = true; } - }; + class Session; /* RAM session that contains the quota of the child */ Ram_session_capability _ram; @@ -316,43 +172,12 @@ namespace Genode { * \throw Ram_session::Quota_exceeded the child's heap partition cannot * hold the session meta data */ - void _add_session(const Session &s) - { - Lock::Guard lock_guard(_lock); - - /* - * Store session information in a new child's meta data - * structure. The allocation from 'heap()' may throw a - * 'Ram_session::Quota_exceeded' exception. - */ - Session *session = 0; - try { - session = new (heap()) - Session(s.cap(), s.service(), - s.donated_ram_quota(), s.ident()); } - catch (Allocator::Out_of_memory) { - throw Parent::Quota_exceeded(); } - - /* these functions may also throw 'Ram_session::Quota_exceeded' */ - _session_pool.insert(session); - _session_list.insert(session); - } + void _add_session(const Session &s); /** * Close session and revert quota donation associated with it */ - void _remove_session(Session *s) - { - /* forget about this session */ - _session_pool.remove(s); - _session_list.remove(s); - - /* return session quota to the ram session of the child */ - if (_policy->ref_ram_session()->transfer_quota(_ram, s->donated_ram_quota())) - PERR("We ran out of our own quota"); - - destroy(heap(), s); - } + void _remove_session(Session *s); /** * Return service interface targetting the parent @@ -362,11 +187,7 @@ namespace Genode { * solely used for targeting resource donations during * 'Parent::upgrade_quota()' calls. */ - static Service *_parent_service() - { - static Parent_service parent_service(""); - return &parent_service; - } + static Service *_parent_service(); public: @@ -403,18 +224,7 @@ namespace Genode { Child_policy *policy, Service &ram_service = *_parent_service(), Service &cpu_service = *_parent_service(), - Service &rm_service = *_parent_service()) - : - _ram(ram), _ram_session_client(ram), _cpu(cpu), _rm(rm), - _ram_service(ram_service), _cpu_service(cpu_service), - _rm_service(rm_service), - _heap(&_ram_session_client, env()->rm_session()), - _entrypoint(entrypoint), - _parent_cap(_entrypoint->manage(this)), - _policy(policy), - _server(ram), - _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), 0) - { } + Service &rm_service = *_parent_service()); /** * Destructor @@ -422,14 +232,7 @@ namespace Genode { * On destruction of a child, we close all sessions of the child to * other services. */ - virtual ~Child() - { - _entrypoint->dissolve(this); - _policy->unregister_services(); - - for (Session *s; (s = _session_pool.first()); ) - close(s->cap()); - } + virtual ~Child(); /** * Return heap that uses the child's quota @@ -450,196 +253,18 @@ namespace Genode { * that the specified server object may not exist anymore. We do * not de-reference the server argument in here! */ - void revoke_server(const Server *server) - { - Lock::Guard lock_guard(_lock); - - while (1) { - /* search session belonging to the specified server */ - Session *s = _session_list.first(); - for ( ; s && (s->server() != server); s = s->next()); - - /* if no matching session exists, we are done */ - if (!s) return; - - _remove_session(s); - } - } + void revoke_server(const Server *server); /********************** ** Parent interface ** **********************/ - void announce(Service_name const &name, Root_capability root) - { - if (!name.is_valid_string()) return; - - _policy->announce_service(name.string(), root, heap(), &_server); - } - - Session_capability session(Service_name const &name, Session_args const &args) - { - if (!name.is_valid_string() || !args.is_valid_string()) throw Unavailable(); - - /* return sessions that we created for the child */ - if (!strcmp("Env::ram_session", name.string())) return _ram; - if (!strcmp("Env::cpu_session", name.string())) return _cpu; - if (!strcmp("Env::rm_session", name.string())) return _rm; - if (!strcmp("Env::pd_session", name.string())) return _process.pd_session_cap(); - - /* filter session arguments according to the child policy */ - strncpy(_args, args.string(), sizeof(_args)); - _policy->filter_session_args(name.string(), _args, sizeof(_args)); - - /* transfer the quota donation from the child's account to ourself */ - size_t ram_quota = Arg_string::find_arg(_args, "ram_quota").long_value(0); - - Transfer donation_from_child(ram_quota, _ram, env()->ram_session_cap()); - - Service *service = _policy->resolve_session_request(name.string(), _args); - - /* raise an error if no matching service provider could be found */ - if (!service) - throw Service_denied(); - - /* transfer session quota from ourself to the service provider */ - Transfer donation_to_service(ram_quota, env()->ram_session_cap(), - service->ram_session_cap()); - - /* create session */ - Session_capability cap; - try { cap = service->session(_args); } - catch (Service::Invalid_args) { throw Service_denied(); } - catch (Service::Unavailable) { throw Service_denied(); } - catch (Service::Quota_exceeded) { throw Quota_exceeded(); } - - /* register session */ - try { _add_session(Session(cap, service, ram_quota, name.string())); } - catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); } - - /* finish transaction */ - donation_from_child.acknowledge(); - donation_to_service.acknowledge(); - - return cap; - } - - void upgrade(Session_capability to_session, Upgrade_args const &args) - { - Service *targeted_service = 0; - - /* check of upgrade refers to an Env:: resource */ - if (to_session.local_name() == _ram.local_name()) - targeted_service = &_ram_service; - if (to_session.local_name() == _cpu.local_name()) - targeted_service = &_cpu_service; - if (to_session.local_name() == _rm.local_name()) - targeted_service = &_rm_service; - - /* check if upgrade refers to server */ - Session * const session = _session_pool.obj_by_cap(to_session); - if (session) - targeted_service = session->service(); - - if (!targeted_service) { - PWRN("could not lookup service for session upgrade"); - return; - } - - if (!args.is_valid_string()) { - PWRN("no valid session-upgrade arguments"); - return; - } - - size_t const ram_quota = - Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); - - /* transfer quota from client to ourself */ - Transfer donation_from_child(ram_quota, _ram, - env()->ram_session_cap()); - - /* transfer session quota from ourself to the service provider */ - Transfer donation_to_service(ram_quota, env()->ram_session_cap(), - targeted_service->ram_session_cap()); - - try { targeted_service->upgrade(to_session, args.string()); } - catch (Service::Quota_exceeded) { throw Quota_exceeded(); } - - /* remember new amount attached to the session */ - if (session) - session->upgrade_ram_quota(ram_quota); - - /* finish transaction */ - donation_from_child.acknowledge(); - donation_to_service.acknowledge(); - } - - void close(Session_capability session_cap) - { - /* refuse to close the child's initial sessions */ - if (session_cap.local_name() == _ram.local_name() - || session_cap.local_name() == _cpu.local_name() - || session_cap.local_name() == _rm.local_name() - || session_cap.local_name() == _process.pd_session_cap().local_name()) - return; - - Session *s = _session_pool.obj_by_cap(session_cap); - - if (!s) { - PWRN("no session structure found"); - return; - } - - /* - * There is a chance that the server is not responding to - * the 'close' call, making us block infinitely. However, - * by using core's cancel-blocking mechanism, we can cancel - * the 'close' call by another (watchdog) thread that - * invokes 'cancel_blocking' at our thread after a timeout. - * The unblocking is reflected at the API level as an - * 'Blocking_canceled' exception. We catch this exception - * to proceed with normal operation after being unblocked. - */ - try { s->service()->close(s->cap()); } - catch (Blocking_canceled) { - PDBG("Got Blocking_canceled exception during %s->close call\n", - s->ident()); } - - /* - * If the session was provided by a child of us, - * 'server()->ram_session_cap()' returns the RAM session of the - * corresponding child. Since the session to the server is - * closed now, we expect that the server released all donated - * resources and we can decrease the servers' quota. - * - * If this goes wrong, the server is misbehaving. - */ - if (s->service()->ram_session_cap().valid()) { - Ram_session_client server_ram(s->service()->ram_session_cap()); - if (server_ram.transfer_quota(env()->ram_session_cap(), - s->donated_ram_quota())) { - PERR("Misbehaving server '%s'!", s->service()->name()); - } - } - - Lock::Guard lock_guard(_lock); - _remove_session(s); - } - - void exit(int exit_value) - { - /* - * This function receives the hint from the child that now, its - * a good time to kill it. An inherited child class could use - * this hint to schedule the destruction of the child object. - * - * Note that the child object must not be destructed from by - * this function because it is executed by the thread contained - * in the child object. - */ - return _policy->exit(exit_value); - } + void announce(Service_name const &, Root_capability); + Session_capability session(Service_name const &, Session_args const &); + void upgrade(Session_capability, Upgrade_args const &); + void close(Session_capability); + void exit(int); }; } diff --git a/base/lib/mk/child.mk b/base/lib/mk/child.mk new file mode 100644 index 0000000000..f868b0daa8 --- /dev/null +++ b/base/lib/mk/child.mk @@ -0,0 +1,4 @@ +SRC_CC = child.cc +LIBS += process + +vpath child.cc $(REP_DIR)/src/base/child diff --git a/base/src/base/child/child.cc b/base/src/base/child/child.cc new file mode 100644 index 0000000000..3dae88c92d --- /dev/null +++ b/base/src/base/child/child.cc @@ -0,0 +1,440 @@ +/* + * \brief Child creation framework + * \author Norman Feske + * \date 2006-07-22 + */ + +/* + * Copyright (C) 2006-2012 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. + */ + +#include + +using namespace Genode; + + +/*************** + ** Utilities ** + ***************/ + +namespace { + + /** + * Guard for transferring quota donation + * + * This class is used to provide transactional semantics of quota + * transfers. Establishing a new session involves several steps, in + * particular subsequent quota transfers. If one intermediate step + * fails, we need to revert all quota transfers that already took + * place. When instantated at a local scope, a 'Transfer' object guards + * a quota transfer. If the scope is left without prior an explicit + * acknowledgement of the transfer (for example via an exception), the + * destructor the 'Transfer' object reverts the transfer in flight. + */ + class Transfer { + + bool _ack; + size_t _quantum; + Ram_session_capability _from; + Ram_session_capability _to; + + public: + + /** + * Constructor + * + * \param quantim number of bytes to transfer + * \param from donator RAM session + * \param to receiver RAM session + */ + Transfer(size_t quantum, + Ram_session_capability from, + Ram_session_capability to) + : _ack(false), _quantum(quantum), _from(from), _to(to) + { + if (_from.valid() && _to.valid() && + Ram_session_client(_from).transfer_quota(_to, quantum)) { + PWRN("not enough quota for a donation of %zd bytes", quantum); + throw Parent::Quota_exceeded(); + } + } + + /** + * Destructor + * + * The destructor will be called when leaving the scope of the + * 'session' function. If the scope is left because of an error + * (e.g., an exception), the donation will be reverted. + */ + ~Transfer() + { + if (!_ack && _from.valid() && _to.valid()) + Ram_session_client(_to).transfer_quota(_from, _quantum); + } + + /** + * Acknowledge quota donation + */ + void acknowledge() { _ack = true; } + }; +} + + +/******************** + ** Child::Session ** + ********************/ + +class Child::Session : public Object_pool::Entry, + public List::Element +{ + private: + + enum { IDENT_LEN = 16 }; + + /** + * Session capability at the server + */ + Session_capability _cap; + + /** + * Service interface that was used to create the session + */ + Service *_service; + + /** + * Server implementing the session + * + * Even though we can normally determine the server of the session via + * '_service->server()', this does not apply when destructing a server. + * During destruction, we use the 'Server' pointer as opaque key for + * revoking active sessions of the server. So we keep a copy + * independent of the 'Service' object. + */ + Server *_server; + + /** + * Total of quota associated with this session + */ + size_t _donated_ram_quota; + + /** + * Name of session, used for debugging + */ + char _ident[IDENT_LEN]; + + public: + + /** + * Constructor + * + * \param session session capability + * \param service service that implements the session + * \param ram_quota initial quota donation associated with + * the session + * \param ident optional session identifier, used for + * debugging + */ + Session(Session_capability session, Service *service, + size_t ram_quota, const char *ident = "") + : + Object_pool::Entry(session), _cap(session), + _service(service), _server(service->server()), + _donated_ram_quota(ram_quota) { + strncpy(_ident, ident, sizeof(_ident)); } + + /** + * Default constructor creates invalid session + */ + Session() : _service(0), _donated_ram_quota(0) { } + + /** + * Extend amount of ram attached to the session + */ + void upgrade_ram_quota(size_t ram_quota) { + _donated_ram_quota += ram_quota; } + + /** + * Accessors + */ + Session_capability cap() const { return _cap; } + size_t donated_ram_quota() const { return _donated_ram_quota; } + bool valid() const { return _service != 0; } + Service *service() const { return _service; } + Server *server() const { return _server; } + const char *ident() const { return _ident; } +}; + + +/*********** + ** Child ** + ***********/ + +void Child::_add_session(Child::Session const &s) +{ + Lock::Guard lock_guard(_lock); + + /* + * Store session information in a new child's meta data structure. The + * allocation from 'heap()' may throw a 'Ram_session::Quota_exceeded' + * exception. + */ + Session *session = 0; + try { + session = new (heap()) + Session(s.cap(), s.service(), + s.donated_ram_quota(), s.ident()); } + catch (Allocator::Out_of_memory) { + throw Parent::Quota_exceeded(); } + + /* these functions may also throw 'Ram_session::Quota_exceeded' */ + _session_pool.insert(session); + _session_list.insert(session); +} + + +void Child::_remove_session(Child::Session *s) +{ + /* forget about this session */ + _session_pool.remove(s); + _session_list.remove(s); + + /* return session quota to the ram session of the child */ + if (_policy->ref_ram_session()->transfer_quota(_ram, s->donated_ram_quota())) + PERR("We ran out of our own quota"); + + destroy(heap(), s); +} + + +Service *Child::_parent_service() +{ + static Parent_service parent_service(""); + return &parent_service; +} + + +void Child::revoke_server(Server const *server) +{ + Lock::Guard lock_guard(_lock); + + while (1) { + /* search session belonging to the specified server */ + Session *s = _session_list.first(); + for ( ; s && (s->server() != server); s = s->next()); + + /* if no matching session exists, we are done */ + if (!s) return; + + _remove_session(s); + } +} + + +void Child::announce(Parent::Service_name const &name, Root_capability root) +{ + if (!name.is_valid_string()) return; + + _policy->announce_service(name.string(), root, heap(), &_server); +} + + +Session_capability Child::session(Parent::Service_name const &name, + Parent::Session_args const &args) +{ + if (!name.is_valid_string() || !args.is_valid_string()) throw Unavailable(); + + /* return sessions that we created for the child */ + if (!strcmp("Env::ram_session", name.string())) return _ram; + if (!strcmp("Env::cpu_session", name.string())) return _cpu; + if (!strcmp("Env::rm_session", name.string())) return _rm; + if (!strcmp("Env::pd_session", name.string())) return _process.pd_session_cap(); + + /* filter session arguments according to the child policy */ + strncpy(_args, args.string(), sizeof(_args)); + _policy->filter_session_args(name.string(), _args, sizeof(_args)); + + /* transfer the quota donation from the child's account to ourself */ + size_t ram_quota = Arg_string::find_arg(_args, "ram_quota").long_value(0); + + Transfer donation_from_child(ram_quota, _ram, env()->ram_session_cap()); + + Service *service = _policy->resolve_session_request(name.string(), _args); + + /* raise an error if no matching service provider could be found */ + if (!service) + throw Service_denied(); + + /* transfer session quota from ourself to the service provider */ + Transfer donation_to_service(ram_quota, env()->ram_session_cap(), + service->ram_session_cap()); + + /* create session */ + Session_capability cap; + try { cap = service->session(_args); } + catch (Service::Invalid_args) { throw Service_denied(); } + catch (Service::Unavailable) { throw Service_denied(); } + catch (Service::Quota_exceeded) { throw Quota_exceeded(); } + + /* register session */ + try { _add_session(Session(cap, service, ram_quota, name.string())); } + catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); } + + /* finish transaction */ + donation_from_child.acknowledge(); + donation_to_service.acknowledge(); + + return cap; +} + + +void Child::upgrade(Session_capability to_session, Parent::Upgrade_args const &args) +{ + Service *targeted_service = 0; + + /* check of upgrade refers to an Env:: resource */ + if (to_session.local_name() == _ram.local_name()) + targeted_service = &_ram_service; + if (to_session.local_name() == _cpu.local_name()) + targeted_service = &_cpu_service; + if (to_session.local_name() == _rm.local_name()) + targeted_service = &_rm_service; + + /* check if upgrade refers to server */ + Session * const session = _session_pool.obj_by_cap(to_session); + if (session) + targeted_service = session->service(); + + if (!targeted_service) { + PWRN("could not lookup service for session upgrade"); + return; + } + + if (!args.is_valid_string()) { + PWRN("no valid session-upgrade arguments"); + return; + } + + size_t const ram_quota = + Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + /* transfer quota from client to ourself */ + Transfer donation_from_child(ram_quota, _ram, + env()->ram_session_cap()); + + /* transfer session quota from ourself to the service provider */ + Transfer donation_to_service(ram_quota, env()->ram_session_cap(), + targeted_service->ram_session_cap()); + + try { targeted_service->upgrade(to_session, args.string()); } + catch (Service::Quota_exceeded) { throw Quota_exceeded(); } + + /* remember new amount attached to the session */ + if (session) + session->upgrade_ram_quota(ram_quota); + + /* finish transaction */ + donation_from_child.acknowledge(); + donation_to_service.acknowledge(); +} + + +void Child::close(Session_capability session_cap) +{ + /* refuse to close the child's initial sessions */ + if (session_cap.local_name() == _ram.local_name() + || session_cap.local_name() == _cpu.local_name() + || session_cap.local_name() == _rm.local_name() + || session_cap.local_name() == _process.pd_session_cap().local_name()) + return; + + Session *s = _session_pool.obj_by_cap(session_cap); + + if (!s) { + PWRN("no session structure found"); + return; + } + + /* + * There is a chance that the server is not responding to the 'close' call, + * making us block infinitely. However, by using core's cancel-blocking + * mechanism, we can cancel the 'close' call by another (watchdog) thread + * that invokes 'cancel_blocking' at our thread after a timeout. The + * unblocking is reflected at the API level as an 'Blocking_canceled' + * exception. We catch this exception to proceed with normal operation + * after being unblocked. + */ + try { s->service()->close(s->cap()); } + catch (Blocking_canceled) { + PDBG("Got Blocking_canceled exception during %s->close call\n", + s->ident()); } + + /* + * If the session was provided by a child of us, + * 'server()->ram_session_cap()' returns the RAM session of the + * corresponding child. Since the session to the server is closed now, we + * expect that the server released all donated resources and we can + * decrease the servers' quota. + * + * If this goes wrong, the server is misbehaving. + */ + if (s->service()->ram_session_cap().valid()) { + Ram_session_client server_ram(s->service()->ram_session_cap()); + if (server_ram.transfer_quota(env()->ram_session_cap(), + s->donated_ram_quota())) { + PERR("Misbehaving server '%s'!", s->service()->name()); + } + } + + Lock::Guard lock_guard(_lock); + _remove_session(s); +} + + +void Child::exit(int exit_value) +{ + /* + * This function receives the hint from the child that now, its a good time + * to kill it. An inherited child class could use this hint to schedule the + * destruction of the child object. + * + * Note that the child object must not be destructed from by this function + * because it is executed by the thread contained in the child object. + */ + return _policy->exit(exit_value); +} + + +Child::Child(Dataspace_capability elf_ds, + Ram_session_capability ram, + Cpu_session_capability cpu, + Rm_session_capability rm, + Rpc_entrypoint *entrypoint, + Child_policy *policy, + Service &ram_service, + Service &cpu_service, + Service &rm_service) +: + _ram(ram), _ram_session_client(ram), _cpu(cpu), _rm(rm), + _ram_service(ram_service), _cpu_service(cpu_service), + _rm_service(rm_service), + _heap(&_ram_session_client, env()->rm_session()), + _entrypoint(entrypoint), + _parent_cap(_entrypoint->manage(this)), + _policy(policy), + _server(ram), + _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), 0) +{ } + + +Child::~Child() +{ + _entrypoint->dissolve(this); + _policy->unregister_services(); + + for (Session *s; (s = _session_pool.first()); ) + close(s->cap()); +} + diff --git a/base/src/test/rm_fault/target.mk b/base/src/test/rm_fault/target.mk index 394c004bb6..f9a4708d8c 100644 --- a/base/src/test/rm_fault/target.mk +++ b/base/src/test/rm_fault/target.mk @@ -1,3 +1,3 @@ TARGET = test-rmfault SRC_CC = main.cc -LIBS = cxx env server signal process +LIBS = cxx env server signal child diff --git a/demo/lib/mk/launchpad.mk b/demo/lib/mk/launchpad.mk index 86e54a9ecb..bb257154d8 100644 --- a/demo/lib/mk/launchpad.mk +++ b/demo/lib/mk/launchpad.mk @@ -1,4 +1,4 @@ -LIBS = process +LIBS = child SRC_CC = launchpad.cc vpath launchpad.cc $(REP_DIR)/src/lib/launchpad diff --git a/gems/src/server/d3m/target.mk b/gems/src/server/d3m/target.mk index cbf0e1257e..9c7e7eaf07 100644 --- a/gems/src/server/d3m/target.mk +++ b/gems/src/server/d3m/target.mk @@ -1,4 +1,4 @@ TARGET = d3m SRC_CC = main.cc -LIBS = cxx env signal server process +LIBS = cxx env signal server child INC_DIR += $(PRG_DIR) diff --git a/os/src/drivers/acpi/x86/target.mk b/os/src/drivers/acpi/x86/target.mk index 9b04a85fdb..48e40ac95f 100644 --- a/os/src/drivers/acpi/x86/target.mk +++ b/os/src/drivers/acpi/x86/target.mk @@ -1,7 +1,7 @@ TARGET = acpi_drv REQUIRES = x86_32 SRC_CC = main.cc acpi.cc -LIBS = cxx env server process +LIBS = cxx env server child INC_DIR = $(PRG_DIR)/.. diff --git a/os/src/init/target.mk b/os/src/init/target.mk index a4445b8461..f9ba59e110 100644 --- a/os/src/init/target.mk +++ b/os/src/init/target.mk @@ -1,3 +1,3 @@ TARGET = init SRC_CC = main.cc -LIBS = env cxx server process +LIBS = env cxx server child diff --git a/os/src/server/loader/target.mk b/os/src/server/loader/target.mk index 9759623c6b..e66f2ad042 100644 --- a/os/src/server/loader/target.mk +++ b/os/src/server/loader/target.mk @@ -1,4 +1,4 @@ TARGET = loader -LIBS = cxx env thread server process signal +LIBS = cxx env thread server child signal SRC_CC = main.cc INC_DIR += $(PRG_DIR) diff --git a/os/src/test/bomb/target.mk b/os/src/test/bomb/target.mk index 6b6de6fda3..0e8b3c97e9 100644 --- a/os/src/test/bomb/target.mk +++ b/os/src/test/bomb/target.mk @@ -1,3 +1,3 @@ TARGET = bomb -LIBS = cxx env thread process server +LIBS = cxx env thread child server SRC_CC = main.cc diff --git a/os/src/test/dynamic_config/master/target.mk b/os/src/test/dynamic_config/master/target.mk index 32f1efa663..664a335317 100644 --- a/os/src/test/dynamic_config/master/target.mk +++ b/os/src/test/dynamic_config/master/target.mk @@ -1,3 +1,3 @@ TARGET = test-dynamic_config_master SRC_CC = main.cc -LIBS = env signal server process +LIBS = env signal server child diff --git a/ports/src/app/gdb_monitor/target.mk b/ports/src/app/gdb_monitor/target.mk index de5128f74f..209eba3494 100644 --- a/ports/src/app/gdb_monitor/target.mk +++ b/ports/src/app/gdb_monitor/target.mk @@ -10,7 +10,8 @@ INC_DIR += $(GDB_CONTRIB_DIR)/include \ $(PRG_DIR)/gdbserver \ $(PRG_DIR) -LIBS = env signal libc libc_log libc_terminal libc_lock_pipe lock process server gdbserver_platform gdbserver_libc_support +LIBS = env signal libc libc_log libc_terminal libc_lock_pipe lock child \ + server gdbserver_platform gdbserver_libc_support SRC_C = event-loop.c \ i386-low.c \ diff --git a/ports/src/noux/minimal/target.mk b/ports/src/noux/minimal/target.mk index 6411fc9b63..c9f5fc92f4 100644 --- a/ports/src/noux/minimal/target.mk +++ b/ports/src/noux/minimal/target.mk @@ -1,5 +1,5 @@ TARGET = noux -LIBS = cxx env server process signal thread alarm +LIBS = cxx env server child signal thread alarm SRC_CC = main.cc dummy_net.cc INC_DIR += $(PRG_DIR) INC_DIR += $(PRG_DIR)/../ diff --git a/ports/src/noux/net/target.mk b/ports/src/noux/net/target.mk index 0d680f9916..159a0d8702 100644 --- a/ports/src/noux/net/target.mk +++ b/ports/src/noux/net/target.mk @@ -1,5 +1,5 @@ TARGET = noux_net -LIBS = cxx env server process signal lwip thread alarm +LIBS = cxx env server child signal lwip thread alarm LIBS += libc libc_lwip