Remove blocking calls from root and parent RPCs

This is a redesign of the root and parent interfaces to eliminate
blocking RPC calls.

- New session representation at the parent (base/session_state.h)
- base-internal root proxy mechanism as migration path
- Redesign of base/service.h
- Removes ancient 'Connection::KEEP_OPEN' feature
- Interface change of 'Child', 'Child_policy', 'Slave', 'Slave_policy'
- New 'Slave::Connection'
- Changed child-construction procedure to be compatible with the
  non-blocking parent interface and to be easier to use
- The child's initial LOG session, its binary ROM session, and the
  linker ROM session have become part of the child's envirenment.
- Session upgrading must now be performed via 'env.upgrade' instead
  of performing a sole RPC call the parent. To make RAM upgrades
  easier, the 'Connection' provides a new 'upgrade_ram' method.

Issue #2120
This commit is contained in:
Norman Feske 2016-11-06 14:26:34 +01:00 committed by Christian Helmuth
parent 3cc2a3f085
commit cfdbccc5c2
80 changed files with 3613 additions and 1854 deletions

View File

@ -18,4 +18,4 @@
#include <base/internal/native_env.h>
void Genode::upgrade_pd_session_quota(Genode::size_t quota) { assert(false); }
void Genode::upgrade_pd_quota_non_blocking(Genode::size_t quota) { assert(false); }

View File

@ -15,21 +15,21 @@
#include <base/service.h>
#include <base/heap.h>
/* Core includes */
/* core includes */
#include <platform_services.h>
#include <core_parent.h> /* for 'Core_service' type */
#include <vm_root.h>
/*
* Add ARM virtualization specific vm service
*/
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep,
Genode::Sliced_heap *sh,
Genode::Service_registry *ls)
void Genode::platform_add_local_services(Rpc_entrypoint *ep,
Sliced_heap *sh,
Registry<Service> *services)
{
using namespace Genode;
static Vm_root vm_root(ep, sh);
static Local_service vm_ls(Vm_session::service_name(), &vm_root);
ls->insert(&vm_ls);
static Core_service<Vm_session_component> vm_service(*services, vm_root);
}

View File

@ -18,19 +18,17 @@
/* Core includes */
#include <platform.h>
#include <platform_services.h>
#include <core_parent.h>
#include <vm_root.h>
/*
* Add TrustZone specific vm service
*/
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep,
Genode::Sliced_heap *sh,
Genode::Service_registry *ls)
void Genode::platform_add_local_services(Rpc_entrypoint *ep,
Sliced_heap *sliced_heap,
Registry<Service> *local_services)
{
using namespace Genode;
static Vm_root vm_root(ep, sh);
static Local_service vm_ls(Vm_session::service_name(), &vm_root);
ls->insert(&vm_ls);
static Vm_root vm_root(ep, sliced_heap);
static Core_service<Vm_session_component> vm_service(*local_services, vm_root);
}

View File

@ -14,7 +14,7 @@
/* Genode includes */
#include <base/service.h>
/* Core includes */
/* core includes */
#include <core_env.h>
#include <platform.h>
#include <platform_services.h>
@ -24,18 +24,15 @@
/*
* Add I/O port service and virtualization specific vm service
*/
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep,
Genode::Sliced_heap *sh,
Genode::Service_registry *ls)
void Genode::platform_add_local_services(Rpc_entrypoint *ep,
Sliced_heap *sh,
Registry<Service> *services)
{
using namespace Genode;
static Vm_root vm_root(ep, sh);
static Local_service vm_ls(Vm_session::service_name(), &vm_root);
static Core_service<Vm_session_component> vm_ls(*services, vm_root);
static Io_port_root io_port_root(core_env()->pd_session(),
platform()->io_port_alloc(), sh);
static Local_service io_port_ls(Io_port_session::service_name(),
&io_port_root);
ls->insert(&vm_ls);
ls->insert(&io_port_ls);
static Core_service<Io_port_session_component> io_port_ls(*services,
io_port_root);
}

View File

@ -20,9 +20,13 @@
namespace Genode
{
/**
* Upgrade quota of the PD session within my Genode environment
* Upgrade quota of the PD session within my Genode environment non-blocking
*
* This function doesn't lock the environment when upgrading. This is
* needed when doing upgrades in situations where the environment is
* already locked due to the operation that triggered the upgrade.
*/
void upgrade_pd_session_quota(Genode::size_t);
void upgrade_pd_quota_non_blocking(size_t);
};
#endif /* _INCLUDE__BASE__INTERNAL__NATIVE_ENV_H_ */

View File

@ -12,18 +12,14 @@
*/
/* Genode includes */
#include <pd_session/client.h>
#include <base/env.h>
/* base-internal includes */
#include <base/internal/globals.h>
#include <base/internal/native_env.h>
void Genode::upgrade_pd_session_quota(Genode::size_t quota)
void Genode::upgrade_pd_quota_non_blocking(size_t quota)
{
char buf[128];
snprintf(buf, sizeof(buf), "ram_quota=%lu", quota);
Pd_session_capability cap =
*static_cast<Pd_session_client*>(env()->pd_session());
env()->parent()->upgrade(cap, buf);
internal_env().parent().upgrade(Parent::Env::pd(),
String<64>("ram_quota=", quota).string());
}

View File

@ -109,7 +109,7 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
}
},
[&] () { upgrade_pd_session_quota(3*4096); });
[&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); });
return Rpc_exception_code(utcb.exception_code());
}
@ -154,7 +154,7 @@ Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &,
default: break;
}
},
[&] () { upgrade_pd_session_quota(3*4096); });
[&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); });
copy_utcb_to_msg(utcb, request_msg);

View File

@ -20,7 +20,9 @@
/* base-internal includes */
#include <base/internal/native_utcb.h>
#include <base/internal/native_env.h>
#include <base/internal/capability_space.h>
#include <base/internal/globals.h>
using namespace Genode;
@ -63,12 +65,10 @@ void Signal_transmitter::submit(unsigned cnt)
Signal_receiver::Signal_receiver()
{
retry<Pd_session::Out_of_metadata>(
[&] () {
_cap = env()->pd_session()->alloc_signal_source();
},
[&] () { _cap = internal_env().pd().alloc_signal_source(); },
[&] () {
log("upgrading quota donation for PD session");
env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K");
internal_env().upgrade(Parent::Env::pd(), "ram_quota=8K");
}
);
}
@ -104,11 +104,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c)
_contexts.insert(&c->_receiver_le);
return c->_cap;
},
[&] () {
log("upgrading quota donation for PD session");
env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K");
}
);
[&] () { upgrade_pd_quota_non_blocking(1024 * sizeof(addr_t)); });
return c->_cap;
}

View File

@ -24,6 +24,7 @@
#include <core_pd_session.h>
#include <ram_session_component.h>
#include <core_pd_session.h>
#include <base/service.h>
/* base-internal includes */
#include <base/internal/platform_env.h>
@ -138,8 +139,6 @@ namespace Genode {
typedef Synchronized_ram_session<Ram_session_component> Core_ram_session;
Core_parent _core_parent;
/*
* Initialize the stack area before creating the first thread,
* which happens to be the '_entrypoint'.
@ -163,6 +162,10 @@ namespace Genode {
Heap _heap;
Ram_session_capability const _ram_session_cap;
Registry<Service> _services;
Core_parent _core_parent { _heap, _services };
public:
/**
@ -210,6 +213,8 @@ namespace Genode {
warning(__FILE__, ":", __LINE__, " not implemented");
return Cpu_session_capability();
}
Registry<Service> &services() { return _services; }
};

View File

@ -23,7 +23,11 @@
/* base-internal includes */
#include <base/internal/expanding_parent_client.h>
namespace Genode { class Local_parent; }
namespace Genode {
class Local_session;
class Local_parent;
}
/**
@ -42,6 +46,7 @@ class Genode::Local_parent : public Expanding_parent_client
private:
Allocator &_alloc;
Id_space<Client> _local_sessions_id_space;
public:
@ -49,10 +54,9 @@ class Genode::Local_parent : public Expanding_parent_client
** Parent interface **
**********************/
Session_capability session(Service_name const &,
Session_args const &,
Affinity const & = Affinity());
void close(Session_capability);
Session_capability session(Client::Id, Service_name const &, Session_args const &,
Affinity const & = Affinity()) override;
Close_result close(Client::Id) override;
/**
* Constructor

View File

@ -19,17 +19,22 @@
#include <base/allocator.h>
/* base-internal includes */
#include <base/internal/local_session.h>
#include <base/internal/region_map_mmap.h>
#include <base/internal/local_capability.h>
namespace Genode { struct Local_rm_session; }
struct Genode::Local_rm_session : Rm_session
struct Genode::Local_rm_session : Rm_session, Local_session
{
Allocator &md_alloc;
Local_rm_session(Allocator &md_alloc) : md_alloc(md_alloc) { }
Local_rm_session(Allocator &md_alloc, Id_space<Parent::Client> &id_space,
Parent::Client::Id id)
:
Local_session(id_space, id, *this), md_alloc(md_alloc)
{ }
Capability<Region_map> create(size_t size)
{

View File

@ -0,0 +1,44 @@
/*
* \brief Meta data for component-local sessions
* \author Norman Feske
* \date 2016-10-13
*/
/*
* Copyright (C) 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__BASE__INTERNAL__LOCAL_SESSION_H_
#define _INCLUDE__BASE__INTERNAL__LOCAL_SESSION_H_
/* Genode includes */
#include <base/id_space.h>
namespace Genode { struct Local_session; }
struct Genode::Local_session : Parent::Client
{
private:
Id_space<Parent::Client>::Element _id_space_element;
Session_capability _cap;
public:
Local_session(Id_space<Parent::Client> &id_space, Parent::Client::Id id,
Session &session)
:
_id_space_element(*this, id_space, id),
_cap(Local_capability<Session>::local_cap(&session))
{ }
Capability<Session> local_session_cap() { return _cap; }
};
#endif /* _INCLUDE__BASE__INTERNAL__LOCAL_SESSION_H_ */

View File

@ -69,9 +69,9 @@ class Genode::Platform_env_base : public Env_deprecated
Pd_session_capability pd_cap)
:
_ram_session_cap(ram_cap),
_ram_session_client(_ram_session_cap),
_ram_session_client(_ram_session_cap, Parent::Env::ram()),
_cpu_session_cap(cpu_cap),
_cpu_session_client(cpu_cap),
_cpu_session_client(cpu_cap, Parent::Env::cpu()),
_region_map_mmap(false),
_pd_session_cap(pd_cap),
_local_pd_session(_pd_session_cap)

View File

@ -59,37 +59,44 @@ bool Region_map_mmap::_dataspace_writable(Dataspace_capability ds)
** Local_parent **
******************/
Session_capability Local_parent::session(Service_name const &service_name,
Session_capability Local_parent::session(Parent::Client::Id id,
Service_name const &service_name,
Session_args const &args,
Affinity const &affinity)
{
if (strcmp(service_name.string(), Rm_session::service_name()) == 0)
{
Local_rm_session *session = new (_alloc) Local_rm_session(_alloc);
if (strcmp(service_name.string(), Rm_session::service_name()) == 0) {
return Local_capability<Session>::local_cap(session);
Local_rm_session *local_rm_session = new (_alloc)
Local_rm_session(_alloc, _local_sessions_id_space, id);
return local_rm_session->local_session_cap();
}
return Expanding_parent_client::session(service_name, args, affinity);
return Expanding_parent_client::session(id, service_name, args, affinity);
}
void Local_parent::close(Session_capability session)
Parent::Close_result Local_parent::close(Client::Id id)
{
auto close_local_fn = [&] (Local_session &local_session)
{
Capability<Rm_session> rm =
static_cap_cast<Rm_session>(local_session.local_session_cap());
destroy(_alloc, Local_capability<Rm_session>::deref(rm));
};
/*
* Handle non-local capabilities
* Local RM sessions are present in the '_local_sessions_id_space'. If the
* apply succeeds, 'id' referred to the local session. Otherwise, we
* forward the request to the parent.
*/
if (session.valid()) {
Parent_client::close(session);
return;
try {
_local_sessions_id_space.apply<Local_session>(id, close_local_fn);
return CLOSE_DONE;
}
catch (Id_space<Client>::Unknown_id) { }
/*
* Detect capability to local RM session
*/
Capability<Rm_session> rm = static_cap_cast<Rm_session>(session);
destroy(_alloc, Local_capability<Rm_session>::deref(rm));
return Parent_client::close(id);
}
@ -148,9 +155,9 @@ Local_parent &Platform_env::_parent()
Platform_env::Platform_env()
:
Platform_env_base(static_cap_cast<Ram_session>(_parent().session("Env::ram_session", "")),
static_cap_cast<Cpu_session>(_parent().session("Env::cpu_session", "")),
static_cap_cast<Pd_session> (_parent().session("Env::pd_session", ""))),
Platform_env_base(static_cap_cast<Ram_session>(_parent().session_cap(Parent::Env::ram())),
static_cap_cast<Cpu_session>(_parent().session_cap(Parent::Env::cpu())),
static_cap_cast<Pd_session> (_parent().session_cap(Parent::Env::pd()))),
_heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()),
_emergency_ram_ds(ram_session()->alloc(_emergency_ram_size()))
{

View File

@ -17,6 +17,9 @@
#include <base/rpc_server.h>
#include <pd_session/client.h>
/* base-internal includes */
#include <base/internal/globals.h>
/* NOVA-specific part of the PD session interface */
#include <nova_native_pd/client.h>
@ -37,11 +40,7 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd, Native_capabili
return native_pd.alloc_rpc_cap(ep, entry, 0);
},
[&] () {
Pd_session_client *client =
dynamic_cast<Pd_session_client*>(&pd);
if (client)
env()->parent()->upgrade(*client, "ram_quota=16K");
internal_env().upgrade(Parent::Env::pd(), "ram_quota=16K");
});
native_pd.imprint_rpc_cap(new_obj_cap, new_obj_cap.local_name());

View File

@ -18,11 +18,14 @@
#include <base/heap.h>
#include <base/service.h>
#include <base/lock.h>
#include <base/local_connection.h>
#include <util/arg_string.h>
#include <ram_session/capability.h>
#include <ram_session/connection.h>
#include <region_map/client.h>
#include <pd_session/client.h>
#include <cpu_session/client.h>
#include <pd_session/connection.h>
#include <cpu_session/connection.h>
#include <log_session/connection.h>
#include <rom_session/connection.h>
#include <parent/capability.h>
namespace Genode {
@ -41,44 +44,47 @@ namespace Genode {
*/
struct Genode::Child_policy
{
typedef String<64> Name;
typedef String<64> Binary_name;
typedef String<64> Linker_name;
virtual ~Child_policy() { }
/**
* Return process name of the child
* Name of the child used as the child's label prefix
*/
virtual const char *name() const = 0;
virtual Name name() const = 0;
/**
* ROM module name of the binary to start
*/
virtual Binary_name binary_name() const { return name(); }
/**
* ROM module name of the dynamic linker
*/
virtual Linker_name linker_name() const { return "ld.lib.so"; }
/**
* Determine service to provide a session request
*
* \return Service to be contacted for the new session, or
* 0 if session request could not be resolved
* \return service to be contacted for the new session
*
* \throw Parent::Service_denied
*/
virtual Service *resolve_session_request(const char * /*service_name*/,
const char * /*args*/)
{ return 0; }
virtual Service &resolve_session_request(Service::Name const &,
Session_state::Args const &) = 0;
/**
* Apply transformations to session arguments
*/
virtual void filter_session_args(const char * /*service*/,
virtual void filter_session_args(Service::Name const &,
char * /*args*/, size_t /*args_len*/) { }
/**
* Register a service provided by the child
*
* \param name service name
* \param root interface for creating sessions for the service
* \param alloc allocator to be used for child-specific
* meta-data allocations
* \return true if announcement succeeded, or false if
* child is not permitted to announce service
*/
virtual bool announce_service(const char * /*name*/,
Root_capability /*root*/,
Allocator * /*alloc*/,
Server * /*server*/)
{ return false; }
virtual void announce_service(Service::Name const &) { }
/**
* Apply session affinity policy
@ -91,11 +97,6 @@ struct Genode::Child_policy
return affinity;
}
/**
* Unregister services that had been provided by the child
*/
virtual void unregister_services() { }
/**
* Exit child
*/
@ -110,8 +111,8 @@ struct Genode::Child_policy
* The RAM session returned by this method is used for session-quota
* transfers.
*/
virtual Ram_session *ref_ram_session() { return env()->ram_session(); }
virtual Ram_session_capability ref_ram_cap() const { return env()->ram_session_cap(); }
virtual Ram_session &ref_ram() = 0;
virtual Ram_session_capability ref_ram_cap() const = 0;
/**
* Respond to the release of resources by the child
@ -125,6 +126,56 @@ struct Genode::Child_policy
* Take action on additional resource needs by the child
*/
virtual void resource_request(Parent::Resource_args const &) { }
/**
* Initialize the child's RAM session
*
* The function must define the child's reference account and transfer
* the child's initial RAM quota.
*/
virtual void init(Ram_session &, Capability<Ram_session>) = 0;
/**
* Initialize the child's CPU session
*
* The function may install an exception signal handler or assign CPU quota
* to the child.
*/
virtual void init(Cpu_session &, Capability<Cpu_session>) { }
/**
* Initialize the child's PD session
*
* The function may install a region-map fault handler for the child's
* address space ('Pd_session::address_space');.
*/
virtual void init(Pd_session &, Capability<Pd_session>) { }
class Nonexistent_id_space : Exception { };
/**
* ID space for sessions provided by the child
*
* \throw Nonexistent_id_space
*/
virtual Id_space<Parent::Server> &server_id_space() { throw Nonexistent_id_space(); }
/**
* Return region map for the child's address space
*
* \param pd the child's PD session capability
*
* By default, the function returns a 'nullptr'. In this case, the 'Child'
* interacts with the address space of the child's PD session via RPC calls
* to the 'Pd_session::address_space'.
*
* By overriding the default, those RPC calls can be omitted, which is
* useful if the child's PD session (including the PD's address space) is
* virtualized by the parent. If the virtual PD session is served by the
* same entrypoint as the child's parent interface, an RPC call to 'pd'
* would otherwise produce a deadlock.
*/
virtual Region_map *address_space(Pd_session &) { return nullptr; }
};
@ -150,9 +201,11 @@ struct Genode::Child_policy
* to our account, and subsequently transfer the same amount from our
* account to the client.
*/
class Genode::Child : protected Rpc_object<Parent>
class Genode::Child : protected Rpc_object<Parent>,
Session_state::Ready_callback,
Session_state::Closed_callback
{
public:
private:
struct Initial_thread_base
{
@ -192,52 +245,114 @@ class Genode::Child : protected Rpc_object<Parent>
Capability<Cpu_thread> cap() { return _cap; }
};
private:
class Session;
/* PD session representing the protection domain of the child */
Pd_session_capability _pd;
/* RAM session that contains the quota of the child */
Ram_session_capability _ram;
/* CPU session that contains the quota of the child */
Cpu_session_capability _cpu;
/* services where the PD, RAM, and CPU resources come from */
Service &_pd_service;
Service &_ram_service;
Service &_cpu_service;
/* heap for child-specific allocations using the child's quota */
Heap _heap;
Rpc_entrypoint &_entrypoint;
Parent_capability _parent_cap;
/* child policy */
Child_policy &_policy;
Child_policy &_policy;
/* sessions opened by the child */
Lock _lock; /* protect list manipulation */
Object_pool<Session> _session_pool;
List<Session> _session_list;
Id_space<Client> _id_space;
/* server role */
Server _server;
typedef Session_state::Args Args;
/* session-argument buffer */
char _args[Parent::Session_args::MAX_SIZE];
template <typename CONNECTION>
struct Env_connection
{
typedef String<64> Label;
Args const _args;
Service &_service;
Local_connection<CONNECTION> _connection;
/**
* Construct session arguments with the child policy applied
*/
Args _construct_args(Child_policy &policy, Label const &label)
{
/* copy original arguments into modifiable buffer */
char buf[Session_state::Args::capacity()];
buf[0] = 0;
/* supply label as session argument */
if (label.valid())
Arg_string::set_arg_string(buf, sizeof(buf), "label", label.string());
/* apply policy to argument buffer */
policy.filter_session_args(CONNECTION::service_name(), buf, sizeof(buf));
return Session_state::Args(Cstring(buf));
}
Env_connection(Child_policy &policy, Id_space<Parent::Client> &id_space,
Id_space<Parent::Client>::Id id, Label const &label = Label())
:
_args(_construct_args(policy, label)),
_service(policy.resolve_session_request(CONNECTION::service_name(), _args)),
_connection(_service, id_space, id, _args,
policy.filter_session_affinity(Affinity()))
{ }
typedef typename CONNECTION::Session_type SESSION;
SESSION &session() { return _connection.session(); }
Capability<SESSION> cap() const { return _connection.cap(); }
};
Env_connection<Ram_connection> _ram { _policy,
_id_space, Parent::Env::ram() };
Env_connection<Pd_connection> _pd { _policy,
_id_space, Parent::Env::pd() };
Env_connection<Cpu_connection> _cpu { _policy,
_id_space, Parent::Env::cpu() };
Env_connection<Log_connection> _log { _policy,
_id_space, Parent::Env::log() };
Env_connection<Rom_connection> _binary { _policy,
_id_space, Parent::Env::binary(), _policy.binary_name() };
Lazy_volatile_object<Env_connection<Rom_connection> > _linker { _policy,
_id_space, Parent::Env::linker(), _policy.linker_name() };
/* call 'Child_policy::init' methods for the environment sessions */
void _init_env_sessions()
{
_policy.init(_ram.session(), _ram.cap());
_policy.init(_cpu.session(), _cpu.cap());
_policy.init(_pd.session(), _pd.cap());
}
bool const _env_sessions_initialized = ( _init_env_sessions(), true );
Dataspace_capability _linker_dataspace()
{
try {
_linker.construct(_policy, _id_space,
Parent::Env::linker(), _policy.linker_name());
return _linker->session().dataspace();
}
catch (Parent::Service_denied) { return Rom_dataspace_capability(); }
}
/* heap for child-specific allocations using the child's quota */
Heap _heap;
/* factory for dynamically created session-state objects */
Session_state::Factory _session_factory { _heap };
Rpc_entrypoint &_entrypoint;
Parent_capability _parent_cap;
/* signal handlers registered by the child */
Signal_context_capability _resource_avail_sigh;
Signal_context_capability _yield_sigh;
Signal_context_capability _session_sigh;
/* arguments fetched by the child in response to a yield signal */
Lock _yield_request_lock;
Resource_args _yield_request_args;
Initial_thread _initial_thread { _cpu.session(), _pd.cap(), "initial" };
struct Process
{
class Missing_dynamic_linker : Exception { };
@ -313,30 +428,19 @@ class Genode::Child : protected Rpc_object<Parent>
Process _process;
/**
* Attach session information to a child
*
* \throw Ram_session::Quota_exceeded the child's heap partition cannot
* hold the session meta data
*/
void _add_session(const Session &s);
void _revert_quota_and_destroy(Session_state &);
Close_result _close(Session_state &);
/**
* Close session and revert quota donation associated with it
* Session_state::Ready_callback
*/
void _remove_session(Session *s);
void _close(Session *s);
void session_ready(Session_state &session) override;
/**
* Return service interface targetting the parent
*
* The service returned by this method is used as default
* provider for the RAM, CPU, and RM resources of the child. It is
* solely used for targeting resource donations during
* 'Parent::upgrade_quota()' calls.
* Session_state::Closed_callback
*/
static Service &_parent_service();
void session_closed(Session_state &) override;
public:
@ -357,60 +461,18 @@ class Genode::Child : protected Rpc_object<Parent>
/**
* Constructor
*
* \param elf_ds dataspace that contains the ELF binary
* \param ldso_ds dataspace that contains the dynamic linker,
* started if 'elf_ds' is a dynamically linked
* executable
* \param pd_cap capability of the new protection domain,
* used as argument for creating the initial
* thread, and handed out to the child as its
* environment
* \param pd PD session used for assigning the parent
* capability of the new process
* \param ram_cap RAM session capability handed out to the
* child as its environment
* \param ram RAM session used to allocate the BSS and
* DATA segments and as backing store for the
* local heap partition to keep child-specific
* meta data
* \param cpu_cap CPU session capability handed out to the
* child as its environment
* \param initial_thread initial thread of the new protection domain
* \param local_rm local address space
* \param remote_rm address space of new protection domain
* \param pd_service provider of the 'pd' session
* \param ram_service provider of the 'ram' session
* \param cpu_service provider of the 'cpu' session
* \param rm local address space, usually 'env.rm()'
* \param entrypoint entrypoint used to serve the parent interface of
* the child
* \param policy policy for the child
*
* \throw Parent::Service_denied if the initial sessions for the
* child's environment could not be
* opened
* \throw Ram_session::Alloc_failed
* \throw Process_startup_failed
*
* Usually, the pairs of 'pd' and 'pd_cap', 'initial_thread' and
* 'cpu_cap', 'ram' and 'ram_cap' belong to each other. References to
* the session interfaces are passed as separate arguments in addition
* to the capabilities to allow the creator of a child to operate on
* locally implemented interfaces during the child initialization.
*
* The 'ram_service', 'cpu_service', and 'pd_service' arguments are
* needed to direct quota upgrades referring to the resources of
* the child environment. By default, we expect that these
* resources are provided by the parent.
*/
Child(Dataspace_capability elf_ds,
Dataspace_capability ldso_ds,
Pd_session_capability pd_cap,
Pd_session &pd,
Ram_session_capability ram_cap,
Ram_session &ram,
Cpu_session_capability cpu_cap,
Initial_thread_base &initial_thread,
Region_map &local_rm,
Region_map &remote_rm,
Rpc_entrypoint &entrypoint,
Child_policy &policy,
Service &pd_service = _parent_service(),
Service &ram_service = _parent_service(),
Service &cpu_service = _parent_service());
Child(Region_map &rm, Rpc_entrypoint &entrypoint, Child_policy &policy);
/**
* Destructor
@ -421,25 +483,40 @@ class Genode::Child : protected Rpc_object<Parent>
virtual ~Child();
/**
* Return heap that uses the child's quota
* RAM quota unconditionally consumed by the child's environment
*/
Allocator *heap() { return &_heap; }
Pd_session_capability pd_session_cap() const { return _pd; }
Ram_session_capability ram_session_cap() const { return _ram; }
Cpu_session_capability cpu_session_cap() const { return _cpu; }
Parent_capability parent_cap() const { return cap(); }
static size_t env_ram_quota()
{
return Cpu_connection::RAM_QUOTA + Ram_connection::RAM_QUOTA +
Pd_connection::RAM_QUOTA + Log_connection::RAM_QUOTA +
2*Rom_connection::RAM_QUOTA;
}
/**
* Discard all sessions to specified service
*
* When this method is called, we assume the server protection
* domain to be dead and all that all server quota was already
* transferred back to our own 'env()->ram_session()' account. Note
* that the specified server object may not exist anymore. We do
* not de-reference the server argument in here!
* Deduce session costs from usable ram quota
*/
void revoke_server(const Server *server);
static size_t effective_ram_quota(size_t const ram_quota)
{
if (ram_quota < env_ram_quota())
return 0;
return ram_quota - env_ram_quota();
}
/**
* Return heap that uses the child's quota
*/
Allocator &heap() { return _heap; }
Ram_session_capability ram_session_cap() const { return _ram.cap(); }
Parent_capability parent_cap() const { return cap(); }
Ram_session &ram() { return _ram.session(); }
Cpu_session &cpu() { return _cpu.session(); }
Pd_session &pd() { return _pd .session(); }
Session_state::Factory &session_factory() { return _session_factory; }
/**
* Instruct the child to yield resources
@ -461,12 +538,16 @@ class Genode::Child : protected Rpc_object<Parent>
** Parent interface **
**********************/
void announce(Service_name const &, Root_capability) override;
Session_capability session(Service_name const &, Session_args const &,
Affinity const &) override;
void upgrade(Session_capability, Upgrade_args const &) override;
void close(Session_capability) override;
void announce(Service_name const &) override;
void session_sigh(Signal_context_capability) override;
Session_capability session(Client::Id, Service_name const &,
Session_args const &, Affinity const &) override;
Session_capability session_cap(Client::Id) override;
Upgrade_result upgrade(Client::Id, Upgrade_args const &) override;
Close_result close(Client::Id) override;
void exit(int) override;
void session_response(Server::Id, Session_response) override;
void deliver_session_cap(Server::Id, Session_capability) override;
Thread_capability main_thread_cap() const override;
void resource_avail_sigh(Signal_context_capability) override;
void resource_request(Resource_args const &) override;

View File

@ -16,20 +16,56 @@
#include <base/env.h>
#include <base/capability.h>
#include <base/log.h>
namespace Genode { template <typename> class Connection; }
namespace Genode {
class Connection_base;
template <typename> class Connection;
}
class Genode::Connection_base : public Noncopyable
{
protected:
Env &_env;
Parent::Client _parent_client;
Id_space<Parent::Client>::Element const _id_space_element;
void _block_for_session_response();
public:
Connection_base(Env &env)
:
_env(env),
_id_space_element(_parent_client, _env.id_space())
{ }
/**
* Legacy constructor
*
* \noapi
*/
Connection_base();
void upgrade_ram(size_t bytes)
{
String<64> const args("ram_quota=", bytes);
_env.upgrade(_id_space_element.id(), args.string());
}
};
/**
* Representation of an open connection to a service
*/
template <typename SESSION_TYPE>
class Genode::Connection : public Noncopyable
class Genode::Connection : public Connection_base
{
public:
enum On_destruction { CLOSE = false, KEEP_OPEN = true };
private:
/*
@ -37,15 +73,12 @@ class Genode::Connection : public Noncopyable
* 'session' method that is called before the 'Connection' is
* constructed.
*/
enum { FORMAT_STRING_SIZE = Parent::Session_args::MAX_SIZE };
char _session_args[FORMAT_STRING_SIZE];
char _affinity_arg[sizeof(Affinity)];
Parent &_parent;
On_destruction _on_destruction;
void _session(Parent &parent,
Affinity const &affinity,
const char *format_args, va_list list)
@ -63,7 +96,8 @@ class Genode::Connection : public Noncopyable
memcpy(&affinity, _affinity_arg, sizeof(Affinity));
try {
return env()->parent()->session<SESSION_TYPE>(_session_args, affinity); }
return _env.session<SESSION_TYPE>(_id_space_element.id(),
_session_args, affinity); }
catch (...) {
error(SESSION_TYPE::service_name(), "-session creation failed "
"(", Cstring(_session_args), ")");
@ -75,20 +109,15 @@ class Genode::Connection : public Noncopyable
public:
typedef SESSION_TYPE Session_type;
/**
* Constructor
*
* \param od session policy applied when destructing the connection
*
* The 'op' argument defines whether the session should automatically
* be closed by the destructor of the connection (CLOSE), or the
* session should stay open (KEEP_OPEN). The latter is useful in
* situations where the creator a connection merely passes the
* session capability of the connection to another party but never
* invokes any of the session's RPC functions.
*/
Connection(Env &env, Capability<SESSION_TYPE>, On_destruction od = CLOSE)
: _parent(env.parent()), _on_destruction(od) { }
Connection(Env &env, Capability<SESSION_TYPE>)
:
Connection_base(env), _cap(_request_cap())
{ }
/**
* Constructor
@ -97,28 +126,18 @@ class Genode::Connection : public Noncopyable
* \deprecated Use the constructor with 'Env &' as first
* argument instead
*/
Connection(Capability<SESSION_TYPE>, On_destruction od = CLOSE)
: _parent(*env()->parent()), _on_destruction(od) { }
Connection(Capability<SESSION_TYPE>) : _cap(_request_cap()) { }
/**
* Destructor
*/
~Connection()
{
if (_on_destruction == CLOSE)
_parent.close(_cap);
}
~Connection() { _env.close(_id_space_element.id()); }
/**
* Return session capability
*/
Capability<SESSION_TYPE> cap() const { return _cap; }
/**
* Define session policy
*/
void on_destruction(On_destruction od) { _on_destruction = od; }
/**
* Issue session request to the parent
*/

View File

@ -73,6 +73,61 @@ struct Genode::Env
* Once we add 'Env::upgrade', we can remove this accessor.
*/
virtual Pd_session_capability pd_session_cap() = 0;
/**
* ID space of sessions obtained from the parent
*/
virtual Id_space<Parent::Client> &id_space() = 0;
virtual Session_capability session(Parent::Service_name const &,
Parent::Client::Id,
Parent::Session_args const &,
Affinity const &) = 0;
/**
* Create session to a service
*
* \param SESSION_TYPE session interface type
* \param id session ID of new session
* \param args session constructor arguments
* \param affinity preferred CPU affinity for the session
*
* \throw Service_denied parent denies session request
* \throw Quota_exceeded our own quota does not suffice for
* the creation of the new session
* \throw Unavailable
*
* This method blocks until the session is available or an error
* occurred.
*/
template <typename SESSION_TYPE>
Capability<SESSION_TYPE> session(Parent::Client::Id id,
Parent::Session_args const &args,
Affinity const &affinity)
{
Session_capability cap = session(SESSION_TYPE::service_name(),
id, args, affinity);
return static_cap_cast<SESSION_TYPE>(cap);
}
/**
* Upgrade session quota
*
* \param id ID of recipient session
* \param args description of the amount of quota to transfer
*
* \throw Quota_exceeded quota could not be transferred
*
* The 'args' argument has the same principle format as the 'args'
* argument of the 'session' operation.
*/
virtual void upgrade(Parent::Client::Id id,
Parent::Upgrade_args const &args) = 0;
/**
* Close session and block until the session is gone
*/
virtual void close(Parent::Client::Id) = 0;
};
#endif /* _INCLUDE__BASE__ENV_H_ */

View File

@ -0,0 +1,135 @@
/*
* \brief Connection to a local child
* \author Norman Feske
* \date 2016-11-10
*
* The 'Local_connection' can be used to locally establish a connection
* to a 'Local_service' or a 'Parent_service'.
*/
/*
* Copyright (C) 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__BASE__LOCAL_CONNECTION_H_
#define _INCLUDE__BASE__LOCAL_CONNECTION_H_
#include <util/arg_string.h>
#include <base/service.h>
namespace Genode {
class Local_connection_base;
template <typename> class Local_connection;
}
struct Genode::Local_connection_base : Noncopyable
{
public:
typedef Session_state::Args Args;
protected:
Session_state _session_state;
private:
static Args _init_args(Args const &args, size_t const &ram_quota)
{
/* copy original arguments into modifiable buffer */
char buf[Args::capacity()];
strncpy(buf, args.string(), sizeof(buf));
Arg_string::set_arg(buf, sizeof(buf), "ram_quota",
String<64>(ram_quota).string());
/* return result as a copy */
return Args(Cstring(buf));
}
protected:
Local_connection_base(Service &service,
Id_space<Parent::Client> &id_space,
Parent::Client::Id id,
Args const &args, Affinity const &affinity,
size_t ram_quota)
:
_session_state(service, id_space, id, _init_args(args, ram_quota),
affinity)
{
_session_state.service().initiate_request(_session_state);
}
~Local_connection_base()
{
if (_session_state.alive()) {
_session_state.phase = Session_state::CLOSE_REQUESTED;
_session_state.service().initiate_request(_session_state);
}
}
};
template <typename CONNECTION>
class Genode::Local_connection : Local_connection_base
{
private:
typedef typename CONNECTION::Session_type SESSION;
Lazy_volatile_object <typename SESSION::Client> _client;
public:
Capability<SESSION> cap() const
{
return reinterpret_cap_cast<SESSION>(_session_state.cap);
}
SESSION &session()
{
/*
* If session comes from a local service (e.g,. a virtualized
* RAM session, we return the reference to the corresponding
* component object, which can be called directly.
*
* Otherwise, if the session is provided
*/
if (_session_state.local_ptr)
return *static_cast<SESSION *>(_session_state.local_ptr);
/*
* The session is provided remotely. So return a client-stub
* for interacting with the session.
*/
if (_client.constructed())
return *_client;
/*
* This error is printed if the session could not be
* established or the session is provided by a child service.
*/
error(SESSION::service_name(), " session (", _session_state.args(), ") "
"unavailable");
throw Parent::Service_denied();
}
Local_connection(Service &service, Id_space<Parent::Client> &id_space,
Parent::Client::Id id, Args const &args,
Affinity const &affinity)
:
Local_connection_base(service, id_space, id, args,
affinity, CONNECTION::RAM_QUOTA)
{
if (_session_state.cap.valid())
_client.construct(cap());
}
};
#endif /* _INCLUDE__BASE__LOCAL_CONNECTION_H_ */

View File

@ -14,100 +14,42 @@
#ifndef _INCLUDE__BASE__SERVICE_H_
#define _INCLUDE__BASE__SERVICE_H_
#include <root/client.h>
#include <base/log.h>
#include <util/list.h>
#include <ram_session/client.h>
#include <base/env.h>
#include <base/session_state.h>
#include <base/log.h>
#include <base/registry.h>
namespace Genode {
class Client;
class Server;
class Service;
class Local_service;
template <typename> class Session_factory;
template <typename> class Local_service;
class Parent_service;
class Child_service;
class Service_registry;
}
/**
* Client role
*
* A client is someone who applies for a service. If the service is not
* available yet, we enqueue the client into a wait queue and wake him up
* as soon as the requested service gets available.
*/
class Genode::Client : public List<Client>::Element
{
private:
Cancelable_lock _service_apply_lock;
const char *_apply_for;
public:
/**
* Constructor
*/
Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { }
virtual ~Client() { }
/**
* Set/Request service name that we are currently applying for
*/
void apply_for(const char *apply_for) { _apply_for = apply_for; }
const char *apply_for() { return _apply_for; }
/**
* Service wait queue support
*/
void sleep() { _service_apply_lock.lock(); }
void wakeup() { _service_apply_lock.unlock(); }
};
/**
* Server role
*
* A server is a process that provides one or multiple services. For the
* most part, this class is used as an opaque key to represent the server
* role.
*/
class Genode::Server
{
private:
Ram_session_capability _ram;
public:
/**
* Constructor
*
* \param ram RAM session capability of the server process used,
* for quota transfers from/to the server
*/
Server(Ram_session_capability ram): _ram(ram) { }
/**
* Return RAM session capability of the server process
*/
Ram_session_capability ram_session_cap() const { return _ram; }
};
class Genode::Service : public List<Service>::Element
class Genode::Service : Noncopyable
{
public:
enum { MAX_NAME_LEN = 32 };
typedef Session_state::Name Name;
private:
char _name[MAX_NAME_LEN];
Name const _name;
Ram_session_capability const _ram;
protected:
typedef Session_state::Factory Factory;
/**
* Return factory to use for creating 'Session_state' objects
*/
virtual Factory &_factory(Factory &client_factory) { return client_factory; }
public:
@ -123,52 +65,52 @@ class Genode::Service : public List<Service>::Element
* Constructor
*
* \param name service name
* \param ram RAM session to receive/withdraw session quota
*/
Service(const char *name) { strncpy(_name, name, sizeof(_name)); }
Service(Name const &name, Ram_session_capability ram)
: _name(name), _ram(ram) { }
virtual ~Service() { }
/**
* Return service name
*/
const char *name() const { return _name; }
Name const &name() const { return _name; }
/**
* Create session
* Create new session-state object
*
* \param args session-construction arguments
* \param affinity preferred CPU affinity of session
* The 'service' argument for the 'Session_state' corresponds to this
* session state. All subsequent 'Session_state' arguments correspond
* to the forwarded 'args'.
*/
template <typename... ARGS>
Session_state &create_session(Factory &client_factory, ARGS &&... args)
{
return _factory(client_factory).create(*this, args...);
}
/**
* Attempt the immediate (synchronous) creation of a session
*
* \throw Invalid_args
* \throw Unavailable
* \throw Quota_exceeded
* Sessions to local services and parent services are usually created
* immediately during the dispatching of the 'Parent::session' request.
* In these cases, it is not needed to wait for an asynchronous
* response.
*/
virtual Session_capability session(char const *args,
Affinity const &affinity) = 0;
virtual void initiate_request(Session_state &session) = 0;
/**
* Extend resource donation to an existing session
* Wake up service to query session requests
*/
virtual void upgrade(Session_capability session, const char *args) = 0;
/**
* Close session
*/
virtual void close(Session_capability /*session*/) { }
/**
* Return server providing the service
*/
virtual Server *server() const { return 0; }
virtual void wakeup() { }
/**
* Return the RAM session to be used for trading resources
*/
Ram_session_capability ram_session_cap()
Ram_session_capability ram() const
{
if (server())
return server()->ram_session_cap();
return Ram_session_capability();
return _ram;
}
};
@ -176,36 +118,118 @@ class Genode::Service : public List<Service>::Element
/**
* Representation of a locally implemented service
*/
template <typename SESSION>
class Genode::Local_service : public Service
{
public:
struct Factory
{
typedef Session_state::Args Args;
class Denied : Exception { };
/**
* \throw Denied
*/
virtual SESSION &create(Args const &, Affinity) = 0;
virtual void upgrade(SESSION &, Args const &) = 0;
virtual void destroy(SESSION &) = 0;
};
/**
* Factory of a local service that provides a single static session
*/
class Single_session_factory : public Factory
{
private:
typedef Session_state::Args Args;
SESSION &_s;
public:
Single_session_factory(SESSION &session) : _s(session) { }
SESSION &create (Args const &, Affinity) override { return _s; }
void upgrade (SESSION &, Args const &) override { }
void destroy (SESSION &) override { }
};
private:
Root *_root;
Factory &_factory;
template <typename FUNC>
void _apply_to_rpc_obj(Session_state &session, FUNC const &fn)
{
SESSION *rpc_obj = dynamic_cast<SESSION *>(session.local_ptr);
if (rpc_obj)
fn(*rpc_obj);
else
warning("local ", SESSION::service_name(), " session "
"(", session.args(), ") has no valid RPC object");
}
public:
Local_service(const char *name, Root *root)
: Service(name), _root(root) { }
/**
* Constructor
*/
Local_service(Factory &factory)
:
Service(SESSION::service_name(), Ram_session_capability()),
_factory(factory)
{ }
Session_capability session(const char *args, Affinity const &affinity) override
void initiate_request(Session_state &session) override
{
try { return _root->session(args, affinity); }
catch (Root::Invalid_args) { throw Invalid_args(); }
catch (Root::Unavailable) { throw Unavailable(); }
catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
switch (session.phase) {
void upgrade(Session_capability session, const char *args) override
{
try { _root->upgrade(session, args); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
case Session_state::CREATE_REQUESTED:
void close(Session_capability session) override
{
try { _root->close(session); }
catch (Genode::Ipc_error) { throw Blocking_canceled(); }
try {
SESSION &rpc_obj = _factory.create(session.args(),
session.affinity());
session.local_ptr = &rpc_obj;
session.cap = rpc_obj.cap();
session.phase = Session_state::AVAILABLE;
}
catch (typename Factory::Denied) {
session.phase = Session_state::INVALID_ARGS; }
break;
case Session_state::UPGRADE_REQUESTED:
{
String<64> const args("ram_quota=", session.ram_upgrade);
_apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) {
_factory.upgrade(rpc_obj, args.string()); });
session.phase = Session_state::CAP_HANDED_OUT;
session.confirm_ram_upgrade();
}
break;
case Session_state::CLOSE_REQUESTED:
{
_apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) {
_factory.destroy(rpc_obj); });
session.phase = Session_state::CLOSED;
}
break;
case Session_state::INVALID_ARGS:
case Session_state::AVAILABLE:
case Session_state::CAP_HANDED_OUT:
case Session_state::CLOSED:
break;
}
}
};
@ -215,31 +239,88 @@ class Genode::Local_service : public Service
*/
class Genode::Parent_service : public Service
{
private:
/*
* \deprecated
*/
Env &_env_deprecated();
Env &_env;
public:
Parent_service(const char *name) : Service(name) { }
/**
* Constructor
*/
Parent_service(Env &env, Service::Name const &name)
: Service(name, Ram_session_capability()), _env(env) { }
Session_capability session(const char *args, Affinity const &affinity) override
/**
* Constructor
*
* \deprecated
*/
Parent_service(Service::Name const &name)
: Service(name, Ram_session_capability()), _env(_env_deprecated()) { }
void initiate_request(Session_state &session) override
{
try { return env()->parent()->session(name(), args, affinity); }
catch (Parent::Unavailable) {
warning("parent has no service \"", name(), "\"");
throw Unavailable();
switch (session.phase) {
case Session_state::CREATE_REQUESTED:
session.id_at_parent.construct(session.parent_client,
_env.id_space());
try {
session.cap = _env.session(name().string(),
session.id_at_parent->id(),
session.args().string(),
session.affinity());
session.phase = Session_state::AVAILABLE;
}
catch (Parent::Quota_exceeded) {
session.id_at_parent.destruct();
session.phase = Session_state::INVALID_ARGS; }
catch (Parent::Service_denied) {
session.id_at_parent.destruct();
session.phase = Session_state::INVALID_ARGS; }
break;
case Session_state::UPGRADE_REQUESTED:
{
String<64> const args("ram_quota=", session.ram_upgrade);
if (!session.id_at_parent.constructed())
error("invalid parent-session state: ", session);
try {
_env.upgrade(session.id_at_parent->id(), args.string()); }
catch (Parent::Quota_exceeded) {
warning("quota exceeded while upgrading parent session"); }
session.confirm_ram_upgrade();
session.phase = Session_state::CAP_HANDED_OUT;
}
break;
case Session_state::CLOSE_REQUESTED:
if (session.id_at_parent.constructed())
_env.close(session.id_at_parent->id());
session.id_at_parent.destruct();
session.phase = Session_state::CLOSED;
break;
case Session_state::INVALID_ARGS:
case Session_state::AVAILABLE:
case Session_state::CAP_HANDED_OUT:
case Session_state::CLOSED:
break;
}
catch (Parent::Quota_exceeded) { throw Quota_exceeded(); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
void upgrade(Session_capability session, const char *args) override
{
try { env()->parent()->upgrade(session, args); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
void close(Session_capability session) override
{
try { env()->parent()->close(session); }
catch (Genode::Ipc_error) { throw Blocking_canceled(); }
}
};
@ -249,205 +330,66 @@ class Genode::Parent_service : public Service
*/
class Genode::Child_service : public Service
{
public:
struct Wakeup { virtual void wakeup_child_service() = 0; };
private:
Root_capability _root_cap;
Root_client _root;
Server *_server;
Id_space<Parent::Server> &_server_id_space;
Session_state::Factory &_server_factory;
Wakeup &_wakeup;
protected:
/*
* In contrast to local services and parent services, session-state
* objects for child services are owned by the server. This enables
* the server to asynchronouly respond to close requests when the
* client is already gone.
*/
Factory &_factory(Factory &) override { return _server_factory; }
public:
/**
* Constructor
*
* \param name name of service
* \param root capability to root interface
* \param server server process providing the service
*/
Child_service(const char *name,
Root_capability root,
Server *server)
: Service(name), _root_cap(root), _root(root), _server(server) { }
Server *server() const override { return _server; }
Session_capability session(const char *args, Affinity const &affinity) override
{
if (!_root_cap.valid())
throw Unavailable();
try { return _root.session(args, affinity); }
catch (Root::Invalid_args) { throw Invalid_args(); }
catch (Root::Unavailable) { throw Unavailable(); }
catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
void upgrade(Session_capability sc, const char *args) override
{
if (!_root_cap.valid())
throw Unavailable();
try { _root.upgrade(sc, args); }
catch (Root::Invalid_args) { throw Invalid_args(); }
catch (Root::Unavailable) { throw Unavailable(); }
catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
void close(Session_capability sc) override
{
try { _root.close(sc); }
catch (Genode::Ipc_error) { throw Blocking_canceled(); }
}
};
/**
* Container for holding service representations
*/
class Genode::Service_registry
{
protected:
Lock _service_wait_queue_lock;
List<Client> _service_wait_queue;
List<Service> _services;
public:
/**
* Probe for service with specified name
* \param factory server-side session-state factory
* \param name name of service
* \param ram recipient of session quota
* \param wakeup callback to be notified on the arrival of new
* session requests
*
* \param name service name
* \param server server providing the service,
* default (0) for any server
*/
Service *find(const char *name, Server *server = 0)
Child_service(Id_space<Parent::Server> &server_id_space,
Session_state::Factory &factory,
Service::Name const &name,
Ram_session_capability ram,
Wakeup &wakeup)
:
Service(name, ram),
_server_id_space(server_id_space),
_server_factory(factory), _wakeup(wakeup)
{ }
void initiate_request(Session_state &session) override
{
if (!name) return 0;
if (!session.id_at_server.constructed())
session.id_at_server.construct(session, _server_id_space);
Lock::Guard lock_guard(_service_wait_queue_lock);
for (Service *s = _services.first(); s; s = s->next())
if (strcmp(s->name(), name) == 0
&& (!server || s->server() == server)) return s;
return 0;
session.async_client_notify = true;
}
/**
* Check if service name is ambiguous
*
* \return true if the same service is provided multiple
* times
*/
bool is_ambiguous(const char *name)
bool has_id_space(Id_space<Parent::Server> const &id_space) const
{
Lock::Guard lock_guard(_service_wait_queue_lock);
/* count number of services with the specified name */
unsigned cnt = 0;
for (Service *s = _services.first(); s; s = s->next())
cnt += (strcmp(s->name(), name) == 0);
return cnt > 1;
return &_server_id_space == &id_space;
}
/**
* Return first service provided by specified server
*/
Service *find_by_server(Server *server)
{
Lock::Guard lock_guard(_service_wait_queue_lock);
for (Service *s = _services.first(); s; s = s->next())
if (s->server() == server)
return s;
return 0;
}
/**
* Wait for service
*
* This method is called by the clients's thread when requesting a
* session creation. It blocks if the requested service is not
* available.
*
* \return service structure that matches the request or
* 0 if the waiting was canceled.
*/
Service *wait_for_service(const char *name, Client *client, const char *client_name)
{
Service *service;
client->apply_for(name);
_service_wait_queue_lock.lock();
_service_wait_queue.insert(client);
_service_wait_queue_lock.unlock();
do {
service = find(name);
/*
* The service that we are seeking is not available today.
* Lets sleep a night over it.
*/
if (!service) {
log(client_name, ": service ", name, " not yet available - sleeping");
try {
client->sleep();
log(client_name, ": service ", name, " got available");
} catch (Blocking_canceled) {
log(client_name, ": cancel waiting for service");
break;
}
}
} while (!service);
/* we got what we needed, stop applying */
_service_wait_queue_lock.lock();
_service_wait_queue.remove(client);
_service_wait_queue_lock.unlock();
client->apply_for(0);
return service;
}
/**
* Register service
*
* This method is called by the server's thread.
*/
void insert(Service *service)
{
/* make new service known */
_services.insert(service);
/* wake up applicants waiting for the service */
Lock::Guard lock_guard(_service_wait_queue_lock);
for (Client *c = _service_wait_queue.first(); c; c = c->next())
if (strcmp(service->name(), c->apply_for()) == 0)
c->wakeup();
}
/**
* Unregister service
*/
void remove(Service *service) { _services.remove(service); }
/**
* Unregister all services
*/
void remove_all()
{
while (_services.first())
remove(_services.first());
}
void wakeup() override { _wakeup.wakeup_child_service(); }
};
#endif /* _INCLUDE__BASE__SERVICE_H_ */

View File

@ -0,0 +1,245 @@
/*
* \brief Representation of a session request
* \author Norman Feske
* \date 2016-10-10
*/
/*
* Copyright (C) 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__BASE__SESSION_STATE_H_
#define _INCLUDE__BASE__SESSION_STATE_H_
#include <util/xml_generator.h>
#include <util/list.h>
#include <util/volatile_object.h>
#include <session/capability.h>
#include <base/id_space.h>
#include <base/env.h>
#include <base/log.h>
namespace Genode {
class Session_state;
class Service;
}
class Genode::Session_state : public Parent::Client, public Parent::Server,
Noncopyable
{
public:
class Factory;
typedef String<32> Name;
typedef String<256> Args;
struct Ready_callback
{
virtual void session_ready(Session_state &) = 0;
};
struct Closed_callback
{
virtual void session_closed(Session_state &) = 0;
};
private:
Service &_service;
/**
* Total of quota associated with this session
*/
size_t _donated_ram_quota = 0;
Factory *_factory = nullptr;
Volatile_object<Id_space<Parent::Client>::Element> _id_at_client;
Args _args;
Affinity _affinity;
public:
Lazy_volatile_object<Id_space<Parent::Server>::Element> id_at_server;
/* ID for session requests towards the parent */
Lazy_volatile_object<Id_space<Parent::Client>::Element> id_at_parent;
Parent::Client parent_client;
enum Phase { CREATE_REQUESTED,
INVALID_ARGS,
AVAILABLE,
CAP_HANDED_OUT,
UPGRADE_REQUESTED,
CLOSE_REQUESTED,
CLOSED };
/**
* If set, the server reponds asynchronously to the session request.
* The client waits for a notification that is delivered as soon as
* the server delivers the session capability.
*/
bool async_client_notify = false;
Phase phase = CREATE_REQUESTED;
Ready_callback *ready_callback = nullptr;
Closed_callback *closed_callback = nullptr;
/*
* Pointer to session interface for sessions that are implemented
* locally.
*/
Session *local_ptr = nullptr;
Session_capability cap;
size_t ram_upgrade = 0;
void print(Output &out) const;
/**
* Constructor
*
* \param service interface that was used to create the session
* \param client_id_space ID space for client-side session IDs
* \param client_id session ID picked by the client
* \param args session arguments
*
* \throw Id_space<Parent::Client>::Conflicting_id
*/
Session_state(Service &service,
Id_space<Parent::Client> &client_id_space,
Parent::Client::Id client_id,
Args const &args,
Affinity const &affinity);
~Session_state()
{
if (id_at_parent.is_constructed())
error("dangling session in parent-side ID space: ", *this);
}
Service &service() { return _service; }
/**
* Extend amount of ram attached to the session
*/
void confirm_ram_upgrade()
{
ram_upgrade = 0;
}
void increase_donated_quota(size_t upgrade)
{
_donated_ram_quota += upgrade;
ram_upgrade = upgrade;
}
Parent::Client::Id id_at_client() const
{
return _id_at_client->id();
}
void discard_id_at_client()
{
_id_at_client.destruct();
}
Args const &args() const { return _args; }
Affinity const &affinity() const { return _affinity; }
void generate_session_request(Xml_generator &xml) const;
size_t donated_ram_quota() const { return _donated_ram_quota; }
bool alive() const
{
switch (phase) {
case CREATE_REQUESTED:
case INVALID_ARGS:
case CLOSED:
return false;
case AVAILABLE:
case CAP_HANDED_OUT:
case UPGRADE_REQUESTED:
case CLOSE_REQUESTED:
return true;
}
return false;
}
/**
* Assign owner
*
* This function is called if the session-state object is created by
* 'Factory'. For statically created session objects, the '_factory' is
* a nullptr. The owner can be defined only once.
*/
void owner(Factory &factory) { if (!_factory) _factory = &factory; }
/**
* Destroy session-state object
*
* This function has no effect for sessions not created via a 'Factory'.
*/
void destroy();
};
class Genode::Session_state::Factory : Noncopyable
{
private:
Allocator &_md_alloc;
public:
/**
* Constructor
*
* \param md_alloc meta-data allocator used for allocating
* 'Session_state' objects
*/
Factory(Allocator &md_alloc) : _md_alloc(md_alloc) { }
/**
* Create a new session-state object
*
* The 'args' are passed the 'Session_state' constructor.
*
* \throw Allocator::Out_of_memory
*/
template <typename... ARGS>
Session_state &create(ARGS &&... args)
{
Session_state &session = *new (_md_alloc) Session_state(args...);
session.owner(*this);
return session;
}
private:
/*
* Allow only 'Session_state::destroy' to call 'Factory::_destroy'.
* This way, we make sure that the 'Session_state' is destroyed with
* the same factory that was used for creating it.
*/
friend class Session_state;
void _destroy(Session_state &session) { Genode::destroy(_md_alloc, &session); }
};
#endif /* _INCLUDE__BASE__SESSION_STATE_H_ */

View File

@ -23,11 +23,20 @@
#include <dataspace/capability.h>
#include <pd_session/pd_session.h>
namespace Genode { struct Cpu_session; }
namespace Genode {
struct Cpu_session;
struct Cpu_session_client;
}
struct Genode::Cpu_session : Session
{
static const char *service_name() { return "CPU"; }
typedef Cpu_session_client Client;
/*********************
** Exception types **
*********************/
@ -36,7 +45,6 @@ struct Genode::Cpu_session : Session
class Quota_exceeded : public Thread_creation_failed { };
class Out_of_metadata : public Exception { };
static const char *service_name() { return "CPU"; }
enum { THREAD_NAME_LEN = 32 };
enum { PRIORITY_LIMIT = 1 << 16 };

View File

@ -119,7 +119,6 @@ struct Genode::Env_deprecated
* \noapi
*/
virtual void reinit_main_thread(Capability<Region_map> &stack_area_rm) = 0;
};
#endif /* _INCLUDE__DEPRECATED__ENV_H_ */

View File

@ -23,14 +23,16 @@ namespace Genode { struct Log_connection; }
struct Genode::Log_connection : Connection<Log_session>, Log_session_client
{
enum { RAM_QUOTA = 8*1024UL };
/**
* Constructor
*/
Log_connection(Env &env, Session_label label = Session_label())
:
Connection<Log_session>(env, session(env.parent(),
"ram_quota=8K, label=\"%s\"",
label.string())),
"ram_quota=%ld, label=\"%s\"",
RAM_QUOTA, label.string())),
Log_session_client(cap())
{ }
@ -43,8 +45,8 @@ struct Genode::Log_connection : Connection<Log_session>, Log_session_client
*/
Log_connection(Session_label label = Session_label())
:
Connection<Log_session>(session("ram_quota=8K, label=\"%s\"",
label.string())),
Connection<Log_session>(session("ram_quota=%ld, label=\"%s\"",
RAM_QUOTA, label.string())),
Log_session_client(cap())
{ }
};

View File

@ -19,13 +19,19 @@
#include <base/rpc_args.h>
#include <session/session.h>
namespace Genode { struct Log_session; }
namespace Genode {
struct Log_session;
struct Log_session_client;
}
struct Genode::Log_session : Session
{
static const char *service_name() { return "LOG"; }
typedef Log_session_client Client;
virtual ~Log_session() { }
/* the lowest platform-specific maximum IPC payload size (OKL4) */

View File

@ -27,18 +27,32 @@ struct Genode::Parent_client : Rpc_client<Parent>
void exit(int exit_value) override { call<Rpc_exit>(exit_value); }
void announce(Service_name const &service, Root_capability root) override {
call<Rpc_announce>(service, root); }
void announce(Service_name const &service) override {
call<Rpc_announce>(service); }
Session_capability session(Service_name const &service,
void session_sigh(Signal_context_capability sigh) override {
call<Rpc_session_sigh>(sigh); }
Session_capability session(Client::Id id,
Service_name const &service,
Session_args const &args,
Affinity const &affinity) override {
return call<Rpc_session>(service, args, affinity); }
return call<Rpc_session>(id, service, args, affinity); }
void upgrade(Session_capability to_session, Upgrade_args const &args) override {
call<Rpc_upgrade>(to_session, args); }
Session_capability session_cap(Client::Id id) override {
return call<Rpc_session_cap>(id); }
void close(Session_capability session) override { call<Rpc_close>(session); }
Upgrade_result upgrade(Client::Id to_session, Upgrade_args const &args) override {
return call<Rpc_upgrade>(to_session, args); }
Close_result close(Client::Id id) override { return call<Rpc_close>(id); }
void session_response(Id_space<Server>::Id id, Session_response response) override {
call<Rpc_session_response>(id, response); }
void deliver_session_cap(Id_space<Server>::Id id,
Session_capability cap) override {
call<Rpc_deliver_session_cap>(id, cap); }
Thread_capability main_thread_cap() const override {
return call<Rpc_main_thread>(); }

View File

@ -18,10 +18,15 @@
#include <base/rpc.h>
#include <base/rpc_args.h>
#include <base/thread.h>
#include <base/id_space.h>
#include <session/capability.h>
#include <root/capability.h>
namespace Genode { class Parent; }
namespace Genode {
class Session_state;
class Parent;
}
class Genode::Parent
@ -61,13 +66,35 @@ class Genode::Parent
typedef Rpc_in_buffer<160> Session_args;
typedef Rpc_in_buffer<160> Upgrade_args;
struct Client { typedef Id_space<Client>::Id Id; };
struct Server { typedef Id_space<Server>::Id Id; };
/**
* Predefined session IDs corresponding to the environment sessions
* created by the parent at the component-construction time
*/
struct Env
{
static Client::Id ram() { return { 1 }; }
static Client::Id cpu() { return { 2 }; }
static Client::Id pd() { return { 3 }; }
static Client::Id log() { return { 4 }; }
static Client::Id binary() { return { 5 }; }
static Client::Id linker() { return { 6 }; }
/**
* True if session ID refers to an environment session
*/
static bool session_id(Client::Id id) {
return id.value >= 1 && id.value <= 6; }
};
/**
* Use 'String' instead of 'Rpc_in_buffer' because 'Resource_args'
* is used as both in and out parameter.
*/
typedef String<160> Resource_args;
virtual ~Parent() { }
/**
@ -78,9 +105,20 @@ class Genode::Parent
/**
* Announce service to the parent
*/
virtual void announce(Service_name const &service_name,
Root_capability service_root) = 0;
virtual void announce(Service_name const &service_name) = 0;
/**
* Emulation of the original synchronous root interface
*
* This function transparently spawns a proxy "root" entrypoint that
* dispatches asynchronous session-management operations (as issued
* by the parent) to the local root interfaces via component-local
* RPC calls.
*
* The function solely exists for API compatibility.
*/
static void announce(Service_name const &service_name,
Root_capability service_root);
/**
* Announce service to the parent
@ -109,9 +147,15 @@ class Genode::Parent
(Meta::Bool_to_type<Rpc_interface_is_inherited<Session>::VALUE> *)0);
}
/**
* Register signal handler for session notifications
*/
virtual void session_sigh(Signal_context_capability) = 0;
/**
* Create session to a service
*
* \param id client-side ID of new session
* \param service_name name of the requested interface
* \param args session constructor arguments
* \param affinity preferred CPU affinity for the session
@ -119,60 +163,73 @@ class Genode::Parent
* \throw Service_denied parent denies session request
* \throw Quota_exceeded our own quota does not suffice for
* the creation of the new session
*
* \return session capability of the new session is immediately
* available, or an invalid capability
*
* If the returned capability is invalid, the request is pending at the
* server. The parent delivers a signal to the handler as registered
* via 'session_sigh' once the server responded to the request. Now the
* session capability can be picked up by calling 'session_cap'.
*
* \throw Unavailable
*
* \return untyped capability to new session
*
* The use of this method is discouraged. Please use the type safe
* 'session()' template instead.
*/
virtual Session_capability session(Service_name const &service_name,
virtual Session_capability session(Client::Id id,
Service_name const &service_name,
Session_args const &args,
Affinity const &affinity = Affinity()) = 0;
/**
* Create session to a service
* Request session capability
*
* \param SESSION_TYPE session interface type
* \param args session constructor arguments
* \param affinity preferred CPU affinity for the session
* \throw Service_denied
*
* \throw Service_denied parent denies session request
* \throw Quota_exceeded our own quota does not suffice for
* the creation of the new session
* \throw Unavailable
*
* \return capability to new session
* In the exception case, the parent implicitly closes the session.
*/
template <typename SESSION_TYPE>
Capability<SESSION_TYPE> session(Session_args const &args,
Affinity const &affinity = Affinity())
{
Session_capability cap = session(SESSION_TYPE::service_name(),
args, affinity);
return reinterpret_cap_cast<SESSION_TYPE>(cap);
}
virtual Session_capability session_cap(Client::Id id) = 0;
enum Upgrade_result { UPGRADE_DONE, UPGRADE_PENDING };
/**
* Transfer our quota to the server that provides the specified session
*
* \param to_session recipient session
* \param id ID of recipient session
* \param args description of the amount of quota to transfer
*
* \throw Quota_exceeded quota could not be transferred
*
* The 'args' argument has the same principle format as the 'args'
* argument of the 'session' operation.
* The error case indicates that there is not enough unused quota on
* the source side.
*/
virtual void upgrade(Session_capability to_session,
Upgrade_args const &args) = 0;
virtual Upgrade_result upgrade(Client::Id to_session,
Upgrade_args const &args) = 0;
enum Close_result { CLOSE_DONE, CLOSE_PENDING };
/**
* Close session
*/
virtual void close(Session_capability session) = 0;
virtual Close_result close(Client::Id) = 0;
/*
* Interface for providing services
*/
enum Session_response { SESSION_OK, SESSION_CLOSED, INVALID_ARGS };
/**
* Set state of a session provided by the child service
*/
virtual void session_response(Server::Id, Session_response) = 0;
/**
* Deliver capability for a new session provided by the child service
*/
virtual void deliver_session_cap(Server::Id, Session_capability) = 0;
/*
* Dynamic resource balancing
*/
/**
* Provide thread_cap of main thread
@ -233,14 +290,23 @@ class Genode::Parent
GENODE_RPC(Rpc_exit, void, exit, int);
GENODE_RPC(Rpc_announce, void, announce,
Service_name const &, Root_capability);
Service_name const &);
GENODE_RPC(Rpc_session_sigh, void, session_sigh, Signal_context_capability);
GENODE_RPC_THROW(Rpc_session, Session_capability, session,
GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable),
Service_name const &, Session_args const &, Affinity const &);
GENODE_RPC_THROW(Rpc_upgrade, void, upgrade,
Client::Id, Service_name const &, Session_args const &,
Affinity const &);
GENODE_RPC_THROW(Rpc_session_cap, Session_capability, session_cap,
GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable),
Client::Id);
GENODE_RPC_THROW(Rpc_upgrade, Upgrade_result, upgrade,
GENODE_TYPE_LIST(Quota_exceeded),
Session_capability, Upgrade_args const &);
GENODE_RPC(Rpc_close, void, close, Session_capability);
Client::Id, Upgrade_args const &);
GENODE_RPC(Rpc_close, Close_result, close, Client::Id);
GENODE_RPC(Rpc_session_response, void, session_response,
Server::Id, Session_response);
GENODE_RPC(Rpc_deliver_session_cap, void, deliver_session_cap,
Server::Id, Session_capability);
GENODE_RPC(Rpc_main_thread, Thread_capability, main_thread_cap);
GENODE_RPC(Rpc_resource_avail_sigh, void, resource_avail_sigh,
Signal_context_capability);
@ -250,10 +316,12 @@ class Genode::Parent
GENODE_RPC(Rpc_yield_request, Resource_args, yield_request);
GENODE_RPC(Rpc_yield_response, void, yield_response);
GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session, Rpc_upgrade,
Rpc_close, Rpc_main_thread, Rpc_resource_avail_sigh,
Rpc_resource_request, Rpc_yield_sigh, Rpc_yield_request,
Rpc_yield_response);
GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session_sigh,
Rpc_session, Rpc_session_cap, Rpc_upgrade,
Rpc_close, Rpc_session_response, Rpc_main_thread,
Rpc_deliver_session_cap, Rpc_resource_avail_sigh,
Rpc_resource_request, Rpc_yield_sigh,
Rpc_yield_request, Rpc_yield_response);
};

View File

@ -25,6 +25,7 @@
namespace Genode {
struct Pd_session;
struct Pd_session_client;
struct Parent;
struct Signal_context;
}
@ -34,6 +35,8 @@ struct Genode::Pd_session : Session
{
static const char *service_name() { return "PD"; }
typedef Pd_session_client Client;
virtual ~Pd_session() { }
/**

View File

@ -27,6 +27,7 @@ namespace Genode {
struct Ram_dataspace;
typedef Capability<Ram_dataspace> Ram_dataspace_capability;
struct Ram_session_client;
struct Ram_session;
}
@ -41,6 +42,8 @@ struct Genode::Ram_session : Session
{
static const char *service_name() { return "RAM"; }
typedef Ram_session_client Client;
/*********************
** Exception types **

View File

@ -28,11 +28,13 @@ class Genode::Rom_connection : public Connection<Rom_session>,
class Rom_connection_failed : public Parent::Exception { };
enum { RAM_QUOTA = 4096UL };
private:
Rom_session_capability _session(Parent &parent, char const *label)
{
return session("ram_quota=4K, label=\"%s\"", label);
return session("ram_quota=%ld, label=\"%s\"", RAM_QUOTA, label);
}
public:

View File

@ -25,6 +25,7 @@ namespace Genode {
struct Rom_dataspace;
struct Rom_session;
struct Rom_session_client;
typedef Capability<Rom_dataspace> Rom_dataspace_capability;
}
@ -37,6 +38,8 @@ struct Genode::Rom_session : Session
{
static const char *service_name() { return "ROM"; }
typedef Rom_session_client Client;
virtual ~Rom_session() { }
/**

View File

@ -21,6 +21,7 @@
#include <base/allocator.h>
#include <base/rpc_server.h>
#include <base/entrypoint.h>
#include <base/service.h>
#include <util/arg_string.h>
#include <base/log.h>
@ -95,6 +96,7 @@ struct Genode::Multiple_clients
*/
template <typename SESSION_TYPE, typename POLICY>
class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
public Local_service<SESSION_TYPE>::Factory,
private POLICY
{
private:
@ -113,6 +115,53 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
*/
Allocator *_md_alloc;
/*
* Used by both the legacy 'Root::session' and the new 'Factory::create'
*/
SESSION_TYPE &_create(Session_state::Args const &args, Affinity affinity)
{
POLICY::aquire(args.string());
/*
* We need to decrease 'ram_quota' by
* the size of the session object.
*/
size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE));
if (needed > ram_quota) {
error("insufficient ram quota, provided=", ram_quota,
", required=", needed);
throw Root::Quota_exceeded();
}
size_t const remaining_ram_quota = ram_quota - needed;
/*
* Deduce ram quota needed for allocating the session object from the
* donated ram quota.
*
* XXX the size of the 'adjusted_args' buffer should dependent
* on the message-buffer size and stack size.
*/
enum { MAX_ARGS_LEN = 256 };
char adjusted_args[MAX_ARGS_LEN];
strncpy(adjusted_args, args.string(), sizeof(adjusted_args));
char ram_quota_buf[64];
snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%lu",
remaining_ram_quota);
Arg_string::set_arg(adjusted_args, sizeof(adjusted_args),
"ram_quota", ram_quota_buf);
SESSION_TYPE *s = 0;
try { s = _create_session(adjusted_args, affinity); }
catch (Allocator::Out_of_memory) { throw Root::Unavailable(); }
_ep->manage(s);
return *s;
}
protected:
/**
@ -169,7 +218,7 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
virtual void _upgrade_session(SESSION_TYPE *, const char *) { }
virtual void _destroy_session(SESSION_TYPE *session) {
destroy(_md_alloc, session); }
Genode::destroy(_md_alloc, session); }
/**
* Return allocator to allocate server object in '_create_session()'
@ -208,6 +257,31 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
{ }
/**************************************
** Local_service::Factory interface **
**************************************/
SESSION_TYPE &create(Session_state::Args const &args,
Affinity affinity) override
{
try {
return _create(args, affinity); }
catch (...) {
throw typename Local_service<SESSION_TYPE>::Factory::Denied(); }
}
void upgrade(SESSION_TYPE &session,
Session_state::Args const &args) override
{
_upgrade_session(&session, args.string());
}
void destroy(SESSION_TYPE &session) override
{
close(session.cap());
}
/********************
** Root interface **
********************/
@ -216,45 +290,8 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
Affinity const &affinity) override
{
if (!args.valid_string()) throw Root::Invalid_args();
POLICY::aquire(args.string());
/*
* We need to decrease 'ram_quota' by
* the size of the session object.
*/
size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE));
if (needed > ram_quota) {
error("Insufficient ram quota, provided=", ram_quota,
", required=", needed);
throw Root::Quota_exceeded();
}
size_t const remaining_ram_quota = ram_quota - needed;
/*
* Deduce ram quota needed for allocating the session object from the
* donated ram quota.
*
* XXX the size of the 'adjusted_args' buffer should dependent
* on the message-buffer size and stack size.
*/
enum { MAX_ARGS_LEN = 256 };
char adjusted_args[MAX_ARGS_LEN];
strncpy(adjusted_args, args.string(), sizeof(adjusted_args));
char ram_quota_buf[64];
snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%lu",
remaining_ram_quota);
Arg_string::set_arg(adjusted_args, sizeof(adjusted_args),
"ram_quota", ram_quota_buf);
SESSION_TYPE *s = 0;
try { s = _create_session(adjusted_args, affinity); }
catch (Allocator::Out_of_memory) { throw Root::Unavailable(); }
return _ep->manage(s);
SESSION_TYPE &session = _create(args.string(), affinity);
return session.cap();
}
void upgrade(Session_capability session, Root::Upgrade_args const &args) override

View File

@ -21,13 +21,13 @@
*/
#include <base/rpc.h>
namespace Genode { class Session; }
namespace Genode { struct Session; }
/**
* Base class of session interfaces
*/
class Genode::Session
struct Genode::Session
{
/*
* Each session interface must implement the class function 'service_name'
@ -35,6 +35,8 @@ class Genode::Session
* This function returns the name of the service provided via the session
* interface.
*/
virtual ~Session() { }
};
#endif /* _INCLUDE__SESSION__SESSION_H_ */

View File

@ -15,6 +15,7 @@ SRC_CC += console.cc
SRC_CC += output.cc
SRC_CC += child.cc
SRC_CC += child_process.cc
SRC_CC += session_state.cc
SRC_CC += elf_binary.cc
SRC_CC += ipc.cc
SRC_CC += lock.cc
@ -29,6 +30,8 @@ SRC_CC += region_map_client.cc
SRC_CC += rm_session_client.cc
SRC_CC += stack_allocator.cc
SRC_CC += trace.cc
SRC_CC += root_proxy.cc
SRC_CC += env_session_id_space.cc
INC_DIR += $(REP_DIR)/src/include $(BASE_DIR)/src/include

View File

@ -56,6 +56,7 @@ Thread_capability Cpu_session_component::create_thread(Capability<Pd_session> pd
error("create_thread: invalid PD argument");
throw Thread_creation_failed();
}
Lock::Guard slab_lock_guard(_thread_alloc_lock);
thread = new (&_thread_alloc)
Cpu_thread_component(

View File

@ -116,8 +116,6 @@ namespace Genode {
typedef Synchronized_ram_session<Ram_session_component> Core_ram_session;
Core_parent _core_parent;
enum { ENTRYPOINT_STACK_SIZE = 2048 * sizeof(Genode::addr_t) };
/*
@ -144,6 +142,10 @@ namespace Genode {
Heap _heap;
Registry<Service> _services;
Core_parent _core_parent { _heap, _services };
public:
/**
@ -202,6 +204,8 @@ namespace Genode {
void reinit(Capability<Parent>::Raw) override { }
void reinit_main_thread(Capability<Region_map> &) override { }
Registry<Service> &services() { return _services; }
};

View File

@ -16,38 +16,83 @@
#define _CORE__INCLUDE__CORE_PARENT_H_
#include <parent/parent.h>
#include <base/service.h>
#include <base/allocator.h>
namespace Genode { struct Core_parent; }
namespace Genode {
template <typename> struct Core_service;
struct Core_parent;
}
template <typename SESSION>
struct Genode::Core_service : Local_service<SESSION>, Registry<Service>::Element
{
Core_service(Registry<Service> &registry,
typename Local_service<SESSION>::Factory &factory)
:
Local_service<SESSION>(factory),
Registry<Service>::Element(registry, *this)
{ }
};
/**
* In fact, Core has _no_ parent. But most of our libraries could work
* seamlessly inside Core too, if it had one. Core_parent fills this gap.
* Core has no parent. But most of Genode's library code could work seamlessly
* inside core if it had one. Core_parent fills this gap.
*/
struct Genode::Core_parent : Parent
class Genode::Core_parent : public Parent
{
void exit(int);
private:
void announce(Service_name const &, Root_capability) { }
Id_space<Client> _id_space;
Allocator &_alloc;
Registry<Service> &_services;
Session_capability session(Service_name const &, Session_args const &,
Affinity const &);
public:
void upgrade(Session_capability, Upgrade_args const &) { throw Quota_exceeded(); }
/**
* Constructor
*
* \alloc allocator to be used for allocating core-local
* 'Session_state' objects
*/
Core_parent(Allocator &alloc, Registry<Service> &services)
: _alloc(alloc), _services(services) { }
void close(Session_capability) { }
void exit(int) override;
Thread_capability main_thread_cap() const { return Thread_capability(); }
void announce(Service_name const &) override { }
void resource_avail_sigh(Signal_context_capability) { }
void session_sigh(Signal_context_capability) override { }
void resource_request(Resource_args const &) { }
Session_capability session(Client::Id, Service_name const &, Session_args const &,
Affinity const &) override;
void yield_sigh(Signal_context_capability) { }
Session_capability session_cap(Client::Id) override { return Session_capability(); }
Resource_args yield_request() { return Resource_args(); }
Upgrade_result upgrade(Client::Id, Upgrade_args const &) override {
throw Quota_exceeded(); }
void yield_response() { }
Close_result close(Client::Id) override { return CLOSE_DONE; }
void session_response(Server::Id, Session_response) override { }
void deliver_session_cap(Server::Id,
Session_capability) override { }
Thread_capability main_thread_cap() const override { return Thread_capability(); }
void resource_avail_sigh(Signal_context_capability) override { }
void resource_request(Resource_args const &) override { }
void yield_sigh(Signal_context_capability) override { }
Resource_args yield_request() override { return Resource_args(); }
void yield_response() override { }
};
#endif /* _CORE__INCLUDE__CORE_PARENT_H_ */

View File

@ -14,11 +14,12 @@
#ifndef _CORE__INCLUDE__PLATFORM_SERVICES_H_
#define _CORE__INCLUDE__PLATFORM_SERVICES_H_
#include <base/service.h>
namespace Genode {
class Rpc_entrypoint;
class Sliced_heap;
class Service_registry;
/**
@ -31,7 +32,7 @@ namespace Genode {
*/
void platform_add_local_services(Rpc_entrypoint *ep,
Sliced_heap *md,
Service_registry *reg);
Registry<Service> *reg);
}
#endif /* _CORE__INCLUDE__PLATFORM_SERVICES_H_ */

View File

@ -42,10 +42,6 @@
using namespace Genode;
/* pool of provided core services */
static Service_registry local_services;
/***************************************
** Core environment/platform support **
***************************************/
@ -85,17 +81,30 @@ Platform_generic *Genode::platform() { return platform_specific(); }
** Core parent support **
*************************/
Session_capability Core_parent::session(Parent::Service_name const &name,
Session_capability Core_parent::session(Parent::Client::Id id,
Parent::Service_name const &name,
Parent::Session_args const &args,
Affinity const &affinity)
{
Service *service = local_services.find(name.string());
Session_capability cap;
if (service)
return service->session(args.string(), affinity);
_services.for_each([&] (Service &service) {
warning("service_name=\"", name.string(), "\" args=\"", args.string(), "\" not handled");
return Session_capability();
if ((service.name() != name.string()) || cap.valid())
return;
Session_state &session = *new (_alloc)
Session_state(service, _id_space, id, args.string(), affinity);
service.initiate_request(session);
cap = session.cap;
});
if (!cap.valid())
error("unexpected core-parent ", name.string(), " session request");
return cap;
}
@ -113,21 +122,15 @@ class Core_child : public Child_policy
Rpc_entrypoint _entrypoint;
enum { STACK_SIZE = 2 * 1024 * sizeof(Genode::addr_t)};
Service_registry &_local_services;
Registry<Service> &_services;
/*
* Dynamic linker, does not need to be valid because init is statically
* linked
*/
Dataspace_capability _ldso_ds;
Capability<Ram_session> _core_ram_cap;
Ram_session &_core_ram;
Pd_session_client _pd;
Ram_session_client _ram;
Cpu_session_client _cpu;
Capability<Cpu_session> _core_cpu_cap;
Cpu_session &_core_cpu;
Child::Initial_thread _initial_thread { _cpu, _pd, "init_main" };
Region_map_client _address_space;
size_t const _ram_quota;
Child _child;
@ -136,20 +139,16 @@ class Core_child : public Child_policy
/**
* Constructor
*/
Core_child(Dataspace_capability elf_ds, Pd_session_capability pd,
Ram_session_capability ram,
Cpu_session_capability cpu,
Service_registry &services)
Core_child(Registry<Service> &services, Ram_session &core_ram,
Capability<Ram_session> core_ram_cap, size_t ram_quota,
Cpu_session &core_cpu, Capability<Cpu_session> core_cpu_cap)
:
_entrypoint(nullptr, STACK_SIZE, "init_child", false),
_local_services(services),
_pd(pd), _ram(ram), _cpu(cpu),
_address_space(Pd_session_client(pd).address_space()),
_child(elf_ds, _ldso_ds, _pd, _pd, _ram, _ram, _cpu, _initial_thread,
*env()->rm_session(), _address_space, _entrypoint, *this,
*_local_services.find(Pd_session::service_name()),
*_local_services.find(Ram_session::service_name()),
*_local_services.find(Cpu_session::service_name()))
_services(services),
_core_ram_cap(core_ram_cap), _core_ram(core_ram),
_core_cpu_cap(core_cpu_cap), _core_cpu(core_cpu),
_ram_quota(Child::effective_ram_quota(ram_quota)),
_child(*env()->rm_session(), _entrypoint, *this)
{
_entrypoint.activate();
}
@ -159,8 +158,7 @@ class Core_child : public Child_policy
** Child-policy interface **
****************************/
void filter_session_args(const char *, char *args,
Genode::size_t args_len)
void filter_session_args(Service::Name const &, char *args, size_t args_len) override
{
using namespace Genode;
@ -177,13 +175,36 @@ class Core_child : public Child_policy
Arg_string::set_arg(args, args_len, "label", value_buf);
}
Name name() const { return "init"; }
const char *name() const { return "init"; }
Service *resolve_session_request(const char *service, const char *)
Service &resolve_session_request(Service::Name const &name,
Session_state::Args const &args) override
{
return _local_services.find(service);
Service *service = nullptr;
_services.for_each([&] (Service &s) {
if (!service && s.name() == name)
service = &s; });
if (!service)
throw Parent::Service_denied();
return *service;
}
void init(Ram_session &session, Capability<Ram_session> cap) override
{
session.ref_account(_core_ram_cap);
_core_ram.transfer_quota(cap, _ram_quota);
}
void init(Cpu_session &session, Capability<Cpu_session> cap) override
{
session.ref_account(_core_cpu_cap);
_core_cpu.transfer_quota(cap, Cpu_session::quota_lim_upscale(100, 100));
}
Ram_session &ref_ram() { return _core_ram; }
Ram_session_capability ref_ram_cap() const { return _core_ram_cap; }
};
@ -240,6 +261,8 @@ int main()
*/
Rpc_entrypoint *e = core_env()->entrypoint();
Registry<Service> &services = core_env()->services();
/*
* Allocate session meta data on distinct dataspaces to enable independent
* destruction (to enable quota trading) of session component objects.
@ -266,70 +289,42 @@ int main()
platform()->irq_alloc(), &sliced_heap);
static Trace::Root trace_root (e, &sliced_heap, Trace::sources(), trace_policies);
/*
* Play our role as parent of init and declare our services.
*/
static Local_service ls[] = {
Local_service(Rom_session::service_name(), &rom_root),
Local_service(Ram_session::service_name(), &ram_root),
Local_service(Rm_session::service_name(), &rm_root),
Local_service(Cpu_session::service_name(), &cpu_root),
Local_service(Pd_session::service_name(), &pd_root),
Local_service(Log_session::service_name(), &log_root),
Local_service(Io_mem_session::service_name(), &io_mem_root),
Local_service(Irq_session::service_name(), &irq_root),
Local_service(Trace::Session::service_name(), &trace_root)
};
/* make our local services known to service pool */
for (unsigned i = 0; i < sizeof(ls) / sizeof(Local_service); i++)
local_services.insert(&ls[i]);
static Core_service<Rom_session_component> rom_service (services, rom_root);
static Core_service<Ram_session_component> ram_service (services, ram_root);
static Core_service<Rm_session_component> rm_service (services, rm_root);
static Core_service<Cpu_session_component> cpu_service (services, cpu_root);
static Core_service<Pd_session_component> pd_service (services, pd_root);
static Core_service<Log_session_component> log_service (services, log_root);
static Core_service<Io_mem_session_component> io_mem_service (services, io_mem_root);
static Core_service<Irq_session_component> irq_service (services, irq_root);
static Core_service<Trace::Session_component> trace_service (services, trace_root);
/* make platform-specific services known to service pool */
platform_add_local_services(e, &sliced_heap, &local_services);
platform_add_local_services(e, &sliced_heap, &services);
/* obtain ROM session with init binary */
Rom_session_capability init_rom_session_cap;
try {
static Rom_connection rom("init");
init_rom_session_cap = rom.cap(); }
catch (...) {
error("ROM module \"init\" not present"); }
/* create ram session for init and transfer some of our own quota */
Ram_session_capability init_ram_session_cap
= static_cap_cast<Ram_session>(ram_root.session("ram_quota=32K", Affinity()));
Ram_session_client(init_ram_session_cap).ref_account(env()->ram_session_cap());
/* create CPU session for init and transfer all of the CPU quota to it */
/* create CPU session representing core */
static Cpu_session_component
cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(),
"label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT);
Cpu_session_capability cpu_cap = core_env()->entrypoint()->manage(&cpu);
Cpu_connection init_cpu("init");
init_cpu.ref_account(cpu_cap);
cpu.transfer_quota(init_cpu, Cpu_session::quota_lim_upscale(100, 100));
core_cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(),
"label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT);
Cpu_session_capability core_cpu_cap = core_env()->entrypoint()->manage(&core_cpu);
/* transfer all left memory to init, but leave some memory left for core */
/* NOTE: exception objects thrown in core components are currently allocated on
core's heap and not accounted by the component's meta data allocator */
Genode::size_t init_quota = platform()->ram_alloc()->avail() - 224*1024;
env()->ram_session()->transfer_quota(init_ram_session_cap, init_quota);
log("", init_quota / (1024*1024), " MiB RAM assigned to init");
/*
* Transfer all left memory to init, but leave some memory left for core
*
* NOTE: exception objects thrown in core components are currently
* allocated on core's heap and not accounted by the component's meta data
* allocator
*/
Pd_connection init_pd("init");
Core_child *init = new (env()->heap())
Core_child(Rom_session_client(init_rom_session_cap).dataspace(),
init_pd, init_ram_session_cap, init_cpu.cap(),
local_services);
Genode::size_t const ram_quota = platform()->ram_alloc()->avail() - 224*1024;
log("", ram_quota / (1024*1024), " MiB RAM assigned to init");
static Volatile_object<Core_child>
init(services, *env()->ram_session(), env()->ram_session_cap(),
ram_quota, core_cpu, core_cpu_cap);
platform()->wait_for_exit();
destroy(env()->heap(), init);
rom_root.close(init_rom_session_cap);
ram_root.close(init_ram_session_cap);
init.destruct();
return 0;
}

View File

@ -15,5 +15,5 @@
#include <platform_services.h>
void Genode::platform_add_local_services(Rpc_entrypoint*, Sliced_heap*,
Service_registry*) { }
void Genode::platform_add_local_services(Rpc_entrypoint *, Sliced_heap*,
Registry<Service> *) { }

View File

@ -78,8 +78,10 @@ int Ram_session_component::_transfer_quota(Ram_session_component *dst, size_t am
/* decrease quota limit of this session - check against used quota */
if (_quota_limit < amount + _payload) {
warning("Insufficient quota for transfer: ", Cstring(_label));
warning(" have ", _quota_limit - _payload, ", need ", amount);
warning("insufficient quota for transfer: "
"'", Cstring(_label), "' to '", Cstring(dst->_label), "' "
"have ", (_quota_limit - _payload)/1024, " KiB, "
"need ", amount/1024, " KiB");
return -3;
}
@ -262,6 +264,10 @@ int Ram_session_component::transfer_quota(Ram_session_capability ram_session_cap
{
auto lambda = [&] (Ram_session_component *dst) {
return _transfer_quota(dst, amount); };
if (this->cap() == ram_session_cap)
return 0;
return _ram_session_ep->apply(ram_session_cap, lambda);
}

View File

@ -26,11 +26,11 @@
*/
void Genode::platform_add_local_services(Rpc_entrypoint*,
Sliced_heap *sliced_heap,
Service_registry *local_services)
Registry<Service> *local_services)
{
static Io_port_root io_port_root(core_env()->pd_session(),
platform()->io_port_alloc(), sliced_heap);
static Local_service io_port_ls(Io_port_session::service_name(),
&io_port_root);
local_services->insert(&io_port_ls);
static Core_service<Io_port_session_component>
io_port_ls(*local_services, io_port_root);
}

View File

@ -30,7 +30,8 @@ struct Genode::Attached_stack_area : Expanding_region_map_client
{
Attached_stack_area(Parent &parent, Pd_session_capability pd)
:
Expanding_region_map_client(pd, Pd_session_client(pd).stack_area())
Expanding_region_map_client(pd, Pd_session_client(pd).stack_area(),
Parent::Env::pd())
{
Region_map_client address_space(Pd_session_client(pd).address_space());

View File

@ -27,7 +27,7 @@ namespace Genode { struct Expanding_cpu_session_client; }
struct Genode::Expanding_cpu_session_client : Upgradeable_client<Genode::Cpu_session_client>
{
Expanding_cpu_session_client(Genode::Cpu_session_capability cap)
Expanding_cpu_session_client(Genode::Cpu_session_capability cap, Parent::Client::Id id)
:
/*
* We need to upcast the capability because on some platforms (i.e.,
@ -35,7 +35,7 @@ struct Genode::Expanding_cpu_session_client : Upgradeable_client<Genode::Cpu_ses
* interface ('Nova_cpu_session').
*/
Upgradeable_client<Genode::Cpu_session_client>
(static_cap_cast<Genode::Cpu_session_client::Rpc_interface>(cap))
(static_cap_cast<Genode::Cpu_session_client::Rpc_interface>(cap), id)
{ }
Thread_capability

View File

@ -87,13 +87,14 @@ class Genode::Expanding_parent_client : public Parent_client
** Parent interface **
**********************/
Session_capability session(Service_name const &name,
Session_capability session(Client::Id id,
Service_name const &name,
Session_args const &args,
Affinity const &affinity) override
{
enum { NUM_ATTEMPTS = 2 };
return retry<Parent::Quota_exceeded>(
[&] () { return Parent_client::session(name, args, affinity); },
[&] () { return Parent_client::session(id, name, args, affinity); },
[&] () {
/*
@ -114,7 +115,7 @@ class Genode::Expanding_parent_client : public Parent_client
NUM_ATTEMPTS);
}
void upgrade(Session_capability to_session, Upgrade_args const &args) override
Upgrade_result upgrade(Client::Id id, Upgrade_args const &args) override
{
/*
* If the upgrade fails, attempt to issue a resource request twice.
@ -131,8 +132,8 @@ class Genode::Expanding_parent_client : public Parent_client
* caller to issue (and respond to) a resource request.
*/
enum { NUM_ATTEMPTS = 2 };
retry<Parent::Quota_exceeded>(
[&] () { Parent_client::upgrade(to_session, args); },
return retry<Parent::Quota_exceeded>(
[&] () { return Parent_client::upgrade(id, args); },
[&] () { resource_request(Resource_args(args.string())); },
NUM_ATTEMPTS);
}

View File

@ -26,8 +26,8 @@ namespace Genode { class Expanding_ram_session_client; }
struct Genode::Expanding_ram_session_client : Upgradeable_client<Genode::Ram_session_client>
{
Expanding_ram_session_client(Ram_session_capability cap)
: Upgradeable_client<Genode::Ram_session_client>(cap) { }
Expanding_ram_session_client(Ram_session_capability cap, Parent::Client::Id id)
: Upgradeable_client<Genode::Ram_session_client>(cap, id) { }
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached = UNCACHED) override
{

View File

@ -29,8 +29,9 @@ struct Genode::Expanding_region_map_client : Region_map_client
{
Upgradeable_client<Genode::Pd_session_client> _pd_client;
Expanding_region_map_client(Pd_session_capability pd, Capability<Region_map> rm)
: Region_map_client(rm), _pd_client(pd) { }
Expanding_region_map_client(Pd_session_capability pd, Capability<Region_map> rm,
Parent::Client::Id pd_id)
: Region_map_client(rm), _pd_client(pd, pd_id) { }
Local_addr attach(Dataspace_capability ds, size_t size, off_t offset,
bool use_local_addr, Local_addr local_addr,

View File

@ -17,11 +17,14 @@
#ifndef _INCLUDE__BASE__INTERNAL__GLOBALS_H_
#define _INCLUDE__BASE__INTERNAL__GLOBALS_H_
#include <parent/parent.h>
namespace Genode {
class Region_map;
class Ram_session;
class Env;
class Local_session_id_space;
extern Region_map *env_stack_area_region_map;
extern Ram_session *env_stack_area_ram_session;
@ -30,7 +33,11 @@ namespace Genode {
void init_cxx_heap(Env &);
void init_ldso_phdr(Env &);
void init_signal_thread(Env &);
void init_root_proxy(Env &);
void init_log();
Id_space<Parent::Client> &env_session_id_space();
Env &internal_env();
}
#endif /* _INCLUDE__BASE__INTERNAL__GLOBALS_H_ */

View File

@ -48,9 +48,9 @@ class Genode::Platform_env : public Env_deprecated,
struct Resources
{
template <typename T>
Capability<T> request(Parent &parent, char const *service)
Capability<T> request(Parent &parent, Parent::Client::Id id)
{
return static_cap_cast<T>(parent.session(service, ""));
return static_cap_cast<T>(parent.session_cap(id));
}
Expanding_ram_session_client ram;
@ -60,10 +60,12 @@ class Genode::Platform_env : public Env_deprecated,
Resources(Parent &parent)
:
ram(request<Ram_session>(parent, "Env::ram_session")),
cpu(request<Cpu_session>(parent, "Env::cpu_session")),
pd (request<Pd_session> (parent, "Env::pd_session")),
rm (pd, pd.address_space())
ram(request<Ram_session>(parent, Parent::Env::ram()),
Parent::Env::ram()),
cpu(request<Cpu_session>(parent, Parent::Env::cpu()),
Parent::Env::cpu()),
pd (request<Pd_session> (parent, Parent::Env::pd())),
rm (pd, pd.address_space(), Parent::Env::pd())
{ }
};

View File

@ -28,19 +28,17 @@ struct Genode::Upgradeable_client : CLIENT
{
typedef Genode::Capability<typename CLIENT::Rpc_interface> Capability;
Capability _cap;
Parent::Client::Id _id;
Upgradeable_client(Capability cap) : CLIENT(cap), _cap(cap) { }
Upgradeable_client(Capability cap, Parent::Client::Id id)
: CLIENT(cap), _id(id) { }
void upgrade_ram(size_t quota)
{
log("upgrading quota donation for Env::", CLIENT::Rpc_interface::service_name(),
" (", quota, " bytes)");
char buf[128];
snprintf(buf, sizeof(buf), "ram_quota=%lu", quota);
env()->parent()->upgrade(_cap, buf);
env()->parent()->upgrade(_id, buf);
}
};

View File

@ -43,12 +43,16 @@ namespace {
public:
class Quota_exceeded : Exception { };
/**
* Constructor
*
* \param quantim number of bytes to transfer
* \param from donator RAM session
* \param to receiver RAM session
*
* \throw Quota_exceeded
*/
Transfer(size_t quantum,
Ram_session_capability from,
@ -58,7 +62,7 @@ namespace {
if (_from.valid() && _to.valid() &&
Ram_session_client(_from).transfer_quota(_to, quantum)) {
warning("not enough quota for a donation of ", quantum, " bytes");
throw Parent::Quota_exceeded();
throw Quota_exceeded();
}
}
@ -83,198 +87,15 @@ namespace {
}
/********************
** Child::Session **
********************/
class Child::Session : public Object_pool<Session>::Entry,
public List<Session>::Element
{
private:
enum { IDENT_LEN = 16 };
/**
* Session capability at the server
*/
Session_capability _cap;
/**
* Service interface that was used to create the session
*/
Service *_service;
/**
* Server implementing the session
*
* Even though we can normally determine the server of the session via
* '_service->server()', this does not apply when destructing a server.
* During destruction, we use the 'Server' pointer as opaque key for
* revoking active sessions of the server. So we keep a copy
* independent of the 'Service' object.
*/
Server *_server;
/**
* Total of quota associated with this session
*/
size_t _donated_ram_quota;
/**
* Name of session, used for debugging
*/
char _ident[IDENT_LEN];
public:
/**
* Constructor
*
* \param session session capability
* \param service service that implements the session
* \param ram_quota initial quota donation associated with
* the session
* \param ident optional session identifier, used for
* debugging
*/
Session(Session_capability session, Service *service,
size_t ram_quota, const char *ident = "<noname>")
:
Object_pool<Session>::Entry(session), _cap(session),
_service(service), _server(service->server()),
_donated_ram_quota(ram_quota) {
strncpy(_ident, ident, sizeof(_ident)); }
/**
* Default constructor creates invalid session
*/
Session() : _service(0), _donated_ram_quota(0) { }
/**
* Extend amount of ram attached to the session
*/
void upgrade_ram_quota(size_t ram_quota) {
_donated_ram_quota += ram_quota; }
/**
* Accessors
*/
Session_capability cap() const { return _cap; }
size_t donated_ram_quota() const { return _donated_ram_quota; }
bool valid() const { return _service != 0; }
Service *service() const { return _service; }
Server *server() const { return _server; }
const char *ident() const { return _ident; }
};
/***********
** Child **
***********/
void Child::_add_session(Child::Session const &s)
template <typename SESSION>
static Service &parent_service()
{
Lock::Guard lock_guard(_lock);
/*
* Store session information in a new child's meta data structure. The
* allocation from 'heap()' may throw a 'Ram_session::Quota_exceeded'
* exception.
*/
Session *session = 0;
try {
session = new (heap())
Session(s.cap(), s.service(),
s.donated_ram_quota(), s.ident()); }
catch (Allocator::Out_of_memory) {
throw Parent::Quota_exceeded(); }
/* these functions may also throw 'Ram_session::Quota_exceeded' */
_session_pool.insert(session);
_session_list.insert(session);
}
void Child::_remove_session(Child::Session *s)
{
/* forget about this session */
_session_list.remove(s);
/* return session quota to the ram session of the child */
if (_policy.ref_ram_session()->transfer_quota(_ram, s->donated_ram_quota()))
error("We ran out of our own quota");
destroy(heap(), s);
}
Service &Child::_parent_service()
{
static Parent_service parent_service("");
return parent_service;
}
void Child::_close(Session* s)
{
if (!s) {
warning("no session structure found");
return;
}
/*
* There is a chance that the server is not responding to the 'close' call,
* making us block infinitely. However, by using core's cancel-blocking
* mechanism, we can cancel the 'close' call by another (watchdog) thread
* that invokes 'cancel_blocking' at our thread after a timeout. The
* unblocking is reflected at the API level as an 'Blocking_canceled'
* exception. We catch this exception to proceed with normal operation
* after being unblocked.
*/
try { s->service()->close(s->cap()); }
catch (Blocking_canceled) {
warning("Got Blocking_canceled exception during ", s->ident(), "->close call"); }
/*
* If the session was provided by a child of us,
* 'server()->ram_session_cap()' returns the RAM session of the
* corresponding child. Since the session to the server is closed now, we
* expect that the server released all donated resources and we can
* decrease the servers' quota.
*
* If this goes wrong, the server is misbehaving.
*/
if (s->service()->ram_session_cap().valid()) {
Ram_session_client server_ram(s->service()->ram_session_cap());
if (server_ram.transfer_quota(_policy.ref_ram_cap(),
s->donated_ram_quota())) {
error("Misbehaving server '", s->service()->name(), "'!");
}
}
{
Lock::Guard lock_guard(_lock);
_remove_session(s);
}
}
void Child::revoke_server(Server const *server)
{
Lock::Guard lock_guard(_lock);
while (1) {
/* search session belonging to the specified server */
Session *s = _session_list.first();
for ( ; s && (s->server() != server); s = s->next());
/* if no matching session exists, we are done */
if (!s) return;
_session_pool.apply(s->cap(), [&] (Session *s) {
if (s) _session_pool.remove(s); });
_remove_session(s);
}
static Parent_service service(SESSION::service_name());
return service;
}
@ -298,133 +119,374 @@ void Child::notify_resource_avail() const
}
void Child::announce(Parent::Service_name const &name, Root_capability root)
void Child::announce(Parent::Service_name const &name)
{
if (!name.valid_string()) return;
_policy.announce_service(name.string(), root, heap(), &_server);
_policy.announce_service(name.string());
}
Session_capability Child::session(Parent::Service_name const &name,
void Child::session_sigh(Signal_context_capability sigh)
{
_session_sigh = sigh;
if (!_session_sigh.valid())
return;
/*
* 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.
*/
_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)
{
try {
return service.create_session(factory, id_space, id, args, affinity);
}
catch (Allocator::Out_of_memory) {
error("could not allocate session meta data for child ", child_name);
throw Parent::Quota_exceeded();
}
catch (Id_space<Parent::Client>::Conflicting_id) {
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();
}
Session_capability Child::session(Parent::Client::Id id,
Parent::Service_name const &name,
Parent::Session_args const &args,
Affinity const &affinity)
{
if (!name.valid_string() || !args.valid_string()) throw Unavailable();
/* return sessions that we created for the child */
if (!strcmp("Env::ram_session", name.string())) return _ram;
if (!strcmp("Env::cpu_session", name.string())) return _cpu;
if (!strcmp("Env::pd_session", name.string())) return _pd;
char argbuf[Parent::Session_args::MAX_SIZE];
/* filter session arguments according to the child policy */
strncpy(_args, args.string(), sizeof(_args));
_policy.filter_session_args(name.string(), _args, sizeof(_args));
strncpy(argbuf, args.string(), sizeof(argbuf));
_policy.filter_session_args(name.string(), argbuf, sizeof(argbuf));
/* filter session affinity */
Affinity const filtered_affinity = _policy.filter_session_affinity(affinity);
/* may throw a 'Parent::Service_denied' exception */
Service &service = _policy.resolve_session_request(name.string(), argbuf);
Session_state &session =
create_session(_policy.name(), service, _session_factory,
_id_space, id, argbuf, filtered_affinity);
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(_args, "ram_quota").ulong_value(0);
size_t ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0);
Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap());
try {
Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap());
Service *service = _policy.resolve_session_request(name.string(), _args);
/* transfer session quota from ourself to the service provider */
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
service.ram());
/* raise an error if no matching service provider could be found */
if (!service)
throw Service_denied();
/* try to dispatch session request synchronously */
service.initiate_request(session);
/* transfer session quota from ourself to the service provider */
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
service->ram_session_cap());
if (session.phase == Session_state::INVALID_ARGS) {
_revert_quota_and_destroy(session);
throw Service_denied();
}
/* create session */
Session_capability cap;
try { cap = service->session(_args, filtered_affinity); }
catch (Service::Invalid_args) { throw Service_denied(); }
catch (Service::Unavailable) { throw Service_denied(); }
catch (Service::Quota_exceeded) { throw Quota_exceeded(); }
/* 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();
}
/* register session */
try { _add_session(Session(cap, service, ram_quota, name.string())); }
catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); }
/*
* 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.
*/
Session_capability cap = session.cap;
/* finish transaction */
donation_from_child.acknowledge();
donation_to_service.acknowledge();
/* if request was not handled synchronously, kick off async operation */
if (session.phase == Session_state::CREATE_REQUESTED)
service.wakeup();
if (cap.valid())
session.phase = Session_state::CAP_HANDED_OUT;
return cap;
}
void Child::upgrade(Session_capability to_session, Parent::Upgrade_args const &args)
Session_capability Child::session_cap(Client::Id id)
{
Service *targeted_service = 0;
Session_capability cap;
/* check of upgrade refers to an Env:: resource */
if (to_session.local_name() == _ram.local_name())
targeted_service = &_ram_service;
if (to_session.local_name() == _cpu.local_name())
targeted_service = &_cpu_service;
if (to_session.local_name() == _pd.local_name())
targeted_service = &_pd_service;
auto lamda = [&] (Session_state &session) {
/* check if upgrade refers to server */
_session_pool.apply(to_session, [&] (Session *session)
{
if (session)
targeted_service = session->service();
if (session.phase == Session_state::INVALID_ARGS) {
if (!targeted_service) {
warning("could not lookup service for session upgrade");
return;
/*
* 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();
}
if (!args.valid_string()) {
warning("no valid session-upgrade arguments");
if (!session.alive())
warning(_policy.name(), ": attempt to request cap for unavailable session: ", session);
if (session.cap.valid())
session.phase = Session_state::CAP_HANDED_OUT;
cap = session.cap;
};
try {
_id_space.apply<Session_state>(id, lamda); }
catch (Id_space<Parent::Client>::Unknown_id) {
warning(_policy.name(), " requested session cap for unknown ID"); }
return cap;
}
Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const &args)
{
if (!args.valid_string()) {
warning("no valid session-upgrade arguments");
return UPGRADE_DONE;
}
Upgrade_result result = UPGRADE_PENDING;
_id_space.apply<Session_state>(id, [&] (Session_state &session) {
if (session.phase != Session_state::CAP_HANDED_OUT) {
warning("attempt to upgrade session in invalid state");
return;
}
size_t const ram_quota =
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
/* transfer quota from client to ourself */
Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap());
try {
/* transfer quota from client to ourself */
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(),
targeted_service->ram_session_cap());
/* transfer session quota from ourself to the service provider */
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
session.service().ram());
try { targeted_service->upgrade(to_session, args.string()); }
catch (Service::Quota_exceeded) { throw Quota_exceeded(); }
session.increase_donated_quota(ram_quota);
session.phase = Session_state::UPGRADE_REQUESTED;
/* remember new amount attached to the session */
if (session)
session->upgrade_ram_quota(ram_quota);
session.service().initiate_request(session);
/* finish transaction */
donation_from_child.acknowledge();
donation_to_service.acknowledge();
/* 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();
}
if (session.phase == Session_state::CAP_HANDED_OUT) {
result = UPGRADE_DONE;
return;
}
session.service().wakeup();
});
return result;
}
void Child::close(Session_capability session_cap)
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());
/* 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, ")"); }
session.destroy();
}
Child::Close_result Child::_close(Session_state &session)
{
/*
* 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;
}
/* close session if alive */
if (session.alive()) {
session.phase = Session_state::CLOSE_REQUESTED;
session.service().initiate_request(session);
}
/*
* The service may have completed the close request immediately (e.g.,
* a locally implemented service). In this case, we can skip the
* asynchonous handling.
*/
if (session.phase == Session_state::CLOSED) {
_revert_quota_and_destroy(session);
return CLOSE_DONE;
}
session.discard_id_at_client();
session.service().wakeup();
return CLOSE_PENDING;
}
Child::Close_result Child::close(Client::Id id)
{
/* refuse to close the child's initial sessions */
if (session_cap.local_name() == _ram.local_name()
|| session_cap.local_name() == _cpu.local_name()
|| session_cap.local_name() == _pd.local_name())
return;
if (Parent::Env::session_id(id))
return CLOSE_DONE;
Session *session = nullptr;
_session_pool.apply(session_cap, [&] (Session *s)
{
session = s;
if (s) _session_pool.remove(s);
});
_close(session);
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; }
}
void Child::session_ready(Session_state &session)
{
if (_session_sigh.valid() && session.async_client_notify)
Signal_transmitter(_session_sigh).submit();
}
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) { }
}
@ -474,32 +536,41 @@ Parent::Resource_args Child::yield_request()
void Child::yield_response() { _policy.yield_response(); }
Child::Child(Dataspace_capability elf_ds,
Dataspace_capability ldso_ds,
Pd_session_capability pd_cap,
Pd_session &pd,
Ram_session_capability ram_cap,
Ram_session &ram,
Cpu_session_capability cpu_cap,
Initial_thread_base &initial_thread,
Region_map &local_rm,
Region_map &remote_rm,
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 &region_map() { return _rm; }
};
}
Child::Child(Region_map &local_rm,
Rpc_entrypoint &entrypoint,
Child_policy &policy,
Service &pd_service,
Service &ram_service,
Service &cpu_service)
Child_policy &policy)
try :
_pd(pd_cap), _ram(ram_cap), _cpu(cpu_cap),
_pd_service(pd_service),
_ram_service(ram_service),
_cpu_service(cpu_service),
_heap(&ram, &local_rm),
_policy(policy),
_heap(&_ram.session(), &local_rm),
_entrypoint(entrypoint),
_parent_cap(_entrypoint.manage(this)),
_policy(policy),
_server(_ram),
_process(elf_ds, ldso_ds, pd_cap, pd, ram, initial_thread, local_rm, remote_rm,
_process(_binary.session().dataspace(), _linker_dataspace(),
_pd.cap(), _pd.session(), _ram.session(), _initial_thread, local_rm,
Child_address_space(_pd.session(), _policy).region_map(),
_parent_cap)
{ }
catch (Cpu_session::Thread_creation_failed) { throw Process_startup_failed(); }
@ -512,8 +583,48 @@ catch (Region_map::Attach_failed) { throw Process_startup_failed(); }
Child::~Child()
{
_entrypoint.dissolve(this);
_policy.unregister_services();
_session_pool.remove_all([&] (Session *s) { _close(s); });
/*
* 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));
}

View File

@ -14,21 +14,55 @@
/* Genode includes */
#include <base/component.h>
#include <base/connection.h>
#include <base/service.h>
#include <base/env.h>
/* base-internal includes */
#include <base/internal/globals.h>
/*
* XXX remove this pointer once 'Env_deprecated' is removed
*/
static Genode::Env *env_ptr = nullptr;
namespace {
using namespace Genode;
struct Env : Genode::Env
{
Genode::Entrypoint &_ep;
Env(Genode::Entrypoint &ep) : _ep(ep) { }
Genode::Parent &_parent = *env()->parent();
Genode::Parent &parent() override { return *Genode::env()->parent(); }
/**
* Lock for serializing 'session' and 'close'
*/
Genode::Lock _lock;
/**
* Utility to used block for single signal
*/
struct Blockade
{
Parent &_parent;
Genode::Signal_receiver _sig_rec;
Genode::Signal_context _sig_ctx;
Blockade(Parent &parent) : _parent(parent)
{
_parent.session_sigh(_sig_rec.manage(&_sig_ctx));
}
void block() { _sig_rec.wait_for_signal(); }
};
Lazy_volatile_object<Blockade> _session_blockade;
Env(Genode::Entrypoint &ep) : _ep(ep) { env_ptr = this; }
Genode::Parent &parent() override { return _parent; }
Genode::Ram_session &ram() override { return *Genode::env()->ram_session(); }
Genode::Cpu_session &cpu() override { return *Genode::env()->cpu_session(); }
Genode::Region_map &rm() override { return *Genode::env()->rm_session(); }
@ -49,6 +83,54 @@ namespace {
{
return Genode::env()->pd_session_cap();
}
Genode::Id_space<Parent::Client> &id_space() override
{
return Genode::env_session_id_space();
}
void _block_for_session()
{
/*
* Construct blockade lazily be avoid it being used in core where
* all session requests are immediately answered.
*/
if (!_session_blockade.constructed())
_session_blockade.construct(_parent);
_session_blockade->block();
}
Session_capability session(Parent::Service_name const &name,
Parent::Client::Id id,
Parent::Session_args const &args,
Affinity const &affinity) override
{
Lock::Guard guard(_lock);
Session_capability cap = _parent.session(id, name, args, affinity);
if (cap.valid())
return cap;
_block_for_session();
return _parent.session_cap(id);
}
void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override
{
Lock::Guard guard(_lock);
if (_parent.upgrade(id, args) == Parent::UPGRADE_PENDING)
_block_for_session();
}
void close(Parent::Client::Id id) override
{
Lock::Guard guard(_lock);
if (_parent.close(id) == Parent::CLOSE_PENDING)
_block_for_session();
}
};
}
@ -58,6 +140,15 @@ namespace Genode {
struct Startup;
extern void bootstrap_component();
Env &internal_env()
{
class Env_ptr_not_initialized { };
if (!env_ptr)
throw Env_ptr_not_initialized();
return *env_ptr;
}
}

View File

@ -85,6 +85,7 @@ void Entrypoint::_process_incoming_signals()
_suspended_callback();
init_signal_thread(_env);
_rpc_ep.construct(&_env.pd(), Component::stack_size(), initial_ep_name());
_signal_proxy_cap = manage(_signal_proxy);
_sig_rec.construct();
@ -165,6 +166,9 @@ Entrypoint::Entrypoint(Env &env)
/* initialize signalling after initializing but before calling the entrypoint */
init_signal_thread(_env);
/* initialize emulation of the original synchronous root interface */
init_root_proxy(_env);
/*
* Invoke Component::construct function in the context of the entrypoint.
*/

View File

@ -13,6 +13,9 @@
*/
#include <base/internal/platform_env.h>
#include <base/internal/globals.h>
#include <base/connection.h>
#include <base/service.h>
namespace Genode {

View File

@ -0,0 +1,59 @@
/*
* \brief Component-local session ID space
* \author Norman Feske
* \date 2016-10-13
*/
/*
* Copyright (C) 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.
*/
/* Genode includes */
#include <base/connection.h>
#include <base/service.h>
/* base-internal includes */
#include <base/internal/globals.h>
#include <base/internal/unmanaged_singleton.h>
using namespace Genode;
Id_space<Parent::Client> &Genode::env_session_id_space()
{
Id_space<Parent::Client> &id_space =
*unmanaged_singleton<Id_space<Parent::Client> >();
/* pre-allocate env session IDs */
static Parent::Client dummy;
static Id_space<Parent::Client>::Element
ram { dummy, id_space, Parent::Env::ram() },
cpu { dummy, id_space, Parent::Env::cpu() },
pd { dummy, id_space, Parent::Env::pd() },
log { dummy, id_space, Parent::Env::log() },
binary { dummy, id_space, Parent::Env::binary() },
linker { dummy, id_space, Parent::Env::linker() };
return id_space;
}
/*
* \deprecated
*/
Connection_base::Connection_base()
:
_env(internal_env()),
_id_space_element(_parent_client, _env.id_space())
{ }
/*
* \deprecated
*/
Env &Parent_service::_env_deprecated() { return internal_env(); }

View File

@ -26,10 +26,21 @@ class Log_console : public Console
enum { _BUF_SIZE = Log_session::MAX_STRING_LEN };
Log_connection _log;
char _buf[_BUF_SIZE];
unsigned _num_chars;
Lock _lock;
struct Log : Log_session_client
{
Session_capability _cap() {
return env()->parent()->session_cap(Parent::Env::log()); }
Log() : Log_session_client(reinterpret_cap_cast<Log_session>(_cap()))
{ }
};
Log _log;
char _buf[_BUF_SIZE];
unsigned _num_chars;
Lock _lock;
void _flush()
{
@ -77,7 +88,7 @@ class Log_console : public Console
/**
* Return LOG session interface
*/
Log_session *log_session() { return &_log; }
Log_session &log_session() { return _log; }
/**
* Re-establish LOG session
@ -85,13 +96,13 @@ class Log_console : public Console
void reconnect()
{
/*
* Note that the destructor of old 'Log_connection' is not called.
* This is not needed because the only designated use of this
* function is the startup procedure of noux processes created
* via fork. At the point of calling this function, the new child
* has no valid capability to the original LOG session anyway.
* We cannot use a 'Volatile_object' because we have to skip
* the object destruction inside a freshly forked process.
* Otherwise, the attempt to destruct the capability contained
* in the 'Log' object would result in an inconsistent ref counter
* of the respective capability-space element.
*/
new (&_log) Log_connection;
construct_at<Log>(&_log);
}
};
@ -109,7 +120,7 @@ static Log_console *stdout_log_console() { return unmanaged_singleton<Log_consol
*/
extern "C" int stdout_write(const char *s)
{
return stdout_log_console()->log_session()->write(s);
return stdout_log_console()->log_session().write(s);
}

View File

@ -0,0 +1,251 @@
/*
* \brief Mechanism for dispatching session requests to root interfaces
* \author Norman Feske
* \date 2016-10-07
*/
/*
* Copyright (C) 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.
*/
#include <base/log.h>
#include <base/attached_rom_dataspace.h>
#include <base/session_state.h>
#include <base/heap.h>
#include <base/tslab.h>
#include <root/client.h>
using namespace Genode;
namespace {
struct Service
{
typedef Session_state::Name Name;
Name name;
Capability<Root> root;
struct Session : Parent::Server
{
Id_space<Parent::Server>::Element id;
Session_capability cap;
Service &service;
Session(Id_space<Parent::Server> &id_space, Parent::Server::Id id,
Service &service, Session_capability cap)
:
id(*this, id_space, id), cap(cap), service(service)
{ }
};
};
class Service_registry : Noncopyable
{
private:
enum { MAX = 32 };
Lock mutable _lock;
Service _services[MAX];
unsigned _cnt = 0;
public:
void insert(Service const &service)
{
Lock::Guard guard(_lock);
if (_cnt == MAX) {
error("maximum number of services announced");
return;
}
_services[_cnt++] = service;
}
/**
* Call functor 'fn' with root capability for a given service name
*/
template <typename FUNC>
void apply(Service::Name const &name, FUNC const &fn)
{
/*
* Protect '_services' but execute 'fn' with the lock released.
*
* If we called 'fn' with the lock held, the following scenario
* may result in a deadlock:
*
* A component provides two services, e.g., "Framebuffer" and
* "Input" (fb_sdl or nit_fb). In-between the two 'announce'
* calls (within the 'Component::construct' function), a
* service request for the already announced service comes in.
* The root proxy calls 'apply' with the service name, which
* results in an RPC call to the root interface that is served
* by the same entrypoint as 'Component::construct'. The RPC
* call blocks until 'Component::construct' returns. However,
* before returning, the function announces the second service,
* eventually arriving at 'Service_registry::insert', which
* tries to acquire the same lock as the blocking 'apply' call.
*/
_lock.lock();
for (unsigned i = 0; i < _cnt; i++) {
if (name != _services[i].name)
continue;
_lock.unlock();
fn(_services[i]);
return;
}
_lock.unlock();
}
};
class Root_proxy
{
private:
Env &_env;
Id_space<Parent::Server> _id_space;
Entrypoint _ep { _env, 2*1024*sizeof(long), "root" };
Attached_rom_dataspace _session_requests { _env, "session_requests" };
Signal_handler<Root_proxy> _session_request_handler {
_ep, *this, &Root_proxy::_handle_session_requests };
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
Tslab<Service::Session, 4000> _session_slab { &_sliced_heap };
void _handle_session_request(Xml_node);
void _handle_session_requests();
Service_registry _services;
public:
Root_proxy(Env &env) : _env(env)
{
_session_requests.sigh(_session_request_handler);
}
void announce(Service const &service)
{
_services.insert(service);
/* trigger re-interpretation of the "session_requests" ROM */
Signal_transmitter(_session_request_handler).submit();
/* notify parent */
_env.parent().announce(service.name.string());
}
};
}
void Root_proxy::_handle_session_request(Xml_node request)
{
if (!request.has_attribute("id"))
return;
Parent::Server::Id const id { request.attribute_value("id", 0UL) };
typedef Service::Session Session;
if (request.has_type("create")) {
if (!request.has_sub_node("args"))
return;
typedef Session_state::Args Args;
Args const args = request.sub_node("args").decoded_content<Args>();
try {
Service::Name const name = request.attribute_value("service", Service::Name());
_services.apply(name, [&] (Service &service) {
// XXX affinity
Session_capability cap =
Root_client(service.root).session(args.string(), Affinity());
new (_session_slab) Session(_id_space, id, service, cap);
_env.parent().deliver_session_cap(id, cap);
});
}
catch (Root::Invalid_args) {
_env.parent().session_response(id, Parent::INVALID_ARGS); }
catch (Root::Quota_exceeded) {
_env.parent().session_response(id, Parent::INVALID_ARGS); }
catch (Root::Unavailable) {
_env.parent().session_response(id, Parent::INVALID_ARGS); }
}
if (request.has_type("upgrade")) {
_id_space.apply<Session>(id, [&] (Session &session) {
size_t ram_quota = request.attribute_value("ram_quota", 0UL);
char buf[64];
snprintf(buf, sizeof(buf), "ram_quota=%ld", ram_quota);
// XXX handle Root::Invalid_args
Root_client(session.service.root).upgrade(session.cap, buf);
_env.parent().session_response(id, Parent::SESSION_OK);
});
}
if (request.has_type("close")) {
_id_space.apply<Session>(id, [&] (Session &session) {
Root_client(session.service.root).close(session.cap);
destroy(_session_slab, &session);
_env.parent().session_response(id, Parent::SESSION_CLOSED);
});
}
}
void Root_proxy::_handle_session_requests()
{
_session_requests.update();
Xml_node const requests = _session_requests.xml();
requests.for_each_sub_node([&] (Xml_node request) {
_handle_session_request(request); });
}
static Env *env_ptr = nullptr;
namespace Genode { void init_root_proxy(Env &env) { env_ptr = &env; } }
void Parent::announce(Service_name const &name, Root_capability root)
{
if (!env_ptr) {
error("announce called prior init_root_proxy");
return;
}
static Root_proxy root_proxy(*env_ptr);
root_proxy.announce({ name.string(), root });
}

View File

@ -26,13 +26,8 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd,
Untyped_capability new_obj_cap =
retry<Genode::Pd_session::Out_of_metadata>(
[&] () { return pd.alloc_rpc_cap(_cap); },
[&] () {
Pd_session_client *client =
dynamic_cast<Pd_session_client*>(&pd);
if (client)
env()->parent()->upgrade(*client, "ram_quota=16K");
});
[&] () { env()->parent()->upgrade(Parent::Env::pd(),
"ram_quota=16K"); });
return new_obj_cap;
}

View File

@ -0,0 +1,97 @@
/*
* \brief Representation of a session request
* \author Norman Feske
* \date 2016-10-10
*/
/*
* Copyright (C) 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.
*/
#include <base/session_state.h>
#include <base/service.h>
#include <util/arg_string.h>
using namespace Genode;
void Session_state::print(Output &out) const
{
using Genode::print;
print(out, "service=", _service.name(), " cid=", _id_at_client, " "
"args='", _args, "' state=", (int)phase, " "
"ram_quota=", _donated_ram_quota);
}
void Session_state::generate_session_request(Xml_generator &xml) const
{
if (!id_at_server.constructed())
warning(__func__, ": id_at_server not initialized");
switch (phase) {
case CREATE_REQUESTED:
xml.node("create", [&] () {
xml.attribute("id", id_at_server->id().value);
xml.attribute("service", _service.name());
xml.node("args", [&] () {
xml.append_sanitized(_args.string());
});
});
break;
case UPGRADE_REQUESTED:
xml.node("upgrade", [&] () {
xml.attribute("id", id_at_server->id().value);
xml.attribute("ram_quota", ram_upgrade);
});
break;
case CLOSE_REQUESTED:
xml.node("close", [&] () {
xml.attribute("id", id_at_server->id().value); });
break;
case INVALID_ARGS:
case AVAILABLE:
case CAP_HANDED_OUT:
case CLOSED:
break;
}
}
void Session_state::destroy()
{
/*
* Manually release client-side ID so that static env sessions are
* immediately by removed from the cliend ID space when 'destroy' is
* called. Otherwise, the iterative cleanup of the cliend ID space
* via 'apply_any' would end up in an infinite loop.
*/
_id_at_client.destruct();
if (_factory)
_factory->_destroy(*this);
}
Session_state::Session_state(Service &service,
Id_space<Parent::Client> &client_id_space,
Parent::Client::Id client_id,
Args const &args,
Affinity const &affinity)
:
_service(service),
_donated_ram_quota(Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0)),
_id_at_client(*this, client_id_space, client_id),
_args(args), _affinity(affinity)
{ }

View File

@ -241,7 +241,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context *context)
log("upgrading quota donation for PD session (", quota, " bytes)");
env()->parent()->upgrade(env()->pd_session_cap(), buf);
env()->parent()->upgrade(Parent::Env::pd(), buf);
}
);

View File

@ -75,14 +75,32 @@ struct Linker::File
*/
struct Linker::Elf_file : File
{
Env &env;
Rom_connection rom;
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR];
bool const loaded;
Env &env;
Lazy_volatile_object<Rom_connection> rom_connection;
Rom_session_client rom;
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR];
bool const loaded;
typedef String<64> Name;
Rom_session_capability _rom_cap(Name const &name)
{
/* request the linker and binary from the component environment */
Session_capability cap;
if (name == binary_name())
cap = env.parent().session_cap(Parent::Env::binary());
if (name == linker_name())
cap = env.parent().session_cap(Parent::Env::linker());
if (cap.valid())
return reinterpret_cap_cast<Rom_session>(cap);
rom_connection.construct(env, name.string());
return *rom_connection;
}
Elf_file(Env &env, Allocator &md_alloc, char const *name, bool load)
:
env(env), rom(env, name), loaded(load)
env(env), rom(_rom_cap(name)), loaded(load)
{
load_phdr();

View File

@ -103,12 +103,6 @@ namespace Linker {
* Global ELF access lock
*/
Lock &lock();
/**
* Invariants
*/
constexpr char const *binary_name() { return "binary"; }
constexpr char const *linker_name() { return "ld.lib.so"; }
}

View File

@ -96,7 +96,7 @@ class Linker::Region_map
[&] () {
return _rm.attach_at(ds, local_addr - _base, size, offset);
},
[&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); });
[&] () { _env.upgrade(Parent::Env::pd(), "ram_quota=8K"); });
}
/**
@ -109,7 +109,7 @@ class Linker::Region_map
[&] () {
return _rm.attach_executable(ds, local_addr - _base, size, offset);
},
[&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); });
[&] () { _env.upgrade(Parent::Env::pd(), "ram_quota=8K"); });
}
void detach(Local_addr local_addr) { _rm.detach((addr_t)local_addr - _base); }

View File

@ -38,6 +38,12 @@ namespace Linker {
enum Bind { BIND_LAZY = Shared_object::BIND_LAZY,
BIND_NOW = Shared_object::BIND_NOW };
/**
* Invariants
*/
constexpr char const *binary_name() { return "binary"; }
constexpr char const *linker_name() { return "ld.lib.so"; }
}
#endif /* _INCLUDE__TYPES_H_ */

View File

@ -4,10 +4,10 @@
* \date 2008-09-24
*
* This program starts itself as child. When started, it first determines
* wheather it is parent or child by requesting its own file from the ROM
* service. Because the program blocks all session-creation calls for the
* ROM service, each program instance can determine its parent or child
* role by the checking the result of the session creation.
* wheather it is parent or child by requesting a RM session. Because the
* program blocks all session-creation calls for the RM service, each program
* instance can determine its parent or child role by the checking the result
* of the session creation.
*/
/*
@ -17,17 +17,11 @@
* under the terms of the GNU General Public License version 2.
*/
#include <base/component.h>
#include <base/log.h>
#include <base/env.h>
#include <base/sleep.h>
#include <base/child.h>
#include <pd_session/connection.h>
#include <rm_session/connection.h>
#include <ram_session/connection.h>
#include <rom_session/connection.h>
#include <cpu_session/connection.h>
#include <cap_session/connection.h>
#include <rm_session/client.h>
#include <os/attached_ram_dataspace.h>
using namespace Genode;
@ -69,108 +63,135 @@ void main_child()
** Parent **
************/
class Test_child : public Child_policy
class Test_child_policy : public Child_policy
{
public:
typedef Registered<Genode::Parent_service> Parent_service;
typedef Registry<Parent_service> Parent_services;
private:
enum { STACK_SIZE = 8*1024 };
/*
* Entry point used for serving the parent interface
*/
Rpc_entrypoint _entrypoint;
Region_map_client _address_space;
Pd_session_client _pd;
Ram_session_client _ram;
Cpu_session_client _cpu;
Child::Initial_thread _initial_thread;
Child _child;
Parent_service _log_service;
Env &_env;
Parent_services &_parent_services;
Signal_context_capability const _fault_handler_sigh;
public:
/**
* Constructor
*/
Test_child(Genode::Dataspace_capability elf_ds,
Genode::Pd_connection &pd,
Genode::Ram_session_capability ram,
Genode::Cpu_session_capability cpu,
Genode::Cap_session *cap)
Test_child_policy(Env &env, Parent_services &parent_services,
Signal_context_capability fault_handler_sigh)
:
_entrypoint(cap, STACK_SIZE, "child", false),
_address_space(pd.address_space()), _pd(pd), _ram(ram), _cpu(cpu),
_initial_thread(_cpu, _pd, "child"),
_child(elf_ds, Dataspace_capability(), _pd, _pd, _ram, _ram,
_cpu, _initial_thread, *env()->rm_session(), _address_space,
_entrypoint, *this),
_log_service("LOG")
{
/* start execution of the new child */
_entrypoint.activate();
}
_env(env),
_parent_services(parent_services),
_fault_handler_sigh(fault_handler_sigh)
{ }
/****************************
** Child-policy interface **
****************************/
const char *name() const { return "rmchild"; }
Name name() const override { return "rmchild"; }
Service *resolve_session_request(const char *service, const char *)
Binary_name binary_name() const override { return "test-rm_fault"; }
Ram_session &ref_ram() override { return _env.ram(); }
Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); }
void init(Ram_session &session, Ram_session_capability cap) override
{
/* forward white-listed session requests to our parent */
return !strcmp(service, "LOG") ? &_log_service : 0;
enum { CHILD_QUOTA = 1*1024*1024 };
session.ref_account(_env.ram_session_cap());
_env.ram().transfer_quota(cap, CHILD_QUOTA);
}
void filter_session_args(const char *service,
void init(Pd_session &session, Pd_session_capability cap) override
{
Region_map_client address_space(session.address_space());
address_space.fault_handler(_fault_handler_sigh);
}
Service &resolve_session_request(Service::Name const &service_name,
Session_state::Args const &args) override
{
Service *service = nullptr;
_parent_services.for_each([&] (Service &s) {
if (!service && service_name == s.name())
service = &s; });
if (!service)
throw Parent::Service_denied();
return *service;
}
void filter_session_args(Service::Name const &,
char *args, size_t args_len)
{
/* define session label for sessions forwarded to our parent */
Arg_string::set_arg_string(args, args_len, "label", "child");
/* prefix session label */
Session_label const orig(label_from_args(args));
Arg_string::set_arg_string(args, args_len, "label",
prefixed_label(name(), orig).string());
}
};
void main_parent(Dataspace_capability elf_ds)
struct Main_parent
{
log("parent role started");
Env &_env;
/* create environment for new child */
static Pd_connection pd;
static Ram_connection ram;
static Cpu_connection cpu;
static Cap_connection cap;
Signal_handler<Main_parent> _fault_handler {
_env.ep(), *this, &Main_parent::_handle_fault };
/* transfer some of our own ram quota to the new child */
enum { CHILD_QUOTA = 1*1024*1024 };
ram.ref_account(env()->ram_session_cap());
env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA);
Heap _heap { _env.ram(), _env.rm() };
static Signal_receiver fault_handler;
/* parent services */
struct Parent_services : Test_child_policy::Parent_services
{
Allocator &alloc;
/* register fault handler at the child's address space */
static Signal_context signal_context;
Region_map_client address_space(pd.address_space());
address_space.fault_handler(fault_handler.manage(&signal_context));
Parent_services(Allocator &alloc) : alloc(alloc)
{
static const char *names[] = {
"RAM", "PD", "CPU", "ROM", "LOG", 0 };
for (unsigned i = 0; names[i]; i++)
new (alloc) Test_child_policy::Parent_service(*this, names[i]);
}
~Parent_services()
{
for_each([&] (Test_child_policy::Parent_service &s) { destroy(alloc, &s); });
}
} _parent_services { _heap };
/* create child */
static Test_child child(elf_ds, pd, ram.cap(), cpu.cap(), &cap);
Test_child_policy _child_policy { _env, _parent_services, _fault_handler };
/* allocate dataspace used for creating shared memory between parent and child */
Dataspace_capability ds = env()->ram_session()->alloc(4096);
volatile int *local_addr = env()->rm_session()->attach(ds);
Child _child { _env.rm(), _env.ep().rpc_ep(), _child_policy };
for (int i = 0; i < 4; i++) {
Region_map_client _address_space { _child.pd().address_space() };
log("wait for region-manager fault");
fault_handler.wait_for_signal();
log("received region-manager fault signal, request fault state");
/* dataspace used for creating shared memory between parent and child */
Attached_ram_dataspace _ds { _env.ram(), _env.rm(), 4096 };
Region_map::State state = address_space.state();
unsigned _fault_cnt = 0;
long volatile &_child_value() { return *_ds.local_addr<long volatile>(); }
void _handle_fault()
{
if (_fault_cnt++ == 4) {
log("--- parent role of region-manager fault test finished ---");
_env.parent().exit(0);
}
log("received region-map fault signal, request fault state");
Region_map::State state = _address_space.state();
char const *state_name =
state.type == Region_map::State::READ_FAULT ? "READ_FAULT" :
@ -182,47 +203,45 @@ void main_parent(Dataspace_capability elf_ds)
/* ignore spuriuous fault signal */
if (state.type == Region_map::State::READY) {
log("ignoring spurious fault signal");
continue;
return;
}
addr_t child_virt_addr = state.addr & ~(4096 - 1);
/* allocate dataspace to resolve the fault */
log("attach dataspace to the child at ", Hex(child_virt_addr));
*local_addr = 0x1234;
_child_value() = 0x1234;
address_space.attach_at(ds, child_virt_addr);
_address_space.attach_at(_ds.cap(), child_virt_addr);
/* wait until our child modifies the dataspace content */
while (*local_addr == 0x1234);
/* poll until our child modifies the dataspace content */
while (_child_value() == 0x1234);
log("child modified dataspace content, new value is ", Hex(*local_addr));
log("child modified dataspace content, new value is ",
Hex(_child_value()));
log("revoke dataspace from child");
address_space.detach((void *)child_virt_addr);
_address_space.detach((void *)child_virt_addr);
}
fault_handler.dissolve(&signal_context);
log("--- parent role of region-manager fault test finished ---");
}
Main_parent(Env &env) : _env(env) { }
};
/*************************
** Common main program **
*************************/
int main(int argc, char **argv)
void Component::construct(Env &env)
{
log("--- region-manager fault test ---");
/* obtain own elf file from rom service */
try {
static Rom_connection rom("test-rm_fault");
main_parent(rom.dataspace());
} catch (Genode::Rom_connection::Rom_connection_failed) {
/*
* Distinguish parent from child by requesting an service that is only
* available to the parent.
*/
Rm_connection rm;
static Main_parent parent(env);
log("-- parent role started --");
}
catch (Parent::Service_denied) {
main_child();
}
return 0;
}

View File

@ -32,6 +32,7 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
private:
Rpc_entrypoint &_thread_ep;
Env &_env;
Allocator &_md_alloc;
Thread_list &_thread_list;
Thread_list_change_handler &_thread_list_change_handler;
@ -41,9 +42,8 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
Cpu_session_component *_create_session(const char *args) override
{
Cpu_session_component *cpu_session_component =
new (md_alloc()) Cpu_session_component(_thread_ep,
_md_alloc,
_thread_list,
new (md_alloc()) Cpu_session_component(_thread_ep, _env,
_md_alloc, _thread_list,
_thread_list_change_handler,
args);
return cpu_session_component;
@ -51,18 +51,20 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
void _upgrade_session(Cpu_session_component *cpu, const char *args) override
{
env()->parent()->upgrade(cpu->parent_cpu_session(), args);
size_t ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0);
cpu->upgrade_ram_quota(ram_quota);
}
public:
Cpu_root(Rpc_entrypoint &session_ep,
Rpc_entrypoint &thread_ep,
Env &env,
Allocator &md_alloc,
Thread_list &thread_list,
Thread_list_change_handler &thread_list_change_handler)
: Root_component<Cpu_session_component>(&session_ep, &md_alloc),
_thread_ep(thread_ep),
_thread_ep(thread_ep), _env(env),
_md_alloc(md_alloc),
_thread_list(thread_list),
_thread_list_change_handler(thread_list_change_handler) { }

View File

@ -89,14 +89,18 @@ Cpu_sampler::Cpu_session_component::trace_control()
}
Cpu_sampler::Cpu_session_component::Cpu_session_component(
Rpc_entrypoint &thread_ep,
Allocator &md_alloc,
Thread_list &thread_list,
Thread_list_change_handler &thread_list_change_handler,
char const *args)
Cpu_sampler::
Cpu_session_component::
Cpu_session_component(Rpc_entrypoint &thread_ep,
Env &env,
Allocator &md_alloc,
Thread_list &thread_list,
Thread_list_change_handler &thread_list_change_handler,
char const *args)
: _thread_ep(thread_ep),
_parent_cpu_session(env()->parent()->session<Cpu_session>(args)),
_env(env),
_id_space_element(_parent_client, _env.id_space()),
_parent_cpu_session(_env.session<Cpu_session>(_id_space_element.id(), args, Affinity())),
_md_alloc(md_alloc),
_thread_list(thread_list),
_thread_list_change_handler(thread_list_change_handler),
@ -105,6 +109,13 @@ Cpu_sampler::Cpu_session_component::Cpu_session_component(
{ }
void Cpu_sampler::Cpu_session_component::upgrade_ram_quota(size_t ram_quota)
{
String<64> const args("ram_quota=", ram_quota);
_env.upgrade(_id_space_element.id(), args.string());
}
Cpu_sampler::Cpu_session_component::~Cpu_session_component()
{
_cleanup_native_cpu();

View File

@ -55,18 +55,18 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object<Cpu_session>
{
private:
Rpc_entrypoint &_thread_ep;
Cpu_session_client _parent_cpu_session;
Allocator &_md_alloc;
Thread_list &_thread_list;
Thread_list_change_handler &_thread_list_change_handler;
Session_label _session_label;
unsigned int _next_thread_id = 0;
Capability<Cpu_session::Native_cpu> _native_cpu_cap;
Capability<Cpu_session::Native_cpu> _setup_native_cpu();
Rpc_entrypoint &_thread_ep;
Env &_env;
Parent::Client _parent_client;
Id_space<Parent::Client>::Element const _id_space_element;
Cpu_session_client _parent_cpu_session;
Allocator &_md_alloc;
Thread_list &_thread_list;
Thread_list_change_handler &_thread_list_change_handler;
Session_label _session_label;
unsigned int _next_thread_id = 0;
Capability<Cpu_session::Native_cpu> _native_cpu_cap;
Capability<Cpu_session::Native_cpu> _setup_native_cpu();
void _cleanup_native_cpu();
public:
@ -79,6 +79,7 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object<Cpu_session>
* Constructor
*/
Cpu_session_component(Rpc_entrypoint &thread_ep,
Env &env,
Allocator &md_alloc,
Thread_list &thread_list,
Thread_list_change_handler &thread_list_change_handler,
@ -89,6 +90,8 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object<Cpu_session>
*/
~Cpu_session_component();
void upgrade_ram_quota(size_t ram_quota);
/***************************
** CPU session interface **

View File

@ -172,7 +172,7 @@ struct Cpu_sampler::Main : Thread_list_change_handler
Main(Genode::Env &env)
: env(env),
alloc(env.ram(), env.rm()),
cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), alloc, thread_list, *this),
cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), env, alloc, thread_list, *this),
config(env, "config")
{
/*

View File

@ -80,7 +80,7 @@ compare_output_to {
[init -> test-ldso] Catch exceptions in program
[init -> test-ldso] ---------------------------
[init -> test-ldso] exception in remote procedure call:
[init -> test-ldso] Error: ROM-session creation failed (ram_quota=4K, label="unknown_file")
[init -> test-ldso] Error: ROM-session creation failed (ram_quota=4096, label="unknown_file")
[init -> test-ldso] Error: Could not open ROM session for "unknown_file"
[init -> test-ldso] caught
[init -> test-ldso] exception in program: caught

View File

@ -21,6 +21,7 @@
#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 */
@ -33,6 +34,16 @@ namespace Init {
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;
}
@ -47,8 +58,8 @@ namespace Init {
static void warn_insuff_quota(Genode::size_t const avail)
{
if (!config_verbose) { return; }
Genode::log("Warning: Specified quota exceeds available quota.");
Genode::log(" Proceeding with a quota of ", avail, ".");
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)
@ -68,7 +79,7 @@ namespace Init {
if (priority && (priority >= prio_levels)) {
long new_prio = prio_levels ? prio_levels-1 : 0;
char name[Genode::Service::MAX_NAME_LEN];
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);
@ -124,7 +135,6 @@ namespace Init {
/**
* 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)
{
@ -164,21 +174,20 @@ namespace Init {
* \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,
char const *child_name,
char const *service_name)
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));
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, Genode::label_from_args(args).string()));
child_name.string(), Genode::label_from_args(args).string()));
return !Genode::Xml_node_label_score(service_node, session_label).conflict();
}
@ -188,8 +197,8 @@ namespace Init {
* Check if arguments satisfy the condition specified for the route
*/
inline bool service_node_args_condition_satisfied(Genode::Xml_node service_node,
const char *args,
char const *child_name)
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");
@ -200,7 +209,7 @@ namespace Init {
if_arg.attribute("value").value(value, sizeof(value));
char arg_value[VALUE_MAX_LEN];
Genode::Arg_string::find_arg(args, key).string(arg_value, sizeof(arg_value), "");
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".
@ -214,7 +223,7 @@ namespace Init {
* the prefix information is redundant.
*/
if (Genode::strcmp("label", key) == 0)
return Genode::strcmp(value, skip_label_prefix(child_name, arg_value)) == 0;
return Genode::strcmp(value, skip_label_prefix(child_name.string(), arg_value)) == 0;
return Genode::strcmp(value, arg_value) == 0;
} catch (...) { }
@ -222,109 +231,80 @@ namespace Init {
/* 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
*
* For init, we introduce this 'Service' variant that distinguishes two
* phases, declared and announced. A 'Routed_service' object is created
* when a '<provides>' declaration is found in init's configuration.
* At that time, however, no children including the server do yet exist.
* If, at this stage, a client tries to open a session to this service,
* the client get enqueued in a list of applicants and blocked. When
* the server officially announces its service and passes over the root
* capability, the 'Routed_service' enters the announced stage and any
* applicants get unblocked.
*/
class Init::Routed_service : public Genode::Service
class Init::Routed_service : public Genode::Child_service
{
public:
typedef Genode::Child_policy::Name Child_name;
private:
Genode::Root_capability _root;
bool _announced;
Genode::Server *_server;
Child_name _child_name;
struct Applicant : public Genode::Cancelable_lock,
public Genode::List<Applicant>::Element
{
Applicant() : Cancelable_lock(Genode::Lock::LOCKED) { }
};
Genode::Lock _applicants_lock;
Genode::List<Applicant> _applicants;
Genode::Registry<Routed_service>::Element _registry_element;
public:
/**
* Constructor
*
* \param name name of service
* \param server server providing the service
* \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(const char *name,
Genode::Server *server)
: Service(name), _announced(false), _server(server) { }
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)
{ }
Genode::Server *server() const { return _server; }
void announce(Genode::Root_capability root)
{
Genode::Lock::Guard guard(_applicants_lock);
_root = root;
_announced = true;
/* wake up aspiring clients */
for (Applicant *a; (a = _applicants.first()); ) {
_applicants.remove(a);
a->unlock();
}
}
Genode::Session_capability session(const char *args,
Genode::Affinity const &affinity)
{
/*
* This method is called from the context of the client's
* activation thread. If the service is not yet announced,
* we let the client block.
*/
_applicants_lock.lock();
if (!_announced) {
Applicant myself;
_applicants.insert(&myself);
_applicants_lock.unlock();
myself.lock();
} else
_applicants_lock.unlock();
Genode::Session_capability cap;
try { cap = Genode::Root_client(_root).session(args, affinity); }
catch (Genode::Root::Invalid_args) { throw Invalid_args(); }
catch (Genode::Root::Unavailable) { throw Unavailable(); }
catch (Genode::Root::Quota_exceeded) { throw Quota_exceeded(); }
catch (Genode::Ipc_error) { throw Unavailable(); }
if (!cap.valid())
throw Unavailable();
return cap;
}
void upgrade(Genode::Session_capability sc, const char *args)
{
try { Genode::Root_client(_root).upgrade(sc, args); }
catch (Genode::Root::Invalid_args) { throw Invalid_args(); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
void close(Genode::Session_capability sc)
{
try { Genode::Root_client(_root).close(sc); }
catch (Genode::Ipc_error) { throw Genode::Blocking_canceled(); }
}
Child_name const &child_name() const { return _child_name; }
};
@ -335,6 +315,8 @@ struct Init::Name_registry
{
virtual ~Name_registry() { }
typedef Genode::Child_policy::Name Name;
/**
* Check if specified name is unique
*
@ -343,13 +325,16 @@ struct Init::Name_registry
virtual bool unique(const char *name) const = 0;
/**
* Find server with specified name
* Return child name for a given alias name
*
* If there is no alias, the function returns the original name.
*/
virtual Genode::Server *lookup_server(const char *name) const = 0;
virtual Name deref_alias(Name const &) = 0;
};
class Init::Child : Genode::Child_policy
class Init::Child : Genode::Child_policy,
Genode::Child_service::Wakeup
{
public:
@ -362,6 +347,8 @@ class Init::Child : Genode::Child_policy
friend class Child_registry;
Genode::Env &_env;
Genode::List_element<Child> _list_element;
Genode::Xml_node _start_node;
@ -463,11 +450,6 @@ class Init::Child : Genode::Child_policy
Genode::size_t ram_quota;
Genode::size_t cpu_quota_pc;
bool constrain_phys;
Genode::Pd_connection pd;
Genode::Ram_connection ram;
Genode::Cpu_connection cpu;
inline void transfer_cpu_quota();
Resources(Genode::Xml_node start_node, const char *label,
long prio_levels,
@ -477,31 +459,13 @@ class Init::Child : Genode::Child_policy
prio_levels_log2(Genode::log2(prio_levels)),
priority(read_priority(start_node, prio_levels)),
affinity(affinity_space,
read_affinity_location(affinity_space, start_node)),
pd(label),
ram(label),
cpu(label,
priority*(Genode::Cpu_session::PRIORITY_LIMIT >> prio_levels_log2),
affinity)
read_affinity_location(affinity_space, start_node))
{
/* deduce session costs from usable ram quota */
Genode::size_t session_donations = Genode::Pd_connection::RAM_QUOTA +
Genode::Cpu_connection::RAM_QUOTA +
Genode::Ram_connection::RAM_QUOTA;
if (ram_quota > session_donations)
ram_quota -= session_donations;
else ram_quota = 0;
ram.ref_account(Genode::env()->ram_session_cap());
Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota);
transfer_cpu_quota();
ram_quota = Genode::Child::effective_ram_quota(ram_quota);
}
} _resources;
Genode::Child::Initial_thread _initial_thread { _resources.cpu, _resources.pd,
_name.unique };
/*
* Entry point used for serving the parent interface and the
* locally provided ROM sessions for the 'config' and 'binary'
@ -510,26 +474,21 @@ class Init::Child : Genode::Child_policy
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
Genode::Rpc_entrypoint _entrypoint;
/**
* ELF binary
*/
Genode::Rom_connection _binary_rom;
Genode::Dataspace_capability _binary_rom_ds;
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;
/**
* Each child of init can act as a server
*/
Genode::Server _server;
Genode::Region_map_client _address_space { _resources.pd.address_space() };
Genode::Child _child;
Genode::Service_registry &_parent_services;
Genode::Service_registry &_child_services;
Genode::Session_requester _session_requester;
/**
* Policy helpers
@ -537,22 +496,39 @@ class Init::Child : Genode::Child_policy
Init::Child_policy_enforce_labeling _labeling_policy;
Init::Child_policy_handle_cpu_priorities _priority_policy;
Init::Child_policy_provide_rom_file _config_policy;
Init::Child_policy_provide_rom_file _binary_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:
Child(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::Service_registry &parent_services,
Genode::Service_registry &child_services,
Genode::Cap_session &cap_session,
Genode::Dataspace_capability ldso_ds)
/**
* 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),
@ -560,23 +536,15 @@ class Init::Child : Genode::Child_policy
_name(start_node, name_registry),
_resources(start_node, _name.unique, prio_levels,
affinity_space),
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false,
_entrypoint(&_env.pd(), ENTRYPOINT_STACK_SIZE, _name.unique, false,
_resources.affinity.location()),
_binary_rom(_name.file),
_binary_rom_ds(_binary_rom.dataspace()),
_config(_resources.ram.cap(), start_node),
_server(_resources.ram.cap()),
_child(_binary_rom_ds, ldso_ds,
_resources.pd, _resources.pd,
_resources.ram, _resources.ram,
_resources.cpu, _initial_thread,
*Genode::env()->rm_session(), _address_space, _entrypoint, *this),
_parent_services(parent_services),
_child_services(child_services),
_config(_env.ram(), _env.rm(), start_node),
_session_requester(_entrypoint, _env.ram(), _env.rm()),
_labeling_policy(_name.unique),
_priority_policy(_resources.prio_levels_log2, _resources.priority),
_config_policy("config", _config.dataspace(), &_entrypoint),
_binary_policy("binary", _binary_rom_ds, &_entrypoint),
_configfile_policy("config", _config.filename()),
_ram_session_policy(_resources.constrain_phys)
{
@ -587,10 +555,10 @@ class Init::Child : Genode::Child_policy
"\"", Genode::Cstring(_name.unique), "\"");
if (config_verbose) {
Genode::log("child \"", Genode::Cstring(_name.unique), "\"");
Genode::log(" RAM quota: ", _resources.ram_quota);
Genode::log(" ELF binary: ", Cstring(_name.file));
Genode::log(" priority: ", _resources.priority);
log("child \"", Genode::Cstring(_name.unique), "\"");
log(" RAM quota: ", _resources.ram_quota);
log(" ELF binary: ", Cstring(_name.file));
log(" priority: ", _resources.priority);
}
/*
@ -601,32 +569,33 @@ class Init::Child : Genode::Child_policy
for (; ; service_node = service_node.next("service")) {
char name[Genode::Service::MAX_NAME_LEN];
char name[Genode::Service::Name::capacity()];
service_node.attribute("name").value(name, sizeof(name));
if (config_verbose)
Genode::log(" provides service ", Genode::Cstring(name));
log(" provides service ", Genode::Cstring(name));
child_services.insert(new (_child.heap())
Routed_service(name, &_server));
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() {
Genode::Service *s;
while ((s = _child_services.find_by_server(&_server))) {
_child_services.remove(s);
}
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(const char *n) const { return !Genode::strcmp(name(), n); }
Genode::Server *server() { return &_server; }
bool has_name(Child_policy::Name const &str) const { return str == name(); }
/**
* Start execution of child
@ -638,22 +607,70 @@ class Init::Child : Genode::Child_policy
** Child-policy interface **
****************************/
const char *name() const { return _name.unique; }
Child_policy::Name name() const override { return _name.unique; }
Genode::Service *resolve_session_request(const char *service_name,
const char *args)
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
{
Genode::Service *service = 0;
using Genode::error;
using Genode::warning;
session.ref_account(Genode::env()->ram_session_cap());
Genode::env()->ram_session()->transfer_quota(cap, _resources.ram_quota);
}
/* check for config file request */
if ((service = _config_policy.resolve_session_request(service_name, args)))
return service;
void init(Genode::Cpu_session &session, Genode::Cpu_session_capability cap) override
{
using Genode::Cpu_session;
using Genode::size_t;
/* check for binary file request */
if ((service = _binary_policy.resolve_session_request(service_name, args)))
return service;
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;
@ -666,7 +683,7 @@ class Init::Child : Genode::Child_policy
bool service_wildcard = service_node.has_type("any-service");
if (!service_node_matches(service_node, args, name(), service_name))
if (!service_node_matches(service_node, args.string(), name(), service_name))
continue;
if (!service_node_args_condition_satisfied(service_node, args, name()))
@ -676,54 +693,51 @@ class Init::Child : Genode::Child_policy
for (; ; target = target.next()) {
if (target.has_type("parent")) {
service = _parent_services.find(service_name);
if (service)
return service;
if ((service = find_service(_parent_services, service_name)))
return *service;
if (!service_wildcard) {
warning(name(), ": service lookup for "
"\"", service_name, "\" at parent failed");
return 0;
throw Genode::Parent::Service_denied();
}
}
if (target.has_type("child")) {
char server_name[Name::MAX_NAME_LEN];
server_name[0] = 0;
target.attribute("name").value(server_name, sizeof(server_name));
Genode::Server *server = _name_registry.lookup_server(server_name);
if (!server) {
warning(name(), ": invalid route to non-existing "
"server \"", Genode::Cstring(server_name), "\"");
return 0;
}
typedef Name_registry::Name Name;
Name server_name = target.attribute_value("name", Name());
server_name = _name_registry.deref_alias(server_name);
service = _child_services.find(service_name, server);
_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;
return *service;
if (!service_wildcard) {
Genode::warning(name(), ": lookup to child "
"service \"", service_name, "\" failed");
return 0;
"server \"", server_name, "\" failed");
throw Genode::Parent::Service_denied();
}
}
if (target.has_type("any-child")) {
if (_child_services.is_ambiguous(service_name)) {
if (is_ambiguous(_child_services, service_name)) {
error(name(), ": ambiguous routes to "
"service \"", service_name, "\"");
return 0;
throw Genode::Parent::Service_denied();
}
service = _child_services.find(service_name);
if (service)
return service;
if ((service = find_service(_child_services, service_name)))
return *service;
if (!service_wildcard) {
warning(name(), ": lookup for service "
"\"", service_name, "\" failed");
return 0;
throw Genode::Parent::Service_denied();
}
}
@ -734,19 +748,23 @@ class Init::Child : Genode::Child_policy
} catch (...) {
warning(name(), ": no route to service \"", service_name, "\"");
}
return service;
if (!service)
throw Genode::Parent::Service_denied();
return *service;
}
void filter_session_args(const char *service,
char *args, Genode::size_t args_len)
void filter_session_args(Service::Name const &service,
char *args, Genode::size_t args_len) override
{
_labeling_policy. filter_session_args(service, args, args_len);
_priority_policy. filter_session_args(service, args, args_len);
_configfile_policy.filter_session_args(service, args, args_len);
_ram_session_policy.filter_session_args(service, args, args_len);
_labeling_policy. filter_session_args(service.string(), args, args_len);
_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)
Genode::Affinity filter_session_affinity(Genode::Affinity const &session_affinity) override
{
using namespace Genode;
@ -772,28 +790,24 @@ class Init::Child : Genode::Child_policy
return Affinity(space, location);
}
bool announce_service(const char *service_name,
Genode::Root_capability root,
Genode::Allocator *alloc,
Genode::Server *server)
void announce_service(Genode::Service::Name const &service_name) override
{
if (config_verbose)
Genode::log("child \"", name(), "\" announces service \"", service_name, "\"");
log("child \"", name(), "\" announces service \"", service_name, "\"");
Genode::Service *s = _child_services.find(service_name, &_server);
Routed_service *rs = dynamic_cast<Routed_service *>(s);
if (!s || !rs) {
Genode::error(name(), ": illegal announcement of service \"", service_name, "\"");
return false;
}
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; });
rs->announce(root);
return true;
if (!found)
Genode::error(name(), ": illegal announcement of "
"service \"", service_name, "\"");
}
void resource_request(Genode::Parent::Resource_args const &args)
void resource_request(Genode::Parent::Resource_args const &args) override
{
Genode::log("child \"", name(), "\" requests resources: ", args.string());
log("child \"", name(), "\" requests resources: ", args.string());
Genode::size_t const requested_ram_quota =
Genode::Arg_string::find_arg(args.string(), "ram_quota")
@ -812,7 +826,7 @@ class Init::Child : Genode::Child_policy
* when calling 'transfer_quota'.
*/
Genode::env()->ram_session()->transfer_quota(_resources.ram.cap(),
Genode::env()->ram_session()->transfer_quota(_child.ram_session_cap(),
requested_ram_quota);
/* wake up child that was starved for resources */
@ -838,23 +852,4 @@ class Init::Child : Genode::Child_policy
};
void Init::Child::Resources::transfer_cpu_quota()
{
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(cpu_quota_pc, 100);
size_t need_adj;
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;
}
cpu.ref_account(Genode::env()->cpu_session_cap());
Genode::env()->cpu_session()->transfer_quota(cpu.cap(), need_adj);
}
#endif /* _INCLUDE__INIT__CHILD_H_ */

View File

@ -14,11 +14,9 @@
#ifndef _INCLUDE__INIT__CHILD_CONFIG_H_
#define _INCLUDE__INIT__CHILD_CONFIG_H_
#include <base/env.h>
#include <base/printf.h>
#include <util/xml_node.h>
#include <rom_session/connection.h>
#include <ram_session/client.h>
#include <base/attached_dataspace.h>
#include <ram_session/ram_session.h>
namespace Init { class Child_config; }
@ -27,11 +25,62 @@ class Init::Child_config
{
private:
enum { CONFIGFILE_NAME_LEN = 64 };
char _filename[CONFIGFILE_NAME_LEN];
Genode::Ram_session &_ram;
Genode::Ram_session_capability _ram_session_cap;
Genode::Ram_dataspace_capability _config_ram_ds;
typedef Genode::String<64> Rom_name;
Rom_name const _rom_name;
Genode::Ram_dataspace_capability const _ram_ds;
Rom_name _rom_name_from_start_node(Genode::Xml_node start)
{
if (!start.has_sub_node("configfile"))
return Rom_name();
return start.sub_node("configfile").attribute_value("name", Rom_name());
}
/**
* Buffer '<config>' sub node in a dedicated RAM dataspace
*
* \throw Ram_session::Alloc_failed
* \throw Rm_session::Attach_failed
*/
Genode::Ram_dataspace_capability
_ram_ds_from_start_node(Genode::Xml_node start,
Genode::Ram_session &ram, Genode::Region_map &rm)
{
/*
* If the start node contains a 'config' entry, we copy this entry
* into a fresh dataspace to be provided to our child.
*/
Genode::Xml_node const config = start.has_sub_node("config")
? start.sub_node("config")
: Genode::Xml_node("<config/>");
Genode::Ram_dataspace_capability ram_ds;
try {
/*
* Allocate RAM dataspace that is big enough to hold the
* configuration and the null termination.
*/
ram_ds = ram.alloc(config.size() + 1);
/*
* Make dataspace locally accessible, copy configuration into the
* dataspace, and append a string-terminating zero.
*/
Genode::Attached_dataspace attached(rm, ram_ds);
Genode::memcpy(attached.local_addr<char>(),
config.addr(), config.size());
attached.local_addr<char>()[config.size()] = 0;
return ram_ds;
}
catch (Genode::Region_map::Attach_failed) { ram.free(ram_ds); throw; }
}
public:
@ -40,84 +89,31 @@ class Init::Child_config
*
* The provided RAM session is used to obtain a dataspace for
* holding the copy of the child's configuration data unless the
* configuration is supplied via a config file. Normally, the
* child's RAM session should be used to account the consumed RAM
* quota to the child.
* configuration is supplied via a config ROM module.
*
* \throw Ram_session::Alloc_failed failed to allocate the backing
* store for holding config data
*
* \throw Region_map::Attach_failed failed to temporarily attach the
* config dataspace to the local
* address space
*
* If the start node contains a 'filename' entry, we only keep the
* information about the ROM module name.
*/
Child_config(Genode::Ram_session_capability ram_session,
Genode::Xml_node start_node)
: _ram_session_cap(ram_session)
{
using namespace Genode;
/*
* If the start node contains a 'filename' entry, we only keep
* the information about the file name.
*/
_filename[0] = 0;
try {
Xml_node configfile_node = start_node.sub_node("configfile");
configfile_node.attribute("name")
.value(_filename, sizeof(_filename));
return;
} catch (...) { }
/*
* If the start node contains a 'config' entry, we copy this
* entry into a fresh dataspace to be provided to our child.
*/
Ram_session_client rsc(_ram_session_cap);
try {
Xml_node config_node = start_node.sub_node("config");
const char *config = config_node.addr();
Genode::size_t config_size = config_node.size();
if (!config || !config_size) return;
/*
* Allocate RAM dataspace that is big enough to
* hold the configuration and the null termination.
*/
_config_ram_ds = rsc.alloc(config_size + 1);
/*
* Make dataspace locally accessible, copy
* configuration into the dataspace, and append
* a string-terminating zero.
*/
void *addr = env()->rm_session()->attach(_config_ram_ds);
Genode::memcpy(addr, config, config_size);
static_cast<char *>(addr)[config_size] = 0;
env()->rm_session()->detach(addr);
} catch (Region_map::Attach_failed) {
rsc.free(_config_ram_ds);
return;
} catch (Ram_session::Alloc_failed) {
return;
} catch (Xml_node::Nonexistent_sub_node) { }
}
Child_config(Genode::Ram_session &ram, Genode::Region_map &local_rm,
Genode::Xml_node start)
:
_ram(ram),
_rom_name(_rom_name_from_start_node(start)),
_ram_ds(_rom_name.valid() ? Genode::Ram_dataspace_capability()
: _ram_ds_from_start_node(start, ram, local_rm))
{ }
/**
* Destructor
*/
~Child_config()
{
using namespace Genode;
/*
* The configuration data is either provided as a ROM session
* (holding a complete configfile) or as a RAM dataspace
* holding a copy of the start node's config entry. In the
* latter case, the child's configuration resides in a
* shadow copy kept in '_config_ram_ds'.
*/
if (_config_ram_ds.valid())
Ram_session_client(_ram_session_cap).free(_config_ram_ds);
}
~Child_config() { if (_ram_ds.valid()) _ram.free(_ram_ds); }
/**
* Return file name if configuration comes from a file
@ -125,7 +121,7 @@ class Init::Child_config
* If the configuration is provided inline, the method returns 0.
*/
char const *filename() const {
return _filename[0] != 0 ? _filename : 0; }
return _rom_name.valid() ? _rom_name.string() : nullptr; }
/**
* Request dataspace holding the start node's configuration data
@ -134,7 +130,7 @@ class Init::Child_config
* inline configuration (if 'filename()' returns 0).
*/
Genode::Dataspace_capability dataspace() {
return Genode::Dataspace_capability(_config_ram_ds); }
return Genode::Dataspace_capability(_ram_ds); }
};
#endif /* _INCLUDE__INIT__CHILD_CONFIG_H_ */

View File

@ -19,9 +19,11 @@
#include <base/child.h>
#include <base/rpc_server.h>
#include <base/session_label.h>
#include <base/attached_ram_dataspace.h>
#include <util/arg_string.h>
#include <rom_session/connection.h>
#include <base/session_label.h>
#include <os/dynamic_rom_session.h>
namespace Init {
@ -29,8 +31,17 @@ namespace Init {
class Child_policy_enforce_labeling;
class Child_policy_handle_cpu_priorities;
class Child_policy_provide_rom_file;
class Child_policy_provide_dynamic_rom;
class Child_policy_redirect_rom_file;
class Traditional_child_policy;
using Genode::Attached_ram_dataspace;
using Genode::size_t;
using Genode::Session_label;
using Genode::error;
using Genode::log;
using Genode::Service;
using Genode::Session_state;
}
@ -154,80 +165,49 @@ class Init::Child_policy_provide_rom_file
struct Local_rom_session_component : Genode::Rpc_object<Genode::Rom_session>
{
Genode::Rpc_entrypoint &ep;
Genode::Dataspace_capability ds_cap;
/**
* Constructor
*/
Local_rom_session_component(Genode::Dataspace_capability ds)
: ds_cap(ds) { }
Local_rom_session_component(Genode::Rpc_entrypoint &ep,
Genode::Dataspace_capability ds)
: ep(ep), ds_cap(ds) { ep.manage(this); }
~Local_rom_session_component() { ep.dissolve(this); }
/***************************
** ROM session interface **
***************************/
Genode::Rom_dataspace_capability dataspace() {
Genode::Rom_dataspace_capability dataspace() override {
return Genode::static_cap_cast<Genode::Rom_dataspace>(ds_cap); }
void sigh(Genode::Signal_context_capability) { }
void sigh(Genode::Signal_context_capability) override { }
} _local_rom_session;
} _session;
Genode::Rpc_entrypoint *_ep;
Genode::Rom_session_capability _rom_session_cap;
Genode::Session_label const _module_name;
Genode::Session_label _module_name;
typedef Genode::Local_service<Local_rom_session_component> Service;
struct Local_rom_service : public Genode::Service
{
Genode::Rom_session_capability _rom_cap;
bool _valid;
/**
* Constructor
*
* \param rom_cap capability to return on session requests
* \param valid true if local rom service is backed by a
* valid dataspace
*/
Local_rom_service(Genode::Rom_session_capability rom_cap, bool valid)
: Genode::Service("ROM"), _rom_cap(rom_cap), _valid(valid) { }
Genode::Session_capability session(char const * /*args*/,
Genode::Affinity const &)
{
if (!_valid)
throw Invalid_args();
return _rom_cap;
}
void upgrade(Genode::Session_capability, const char * /*args*/) { }
void close(Genode::Session_capability) { }
} _local_rom_service;
Service::Single_session_factory _session_factory { _session };
Service _service { _session_factory };
public:
/**
* Constructor
*/
Child_policy_provide_rom_file(const char *module_name,
Child_policy_provide_rom_file(Genode::Session_label const &module_name,
Genode::Dataspace_capability ds_cap,
Genode::Rpc_entrypoint *ep)
:
_local_rom_session(ds_cap), _ep(ep),
_rom_session_cap(_ep->manage(&_local_rom_session)),
_module_name(module_name),
_local_rom_service(_rom_session_cap, ds_cap.valid())
_session(*ep, ds_cap), _module_name(module_name)
{ }
/**
* Destructor
*/
~Child_policy_provide_rom_file() { _ep->dissolve(&_local_rom_session); }
Genode::Service *resolve_session_request(const char *service_name,
const char *args)
{
@ -238,7 +218,7 @@ class Init::Child_policy_provide_rom_file
{
Genode::Session_label const label = Genode::label_from_args(args);
return label.last_element() == _module_name
? &_local_rom_service : nullptr;
? &_service : nullptr;
}
}
};

View File

@ -2,6 +2,9 @@
* \brief Child policy helper for supplying dynamic ROM modules
* \author Norman Feske
* \date 2012-04-04
*
* \deprecated use 'Local_service::Single_session_service' combined with
* 'Dynamic_rom_session' instead
*/
/*
@ -73,7 +76,7 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object<Rom_session>,
Rpc_entrypoint &ep,
Ram_session *ram)
:
Service("ROM"),
Service("ROM", Ram_session_capability()),
_ram(ram),
_fg(0, 0), _bg(0, 0),
_bg_has_pending_data(false),
@ -147,11 +150,31 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object<Rom_session>,
** Service interface **
***********************/
Session_capability session(const char *, Affinity const &) {
return _rom_session_cap; }
void initiate_request(Session_state &session) override
{
switch (session.phase) {
void upgrade(Session_capability, const char *) { }
void close(Session_capability) { }
case Session_state::CREATE_REQUESTED:
session.cap = _rom_session_cap;
session.phase = Session_state::AVAILABLE;
break;
case Session_state::UPGRADE_REQUESTED:
session.phase = Session_state::CAP_HANDED_OUT;
session.confirm_ram_upgrade();
break;
case Session_state::CLOSE_REQUESTED:
session.phase = Session_state::CLOSED;
break;
case Session_state::INVALID_ARGS:
case Session_state::AVAILABLE:
case Session_state::CAP_HANDED_OUT:
case Session_state::CLOSED:
break;
}
}
/**********************

View File

@ -26,7 +26,7 @@ namespace Genode {
/**
* Return singleton instance of config
*/
Config *config();
Volatile_object<Config> &config();
}

View File

@ -0,0 +1,195 @@
/*
* \brief ROM session implementation for serving dynamic content
* \author Norman Feske
* \date 2016-11-03
*/
/*
* Copyright (C) 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__OS__DYNAMIC_ROM_SESSION_H_
#define _INCLUDE__OS__DYNAMIC_ROM_SESSION_H_
#include <util/volatile_object.h>
#include <base/rpc_server.h>
#include <base/session_label.h>
#include <base/attached_ram_dataspace.h>
#include <rom_session/rom_session.h>
namespace Genode { class Dynamic_rom_session; }
class Genode::Dynamic_rom_session : public Rpc_object<Rom_session>
{
public:
struct Content_producer
{
class Buffer_capacity_exceeded : Exception { };
/**
* Write content into the specified buffer
*
* \throw Buffer_capacity_exceeded
*/
virtual void produce_content(char *dst, size_t dst_len) = 0;
};
private:
/*
* Synchronize calls of 'trigger_update' (called locally) with the
* 'Rom_session' methods (invoked via RPC).
*/
Lock _lock;
Rpc_entrypoint &_ep;
Ram_session &_ram;
Region_map &_rm;
Signal_context_capability _sigh;
Content_producer &_content_producer;
/*
* Keep track of the last version handed out to the client (at the
* time of the last 'Rom_session::update' RPC call, and the newest
* version that is available. If the client version is out of date
* when the client registers a signal handler, submit a signal
* immediately.
*/
unsigned _current_version = 0, _client_version = 0;
size_t _ds_size = 4096;
Lazy_volatile_object<Attached_ram_dataspace> _ds;
void _notify_client()
{
if (_sigh.valid() && (_current_version != _client_version))
Signal_transmitter(_sigh).submit();
}
bool _unsynchronized_update()
{
bool ds_reallocated = false;
for (;;) {
try {
if (!_ds.constructed()) {
_ds.construct(_ram, _rm, _ds_size);
ds_reallocated = true;
}
}
catch (Ram_session::Quota_exceeded) {
error("ouf of child quota while delivering dynamic ROM");
/*
* XXX We may try to generate a resource request on
* behalf of the child.
*/
/*
* Don't let the child try again to obtain a dataspace
* by pretending that the ROM module is up-to-date.
*/
return true;
}
catch (Ram_session::Out_of_metadata) {
error("ouf of RAM session quota while delivering dynamic ROM");
return true;
}
try {
_content_producer.produce_content(_ds->local_addr<char>(),
_ds->size());
_client_version = _current_version;
return !ds_reallocated;
}
catch (Content_producer::Buffer_capacity_exceeded) {
/* force the re-allocation of a larger buffer */
_ds.destruct();
_ds_size *= 2;
}
}
}
public:
/**
* Constructor
*
* \param ep entrypoint serving the ROM session
* \param ram RAM session used to allocate the backing
* store for the dataspace handed out to the
* client
* \param rm local region map ('env.rm()') required to
* make the dataspace locally visible to
* populate its content
* \param content_producer callback to generate the content of the
* ROM dataspace
*
* The 'Dynamic_rom_session' associates/disassociates itself with 'ep'.
*/
Dynamic_rom_session(Rpc_entrypoint &ep,
Ram_session &ram,
Region_map &rm,
Content_producer &content_producer)
:
_ep(ep), _ram(ram), _rm(rm), _content_producer(content_producer)
{
_ep.manage(this);
}
~Dynamic_rom_session() { _ep.dissolve(this); }
/*
* Called locally, potentially from another thread than 'ep'
*/
void trigger_update()
{
Lock::Guard guard(_lock);
_current_version++;
_notify_client();
}
/***************************
** ROM session interface **
***************************/
Rom_dataspace_capability dataspace() override
{
Lock::Guard guard(_lock);
if (!_ds.constructed())
_unsynchronized_update();
Dataspace_capability ds_cap = _ds->cap();
return static_cap_cast<Rom_dataspace>(ds_cap);
}
bool update() override
{
Lock::Guard guard(_lock);
return _unsynchronized_update();
}
void sigh(Signal_context_capability sigh) override
{
Lock::Guard guard(_lock);
_sigh = sigh;
_notify_client();
}
};
#endif /* _INCLUDE__OS__DYNAMIC_ROM_SESSION_H_ */

View File

@ -0,0 +1,84 @@
/*
* \brief Utility for providing "session_requests" ROM to a child service
* \author Norman Feske
* \date 2016-11-10
*/
/*
* Copyright (C) 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__OS__SESSION_REQUESTER_H_
#define _INCLUDE__OS__SESSION_REQUESTER_H_
#include <parent/parent.h>
#include <os/dynamic_rom_session.h>
namespace Genode { class Session_requester; }
class Genode::Session_requester
{
private:
Id_space<Parent::Server> _id_space;
struct Content_producer : Dynamic_rom_session::Content_producer
{
Id_space<Parent::Server> &_id_space;
Content_producer(Id_space<Parent::Server> &id_space)
: _id_space(id_space) { }
void produce_content(char *dst, Genode::size_t dst_len) override
{
Xml_generator xml(dst, dst_len, "session_requests", [&] () {
_id_space.for_each<Session_state const>([&] (Session_state const &s) {
s.generate_session_request(xml); }); });
}
} _content_producer { _id_space };
typedef Local_service<Dynamic_rom_session> Service;
Dynamic_rom_session _session;
Service::Single_session_factory _factory { _session };
Service _service { _factory };
public:
typedef String<32> Rom_name;
static Rom_name rom_name() { return "session_requests"; }
/**
* Constructor
*
* \param ep entrypoint serving the local ROM service
* \param ram backing store for the ROM dataspace
* \param rm local address space, needed to populate the dataspace
*/
Session_requester(Rpc_entrypoint &ep, Ram_session &ram, Region_map &rm)
:
_session(ep, ram, rm, _content_producer)
{ }
/**
* Inform the child about a new version of the "session_requests" ROM
*/
void trigger_update() { _session.trigger_update(); }
/**
* ID space for sessios requests supplied to the child
*/
Id_space<Parent::Server> &id_space() { return _id_space; }
/**
* ROM service providing a single "session_requests" session
*/
Service &service() { return _service; }
};
#endif /* _INCLUDE__OS__SESSION_REQUESTER_H_ */

View File

@ -17,31 +17,41 @@
/* Genode includes */
#include <base/rpc_server.h>
#include <base/local_connection.h>
#include <base/child.h>
#include <init/child_policy.h>
#include <ram_session/connection.h>
#include <cpu_session/connection.h>
#include <rm_session/connection.h>
#include <pd_session/connection.h>
#include <os/child_policy_dynamic_rom.h>
#include <os/session_requester.h>
namespace Genode {
class Slave_policy;
class Slave;
}
namespace Genode { struct Slave; }
/**
* Slave-policy class
*
* This class provides a convenience policy for single-service slaves using a
* white list of parent services.
*/
class Genode::Slave_policy : public Genode::Child_policy
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;
protected:
typedef Registered<Genode::Parent_service> Parent_service;
typedef Registry<Parent_service> Parent_services;
/**
* Return white list of services the slave is permitted to use
*
@ -51,58 +61,66 @@ class Genode::Slave_policy : public Genode::Child_policy
private:
char const *_label;
Genode::Service_registry _parent_services;
Genode::Rpc_entrypoint &_entrypoint;
Genode::Rom_connection _binary_rom;
Label const _label;
Binary_name const _binary_name;
Ram_session_client _ram;
Genode::Parent_service _binary_service;
size_t _ram_quota;
Parent_services _parent_services;
Rpc_entrypoint &_ep;
Init::Child_policy_enforce_labeling _labeling_policy;
Init::Child_policy_provide_rom_file _binary_policy;
Genode::Child_policy_dynamic_rom_file _config_policy;
Child_policy_dynamic_rom_file _config_policy;
bool _service_permitted(const char *service_name)
bool _service_permitted(Service::Name const &service_name) const
{
for (const char **s = _permitted_services(); *s; ++s)
if (!Genode::strcmp(service_name, *s))
if (service_name == *s)
return true;
return false;
}
Session_requester _session_requester;
public:
class Connection;
/**
* Slave-policy constructor
*
* \param label name of the program to start
* \param entrypoint entrypoint used to provide local services
* such as the config ROM service
* \param ram RAM session used for buffering config data
* \param ep entrypoint used to provide local services
* such as the config ROM service
* \param local_rm local address space, needed to populate dataspaces
* provided to the child (config, session_requests)
*
* If 'ram' is set to 0, no configuration can be supplied to the
* slave.
* \throw Ram_session::Alloc_failed by 'Child_policy_dynamic_rom_file'
* \throw Rm_session::Attach_failed by 'Child_policy_dynamic_rom_file'
*/
Slave_policy(const char *label,
Genode::Rpc_entrypoint &entrypoint,
Genode::Ram_session *ram = 0,
const char *binary = nullptr)
Policy(Label const &label,
Name const &binary_name,
Rpc_entrypoint &ep,
Region_map &rm,
Ram_session_capability ram_cap,
size_t ram_quota)
:
_label(label),
_entrypoint(entrypoint),
_binary_rom(binary ? prefixed_label(Session_label(label),
Session_label(binary)).string() : label),
_labeling_policy(_label),
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
_config_policy("config", _entrypoint, ram)
{ }
Genode::Dataspace_capability binary() { return _binary_rom.dataspace(); }
_label(label), _binary_name(binary_name), _ram(ram_cap),
_binary_service(Rom_session::service_name()),
_ram_quota(ram_quota), _ep(ep), _labeling_policy(_label.string()),
_config_policy("config", _ep, &_ram),
_session_requester(ep, _ram, 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, Genode::strlen(config) + 1);
_config_policy.load(config, strlen(config) + 1);
}
void configure(char const *config, size_t len)
@ -110,106 +128,187 @@ class Genode::Slave_policy : public Genode::Child_policy
_config_policy.load(config, len);
}
void trigger_session_requests()
{
_session_requester.trigger_update();
}
/****************************
** Child_policy interface **
****************************/
const char *name() const { return _label; }
Name name() const override { return _label; }
Genode::Service *resolve_session_request(const char *service_name,
const char *args)
Binary_name binary_name() const override { return _binary_name; }
Ram_session &ref_ram() override { return _ram; }
Ram_session_capability ref_ram_cap() const override { return _ram; }
void init(Ram_session &session, Ram_session_capability cap) override
{
Genode::Service *service = 0;
/* check for binary file request */
if ((service = _binary_policy.resolve_session_request(service_name, args)))
return service;
session.ref_account(_ram);
_ram.transfer_quota(cap, _ram_quota);
}
Service &resolve_session_request(Service::Name const &service_name,
Session_state::Args const &args)
{
/* check for config file request */
if ((service = _config_policy.resolve_session_request(service_name, args)))
return service;
if (Service *s = _config_policy.resolve_session_request(service_name.string(), args.string()))
return *s;
if (service_name == "ROM") {
Session_label const rom_name(label_from_args(args.string()).last_element());
if (rom_name == _binary_name) return _binary_service;
if (rom_name == "session_requests") return _session_requester.service();
}
if (!_service_permitted(service_name)) {
error(name(), ": illegal session request of service \"", service_name, "\"");
return 0;
error(name(), ": illegal session request of "
"service \"", service_name, "\" (", args, ")");
throw Parent::Service_denied();
}
/* fill parent service registry on demand */
if (!(service = _parent_services.find(service_name))) {
service = new (Genode::env()->heap())
Genode::Parent_service(service_name);
_parent_services.insert(service);
}
Parent_service *service = nullptr;
_parent_services.for_each([&] (Parent_service &s) {
if (!service && s.name() == service_name)
service = &s; });
/* return parent service */
return service;
if (!service)
service = new (env()->heap())
Parent_service(_parent_services, service_name);
return *service;
}
void filter_session_args(const char *service,
char *args, Genode::size_t args_len)
Id_space<Parent::Server> &server_id_space() override {
return _session_requester.id_space(); }
void filter_session_args(Service::Name const &service,
char *args, size_t args_len)
{
_labeling_policy.filter_session_args(service, args, args_len);
_labeling_policy.filter_session_args(service.string(), args, args_len);
}
};
class Genode::Slave
template <typename CONNECTION>
class Genode::Slave::Connection_base
{
private:
protected:
struct Resources
/* 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
{
Genode::Pd_connection pd;
Genode::Ram_connection ram;
Genode::Cpu_connection cpu;
Id_space<Parent::Server> &_id_space;
class Quota_exceeded : public Genode::Exception { };
Lock _lock { Lock::LOCKED };
bool _alive = false;
Resources(const char *label, Genode::size_t ram_quota,
Ram_session_capability ram_ref_cap)
: pd(label), ram(label), cpu(label)
Service(Policy &policy)
:
Genode::Service(CONNECTION::service_name(), policy.ref_ram_cap()),
_id_space(policy.server_id_space())
{ }
void initiate_request(Session_state &session) override
{
ram.ref_account(ram_ref_cap);
Ram_session_client ram_ref(ram_ref_cap);
switch (session.phase) {
if (ram_ref.transfer_quota(ram.cap(), ram_quota))
throw Quota_exceeded();
case Session_state::CREATE_REQUESTED:
if (!session.id_at_server.constructed())
session.id_at_server.construct(session, _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::INVALID_ARGS:
case Session_state::AVAILABLE:
case Session_state::CAP_HANDED_OUT:
case Session_state::CLOSED:
break;
}
}
} _resources;
Genode::Child::Initial_thread _initial_thread;
void wakeup() override { }
Genode::Region_map_client _address_space { _resources.pd.address_space() };
Genode::Child _child;
/**
* Session_state::Ready_callback
*/
void session_ready(Session_state &session) override
{
_alive = session.alive();
_lock.unlock();
}
public:
/**
* Session_state::Closed_callback
*/
void session_closed(Session_state &s) override { _lock.unlock(); }
Slave(Genode::Rpc_entrypoint &entrypoint,
Slave_policy &slave_policy,
Genode::size_t ram_quota,
Ram_session_capability ram_ref_cap = env()->ram_session_cap(),
Dataspace_capability ldso_ds = Dataspace_capability())
} _service;
Local_connection<CONNECTION> _connection;
Connection_base(Policy &policy, Args const &args, Affinity const &affinity)
:
_resources(slave_policy.name(), ram_quota, ram_ref_cap),
_initial_thread(_resources.cpu, _resources.pd, slave_policy.name()),
_child(slave_policy.binary(), ldso_ds, _resources.pd, _resources.pd,
_resources.ram, _resources.ram, _resources.cpu, _initial_thread,
*env()->rm_session(), _address_space, entrypoint, slave_policy)
{ }
_policy(policy), _service(_policy),
_connection(_service, _id_space, { 1 }, args, affinity)
{
_policy.trigger_session_requests();
_service._lock.lock();
if (!_service._alive)
throw Parent::Service_denied();
}
Genode::Ram_connection &ram() { return _resources.ram; }
~Connection_base()
{
_policy.trigger_session_requests();
_service._lock.lock();
}
typedef typename CONNECTION::Session_type SESSION;
Capability<SESSION> _cap() const { return _connection.cap(); }
};
/***************************************
** Wrappers of the 'Child' interface **
***************************************/
void yield(Genode::Parent::Resource_args const &args) {
_child.yield(args); }
void notify_resource_avail() const {
_child.notify_resource_avail(); }
template <typename CONNECTION>
struct Genode::Slave::Connection : private Connection_base<CONNECTION>,
public CONNECTION::Client
{
/**
* Constructor
*
* \throw Parent::Service_denied parent denies session request
* \throw Parent::Quota_exceeded our own quota does not suffice for
* the creation of the new session
*/
Connection(Slave::Policy &policy, Args const &args,
Affinity const &affinity = Affinity())
:
Connection_base<CONNECTION>(policy, args, affinity),
CONNECTION::Client(Connection_base<CONNECTION>::_cap())
{ }
};
#endif /* _INCLUDE__OS__SLAVE_H_ */

View File

@ -1,16 +1,17 @@
/*
* \brief Init process
* \brief Init component
* \author Norman Feske
* \date 2010-04-27
*/
/*
* Copyright (C) 2010-2013 Genode Labs GmbH
* 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.
*/
#include <base/component.h>
#include <init/child.h>
#include <base/sleep.h>
#include <os/config.h>
@ -65,7 +66,7 @@ inline Genode::Affinity::Space read_affinity_space()
/**
* Read parent-provided services from config
*/
inline void determine_parent_services(Genode::Service_registry *services)
inline void determine_parent_services(Genode::Registry<Init::Parent_service> *services)
{
using namespace Genode;
@ -75,11 +76,10 @@ inline void determine_parent_services(Genode::Service_registry *services)
Xml_node node = config()->xml_node().sub_node("parent-provides").sub_node("service");
for (; ; node = node.next("service")) {
char service_name[Genode::Service::MAX_NAME_LEN];
char service_name[Genode::Service::Name::capacity()];
node.attribute("name").value(service_name, sizeof(service_name));
Parent_service *s = new (env()->heap()) Parent_service(service_name);
services->insert(s);
new (env()->heap()) Init::Parent_service(*services, service_name);
if (Init::config_verbose)
log(" service \"", Cstring(service_name), "\"");
@ -231,13 +231,6 @@ class Init::Child_registry : public Name_registry, Child_list
return _aliases.first() ? _aliases.first() : 0;
}
void revoke_server(Genode::Server const *server)
{
Genode::List_element<Child> *curr = first();
for (; curr; curr = curr->next())
curr->object()->_child.revoke_server(server);
}
/*****************************
** Name-registry interface **
@ -260,41 +253,24 @@ class Init::Child_registry : public Name_registry, Child_list
return true;
}
Genode::Server *lookup_server(const char *name) const
Name deref_alias(Name const &name) override
{
/*
* Check if an alias with the specified name exists. If so,
* look up the server referred to by the alias.
*/
for (Alias const *a = _aliases.first(); a; a = a->next())
if (Alias::Name(name) == a->name)
name = a->child.string();
if (name == a->name)
return a->child;
/* look up child with the name */
Genode::List_element<Child> const *curr = first();
for (; curr; curr = curr->next())
if (curr->object()->has_name(name))
return curr->object()->server();
return 0;
return name;
}
};
int main(int, char **)
void Component::construct(Genode::Env &env)
{
using namespace Init;
using namespace Genode;
/* obtain dynamic linker */
Dataspace_capability ldso_ds;
try {
static Rom_connection rom("ld.lib.so");
ldso_ds = rom.dataspace();
} catch (...) { }
static Service_registry parent_services;
static Service_registry child_services;
static Registry<Init::Parent_service> parent_services;
static Registry<Routed_service> child_services;
static Child_registry children;
static Cap_connection cap;
@ -306,7 +282,7 @@ int main(int, char **)
Signal_context sig_ctx_res_avail;
config()->sigh(sig_rec.manage(&sig_ctx_config));
/* prevent init to block for resource upgrades (never satisfied by core) */
env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail));
Genode::env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail));
for (;;) {
@ -327,7 +303,7 @@ int main(int, char **)
config()->xml_node().for_each_sub_node("alias", [&] (Xml_node alias_node) {
try {
children.insert_alias(new (env()->heap()) Alias(alias_node));
children.insert_alias(new (Genode::env()->heap()) Alias(alias_node));
}
catch (Alias::Name_is_missing) {
warning("missing 'name' attribute in '<alias>' entry"); }
@ -341,12 +317,11 @@ int main(int, char **)
config()->xml_node().for_each_sub_node("start", [&] (Xml_node start_node) {
try {
children.insert(new (env()->heap())
Init::Child(start_node, default_route_node,
children.insert(new (Genode::env()->heap())
Init::Child(env, start_node, default_route_node,
children, read_prio_levels(),
read_affinity_space(),
parent_services, child_services, cap,
ldso_ds));
parent_services, child_services));
}
catch (Rom_connection::Rom_connection_failed) {
/*
@ -354,6 +329,11 @@ int main(int, char **)
* by the Rom_connection constructor.
*/
}
catch (Ram_session::Alloc_failed) {
warning("failed to allocate memory during child construction"); }
catch (Region_map::Attach_failed) {
warning("failed to attach dataspace to local address space "
"during child construction"); }
});
/* start children */
@ -386,35 +366,22 @@ int main(int, char **)
while (children.any()) {
Init::Child *child = children.any();
children.remove(child);
Genode::Server const *server = child->server();
destroy(env()->heap(), child);
/*
* The killed child may have provided services to other children.
* Since the server is dead by now, we cannot close its sessions
* in the cooperative way. Instead, we need to instruct each
* other child to forget about session associated with the dead
* server. Note that the 'child' pointer points a a no-more
* existing object. It is only used to identify the corresponding
* session. It must never by de-referenced!
*/
children.revoke_server(server);
destroy(Genode::env()->heap(), child);
}
/* remove all known aliases */
while (children.any_alias()) {
Init::Alias *alias = children.any_alias();
children.remove_alias(alias);
destroy(env()->heap(), alias);
destroy(Genode::env()->heap(), alias);
}
/* reset knowledge about parent services */
parent_services.remove_all();
parent_services.for_each([&] (Init::Parent_service &service) {
destroy(Genode::env()->heap(), &service); });
/* reload config */
try { config()->reload(); } catch (...) { }
}
return 0;
}

View File

@ -50,7 +50,7 @@ void Config::reload()
_config_xml = _config_xml_node(_config_ds);
} catch (Genode::Xml_node::Invalid_syntax) {
Genode::error("Config file has invalid syntax");
Genode::error("config ROM has invalid syntax");
_config_xml = fallback_config_xml();
}
}
@ -80,13 +80,13 @@ Config::Config()
{ }
Config *Genode::config()
Volatile_object<Config> &Genode::config()
{
static bool config_failed = false;
if (!config_failed) {
try {
static Config config_inst;
return &config_inst;
static Volatile_object<Config> config_inst;
return config_inst;
} catch (Genode::Rom_connection::Rom_connection_failed) {
Genode::error("Could not obtain config file");
} catch (Genode::Xml_node::Invalid_syntax) {
@ -97,6 +97,7 @@ Config *Genode::config()
}
/* do not try again to construct 'config_inst' */
config_failed = true;
return 0;
class Config_construction_failed : Genode::Exception { };
throw Config_construction_failed();
}