mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-27 06:39:22 +00:00
5a1cef6381
This patch unconditionally applies the labeling of sessions and thereby removes the most common use case of 'Child_policy::filter_session_args'. Furthermore, the patch removes an ambiguity of the session labels of sessions created by the parent of behalf of its child, e.g., the PD session created as part of 'Child' now has the label "<child-name>" whereas an unlabeled PD-session request originating from the child has the label "<child-name> -> ". This way, the routing-policy of 'Child_policy::resolve_session_request' can differentiate both cases. As a consequence, the stricter labeling must now be considered wherever a precise label was specified as a key for a session route or a server- side policy selection. The simplest way to adapt those cases is to use a 'label_prefix' instead of the 'label' attribute. Alternatively, the 'label' attribute may used by appending " -> " (note the whitespace). Fixes #2171
853 lines
26 KiB
C++
853 lines
26 KiB
C++
/*
|
|
* \brief Representation used for children of the init process
|
|
* \author Norman Feske
|
|
* \date 2010-05-04
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2010-2016 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#ifndef _INCLUDE__INIT__CHILD_H_
|
|
#define _INCLUDE__INIT__CHILD_H_
|
|
|
|
/* Genode includes */
|
|
#include <pd_session/connection.h>
|
|
#include <ram_session/connection.h>
|
|
#include <cpu_session/connection.h>
|
|
#include <cap_session/connection.h>
|
|
#include <base/log.h>
|
|
#include <base/child.h>
|
|
#include <os/session_requester.h>
|
|
#include <os/session_policy.h>
|
|
|
|
/* init includes */
|
|
#include <init/child_config.h>
|
|
#include <init/child_policy.h>
|
|
|
|
namespace Init {
|
|
|
|
class Routed_service;
|
|
class Name_registry;
|
|
class Child_registry;
|
|
class Child;
|
|
|
|
using Genode::log;
|
|
using Genode::error;
|
|
using Genode::warning;
|
|
using Genode::Session_state;
|
|
using Genode::Xml_generator;
|
|
using Genode::Parent;
|
|
using Genode::Id_space;
|
|
|
|
typedef Genode::Registered<Genode::Parent_service> Parent_service;
|
|
}
|
|
|
|
|
|
/***************
|
|
** Utilities **
|
|
***************/
|
|
|
|
namespace Init {
|
|
|
|
extern bool config_verbose;
|
|
|
|
static void warn_insuff_quota(Genode::size_t const avail)
|
|
{
|
|
if (!config_verbose) { return; }
|
|
log("Warning: Specified quota exceeds available quota.");
|
|
log(" Proceeding with a quota of ", avail, ".");
|
|
}
|
|
|
|
inline long read_priority(Genode::Xml_node start_node, long prio_levels)
|
|
{
|
|
long priority = Genode::Cpu_session::DEFAULT_PRIORITY;
|
|
try { start_node.attribute("priority").value(&priority); }
|
|
catch (...) { }
|
|
|
|
/*
|
|
* 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)) {
|
|
long new_prio = prio_levels ? prio_levels-1 : 0;
|
|
char name[Genode::Service::Name::capacity()];
|
|
start_node.attribute("name").value(name, sizeof(name));
|
|
Genode::warning(Genode::Cstring(name), ": invalid priority, upgrading "
|
|
"from ", -priority, " to ", -new_prio);
|
|
return new_prio;
|
|
}
|
|
|
|
return priority;
|
|
}
|
|
|
|
|
|
inline Genode::Affinity::Location
|
|
read_affinity_location(Genode::Affinity::Space const &space,
|
|
Genode::Xml_node start_node)
|
|
{
|
|
typedef Genode::Affinity::Location Location;
|
|
try {
|
|
Genode::Xml_node node = start_node.sub_node("affinity");
|
|
|
|
/* if no position value is specified, select the whole row/column */
|
|
unsigned long const
|
|
default_width = node.has_attribute("xpos") ? 1 : space.width(),
|
|
default_height = node.has_attribute("ypos") ? 1 : space.height();
|
|
|
|
unsigned long const
|
|
width = node.attribute_value<unsigned long>("width", default_width),
|
|
height = node.attribute_value<unsigned long>("height", default_height);
|
|
|
|
long const x1 = node.attribute_value<long>("xpos", 0),
|
|
y1 = node.attribute_value<long>("ypos", 0),
|
|
x2 = x1 + width - 1,
|
|
y2 = y1 + height - 1;
|
|
|
|
/* clip location to space boundary */
|
|
return Location(Genode::max(x1, 0L), Genode::max(y1, 0L),
|
|
Genode::min((unsigned)(x2 - x1 + 1), space.width()),
|
|
Genode::min((unsigned)(y2 - y1 + 1), space.height()));
|
|
}
|
|
catch (...) { return Location(0, 0, space.width(), space.height()); }
|
|
}
|
|
|
|
|
|
/**
|
|
* Return amount of RAM that is currently unused
|
|
*/
|
|
static inline Genode::size_t avail_slack_ram_quota()
|
|
{
|
|
Genode::size_t const preserve = 148*1024;
|
|
Genode::size_t const avail = Genode::env()->ram_session()->avail();
|
|
|
|
return avail > preserve ? avail - preserve : 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return sub string of label with the leading child name stripped out
|
|
*/
|
|
inline char const *skip_label_prefix(char const *child_name, char const *label)
|
|
{
|
|
Genode::size_t const child_name_len = Genode::strlen(child_name);
|
|
|
|
/*
|
|
* If the method was called with a valid "label" string, the
|
|
* following condition should be always satisfied. See the
|
|
* comment in 'service_node_args_condition_satisfied'.
|
|
*/
|
|
if (Genode::strcmp(child_name, label, child_name_len) == 0)
|
|
label += child_name_len;
|
|
|
|
/*
|
|
* If the original label was empty, the 'Child_policy_enforce_labeling'
|
|
* does not append a label separator after the child-name prefix. In
|
|
* this case, we resulting label is empty.
|
|
*/
|
|
if (*label == 0)
|
|
return label;
|
|
|
|
/*
|
|
* Skip label separator. This condition should be always satisfied.
|
|
*/
|
|
if (Genode::strcmp(" -> ", label, 4) == 0)
|
|
return label + 4;
|
|
|
|
Genode::warning("cannot skip label prefix while processing <if-arg>");
|
|
return label;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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(Genode::Xml_node service_node, char const *args,
|
|
Genode::Child_policy::Name const &child_name,
|
|
Genode::Service::Name const &service_name)
|
|
{
|
|
bool const service_matches =
|
|
service_node.has_type("any-service") ||
|
|
(service_node.has_type("service") &&
|
|
service_node.attribute("name").has_value(service_name.string()));
|
|
|
|
if (!service_matches)
|
|
return false;
|
|
|
|
Genode::Session_label const session_label(skip_label_prefix(
|
|
child_name.string(), Genode::label_from_args(args).string()));
|
|
|
|
return !Genode::Xml_node_label_score(service_node, session_label).conflict();
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if arguments satisfy the condition specified for the route
|
|
*/
|
|
inline bool service_node_args_condition_satisfied(Genode::Xml_node service_node,
|
|
Genode::Session_state::Args const &args,
|
|
Genode::Child_policy::Name const &child_name)
|
|
{
|
|
try {
|
|
Genode::Xml_node if_arg = service_node.sub_node("if-arg");
|
|
enum { KEY_MAX_LEN = 64, VALUE_MAX_LEN = 64 };
|
|
char key[KEY_MAX_LEN];
|
|
char value[VALUE_MAX_LEN];
|
|
if_arg.attribute("key").value(key, sizeof(key));
|
|
if_arg.attribute("value").value(value, sizeof(value));
|
|
|
|
char arg_value[VALUE_MAX_LEN];
|
|
Genode::Arg_string::find_arg(args.string(), key).string(arg_value, sizeof(arg_value), "");
|
|
|
|
/*
|
|
* Skip child-name prefix if the key is the process "label".
|
|
*
|
|
* Because 'filter_session_args' is called prior the call of
|
|
* 'resolve_session_request' from the 'Child::session' method,
|
|
* 'args' contains the filtered arguments, in particular the label
|
|
* prefixed with the child's name. For the 'if-args' declaration,
|
|
* however, we want to omit specifying this prefix because the
|
|
* session route is specific to the named start node anyway. So
|
|
* the prefix information is redundant.
|
|
*/
|
|
if (Genode::strcmp("label", key) == 0)
|
|
return Genode::strcmp(value, skip_label_prefix(child_name.string(), arg_value)) == 0;
|
|
|
|
return Genode::strcmp(value, arg_value) == 0;
|
|
} catch (...) { }
|
|
|
|
/* if no if-arg node exists, the condition is met */
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if service name is ambiguous
|
|
*
|
|
* \return true if the same service is provided multiple
|
|
* times
|
|
*
|
|
* \deprecated
|
|
*/
|
|
template <typename T>
|
|
inline bool is_ambiguous(Genode::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) {
|
|
cnt += (service.name() == name); });
|
|
|
|
return cnt > 1;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline Genode::Service *find_service(Genode::Registry<T> &services,
|
|
Genode::Service::Name const &name)
|
|
{
|
|
T *service = nullptr;
|
|
services.for_each([&] (T &s) {
|
|
if (!service && (s.name() == name))
|
|
service = &s; });
|
|
return service;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Init-specific representation of a child service
|
|
*/
|
|
class Init::Routed_service : public Genode::Child_service
|
|
{
|
|
public:
|
|
|
|
typedef Genode::Child_policy::Name Child_name;
|
|
|
|
private:
|
|
|
|
Child_name _child_name;
|
|
|
|
Genode::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 'Child_service'.
|
|
*/
|
|
Routed_service(Genode::Registry<Routed_service> &services,
|
|
Child_name const &child_name,
|
|
Id_space<Parent::Server> &server_id_space,
|
|
Session_state::Factory &factory,
|
|
Service::Name const &name,
|
|
Genode::Ram_session_capability ram,
|
|
Child_service::Wakeup &wakeup)
|
|
:
|
|
Child_service(server_id_space, factory, name, ram, wakeup),
|
|
_child_name(child_name), _registry_element(services, *this)
|
|
{ }
|
|
|
|
Child_name const &child_name() const { return _child_name; }
|
|
};
|
|
|
|
|
|
/**
|
|
* Interface for name database
|
|
*/
|
|
struct Init::Name_registry
|
|
{
|
|
virtual ~Name_registry() { }
|
|
|
|
typedef Genode::Child_policy::Name Name;
|
|
|
|
/**
|
|
* Check if specified name is unique
|
|
*
|
|
* \return false if name already exists
|
|
*/
|
|
virtual bool unique(const char *name) const = 0;
|
|
|
|
/**
|
|
* 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;
|
|
};
|
|
|
|
|
|
class Init::Child : Genode::Child_policy,
|
|
Genode::Child_service::Wakeup
|
|
{
|
|
public:
|
|
|
|
/**
|
|
* Exception type
|
|
*/
|
|
class Child_name_is_not_unique { };
|
|
|
|
private:
|
|
|
|
friend class Child_registry;
|
|
|
|
Genode::Env &_env;
|
|
|
|
Genode::List_element<Child> _list_element;
|
|
|
|
Genode::Xml_node _start_node;
|
|
|
|
Genode::Xml_node _default_route_node;
|
|
|
|
Name_registry &_name_registry;
|
|
|
|
/**
|
|
* Unique child name and file name of ELF binary
|
|
*/
|
|
struct Name
|
|
{
|
|
enum { MAX_NAME_LEN = 64 };
|
|
char file[MAX_NAME_LEN];
|
|
char unique[MAX_NAME_LEN];
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* Obtains file name and unique process name from XML node
|
|
*
|
|
* \param start_node XML start node
|
|
* \param registry registry tracking unique names
|
|
*/
|
|
Name(Genode::Xml_node start_node, Name_registry const ®istry) {
|
|
try {
|
|
start_node.attribute("name").value(unique, sizeof(unique)); }
|
|
catch (Genode::Xml_node::Nonexistent_attribute) {
|
|
Genode::warning("missing 'name' attribute in '<start>' entry");
|
|
throw; }
|
|
|
|
/* check for a name confict with the other children */
|
|
if (!registry.unique(unique)) {
|
|
Genode::error("child name \"", Genode::Cstring(unique), "\" "
|
|
"is not unique");
|
|
throw Child_name_is_not_unique();
|
|
}
|
|
|
|
/* use name as default file name if not declared otherwise */
|
|
Genode::strncpy(file, unique, sizeof(file));
|
|
|
|
/* check for a binary declaration */
|
|
try {
|
|
Genode::Xml_node binary = start_node.sub_node("binary");
|
|
binary.attribute("name").value(file, sizeof(file));
|
|
} catch (...) { }
|
|
}
|
|
} _name;
|
|
|
|
struct Read_quota
|
|
{
|
|
Read_quota(Genode::Xml_node start_node,
|
|
Genode::size_t & ram_quota,
|
|
Genode::size_t & cpu_quota_pc,
|
|
bool & constrain_phys)
|
|
{
|
|
cpu_quota_pc = 0;
|
|
constrain_phys = false;
|
|
|
|
Genode::Number_of_bytes ram_bytes = 0;
|
|
|
|
try {
|
|
Genode::Xml_node rsc = start_node.sub_node("resource");
|
|
for (;; rsc = rsc.next("resource")) {
|
|
try {
|
|
if (rsc.attribute("name").has_value("RAM")) {
|
|
rsc.attribute("quantum").value(&ram_bytes);
|
|
constrain_phys = rsc.attribute_value("constrain_phys", false);
|
|
} else if (rsc.attribute("name").has_value("CPU")) {
|
|
rsc.attribute("quantum").value(&cpu_quota_pc); }
|
|
} catch (...) { }
|
|
}
|
|
} catch (...) { }
|
|
ram_quota = ram_bytes;
|
|
|
|
/*
|
|
* If the configured RAM quota exceeds our own quota, we donate
|
|
* all remaining quota to the child but we need to count in
|
|
* our allocation of the child meta data from the heap.
|
|
* Hence, we preserve some of our own quota.
|
|
*/
|
|
Genode::size_t const ram_avail = avail_slack_ram_quota();
|
|
if (ram_quota > ram_avail) {
|
|
ram_quota = ram_avail;
|
|
warn_insuff_quota(ram_avail);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Resources assigned to the child
|
|
*/
|
|
struct Resources : Read_quota
|
|
{
|
|
long prio_levels_log2;
|
|
long priority;
|
|
Genode::Affinity affinity;
|
|
Genode::size_t ram_quota;
|
|
Genode::size_t cpu_quota_pc;
|
|
bool constrain_phys;
|
|
|
|
Resources(Genode::Xml_node start_node, const char *label,
|
|
long prio_levels,
|
|
Genode::Affinity::Space const &affinity_space)
|
|
:
|
|
Read_quota(start_node, ram_quota, cpu_quota_pc, constrain_phys),
|
|
prio_levels_log2(Genode::log2(prio_levels)),
|
|
priority(read_priority(start_node, prio_levels)),
|
|
affinity(affinity_space,
|
|
read_affinity_location(affinity_space, start_node))
|
|
{
|
|
/* deduce session costs from usable ram quota */
|
|
ram_quota = Genode::Child::effective_ram_quota(ram_quota);
|
|
}
|
|
} _resources;
|
|
|
|
/*
|
|
* Entry point used for serving the parent interface and the
|
|
* locally provided ROM sessions for the 'config' and 'binary'
|
|
* files.
|
|
*/
|
|
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
|
|
Genode::Rpc_entrypoint _entrypoint;
|
|
|
|
Genode::Parent_service _env_ram_service { _env, Genode::Ram_session::service_name() };
|
|
Genode::Parent_service _env_cpu_service { _env, Genode::Cpu_session::service_name() };
|
|
Genode::Parent_service _env_pd_service { _env, Genode:: Pd_session::service_name() };
|
|
Genode::Parent_service _env_log_service { _env, Genode::Log_session::service_name() };
|
|
Genode::Parent_service _env_rom_service { _env, Genode::Rom_session::service_name() };
|
|
|
|
Genode::Registry<Parent_service> &_parent_services;
|
|
Genode::Registry<Routed_service> &_child_services;
|
|
|
|
/**
|
|
* Private child configuration
|
|
*/
|
|
Init::Child_config _config;
|
|
|
|
Genode::Session_requester _session_requester;
|
|
|
|
/**
|
|
* Policy helpers
|
|
*/
|
|
Init::Child_policy_handle_cpu_priorities _priority_policy;
|
|
Init::Child_policy_provide_rom_file _config_policy;
|
|
Init::Child_policy_redirect_rom_file _configfile_policy;
|
|
Init::Child_policy_ram_phys _ram_session_policy;
|
|
|
|
Genode::Child _child { _env.rm(), _entrypoint, *this };
|
|
|
|
/**
|
|
* Child_service::Wakeup callback
|
|
*/
|
|
void wakeup_child_service() override
|
|
{
|
|
_session_requester.trigger_update();
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \throw Ram_session::Alloc_failed allocation of config buffer failed
|
|
* \throw Region_map::Attach_failed failed to temporarily attach
|
|
* config dataspace to local address
|
|
* space
|
|
*/
|
|
Child(Genode::Env &env,
|
|
Genode::Xml_node start_node,
|
|
Genode::Xml_node default_route_node,
|
|
Name_registry &name_registry,
|
|
long prio_levels,
|
|
Genode::Affinity::Space const &affinity_space,
|
|
Genode::Registry<Parent_service> &parent_services,
|
|
Genode::Registry<Routed_service> &child_services)
|
|
:
|
|
_env(env),
|
|
_list_element(this),
|
|
_start_node(start_node),
|
|
_default_route_node(default_route_node),
|
|
_name_registry(name_registry),
|
|
_name(start_node, name_registry),
|
|
_resources(start_node, _name.unique, prio_levels,
|
|
affinity_space),
|
|
_entrypoint(&_env.pd(), ENTRYPOINT_STACK_SIZE, _name.unique, false,
|
|
_resources.affinity.location()),
|
|
_parent_services(parent_services),
|
|
_child_services(child_services),
|
|
_config(_env.ram(), _env.rm(), start_node),
|
|
_session_requester(_entrypoint, _env.ram(), _env.rm()),
|
|
_priority_policy(_resources.prio_levels_log2, _resources.priority),
|
|
_config_policy("config", _config.dataspace(), &_entrypoint),
|
|
_configfile_policy("config", _config.filename()),
|
|
_ram_session_policy(_resources.constrain_phys)
|
|
{
|
|
using namespace Genode;
|
|
|
|
if (_resources.ram_quota == 0)
|
|
Genode::warning("no valid RAM resource for child "
|
|
"\"", Genode::Cstring(_name.unique), "\"");
|
|
|
|
if (config_verbose) {
|
|
log("child \"", Genode::Cstring(_name.unique), "\"");
|
|
log(" RAM quota: ", _resources.ram_quota);
|
|
log(" ELF binary: ", Cstring(_name.file));
|
|
log(" priority: ", _resources.priority);
|
|
}
|
|
|
|
/*
|
|
* 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[Genode::Service::Name::capacity()];
|
|
service_node.attribute("name").value(name, sizeof(name));
|
|
|
|
if (config_verbose)
|
|
log(" provides service ", Genode::Cstring(name));
|
|
|
|
new (_child.heap())
|
|
Routed_service(child_services, this->name(),
|
|
_session_requester.id_space(),
|
|
_child.session_factory(),
|
|
name, _child.ram_session_cap(), *this);
|
|
|
|
}
|
|
} catch (Xml_node::Nonexistent_sub_node) { }
|
|
}
|
|
|
|
virtual ~Child()
|
|
{
|
|
_child_services.for_each([&] (Routed_service &service) {
|
|
if (service.has_id_space(_session_requester.id_space()))
|
|
destroy(_child.heap(), &service); });
|
|
}
|
|
|
|
/**
|
|
* Return true if the child has the specified name
|
|
*/
|
|
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
|
|
|
/**
|
|
* Start execution of child
|
|
*/
|
|
void start() { _entrypoint.activate(); }
|
|
|
|
|
|
/****************************
|
|
** Child-policy interface **
|
|
****************************/
|
|
|
|
Child_policy::Name name() const override { return _name.unique; }
|
|
|
|
Binary_name binary_name() const override { return _name.file; }
|
|
|
|
Genode::Ram_session &ref_ram() override { return _env.ram(); }
|
|
Genode::Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); }
|
|
|
|
void init(Genode::Ram_session &session, Genode::Ram_session_capability cap) override
|
|
{
|
|
session.ref_account(Genode::env()->ram_session_cap());
|
|
Genode::env()->ram_session()->transfer_quota(cap, _resources.ram_quota);
|
|
}
|
|
|
|
void init(Genode::Cpu_session &session, Genode::Cpu_session_capability cap) override
|
|
{
|
|
using Genode::Cpu_session;
|
|
using Genode::size_t;
|
|
|
|
static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
|
|
size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100);
|
|
size_t need_adj = 0;
|
|
if (need > avail || avail == 0) {
|
|
warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100));
|
|
need_adj = Cpu_session::quota_lim_upscale(100, 100);
|
|
avail = 0;
|
|
} else {
|
|
need_adj = Cpu_session::quota_lim_upscale(need, avail);
|
|
avail -= need;
|
|
}
|
|
session.ref_account(_env.cpu_session_cap());
|
|
_env.cpu().transfer_quota(cap, need_adj);
|
|
}
|
|
|
|
Genode::Id_space<Genode::Parent::Server> &server_id_space() override {
|
|
return _session_requester.id_space(); }
|
|
|
|
Genode::Service &resolve_session_request(Genode::Service::Name const &service_name,
|
|
Genode::Session_state::Args const &args) override
|
|
{
|
|
/* route environment session requests to the parent */
|
|
Genode::Session_label const label(Genode::label_from_args(args.string()));
|
|
if (label == name()) {
|
|
if (service_name == Genode::Ram_session::service_name()) return _env_ram_service;
|
|
if (service_name == Genode::Cpu_session::service_name()) return _env_cpu_service;
|
|
if (service_name == Genode::Pd_session::service_name()) return _env_pd_service;
|
|
if (service_name == Genode::Log_session::service_name()) return _env_log_service;
|
|
}
|
|
|
|
/* route initial ROM requests (binary and linker) to the parent */
|
|
if (service_name == Genode::Rom_session::service_name()) {
|
|
if (label.last_element() == binary_name()) return _env_rom_service;
|
|
if (label.last_element() == linker_name()) return _env_rom_service;
|
|
}
|
|
|
|
Genode::Service *service = nullptr;
|
|
|
|
/* check for "config" ROM request */
|
|
if ((service = _config_policy.resolve_session_request(service_name.string(), args.string())))
|
|
return *service;
|
|
|
|
/* check for "session_requests" ROM request */
|
|
if (service_name == Genode::Rom_session::service_name()
|
|
&& label.last_element() == Genode::Session_requester::rom_name())
|
|
return _session_requester.service();
|
|
|
|
try {
|
|
Genode::Xml_node route_node = _default_route_node;
|
|
try {
|
|
route_node = _start_node.sub_node("route"); }
|
|
catch (...) { }
|
|
Genode::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, args.string(), name(), service_name))
|
|
continue;
|
|
|
|
if (!service_node_args_condition_satisfied(service_node, args, name()))
|
|
continue;
|
|
|
|
Genode::Xml_node target = service_node.sub_node();
|
|
for (; ; target = target.next()) {
|
|
|
|
if (target.has_type("parent")) {
|
|
|
|
if ((service = find_service(_parent_services, service_name)))
|
|
return *service;
|
|
|
|
if (!service_wildcard) {
|
|
warning(name(), ": service lookup for "
|
|
"\"", service_name, "\" at parent failed");
|
|
throw Genode::Parent::Service_denied();
|
|
}
|
|
}
|
|
|
|
if (target.has_type("child")) {
|
|
|
|
typedef Name_registry::Name Name;
|
|
Name server_name = target.attribute_value("name", Name());
|
|
server_name = _name_registry.deref_alias(server_name);
|
|
|
|
_child_services.for_each([&] (Routed_service &s) {
|
|
if (s.name() == Service::Name(service_name)
|
|
&& s.child_name() == server_name)
|
|
service = &s; });
|
|
if (service)
|
|
return *service;
|
|
|
|
if (!service_wildcard) {
|
|
Genode::warning(name(), ": lookup to child "
|
|
"server \"", server_name, "\" failed");
|
|
throw Genode::Parent::Service_denied();
|
|
}
|
|
}
|
|
|
|
if (target.has_type("any-child")) {
|
|
if (is_ambiguous(_child_services, service_name)) {
|
|
error(name(), ": ambiguous routes to "
|
|
"service \"", service_name, "\"");
|
|
throw Genode::Parent::Service_denied();
|
|
}
|
|
|
|
if ((service = find_service(_child_services, service_name)))
|
|
return *service;
|
|
|
|
if (!service_wildcard) {
|
|
warning(name(), ": lookup for service "
|
|
"\"", service_name, "\" failed");
|
|
throw Genode::Parent::Service_denied();
|
|
}
|
|
}
|
|
|
|
if (target.last())
|
|
break;
|
|
}
|
|
}
|
|
} catch (...) {
|
|
warning(name(), ": no route to service \"", service_name, "\"");
|
|
}
|
|
|
|
if (!service)
|
|
throw Genode::Parent::Service_denied();
|
|
|
|
return *service;
|
|
}
|
|
|
|
void filter_session_args(Service::Name const &service,
|
|
char *args, Genode::size_t args_len) override
|
|
{
|
|
_priority_policy. filter_session_args(service.string(), args, args_len);
|
|
_configfile_policy. filter_session_args(service.string(), args, args_len);
|
|
_ram_session_policy.filter_session_args(service.string(), args, args_len);
|
|
}
|
|
|
|
Genode::Affinity filter_session_affinity(Genode::Affinity const &session_affinity) override
|
|
{
|
|
using namespace Genode;
|
|
|
|
Affinity::Space const &child_space = _resources.affinity.space();
|
|
Affinity::Location const &child_location = _resources.affinity.location();
|
|
|
|
/* check if no valid affinity space was specified */
|
|
if (session_affinity.space().total() == 0)
|
|
return Affinity(child_space, child_location);
|
|
|
|
Affinity::Space const &session_space = session_affinity.space();
|
|
Affinity::Location const &session_location = session_affinity.location();
|
|
|
|
/* scale resolution of resulting space */
|
|
Affinity::Space space(child_space.multiply(session_space));
|
|
|
|
/* subordinate session affinity to child affinity subspace */
|
|
Affinity::Location location(child_location
|
|
.multiply_position(session_space)
|
|
.transpose(session_location.xpos(),
|
|
session_location.ypos()));
|
|
|
|
return Affinity(space, location);
|
|
}
|
|
|
|
void announce_service(Genode::Service::Name const &service_name) override
|
|
{
|
|
log("child \"", name(), "\" announces service \"", service_name, "\"");
|
|
|
|
bool found = false;
|
|
_child_services.for_each([&] (Routed_service &service) {
|
|
if (service.has_id_space(_session_requester.id_space())
|
|
&& service.name() == service_name)
|
|
found = true; });
|
|
|
|
if (!found)
|
|
Genode::error(name(), ": illegal announcement of "
|
|
"service \"", service_name, "\"");
|
|
}
|
|
|
|
void resource_request(Genode::Parent::Resource_args const &args) override
|
|
{
|
|
log("child \"", name(), "\" requests resources: ", args.string());
|
|
|
|
Genode::size_t const requested_ram_quota =
|
|
Genode::Arg_string::find_arg(args.string(), "ram_quota")
|
|
.ulong_value(0);
|
|
|
|
if (avail_slack_ram_quota() < requested_ram_quota) {
|
|
Genode::warning("cannot respond to resource request - out of memory");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* XXX Synchronize quota transfers from/to env()->ram_session()
|
|
*
|
|
* If multiple children issue concurrent resource requests, the
|
|
* value reported by 'avail_slack_ram_quota' may be out of date
|
|
* when calling 'transfer_quota'.
|
|
*/
|
|
|
|
Genode::env()->ram_session()->transfer_quota(_child.ram_session_cap(),
|
|
requested_ram_quota);
|
|
|
|
/* wake up child that was starved for resources */
|
|
_child.notify_resource_avail();
|
|
}
|
|
|
|
void exit(int exit_value) override
|
|
{
|
|
try {
|
|
if (_start_node.sub_node("exit").attribute_value("propagate", false)) {
|
|
Genode::env()->parent()->exit(exit_value);
|
|
return;
|
|
}
|
|
} catch (...) { }
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
};
|
|
|
|
|
|
#endif /* _INCLUDE__INIT__CHILD_H_ */
|