2012-10-09 13:45:33 +02:00
|
|
|
/*
|
|
|
|
* \brief Child creation framework
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2006-07-22
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2006-2013 Genode Labs GmbH
|
2012-10-09 13:45:33 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <base/child.h>
|
|
|
|
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
|
|
|
|
/***************
|
|
|
|
** Utilities **
|
|
|
|
***************/
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Guard for transferring quota donation
|
|
|
|
*
|
|
|
|
* This class is used to provide transactional semantics of quota
|
|
|
|
* transfers. Establishing a new session involves several steps, in
|
|
|
|
* particular subsequent quota transfers. If one intermediate step
|
|
|
|
* fails, we need to revert all quota transfers that already took
|
|
|
|
* place. When instantated at a local scope, a 'Transfer' object guards
|
|
|
|
* a quota transfer. If the scope is left without prior an explicit
|
|
|
|
* acknowledgement of the transfer (for example via an exception), the
|
|
|
|
* destructor the 'Transfer' object reverts the transfer in flight.
|
|
|
|
*/
|
|
|
|
class Transfer {
|
|
|
|
|
|
|
|
bool _ack;
|
|
|
|
size_t _quantum;
|
|
|
|
Ram_session_capability _from;
|
|
|
|
Ram_session_capability _to;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
class Quota_exceeded : Exception { };
|
|
|
|
|
2012-10-09 13:45:33 +02:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* \param quantim number of bytes to transfer
|
|
|
|
* \param from donator RAM session
|
|
|
|
* \param to receiver RAM session
|
2016-11-06 14:26:34 +01:00
|
|
|
*
|
|
|
|
* \throw Quota_exceeded
|
2012-10-09 13:45:33 +02:00
|
|
|
*/
|
|
|
|
Transfer(size_t quantum,
|
|
|
|
Ram_session_capability from,
|
|
|
|
Ram_session_capability to)
|
|
|
|
: _ack(false), _quantum(quantum), _from(from), _to(to)
|
|
|
|
{
|
|
|
|
if (_from.valid() && _to.valid() &&
|
|
|
|
Ram_session_client(_from).transfer_quota(_to, quantum)) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
warning("not enough quota for a donation of ", quantum, " bytes");
|
2016-11-06 14:26:34 +01:00
|
|
|
throw Quota_exceeded();
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
*
|
|
|
|
* The destructor will be called when leaving the scope of the
|
|
|
|
* 'session' function. If the scope is left because of an error
|
|
|
|
* (e.g., an exception), the donation will be reverted.
|
|
|
|
*/
|
|
|
|
~Transfer()
|
|
|
|
{
|
|
|
|
if (!_ack && _from.valid() && _to.valid())
|
|
|
|
Ram_session_client(_to).transfer_quota(_from, _quantum);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Acknowledge quota donation
|
|
|
|
*/
|
|
|
|
void acknowledge() { _ack = true; }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/***********
|
|
|
|
** Child **
|
|
|
|
***********/
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
template <typename SESSION>
|
|
|
|
static Service &parent_service()
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
static Parent_service service(SESSION::service_name());
|
|
|
|
return service;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
void Child::yield(Resource_args const &args)
|
|
|
|
{
|
|
|
|
Lock::Guard guard(_yield_request_lock);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* buffer yield request arguments to be picked up by the child */
|
|
|
|
_yield_request_args = args;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* notify the child about the yield request */
|
|
|
|
if (_yield_sigh.valid())
|
|
|
|
Signal_transmitter(_yield_sigh).submit();
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
void Child::notify_resource_avail() const
|
|
|
|
{
|
|
|
|
if (_resource_avail_sigh.valid())
|
|
|
|
Signal_transmitter(_resource_avail_sigh).submit();
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
void Child::announce(Parent::Service_name const &name)
|
|
|
|
{
|
|
|
|
if (!name.valid_string()) return;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
_policy.announce_service(name.string());
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
void Child::session_sigh(Signal_context_capability sigh)
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
_session_sigh = sigh;
|
|
|
|
|
|
|
|
if (!_session_sigh.valid())
|
|
|
|
return;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
/*
|
2016-11-06 14:26:34 +01:00
|
|
|
* Deliver pending session response if a session became available before
|
|
|
|
* the signal handler got installed. This can happen for the very first
|
|
|
|
* asynchronously created session of a component. In 'component.cc', the
|
|
|
|
* signal handler is registered as response of the session request that
|
|
|
|
* needs asynchronous handling.
|
2012-10-09 13:45:33 +02:00
|
|
|
*/
|
2016-11-06 14:26:34 +01:00
|
|
|
_id_space.for_each<Session_state const>([&] (Session_state const &session) {
|
|
|
|
if (session.phase == Session_state::AVAILABLE
|
|
|
|
&& sigh.valid() && session.async_client_notify)
|
|
|
|
Signal_transmitter(sigh).submit(); });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create session-state object for a dynamically created session
|
|
|
|
*
|
|
|
|
* \throw Parent::Quota_exceeded
|
|
|
|
* \throw Parent::Service_denied
|
|
|
|
*/
|
|
|
|
Session_state &
|
|
|
|
create_session(Child_policy::Name const &child_name, Service &service,
|
|
|
|
Session_state::Factory &factory, Id_space<Parent::Client> &id_space,
|
|
|
|
Parent::Client::Id id, Session_state::Args const &args,
|
|
|
|
Affinity const &affinity)
|
|
|
|
{
|
2012-10-09 13:45:33 +02:00
|
|
|
try {
|
2016-11-06 14:26:34 +01:00
|
|
|
return service.create_session(factory, id_space, id, args, affinity);
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
catch (Allocator::Out_of_memory) {
|
2016-11-06 14:26:34 +01:00
|
|
|
error("could not allocate session meta data for child ", child_name);
|
|
|
|
throw Parent::Quota_exceeded();
|
|
|
|
}
|
|
|
|
catch (Id_space<Parent::Client>::Conflicting_id) {
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
error(child_name, " requested conflicting session ID ", id, " "
|
|
|
|
"(service=", service.name(), " args=", args, ")");
|
|
|
|
|
|
|
|
id_space.apply<Session_state>(id, [&] (Session_state &session) {
|
|
|
|
error("existing session: ", session); });
|
|
|
|
}
|
|
|
|
throw Parent::Service_denied();
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Session_capability Child::session(Parent::Client::Id id,
|
|
|
|
Parent::Service_name const &name,
|
|
|
|
Parent::Session_args const &args,
|
|
|
|
Affinity const &affinity)
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
if (!name.valid_string() || !args.valid_string()) throw Unavailable();
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
char argbuf[Parent::Session_args::MAX_SIZE];
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* filter session arguments according to the child policy */
|
|
|
|
strncpy(argbuf, args.string(), sizeof(argbuf));
|
|
|
|
_policy.filter_session_args(name.string(), argbuf, sizeof(argbuf));
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* filter session affinity */
|
|
|
|
Affinity const filtered_affinity = _policy.filter_session_affinity(affinity);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* may throw a 'Parent::Service_denied' exception */
|
|
|
|
Service &service = _policy.resolve_session_request(name.string(), argbuf);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Session_state &session =
|
|
|
|
create_session(_policy.name(), service, _session_factory,
|
|
|
|
_id_space, id, argbuf, filtered_affinity);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
session.ready_callback = this;
|
|
|
|
session.closed_callback = this;
|
|
|
|
|
|
|
|
/* transfer the quota donation from the child's account to ourself */
|
|
|
|
size_t ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0);
|
|
|
|
|
|
|
|
try {
|
|
|
|
Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap());
|
|
|
|
|
|
|
|
/* transfer session quota from ourself to the service provider */
|
|
|
|
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
|
|
|
|
service.ram());
|
|
|
|
|
|
|
|
/* try to dispatch session request synchronously */
|
|
|
|
service.initiate_request(session);
|
|
|
|
|
|
|
|
if (session.phase == Session_state::INVALID_ARGS) {
|
|
|
|
_revert_quota_and_destroy(session);
|
|
|
|
throw Service_denied();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finish transaction */
|
|
|
|
donation_from_child.acknowledge();
|
|
|
|
donation_to_service.acknowledge();
|
|
|
|
}
|
|
|
|
catch (Transfer::Quota_exceeded) {
|
|
|
|
/*
|
|
|
|
* Release session meta data if one of the quota transfers went wrong.
|
|
|
|
*/
|
|
|
|
session.destroy();
|
|
|
|
throw Parent::Quota_exceeded();
|
2015-08-10 13:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-11-06 14:26:34 +01:00
|
|
|
* Copy out the session cap before we are potentially kicking off the
|
|
|
|
* asynchonous request handling at the server to avoid doule-read
|
|
|
|
* issues with the session.cap, which will be asynchronously assigned
|
|
|
|
* by the server side.
|
2015-08-10 13:34:16 +02:00
|
|
|
*/
|
2016-11-06 14:26:34 +01:00
|
|
|
Session_capability cap = session.cap;
|
2015-08-10 13:34:16 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* if request was not handled synchronously, kick off async operation */
|
|
|
|
if (session.phase == Session_state::CREATE_REQUESTED)
|
|
|
|
service.wakeup();
|
2015-08-10 13:34:16 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (cap.valid())
|
|
|
|
session.phase = Session_state::CAP_HANDED_OUT;
|
|
|
|
|
|
|
|
return cap;
|
2015-08-10 13:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Session_capability Child::session_cap(Client::Id id)
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
Session_capability cap;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
auto lamda = [&] (Session_state &session) {
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (session.phase == Session_state::INVALID_ARGS) {
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/*
|
|
|
|
* Implicity discard the session request when delivering an
|
|
|
|
* exception because the exception will trigger the deallocation
|
|
|
|
* of the session ID at the child anyway.
|
|
|
|
*/
|
|
|
|
_revert_quota_and_destroy(session);
|
|
|
|
throw Parent::Service_denied();
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (!session.alive())
|
|
|
|
warning(_policy.name(), ": attempt to request cap for unavailable session: ", session);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (session.cap.valid())
|
|
|
|
session.phase = Session_state::CAP_HANDED_OUT;
|
2013-09-25 10:51:57 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
cap = session.cap;
|
|
|
|
};
|
2013-09-25 10:51:57 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
try {
|
|
|
|
_id_space.apply<Session_state>(id, lamda); }
|
2013-09-25 10:51:57 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
catch (Id_space<Parent::Client>::Unknown_id) {
|
|
|
|
warning(_policy.name(), " requested session cap for unknown ID"); }
|
2013-09-25 10:51:57 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
return cap;
|
2013-09-25 10:51:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const &args)
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
if (!args.valid_string()) {
|
|
|
|
warning("no valid session-upgrade arguments");
|
|
|
|
return UPGRADE_DONE;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Upgrade_result result = UPGRADE_PENDING;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
_id_space.apply<Session_state>(id, [&] (Session_state &session) {
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (session.phase != Session_state::CAP_HANDED_OUT) {
|
|
|
|
warning("attempt to upgrade session in invalid state");
|
|
|
|
return;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
size_t const ram_quota =
|
|
|
|
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
try {
|
|
|
|
/* transfer quota from client to ourself */
|
|
|
|
Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap());
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* transfer session quota from ourself to the service provider */
|
|
|
|
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
|
|
|
|
session.service().ram());
|
2013-08-14 21:19:11 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
session.increase_donated_quota(ram_quota);
|
|
|
|
session.phase = Session_state::UPGRADE_REQUESTED;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
session.service().initiate_request(session);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* finish transaction */
|
|
|
|
donation_from_child.acknowledge();
|
|
|
|
donation_to_service.acknowledge();
|
|
|
|
}
|
|
|
|
catch (Transfer::Quota_exceeded) {
|
|
|
|
warning(_policy.name(), ": upgrade of ", session.service().name(), " failed");
|
|
|
|
throw Parent::Quota_exceeded();
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (session.phase == Session_state::CAP_HANDED_OUT) {
|
|
|
|
result = UPGRADE_DONE;
|
|
|
|
return;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
session.service().wakeup();
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
void Child::_revert_quota_and_destroy(Session_state &session)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
/* transfer session quota from the service to ourself */
|
|
|
|
Transfer donation_from_service(session.donated_ram_quota(),
|
|
|
|
session.service().ram(), _policy.ref_ram_cap());
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* transfer session quota from ourself to the client (our child) */
|
|
|
|
Transfer donation_to_client(session.donated_ram_quota(),
|
|
|
|
_policy.ref_ram_cap(), ram_session_cap());
|
|
|
|
/* finish transaction */
|
|
|
|
donation_from_service.acknowledge();
|
|
|
|
donation_to_client.acknowledge();
|
|
|
|
}
|
|
|
|
catch (Transfer::Quota_exceeded) {
|
|
|
|
warning(_policy.name(), ": could not revert session quota (", session, ")"); }
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
session.destroy();
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Child::Close_result Child::_close(Session_state &session)
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
/*
|
|
|
|
* If session could not be established, destruct session immediately
|
|
|
|
* without involving the server
|
|
|
|
*/
|
|
|
|
if (session.phase == Session_state::INVALID_ARGS) {
|
|
|
|
_revert_quota_and_destroy(session);
|
|
|
|
return CLOSE_DONE;
|
|
|
|
}
|
2015-08-10 13:34:16 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/* close session if alive */
|
|
|
|
if (session.alive()) {
|
|
|
|
session.phase = Session_state::CLOSE_REQUESTED;
|
|
|
|
session.service().initiate_request(session);
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/*
|
|
|
|
* The service may have completed the close request immediately (e.g.,
|
|
|
|
* a locally implemented service). In this case, we can skip the
|
|
|
|
* asynchonous handling.
|
|
|
|
*/
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
if (session.phase == Session_state::CLOSED) {
|
|
|
|
_revert_quota_and_destroy(session);
|
|
|
|
return CLOSE_DONE;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
session.discard_id_at_client();
|
|
|
|
session.service().wakeup();
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
return CLOSE_PENDING;
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
Child::Close_result Child::close(Client::Id id)
|
|
|
|
{
|
|
|
|
/* refuse to close the child's initial sessions */
|
|
|
|
if (Parent::Env::session_id(id))
|
|
|
|
return CLOSE_DONE;
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
try {
|
|
|
|
Close_result result = CLOSE_PENDING;
|
|
|
|
auto lamda = [&] (Session_state &session) { result = _close(session); };
|
|
|
|
_id_space.apply<Session_state>(id, lamda);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
catch (Id_space<Parent::Client>::Unknown_id) { return CLOSE_DONE; }
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
void Child::session_ready(Session_state &session)
|
2012-10-09 13:45:33 +02:00
|
|
|
{
|
2016-11-06 14:26:34 +01:00
|
|
|
if (_session_sigh.valid() && session.async_client_notify)
|
|
|
|
Signal_transmitter(_session_sigh).submit();
|
|
|
|
}
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
|
|
|
|
void Child::session_closed(Session_state &session)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the session was provided by a child of us, 'service.ram()' returns
|
|
|
|
* the RAM session of the corresponding child. Since the session to the
|
|
|
|
* server is closed now, we expect the server to have released all donated
|
|
|
|
* resources so that we can decrease the servers' quota.
|
|
|
|
*
|
|
|
|
* If this goes wrong, the server is misbehaving.
|
|
|
|
*/
|
|
|
|
_revert_quota_and_destroy(session);
|
|
|
|
|
|
|
|
if (_session_sigh.valid())
|
|
|
|
Signal_transmitter(_session_sigh).submit();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Child::session_response(Server::Id id, Session_response response)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
_policy.server_id_space().apply<Session_state>(id, [&] (Session_state &session) {
|
|
|
|
|
|
|
|
switch (response) {
|
|
|
|
|
|
|
|
case Parent::SESSION_CLOSED:
|
|
|
|
session.phase = Session_state::CLOSED;
|
|
|
|
if (session.closed_callback)
|
|
|
|
session.closed_callback->session_closed(session);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Parent::INVALID_ARGS:
|
|
|
|
session.phase = Session_state::INVALID_ARGS;
|
|
|
|
if (session.ready_callback)
|
|
|
|
session.ready_callback->session_ready(session);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Parent::SESSION_OK:
|
|
|
|
if (session.phase == Session_state::UPGRADE_REQUESTED) {
|
|
|
|
session.phase = Session_state::CAP_HANDED_OUT;
|
|
|
|
if (session.ready_callback)
|
|
|
|
session.ready_callback->session_ready(session);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (Child_policy::Nonexistent_id_space) { }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Child::deliver_session_cap(Server::Id id, Session_capability cap)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
_policy.server_id_space().apply<Session_state>(id, [&] (Session_state &session) {
|
|
|
|
|
|
|
|
if (session.cap.valid()) {
|
|
|
|
error("attempt to assign session cap twice");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
session.cap = cap;
|
|
|
|
session.phase = Session_state::AVAILABLE;
|
|
|
|
|
|
|
|
if (session.ready_callback)
|
|
|
|
session.ready_callback->session_ready(session);
|
|
|
|
});
|
|
|
|
} catch (Child_policy::Nonexistent_id_space) { }
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Child::exit(int exit_value)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This function receives the hint from the child that now, its a good time
|
|
|
|
* to kill it. An inherited child class could use this hint to schedule the
|
|
|
|
* destruction of the child object.
|
|
|
|
*
|
|
|
|
* Note that the child object must not be destructed from by this function
|
|
|
|
* because it is executed by the thread contained in the child object.
|
|
|
|
*/
|
2016-04-27 16:04:58 +02:00
|
|
|
return _policy.exit(exit_value);
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-10 13:05:54 +02:00
|
|
|
Thread_capability Child::main_thread_cap() const
|
|
|
|
{
|
2016-05-10 18:05:38 +02:00
|
|
|
return _process.initial_thread.cap();
|
2012-10-10 13:05:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-25 10:51:57 +02:00
|
|
|
void Child::resource_avail_sigh(Signal_context_capability sigh)
|
|
|
|
{
|
|
|
|
_resource_avail_sigh = sigh;
|
|
|
|
}
|
|
|
|
|
2013-09-19 20:39:35 +02:00
|
|
|
|
2013-09-25 10:51:57 +02:00
|
|
|
void Child::resource_request(Resource_args const &args)
|
|
|
|
{
|
2016-04-27 16:04:58 +02:00
|
|
|
_policy.resource_request(args);
|
2013-09-25 10:51:57 +02:00
|
|
|
}
|
2013-09-19 20:39:35 +02:00
|
|
|
|
|
|
|
|
2013-09-25 10:51:57 +02:00
|
|
|
void Child::yield_sigh(Signal_context_capability sigh) { _yield_sigh = sigh; }
|
2013-09-19 20:39:35 +02:00
|
|
|
|
|
|
|
|
2013-09-25 10:51:57 +02:00
|
|
|
Parent::Resource_args Child::yield_request()
|
|
|
|
{
|
|
|
|
Lock::Guard guard(_yield_request_lock);
|
2013-09-19 20:39:35 +02:00
|
|
|
|
2013-09-25 10:51:57 +02:00
|
|
|
return _yield_request_args;
|
|
|
|
}
|
2013-09-19 20:39:35 +02:00
|
|
|
|
|
|
|
|
2016-04-27 16:04:58 +02:00
|
|
|
void Child::yield_response() { _policy.yield_response(); }
|
2013-09-19 20:39:35 +02:00
|
|
|
|
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return interface for interacting with the child's address space
|
|
|
|
*
|
|
|
|
* Depending on the return value of 'Child_policy::address_space', we
|
|
|
|
* either interact with a local object of via an RPC client stub.
|
|
|
|
*/
|
|
|
|
struct Child_address_space
|
|
|
|
{
|
|
|
|
Region_map_client _rm_client;
|
|
|
|
Region_map &_rm;
|
|
|
|
|
|
|
|
Child_address_space(Pd_session &pd, Child_policy &policy)
|
|
|
|
:
|
|
|
|
_rm_client(pd.address_space()),
|
|
|
|
_rm(policy.address_space(pd) ? *policy.address_space(pd) : _rm_client)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
Region_map ®ion_map() { return _rm; }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Child::Child(Region_map &local_rm,
|
2016-04-27 16:04:58 +02:00
|
|
|
Rpc_entrypoint &entrypoint,
|
2016-11-06 14:26:34 +01:00
|
|
|
Child_policy &policy)
|
2016-04-27 16:04:58 +02:00
|
|
|
try :
|
2016-11-06 14:26:34 +01:00
|
|
|
_policy(policy),
|
|
|
|
_heap(&_ram.session(), &local_rm),
|
2012-10-09 13:45:33 +02:00
|
|
|
_entrypoint(entrypoint),
|
2016-04-27 16:04:58 +02:00
|
|
|
_parent_cap(_entrypoint.manage(this)),
|
2016-11-06 14:26:34 +01:00
|
|
|
_process(_binary.session().dataspace(), _linker_dataspace(),
|
|
|
|
_pd.cap(), _pd.session(), _ram.session(), _initial_thread, local_rm,
|
|
|
|
Child_address_space(_pd.session(), _policy).region_map(),
|
2016-05-10 18:05:38 +02:00
|
|
|
_parent_cap)
|
2012-10-09 13:45:33 +02:00
|
|
|
{ }
|
2016-04-27 16:04:58 +02:00
|
|
|
catch (Cpu_session::Thread_creation_failed) { throw Process_startup_failed(); }
|
|
|
|
catch (Cpu_session::Out_of_metadata) { throw Process_startup_failed(); }
|
|
|
|
catch (Process::Missing_dynamic_linker) { throw Process_startup_failed(); }
|
|
|
|
catch (Process::Invalid_executable) { throw Process_startup_failed(); }
|
|
|
|
catch (Region_map::Attach_failed) { throw Process_startup_failed(); }
|
2012-10-09 13:45:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
Child::~Child()
|
|
|
|
{
|
2016-04-27 16:04:58 +02:00
|
|
|
_entrypoint.dissolve(this);
|
2012-10-09 13:45:33 +02:00
|
|
|
|
2016-11-06 14:26:34 +01:00
|
|
|
/*
|
|
|
|
* Purge the meta data about any dangling sessions provided by the child to
|
|
|
|
* other children.
|
|
|
|
*
|
|
|
|
* Note that the session quota is not transferred back to the respective
|
|
|
|
* clients.
|
|
|
|
*
|
|
|
|
* All the session meta data is lost after this point. In principle, we
|
|
|
|
* could accumulate the to-be-replenished quota at each client. Once the
|
|
|
|
* server is completely destroyed (and we thereby regained all of the
|
|
|
|
* server's resources, the RAM sessions of the clients could be updated.
|
|
|
|
* However, a client of a suddenly disappearing server is expected to be in
|
|
|
|
* trouble anyway and likely to get stuck on the next attempt to interact
|
|
|
|
* with the server. So the added complexity of reverting the session quotas
|
|
|
|
* would be to no benefit.
|
|
|
|
*/
|
|
|
|
try {
|
|
|
|
auto lambda = [&] (Session_state &s) { _revert_quota_and_destroy(s); };
|
|
|
|
while (_policy.server_id_space().apply_any<Session_state>(lambda));
|
|
|
|
}
|
|
|
|
catch (Child_policy::Nonexistent_id_space) { }
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove statically created env sessions from the child's ID space.
|
|
|
|
*/
|
|
|
|
auto discard_id_fn = [&] (Session_state &s) { s.discard_id_at_client(); };
|
|
|
|
|
|
|
|
_id_space.apply<Session_state>(Env::ram(), discard_id_fn);
|
|
|
|
_id_space.apply<Session_state>(Env::cpu(), discard_id_fn);
|
|
|
|
_id_space.apply<Session_state>(Env::pd(), discard_id_fn);
|
|
|
|
_id_space.apply<Session_state>(Env::log(), discard_id_fn);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove dynamically created sessions from the child's ID space.
|
|
|
|
*/
|
|
|
|
auto close_fn = [&] (Session_state &session) {
|
|
|
|
session.closed_callback = nullptr;
|
|
|
|
session.ready_callback = nullptr;
|
|
|
|
_close(session);
|
|
|
|
};
|
|
|
|
|
|
|
|
while (_id_space.apply_any<Session_state>(close_fn));
|
2012-10-09 13:45:33 +02:00
|
|
|
}
|
|
|
|
|