mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 18:56:29 +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:
parent
0202048eb6
commit
9dca1503a8
@ -658,6 +658,49 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
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:
|
||||
|
||||
/**
|
||||
@ -719,25 +762,9 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
/*
|
||||
* Determine services provided by the child
|
||||
*/
|
||||
try {
|
||||
Xml_node service_node = start_node.sub_node("provides").sub_node("service");
|
||||
|
||||
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) { }
|
||||
_provides_sub_node(start_node)
|
||||
.for_each_sub_node("service",
|
||||
[&] (Xml_node node) { _add_service(node); });
|
||||
|
||||
/*
|
||||
* 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)
|
||||
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,
|
||||
CONFIG_CHANGED, CONFIG_UNCHANGED };
|
||||
|
||||
@ -856,6 +894,41 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (provided_services_changed)
|
||||
return MAY_HAVE_SIDE_EFFECTS;
|
||||
|
||||
return NO_SIDE_EFFECTS;
|
||||
}
|
||||
|
||||
@ -908,6 +985,9 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
if (detail.ids())
|
||||
xml.attribute("id", _id.value);
|
||||
|
||||
if (!_child.active())
|
||||
xml.attribute("state", "incomplete");
|
||||
|
||||
if (detail.child_ram() && _child.ram_session_cap().valid()) {
|
||||
xml.node("ram", [&] () {
|
||||
/*
|
||||
|
@ -200,6 +200,111 @@ append config {
|
||||
<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"/>
|
||||
|
||||
<init_config>
|
||||
|
@ -178,6 +178,8 @@ struct Test::Main : Log_message_handler
|
||||
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
bool _timer_scheduled = false;
|
||||
|
||||
Reporter _init_config_reporter { _env, "config", "init.config" };
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
@ -269,8 +271,11 @@ struct Test::Main : Log_message_handler
|
||||
}
|
||||
|
||||
if (step.type() == "sleep") {
|
||||
unsigned long const timeout_ms = step.attribute_value("ms", 250UL);
|
||||
_timer.trigger_once(timeout_ms*1000);
|
||||
if (!_timer_scheduled) {
|
||||
unsigned long const timeout_ms = step.attribute_value("ms", 250UL);
|
||||
_timer.trigger_once(timeout_ms*1000);
|
||||
_timer_scheduled = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -311,6 +316,8 @@ struct Test::Main : Log_message_handler
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
_timer_scheduled = false;
|
||||
|
||||
_advance_step();
|
||||
_execute_curr_step();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user