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> #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/service.h>
#include <base/heap.h> #include <base/heap.h>
/* Core includes */ /* core includes */
#include <platform_services.h> #include <platform_services.h>
#include <core_parent.h> /* for 'Core_service' type */
#include <vm_root.h> #include <vm_root.h>
/* /*
* Add ARM virtualization specific vm service * Add ARM virtualization specific vm service
*/ */
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep, void Genode::platform_add_local_services(Rpc_entrypoint *ep,
Genode::Sliced_heap *sh, Sliced_heap *sh,
Genode::Service_registry *ls) Registry<Service> *services)
{ {
using namespace Genode; using namespace Genode;
static Vm_root vm_root(ep, sh); 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_service(*services, vm_root);
ls->insert(&vm_ls);
} }

View File

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

View File

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

View File

@ -20,9 +20,13 @@
namespace Genode 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_ */ #endif /* _INCLUDE__BASE__INTERNAL__NATIVE_ENV_H_ */

View File

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

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()); return Rpc_exception_code(utcb.exception_code());
} }
@ -154,7 +154,7 @@ Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &,
default: break; 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); copy_utcb_to_msg(utcb, request_msg);

View File

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

View File

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

View File

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

View File

@ -19,17 +19,22 @@
#include <base/allocator.h> #include <base/allocator.h>
/* base-internal includes */ /* base-internal includes */
#include <base/internal/local_session.h>
#include <base/internal/region_map_mmap.h> #include <base/internal/region_map_mmap.h>
#include <base/internal/local_capability.h> #include <base/internal/local_capability.h>
namespace Genode { struct Local_rm_session; } 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; 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) 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) Pd_session_capability pd_cap)
: :
_ram_session_cap(ram_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_cap(cpu_cap),
_cpu_session_client(cpu_cap), _cpu_session_client(cpu_cap, Parent::Env::cpu()),
_region_map_mmap(false), _region_map_mmap(false),
_pd_session_cap(pd_cap), _pd_session_cap(pd_cap),
_local_pd_session(_pd_session_cap) _local_pd_session(_pd_session_cap)

View File

@ -59,37 +59,44 @@ bool Region_map_mmap::_dataspace_writable(Dataspace_capability ds)
** Local_parent ** ** 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, Session_args const &args,
Affinity const &affinity) Affinity const &affinity)
{ {
if (strcmp(service_name.string(), Rm_session::service_name()) == 0) if (strcmp(service_name.string(), Rm_session::service_name()) == 0) {
{
Local_rm_session *session = new (_alloc) Local_rm_session(_alloc);
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()) { try {
Parent_client::close(session); _local_sessions_id_space.apply<Local_session>(id, close_local_fn);
return; return CLOSE_DONE;
} }
catch (Id_space<Client>::Unknown_id) { }
/* return Parent_client::close(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));
} }
@ -148,9 +155,9 @@ Local_parent &Platform_env::_parent()
Platform_env::Platform_env() Platform_env::Platform_env()
: :
Platform_env_base(static_cap_cast<Ram_session>(_parent().session("Env::ram_session", "")), Platform_env_base(static_cap_cast<Ram_session>(_parent().session_cap(Parent::Env::ram())),
static_cap_cast<Cpu_session>(_parent().session("Env::cpu_session", "")), static_cap_cast<Cpu_session>(_parent().session_cap(Parent::Env::cpu())),
static_cap_cast<Pd_session> (_parent().session("Env::pd_session", ""))), static_cap_cast<Pd_session> (_parent().session_cap(Parent::Env::pd()))),
_heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()), _heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()),
_emergency_ram_ds(ram_session()->alloc(_emergency_ram_size())) _emergency_ram_ds(ram_session()->alloc(_emergency_ram_size()))
{ {

View File

@ -17,6 +17,9 @@
#include <base/rpc_server.h> #include <base/rpc_server.h>
#include <pd_session/client.h> #include <pd_session/client.h>
/* base-internal includes */
#include <base/internal/globals.h>
/* NOVA-specific part of the PD session interface */ /* NOVA-specific part of the PD session interface */
#include <nova_native_pd/client.h> #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); return native_pd.alloc_rpc_cap(ep, entry, 0);
}, },
[&] () { [&] () {
Pd_session_client *client = internal_env().upgrade(Parent::Env::pd(), "ram_quota=16K");
dynamic_cast<Pd_session_client*>(&pd);
if (client)
env()->parent()->upgrade(*client, "ram_quota=16K");
}); });
native_pd.imprint_rpc_cap(new_obj_cap, new_obj_cap.local_name()); 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/heap.h>
#include <base/service.h> #include <base/service.h>
#include <base/lock.h> #include <base/lock.h>
#include <base/local_connection.h>
#include <util/arg_string.h> #include <util/arg_string.h>
#include <ram_session/capability.h> #include <ram_session/connection.h>
#include <region_map/client.h> #include <region_map/client.h>
#include <pd_session/client.h> #include <pd_session/connection.h>
#include <cpu_session/client.h> #include <cpu_session/connection.h>
#include <log_session/connection.h>
#include <rom_session/connection.h>
#include <parent/capability.h> #include <parent/capability.h>
namespace Genode { namespace Genode {
@ -41,44 +44,47 @@ namespace Genode {
*/ */
struct Genode::Child_policy struct Genode::Child_policy
{ {
typedef String<64> Name;
typedef String<64> Binary_name;
typedef String<64> Linker_name;
virtual ~Child_policy() { } 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 * Determine service to provide a session request
* *
* \return Service to be contacted for the new session, or * \return service to be contacted for the new session
* 0 if session request could not be resolved *
* \throw Parent::Service_denied
*/ */
virtual Service *resolve_session_request(const char * /*service_name*/, virtual Service &resolve_session_request(Service::Name const &,
const char * /*args*/) Session_state::Args const &) = 0;
{ return 0; }
/** /**
* Apply transformations to session arguments * 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*/) { } char * /*args*/, size_t /*args_len*/) { }
/** /**
* Register a service provided by the child * 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*/, virtual void announce_service(Service::Name const &) { }
Root_capability /*root*/,
Allocator * /*alloc*/,
Server * /*server*/)
{ return false; }
/** /**
* Apply session affinity policy * Apply session affinity policy
@ -91,11 +97,6 @@ struct Genode::Child_policy
return affinity; return affinity;
} }
/**
* Unregister services that had been provided by the child
*/
virtual void unregister_services() { }
/** /**
* Exit child * Exit child
*/ */
@ -110,8 +111,8 @@ struct Genode::Child_policy
* The RAM session returned by this method is used for session-quota * The RAM session returned by this method is used for session-quota
* transfers. * transfers.
*/ */
virtual Ram_session *ref_ram_session() { return env()->ram_session(); } virtual Ram_session &ref_ram() = 0;
virtual Ram_session_capability ref_ram_cap() const { return env()->ram_session_cap(); } virtual Ram_session_capability ref_ram_cap() const = 0;
/** /**
* Respond to the release of resources by the child * 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 * Take action on additional resource needs by the child
*/ */
virtual void resource_request(Parent::Resource_args const &) { } 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 * to our account, and subsequently transfer the same amount from our
* account to the client. * 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 struct Initial_thread_base
{ {
@ -192,52 +245,114 @@ class Genode::Child : protected Rpc_object<Parent>
Capability<Cpu_thread> cap() { return _cap; } 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 */
Child_policy &_policy; Child_policy &_policy;
/* sessions opened by the child */ /* sessions opened by the child */
Lock _lock; /* protect list manipulation */ Id_space<Client> _id_space;
Object_pool<Session> _session_pool;
List<Session> _session_list;
/* server role */ typedef Session_state::Args Args;
Server _server;
/* session-argument buffer */ template <typename CONNECTION>
char _args[Parent::Session_args::MAX_SIZE]; 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 handlers registered by the child */
Signal_context_capability _resource_avail_sigh; Signal_context_capability _resource_avail_sigh;
Signal_context_capability _yield_sigh; Signal_context_capability _yield_sigh;
Signal_context_capability _session_sigh;
/* arguments fetched by the child in response to a yield signal */ /* arguments fetched by the child in response to a yield signal */
Lock _yield_request_lock; Lock _yield_request_lock;
Resource_args _yield_request_args; Resource_args _yield_request_args;
Initial_thread _initial_thread { _cpu.session(), _pd.cap(), "initial" };
struct Process struct Process
{ {
class Missing_dynamic_linker : Exception { }; class Missing_dynamic_linker : Exception { };
@ -313,30 +428,19 @@ class Genode::Child : protected Rpc_object<Parent>
Process _process; Process _process;
/** void _revert_quota_and_destroy(Session_state &);
* Attach session information to a child
* Close_result _close(Session_state &);
* \throw Ram_session::Quota_exceeded the child's heap partition cannot
* hold the session meta data
*/
void _add_session(const Session &s);
/** /**
* Close session and revert quota donation associated with it * Session_state::Ready_callback
*/ */
void _remove_session(Session *s); void session_ready(Session_state &session) override;
void _close(Session *s);
/** /**
* Return service interface targetting the parent * Session_state::Closed_callback
*
* 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.
*/ */
static Service &_parent_service(); void session_closed(Session_state &) override;
public: public:
@ -357,60 +461,18 @@ class Genode::Child : protected Rpc_object<Parent>
/** /**
* Constructor * Constructor
* *
* \param elf_ds dataspace that contains the ELF binary * \param rm local address space, usually 'env.rm()'
* \param ldso_ds dataspace that contains the dynamic linker, * \param entrypoint entrypoint used to serve the parent interface of
* started if 'elf_ds' is a dynamically linked * the child
* executable * \param policy policy for the child
* \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
* *
* \throw Parent::Service_denied if the initial sessions for the
* child's environment could not be
* opened
* \throw Ram_session::Alloc_failed * \throw Ram_session::Alloc_failed
* \throw Process_startup_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, Child(Region_map &rm, Rpc_entrypoint &entrypoint, Child_policy &policy);
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());
/** /**
* Destructor * Destructor
@ -421,25 +483,40 @@ class Genode::Child : protected Rpc_object<Parent>
virtual ~Child(); virtual ~Child();
/** /**
* Return heap that uses the child's quota * RAM quota unconditionally consumed by the child's environment
*/ */
Allocator *heap() { return &_heap; } static size_t env_ram_quota()
{
Pd_session_capability pd_session_cap() const { return _pd; } return Cpu_connection::RAM_QUOTA + Ram_connection::RAM_QUOTA +
Ram_session_capability ram_session_cap() const { return _ram; } Pd_connection::RAM_QUOTA + Log_connection::RAM_QUOTA +
Cpu_session_capability cpu_session_cap() const { return _cpu; } 2*Rom_connection::RAM_QUOTA;
Parent_capability parent_cap() const { return cap(); } }
/** /**
* Discard all sessions to specified service * Deduce session costs from usable ram quota
*
* 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!
*/ */
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 * Instruct the child to yield resources
@ -461,12 +538,16 @@ class Genode::Child : protected Rpc_object<Parent>
** Parent interface ** ** Parent interface **
**********************/ **********************/
void announce(Service_name const &, Root_capability) override; void announce(Service_name const &) override;
Session_capability session(Service_name const &, Session_args const &, void session_sigh(Signal_context_capability) override;
Affinity const &) override; Session_capability session(Client::Id, Service_name const &,
void upgrade(Session_capability, Upgrade_args const &) override; Session_args const &, Affinity const &) override;
void close(Session_capability) 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 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; Thread_capability main_thread_cap() const override;
void resource_avail_sigh(Signal_context_capability) override; void resource_avail_sigh(Signal_context_capability) override;
void resource_request(Resource_args const &) override; void resource_request(Resource_args const &) override;

View File

@ -16,20 +16,56 @@
#include <base/env.h> #include <base/env.h>
#include <base/capability.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 * Representation of an open connection to a service
*/ */
template <typename SESSION_TYPE> 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: private:
/* /*
@ -37,15 +73,12 @@ class Genode::Connection : public Noncopyable
* 'session' method that is called before the 'Connection' is * 'session' method that is called before the 'Connection' is
* constructed. * constructed.
*/ */
enum { FORMAT_STRING_SIZE = Parent::Session_args::MAX_SIZE }; enum { FORMAT_STRING_SIZE = Parent::Session_args::MAX_SIZE };
char _session_args[FORMAT_STRING_SIZE]; char _session_args[FORMAT_STRING_SIZE];
char _affinity_arg[sizeof(Affinity)]; char _affinity_arg[sizeof(Affinity)];
Parent &_parent;
On_destruction _on_destruction;
void _session(Parent &parent, void _session(Parent &parent,
Affinity const &affinity, Affinity const &affinity,
const char *format_args, va_list list) const char *format_args, va_list list)
@ -63,7 +96,8 @@ class Genode::Connection : public Noncopyable
memcpy(&affinity, _affinity_arg, sizeof(Affinity)); memcpy(&affinity, _affinity_arg, sizeof(Affinity));
try { try {
return env()->parent()->session<SESSION_TYPE>(_session_args, affinity); } return _env.session<SESSION_TYPE>(_id_space_element.id(),
_session_args, affinity); }
catch (...) { catch (...) {
error(SESSION_TYPE::service_name(), "-session creation failed " error(SESSION_TYPE::service_name(), "-session creation failed "
"(", Cstring(_session_args), ")"); "(", Cstring(_session_args), ")");
@ -75,20 +109,15 @@ class Genode::Connection : public Noncopyable
public: public:
typedef SESSION_TYPE Session_type;
/** /**
* Constructor * 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) Connection(Env &env, Capability<SESSION_TYPE>)
: _parent(env.parent()), _on_destruction(od) { } :
Connection_base(env), _cap(_request_cap())
{ }
/** /**
* Constructor * Constructor
@ -97,28 +126,18 @@ class Genode::Connection : public Noncopyable
* \deprecated Use the constructor with 'Env &' as first * \deprecated Use the constructor with 'Env &' as first
* argument instead * argument instead
*/ */
Connection(Capability<SESSION_TYPE>, On_destruction od = CLOSE) Connection(Capability<SESSION_TYPE>) : _cap(_request_cap()) { }
: _parent(*env()->parent()), _on_destruction(od) { }
/** /**
* Destructor * Destructor
*/ */
~Connection() ~Connection() { _env.close(_id_space_element.id()); }
{
if (_on_destruction == CLOSE)
_parent.close(_cap);
}
/** /**
* Return session capability * Return session capability
*/ */
Capability<SESSION_TYPE> cap() const { return _cap; } 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 * 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. * Once we add 'Env::upgrade', we can remove this accessor.
*/ */
virtual Pd_session_capability pd_session_cap() = 0; 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_ */ #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_ #ifndef _INCLUDE__BASE__SERVICE_H_
#define _INCLUDE__BASE__SERVICE_H_ #define _INCLUDE__BASE__SERVICE_H_
#include <root/client.h>
#include <base/log.h>
#include <util/list.h> #include <util/list.h>
#include <ram_session/client.h> #include <ram_session/client.h>
#include <base/env.h> #include <base/env.h>
#include <base/session_state.h>
#include <base/log.h>
#include <base/registry.h>
namespace Genode { namespace Genode {
class Client;
class Server;
class Service; class Service;
class Local_service; template <typename> class Session_factory;
template <typename> class Local_service;
class Parent_service; class Parent_service;
class Child_service; class Child_service;
class Service_registry;
} }
/** class Genode::Service : Noncopyable
* 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
{ {
public: public:
enum { MAX_NAME_LEN = 32 }; typedef Session_state::Name Name;
private: 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: public:
@ -123,52 +65,52 @@ class Genode::Service : public List<Service>::Element
* Constructor * Constructor
* *
* \param name service name * \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() { } virtual ~Service() { }
/** /**
* Return service name * 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 * The 'service' argument for the 'Session_state' corresponds to this
* \param affinity preferred CPU affinity of session * 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 * Sessions to local services and parent services are usually created
* \throw Unavailable * immediately during the dispatching of the 'Parent::session' request.
* \throw Quota_exceeded * In these cases, it is not needed to wait for an asynchronous
* response.
*/ */
virtual Session_capability session(char const *args, virtual void initiate_request(Session_state &session) = 0;
Affinity const &affinity) = 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; virtual void wakeup() { }
/**
* Close session
*/
virtual void close(Session_capability /*session*/) { }
/**
* Return server providing the service
*/
virtual Server *server() const { return 0; }
/** /**
* Return the RAM session to be used for trading resources * Return the RAM session to be used for trading resources
*/ */
Ram_session_capability ram_session_cap() Ram_session_capability ram() const
{ {
if (server()) return _ram;
return server()->ram_session_cap();
return Ram_session_capability();
} }
}; };
@ -176,36 +118,118 @@ class Genode::Service : public List<Service>::Element
/** /**
* Representation of a locally implemented service * Representation of a locally implemented service
*/ */
template <typename SESSION>
class Genode::Local_service : public Service 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: 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: 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); } switch (session.phase) {
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 session, const char *args) override case Session_state::CREATE_REQUESTED:
{
try { _root->upgrade(session, args); }
catch (Genode::Ipc_error) { throw Unavailable(); }
}
void close(Session_capability session) override try {
{ SESSION &rpc_obj = _factory.create(session.args(),
try { _root->close(session); } session.affinity());
catch (Genode::Ipc_error) { throw Blocking_canceled(); } 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 class Genode::Parent_service : public Service
{ {
private:
/*
* \deprecated
*/
Env &_env_deprecated();
Env &_env;
public: 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); } switch (session.phase) {
catch (Parent::Unavailable) {
warning("parent has no service \"", name(), "\""); case Session_state::CREATE_REQUESTED:
throw Unavailable();
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 class Genode::Child_service : public Service
{ {
public:
struct Wakeup { virtual void wakeup_child_service() = 0; };
private: private:
Root_capability _root_cap; Id_space<Parent::Server> &_server_id_space;
Root_client _root;
Server *_server; 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: public:
/** /**
* Constructor * Constructor
* *
* \param name name of service * \param factory server-side session-state factory
* \param root capability to root interface * \param name name of service
* \param server server process providing the service * \param ram recipient of session quota
*/ * \param wakeup callback to be notified on the arrival of new
Child_service(const char *name, * session requests
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 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); session.async_client_notify = true;
for (Service *s = _services.first(); s; s = s->next())
if (strcmp(s->name(), name) == 0
&& (!server || s->server() == server)) return s;
return 0;
} }
/** bool has_id_space(Id_space<Parent::Server> const &id_space) const
* Check if service name is ambiguous
*
* \return true if the same service is provided multiple
* times
*/
bool is_ambiguous(const char *name)
{ {
Lock::Guard lock_guard(_service_wait_queue_lock); return &_server_id_space == &id_space;
/* 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;
} }
/** void wakeup() override { _wakeup.wakeup_child_service(); }
* 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());
}
}; };
#endif /* _INCLUDE__BASE__SERVICE_H_ */ #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 <dataspace/capability.h>
#include <pd_session/pd_session.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 struct Genode::Cpu_session : Session
{ {
static const char *service_name() { return "CPU"; }
typedef Cpu_session_client Client;
/********************* /*********************
** Exception types ** ** Exception types **
*********************/ *********************/
@ -36,7 +45,6 @@ struct Genode::Cpu_session : Session
class Quota_exceeded : public Thread_creation_failed { }; class Quota_exceeded : public Thread_creation_failed { };
class Out_of_metadata : public Exception { }; class Out_of_metadata : public Exception { };
static const char *service_name() { return "CPU"; }
enum { THREAD_NAME_LEN = 32 }; enum { THREAD_NAME_LEN = 32 };
enum { PRIORITY_LIMIT = 1 << 16 }; enum { PRIORITY_LIMIT = 1 << 16 };

View File

@ -119,7 +119,6 @@ struct Genode::Env_deprecated
* \noapi * \noapi
*/ */
virtual void reinit_main_thread(Capability<Region_map> &stack_area_rm) = 0; virtual void reinit_main_thread(Capability<Region_map> &stack_area_rm) = 0;
}; };
#endif /* _INCLUDE__DEPRECATED__ENV_H_ */ #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 struct Genode::Log_connection : Connection<Log_session>, Log_session_client
{ {
enum { RAM_QUOTA = 8*1024UL };
/** /**
* Constructor * Constructor
*/ */
Log_connection(Env &env, Session_label label = Session_label()) Log_connection(Env &env, Session_label label = Session_label())
: :
Connection<Log_session>(env, session(env.parent(), Connection<Log_session>(env, session(env.parent(),
"ram_quota=8K, label=\"%s\"", "ram_quota=%ld, label=\"%s\"",
label.string())), RAM_QUOTA, label.string())),
Log_session_client(cap()) 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()) Log_connection(Session_label label = Session_label())
: :
Connection<Log_session>(session("ram_quota=8K, label=\"%s\"", Connection<Log_session>(session("ram_quota=%ld, label=\"%s\"",
label.string())), RAM_QUOTA, label.string())),
Log_session_client(cap()) Log_session_client(cap())
{ } { }
}; };

View File

@ -19,13 +19,19 @@
#include <base/rpc_args.h> #include <base/rpc_args.h>
#include <session/session.h> #include <session/session.h>
namespace Genode { struct Log_session; } namespace Genode {
struct Log_session;
struct Log_session_client;
}
struct Genode::Log_session : Session struct Genode::Log_session : Session
{ {
static const char *service_name() { return "LOG"; } static const char *service_name() { return "LOG"; }
typedef Log_session_client Client;
virtual ~Log_session() { } virtual ~Log_session() { }
/* the lowest platform-specific maximum IPC payload size (OKL4) */ /* 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 exit(int exit_value) override { call<Rpc_exit>(exit_value); }
void announce(Service_name const &service, Root_capability root) override { void announce(Service_name const &service) override {
call<Rpc_announce>(service, root); } 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, Session_args const &args,
Affinity const &affinity) override { 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 { Session_capability session_cap(Client::Id id) override {
call<Rpc_upgrade>(to_session, args); } 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 { Thread_capability main_thread_cap() const override {
return call<Rpc_main_thread>(); } return call<Rpc_main_thread>(); }

View File

@ -18,10 +18,15 @@
#include <base/rpc.h> #include <base/rpc.h>
#include <base/rpc_args.h> #include <base/rpc_args.h>
#include <base/thread.h> #include <base/thread.h>
#include <base/id_space.h>
#include <session/capability.h> #include <session/capability.h>
#include <root/capability.h> #include <root/capability.h>
namespace Genode { class Parent; } namespace Genode {
class Session_state;
class Parent;
}
class Genode::Parent class Genode::Parent
@ -61,13 +66,35 @@ class Genode::Parent
typedef Rpc_in_buffer<160> Session_args; typedef Rpc_in_buffer<160> Session_args;
typedef Rpc_in_buffer<160> Upgrade_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' * Use 'String' instead of 'Rpc_in_buffer' because 'Resource_args'
* is used as both in and out parameter. * is used as both in and out parameter.
*/ */
typedef String<160> Resource_args; typedef String<160> Resource_args;
virtual ~Parent() { } virtual ~Parent() { }
/** /**
@ -78,9 +105,20 @@ class Genode::Parent
/** /**
* Announce service to the parent * Announce service to the parent
*/ */
virtual void announce(Service_name const &service_name, virtual void announce(Service_name const &service_name) = 0;
Root_capability service_root) = 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 * Announce service to the parent
@ -109,9 +147,15 @@ class Genode::Parent
(Meta::Bool_to_type<Rpc_interface_is_inherited<Session>::VALUE> *)0); (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 * Create session to a service
* *
* \param id client-side ID of new session
* \param service_name name of the requested interface * \param service_name name of the requested interface
* \param args session constructor arguments * \param args session constructor arguments
* \param affinity preferred CPU affinity for the session * \param affinity preferred CPU affinity for the session
@ -119,60 +163,73 @@ class Genode::Parent
* \throw Service_denied parent denies session request * \throw Service_denied parent denies session request
* \throw Quota_exceeded our own quota does not suffice for * \throw Quota_exceeded our own quota does not suffice for
* the creation of the new session * 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 * \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, Session_args const &args,
Affinity const &affinity = Affinity()) = 0; Affinity const &affinity = Affinity()) = 0;
/** /**
* Create session to a service * Request session capability
* *
* \param SESSION_TYPE session interface type * \throw Service_denied
* \param args session constructor arguments
* \param affinity preferred CPU affinity for the session
* *
* \throw Service_denied parent denies session request * In the exception case, the parent implicitly closes the session.
* \throw Quota_exceeded our own quota does not suffice for
* the creation of the new session
* \throw Unavailable
*
* \return capability to new session
*/ */
template <typename SESSION_TYPE> virtual Session_capability session_cap(Client::Id id) = 0;
Capability<SESSION_TYPE> session(Session_args const &args,
Affinity const &affinity = Affinity()) enum Upgrade_result { UPGRADE_DONE, UPGRADE_PENDING };
{
Session_capability cap = session(SESSION_TYPE::service_name(),
args, affinity);
return reinterpret_cap_cast<SESSION_TYPE>(cap);
}
/** /**
* Transfer our quota to the server that provides the specified session * 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 * \param args description of the amount of quota to transfer
* *
* \throw Quota_exceeded quota could not be transferred * \throw Quota_exceeded quota could not be transferred
* *
* The 'args' argument has the same principle format as the 'args' * The 'args' argument has the same principle format as the 'args'
* argument of the 'session' operation. * 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, virtual Upgrade_result upgrade(Client::Id to_session,
Upgrade_args const &args) = 0; Upgrade_args const &args) = 0;
enum Close_result { CLOSE_DONE, CLOSE_PENDING };
/** /**
* Close session * 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 * Provide thread_cap of main thread
@ -233,14 +290,23 @@ class Genode::Parent
GENODE_RPC(Rpc_exit, void, exit, int); GENODE_RPC(Rpc_exit, void, exit, int);
GENODE_RPC(Rpc_announce, void, announce, 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_RPC_THROW(Rpc_session, Session_capability, session,
GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable), GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable),
Service_name const &, Session_args const &, Affinity const &); Client::Id, Service_name const &, Session_args const &,
GENODE_RPC_THROW(Rpc_upgrade, void, upgrade, 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), GENODE_TYPE_LIST(Quota_exceeded),
Session_capability, Upgrade_args const &); Client::Id, Upgrade_args const &);
GENODE_RPC(Rpc_close, void, close, Session_capability); 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_main_thread, Thread_capability, main_thread_cap);
GENODE_RPC(Rpc_resource_avail_sigh, void, resource_avail_sigh, GENODE_RPC(Rpc_resource_avail_sigh, void, resource_avail_sigh,
Signal_context_capability); Signal_context_capability);
@ -250,10 +316,12 @@ class Genode::Parent
GENODE_RPC(Rpc_yield_request, Resource_args, yield_request); GENODE_RPC(Rpc_yield_request, Resource_args, yield_request);
GENODE_RPC(Rpc_yield_response, void, yield_response); GENODE_RPC(Rpc_yield_response, void, yield_response);
GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session, Rpc_upgrade, GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session_sigh,
Rpc_close, Rpc_main_thread, Rpc_resource_avail_sigh, Rpc_session, Rpc_session_cap, Rpc_upgrade,
Rpc_resource_request, Rpc_yield_sigh, Rpc_yield_request, Rpc_close, Rpc_session_response, Rpc_main_thread,
Rpc_yield_response); 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 { namespace Genode {
struct Pd_session; struct Pd_session;
struct Pd_session_client;
struct Parent; struct Parent;
struct Signal_context; struct Signal_context;
} }
@ -34,6 +35,8 @@ struct Genode::Pd_session : Session
{ {
static const char *service_name() { return "PD"; } static const char *service_name() { return "PD"; }
typedef Pd_session_client Client;
virtual ~Pd_session() { } virtual ~Pd_session() { }
/** /**

View File

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

View File

@ -28,11 +28,13 @@ class Genode::Rom_connection : public Connection<Rom_session>,
class Rom_connection_failed : public Parent::Exception { }; class Rom_connection_failed : public Parent::Exception { };
enum { RAM_QUOTA = 4096UL };
private: private:
Rom_session_capability _session(Parent &parent, char const *label) 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: public:

View File

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

View File

@ -21,6 +21,7 @@
#include <base/allocator.h> #include <base/allocator.h>
#include <base/rpc_server.h> #include <base/rpc_server.h>
#include <base/entrypoint.h> #include <base/entrypoint.h>
#include <base/service.h>
#include <util/arg_string.h> #include <util/arg_string.h>
#include <base/log.h> #include <base/log.h>
@ -95,6 +96,7 @@ struct Genode::Multiple_clients
*/ */
template <typename SESSION_TYPE, typename POLICY> template <typename SESSION_TYPE, typename POLICY>
class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >, class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
public Local_service<SESSION_TYPE>::Factory,
private POLICY private POLICY
{ {
private: private:
@ -113,6 +115,53 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
*/ */
Allocator *_md_alloc; 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: 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 _upgrade_session(SESSION_TYPE *, const char *) { }
virtual void _destroy_session(SESSION_TYPE *session) { 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()' * 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 ** ** Root interface **
********************/ ********************/
@ -216,45 +290,8 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
Affinity const &affinity) override Affinity const &affinity) override
{ {
if (!args.valid_string()) throw Root::Invalid_args(); if (!args.valid_string()) throw Root::Invalid_args();
SESSION_TYPE &session = _create(args.string(), affinity);
POLICY::aquire(args.string()); return session.cap();
/*
* 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);
} }
void upgrade(Session_capability session, Root::Upgrade_args const &args) override void upgrade(Session_capability session, Root::Upgrade_args const &args) override

View File

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

View File

@ -15,6 +15,7 @@ SRC_CC += console.cc
SRC_CC += output.cc SRC_CC += output.cc
SRC_CC += child.cc SRC_CC += child.cc
SRC_CC += child_process.cc SRC_CC += child_process.cc
SRC_CC += session_state.cc
SRC_CC += elf_binary.cc SRC_CC += elf_binary.cc
SRC_CC += ipc.cc SRC_CC += ipc.cc
SRC_CC += lock.cc SRC_CC += lock.cc
@ -29,6 +30,8 @@ SRC_CC += region_map_client.cc
SRC_CC += rm_session_client.cc SRC_CC += rm_session_client.cc
SRC_CC += stack_allocator.cc SRC_CC += stack_allocator.cc
SRC_CC += trace.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 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"); error("create_thread: invalid PD argument");
throw Thread_creation_failed(); throw Thread_creation_failed();
} }
Lock::Guard slab_lock_guard(_thread_alloc_lock); Lock::Guard slab_lock_guard(_thread_alloc_lock);
thread = new (&_thread_alloc) thread = new (&_thread_alloc)
Cpu_thread_component( Cpu_thread_component(

View File

@ -116,8 +116,6 @@ namespace Genode {
typedef Synchronized_ram_session<Ram_session_component> Core_ram_session; typedef Synchronized_ram_session<Ram_session_component> Core_ram_session;
Core_parent _core_parent;
enum { ENTRYPOINT_STACK_SIZE = 2048 * sizeof(Genode::addr_t) }; enum { ENTRYPOINT_STACK_SIZE = 2048 * sizeof(Genode::addr_t) };
/* /*
@ -144,6 +142,10 @@ namespace Genode {
Heap _heap; Heap _heap;
Registry<Service> _services;
Core_parent _core_parent { _heap, _services };
public: public:
/** /**
@ -202,6 +204,8 @@ namespace Genode {
void reinit(Capability<Parent>::Raw) override { } void reinit(Capability<Parent>::Raw) override { }
void reinit_main_thread(Capability<Region_map> &) 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_ #define _CORE__INCLUDE__CORE_PARENT_H_
#include <parent/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 * Core has no parent. But most of Genode's library code could work seamlessly
* seamlessly inside Core too, if it had one. Core_parent fills this gap. * 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 &, public:
Affinity const &);
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_ */ #endif /* _CORE__INCLUDE__CORE_PARENT_H_ */

View File

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

View File

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

View File

@ -15,5 +15,5 @@
#include <platform_services.h> #include <platform_services.h>
void Genode::platform_add_local_services(Rpc_entrypoint*, Sliced_heap*, void Genode::platform_add_local_services(Rpc_entrypoint *, Sliced_heap*,
Service_registry*) { } 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 */ /* decrease quota limit of this session - check against used quota */
if (_quota_limit < amount + _payload) { if (_quota_limit < amount + _payload) {
warning("Insufficient quota for transfer: ", Cstring(_label)); warning("insufficient quota for transfer: "
warning(" have ", _quota_limit - _payload, ", need ", amount); "'", Cstring(_label), "' to '", Cstring(dst->_label), "' "
"have ", (_quota_limit - _payload)/1024, " KiB, "
"need ", amount/1024, " KiB");
return -3; return -3;
} }
@ -262,6 +264,10 @@ int Ram_session_component::transfer_quota(Ram_session_capability ram_session_cap
{ {
auto lambda = [&] (Ram_session_component *dst) { auto lambda = [&] (Ram_session_component *dst) {
return _transfer_quota(dst, amount); }; return _transfer_quota(dst, amount); };
if (this->cap() == ram_session_cap)
return 0;
return _ram_session_ep->apply(ram_session_cap, lambda); return _ram_session_ep->apply(ram_session_cap, lambda);
} }

View File

@ -26,11 +26,11 @@
*/ */
void Genode::platform_add_local_services(Rpc_entrypoint*, void Genode::platform_add_local_services(Rpc_entrypoint*,
Sliced_heap *sliced_heap, Sliced_heap *sliced_heap,
Service_registry *local_services) Registry<Service> *local_services)
{ {
static Io_port_root io_port_root(core_env()->pd_session(), static Io_port_root io_port_root(core_env()->pd_session(),
platform()->io_port_alloc(), sliced_heap); platform()->io_port_alloc(), sliced_heap);
static Local_service io_port_ls(Io_port_session::service_name(),
&io_port_root); static Core_service<Io_port_session_component>
local_services->insert(&io_port_ls); 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) 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()); 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> 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., * 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'). * interface ('Nova_cpu_session').
*/ */
Upgradeable_client<Genode::Cpu_session_client> 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 Thread_capability

View File

@ -87,13 +87,14 @@ class Genode::Expanding_parent_client : public Parent_client
** Parent interface ** ** Parent interface **
**********************/ **********************/
Session_capability session(Service_name const &name, Session_capability session(Client::Id id,
Service_name const &name,
Session_args const &args, Session_args const &args,
Affinity const &affinity) override Affinity const &affinity) override
{ {
enum { NUM_ATTEMPTS = 2 }; enum { NUM_ATTEMPTS = 2 };
return retry<Parent::Quota_exceeded>( 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); 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. * 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. * caller to issue (and respond to) a resource request.
*/ */
enum { NUM_ATTEMPTS = 2 }; enum { NUM_ATTEMPTS = 2 };
retry<Parent::Quota_exceeded>( return retry<Parent::Quota_exceeded>(
[&] () { Parent_client::upgrade(to_session, args); }, [&] () { return Parent_client::upgrade(id, args); },
[&] () { resource_request(Resource_args(args.string())); }, [&] () { resource_request(Resource_args(args.string())); },
NUM_ATTEMPTS); 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> struct Genode::Expanding_ram_session_client : Upgradeable_client<Genode::Ram_session_client>
{ {
Expanding_ram_session_client(Ram_session_capability cap) Expanding_ram_session_client(Ram_session_capability cap, Parent::Client::Id id)
: Upgradeable_client<Genode::Ram_session_client>(cap) { } : Upgradeable_client<Genode::Ram_session_client>(cap, id) { }
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached = UNCACHED) override 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; Upgradeable_client<Genode::Pd_session_client> _pd_client;
Expanding_region_map_client(Pd_session_capability pd, Capability<Region_map> rm) Expanding_region_map_client(Pd_session_capability pd, Capability<Region_map> rm,
: Region_map_client(rm), _pd_client(pd) { } 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, Local_addr attach(Dataspace_capability ds, size_t size, off_t offset,
bool use_local_addr, Local_addr local_addr, bool use_local_addr, Local_addr local_addr,

View File

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

View File

@ -48,9 +48,9 @@ class Genode::Platform_env : public Env_deprecated,
struct Resources struct Resources
{ {
template <typename T> 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; Expanding_ram_session_client ram;
@ -60,10 +60,12 @@ class Genode::Platform_env : public Env_deprecated,
Resources(Parent &parent) Resources(Parent &parent)
: :
ram(request<Ram_session>(parent, "Env::ram_session")), ram(request<Ram_session>(parent, Parent::Env::ram()),
cpu(request<Cpu_session>(parent, "Env::cpu_session")), Parent::Env::ram()),
pd (request<Pd_session> (parent, "Env::pd_session")), cpu(request<Cpu_session>(parent, Parent::Env::cpu()),
rm (pd, pd.address_space()) 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; 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) void upgrade_ram(size_t quota)
{ {
log("upgrading quota donation for Env::", CLIENT::Rpc_interface::service_name(),
" (", quota, " bytes)");
char buf[128]; char buf[128];
snprintf(buf, sizeof(buf), "ram_quota=%lu", quota); 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: public:
class Quota_exceeded : Exception { };
/** /**
* Constructor * Constructor
* *
* \param quantim number of bytes to transfer * \param quantim number of bytes to transfer
* \param from donator RAM session * \param from donator RAM session
* \param to receiver RAM session * \param to receiver RAM session
*
* \throw Quota_exceeded
*/ */
Transfer(size_t quantum, Transfer(size_t quantum,
Ram_session_capability from, Ram_session_capability from,
@ -58,7 +62,7 @@ namespace {
if (_from.valid() && _to.valid() && if (_from.valid() && _to.valid() &&
Ram_session_client(_from).transfer_quota(_to, quantum)) { Ram_session_client(_from).transfer_quota(_to, quantum)) {
warning("not enough quota for a donation of ", quantum, " bytes"); 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 ** ** Child **
***********/ ***********/
void Child::_add_session(Child::Session const &s) template <typename SESSION>
static Service &parent_service()
{ {
Lock::Guard lock_guard(_lock); static Parent_service service(SESSION::service_name());
return service;
/*
* 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);
}
} }
@ -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; 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, Parent::Session_args const &args,
Affinity const &affinity) Affinity const &affinity)
{ {
if (!name.valid_string() || !args.valid_string()) throw Unavailable(); if (!name.valid_string() || !args.valid_string()) throw Unavailable();
/* return sessions that we created for the child */ char argbuf[Parent::Session_args::MAX_SIZE];
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;
/* filter session arguments according to the child policy */ /* filter session arguments according to the child policy */
strncpy(_args, args.string(), sizeof(_args)); strncpy(argbuf, args.string(), sizeof(argbuf));
_policy.filter_session_args(name.string(), _args, sizeof(_args)); _policy.filter_session_args(name.string(), argbuf, sizeof(argbuf));
/* filter session affinity */ /* filter session affinity */
Affinity const filtered_affinity = _policy.filter_session_affinity(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 */ /* 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 */ /* try to dispatch session request synchronously */
if (!service) service.initiate_request(session);
throw Service_denied();
/* transfer session quota from ourself to the service provider */ if (session.phase == Session_state::INVALID_ARGS) {
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(), _revert_quota_and_destroy(session);
service->ram_session_cap()); throw Service_denied();
}
/* create session */ /* finish transaction */
Session_capability cap; donation_from_child.acknowledge();
try { cap = service->session(_args, filtered_affinity); } donation_to_service.acknowledge();
catch (Service::Invalid_args) { throw Service_denied(); } }
catch (Service::Unavailable) { throw Service_denied(); } catch (Transfer::Quota_exceeded) {
catch (Service::Quota_exceeded) { throw 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())); } * Copy out the session cap before we are potentially kicking off the
catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); } * 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 */ /* if request was not handled synchronously, kick off async operation */
donation_from_child.acknowledge(); if (session.phase == Session_state::CREATE_REQUESTED)
donation_to_service.acknowledge(); service.wakeup();
if (cap.valid())
session.phase = Session_state::CAP_HANDED_OUT;
return cap; 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 */ auto lamda = [&] (Session_state &session) {
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;
/* check if upgrade refers to server */ if (session.phase == Session_state::INVALID_ARGS) {
_session_pool.apply(to_session, [&] (Session *session)
{
if (session)
targeted_service = session->service();
if (!targeted_service) { /*
warning("could not lookup service for session upgrade"); * Implicity discard the session request when delivering an
return; * 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()) { if (!session.alive())
warning("no valid session-upgrade arguments"); 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; return;
} }
size_t const ram_quota = size_t const ram_quota =
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
/* transfer quota from client to ourself */ try {
Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap()); /* 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 session quota from ourself to the service provider */
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(), Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
targeted_service->ram_session_cap()); session.service().ram());
try { targeted_service->upgrade(to_session, args.string()); } session.increase_donated_quota(ram_quota);
catch (Service::Quota_exceeded) { throw Quota_exceeded(); } session.phase = Session_state::UPGRADE_REQUESTED;
/* remember new amount attached to the session */ session.service().initiate_request(session);
if (session)
session->upgrade_ram_quota(ram_quota);
/* finish transaction */ /* finish transaction */
donation_from_child.acknowledge(); donation_from_child.acknowledge();
donation_to_service.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 */ /* refuse to close the child's initial sessions */
if (session_cap.local_name() == _ram.local_name() if (Parent::Env::session_id(id))
|| session_cap.local_name() == _cpu.local_name() return CLOSE_DONE;
|| session_cap.local_name() == _pd.local_name())
return;
Session *session = nullptr; try {
_session_pool.apply(session_cap, [&] (Session *s) Close_result result = CLOSE_PENDING;
{ auto lamda = [&] (Session_state &session) { result = _close(session); };
session = s; _id_space.apply<Session_state>(id, lamda);
if (s) _session_pool.remove(s); return result;
}); }
_close(session); 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(); } void Child::yield_response() { _policy.yield_response(); }
Child::Child(Dataspace_capability elf_ds, namespace {
Dataspace_capability ldso_ds,
Pd_session_capability pd_cap, /**
Pd_session &pd, * Return interface for interacting with the child's address space
Ram_session_capability ram_cap, *
Ram_session &ram, * Depending on the return value of 'Child_policy::address_space', we
Cpu_session_capability cpu_cap, * either interact with a local object of via an RPC client stub.
Initial_thread_base &initial_thread, */
Region_map &local_rm, struct Child_address_space
Region_map &remote_rm, {
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, Rpc_entrypoint &entrypoint,
Child_policy &policy, Child_policy &policy)
Service &pd_service,
Service &ram_service,
Service &cpu_service)
try : try :
_pd(pd_cap), _ram(ram_cap), _cpu(cpu_cap), _policy(policy),
_pd_service(pd_service), _heap(&_ram.session(), &local_rm),
_ram_service(ram_service),
_cpu_service(cpu_service),
_heap(&ram, &local_rm),
_entrypoint(entrypoint), _entrypoint(entrypoint),
_parent_cap(_entrypoint.manage(this)), _parent_cap(_entrypoint.manage(this)),
_policy(policy), _process(_binary.session().dataspace(), _linker_dataspace(),
_server(_ram), _pd.cap(), _pd.session(), _ram.session(), _initial_thread, local_rm,
_process(elf_ds, ldso_ds, pd_cap, pd, ram, initial_thread, local_rm, remote_rm, Child_address_space(_pd.session(), _policy).region_map(),
_parent_cap) _parent_cap)
{ } { }
catch (Cpu_session::Thread_creation_failed) { throw Process_startup_failed(); } 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() Child::~Child()
{ {
_entrypoint.dissolve(this); _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 */ /* Genode includes */
#include <base/component.h> #include <base/component.h>
#include <base/connection.h>
#include <base/service.h>
#include <base/env.h> #include <base/env.h>
/* base-internal includes */ /* base-internal includes */
#include <base/internal/globals.h> #include <base/internal/globals.h>
/*
* XXX remove this pointer once 'Env_deprecated' is removed
*/
static Genode::Env *env_ptr = nullptr;
namespace { namespace {
using namespace Genode;
struct Env : Genode::Env struct Env : Genode::Env
{ {
Genode::Entrypoint &_ep; 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::Ram_session &ram() override { return *Genode::env()->ram_session(); }
Genode::Cpu_session &cpu() override { return *Genode::env()->cpu_session(); } Genode::Cpu_session &cpu() override { return *Genode::env()->cpu_session(); }
Genode::Region_map &rm() override { return *Genode::env()->rm_session(); } Genode::Region_map &rm() override { return *Genode::env()->rm_session(); }
@ -49,6 +83,54 @@ namespace {
{ {
return Genode::env()->pd_session_cap(); 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; struct Startup;
extern void bootstrap_component(); 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(); _suspended_callback();
init_signal_thread(_env); init_signal_thread(_env);
_rpc_ep.construct(&_env.pd(), Component::stack_size(), initial_ep_name()); _rpc_ep.construct(&_env.pd(), Component::stack_size(), initial_ep_name());
_signal_proxy_cap = manage(_signal_proxy); _signal_proxy_cap = manage(_signal_proxy);
_sig_rec.construct(); _sig_rec.construct();
@ -165,6 +166,9 @@ Entrypoint::Entrypoint(Env &env)
/* initialize signalling after initializing but before calling the entrypoint */ /* initialize signalling after initializing but before calling the entrypoint */
init_signal_thread(_env); 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. * 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/platform_env.h>
#include <base/internal/globals.h>
#include <base/connection.h>
#include <base/service.h>
namespace Genode { 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 }; enum { _BUF_SIZE = Log_session::MAX_STRING_LEN };
Log_connection _log;
char _buf[_BUF_SIZE]; struct Log : Log_session_client
unsigned _num_chars; {
Lock _lock; 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() void _flush()
{ {
@ -77,7 +88,7 @@ class Log_console : public Console
/** /**
* Return LOG session interface * Return LOG session interface
*/ */
Log_session *log_session() { return &_log; } Log_session &log_session() { return _log; }
/** /**
* Re-establish LOG session * Re-establish LOG session
@ -85,13 +96,13 @@ class Log_console : public Console
void reconnect() void reconnect()
{ {
/* /*
* Note that the destructor of old 'Log_connection' is not called. * We cannot use a 'Volatile_object' because we have to skip
* This is not needed because the only designated use of this * the object destruction inside a freshly forked process.
* function is the startup procedure of noux processes created * Otherwise, the attempt to destruct the capability contained
* via fork. At the point of calling this function, the new child * in the 'Log' object would result in an inconsistent ref counter
* has no valid capability to the original LOG session anyway. * 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) 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 = Untyped_capability new_obj_cap =
retry<Genode::Pd_session::Out_of_metadata>( retry<Genode::Pd_session::Out_of_metadata>(
[&] () { return pd.alloc_rpc_cap(_cap); }, [&] () { return pd.alloc_rpc_cap(_cap); },
[&] () { [&] () { env()->parent()->upgrade(Parent::Env::pd(),
Pd_session_client *client = "ram_quota=16K"); });
dynamic_cast<Pd_session_client*>(&pd);
if (client)
env()->parent()->upgrade(*client, "ram_quota=16K");
});
return new_obj_cap; 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)"); 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 struct Linker::Elf_file : File
{ {
Env &env; Env &env;
Rom_connection rom; Lazy_volatile_object<Rom_connection> rom_connection;
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR]; Rom_session_client rom;
bool const loaded; 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) 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(); load_phdr();

View File

@ -103,12 +103,6 @@ namespace Linker {
* Global ELF access lock * Global ELF access lock
*/ */
Lock &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); 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); 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); } 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, enum Bind { BIND_LAZY = Shared_object::BIND_LAZY,
BIND_NOW = Shared_object::BIND_NOW }; 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_ */ #endif /* _INCLUDE__TYPES_H_ */

View File

@ -4,10 +4,10 @@
* \date 2008-09-24 * \date 2008-09-24
* *
* This program starts itself as child. When started, it first determines * 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 * wheather it is parent or child by requesting a RM session. Because the
* service. Because the program blocks all session-creation calls for the * program blocks all session-creation calls for the RM service, each program
* ROM service, each program instance can determine its parent or child * instance can determine its parent or child role by the checking the result
* role by the checking the result of the session creation. * of the session creation.
*/ */
/* /*
@ -17,17 +17,11 @@
* under the terms of the GNU General Public License version 2. * under the terms of the GNU General Public License version 2.
*/ */
#include <base/component.h>
#include <base/log.h> #include <base/log.h>
#include <base/env.h>
#include <base/sleep.h>
#include <base/child.h> #include <base/child.h>
#include <pd_session/connection.h>
#include <rm_session/connection.h> #include <rm_session/connection.h>
#include <ram_session/connection.h> #include <os/attached_ram_dataspace.h>
#include <rom_session/connection.h>
#include <cpu_session/connection.h>
#include <cap_session/connection.h>
#include <rm_session/client.h>
using namespace Genode; using namespace Genode;
@ -69,108 +63,135 @@ void main_child()
** Parent ** ** 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: private:
enum { STACK_SIZE = 8*1024 }; Env &_env;
Parent_services &_parent_services;
/* Signal_context_capability const _fault_handler_sigh;
* 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;
public: public:
/** /**
* Constructor * Constructor
*/ */
Test_child(Genode::Dataspace_capability elf_ds, Test_child_policy(Env &env, Parent_services &parent_services,
Genode::Pd_connection &pd, Signal_context_capability fault_handler_sigh)
Genode::Ram_session_capability ram,
Genode::Cpu_session_capability cpu,
Genode::Cap_session *cap)
: :
_entrypoint(cap, STACK_SIZE, "child", false), _env(env),
_address_space(pd.address_space()), _pd(pd), _ram(ram), _cpu(cpu), _parent_services(parent_services),
_initial_thread(_cpu, _pd, "child"), _fault_handler_sigh(fault_handler_sigh)
_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();
}
/**************************** /****************************
** Child-policy interface ** ** 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 */ enum { CHILD_QUOTA = 1*1024*1024 };
return !strcmp(service, "LOG") ? &_log_service : 0; 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) char *args, size_t args_len)
{ {
/* define session label for sessions forwarded to our parent */ /* prefix session label */
Arg_string::set_arg_string(args, args_len, "label", "child"); 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 */ Signal_handler<Main_parent> _fault_handler {
static Pd_connection pd; _env.ep(), *this, &Main_parent::_handle_fault };
static Ram_connection ram;
static Cpu_connection cpu;
static Cap_connection cap;
/* transfer some of our own ram quota to the new child */ Heap _heap { _env.ram(), _env.rm() };
enum { CHILD_QUOTA = 1*1024*1024 };
ram.ref_account(env()->ram_session_cap());
env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA);
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 */ Parent_services(Allocator &alloc) : alloc(alloc)
static Signal_context signal_context; {
Region_map_client address_space(pd.address_space()); static const char *names[] = {
address_space.fault_handler(fault_handler.manage(&signal_context)); "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 */ /* 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 */ Child _child { _env.rm(), _env.ep().rpc_ep(), _child_policy };
Dataspace_capability ds = env()->ram_session()->alloc(4096);
volatile int *local_addr = env()->rm_session()->attach(ds);
for (int i = 0; i < 4; i++) { Region_map_client _address_space { _child.pd().address_space() };
log("wait for region-manager fault"); /* dataspace used for creating shared memory between parent and child */
fault_handler.wait_for_signal(); Attached_ram_dataspace _ds { _env.ram(), _env.rm(), 4096 };
log("received region-manager fault signal, request fault state");
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 = char const *state_name =
state.type == Region_map::State::READ_FAULT ? "READ_FAULT" : state.type == Region_map::State::READ_FAULT ? "READ_FAULT" :
@ -182,47 +203,45 @@ void main_parent(Dataspace_capability elf_ds)
/* ignore spuriuous fault signal */ /* ignore spuriuous fault signal */
if (state.type == Region_map::State::READY) { if (state.type == Region_map::State::READY) {
log("ignoring spurious fault signal"); log("ignoring spurious fault signal");
continue; return;
} }
addr_t child_virt_addr = state.addr & ~(4096 - 1); addr_t child_virt_addr = state.addr & ~(4096 - 1);
/* allocate dataspace to resolve the fault */ /* allocate dataspace to resolve the fault */
log("attach dataspace to the child at ", Hex(child_virt_addr)); 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 */ /* poll until our child modifies the dataspace content */
while (*local_addr == 0x1234); 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"); log("revoke dataspace from child");
address_space.detach((void *)child_virt_addr); _address_space.detach((void *)child_virt_addr);
} }
fault_handler.dissolve(&signal_context); Main_parent(Env &env) : _env(env) { }
};
log("--- parent role of region-manager fault test finished ---");
}
/************************* void Component::construct(Env &env)
** Common main program **
*************************/
int main(int argc, char **argv)
{ {
log("--- region-manager fault test ---"); log("--- region-manager fault test ---");
/* obtain own elf file from rom service */
try { try {
static Rom_connection rom("test-rm_fault"); /*
main_parent(rom.dataspace()); * Distinguish parent from child by requesting an service that is only
} catch (Genode::Rom_connection::Rom_connection_failed) { * available to the parent.
*/
Rm_connection rm;
static Main_parent parent(env);
log("-- parent role started --");
}
catch (Parent::Service_denied) {
main_child(); main_child();
} }
return 0;
} }

View File

@ -32,6 +32,7 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
private: private:
Rpc_entrypoint &_thread_ep; Rpc_entrypoint &_thread_ep;
Env &_env;
Allocator &_md_alloc; Allocator &_md_alloc;
Thread_list &_thread_list; Thread_list &_thread_list;
Thread_list_change_handler &_thread_list_change_handler; 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 *_create_session(const char *args) override
{ {
Cpu_session_component *cpu_session_component = Cpu_session_component *cpu_session_component =
new (md_alloc()) Cpu_session_component(_thread_ep, new (md_alloc()) Cpu_session_component(_thread_ep, _env,
_md_alloc, _md_alloc, _thread_list,
_thread_list,
_thread_list_change_handler, _thread_list_change_handler,
args); args);
return cpu_session_component; 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 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: public:
Cpu_root(Rpc_entrypoint &session_ep, Cpu_root(Rpc_entrypoint &session_ep,
Rpc_entrypoint &thread_ep, Rpc_entrypoint &thread_ep,
Env &env,
Allocator &md_alloc, Allocator &md_alloc,
Thread_list &thread_list, Thread_list &thread_list,
Thread_list_change_handler &thread_list_change_handler) Thread_list_change_handler &thread_list_change_handler)
: Root_component<Cpu_session_component>(&session_ep, &md_alloc), : Root_component<Cpu_session_component>(&session_ep, &md_alloc),
_thread_ep(thread_ep), _thread_ep(thread_ep), _env(env),
_md_alloc(md_alloc), _md_alloc(md_alloc),
_thread_list(thread_list), _thread_list(thread_list),
_thread_list_change_handler(thread_list_change_handler) { } _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( Cpu_sampler::
Rpc_entrypoint &thread_ep, Cpu_session_component::
Allocator &md_alloc, Cpu_session_component(Rpc_entrypoint &thread_ep,
Thread_list &thread_list, Env &env,
Thread_list_change_handler &thread_list_change_handler, Allocator &md_alloc,
char const *args) Thread_list &thread_list,
Thread_list_change_handler &thread_list_change_handler,
char const *args)
: _thread_ep(thread_ep), : _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), _md_alloc(md_alloc),
_thread_list(thread_list), _thread_list(thread_list),
_thread_list_change_handler(thread_list_change_handler), _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() Cpu_sampler::Cpu_session_component::~Cpu_session_component()
{ {
_cleanup_native_cpu(); _cleanup_native_cpu();

View File

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

View File

@ -172,7 +172,7 @@ struct Cpu_sampler::Main : Thread_list_change_handler
Main(Genode::Env &env) Main(Genode::Env &env)
: env(env), : env(env),
alloc(env.ram(), env.rm()), 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") config(env, "config")
{ {
/* /*

View File

@ -80,7 +80,7 @@ compare_output_to {
[init -> test-ldso] Catch exceptions in program [init -> test-ldso] Catch exceptions in program
[init -> test-ldso] --------------------------- [init -> test-ldso] ---------------------------
[init -> test-ldso] exception in remote procedure call: [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] Error: Could not open ROM session for "unknown_file"
[init -> test-ldso] caught [init -> test-ldso] caught
[init -> test-ldso] exception in program: caught [init -> test-ldso] exception in program: caught

View File

@ -21,6 +21,7 @@
#include <cap_session/connection.h> #include <cap_session/connection.h>
#include <base/log.h> #include <base/log.h>
#include <base/child.h> #include <base/child.h>
#include <os/session_requester.h>
#include <os/session_policy.h> #include <os/session_policy.h>
/* init includes */ /* init includes */
@ -33,6 +34,16 @@ namespace Init {
class Name_registry; class Name_registry;
class Child_registry; class Child_registry;
class Child; 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) static void warn_insuff_quota(Genode::size_t const avail)
{ {
if (!config_verbose) { return; } if (!config_verbose) { return; }
Genode::log("Warning: Specified quota exceeds available quota."); log("Warning: Specified quota exceeds available quota.");
Genode::log(" Proceeding with a quota of ", avail, "."); log(" Proceeding with a quota of ", avail, ".");
} }
inline long read_priority(Genode::Xml_node start_node, long prio_levels) inline long read_priority(Genode::Xml_node start_node, long prio_levels)
@ -68,7 +79,7 @@ namespace Init {
if (priority && (priority >= prio_levels)) { if (priority && (priority >= prio_levels)) {
long new_prio = prio_levels ? prio_levels-1 : 0; 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)); start_node.attribute("name").value(name, sizeof(name));
Genode::warning(Genode::Cstring(name), ": invalid priority, upgrading " Genode::warning(Genode::Cstring(name), ": invalid priority, upgrading "
"from ", -priority, " to ", -new_prio); "from ", -priority, " to ", -new_prio);
@ -124,7 +135,6 @@ namespace Init {
/** /**
* Return sub string of label with the leading child name stripped out * 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) 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 child_name name of the originator of the session request
* \param service_name name of the requested service * \param service_name name of the requested service
*/ */
inline bool service_node_matches(Genode::Xml_node service_node, inline bool service_node_matches(Genode::Xml_node service_node, char const *args,
char const *args, Genode::Child_policy::Name const &child_name,
char const *child_name, Genode::Service::Name const &service_name)
char const *service_name)
{ {
bool const service_matches = bool const service_matches =
service_node.has_type("any-service") || service_node.has_type("any-service") ||
(service_node.has_type("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) if (!service_matches)
return false; return false;
Genode::Session_label const session_label(skip_label_prefix( 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(); 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 * Check if arguments satisfy the condition specified for the route
*/ */
inline bool service_node_args_condition_satisfied(Genode::Xml_node service_node, inline bool service_node_args_condition_satisfied(Genode::Xml_node service_node,
const char *args, Genode::Session_state::Args const &args,
char const *child_name) Genode::Child_policy::Name const &child_name)
{ {
try { try {
Genode::Xml_node if_arg = service_node.sub_node("if-arg"); 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)); if_arg.attribute("value").value(value, sizeof(value));
char arg_value[VALUE_MAX_LEN]; 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". * Skip child-name prefix if the key is the process "label".
@ -214,7 +223,7 @@ namespace Init {
* the prefix information is redundant. * the prefix information is redundant.
*/ */
if (Genode::strcmp("label", key) == 0) 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; return Genode::strcmp(value, arg_value) == 0;
} catch (...) { } } catch (...) { }
@ -222,109 +231,80 @@ namespace Init {
/* if no if-arg node exists, the condition is met */ /* if no if-arg node exists, the condition is met */
return true; 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 * 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: private:
Genode::Root_capability _root; Child_name _child_name;
bool _announced;
Genode::Server *_server;
struct Applicant : public Genode::Cancelable_lock, Genode::Registry<Routed_service>::Element _registry_element;
public Genode::List<Applicant>::Element
{
Applicant() : Cancelable_lock(Genode::Lock::LOCKED) { }
};
Genode::Lock _applicants_lock;
Genode::List<Applicant> _applicants;
public: public:
/** /**
* Constructor * Constructor
* *
* \param name name of service * \param services registry of all services provides by children
* \param server server providing the service * \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, Routed_service(Genode::Registry<Routed_service> &services,
Genode::Server *server) Child_name const &child_name,
: Service(name), _announced(false), _server(server) { } 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; } Child_name const &child_name() const { return _child_name; }
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(); }
}
}; };
@ -335,6 +315,8 @@ struct Init::Name_registry
{ {
virtual ~Name_registry() { } virtual ~Name_registry() { }
typedef Genode::Child_policy::Name Name;
/** /**
* Check if specified name is unique * Check if specified name is unique
* *
@ -343,13 +325,16 @@ struct Init::Name_registry
virtual bool unique(const char *name) const = 0; 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: public:
@ -362,6 +347,8 @@ class Init::Child : Genode::Child_policy
friend class Child_registry; friend class Child_registry;
Genode::Env &_env;
Genode::List_element<Child> _list_element; Genode::List_element<Child> _list_element;
Genode::Xml_node _start_node; Genode::Xml_node _start_node;
@ -463,11 +450,6 @@ class Init::Child : Genode::Child_policy
Genode::size_t ram_quota; Genode::size_t ram_quota;
Genode::size_t cpu_quota_pc; Genode::size_t cpu_quota_pc;
bool constrain_phys; 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, Resources(Genode::Xml_node start_node, const char *label,
long prio_levels, long prio_levels,
@ -477,31 +459,13 @@ class Init::Child : Genode::Child_policy
prio_levels_log2(Genode::log2(prio_levels)), prio_levels_log2(Genode::log2(prio_levels)),
priority(read_priority(start_node, prio_levels)), priority(read_priority(start_node, prio_levels)),
affinity(affinity_space, affinity(affinity_space,
read_affinity_location(affinity_space, start_node)), read_affinity_location(affinity_space, start_node))
pd(label),
ram(label),
cpu(label,
priority*(Genode::Cpu_session::PRIORITY_LIMIT >> prio_levels_log2),
affinity)
{ {
/* deduce session costs from usable ram quota */ /* deduce session costs from usable ram quota */
Genode::size_t session_donations = Genode::Pd_connection::RAM_QUOTA + ram_quota = Genode::Child::effective_ram_quota(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();
} }
} _resources; } _resources;
Genode::Child::Initial_thread _initial_thread { _resources.cpu, _resources.pd,
_name.unique };
/* /*
* Entry point used for serving the parent interface and the * Entry point used for serving the parent interface and the
* locally provided ROM sessions for the 'config' and 'binary' * 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 }; enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
Genode::Rpc_entrypoint _entrypoint; Genode::Rpc_entrypoint _entrypoint;
/** Genode::Parent_service _env_ram_service { _env, Genode::Ram_session::service_name() };
* ELF binary 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::Rom_connection _binary_rom; Genode::Parent_service _env_log_service { _env, Genode::Log_session::service_name() };
Genode::Dataspace_capability _binary_rom_ds; 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 * Private child configuration
*/ */
Init::Child_config _config; Init::Child_config _config;
/** Genode::Session_requester _session_requester;
* 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;
/** /**
* Policy helpers * Policy helpers
@ -537,22 +496,39 @@ class Init::Child : Genode::Child_policy
Init::Child_policy_enforce_labeling _labeling_policy; Init::Child_policy_enforce_labeling _labeling_policy;
Init::Child_policy_handle_cpu_priorities _priority_policy; Init::Child_policy_handle_cpu_priorities _priority_policy;
Init::Child_policy_provide_rom_file _config_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_redirect_rom_file _configfile_policy;
Init::Child_policy_ram_phys _ram_session_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: public:
Child(Genode::Xml_node start_node, /**
Genode::Xml_node default_route_node, * Constructor
Name_registry &name_registry, *
long prio_levels, * \throw Ram_session::Alloc_failed allocation of config buffer failed
Genode::Affinity::Space const &affinity_space, * \throw Region_map::Attach_failed failed to temporarily attach
Genode::Service_registry &parent_services, * config dataspace to local address
Genode::Service_registry &child_services, * space
Genode::Cap_session &cap_session, */
Genode::Dataspace_capability ldso_ds) 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), _list_element(this),
_start_node(start_node), _start_node(start_node),
_default_route_node(default_route_node), _default_route_node(default_route_node),
@ -560,23 +536,15 @@ class Init::Child : Genode::Child_policy
_name(start_node, name_registry), _name(start_node, name_registry),
_resources(start_node, _name.unique, prio_levels, _resources(start_node, _name.unique, prio_levels,
affinity_space), affinity_space),
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false, _entrypoint(&_env.pd(), ENTRYPOINT_STACK_SIZE, _name.unique, false,
_resources.affinity.location()), _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), _parent_services(parent_services),
_child_services(child_services), _child_services(child_services),
_config(_env.ram(), _env.rm(), start_node),
_session_requester(_entrypoint, _env.ram(), _env.rm()),
_labeling_policy(_name.unique), _labeling_policy(_name.unique),
_priority_policy(_resources.prio_levels_log2, _resources.priority), _priority_policy(_resources.prio_levels_log2, _resources.priority),
_config_policy("config", _config.dataspace(), &_entrypoint), _config_policy("config", _config.dataspace(), &_entrypoint),
_binary_policy("binary", _binary_rom_ds, &_entrypoint),
_configfile_policy("config", _config.filename()), _configfile_policy("config", _config.filename()),
_ram_session_policy(_resources.constrain_phys) _ram_session_policy(_resources.constrain_phys)
{ {
@ -587,10 +555,10 @@ class Init::Child : Genode::Child_policy
"\"", Genode::Cstring(_name.unique), "\""); "\"", Genode::Cstring(_name.unique), "\"");
if (config_verbose) { if (config_verbose) {
Genode::log("child \"", Genode::Cstring(_name.unique), "\""); log("child \"", Genode::Cstring(_name.unique), "\"");
Genode::log(" RAM quota: ", _resources.ram_quota); log(" RAM quota: ", _resources.ram_quota);
Genode::log(" ELF binary: ", Cstring(_name.file)); log(" ELF binary: ", Cstring(_name.file));
Genode::log(" priority: ", _resources.priority); log(" priority: ", _resources.priority);
} }
/* /*
@ -601,32 +569,33 @@ class Init::Child : Genode::Child_policy
for (; ; service_node = service_node.next("service")) { 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)); service_node.attribute("name").value(name, sizeof(name));
if (config_verbose) if (config_verbose)
Genode::log(" provides service ", Genode::Cstring(name)); log(" provides service ", Genode::Cstring(name));
child_services.insert(new (_child.heap()) new (_child.heap())
Routed_service(name, &_server)); 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) { } } catch (Xml_node::Nonexistent_sub_node) { }
} }
virtual ~Child() { virtual ~Child()
Genode::Service *s; {
while ((s = _child_services.find_by_server(&_server))) { _child_services.for_each([&] (Routed_service &service) {
_child_services.remove(s); if (service.has_id_space(_session_requester.id_space()))
} destroy(_child.heap(), &service); });
} }
/** /**
* Return true if the child has the specified name * Return true if the child has the specified name
*/ */
bool has_name(const char *n) const { return !Genode::strcmp(name(), n); } bool has_name(Child_policy::Name const &str) const { return str == name(); }
Genode::Server *server() { return &_server; }
/** /**
* Start execution of child * Start execution of child
@ -638,22 +607,70 @@ class Init::Child : Genode::Child_policy
** Child-policy interface ** ** 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, Binary_name binary_name() const override { return _name.file; }
const char *args)
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; session.ref_account(Genode::env()->ram_session_cap());
using Genode::error; Genode::env()->ram_session()->transfer_quota(cap, _resources.ram_quota);
using Genode::warning; }
/* check for config file request */ void init(Genode::Cpu_session &session, Genode::Cpu_session_capability cap) override
if ((service = _config_policy.resolve_session_request(service_name, args))) {
return service; using Genode::Cpu_session;
using Genode::size_t;
/* check for binary file request */ static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
if ((service = _binary_policy.resolve_session_request(service_name, args))) size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100);
return service; 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 { try {
Genode::Xml_node route_node = _default_route_node; 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"); 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; continue;
if (!service_node_args_condition_satisfied(service_node, args, name())) if (!service_node_args_condition_satisfied(service_node, args, name()))
@ -676,54 +693,51 @@ class Init::Child : Genode::Child_policy
for (; ; target = target.next()) { for (; ; target = target.next()) {
if (target.has_type("parent")) { if (target.has_type("parent")) {
service = _parent_services.find(service_name);
if (service) if ((service = find_service(_parent_services, service_name)))
return service; return *service;
if (!service_wildcard) { if (!service_wildcard) {
warning(name(), ": service lookup for " warning(name(), ": service lookup for "
"\"", service_name, "\" at parent failed"); "\"", service_name, "\" at parent failed");
return 0; throw Genode::Parent::Service_denied();
} }
} }
if (target.has_type("child")) { 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); typedef Name_registry::Name Name;
if (!server) { Name server_name = target.attribute_value("name", Name());
warning(name(), ": invalid route to non-existing " server_name = _name_registry.deref_alias(server_name);
"server \"", Genode::Cstring(server_name), "\"");
return 0;
}
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) if (service)
return service; return *service;
if (!service_wildcard) { if (!service_wildcard) {
Genode::warning(name(), ": lookup to child " Genode::warning(name(), ": lookup to child "
"service \"", service_name, "\" failed"); "server \"", server_name, "\" failed");
return 0; throw Genode::Parent::Service_denied();
} }
} }
if (target.has_type("any-child")) { 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 " error(name(), ": ambiguous routes to "
"service \"", service_name, "\""); "service \"", service_name, "\"");
return 0; throw Genode::Parent::Service_denied();
} }
service = _child_services.find(service_name);
if (service) if ((service = find_service(_child_services, service_name)))
return service; return *service;
if (!service_wildcard) { if (!service_wildcard) {
warning(name(), ": lookup for service " warning(name(), ": lookup for service "
"\"", service_name, "\" failed"); "\"", service_name, "\" failed");
return 0; throw Genode::Parent::Service_denied();
} }
} }
@ -734,19 +748,23 @@ class Init::Child : Genode::Child_policy
} catch (...) { } catch (...) {
warning(name(), ": no route to service \"", service_name, "\""); 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, void filter_session_args(Service::Name const &service,
char *args, Genode::size_t args_len) char *args, Genode::size_t args_len) override
{ {
_labeling_policy. filter_session_args(service, args, args_len); _labeling_policy. filter_session_args(service.string(), args, args_len);
_priority_policy. filter_session_args(service, args, args_len); _priority_policy. filter_session_args(service.string(), args, args_len);
_configfile_policy.filter_session_args(service, args, args_len); _configfile_policy. filter_session_args(service.string(), args, args_len);
_ram_session_policy.filter_session_args(service, 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; using namespace Genode;
@ -772,28 +790,24 @@ class Init::Child : Genode::Child_policy
return Affinity(space, location); return Affinity(space, location);
} }
bool announce_service(const char *service_name, void announce_service(Genode::Service::Name const &service_name) override
Genode::Root_capability root,
Genode::Allocator *alloc,
Genode::Server *server)
{ {
if (config_verbose) log("child \"", name(), "\" announces service \"", service_name, "\"");
Genode::log("child \"", name(), "\" announces service \"", service_name, "\"");
Genode::Service *s = _child_services.find(service_name, &_server); bool found = false;
Routed_service *rs = dynamic_cast<Routed_service *>(s); _child_services.for_each([&] (Routed_service &service) {
if (!s || !rs) { if (service.has_id_space(_session_requester.id_space())
Genode::error(name(), ": illegal announcement of service \"", service_name, "\""); && service.name() == service_name)
return false; found = true; });
}
rs->announce(root); if (!found)
return true; 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::size_t const requested_ram_quota =
Genode::Arg_string::find_arg(args.string(), "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'. * 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); requested_ram_quota);
/* wake up child that was starved for resources */ /* 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_ */ #endif /* _INCLUDE__INIT__CHILD_H_ */

View File

@ -14,11 +14,9 @@
#ifndef _INCLUDE__INIT__CHILD_CONFIG_H_ #ifndef _INCLUDE__INIT__CHILD_CONFIG_H_
#define _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 <util/xml_node.h>
#include <rom_session/connection.h> #include <base/attached_dataspace.h>
#include <ram_session/client.h> #include <ram_session/ram_session.h>
namespace Init { class Child_config; } namespace Init { class Child_config; }
@ -27,11 +25,62 @@ class Init::Child_config
{ {
private: private:
enum { CONFIGFILE_NAME_LEN = 64 }; Genode::Ram_session &_ram;
char _filename[CONFIGFILE_NAME_LEN];
Genode::Ram_session_capability _ram_session_cap; typedef Genode::String<64> Rom_name;
Genode::Ram_dataspace_capability _config_ram_ds; 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: public:
@ -40,84 +89,31 @@ class Init::Child_config
* *
* The provided RAM session is used to obtain a dataspace for * The provided RAM session is used to obtain a dataspace for
* holding the copy of the child's configuration data unless the * holding the copy of the child's configuration data unless the
* configuration is supplied via a config file. Normally, the * configuration is supplied via a config ROM module.
* child's RAM session should be used to account the consumed RAM *
* quota to the child. * \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, Child_config(Genode::Ram_session &ram, Genode::Region_map &local_rm,
Genode::Xml_node start_node) Genode::Xml_node start)
: _ram_session_cap(ram_session) :
{ _ram(ram),
using namespace Genode; _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))
* 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) { }
}
/** /**
* Destructor * Destructor
*/ */
~Child_config() ~Child_config() { if (_ram_ds.valid()) _ram.free(_ram_ds); }
{
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);
}
/** /**
* Return file name if configuration comes from a file * 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. * If the configuration is provided inline, the method returns 0.
*/ */
char const *filename() const { 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 * Request dataspace holding the start node's configuration data
@ -134,7 +130,7 @@ class Init::Child_config
* inline configuration (if 'filename()' returns 0). * inline configuration (if 'filename()' returns 0).
*/ */
Genode::Dataspace_capability dataspace() { Genode::Dataspace_capability dataspace() {
return Genode::Dataspace_capability(_config_ram_ds); } return Genode::Dataspace_capability(_ram_ds); }
}; };
#endif /* _INCLUDE__INIT__CHILD_CONFIG_H_ */ #endif /* _INCLUDE__INIT__CHILD_CONFIG_H_ */

View File

@ -19,9 +19,11 @@
#include <base/child.h> #include <base/child.h>
#include <base/rpc_server.h> #include <base/rpc_server.h>
#include <base/session_label.h> #include <base/session_label.h>
#include <base/attached_ram_dataspace.h>
#include <util/arg_string.h> #include <util/arg_string.h>
#include <rom_session/connection.h> #include <rom_session/connection.h>
#include <base/session_label.h> #include <base/session_label.h>
#include <os/dynamic_rom_session.h>
namespace Init { namespace Init {
@ -29,8 +31,17 @@ namespace Init {
class Child_policy_enforce_labeling; class Child_policy_enforce_labeling;
class Child_policy_handle_cpu_priorities; class Child_policy_handle_cpu_priorities;
class Child_policy_provide_rom_file; class Child_policy_provide_rom_file;
class Child_policy_provide_dynamic_rom;
class Child_policy_redirect_rom_file; class Child_policy_redirect_rom_file;
class Traditional_child_policy; 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> struct Local_rom_session_component : Genode::Rpc_object<Genode::Rom_session>
{ {
Genode::Rpc_entrypoint &ep;
Genode::Dataspace_capability ds_cap; Genode::Dataspace_capability ds_cap;
/** /**
* Constructor * Constructor
*/ */
Local_rom_session_component(Genode::Dataspace_capability ds) Local_rom_session_component(Genode::Rpc_entrypoint &ep,
: ds_cap(ds) { } Genode::Dataspace_capability ds)
: ep(ep), ds_cap(ds) { ep.manage(this); }
~Local_rom_session_component() { ep.dissolve(this); }
/*************************** /***************************
** ROM session interface ** ** ROM session interface **
***************************/ ***************************/
Genode::Rom_dataspace_capability dataspace() { Genode::Rom_dataspace_capability dataspace() override {
return Genode::static_cap_cast<Genode::Rom_dataspace>(ds_cap); } 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::Session_label const _module_name;
Genode::Rom_session_capability _rom_session_cap;
Genode::Session_label _module_name; typedef Genode::Local_service<Local_rom_session_component> Service;
struct Local_rom_service : public Genode::Service Service::Single_session_factory _session_factory { _session };
{ Service _service { _session_factory };
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;
public: public:
/** /**
* Constructor * 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::Dataspace_capability ds_cap,
Genode::Rpc_entrypoint *ep) Genode::Rpc_entrypoint *ep)
: :
_local_rom_session(ds_cap), _ep(ep), _session(*ep, ds_cap), _module_name(module_name)
_rom_session_cap(_ep->manage(&_local_rom_session)),
_module_name(module_name),
_local_rom_service(_rom_session_cap, ds_cap.valid())
{ } { }
/**
* Destructor
*/
~Child_policy_provide_rom_file() { _ep->dissolve(&_local_rom_session); }
Genode::Service *resolve_session_request(const char *service_name, Genode::Service *resolve_session_request(const char *service_name,
const char *args) const char *args)
{ {
@ -238,7 +218,7 @@ class Init::Child_policy_provide_rom_file
{ {
Genode::Session_label const label = Genode::label_from_args(args); Genode::Session_label const label = Genode::label_from_args(args);
return label.last_element() == _module_name 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 * \brief Child policy helper for supplying dynamic ROM modules
* \author Norman Feske * \author Norman Feske
* \date 2012-04-04 * \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, Rpc_entrypoint &ep,
Ram_session *ram) Ram_session *ram)
: :
Service("ROM"), Service("ROM", Ram_session_capability()),
_ram(ram), _ram(ram),
_fg(0, 0), _bg(0, 0), _fg(0, 0), _bg(0, 0),
_bg_has_pending_data(false), _bg_has_pending_data(false),
@ -147,11 +150,31 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object<Rom_session>,
** Service interface ** ** Service interface **
***********************/ ***********************/
Session_capability session(const char *, Affinity const &) { void initiate_request(Session_state &session) override
return _rom_session_cap; } {
switch (session.phase) {
void upgrade(Session_capability, const char *) { } case Session_state::CREATE_REQUESTED:
void close(Session_capability) { } 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 * 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 */ /* Genode includes */
#include <base/rpc_server.h> #include <base/rpc_server.h>
#include <base/local_connection.h>
#include <base/child.h> #include <base/child.h>
#include <init/child_policy.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/child_policy_dynamic_rom.h>
#include <os/session_requester.h>
namespace Genode { namespace Genode { struct Slave; }
class Slave_policy;
class Slave;
}
/** struct Genode::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
{ {
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: 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 * Return white list of services the slave is permitted to use
* *
@ -51,58 +61,66 @@ class Genode::Slave_policy : public Genode::Child_policy
private: private:
char const *_label; Label const _label;
Genode::Service_registry _parent_services; Binary_name const _binary_name;
Genode::Rpc_entrypoint &_entrypoint; Ram_session_client _ram;
Genode::Rom_connection _binary_rom; 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_enforce_labeling _labeling_policy;
Init::Child_policy_provide_rom_file _binary_policy; Child_policy_dynamic_rom_file _config_policy;
Genode::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) for (const char **s = _permitted_services(); *s; ++s)
if (!Genode::strcmp(service_name, *s)) if (service_name == *s)
return true; return true;
return false; return false;
} }
Session_requester _session_requester;
public: public:
class Connection;
/** /**
* Slave-policy constructor * Slave-policy constructor
* *
* \param label name of the program to start * \param ep entrypoint used to provide local services
* \param entrypoint entrypoint used to provide local services * such as the config ROM service
* such as the config ROM service * \param local_rm local address space, needed to populate dataspaces
* \param ram RAM session used for buffering config data * provided to the child (config, session_requests)
* *
* If 'ram' is set to 0, no configuration can be supplied to the * \throw Ram_session::Alloc_failed by 'Child_policy_dynamic_rom_file'
* slave. * \throw Rm_session::Attach_failed by 'Child_policy_dynamic_rom_file'
*/ */
Slave_policy(const char *label, Policy(Label const &label,
Genode::Rpc_entrypoint &entrypoint, Name const &binary_name,
Genode::Ram_session *ram = 0, Rpc_entrypoint &ep,
const char *binary = nullptr) Region_map &rm,
Ram_session_capability ram_cap,
size_t ram_quota)
: :
_label(label), _label(label), _binary_name(binary_name), _ram(ram_cap),
_entrypoint(entrypoint), _binary_service(Rom_session::service_name()),
_binary_rom(binary ? prefixed_label(Session_label(label), _ram_quota(ram_quota), _ep(ep), _labeling_policy(_label.string()),
Session_label(binary)).string() : label), _config_policy("config", _ep, &_ram),
_labeling_policy(_label), _session_requester(ep, _ram, rm)
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint), {
_config_policy("config", _entrypoint, ram) configure("<config/>");
{ } }
Genode::Dataspace_capability binary() { return _binary_rom.dataspace(); }
/** /**
* Assign new configuration to slave * Assign new configuration to slave
*
* \param config new configuration as null-terminated string
*/ */
void configure(char const *config) 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) 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); _config_policy.load(config, len);
} }
void trigger_session_requests()
{
_session_requester.trigger_update();
}
/**************************** /****************************
** Child_policy interface ** ** Child_policy interface **
****************************/ ****************************/
const char *name() const { return _label; } Name name() const override { return _label; }
Genode::Service *resolve_session_request(const char *service_name, Binary_name binary_name() const override { return _binary_name; }
const char *args)
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; session.ref_account(_ram);
_ram.transfer_quota(cap, _ram_quota);
/* check for binary file request */ }
if ((service = _binary_policy.resolve_session_request(service_name, args)))
return service;
Service &resolve_session_request(Service::Name const &service_name,
Session_state::Args const &args)
{
/* check for config file request */ /* check for config file request */
if ((service = _config_policy.resolve_session_request(service_name, args))) if (Service *s = _config_policy.resolve_session_request(service_name.string(), args.string()))
return service; 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)) { if (!_service_permitted(service_name)) {
error(name(), ": illegal session request of service \"", service_name, "\""); error(name(), ": illegal session request of "
return 0; "service \"", service_name, "\" (", args, ")");
throw Parent::Service_denied();
} }
/* fill parent service registry on demand */ /* fill parent service registry on demand */
if (!(service = _parent_services.find(service_name))) { Parent_service *service = nullptr;
service = new (Genode::env()->heap()) _parent_services.for_each([&] (Parent_service &s) {
Genode::Parent_service(service_name); if (!service && s.name() == service_name)
_parent_services.insert(service); service = &s; });
}
/* return parent service */ if (!service)
return service; service = new (env()->heap())
Parent_service(_parent_services, service_name);
return *service;
} }
void filter_session_args(const char *service, Id_space<Parent::Server> &server_id_space() override {
char *args, Genode::size_t args_len) 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; Id_space<Parent::Server> &_id_space;
Genode::Ram_connection ram;
Genode::Cpu_connection cpu;
class Quota_exceeded : public Genode::Exception { }; Lock _lock { Lock::LOCKED };
bool _alive = false;
Resources(const char *label, Genode::size_t ram_quota, Service(Policy &policy)
Ram_session_capability ram_ref_cap) :
: pd(label), ram(label), cpu(label) 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); switch (session.phase) {
Ram_session_client ram_ref(ram_ref_cap);
if (ram_ref.transfer_quota(ram.cap(), ram_quota)) case Session_state::CREATE_REQUESTED:
throw Quota_exceeded();
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, } _service;
Slave_policy &slave_policy,
Genode::size_t ram_quota, Local_connection<CONNECTION> _connection;
Ram_session_capability ram_ref_cap = env()->ram_session_cap(),
Dataspace_capability ldso_ds = Dataspace_capability()) Connection_base(Policy &policy, Args const &args, Affinity const &affinity)
: :
_resources(slave_policy.name(), ram_quota, ram_ref_cap), _policy(policy), _service(_policy),
_initial_thread(_resources.cpu, _resources.pd, slave_policy.name()), _connection(_service, _id_space, { 1 }, args, affinity)
_child(slave_policy.binary(), ldso_ds, _resources.pd, _resources.pd, {
_resources.ram, _resources.ram, _resources.cpu, _initial_thread, _policy.trigger_session_requests();
*env()->rm_session(), _address_space, entrypoint, slave_policy) _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(); }
};
/*************************************** template <typename CONNECTION>
** Wrappers of the 'Child' interface ** struct Genode::Slave::Connection : private Connection_base<CONNECTION>,
***************************************/ public CONNECTION::Client
{
void yield(Genode::Parent::Resource_args const &args) { /**
_child.yield(args); } * Constructor
*
void notify_resource_avail() const { * \throw Parent::Service_denied parent denies session request
_child.notify_resource_avail(); } * \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_ */ #endif /* _INCLUDE__OS__SLAVE_H_ */

View File

@ -1,16 +1,17 @@
/* /*
* \brief Init process * \brief Init component
* \author Norman Feske * \author Norman Feske
* \date 2010-04-27 * \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 * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2. * under the terms of the GNU General Public License version 2.
*/ */
#include <base/component.h>
#include <init/child.h> #include <init/child.h>
#include <base/sleep.h> #include <base/sleep.h>
#include <os/config.h> #include <os/config.h>
@ -65,7 +66,7 @@ inline Genode::Affinity::Space read_affinity_space()
/** /**
* Read parent-provided services from config * 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; 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"); Xml_node node = config()->xml_node().sub_node("parent-provides").sub_node("service");
for (; ; node = node.next("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)); node.attribute("name").value(service_name, sizeof(service_name));
Parent_service *s = new (env()->heap()) Parent_service(service_name); new (env()->heap()) Init::Parent_service(*services, service_name);
services->insert(s);
if (Init::config_verbose) if (Init::config_verbose)
log(" service \"", Cstring(service_name), "\""); log(" service \"", Cstring(service_name), "\"");
@ -231,13 +231,6 @@ class Init::Child_registry : public Name_registry, Child_list
return _aliases.first() ? _aliases.first() : 0; 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 ** ** Name-registry interface **
@ -260,41 +253,24 @@ class Init::Child_registry : public Name_registry, Child_list
return true; 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()) for (Alias const *a = _aliases.first(); a; a = a->next())
if (Alias::Name(name) == a->name) if (name == a->name)
name = a->child.string(); return a->child;
/* look up child with the name */ return 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;
} }
}; };
int main(int, char **) void Component::construct(Genode::Env &env)
{ {
using namespace Init; using namespace Init;
using namespace Genode; using namespace Genode;
/* obtain dynamic linker */ static Registry<Init::Parent_service> parent_services;
Dataspace_capability ldso_ds; static Registry<Routed_service> child_services;
try {
static Rom_connection rom("ld.lib.so");
ldso_ds = rom.dataspace();
} catch (...) { }
static Service_registry parent_services;
static Service_registry child_services;
static Child_registry children; static Child_registry children;
static Cap_connection cap; static Cap_connection cap;
@ -306,7 +282,7 @@ int main(int, char **)
Signal_context sig_ctx_res_avail; Signal_context sig_ctx_res_avail;
config()->sigh(sig_rec.manage(&sig_ctx_config)); config()->sigh(sig_rec.manage(&sig_ctx_config));
/* prevent init to block for resource upgrades (never satisfied by core) */ /* 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 (;;) { for (;;) {
@ -327,7 +303,7 @@ int main(int, char **)
config()->xml_node().for_each_sub_node("alias", [&] (Xml_node alias_node) { config()->xml_node().for_each_sub_node("alias", [&] (Xml_node alias_node) {
try { 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) { catch (Alias::Name_is_missing) {
warning("missing 'name' attribute in '<alias>' entry"); } 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) { config()->xml_node().for_each_sub_node("start", [&] (Xml_node start_node) {
try { try {
children.insert(new (env()->heap()) children.insert(new (Genode::env()->heap())
Init::Child(start_node, default_route_node, Init::Child(env, start_node, default_route_node,
children, read_prio_levels(), children, read_prio_levels(),
read_affinity_space(), read_affinity_space(),
parent_services, child_services, cap, parent_services, child_services));
ldso_ds));
} }
catch (Rom_connection::Rom_connection_failed) { catch (Rom_connection::Rom_connection_failed) {
/* /*
@ -354,6 +329,11 @@ int main(int, char **)
* by the Rom_connection constructor. * 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 */ /* start children */
@ -386,35 +366,22 @@ int main(int, char **)
while (children.any()) { while (children.any()) {
Init::Child *child = children.any(); Init::Child *child = children.any();
children.remove(child); children.remove(child);
Genode::Server const *server = child->server(); destroy(Genode::env()->heap(), child);
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);
} }
/* remove all known aliases */ /* remove all known aliases */
while (children.any_alias()) { while (children.any_alias()) {
Init::Alias *alias = children.any_alias(); Init::Alias *alias = children.any_alias();
children.remove_alias(alias); children.remove_alias(alias);
destroy(env()->heap(), alias); destroy(Genode::env()->heap(), alias);
} }
/* reset knowledge about parent services */ /* reset knowledge about parent services */
parent_services.remove_all(); parent_services.for_each([&] (Init::Parent_service &service) {
destroy(Genode::env()->heap(), &service); });
/* reload config */ /* reload config */
try { config()->reload(); } catch (...) { } try { config()->reload(); } catch (...) { }
} }
return 0;
} }

View File

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