mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-24 15:56:41 +00:00
sandbox/init: route model
This commit optimizes the 'Child::resolve_session_request' implementation by introducing an internal 'Route_model' for quickly traversing routing rules instead of parsing XML on each session request. Fixes #4068
This commit is contained in:
parent
b661459aca
commit
f5f5b8c1f1
@ -68,8 +68,9 @@ Sandbox::Child::apply_config(Xml_node start_node)
|
|||||||
*/
|
*/
|
||||||
start_node.with_sub_node("route", [&] (Xml_node const &route) {
|
start_node.with_sub_node("route", [&] (Xml_node const &route) {
|
||||||
_start_node->xml().with_sub_node("route", [&] (Xml_node const &orig) {
|
_start_node->xml().with_sub_node("route", [&] (Xml_node const &orig) {
|
||||||
if (route.differs_from(orig))
|
if (route.differs_from(orig)) {
|
||||||
_uncertain_dependencies = true; }); });
|
_construct_route_model_from_start_node(start_node);
|
||||||
|
_uncertain_dependencies = true; } }); });
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine how the inline config is affected.
|
* Determine how the inline config is affected.
|
||||||
@ -505,9 +506,10 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name,
|
|||||||
Session_label const &label,
|
Session_label const &label,
|
||||||
Session::Diag const diag)
|
Session::Diag const diag)
|
||||||
{
|
{
|
||||||
|
bool const rom_service = (service_name == Rom_session::service_name());
|
||||||
|
|
||||||
/* check for "config" ROM request */
|
/* check for "config" ROM request */
|
||||||
if (service_name == Rom_session::service_name() &&
|
if (rom_service && label.last_element() == "config") {
|
||||||
label.last_element() == "config") {
|
|
||||||
|
|
||||||
if (_config_rom_service.constructed() &&
|
if (_config_rom_service.constructed() &&
|
||||||
!_config_rom_service->abandoned())
|
!_config_rom_service->abandoned())
|
||||||
@ -529,36 +531,19 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name,
|
|||||||
* we resolve the session request with the binary name as label.
|
* we resolve the session request with the binary name as label.
|
||||||
* Otherwise the regular routing is applied.
|
* Otherwise the regular routing is applied.
|
||||||
*/
|
*/
|
||||||
if (service_name == Rom_session::service_name() &&
|
if (rom_service && label == _unique_name && _unique_name != _binary_name)
|
||||||
label == _unique_name && _unique_name != _binary_name)
|
|
||||||
return resolve_session_request(service_name, _binary_name, diag);
|
return resolve_session_request(service_name, _binary_name, diag);
|
||||||
|
|
||||||
/* supply binary as dynamic linker if '<start ld="no">' */
|
/* supply binary as dynamic linker if '<start ld="no">' */
|
||||||
if (!_use_ld && service_name == Rom_session::service_name() && label == "ld.lib.so")
|
if (rom_service && !_use_ld && label == "ld.lib.so")
|
||||||
return resolve_session_request(service_name, _binary_name, diag);
|
return resolve_session_request(service_name, _binary_name, diag);
|
||||||
|
|
||||||
/* check for "session_requests" ROM request */
|
/* check for "session_requests" ROM request */
|
||||||
if (service_name == Rom_session::service_name()
|
if (rom_service && label.last_element() == Session_requester::rom_name())
|
||||||
&& label.last_element() == Session_requester::rom_name())
|
|
||||||
return Route { _session_requester.service(), Session::Label(), diag };
|
return Route { _session_requester.service(), Session::Label(), diag };
|
||||||
|
|
||||||
try {
|
auto resolve_at_target = [&] (Xml_node const &target) -> Route
|
||||||
Xml_node route_node = _default_route_accessor.default_route();
|
{
|
||||||
try {
|
|
||||||
route_node = _start_node->xml().sub_node("route"); }
|
|
||||||
catch (...) { }
|
|
||||||
Xml_node service_node = route_node.sub_node();
|
|
||||||
|
|
||||||
for (; ; service_node = service_node.next()) {
|
|
||||||
|
|
||||||
bool service_wildcard = service_node.has_type("any-service");
|
|
||||||
|
|
||||||
if (!service_node_matches(service_node, label, name(), service_name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Xml_node target = service_node.sub_node();
|
|
||||||
for (; ; target = target.next()) {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine session label to be provided to the server
|
* Determine session label to be provided to the server
|
||||||
*
|
*
|
||||||
@ -603,8 +588,8 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name,
|
|||||||
return s.child_name() != server_name; };
|
return s.child_name() != server_name; };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Route { find_service(_child_services, service_name, filter_server_name),
|
return Route { find_service(_child_services, service_name,
|
||||||
target_label, target_diag };
|
filter_server_name), target_label, target_diag };
|
||||||
|
|
||||||
} catch (Service_denied) { }
|
} catch (Service_denied) { }
|
||||||
}
|
}
|
||||||
@ -617,25 +602,18 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name,
|
|||||||
throw Service_denied();
|
throw Service_denied();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Route { find_service(_child_services, service_name, no_filter),
|
return Route { find_service(_child_services, service_name,
|
||||||
target_label, target_diag };
|
no_filter), target_label, target_diag };
|
||||||
|
|
||||||
} catch (Service_denied) { }
|
} catch (Service_denied) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!service_wildcard) {
|
|
||||||
warning(name(), ": lookup for service \"", service_name, "\" failed");
|
|
||||||
throw Service_denied();
|
throw Service_denied();
|
||||||
}
|
};
|
||||||
|
|
||||||
if (target.last())
|
Route_model::Query const query(name(), service_name, label);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Xml_node::Nonexistent_sub_node) { }
|
|
||||||
|
|
||||||
warning(name(), ": no route to service \"", service_name, "\" (label=\"", label, "\")");
|
return _route_model->resolve(query, resolve_at_target);
|
||||||
throw Service_denied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -796,6 +774,8 @@ Sandbox::Child::Child(Env &env,
|
|||||||
log(" priority: ", _resources.priority);
|
log(" priority: ", _resources.priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_construct_route_model_from_start_node(start_node);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine services provided by the child
|
* Determine services provided by the child
|
||||||
*/
|
*/
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <name_registry.h>
|
#include <name_registry.h>
|
||||||
#include <service.h>
|
#include <service.h>
|
||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
|
#include <route_model.h>
|
||||||
|
|
||||||
namespace Sandbox { class Child; }
|
namespace Sandbox { class Child; }
|
||||||
|
|
||||||
@ -113,6 +114,21 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
|||||||
|
|
||||||
Reconstructible<Buffered_xml> _start_node;
|
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); });
|
||||||
|
|
||||||
|
if (_route_model.constructed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_route_model.construct(_alloc, _default_route_accessor.default_route());
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Version attribute of the start node, used to force child restarts.
|
* Version attribute of the start node, used to force child restarts.
|
||||||
*/
|
*/
|
||||||
|
280
repos/os/src/lib/sandbox/route_model.h
Normal file
280
repos/os/src/lib/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 <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 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_ */
|
Loading…
Reference in New Issue
Block a user