diff --git a/repos/os/run/init.run b/repos/os/run/init.run
index 56e58db4d..4f1c77038 100644
--- a/repos/os/run/init.run
+++ b/repos/os/run/init.run
@@ -743,6 +743,118 @@ append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -786,5 +898,5 @@ build_boot_image $boot_modules
append qemu_args " -nographic "
-run_genode_until {.*child "test-init" exited with exit value 0.*} 60
+run_genode_until {.*child "test-init" exited with exit value 0.*} 120
diff --git a/repos/os/src/app/dummy/main.cc b/repos/os/src/app/dummy/main.cc
index b010eab29..f6f965490 100644
--- a/repos/os/src/app/dummy/main.cc
+++ b/repos/os/src/app/dummy/main.cc
@@ -37,11 +37,27 @@ struct Dummy::Log_service
Heap _heap { _env.ram(), _env.rm() };
+ bool const _verbose;
+
struct Session_component : Rpc_object
{
Session_label const _label;
- Session_component(Session_label const &label) : _label(label) { }
+ bool const _verbose;
+
+ Session_component(Session_label const &label, bool verbose)
+ :
+ _label(label), _verbose(verbose)
+ {
+ if (_verbose)
+ log("opening session with label ", _label);
+ }
+
+ ~Session_component()
+ {
+ if (_verbose)
+ log("closing session with label ", _label);
+ }
size_t write(String const &string) override
{
@@ -60,17 +76,33 @@ struct Dummy::Log_service
struct Root : Root_component
{
- Root(Entrypoint &ep, Allocator &alloc) : Root_component(ep, alloc) { }
+ Ram_session &_ram;
+
+ bool const _verbose;
+
+ Root(Entrypoint &ep, Allocator &alloc, Ram_session &ram, bool verbose)
+ :
+ Root_component(ep, alloc), _ram(ram), _verbose(verbose)
+ { }
Session_component *_create_session(const char *args, Affinity const &) override
{
- return new (md_alloc()) Session_component(label_from_args(args));
+ return new (md_alloc()) Session_component(label_from_args(args), _verbose);
+ }
+
+ void _upgrade_session(Session_component *, const char *args) override
+ {
+ size_t const ram_quota =
+ Arg_string::find_arg(args, "ram_quota").ulong_value(0);
+
+ if (_ram.avail() >= ram_quota)
+ log("received session quota upgrade");
}
};
- Root _root { _env.ep(), _heap };
+ Root _root { _env.ep(), _heap, _env.ram(), _verbose };
- Log_service(Env &env) : _env(env)
+ Log_service(Env &env, bool verbose) : _env(env), _verbose(verbose)
{
_env.parent().announce(_env.ep().manage(_root));
log("created LOG service");
@@ -94,10 +126,21 @@ struct Dummy::Log_connections
{
unsigned const count = node.attribute_value("count", 0UL);
+ Number_of_bytes const ram_upgrade =
+ node.attribute_value("ram_upgrade", Number_of_bytes());
+
log("going to create ", count, " LOG connections");
- for (unsigned i = 0; i < count; i++)
- new (_heap) Connection(_connections, _env, Session_label { i });
+ for (unsigned i = 0; i < count; i++) {
+
+ Connection *connection =
+ new (_heap) Connection(_connections, _env, Session_label { i });
+
+ if (ram_upgrade > 0) {
+ log("upgrade connection ", i);
+ connection->upgrade_ram(ram_upgrade);
+ }
+ }
log("created all LOG connections");
}
@@ -212,7 +255,7 @@ struct Dummy::Main
_log_connections.destruct();
if (node.type() == "log_service")
- _log_service.construct(_env);
+ _log_service.construct(_env, node.attribute_value("verbose", false));
if (node.type() == "consume_ram")
_ram_consumer.consume(node.attribute_value("amount", Number_of_bytes()));
diff --git a/repos/os/src/init/buffered_xml.h b/repos/os/src/init/buffered_xml.h
index 8efda75b7..c3d183311 100644
--- a/repos/os/src/init/buffered_xml.h
+++ b/repos/os/src/init/buffered_xml.h
@@ -14,6 +14,9 @@
#ifndef _SRC__INIT__BUFFERED_XML_H_
#define _SRC__INIT__BUFFERED_XML_H_
+/* Genode includes */
+#include
+
namespace Init { class Buffered_xml; }
diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc
index ee2d61045..a95a5df03 100644
--- a/repos/os/src/init/main.cc
+++ b/repos/os/src/init/main.cc
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
namespace Init { struct Main; }
@@ -109,6 +110,8 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor,
Signal_handler _config_handler {
_env.ep(), *this, &Main::_handle_config };
+ Server _server { _env, _heap, _child_services, _state_reporter };
+
Main(Env &env) : _env(env)
{
_config.sigh(_config_handler);
@@ -358,6 +361,8 @@ void Init::Main::_handle_config()
*/
_children.for_each_child([&] (Child &child) { child.apply_ram_downgrade(); });
_children.for_each_child([&] (Child &child) { child.apply_ram_upgrade(); });
+
+ _server.apply_config(_config.xml());
}
diff --git a/repos/os/src/init/server.cc b/repos/os/src/init/server.cc
new file mode 100644
index 000000000..ae523247e
--- /dev/null
+++ b/repos/os/src/init/server.cc
@@ -0,0 +1,341 @@
+/*
+ * \brief Server role of init, forwarding session requests to children
+ * \author Norman Feske
+ * \date 2017-03-07
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+
+/***************************
+ ** Init::Server::Service **
+ ***************************/
+
+struct Init::Server::Service
+{
+ Registry::Element _registry_element;
+
+ Buffered_xml _service_node;
+
+ typedef Genode::Service::Name Name;
+
+ Registry &_child_services;
+
+ Name const _name { _service_node.xml().attribute_value("name", Name()) };
+
+ /**
+ * Constructor
+ *
+ * \param alloc allocator used for buffering the 'service_node'
+ */
+ Service(Registry &services,
+ Allocator &alloc,
+ Xml_node service_node,
+ Registry &child_services)
+ :
+ _registry_element(services, *this),
+ _service_node(alloc, service_node),
+ _child_services(child_services)
+ { }
+
+ /**
+ * Determine route to child service for a given label according
+ * to the node policy
+ *
+ * \throw Parent::Service_denied
+ */
+ Route resolve_session_request(Session_label const &);
+
+ Name name() const { return _name; }
+};
+
+
+Init::Server::Route
+Init::Server::Service::resolve_session_request(Session_label const &label)
+{
+ try {
+ Session_policy policy(label, _service_node.xml());
+
+ if (!policy.has_sub_node("child"))
+ throw Parent::Service_denied();
+
+ Xml_node target_node = policy.sub_node("child");
+
+ Child_policy::Name const child_name =
+ target_node.attribute_value("name", Child_policy::Name());
+
+ typedef String Label;
+ Label const target_label =
+ target_node.attribute_value("label", Label(label.string()));
+
+ Routed_service *match = nullptr;
+ _child_services.for_each([&] (Routed_service &service) {
+ if (service.child_name() == child_name)
+ match = &service; });
+
+ if (!match || match->abandoned())
+ throw Parent::Service_denied();
+
+ return Route { *match, target_label };
+ }
+ catch (Session_policy::No_policy_defined) {
+ throw Parent::Service_denied(); }
+}
+
+
+/******************
+ ** Init::Server **
+ ******************/
+
+Init::Server::Route
+Init::Server::_resolve_session_request(Service::Name const &service_name,
+ Session_label const &label)
+{
+ Service *matching_service = nullptr;
+ _services.for_each([&] (Service &service) {
+ if (service.name() == service_name)
+ matching_service = &service; });
+
+ if (!matching_service)
+ throw Parent::Service_denied();
+
+ return matching_service->resolve_session_request(label);
+}
+
+
+static void close_session(Genode::Session_state &session)
+{
+ session.phase = Genode::Session_state::CLOSE_REQUESTED;
+ session.service().initiate_request(session);
+ session.service().wakeup();
+}
+
+
+void Init::Server::session_ready(Session_state &session)
+{
+ _report_update_trigger.trigger_report_update();
+
+ /*
+ * If 'session_ready' is called as response to a session-quota upgrade,
+ * the 'phase' is set to 'CAP_HANDED_OUT' by 'Child::session_response'.
+ * We just need to forward the state change to our parent.
+ */
+ if (session.phase == Session_state::CAP_HANDED_OUT) {
+ Parent::Server::Id id { session.id_at_client().value };
+ _env.parent().session_response(id, Parent::SESSION_OK);
+ }
+
+ if (session.phase == Session_state::AVAILABLE) {
+ Parent::Server::Id id { session.id_at_client().value };
+ _env.parent().deliver_session_cap(id, session.cap);
+ session.phase = Session_state::CAP_HANDED_OUT;
+ }
+}
+
+
+void Init::Server::session_closed(Session_state &session)
+{
+ _report_update_trigger.trigger_report_update();
+
+ Parent::Server::Id id { session.id_at_client().value };
+ _env.parent().session_response(id, Parent::SESSION_CLOSED);
+
+ Ram_session_client(session.service().ram())
+ .transfer_quota(_env.ram_session_cap(), session.donated_ram_quota());
+
+ session.destroy();
+}
+
+
+void Init::Server::_handle_create_session_request(Xml_node request,
+ Parent::Client::Id id)
+{
+ if (!request.has_sub_node("args"))
+ return;
+
+ typedef Session_state::Args Args;
+ Args const args = request.sub_node("args").decoded_content();
+
+ Service::Name const name = request.attribute_value("service", Service::Name());
+
+ Session_label const label = label_from_args(args.string());
+
+ try {
+ Route const route = _resolve_session_request(name, label);
+
+ /*
+ * Reduce session quota by local session costs
+ */
+ char argbuf[Parent::Session_args::MAX_SIZE];
+ strncpy(argbuf, args.string(), sizeof(argbuf));
+
+ size_t const ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0);
+ size_t const keep_quota = route.service.factory().session_costs();
+
+ if (ram_quota < keep_quota)
+ throw Genode::Service::Quota_exceeded();
+
+ size_t const forward_ram_quota = ram_quota - keep_quota;
+
+ Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", forward_ram_quota);
+
+ Session_state &session =
+ route.service.create_session(route.service.factory(),
+ _client_id_space, id,
+ route.label, argbuf, Affinity());
+
+ /* transfer session quota */
+ if (_env.ram().transfer_quota(route.service.ram(), ram_quota)) {
+
+ /*
+ * This should never happen unless our parent missed to
+ * transfor the session quota to us prior issuing the session
+ * request.
+ */
+ warning("unable to transfer session quota (", ram_quota, " bytes) "
+ "of forwarded ", name, " session");
+ session.destroy();
+ throw Parent::Service_denied();
+ }
+
+ session.ready_callback = this;
+ session.closed_callback = this;
+
+ /* initiate request */
+ route.service.initiate_request(session);
+
+ /* if request was not handled synchronously, kick off async operation */
+ if (session.phase == Session_state::CREATE_REQUESTED)
+ route.service.wakeup();
+
+ if (session.phase == Session_state::INVALID_ARGS)
+ throw Parent::Service_denied();
+
+ if (session.phase == Session_state::QUOTA_EXCEEDED)
+ throw Genode::Service::Quota_exceeded();
+ }
+ catch (Parent::Service_denied) {
+ _env.parent().session_response(Parent::Server::Id { id.value }, Parent::INVALID_ARGS); }
+ catch (Genode::Service::Quota_exceeded) {
+ _env.parent().session_response(Parent::Server::Id { id.value }, Parent::QUOTA_EXCEEDED); }
+}
+
+
+void Init::Server::_handle_upgrade_session_request(Xml_node request,
+ Parent::Client::Id id)
+{
+ _client_id_space.apply(id, [&] (Session_state &session) {
+
+ size_t const ram_quota = request.attribute_value("ram_quota", 0UL);
+
+ session.phase = Session_state::UPGRADE_REQUESTED;
+
+ if (_env.ram().transfer_quota(session.service().ram(), ram_quota)) {
+ warning("unable to upgrade session quota (", ram_quota, " bytes) "
+ "of forwarded ", session.service().name(), " session");
+ return;
+ }
+
+ session.increase_donated_quota(ram_quota);
+ session.service().initiate_request(session);
+ session.service().wakeup();
+ });
+}
+
+
+void Init::Server::_handle_close_session_request(Xml_node request,
+ Parent::Client::Id id)
+{
+ _client_id_space.apply(id, [&] (Session_state &session) {
+ close_session(session); });
+}
+
+
+void Init::Server::_handle_session_request(Xml_node request)
+{
+ if (!request.has_attribute("id"))
+ return;
+
+ /*
+ * We use the 'Parent::Server::Id' of the incoming request as the
+ * 'Parent::Client::Id' of the forwarded request.
+ */
+ Parent::Client::Id const id { request.attribute_value("id", 0UL) };
+
+ if (request.has_type("create"))
+ _handle_create_session_request(request, id);
+
+ if (request.has_type("upgrade"))
+ _handle_upgrade_session_request(request, id);
+
+ if (request.has_type("close"))
+ _handle_close_session_request(request, id);
+}
+
+
+void Init::Server::_handle_session_requests()
+{
+ _session_requests->update();
+
+ Xml_node const requests = _session_requests->xml();
+
+ requests.for_each_sub_node([&] (Xml_node request) {
+ _handle_session_request(request); });
+
+ _report_update_trigger.trigger_report_update();
+}
+
+
+void Init::Server::apply_config(Xml_node config)
+{
+ _services.for_each([&] (Service &service) { destroy(_alloc, &service); });
+
+ config.for_each_sub_node("service", [&] (Xml_node node) {
+ new (_alloc) Service(_services, _alloc, node, _child_services); });
+
+ /*
+ * Construct mechanics for responding to our parent's session requests
+ * on demand.
+ */
+ bool services_provided = false;
+ _services.for_each([&] (Service const &) { services_provided = true; });
+
+ if (services_provided && !_session_requests.constructed()) {
+ _session_requests.construct(_env, "session_requests");
+ _session_request_handler.construct(_env.ep(), *this,
+ &Server::_handle_session_requests);
+ _session_requests->sigh(*_session_request_handler);
+
+ if (_session_requests.constructed())
+ _handle_session_requests();
+ }
+
+ /*
+ * Re-validate routes of existing sessions, close sessions whose routes
+ * changed.
+ */
+ _client_id_space.for_each([&] (Session_state &session) {
+ try {
+ Route const route = _resolve_session_request(session.service().name(),
+ session.client_label());
+
+ bool const route_unchanged = (route.service == session.service())
+ && (route.label == session.label());
+ if (!route_unchanged)
+ throw Parent::Service_denied();
+ }
+ catch (Parent::Service_denied) {
+ close_session(session); }
+ });
+}
diff --git a/repos/os/src/init/server.h b/repos/os/src/init/server.h
new file mode 100644
index 000000000..b458bb5fc
--- /dev/null
+++ b/repos/os/src/init/server.h
@@ -0,0 +1,112 @@
+/*
+ * \brief Server role of init, forwarding session requests to children
+ * \author Norman Feske
+ * \date 2017-03-07
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _SRC__INIT__SERVER_H_
+#define _SRC__INIT__SERVER_H_
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+#include
+#include
+#include
+
+namespace Init { class Server; }
+
+
+class Init::Server : Session_state::Ready_callback,
+ Session_state::Closed_callback
+{
+ private:
+
+ struct Route
+ {
+ Routed_service &service;
+ Session_label label;
+ };
+
+ Env &_env;
+
+ Allocator &_alloc;
+
+ /*
+ * ID space of requests originating from the parent
+ */
+ Id_space _server_id_space;
+
+ /*
+ * ID space of requests issued to the children of init
+ */
+ Id_space _client_id_space;
+
+ /**
+ * Meta data of service provided to our parent
+ */
+ struct Service;
+
+ Registry _services;
+
+ /**
+ * Services provided by our children
+ */
+ Registry &_child_services;
+
+ Report_update_trigger &_report_update_trigger;
+
+ Constructible _session_requests;
+ Constructible > _session_request_handler;
+
+ /**
+ * \throw Parent::Service_denied
+ */
+ Route _resolve_session_request(Genode::Service::Name const &,
+ Session_label const &);
+
+ void _handle_create_session_request (Xml_node, Parent::Client::Id);
+ void _handle_upgrade_session_request(Xml_node, Parent::Client::Id);
+ void _handle_close_session_request (Xml_node, Parent::Client::Id);
+
+ void _handle_session_request(Xml_node);
+ void _handle_session_requests();
+
+ /**
+ * Session_state::Closed_callback interface
+ */
+ void session_closed(Session_state &) override;
+
+ /**
+ * Session_state::Ready_callback interface
+ */
+ void session_ready(Session_state &) override;
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param alloc allocator used for buffering XML config data and
+ * for allocating per-service meta data
+ */
+ Server(Env &env, Allocator &alloc, Registry &services,
+ Report_update_trigger &report_update_trigger)
+ :
+ _env(env), _alloc(alloc), _child_services(services),
+ _report_update_trigger(report_update_trigger)
+ { }
+
+ void apply_config(Xml_node);
+};
+
+#endif /* _SRC__INIT__SERVER_H_ */
diff --git a/repos/os/src/init/service.h b/repos/os/src/init/service.h
index 3ee0cf33f..2172e3f02 100644
--- a/repos/os/src/init/service.h
+++ b/repos/os/src/init/service.h
@@ -14,10 +14,15 @@
#ifndef _SRC__INIT__SERVICE_H_
#define _SRC__INIT__SERVICE_H_
+/* Genode includes */
+#include
+#include
+
namespace Init {
class Abandonable;
class Parent_service;
class Routed_service;
+ class Forwarded_service;
}
@@ -68,6 +73,8 @@ class Init::Routed_service : public Child_service, public Abandonable
Ram_accessor &_ram_accessor;
+ Session_state::Factory &_factory;
+
Registry::Element _registry_element;
public:
@@ -91,12 +98,21 @@ class Init::Routed_service : public Child_service, public Abandonable
Child_service(server_id_space, factory, name,
Ram_session_capability(), wakeup),
_child_name(child_name), _ram_accessor(ram_accessor),
- _registry_element(services, *this)
+ _factory(factory), _registry_element(services, *this)
{ }
Child_name const &child_name() const { return _child_name; }
Ram_session_capability ram() const { return _ram_accessor.ram(); }
+
+ /**
+ * Return factory for creating/destroying session-state objects
+ *
+ * This accessor is solely meant to be used by 'Forwarded_service' to
+ * allocate session-state objects for sessions requested by init's
+ * parent.
+ */
+ Session_state::Factory &factory() { return _factory; }
};
#endif /* _SRC__INIT__SERVICE_H_ */
diff --git a/repos/os/src/init/state_reporter.h b/repos/os/src/init/state_reporter.h
index 3391f8cea..4b22e16b9 100644
--- a/repos/os/src/init/state_reporter.h
+++ b/repos/os/src/init/state_reporter.h
@@ -14,9 +14,13 @@
#ifndef _SRC__INIT__STATE_REPORTER_H_
#define _SRC__INIT__STATE_REPORTER_H_
+/* Genode includes */
#include
#include
+/* local includes */
+#include
+
namespace Init { class State_reporter; }
class Init::State_reporter : public Report_update_trigger
diff --git a/repos/os/src/init/target.mk b/repos/os/src/init/target.mk
index 9180584f6..f42589499 100644
--- a/repos/os/src/init/target.mk
+++ b/repos/os/src/init/target.mk
@@ -1,4 +1,4 @@
TARGET = init
-SRC_CC = main.cc child.cc
+SRC_CC = main.cc child.cc server.cc
LIBS = base
INC_DIR += $(PRG_DIR)
diff --git a/repos/os/src/init/types.h b/repos/os/src/init/types.h
index 46f08d80a..418723899 100644
--- a/repos/os/src/init/types.h
+++ b/repos/os/src/init/types.h
@@ -15,6 +15,7 @@
#define _SRC__INIT__TYPES_H_
#include
+#include
namespace Init {