mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-20 22:23:16 +00:00
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:
parent
3cc2a3f085
commit
cfdbccc5c2
@ -18,4 +18,4 @@
|
||||
#include <base/internal/native_env.h>
|
||||
|
||||
|
||||
void Genode::upgrade_pd_session_quota(Genode::size_t quota) { assert(false); }
|
||||
void Genode::upgrade_pd_quota_non_blocking(Genode::size_t quota) { assert(false); }
|
||||
|
@ -15,21 +15,21 @@
|
||||
#include <base/service.h>
|
||||
#include <base/heap.h>
|
||||
|
||||
/* Core includes */
|
||||
/* core includes */
|
||||
#include <platform_services.h>
|
||||
#include <core_parent.h> /* for 'Core_service' type */
|
||||
#include <vm_root.h>
|
||||
|
||||
|
||||
/*
|
||||
* Add ARM virtualization specific vm service
|
||||
*/
|
||||
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep,
|
||||
Genode::Sliced_heap *sh,
|
||||
Genode::Service_registry *ls)
|
||||
void Genode::platform_add_local_services(Rpc_entrypoint *ep,
|
||||
Sliced_heap *sh,
|
||||
Registry<Service> *services)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
static Vm_root vm_root(ep, sh);
|
||||
static Local_service vm_ls(Vm_session::service_name(), &vm_root);
|
||||
ls->insert(&vm_ls);
|
||||
static Core_service<Vm_session_component> vm_service(*services, vm_root);
|
||||
}
|
||||
|
@ -18,19 +18,17 @@
|
||||
/* Core includes */
|
||||
#include <platform.h>
|
||||
#include <platform_services.h>
|
||||
#include <core_parent.h>
|
||||
#include <vm_root.h>
|
||||
|
||||
|
||||
/*
|
||||
* Add TrustZone specific vm service
|
||||
*/
|
||||
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep,
|
||||
Genode::Sliced_heap *sh,
|
||||
Genode::Service_registry *ls)
|
||||
void Genode::platform_add_local_services(Rpc_entrypoint *ep,
|
||||
Sliced_heap *sliced_heap,
|
||||
Registry<Service> *local_services)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
static Vm_root vm_root(ep, sh);
|
||||
static Local_service vm_ls(Vm_session::service_name(), &vm_root);
|
||||
ls->insert(&vm_ls);
|
||||
static Vm_root vm_root(ep, sliced_heap);
|
||||
static Core_service<Vm_session_component> vm_service(*local_services, vm_root);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
/* Genode includes */
|
||||
#include <base/service.h>
|
||||
|
||||
/* Core includes */
|
||||
/* core includes */
|
||||
#include <core_env.h>
|
||||
#include <platform.h>
|
||||
#include <platform_services.h>
|
||||
@ -24,18 +24,15 @@
|
||||
/*
|
||||
* Add I/O port service and virtualization specific vm service
|
||||
*/
|
||||
void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep,
|
||||
Genode::Sliced_heap *sh,
|
||||
Genode::Service_registry *ls)
|
||||
void Genode::platform_add_local_services(Rpc_entrypoint *ep,
|
||||
Sliced_heap *sh,
|
||||
Registry<Service> *services)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
static Vm_root vm_root(ep, sh);
|
||||
static Local_service vm_ls(Vm_session::service_name(), &vm_root);
|
||||
static Core_service<Vm_session_component> vm_ls(*services, vm_root);
|
||||
|
||||
static Io_port_root io_port_root(core_env()->pd_session(),
|
||||
platform()->io_port_alloc(), sh);
|
||||
static Local_service io_port_ls(Io_port_session::service_name(),
|
||||
&io_port_root);
|
||||
ls->insert(&vm_ls);
|
||||
ls->insert(&io_port_ls);
|
||||
static Core_service<Io_port_session_component> io_port_ls(*services,
|
||||
io_port_root);
|
||||
}
|
||||
|
@ -20,9 +20,13 @@
|
||||
namespace Genode
|
||||
{
|
||||
/**
|
||||
* Upgrade quota of the PD session within my Genode environment
|
||||
* Upgrade quota of the PD session within my Genode environment non-blocking
|
||||
*
|
||||
* This function doesn't lock the environment when upgrading. This is
|
||||
* needed when doing upgrades in situations where the environment is
|
||||
* already locked due to the operation that triggered the upgrade.
|
||||
*/
|
||||
void upgrade_pd_session_quota(Genode::size_t);
|
||||
void upgrade_pd_quota_non_blocking(size_t);
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__BASE__INTERNAL__NATIVE_ENV_H_ */
|
||||
|
@ -12,18 +12,14 @@
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <pd_session/client.h>
|
||||
#include <base/env.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/globals.h>
|
||||
#include <base/internal/native_env.h>
|
||||
|
||||
|
||||
void Genode::upgrade_pd_session_quota(Genode::size_t quota)
|
||||
void Genode::upgrade_pd_quota_non_blocking(size_t quota)
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "ram_quota=%lu", quota);
|
||||
Pd_session_capability cap =
|
||||
*static_cast<Pd_session_client*>(env()->pd_session());
|
||||
env()->parent()->upgrade(cap, buf);
|
||||
internal_env().parent().upgrade(Parent::Env::pd(),
|
||||
String<64>("ram_quota=", quota).string());
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
|
||||
}
|
||||
|
||||
},
|
||||
[&] () { upgrade_pd_session_quota(3*4096); });
|
||||
[&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); });
|
||||
|
||||
return Rpc_exception_code(utcb.exception_code());
|
||||
}
|
||||
@ -154,7 +154,7 @@ Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &,
|
||||
default: break;
|
||||
}
|
||||
},
|
||||
[&] () { upgrade_pd_session_quota(3*4096); });
|
||||
[&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); });
|
||||
|
||||
copy_utcb_to_msg(utcb, request_msg);
|
||||
|
||||
|
@ -20,7 +20,9 @@
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/native_utcb.h>
|
||||
#include <base/internal/native_env.h>
|
||||
#include <base/internal/capability_space.h>
|
||||
#include <base/internal/globals.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
@ -63,12 +65,10 @@ void Signal_transmitter::submit(unsigned cnt)
|
||||
Signal_receiver::Signal_receiver()
|
||||
{
|
||||
retry<Pd_session::Out_of_metadata>(
|
||||
[&] () {
|
||||
_cap = env()->pd_session()->alloc_signal_source();
|
||||
},
|
||||
[&] () { _cap = internal_env().pd().alloc_signal_source(); },
|
||||
[&] () {
|
||||
log("upgrading quota donation for PD session");
|
||||
env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K");
|
||||
internal_env().upgrade(Parent::Env::pd(), "ram_quota=8K");
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -104,11 +104,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c)
|
||||
_contexts.insert(&c->_receiver_le);
|
||||
return c->_cap;
|
||||
},
|
||||
[&] () {
|
||||
log("upgrading quota donation for PD session");
|
||||
env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K");
|
||||
}
|
||||
);
|
||||
[&] () { upgrade_pd_quota_non_blocking(1024 * sizeof(addr_t)); });
|
||||
|
||||
return c->_cap;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <core_pd_session.h>
|
||||
#include <ram_session_component.h>
|
||||
#include <core_pd_session.h>
|
||||
#include <base/service.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/platform_env.h>
|
||||
@ -138,8 +139,6 @@ namespace Genode {
|
||||
|
||||
typedef Synchronized_ram_session<Ram_session_component> Core_ram_session;
|
||||
|
||||
Core_parent _core_parent;
|
||||
|
||||
/*
|
||||
* Initialize the stack area before creating the first thread,
|
||||
* which happens to be the '_entrypoint'.
|
||||
@ -163,6 +162,10 @@ namespace Genode {
|
||||
Heap _heap;
|
||||
Ram_session_capability const _ram_session_cap;
|
||||
|
||||
Registry<Service> _services;
|
||||
|
||||
Core_parent _core_parent { _heap, _services };
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -210,6 +213,8 @@ namespace Genode {
|
||||
warning(__FILE__, ":", __LINE__, " not implemented");
|
||||
return Cpu_session_capability();
|
||||
}
|
||||
|
||||
Registry<Service> &services() { return _services; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -23,7 +23,11 @@
|
||||
/* base-internal includes */
|
||||
#include <base/internal/expanding_parent_client.h>
|
||||
|
||||
namespace Genode { class Local_parent; }
|
||||
namespace Genode {
|
||||
|
||||
class Local_session;
|
||||
class Local_parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -42,6 +46,7 @@ class Genode::Local_parent : public Expanding_parent_client
|
||||
private:
|
||||
|
||||
Allocator &_alloc;
|
||||
Id_space<Client> _local_sessions_id_space;
|
||||
|
||||
public:
|
||||
|
||||
@ -49,10 +54,9 @@ class Genode::Local_parent : public Expanding_parent_client
|
||||
** Parent interface **
|
||||
**********************/
|
||||
|
||||
Session_capability session(Service_name const &,
|
||||
Session_args const &,
|
||||
Affinity const & = Affinity());
|
||||
void close(Session_capability);
|
||||
Session_capability session(Client::Id, Service_name const &, Session_args const &,
|
||||
Affinity const & = Affinity()) override;
|
||||
Close_result close(Client::Id) override;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -19,17 +19,22 @@
|
||||
#include <base/allocator.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/local_session.h>
|
||||
#include <base/internal/region_map_mmap.h>
|
||||
#include <base/internal/local_capability.h>
|
||||
|
||||
namespace Genode { struct Local_rm_session; }
|
||||
|
||||
|
||||
struct Genode::Local_rm_session : Rm_session
|
||||
struct Genode::Local_rm_session : Rm_session, Local_session
|
||||
{
|
||||
Allocator &md_alloc;
|
||||
|
||||
Local_rm_session(Allocator &md_alloc) : md_alloc(md_alloc) { }
|
||||
Local_rm_session(Allocator &md_alloc, Id_space<Parent::Client> &id_space,
|
||||
Parent::Client::Id id)
|
||||
:
|
||||
Local_session(id_space, id, *this), md_alloc(md_alloc)
|
||||
{ }
|
||||
|
||||
Capability<Region_map> create(size_t size)
|
||||
{
|
||||
|
44
repos/base-linux/src/include/base/internal/local_session.h
Normal file
44
repos/base-linux/src/include/base/internal/local_session.h
Normal 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_ */
|
@ -69,9 +69,9 @@ class Genode::Platform_env_base : public Env_deprecated
|
||||
Pd_session_capability pd_cap)
|
||||
:
|
||||
_ram_session_cap(ram_cap),
|
||||
_ram_session_client(_ram_session_cap),
|
||||
_ram_session_client(_ram_session_cap, Parent::Env::ram()),
|
||||
_cpu_session_cap(cpu_cap),
|
||||
_cpu_session_client(cpu_cap),
|
||||
_cpu_session_client(cpu_cap, Parent::Env::cpu()),
|
||||
_region_map_mmap(false),
|
||||
_pd_session_cap(pd_cap),
|
||||
_local_pd_session(_pd_session_cap)
|
||||
|
@ -59,37 +59,44 @@ bool Region_map_mmap::_dataspace_writable(Dataspace_capability ds)
|
||||
** Local_parent **
|
||||
******************/
|
||||
|
||||
Session_capability Local_parent::session(Service_name const &service_name,
|
||||
Session_capability Local_parent::session(Parent::Client::Id id,
|
||||
Service_name const &service_name,
|
||||
Session_args const &args,
|
||||
Affinity const &affinity)
|
||||
{
|
||||
if (strcmp(service_name.string(), Rm_session::service_name()) == 0)
|
||||
{
|
||||
Local_rm_session *session = new (_alloc) Local_rm_session(_alloc);
|
||||
if (strcmp(service_name.string(), Rm_session::service_name()) == 0) {
|
||||
|
||||
return Local_capability<Session>::local_cap(session);
|
||||
Local_rm_session *local_rm_session = new (_alloc)
|
||||
Local_rm_session(_alloc, _local_sessions_id_space, id);
|
||||
|
||||
return local_rm_session->local_session_cap();
|
||||
}
|
||||
|
||||
return Expanding_parent_client::session(service_name, args, affinity);
|
||||
return Expanding_parent_client::session(id, service_name, args, affinity);
|
||||
}
|
||||
|
||||
|
||||
void Local_parent::close(Session_capability session)
|
||||
Parent::Close_result Local_parent::close(Client::Id id)
|
||||
{
|
||||
auto close_local_fn = [&] (Local_session &local_session)
|
||||
{
|
||||
Capability<Rm_session> rm =
|
||||
static_cap_cast<Rm_session>(local_session.local_session_cap());
|
||||
destroy(_alloc, Local_capability<Rm_session>::deref(rm));
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle non-local capabilities
|
||||
* Local RM sessions are present in the '_local_sessions_id_space'. If the
|
||||
* apply succeeds, 'id' referred to the local session. Otherwise, we
|
||||
* forward the request to the parent.
|
||||
*/
|
||||
if (session.valid()) {
|
||||
Parent_client::close(session);
|
||||
return;
|
||||
try {
|
||||
_local_sessions_id_space.apply<Local_session>(id, close_local_fn);
|
||||
return CLOSE_DONE;
|
||||
}
|
||||
catch (Id_space<Client>::Unknown_id) { }
|
||||
|
||||
/*
|
||||
* Detect capability to local RM session
|
||||
*/
|
||||
Capability<Rm_session> rm = static_cap_cast<Rm_session>(session);
|
||||
|
||||
destroy(_alloc, Local_capability<Rm_session>::deref(rm));
|
||||
return Parent_client::close(id);
|
||||
}
|
||||
|
||||
|
||||
@ -148,9 +155,9 @@ Local_parent &Platform_env::_parent()
|
||||
|
||||
Platform_env::Platform_env()
|
||||
:
|
||||
Platform_env_base(static_cap_cast<Ram_session>(_parent().session("Env::ram_session", "")),
|
||||
static_cap_cast<Cpu_session>(_parent().session("Env::cpu_session", "")),
|
||||
static_cap_cast<Pd_session> (_parent().session("Env::pd_session", ""))),
|
||||
Platform_env_base(static_cap_cast<Ram_session>(_parent().session_cap(Parent::Env::ram())),
|
||||
static_cap_cast<Cpu_session>(_parent().session_cap(Parent::Env::cpu())),
|
||||
static_cap_cast<Pd_session> (_parent().session_cap(Parent::Env::pd()))),
|
||||
_heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()),
|
||||
_emergency_ram_ds(ram_session()->alloc(_emergency_ram_size()))
|
||||
{
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include <base/rpc_server.h>
|
||||
#include <pd_session/client.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/globals.h>
|
||||
|
||||
/* NOVA-specific part of the PD session interface */
|
||||
#include <nova_native_pd/client.h>
|
||||
|
||||
@ -37,11 +40,7 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd, Native_capabili
|
||||
return native_pd.alloc_rpc_cap(ep, entry, 0);
|
||||
},
|
||||
[&] () {
|
||||
Pd_session_client *client =
|
||||
dynamic_cast<Pd_session_client*>(&pd);
|
||||
|
||||
if (client)
|
||||
env()->parent()->upgrade(*client, "ram_quota=16K");
|
||||
internal_env().upgrade(Parent::Env::pd(), "ram_quota=16K");
|
||||
});
|
||||
|
||||
native_pd.imprint_rpc_cap(new_obj_cap, new_obj_cap.local_name());
|
||||
|
@ -18,11 +18,14 @@
|
||||
#include <base/heap.h>
|
||||
#include <base/service.h>
|
||||
#include <base/lock.h>
|
||||
#include <base/local_connection.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <ram_session/capability.h>
|
||||
#include <ram_session/connection.h>
|
||||
#include <region_map/client.h>
|
||||
#include <pd_session/client.h>
|
||||
#include <cpu_session/client.h>
|
||||
#include <pd_session/connection.h>
|
||||
#include <cpu_session/connection.h>
|
||||
#include <log_session/connection.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <parent/capability.h>
|
||||
|
||||
namespace Genode {
|
||||
@ -41,44 +44,47 @@ namespace Genode {
|
||||
*/
|
||||
struct Genode::Child_policy
|
||||
{
|
||||
typedef String<64> Name;
|
||||
typedef String<64> Binary_name;
|
||||
typedef String<64> Linker_name;
|
||||
|
||||
virtual ~Child_policy() { }
|
||||
|
||||
/**
|
||||
* Return process name of the child
|
||||
* Name of the child used as the child's label prefix
|
||||
*/
|
||||
virtual const char *name() const = 0;
|
||||
virtual Name name() const = 0;
|
||||
|
||||
/**
|
||||
* ROM module name of the binary to start
|
||||
*/
|
||||
virtual Binary_name binary_name() const { return name(); }
|
||||
|
||||
/**
|
||||
* ROM module name of the dynamic linker
|
||||
*/
|
||||
virtual Linker_name linker_name() const { return "ld.lib.so"; }
|
||||
|
||||
/**
|
||||
* Determine service to provide a session request
|
||||
*
|
||||
* \return Service to be contacted for the new session, or
|
||||
* 0 if session request could not be resolved
|
||||
* \return service to be contacted for the new session
|
||||
*
|
||||
* \throw Parent::Service_denied
|
||||
*/
|
||||
virtual Service *resolve_session_request(const char * /*service_name*/,
|
||||
const char * /*args*/)
|
||||
{ return 0; }
|
||||
virtual Service &resolve_session_request(Service::Name const &,
|
||||
Session_state::Args const &) = 0;
|
||||
|
||||
/**
|
||||
* Apply transformations to session arguments
|
||||
*/
|
||||
virtual void filter_session_args(const char * /*service*/,
|
||||
virtual void filter_session_args(Service::Name const &,
|
||||
char * /*args*/, size_t /*args_len*/) { }
|
||||
|
||||
/**
|
||||
* Register a service provided by the child
|
||||
*
|
||||
* \param name service name
|
||||
* \param root interface for creating sessions for the service
|
||||
* \param alloc allocator to be used for child-specific
|
||||
* meta-data allocations
|
||||
* \return true if announcement succeeded, or false if
|
||||
* child is not permitted to announce service
|
||||
*/
|
||||
virtual bool announce_service(const char * /*name*/,
|
||||
Root_capability /*root*/,
|
||||
Allocator * /*alloc*/,
|
||||
Server * /*server*/)
|
||||
{ return false; }
|
||||
virtual void announce_service(Service::Name const &) { }
|
||||
|
||||
/**
|
||||
* Apply session affinity policy
|
||||
@ -91,11 +97,6 @@ struct Genode::Child_policy
|
||||
return affinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister services that had been provided by the child
|
||||
*/
|
||||
virtual void unregister_services() { }
|
||||
|
||||
/**
|
||||
* Exit child
|
||||
*/
|
||||
@ -110,8 +111,8 @@ struct Genode::Child_policy
|
||||
* The RAM session returned by this method is used for session-quota
|
||||
* transfers.
|
||||
*/
|
||||
virtual Ram_session *ref_ram_session() { return env()->ram_session(); }
|
||||
virtual Ram_session_capability ref_ram_cap() const { return env()->ram_session_cap(); }
|
||||
virtual Ram_session &ref_ram() = 0;
|
||||
virtual Ram_session_capability ref_ram_cap() const = 0;
|
||||
|
||||
/**
|
||||
* Respond to the release of resources by the child
|
||||
@ -125,6 +126,56 @@ struct Genode::Child_policy
|
||||
* Take action on additional resource needs by the child
|
||||
*/
|
||||
virtual void resource_request(Parent::Resource_args const &) { }
|
||||
|
||||
/**
|
||||
* Initialize the child's RAM session
|
||||
*
|
||||
* The function must define the child's reference account and transfer
|
||||
* the child's initial RAM quota.
|
||||
*/
|
||||
virtual void init(Ram_session &, Capability<Ram_session>) = 0;
|
||||
|
||||
/**
|
||||
* Initialize the child's CPU session
|
||||
*
|
||||
* The function may install an exception signal handler or assign CPU quota
|
||||
* to the child.
|
||||
*/
|
||||
virtual void init(Cpu_session &, Capability<Cpu_session>) { }
|
||||
|
||||
/**
|
||||
* Initialize the child's PD session
|
||||
*
|
||||
* The function may install a region-map fault handler for the child's
|
||||
* address space ('Pd_session::address_space');.
|
||||
*/
|
||||
virtual void init(Pd_session &, Capability<Pd_session>) { }
|
||||
|
||||
class Nonexistent_id_space : Exception { };
|
||||
|
||||
/**
|
||||
* ID space for sessions provided by the child
|
||||
*
|
||||
* \throw Nonexistent_id_space
|
||||
*/
|
||||
virtual Id_space<Parent::Server> &server_id_space() { throw Nonexistent_id_space(); }
|
||||
|
||||
/**
|
||||
* Return region map for the child's address space
|
||||
*
|
||||
* \param pd the child's PD session capability
|
||||
*
|
||||
* By default, the function returns a 'nullptr'. In this case, the 'Child'
|
||||
* interacts with the address space of the child's PD session via RPC calls
|
||||
* to the 'Pd_session::address_space'.
|
||||
*
|
||||
* By overriding the default, those RPC calls can be omitted, which is
|
||||
* useful if the child's PD session (including the PD's address space) is
|
||||
* virtualized by the parent. If the virtual PD session is served by the
|
||||
* same entrypoint as the child's parent interface, an RPC call to 'pd'
|
||||
* would otherwise produce a deadlock.
|
||||
*/
|
||||
virtual Region_map *address_space(Pd_session &) { return nullptr; }
|
||||
};
|
||||
|
||||
|
||||
@ -150,9 +201,11 @@ struct Genode::Child_policy
|
||||
* to our account, and subsequently transfer the same amount from our
|
||||
* account to the client.
|
||||
*/
|
||||
class Genode::Child : protected Rpc_object<Parent>
|
||||
class Genode::Child : protected Rpc_object<Parent>,
|
||||
Session_state::Ready_callback,
|
||||
Session_state::Closed_callback
|
||||
{
|
||||
public:
|
||||
private:
|
||||
|
||||
struct Initial_thread_base
|
||||
{
|
||||
@ -192,52 +245,114 @@ class Genode::Child : protected Rpc_object<Parent>
|
||||
Capability<Cpu_thread> cap() { return _cap; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
class Session;
|
||||
|
||||
/* PD session representing the protection domain of the child */
|
||||
Pd_session_capability _pd;
|
||||
|
||||
/* RAM session that contains the quota of the child */
|
||||
Ram_session_capability _ram;
|
||||
|
||||
/* CPU session that contains the quota of the child */
|
||||
Cpu_session_capability _cpu;
|
||||
|
||||
/* services where the PD, RAM, and CPU resources come from */
|
||||
Service &_pd_service;
|
||||
Service &_ram_service;
|
||||
Service &_cpu_service;
|
||||
|
||||
/* heap for child-specific allocations using the child's quota */
|
||||
Heap _heap;
|
||||
|
||||
Rpc_entrypoint &_entrypoint;
|
||||
Parent_capability _parent_cap;
|
||||
|
||||
/* child policy */
|
||||
Child_policy &_policy;
|
||||
Child_policy &_policy;
|
||||
|
||||
/* sessions opened by the child */
|
||||
Lock _lock; /* protect list manipulation */
|
||||
Object_pool<Session> _session_pool;
|
||||
List<Session> _session_list;
|
||||
Id_space<Client> _id_space;
|
||||
|
||||
/* server role */
|
||||
Server _server;
|
||||
typedef Session_state::Args Args;
|
||||
|
||||
/* session-argument buffer */
|
||||
char _args[Parent::Session_args::MAX_SIZE];
|
||||
template <typename CONNECTION>
|
||||
struct Env_connection
|
||||
{
|
||||
typedef String<64> Label;
|
||||
|
||||
Args const _args;
|
||||
Service &_service;
|
||||
Local_connection<CONNECTION> _connection;
|
||||
|
||||
/**
|
||||
* Construct session arguments with the child policy applied
|
||||
*/
|
||||
Args _construct_args(Child_policy &policy, Label const &label)
|
||||
{
|
||||
/* copy original arguments into modifiable buffer */
|
||||
char buf[Session_state::Args::capacity()];
|
||||
buf[0] = 0;
|
||||
|
||||
/* supply label as session argument */
|
||||
if (label.valid())
|
||||
Arg_string::set_arg_string(buf, sizeof(buf), "label", label.string());
|
||||
|
||||
/* apply policy to argument buffer */
|
||||
policy.filter_session_args(CONNECTION::service_name(), buf, sizeof(buf));
|
||||
|
||||
return Session_state::Args(Cstring(buf));
|
||||
}
|
||||
|
||||
Env_connection(Child_policy &policy, Id_space<Parent::Client> &id_space,
|
||||
Id_space<Parent::Client>::Id id, Label const &label = Label())
|
||||
:
|
||||
_args(_construct_args(policy, label)),
|
||||
_service(policy.resolve_session_request(CONNECTION::service_name(), _args)),
|
||||
_connection(_service, id_space, id, _args,
|
||||
policy.filter_session_affinity(Affinity()))
|
||||
{ }
|
||||
|
||||
typedef typename CONNECTION::Session_type SESSION;
|
||||
|
||||
SESSION &session() { return _connection.session(); }
|
||||
Capability<SESSION> cap() const { return _connection.cap(); }
|
||||
};
|
||||
|
||||
Env_connection<Ram_connection> _ram { _policy,
|
||||
_id_space, Parent::Env::ram() };
|
||||
|
||||
Env_connection<Pd_connection> _pd { _policy,
|
||||
_id_space, Parent::Env::pd() };
|
||||
|
||||
Env_connection<Cpu_connection> _cpu { _policy,
|
||||
_id_space, Parent::Env::cpu() };
|
||||
|
||||
Env_connection<Log_connection> _log { _policy,
|
||||
_id_space, Parent::Env::log() };
|
||||
|
||||
Env_connection<Rom_connection> _binary { _policy,
|
||||
_id_space, Parent::Env::binary(), _policy.binary_name() };
|
||||
|
||||
Lazy_volatile_object<Env_connection<Rom_connection> > _linker { _policy,
|
||||
_id_space, Parent::Env::linker(), _policy.linker_name() };
|
||||
|
||||
/* call 'Child_policy::init' methods for the environment sessions */
|
||||
void _init_env_sessions()
|
||||
{
|
||||
_policy.init(_ram.session(), _ram.cap());
|
||||
_policy.init(_cpu.session(), _cpu.cap());
|
||||
_policy.init(_pd.session(), _pd.cap());
|
||||
}
|
||||
bool const _env_sessions_initialized = ( _init_env_sessions(), true );
|
||||
|
||||
Dataspace_capability _linker_dataspace()
|
||||
{
|
||||
try {
|
||||
_linker.construct(_policy, _id_space,
|
||||
Parent::Env::linker(), _policy.linker_name());
|
||||
return _linker->session().dataspace();
|
||||
}
|
||||
catch (Parent::Service_denied) { return Rom_dataspace_capability(); }
|
||||
}
|
||||
|
||||
/* heap for child-specific allocations using the child's quota */
|
||||
Heap _heap;
|
||||
|
||||
/* factory for dynamically created session-state objects */
|
||||
Session_state::Factory _session_factory { _heap };
|
||||
|
||||
Rpc_entrypoint &_entrypoint;
|
||||
Parent_capability _parent_cap;
|
||||
|
||||
/* signal handlers registered by the child */
|
||||
Signal_context_capability _resource_avail_sigh;
|
||||
Signal_context_capability _yield_sigh;
|
||||
Signal_context_capability _session_sigh;
|
||||
|
||||
/* arguments fetched by the child in response to a yield signal */
|
||||
Lock _yield_request_lock;
|
||||
Resource_args _yield_request_args;
|
||||
|
||||
Initial_thread _initial_thread { _cpu.session(), _pd.cap(), "initial" };
|
||||
|
||||
struct Process
|
||||
{
|
||||
class Missing_dynamic_linker : Exception { };
|
||||
@ -313,30 +428,19 @@ class Genode::Child : protected Rpc_object<Parent>
|
||||
|
||||
Process _process;
|
||||
|
||||
/**
|
||||
* Attach session information to a child
|
||||
*
|
||||
* \throw Ram_session::Quota_exceeded the child's heap partition cannot
|
||||
* hold the session meta data
|
||||
*/
|
||||
void _add_session(const Session &s);
|
||||
void _revert_quota_and_destroy(Session_state &);
|
||||
|
||||
Close_result _close(Session_state &);
|
||||
|
||||
/**
|
||||
* Close session and revert quota donation associated with it
|
||||
* Session_state::Ready_callback
|
||||
*/
|
||||
void _remove_session(Session *s);
|
||||
|
||||
void _close(Session *s);
|
||||
void session_ready(Session_state &session) override;
|
||||
|
||||
/**
|
||||
* Return service interface targetting the parent
|
||||
*
|
||||
* The service returned by this method is used as default
|
||||
* provider for the RAM, CPU, and RM resources of the child. It is
|
||||
* solely used for targeting resource donations during
|
||||
* 'Parent::upgrade_quota()' calls.
|
||||
* Session_state::Closed_callback
|
||||
*/
|
||||
static Service &_parent_service();
|
||||
void session_closed(Session_state &) override;
|
||||
|
||||
public:
|
||||
|
||||
@ -357,60 +461,18 @@ class Genode::Child : protected Rpc_object<Parent>
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param elf_ds dataspace that contains the ELF binary
|
||||
* \param ldso_ds dataspace that contains the dynamic linker,
|
||||
* started if 'elf_ds' is a dynamically linked
|
||||
* executable
|
||||
* \param pd_cap capability of the new protection domain,
|
||||
* used as argument for creating the initial
|
||||
* thread, and handed out to the child as its
|
||||
* environment
|
||||
* \param pd PD session used for assigning the parent
|
||||
* capability of the new process
|
||||
* \param ram_cap RAM session capability handed out to the
|
||||
* child as its environment
|
||||
* \param ram RAM session used to allocate the BSS and
|
||||
* DATA segments and as backing store for the
|
||||
* local heap partition to keep child-specific
|
||||
* meta data
|
||||
* \param cpu_cap CPU session capability handed out to the
|
||||
* child as its environment
|
||||
* \param initial_thread initial thread of the new protection domain
|
||||
* \param local_rm local address space
|
||||
* \param remote_rm address space of new protection domain
|
||||
* \param pd_service provider of the 'pd' session
|
||||
* \param ram_service provider of the 'ram' session
|
||||
* \param cpu_service provider of the 'cpu' session
|
||||
* \param rm local address space, usually 'env.rm()'
|
||||
* \param entrypoint entrypoint used to serve the parent interface of
|
||||
* the child
|
||||
* \param policy policy for the child
|
||||
*
|
||||
* \throw Parent::Service_denied if the initial sessions for the
|
||||
* child's environment could not be
|
||||
* opened
|
||||
* \throw Ram_session::Alloc_failed
|
||||
* \throw Process_startup_failed
|
||||
*
|
||||
* Usually, the pairs of 'pd' and 'pd_cap', 'initial_thread' and
|
||||
* 'cpu_cap', 'ram' and 'ram_cap' belong to each other. References to
|
||||
* the session interfaces are passed as separate arguments in addition
|
||||
* to the capabilities to allow the creator of a child to operate on
|
||||
* locally implemented interfaces during the child initialization.
|
||||
*
|
||||
* The 'ram_service', 'cpu_service', and 'pd_service' arguments are
|
||||
* needed to direct quota upgrades referring to the resources of
|
||||
* the child environment. By default, we expect that these
|
||||
* resources are provided by the parent.
|
||||
*/
|
||||
Child(Dataspace_capability elf_ds,
|
||||
Dataspace_capability ldso_ds,
|
||||
Pd_session_capability pd_cap,
|
||||
Pd_session &pd,
|
||||
Ram_session_capability ram_cap,
|
||||
Ram_session &ram,
|
||||
Cpu_session_capability cpu_cap,
|
||||
Initial_thread_base &initial_thread,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
Rpc_entrypoint &entrypoint,
|
||||
Child_policy &policy,
|
||||
Service &pd_service = _parent_service(),
|
||||
Service &ram_service = _parent_service(),
|
||||
Service &cpu_service = _parent_service());
|
||||
Child(Region_map &rm, Rpc_entrypoint &entrypoint, Child_policy &policy);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
@ -421,25 +483,40 @@ class Genode::Child : protected Rpc_object<Parent>
|
||||
virtual ~Child();
|
||||
|
||||
/**
|
||||
* Return heap that uses the child's quota
|
||||
* RAM quota unconditionally consumed by the child's environment
|
||||
*/
|
||||
Allocator *heap() { return &_heap; }
|
||||
|
||||
Pd_session_capability pd_session_cap() const { return _pd; }
|
||||
Ram_session_capability ram_session_cap() const { return _ram; }
|
||||
Cpu_session_capability cpu_session_cap() const { return _cpu; }
|
||||
Parent_capability parent_cap() const { return cap(); }
|
||||
static size_t env_ram_quota()
|
||||
{
|
||||
return Cpu_connection::RAM_QUOTA + Ram_connection::RAM_QUOTA +
|
||||
Pd_connection::RAM_QUOTA + Log_connection::RAM_QUOTA +
|
||||
2*Rom_connection::RAM_QUOTA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard all sessions to specified service
|
||||
*
|
||||
* When this method is called, we assume the server protection
|
||||
* domain to be dead and all that all server quota was already
|
||||
* transferred back to our own 'env()->ram_session()' account. Note
|
||||
* that the specified server object may not exist anymore. We do
|
||||
* not de-reference the server argument in here!
|
||||
* Deduce session costs from usable ram quota
|
||||
*/
|
||||
void revoke_server(const Server *server);
|
||||
static size_t effective_ram_quota(size_t const ram_quota)
|
||||
{
|
||||
if (ram_quota < env_ram_quota())
|
||||
return 0;
|
||||
|
||||
return ram_quota - env_ram_quota();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return heap that uses the child's quota
|
||||
*/
|
||||
Allocator &heap() { return _heap; }
|
||||
|
||||
Ram_session_capability ram_session_cap() const { return _ram.cap(); }
|
||||
|
||||
Parent_capability parent_cap() const { return cap(); }
|
||||
|
||||
Ram_session &ram() { return _ram.session(); }
|
||||
Cpu_session &cpu() { return _cpu.session(); }
|
||||
Pd_session &pd() { return _pd .session(); }
|
||||
|
||||
Session_state::Factory &session_factory() { return _session_factory; }
|
||||
|
||||
/**
|
||||
* Instruct the child to yield resources
|
||||
@ -461,12 +538,16 @@ class Genode::Child : protected Rpc_object<Parent>
|
||||
** Parent interface **
|
||||
**********************/
|
||||
|
||||
void announce(Service_name const &, Root_capability) override;
|
||||
Session_capability session(Service_name const &, Session_args const &,
|
||||
Affinity const &) override;
|
||||
void upgrade(Session_capability, Upgrade_args const &) override;
|
||||
void close(Session_capability) override;
|
||||
void announce(Service_name const &) override;
|
||||
void session_sigh(Signal_context_capability) override;
|
||||
Session_capability session(Client::Id, Service_name const &,
|
||||
Session_args const &, Affinity const &) override;
|
||||
Session_capability session_cap(Client::Id) override;
|
||||
Upgrade_result upgrade(Client::Id, Upgrade_args const &) override;
|
||||
Close_result close(Client::Id) override;
|
||||
void exit(int) override;
|
||||
void session_response(Server::Id, Session_response) override;
|
||||
void deliver_session_cap(Server::Id, Session_capability) override;
|
||||
Thread_capability main_thread_cap() const override;
|
||||
void resource_avail_sigh(Signal_context_capability) override;
|
||||
void resource_request(Resource_args const &) override;
|
||||
|
@ -16,20 +16,56 @@
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/capability.h>
|
||||
#include <base/log.h>
|
||||
|
||||
namespace Genode { template <typename> class Connection; }
|
||||
namespace Genode {
|
||||
|
||||
class Connection_base;
|
||||
template <typename> class Connection;
|
||||
}
|
||||
|
||||
|
||||
class Genode::Connection_base : public Noncopyable
|
||||
{
|
||||
protected:
|
||||
|
||||
Env &_env;
|
||||
|
||||
Parent::Client _parent_client;
|
||||
|
||||
Id_space<Parent::Client>::Element const _id_space_element;
|
||||
|
||||
void _block_for_session_response();
|
||||
|
||||
public:
|
||||
|
||||
Connection_base(Env &env)
|
||||
:
|
||||
_env(env),
|
||||
_id_space_element(_parent_client, _env.id_space())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Legacy constructor
|
||||
*
|
||||
* \noapi
|
||||
*/
|
||||
Connection_base();
|
||||
|
||||
void upgrade_ram(size_t bytes)
|
||||
{
|
||||
String<64> const args("ram_quota=", bytes);
|
||||
_env.upgrade(_id_space_element.id(), args.string());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Representation of an open connection to a service
|
||||
*/
|
||||
template <typename SESSION_TYPE>
|
||||
class Genode::Connection : public Noncopyable
|
||||
class Genode::Connection : public Connection_base
|
||||
{
|
||||
public:
|
||||
|
||||
enum On_destruction { CLOSE = false, KEEP_OPEN = true };
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
@ -37,15 +73,12 @@ class Genode::Connection : public Noncopyable
|
||||
* 'session' method that is called before the 'Connection' is
|
||||
* constructed.
|
||||
*/
|
||||
|
||||
enum { FORMAT_STRING_SIZE = Parent::Session_args::MAX_SIZE };
|
||||
|
||||
char _session_args[FORMAT_STRING_SIZE];
|
||||
char _affinity_arg[sizeof(Affinity)];
|
||||
|
||||
Parent &_parent;
|
||||
|
||||
On_destruction _on_destruction;
|
||||
|
||||
void _session(Parent &parent,
|
||||
Affinity const &affinity,
|
||||
const char *format_args, va_list list)
|
||||
@ -63,7 +96,8 @@ class Genode::Connection : public Noncopyable
|
||||
memcpy(&affinity, _affinity_arg, sizeof(Affinity));
|
||||
|
||||
try {
|
||||
return env()->parent()->session<SESSION_TYPE>(_session_args, affinity); }
|
||||
return _env.session<SESSION_TYPE>(_id_space_element.id(),
|
||||
_session_args, affinity); }
|
||||
catch (...) {
|
||||
error(SESSION_TYPE::service_name(), "-session creation failed "
|
||||
"(", Cstring(_session_args), ")");
|
||||
@ -75,20 +109,15 @@ class Genode::Connection : public Noncopyable
|
||||
|
||||
public:
|
||||
|
||||
typedef SESSION_TYPE Session_type;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param od session policy applied when destructing the connection
|
||||
*
|
||||
* The 'op' argument defines whether the session should automatically
|
||||
* be closed by the destructor of the connection (CLOSE), or the
|
||||
* session should stay open (KEEP_OPEN). The latter is useful in
|
||||
* situations where the creator a connection merely passes the
|
||||
* session capability of the connection to another party but never
|
||||
* invokes any of the session's RPC functions.
|
||||
*/
|
||||
Connection(Env &env, Capability<SESSION_TYPE>, On_destruction od = CLOSE)
|
||||
: _parent(env.parent()), _on_destruction(od) { }
|
||||
Connection(Env &env, Capability<SESSION_TYPE>)
|
||||
:
|
||||
Connection_base(env), _cap(_request_cap())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -97,28 +126,18 @@ class Genode::Connection : public Noncopyable
|
||||
* \deprecated Use the constructor with 'Env &' as first
|
||||
* argument instead
|
||||
*/
|
||||
Connection(Capability<SESSION_TYPE>, On_destruction od = CLOSE)
|
||||
: _parent(*env()->parent()), _on_destruction(od) { }
|
||||
Connection(Capability<SESSION_TYPE>) : _cap(_request_cap()) { }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Connection()
|
||||
{
|
||||
if (_on_destruction == CLOSE)
|
||||
_parent.close(_cap);
|
||||
}
|
||||
~Connection() { _env.close(_id_space_element.id()); }
|
||||
|
||||
/**
|
||||
* Return session capability
|
||||
*/
|
||||
Capability<SESSION_TYPE> cap() const { return _cap; }
|
||||
|
||||
/**
|
||||
* Define session policy
|
||||
*/
|
||||
void on_destruction(On_destruction od) { _on_destruction = od; }
|
||||
|
||||
/**
|
||||
* Issue session request to the parent
|
||||
*/
|
||||
|
@ -73,6 +73,61 @@ struct Genode::Env
|
||||
* Once we add 'Env::upgrade', we can remove this accessor.
|
||||
*/
|
||||
virtual Pd_session_capability pd_session_cap() = 0;
|
||||
|
||||
/**
|
||||
* ID space of sessions obtained from the parent
|
||||
*/
|
||||
virtual Id_space<Parent::Client> &id_space() = 0;
|
||||
|
||||
virtual Session_capability session(Parent::Service_name const &,
|
||||
Parent::Client::Id,
|
||||
Parent::Session_args const &,
|
||||
Affinity const &) = 0;
|
||||
|
||||
/**
|
||||
* Create session to a service
|
||||
*
|
||||
* \param SESSION_TYPE session interface type
|
||||
* \param id session ID of new session
|
||||
* \param args session constructor arguments
|
||||
* \param affinity preferred CPU affinity for the session
|
||||
*
|
||||
* \throw Service_denied parent denies session request
|
||||
* \throw Quota_exceeded our own quota does not suffice for
|
||||
* the creation of the new session
|
||||
* \throw Unavailable
|
||||
*
|
||||
* This method blocks until the session is available or an error
|
||||
* occurred.
|
||||
*/
|
||||
template <typename SESSION_TYPE>
|
||||
Capability<SESSION_TYPE> session(Parent::Client::Id id,
|
||||
Parent::Session_args const &args,
|
||||
Affinity const &affinity)
|
||||
{
|
||||
Session_capability cap = session(SESSION_TYPE::service_name(),
|
||||
id, args, affinity);
|
||||
return static_cap_cast<SESSION_TYPE>(cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade session quota
|
||||
*
|
||||
* \param id ID of recipient session
|
||||
* \param args description of the amount of quota to transfer
|
||||
*
|
||||
* \throw Quota_exceeded quota could not be transferred
|
||||
*
|
||||
* The 'args' argument has the same principle format as the 'args'
|
||||
* argument of the 'session' operation.
|
||||
*/
|
||||
virtual void upgrade(Parent::Client::Id id,
|
||||
Parent::Upgrade_args const &args) = 0;
|
||||
|
||||
/**
|
||||
* Close session and block until the session is gone
|
||||
*/
|
||||
virtual void close(Parent::Client::Id) = 0;
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__BASE__ENV_H_ */
|
||||
|
135
repos/base/include/base/local_connection.h
Normal file
135
repos/base/include/base/local_connection.h
Normal 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_ */
|
@ -14,100 +14,42 @@
|
||||
#ifndef _INCLUDE__BASE__SERVICE_H_
|
||||
#define _INCLUDE__BASE__SERVICE_H_
|
||||
|
||||
#include <root/client.h>
|
||||
#include <base/log.h>
|
||||
#include <util/list.h>
|
||||
#include <ram_session/client.h>
|
||||
#include <base/env.h>
|
||||
#include <base/session_state.h>
|
||||
#include <base/log.h>
|
||||
#include <base/registry.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Client;
|
||||
class Server;
|
||||
class Service;
|
||||
class Local_service;
|
||||
template <typename> class Session_factory;
|
||||
template <typename> class Local_service;
|
||||
class Parent_service;
|
||||
class Child_service;
|
||||
class Service_registry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Client role
|
||||
*
|
||||
* A client is someone who applies for a service. If the service is not
|
||||
* available yet, we enqueue the client into a wait queue and wake him up
|
||||
* as soon as the requested service gets available.
|
||||
*/
|
||||
class Genode::Client : public List<Client>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
Cancelable_lock _service_apply_lock;
|
||||
const char *_apply_for;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { }
|
||||
|
||||
virtual ~Client() { }
|
||||
|
||||
/**
|
||||
* Set/Request service name that we are currently applying for
|
||||
*/
|
||||
void apply_for(const char *apply_for) { _apply_for = apply_for; }
|
||||
const char *apply_for() { return _apply_for; }
|
||||
|
||||
/**
|
||||
* Service wait queue support
|
||||
*/
|
||||
void sleep() { _service_apply_lock.lock(); }
|
||||
void wakeup() { _service_apply_lock.unlock(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Server role
|
||||
*
|
||||
* A server is a process that provides one or multiple services. For the
|
||||
* most part, this class is used as an opaque key to represent the server
|
||||
* role.
|
||||
*/
|
||||
class Genode::Server
|
||||
{
|
||||
private:
|
||||
|
||||
Ram_session_capability _ram;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param ram RAM session capability of the server process used,
|
||||
* for quota transfers from/to the server
|
||||
*/
|
||||
Server(Ram_session_capability ram): _ram(ram) { }
|
||||
|
||||
/**
|
||||
* Return RAM session capability of the server process
|
||||
*/
|
||||
Ram_session_capability ram_session_cap() const { return _ram; }
|
||||
};
|
||||
|
||||
|
||||
class Genode::Service : public List<Service>::Element
|
||||
class Genode::Service : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
enum { MAX_NAME_LEN = 32 };
|
||||
typedef Session_state::Name Name;
|
||||
|
||||
private:
|
||||
|
||||
char _name[MAX_NAME_LEN];
|
||||
Name const _name;
|
||||
Ram_session_capability const _ram;
|
||||
|
||||
protected:
|
||||
|
||||
typedef Session_state::Factory Factory;
|
||||
|
||||
/**
|
||||
* Return factory to use for creating 'Session_state' objects
|
||||
*/
|
||||
virtual Factory &_factory(Factory &client_factory) { return client_factory; }
|
||||
|
||||
public:
|
||||
|
||||
@ -123,52 +65,52 @@ class Genode::Service : public List<Service>::Element
|
||||
* Constructor
|
||||
*
|
||||
* \param name service name
|
||||
* \param ram RAM session to receive/withdraw session quota
|
||||
*/
|
||||
Service(const char *name) { strncpy(_name, name, sizeof(_name)); }
|
||||
Service(Name const &name, Ram_session_capability ram)
|
||||
: _name(name), _ram(ram) { }
|
||||
|
||||
virtual ~Service() { }
|
||||
|
||||
/**
|
||||
* Return service name
|
||||
*/
|
||||
const char *name() const { return _name; }
|
||||
Name const &name() const { return _name; }
|
||||
|
||||
/**
|
||||
* Create session
|
||||
* Create new session-state object
|
||||
*
|
||||
* \param args session-construction arguments
|
||||
* \param affinity preferred CPU affinity of session
|
||||
* The 'service' argument for the 'Session_state' corresponds to this
|
||||
* session state. All subsequent 'Session_state' arguments correspond
|
||||
* to the forwarded 'args'.
|
||||
*/
|
||||
template <typename... ARGS>
|
||||
Session_state &create_session(Factory &client_factory, ARGS &&... args)
|
||||
{
|
||||
return _factory(client_factory).create(*this, args...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt the immediate (synchronous) creation of a session
|
||||
*
|
||||
* \throw Invalid_args
|
||||
* \throw Unavailable
|
||||
* \throw Quota_exceeded
|
||||
* Sessions to local services and parent services are usually created
|
||||
* immediately during the dispatching of the 'Parent::session' request.
|
||||
* In these cases, it is not needed to wait for an asynchronous
|
||||
* response.
|
||||
*/
|
||||
virtual Session_capability session(char const *args,
|
||||
Affinity const &affinity) = 0;
|
||||
virtual void initiate_request(Session_state &session) = 0;
|
||||
|
||||
/**
|
||||
* Extend resource donation to an existing session
|
||||
* Wake up service to query session requests
|
||||
*/
|
||||
virtual void upgrade(Session_capability session, const char *args) = 0;
|
||||
|
||||
/**
|
||||
* Close session
|
||||
*/
|
||||
virtual void close(Session_capability /*session*/) { }
|
||||
|
||||
/**
|
||||
* Return server providing the service
|
||||
*/
|
||||
virtual Server *server() const { return 0; }
|
||||
virtual void wakeup() { }
|
||||
|
||||
/**
|
||||
* Return the RAM session to be used for trading resources
|
||||
*/
|
||||
Ram_session_capability ram_session_cap()
|
||||
Ram_session_capability ram() const
|
||||
{
|
||||
if (server())
|
||||
return server()->ram_session_cap();
|
||||
return Ram_session_capability();
|
||||
return _ram;
|
||||
}
|
||||
};
|
||||
|
||||
@ -176,36 +118,118 @@ class Genode::Service : public List<Service>::Element
|
||||
/**
|
||||
* Representation of a locally implemented service
|
||||
*/
|
||||
template <typename SESSION>
|
||||
class Genode::Local_service : public Service
|
||||
{
|
||||
public:
|
||||
|
||||
struct Factory
|
||||
{
|
||||
typedef Session_state::Args Args;
|
||||
|
||||
class Denied : Exception { };
|
||||
|
||||
/**
|
||||
* \throw Denied
|
||||
*/
|
||||
virtual SESSION &create(Args const &, Affinity) = 0;
|
||||
|
||||
virtual void upgrade(SESSION &, Args const &) = 0;
|
||||
virtual void destroy(SESSION &) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory of a local service that provides a single static session
|
||||
*/
|
||||
class Single_session_factory : public Factory
|
||||
{
|
||||
private:
|
||||
|
||||
typedef Session_state::Args Args;
|
||||
|
||||
SESSION &_s;
|
||||
|
||||
public:
|
||||
|
||||
Single_session_factory(SESSION &session) : _s(session) { }
|
||||
|
||||
SESSION &create (Args const &, Affinity) override { return _s; }
|
||||
void upgrade (SESSION &, Args const &) override { }
|
||||
void destroy (SESSION &) override { }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Root *_root;
|
||||
Factory &_factory;
|
||||
|
||||
template <typename FUNC>
|
||||
void _apply_to_rpc_obj(Session_state &session, FUNC const &fn)
|
||||
{
|
||||
SESSION *rpc_obj = dynamic_cast<SESSION *>(session.local_ptr);
|
||||
|
||||
if (rpc_obj)
|
||||
fn(*rpc_obj);
|
||||
else
|
||||
warning("local ", SESSION::service_name(), " session "
|
||||
"(", session.args(), ") has no valid RPC object");
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Local_service(const char *name, Root *root)
|
||||
: Service(name), _root(root) { }
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Local_service(Factory &factory)
|
||||
:
|
||||
Service(SESSION::service_name(), Ram_session_capability()),
|
||||
_factory(factory)
|
||||
{ }
|
||||
|
||||
Session_capability session(const char *args, Affinity const &affinity) override
|
||||
void initiate_request(Session_state &session) override
|
||||
{
|
||||
try { return _root->session(args, affinity); }
|
||||
catch (Root::Invalid_args) { throw Invalid_args(); }
|
||||
catch (Root::Unavailable) { throw Unavailable(); }
|
||||
catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
switch (session.phase) {
|
||||
|
||||
void upgrade(Session_capability session, const char *args) override
|
||||
{
|
||||
try { _root->upgrade(session, args); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
case Session_state::CREATE_REQUESTED:
|
||||
|
||||
void close(Session_capability session) override
|
||||
{
|
||||
try { _root->close(session); }
|
||||
catch (Genode::Ipc_error) { throw Blocking_canceled(); }
|
||||
try {
|
||||
SESSION &rpc_obj = _factory.create(session.args(),
|
||||
session.affinity());
|
||||
session.local_ptr = &rpc_obj;
|
||||
session.cap = rpc_obj.cap();
|
||||
session.phase = Session_state::AVAILABLE;
|
||||
}
|
||||
catch (typename Factory::Denied) {
|
||||
session.phase = Session_state::INVALID_ARGS; }
|
||||
|
||||
break;
|
||||
|
||||
case Session_state::UPGRADE_REQUESTED:
|
||||
{
|
||||
String<64> const args("ram_quota=", session.ram_upgrade);
|
||||
|
||||
_apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) {
|
||||
_factory.upgrade(rpc_obj, args.string()); });
|
||||
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
session.confirm_ram_upgrade();
|
||||
}
|
||||
break;
|
||||
|
||||
case Session_state::CLOSE_REQUESTED:
|
||||
{
|
||||
_apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) {
|
||||
_factory.destroy(rpc_obj); });
|
||||
|
||||
session.phase = Session_state::CLOSED;
|
||||
}
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -215,31 +239,88 @@ class Genode::Local_service : public Service
|
||||
*/
|
||||
class Genode::Parent_service : public Service
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* \deprecated
|
||||
*/
|
||||
Env &_env_deprecated();
|
||||
Env &_env;
|
||||
|
||||
public:
|
||||
|
||||
Parent_service(const char *name) : Service(name) { }
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Parent_service(Env &env, Service::Name const &name)
|
||||
: Service(name, Ram_session_capability()), _env(env) { }
|
||||
|
||||
Session_capability session(const char *args, Affinity const &affinity) override
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Parent_service(Service::Name const &name)
|
||||
: Service(name, Ram_session_capability()), _env(_env_deprecated()) { }
|
||||
|
||||
void initiate_request(Session_state &session) override
|
||||
{
|
||||
try { return env()->parent()->session(name(), args, affinity); }
|
||||
catch (Parent::Unavailable) {
|
||||
warning("parent has no service \"", name(), "\"");
|
||||
throw Unavailable();
|
||||
switch (session.phase) {
|
||||
|
||||
case Session_state::CREATE_REQUESTED:
|
||||
|
||||
session.id_at_parent.construct(session.parent_client,
|
||||
_env.id_space());
|
||||
try {
|
||||
session.cap = _env.session(name().string(),
|
||||
session.id_at_parent->id(),
|
||||
session.args().string(),
|
||||
session.affinity());
|
||||
|
||||
session.phase = Session_state::AVAILABLE;
|
||||
}
|
||||
catch (Parent::Quota_exceeded) {
|
||||
session.id_at_parent.destruct();
|
||||
session.phase = Session_state::INVALID_ARGS; }
|
||||
|
||||
catch (Parent::Service_denied) {
|
||||
session.id_at_parent.destruct();
|
||||
session.phase = Session_state::INVALID_ARGS; }
|
||||
|
||||
break;
|
||||
|
||||
case Session_state::UPGRADE_REQUESTED:
|
||||
{
|
||||
String<64> const args("ram_quota=", session.ram_upgrade);
|
||||
|
||||
if (!session.id_at_parent.constructed())
|
||||
error("invalid parent-session state: ", session);
|
||||
|
||||
try {
|
||||
_env.upgrade(session.id_at_parent->id(), args.string()); }
|
||||
catch (Parent::Quota_exceeded) {
|
||||
warning("quota exceeded while upgrading parent session"); }
|
||||
|
||||
session.confirm_ram_upgrade();
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
}
|
||||
break;
|
||||
|
||||
case Session_state::CLOSE_REQUESTED:
|
||||
|
||||
if (session.id_at_parent.constructed())
|
||||
_env.close(session.id_at_parent->id());
|
||||
|
||||
session.id_at_parent.destruct();
|
||||
session.phase = Session_state::CLOSED;
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
break;
|
||||
}
|
||||
catch (Parent::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
|
||||
void upgrade(Session_capability session, const char *args) override
|
||||
{
|
||||
try { env()->parent()->upgrade(session, args); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
|
||||
void close(Session_capability session) override
|
||||
{
|
||||
try { env()->parent()->close(session); }
|
||||
catch (Genode::Ipc_error) { throw Blocking_canceled(); }
|
||||
}
|
||||
};
|
||||
|
||||
@ -249,205 +330,66 @@ class Genode::Parent_service : public Service
|
||||
*/
|
||||
class Genode::Child_service : public Service
|
||||
{
|
||||
public:
|
||||
|
||||
struct Wakeup { virtual void wakeup_child_service() = 0; };
|
||||
|
||||
private:
|
||||
|
||||
Root_capability _root_cap;
|
||||
Root_client _root;
|
||||
Server *_server;
|
||||
Id_space<Parent::Server> &_server_id_space;
|
||||
|
||||
Session_state::Factory &_server_factory;
|
||||
|
||||
Wakeup &_wakeup;
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
* In contrast to local services and parent services, session-state
|
||||
* objects for child services are owned by the server. This enables
|
||||
* the server to asynchronouly respond to close requests when the
|
||||
* client is already gone.
|
||||
*/
|
||||
Factory &_factory(Factory &) override { return _server_factory; }
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param name name of service
|
||||
* \param root capability to root interface
|
||||
* \param server server process providing the service
|
||||
*/
|
||||
Child_service(const char *name,
|
||||
Root_capability root,
|
||||
Server *server)
|
||||
: Service(name), _root_cap(root), _root(root), _server(server) { }
|
||||
|
||||
Server *server() const override { return _server; }
|
||||
|
||||
Session_capability session(const char *args, Affinity const &affinity) override
|
||||
{
|
||||
if (!_root_cap.valid())
|
||||
throw Unavailable();
|
||||
|
||||
try { return _root.session(args, affinity); }
|
||||
catch (Root::Invalid_args) { throw Invalid_args(); }
|
||||
catch (Root::Unavailable) { throw Unavailable(); }
|
||||
catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
|
||||
void upgrade(Session_capability sc, const char *args) override
|
||||
{
|
||||
if (!_root_cap.valid())
|
||||
throw Unavailable();
|
||||
|
||||
try { _root.upgrade(sc, args); }
|
||||
catch (Root::Invalid_args) { throw Invalid_args(); }
|
||||
catch (Root::Unavailable) { throw Unavailable(); }
|
||||
catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
|
||||
void close(Session_capability sc) override
|
||||
{
|
||||
try { _root.close(sc); }
|
||||
catch (Genode::Ipc_error) { throw Blocking_canceled(); }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Container for holding service representations
|
||||
*/
|
||||
class Genode::Service_registry
|
||||
{
|
||||
protected:
|
||||
|
||||
Lock _service_wait_queue_lock;
|
||||
List<Client> _service_wait_queue;
|
||||
List<Service> _services;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Probe for service with specified name
|
||||
* \param factory server-side session-state factory
|
||||
* \param name name of service
|
||||
* \param ram recipient of session quota
|
||||
* \param wakeup callback to be notified on the arrival of new
|
||||
* session requests
|
||||
*
|
||||
* \param name service name
|
||||
* \param server server providing the service,
|
||||
* default (0) for any server
|
||||
*/
|
||||
Service *find(const char *name, Server *server = 0)
|
||||
Child_service(Id_space<Parent::Server> &server_id_space,
|
||||
Session_state::Factory &factory,
|
||||
Service::Name const &name,
|
||||
Ram_session_capability ram,
|
||||
Wakeup &wakeup)
|
||||
:
|
||||
Service(name, ram),
|
||||
_server_id_space(server_id_space),
|
||||
_server_factory(factory), _wakeup(wakeup)
|
||||
{ }
|
||||
|
||||
void initiate_request(Session_state &session) override
|
||||
{
|
||||
if (!name) return 0;
|
||||
if (!session.id_at_server.constructed())
|
||||
session.id_at_server.construct(session, _server_id_space);
|
||||
|
||||
Lock::Guard lock_guard(_service_wait_queue_lock);
|
||||
|
||||
for (Service *s = _services.first(); s; s = s->next())
|
||||
if (strcmp(s->name(), name) == 0
|
||||
&& (!server || s->server() == server)) return s;
|
||||
|
||||
return 0;
|
||||
session.async_client_notify = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if service name is ambiguous
|
||||
*
|
||||
* \return true if the same service is provided multiple
|
||||
* times
|
||||
*/
|
||||
bool is_ambiguous(const char *name)
|
||||
bool has_id_space(Id_space<Parent::Server> const &id_space) const
|
||||
{
|
||||
Lock::Guard lock_guard(_service_wait_queue_lock);
|
||||
|
||||
/* count number of services with the specified name */
|
||||
unsigned cnt = 0;
|
||||
for (Service *s = _services.first(); s; s = s->next())
|
||||
cnt += (strcmp(s->name(), name) == 0);
|
||||
return cnt > 1;
|
||||
return &_server_id_space == &id_space;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first service provided by specified server
|
||||
*/
|
||||
Service *find_by_server(Server *server)
|
||||
{
|
||||
Lock::Guard lock_guard(_service_wait_queue_lock);
|
||||
|
||||
for (Service *s = _services.first(); s; s = s->next())
|
||||
if (s->server() == server)
|
||||
return s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for service
|
||||
*
|
||||
* This method is called by the clients's thread when requesting a
|
||||
* session creation. It blocks if the requested service is not
|
||||
* available.
|
||||
*
|
||||
* \return service structure that matches the request or
|
||||
* 0 if the waiting was canceled.
|
||||
*/
|
||||
Service *wait_for_service(const char *name, Client *client, const char *client_name)
|
||||
{
|
||||
Service *service;
|
||||
|
||||
client->apply_for(name);
|
||||
|
||||
_service_wait_queue_lock.lock();
|
||||
_service_wait_queue.insert(client);
|
||||
_service_wait_queue_lock.unlock();
|
||||
|
||||
do {
|
||||
service = find(name);
|
||||
|
||||
/*
|
||||
* The service that we are seeking is not available today.
|
||||
* Lets sleep a night over it.
|
||||
*/
|
||||
if (!service) {
|
||||
log(client_name, ": service ", name, " not yet available - sleeping");
|
||||
|
||||
try {
|
||||
client->sleep();
|
||||
log(client_name, ": service ", name, " got available");
|
||||
} catch (Blocking_canceled) {
|
||||
log(client_name, ": cancel waiting for service");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!service);
|
||||
|
||||
/* we got what we needed, stop applying */
|
||||
_service_wait_queue_lock.lock();
|
||||
_service_wait_queue.remove(client);
|
||||
_service_wait_queue_lock.unlock();
|
||||
|
||||
client->apply_for(0);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register service
|
||||
*
|
||||
* This method is called by the server's thread.
|
||||
*/
|
||||
void insert(Service *service)
|
||||
{
|
||||
/* make new service known */
|
||||
_services.insert(service);
|
||||
|
||||
/* wake up applicants waiting for the service */
|
||||
Lock::Guard lock_guard(_service_wait_queue_lock);
|
||||
for (Client *c = _service_wait_queue.first(); c; c = c->next())
|
||||
if (strcmp(service->name(), c->apply_for()) == 0)
|
||||
c->wakeup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister service
|
||||
*/
|
||||
void remove(Service *service) { _services.remove(service); }
|
||||
|
||||
/**
|
||||
* Unregister all services
|
||||
*/
|
||||
void remove_all()
|
||||
{
|
||||
while (_services.first())
|
||||
remove(_services.first());
|
||||
}
|
||||
void wakeup() override { _wakeup.wakeup_child_service(); }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__BASE__SERVICE_H_ */
|
||||
|
245
repos/base/include/base/session_state.h
Normal file
245
repos/base/include/base/session_state.h
Normal 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_ */
|
@ -23,11 +23,20 @@
|
||||
#include <dataspace/capability.h>
|
||||
#include <pd_session/pd_session.h>
|
||||
|
||||
namespace Genode { struct Cpu_session; }
|
||||
namespace Genode {
|
||||
|
||||
struct Cpu_session;
|
||||
struct Cpu_session_client;
|
||||
}
|
||||
|
||||
|
||||
struct Genode::Cpu_session : Session
|
||||
{
|
||||
static const char *service_name() { return "CPU"; }
|
||||
|
||||
typedef Cpu_session_client Client;
|
||||
|
||||
|
||||
/*********************
|
||||
** Exception types **
|
||||
*********************/
|
||||
@ -36,7 +45,6 @@ struct Genode::Cpu_session : Session
|
||||
class Quota_exceeded : public Thread_creation_failed { };
|
||||
class Out_of_metadata : public Exception { };
|
||||
|
||||
static const char *service_name() { return "CPU"; }
|
||||
|
||||
enum { THREAD_NAME_LEN = 32 };
|
||||
enum { PRIORITY_LIMIT = 1 << 16 };
|
||||
|
@ -119,7 +119,6 @@ struct Genode::Env_deprecated
|
||||
* \noapi
|
||||
*/
|
||||
virtual void reinit_main_thread(Capability<Region_map> &stack_area_rm) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__DEPRECATED__ENV_H_ */
|
||||
|
@ -23,14 +23,16 @@ namespace Genode { struct Log_connection; }
|
||||
|
||||
struct Genode::Log_connection : Connection<Log_session>, Log_session_client
|
||||
{
|
||||
enum { RAM_QUOTA = 8*1024UL };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Log_connection(Env &env, Session_label label = Session_label())
|
||||
:
|
||||
Connection<Log_session>(env, session(env.parent(),
|
||||
"ram_quota=8K, label=\"%s\"",
|
||||
label.string())),
|
||||
"ram_quota=%ld, label=\"%s\"",
|
||||
RAM_QUOTA, label.string())),
|
||||
Log_session_client(cap())
|
||||
{ }
|
||||
|
||||
@ -43,8 +45,8 @@ struct Genode::Log_connection : Connection<Log_session>, Log_session_client
|
||||
*/
|
||||
Log_connection(Session_label label = Session_label())
|
||||
:
|
||||
Connection<Log_session>(session("ram_quota=8K, label=\"%s\"",
|
||||
label.string())),
|
||||
Connection<Log_session>(session("ram_quota=%ld, label=\"%s\"",
|
||||
RAM_QUOTA, label.string())),
|
||||
Log_session_client(cap())
|
||||
{ }
|
||||
};
|
||||
|
@ -19,13 +19,19 @@
|
||||
#include <base/rpc_args.h>
|
||||
#include <session/session.h>
|
||||
|
||||
namespace Genode { struct Log_session; }
|
||||
namespace Genode {
|
||||
|
||||
struct Log_session;
|
||||
struct Log_session_client;
|
||||
}
|
||||
|
||||
|
||||
struct Genode::Log_session : Session
|
||||
{
|
||||
static const char *service_name() { return "LOG"; }
|
||||
|
||||
typedef Log_session_client Client;
|
||||
|
||||
virtual ~Log_session() { }
|
||||
|
||||
/* the lowest platform-specific maximum IPC payload size (OKL4) */
|
||||
|
@ -27,18 +27,32 @@ struct Genode::Parent_client : Rpc_client<Parent>
|
||||
|
||||
void exit(int exit_value) override { call<Rpc_exit>(exit_value); }
|
||||
|
||||
void announce(Service_name const &service, Root_capability root) override {
|
||||
call<Rpc_announce>(service, root); }
|
||||
void announce(Service_name const &service) override {
|
||||
call<Rpc_announce>(service); }
|
||||
|
||||
Session_capability session(Service_name const &service,
|
||||
void session_sigh(Signal_context_capability sigh) override {
|
||||
call<Rpc_session_sigh>(sigh); }
|
||||
|
||||
Session_capability session(Client::Id id,
|
||||
Service_name const &service,
|
||||
Session_args const &args,
|
||||
Affinity const &affinity) override {
|
||||
return call<Rpc_session>(service, args, affinity); }
|
||||
return call<Rpc_session>(id, service, args, affinity); }
|
||||
|
||||
void upgrade(Session_capability to_session, Upgrade_args const &args) override {
|
||||
call<Rpc_upgrade>(to_session, args); }
|
||||
Session_capability session_cap(Client::Id id) override {
|
||||
return call<Rpc_session_cap>(id); }
|
||||
|
||||
void close(Session_capability session) override { call<Rpc_close>(session); }
|
||||
Upgrade_result upgrade(Client::Id to_session, Upgrade_args const &args) override {
|
||||
return call<Rpc_upgrade>(to_session, args); }
|
||||
|
||||
Close_result close(Client::Id id) override { return call<Rpc_close>(id); }
|
||||
|
||||
void session_response(Id_space<Server>::Id id, Session_response response) override {
|
||||
call<Rpc_session_response>(id, response); }
|
||||
|
||||
void deliver_session_cap(Id_space<Server>::Id id,
|
||||
Session_capability cap) override {
|
||||
call<Rpc_deliver_session_cap>(id, cap); }
|
||||
|
||||
Thread_capability main_thread_cap() const override {
|
||||
return call<Rpc_main_thread>(); }
|
||||
|
@ -18,10 +18,15 @@
|
||||
#include <base/rpc.h>
|
||||
#include <base/rpc_args.h>
|
||||
#include <base/thread.h>
|
||||
#include <base/id_space.h>
|
||||
#include <session/capability.h>
|
||||
#include <root/capability.h>
|
||||
|
||||
namespace Genode { class Parent; }
|
||||
namespace Genode {
|
||||
|
||||
class Session_state;
|
||||
class Parent;
|
||||
}
|
||||
|
||||
|
||||
class Genode::Parent
|
||||
@ -61,13 +66,35 @@ class Genode::Parent
|
||||
typedef Rpc_in_buffer<160> Session_args;
|
||||
typedef Rpc_in_buffer<160> Upgrade_args;
|
||||
|
||||
struct Client { typedef Id_space<Client>::Id Id; };
|
||||
struct Server { typedef Id_space<Server>::Id Id; };
|
||||
|
||||
/**
|
||||
* Predefined session IDs corresponding to the environment sessions
|
||||
* created by the parent at the component-construction time
|
||||
*/
|
||||
struct Env
|
||||
{
|
||||
static Client::Id ram() { return { 1 }; }
|
||||
static Client::Id cpu() { return { 2 }; }
|
||||
static Client::Id pd() { return { 3 }; }
|
||||
static Client::Id log() { return { 4 }; }
|
||||
static Client::Id binary() { return { 5 }; }
|
||||
static Client::Id linker() { return { 6 }; }
|
||||
|
||||
/**
|
||||
* True if session ID refers to an environment session
|
||||
*/
|
||||
static bool session_id(Client::Id id) {
|
||||
return id.value >= 1 && id.value <= 6; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Use 'String' instead of 'Rpc_in_buffer' because 'Resource_args'
|
||||
* is used as both in and out parameter.
|
||||
*/
|
||||
typedef String<160> Resource_args;
|
||||
|
||||
|
||||
virtual ~Parent() { }
|
||||
|
||||
/**
|
||||
@ -78,9 +105,20 @@ class Genode::Parent
|
||||
/**
|
||||
* Announce service to the parent
|
||||
*/
|
||||
virtual void announce(Service_name const &service_name,
|
||||
Root_capability service_root) = 0;
|
||||
virtual void announce(Service_name const &service_name) = 0;
|
||||
|
||||
/**
|
||||
* Emulation of the original synchronous root interface
|
||||
*
|
||||
* This function transparently spawns a proxy "root" entrypoint that
|
||||
* dispatches asynchronous session-management operations (as issued
|
||||
* by the parent) to the local root interfaces via component-local
|
||||
* RPC calls.
|
||||
*
|
||||
* The function solely exists for API compatibility.
|
||||
*/
|
||||
static void announce(Service_name const &service_name,
|
||||
Root_capability service_root);
|
||||
|
||||
/**
|
||||
* Announce service to the parent
|
||||
@ -109,9 +147,15 @@ class Genode::Parent
|
||||
(Meta::Bool_to_type<Rpc_interface_is_inherited<Session>::VALUE> *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register signal handler for session notifications
|
||||
*/
|
||||
virtual void session_sigh(Signal_context_capability) = 0;
|
||||
|
||||
/**
|
||||
* Create session to a service
|
||||
*
|
||||
* \param id client-side ID of new session
|
||||
* \param service_name name of the requested interface
|
||||
* \param args session constructor arguments
|
||||
* \param affinity preferred CPU affinity for the session
|
||||
@ -119,60 +163,73 @@ class Genode::Parent
|
||||
* \throw Service_denied parent denies session request
|
||||
* \throw Quota_exceeded our own quota does not suffice for
|
||||
* the creation of the new session
|
||||
*
|
||||
* \return session capability of the new session is immediately
|
||||
* available, or an invalid capability
|
||||
*
|
||||
* If the returned capability is invalid, the request is pending at the
|
||||
* server. The parent delivers a signal to the handler as registered
|
||||
* via 'session_sigh' once the server responded to the request. Now the
|
||||
* session capability can be picked up by calling 'session_cap'.
|
||||
*
|
||||
* \throw Unavailable
|
||||
*
|
||||
* \return untyped capability to new session
|
||||
*
|
||||
* The use of this method is discouraged. Please use the type safe
|
||||
* 'session()' template instead.
|
||||
*/
|
||||
virtual Session_capability session(Service_name const &service_name,
|
||||
virtual Session_capability session(Client::Id id,
|
||||
Service_name const &service_name,
|
||||
Session_args const &args,
|
||||
Affinity const &affinity = Affinity()) = 0;
|
||||
|
||||
/**
|
||||
* Create session to a service
|
||||
* Request session capability
|
||||
*
|
||||
* \param SESSION_TYPE session interface type
|
||||
* \param args session constructor arguments
|
||||
* \param affinity preferred CPU affinity for the session
|
||||
* \throw Service_denied
|
||||
*
|
||||
* \throw Service_denied parent denies session request
|
||||
* \throw Quota_exceeded our own quota does not suffice for
|
||||
* the creation of the new session
|
||||
* \throw Unavailable
|
||||
*
|
||||
* \return capability to new session
|
||||
* In the exception case, the parent implicitly closes the session.
|
||||
*/
|
||||
template <typename SESSION_TYPE>
|
||||
Capability<SESSION_TYPE> session(Session_args const &args,
|
||||
Affinity const &affinity = Affinity())
|
||||
{
|
||||
Session_capability cap = session(SESSION_TYPE::service_name(),
|
||||
args, affinity);
|
||||
return reinterpret_cap_cast<SESSION_TYPE>(cap);
|
||||
}
|
||||
virtual Session_capability session_cap(Client::Id id) = 0;
|
||||
|
||||
enum Upgrade_result { UPGRADE_DONE, UPGRADE_PENDING };
|
||||
|
||||
/**
|
||||
* Transfer our quota to the server that provides the specified session
|
||||
*
|
||||
* \param to_session recipient session
|
||||
* \param id ID of recipient session
|
||||
* \param args description of the amount of quota to transfer
|
||||
*
|
||||
* \throw Quota_exceeded quota could not be transferred
|
||||
*
|
||||
* The 'args' argument has the same principle format as the 'args'
|
||||
* argument of the 'session' operation.
|
||||
* The error case indicates that there is not enough unused quota on
|
||||
* the source side.
|
||||
*/
|
||||
virtual void upgrade(Session_capability to_session,
|
||||
Upgrade_args const &args) = 0;
|
||||
virtual Upgrade_result upgrade(Client::Id to_session,
|
||||
Upgrade_args const &args) = 0;
|
||||
|
||||
enum Close_result { CLOSE_DONE, CLOSE_PENDING };
|
||||
|
||||
/**
|
||||
* Close session
|
||||
*/
|
||||
virtual void close(Session_capability session) = 0;
|
||||
virtual Close_result close(Client::Id) = 0;
|
||||
|
||||
/*
|
||||
* Interface for providing services
|
||||
*/
|
||||
|
||||
enum Session_response { SESSION_OK, SESSION_CLOSED, INVALID_ARGS };
|
||||
|
||||
/**
|
||||
* Set state of a session provided by the child service
|
||||
*/
|
||||
virtual void session_response(Server::Id, Session_response) = 0;
|
||||
|
||||
/**
|
||||
* Deliver capability for a new session provided by the child service
|
||||
*/
|
||||
virtual void deliver_session_cap(Server::Id, Session_capability) = 0;
|
||||
|
||||
/*
|
||||
* Dynamic resource balancing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide thread_cap of main thread
|
||||
@ -233,14 +290,23 @@ class Genode::Parent
|
||||
|
||||
GENODE_RPC(Rpc_exit, void, exit, int);
|
||||
GENODE_RPC(Rpc_announce, void, announce,
|
||||
Service_name const &, Root_capability);
|
||||
Service_name const &);
|
||||
GENODE_RPC(Rpc_session_sigh, void, session_sigh, Signal_context_capability);
|
||||
GENODE_RPC_THROW(Rpc_session, Session_capability, session,
|
||||
GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable),
|
||||
Service_name const &, Session_args const &, Affinity const &);
|
||||
GENODE_RPC_THROW(Rpc_upgrade, void, upgrade,
|
||||
Client::Id, Service_name const &, Session_args const &,
|
||||
Affinity const &);
|
||||
GENODE_RPC_THROW(Rpc_session_cap, Session_capability, session_cap,
|
||||
GENODE_TYPE_LIST(Service_denied, Quota_exceeded, Unavailable),
|
||||
Client::Id);
|
||||
GENODE_RPC_THROW(Rpc_upgrade, Upgrade_result, upgrade,
|
||||
GENODE_TYPE_LIST(Quota_exceeded),
|
||||
Session_capability, Upgrade_args const &);
|
||||
GENODE_RPC(Rpc_close, void, close, Session_capability);
|
||||
Client::Id, Upgrade_args const &);
|
||||
GENODE_RPC(Rpc_close, Close_result, close, Client::Id);
|
||||
GENODE_RPC(Rpc_session_response, void, session_response,
|
||||
Server::Id, Session_response);
|
||||
GENODE_RPC(Rpc_deliver_session_cap, void, deliver_session_cap,
|
||||
Server::Id, Session_capability);
|
||||
GENODE_RPC(Rpc_main_thread, Thread_capability, main_thread_cap);
|
||||
GENODE_RPC(Rpc_resource_avail_sigh, void, resource_avail_sigh,
|
||||
Signal_context_capability);
|
||||
@ -250,10 +316,12 @@ class Genode::Parent
|
||||
GENODE_RPC(Rpc_yield_request, Resource_args, yield_request);
|
||||
GENODE_RPC(Rpc_yield_response, void, yield_response);
|
||||
|
||||
GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session, Rpc_upgrade,
|
||||
Rpc_close, Rpc_main_thread, Rpc_resource_avail_sigh,
|
||||
Rpc_resource_request, Rpc_yield_sigh, Rpc_yield_request,
|
||||
Rpc_yield_response);
|
||||
GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session_sigh,
|
||||
Rpc_session, Rpc_session_cap, Rpc_upgrade,
|
||||
Rpc_close, Rpc_session_response, Rpc_main_thread,
|
||||
Rpc_deliver_session_cap, Rpc_resource_avail_sigh,
|
||||
Rpc_resource_request, Rpc_yield_sigh,
|
||||
Rpc_yield_request, Rpc_yield_response);
|
||||
};
|
||||
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
namespace Genode {
|
||||
|
||||
struct Pd_session;
|
||||
struct Pd_session_client;
|
||||
struct Parent;
|
||||
struct Signal_context;
|
||||
}
|
||||
@ -34,6 +35,8 @@ struct Genode::Pd_session : Session
|
||||
{
|
||||
static const char *service_name() { return "PD"; }
|
||||
|
||||
typedef Pd_session_client Client;
|
||||
|
||||
virtual ~Pd_session() { }
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ namespace Genode {
|
||||
struct Ram_dataspace;
|
||||
typedef Capability<Ram_dataspace> Ram_dataspace_capability;
|
||||
|
||||
struct Ram_session_client;
|
||||
struct Ram_session;
|
||||
}
|
||||
|
||||
@ -41,6 +42,8 @@ struct Genode::Ram_session : Session
|
||||
{
|
||||
static const char *service_name() { return "RAM"; }
|
||||
|
||||
typedef Ram_session_client Client;
|
||||
|
||||
|
||||
/*********************
|
||||
** Exception types **
|
||||
|
@ -28,11 +28,13 @@ class Genode::Rom_connection : public Connection<Rom_session>,
|
||||
|
||||
class Rom_connection_failed : public Parent::Exception { };
|
||||
|
||||
enum { RAM_QUOTA = 4096UL };
|
||||
|
||||
private:
|
||||
|
||||
Rom_session_capability _session(Parent &parent, char const *label)
|
||||
{
|
||||
return session("ram_quota=4K, label=\"%s\"", label);
|
||||
return session("ram_quota=%ld, label=\"%s\"", RAM_QUOTA, label);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -25,6 +25,7 @@ namespace Genode {
|
||||
|
||||
struct Rom_dataspace;
|
||||
struct Rom_session;
|
||||
struct Rom_session_client;
|
||||
|
||||
typedef Capability<Rom_dataspace> Rom_dataspace_capability;
|
||||
}
|
||||
@ -37,6 +38,8 @@ struct Genode::Rom_session : Session
|
||||
{
|
||||
static const char *service_name() { return "ROM"; }
|
||||
|
||||
typedef Rom_session_client Client;
|
||||
|
||||
virtual ~Rom_session() { }
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <base/allocator.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/entrypoint.h>
|
||||
#include <base/service.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <base/log.h>
|
||||
|
||||
@ -95,6 +96,7 @@ struct Genode::Multiple_clients
|
||||
*/
|
||||
template <typename SESSION_TYPE, typename POLICY>
|
||||
class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
public Local_service<SESSION_TYPE>::Factory,
|
||||
private POLICY
|
||||
{
|
||||
private:
|
||||
@ -113,6 +115,53 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
*/
|
||||
Allocator *_md_alloc;
|
||||
|
||||
/*
|
||||
* Used by both the legacy 'Root::session' and the new 'Factory::create'
|
||||
*/
|
||||
SESSION_TYPE &_create(Session_state::Args const &args, Affinity affinity)
|
||||
{
|
||||
POLICY::aquire(args.string());
|
||||
|
||||
/*
|
||||
* We need to decrease 'ram_quota' by
|
||||
* the size of the session object.
|
||||
*/
|
||||
size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
|
||||
size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE));
|
||||
|
||||
if (needed > ram_quota) {
|
||||
error("insufficient ram quota, provided=", ram_quota,
|
||||
", required=", needed);
|
||||
throw Root::Quota_exceeded();
|
||||
}
|
||||
|
||||
size_t const remaining_ram_quota = ram_quota - needed;
|
||||
|
||||
/*
|
||||
* Deduce ram quota needed for allocating the session object from the
|
||||
* donated ram quota.
|
||||
*
|
||||
* XXX the size of the 'adjusted_args' buffer should dependent
|
||||
* on the message-buffer size and stack size.
|
||||
*/
|
||||
enum { MAX_ARGS_LEN = 256 };
|
||||
char adjusted_args[MAX_ARGS_LEN];
|
||||
strncpy(adjusted_args, args.string(), sizeof(adjusted_args));
|
||||
char ram_quota_buf[64];
|
||||
snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%lu",
|
||||
remaining_ram_quota);
|
||||
Arg_string::set_arg(adjusted_args, sizeof(adjusted_args),
|
||||
"ram_quota", ram_quota_buf);
|
||||
|
||||
SESSION_TYPE *s = 0;
|
||||
try { s = _create_session(adjusted_args, affinity); }
|
||||
catch (Allocator::Out_of_memory) { throw Root::Unavailable(); }
|
||||
|
||||
_ep->manage(s);
|
||||
|
||||
return *s;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
@ -169,7 +218,7 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
virtual void _upgrade_session(SESSION_TYPE *, const char *) { }
|
||||
|
||||
virtual void _destroy_session(SESSION_TYPE *session) {
|
||||
destroy(_md_alloc, session); }
|
||||
Genode::destroy(_md_alloc, session); }
|
||||
|
||||
/**
|
||||
* Return allocator to allocate server object in '_create_session()'
|
||||
@ -208,6 +257,31 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
{ }
|
||||
|
||||
|
||||
/**************************************
|
||||
** Local_service::Factory interface **
|
||||
**************************************/
|
||||
|
||||
SESSION_TYPE &create(Session_state::Args const &args,
|
||||
Affinity affinity) override
|
||||
{
|
||||
try {
|
||||
return _create(args, affinity); }
|
||||
catch (...) {
|
||||
throw typename Local_service<SESSION_TYPE>::Factory::Denied(); }
|
||||
}
|
||||
|
||||
void upgrade(SESSION_TYPE &session,
|
||||
Session_state::Args const &args) override
|
||||
{
|
||||
_upgrade_session(&session, args.string());
|
||||
}
|
||||
|
||||
void destroy(SESSION_TYPE &session) override
|
||||
{
|
||||
close(session.cap());
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Root interface **
|
||||
********************/
|
||||
@ -216,45 +290,8 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
Affinity const &affinity) override
|
||||
{
|
||||
if (!args.valid_string()) throw Root::Invalid_args();
|
||||
|
||||
POLICY::aquire(args.string());
|
||||
|
||||
/*
|
||||
* We need to decrease 'ram_quota' by
|
||||
* the size of the session object.
|
||||
*/
|
||||
size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
|
||||
size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE));
|
||||
|
||||
if (needed > ram_quota) {
|
||||
error("Insufficient ram quota, provided=", ram_quota,
|
||||
", required=", needed);
|
||||
throw Root::Quota_exceeded();
|
||||
}
|
||||
|
||||
size_t const remaining_ram_quota = ram_quota - needed;
|
||||
|
||||
/*
|
||||
* Deduce ram quota needed for allocating the session object from the
|
||||
* donated ram quota.
|
||||
*
|
||||
* XXX the size of the 'adjusted_args' buffer should dependent
|
||||
* on the message-buffer size and stack size.
|
||||
*/
|
||||
enum { MAX_ARGS_LEN = 256 };
|
||||
char adjusted_args[MAX_ARGS_LEN];
|
||||
strncpy(adjusted_args, args.string(), sizeof(adjusted_args));
|
||||
char ram_quota_buf[64];
|
||||
snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%lu",
|
||||
remaining_ram_quota);
|
||||
Arg_string::set_arg(adjusted_args, sizeof(adjusted_args),
|
||||
"ram_quota", ram_quota_buf);
|
||||
|
||||
SESSION_TYPE *s = 0;
|
||||
try { s = _create_session(adjusted_args, affinity); }
|
||||
catch (Allocator::Out_of_memory) { throw Root::Unavailable(); }
|
||||
|
||||
return _ep->manage(s);
|
||||
SESSION_TYPE &session = _create(args.string(), affinity);
|
||||
return session.cap();
|
||||
}
|
||||
|
||||
void upgrade(Session_capability session, Root::Upgrade_args const &args) override
|
||||
|
@ -21,13 +21,13 @@
|
||||
*/
|
||||
#include <base/rpc.h>
|
||||
|
||||
namespace Genode { class Session; }
|
||||
namespace Genode { struct Session; }
|
||||
|
||||
|
||||
/**
|
||||
* Base class of session interfaces
|
||||
*/
|
||||
class Genode::Session
|
||||
struct Genode::Session
|
||||
{
|
||||
/*
|
||||
* Each session interface must implement the class function 'service_name'
|
||||
@ -35,6 +35,8 @@ class Genode::Session
|
||||
* This function returns the name of the service provided via the session
|
||||
* interface.
|
||||
*/
|
||||
|
||||
virtual ~Session() { }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__SESSION__SESSION_H_ */
|
||||
|
@ -15,6 +15,7 @@ SRC_CC += console.cc
|
||||
SRC_CC += output.cc
|
||||
SRC_CC += child.cc
|
||||
SRC_CC += child_process.cc
|
||||
SRC_CC += session_state.cc
|
||||
SRC_CC += elf_binary.cc
|
||||
SRC_CC += ipc.cc
|
||||
SRC_CC += lock.cc
|
||||
@ -29,6 +30,8 @@ SRC_CC += region_map_client.cc
|
||||
SRC_CC += rm_session_client.cc
|
||||
SRC_CC += stack_allocator.cc
|
||||
SRC_CC += trace.cc
|
||||
SRC_CC += root_proxy.cc
|
||||
SRC_CC += env_session_id_space.cc
|
||||
|
||||
INC_DIR += $(REP_DIR)/src/include $(BASE_DIR)/src/include
|
||||
|
||||
|
@ -56,6 +56,7 @@ Thread_capability Cpu_session_component::create_thread(Capability<Pd_session> pd
|
||||
error("create_thread: invalid PD argument");
|
||||
throw Thread_creation_failed();
|
||||
}
|
||||
|
||||
Lock::Guard slab_lock_guard(_thread_alloc_lock);
|
||||
thread = new (&_thread_alloc)
|
||||
Cpu_thread_component(
|
||||
|
@ -116,8 +116,6 @@ namespace Genode {
|
||||
|
||||
typedef Synchronized_ram_session<Ram_session_component> Core_ram_session;
|
||||
|
||||
Core_parent _core_parent;
|
||||
|
||||
enum { ENTRYPOINT_STACK_SIZE = 2048 * sizeof(Genode::addr_t) };
|
||||
|
||||
/*
|
||||
@ -144,6 +142,10 @@ namespace Genode {
|
||||
|
||||
Heap _heap;
|
||||
|
||||
Registry<Service> _services;
|
||||
|
||||
Core_parent _core_parent { _heap, _services };
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -202,6 +204,8 @@ namespace Genode {
|
||||
void reinit(Capability<Parent>::Raw) override { }
|
||||
|
||||
void reinit_main_thread(Capability<Region_map> &) override { }
|
||||
|
||||
Registry<Service> &services() { return _services; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -16,38 +16,83 @@
|
||||
#define _CORE__INCLUDE__CORE_PARENT_H_
|
||||
|
||||
#include <parent/parent.h>
|
||||
#include <base/service.h>
|
||||
#include <base/allocator.h>
|
||||
|
||||
namespace Genode { struct Core_parent; }
|
||||
namespace Genode {
|
||||
|
||||
template <typename> struct Core_service;
|
||||
struct Core_parent;
|
||||
}
|
||||
|
||||
|
||||
template <typename SESSION>
|
||||
struct Genode::Core_service : Local_service<SESSION>, Registry<Service>::Element
|
||||
{
|
||||
Core_service(Registry<Service> ®istry,
|
||||
typename Local_service<SESSION>::Factory &factory)
|
||||
:
|
||||
Local_service<SESSION>(factory),
|
||||
Registry<Service>::Element(registry, *this)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* In fact, Core has _no_ parent. But most of our libraries could work
|
||||
* seamlessly inside Core too, if it had one. Core_parent fills this gap.
|
||||
* Core has no parent. But most of Genode's library code could work seamlessly
|
||||
* inside core if it had one. Core_parent fills this gap.
|
||||
*/
|
||||
struct Genode::Core_parent : Parent
|
||||
class Genode::Core_parent : public Parent
|
||||
{
|
||||
void exit(int);
|
||||
private:
|
||||
|
||||
void announce(Service_name const &, Root_capability) { }
|
||||
Id_space<Client> _id_space;
|
||||
Allocator &_alloc;
|
||||
Registry<Service> &_services;
|
||||
|
||||
Session_capability session(Service_name const &, Session_args const &,
|
||||
Affinity const &);
|
||||
public:
|
||||
|
||||
void upgrade(Session_capability, Upgrade_args const &) { throw Quota_exceeded(); }
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \alloc allocator to be used for allocating core-local
|
||||
* 'Session_state' objects
|
||||
*/
|
||||
Core_parent(Allocator &alloc, Registry<Service> &services)
|
||||
: _alloc(alloc), _services(services) { }
|
||||
|
||||
void close(Session_capability) { }
|
||||
void exit(int) override;
|
||||
|
||||
Thread_capability main_thread_cap() const { return Thread_capability(); }
|
||||
void announce(Service_name const &) override { }
|
||||
|
||||
void resource_avail_sigh(Signal_context_capability) { }
|
||||
void session_sigh(Signal_context_capability) override { }
|
||||
|
||||
void resource_request(Resource_args const &) { }
|
||||
Session_capability session(Client::Id, Service_name const &, Session_args const &,
|
||||
Affinity const &) override;
|
||||
|
||||
void yield_sigh(Signal_context_capability) { }
|
||||
Session_capability session_cap(Client::Id) override { return Session_capability(); }
|
||||
|
||||
Resource_args yield_request() { return Resource_args(); }
|
||||
Upgrade_result upgrade(Client::Id, Upgrade_args const &) override {
|
||||
throw Quota_exceeded(); }
|
||||
|
||||
void yield_response() { }
|
||||
Close_result close(Client::Id) override { return CLOSE_DONE; }
|
||||
|
||||
void session_response(Server::Id, Session_response) override { }
|
||||
|
||||
void deliver_session_cap(Server::Id,
|
||||
Session_capability) override { }
|
||||
|
||||
Thread_capability main_thread_cap() const override { return Thread_capability(); }
|
||||
|
||||
void resource_avail_sigh(Signal_context_capability) override { }
|
||||
|
||||
void resource_request(Resource_args const &) override { }
|
||||
|
||||
void yield_sigh(Signal_context_capability) override { }
|
||||
|
||||
Resource_args yield_request() override { return Resource_args(); }
|
||||
|
||||
void yield_response() override { }
|
||||
};
|
||||
|
||||
#endif /* _CORE__INCLUDE__CORE_PARENT_H_ */
|
||||
|
@ -14,11 +14,12 @@
|
||||
#ifndef _CORE__INCLUDE__PLATFORM_SERVICES_H_
|
||||
#define _CORE__INCLUDE__PLATFORM_SERVICES_H_
|
||||
|
||||
#include <base/service.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Rpc_entrypoint;
|
||||
class Sliced_heap;
|
||||
class Service_registry;
|
||||
|
||||
|
||||
/**
|
||||
@ -31,7 +32,7 @@ namespace Genode {
|
||||
*/
|
||||
void platform_add_local_services(Rpc_entrypoint *ep,
|
||||
Sliced_heap *md,
|
||||
Service_registry *reg);
|
||||
Registry<Service> *reg);
|
||||
}
|
||||
|
||||
#endif /* _CORE__INCLUDE__PLATFORM_SERVICES_H_ */
|
||||
|
@ -42,10 +42,6 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/* pool of provided core services */
|
||||
static Service_registry local_services;
|
||||
|
||||
|
||||
/***************************************
|
||||
** Core environment/platform support **
|
||||
***************************************/
|
||||
@ -85,17 +81,30 @@ Platform_generic *Genode::platform() { return platform_specific(); }
|
||||
** Core parent support **
|
||||
*************************/
|
||||
|
||||
Session_capability Core_parent::session(Parent::Service_name const &name,
|
||||
Session_capability Core_parent::session(Parent::Client::Id id,
|
||||
Parent::Service_name const &name,
|
||||
Parent::Session_args const &args,
|
||||
Affinity const &affinity)
|
||||
{
|
||||
Service *service = local_services.find(name.string());
|
||||
Session_capability cap;
|
||||
|
||||
if (service)
|
||||
return service->session(args.string(), affinity);
|
||||
_services.for_each([&] (Service &service) {
|
||||
|
||||
warning("service_name=\"", name.string(), "\" args=\"", args.string(), "\" not handled");
|
||||
return Session_capability();
|
||||
if ((service.name() != name.string()) || cap.valid())
|
||||
return;
|
||||
|
||||
Session_state &session = *new (_alloc)
|
||||
Session_state(service, _id_space, id, args.string(), affinity);
|
||||
|
||||
service.initiate_request(session);
|
||||
|
||||
cap = session.cap;
|
||||
});
|
||||
|
||||
if (!cap.valid())
|
||||
error("unexpected core-parent ", name.string(), " session request");
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
|
||||
@ -113,21 +122,15 @@ class Core_child : public Child_policy
|
||||
Rpc_entrypoint _entrypoint;
|
||||
enum { STACK_SIZE = 2 * 1024 * sizeof(Genode::addr_t)};
|
||||
|
||||
Service_registry &_local_services;
|
||||
Registry<Service> &_services;
|
||||
|
||||
/*
|
||||
* Dynamic linker, does not need to be valid because init is statically
|
||||
* linked
|
||||
*/
|
||||
Dataspace_capability _ldso_ds;
|
||||
Capability<Ram_session> _core_ram_cap;
|
||||
Ram_session &_core_ram;
|
||||
|
||||
Pd_session_client _pd;
|
||||
Ram_session_client _ram;
|
||||
Cpu_session_client _cpu;
|
||||
Capability<Cpu_session> _core_cpu_cap;
|
||||
Cpu_session &_core_cpu;
|
||||
|
||||
Child::Initial_thread _initial_thread { _cpu, _pd, "init_main" };
|
||||
|
||||
Region_map_client _address_space;
|
||||
size_t const _ram_quota;
|
||||
|
||||
Child _child;
|
||||
|
||||
@ -136,20 +139,16 @@ class Core_child : public Child_policy
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Core_child(Dataspace_capability elf_ds, Pd_session_capability pd,
|
||||
Ram_session_capability ram,
|
||||
Cpu_session_capability cpu,
|
||||
Service_registry &services)
|
||||
Core_child(Registry<Service> &services, Ram_session &core_ram,
|
||||
Capability<Ram_session> core_ram_cap, size_t ram_quota,
|
||||
Cpu_session &core_cpu, Capability<Cpu_session> core_cpu_cap)
|
||||
:
|
||||
_entrypoint(nullptr, STACK_SIZE, "init_child", false),
|
||||
_local_services(services),
|
||||
_pd(pd), _ram(ram), _cpu(cpu),
|
||||
_address_space(Pd_session_client(pd).address_space()),
|
||||
_child(elf_ds, _ldso_ds, _pd, _pd, _ram, _ram, _cpu, _initial_thread,
|
||||
*env()->rm_session(), _address_space, _entrypoint, *this,
|
||||
*_local_services.find(Pd_session::service_name()),
|
||||
*_local_services.find(Ram_session::service_name()),
|
||||
*_local_services.find(Cpu_session::service_name()))
|
||||
_services(services),
|
||||
_core_ram_cap(core_ram_cap), _core_ram(core_ram),
|
||||
_core_cpu_cap(core_cpu_cap), _core_cpu(core_cpu),
|
||||
_ram_quota(Child::effective_ram_quota(ram_quota)),
|
||||
_child(*env()->rm_session(), _entrypoint, *this)
|
||||
{
|
||||
_entrypoint.activate();
|
||||
}
|
||||
@ -159,8 +158,7 @@ class Core_child : public Child_policy
|
||||
** Child-policy interface **
|
||||
****************************/
|
||||
|
||||
void filter_session_args(const char *, char *args,
|
||||
Genode::size_t args_len)
|
||||
void filter_session_args(Service::Name const &, char *args, size_t args_len) override
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
@ -177,13 +175,36 @@ class Core_child : public Child_policy
|
||||
Arg_string::set_arg(args, args_len, "label", value_buf);
|
||||
}
|
||||
|
||||
Name name() const { return "init"; }
|
||||
|
||||
const char *name() const { return "init"; }
|
||||
|
||||
Service *resolve_session_request(const char *service, const char *)
|
||||
Service &resolve_session_request(Service::Name const &name,
|
||||
Session_state::Args const &args) override
|
||||
{
|
||||
return _local_services.find(service);
|
||||
Service *service = nullptr;
|
||||
_services.for_each([&] (Service &s) {
|
||||
if (!service && s.name() == name)
|
||||
service = &s; });
|
||||
|
||||
if (!service)
|
||||
throw Parent::Service_denied();
|
||||
|
||||
return *service;
|
||||
}
|
||||
|
||||
void init(Ram_session &session, Capability<Ram_session> cap) override
|
||||
{
|
||||
session.ref_account(_core_ram_cap);
|
||||
_core_ram.transfer_quota(cap, _ram_quota);
|
||||
}
|
||||
|
||||
void init(Cpu_session &session, Capability<Cpu_session> cap) override
|
||||
{
|
||||
session.ref_account(_core_cpu_cap);
|
||||
_core_cpu.transfer_quota(cap, Cpu_session::quota_lim_upscale(100, 100));
|
||||
}
|
||||
|
||||
Ram_session &ref_ram() { return _core_ram; }
|
||||
Ram_session_capability ref_ram_cap() const { return _core_ram_cap; }
|
||||
};
|
||||
|
||||
|
||||
@ -240,6 +261,8 @@ int main()
|
||||
*/
|
||||
Rpc_entrypoint *e = core_env()->entrypoint();
|
||||
|
||||
Registry<Service> &services = core_env()->services();
|
||||
|
||||
/*
|
||||
* Allocate session meta data on distinct dataspaces to enable independent
|
||||
* destruction (to enable quota trading) of session component objects.
|
||||
@ -266,70 +289,42 @@ int main()
|
||||
platform()->irq_alloc(), &sliced_heap);
|
||||
static Trace::Root trace_root (e, &sliced_heap, Trace::sources(), trace_policies);
|
||||
|
||||
/*
|
||||
* Play our role as parent of init and declare our services.
|
||||
*/
|
||||
|
||||
static Local_service ls[] = {
|
||||
Local_service(Rom_session::service_name(), &rom_root),
|
||||
Local_service(Ram_session::service_name(), &ram_root),
|
||||
Local_service(Rm_session::service_name(), &rm_root),
|
||||
Local_service(Cpu_session::service_name(), &cpu_root),
|
||||
Local_service(Pd_session::service_name(), &pd_root),
|
||||
Local_service(Log_session::service_name(), &log_root),
|
||||
Local_service(Io_mem_session::service_name(), &io_mem_root),
|
||||
Local_service(Irq_session::service_name(), &irq_root),
|
||||
Local_service(Trace::Session::service_name(), &trace_root)
|
||||
};
|
||||
|
||||
/* make our local services known to service pool */
|
||||
for (unsigned i = 0; i < sizeof(ls) / sizeof(Local_service); i++)
|
||||
local_services.insert(&ls[i]);
|
||||
static Core_service<Rom_session_component> rom_service (services, rom_root);
|
||||
static Core_service<Ram_session_component> ram_service (services, ram_root);
|
||||
static Core_service<Rm_session_component> rm_service (services, rm_root);
|
||||
static Core_service<Cpu_session_component> cpu_service (services, cpu_root);
|
||||
static Core_service<Pd_session_component> pd_service (services, pd_root);
|
||||
static Core_service<Log_session_component> log_service (services, log_root);
|
||||
static Core_service<Io_mem_session_component> io_mem_service (services, io_mem_root);
|
||||
static Core_service<Irq_session_component> irq_service (services, irq_root);
|
||||
static Core_service<Trace::Session_component> trace_service (services, trace_root);
|
||||
|
||||
/* make platform-specific services known to service pool */
|
||||
platform_add_local_services(e, &sliced_heap, &local_services);
|
||||
platform_add_local_services(e, &sliced_heap, &services);
|
||||
|
||||
/* obtain ROM session with init binary */
|
||||
Rom_session_capability init_rom_session_cap;
|
||||
try {
|
||||
static Rom_connection rom("init");
|
||||
init_rom_session_cap = rom.cap(); }
|
||||
catch (...) {
|
||||
error("ROM module \"init\" not present"); }
|
||||
|
||||
/* create ram session for init and transfer some of our own quota */
|
||||
Ram_session_capability init_ram_session_cap
|
||||
= static_cap_cast<Ram_session>(ram_root.session("ram_quota=32K", Affinity()));
|
||||
Ram_session_client(init_ram_session_cap).ref_account(env()->ram_session_cap());
|
||||
|
||||
/* create CPU session for init and transfer all of the CPU quota to it */
|
||||
/* create CPU session representing core */
|
||||
static Cpu_session_component
|
||||
cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(),
|
||||
"label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT);
|
||||
Cpu_session_capability cpu_cap = core_env()->entrypoint()->manage(&cpu);
|
||||
Cpu_connection init_cpu("init");
|
||||
init_cpu.ref_account(cpu_cap);
|
||||
cpu.transfer_quota(init_cpu, Cpu_session::quota_lim_upscale(100, 100));
|
||||
core_cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(),
|
||||
"label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT);
|
||||
Cpu_session_capability core_cpu_cap = core_env()->entrypoint()->manage(&core_cpu);
|
||||
|
||||
/* transfer all left memory to init, but leave some memory left for core */
|
||||
/* NOTE: exception objects thrown in core components are currently allocated on
|
||||
core's heap and not accounted by the component's meta data allocator */
|
||||
Genode::size_t init_quota = platform()->ram_alloc()->avail() - 224*1024;
|
||||
env()->ram_session()->transfer_quota(init_ram_session_cap, init_quota);
|
||||
log("", init_quota / (1024*1024), " MiB RAM assigned to init");
|
||||
/*
|
||||
* Transfer all left memory to init, but leave some memory left for core
|
||||
*
|
||||
* NOTE: exception objects thrown in core components are currently
|
||||
* allocated on core's heap and not accounted by the component's meta data
|
||||
* allocator
|
||||
*/
|
||||
|
||||
Pd_connection init_pd("init");
|
||||
Core_child *init = new (env()->heap())
|
||||
Core_child(Rom_session_client(init_rom_session_cap).dataspace(),
|
||||
init_pd, init_ram_session_cap, init_cpu.cap(),
|
||||
local_services);
|
||||
Genode::size_t const ram_quota = platform()->ram_alloc()->avail() - 224*1024;
|
||||
log("", ram_quota / (1024*1024), " MiB RAM assigned to init");
|
||||
|
||||
static Volatile_object<Core_child>
|
||||
init(services, *env()->ram_session(), env()->ram_session_cap(),
|
||||
ram_quota, core_cpu, core_cpu_cap);
|
||||
|
||||
platform()->wait_for_exit();
|
||||
|
||||
destroy(env()->heap(), init);
|
||||
|
||||
rom_root.close(init_rom_session_cap);
|
||||
ram_root.close(init_ram_session_cap);
|
||||
|
||||
init.destruct();
|
||||
return 0;
|
||||
}
|
||||
|
@ -15,5 +15,5 @@
|
||||
#include <platform_services.h>
|
||||
|
||||
|
||||
void Genode::platform_add_local_services(Rpc_entrypoint*, Sliced_heap*,
|
||||
Service_registry*) { }
|
||||
void Genode::platform_add_local_services(Rpc_entrypoint *, Sliced_heap*,
|
||||
Registry<Service> *) { }
|
||||
|
@ -78,8 +78,10 @@ int Ram_session_component::_transfer_quota(Ram_session_component *dst, size_t am
|
||||
|
||||
/* decrease quota limit of this session - check against used quota */
|
||||
if (_quota_limit < amount + _payload) {
|
||||
warning("Insufficient quota for transfer: ", Cstring(_label));
|
||||
warning(" have ", _quota_limit - _payload, ", need ", amount);
|
||||
warning("insufficient quota for transfer: "
|
||||
"'", Cstring(_label), "' to '", Cstring(dst->_label), "' "
|
||||
"have ", (_quota_limit - _payload)/1024, " KiB, "
|
||||
"need ", amount/1024, " KiB");
|
||||
return -3;
|
||||
}
|
||||
|
||||
@ -262,6 +264,10 @@ int Ram_session_component::transfer_quota(Ram_session_capability ram_session_cap
|
||||
{
|
||||
auto lambda = [&] (Ram_session_component *dst) {
|
||||
return _transfer_quota(dst, amount); };
|
||||
|
||||
if (this->cap() == ram_session_cap)
|
||||
return 0;
|
||||
|
||||
return _ram_session_ep->apply(ram_session_cap, lambda);
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,11 @@
|
||||
*/
|
||||
void Genode::platform_add_local_services(Rpc_entrypoint*,
|
||||
Sliced_heap *sliced_heap,
|
||||
Service_registry *local_services)
|
||||
Registry<Service> *local_services)
|
||||
{
|
||||
static Io_port_root io_port_root(core_env()->pd_session(),
|
||||
platform()->io_port_alloc(), sliced_heap);
|
||||
static Local_service io_port_ls(Io_port_session::service_name(),
|
||||
&io_port_root);
|
||||
local_services->insert(&io_port_ls);
|
||||
|
||||
static Core_service<Io_port_session_component>
|
||||
io_port_ls(*local_services, io_port_root);
|
||||
}
|
||||
|
@ -30,7 +30,8 @@ struct Genode::Attached_stack_area : Expanding_region_map_client
|
||||
{
|
||||
Attached_stack_area(Parent &parent, Pd_session_capability pd)
|
||||
:
|
||||
Expanding_region_map_client(pd, Pd_session_client(pd).stack_area())
|
||||
Expanding_region_map_client(pd, Pd_session_client(pd).stack_area(),
|
||||
Parent::Env::pd())
|
||||
{
|
||||
Region_map_client address_space(Pd_session_client(pd).address_space());
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace Genode { struct Expanding_cpu_session_client; }
|
||||
|
||||
struct Genode::Expanding_cpu_session_client : Upgradeable_client<Genode::Cpu_session_client>
|
||||
{
|
||||
Expanding_cpu_session_client(Genode::Cpu_session_capability cap)
|
||||
Expanding_cpu_session_client(Genode::Cpu_session_capability cap, Parent::Client::Id id)
|
||||
:
|
||||
/*
|
||||
* We need to upcast the capability because on some platforms (i.e.,
|
||||
@ -35,7 +35,7 @@ struct Genode::Expanding_cpu_session_client : Upgradeable_client<Genode::Cpu_ses
|
||||
* interface ('Nova_cpu_session').
|
||||
*/
|
||||
Upgradeable_client<Genode::Cpu_session_client>
|
||||
(static_cap_cast<Genode::Cpu_session_client::Rpc_interface>(cap))
|
||||
(static_cap_cast<Genode::Cpu_session_client::Rpc_interface>(cap), id)
|
||||
{ }
|
||||
|
||||
Thread_capability
|
||||
|
@ -87,13 +87,14 @@ class Genode::Expanding_parent_client : public Parent_client
|
||||
** Parent interface **
|
||||
**********************/
|
||||
|
||||
Session_capability session(Service_name const &name,
|
||||
Session_capability session(Client::Id id,
|
||||
Service_name const &name,
|
||||
Session_args const &args,
|
||||
Affinity const &affinity) override
|
||||
{
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
return retry<Parent::Quota_exceeded>(
|
||||
[&] () { return Parent_client::session(name, args, affinity); },
|
||||
[&] () { return Parent_client::session(id, name, args, affinity); },
|
||||
[&] () {
|
||||
|
||||
/*
|
||||
@ -114,7 +115,7 @@ class Genode::Expanding_parent_client : public Parent_client
|
||||
NUM_ATTEMPTS);
|
||||
}
|
||||
|
||||
void upgrade(Session_capability to_session, Upgrade_args const &args) override
|
||||
Upgrade_result upgrade(Client::Id id, Upgrade_args const &args) override
|
||||
{
|
||||
/*
|
||||
* If the upgrade fails, attempt to issue a resource request twice.
|
||||
@ -131,8 +132,8 @@ class Genode::Expanding_parent_client : public Parent_client
|
||||
* caller to issue (and respond to) a resource request.
|
||||
*/
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
retry<Parent::Quota_exceeded>(
|
||||
[&] () { Parent_client::upgrade(to_session, args); },
|
||||
return retry<Parent::Quota_exceeded>(
|
||||
[&] () { return Parent_client::upgrade(id, args); },
|
||||
[&] () { resource_request(Resource_args(args.string())); },
|
||||
NUM_ATTEMPTS);
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ namespace Genode { class Expanding_ram_session_client; }
|
||||
|
||||
struct Genode::Expanding_ram_session_client : Upgradeable_client<Genode::Ram_session_client>
|
||||
{
|
||||
Expanding_ram_session_client(Ram_session_capability cap)
|
||||
: Upgradeable_client<Genode::Ram_session_client>(cap) { }
|
||||
Expanding_ram_session_client(Ram_session_capability cap, Parent::Client::Id id)
|
||||
: Upgradeable_client<Genode::Ram_session_client>(cap, id) { }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached = UNCACHED) override
|
||||
{
|
||||
|
@ -29,8 +29,9 @@ struct Genode::Expanding_region_map_client : Region_map_client
|
||||
{
|
||||
Upgradeable_client<Genode::Pd_session_client> _pd_client;
|
||||
|
||||
Expanding_region_map_client(Pd_session_capability pd, Capability<Region_map> rm)
|
||||
: Region_map_client(rm), _pd_client(pd) { }
|
||||
Expanding_region_map_client(Pd_session_capability pd, Capability<Region_map> rm,
|
||||
Parent::Client::Id pd_id)
|
||||
: Region_map_client(rm), _pd_client(pd, pd_id) { }
|
||||
|
||||
Local_addr attach(Dataspace_capability ds, size_t size, off_t offset,
|
||||
bool use_local_addr, Local_addr local_addr,
|
||||
|
@ -17,11 +17,14 @@
|
||||
#ifndef _INCLUDE__BASE__INTERNAL__GLOBALS_H_
|
||||
#define _INCLUDE__BASE__INTERNAL__GLOBALS_H_
|
||||
|
||||
#include <parent/parent.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Region_map;
|
||||
class Ram_session;
|
||||
class Env;
|
||||
class Local_session_id_space;
|
||||
|
||||
extern Region_map *env_stack_area_region_map;
|
||||
extern Ram_session *env_stack_area_ram_session;
|
||||
@ -30,7 +33,11 @@ namespace Genode {
|
||||
void init_cxx_heap(Env &);
|
||||
void init_ldso_phdr(Env &);
|
||||
void init_signal_thread(Env &);
|
||||
void init_root_proxy(Env &);
|
||||
void init_log();
|
||||
|
||||
Id_space<Parent::Client> &env_session_id_space();
|
||||
Env &internal_env();
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__BASE__INTERNAL__GLOBALS_H_ */
|
||||
|
@ -48,9 +48,9 @@ class Genode::Platform_env : public Env_deprecated,
|
||||
struct Resources
|
||||
{
|
||||
template <typename T>
|
||||
Capability<T> request(Parent &parent, char const *service)
|
||||
Capability<T> request(Parent &parent, Parent::Client::Id id)
|
||||
{
|
||||
return static_cap_cast<T>(parent.session(service, ""));
|
||||
return static_cap_cast<T>(parent.session_cap(id));
|
||||
}
|
||||
|
||||
Expanding_ram_session_client ram;
|
||||
@ -60,10 +60,12 @@ class Genode::Platform_env : public Env_deprecated,
|
||||
|
||||
Resources(Parent &parent)
|
||||
:
|
||||
ram(request<Ram_session>(parent, "Env::ram_session")),
|
||||
cpu(request<Cpu_session>(parent, "Env::cpu_session")),
|
||||
pd (request<Pd_session> (parent, "Env::pd_session")),
|
||||
rm (pd, pd.address_space())
|
||||
ram(request<Ram_session>(parent, Parent::Env::ram()),
|
||||
Parent::Env::ram()),
|
||||
cpu(request<Cpu_session>(parent, Parent::Env::cpu()),
|
||||
Parent::Env::cpu()),
|
||||
pd (request<Pd_session> (parent, Parent::Env::pd())),
|
||||
rm (pd, pd.address_space(), Parent::Env::pd())
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -28,19 +28,17 @@ struct Genode::Upgradeable_client : CLIENT
|
||||
{
|
||||
typedef Genode::Capability<typename CLIENT::Rpc_interface> Capability;
|
||||
|
||||
Capability _cap;
|
||||
Parent::Client::Id _id;
|
||||
|
||||
Upgradeable_client(Capability cap) : CLIENT(cap), _cap(cap) { }
|
||||
Upgradeable_client(Capability cap, Parent::Client::Id id)
|
||||
: CLIENT(cap), _id(id) { }
|
||||
|
||||
void upgrade_ram(size_t quota)
|
||||
{
|
||||
log("upgrading quota donation for Env::", CLIENT::Rpc_interface::service_name(),
|
||||
" (", quota, " bytes)");
|
||||
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "ram_quota=%lu", quota);
|
||||
|
||||
env()->parent()->upgrade(_cap, buf);
|
||||
env()->parent()->upgrade(_id, buf);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -43,12 +43,16 @@ namespace {
|
||||
|
||||
public:
|
||||
|
||||
class Quota_exceeded : Exception { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param quantim number of bytes to transfer
|
||||
* \param from donator RAM session
|
||||
* \param to receiver RAM session
|
||||
*
|
||||
* \throw Quota_exceeded
|
||||
*/
|
||||
Transfer(size_t quantum,
|
||||
Ram_session_capability from,
|
||||
@ -58,7 +62,7 @@ namespace {
|
||||
if (_from.valid() && _to.valid() &&
|
||||
Ram_session_client(_from).transfer_quota(_to, quantum)) {
|
||||
warning("not enough quota for a donation of ", quantum, " bytes");
|
||||
throw Parent::Quota_exceeded();
|
||||
throw Quota_exceeded();
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,198 +87,15 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Child::Session **
|
||||
********************/
|
||||
|
||||
class Child::Session : public Object_pool<Session>::Entry,
|
||||
public List<Session>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
enum { IDENT_LEN = 16 };
|
||||
|
||||
/**
|
||||
* Session capability at the server
|
||||
*/
|
||||
Session_capability _cap;
|
||||
|
||||
/**
|
||||
* Service interface that was used to create the session
|
||||
*/
|
||||
Service *_service;
|
||||
|
||||
/**
|
||||
* Server implementing the session
|
||||
*
|
||||
* Even though we can normally determine the server of the session via
|
||||
* '_service->server()', this does not apply when destructing a server.
|
||||
* During destruction, we use the 'Server' pointer as opaque key for
|
||||
* revoking active sessions of the server. So we keep a copy
|
||||
* independent of the 'Service' object.
|
||||
*/
|
||||
Server *_server;
|
||||
|
||||
/**
|
||||
* Total of quota associated with this session
|
||||
*/
|
||||
size_t _donated_ram_quota;
|
||||
|
||||
/**
|
||||
* Name of session, used for debugging
|
||||
*/
|
||||
char _ident[IDENT_LEN];
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param session session capability
|
||||
* \param service service that implements the session
|
||||
* \param ram_quota initial quota donation associated with
|
||||
* the session
|
||||
* \param ident optional session identifier, used for
|
||||
* debugging
|
||||
*/
|
||||
Session(Session_capability session, Service *service,
|
||||
size_t ram_quota, const char *ident = "<noname>")
|
||||
:
|
||||
Object_pool<Session>::Entry(session), _cap(session),
|
||||
_service(service), _server(service->server()),
|
||||
_donated_ram_quota(ram_quota) {
|
||||
strncpy(_ident, ident, sizeof(_ident)); }
|
||||
|
||||
/**
|
||||
* Default constructor creates invalid session
|
||||
*/
|
||||
Session() : _service(0), _donated_ram_quota(0) { }
|
||||
|
||||
/**
|
||||
* Extend amount of ram attached to the session
|
||||
*/
|
||||
void upgrade_ram_quota(size_t ram_quota) {
|
||||
_donated_ram_quota += ram_quota; }
|
||||
|
||||
/**
|
||||
* Accessors
|
||||
*/
|
||||
Session_capability cap() const { return _cap; }
|
||||
size_t donated_ram_quota() const { return _donated_ram_quota; }
|
||||
bool valid() const { return _service != 0; }
|
||||
Service *service() const { return _service; }
|
||||
Server *server() const { return _server; }
|
||||
const char *ident() const { return _ident; }
|
||||
};
|
||||
|
||||
|
||||
/***********
|
||||
** Child **
|
||||
***********/
|
||||
|
||||
void Child::_add_session(Child::Session const &s)
|
||||
template <typename SESSION>
|
||||
static Service &parent_service()
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
/*
|
||||
* Store session information in a new child's meta data structure. The
|
||||
* allocation from 'heap()' may throw a 'Ram_session::Quota_exceeded'
|
||||
* exception.
|
||||
*/
|
||||
Session *session = 0;
|
||||
try {
|
||||
session = new (heap())
|
||||
Session(s.cap(), s.service(),
|
||||
s.donated_ram_quota(), s.ident()); }
|
||||
catch (Allocator::Out_of_memory) {
|
||||
throw Parent::Quota_exceeded(); }
|
||||
|
||||
/* these functions may also throw 'Ram_session::Quota_exceeded' */
|
||||
_session_pool.insert(session);
|
||||
_session_list.insert(session);
|
||||
}
|
||||
|
||||
|
||||
void Child::_remove_session(Child::Session *s)
|
||||
{
|
||||
/* forget about this session */
|
||||
_session_list.remove(s);
|
||||
|
||||
/* return session quota to the ram session of the child */
|
||||
if (_policy.ref_ram_session()->transfer_quota(_ram, s->donated_ram_quota()))
|
||||
error("We ran out of our own quota");
|
||||
|
||||
destroy(heap(), s);
|
||||
}
|
||||
|
||||
|
||||
Service &Child::_parent_service()
|
||||
{
|
||||
static Parent_service parent_service("");
|
||||
return parent_service;
|
||||
}
|
||||
|
||||
|
||||
void Child::_close(Session* s)
|
||||
{
|
||||
if (!s) {
|
||||
warning("no session structure found");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a chance that the server is not responding to the 'close' call,
|
||||
* making us block infinitely. However, by using core's cancel-blocking
|
||||
* mechanism, we can cancel the 'close' call by another (watchdog) thread
|
||||
* that invokes 'cancel_blocking' at our thread after a timeout. The
|
||||
* unblocking is reflected at the API level as an 'Blocking_canceled'
|
||||
* exception. We catch this exception to proceed with normal operation
|
||||
* after being unblocked.
|
||||
*/
|
||||
try { s->service()->close(s->cap()); }
|
||||
catch (Blocking_canceled) {
|
||||
warning("Got Blocking_canceled exception during ", s->ident(), "->close call"); }
|
||||
|
||||
/*
|
||||
* If the session was provided by a child of us,
|
||||
* 'server()->ram_session_cap()' returns the RAM session of the
|
||||
* corresponding child. Since the session to the server is closed now, we
|
||||
* expect that the server released all donated resources and we can
|
||||
* decrease the servers' quota.
|
||||
*
|
||||
* If this goes wrong, the server is misbehaving.
|
||||
*/
|
||||
if (s->service()->ram_session_cap().valid()) {
|
||||
Ram_session_client server_ram(s->service()->ram_session_cap());
|
||||
if (server_ram.transfer_quota(_policy.ref_ram_cap(),
|
||||
s->donated_ram_quota())) {
|
||||
error("Misbehaving server '", s->service()->name(), "'!");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
_remove_session(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Child::revoke_server(Server const *server)
|
||||
{
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
while (1) {
|
||||
/* search session belonging to the specified server */
|
||||
Session *s = _session_list.first();
|
||||
for ( ; s && (s->server() != server); s = s->next());
|
||||
|
||||
/* if no matching session exists, we are done */
|
||||
if (!s) return;
|
||||
|
||||
_session_pool.apply(s->cap(), [&] (Session *s) {
|
||||
if (s) _session_pool.remove(s); });
|
||||
_remove_session(s);
|
||||
}
|
||||
static Parent_service service(SESSION::service_name());
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
@ -298,133 +119,374 @@ void Child::notify_resource_avail() const
|
||||
}
|
||||
|
||||
|
||||
void Child::announce(Parent::Service_name const &name, Root_capability root)
|
||||
void Child::announce(Parent::Service_name const &name)
|
||||
{
|
||||
if (!name.valid_string()) return;
|
||||
|
||||
_policy.announce_service(name.string(), root, heap(), &_server);
|
||||
_policy.announce_service(name.string());
|
||||
}
|
||||
|
||||
|
||||
Session_capability Child::session(Parent::Service_name const &name,
|
||||
void Child::session_sigh(Signal_context_capability sigh)
|
||||
{
|
||||
_session_sigh = sigh;
|
||||
|
||||
if (!_session_sigh.valid())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Deliver pending session response if a session became available before
|
||||
* the signal handler got installed. This can happen for the very first
|
||||
* asynchronously created session of a component. In 'component.cc', the
|
||||
* signal handler is registered as response of the session request that
|
||||
* needs asynchronous handling.
|
||||
*/
|
||||
_id_space.for_each<Session_state const>([&] (Session_state const &session) {
|
||||
if (session.phase == Session_state::AVAILABLE
|
||||
&& sigh.valid() && session.async_client_notify)
|
||||
Signal_transmitter(sigh).submit(); });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create session-state object for a dynamically created session
|
||||
*
|
||||
* \throw Parent::Quota_exceeded
|
||||
* \throw Parent::Service_denied
|
||||
*/
|
||||
Session_state &
|
||||
create_session(Child_policy::Name const &child_name, Service &service,
|
||||
Session_state::Factory &factory, Id_space<Parent::Client> &id_space,
|
||||
Parent::Client::Id id, Session_state::Args const &args,
|
||||
Affinity const &affinity)
|
||||
{
|
||||
try {
|
||||
return service.create_session(factory, id_space, id, args, affinity);
|
||||
}
|
||||
catch (Allocator::Out_of_memory) {
|
||||
error("could not allocate session meta data for child ", child_name);
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
catch (Id_space<Parent::Client>::Conflicting_id) {
|
||||
|
||||
error(child_name, " requested conflicting session ID ", id, " "
|
||||
"(service=", service.name(), " args=", args, ")");
|
||||
|
||||
id_space.apply<Session_state>(id, [&] (Session_state &session) {
|
||||
error("existing session: ", session); });
|
||||
}
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
||||
|
||||
Session_capability Child::session(Parent::Client::Id id,
|
||||
Parent::Service_name const &name,
|
||||
Parent::Session_args const &args,
|
||||
Affinity const &affinity)
|
||||
{
|
||||
if (!name.valid_string() || !args.valid_string()) throw Unavailable();
|
||||
|
||||
/* return sessions that we created for the child */
|
||||
if (!strcmp("Env::ram_session", name.string())) return _ram;
|
||||
if (!strcmp("Env::cpu_session", name.string())) return _cpu;
|
||||
if (!strcmp("Env::pd_session", name.string())) return _pd;
|
||||
char argbuf[Parent::Session_args::MAX_SIZE];
|
||||
|
||||
/* filter session arguments according to the child policy */
|
||||
strncpy(_args, args.string(), sizeof(_args));
|
||||
_policy.filter_session_args(name.string(), _args, sizeof(_args));
|
||||
strncpy(argbuf, args.string(), sizeof(argbuf));
|
||||
_policy.filter_session_args(name.string(), argbuf, sizeof(argbuf));
|
||||
|
||||
/* filter session affinity */
|
||||
Affinity const filtered_affinity = _policy.filter_session_affinity(affinity);
|
||||
|
||||
/* may throw a 'Parent::Service_denied' exception */
|
||||
Service &service = _policy.resolve_session_request(name.string(), argbuf);
|
||||
|
||||
Session_state &session =
|
||||
create_session(_policy.name(), service, _session_factory,
|
||||
_id_space, id, argbuf, filtered_affinity);
|
||||
|
||||
session.ready_callback = this;
|
||||
session.closed_callback = this;
|
||||
|
||||
/* transfer the quota donation from the child's account to ourself */
|
||||
size_t ram_quota = Arg_string::find_arg(_args, "ram_quota").ulong_value(0);
|
||||
size_t ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0);
|
||||
|
||||
Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap());
|
||||
try {
|
||||
Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap());
|
||||
|
||||
Service *service = _policy.resolve_session_request(name.string(), _args);
|
||||
/* transfer session quota from ourself to the service provider */
|
||||
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
|
||||
service.ram());
|
||||
|
||||
/* raise an error if no matching service provider could be found */
|
||||
if (!service)
|
||||
throw Service_denied();
|
||||
/* try to dispatch session request synchronously */
|
||||
service.initiate_request(session);
|
||||
|
||||
/* transfer session quota from ourself to the service provider */
|
||||
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
|
||||
service->ram_session_cap());
|
||||
if (session.phase == Session_state::INVALID_ARGS) {
|
||||
_revert_quota_and_destroy(session);
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
/* create session */
|
||||
Session_capability cap;
|
||||
try { cap = service->session(_args, filtered_affinity); }
|
||||
catch (Service::Invalid_args) { throw Service_denied(); }
|
||||
catch (Service::Unavailable) { throw Service_denied(); }
|
||||
catch (Service::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
/* finish transaction */
|
||||
donation_from_child.acknowledge();
|
||||
donation_to_service.acknowledge();
|
||||
}
|
||||
catch (Transfer::Quota_exceeded) {
|
||||
/*
|
||||
* Release session meta data if one of the quota transfers went wrong.
|
||||
*/
|
||||
session.destroy();
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
|
||||
/* register session */
|
||||
try { _add_session(Session(cap, service, ram_quota, name.string())); }
|
||||
catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
/*
|
||||
* Copy out the session cap before we are potentially kicking off the
|
||||
* asynchonous request handling at the server to avoid doule-read
|
||||
* issues with the session.cap, which will be asynchronously assigned
|
||||
* by the server side.
|
||||
*/
|
||||
Session_capability cap = session.cap;
|
||||
|
||||
/* finish transaction */
|
||||
donation_from_child.acknowledge();
|
||||
donation_to_service.acknowledge();
|
||||
/* if request was not handled synchronously, kick off async operation */
|
||||
if (session.phase == Session_state::CREATE_REQUESTED)
|
||||
service.wakeup();
|
||||
|
||||
if (cap.valid())
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
|
||||
void Child::upgrade(Session_capability to_session, Parent::Upgrade_args const &args)
|
||||
Session_capability Child::session_cap(Client::Id id)
|
||||
{
|
||||
Service *targeted_service = 0;
|
||||
Session_capability cap;
|
||||
|
||||
/* check of upgrade refers to an Env:: resource */
|
||||
if (to_session.local_name() == _ram.local_name())
|
||||
targeted_service = &_ram_service;
|
||||
if (to_session.local_name() == _cpu.local_name())
|
||||
targeted_service = &_cpu_service;
|
||||
if (to_session.local_name() == _pd.local_name())
|
||||
targeted_service = &_pd_service;
|
||||
auto lamda = [&] (Session_state &session) {
|
||||
|
||||
/* check if upgrade refers to server */
|
||||
_session_pool.apply(to_session, [&] (Session *session)
|
||||
{
|
||||
if (session)
|
||||
targeted_service = session->service();
|
||||
if (session.phase == Session_state::INVALID_ARGS) {
|
||||
|
||||
if (!targeted_service) {
|
||||
warning("could not lookup service for session upgrade");
|
||||
return;
|
||||
/*
|
||||
* Implicity discard the session request when delivering an
|
||||
* exception because the exception will trigger the deallocation
|
||||
* of the session ID at the child anyway.
|
||||
*/
|
||||
_revert_quota_and_destroy(session);
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
||||
if (!args.valid_string()) {
|
||||
warning("no valid session-upgrade arguments");
|
||||
if (!session.alive())
|
||||
warning(_policy.name(), ": attempt to request cap for unavailable session: ", session);
|
||||
|
||||
if (session.cap.valid())
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
|
||||
cap = session.cap;
|
||||
};
|
||||
|
||||
try {
|
||||
_id_space.apply<Session_state>(id, lamda); }
|
||||
|
||||
catch (Id_space<Parent::Client>::Unknown_id) {
|
||||
warning(_policy.name(), " requested session cap for unknown ID"); }
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
|
||||
Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const &args)
|
||||
{
|
||||
if (!args.valid_string()) {
|
||||
warning("no valid session-upgrade arguments");
|
||||
return UPGRADE_DONE;
|
||||
}
|
||||
|
||||
Upgrade_result result = UPGRADE_PENDING;
|
||||
|
||||
_id_space.apply<Session_state>(id, [&] (Session_state &session) {
|
||||
|
||||
if (session.phase != Session_state::CAP_HANDED_OUT) {
|
||||
warning("attempt to upgrade session in invalid state");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t const ram_quota =
|
||||
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
|
||||
|
||||
/* transfer quota from client to ourself */
|
||||
Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap());
|
||||
try {
|
||||
/* transfer quota from client to ourself */
|
||||
Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap());
|
||||
|
||||
/* transfer session quota from ourself to the service provider */
|
||||
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
|
||||
targeted_service->ram_session_cap());
|
||||
/* transfer session quota from ourself to the service provider */
|
||||
Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(),
|
||||
session.service().ram());
|
||||
|
||||
try { targeted_service->upgrade(to_session, args.string()); }
|
||||
catch (Service::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
session.increase_donated_quota(ram_quota);
|
||||
session.phase = Session_state::UPGRADE_REQUESTED;
|
||||
|
||||
/* remember new amount attached to the session */
|
||||
if (session)
|
||||
session->upgrade_ram_quota(ram_quota);
|
||||
session.service().initiate_request(session);
|
||||
|
||||
/* finish transaction */
|
||||
donation_from_child.acknowledge();
|
||||
donation_to_service.acknowledge();
|
||||
/* finish transaction */
|
||||
donation_from_child.acknowledge();
|
||||
donation_to_service.acknowledge();
|
||||
}
|
||||
catch (Transfer::Quota_exceeded) {
|
||||
warning(_policy.name(), ": upgrade of ", session.service().name(), " failed");
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
|
||||
if (session.phase == Session_state::CAP_HANDED_OUT) {
|
||||
result = UPGRADE_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
session.service().wakeup();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void Child::close(Session_capability session_cap)
|
||||
void Child::_revert_quota_and_destroy(Session_state &session)
|
||||
{
|
||||
try {
|
||||
/* transfer session quota from the service to ourself */
|
||||
Transfer donation_from_service(session.donated_ram_quota(),
|
||||
session.service().ram(), _policy.ref_ram_cap());
|
||||
|
||||
/* transfer session quota from ourself to the client (our child) */
|
||||
Transfer donation_to_client(session.donated_ram_quota(),
|
||||
_policy.ref_ram_cap(), ram_session_cap());
|
||||
/* finish transaction */
|
||||
donation_from_service.acknowledge();
|
||||
donation_to_client.acknowledge();
|
||||
}
|
||||
catch (Transfer::Quota_exceeded) {
|
||||
warning(_policy.name(), ": could not revert session quota (", session, ")"); }
|
||||
|
||||
session.destroy();
|
||||
}
|
||||
|
||||
|
||||
Child::Close_result Child::_close(Session_state &session)
|
||||
{
|
||||
/*
|
||||
* If session could not be established, destruct session immediately
|
||||
* without involving the server
|
||||
*/
|
||||
if (session.phase == Session_state::INVALID_ARGS) {
|
||||
_revert_quota_and_destroy(session);
|
||||
return CLOSE_DONE;
|
||||
}
|
||||
|
||||
/* close session if alive */
|
||||
if (session.alive()) {
|
||||
session.phase = Session_state::CLOSE_REQUESTED;
|
||||
session.service().initiate_request(session);
|
||||
}
|
||||
|
||||
/*
|
||||
* The service may have completed the close request immediately (e.g.,
|
||||
* a locally implemented service). In this case, we can skip the
|
||||
* asynchonous handling.
|
||||
*/
|
||||
|
||||
if (session.phase == Session_state::CLOSED) {
|
||||
_revert_quota_and_destroy(session);
|
||||
return CLOSE_DONE;
|
||||
}
|
||||
|
||||
session.discard_id_at_client();
|
||||
session.service().wakeup();
|
||||
|
||||
return CLOSE_PENDING;
|
||||
}
|
||||
|
||||
|
||||
Child::Close_result Child::close(Client::Id id)
|
||||
{
|
||||
/* refuse to close the child's initial sessions */
|
||||
if (session_cap.local_name() == _ram.local_name()
|
||||
|| session_cap.local_name() == _cpu.local_name()
|
||||
|| session_cap.local_name() == _pd.local_name())
|
||||
return;
|
||||
if (Parent::Env::session_id(id))
|
||||
return CLOSE_DONE;
|
||||
|
||||
Session *session = nullptr;
|
||||
_session_pool.apply(session_cap, [&] (Session *s)
|
||||
{
|
||||
session = s;
|
||||
if (s) _session_pool.remove(s);
|
||||
});
|
||||
_close(session);
|
||||
try {
|
||||
Close_result result = CLOSE_PENDING;
|
||||
auto lamda = [&] (Session_state &session) { result = _close(session); };
|
||||
_id_space.apply<Session_state>(id, lamda);
|
||||
return result;
|
||||
}
|
||||
catch (Id_space<Parent::Client>::Unknown_id) { return CLOSE_DONE; }
|
||||
}
|
||||
|
||||
|
||||
void Child::session_ready(Session_state &session)
|
||||
{
|
||||
if (_session_sigh.valid() && session.async_client_notify)
|
||||
Signal_transmitter(_session_sigh).submit();
|
||||
}
|
||||
|
||||
|
||||
void Child::session_closed(Session_state &session)
|
||||
{
|
||||
/*
|
||||
* If the session was provided by a child of us, 'service.ram()' returns
|
||||
* the RAM session of the corresponding child. Since the session to the
|
||||
* server is closed now, we expect the server to have released all donated
|
||||
* resources so that we can decrease the servers' quota.
|
||||
*
|
||||
* If this goes wrong, the server is misbehaving.
|
||||
*/
|
||||
_revert_quota_and_destroy(session);
|
||||
|
||||
if (_session_sigh.valid())
|
||||
Signal_transmitter(_session_sigh).submit();
|
||||
}
|
||||
|
||||
|
||||
void Child::session_response(Server::Id id, Session_response response)
|
||||
{
|
||||
try {
|
||||
_policy.server_id_space().apply<Session_state>(id, [&] (Session_state &session) {
|
||||
|
||||
switch (response) {
|
||||
|
||||
case Parent::SESSION_CLOSED:
|
||||
session.phase = Session_state::CLOSED;
|
||||
if (session.closed_callback)
|
||||
session.closed_callback->session_closed(session);
|
||||
break;
|
||||
|
||||
case Parent::INVALID_ARGS:
|
||||
session.phase = Session_state::INVALID_ARGS;
|
||||
if (session.ready_callback)
|
||||
session.ready_callback->session_ready(session);
|
||||
break;
|
||||
|
||||
case Parent::SESSION_OK:
|
||||
if (session.phase == Session_state::UPGRADE_REQUESTED) {
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
if (session.ready_callback)
|
||||
session.ready_callback->session_ready(session);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
} catch (Child_policy::Nonexistent_id_space) { }
|
||||
}
|
||||
|
||||
|
||||
void Child::deliver_session_cap(Server::Id id, Session_capability cap)
|
||||
{
|
||||
try {
|
||||
_policy.server_id_space().apply<Session_state>(id, [&] (Session_state &session) {
|
||||
|
||||
if (session.cap.valid()) {
|
||||
error("attempt to assign session cap twice");
|
||||
return;
|
||||
}
|
||||
|
||||
session.cap = cap;
|
||||
session.phase = Session_state::AVAILABLE;
|
||||
|
||||
if (session.ready_callback)
|
||||
session.ready_callback->session_ready(session);
|
||||
});
|
||||
} catch (Child_policy::Nonexistent_id_space) { }
|
||||
}
|
||||
|
||||
|
||||
@ -474,32 +536,41 @@ Parent::Resource_args Child::yield_request()
|
||||
void Child::yield_response() { _policy.yield_response(); }
|
||||
|
||||
|
||||
Child::Child(Dataspace_capability elf_ds,
|
||||
Dataspace_capability ldso_ds,
|
||||
Pd_session_capability pd_cap,
|
||||
Pd_session &pd,
|
||||
Ram_session_capability ram_cap,
|
||||
Ram_session &ram,
|
||||
Cpu_session_capability cpu_cap,
|
||||
Initial_thread_base &initial_thread,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Return interface for interacting with the child's address space
|
||||
*
|
||||
* Depending on the return value of 'Child_policy::address_space', we
|
||||
* either interact with a local object of via an RPC client stub.
|
||||
*/
|
||||
struct Child_address_space
|
||||
{
|
||||
Region_map_client _rm_client;
|
||||
Region_map &_rm;
|
||||
|
||||
Child_address_space(Pd_session &pd, Child_policy &policy)
|
||||
:
|
||||
_rm_client(pd.address_space()),
|
||||
_rm(policy.address_space(pd) ? *policy.address_space(pd) : _rm_client)
|
||||
{ }
|
||||
|
||||
Region_map ®ion_map() { return _rm; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Child::Child(Region_map &local_rm,
|
||||
Rpc_entrypoint &entrypoint,
|
||||
Child_policy &policy,
|
||||
Service &pd_service,
|
||||
Service &ram_service,
|
||||
Service &cpu_service)
|
||||
Child_policy &policy)
|
||||
try :
|
||||
_pd(pd_cap), _ram(ram_cap), _cpu(cpu_cap),
|
||||
_pd_service(pd_service),
|
||||
_ram_service(ram_service),
|
||||
_cpu_service(cpu_service),
|
||||
_heap(&ram, &local_rm),
|
||||
_policy(policy),
|
||||
_heap(&_ram.session(), &local_rm),
|
||||
_entrypoint(entrypoint),
|
||||
_parent_cap(_entrypoint.manage(this)),
|
||||
_policy(policy),
|
||||
_server(_ram),
|
||||
_process(elf_ds, ldso_ds, pd_cap, pd, ram, initial_thread, local_rm, remote_rm,
|
||||
_process(_binary.session().dataspace(), _linker_dataspace(),
|
||||
_pd.cap(), _pd.session(), _ram.session(), _initial_thread, local_rm,
|
||||
Child_address_space(_pd.session(), _policy).region_map(),
|
||||
_parent_cap)
|
||||
{ }
|
||||
catch (Cpu_session::Thread_creation_failed) { throw Process_startup_failed(); }
|
||||
@ -512,8 +583,48 @@ catch (Region_map::Attach_failed) { throw Process_startup_failed(); }
|
||||
Child::~Child()
|
||||
{
|
||||
_entrypoint.dissolve(this);
|
||||
_policy.unregister_services();
|
||||
|
||||
_session_pool.remove_all([&] (Session *s) { _close(s); });
|
||||
/*
|
||||
* Purge the meta data about any dangling sessions provided by the child to
|
||||
* other children.
|
||||
*
|
||||
* Note that the session quota is not transferred back to the respective
|
||||
* clients.
|
||||
*
|
||||
* All the session meta data is lost after this point. In principle, we
|
||||
* could accumulate the to-be-replenished quota at each client. Once the
|
||||
* server is completely destroyed (and we thereby regained all of the
|
||||
* server's resources, the RAM sessions of the clients could be updated.
|
||||
* However, a client of a suddenly disappearing server is expected to be in
|
||||
* trouble anyway and likely to get stuck on the next attempt to interact
|
||||
* with the server. So the added complexity of reverting the session quotas
|
||||
* would be to no benefit.
|
||||
*/
|
||||
try {
|
||||
auto lambda = [&] (Session_state &s) { _revert_quota_and_destroy(s); };
|
||||
while (_policy.server_id_space().apply_any<Session_state>(lambda));
|
||||
}
|
||||
catch (Child_policy::Nonexistent_id_space) { }
|
||||
|
||||
/*
|
||||
* Remove statically created env sessions from the child's ID space.
|
||||
*/
|
||||
auto discard_id_fn = [&] (Session_state &s) { s.discard_id_at_client(); };
|
||||
|
||||
_id_space.apply<Session_state>(Env::ram(), discard_id_fn);
|
||||
_id_space.apply<Session_state>(Env::cpu(), discard_id_fn);
|
||||
_id_space.apply<Session_state>(Env::pd(), discard_id_fn);
|
||||
_id_space.apply<Session_state>(Env::log(), discard_id_fn);
|
||||
|
||||
/*
|
||||
* Remove dynamically created sessions from the child's ID space.
|
||||
*/
|
||||
auto close_fn = [&] (Session_state &session) {
|
||||
session.closed_callback = nullptr;
|
||||
session.ready_callback = nullptr;
|
||||
_close(session);
|
||||
};
|
||||
|
||||
while (_id_space.apply_any<Session_state>(close_fn));
|
||||
}
|
||||
|
||||
|
@ -14,21 +14,55 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/component.h>
|
||||
#include <base/connection.h>
|
||||
#include <base/service.h>
|
||||
#include <base/env.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/globals.h>
|
||||
|
||||
/*
|
||||
* XXX remove this pointer once 'Env_deprecated' is removed
|
||||
*/
|
||||
static Genode::Env *env_ptr = nullptr;
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct Env : Genode::Env
|
||||
{
|
||||
Genode::Entrypoint &_ep;
|
||||
|
||||
Env(Genode::Entrypoint &ep) : _ep(ep) { }
|
||||
Genode::Parent &_parent = *env()->parent();
|
||||
|
||||
Genode::Parent &parent() override { return *Genode::env()->parent(); }
|
||||
/**
|
||||
* Lock for serializing 'session' and 'close'
|
||||
*/
|
||||
Genode::Lock _lock;
|
||||
|
||||
/**
|
||||
* Utility to used block for single signal
|
||||
*/
|
||||
struct Blockade
|
||||
{
|
||||
Parent &_parent;
|
||||
Genode::Signal_receiver _sig_rec;
|
||||
Genode::Signal_context _sig_ctx;
|
||||
|
||||
Blockade(Parent &parent) : _parent(parent)
|
||||
{
|
||||
_parent.session_sigh(_sig_rec.manage(&_sig_ctx));
|
||||
}
|
||||
|
||||
void block() { _sig_rec.wait_for_signal(); }
|
||||
};
|
||||
|
||||
Lazy_volatile_object<Blockade> _session_blockade;
|
||||
|
||||
Env(Genode::Entrypoint &ep) : _ep(ep) { env_ptr = this; }
|
||||
|
||||
Genode::Parent &parent() override { return _parent; }
|
||||
Genode::Ram_session &ram() override { return *Genode::env()->ram_session(); }
|
||||
Genode::Cpu_session &cpu() override { return *Genode::env()->cpu_session(); }
|
||||
Genode::Region_map &rm() override { return *Genode::env()->rm_session(); }
|
||||
@ -49,6 +83,54 @@ namespace {
|
||||
{
|
||||
return Genode::env()->pd_session_cap();
|
||||
}
|
||||
|
||||
Genode::Id_space<Parent::Client> &id_space() override
|
||||
{
|
||||
return Genode::env_session_id_space();
|
||||
}
|
||||
|
||||
void _block_for_session()
|
||||
{
|
||||
/*
|
||||
* Construct blockade lazily be avoid it being used in core where
|
||||
* all session requests are immediately answered.
|
||||
*/
|
||||
if (!_session_blockade.constructed())
|
||||
_session_blockade.construct(_parent);
|
||||
|
||||
_session_blockade->block();
|
||||
}
|
||||
|
||||
Session_capability session(Parent::Service_name const &name,
|
||||
Parent::Client::Id id,
|
||||
Parent::Session_args const &args,
|
||||
Affinity const &affinity) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Session_capability cap = _parent.session(id, name, args, affinity);
|
||||
if (cap.valid())
|
||||
return cap;
|
||||
|
||||
_block_for_session();
|
||||
return _parent.session_cap(id);
|
||||
}
|
||||
|
||||
void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (_parent.upgrade(id, args) == Parent::UPGRADE_PENDING)
|
||||
_block_for_session();
|
||||
}
|
||||
|
||||
void close(Parent::Client::Id id) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (_parent.close(id) == Parent::CLOSE_PENDING)
|
||||
_block_for_session();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -58,6 +140,15 @@ namespace Genode {
|
||||
struct Startup;
|
||||
|
||||
extern void bootstrap_component();
|
||||
|
||||
Env &internal_env()
|
||||
{
|
||||
class Env_ptr_not_initialized { };
|
||||
if (!env_ptr)
|
||||
throw Env_ptr_not_initialized();
|
||||
|
||||
return *env_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,6 +85,7 @@ void Entrypoint::_process_incoming_signals()
|
||||
_suspended_callback();
|
||||
|
||||
init_signal_thread(_env);
|
||||
|
||||
_rpc_ep.construct(&_env.pd(), Component::stack_size(), initial_ep_name());
|
||||
_signal_proxy_cap = manage(_signal_proxy);
|
||||
_sig_rec.construct();
|
||||
@ -165,6 +166,9 @@ Entrypoint::Entrypoint(Env &env)
|
||||
/* initialize signalling after initializing but before calling the entrypoint */
|
||||
init_signal_thread(_env);
|
||||
|
||||
/* initialize emulation of the original synchronous root interface */
|
||||
init_root_proxy(_env);
|
||||
|
||||
/*
|
||||
* Invoke Component::construct function in the context of the entrypoint.
|
||||
*/
|
||||
|
@ -13,6 +13,9 @@
|
||||
*/
|
||||
|
||||
#include <base/internal/platform_env.h>
|
||||
#include <base/internal/globals.h>
|
||||
#include <base/connection.h>
|
||||
#include <base/service.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
|
59
repos/base/src/lib/base/env_session_id_space.cc
Normal file
59
repos/base/src/lib/base/env_session_id_space.cc
Normal 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(); }
|
||||
|
||||
|
@ -26,10 +26,21 @@ class Log_console : public Console
|
||||
|
||||
enum { _BUF_SIZE = Log_session::MAX_STRING_LEN };
|
||||
|
||||
Log_connection _log;
|
||||
char _buf[_BUF_SIZE];
|
||||
unsigned _num_chars;
|
||||
Lock _lock;
|
||||
|
||||
struct Log : Log_session_client
|
||||
{
|
||||
Session_capability _cap() {
|
||||
return env()->parent()->session_cap(Parent::Env::log()); }
|
||||
|
||||
Log() : Log_session_client(reinterpret_cap_cast<Log_session>(_cap()))
|
||||
{ }
|
||||
};
|
||||
|
||||
Log _log;
|
||||
|
||||
char _buf[_BUF_SIZE];
|
||||
unsigned _num_chars;
|
||||
Lock _lock;
|
||||
|
||||
void _flush()
|
||||
{
|
||||
@ -77,7 +88,7 @@ class Log_console : public Console
|
||||
/**
|
||||
* Return LOG session interface
|
||||
*/
|
||||
Log_session *log_session() { return &_log; }
|
||||
Log_session &log_session() { return _log; }
|
||||
|
||||
/**
|
||||
* Re-establish LOG session
|
||||
@ -85,13 +96,13 @@ class Log_console : public Console
|
||||
void reconnect()
|
||||
{
|
||||
/*
|
||||
* Note that the destructor of old 'Log_connection' is not called.
|
||||
* This is not needed because the only designated use of this
|
||||
* function is the startup procedure of noux processes created
|
||||
* via fork. At the point of calling this function, the new child
|
||||
* has no valid capability to the original LOG session anyway.
|
||||
* We cannot use a 'Volatile_object' because we have to skip
|
||||
* the object destruction inside a freshly forked process.
|
||||
* Otherwise, the attempt to destruct the capability contained
|
||||
* in the 'Log' object would result in an inconsistent ref counter
|
||||
* of the respective capability-space element.
|
||||
*/
|
||||
new (&_log) Log_connection;
|
||||
construct_at<Log>(&_log);
|
||||
}
|
||||
};
|
||||
|
||||
@ -109,7 +120,7 @@ static Log_console *stdout_log_console() { return unmanaged_singleton<Log_consol
|
||||
*/
|
||||
extern "C" int stdout_write(const char *s)
|
||||
{
|
||||
return stdout_log_console()->log_session()->write(s);
|
||||
return stdout_log_console()->log_session().write(s);
|
||||
}
|
||||
|
||||
|
||||
|
251
repos/base/src/lib/base/root_proxy.cc
Normal file
251
repos/base/src/lib/base/root_proxy.cc
Normal 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 });
|
||||
}
|
@ -26,13 +26,8 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd,
|
||||
Untyped_capability new_obj_cap =
|
||||
retry<Genode::Pd_session::Out_of_metadata>(
|
||||
[&] () { return pd.alloc_rpc_cap(_cap); },
|
||||
[&] () {
|
||||
Pd_session_client *client =
|
||||
dynamic_cast<Pd_session_client*>(&pd);
|
||||
|
||||
if (client)
|
||||
env()->parent()->upgrade(*client, "ram_quota=16K");
|
||||
});
|
||||
[&] () { env()->parent()->upgrade(Parent::Env::pd(),
|
||||
"ram_quota=16K"); });
|
||||
|
||||
return new_obj_cap;
|
||||
}
|
||||
|
97
repos/base/src/lib/base/session_state.cc
Normal file
97
repos/base/src/lib/base/session_state.cc
Normal 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)
|
||||
{ }
|
@ -241,7 +241,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context *context)
|
||||
|
||||
log("upgrading quota donation for PD session (", quota, " bytes)");
|
||||
|
||||
env()->parent()->upgrade(env()->pd_session_cap(), buf);
|
||||
env()->parent()->upgrade(Parent::Env::pd(), buf);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -75,14 +75,32 @@ struct Linker::File
|
||||
*/
|
||||
struct Linker::Elf_file : File
|
||||
{
|
||||
Env &env;
|
||||
Rom_connection rom;
|
||||
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR];
|
||||
bool const loaded;
|
||||
Env &env;
|
||||
Lazy_volatile_object<Rom_connection> rom_connection;
|
||||
Rom_session_client rom;
|
||||
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR];
|
||||
bool const loaded;
|
||||
|
||||
typedef String<64> Name;
|
||||
|
||||
Rom_session_capability _rom_cap(Name const &name)
|
||||
{
|
||||
/* request the linker and binary from the component environment */
|
||||
Session_capability cap;
|
||||
if (name == binary_name())
|
||||
cap = env.parent().session_cap(Parent::Env::binary());
|
||||
if (name == linker_name())
|
||||
cap = env.parent().session_cap(Parent::Env::linker());
|
||||
if (cap.valid())
|
||||
return reinterpret_cap_cast<Rom_session>(cap);
|
||||
|
||||
rom_connection.construct(env, name.string());
|
||||
return *rom_connection;
|
||||
}
|
||||
|
||||
Elf_file(Env &env, Allocator &md_alloc, char const *name, bool load)
|
||||
:
|
||||
env(env), rom(env, name), loaded(load)
|
||||
env(env), rom(_rom_cap(name)), loaded(load)
|
||||
{
|
||||
load_phdr();
|
||||
|
||||
|
@ -103,12 +103,6 @@ namespace Linker {
|
||||
* Global ELF access lock
|
||||
*/
|
||||
Lock &lock();
|
||||
|
||||
/**
|
||||
* Invariants
|
||||
*/
|
||||
constexpr char const *binary_name() { return "binary"; }
|
||||
constexpr char const *linker_name() { return "ld.lib.so"; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -96,7 +96,7 @@ class Linker::Region_map
|
||||
[&] () {
|
||||
return _rm.attach_at(ds, local_addr - _base, size, offset);
|
||||
},
|
||||
[&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); });
|
||||
[&] () { _env.upgrade(Parent::Env::pd(), "ram_quota=8K"); });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +109,7 @@ class Linker::Region_map
|
||||
[&] () {
|
||||
return _rm.attach_executable(ds, local_addr - _base, size, offset);
|
||||
},
|
||||
[&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); });
|
||||
[&] () { _env.upgrade(Parent::Env::pd(), "ram_quota=8K"); });
|
||||
}
|
||||
|
||||
void detach(Local_addr local_addr) { _rm.detach((addr_t)local_addr - _base); }
|
||||
|
@ -38,6 +38,12 @@ namespace Linker {
|
||||
|
||||
enum Bind { BIND_LAZY = Shared_object::BIND_LAZY,
|
||||
BIND_NOW = Shared_object::BIND_NOW };
|
||||
|
||||
/**
|
||||
* Invariants
|
||||
*/
|
||||
constexpr char const *binary_name() { return "binary"; }
|
||||
constexpr char const *linker_name() { return "ld.lib.so"; }
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__TYPES_H_ */
|
||||
|
@ -4,10 +4,10 @@
|
||||
* \date 2008-09-24
|
||||
*
|
||||
* This program starts itself as child. When started, it first determines
|
||||
* wheather it is parent or child by requesting its own file from the ROM
|
||||
* service. Because the program blocks all session-creation calls for the
|
||||
* ROM service, each program instance can determine its parent or child
|
||||
* role by the checking the result of the session creation.
|
||||
* wheather it is parent or child by requesting a RM session. Because the
|
||||
* program blocks all session-creation calls for the RM service, each program
|
||||
* instance can determine its parent or child role by the checking the result
|
||||
* of the session creation.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -17,17 +17,11 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#include <base/component.h>
|
||||
#include <base/log.h>
|
||||
#include <base/env.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/child.h>
|
||||
#include <pd_session/connection.h>
|
||||
#include <rm_session/connection.h>
|
||||
#include <ram_session/connection.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <cpu_session/connection.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <rm_session/client.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
@ -69,108 +63,135 @@ void main_child()
|
||||
** Parent **
|
||||
************/
|
||||
|
||||
class Test_child : public Child_policy
|
||||
class Test_child_policy : public Child_policy
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Registered<Genode::Parent_service> Parent_service;
|
||||
typedef Registry<Parent_service> Parent_services;
|
||||
|
||||
private:
|
||||
|
||||
enum { STACK_SIZE = 8*1024 };
|
||||
|
||||
/*
|
||||
* Entry point used for serving the parent interface
|
||||
*/
|
||||
Rpc_entrypoint _entrypoint;
|
||||
|
||||
Region_map_client _address_space;
|
||||
Pd_session_client _pd;
|
||||
Ram_session_client _ram;
|
||||
Cpu_session_client _cpu;
|
||||
Child::Initial_thread _initial_thread;
|
||||
|
||||
Child _child;
|
||||
|
||||
Parent_service _log_service;
|
||||
Env &_env;
|
||||
Parent_services &_parent_services;
|
||||
Signal_context_capability const _fault_handler_sigh;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Test_child(Genode::Dataspace_capability elf_ds,
|
||||
Genode::Pd_connection &pd,
|
||||
Genode::Ram_session_capability ram,
|
||||
Genode::Cpu_session_capability cpu,
|
||||
Genode::Cap_session *cap)
|
||||
Test_child_policy(Env &env, Parent_services &parent_services,
|
||||
Signal_context_capability fault_handler_sigh)
|
||||
:
|
||||
_entrypoint(cap, STACK_SIZE, "child", false),
|
||||
_address_space(pd.address_space()), _pd(pd), _ram(ram), _cpu(cpu),
|
||||
_initial_thread(_cpu, _pd, "child"),
|
||||
_child(elf_ds, Dataspace_capability(), _pd, _pd, _ram, _ram,
|
||||
_cpu, _initial_thread, *env()->rm_session(), _address_space,
|
||||
_entrypoint, *this),
|
||||
_log_service("LOG")
|
||||
{
|
||||
/* start execution of the new child */
|
||||
_entrypoint.activate();
|
||||
}
|
||||
_env(env),
|
||||
_parent_services(parent_services),
|
||||
_fault_handler_sigh(fault_handler_sigh)
|
||||
{ }
|
||||
|
||||
|
||||
/****************************
|
||||
** Child-policy interface **
|
||||
****************************/
|
||||
|
||||
const char *name() const { return "rmchild"; }
|
||||
Name name() const override { return "rmchild"; }
|
||||
|
||||
Service *resolve_session_request(const char *service, const char *)
|
||||
Binary_name binary_name() const override { return "test-rm_fault"; }
|
||||
|
||||
Ram_session &ref_ram() override { return _env.ram(); }
|
||||
|
||||
Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); }
|
||||
|
||||
void init(Ram_session &session, Ram_session_capability cap) override
|
||||
{
|
||||
/* forward white-listed session requests to our parent */
|
||||
return !strcmp(service, "LOG") ? &_log_service : 0;
|
||||
enum { CHILD_QUOTA = 1*1024*1024 };
|
||||
session.ref_account(_env.ram_session_cap());
|
||||
_env.ram().transfer_quota(cap, CHILD_QUOTA);
|
||||
}
|
||||
|
||||
void filter_session_args(const char *service,
|
||||
void init(Pd_session &session, Pd_session_capability cap) override
|
||||
{
|
||||
Region_map_client address_space(session.address_space());
|
||||
address_space.fault_handler(_fault_handler_sigh);
|
||||
}
|
||||
|
||||
Service &resolve_session_request(Service::Name const &service_name,
|
||||
Session_state::Args const &args) override
|
||||
{
|
||||
Service *service = nullptr;
|
||||
_parent_services.for_each([&] (Service &s) {
|
||||
if (!service && service_name == s.name())
|
||||
service = &s; });
|
||||
|
||||
if (!service)
|
||||
throw Parent::Service_denied();
|
||||
|
||||
return *service;
|
||||
}
|
||||
|
||||
void filter_session_args(Service::Name const &,
|
||||
char *args, size_t args_len)
|
||||
{
|
||||
/* define session label for sessions forwarded to our parent */
|
||||
Arg_string::set_arg_string(args, args_len, "label", "child");
|
||||
/* prefix session label */
|
||||
Session_label const orig(label_from_args(args));
|
||||
Arg_string::set_arg_string(args, args_len, "label",
|
||||
prefixed_label(name(), orig).string());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void main_parent(Dataspace_capability elf_ds)
|
||||
struct Main_parent
|
||||
{
|
||||
log("parent role started");
|
||||
Env &_env;
|
||||
|
||||
/* create environment for new child */
|
||||
static Pd_connection pd;
|
||||
static Ram_connection ram;
|
||||
static Cpu_connection cpu;
|
||||
static Cap_connection cap;
|
||||
Signal_handler<Main_parent> _fault_handler {
|
||||
_env.ep(), *this, &Main_parent::_handle_fault };
|
||||
|
||||
/* transfer some of our own ram quota to the new child */
|
||||
enum { CHILD_QUOTA = 1*1024*1024 };
|
||||
ram.ref_account(env()->ram_session_cap());
|
||||
env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA);
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
static Signal_receiver fault_handler;
|
||||
/* parent services */
|
||||
struct Parent_services : Test_child_policy::Parent_services
|
||||
{
|
||||
Allocator &alloc;
|
||||
|
||||
/* register fault handler at the child's address space */
|
||||
static Signal_context signal_context;
|
||||
Region_map_client address_space(pd.address_space());
|
||||
address_space.fault_handler(fault_handler.manage(&signal_context));
|
||||
Parent_services(Allocator &alloc) : alloc(alloc)
|
||||
{
|
||||
static const char *names[] = {
|
||||
"RAM", "PD", "CPU", "ROM", "LOG", 0 };
|
||||
for (unsigned i = 0; names[i]; i++)
|
||||
new (alloc) Test_child_policy::Parent_service(*this, names[i]);
|
||||
}
|
||||
|
||||
~Parent_services()
|
||||
{
|
||||
for_each([&] (Test_child_policy::Parent_service &s) { destroy(alloc, &s); });
|
||||
}
|
||||
} _parent_services { _heap };
|
||||
|
||||
/* create child */
|
||||
static Test_child child(elf_ds, pd, ram.cap(), cpu.cap(), &cap);
|
||||
Test_child_policy _child_policy { _env, _parent_services, _fault_handler };
|
||||
|
||||
/* allocate dataspace used for creating shared memory between parent and child */
|
||||
Dataspace_capability ds = env()->ram_session()->alloc(4096);
|
||||
volatile int *local_addr = env()->rm_session()->attach(ds);
|
||||
Child _child { _env.rm(), _env.ep().rpc_ep(), _child_policy };
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Region_map_client _address_space { _child.pd().address_space() };
|
||||
|
||||
log("wait for region-manager fault");
|
||||
fault_handler.wait_for_signal();
|
||||
log("received region-manager fault signal, request fault state");
|
||||
/* dataspace used for creating shared memory between parent and child */
|
||||
Attached_ram_dataspace _ds { _env.ram(), _env.rm(), 4096 };
|
||||
|
||||
Region_map::State state = address_space.state();
|
||||
unsigned _fault_cnt = 0;
|
||||
|
||||
long volatile &_child_value() { return *_ds.local_addr<long volatile>(); }
|
||||
|
||||
void _handle_fault()
|
||||
{
|
||||
if (_fault_cnt++ == 4) {
|
||||
log("--- parent role of region-manager fault test finished ---");
|
||||
_env.parent().exit(0);
|
||||
}
|
||||
|
||||
log("received region-map fault signal, request fault state");
|
||||
|
||||
Region_map::State state = _address_space.state();
|
||||
|
||||
char const *state_name =
|
||||
state.type == Region_map::State::READ_FAULT ? "READ_FAULT" :
|
||||
@ -182,47 +203,45 @@ void main_parent(Dataspace_capability elf_ds)
|
||||
/* ignore spuriuous fault signal */
|
||||
if (state.type == Region_map::State::READY) {
|
||||
log("ignoring spurious fault signal");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
addr_t child_virt_addr = state.addr & ~(4096 - 1);
|
||||
|
||||
/* allocate dataspace to resolve the fault */
|
||||
log("attach dataspace to the child at ", Hex(child_virt_addr));
|
||||
*local_addr = 0x1234;
|
||||
_child_value() = 0x1234;
|
||||
|
||||
address_space.attach_at(ds, child_virt_addr);
|
||||
_address_space.attach_at(_ds.cap(), child_virt_addr);
|
||||
|
||||
/* wait until our child modifies the dataspace content */
|
||||
while (*local_addr == 0x1234);
|
||||
/* poll until our child modifies the dataspace content */
|
||||
while (_child_value() == 0x1234);
|
||||
|
||||
log("child modified dataspace content, new value is ", Hex(*local_addr));
|
||||
log("child modified dataspace content, new value is ",
|
||||
Hex(_child_value()));
|
||||
|
||||
log("revoke dataspace from child");
|
||||
address_space.detach((void *)child_virt_addr);
|
||||
_address_space.detach((void *)child_virt_addr);
|
||||
}
|
||||
|
||||
fault_handler.dissolve(&signal_context);
|
||||
|
||||
log("--- parent role of region-manager fault test finished ---");
|
||||
}
|
||||
Main_parent(Env &env) : _env(env) { }
|
||||
};
|
||||
|
||||
|
||||
/*************************
|
||||
** Common main program **
|
||||
*************************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
void Component::construct(Env &env)
|
||||
{
|
||||
log("--- region-manager fault test ---");
|
||||
|
||||
/* obtain own elf file from rom service */
|
||||
try {
|
||||
static Rom_connection rom("test-rm_fault");
|
||||
main_parent(rom.dataspace());
|
||||
} catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||
/*
|
||||
* Distinguish parent from child by requesting an service that is only
|
||||
* available to the parent.
|
||||
*/
|
||||
Rm_connection rm;
|
||||
static Main_parent parent(env);
|
||||
log("-- parent role started --");
|
||||
}
|
||||
catch (Parent::Service_denied) {
|
||||
main_child();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_thread_ep;
|
||||
Env &_env;
|
||||
Allocator &_md_alloc;
|
||||
Thread_list &_thread_list;
|
||||
Thread_list_change_handler &_thread_list_change_handler;
|
||||
@ -41,9 +42,8 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
|
||||
Cpu_session_component *_create_session(const char *args) override
|
||||
{
|
||||
Cpu_session_component *cpu_session_component =
|
||||
new (md_alloc()) Cpu_session_component(_thread_ep,
|
||||
_md_alloc,
|
||||
_thread_list,
|
||||
new (md_alloc()) Cpu_session_component(_thread_ep, _env,
|
||||
_md_alloc, _thread_list,
|
||||
_thread_list_change_handler,
|
||||
args);
|
||||
return cpu_session_component;
|
||||
@ -51,18 +51,20 @@ class Cpu_sampler::Cpu_root : public Root_component<Cpu_session_component>
|
||||
|
||||
void _upgrade_session(Cpu_session_component *cpu, const char *args) override
|
||||
{
|
||||
env()->parent()->upgrade(cpu->parent_cpu_session(), args);
|
||||
size_t ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0);
|
||||
cpu->upgrade_ram_quota(ram_quota);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Cpu_root(Rpc_entrypoint &session_ep,
|
||||
Rpc_entrypoint &thread_ep,
|
||||
Env &env,
|
||||
Allocator &md_alloc,
|
||||
Thread_list &thread_list,
|
||||
Thread_list_change_handler &thread_list_change_handler)
|
||||
: Root_component<Cpu_session_component>(&session_ep, &md_alloc),
|
||||
_thread_ep(thread_ep),
|
||||
_thread_ep(thread_ep), _env(env),
|
||||
_md_alloc(md_alloc),
|
||||
_thread_list(thread_list),
|
||||
_thread_list_change_handler(thread_list_change_handler) { }
|
||||
|
@ -89,14 +89,18 @@ Cpu_sampler::Cpu_session_component::trace_control()
|
||||
}
|
||||
|
||||
|
||||
Cpu_sampler::Cpu_session_component::Cpu_session_component(
|
||||
Rpc_entrypoint &thread_ep,
|
||||
Allocator &md_alloc,
|
||||
Thread_list &thread_list,
|
||||
Thread_list_change_handler &thread_list_change_handler,
|
||||
char const *args)
|
||||
Cpu_sampler::
|
||||
Cpu_session_component::
|
||||
Cpu_session_component(Rpc_entrypoint &thread_ep,
|
||||
Env &env,
|
||||
Allocator &md_alloc,
|
||||
Thread_list &thread_list,
|
||||
Thread_list_change_handler &thread_list_change_handler,
|
||||
char const *args)
|
||||
: _thread_ep(thread_ep),
|
||||
_parent_cpu_session(env()->parent()->session<Cpu_session>(args)),
|
||||
_env(env),
|
||||
_id_space_element(_parent_client, _env.id_space()),
|
||||
_parent_cpu_session(_env.session<Cpu_session>(_id_space_element.id(), args, Affinity())),
|
||||
_md_alloc(md_alloc),
|
||||
_thread_list(thread_list),
|
||||
_thread_list_change_handler(thread_list_change_handler),
|
||||
@ -105,6 +109,13 @@ Cpu_sampler::Cpu_session_component::Cpu_session_component(
|
||||
{ }
|
||||
|
||||
|
||||
void Cpu_sampler::Cpu_session_component::upgrade_ram_quota(size_t ram_quota)
|
||||
{
|
||||
String<64> const args("ram_quota=", ram_quota);
|
||||
_env.upgrade(_id_space_element.id(), args.string());
|
||||
}
|
||||
|
||||
|
||||
Cpu_sampler::Cpu_session_component::~Cpu_session_component()
|
||||
{
|
||||
_cleanup_native_cpu();
|
||||
|
@ -55,18 +55,18 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object<Cpu_session>
|
||||
{
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_thread_ep;
|
||||
|
||||
Cpu_session_client _parent_cpu_session;
|
||||
Allocator &_md_alloc;
|
||||
Thread_list &_thread_list;
|
||||
Thread_list_change_handler &_thread_list_change_handler;
|
||||
Session_label _session_label;
|
||||
unsigned int _next_thread_id = 0;
|
||||
|
||||
Capability<Cpu_session::Native_cpu> _native_cpu_cap;
|
||||
|
||||
Capability<Cpu_session::Native_cpu> _setup_native_cpu();
|
||||
Rpc_entrypoint &_thread_ep;
|
||||
Env &_env;
|
||||
Parent::Client _parent_client;
|
||||
Id_space<Parent::Client>::Element const _id_space_element;
|
||||
Cpu_session_client _parent_cpu_session;
|
||||
Allocator &_md_alloc;
|
||||
Thread_list &_thread_list;
|
||||
Thread_list_change_handler &_thread_list_change_handler;
|
||||
Session_label _session_label;
|
||||
unsigned int _next_thread_id = 0;
|
||||
Capability<Cpu_session::Native_cpu> _native_cpu_cap;
|
||||
Capability<Cpu_session::Native_cpu> _setup_native_cpu();
|
||||
void _cleanup_native_cpu();
|
||||
|
||||
public:
|
||||
@ -79,6 +79,7 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object<Cpu_session>
|
||||
* Constructor
|
||||
*/
|
||||
Cpu_session_component(Rpc_entrypoint &thread_ep,
|
||||
Env &env,
|
||||
Allocator &md_alloc,
|
||||
Thread_list &thread_list,
|
||||
Thread_list_change_handler &thread_list_change_handler,
|
||||
@ -89,6 +90,8 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object<Cpu_session>
|
||||
*/
|
||||
~Cpu_session_component();
|
||||
|
||||
void upgrade_ram_quota(size_t ram_quota);
|
||||
|
||||
|
||||
/***************************
|
||||
** CPU session interface **
|
||||
|
@ -172,7 +172,7 @@ struct Cpu_sampler::Main : Thread_list_change_handler
|
||||
Main(Genode::Env &env)
|
||||
: env(env),
|
||||
alloc(env.ram(), env.rm()),
|
||||
cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), alloc, thread_list, *this),
|
||||
cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), env, alloc, thread_list, *this),
|
||||
config(env, "config")
|
||||
{
|
||||
/*
|
||||
|
@ -80,7 +80,7 @@ compare_output_to {
|
||||
[init -> test-ldso] Catch exceptions in program
|
||||
[init -> test-ldso] ---------------------------
|
||||
[init -> test-ldso] exception in remote procedure call:
|
||||
[init -> test-ldso] Error: ROM-session creation failed (ram_quota=4K, label="unknown_file")
|
||||
[init -> test-ldso] Error: ROM-session creation failed (ram_quota=4096, label="unknown_file")
|
||||
[init -> test-ldso] Error: Could not open ROM session for "unknown_file"
|
||||
[init -> test-ldso] caught
|
||||
[init -> test-ldso] exception in program: caught
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <cap_session/connection.h>
|
||||
#include <base/log.h>
|
||||
#include <base/child.h>
|
||||
#include <os/session_requester.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
/* init includes */
|
||||
@ -33,6 +34,16 @@ namespace Init {
|
||||
class Name_registry;
|
||||
class Child_registry;
|
||||
class Child;
|
||||
|
||||
using Genode::log;
|
||||
using Genode::error;
|
||||
using Genode::warning;
|
||||
using Genode::Session_state;
|
||||
using Genode::Xml_generator;
|
||||
using Genode::Parent;
|
||||
using Genode::Id_space;
|
||||
|
||||
typedef Genode::Registered<Genode::Parent_service> Parent_service;
|
||||
}
|
||||
|
||||
|
||||
@ -47,8 +58,8 @@ namespace Init {
|
||||
static void warn_insuff_quota(Genode::size_t const avail)
|
||||
{
|
||||
if (!config_verbose) { return; }
|
||||
Genode::log("Warning: Specified quota exceeds available quota.");
|
||||
Genode::log(" Proceeding with a quota of ", avail, ".");
|
||||
log("Warning: Specified quota exceeds available quota.");
|
||||
log(" Proceeding with a quota of ", avail, ".");
|
||||
}
|
||||
|
||||
inline long read_priority(Genode::Xml_node start_node, long prio_levels)
|
||||
@ -68,7 +79,7 @@ namespace Init {
|
||||
|
||||
if (priority && (priority >= prio_levels)) {
|
||||
long new_prio = prio_levels ? prio_levels-1 : 0;
|
||||
char name[Genode::Service::MAX_NAME_LEN];
|
||||
char name[Genode::Service::Name::capacity()];
|
||||
start_node.attribute("name").value(name, sizeof(name));
|
||||
Genode::warning(Genode::Cstring(name), ": invalid priority, upgrading "
|
||||
"from ", -priority, " to ", -new_prio);
|
||||
@ -124,7 +135,6 @@ namespace Init {
|
||||
|
||||
/**
|
||||
* Return sub string of label with the leading child name stripped out
|
||||
*
|
||||
*/
|
||||
inline char const *skip_label_prefix(char const *child_name, char const *label)
|
||||
{
|
||||
@ -164,21 +174,20 @@ namespace Init {
|
||||
* \param child_name name of the originator of the session request
|
||||
* \param service_name name of the requested service
|
||||
*/
|
||||
inline bool service_node_matches(Genode::Xml_node service_node,
|
||||
char const *args,
|
||||
char const *child_name,
|
||||
char const *service_name)
|
||||
inline bool service_node_matches(Genode::Xml_node service_node, char const *args,
|
||||
Genode::Child_policy::Name const &child_name,
|
||||
Genode::Service::Name const &service_name)
|
||||
{
|
||||
bool const service_matches =
|
||||
service_node.has_type("any-service") ||
|
||||
(service_node.has_type("service") &&
|
||||
service_node.attribute("name").has_value(service_name));
|
||||
service_node.attribute("name").has_value(service_name.string()));
|
||||
|
||||
if (!service_matches)
|
||||
return false;
|
||||
|
||||
Genode::Session_label const session_label(skip_label_prefix(
|
||||
child_name, Genode::label_from_args(args).string()));
|
||||
child_name.string(), Genode::label_from_args(args).string()));
|
||||
|
||||
return !Genode::Xml_node_label_score(service_node, session_label).conflict();
|
||||
}
|
||||
@ -188,8 +197,8 @@ namespace Init {
|
||||
* Check if arguments satisfy the condition specified for the route
|
||||
*/
|
||||
inline bool service_node_args_condition_satisfied(Genode::Xml_node service_node,
|
||||
const char *args,
|
||||
char const *child_name)
|
||||
Genode::Session_state::Args const &args,
|
||||
Genode::Child_policy::Name const &child_name)
|
||||
{
|
||||
try {
|
||||
Genode::Xml_node if_arg = service_node.sub_node("if-arg");
|
||||
@ -200,7 +209,7 @@ namespace Init {
|
||||
if_arg.attribute("value").value(value, sizeof(value));
|
||||
|
||||
char arg_value[VALUE_MAX_LEN];
|
||||
Genode::Arg_string::find_arg(args, key).string(arg_value, sizeof(arg_value), "");
|
||||
Genode::Arg_string::find_arg(args.string(), key).string(arg_value, sizeof(arg_value), "");
|
||||
|
||||
/*
|
||||
* Skip child-name prefix if the key is the process "label".
|
||||
@ -214,7 +223,7 @@ namespace Init {
|
||||
* the prefix information is redundant.
|
||||
*/
|
||||
if (Genode::strcmp("label", key) == 0)
|
||||
return Genode::strcmp(value, skip_label_prefix(child_name, arg_value)) == 0;
|
||||
return Genode::strcmp(value, skip_label_prefix(child_name.string(), arg_value)) == 0;
|
||||
|
||||
return Genode::strcmp(value, arg_value) == 0;
|
||||
} catch (...) { }
|
||||
@ -222,109 +231,80 @@ namespace Init {
|
||||
/* if no if-arg node exists, the condition is met */
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if service name is ambiguous
|
||||
*
|
||||
* \return true if the same service is provided multiple
|
||||
* times
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
template <typename T>
|
||||
inline bool is_ambiguous(Genode::Registry<T> const &services,
|
||||
Service::Name const &name)
|
||||
{
|
||||
/* count number of services with the specified name */
|
||||
unsigned cnt = 0;
|
||||
services.for_each([&] (T const &service) {
|
||||
cnt += (service.name() == name); });
|
||||
|
||||
return cnt > 1;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline Genode::Service *find_service(Genode::Registry<T> &services,
|
||||
Genode::Service::Name const &name)
|
||||
{
|
||||
T *service = nullptr;
|
||||
services.for_each([&] (T &s) {
|
||||
if (!service && (s.name() == name))
|
||||
service = &s; });
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Init-specific representation of a child service
|
||||
*
|
||||
* For init, we introduce this 'Service' variant that distinguishes two
|
||||
* phases, declared and announced. A 'Routed_service' object is created
|
||||
* when a '<provides>' declaration is found in init's configuration.
|
||||
* At that time, however, no children including the server do yet exist.
|
||||
* If, at this stage, a client tries to open a session to this service,
|
||||
* the client get enqueued in a list of applicants and blocked. When
|
||||
* the server officially announces its service and passes over the root
|
||||
* capability, the 'Routed_service' enters the announced stage and any
|
||||
* applicants get unblocked.
|
||||
*/
|
||||
class Init::Routed_service : public Genode::Service
|
||||
class Init::Routed_service : public Genode::Child_service
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Genode::Child_policy::Name Child_name;
|
||||
|
||||
private:
|
||||
|
||||
Genode::Root_capability _root;
|
||||
bool _announced;
|
||||
Genode::Server *_server;
|
||||
Child_name _child_name;
|
||||
|
||||
struct Applicant : public Genode::Cancelable_lock,
|
||||
public Genode::List<Applicant>::Element
|
||||
{
|
||||
Applicant() : Cancelable_lock(Genode::Lock::LOCKED) { }
|
||||
};
|
||||
|
||||
Genode::Lock _applicants_lock;
|
||||
Genode::List<Applicant> _applicants;
|
||||
Genode::Registry<Routed_service>::Element _registry_element;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param name name of service
|
||||
* \param server server providing the service
|
||||
* \param services registry of all services provides by children
|
||||
* \param child_name child name of server, used for session routing
|
||||
*
|
||||
* The other arguments correspond to the arguments of 'Child_service'.
|
||||
*/
|
||||
Routed_service(const char *name,
|
||||
Genode::Server *server)
|
||||
: Service(name), _announced(false), _server(server) { }
|
||||
Routed_service(Genode::Registry<Routed_service> &services,
|
||||
Child_name const &child_name,
|
||||
Id_space<Parent::Server> &server_id_space,
|
||||
Session_state::Factory &factory,
|
||||
Service::Name const &name,
|
||||
Genode::Ram_session_capability ram,
|
||||
Child_service::Wakeup &wakeup)
|
||||
:
|
||||
Child_service(server_id_space, factory, name, ram, wakeup),
|
||||
_child_name(child_name), _registry_element(services, *this)
|
||||
{ }
|
||||
|
||||
Genode::Server *server() const { return _server; }
|
||||
|
||||
void announce(Genode::Root_capability root)
|
||||
{
|
||||
Genode::Lock::Guard guard(_applicants_lock);
|
||||
|
||||
_root = root;
|
||||
_announced = true;
|
||||
|
||||
/* wake up aspiring clients */
|
||||
for (Applicant *a; (a = _applicants.first()); ) {
|
||||
_applicants.remove(a);
|
||||
a->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
Genode::Session_capability session(const char *args,
|
||||
Genode::Affinity const &affinity)
|
||||
{
|
||||
/*
|
||||
* This method is called from the context of the client's
|
||||
* activation thread. If the service is not yet announced,
|
||||
* we let the client block.
|
||||
*/
|
||||
_applicants_lock.lock();
|
||||
if (!_announced) {
|
||||
Applicant myself;
|
||||
_applicants.insert(&myself);
|
||||
_applicants_lock.unlock();
|
||||
myself.lock();
|
||||
} else
|
||||
_applicants_lock.unlock();
|
||||
|
||||
Genode::Session_capability cap;
|
||||
try { cap = Genode::Root_client(_root).session(args, affinity); }
|
||||
catch (Genode::Root::Invalid_args) { throw Invalid_args(); }
|
||||
catch (Genode::Root::Unavailable) { throw Unavailable(); }
|
||||
catch (Genode::Root::Quota_exceeded) { throw Quota_exceeded(); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
|
||||
if (!cap.valid())
|
||||
throw Unavailable();
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
void upgrade(Genode::Session_capability sc, const char *args)
|
||||
{
|
||||
try { Genode::Root_client(_root).upgrade(sc, args); }
|
||||
catch (Genode::Root::Invalid_args) { throw Invalid_args(); }
|
||||
catch (Genode::Ipc_error) { throw Unavailable(); }
|
||||
}
|
||||
|
||||
void close(Genode::Session_capability sc)
|
||||
{
|
||||
try { Genode::Root_client(_root).close(sc); }
|
||||
catch (Genode::Ipc_error) { throw Genode::Blocking_canceled(); }
|
||||
}
|
||||
Child_name const &child_name() const { return _child_name; }
|
||||
};
|
||||
|
||||
|
||||
@ -335,6 +315,8 @@ struct Init::Name_registry
|
||||
{
|
||||
virtual ~Name_registry() { }
|
||||
|
||||
typedef Genode::Child_policy::Name Name;
|
||||
|
||||
/**
|
||||
* Check if specified name is unique
|
||||
*
|
||||
@ -343,13 +325,16 @@ struct Init::Name_registry
|
||||
virtual bool unique(const char *name) const = 0;
|
||||
|
||||
/**
|
||||
* Find server with specified name
|
||||
* Return child name for a given alias name
|
||||
*
|
||||
* If there is no alias, the function returns the original name.
|
||||
*/
|
||||
virtual Genode::Server *lookup_server(const char *name) const = 0;
|
||||
virtual Name deref_alias(Name const &) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Init::Child : Genode::Child_policy
|
||||
class Init::Child : Genode::Child_policy,
|
||||
Genode::Child_service::Wakeup
|
||||
{
|
||||
public:
|
||||
|
||||
@ -362,6 +347,8 @@ class Init::Child : Genode::Child_policy
|
||||
|
||||
friend class Child_registry;
|
||||
|
||||
Genode::Env &_env;
|
||||
|
||||
Genode::List_element<Child> _list_element;
|
||||
|
||||
Genode::Xml_node _start_node;
|
||||
@ -463,11 +450,6 @@ class Init::Child : Genode::Child_policy
|
||||
Genode::size_t ram_quota;
|
||||
Genode::size_t cpu_quota_pc;
|
||||
bool constrain_phys;
|
||||
Genode::Pd_connection pd;
|
||||
Genode::Ram_connection ram;
|
||||
Genode::Cpu_connection cpu;
|
||||
|
||||
inline void transfer_cpu_quota();
|
||||
|
||||
Resources(Genode::Xml_node start_node, const char *label,
|
||||
long prio_levels,
|
||||
@ -477,31 +459,13 @@ class Init::Child : Genode::Child_policy
|
||||
prio_levels_log2(Genode::log2(prio_levels)),
|
||||
priority(read_priority(start_node, prio_levels)),
|
||||
affinity(affinity_space,
|
||||
read_affinity_location(affinity_space, start_node)),
|
||||
pd(label),
|
||||
ram(label),
|
||||
cpu(label,
|
||||
priority*(Genode::Cpu_session::PRIORITY_LIMIT >> prio_levels_log2),
|
||||
affinity)
|
||||
read_affinity_location(affinity_space, start_node))
|
||||
{
|
||||
/* deduce session costs from usable ram quota */
|
||||
Genode::size_t session_donations = Genode::Pd_connection::RAM_QUOTA +
|
||||
Genode::Cpu_connection::RAM_QUOTA +
|
||||
Genode::Ram_connection::RAM_QUOTA;
|
||||
|
||||
if (ram_quota > session_donations)
|
||||
ram_quota -= session_donations;
|
||||
else ram_quota = 0;
|
||||
|
||||
ram.ref_account(Genode::env()->ram_session_cap());
|
||||
Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota);
|
||||
|
||||
transfer_cpu_quota();
|
||||
ram_quota = Genode::Child::effective_ram_quota(ram_quota);
|
||||
}
|
||||
} _resources;
|
||||
|
||||
Genode::Child::Initial_thread _initial_thread { _resources.cpu, _resources.pd,
|
||||
_name.unique };
|
||||
/*
|
||||
* Entry point used for serving the parent interface and the
|
||||
* locally provided ROM sessions for the 'config' and 'binary'
|
||||
@ -510,26 +474,21 @@ class Init::Child : Genode::Child_policy
|
||||
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
|
||||
Genode::Rpc_entrypoint _entrypoint;
|
||||
|
||||
/**
|
||||
* ELF binary
|
||||
*/
|
||||
Genode::Rom_connection _binary_rom;
|
||||
Genode::Dataspace_capability _binary_rom_ds;
|
||||
Genode::Parent_service _env_ram_service { _env, Genode::Ram_session::service_name() };
|
||||
Genode::Parent_service _env_cpu_service { _env, Genode::Cpu_session::service_name() };
|
||||
Genode::Parent_service _env_pd_service { _env, Genode:: Pd_session::service_name() };
|
||||
Genode::Parent_service _env_log_service { _env, Genode::Log_session::service_name() };
|
||||
Genode::Parent_service _env_rom_service { _env, Genode::Rom_session::service_name() };
|
||||
|
||||
Genode::Registry<Parent_service> &_parent_services;
|
||||
Genode::Registry<Routed_service> &_child_services;
|
||||
|
||||
/**
|
||||
* Private child configuration
|
||||
*/
|
||||
Init::Child_config _config;
|
||||
|
||||
/**
|
||||
* Each child of init can act as a server
|
||||
*/
|
||||
Genode::Server _server;
|
||||
|
||||
Genode::Region_map_client _address_space { _resources.pd.address_space() };
|
||||
Genode::Child _child;
|
||||
|
||||
Genode::Service_registry &_parent_services;
|
||||
Genode::Service_registry &_child_services;
|
||||
Genode::Session_requester _session_requester;
|
||||
|
||||
/**
|
||||
* Policy helpers
|
||||
@ -537,22 +496,39 @@ class Init::Child : Genode::Child_policy
|
||||
Init::Child_policy_enforce_labeling _labeling_policy;
|
||||
Init::Child_policy_handle_cpu_priorities _priority_policy;
|
||||
Init::Child_policy_provide_rom_file _config_policy;
|
||||
Init::Child_policy_provide_rom_file _binary_policy;
|
||||
Init::Child_policy_redirect_rom_file _configfile_policy;
|
||||
Init::Child_policy_ram_phys _ram_session_policy;
|
||||
|
||||
Genode::Child _child { _env.rm(), _entrypoint, *this };
|
||||
|
||||
/**
|
||||
* Child_service::Wakeup callback
|
||||
*/
|
||||
void wakeup_child_service() override
|
||||
{
|
||||
_session_requester.trigger_update();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Child(Genode::Xml_node start_node,
|
||||
Genode::Xml_node default_route_node,
|
||||
Name_registry &name_registry,
|
||||
long prio_levels,
|
||||
Genode::Affinity::Space const &affinity_space,
|
||||
Genode::Service_registry &parent_services,
|
||||
Genode::Service_registry &child_services,
|
||||
Genode::Cap_session &cap_session,
|
||||
Genode::Dataspace_capability ldso_ds)
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \throw Ram_session::Alloc_failed allocation of config buffer failed
|
||||
* \throw Region_map::Attach_failed failed to temporarily attach
|
||||
* config dataspace to local address
|
||||
* space
|
||||
*/
|
||||
Child(Genode::Env &env,
|
||||
Genode::Xml_node start_node,
|
||||
Genode::Xml_node default_route_node,
|
||||
Name_registry &name_registry,
|
||||
long prio_levels,
|
||||
Genode::Affinity::Space const &affinity_space,
|
||||
Genode::Registry<Parent_service> &parent_services,
|
||||
Genode::Registry<Routed_service> &child_services)
|
||||
:
|
||||
_env(env),
|
||||
_list_element(this),
|
||||
_start_node(start_node),
|
||||
_default_route_node(default_route_node),
|
||||
@ -560,23 +536,15 @@ class Init::Child : Genode::Child_policy
|
||||
_name(start_node, name_registry),
|
||||
_resources(start_node, _name.unique, prio_levels,
|
||||
affinity_space),
|
||||
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false,
|
||||
_entrypoint(&_env.pd(), ENTRYPOINT_STACK_SIZE, _name.unique, false,
|
||||
_resources.affinity.location()),
|
||||
_binary_rom(_name.file),
|
||||
_binary_rom_ds(_binary_rom.dataspace()),
|
||||
_config(_resources.ram.cap(), start_node),
|
||||
_server(_resources.ram.cap()),
|
||||
_child(_binary_rom_ds, ldso_ds,
|
||||
_resources.pd, _resources.pd,
|
||||
_resources.ram, _resources.ram,
|
||||
_resources.cpu, _initial_thread,
|
||||
*Genode::env()->rm_session(), _address_space, _entrypoint, *this),
|
||||
_parent_services(parent_services),
|
||||
_child_services(child_services),
|
||||
_config(_env.ram(), _env.rm(), start_node),
|
||||
_session_requester(_entrypoint, _env.ram(), _env.rm()),
|
||||
_labeling_policy(_name.unique),
|
||||
_priority_policy(_resources.prio_levels_log2, _resources.priority),
|
||||
_config_policy("config", _config.dataspace(), &_entrypoint),
|
||||
_binary_policy("binary", _binary_rom_ds, &_entrypoint),
|
||||
_configfile_policy("config", _config.filename()),
|
||||
_ram_session_policy(_resources.constrain_phys)
|
||||
{
|
||||
@ -587,10 +555,10 @@ class Init::Child : Genode::Child_policy
|
||||
"\"", Genode::Cstring(_name.unique), "\"");
|
||||
|
||||
if (config_verbose) {
|
||||
Genode::log("child \"", Genode::Cstring(_name.unique), "\"");
|
||||
Genode::log(" RAM quota: ", _resources.ram_quota);
|
||||
Genode::log(" ELF binary: ", Cstring(_name.file));
|
||||
Genode::log(" priority: ", _resources.priority);
|
||||
log("child \"", Genode::Cstring(_name.unique), "\"");
|
||||
log(" RAM quota: ", _resources.ram_quota);
|
||||
log(" ELF binary: ", Cstring(_name.file));
|
||||
log(" priority: ", _resources.priority);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -601,32 +569,33 @@ class Init::Child : Genode::Child_policy
|
||||
|
||||
for (; ; service_node = service_node.next("service")) {
|
||||
|
||||
char name[Genode::Service::MAX_NAME_LEN];
|
||||
char name[Genode::Service::Name::capacity()];
|
||||
service_node.attribute("name").value(name, sizeof(name));
|
||||
|
||||
if (config_verbose)
|
||||
Genode::log(" provides service ", Genode::Cstring(name));
|
||||
log(" provides service ", Genode::Cstring(name));
|
||||
|
||||
child_services.insert(new (_child.heap())
|
||||
Routed_service(name, &_server));
|
||||
new (_child.heap())
|
||||
Routed_service(child_services, this->name(),
|
||||
_session_requester.id_space(),
|
||||
_child.session_factory(),
|
||||
name, _child.ram_session_cap(), *this);
|
||||
|
||||
}
|
||||
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||
}
|
||||
|
||||
virtual ~Child() {
|
||||
Genode::Service *s;
|
||||
while ((s = _child_services.find_by_server(&_server))) {
|
||||
_child_services.remove(s);
|
||||
}
|
||||
virtual ~Child()
|
||||
{
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space()))
|
||||
destroy(_child.heap(), &service); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the child has the specified name
|
||||
*/
|
||||
bool has_name(const char *n) const { return !Genode::strcmp(name(), n); }
|
||||
|
||||
Genode::Server *server() { return &_server; }
|
||||
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
||||
|
||||
/**
|
||||
* Start execution of child
|
||||
@ -638,22 +607,70 @@ class Init::Child : Genode::Child_policy
|
||||
** Child-policy interface **
|
||||
****************************/
|
||||
|
||||
const char *name() const { return _name.unique; }
|
||||
Child_policy::Name name() const override { return _name.unique; }
|
||||
|
||||
Genode::Service *resolve_session_request(const char *service_name,
|
||||
const char *args)
|
||||
Binary_name binary_name() const override { return _name.file; }
|
||||
|
||||
Genode::Ram_session &ref_ram() override { return _env.ram(); }
|
||||
Genode::Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); }
|
||||
|
||||
void init(Genode::Ram_session &session, Genode::Ram_session_capability cap) override
|
||||
{
|
||||
Genode::Service *service = 0;
|
||||
using Genode::error;
|
||||
using Genode::warning;
|
||||
session.ref_account(Genode::env()->ram_session_cap());
|
||||
Genode::env()->ram_session()->transfer_quota(cap, _resources.ram_quota);
|
||||
}
|
||||
|
||||
/* check for config file request */
|
||||
if ((service = _config_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
void init(Genode::Cpu_session &session, Genode::Cpu_session_capability cap) override
|
||||
{
|
||||
using Genode::Cpu_session;
|
||||
using Genode::size_t;
|
||||
|
||||
/* check for binary file request */
|
||||
if ((service = _binary_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
|
||||
size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100);
|
||||
size_t need_adj = 0;
|
||||
if (need > avail || avail == 0) {
|
||||
warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100));
|
||||
need_adj = Cpu_session::quota_lim_upscale(100, 100);
|
||||
avail = 0;
|
||||
} else {
|
||||
need_adj = Cpu_session::quota_lim_upscale(need, avail);
|
||||
avail -= need;
|
||||
}
|
||||
session.ref_account(_env.cpu_session_cap());
|
||||
_env.cpu().transfer_quota(cap, need_adj);
|
||||
}
|
||||
|
||||
Genode::Id_space<Genode::Parent::Server> &server_id_space() override {
|
||||
return _session_requester.id_space(); }
|
||||
|
||||
Genode::Service &resolve_session_request(Genode::Service::Name const &service_name,
|
||||
Genode::Session_state::Args const &args) override
|
||||
{
|
||||
/* route environment session requests to the parent */
|
||||
Genode::Session_label const label(Genode::label_from_args(args.string()));
|
||||
if (label == name()) {
|
||||
if (service_name == Genode::Ram_session::service_name()) return _env_ram_service;
|
||||
if (service_name == Genode::Cpu_session::service_name()) return _env_cpu_service;
|
||||
if (service_name == Genode::Pd_session::service_name()) return _env_pd_service;
|
||||
if (service_name == Genode::Log_session::service_name()) return _env_log_service;
|
||||
}
|
||||
|
||||
/* route initial ROM requests (binary and linker) to the parent */
|
||||
if (service_name == Genode::Rom_session::service_name()) {
|
||||
if (label.last_element() == binary_name()) return _env_rom_service;
|
||||
if (label.last_element() == linker_name()) return _env_rom_service;
|
||||
}
|
||||
|
||||
Genode::Service *service = nullptr;
|
||||
|
||||
/* check for "config" ROM request */
|
||||
if ((service = _config_policy.resolve_session_request(service_name.string(), args.string())))
|
||||
return *service;
|
||||
|
||||
/* check for "session_requests" ROM request */
|
||||
if (service_name == Genode::Rom_session::service_name()
|
||||
&& label.last_element() == Genode::Session_requester::rom_name())
|
||||
return _session_requester.service();
|
||||
|
||||
try {
|
||||
Genode::Xml_node route_node = _default_route_node;
|
||||
@ -666,7 +683,7 @@ class Init::Child : Genode::Child_policy
|
||||
|
||||
bool service_wildcard = service_node.has_type("any-service");
|
||||
|
||||
if (!service_node_matches(service_node, args, name(), service_name))
|
||||
if (!service_node_matches(service_node, args.string(), name(), service_name))
|
||||
continue;
|
||||
|
||||
if (!service_node_args_condition_satisfied(service_node, args, name()))
|
||||
@ -676,54 +693,51 @@ class Init::Child : Genode::Child_policy
|
||||
for (; ; target = target.next()) {
|
||||
|
||||
if (target.has_type("parent")) {
|
||||
service = _parent_services.find(service_name);
|
||||
if (service)
|
||||
return service;
|
||||
|
||||
if ((service = find_service(_parent_services, service_name)))
|
||||
return *service;
|
||||
|
||||
if (!service_wildcard) {
|
||||
warning(name(), ": service lookup for "
|
||||
"\"", service_name, "\" at parent failed");
|
||||
return 0;
|
||||
throw Genode::Parent::Service_denied();
|
||||
}
|
||||
}
|
||||
|
||||
if (target.has_type("child")) {
|
||||
char server_name[Name::MAX_NAME_LEN];
|
||||
server_name[0] = 0;
|
||||
target.attribute("name").value(server_name, sizeof(server_name));
|
||||
|
||||
Genode::Server *server = _name_registry.lookup_server(server_name);
|
||||
if (!server) {
|
||||
warning(name(), ": invalid route to non-existing "
|
||||
"server \"", Genode::Cstring(server_name), "\"");
|
||||
return 0;
|
||||
}
|
||||
typedef Name_registry::Name Name;
|
||||
Name server_name = target.attribute_value("name", Name());
|
||||
server_name = _name_registry.deref_alias(server_name);
|
||||
|
||||
service = _child_services.find(service_name, server);
|
||||
_child_services.for_each([&] (Routed_service &s) {
|
||||
if (s.name() == Service::Name(service_name)
|
||||
&& s.child_name() == server_name)
|
||||
service = &s; });
|
||||
if (service)
|
||||
return service;
|
||||
return *service;
|
||||
|
||||
if (!service_wildcard) {
|
||||
Genode::warning(name(), ": lookup to child "
|
||||
"service \"", service_name, "\" failed");
|
||||
return 0;
|
||||
"server \"", server_name, "\" failed");
|
||||
throw Genode::Parent::Service_denied();
|
||||
}
|
||||
}
|
||||
|
||||
if (target.has_type("any-child")) {
|
||||
if (_child_services.is_ambiguous(service_name)) {
|
||||
if (is_ambiguous(_child_services, service_name)) {
|
||||
error(name(), ": ambiguous routes to "
|
||||
"service \"", service_name, "\"");
|
||||
return 0;
|
||||
throw Genode::Parent::Service_denied();
|
||||
}
|
||||
service = _child_services.find(service_name);
|
||||
if (service)
|
||||
return service;
|
||||
|
||||
if ((service = find_service(_child_services, service_name)))
|
||||
return *service;
|
||||
|
||||
if (!service_wildcard) {
|
||||
warning(name(), ": lookup for service "
|
||||
"\"", service_name, "\" failed");
|
||||
return 0;
|
||||
throw Genode::Parent::Service_denied();
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,19 +748,23 @@ class Init::Child : Genode::Child_policy
|
||||
} catch (...) {
|
||||
warning(name(), ": no route to service \"", service_name, "\"");
|
||||
}
|
||||
return service;
|
||||
|
||||
if (!service)
|
||||
throw Genode::Parent::Service_denied();
|
||||
|
||||
return *service;
|
||||
}
|
||||
|
||||
void filter_session_args(const char *service,
|
||||
char *args, Genode::size_t args_len)
|
||||
void filter_session_args(Service::Name const &service,
|
||||
char *args, Genode::size_t args_len) override
|
||||
{
|
||||
_labeling_policy. filter_session_args(service, args, args_len);
|
||||
_priority_policy. filter_session_args(service, args, args_len);
|
||||
_configfile_policy.filter_session_args(service, args, args_len);
|
||||
_ram_session_policy.filter_session_args(service, args, args_len);
|
||||
_labeling_policy. filter_session_args(service.string(), args, args_len);
|
||||
_priority_policy. filter_session_args(service.string(), args, args_len);
|
||||
_configfile_policy. filter_session_args(service.string(), args, args_len);
|
||||
_ram_session_policy.filter_session_args(service.string(), args, args_len);
|
||||
}
|
||||
|
||||
Genode::Affinity filter_session_affinity(Genode::Affinity const &session_affinity)
|
||||
Genode::Affinity filter_session_affinity(Genode::Affinity const &session_affinity) override
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
@ -772,28 +790,24 @@ class Init::Child : Genode::Child_policy
|
||||
return Affinity(space, location);
|
||||
}
|
||||
|
||||
bool announce_service(const char *service_name,
|
||||
Genode::Root_capability root,
|
||||
Genode::Allocator *alloc,
|
||||
Genode::Server *server)
|
||||
void announce_service(Genode::Service::Name const &service_name) override
|
||||
{
|
||||
if (config_verbose)
|
||||
Genode::log("child \"", name(), "\" announces service \"", service_name, "\"");
|
||||
log("child \"", name(), "\" announces service \"", service_name, "\"");
|
||||
|
||||
Genode::Service *s = _child_services.find(service_name, &_server);
|
||||
Routed_service *rs = dynamic_cast<Routed_service *>(s);
|
||||
if (!s || !rs) {
|
||||
Genode::error(name(), ": illegal announcement of service \"", service_name, "\"");
|
||||
return false;
|
||||
}
|
||||
bool found = false;
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space())
|
||||
&& service.name() == service_name)
|
||||
found = true; });
|
||||
|
||||
rs->announce(root);
|
||||
return true;
|
||||
if (!found)
|
||||
Genode::error(name(), ": illegal announcement of "
|
||||
"service \"", service_name, "\"");
|
||||
}
|
||||
|
||||
void resource_request(Genode::Parent::Resource_args const &args)
|
||||
void resource_request(Genode::Parent::Resource_args const &args) override
|
||||
{
|
||||
Genode::log("child \"", name(), "\" requests resources: ", args.string());
|
||||
log("child \"", name(), "\" requests resources: ", args.string());
|
||||
|
||||
Genode::size_t const requested_ram_quota =
|
||||
Genode::Arg_string::find_arg(args.string(), "ram_quota")
|
||||
@ -812,7 +826,7 @@ class Init::Child : Genode::Child_policy
|
||||
* when calling 'transfer_quota'.
|
||||
*/
|
||||
|
||||
Genode::env()->ram_session()->transfer_quota(_resources.ram.cap(),
|
||||
Genode::env()->ram_session()->transfer_quota(_child.ram_session_cap(),
|
||||
requested_ram_quota);
|
||||
|
||||
/* wake up child that was starved for resources */
|
||||
@ -838,23 +852,4 @@ class Init::Child : Genode::Child_policy
|
||||
};
|
||||
|
||||
|
||||
void Init::Child::Resources::transfer_cpu_quota()
|
||||
{
|
||||
using Genode::Cpu_session;
|
||||
using Genode::size_t;
|
||||
static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
|
||||
size_t const need = Cpu_session::quota_lim_upscale(cpu_quota_pc, 100);
|
||||
size_t need_adj;
|
||||
if (need > avail || avail == 0) {
|
||||
warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100));
|
||||
need_adj = Cpu_session::quota_lim_upscale(100, 100);
|
||||
avail = 0;
|
||||
} else {
|
||||
need_adj = Cpu_session::quota_lim_upscale(need, avail);
|
||||
avail -= need;
|
||||
}
|
||||
cpu.ref_account(Genode::env()->cpu_session_cap());
|
||||
Genode::env()->cpu_session()->transfer_quota(cpu.cap(), need_adj);
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__INIT__CHILD_H_ */
|
||||
|
@ -14,11 +14,9 @@
|
||||
#ifndef _INCLUDE__INIT__CHILD_CONFIG_H_
|
||||
#define _INCLUDE__INIT__CHILD_CONFIG_H_
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/printf.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <ram_session/client.h>
|
||||
#include <base/attached_dataspace.h>
|
||||
#include <ram_session/ram_session.h>
|
||||
|
||||
namespace Init { class Child_config; }
|
||||
|
||||
@ -27,11 +25,62 @@ class Init::Child_config
|
||||
{
|
||||
private:
|
||||
|
||||
enum { CONFIGFILE_NAME_LEN = 64 };
|
||||
char _filename[CONFIGFILE_NAME_LEN];
|
||||
Genode::Ram_session &_ram;
|
||||
|
||||
Genode::Ram_session_capability _ram_session_cap;
|
||||
Genode::Ram_dataspace_capability _config_ram_ds;
|
||||
typedef Genode::String<64> Rom_name;
|
||||
Rom_name const _rom_name;
|
||||
|
||||
Genode::Ram_dataspace_capability const _ram_ds;
|
||||
|
||||
Rom_name _rom_name_from_start_node(Genode::Xml_node start)
|
||||
{
|
||||
if (!start.has_sub_node("configfile"))
|
||||
return Rom_name();
|
||||
|
||||
return start.sub_node("configfile").attribute_value("name", Rom_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer '<config>' sub node in a dedicated RAM dataspace
|
||||
*
|
||||
* \throw Ram_session::Alloc_failed
|
||||
* \throw Rm_session::Attach_failed
|
||||
*/
|
||||
Genode::Ram_dataspace_capability
|
||||
_ram_ds_from_start_node(Genode::Xml_node start,
|
||||
Genode::Ram_session &ram, Genode::Region_map &rm)
|
||||
{
|
||||
/*
|
||||
* If the start node contains a 'config' entry, we copy this entry
|
||||
* into a fresh dataspace to be provided to our child.
|
||||
*/
|
||||
Genode::Xml_node const config = start.has_sub_node("config")
|
||||
? start.sub_node("config")
|
||||
: Genode::Xml_node("<config/>");
|
||||
|
||||
Genode::Ram_dataspace_capability ram_ds;
|
||||
try {
|
||||
/*
|
||||
* Allocate RAM dataspace that is big enough to hold the
|
||||
* configuration and the null termination.
|
||||
*/
|
||||
ram_ds = ram.alloc(config.size() + 1);
|
||||
|
||||
/*
|
||||
* Make dataspace locally accessible, copy configuration into the
|
||||
* dataspace, and append a string-terminating zero.
|
||||
*/
|
||||
Genode::Attached_dataspace attached(rm, ram_ds);
|
||||
|
||||
Genode::memcpy(attached.local_addr<char>(),
|
||||
config.addr(), config.size());
|
||||
|
||||
attached.local_addr<char>()[config.size()] = 0;
|
||||
|
||||
return ram_ds;
|
||||
}
|
||||
catch (Genode::Region_map::Attach_failed) { ram.free(ram_ds); throw; }
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@ -40,84 +89,31 @@ class Init::Child_config
|
||||
*
|
||||
* The provided RAM session is used to obtain a dataspace for
|
||||
* holding the copy of the child's configuration data unless the
|
||||
* configuration is supplied via a config file. Normally, the
|
||||
* child's RAM session should be used to account the consumed RAM
|
||||
* quota to the child.
|
||||
* configuration is supplied via a config ROM module.
|
||||
*
|
||||
* \throw Ram_session::Alloc_failed failed to allocate the backing
|
||||
* store for holding config data
|
||||
*
|
||||
* \throw Region_map::Attach_failed failed to temporarily attach the
|
||||
* config dataspace to the local
|
||||
* address space
|
||||
*
|
||||
* If the start node contains a 'filename' entry, we only keep the
|
||||
* information about the ROM module name.
|
||||
*/
|
||||
Child_config(Genode::Ram_session_capability ram_session,
|
||||
Genode::Xml_node start_node)
|
||||
: _ram_session_cap(ram_session)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/*
|
||||
* If the start node contains a 'filename' entry, we only keep
|
||||
* the information about the file name.
|
||||
*/
|
||||
_filename[0] = 0;
|
||||
try {
|
||||
Xml_node configfile_node = start_node.sub_node("configfile");
|
||||
configfile_node.attribute("name")
|
||||
.value(_filename, sizeof(_filename));
|
||||
|
||||
return;
|
||||
} catch (...) { }
|
||||
|
||||
/*
|
||||
* If the start node contains a 'config' entry, we copy this
|
||||
* entry into a fresh dataspace to be provided to our child.
|
||||
*/
|
||||
Ram_session_client rsc(_ram_session_cap);
|
||||
try {
|
||||
Xml_node config_node = start_node.sub_node("config");
|
||||
|
||||
const char *config = config_node.addr();
|
||||
Genode::size_t config_size = config_node.size();
|
||||
|
||||
if (!config || !config_size) return;
|
||||
|
||||
/*
|
||||
* Allocate RAM dataspace that is big enough to
|
||||
* hold the configuration and the null termination.
|
||||
*/
|
||||
_config_ram_ds = rsc.alloc(config_size + 1);
|
||||
|
||||
/*
|
||||
* Make dataspace locally accessible, copy
|
||||
* configuration into the dataspace, and append
|
||||
* a string-terminating zero.
|
||||
*/
|
||||
void *addr = env()->rm_session()->attach(_config_ram_ds);
|
||||
|
||||
Genode::memcpy(addr, config, config_size);
|
||||
static_cast<char *>(addr)[config_size] = 0;
|
||||
env()->rm_session()->detach(addr);
|
||||
|
||||
} catch (Region_map::Attach_failed) {
|
||||
rsc.free(_config_ram_ds);
|
||||
return;
|
||||
} catch (Ram_session::Alloc_failed) {
|
||||
return;
|
||||
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||
}
|
||||
Child_config(Genode::Ram_session &ram, Genode::Region_map &local_rm,
|
||||
Genode::Xml_node start)
|
||||
:
|
||||
_ram(ram),
|
||||
_rom_name(_rom_name_from_start_node(start)),
|
||||
_ram_ds(_rom_name.valid() ? Genode::Ram_dataspace_capability()
|
||||
: _ram_ds_from_start_node(start, ram, local_rm))
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Child_config()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/*
|
||||
* The configuration data is either provided as a ROM session
|
||||
* (holding a complete configfile) or as a RAM dataspace
|
||||
* holding a copy of the start node's config entry. In the
|
||||
* latter case, the child's configuration resides in a
|
||||
* shadow copy kept in '_config_ram_ds'.
|
||||
*/
|
||||
if (_config_ram_ds.valid())
|
||||
Ram_session_client(_ram_session_cap).free(_config_ram_ds);
|
||||
}
|
||||
~Child_config() { if (_ram_ds.valid()) _ram.free(_ram_ds); }
|
||||
|
||||
/**
|
||||
* Return file name if configuration comes from a file
|
||||
@ -125,7 +121,7 @@ class Init::Child_config
|
||||
* If the configuration is provided inline, the method returns 0.
|
||||
*/
|
||||
char const *filename() const {
|
||||
return _filename[0] != 0 ? _filename : 0; }
|
||||
return _rom_name.valid() ? _rom_name.string() : nullptr; }
|
||||
|
||||
/**
|
||||
* Request dataspace holding the start node's configuration data
|
||||
@ -134,7 +130,7 @@ class Init::Child_config
|
||||
* inline configuration (if 'filename()' returns 0).
|
||||
*/
|
||||
Genode::Dataspace_capability dataspace() {
|
||||
return Genode::Dataspace_capability(_config_ram_ds); }
|
||||
return Genode::Dataspace_capability(_ram_ds); }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__INIT__CHILD_CONFIG_H_ */
|
||||
|
@ -19,9 +19,11 @@
|
||||
#include <base/child.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/session_label.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <base/session_label.h>
|
||||
#include <os/dynamic_rom_session.h>
|
||||
|
||||
namespace Init {
|
||||
|
||||
@ -29,8 +31,17 @@ namespace Init {
|
||||
class Child_policy_enforce_labeling;
|
||||
class Child_policy_handle_cpu_priorities;
|
||||
class Child_policy_provide_rom_file;
|
||||
class Child_policy_provide_dynamic_rom;
|
||||
class Child_policy_redirect_rom_file;
|
||||
class Traditional_child_policy;
|
||||
|
||||
using Genode::Attached_ram_dataspace;
|
||||
using Genode::size_t;
|
||||
using Genode::Session_label;
|
||||
using Genode::error;
|
||||
using Genode::log;
|
||||
using Genode::Service;
|
||||
using Genode::Session_state;
|
||||
}
|
||||
|
||||
|
||||
@ -154,80 +165,49 @@ class Init::Child_policy_provide_rom_file
|
||||
|
||||
struct Local_rom_session_component : Genode::Rpc_object<Genode::Rom_session>
|
||||
{
|
||||
Genode::Rpc_entrypoint &ep;
|
||||
Genode::Dataspace_capability ds_cap;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Local_rom_session_component(Genode::Dataspace_capability ds)
|
||||
: ds_cap(ds) { }
|
||||
Local_rom_session_component(Genode::Rpc_entrypoint &ep,
|
||||
Genode::Dataspace_capability ds)
|
||||
: ep(ep), ds_cap(ds) { ep.manage(this); }
|
||||
|
||||
~Local_rom_session_component() { ep.dissolve(this); }
|
||||
|
||||
|
||||
/***************************
|
||||
** ROM session interface **
|
||||
***************************/
|
||||
|
||||
Genode::Rom_dataspace_capability dataspace() {
|
||||
Genode::Rom_dataspace_capability dataspace() override {
|
||||
return Genode::static_cap_cast<Genode::Rom_dataspace>(ds_cap); }
|
||||
|
||||
void sigh(Genode::Signal_context_capability) { }
|
||||
void sigh(Genode::Signal_context_capability) override { }
|
||||
|
||||
} _local_rom_session;
|
||||
} _session;
|
||||
|
||||
Genode::Rpc_entrypoint *_ep;
|
||||
Genode::Rom_session_capability _rom_session_cap;
|
||||
Genode::Session_label const _module_name;
|
||||
|
||||
Genode::Session_label _module_name;
|
||||
typedef Genode::Local_service<Local_rom_session_component> Service;
|
||||
|
||||
struct Local_rom_service : public Genode::Service
|
||||
{
|
||||
Genode::Rom_session_capability _rom_cap;
|
||||
bool _valid;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param rom_cap capability to return on session requests
|
||||
* \param valid true if local rom service is backed by a
|
||||
* valid dataspace
|
||||
*/
|
||||
Local_rom_service(Genode::Rom_session_capability rom_cap, bool valid)
|
||||
: Genode::Service("ROM"), _rom_cap(rom_cap), _valid(valid) { }
|
||||
|
||||
Genode::Session_capability session(char const * /*args*/,
|
||||
Genode::Affinity const &)
|
||||
{
|
||||
if (!_valid)
|
||||
throw Invalid_args();
|
||||
|
||||
return _rom_cap;
|
||||
}
|
||||
|
||||
void upgrade(Genode::Session_capability, const char * /*args*/) { }
|
||||
void close(Genode::Session_capability) { }
|
||||
|
||||
} _local_rom_service;
|
||||
Service::Single_session_factory _session_factory { _session };
|
||||
Service _service { _session_factory };
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Child_policy_provide_rom_file(const char *module_name,
|
||||
Child_policy_provide_rom_file(Genode::Session_label const &module_name,
|
||||
Genode::Dataspace_capability ds_cap,
|
||||
Genode::Rpc_entrypoint *ep)
|
||||
:
|
||||
_local_rom_session(ds_cap), _ep(ep),
|
||||
_rom_session_cap(_ep->manage(&_local_rom_session)),
|
||||
_module_name(module_name),
|
||||
_local_rom_service(_rom_session_cap, ds_cap.valid())
|
||||
_session(*ep, ds_cap), _module_name(module_name)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Child_policy_provide_rom_file() { _ep->dissolve(&_local_rom_session); }
|
||||
|
||||
Genode::Service *resolve_session_request(const char *service_name,
|
||||
const char *args)
|
||||
{
|
||||
@ -238,7 +218,7 @@ class Init::Child_policy_provide_rom_file
|
||||
{
|
||||
Genode::Session_label const label = Genode::label_from_args(args);
|
||||
return label.last_element() == _module_name
|
||||
? &_local_rom_service : nullptr;
|
||||
? &_service : nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,6 +2,9 @@
|
||||
* \brief Child policy helper for supplying dynamic ROM modules
|
||||
* \author Norman Feske
|
||||
* \date 2012-04-04
|
||||
*
|
||||
* \deprecated use 'Local_service::Single_session_service' combined with
|
||||
* 'Dynamic_rom_session' instead
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -73,7 +76,7 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object<Rom_session>,
|
||||
Rpc_entrypoint &ep,
|
||||
Ram_session *ram)
|
||||
:
|
||||
Service("ROM"),
|
||||
Service("ROM", Ram_session_capability()),
|
||||
_ram(ram),
|
||||
_fg(0, 0), _bg(0, 0),
|
||||
_bg_has_pending_data(false),
|
||||
@ -147,11 +150,31 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object<Rom_session>,
|
||||
** Service interface **
|
||||
***********************/
|
||||
|
||||
Session_capability session(const char *, Affinity const &) {
|
||||
return _rom_session_cap; }
|
||||
void initiate_request(Session_state &session) override
|
||||
{
|
||||
switch (session.phase) {
|
||||
|
||||
void upgrade(Session_capability, const char *) { }
|
||||
void close(Session_capability) { }
|
||||
case Session_state::CREATE_REQUESTED:
|
||||
session.cap = _rom_session_cap;
|
||||
session.phase = Session_state::AVAILABLE;
|
||||
break;
|
||||
|
||||
case Session_state::UPGRADE_REQUESTED:
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
session.confirm_ram_upgrade();
|
||||
break;
|
||||
|
||||
case Session_state::CLOSE_REQUESTED:
|
||||
session.phase = Session_state::CLOSED;
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
|
@ -26,7 +26,7 @@ namespace Genode {
|
||||
/**
|
||||
* Return singleton instance of config
|
||||
*/
|
||||
Config *config();
|
||||
Volatile_object<Config> &config();
|
||||
}
|
||||
|
||||
|
||||
|
195
repos/os/include/os/dynamic_rom_session.h
Normal file
195
repos/os/include/os/dynamic_rom_session.h
Normal 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_ */
|
||||
|
84
repos/os/include/os/session_requester.h
Normal file
84
repos/os/include/os/session_requester.h
Normal 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_ */
|
@ -17,31 +17,41 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/local_connection.h>
|
||||
#include <base/child.h>
|
||||
#include <init/child_policy.h>
|
||||
#include <ram_session/connection.h>
|
||||
#include <cpu_session/connection.h>
|
||||
#include <rm_session/connection.h>
|
||||
#include <pd_session/connection.h>
|
||||
#include <os/child_policy_dynamic_rom.h>
|
||||
#include <os/session_requester.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Slave_policy;
|
||||
class Slave;
|
||||
}
|
||||
namespace Genode { struct Slave; }
|
||||
|
||||
|
||||
/**
|
||||
* Slave-policy class
|
||||
*
|
||||
* This class provides a convenience policy for single-service slaves using a
|
||||
* white list of parent services.
|
||||
*/
|
||||
class Genode::Slave_policy : public Genode::Child_policy
|
||||
struct Genode::Slave
|
||||
{
|
||||
typedef Session_state::Args Args;
|
||||
|
||||
class Policy;
|
||||
|
||||
template <typename>
|
||||
class Connection_base;
|
||||
|
||||
template <typename>
|
||||
struct Connection;
|
||||
};
|
||||
|
||||
|
||||
class Genode::Slave::Policy : public Child_policy
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Child_policy::Name Name;
|
||||
typedef Session_label Label;
|
||||
|
||||
protected:
|
||||
|
||||
typedef Registered<Genode::Parent_service> Parent_service;
|
||||
typedef Registry<Parent_service> Parent_services;
|
||||
|
||||
/**
|
||||
* Return white list of services the slave is permitted to use
|
||||
*
|
||||
@ -51,58 +61,66 @@ class Genode::Slave_policy : public Genode::Child_policy
|
||||
|
||||
private:
|
||||
|
||||
char const *_label;
|
||||
Genode::Service_registry _parent_services;
|
||||
Genode::Rpc_entrypoint &_entrypoint;
|
||||
Genode::Rom_connection _binary_rom;
|
||||
Label const _label;
|
||||
Binary_name const _binary_name;
|
||||
Ram_session_client _ram;
|
||||
Genode::Parent_service _binary_service;
|
||||
size_t _ram_quota;
|
||||
Parent_services _parent_services;
|
||||
Rpc_entrypoint &_ep;
|
||||
Init::Child_policy_enforce_labeling _labeling_policy;
|
||||
Init::Child_policy_provide_rom_file _binary_policy;
|
||||
Genode::Child_policy_dynamic_rom_file _config_policy;
|
||||
Child_policy_dynamic_rom_file _config_policy;
|
||||
|
||||
bool _service_permitted(const char *service_name)
|
||||
bool _service_permitted(Service::Name const &service_name) const
|
||||
{
|
||||
for (const char **s = _permitted_services(); *s; ++s)
|
||||
if (!Genode::strcmp(service_name, *s))
|
||||
if (service_name == *s)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Session_requester _session_requester;
|
||||
|
||||
public:
|
||||
|
||||
class Connection;
|
||||
|
||||
/**
|
||||
* Slave-policy constructor
|
||||
*
|
||||
* \param label name of the program to start
|
||||
* \param entrypoint entrypoint used to provide local services
|
||||
* such as the config ROM service
|
||||
* \param ram RAM session used for buffering config data
|
||||
* \param ep entrypoint used to provide local services
|
||||
* such as the config ROM service
|
||||
* \param local_rm local address space, needed to populate dataspaces
|
||||
* provided to the child (config, session_requests)
|
||||
*
|
||||
* If 'ram' is set to 0, no configuration can be supplied to the
|
||||
* slave.
|
||||
* \throw Ram_session::Alloc_failed by 'Child_policy_dynamic_rom_file'
|
||||
* \throw Rm_session::Attach_failed by 'Child_policy_dynamic_rom_file'
|
||||
*/
|
||||
Slave_policy(const char *label,
|
||||
Genode::Rpc_entrypoint &entrypoint,
|
||||
Genode::Ram_session *ram = 0,
|
||||
const char *binary = nullptr)
|
||||
Policy(Label const &label,
|
||||
Name const &binary_name,
|
||||
Rpc_entrypoint &ep,
|
||||
Region_map &rm,
|
||||
Ram_session_capability ram_cap,
|
||||
size_t ram_quota)
|
||||
:
|
||||
_label(label),
|
||||
_entrypoint(entrypoint),
|
||||
_binary_rom(binary ? prefixed_label(Session_label(label),
|
||||
Session_label(binary)).string() : label),
|
||||
_labeling_policy(_label),
|
||||
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
|
||||
_config_policy("config", _entrypoint, ram)
|
||||
{ }
|
||||
|
||||
Genode::Dataspace_capability binary() { return _binary_rom.dataspace(); }
|
||||
_label(label), _binary_name(binary_name), _ram(ram_cap),
|
||||
_binary_service(Rom_session::service_name()),
|
||||
_ram_quota(ram_quota), _ep(ep), _labeling_policy(_label.string()),
|
||||
_config_policy("config", _ep, &_ram),
|
||||
_session_requester(ep, _ram, rm)
|
||||
{
|
||||
configure("<config/>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign new configuration to slave
|
||||
*
|
||||
* \param config new configuration as null-terminated string
|
||||
*/
|
||||
void configure(char const *config)
|
||||
{
|
||||
_config_policy.load(config, Genode::strlen(config) + 1);
|
||||
_config_policy.load(config, strlen(config) + 1);
|
||||
}
|
||||
|
||||
void configure(char const *config, size_t len)
|
||||
@ -110,106 +128,187 @@ class Genode::Slave_policy : public Genode::Child_policy
|
||||
_config_policy.load(config, len);
|
||||
}
|
||||
|
||||
void trigger_session_requests()
|
||||
{
|
||||
_session_requester.trigger_update();
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
** Child_policy interface **
|
||||
****************************/
|
||||
|
||||
const char *name() const { return _label; }
|
||||
Name name() const override { return _label; }
|
||||
|
||||
Genode::Service *resolve_session_request(const char *service_name,
|
||||
const char *args)
|
||||
Binary_name binary_name() const override { return _binary_name; }
|
||||
|
||||
Ram_session &ref_ram() override { return _ram; }
|
||||
Ram_session_capability ref_ram_cap() const override { return _ram; }
|
||||
|
||||
void init(Ram_session &session, Ram_session_capability cap) override
|
||||
{
|
||||
Genode::Service *service = 0;
|
||||
|
||||
/* check for binary file request */
|
||||
if ((service = _binary_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
session.ref_account(_ram);
|
||||
_ram.transfer_quota(cap, _ram_quota);
|
||||
}
|
||||
|
||||
Service &resolve_session_request(Service::Name const &service_name,
|
||||
Session_state::Args const &args)
|
||||
{
|
||||
/* check for config file request */
|
||||
if ((service = _config_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
if (Service *s = _config_policy.resolve_session_request(service_name.string(), args.string()))
|
||||
return *s;
|
||||
|
||||
if (service_name == "ROM") {
|
||||
Session_label const rom_name(label_from_args(args.string()).last_element());
|
||||
if (rom_name == _binary_name) return _binary_service;
|
||||
if (rom_name == "session_requests") return _session_requester.service();
|
||||
}
|
||||
|
||||
if (!_service_permitted(service_name)) {
|
||||
error(name(), ": illegal session request of service \"", service_name, "\"");
|
||||
return 0;
|
||||
error(name(), ": illegal session request of "
|
||||
"service \"", service_name, "\" (", args, ")");
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
||||
/* fill parent service registry on demand */
|
||||
if (!(service = _parent_services.find(service_name))) {
|
||||
service = new (Genode::env()->heap())
|
||||
Genode::Parent_service(service_name);
|
||||
_parent_services.insert(service);
|
||||
}
|
||||
Parent_service *service = nullptr;
|
||||
_parent_services.for_each([&] (Parent_service &s) {
|
||||
if (!service && s.name() == service_name)
|
||||
service = &s; });
|
||||
|
||||
/* return parent service */
|
||||
return service;
|
||||
if (!service)
|
||||
service = new (env()->heap())
|
||||
Parent_service(_parent_services, service_name);
|
||||
|
||||
return *service;
|
||||
}
|
||||
|
||||
void filter_session_args(const char *service,
|
||||
char *args, Genode::size_t args_len)
|
||||
Id_space<Parent::Server> &server_id_space() override {
|
||||
return _session_requester.id_space(); }
|
||||
|
||||
void filter_session_args(Service::Name const &service,
|
||||
char *args, size_t args_len)
|
||||
{
|
||||
_labeling_policy.filter_session_args(service, args, args_len);
|
||||
_labeling_policy.filter_session_args(service.string(), args, args_len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Genode::Slave
|
||||
template <typename CONNECTION>
|
||||
class Genode::Slave::Connection_base
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
|
||||
struct Resources
|
||||
/* each connection appears as a separate client */
|
||||
Id_space<Parent::Client> _id_space;
|
||||
|
||||
Policy &_policy;
|
||||
|
||||
struct Service : Genode::Service, Session_state::Ready_callback,
|
||||
Session_state::Closed_callback
|
||||
{
|
||||
Genode::Pd_connection pd;
|
||||
Genode::Ram_connection ram;
|
||||
Genode::Cpu_connection cpu;
|
||||
Id_space<Parent::Server> &_id_space;
|
||||
|
||||
class Quota_exceeded : public Genode::Exception { };
|
||||
Lock _lock { Lock::LOCKED };
|
||||
bool _alive = false;
|
||||
|
||||
Resources(const char *label, Genode::size_t ram_quota,
|
||||
Ram_session_capability ram_ref_cap)
|
||||
: pd(label), ram(label), cpu(label)
|
||||
Service(Policy &policy)
|
||||
:
|
||||
Genode::Service(CONNECTION::service_name(), policy.ref_ram_cap()),
|
||||
_id_space(policy.server_id_space())
|
||||
{ }
|
||||
|
||||
void initiate_request(Session_state &session) override
|
||||
{
|
||||
ram.ref_account(ram_ref_cap);
|
||||
Ram_session_client ram_ref(ram_ref_cap);
|
||||
switch (session.phase) {
|
||||
|
||||
if (ram_ref.transfer_quota(ram.cap(), ram_quota))
|
||||
throw Quota_exceeded();
|
||||
case Session_state::CREATE_REQUESTED:
|
||||
|
||||
if (!session.id_at_server.constructed())
|
||||
session.id_at_server.construct(session, _id_space);
|
||||
|
||||
session.ready_callback = this;
|
||||
session.async_client_notify = true;
|
||||
break;
|
||||
|
||||
case Session_state::UPGRADE_REQUESTED:
|
||||
warning("upgrading slaves is not implemented");
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
break;
|
||||
|
||||
case Session_state::CLOSE_REQUESTED:
|
||||
warning("closing slave connections is not implemented");
|
||||
session.phase = Session_state::CLOSED;
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} _resources;
|
||||
|
||||
Genode::Child::Initial_thread _initial_thread;
|
||||
void wakeup() override { }
|
||||
|
||||
Genode::Region_map_client _address_space { _resources.pd.address_space() };
|
||||
Genode::Child _child;
|
||||
/**
|
||||
* Session_state::Ready_callback
|
||||
*/
|
||||
void session_ready(Session_state &session) override
|
||||
{
|
||||
_alive = session.alive();
|
||||
_lock.unlock();
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Session_state::Closed_callback
|
||||
*/
|
||||
void session_closed(Session_state &s) override { _lock.unlock(); }
|
||||
|
||||
Slave(Genode::Rpc_entrypoint &entrypoint,
|
||||
Slave_policy &slave_policy,
|
||||
Genode::size_t ram_quota,
|
||||
Ram_session_capability ram_ref_cap = env()->ram_session_cap(),
|
||||
Dataspace_capability ldso_ds = Dataspace_capability())
|
||||
} _service;
|
||||
|
||||
Local_connection<CONNECTION> _connection;
|
||||
|
||||
Connection_base(Policy &policy, Args const &args, Affinity const &affinity)
|
||||
:
|
||||
_resources(slave_policy.name(), ram_quota, ram_ref_cap),
|
||||
_initial_thread(_resources.cpu, _resources.pd, slave_policy.name()),
|
||||
_child(slave_policy.binary(), ldso_ds, _resources.pd, _resources.pd,
|
||||
_resources.ram, _resources.ram, _resources.cpu, _initial_thread,
|
||||
*env()->rm_session(), _address_space, entrypoint, slave_policy)
|
||||
{ }
|
||||
_policy(policy), _service(_policy),
|
||||
_connection(_service, _id_space, { 1 }, args, affinity)
|
||||
{
|
||||
_policy.trigger_session_requests();
|
||||
_service._lock.lock();
|
||||
if (!_service._alive)
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
||||
Genode::Ram_connection &ram() { return _resources.ram; }
|
||||
~Connection_base()
|
||||
{
|
||||
_policy.trigger_session_requests();
|
||||
_service._lock.lock();
|
||||
}
|
||||
|
||||
typedef typename CONNECTION::Session_type SESSION;
|
||||
|
||||
Capability<SESSION> _cap() const { return _connection.cap(); }
|
||||
};
|
||||
|
||||
|
||||
/***************************************
|
||||
** Wrappers of the 'Child' interface **
|
||||
***************************************/
|
||||
|
||||
void yield(Genode::Parent::Resource_args const &args) {
|
||||
_child.yield(args); }
|
||||
|
||||
void notify_resource_avail() const {
|
||||
_child.notify_resource_avail(); }
|
||||
template <typename CONNECTION>
|
||||
struct Genode::Slave::Connection : private Connection_base<CONNECTION>,
|
||||
public CONNECTION::Client
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \throw Parent::Service_denied parent denies session request
|
||||
* \throw Parent::Quota_exceeded our own quota does not suffice for
|
||||
* the creation of the new session
|
||||
*/
|
||||
Connection(Slave::Policy &policy, Args const &args,
|
||||
Affinity const &affinity = Affinity())
|
||||
:
|
||||
Connection_base<CONNECTION>(policy, args, affinity),
|
||||
CONNECTION::Client(Connection_base<CONNECTION>::_cap())
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__OS__SLAVE_H_ */
|
||||
|
@ -1,16 +1,17 @@
|
||||
/*
|
||||
* \brief Init process
|
||||
* \brief Init component
|
||||
* \author Norman Feske
|
||||
* \date 2010-04-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
* Copyright (C) 2010-2016 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#include <base/component.h>
|
||||
#include <init/child.h>
|
||||
#include <base/sleep.h>
|
||||
#include <os/config.h>
|
||||
@ -65,7 +66,7 @@ inline Genode::Affinity::Space read_affinity_space()
|
||||
/**
|
||||
* Read parent-provided services from config
|
||||
*/
|
||||
inline void determine_parent_services(Genode::Service_registry *services)
|
||||
inline void determine_parent_services(Genode::Registry<Init::Parent_service> *services)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
@ -75,11 +76,10 @@ inline void determine_parent_services(Genode::Service_registry *services)
|
||||
Xml_node node = config()->xml_node().sub_node("parent-provides").sub_node("service");
|
||||
for (; ; node = node.next("service")) {
|
||||
|
||||
char service_name[Genode::Service::MAX_NAME_LEN];
|
||||
char service_name[Genode::Service::Name::capacity()];
|
||||
node.attribute("name").value(service_name, sizeof(service_name));
|
||||
|
||||
Parent_service *s = new (env()->heap()) Parent_service(service_name);
|
||||
services->insert(s);
|
||||
new (env()->heap()) Init::Parent_service(*services, service_name);
|
||||
if (Init::config_verbose)
|
||||
log(" service \"", Cstring(service_name), "\"");
|
||||
|
||||
@ -231,13 +231,6 @@ class Init::Child_registry : public Name_registry, Child_list
|
||||
return _aliases.first() ? _aliases.first() : 0;
|
||||
}
|
||||
|
||||
void revoke_server(Genode::Server const *server)
|
||||
{
|
||||
Genode::List_element<Child> *curr = first();
|
||||
for (; curr; curr = curr->next())
|
||||
curr->object()->_child.revoke_server(server);
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
** Name-registry interface **
|
||||
@ -260,41 +253,24 @@ class Init::Child_registry : public Name_registry, Child_list
|
||||
return true;
|
||||
}
|
||||
|
||||
Genode::Server *lookup_server(const char *name) const
|
||||
Name deref_alias(Name const &name) override
|
||||
{
|
||||
/*
|
||||
* Check if an alias with the specified name exists. If so,
|
||||
* look up the server referred to by the alias.
|
||||
*/
|
||||
for (Alias const *a = _aliases.first(); a; a = a->next())
|
||||
if (Alias::Name(name) == a->name)
|
||||
name = a->child.string();
|
||||
if (name == a->name)
|
||||
return a->child;
|
||||
|
||||
/* look up child with the name */
|
||||
Genode::List_element<Child> const *curr = first();
|
||||
for (; curr; curr = curr->next())
|
||||
if (curr->object()->has_name(name))
|
||||
return curr->object()->server();
|
||||
|
||||
return 0;
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int, char **)
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
using namespace Init;
|
||||
using namespace Genode;
|
||||
|
||||
/* obtain dynamic linker */
|
||||
Dataspace_capability ldso_ds;
|
||||
try {
|
||||
static Rom_connection rom("ld.lib.so");
|
||||
ldso_ds = rom.dataspace();
|
||||
} catch (...) { }
|
||||
|
||||
static Service_registry parent_services;
|
||||
static Service_registry child_services;
|
||||
static Registry<Init::Parent_service> parent_services;
|
||||
static Registry<Routed_service> child_services;
|
||||
static Child_registry children;
|
||||
static Cap_connection cap;
|
||||
|
||||
@ -306,7 +282,7 @@ int main(int, char **)
|
||||
Signal_context sig_ctx_res_avail;
|
||||
config()->sigh(sig_rec.manage(&sig_ctx_config));
|
||||
/* prevent init to block for resource upgrades (never satisfied by core) */
|
||||
env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail));
|
||||
Genode::env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail));
|
||||
|
||||
for (;;) {
|
||||
|
||||
@ -327,7 +303,7 @@ int main(int, char **)
|
||||
config()->xml_node().for_each_sub_node("alias", [&] (Xml_node alias_node) {
|
||||
|
||||
try {
|
||||
children.insert_alias(new (env()->heap()) Alias(alias_node));
|
||||
children.insert_alias(new (Genode::env()->heap()) Alias(alias_node));
|
||||
}
|
||||
catch (Alias::Name_is_missing) {
|
||||
warning("missing 'name' attribute in '<alias>' entry"); }
|
||||
@ -341,12 +317,11 @@ int main(int, char **)
|
||||
config()->xml_node().for_each_sub_node("start", [&] (Xml_node start_node) {
|
||||
|
||||
try {
|
||||
children.insert(new (env()->heap())
|
||||
Init::Child(start_node, default_route_node,
|
||||
children.insert(new (Genode::env()->heap())
|
||||
Init::Child(env, start_node, default_route_node,
|
||||
children, read_prio_levels(),
|
||||
read_affinity_space(),
|
||||
parent_services, child_services, cap,
|
||||
ldso_ds));
|
||||
parent_services, child_services));
|
||||
}
|
||||
catch (Rom_connection::Rom_connection_failed) {
|
||||
/*
|
||||
@ -354,6 +329,11 @@ int main(int, char **)
|
||||
* by the Rom_connection constructor.
|
||||
*/
|
||||
}
|
||||
catch (Ram_session::Alloc_failed) {
|
||||
warning("failed to allocate memory during child construction"); }
|
||||
catch (Region_map::Attach_failed) {
|
||||
warning("failed to attach dataspace to local address space "
|
||||
"during child construction"); }
|
||||
});
|
||||
|
||||
/* start children */
|
||||
@ -386,35 +366,22 @@ int main(int, char **)
|
||||
while (children.any()) {
|
||||
Init::Child *child = children.any();
|
||||
children.remove(child);
|
||||
Genode::Server const *server = child->server();
|
||||
destroy(env()->heap(), child);
|
||||
|
||||
/*
|
||||
* The killed child may have provided services to other children.
|
||||
* Since the server is dead by now, we cannot close its sessions
|
||||
* in the cooperative way. Instead, we need to instruct each
|
||||
* other child to forget about session associated with the dead
|
||||
* server. Note that the 'child' pointer points a a no-more
|
||||
* existing object. It is only used to identify the corresponding
|
||||
* session. It must never by de-referenced!
|
||||
*/
|
||||
children.revoke_server(server);
|
||||
destroy(Genode::env()->heap(), child);
|
||||
}
|
||||
|
||||
/* remove all known aliases */
|
||||
while (children.any_alias()) {
|
||||
Init::Alias *alias = children.any_alias();
|
||||
children.remove_alias(alias);
|
||||
destroy(env()->heap(), alias);
|
||||
destroy(Genode::env()->heap(), alias);
|
||||
}
|
||||
|
||||
/* reset knowledge about parent services */
|
||||
parent_services.remove_all();
|
||||
parent_services.for_each([&] (Init::Parent_service &service) {
|
||||
destroy(Genode::env()->heap(), &service); });
|
||||
|
||||
/* reload config */
|
||||
try { config()->reload(); } catch (...) { }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ void Config::reload()
|
||||
_config_xml = _config_xml_node(_config_ds);
|
||||
|
||||
} catch (Genode::Xml_node::Invalid_syntax) {
|
||||
Genode::error("Config file has invalid syntax");
|
||||
Genode::error("config ROM has invalid syntax");
|
||||
_config_xml = fallback_config_xml();
|
||||
}
|
||||
}
|
||||
@ -80,13 +80,13 @@ Config::Config()
|
||||
{ }
|
||||
|
||||
|
||||
Config *Genode::config()
|
||||
Volatile_object<Config> &Genode::config()
|
||||
{
|
||||
static bool config_failed = false;
|
||||
if (!config_failed) {
|
||||
try {
|
||||
static Config config_inst;
|
||||
return &config_inst;
|
||||
static Volatile_object<Config> config_inst;
|
||||
return config_inst;
|
||||
} catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||
Genode::error("Could not obtain config file");
|
||||
} catch (Genode::Xml_node::Invalid_syntax) {
|
||||
@ -97,6 +97,7 @@ Config *Genode::config()
|
||||
}
|
||||
/* do not try again to construct 'config_inst' */
|
||||
config_failed = true;
|
||||
return 0;
|
||||
class Config_construction_failed : Genode::Exception { };
|
||||
throw Config_construction_failed();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user