genode/repos/os/include/os/slave.h

338 lines
8.7 KiB
C
Raw Permalink Normal View History

/*
* \brief Convenience helper for running a service as child process
* \author Norman Feske
* \author Christian Helmuth
* \date 2012-01-25
*/
/*
* Copyright (C) 2012-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 _INCLUDE__OS__SLAVE_H_
#define _INCLUDE__OS__SLAVE_H_
/* Genode includes */
#include <base/rpc_server.h>
#include <base/local_connection.h>
#include <base/child.h>
#include <init/child_policy.h>
Support for dynamic ROM sessions, fix #170 This patch introduces support for ROM sessions that update their provided data during the lifetime of the session. The 'Rom_session' interface had been extended with the new 'release()' and 'sigh()' functions, which are needed to support the new protocol. All ROM services have been updated to the new interface. Furthermore, the patch changes the child policy of init with regard to the handling of configuration files. The 'Init::Child' used to always provide the ROM dataspace with the child's config file via a locally implemented ROM service. However, for dynamic ROM sessions, we need to establish a session to the real supplier of the ROM data. This is achieved by using a new 'Child_policy_redirect_rom_file' policy to handle the 'configfile' rather than handling the 'configfile' case entirely within 'Child_config'. To see the new facility in action, the new 'os/run/dynamic_config.run' script provides a simple scenario. The config file of the test program is provided by a service, which generates and updates the config data at regular intervals. In addition, new support has been added to let slaves use dynamic reconfiguration. By using the new 'Child_policy_dynamic_rom_file', the configuration of a slave can be changed dynamically at runtime via the new 'configure()' function. The config is provided as plain null-terminated string (instead of a dataspace capability) because we need to buffer the config data anyway. So there is no benefit of using a dataspace. For buffering configuration data, a 'Ram_session' must be supplied. If no 'Ram_session' is specified at construction time of a 'Slave_policy', no config is supplied to the slave (which is still a common case). An example for dynamically reconfiguring a slave is provided by 'os/run/dynamic_config_slave.run'.
2012-04-04 17:07:19 +02:00
#include <os/child_policy_dynamic_rom.h>
#include <os/session_requester.h>
namespace Genode { struct Slave; }
struct Genode::Slave
{
typedef Session_state::Args Args;
class Policy;
template <typename>
class Connection_base;
template <typename>
struct Connection;
};
class Genode::Slave::Policy : public Child_policy
{
public:
typedef Child_policy::Name Name;
typedef Session_label Label;
typedef Registered<Genode::Parent_service> Parent_service;
typedef Registry<Parent_service> Parent_services;
private:
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
Label const _label;
Binary_name const _binary_name;
Pd_session &_ref_pd;
Pd_session_capability _ref_pd_cap;
Genode::Parent_service _binary_service;
Cap_quota const _cap_quota;
Ram_quota const _ram_quota;
Parent_services &_parent_services;
Rpc_entrypoint &_ep;
Child_policy_dynamic_rom_file _config_policy;
Session_requester _session_requester;
Service &_matching_service(Service::Name const &service_name,
Session_label const &label)
{
/* check for config file request */
if (Service *s = _config_policy.resolve_session_request(service_name, label))
return *s;
if (service_name == "ROM") {
Session_label const rom_name(label.last_element());
if (rom_name == _binary_name) return _binary_service;
if (rom_name == "session_requests") return _session_requester.service();
}
/* fill parent service registry on demand */
Parent_service *service = nullptr;
_parent_services.for_each([&] (Parent_service &s) {
if (!service && s.name() == service_name)
service = &s; });
if (!service) {
error(name(), ": illegal session request of "
"service \"", service_name, "\" (", label, ")");
throw Service_denied();
}
return *service;
}
public:
class Connection;
/**
* Slave-policy constructor
*
* \param ep entrypoint used to provide local services
* such as the config ROM service
*
* \throw Out_of_ram by 'Child_policy_dynamic_rom_file'
* \throw Out_of_caps by 'Child_policy_dynamic_rom_file'
*/
Policy(Env &env,
Label const &label,
Name const &binary_name,
Parent_services &parent_services,
Rpc_entrypoint &ep,
Cap_quota cap_quota,
Ram_quota ram_quota)
:
_label(label), _binary_name(binary_name),
_ref_pd(env.pd()), _ref_pd_cap(env.pd_session_cap()),
_binary_service(env, Rom_session::service_name()),
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
_cap_quota(cap_quota), _ram_quota(ram_quota),
_parent_services(parent_services), _ep(ep),
_config_policy(env.rm(), "config", _ep, &env.pd()),
_session_requester(ep, env.pd(), env.rm())
{
configure("<config/>");
}
/**
* Assign new configuration to slave
*
* \param config new configuration as null-terminated string
*/
void configure(char const *config)
{
_config_policy.load(config, strlen(config) + 1);
}
void configure(char const *config, size_t len)
{
_config_policy.load(config, len);
}
void trigger_session_requests()
{
_session_requester.trigger_update();
}
/****************************
** Child_policy interface **
****************************/
Name name() const override { return _label; }
Binary_name binary_name() const override { return _binary_name; }
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
Pd_session &ref_pd() override { return _ref_pd; }
Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
void init(Pd_session &session, Pd_session_capability cap) override
{
session.ref_account(_ref_pd_cap);
_ref_pd.transfer_quota(cap, _cap_quota);
_ref_pd.transfer_quota(cap, _ram_quota);
}
Route resolve_session_request(Service::Name const &name,
Session_label const &label) override
{
return Route { .service = _matching_service(name, label),
.label = label,
.diag = Session::Diag() };
}
Id_space<Parent::Server> &server_id_space() override {
return _session_requester.id_space(); }
};
template <typename CONNECTION>
class Genode::Slave::Connection_base
{
protected:
/* each connection appears as a separate client */
Id_space<Parent::Client> _id_space;
Policy &_policy;
struct Service : Genode::Service, Session_state::Ready_callback,
Session_state::Closed_callback
{
Policy &_policy;
Lock _lock { Lock::LOCKED };
bool _alive = false;
Service(Policy &policy)
:
Genode::Service(CONNECTION::service_name()), _policy(policy)
{ }
void initiate_request(Session_state &session) override
{
switch (session.phase) {
case Session_state::CREATE_REQUESTED:
if (!session.id_at_server.constructed())
session.id_at_server.construct(session,
_policy.server_id_space());
session.ready_callback = this;
session.async_client_notify = true;
break;
case Session_state::UPGRADE_REQUESTED:
warning("upgrading slaves is not implemented");
session.phase = Session_state::CAP_HANDED_OUT;
break;
case Session_state::CLOSE_REQUESTED:
warning("closing slave connections is not implemented");
session.phase = Session_state::CLOSED;
break;
case Session_state::SERVICE_DENIED:
case Session_state::INSUFFICIENT_RAM_QUOTA:
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
case Session_state::INSUFFICIENT_CAP_QUOTA:
case Session_state::AVAILABLE:
case Session_state::CAP_HANDED_OUT:
case Session_state::CLOSED:
break;
}
}
void wakeup() override { }
/**
* Session_state::Ready_callback
*/
void session_ready(Session_state &session) override
{
_alive = session.alive();
_lock.unlock();
}
/**
* Session_state::Closed_callback
*/
void session_closed(Session_state &s) override { _lock.unlock(); }
/**
* Service ('Ram_transfer::Account') interface
*/
void transfer(Pd_session_capability to, Ram_quota amount) override
{
if (to.valid()) _policy.ref_pd().transfer_quota(to, amount);
}
/**
* Service ('Ram_transfer::Account') interface
*/
Pd_session_capability cap(Ram_quota) const override
{
return _policy.ref_pd_cap();
}
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
/**
* Service ('Cap_transfer::Account') interface
*/
void transfer(Pd_session_capability to, Cap_quota amount) override
{
if (to.valid()) _policy.ref_pd().transfer_quota(to, amount);
}
/**
* Service ('Cap_transfer::Account') interface
*/
Pd_session_capability cap(Cap_quota) const override
{
return _policy.ref_pd_cap();
}
} _service;
Local_connection<CONNECTION> _connection;
Connection_base(Policy &policy, Args const &args, Affinity const &affinity)
:
_policy(policy), _service(_policy),
_connection(_service, _id_space, { 1 }, args, affinity)
{
_policy.trigger_session_requests();
_service._lock.lock();
if (!_service._alive)
throw Service_denied();
}
~Connection_base()
{
_policy.trigger_session_requests();
_service._lock.lock();
}
typedef typename CONNECTION::Session_type SESSION;
Capability<SESSION> _cap() const { return _connection.cap(); }
};
template <typename CONNECTION>
struct Genode::Slave::Connection : private Connection_base<CONNECTION>,
public CONNECTION::Client
{
/**
* Constructor
*
* \throw Service_denied parent denies session request
* \throw Out_of_ram our own quota does not suffice for
* the creation of the new session
* \throw Out_of_caps
*/
Connection(Slave::Policy &policy, Args const &args,
Affinity const &affinity = Affinity())
:
Connection_base<CONNECTION>(policy, args, affinity),
CONNECTION::Client(Connection_base<CONNECTION>::_cap())
{ }
Connection(Region_map &rm, Slave::Policy &policy, Args const &args,
Affinity const &affinity = Affinity())
:
Connection_base<CONNECTION>(policy, args, affinity),
CONNECTION::Client(rm, Connection_base<CONNECTION>::_cap())
{ }
};
#endif /* _INCLUDE__OS__SLAVE_H_ */