mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-25 00:11:07 +00:00
cfdbccc5c2
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
328 lines
9.0 KiB
C++
328 lines
9.0 KiB
C++
/*
|
|
* \brief Generic root component implementation
|
|
* \author Norman Feske
|
|
* \date 2006-05-22
|
|
*
|
|
* This class is there for your convenience. It performs the common actions
|
|
* that must always be taken when creating a new session.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2006-2013 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__ROOT__COMPONENT_H_
|
|
#define _INCLUDE__ROOT__COMPONENT_H_
|
|
|
|
#include <root/root.h>
|
|
#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>
|
|
|
|
namespace Genode {
|
|
|
|
class Single_client;
|
|
class Multiple_clients;
|
|
template <typename, typename POLICY = Multiple_clients> class Root_component;
|
|
}
|
|
|
|
|
|
/**
|
|
* Session creation policy for a single-client service
|
|
*/
|
|
class Genode::Single_client
|
|
{
|
|
private:
|
|
|
|
bool _used;
|
|
|
|
public:
|
|
|
|
Single_client() : _used(0) { }
|
|
|
|
void aquire(const char *)
|
|
{
|
|
if (_used)
|
|
throw Root::Unavailable();
|
|
|
|
_used = true;
|
|
}
|
|
|
|
void release() { _used = false; }
|
|
};
|
|
|
|
|
|
/**
|
|
* Session-creation policy for a multi-client service
|
|
*/
|
|
struct Genode::Multiple_clients
|
|
{
|
|
void aquire(const char *) { }
|
|
void release() { }
|
|
};
|
|
|
|
|
|
/**
|
|
* Template for implementing the root interface
|
|
*
|
|
* \param SESSION_TYPE session-component type to manage,
|
|
* derived from 'Rpc_object'
|
|
* \param POLICY session-creation policy
|
|
*
|
|
* The 'POLICY' template parameter allows for constraining the session
|
|
* creation to only one instance at a time (using the 'Single_session'
|
|
* policy) or multiple instances (using the 'Multiple_sessions' policy).
|
|
*
|
|
* The 'POLICY' class must provide the following two methods:
|
|
*
|
|
* 'aquire(const char *args)' is called with the session arguments
|
|
* at creation time of each new session. It can therefore implement
|
|
* a session-creation policy taking session arguments into account.
|
|
* If the policy denies the creation of a new session, it throws
|
|
* one of the exceptions defined in the 'Root' interface.
|
|
*
|
|
* 'release' is called at the destruction time of a session. It enables
|
|
* the policy to keep track of and impose restrictions on the number
|
|
* of existing sessions.
|
|
*
|
|
* The default policy 'Multiple_clients' imposes no restrictions on the
|
|
* creation of new sessions.
|
|
*/
|
|
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:
|
|
|
|
/*
|
|
* Entry point that manages the session objects
|
|
* created by this root interface
|
|
*/
|
|
Rpc_entrypoint *_ep;
|
|
|
|
/*
|
|
* Allocator for allocating session objects.
|
|
* This allocator must be used by the derived
|
|
* class when calling the 'new' operator for
|
|
* creating a new session.
|
|
*/
|
|
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:
|
|
|
|
/**
|
|
* Create new session (to be implemented by a derived class)
|
|
*
|
|
* Only a derived class knows the constructor arguments of
|
|
* a specific session. Therefore, we cannot unify the call
|
|
* of its 'new' operator and must implement the session
|
|
* creation at a place, where the required knowledge exist.
|
|
*
|
|
* In the implementation of this method, the heap, provided
|
|
* by 'Root_component' must be used for allocating the session
|
|
* object.
|
|
*
|
|
* If the server implementation does not evaluate the session
|
|
* affinity, it suffices to override the overload without the
|
|
* affinity argument.
|
|
*
|
|
* \throw Allocator::Out_of_memory typically caused by the
|
|
* meta-data allocator
|
|
* \throw Root::Invalid_args typically caused by the
|
|
* session-component constructor
|
|
*/
|
|
virtual SESSION_TYPE *_create_session(const char *args,
|
|
Affinity const &)
|
|
{
|
|
return _create_session(args);
|
|
}
|
|
|
|
virtual SESSION_TYPE *_create_session(const char *args)
|
|
{
|
|
throw Root::Invalid_args();
|
|
}
|
|
|
|
/**
|
|
* Inform session about a quota upgrade
|
|
*
|
|
* Once a session is created, its client can successively extend
|
|
* its quota donation via the 'Parent::transfer_quota' operation.
|
|
* This will result in the invokation of 'Root::upgrade' at the
|
|
* root interface the session was created with. The root interface,
|
|
* in turn, informs the session about the new resources via the
|
|
* '_upgrade_session' method. The default implementation is
|
|
* suited for sessions that use a static amount of resources
|
|
* accounted for at session-creation time. For such sessions, an
|
|
* upgrade is not useful. However, sessions that dynamically
|
|
* allocate resources on behalf of its client, should respond to
|
|
* quota upgrades by implementing this method.
|
|
*
|
|
* \param session session to upgrade
|
|
* \param args description of additional resources in the
|
|
* same format as used at session creation
|
|
*/
|
|
virtual void _upgrade_session(SESSION_TYPE *, const char *) { }
|
|
|
|
virtual void _destroy_session(SESSION_TYPE *session) {
|
|
Genode::destroy(_md_alloc, session); }
|
|
|
|
/**
|
|
* Return allocator to allocate server object in '_create_session()'
|
|
*/
|
|
Allocator *md_alloc() { return _md_alloc; }
|
|
|
|
/**
|
|
* Return entrypoint that serves the root component
|
|
*/
|
|
Rpc_entrypoint *ep() { return _ep; }
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param ep entry point that manages the sessions of this
|
|
* root interface
|
|
* \param md_alloc meta-data allocator providing the backing store
|
|
* for session objects
|
|
*/
|
|
Root_component(Entrypoint &ep, Allocator &md_alloc)
|
|
:
|
|
_ep(&ep.rpc_ep()), _md_alloc(&md_alloc)
|
|
{ }
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \deprecated use the constructor with the 'Entrypoint &'
|
|
* argument instead
|
|
*/
|
|
Root_component(Rpc_entrypoint *ep, Allocator *md_alloc)
|
|
:
|
|
_ep(ep), _md_alloc(md_alloc)
|
|
{ }
|
|
|
|
|
|
/**************************************
|
|
** 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 **
|
|
********************/
|
|
|
|
Session_capability session(Root::Session_args const &args,
|
|
Affinity const &affinity) override
|
|
{
|
|
if (!args.valid_string()) throw Root::Invalid_args();
|
|
SESSION_TYPE &session = _create(args.string(), affinity);
|
|
return session.cap();
|
|
}
|
|
|
|
void upgrade(Session_capability session, Root::Upgrade_args const &args) override
|
|
{
|
|
if (!args.valid_string()) throw Root::Invalid_args();
|
|
|
|
_ep->apply(session, [&] (SESSION_TYPE *s) {
|
|
if (!s) return;
|
|
|
|
_upgrade_session(s, args.string());
|
|
});
|
|
}
|
|
|
|
void close(Session_capability session_cap) override
|
|
{
|
|
SESSION_TYPE * session;
|
|
|
|
_ep->apply(session_cap, [&] (SESSION_TYPE *s) {
|
|
session = s;
|
|
|
|
/* let the entry point forget the session object */
|
|
if (session) _ep->dissolve(session);
|
|
});
|
|
|
|
if (!session) return;
|
|
|
|
_destroy_session(session);
|
|
|
|
POLICY::release();
|
|
}
|
|
};
|
|
|
|
#endif /* _INCLUDE__ROOT__COMPONENT_H_ */
|