mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-14 21:28:16 +00:00
init: apply changes of <provides> nodes
This patch enables init to apply changes of any server's <provides> declarations in a differential way. Servers can in principle be extended by new services without re-starting them. Of course, changes of the <provides> declarations may affect clients or would-be clients as this information is taken into account for the session routing.
This commit is contained in:
committed by
Christian Helmuth
parent
0202048eb6
commit
9dca1503a8
@ -658,6 +658,49 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
catch (Parent::Service_denied) { return false; }
|
catch (Parent::Service_denied) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(), _ram_accessor,
|
||||||
|
_session_requester.id_space(),
|
||||||
|
_child.session_factory(),
|
||||||
|
name, *this);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -719,25 +762,9 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
/*
|
/*
|
||||||
* Determine services provided by the child
|
* Determine services provided by the child
|
||||||
*/
|
*/
|
||||||
try {
|
_provides_sub_node(start_node)
|
||||||
Xml_node service_node = start_node.sub_node("provides").sub_node("service");
|
.for_each_sub_node("service",
|
||||||
|
[&] (Xml_node node) { _add_service(node); });
|
||||||
for (; ; service_node = service_node.next("service")) {
|
|
||||||
|
|
||||||
char name[Service::Name::capacity()];
|
|
||||||
service_node.attribute("name").value(name, sizeof(name));
|
|
||||||
|
|
||||||
if (_verbose.enabled())
|
|
||||||
log(" provides service ", Cstring(name));
|
|
||||||
|
|
||||||
new (_alloc)
|
|
||||||
Routed_service(child_services, this->name(), _ram_accessor,
|
|
||||||
_session_requester.id_space(),
|
|
||||||
_child.session_factory(),
|
|
||||||
name, *this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Xml_node::Nonexistent_sub_node) { }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct inline config ROM service if "config" node is present.
|
* Construct inline config ROM service if "config" node is present.
|
||||||
@ -812,6 +839,17 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
if (_state == STATE_ABANDONED)
|
if (_state == STATE_ABANDONED)
|
||||||
return NO_SIDE_EFFECTS;
|
return NO_SIDE_EFFECTS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the child's environment is incomplete, restart it to attempt
|
||||||
|
* the re-routing of its environment sessions.
|
||||||
|
*/
|
||||||
|
if (!_child.active()) {
|
||||||
|
abandon();
|
||||||
|
return MAY_HAVE_SIDE_EFFECTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool provided_services_changed = false;
|
||||||
|
|
||||||
enum Config_update { CONFIG_APPEARED, CONFIG_VANISHED,
|
enum Config_update { CONFIG_APPEARED, CONFIG_VANISHED,
|
||||||
CONFIG_CHANGED, CONFIG_UNCHANGED };
|
CONFIG_CHANGED, CONFIG_UNCHANGED };
|
||||||
|
|
||||||
@ -856,6 +894,41 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
config_update = CONFIG_CHANGED;
|
config_update = CONFIG_CHANGED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Import updated <provides> node
|
||||||
|
*
|
||||||
|
* First abandon services that are no longer present in the
|
||||||
|
* <provides> node. Then add services that have newly appeared.
|
||||||
|
*/
|
||||||
|
_child_services.for_each([&] (Routed_service &service) {
|
||||||
|
|
||||||
|
if (!_provided_by_this(service))
|
||||||
|
return;
|
||||||
|
|
||||||
|
typedef Service::Name Name;
|
||||||
|
Name const name = service.name();
|
||||||
|
|
||||||
|
bool still_provided = false;
|
||||||
|
_provides_sub_node(start_node)
|
||||||
|
.for_each_sub_node("service", [&] (Xml_node node) {
|
||||||
|
if (name == node.attribute_value("name", Name()))
|
||||||
|
still_provided = true; });
|
||||||
|
|
||||||
|
if (!still_provided) {
|
||||||
|
service.abandon();
|
||||||
|
provided_services_changed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_provides_sub_node(start_node).for_each_sub_node("service",
|
||||||
|
[&] (Xml_node node) {
|
||||||
|
if (_service_exists(node))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_add_service(node);
|
||||||
|
provided_services_changed = true;
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Import new binary name. A change may affect the route for
|
* Import new binary name. A change may affect the route for
|
||||||
* the binary's ROM session, triggering the restart of the
|
* the binary's ROM session, triggering the restart of the
|
||||||
@ -892,6 +965,10 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
return MAY_HAVE_SIDE_EFFECTS;
|
return MAY_HAVE_SIDE_EFFECTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (provided_services_changed)
|
||||||
|
return MAY_HAVE_SIDE_EFFECTS;
|
||||||
|
|
||||||
return NO_SIDE_EFFECTS;
|
return NO_SIDE_EFFECTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -908,6 +985,9 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
if (detail.ids())
|
if (detail.ids())
|
||||||
xml.attribute("id", _id.value);
|
xml.attribute("id", _id.value);
|
||||||
|
|
||||||
|
if (!_child.active())
|
||||||
|
xml.attribute("state", "incomplete");
|
||||||
|
|
||||||
if (detail.child_ram() && _child.ram_session_cap().valid()) {
|
if (detail.child_ram() && _child.ram_session_cap().valid()) {
|
||||||
xml.node("ram", [&] () {
|
xml.node("ram", [&] () {
|
||||||
/*
|
/*
|
||||||
|
@ -200,6 +200,111 @@ append config {
|
|||||||
<sleep ms="100"/>
|
<sleep ms="100"/>
|
||||||
|
|
||||||
|
|
||||||
|
<message string="test changing provided services"/>
|
||||||
|
|
||||||
|
<!-- Initially, the log service lacks the <provides> declaration.
|
||||||
|
Therefore the attempt to route the LOG session of the client
|
||||||
|
to the 'log' fails. The environment of 'dummy' will remain
|
||||||
|
incomplete. -->
|
||||||
|
|
||||||
|
<init_config>
|
||||||
|
<report requested="yes"/>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/> <service name="RAM"/>
|
||||||
|
<service name="CPU"/> <service name="PD"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
</parent-provides>
|
||||||
|
<start name="log">
|
||||||
|
<binary name="dummy"/>
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<config/>
|
||||||
|
<route> <any-service> <parent/> </any-service> </route>
|
||||||
|
</start>
|
||||||
|
<start name="dummy">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<config> <log string="started"/> </config>
|
||||||
|
<route>
|
||||||
|
<service name="LOG"> <child name="log"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</init_config>
|
||||||
|
<sleep ms="200"/>
|
||||||
|
<expect_init_state>
|
||||||
|
<node name="child"> <attribute name="name" value="log"/> </node>
|
||||||
|
<node name="child">
|
||||||
|
<attribute name="name" value="dummy"/>
|
||||||
|
<attribute name="state" value="incomplete"/>
|
||||||
|
</node>
|
||||||
|
</expect_init_state>
|
||||||
|
|
||||||
|
<!-- We add the <provides> node to the log server and thereby
|
||||||
|
make the LOG route of the 'dummy' client available. The
|
||||||
|
server is expected to remain unaffected but the client should
|
||||||
|
be restarted to re-route its LOG session to the server -->
|
||||||
|
|
||||||
|
<init_config>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/> <service name="RAM"/>
|
||||||
|
<service name="CPU"/> <service name="PD"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
</parent-provides>
|
||||||
|
<start name="log">
|
||||||
|
<binary name="dummy"/>
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides> <service name="LOG"/> </provides>
|
||||||
|
<config version="providing service"> <log_service/> </config>
|
||||||
|
<route> <any-service> <parent/> </any-service> </route>
|
||||||
|
</start>
|
||||||
|
<start name="dummy">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<config> <log string="started"/> </config>
|
||||||
|
<route>
|
||||||
|
<service name="LOG"> <child name="log"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</init_config>
|
||||||
|
<expect_log string="[init -> log] config 2: providing service"/>
|
||||||
|
<expect_log string="[init -> log] [dummy] started"/>
|
||||||
|
|
||||||
|
<!-- We remove <provides> node from 'log' service and thereby
|
||||||
|
make the LOG service unavailble for 'dummy'. Consequently,
|
||||||
|
'dummy' will be restarted but will ultimately remain
|
||||||
|
incomplete (as the LOG environment session cannot be routed).
|
||||||
|
The server stays alive and reports its third config. -->
|
||||||
|
|
||||||
|
<init_config>
|
||||||
|
<report requested="yes"/>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/> <service name="RAM"/>
|
||||||
|
<service name="CPU"/> <service name="PD"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
</parent-provides>
|
||||||
|
<start name="log">
|
||||||
|
<binary name="dummy"/>
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<config version="became unavailable"/>
|
||||||
|
<route> <any-service> <parent/> </any-service> </route>
|
||||||
|
</start>
|
||||||
|
<start name="dummy">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<config> <log string="started"/> </config>
|
||||||
|
<route>
|
||||||
|
<service name="LOG"> <child name="log"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</init_config>
|
||||||
|
<sleep ms="150"/>
|
||||||
|
<expect_init_state>
|
||||||
|
<node name="child">
|
||||||
|
<attribute name="name" value="dummy"/>
|
||||||
|
<attribute name="state" value="incomplete"/>
|
||||||
|
</node>
|
||||||
|
</expect_init_state>
|
||||||
|
|
||||||
|
|
||||||
<message string="update child config"/>
|
<message string="update child config"/>
|
||||||
|
|
||||||
<init_config>
|
<init_config>
|
||||||
|
@ -178,6 +178,8 @@ struct Test::Main : Log_message_handler
|
|||||||
|
|
||||||
Timer::Connection _timer { _env };
|
Timer::Connection _timer { _env };
|
||||||
|
|
||||||
|
bool _timer_scheduled = false;
|
||||||
|
|
||||||
Reporter _init_config_reporter { _env, "config", "init.config" };
|
Reporter _init_config_reporter { _env, "config", "init.config" };
|
||||||
|
|
||||||
Attached_rom_dataspace _config { _env, "config" };
|
Attached_rom_dataspace _config { _env, "config" };
|
||||||
@ -269,8 +271,11 @@ struct Test::Main : Log_message_handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (step.type() == "sleep") {
|
if (step.type() == "sleep") {
|
||||||
unsigned long const timeout_ms = step.attribute_value("ms", 250UL);
|
if (!_timer_scheduled) {
|
||||||
_timer.trigger_once(timeout_ms*1000);
|
unsigned long const timeout_ms = step.attribute_value("ms", 250UL);
|
||||||
|
_timer.trigger_once(timeout_ms*1000);
|
||||||
|
_timer_scheduled = true;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +316,8 @@ struct Test::Main : Log_message_handler
|
|||||||
throw Exception();
|
throw Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_timer_scheduled = false;
|
||||||
|
|
||||||
_advance_step();
|
_advance_step();
|
||||||
_execute_curr_step();
|
_execute_curr_step();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user