genode/repos/os/src/init/main.cc

521 lines
12 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Init component
2011-12-22 16:19:25 +01:00
* \author Norman Feske
* \date 2010-04-27
*/
/*
* Copyright (C) 2010-2016 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/component.h>
2017-01-03 18:12:53 +01:00
#include <base/attached_rom_dataspace.h>
#include <os/reporter.h>
#include <timer_session/connection.h>
/* init includes */
2011-12-22 16:19:25 +01:00
#include <init/child.h>
2017-01-03 18:12:53 +01:00
namespace Init {
2011-12-22 16:19:25 +01:00
using namespace Genode;
2017-01-03 18:12:53 +01:00
/**
* Read priority-levels declaration from config
*/
inline long read_prio_levels(Xml_node config)
{
long const prio_levels = config.attribute_value("prio_levels", 0UL);
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
if (prio_levels && (prio_levels != (1 << log2(prio_levels)))) {
warning("prio levels is not power of two, priorities are disabled");
return 0;
}
return prio_levels;
2011-12-22 16:19:25 +01:00
}
2017-01-03 18:12:53 +01:00
/**
* Read affinity-space parameters from config
*
* If no affinity space is declared, construct a space with a single element,
* width and height being 1. If only one of both dimensions is specified, the
* other dimension is set to 1.
*/
inline Genode::Affinity::Space read_affinity_space(Xml_node config)
{
try {
Xml_node node = config.sub_node("affinity-space");
return Affinity::Space(node.attribute_value<unsigned long>("width", 1),
node.attribute_value<unsigned long>("height", 1));
} catch (...) {
return Affinity::Space(1, 1); }
}
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
/**
* Read parent-provided services from config
*/
inline void determine_parent_services(Registry<Init::Parent_service> &services,
Xml_node config, Allocator &alloc,
bool verbose)
{
if (verbose)
log("parent provides");
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
config.sub_node("parent-provides")
.for_each_sub_node("service", [&] (Xml_node node) {
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
Service::Name name = node.attribute_value("name", Service::Name());
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
new (alloc) Init::Parent_service(services, name);
if (verbose)
log(" service \"", name, "\"");
});
2011-12-22 16:19:25 +01:00
}
}
/********************
** Child registry **
********************/
namespace Init { struct Alias; }
/**
* Representation of an alias for a child
*/
struct Init::Alias : Genode::List<Alias>::Element
{
typedef Genode::String<128> Name;
typedef Genode::String<128> Child;
Name name;
Child child;
/**
* Exception types
*/
class Name_is_missing { };
class Child_is_missing { };
/**
* Utility to read a string attribute from an XML node
*
* \param STR string type
* \param EXC exception type raised if attribute is not present
*
* \param node XML node
* \param attr_name name of attribute to read
*/
template <typename STR, typename EXC>
static STR _read_string_attr(Genode::Xml_node node, char const *attr_name)
{
char buf[STR::size()];
if (!node.has_attribute(attr_name))
throw EXC();
node.attribute(attr_name).value(buf, sizeof(buf));
return STR(buf);
}
/**
* Constructor
*
* \throw Name_is_missing
* \throw Child_is_missing
*/
Alias(Genode::Xml_node alias)
:
name (_read_string_attr<Name, Name_is_missing> (alias, "name")),
child(_read_string_attr<Name, Child_is_missing>(alias, "child"))
{ }
};
2011-12-22 16:19:25 +01:00
namespace Init {
typedef Genode::List<Genode::List_element<Child> > Child_list;
struct Child_registry;
}
2011-12-22 16:19:25 +01:00
class Init::Child_registry : public Name_registry, Child_list
{
private:
List<Alias> _aliases;
2011-12-22 16:19:25 +01:00
public:
/**
* Exception type
*/
class Alias_name_is_not_unique { };
2011-12-22 16:19:25 +01:00
/**
* Register child
*/
void insert(Child *child)
{
Child_list::insert(&child->_list_element);
}
2011-12-22 16:19:25 +01:00
/**
* Unregister child
*/
void remove(Child *child)
{
Child_list::remove(&child->_list_element);
}
2011-12-22 16:19:25 +01:00
/**
* Register alias
*/
void insert_alias(Alias *alias)
{
if (!unique(alias->name.string())) {
error("alias name ", alias->name, " is not unique");
throw Alias_name_is_not_unique();
2011-12-22 16:19:25 +01:00
}
_aliases.insert(alias);
}
/**
* Unregister alias
*/
void remove_alias(Alias *alias)
{
_aliases.remove(alias);
}
/**
* Return any of the registered children, or 0 if no child exists
*/
Child *any()
{
return first() ? first()->object() : 0;
}
/**
* Return any of the registered aliases, or 0 if no alias exists
*/
Alias *any_alias()
{
return _aliases.first() ? _aliases.first() : 0;
}
void report_state(Xml_generator &xml, Report_detail const &detail) const
{
Genode::List_element<Child> const *curr = first();
for (; curr; curr = curr->next())
curr->object()->report_state(xml, detail);
/* check for name clash with an existing alias */
for (Alias const *a = _aliases.first(); a; a = a->next()) {
xml.node("alias", [&] () {
xml.attribute("name", a->name);
xml.attribute("child", a->child);
});
}
}
/*****************************
** Name-registry interface **
*****************************/
2011-12-22 16:19:25 +01:00
bool unique(const char *name) const
{
/* check for name clash with an existing child */
Genode::List_element<Child> const *curr = first();
for (; curr; curr = curr->next())
if (curr->object()->has_name(name))
return false;
2011-12-22 16:19:25 +01:00
/* check for name clash with an existing alias */
for (Alias const *a = _aliases.first(); a; a = a->next()) {
if (Alias::Name(name) == a->name)
return false;
2011-12-22 16:19:25 +01:00
}
return true;
}
Name deref_alias(Name const &name) override
{
for (Alias const *a = _aliases.first(); a; a = a->next())
if (name == a->name)
return a->child;
return name;
}
};
2011-12-22 16:19:25 +01:00
namespace Init {
struct State_reporter;
struct Main;
}
2017-01-03 18:12:53 +01:00
class Init::State_reporter : public Report_update_trigger
{
public:
struct Producer
{
virtual void produce_state_report(Xml_generator &xml,
Report_detail const &) const = 0;
};
private:
Env &_env;
Producer &_producer;
Constructible<Reporter> _reporter;
size_t _buffer_size = 0;
Reconstructible<Report_detail> _report_detail;
unsigned _report_delay_ms = 0;
/* version string from config, to be reflected in the report */
typedef String<64> Version;
Version _version;
Constructible<Timer::Connection> _timer;
Signal_handler<State_reporter> _timer_handler {
_env.ep(), *this, &State_reporter::_handle_timer };
bool _scheduled = false;
void _handle_timer()
{
_scheduled = false;
try {
Reporter::Xml_generator xml(*_reporter, [&] () {
if (_version.valid())
xml.attribute("version", _version);
_producer.produce_state_report(xml, *_report_detail);
});
}
catch(Xml_generator::Buffer_exceeded) {
error("state report exceeds maximum size");
/* try to reflect the error condition as state report */
try {
Reporter::Xml_generator xml(*_reporter, [&] () {
xml.attribute("error", "report buffer exceeded"); });
}
catch (...) { }
}
}
public:
State_reporter(Env &env, Producer &producer)
:
_env(env), _producer(producer)
{ }
void apply_config(Xml_node config)
{
try {
Xml_node report = config.sub_node("report");
/* (re-)construct reporter whenever the buffer size is changed */
Number_of_bytes const buffer_size =
report.attribute_value("buffer", Number_of_bytes(4096));
if (buffer_size != _buffer_size || !_reporter.constructed()) {
_buffer_size = buffer_size;
_reporter.construct(_env, "state", "state", _buffer_size);
}
_report_detail.construct(report);
_report_delay_ms = report.attribute_value("delay_ms", 100UL);
_reporter->enabled(true);
}
catch (Xml_node::Nonexistent_sub_node) {
_report_detail.construct();
_report_delay_ms = 0;
if (_reporter.constructed())
_reporter->enabled(false);
}
_version = config.attribute_value("version", Version());
if (_report_delay_ms) {
if (!_timer.constructed()) {
_timer.construct(_env);
_timer->sigh(_timer_handler);
}
trigger_report_update();
}
}
void trigger_report_update() override
{
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
_timer->trigger_once(_report_delay_ms*1000);
_scheduled = true;
}
}
};
struct Init::Main : State_reporter::Producer
2011-12-22 16:19:25 +01:00
{
2017-01-03 18:12:53 +01:00
Env &_env;
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
Registry<Init::Parent_service> _parent_services;
Registry<Routed_service> _child_services;
Child_registry _children;
2017-01-03 18:12:53 +01:00
Heap _heap { _env.ram(), _env.rm() };
2017-01-03 18:12:53 +01:00
Attached_rom_dataspace _config { _env, "config" };
2017-01-03 18:12:53 +01:00
Reconstructible<Verbose> _verbose { _config.xml() };
unsigned _child_cnt = 0;
2017-01-03 18:12:53 +01:00
void _handle_resource_avail() { }
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const
{
if (detail.init_ram())
xml.node("ram", [&] () { generate_ram_info(xml, _env.ram()); });
if (detail.children())
_children.report_state(xml, detail);
}
State_reporter _state_reporter { _env, *this };
2017-01-03 18:12:53 +01:00
Signal_handler<Main> _resource_avail_handler {
_env.ep(), *this, &Main::_handle_resource_avail };
2017-01-03 18:12:53 +01:00
void _handle_config();
2017-01-03 18:12:53 +01:00
Signal_handler<Main> _config_handler {
_env.ep(), *this, &Main::_handle_config };
2017-01-03 18:12:53 +01:00
Main(Env &env) : _env(env)
{
_config.sigh(_config_handler);
2017-01-03 18:12:53 +01:00
/* prevent init to block for resource upgrades (never satisfied by core) */
_env.parent().resource_avail_sigh(_resource_avail_handler);
2017-01-03 18:12:53 +01:00
_handle_config();
}
};
2017-01-03 18:12:53 +01:00
void Init::Main::_handle_config()
{
/* kill all currently running children */
while (_children.any()) {
Init::Child *child = _children.any();
_children.remove(child);
destroy(_heap, child);
}
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
/* remove all known aliases */
while (_children.any_alias()) {
Init::Alias *alias = _children.any_alias();
_children.remove_alias(alias);
destroy(_heap, alias);
}
/* reset knowledge about parent services */
_parent_services.for_each([&] (Init::Parent_service &service) {
destroy(_heap, &service); });
_config.update();
_verbose.construct(_config.xml());
_state_reporter.apply_config(_config.xml());
2017-01-03 18:12:53 +01:00
try { determine_parent_services(_parent_services, _config.xml(),
_heap, _verbose->enabled()); }
catch (...) { }
/* determine default route for resolving service requests */
Xml_node default_route_node("<empty/>");
try {
default_route_node =
_config.xml().sub_node("default-route"); }
catch (...) { }
2017-01-03 18:12:53 +01:00
/* create aliases */
_config.xml().for_each_sub_node("alias", [&] (Xml_node alias_node) {
2011-12-22 16:19:25 +01:00
2017-01-03 18:12:53 +01:00
try {
_children.insert_alias(new (_heap) Alias(alias_node)); }
catch (Alias::Name_is_missing) {
warning("missing 'name' attribute in '<alias>' entry"); }
catch (Alias::Child_is_missing) {
warning("missing 'child' attribute in '<alias>' entry"); }
});
/* create children */
try {
_config.xml().for_each_sub_node("start", [&] (Xml_node start_node) {
try {
_children.insert(new (_heap)
base: remove Child::heap This patch improves the accounting for the backing store of session-state meta data. Originally, the session state used to be allocated by a child-local heap partition fed from the child's RAM session. However, whereas this approach was somehow practical from a runtime's (parent's) point of view, the child component could not count on the quota in its own RAM session. I.e., if the Child::heap grew at the parent side, the child's RAM session would magically diminish. This caused two problems. First, it violates assumptions of components like init that carefully manage their RAM resources (and giving most of them away their children). Second, if a child transfers most of its RAM session quota to another RAM session (like init does), the child's RAM session may actually not allow the parent's heap to grow, which is a very difficult error condition to deal with. In the new version, there is no Child::heap anymore. Instead, session states are allocated from the runtime's RAM session. In order to let children pay for these costs, the parent withdraws the local session costs from the session quota donated from the child when the child initiates a new session. Hence, in principle, all components on the route of the session request take a small bite from the session quota to pay for their local book keeping Consequently, the session quota that ends up at the server may become depleted more or less, depending on the route. In the case where the remaining quota is insufficient for the server, the server responds with 'QUOTA_EXCEEDED'. Since this behavior must generally be expected, this patch equips the client-side 'Env::session' implementation with the ability to re-issue session requests with successively growing quota donations. For several of core's services (ROM, IO_MEM, IRQ), the default session quota has now increased by 2 KiB, which should suffice for session requests to up to 3 hops as is the common case for most run scripts. For longer routes, the retry mechanism as described above comes into effect. For the time being, we give a warning whenever the server-side quota check triggers the retry mechanism. The warning may eventually be removed at a later stage.
2017-02-19 10:31:50 +01:00
Init::Child(_env, _heap, *_verbose,
Init::Child::Id { ++_child_cnt },
_state_reporter,
start_node, default_route_node,
2017-01-03 18:12:53 +01:00
_children, read_prio_levels(_config.xml()),
read_affinity_space(_config.xml()),
_parent_services, _child_services));
}
catch (Rom_connection::Rom_connection_failed) {
/*
* The binary does not exist. An error message is printed
* by the Rom_connection constructor.
*/
}
catch (Ram_session::Alloc_failed) {
warning("failed to allocate memory during child construction"); }
catch (Region_map::Attach_failed) {
warning("failed to attach dataspace to local address space "
"during child construction"); }
catch (Parent::Service_denied) {
warning("failed to create session during child construction"); }
2017-01-03 18:12:53 +01:00
});
2011-12-22 16:19:25 +01:00
}
2017-01-03 18:12:53 +01:00
catch (Xml_node::Nonexistent_sub_node) { error("no children to start"); }
catch (Xml_node::Invalid_syntax) { error("config has invalid syntax"); }
catch (Init::Child::Child_name_is_not_unique) { }
catch (Init::Child_registry::Alias_name_is_not_unique) { }
2011-12-22 16:19:25 +01:00
}
2017-01-03 18:12:53 +01:00
void Component::construct(Genode::Env &env) { static Init::Main main(env); }