mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 23:42:32 +00:00
ba2b0b8360
This patch also updates os/slave.h because the app/launcher cannot be reasonably updated without it. Issue #1987 Issue #3125
338 lines
8.7 KiB
C++
338 lines
8.7 KiB
C++
/*
|
|
* \brief Convenience helper for running a service as child process
|
|
* \author Norman Feske
|
|
* \author Christian Helmuth
|
|
* \date 2012-01-25
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2012-2017 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 _INCLUDE__OS__SLAVE_H_
|
|
#define _INCLUDE__OS__SLAVE_H_
|
|
|
|
/* Genode includes */
|
|
#include <base/rpc_server.h>
|
|
#include <base/local_connection.h>
|
|
#include <base/child.h>
|
|
#include <init/child_policy.h>
|
|
#include <os/child_policy_dynamic_rom.h>
|
|
#include <os/session_requester.h>
|
|
|
|
namespace Genode { struct Slave; }
|
|
|
|
|
|
struct Genode::Slave
|
|
{
|
|
typedef Session_state::Args Args;
|
|
|
|
class Policy;
|
|
|
|
template <typename>
|
|
class Connection_base;
|
|
|
|
template <typename>
|
|
struct Connection;
|
|
};
|
|
|
|
|
|
class Genode::Slave::Policy : public Child_policy
|
|
{
|
|
public:
|
|
|
|
typedef Child_policy::Name Name;
|
|
typedef Session_label Label;
|
|
|
|
typedef Registered<Genode::Parent_service> Parent_service;
|
|
typedef Registry<Parent_service> Parent_services;
|
|
|
|
private:
|
|
|
|
Label const _label;
|
|
Binary_name const _binary_name;
|
|
Pd_session &_ref_pd;
|
|
Pd_session_capability _ref_pd_cap;
|
|
Genode::Parent_service _binary_service;
|
|
Cap_quota const _cap_quota;
|
|
Ram_quota const _ram_quota;
|
|
Parent_services &_parent_services;
|
|
Rpc_entrypoint &_ep;
|
|
Child_policy_dynamic_rom_file _config_policy;
|
|
Session_requester _session_requester;
|
|
|
|
Service &_matching_service(Service::Name const &service_name,
|
|
Session_label const &label)
|
|
{
|
|
/* check for config file request */
|
|
if (Service *s = _config_policy.resolve_session_request(service_name, label))
|
|
return *s;
|
|
|
|
if (service_name == "ROM") {
|
|
Session_label const rom_name(label.last_element());
|
|
if (rom_name == _binary_name) return _binary_service;
|
|
if (rom_name == "session_requests") return _session_requester.service();
|
|
}
|
|
|
|
/* fill parent service registry on demand */
|
|
Parent_service *service = nullptr;
|
|
_parent_services.for_each([&] (Parent_service &s) {
|
|
if (!service && s.name() == service_name)
|
|
service = &s; });
|
|
|
|
if (!service) {
|
|
error(name(), ": illegal session request of "
|
|
"service \"", service_name, "\" (", label, ")");
|
|
throw Service_denied();
|
|
}
|
|
|
|
return *service;
|
|
}
|
|
|
|
public:
|
|
|
|
class Connection;
|
|
|
|
/**
|
|
* Slave-policy constructor
|
|
*
|
|
* \param ep entrypoint used to provide local services
|
|
* such as the config ROM service
|
|
*
|
|
* \throw Out_of_ram by 'Child_policy_dynamic_rom_file'
|
|
* \throw Out_of_caps by 'Child_policy_dynamic_rom_file'
|
|
*/
|
|
Policy(Env &env,
|
|
Label const &label,
|
|
Name const &binary_name,
|
|
Parent_services &parent_services,
|
|
Rpc_entrypoint &ep,
|
|
Cap_quota cap_quota,
|
|
Ram_quota ram_quota)
|
|
:
|
|
_label(label), _binary_name(binary_name),
|
|
_ref_pd(env.pd()), _ref_pd_cap(env.pd_session_cap()),
|
|
_binary_service(env, Rom_session::service_name()),
|
|
_cap_quota(cap_quota), _ram_quota(ram_quota),
|
|
_parent_services(parent_services), _ep(ep),
|
|
_config_policy(env.rm(), "config", _ep, &env.pd()),
|
|
_session_requester(ep, env.pd(), env.rm())
|
|
{
|
|
configure("<config/>");
|
|
}
|
|
|
|
/**
|
|
* Assign new configuration to slave
|
|
*
|
|
* \param config new configuration as null-terminated string
|
|
*/
|
|
void configure(char const *config)
|
|
{
|
|
_config_policy.load(config, strlen(config) + 1);
|
|
}
|
|
|
|
void configure(char const *config, size_t len)
|
|
{
|
|
_config_policy.load(config, len);
|
|
}
|
|
|
|
void trigger_session_requests()
|
|
{
|
|
_session_requester.trigger_update();
|
|
}
|
|
|
|
|
|
/****************************
|
|
** Child_policy interface **
|
|
****************************/
|
|
|
|
Name name() const override { return _label; }
|
|
|
|
Binary_name binary_name() const override { return _binary_name; }
|
|
|
|
Pd_session &ref_pd() override { return _ref_pd; }
|
|
Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
|
|
|
|
void init(Pd_session &session, Pd_session_capability cap) override
|
|
{
|
|
session.ref_account(_ref_pd_cap);
|
|
_ref_pd.transfer_quota(cap, _cap_quota);
|
|
_ref_pd.transfer_quota(cap, _ram_quota);
|
|
}
|
|
|
|
Route resolve_session_request(Service::Name const &name,
|
|
Session_label const &label) override
|
|
{
|
|
return Route { .service = _matching_service(name, label),
|
|
.label = label,
|
|
.diag = Session::Diag() };
|
|
}
|
|
|
|
Id_space<Parent::Server> &server_id_space() override {
|
|
return _session_requester.id_space(); }
|
|
};
|
|
|
|
|
|
template <typename CONNECTION>
|
|
class Genode::Slave::Connection_base
|
|
{
|
|
protected:
|
|
|
|
/* each connection appears as a separate client */
|
|
Id_space<Parent::Client> _id_space;
|
|
|
|
Policy &_policy;
|
|
|
|
struct Service : Genode::Service, Session_state::Ready_callback,
|
|
Session_state::Closed_callback
|
|
{
|
|
Policy &_policy;
|
|
Lock _lock { Lock::LOCKED };
|
|
bool _alive = false;
|
|
|
|
Service(Policy &policy)
|
|
:
|
|
Genode::Service(CONNECTION::service_name()), _policy(policy)
|
|
{ }
|
|
|
|
void initiate_request(Session_state &session) override
|
|
{
|
|
switch (session.phase) {
|
|
|
|
case Session_state::CREATE_REQUESTED:
|
|
|
|
if (!session.id_at_server.constructed())
|
|
session.id_at_server.construct(session,
|
|
_policy.server_id_space());
|
|
|
|
session.ready_callback = this;
|
|
session.async_client_notify = true;
|
|
break;
|
|
|
|
case Session_state::UPGRADE_REQUESTED:
|
|
warning("upgrading slaves is not implemented");
|
|
session.phase = Session_state::CAP_HANDED_OUT;
|
|
break;
|
|
|
|
case Session_state::CLOSE_REQUESTED:
|
|
warning("closing slave connections is not implemented");
|
|
session.phase = Session_state::CLOSED;
|
|
break;
|
|
|
|
case Session_state::SERVICE_DENIED:
|
|
case Session_state::INSUFFICIENT_RAM_QUOTA:
|
|
case Session_state::INSUFFICIENT_CAP_QUOTA:
|
|
case Session_state::AVAILABLE:
|
|
case Session_state::CAP_HANDED_OUT:
|
|
case Session_state::CLOSED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void wakeup() override { }
|
|
|
|
/**
|
|
* Session_state::Ready_callback
|
|
*/
|
|
void session_ready(Session_state &session) override
|
|
{
|
|
_alive = session.alive();
|
|
_lock.unlock();
|
|
}
|
|
|
|
/**
|
|
* Session_state::Closed_callback
|
|
*/
|
|
void session_closed(Session_state &s) override { _lock.unlock(); }
|
|
|
|
/**
|
|
* Service ('Ram_transfer::Account') interface
|
|
*/
|
|
void transfer(Pd_session_capability to, Ram_quota amount) override
|
|
{
|
|
if (to.valid()) _policy.ref_pd().transfer_quota(to, amount);
|
|
}
|
|
|
|
/**
|
|
* Service ('Ram_transfer::Account') interface
|
|
*/
|
|
Pd_session_capability cap(Ram_quota) const override
|
|
{
|
|
return _policy.ref_pd_cap();
|
|
}
|
|
|
|
/**
|
|
* Service ('Cap_transfer::Account') interface
|
|
*/
|
|
void transfer(Pd_session_capability to, Cap_quota amount) override
|
|
{
|
|
if (to.valid()) _policy.ref_pd().transfer_quota(to, amount);
|
|
}
|
|
|
|
/**
|
|
* Service ('Cap_transfer::Account') interface
|
|
*/
|
|
Pd_session_capability cap(Cap_quota) const override
|
|
{
|
|
return _policy.ref_pd_cap();
|
|
}
|
|
|
|
} _service;
|
|
|
|
Local_connection<CONNECTION> _connection;
|
|
|
|
Connection_base(Policy &policy, Args const &args, Affinity const &affinity)
|
|
:
|
|
_policy(policy), _service(_policy),
|
|
_connection(_service, _id_space, { 1 }, args, affinity)
|
|
{
|
|
_policy.trigger_session_requests();
|
|
_service._lock.lock();
|
|
if (!_service._alive)
|
|
throw Service_denied();
|
|
}
|
|
|
|
~Connection_base()
|
|
{
|
|
_policy.trigger_session_requests();
|
|
_service._lock.lock();
|
|
}
|
|
|
|
typedef typename CONNECTION::Session_type SESSION;
|
|
|
|
Capability<SESSION> _cap() const { return _connection.cap(); }
|
|
};
|
|
|
|
|
|
template <typename CONNECTION>
|
|
struct Genode::Slave::Connection : private Connection_base<CONNECTION>,
|
|
public CONNECTION::Client
|
|
{
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \throw Service_denied parent denies session request
|
|
* \throw Out_of_ram our own quota does not suffice for
|
|
* the creation of the new session
|
|
* \throw Out_of_caps
|
|
*/
|
|
Connection(Slave::Policy &policy, Args const &args,
|
|
Affinity const &affinity = Affinity())
|
|
:
|
|
Connection_base<CONNECTION>(policy, args, affinity),
|
|
CONNECTION::Client(Connection_base<CONNECTION>::_cap())
|
|
{ }
|
|
|
|
Connection(Region_map &rm, Slave::Policy &policy, Args const &args,
|
|
Affinity const &affinity = Affinity())
|
|
:
|
|
Connection_base<CONNECTION>(policy, args, affinity),
|
|
CONNECTION::Client(rm, Connection_base<CONNECTION>::_cap())
|
|
{ }
|
|
};
|
|
|
|
#endif /* _INCLUDE__OS__SLAVE_H_ */
|