mirror of
https://github.com/mmueller41/genode.git
synced 2026-01-21 12:32:56 +01:00
Refactored sandbox implementation to make its functionality available to alternative implementations of Init, e.g. Hoitaja.
This commit is contained in:
47
repos/os/include/sandbox/alias.h
Normal file
47
repos/os/include/sandbox/alias.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* \brief Representation of an alias for a child
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2010-04-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-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 _LIB__SANDBOX__ALIAS_H_
|
||||||
|
#define _LIB__SANDBOX__ALIAS_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
|
||||||
|
namespace Sandbox { struct Alias; }
|
||||||
|
|
||||||
|
struct Sandbox::Alias : List<Alias>::Element, Noncopyable
|
||||||
|
{
|
||||||
|
typedef Child_policy::Name Name;
|
||||||
|
typedef Child_policy::Name Child;
|
||||||
|
|
||||||
|
Name const name;
|
||||||
|
|
||||||
|
Child child { }; /* defined by 'update' */
|
||||||
|
|
||||||
|
Alias(Name const &name) : name(name) { }
|
||||||
|
|
||||||
|
class Child_attribute_missing : Exception { };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \throw Child_attribute_missing
|
||||||
|
*/
|
||||||
|
void update(Xml_node const &alias)
|
||||||
|
{
|
||||||
|
if (!alias.has_attribute("child"))
|
||||||
|
warning("alias node \"", name, "\" lacks child attribute");
|
||||||
|
|
||||||
|
child = alias.attribute_value("child", Child());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__ALIAS_H_ */
|
||||||
742
repos/os/include/sandbox/child.h
Normal file
742
repos/os/include/sandbox/child.h
Normal file
@@ -0,0 +1,742 @@
|
|||||||
|
/*
|
||||||
|
* \brief Child representation
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2010-05-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-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 _LIB__SANDBOX__CHILD_H_
|
||||||
|
#define _LIB__SANDBOX__CHILD_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/log.h>
|
||||||
|
#include <base/child.h>
|
||||||
|
#include <os/session_requester.h>
|
||||||
|
#include <os/session_policy.h>
|
||||||
|
#include <os/buffered_xml.h>
|
||||||
|
#include <sandbox/sandbox.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
#include <sandbox/verbose.h>
|
||||||
|
#include <sandbox/report.h>
|
||||||
|
#include <sandbox/name_registry.h>
|
||||||
|
#include <sandbox/service.h>
|
||||||
|
#include <sandbox/utils.h>
|
||||||
|
#include <sandbox/route_model.h>
|
||||||
|
|
||||||
|
namespace Sandbox { class Child; }
|
||||||
|
|
||||||
|
class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef String<80> Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception types
|
||||||
|
*/
|
||||||
|
struct Child_name_is_not_unique : Exception { };
|
||||||
|
struct Missing_name_attribute : Exception { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique ID of the child, solely used for diagnostic purposes
|
||||||
|
*/
|
||||||
|
struct Id { unsigned value; };
|
||||||
|
|
||||||
|
struct Default_route_accessor : Interface { virtual Xml_node default_route() = 0; };
|
||||||
|
struct Default_caps_accessor : Interface { virtual Cap_quota default_caps() = 0; };
|
||||||
|
|
||||||
|
template <typename QUOTA>
|
||||||
|
struct Resource_limit_accessor : Interface
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The argument is unused. It exists solely as an overload selector.
|
||||||
|
*/
|
||||||
|
virtual QUOTA resource_limit(QUOTA const &) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Resource_limit_accessor<Ram_quota> Ram_limit_accessor;
|
||||||
|
typedef Resource_limit_accessor<Cap_quota> Cap_limit_accessor;
|
||||||
|
typedef Resource_limit_accessor<Cpu_quota> Cpu_limit_accessor;
|
||||||
|
|
||||||
|
struct Cpu_quota_transfer : Interface
|
||||||
|
{
|
||||||
|
virtual void transfer_cpu_quota(Cpu_session_capability, Cpu_quota) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Sample_state_result { CHANGED, UNCHANGED };
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Child_registry;
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
|
||||||
|
Allocator &_alloc;
|
||||||
|
|
||||||
|
Verbose const &_verbose;
|
||||||
|
|
||||||
|
Id const _id;
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* States modelling the child's boostrap phase
|
||||||
|
*/
|
||||||
|
INITIAL, RAM_INITIALIZED, ALIVE,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The child is present in the config model but its bootstrapping
|
||||||
|
* permanently failed.
|
||||||
|
*/
|
||||||
|
STUCK,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The child must be restarted because a fundamental dependency
|
||||||
|
* changed. While the child is in this state, it is still
|
||||||
|
* referenced by the config model.
|
||||||
|
*/
|
||||||
|
RESTART_SCHEDULED,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The child is no longer referenced by config model and can
|
||||||
|
* safely be destructed.
|
||||||
|
*/
|
||||||
|
ABANDONED
|
||||||
|
};
|
||||||
|
|
||||||
|
State _state = State::INITIAL;
|
||||||
|
|
||||||
|
Report_update_trigger &_report_update_trigger;
|
||||||
|
|
||||||
|
List_element<Child> _list_element;
|
||||||
|
|
||||||
|
Reconstructible<Buffered_xml> _start_node;
|
||||||
|
|
||||||
|
Constructible<Route_model> _route_model { };
|
||||||
|
|
||||||
|
void _construct_route_model_from_start_node(Xml_node const &start)
|
||||||
|
{
|
||||||
|
_route_model.destruct();
|
||||||
|
|
||||||
|
start.with_sub_node("route",
|
||||||
|
[&] (Xml_node const &route) {
|
||||||
|
_route_model.construct(_alloc, route); },
|
||||||
|
[&] () {
|
||||||
|
_route_model.construct(_alloc, _default_route_accessor.default_route()); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Version attribute of the start node, used to force child restarts.
|
||||||
|
*/
|
||||||
|
Version _version { _start_node->xml().attribute_value("version", Version()) };
|
||||||
|
|
||||||
|
bool _uncertain_dependencies = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* True if the binary is loaded with ld.lib.so
|
||||||
|
*/
|
||||||
|
bool const _use_ld = _start_node->xml().attribute_value("ld", true);
|
||||||
|
|
||||||
|
Default_route_accessor &_default_route_accessor;
|
||||||
|
Default_caps_accessor &_default_caps_accessor;
|
||||||
|
Ram_limit_accessor &_ram_limit_accessor;
|
||||||
|
Cap_limit_accessor &_cap_limit_accessor;
|
||||||
|
Cpu_limit_accessor &_cpu_limit_accessor;
|
||||||
|
Cpu_quota_transfer &_cpu_quota_transfer;
|
||||||
|
|
||||||
|
Name_registry &_name_registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read name from XML and check for name confict with other children
|
||||||
|
*
|
||||||
|
* \throw Missing_name_attribute
|
||||||
|
*/
|
||||||
|
static Name _name_from_xml(Xml_node start_node)
|
||||||
|
{
|
||||||
|
Name const name = start_node.attribute_value("name", Name());
|
||||||
|
if (name.valid())
|
||||||
|
return name;
|
||||||
|
|
||||||
|
warning("missing 'name' attribute in '<start>' entry");
|
||||||
|
throw Missing_name_attribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef String<64> Name;
|
||||||
|
Name const _unique_name { _name_from_xml(_start_node->xml()) };
|
||||||
|
|
||||||
|
static Binary_name _binary_from_xml(Xml_node start_node,
|
||||||
|
Name const &unique_name)
|
||||||
|
{
|
||||||
|
if (!start_node.has_sub_node("binary"))
|
||||||
|
return unique_name;
|
||||||
|
|
||||||
|
return start_node.sub_node("binary").attribute_value("name", Name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* updated on configuration update */
|
||||||
|
Binary_name _binary_name { _binary_from_xml(_start_node->xml(), _unique_name) };
|
||||||
|
|
||||||
|
/* initialized in constructor, updated by 'apply_config' */
|
||||||
|
bool _heartbeat_enabled;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of skipped heartbeats when last checked
|
||||||
|
*
|
||||||
|
* This variable is used for the triggering of state-report updates
|
||||||
|
* due to heartbeat events.
|
||||||
|
*/
|
||||||
|
unsigned _last_skipped_heartbeats = 0;
|
||||||
|
|
||||||
|
/* return true if heartbeat tracking is active */
|
||||||
|
bool _heartbeat_expected() const
|
||||||
|
{
|
||||||
|
/* don't expect heartbeats from a child that is not yet complete */
|
||||||
|
return _heartbeat_enabled && (_state == State::ALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resources assigned to the child
|
||||||
|
*/
|
||||||
|
struct Resources
|
||||||
|
{
|
||||||
|
long prio_levels_log2;
|
||||||
|
long priority;
|
||||||
|
Affinity affinity;
|
||||||
|
Ram_quota assigned_ram_quota;
|
||||||
|
Cap_quota assigned_cap_quota;
|
||||||
|
Cpu_quota assigned_cpu_quota;
|
||||||
|
|
||||||
|
Ram_quota effective_ram_quota() const
|
||||||
|
{
|
||||||
|
return Genode::Child::effective_quota(assigned_ram_quota);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cap_quota effective_cap_quota() const
|
||||||
|
{
|
||||||
|
/* capabilities consumed by 'Genode::Child' */
|
||||||
|
Cap_quota const effective =
|
||||||
|
Genode::Child::effective_quota(assigned_cap_quota);
|
||||||
|
|
||||||
|
/* capabilities additionally consumed by init */
|
||||||
|
enum {
|
||||||
|
STATIC_COSTS = 1 /* possible heap backing-store
|
||||||
|
allocation for session object */
|
||||||
|
+ 1 /* buffered XML start node */
|
||||||
|
+ 2 /* dynamic ROM for config */
|
||||||
|
+ 2 /* dynamic ROM for session requester */
|
||||||
|
};
|
||||||
|
|
||||||
|
if (effective.value < STATIC_COSTS)
|
||||||
|
return Cap_quota{0};
|
||||||
|
|
||||||
|
return Cap_quota{effective.value - STATIC_COSTS};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
Resources _resources_from_start_node(Xml_node start_node, Prio_levels prio_levels,
|
||||||
|
Affinity::Space const &affinity_space,
|
||||||
|
Cap_quota default_cap_quota)
|
||||||
|
{
|
||||||
|
unsigned cpu_percent = 0;
|
||||||
|
Number_of_bytes ram_bytes = 0;
|
||||||
|
|
||||||
|
size_t caps = start_node.attribute_value("caps", default_cap_quota.value);
|
||||||
|
|
||||||
|
start_node.for_each_sub_node("resource", [&] (Xml_node rsc) {
|
||||||
|
|
||||||
|
typedef String<8> Name;
|
||||||
|
Name const name = rsc.attribute_value("name", Name());
|
||||||
|
|
||||||
|
if (name == "RAM")
|
||||||
|
ram_bytes = rsc.attribute_value("quantum", ram_bytes);
|
||||||
|
|
||||||
|
if (name == "CPU")
|
||||||
|
cpu_percent = rsc.attribute_value("quantum", 0U);
|
||||||
|
|
||||||
|
if (name == "CAP")
|
||||||
|
caps = rsc.attribute_value("quantum", 0UL);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Resources { log2(prio_levels.value),
|
||||||
|
priority_from_xml(start_node, prio_levels),
|
||||||
|
Affinity(affinity_space,
|
||||||
|
affinity_location_from_xml(affinity_space, start_node)),
|
||||||
|
Ram_quota { ram_bytes },
|
||||||
|
Cap_quota { caps },
|
||||||
|
Cpu_quota { cpu_percent } };
|
||||||
|
}
|
||||||
|
|
||||||
|
Resources _resources;
|
||||||
|
|
||||||
|
Ram_quota _configured_ram_quota() const;
|
||||||
|
Cap_quota _configured_cap_quota() const;
|
||||||
|
|
||||||
|
using Local_service = Genode::Sandbox::Local_service_base;
|
||||||
|
|
||||||
|
Registry<Parent_service> &_parent_services;
|
||||||
|
Registry<Routed_service> &_child_services;
|
||||||
|
Registry<Local_service> &_local_services;
|
||||||
|
|
||||||
|
struct Inline_config_rom_service : Abandonable, Dynamic_rom_session::Content_producer
|
||||||
|
{
|
||||||
|
typedef Genode::Local_service<Dynamic_rom_session> Service;
|
||||||
|
|
||||||
|
Child &_child;
|
||||||
|
|
||||||
|
Dynamic_rom_session _session { _child._env.ep().rpc_ep(),
|
||||||
|
_child.ref_pd(), _child._env.rm(),
|
||||||
|
*this };
|
||||||
|
|
||||||
|
Service::Single_session_factory _factory { _session };
|
||||||
|
Service _service { _factory };
|
||||||
|
|
||||||
|
Inline_config_rom_service(Child &child) : _child(child) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic_rom_session::Content_producer interface
|
||||||
|
*/
|
||||||
|
void produce_content(char *dst, Genode::size_t dst_len) override
|
||||||
|
{
|
||||||
|
Xml_node config = _child._start_node->xml().has_sub_node("config")
|
||||||
|
? _child._start_node->xml().sub_node("config")
|
||||||
|
: Xml_node("<config/>");
|
||||||
|
|
||||||
|
size_t const config_len = config.size();
|
||||||
|
|
||||||
|
if (config_len + 1 /* null termination */ >= dst_len)
|
||||||
|
throw Buffer_capacity_exceeded();
|
||||||
|
|
||||||
|
config.with_raw_node([&] (char const *start, size_t length) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The 'length' is the number of bytes of the config-node
|
||||||
|
* content, which is not null-terminated. Since
|
||||||
|
* 'Genode::copy_cstring' always null-terminates the
|
||||||
|
* result, the last byte of the source string is not
|
||||||
|
* copied. Hence, it is safe to add '1' to 'length' and
|
||||||
|
* thereby include the last actual config-content character
|
||||||
|
* in the result.
|
||||||
|
*/
|
||||||
|
copy_cstring(dst, start, length + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger_update() { _session.trigger_update(); }
|
||||||
|
|
||||||
|
Service &service() { return _service; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Constructible<Inline_config_rom_service> _config_rom_service { };
|
||||||
|
|
||||||
|
Session_requester _session_requester;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CPU-session priority parameters
|
||||||
|
*/
|
||||||
|
long const _prio_levels_log2 { _resources.prio_levels_log2 };
|
||||||
|
long const _priority { _resources.priority };
|
||||||
|
|
||||||
|
Cpu_quota const _effective_cpu_quota {
|
||||||
|
min(_cpu_limit_accessor.resource_limit(Cpu_quota{}).percent,
|
||||||
|
_resources.assigned_cpu_quota.percent) };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, the child is allowed to do system management,
|
||||||
|
* e.g., constrain physical RAM allocations.
|
||||||
|
*/
|
||||||
|
bool const _managing_system {
|
||||||
|
_start_node->xml().attribute_value("managing_system", false) };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource request initiated by the child
|
||||||
|
*/
|
||||||
|
struct Requested_resources
|
||||||
|
{
|
||||||
|
Ram_quota const ram;
|
||||||
|
Cap_quota const caps;
|
||||||
|
|
||||||
|
Requested_resources(Parent::Resource_args const &args)
|
||||||
|
:
|
||||||
|
ram (ram_quota_from_args(args.string())),
|
||||||
|
caps(cap_quota_from_args(args.string()))
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
Constructible<Requested_resources> _requested_resources { };
|
||||||
|
|
||||||
|
Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this };
|
||||||
|
|
||||||
|
struct Pd_accessor : Routed_service::Pd_accessor
|
||||||
|
{
|
||||||
|
Genode::Child &_child;
|
||||||
|
|
||||||
|
Pd_accessor(Genode::Child &child) : _child(child) { }
|
||||||
|
|
||||||
|
Pd_session &pd() override { return _child.pd(); }
|
||||||
|
Pd_session_capability pd_cap() const override { return _child.pd_session_cap(); }
|
||||||
|
|
||||||
|
} _pd_accessor { _child };
|
||||||
|
|
||||||
|
struct Ram_accessor : Routed_service::Ram_accessor
|
||||||
|
{
|
||||||
|
Genode::Child &_child;
|
||||||
|
|
||||||
|
Ram_accessor(Genode::Child &child) : _child(child) { }
|
||||||
|
|
||||||
|
Pd_session &ram() override { return _child.pd(); }
|
||||||
|
Pd_session_capability ram_cap() const override { return _child.pd_session_cap(); }
|
||||||
|
|
||||||
|
} _ram_accessor { _child };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async_service::Wakeup callback
|
||||||
|
*/
|
||||||
|
void wakeup_async_service() override
|
||||||
|
{
|
||||||
|
_session_requester.trigger_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Route_state { VALID, MISMATCH, UNAVAILABLE };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the policy results in the current route of the session
|
||||||
|
*
|
||||||
|
* This method is used to check if a policy change affects an existing
|
||||||
|
* client session of a child, i.e., to determine whether the child must
|
||||||
|
* be restarted.
|
||||||
|
*/
|
||||||
|
Route_state _route_valid(Session_state const &session)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Route const route =
|
||||||
|
resolve_session_request(session.service().name(),
|
||||||
|
session.client_label(),
|
||||||
|
session.diag());
|
||||||
|
|
||||||
|
bool const valid = (session.service() == route.service)
|
||||||
|
&& (route.label == session.label());
|
||||||
|
|
||||||
|
return valid ? Route_state::VALID : Route_state::MISMATCH;
|
||||||
|
}
|
||||||
|
catch (Service_denied) { return Route_state::UNAVAILABLE; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static Xml_node _provides_sub_node(Xml_node start_node)
|
||||||
|
{
|
||||||
|
return start_node.has_sub_node("provides")
|
||||||
|
? start_node.sub_node("provides") : Xml_node("<provides/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if service is provided by this child
|
||||||
|
*/
|
||||||
|
bool _provided_by_this(Routed_service const &service) const
|
||||||
|
{
|
||||||
|
return service.has_id_space(_session_requester.id_space());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if service of specified <provides> sub node is known
|
||||||
|
*/
|
||||||
|
bool _service_exists(Xml_node node) const
|
||||||
|
{
|
||||||
|
bool exists = false;
|
||||||
|
_child_services.for_each([&] (Routed_service const &service) {
|
||||||
|
if (_provided_by_this(service) &&
|
||||||
|
service.name() == node.attribute_value("name", Service::Name()))
|
||||||
|
exists = true; });
|
||||||
|
|
||||||
|
return exists && !abandoned() && !restart_scheduled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _add_service(Xml_node service)
|
||||||
|
{
|
||||||
|
Service::Name const name =
|
||||||
|
service.attribute_value("name", Service::Name());
|
||||||
|
|
||||||
|
if (_verbose.enabled())
|
||||||
|
log(" provides service ", name);
|
||||||
|
|
||||||
|
new (_alloc)
|
||||||
|
Routed_service(_child_services, this->name(),
|
||||||
|
_pd_accessor, _ram_accessor,
|
||||||
|
_session_requester.id_space(),
|
||||||
|
_child.session_factory(),
|
||||||
|
name, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exit state of the child set when 'exit()' is executed
|
||||||
|
* and reported afterwards through the state report.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool _exited { false };
|
||||||
|
int _exit_value { -1 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if it's safe to call the PD for requesting resource
|
||||||
|
* information
|
||||||
|
*/
|
||||||
|
bool _pd_alive() const
|
||||||
|
{
|
||||||
|
return !abandoned() && !restart_scheduled() && !_exited;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _destroy_services();
|
||||||
|
|
||||||
|
struct Sampled_state
|
||||||
|
{
|
||||||
|
Ram_info ram;
|
||||||
|
Cap_info caps;
|
||||||
|
|
||||||
|
static Sampled_state from_pd(Pd_session &pd)
|
||||||
|
{
|
||||||
|
return { .ram = Ram_info::from_pd(pd),
|
||||||
|
.caps = Cap_info::from_pd(pd) };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (Sampled_state const &other) const
|
||||||
|
{
|
||||||
|
return (ram != other.ram) || (caps != other.caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
} _sampled_state { };
|
||||||
|
|
||||||
|
void _abandon_services()
|
||||||
|
{
|
||||||
|
_child_services.for_each([&] (Routed_service &service) {
|
||||||
|
if (service.has_id_space(_session_requester.id_space()))
|
||||||
|
service.abandon(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void _schedule_restart()
|
||||||
|
{
|
||||||
|
_state = State::RESTART_SCHEDULED;
|
||||||
|
_abandon_services();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* \param alloc allocator solely used for configuration-
|
||||||
|
* dependent allocations. It is not used for
|
||||||
|
* allocations on behalf of the child's
|
||||||
|
* behavior.
|
||||||
|
*
|
||||||
|
* \param ram_limit_accessor interface for querying the available
|
||||||
|
* RAM, used for dynamic RAM balancing at
|
||||||
|
* runtime.
|
||||||
|
*
|
||||||
|
* \throw Allocator::Out_of_memory could not buffer the XML start node
|
||||||
|
*/
|
||||||
|
Child(Env &env,
|
||||||
|
Allocator &alloc,
|
||||||
|
Verbose const &verbose,
|
||||||
|
Id id,
|
||||||
|
Report_update_trigger &report_update_trigger,
|
||||||
|
Xml_node start_node,
|
||||||
|
Default_route_accessor &default_route_accessor,
|
||||||
|
Default_caps_accessor &default_caps_accessor,
|
||||||
|
Name_registry &name_registry,
|
||||||
|
Ram_limit_accessor &ram_limit_accessor,
|
||||||
|
Cap_limit_accessor &cap_limit_accessor,
|
||||||
|
Cpu_limit_accessor &cpu_limit_accessor,
|
||||||
|
Cpu_quota_transfer &cpu_quota_transfer,
|
||||||
|
Prio_levels prio_levels,
|
||||||
|
Affinity::Space const &affinity_space,
|
||||||
|
Registry<Parent_service> &parent_services,
|
||||||
|
Registry<Routed_service> &child_services,
|
||||||
|
Registry<Local_service> &local_services);
|
||||||
|
|
||||||
|
virtual ~Child();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the child has the specified name
|
||||||
|
*/
|
||||||
|
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
||||||
|
|
||||||
|
bool has_version(Version const &version) const { return version == _version; }
|
||||||
|
|
||||||
|
Ram_quota ram_quota() const { return _resources.assigned_ram_quota; }
|
||||||
|
Cap_quota cap_quota() const { return _resources.assigned_cap_quota; }
|
||||||
|
Cpu_quota cpu_quota() const { return _effective_cpu_quota; }
|
||||||
|
|
||||||
|
void try_start()
|
||||||
|
{
|
||||||
|
if (_state == State::INITIAL) {
|
||||||
|
_child.initiate_env_pd_session();
|
||||||
|
_state = State::RAM_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the state if async env sessions have brought the child to
|
||||||
|
* life. Otherwise, we would wrongly call 'initiate_env_sessions()'
|
||||||
|
* another time.
|
||||||
|
*/
|
||||||
|
if (_state == State::RAM_INITIALIZED && _child.active())
|
||||||
|
_state = State::ALIVE;
|
||||||
|
|
||||||
|
if (_state == State::RAM_INITIALIZED) {
|
||||||
|
_child.initiate_env_sessions();
|
||||||
|
|
||||||
|
if (_child.active())
|
||||||
|
_state = State::ALIVE;
|
||||||
|
else
|
||||||
|
_uncertain_dependencies = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark child as to be removed because its was dropped from the
|
||||||
|
* config model. Either <start> node disappeared or 'restart_scheduled'
|
||||||
|
* was handled.
|
||||||
|
*/
|
||||||
|
void abandon()
|
||||||
|
{
|
||||||
|
_state = State::ABANDONED;
|
||||||
|
_abandon_services();
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_services();
|
||||||
|
|
||||||
|
void close_all_sessions() { _child.close_all_sessions(); }
|
||||||
|
|
||||||
|
bool abandoned() const { return _state == State::ABANDONED; }
|
||||||
|
|
||||||
|
bool restart_scheduled() const { return _state == State::RESTART_SCHEDULED; }
|
||||||
|
|
||||||
|
bool stuck() const { return _state == State::STUCK; }
|
||||||
|
|
||||||
|
bool env_sessions_closed() const { return _child.env_sessions_closed(); }
|
||||||
|
|
||||||
|
enum Apply_config_result { PROVIDED_SERVICES_CHANGED, NO_SIDE_EFFECTS };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply new configuration to child
|
||||||
|
*
|
||||||
|
* \throw Allocator::Out_of_memory unable to allocate buffer for new
|
||||||
|
* config
|
||||||
|
*/
|
||||||
|
Apply_config_result apply_config(Xml_node start_node);
|
||||||
|
|
||||||
|
bool uncertain_dependencies() const { return _uncertain_dependencies; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that the routes of all existing sessions remain intact
|
||||||
|
*
|
||||||
|
* The child may become scheduled for restart or get stuck.
|
||||||
|
*/
|
||||||
|
void evaluate_dependencies();
|
||||||
|
|
||||||
|
/* common code for upgrading RAM and caps */
|
||||||
|
template <typename QUOTA, typename LIMIT_ACCESSOR>
|
||||||
|
void _apply_resource_upgrade(QUOTA &, QUOTA, LIMIT_ACCESSOR const &);
|
||||||
|
|
||||||
|
template <typename QUOTA, typename CHILD_AVAIL_QUOTA_FN>
|
||||||
|
void _apply_resource_downgrade(QUOTA &, QUOTA, QUOTA,
|
||||||
|
CHILD_AVAIL_QUOTA_FN const &);
|
||||||
|
|
||||||
|
void apply_upgrade();
|
||||||
|
void apply_downgrade();
|
||||||
|
|
||||||
|
void heartbeat()
|
||||||
|
{
|
||||||
|
if (_heartbeat_expected())
|
||||||
|
_child.heartbeat();
|
||||||
|
|
||||||
|
unsigned const skipped_heartbeats = _child.skipped_heartbeats();
|
||||||
|
|
||||||
|
if (_last_skipped_heartbeats != skipped_heartbeats)
|
||||||
|
_report_update_trigger.trigger_report_update();
|
||||||
|
|
||||||
|
_last_skipped_heartbeats = skipped_heartbeats;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned skipped_heartbeats() const
|
||||||
|
{
|
||||||
|
return _heartbeat_expected() ? _child.skipped_heartbeats() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_state(Xml_generator &, Report_detail const &) const;
|
||||||
|
|
||||||
|
Sample_state_result sample_state();
|
||||||
|
|
||||||
|
|
||||||
|
/****************************
|
||||||
|
** Child-policy interface **
|
||||||
|
****************************/
|
||||||
|
|
||||||
|
Child_policy::Name name() const override { return _unique_name; }
|
||||||
|
|
||||||
|
Pd_session &ref_pd() override { return _env.pd(); }
|
||||||
|
Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); }
|
||||||
|
|
||||||
|
void init(Pd_session &, Pd_session_capability) override;
|
||||||
|
void init(Cpu_session &, Cpu_session_capability) override;
|
||||||
|
|
||||||
|
Id_space<Parent::Server> &server_id_space() override {
|
||||||
|
return _session_requester.id_space(); }
|
||||||
|
|
||||||
|
Route resolve_session_request(Service::Name const &,
|
||||||
|
Session_label const &, Session::Diag) override;
|
||||||
|
|
||||||
|
void filter_session_args(Service::Name const &, char *, size_t) override;
|
||||||
|
Affinity filter_session_affinity(Affinity const &) override;
|
||||||
|
void announce_service(Service::Name const &) override;
|
||||||
|
void resource_request(Parent::Resource_args const &) override;
|
||||||
|
|
||||||
|
void exit(int exit_value) override
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (_start_node->xml().sub_node("exit").attribute_value("propagate", false)) {
|
||||||
|
_env.parent().exit(exit_value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (...) { }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger a new report for exited children so that any management
|
||||||
|
* component may react upon it.
|
||||||
|
*/
|
||||||
|
_exited = true;
|
||||||
|
_exit_value = exit_value;
|
||||||
|
|
||||||
|
_child.close_all_sessions();
|
||||||
|
|
||||||
|
_report_update_trigger.trigger_report_update();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a message as the exit is not handled otherwise. There are
|
||||||
|
* a number of automated tests that rely on this message. It is
|
||||||
|
* printed by the default implementation of 'Child_policy::exit'.
|
||||||
|
*/
|
||||||
|
Child_policy::exit(exit_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_state_changed() override
|
||||||
|
{
|
||||||
|
_report_update_trigger.trigger_report_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initiate_env_sessions() const override { return false; }
|
||||||
|
|
||||||
|
void yield_response() override
|
||||||
|
{
|
||||||
|
apply_downgrade();
|
||||||
|
_report_update_trigger.trigger_report_update();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__CHILD_H_ */
|
||||||
117
repos/os/include/sandbox/child_registry.h
Normal file
117
repos/os/include/sandbox/child_registry.h
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* \brief Child registry
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2010-04-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-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 _LIB__SANDBOX__CHILD_REGISTRY_H_
|
||||||
|
#define _LIB__SANDBOX__CHILD_REGISTRY_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/child.h>
|
||||||
|
#include <sandbox/name_registry.h>
|
||||||
|
#include <sandbox/alias.h>
|
||||||
|
#include <sandbox/report.h>
|
||||||
|
|
||||||
|
namespace Sandbox { struct Child_registry; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Child_registry : public Name_registry, Child_list
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
List<Alias> _aliases { };
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register child
|
||||||
|
*/
|
||||||
|
void insert(Child *child)
|
||||||
|
{
|
||||||
|
Child_list::insert(&child->_list_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister child
|
||||||
|
*/
|
||||||
|
void remove(Child *child)
|
||||||
|
{
|
||||||
|
Child_list::remove(&child->_list_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register alias
|
||||||
|
*/
|
||||||
|
void insert_alias(Alias *alias)
|
||||||
|
{
|
||||||
|
_aliases.insert(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister alias
|
||||||
|
*/
|
||||||
|
void remove_alias(Alias *alias)
|
||||||
|
{
|
||||||
|
_aliases.remove(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
void for_each_child(FN const &fn) const
|
||||||
|
{
|
||||||
|
Genode::List_element<Child> const *curr = first();
|
||||||
|
for (; curr; curr = curr->next())
|
||||||
|
fn(*curr->object());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
void for_each_child(FN const &fn)
|
||||||
|
{
|
||||||
|
Genode::List_element<Child> *curr = first(), *next = nullptr;
|
||||||
|
for (; curr; curr = next) {
|
||||||
|
next = curr->next();
|
||||||
|
fn(*curr->object());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_state(Xml_generator &xml, Report_detail const &detail) const
|
||||||
|
{
|
||||||
|
for_each_child([&] (Child &child) { child.report_state(xml, detail); });
|
||||||
|
|
||||||
|
for (Alias const *a = _aliases.first(); a; a = a->next()) {
|
||||||
|
xml.node("alias", [&] () {
|
||||||
|
xml.attribute("name", a->name);
|
||||||
|
xml.attribute("child", a->child);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Child::Sample_state_result sample_state()
|
||||||
|
{
|
||||||
|
auto result = Child::Sample_state_result::UNCHANGED;
|
||||||
|
|
||||||
|
for_each_child([&] (Child &child) {
|
||||||
|
if (result == Child::Sample_state_result::UNCHANGED)
|
||||||
|
result = child.sample_state(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Child::Name deref_alias(Child::Name const &name) override
|
||||||
|
{
|
||||||
|
for (Alias const *a = _aliases.first(); a; a = a->next())
|
||||||
|
if (name == a->name)
|
||||||
|
return a->child;
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__CHILD_REGISTRY_H_ */
|
||||||
292
repos/os/include/sandbox/config_model.h
Normal file
292
repos/os/include/sandbox/config_model.h
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* \brief Internal model of the XML configuration
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2021-04-01
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 _CONFIG_MODEL_H_
|
||||||
|
#define _CONFIG_MODEL_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/list_model.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/heartbeat.h>
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
|
||||||
|
struct Parent_provides_model;
|
||||||
|
struct Start_model;
|
||||||
|
struct Service_model;
|
||||||
|
struct Config_model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Sandbox::Parent_provides_model : Noncopyable
|
||||||
|
{
|
||||||
|
struct Factory : Interface, Noncopyable
|
||||||
|
{
|
||||||
|
virtual Parent_service &create_parent_service(Service::Name const &) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Allocator &_alloc;
|
||||||
|
Verbose const &_verbose;
|
||||||
|
Factory &_factory;
|
||||||
|
|
||||||
|
struct Node : Noncopyable, List_model<Node>::Element
|
||||||
|
{
|
||||||
|
Parent_service &service;
|
||||||
|
|
||||||
|
Node(Factory &factory, Service::Name const &name)
|
||||||
|
:
|
||||||
|
service(factory.create_parent_service(name))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~Node()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The destruction of the 'Parent_service' is deferred to the
|
||||||
|
* handling of abandoned entries of the 'Registry<Parent_service>'.
|
||||||
|
*/
|
||||||
|
service.abandon();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool type_matches(Xml_node const &) { return true; }
|
||||||
|
|
||||||
|
bool matches(Xml_node const &xml) const
|
||||||
|
{
|
||||||
|
return xml.attribute_value("name", Service::Name()) == service.name();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
List_model<Node> _model { };
|
||||||
|
|
||||||
|
Parent_provides_model(Allocator &alloc, Verbose const &verbose, Factory &factory)
|
||||||
|
:
|
||||||
|
_alloc(alloc), _verbose(verbose), _factory(factory)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~Parent_provides_model()
|
||||||
|
{
|
||||||
|
update_from_xml(Xml_node("<empty/>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_from_xml(Xml_node const &xml)
|
||||||
|
{
|
||||||
|
bool first_log = true;
|
||||||
|
|
||||||
|
auto create = [&] (Xml_node const &xml) -> Node &
|
||||||
|
{
|
||||||
|
Service::Name const name = xml.attribute_value("name", Service::Name());
|
||||||
|
|
||||||
|
if (_verbose.enabled()) {
|
||||||
|
if (first_log)
|
||||||
|
log("parent provides");
|
||||||
|
|
||||||
|
log(" service \"", name, "\"");
|
||||||
|
first_log = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *new (_alloc) Node(_factory, name);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto destroy = [&] (Node &node) { Genode::destroy(_alloc, &node); };
|
||||||
|
|
||||||
|
auto update = [&] (Node &, Xml_node const &) { };
|
||||||
|
|
||||||
|
try {
|
||||||
|
update_list_model_from_xml(_model, xml, create, destroy, update);
|
||||||
|
} catch (...) {
|
||||||
|
error("unable to apply complete configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Sandbox::Start_model : Noncopyable
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The 'Start_model' represents both '<alias>' nodes and '<start>' nodes
|
||||||
|
* because both node types share the same name space.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef Child_policy::Name Name;
|
||||||
|
typedef Child::Version Version;
|
||||||
|
|
||||||
|
static char const *start_type() { return "start"; }
|
||||||
|
static char const *alias_type() { return "alias"; }
|
||||||
|
|
||||||
|
struct Factory : Interface
|
||||||
|
{
|
||||||
|
class Creation_failed : Exception { };
|
||||||
|
|
||||||
|
virtual bool ready_to_create_child(Name const &, Version const &) const = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \throw Creation_failed
|
||||||
|
*/
|
||||||
|
virtual Child &create_child(Xml_node const &start) = 0;
|
||||||
|
|
||||||
|
virtual void update_child(Child &child, Xml_node const &start) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \throw Creation_failed
|
||||||
|
*/
|
||||||
|
virtual Alias &create_alias(Name const &) = 0;
|
||||||
|
|
||||||
|
virtual void destroy_alias(Alias &) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Name const _name;
|
||||||
|
Version const _version;
|
||||||
|
|
||||||
|
Factory &_factory;
|
||||||
|
|
||||||
|
bool _alias = false;
|
||||||
|
|
||||||
|
struct { Child *_child_ptr = nullptr; };
|
||||||
|
struct { Alias *_alias_ptr = nullptr; };
|
||||||
|
|
||||||
|
void _reset()
|
||||||
|
{
|
||||||
|
if (_child_ptr) _child_ptr->abandon();
|
||||||
|
if (_alias_ptr) _factory.destroy_alias(*_alias_ptr);
|
||||||
|
|
||||||
|
_child_ptr = nullptr;
|
||||||
|
_alias_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(Xml_node const &xml) const
|
||||||
|
{
|
||||||
|
return _name == xml.attribute_value("name", Name())
|
||||||
|
&& _version == xml.attribute_value("version", Version());
|
||||||
|
}
|
||||||
|
|
||||||
|
Start_model(Factory &factory, Xml_node const &xml)
|
||||||
|
:
|
||||||
|
_name(xml.attribute_value("name", Name())),
|
||||||
|
_version(xml.attribute_value("version", Version())),
|
||||||
|
_factory(factory)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~Start_model() { _reset(); }
|
||||||
|
|
||||||
|
void update_from_xml(Xml_node const &xml)
|
||||||
|
{
|
||||||
|
/* handle case where the node keeps the name but changes the type */
|
||||||
|
|
||||||
|
bool const orig_alias = _alias;
|
||||||
|
_alias = xml.has_type("alias");
|
||||||
|
if (orig_alias != _alias)
|
||||||
|
_reset();
|
||||||
|
|
||||||
|
/* create alias or child depending of the node type */
|
||||||
|
|
||||||
|
if (_alias) {
|
||||||
|
|
||||||
|
if (!_alias_ptr)
|
||||||
|
_alias_ptr = &_factory.create_alias(_name);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!_child_ptr && _factory.ready_to_create_child(_name, _version))
|
||||||
|
_child_ptr = &_factory.create_child(xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update */
|
||||||
|
|
||||||
|
if (_alias_ptr)
|
||||||
|
_alias_ptr->update(xml);
|
||||||
|
|
||||||
|
if (_child_ptr)
|
||||||
|
_factory.update_child(*_child_ptr, xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_child_restart(Xml_node const &xml)
|
||||||
|
{
|
||||||
|
if (_child_ptr && _child_ptr->restart_scheduled()) {
|
||||||
|
|
||||||
|
/* tear down */
|
||||||
|
_child_ptr->abandon();
|
||||||
|
_child_ptr = nullptr;
|
||||||
|
|
||||||
|
/* respawn */
|
||||||
|
update_from_xml(xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger_start_child()
|
||||||
|
{
|
||||||
|
if (_child_ptr)
|
||||||
|
_child_ptr->try_start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Sandbox::Service_model : Interface, Noncopyable
|
||||||
|
{
|
||||||
|
struct Factory : Interface, Noncopyable
|
||||||
|
{
|
||||||
|
virtual Service_model &create_service(Xml_node const &) = 0;
|
||||||
|
virtual void destroy_service(Service_model &) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void update_from_xml(Xml_node const &) = 0;
|
||||||
|
|
||||||
|
virtual bool matches(Xml_node const &) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Config_model : Noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Node;
|
||||||
|
|
||||||
|
List_model<Node> _model { };
|
||||||
|
|
||||||
|
struct Parent_provides_node;
|
||||||
|
struct Default_route_node;
|
||||||
|
struct Default_node;
|
||||||
|
struct Affinity_space_node;
|
||||||
|
struct Start_node;
|
||||||
|
struct Report_node;
|
||||||
|
struct Resource_node;
|
||||||
|
struct Heartbeat_node;
|
||||||
|
struct Service_node;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef State_reporter::Version Version;
|
||||||
|
|
||||||
|
void update_from_xml(Xml_node const &,
|
||||||
|
Allocator &,
|
||||||
|
Reconstructible<Verbose> &,
|
||||||
|
Version &,
|
||||||
|
Preservation &,
|
||||||
|
Constructible<Buffered_xml> &,
|
||||||
|
Cap_quota &,
|
||||||
|
Prio_levels &,
|
||||||
|
Constructible<Affinity::Space> &,
|
||||||
|
Start_model::Factory &,
|
||||||
|
Parent_provides_model::Factory &,
|
||||||
|
Service_model::Factory &,
|
||||||
|
State_reporter &,
|
||||||
|
Heartbeat &);
|
||||||
|
|
||||||
|
void apply_children_restart(Xml_node const &);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call 'Child::try_start' for each child in start-node order
|
||||||
|
*/
|
||||||
|
void trigger_start_children();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _CONFIG_MODEL_H_ */
|
||||||
89
repos/os/include/sandbox/heartbeat.h
Normal file
89
repos/os/include/sandbox/heartbeat.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* \brief Heartbeat monitoring
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2018-11-15
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 _LIB__SANDBOX__HEARTBEAT_H_
|
||||||
|
#define _LIB__SANDBOX__HEARTBEAT_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/state_reporter.h>
|
||||||
|
#include <sandbox/child_registry.h>
|
||||||
|
#include <util/noncopyable.h>
|
||||||
|
|
||||||
|
namespace Sandbox { class Heartbeat; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Heartbeat : Noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
|
||||||
|
Child_registry &_children;
|
||||||
|
|
||||||
|
Report_update_trigger &_report_update_trigger;
|
||||||
|
|
||||||
|
Constructible<Timer::Connection> _timer { };
|
||||||
|
|
||||||
|
uint64_t _rate_ms = 0;
|
||||||
|
|
||||||
|
Signal_handler<Heartbeat> _timer_handler;
|
||||||
|
|
||||||
|
void _handle_timer()
|
||||||
|
{
|
||||||
|
bool any_skipped_heartbeats = false;
|
||||||
|
|
||||||
|
_children.for_each_child([&] (Child &child) {
|
||||||
|
|
||||||
|
if (child.skipped_heartbeats())
|
||||||
|
any_skipped_heartbeats = true;
|
||||||
|
|
||||||
|
child.heartbeat();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (any_skipped_heartbeats)
|
||||||
|
_report_update_trigger.trigger_report_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Heartbeat(Env &env, Child_registry &children,
|
||||||
|
Report_update_trigger &report_update_trigger)
|
||||||
|
:
|
||||||
|
_env(env), _children(children),
|
||||||
|
_report_update_trigger(report_update_trigger),
|
||||||
|
_timer_handler(_env.ep(), *this, &Heartbeat::_handle_timer)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void disable()
|
||||||
|
{
|
||||||
|
_timer.destruct();
|
||||||
|
_rate_ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_config(Xml_node heartbeat)
|
||||||
|
{
|
||||||
|
if (!_timer.constructed()) {
|
||||||
|
_timer.construct(_env);
|
||||||
|
_timer->sigh(_timer_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned const rate_ms = heartbeat.attribute_value("rate_ms", 1000U);
|
||||||
|
|
||||||
|
if (rate_ms != _rate_ms) {
|
||||||
|
_rate_ms = rate_ms;
|
||||||
|
_timer->trigger_periodic(_rate_ms*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__HEARTBEAT_H_ */
|
||||||
263
repos/os/include/sandbox/library.h
Normal file
263
repos/os/include/sandbox/library.h
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
|
||||||
|
#include <sandbox/child.h>
|
||||||
|
#include <sandbox/alias.h>
|
||||||
|
#include <sandbox/server.h>
|
||||||
|
#include <sandbox/heartbeat.h>
|
||||||
|
#include <sandbox/config_model.h>
|
||||||
|
|
||||||
|
#include <base/env.h>
|
||||||
|
#include <base/heap.h>
|
||||||
|
#include <base/attached_rom_dataspace.h>
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
#include <util/noncopyable.h>
|
||||||
|
#include <base/registry.h>
|
||||||
|
#include <base/service.h>
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
class Library;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Genode {
|
||||||
|
class Sandbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sandbox::Library : ::Sandbox::State_reporter::Producer,
|
||||||
|
::Sandbox::Child::Default_route_accessor,
|
||||||
|
::Sandbox::Child::Default_caps_accessor,
|
||||||
|
::Sandbox::Child::Ram_limit_accessor,
|
||||||
|
::Sandbox::Child::Cap_limit_accessor,
|
||||||
|
::Sandbox::Child::Cpu_limit_accessor,
|
||||||
|
::Sandbox::Child::Cpu_quota_transfer,
|
||||||
|
::Sandbox::Start_model::Factory,
|
||||||
|
::Sandbox::Parent_provides_model::Factory
|
||||||
|
{
|
||||||
|
using Routed_service = ::Sandbox::Routed_service;
|
||||||
|
using Parent_service = ::Sandbox::Parent_service;
|
||||||
|
using Local_service = ::Genode::Sandbox::Local_service_base;
|
||||||
|
using Report_detail = ::Sandbox::Report_detail;
|
||||||
|
using Child_registry = ::Sandbox::Child_registry;
|
||||||
|
using Verbose = ::Sandbox::Verbose;
|
||||||
|
using State_reporter = ::Sandbox::State_reporter;
|
||||||
|
using Heartbeat = ::Sandbox::Heartbeat;
|
||||||
|
using Server = ::Sandbox::Server;
|
||||||
|
using Alias = ::Sandbox::Alias;
|
||||||
|
using Child = ::Sandbox::Child;
|
||||||
|
using Prio_levels = ::Sandbox::Prio_levels;
|
||||||
|
using Ram_info = ::Sandbox::Ram_info;
|
||||||
|
using Cap_info = ::Sandbox::Cap_info;
|
||||||
|
using Cpu_quota = ::Sandbox::Cpu_quota;
|
||||||
|
using Config_model = ::Sandbox::Config_model;
|
||||||
|
using Start_model = ::Sandbox::Start_model;
|
||||||
|
using Preservation = ::Sandbox::Preservation;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Env &_env;
|
||||||
|
Heap &_heap;
|
||||||
|
|
||||||
|
Registry<Parent_service> _parent_services { };
|
||||||
|
Registry<Routed_service> _child_services { };
|
||||||
|
Registry<Local_service> &_local_services;
|
||||||
|
Child_registry _children { };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global parameters obtained from config
|
||||||
|
*/
|
||||||
|
Reconstructible<Verbose> _verbose { };
|
||||||
|
Config_model::Version _version { };
|
||||||
|
Constructible<Buffered_xml> _default_route { };
|
||||||
|
Cap_quota _default_caps { 0 };
|
||||||
|
Prio_levels _prio_levels { };
|
||||||
|
Constructible<Affinity::Space> _affinity_space { };
|
||||||
|
Preservation _preservation { };
|
||||||
|
|
||||||
|
Affinity::Space _effective_affinity_space() const
|
||||||
|
{
|
||||||
|
return _affinity_space.constructed() ? *_affinity_space
|
||||||
|
: Affinity::Space { 1, 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
State_reporter _state_reporter;
|
||||||
|
|
||||||
|
Heartbeat _heartbeat { _env, _children, _state_reporter };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal representation of the XML configuration
|
||||||
|
*/
|
||||||
|
Config_model _config_model { };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Variables for tracking the side effects of updating the config model
|
||||||
|
*/
|
||||||
|
bool _server_appeared_or_disappeared = false;
|
||||||
|
bool _state_report_outdated = false;
|
||||||
|
|
||||||
|
unsigned _child_cnt = 0;
|
||||||
|
|
||||||
|
Cpu_quota _avail_cpu { .percent = 100 };
|
||||||
|
Cpu_quota _transferred_cpu { .percent = 0 };
|
||||||
|
|
||||||
|
Ram_quota _avail_ram() const
|
||||||
|
{
|
||||||
|
Ram_quota avail_ram = _env.pd().avail_ram();
|
||||||
|
|
||||||
|
if (_preservation.ram.value > avail_ram.value) {
|
||||||
|
error("RAM preservation exceeds available memory");
|
||||||
|
return Ram_quota { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deduce preserved quota from available quota */
|
||||||
|
return Ram_quota { avail_ram.value - _preservation.ram.value };
|
||||||
|
}
|
||||||
|
|
||||||
|
Cap_quota _avail_caps() const
|
||||||
|
{
|
||||||
|
Cap_quota avail_caps { _env.pd().avail_caps().value };
|
||||||
|
|
||||||
|
if (_preservation.caps.value > avail_caps.value) {
|
||||||
|
error("Capability preservation exceeds available capabilities");
|
||||||
|
return Cap_quota { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deduce preserved quota from available quota */
|
||||||
|
return Cap_quota { avail_caps.value - _preservation.caps.value };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child::Ram_limit_accessor interface
|
||||||
|
*/
|
||||||
|
Ram_quota resource_limit(Ram_quota const &) const override
|
||||||
|
{
|
||||||
|
return _avail_ram();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child::Cap_limit_accessor interface
|
||||||
|
*/
|
||||||
|
Cap_quota resource_limit(Cap_quota const &) const override { return _avail_caps(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child::Cpu_limit_accessor interface
|
||||||
|
*/
|
||||||
|
Cpu_quota resource_limit(Cpu_quota const &) const override { return _avail_cpu; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child::Cpu_quota_transfer interface
|
||||||
|
*/
|
||||||
|
void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override
|
||||||
|
{
|
||||||
|
Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) };
|
||||||
|
|
||||||
|
/* prevent division by zero in 'quota_lim_upscale' */
|
||||||
|
if (remaining.percent == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t const fraction =
|
||||||
|
Cpu_session::quota_lim_upscale(quota.percent, remaining.percent);
|
||||||
|
|
||||||
|
_env.cpu().transfer_quota(cap, fraction);
|
||||||
|
|
||||||
|
_transferred_cpu.percent += quota.percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State_reporter::Producer interface
|
||||||
|
*/
|
||||||
|
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const override
|
||||||
|
{
|
||||||
|
if (detail.init_ram())
|
||||||
|
xml.node("ram", [&] () { Ram_info::from_pd(_env.pd()).generate(xml); });
|
||||||
|
|
||||||
|
if (detail.init_caps())
|
||||||
|
xml.node("caps", [&] () { Cap_info::from_pd(_env.pd()).generate(xml); });
|
||||||
|
|
||||||
|
if (detail.children())
|
||||||
|
_children.report_state(xml, detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State_reporter::Producer interface
|
||||||
|
*/
|
||||||
|
Child::Sample_state_result sample_children_state() override
|
||||||
|
{
|
||||||
|
return _children.sample_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default_route_accessor interface
|
||||||
|
*/
|
||||||
|
Xml_node default_route() override
|
||||||
|
{
|
||||||
|
return _default_route.constructed() ? _default_route->xml()
|
||||||
|
: Xml_node("<empty/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default_caps_accessor interface
|
||||||
|
*/
|
||||||
|
Cap_quota default_caps() override { return _default_caps; }
|
||||||
|
|
||||||
|
void _update_aliases_from_config(Xml_node const &);
|
||||||
|
void _update_parent_services_from_config(Xml_node const &);
|
||||||
|
void _update_children_config(Xml_node const &);
|
||||||
|
void _destroy_abandoned_parent_services();
|
||||||
|
void _destroy_abandoned_children();
|
||||||
|
|
||||||
|
Server _server { _env, _heap, _child_services, _state_reporter };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox::Start_model::Factory
|
||||||
|
*/
|
||||||
|
virtual Child &create_child(Xml_node const &) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox::Start_model::Factory
|
||||||
|
*/
|
||||||
|
virtual void update_child(Child &, Xml_node const &) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox::Start_model::Factory
|
||||||
|
*/
|
||||||
|
Alias &create_alias(Child_policy::Name const &name) override
|
||||||
|
{
|
||||||
|
Alias &alias = *new (_heap) Alias(name);
|
||||||
|
_children.insert_alias(&alias);
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox::Start_model::Factory
|
||||||
|
*/
|
||||||
|
void destroy_alias(Alias &alias) override
|
||||||
|
{
|
||||||
|
_children.remove_alias(&alias);
|
||||||
|
destroy(_heap, &alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox::Start_model::Factory
|
||||||
|
*/
|
||||||
|
bool ready_to_create_child(Start_model::Name const &,
|
||||||
|
Start_model::Version const &) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox::Parent_provides_model::Factory
|
||||||
|
*/
|
||||||
|
Parent_service &create_parent_service(Service::Name const &name) override
|
||||||
|
{
|
||||||
|
return *new (_heap) Parent_service(_parent_services, _env, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Library(Env &env, Heap &heap, Registry<Local_service> &local_services,
|
||||||
|
Genode::Sandbox::State_handler &state_handler)
|
||||||
|
:
|
||||||
|
_env(env), _heap(heap), _local_services(local_services),
|
||||||
|
_state_reporter(_env, *this, state_handler)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual void apply_config(Xml_node const &);
|
||||||
|
|
||||||
|
virtual void generate_state_report(Xml_generator &xml) const
|
||||||
|
{
|
||||||
|
_state_reporter.generate(xml);
|
||||||
|
}
|
||||||
|
};
|
||||||
39
repos/os/include/sandbox/name_registry.h
Normal file
39
repos/os/include/sandbox/name_registry.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* \brief Interface for database of child names
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-03-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _LIB__SANDBOX__NAME_REGISTRY_H_
|
||||||
|
#define _LIB__SANDBOX__NAME_REGISTRY_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/child.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
|
||||||
|
namespace Sandbox { struct Name_registry; }
|
||||||
|
|
||||||
|
struct Sandbox::Name_registry
|
||||||
|
{
|
||||||
|
virtual ~Name_registry() { }
|
||||||
|
|
||||||
|
typedef Child_policy::Name Name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return child name for a given alias name
|
||||||
|
*
|
||||||
|
* If there is no alias, the function returns the original name.
|
||||||
|
*/
|
||||||
|
virtual Name deref_alias(Name const &) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__NAME_REGISTRY_H_ */
|
||||||
91
repos/os/include/sandbox/report.h
Normal file
91
repos/os/include/sandbox/report.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* \brief Report configuration
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-01-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _LIB__SANDBOX__REPORT_H_
|
||||||
|
#define _LIB__SANDBOX__REPORT_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/noncopyable.h>
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
struct Report_update_trigger;
|
||||||
|
struct Report_detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Report_detail : Genode::Noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool _children = false;
|
||||||
|
bool _ids = false;
|
||||||
|
bool _requested = false;
|
||||||
|
bool _provided = false;
|
||||||
|
bool _session_args = false;
|
||||||
|
bool _child_ram = false;
|
||||||
|
bool _child_caps = false;
|
||||||
|
bool _init_ram = false;
|
||||||
|
bool _init_caps = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Report_detail() { }
|
||||||
|
|
||||||
|
Report_detail(Genode::Xml_node report)
|
||||||
|
{
|
||||||
|
_children = true;
|
||||||
|
_ids = report.attribute_value("ids", false);
|
||||||
|
_requested = report.attribute_value("requested", false);
|
||||||
|
_provided = report.attribute_value("provided", false);
|
||||||
|
_session_args = report.attribute_value("session_args", false);
|
||||||
|
_child_ram = report.attribute_value("child_ram", false);
|
||||||
|
_child_caps = report.attribute_value("child_caps", false);
|
||||||
|
_init_ram = report.attribute_value("init_ram", false);
|
||||||
|
_init_caps = report.attribute_value("init_caps", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool children() const { return _children; }
|
||||||
|
bool ids() const { return _ids; }
|
||||||
|
bool requested() const { return _requested; }
|
||||||
|
bool provided() const { return _provided; }
|
||||||
|
bool session_args() const { return _session_args; }
|
||||||
|
bool child_ram() const { return _child_ram; }
|
||||||
|
bool child_caps() const { return _child_caps; }
|
||||||
|
bool init_ram() const { return _init_ram; }
|
||||||
|
bool init_caps() const { return _init_caps; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Sandbox::Report_update_trigger : Interface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Trigger regular (rate-limited) report update
|
||||||
|
*/
|
||||||
|
virtual void trigger_report_update() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger immediate report update
|
||||||
|
*
|
||||||
|
* This method is intended for situations that require a timely response of
|
||||||
|
* the consumer of the report. This is particularly important for resource
|
||||||
|
* requests that would otherwise unnecessarily stall the execution of the
|
||||||
|
* respective child.
|
||||||
|
*/
|
||||||
|
virtual void trigger_immediate_report_update() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__REPORT_H_ */
|
||||||
280
repos/os/include/sandbox/route_model.h
Normal file
280
repos/os/include/sandbox/route_model.h
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
/*
|
||||||
|
* \brief Internal model of routing rules
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2021-04-05
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 _ROUTE_MODEL_H_
|
||||||
|
#define _ROUTE_MODEL_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
|
||||||
|
struct Checksum;
|
||||||
|
class Route_model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Sandbox::Checksum
|
||||||
|
{
|
||||||
|
unsigned long value = 0;
|
||||||
|
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Checksum(char const *s) : valid(s != nullptr)
|
||||||
|
{
|
||||||
|
if (!s)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (uint8_t const byte = *s++) {
|
||||||
|
|
||||||
|
/* rotate value */
|
||||||
|
unsigned long const sign_bit = ((long)value < 0);
|
||||||
|
value = (value << 1) | sign_bit;
|
||||||
|
|
||||||
|
/* xor byte to lowest 8 bit */
|
||||||
|
value = value ^ (unsigned long)byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
Checksum(String<N> const &s) : Checksum(s.string()) { }
|
||||||
|
|
||||||
|
bool operator != (Checksum const &other) const
|
||||||
|
{
|
||||||
|
return (other.value != value) || !valid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Route_model : Noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Query : Noncopyable
|
||||||
|
{
|
||||||
|
Child_policy::Name const &child;
|
||||||
|
Service::Name const &service;
|
||||||
|
Session_label const &label;
|
||||||
|
|
||||||
|
Checksum const service_checksum { service };
|
||||||
|
Checksum const label_checksum { skip_label_prefix(child.string(),
|
||||||
|
label.string()) };
|
||||||
|
|
||||||
|
Query(Child_policy::Name const &child,
|
||||||
|
Service::Name const &service,
|
||||||
|
Session_label const &label)
|
||||||
|
:
|
||||||
|
child(child), service(service), label(label)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Rule : Noncopyable, List<Rule>::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class List<Rule>;
|
||||||
|
friend class Route_model;
|
||||||
|
friend void Genode::destroy<Rule>(Allocator &, Rule *);
|
||||||
|
|
||||||
|
Allocator &_alloc;
|
||||||
|
|
||||||
|
Xml_node const _node; /* points to 'Route_model::_route_node' */
|
||||||
|
|
||||||
|
struct Selector
|
||||||
|
{
|
||||||
|
typedef String<Session_label::capacity()> Label;
|
||||||
|
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
NO_LABEL, SPECIFIC_LABEL,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Presence of 'label_last', 'label_prefix',
|
||||||
|
* 'label_suffix', 'unscoped_label', or even
|
||||||
|
* a combination of attributes.
|
||||||
|
*/
|
||||||
|
COMPLICATED
|
||||||
|
|
||||||
|
} type = Type::NO_LABEL;
|
||||||
|
|
||||||
|
Checksum label_checksum { "" };
|
||||||
|
|
||||||
|
Selector(Xml_node const &node)
|
||||||
|
{
|
||||||
|
bool const complicated =
|
||||||
|
node.has_attribute("label_prefix") ||
|
||||||
|
node.has_attribute("label_suffix") ||
|
||||||
|
node.has_attribute("label_last") ||
|
||||||
|
node.has_attribute("unscoped_label");
|
||||||
|
|
||||||
|
if (complicated) {
|
||||||
|
type = Type::COMPLICATED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Label const label = node.attribute_value("label", Label());
|
||||||
|
if (label.valid()) {
|
||||||
|
type = Type::SPECIFIC_LABEL;
|
||||||
|
label_checksum = Checksum(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Selector const _selector;
|
||||||
|
Checksum const _service_checksum;
|
||||||
|
bool const _specific_service { _node.has_type("service") };
|
||||||
|
|
||||||
|
struct Target : Noncopyable, private List<Target>::Element
|
||||||
|
{
|
||||||
|
friend class List<Target>;
|
||||||
|
friend class Rule;
|
||||||
|
|
||||||
|
Xml_node const node; /* points to 'Route_model::_route_node' */
|
||||||
|
|
||||||
|
Target(Xml_node const &node) : node(node) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
List<Target> _targets { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor is private to 'Route_model'
|
||||||
|
*/
|
||||||
|
Rule(Allocator &alloc, Xml_node const &node)
|
||||||
|
:
|
||||||
|
_alloc(alloc), _node(node), _selector(node),
|
||||||
|
_service_checksum(node.attribute_value("name", Service::Name()))
|
||||||
|
{
|
||||||
|
Target const *at_ptr = nullptr;
|
||||||
|
node.for_each_sub_node([&] (Xml_node sub_node) {
|
||||||
|
Target &target = *new (_alloc) Target(sub_node);
|
||||||
|
_targets.insert(&target, at_ptr);
|
||||||
|
at_ptr = ⌖
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~Rule()
|
||||||
|
{
|
||||||
|
while (Target *target_ptr = _targets.first()) {
|
||||||
|
_targets.remove(target_ptr);
|
||||||
|
destroy(_alloc, target_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick check for early detection of definite mismatches
|
||||||
|
*
|
||||||
|
* \return true if query definitely mismatches the rule,
|
||||||
|
* false if the undecided
|
||||||
|
*/
|
||||||
|
bool _mismatches(Query const &query) const
|
||||||
|
{
|
||||||
|
if (_specific_service
|
||||||
|
&& query.service_checksum != _service_checksum)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (_selector.type == Selector::Type::SPECIFIC_LABEL
|
||||||
|
&& query.label_checksum != _selector.label_checksum)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool matches(Query const &query) const
|
||||||
|
{
|
||||||
|
/* handle common case */
|
||||||
|
if (_mismatches(query))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return service_node_matches(_node,
|
||||||
|
query.label,
|
||||||
|
query.child,
|
||||||
|
query.service);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
Child_policy::Route resolve(FN const &fn) const
|
||||||
|
{
|
||||||
|
for (Target const *t = _targets.first(); t; t = t->next()) {
|
||||||
|
try { return fn(t->node); }
|
||||||
|
catch (Service_denied) { /* try next target */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* query is not accepted by any of the targets */
|
||||||
|
throw Service_denied();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Allocator &_alloc;
|
||||||
|
|
||||||
|
Buffered_xml const _route_node;
|
||||||
|
|
||||||
|
List<Rule> _rules { };
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Route_model(Allocator &alloc, Xml_node const &route)
|
||||||
|
:
|
||||||
|
_alloc(alloc), _route_node(_alloc, route)
|
||||||
|
{
|
||||||
|
Rule const *at_ptr = nullptr;
|
||||||
|
_route_node.xml().for_each_sub_node([&] (Xml_node const &node) {
|
||||||
|
Rule &rule = *new (_alloc) Rule(_alloc, node);
|
||||||
|
_rules.insert(&rule, at_ptr); /* append */
|
||||||
|
at_ptr = &rule;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~Route_model()
|
||||||
|
{
|
||||||
|
while (Rule *rule_ptr = _rules.first()) {
|
||||||
|
_rules.remove(rule_ptr);
|
||||||
|
destroy(_alloc, rule_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
Child_policy::Route resolve(Query const &query, FN const &fn) const
|
||||||
|
{
|
||||||
|
for (Rule const *r = _rules.first(); r; r = r->next())
|
||||||
|
if (r->matches(query)) {
|
||||||
|
try {
|
||||||
|
return r->resolve(fn);
|
||||||
|
}
|
||||||
|
catch (Service_denied) {
|
||||||
|
if (r->_specific_service)
|
||||||
|
throw;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If none of the targets of a wildcard rule was
|
||||||
|
* satisfied with the query, continue with the next
|
||||||
|
* rule.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warning(query.child, ": no route to "
|
||||||
|
"service \"", query.service, "\" "
|
||||||
|
"(label=\"", query.label, "\")");
|
||||||
|
|
||||||
|
throw Service_denied();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _ROUTE_MODEL_H_ */
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
#include <base/heap.h>
|
#include <base/heap.h>
|
||||||
|
|
||||||
namespace Genode { class Sandbox; }
|
namespace Genode { class Sandbox; }
|
||||||
|
namespace Sandbox { class Library; }
|
||||||
|
|
||||||
class Genode::Sandbox : Noncopyable
|
class Genode::Sandbox : Noncopyable
|
||||||
{
|
{
|
||||||
@@ -50,9 +50,7 @@ class Genode::Sandbox : Noncopyable
|
|||||||
|
|
||||||
Heap _heap;
|
Heap _heap;
|
||||||
|
|
||||||
class Library;
|
::Sandbox::Library &_library;
|
||||||
|
|
||||||
Library &_library;
|
|
||||||
|
|
||||||
Registry<Local_service_base> _local_services { };
|
Registry<Local_service_base> _local_services { };
|
||||||
|
|
||||||
@@ -60,7 +58,9 @@ class Genode::Sandbox : Noncopyable
|
|||||||
|
|
||||||
Sandbox(Env &, State_handler &);
|
Sandbox(Env &, State_handler &);
|
||||||
|
|
||||||
void apply_config(Xml_node const &);
|
virtual ~Sandbox() = default;
|
||||||
|
|
||||||
|
virtual void apply_config(Xml_node const &);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate state report as configured by the <report> config node
|
* Generate state report as configured by the <report> config node
|
||||||
|
|||||||
131
repos/os/include/sandbox/server.h
Normal file
131
repos/os/include/sandbox/server.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* \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 _LIB__SANDBOX__SERVER_H_
|
||||||
|
#define _LIB__SANDBOX__SERVER_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/attached_rom_dataspace.h>
|
||||||
|
#include <os/buffered_xml.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
#include <sandbox/service.h>
|
||||||
|
#include <sandbox/state_reporter.h>
|
||||||
|
#include <sandbox/config_model.h>
|
||||||
|
|
||||||
|
namespace Sandbox { class Server; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Server : Session_state::Ready_callback,
|
||||||
|
Session_state::Closed_callback,
|
||||||
|
public Service_model::Factory
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Route
|
||||||
|
{
|
||||||
|
Routed_service &service;
|
||||||
|
Session_label label;
|
||||||
|
};
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
|
||||||
|
Allocator &_alloc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ID space of requests originating from the parent
|
||||||
|
*/
|
||||||
|
Id_space<Parent::Server> _server_id_space { };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ID space of requests issued to the children
|
||||||
|
*/
|
||||||
|
Id_space<Parent::Client> _client_id_space { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception type
|
||||||
|
*/
|
||||||
|
class Service_not_present : Exception { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta data of service provided to our parent
|
||||||
|
*/
|
||||||
|
struct Service;
|
||||||
|
|
||||||
|
Registry<Service> _services { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Services provided by our children
|
||||||
|
*/
|
||||||
|
Registry<Routed_service> &_child_services;
|
||||||
|
|
||||||
|
Report_update_trigger &_report_update_trigger;
|
||||||
|
|
||||||
|
Constructible<Attached_rom_dataspace> _session_requests { };
|
||||||
|
Constructible<Signal_handler<Server> > _session_request_handler { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \throw 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();
|
||||||
|
|
||||||
|
void _close_session(Session_state &, Parent::Session_response response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<Routed_service> &services,
|
||||||
|
Report_update_trigger &report_update_trigger)
|
||||||
|
:
|
||||||
|
_env(env), _alloc(alloc), _child_services(services),
|
||||||
|
_report_update_trigger(report_update_trigger)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void apply_updated_policy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service_model::Factory
|
||||||
|
*/
|
||||||
|
Service_model &create_service(Xml_node const &) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service_model::Factory
|
||||||
|
*/
|
||||||
|
void destroy_service(Service_model &) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__SERVER_H_ */
|
||||||
151
repos/os/include/sandbox/service.h
Normal file
151
repos/os/include/sandbox/service.h
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* \brief Services as targeted by session routes
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-03-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _LIB__SANDBOX__SERVICE_H_
|
||||||
|
#define _LIB__SANDBOX__SERVICE_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/service.h>
|
||||||
|
#include <base/child.h>
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
class Abandonable;
|
||||||
|
class Parent_service;
|
||||||
|
class Routed_service;
|
||||||
|
class Forwarded_service;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Abandonable : Interface
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool _abandoned = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void abandon() { _abandoned = true; }
|
||||||
|
|
||||||
|
bool abandoned() const { return _abandoned; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Parent_service : public Genode::Try_parent_service, public Abandonable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Registry<Parent_service>::Element _reg_elem;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Parent_service(Registry<Parent_service> ®istry, Env &env,
|
||||||
|
Service::Name const &name)
|
||||||
|
:
|
||||||
|
Genode::Try_parent_service(env, name), _reg_elem(registry, *this)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandbox-specific representation of a child service
|
||||||
|
*/
|
||||||
|
class Sandbox::Routed_service : public Async_service, public Abandonable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef Child_policy::Name Child_name;
|
||||||
|
|
||||||
|
struct Pd_accessor : Interface
|
||||||
|
{
|
||||||
|
virtual Pd_session &pd() = 0;
|
||||||
|
virtual Pd_session_capability pd_cap() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ram_accessor : Interface
|
||||||
|
{
|
||||||
|
virtual Pd_session &ram() = 0;
|
||||||
|
virtual Pd_session_capability ram_cap() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Child_name _child_name;
|
||||||
|
|
||||||
|
Pd_accessor &_pd_accessor;
|
||||||
|
|
||||||
|
Session_state::Factory &_factory;
|
||||||
|
|
||||||
|
Registry<Routed_service>::Element _registry_element;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* \param services registry of all services provides by children
|
||||||
|
* \param child_name child name of server, used for session routing
|
||||||
|
*
|
||||||
|
* The other arguments correspond to the arguments of 'Async_service'.
|
||||||
|
*/
|
||||||
|
Routed_service(Registry<Routed_service> &services,
|
||||||
|
Child_name const &child_name,
|
||||||
|
Pd_accessor &pd_accessor,
|
||||||
|
Ram_accessor &,
|
||||||
|
Id_space<Parent::Server> &server_id_space,
|
||||||
|
Session_state::Factory &factory,
|
||||||
|
Service::Name const &name,
|
||||||
|
Wakeup &wakeup)
|
||||||
|
:
|
||||||
|
Async_service(name, server_id_space, factory, wakeup),
|
||||||
|
_child_name(child_name), _pd_accessor(pd_accessor),
|
||||||
|
_factory(factory), _registry_element(services, *this)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Child_name const &child_name() const { return _child_name; }
|
||||||
|
|
||||||
|
Session_state::Factory &factory() { return _factory; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ram_transfer::Account interface
|
||||||
|
*/
|
||||||
|
void transfer(Pd_session_capability to, Ram_quota amount) override
|
||||||
|
{
|
||||||
|
if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ram_transfer::Account interface
|
||||||
|
*/
|
||||||
|
Pd_session_capability cap(Ram_quota) const override
|
||||||
|
{
|
||||||
|
return _pd_accessor.pd_cap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cap_transfer::Account interface
|
||||||
|
*/
|
||||||
|
void transfer(Pd_session_capability to, Cap_quota amount) override
|
||||||
|
{
|
||||||
|
if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cap_transfer::Account interface
|
||||||
|
*/
|
||||||
|
Pd_session_capability cap(Cap_quota) const override
|
||||||
|
{
|
||||||
|
return _pd_accessor.pd_cap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__SERVICE_H_ */
|
||||||
191
repos/os/include/sandbox/state_reporter.h
Normal file
191
repos/os/include/sandbox/state_reporter.h
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* \brief State reporting mechanism
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-03-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _LIB__SANDBOX__STATE_REPORTER_H_
|
||||||
|
#define _LIB__SANDBOX__STATE_REPORTER_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
#include <sandbox/sandbox.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/report.h>
|
||||||
|
#include <sandbox/child.h>
|
||||||
|
|
||||||
|
namespace Sandbox { class State_reporter; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::State_reporter : public Report_update_trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef String<64> Version;
|
||||||
|
|
||||||
|
struct Producer : Interface
|
||||||
|
{
|
||||||
|
virtual void produce_state_report(Xml_generator &xml,
|
||||||
|
Report_detail const &) const = 0;
|
||||||
|
|
||||||
|
virtual Child::Sample_state_result sample_children_state() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
using State_handler = Genode::Sandbox::State_handler;
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
|
||||||
|
Producer &_producer;
|
||||||
|
|
||||||
|
Reconstructible<Report_detail> _report_detail { };
|
||||||
|
|
||||||
|
uint64_t _report_delay_ms = 0;
|
||||||
|
|
||||||
|
/* interval used when child-ram reporting is enabled */
|
||||||
|
uint64_t _report_period_ms = 0;
|
||||||
|
|
||||||
|
/* version string from config, to be reflected in the report */
|
||||||
|
Version _version { };
|
||||||
|
|
||||||
|
Constructible<Timer::Connection> _timer { };
|
||||||
|
Constructible<Timer::Connection> _timer_periodic { };
|
||||||
|
|
||||||
|
Signal_handler<State_reporter> _timer_handler {
|
||||||
|
_env.ep(), *this, &State_reporter::_handle_timer };
|
||||||
|
|
||||||
|
Signal_handler<State_reporter> _timer_periodic_handler {
|
||||||
|
_env.ep(), *this, &State_reporter::_handle_periodic_timer };
|
||||||
|
|
||||||
|
Signal_handler<State_reporter> _immediate_handler {
|
||||||
|
_env.ep(), *this, &State_reporter::_handle_timer };
|
||||||
|
|
||||||
|
bool _scheduled = false;
|
||||||
|
|
||||||
|
State_handler &_state_handler;
|
||||||
|
|
||||||
|
bool _periodic_sampling_needed() const
|
||||||
|
{
|
||||||
|
return _report_detail->child_ram()
|
||||||
|
|| _report_detail->child_caps();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handle_periodic_timer()
|
||||||
|
{
|
||||||
|
if (!_periodic_sampling_needed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_producer.sample_children_state() == Child::Sample_state_result::CHANGED)
|
||||||
|
_handle_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handle_timer()
|
||||||
|
{
|
||||||
|
_scheduled = false;
|
||||||
|
|
||||||
|
_state_handler.handle_sandbox_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
State_reporter(Env &env, Producer &producer, State_handler &state_handler)
|
||||||
|
:
|
||||||
|
_env(env), _producer(producer),
|
||||||
|
_state_handler(state_handler)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void generate(Xml_generator &xml) const
|
||||||
|
{
|
||||||
|
if (_version.valid())
|
||||||
|
xml.attribute("version", _version);
|
||||||
|
|
||||||
|
if (_report_detail.constructed())
|
||||||
|
_producer.produce_state_report(xml, *_report_detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_config(Version const &version, Xml_node const &report)
|
||||||
|
{
|
||||||
|
if (report.type() == "report") {
|
||||||
|
_report_detail.construct(report);
|
||||||
|
_report_delay_ms = report.attribute_value("delay_ms", 100UL);
|
||||||
|
} else {
|
||||||
|
_report_detail.construct();
|
||||||
|
_report_delay_ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trigger_update = false;
|
||||||
|
|
||||||
|
if (version != _version) {
|
||||||
|
_version = version;
|
||||||
|
trigger_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_report_delay_ms) {
|
||||||
|
if (!_timer.constructed()) {
|
||||||
|
_timer.construct(_env);
|
||||||
|
_timer->sigh(_timer_handler);
|
||||||
|
}
|
||||||
|
trigger_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trigger_update)
|
||||||
|
trigger_report_update();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the report features information about child-RAM quotas, we
|
||||||
|
* update the report periodically. Even in the absence of any other
|
||||||
|
* report-triggering event, a child may consume/free RAM from its
|
||||||
|
* RAM session without any interplay with the sandbox. The periodic
|
||||||
|
* reports ensure that such changes are reflected by the sandbox'
|
||||||
|
* state report.
|
||||||
|
*
|
||||||
|
* By default, the interval is one second. However, when the
|
||||||
|
* 'delay_ms' attribute is defined with a higher value than that,
|
||||||
|
* the user intends to limit the rate of state reports. If so, we
|
||||||
|
* use the value of 'delay_ms' as interval.
|
||||||
|
*/
|
||||||
|
uint64_t const period_ms = max((uint64_t)1000, _report_delay_ms);
|
||||||
|
bool const period_changed = (_report_period_ms != period_ms);
|
||||||
|
bool const report_periodically = _periodic_sampling_needed();
|
||||||
|
|
||||||
|
if (report_periodically && !_timer_periodic.constructed()) {
|
||||||
|
_timer_periodic.construct(_env);
|
||||||
|
_timer_periodic->sigh(_timer_periodic_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!report_periodically && _timer_periodic.constructed()) {
|
||||||
|
_report_period_ms = 0;
|
||||||
|
_timer_periodic.destruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (period_changed && _timer_periodic.constructed()) {
|
||||||
|
_report_period_ms = period_ms;
|
||||||
|
_timer_periodic->trigger_periodic(1000*_report_period_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger_report_update() override
|
||||||
|
{
|
||||||
|
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
|
||||||
|
_timer->trigger_once(_report_delay_ms*1000);
|
||||||
|
_scheduled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger_immediate_report_update() override
|
||||||
|
{
|
||||||
|
if (_report_delay_ms)
|
||||||
|
Signal_transmitter(_immediate_handler).submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__STATE_REPORTER_H_ */
|
||||||
100
repos/os/include/sandbox/types.h
Normal file
100
repos/os/include/sandbox/types.h
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* \brief Common types used within init
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-03-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _LIB__SANDBOX__TYPES_H_
|
||||||
|
#define _LIB__SANDBOX__TYPES_H_
|
||||||
|
|
||||||
|
#include <util/list.h>
|
||||||
|
#include <util/xml_generator.h>
|
||||||
|
#include <pd_session/pd_session.h>
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
|
||||||
|
class Child;
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using Genode::size_t;
|
||||||
|
using Genode::strlen;
|
||||||
|
|
||||||
|
struct Prio_levels { long value; };
|
||||||
|
|
||||||
|
struct Cpu_quota
|
||||||
|
{
|
||||||
|
unsigned percent;
|
||||||
|
|
||||||
|
void print(Output &out) const { Genode::print(out, percent, "%"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef List<List_element<Child> > Child_list;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Resource_info
|
||||||
|
{
|
||||||
|
T quota, used, avail;
|
||||||
|
|
||||||
|
static Resource_info from_pd(Pd_session const &pd);
|
||||||
|
|
||||||
|
void generate(Xml_generator &xml) const
|
||||||
|
{
|
||||||
|
typedef String<32> Value;
|
||||||
|
xml.attribute("quota", Value(quota));
|
||||||
|
xml.attribute("used", Value(used));
|
||||||
|
xml.attribute("avail", Value(avail));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (Resource_info const &other) const
|
||||||
|
{
|
||||||
|
return quota.value != other.quota.value
|
||||||
|
|| used.value != other.used.value
|
||||||
|
|| avail.value != other.avail.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Resource_info<Ram_quota> Ram_info;
|
||||||
|
typedef Resource_info<Cap_quota> Cap_info;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline Ram_info Ram_info::from_pd(Pd_session const &pd)
|
||||||
|
{
|
||||||
|
return { .quota = pd.ram_quota(),
|
||||||
|
.used = pd.used_ram(),
|
||||||
|
.avail = pd.avail_ram() };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline Cap_info Cap_info::from_pd(Pd_session const &pd)
|
||||||
|
{
|
||||||
|
return { .quota = pd.cap_quota(),
|
||||||
|
.used = pd.used_caps(),
|
||||||
|
.avail = pd.avail_caps() };
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Preservation : private Noncopyable
|
||||||
|
{
|
||||||
|
static Ram_quota default_ram() { return { 40*sizeof(long)*1024 }; }
|
||||||
|
static Cap_quota default_caps() { return { 20 }; }
|
||||||
|
|
||||||
|
Ram_quota ram { };
|
||||||
|
Cap_quota caps { };
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
ram = default_ram();
|
||||||
|
caps = default_caps();
|
||||||
|
}
|
||||||
|
|
||||||
|
Preservation() { reset(); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__TYPES_H_ */
|
||||||
233
repos/os/include/sandbox/utils.h
Normal file
233
repos/os/include/sandbox/utils.h
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* \brief Utilities
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2010-05-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-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 _LIB__SANDBOX__UTILS_H_
|
||||||
|
#define _LIB__SANDBOX__UTILS_H_
|
||||||
|
|
||||||
|
namespace Sandbox {
|
||||||
|
|
||||||
|
static inline void warn_insuff_quota(size_t const avail)
|
||||||
|
{
|
||||||
|
warning("specified quota exceeds available quota, "
|
||||||
|
"proceeding with a quota of ", avail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return sub string of label with the leading child name stripped out
|
||||||
|
*
|
||||||
|
* \return character pointer to the scoped part of the label,
|
||||||
|
* or nullptr if the label is not correctly prefixed with the child's
|
||||||
|
* name
|
||||||
|
*/
|
||||||
|
inline char const *skip_label_prefix(char const *child_name, char const *label)
|
||||||
|
{
|
||||||
|
size_t const child_name_len = strlen(child_name);
|
||||||
|
|
||||||
|
if (strcmp(child_name, label, child_name_len) != 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
label += child_name_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip label separator. This condition should be always satisfied.
|
||||||
|
*/
|
||||||
|
if (strcmp(" -> ", label, 4) != 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return label + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if service XML node matches service request
|
||||||
|
*
|
||||||
|
* \param args session arguments, inspected for the session label
|
||||||
|
* \param child_name name of the originator of the session request
|
||||||
|
* \param service_name name of the requested service
|
||||||
|
*/
|
||||||
|
inline bool service_node_matches(Xml_node const service_node,
|
||||||
|
Session_label const &label,
|
||||||
|
Child_policy::Name const &child_name,
|
||||||
|
Service::Name const &service_name)
|
||||||
|
{
|
||||||
|
bool const service_matches =
|
||||||
|
service_node.has_type("any-service") ||
|
||||||
|
(service_node.has_type("service") &&
|
||||||
|
service_node.attribute_value("name", Service::Name()) == service_name);
|
||||||
|
|
||||||
|
if (!service_matches)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
typedef String<Session_label::capacity()> Label;
|
||||||
|
|
||||||
|
char const *unscoped_attr = "unscoped_label";
|
||||||
|
char const *label_last_attr = "label_last";
|
||||||
|
|
||||||
|
bool const route_depends_on_child_provided_label =
|
||||||
|
service_node.has_attribute("label") ||
|
||||||
|
service_node.has_attribute("label_prefix") ||
|
||||||
|
service_node.has_attribute("label_suffix") ||
|
||||||
|
service_node.has_attribute(label_last_attr);
|
||||||
|
|
||||||
|
if (service_node.has_attribute(unscoped_attr)) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If an 'unscoped_label' attribute is provided, don't consider any
|
||||||
|
* scoped label attribute.
|
||||||
|
*/
|
||||||
|
if (route_depends_on_child_provided_label)
|
||||||
|
warning("service node contains both scoped and unscoped label attributes");
|
||||||
|
|
||||||
|
return label == service_node.attribute_value(unscoped_attr, Label());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service_node.has_attribute(label_last_attr))
|
||||||
|
return service_node.attribute_value(label_last_attr, Label()) == label.last_element();
|
||||||
|
|
||||||
|
if (!route_depends_on_child_provided_label)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
char const * const scoped_label = skip_label_prefix(
|
||||||
|
child_name.string(), label.string());
|
||||||
|
|
||||||
|
if (!scoped_label)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Session_label const session_label(scoped_label);
|
||||||
|
|
||||||
|
return !Xml_node_label_score(service_node, session_label).conflict();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if service name is ambiguous
|
||||||
|
*
|
||||||
|
* \return true if the same service is provided multiple
|
||||||
|
* times
|
||||||
|
*
|
||||||
|
* \deprecated
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
inline bool is_ambiguous(Registry<T> const &services, Service::Name const &name)
|
||||||
|
{
|
||||||
|
/* count number of services with the specified name */
|
||||||
|
unsigned cnt = 0;
|
||||||
|
services.for_each([&] (T const &service) {
|
||||||
|
if (!service.abandoned())
|
||||||
|
cnt += (service.name() == name); });
|
||||||
|
|
||||||
|
return cnt > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find service with certain values in given registry
|
||||||
|
*
|
||||||
|
* \param services service registry
|
||||||
|
* \param name name of wanted service
|
||||||
|
* \param filter_fn function that applies additional filters
|
||||||
|
*
|
||||||
|
* \throw Service_denied
|
||||||
|
*/
|
||||||
|
template <typename T, typename FILTER_FN>
|
||||||
|
inline T &find_service(Registry<T> &services,
|
||||||
|
Service::Name const &name,
|
||||||
|
FILTER_FN const &filter_fn)
|
||||||
|
{
|
||||||
|
T *service = nullptr;
|
||||||
|
services.for_each([&] (T &s) {
|
||||||
|
|
||||||
|
if (service || s.name() != name || filter_fn(s))
|
||||||
|
return;
|
||||||
|
|
||||||
|
service = &s;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!service)
|
||||||
|
throw Service_denied();
|
||||||
|
|
||||||
|
if (service->abandoned())
|
||||||
|
throw Service_denied();
|
||||||
|
|
||||||
|
return *service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read priority-levels declaration from config
|
||||||
|
*/
|
||||||
|
inline Prio_levels prio_levels_from_xml(Xml_node const &config)
|
||||||
|
{
|
||||||
|
long const prio_levels = config.attribute_value("prio_levels", 0L);
|
||||||
|
|
||||||
|
if (prio_levels && ((prio_levels >= (long)sizeof(prio_levels)*8) ||
|
||||||
|
(prio_levels != (1L << log2(prio_levels))))) {
|
||||||
|
warning("prio levels is not power of two, priorities are disabled");
|
||||||
|
return Prio_levels { 0 };
|
||||||
|
}
|
||||||
|
return Prio_levels { prio_levels };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline long priority_from_xml(Xml_node start_node, Prio_levels prio_levels)
|
||||||
|
{
|
||||||
|
long const default_priority = Cpu_session::DEFAULT_PRIORITY;
|
||||||
|
|
||||||
|
long priority = start_node.attribute_value("priority", default_priority);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All priority declarations in the config file are
|
||||||
|
* negative because child priorities can never be higher
|
||||||
|
* than parent priorities. To simplify priority
|
||||||
|
* calculations, we use inverted values. Lower values
|
||||||
|
* correspond to higher priorities.
|
||||||
|
*/
|
||||||
|
priority = -priority;
|
||||||
|
|
||||||
|
if (priority && (priority >= prio_levels.value)) {
|
||||||
|
long new_prio = prio_levels.value ? prio_levels.value - 1 : 0;
|
||||||
|
|
||||||
|
Service::Name const name = start_node.attribute_value("name", Service::Name());
|
||||||
|
warning(name, ": invalid priority, upgrading "
|
||||||
|
"from ", -priority, " to ", -new_prio);
|
||||||
|
return new_prio;
|
||||||
|
}
|
||||||
|
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline Affinity::Location
|
||||||
|
affinity_location_from_xml(Affinity::Space const &space, Xml_node start_node)
|
||||||
|
{
|
||||||
|
typedef Affinity::Location Location;
|
||||||
|
|
||||||
|
Location result = Location(0, 0, space.width(), space.height());
|
||||||
|
|
||||||
|
start_node.with_optional_sub_node("affinity", [&] (Xml_node node) {
|
||||||
|
|
||||||
|
Location const location = Location::from_xml(space, node);
|
||||||
|
|
||||||
|
if (!location.within(space)) {
|
||||||
|
Service::Name const name = start_node.attribute_value("name", Service::Name());
|
||||||
|
warning(name, ": affinity location exceeds affinity-space boundary");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = location;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__UTILS_H_ */
|
||||||
43
repos/os/include/sandbox/verbose.h
Normal file
43
repos/os/include/sandbox/verbose.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* \brief Sandbox verbosity
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _LIB__SANDBOX__VERBOSE_H_
|
||||||
|
#define _LIB__SANDBOX__VERBOSE_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/noncopyable.h>
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sandbox/types.h>
|
||||||
|
|
||||||
|
namespace Sandbox { struct Verbose; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox::Verbose : Genode::Noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool _enabled = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Verbose() { }
|
||||||
|
|
||||||
|
Verbose(Xml_node const &config)
|
||||||
|
: _enabled(config.attribute_value("verbose", false)) { }
|
||||||
|
|
||||||
|
bool enabled() const { return _enabled; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _LIB__SANDBOX__VERBOSE_H_ */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
SRC_CC = library.cc child.cc server.cc config_model.cc
|
SRC_CC = library.cc child.cc server.cc config_model.cc
|
||||||
INC_DIR += $(REP_DIR)/src/lib/sandbox
|
INC_DIR += $(REP_DIR)/include/sandbox
|
||||||
LIBS += base
|
LIBS += base
|
||||||
|
|
||||||
SHARED_LIB = yes
|
SHARED_LIB = yes
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ namespace Init {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Init::Main : Sandbox::State_handler
|
struct Init::Main : Genode::Sandbox::State_handler
|
||||||
{
|
{
|
||||||
Env &_env;
|
Env &_env;
|
||||||
|
|
||||||
Sandbox _sandbox { _env, *this };
|
Genode::Sandbox _sandbox { _env, *this };
|
||||||
|
|
||||||
Attached_rom_dataspace _config { _env, "config" };
|
Attached_rom_dataspace _config { _env, "config" };
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include <vm_session/vm_session.h>
|
#include <vm_session/vm_session.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <child.h>
|
#include <sandbox/child.h>
|
||||||
|
|
||||||
|
|
||||||
void Sandbox::Child::destroy_services()
|
void Sandbox::Child::destroy_services()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config_model.h>
|
#include <sandbox/config_model.h>
|
||||||
|
|
||||||
using namespace Sandbox;
|
using namespace Sandbox;
|
||||||
|
|
||||||
|
|||||||
@@ -16,254 +16,11 @@
|
|||||||
#include <sandbox/sandbox.h>
|
#include <sandbox/sandbox.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <child.h>
|
#include <sandbox/library.h>
|
||||||
#include <alias.h>
|
|
||||||
#include <server.h>
|
|
||||||
#include <heartbeat.h>
|
|
||||||
#include <config_model.h>
|
|
||||||
|
|
||||||
struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
|
|
||||||
::Sandbox::Child::Default_route_accessor,
|
|
||||||
::Sandbox::Child::Default_caps_accessor,
|
|
||||||
::Sandbox::Child::Ram_limit_accessor,
|
|
||||||
::Sandbox::Child::Cap_limit_accessor,
|
|
||||||
::Sandbox::Child::Cpu_limit_accessor,
|
|
||||||
::Sandbox::Child::Cpu_quota_transfer,
|
|
||||||
::Sandbox::Start_model::Factory,
|
|
||||||
::Sandbox::Parent_provides_model::Factory
|
|
||||||
{
|
|
||||||
using Routed_service = ::Sandbox::Routed_service;
|
|
||||||
using Parent_service = ::Sandbox::Parent_service;
|
|
||||||
using Local_service = ::Genode::Sandbox::Local_service_base;
|
|
||||||
using Report_detail = ::Sandbox::Report_detail;
|
|
||||||
using Child_registry = ::Sandbox::Child_registry;
|
|
||||||
using Verbose = ::Sandbox::Verbose;
|
|
||||||
using State_reporter = ::Sandbox::State_reporter;
|
|
||||||
using Heartbeat = ::Sandbox::Heartbeat;
|
|
||||||
using Server = ::Sandbox::Server;
|
|
||||||
using Alias = ::Sandbox::Alias;
|
|
||||||
using Child = ::Sandbox::Child;
|
|
||||||
using Prio_levels = ::Sandbox::Prio_levels;
|
|
||||||
using Ram_info = ::Sandbox::Ram_info;
|
|
||||||
using Cap_info = ::Sandbox::Cap_info;
|
|
||||||
using Cpu_quota = ::Sandbox::Cpu_quota;
|
|
||||||
using Config_model = ::Sandbox::Config_model;
|
|
||||||
using Start_model = ::Sandbox::Start_model;
|
|
||||||
using Preservation = ::Sandbox::Preservation;
|
|
||||||
|
|
||||||
Env &_env;
|
|
||||||
Heap &_heap;
|
|
||||||
|
|
||||||
Registry<Parent_service> _parent_services { };
|
|
||||||
Registry<Routed_service> _child_services { };
|
|
||||||
Registry<Local_service> &_local_services;
|
|
||||||
Child_registry _children { };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Global parameters obtained from config
|
|
||||||
*/
|
|
||||||
Reconstructible<Verbose> _verbose { };
|
|
||||||
Config_model::Version _version { };
|
|
||||||
Constructible<Buffered_xml> _default_route { };
|
|
||||||
Cap_quota _default_caps { 0 };
|
|
||||||
Prio_levels _prio_levels { };
|
|
||||||
Constructible<Affinity::Space> _affinity_space { };
|
|
||||||
Preservation _preservation { };
|
|
||||||
|
|
||||||
Affinity::Space _effective_affinity_space() const
|
|
||||||
{
|
|
||||||
return _affinity_space.constructed() ? *_affinity_space
|
|
||||||
: Affinity::Space { 1, 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
State_reporter _state_reporter;
|
|
||||||
|
|
||||||
Heartbeat _heartbeat { _env, _children, _state_reporter };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal representation of the XML configuration
|
|
||||||
*/
|
|
||||||
Config_model _config_model { };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Variables for tracking the side effects of updating the config model
|
|
||||||
*/
|
|
||||||
bool _server_appeared_or_disappeared = false;
|
|
||||||
bool _state_report_outdated = false;
|
|
||||||
|
|
||||||
unsigned _child_cnt = 0;
|
|
||||||
|
|
||||||
Cpu_quota _avail_cpu { .percent = 100 };
|
|
||||||
Cpu_quota _transferred_cpu { .percent = 0 };
|
|
||||||
|
|
||||||
Ram_quota _avail_ram() const
|
|
||||||
{
|
|
||||||
Ram_quota avail_ram = _env.pd().avail_ram();
|
|
||||||
|
|
||||||
if (_preservation.ram.value > avail_ram.value) {
|
|
||||||
error("RAM preservation exceeds available memory");
|
|
||||||
return Ram_quota { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* deduce preserved quota from available quota */
|
|
||||||
return Ram_quota { avail_ram.value - _preservation.ram.value };
|
|
||||||
}
|
|
||||||
|
|
||||||
Cap_quota _avail_caps() const
|
|
||||||
{
|
|
||||||
Cap_quota avail_caps { _env.pd().avail_caps().value };
|
|
||||||
|
|
||||||
if (_preservation.caps.value > avail_caps.value) {
|
|
||||||
error("Capability preservation exceeds available capabilities");
|
|
||||||
return Cap_quota { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* deduce preserved quota from available quota */
|
|
||||||
return Cap_quota { avail_caps.value - _preservation.caps.value };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Child::Ram_limit_accessor interface
|
|
||||||
*/
|
|
||||||
Ram_quota resource_limit(Ram_quota const &) const override
|
|
||||||
{
|
|
||||||
return _avail_ram();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Child::Cap_limit_accessor interface
|
|
||||||
*/
|
|
||||||
Cap_quota resource_limit(Cap_quota const &) const override { return _avail_caps(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Child::Cpu_limit_accessor interface
|
|
||||||
*/
|
|
||||||
Cpu_quota resource_limit(Cpu_quota const &) const override { return _avail_cpu; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Child::Cpu_quota_transfer interface
|
|
||||||
*/
|
|
||||||
void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override
|
|
||||||
{
|
|
||||||
Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) };
|
|
||||||
|
|
||||||
/* prevent division by zero in 'quota_lim_upscale' */
|
|
||||||
if (remaining.percent == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
size_t const fraction =
|
|
||||||
Cpu_session::quota_lim_upscale(quota.percent, remaining.percent);
|
|
||||||
|
|
||||||
_env.cpu().transfer_quota(cap, fraction);
|
|
||||||
|
|
||||||
_transferred_cpu.percent += quota.percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* State_reporter::Producer interface
|
|
||||||
*/
|
|
||||||
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const override
|
|
||||||
{
|
|
||||||
if (detail.init_ram())
|
|
||||||
xml.node("ram", [&] () { Ram_info::from_pd(_env.pd()).generate(xml); });
|
|
||||||
|
|
||||||
if (detail.init_caps())
|
|
||||||
xml.node("caps", [&] () { Cap_info::from_pd(_env.pd()).generate(xml); });
|
|
||||||
|
|
||||||
if (detail.children())
|
|
||||||
_children.report_state(xml, detail);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* State_reporter::Producer interface
|
|
||||||
*/
|
|
||||||
Child::Sample_state_result sample_children_state() override
|
|
||||||
{
|
|
||||||
return _children.sample_state();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default_route_accessor interface
|
|
||||||
*/
|
|
||||||
Xml_node default_route() override
|
|
||||||
{
|
|
||||||
return _default_route.constructed() ? _default_route->xml()
|
|
||||||
: Xml_node("<empty/>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default_caps_accessor interface
|
|
||||||
*/
|
|
||||||
Cap_quota default_caps() override { return _default_caps; }
|
|
||||||
|
|
||||||
void _update_aliases_from_config(Xml_node const &);
|
|
||||||
void _update_parent_services_from_config(Xml_node const &);
|
|
||||||
void _update_children_config(Xml_node const &);
|
|
||||||
void _destroy_abandoned_parent_services();
|
|
||||||
void _destroy_abandoned_children();
|
|
||||||
|
|
||||||
Server _server { _env, _heap, _child_services, _state_reporter };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sandbox::Start_model::Factory
|
|
||||||
*/
|
|
||||||
Child &create_child(Xml_node const &) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sandbox::Start_model::Factory
|
|
||||||
*/
|
|
||||||
void update_child(Child &, Xml_node const &) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sandbox::Start_model::Factory
|
|
||||||
*/
|
|
||||||
Alias &create_alias(Child_policy::Name const &name) override
|
|
||||||
{
|
|
||||||
Alias &alias = *new (_heap) Alias(name);
|
|
||||||
_children.insert_alias(&alias);
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sandbox::Start_model::Factory
|
|
||||||
*/
|
|
||||||
void destroy_alias(Alias &alias) override
|
|
||||||
{
|
|
||||||
_children.remove_alias(&alias);
|
|
||||||
destroy(_heap, &alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sandbox::Start_model::Factory
|
|
||||||
*/
|
|
||||||
bool ready_to_create_child(Start_model::Name const &,
|
|
||||||
Start_model::Version const &) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sandbox::Parent_provides_model::Factory
|
|
||||||
*/
|
|
||||||
Parent_service &create_parent_service(Service::Name const &name) override
|
|
||||||
{
|
|
||||||
return *new (_heap) Parent_service(_parent_services, _env, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Library(Env &env, Heap &heap, Registry<Local_service> &local_services,
|
|
||||||
State_handler &state_handler)
|
|
||||||
:
|
|
||||||
_env(env), _heap(heap), _local_services(local_services),
|
|
||||||
_state_reporter(_env, *this, state_handler)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void apply_config(Xml_node const &);
|
|
||||||
|
|
||||||
void generate_state_report(Xml_generator &xml) const
|
|
||||||
{
|
|
||||||
_state_reporter.generate(xml);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void Genode::Sandbox::Library::_destroy_abandoned_parent_services()
|
|
||||||
|
void Sandbox::Library::_destroy_abandoned_parent_services()
|
||||||
{
|
{
|
||||||
_parent_services.for_each([&] (Parent_service &service) {
|
_parent_services.for_each([&] (Parent_service &service) {
|
||||||
if (service.abandoned())
|
if (service.abandoned())
|
||||||
@@ -271,7 +28,7 @@ void Genode::Sandbox::Library::_destroy_abandoned_parent_services()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Genode::Sandbox::Library::_destroy_abandoned_children()
|
void Sandbox::Library::_destroy_abandoned_children()
|
||||||
{
|
{
|
||||||
_children.for_each_child([&] (Child &child) {
|
_children.for_each_child([&] (Child &child) {
|
||||||
|
|
||||||
@@ -300,7 +57,7 @@ void Genode::Sandbox::Library::_destroy_abandoned_children()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const &name,
|
bool Sandbox::Library::ready_to_create_child(Start_model::Name const &name,
|
||||||
Start_model::Version const &version) const
|
Start_model::Version const &version) const
|
||||||
{
|
{
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
@@ -328,7 +85,7 @@ bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
::Sandbox::Child &Genode::Sandbox::Library::create_child(Xml_node const &start_node)
|
::Sandbox::Child &Sandbox::Library::create_child(Xml_node const &start_node)
|
||||||
{
|
{
|
||||||
if (!_affinity_space.constructed() && start_node.has_sub_node("affinity"))
|
if (!_affinity_space.constructed() && start_node.has_sub_node("affinity"))
|
||||||
warning("affinity-space configuration missing, "
|
warning("affinity-space configuration missing, "
|
||||||
@@ -378,7 +135,7 @@ bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Genode::Sandbox::Library::update_child(Child &child, Xml_node const &start)
|
void Sandbox::Library::update_child(Child &child, Xml_node const &start)
|
||||||
{
|
{
|
||||||
if (child.abandoned())
|
if (child.abandoned())
|
||||||
return;
|
return;
|
||||||
@@ -395,7 +152,7 @@ void Genode::Sandbox::Library::update_child(Child &child, Xml_node const &start)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Genode::Sandbox::Library::apply_config(Xml_node const &config)
|
void Sandbox::Library::apply_config(Xml_node const &config)
|
||||||
{
|
{
|
||||||
_server_appeared_or_disappeared = false;
|
_server_appeared_or_disappeared = false;
|
||||||
_state_report_outdated = false;
|
_state_report_outdated = false;
|
||||||
@@ -630,5 +387,5 @@ void Genode::Sandbox::generate_state_report(Xml_generator &xml) const
|
|||||||
Genode::Sandbox::Sandbox(Env &env, State_handler &state_handler)
|
Genode::Sandbox::Sandbox(Env &env, State_handler &state_handler)
|
||||||
:
|
:
|
||||||
_heap(env.ram(), env.rm()),
|
_heap(env.ram(), env.rm()),
|
||||||
_library(*new (_heap) Library(env, _heap, _local_services, state_handler))
|
_library(*new (_heap) ::Sandbox::Library(env, _heap, _local_services, state_handler))
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include <os/session_policy.h>
|
#include <os/session_policy.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include "server.h"
|
#include <sandbox/server.h>
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
|
|||||||
Reference in New Issue
Block a user