mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-20 06:07:59 +00:00
base/env: Implementation of 'Expanding_parent'
This commit is contained in:
parent
ae40cb545c
commit
65c4246f95
17
base-linux/src/base/env/platform_env.cc
vendored
17
base-linux/src/base/env/platform_env.cc
vendored
@ -67,7 +67,7 @@ Platform_env::Local_parent::session(Service_name const &service_name,
|
||||
.ulong_value(~0);
|
||||
|
||||
if (size == 0)
|
||||
return Parent_client::session(service_name, args, affinity);
|
||||
return Expanding_parent_client::session(service_name, args, affinity);
|
||||
|
||||
Rm_session_mmap *rm = new (env()->heap())
|
||||
Rm_session_mmap(true, size);
|
||||
@ -75,7 +75,7 @@ Platform_env::Local_parent::session(Service_name const &service_name,
|
||||
return Session_capability::local_cap(rm);
|
||||
}
|
||||
|
||||
return Parent_client::session(service_name, args, affinity);
|
||||
return Expanding_parent_client::session(service_name, args, affinity);
|
||||
}
|
||||
|
||||
|
||||
@ -98,10 +98,10 @@ void Platform_env::Local_parent::close(Session_capability session)
|
||||
}
|
||||
|
||||
|
||||
Platform_env::Local_parent::Local_parent(Parent_capability parent_cap)
|
||||
: Parent_client(parent_cap)
|
||||
{
|
||||
}
|
||||
Platform_env::Local_parent::Local_parent(Parent_capability parent_cap,
|
||||
Emergency_ram_reserve &reserve)
|
||||
: Expanding_parent_client(parent_cap, reserve)
|
||||
{ }
|
||||
|
||||
|
||||
/******************
|
||||
@ -143,7 +143,7 @@ static Parent_capability obtain_parent_cap()
|
||||
|
||||
Platform_env::Local_parent &Platform_env::_parent()
|
||||
{
|
||||
static Local_parent local_parent(obtain_parent_cap());
|
||||
static Local_parent local_parent(obtain_parent_cap(), *this);
|
||||
return local_parent;
|
||||
}
|
||||
|
||||
@ -153,7 +153,8 @@ 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", ""))),
|
||||
_heap(Platform_env_base::ram_session(), Platform_env_base::rm_session())
|
||||
_heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()),
|
||||
_emergency_ram_ds(ram_session()->alloc(_emergency_ram_size()))
|
||||
{
|
||||
/* register TID and PID of the main thread at core */
|
||||
cpu_session()->thread_id(parent()->main_thread_cap(),
|
||||
|
26
base-linux/src/base/env/platform_env.h
vendored
26
base-linux/src/base/env/platform_env.h
vendored
@ -360,7 +360,7 @@ namespace Genode {
|
||||
/**
|
||||
* 'Platform_env' used by all processes except for core
|
||||
*/
|
||||
class Platform_env : public Platform_env_base
|
||||
class Platform_env : public Platform_env_base, public Emergency_ram_reserve
|
||||
{
|
||||
private:
|
||||
|
||||
@ -376,7 +376,7 @@ namespace Genode {
|
||||
* All requests that do not refer to the RM service are passed
|
||||
* through the real parent interface.
|
||||
*/
|
||||
class Local_parent : public Parent_client
|
||||
class Local_parent : public Expanding_parent_client
|
||||
{
|
||||
public:
|
||||
|
||||
@ -396,16 +396,25 @@ namespace Genode {
|
||||
* promote requests to non-local
|
||||
* services
|
||||
*/
|
||||
Local_parent(Parent_capability parent_cap);
|
||||
Local_parent(Parent_capability parent_cap,
|
||||
Emergency_ram_reserve &);
|
||||
};
|
||||
|
||||
/**
|
||||
* Obtain singleton instance of parent interface
|
||||
* Return instance of parent interface
|
||||
*/
|
||||
static Local_parent &_parent();
|
||||
Local_parent &_parent();
|
||||
|
||||
Heap _heap;
|
||||
|
||||
/*
|
||||
* Emergency RAM reserve
|
||||
*
|
||||
* See the comment of '_fallback_sig_cap()' in 'env/env.cc'.
|
||||
*/
|
||||
constexpr static size_t _emergency_ram_size() { return 4*1024; }
|
||||
Ram_dataspace_capability _emergency_ram_ds;
|
||||
|
||||
|
||||
/*************************************
|
||||
** Linux-specific helper functions **
|
||||
@ -432,6 +441,13 @@ namespace Genode {
|
||||
}
|
||||
|
||||
|
||||
/*************************************
|
||||
** Emergency_ram_reserve interface **
|
||||
*************************************/
|
||||
|
||||
void release() { ram_session()->free(_emergency_ram_ds); }
|
||||
|
||||
|
||||
/*******************
|
||||
** Env interface **
|
||||
*******************/
|
||||
|
46
base/src/base/env/env.cc
vendored
46
base/src/base/env/env.cc
vendored
@ -29,3 +29,49 @@ namespace Genode {
|
||||
return &_env;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Genode::Signal_receiver *resource_sig_rec()
|
||||
{
|
||||
static Genode::Signal_receiver sig_rec;
|
||||
return &sig_rec;
|
||||
}
|
||||
|
||||
|
||||
Genode::Signal_context_capability
|
||||
Genode::Expanding_parent_client::_fallback_sig_cap()
|
||||
{
|
||||
static Signal_context _sig_ctx;
|
||||
static Signal_context_capability _sig_cap;
|
||||
|
||||
/* create signal-context capability only once */
|
||||
if (!_sig_cap.valid()) {
|
||||
|
||||
/*
|
||||
* Because the 'manage' function consumes meta data of the signal
|
||||
* session, calling it may result in an 'Out_of_metadata' error. The
|
||||
* 'manage' function handles this error by upgrading the session quota
|
||||
* accordingly. However, this upgrade, in turn, may result in the
|
||||
* depletion of the process' RAM quota. In this case, the process would
|
||||
* issue a resource request to the parent. But in order to do so, the
|
||||
* fallback signal handler has to be constructed. To solve this
|
||||
* hen-and-egg problem, we allocate a so-called emergency RAM reserve
|
||||
* immediately at the startup of the process as part of the
|
||||
* 'Platform_env'. When initializing the fallback signal handler, these
|
||||
* resources get released in order to ensure an eventual upgrade of the
|
||||
* signal session to succeed.
|
||||
*
|
||||
* The corner case is tested by 'os/src/test/resource_request'.
|
||||
*/
|
||||
_emergency_ram_reserve.release();
|
||||
_sig_cap = resource_sig_rec()->manage(&_sig_ctx);
|
||||
}
|
||||
|
||||
return _sig_cap;
|
||||
}
|
||||
|
||||
|
||||
void Genode::Expanding_parent_client::_wait_for_resource_response()
|
||||
{
|
||||
resource_sig_rec()->wait_for_signal();
|
||||
}
|
||||
|
26
base/src/base/env/platform_env.h
vendored
26
base/src/base/env/platform_env.h
vendored
@ -85,11 +85,11 @@ struct Genode::Expanding_cpu_session_client : Upgradeable_client<Genode::Cpu_ses
|
||||
};
|
||||
|
||||
|
||||
class Genode::Platform_env : public Genode::Env
|
||||
class Genode::Platform_env : public Genode::Env, public Emergency_ram_reserve
|
||||
{
|
||||
private:
|
||||
|
||||
Parent_client _parent_client;
|
||||
Expanding_parent_client _parent_client;
|
||||
|
||||
struct Resources
|
||||
{
|
||||
@ -115,7 +115,15 @@ class Genode::Platform_env : public Genode::Env
|
||||
Resources _resources;
|
||||
Heap _heap;
|
||||
|
||||
char _initial_junk[sizeof(addr_t) * 4096];
|
||||
char _initial_heap_chunk[sizeof(addr_t) * 4096];
|
||||
|
||||
/*
|
||||
* Emergency RAM reserve
|
||||
*
|
||||
* See the comment of '_fallback_sig_cap()' in 'env/env.cc'.
|
||||
*/
|
||||
constexpr static size_t _emergency_ram_size() { return 4*1024; }
|
||||
Ram_dataspace_capability _emergency_ram_ds;
|
||||
|
||||
public:
|
||||
|
||||
@ -124,15 +132,23 @@ class Genode::Platform_env : public Genode::Env
|
||||
*/
|
||||
Platform_env()
|
||||
:
|
||||
_parent_client(Genode::parent_cap()),
|
||||
_parent_client(Genode::parent_cap(), *this),
|
||||
_resources(_parent_client),
|
||||
_heap(&_resources.ram, &_resources.rm, Heap::UNLIMITED,
|
||||
_initial_junk, sizeof(_initial_junk))
|
||||
_initial_heap_chunk, sizeof(_initial_heap_chunk)),
|
||||
_emergency_ram_ds(_resources.ram.alloc(_emergency_ram_size()))
|
||||
{ }
|
||||
|
||||
void reload_parent_cap(Native_capability::Dst, long);
|
||||
|
||||
|
||||
/*************************************
|
||||
** Emergency_ram_reserve interface **
|
||||
*************************************/
|
||||
|
||||
void release() { _resources.ram.free(_emergency_ram_ds); }
|
||||
|
||||
|
||||
/*******************
|
||||
** Env interface **
|
||||
*******************/
|
||||
|
248
base/src/base/env/platform_env_common.h
vendored
248
base/src/base/env/platform_env_common.h
vendored
@ -17,6 +17,7 @@
|
||||
#define _PLATFORM_ENV_COMMON_H_
|
||||
|
||||
#include <base/env.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <parent/client.h>
|
||||
#include <ram_session/client.h>
|
||||
#include <rm_session/client.h>
|
||||
@ -27,6 +28,7 @@ namespace Genode {
|
||||
class Expanding_rm_session_client;
|
||||
class Expanding_ram_session_client;
|
||||
class Expanding_cpu_session_client;
|
||||
class Expanding_parent_client;
|
||||
}
|
||||
|
||||
|
||||
@ -35,13 +37,23 @@ namespace Genode {
|
||||
*
|
||||
* If the function 'func' throws an exception of type 'EXC', the 'handler'
|
||||
* is called and the function call is retried.
|
||||
*
|
||||
* \param EXC exception type to handle
|
||||
* \param func functor to execute
|
||||
* \param handler exception handler executed if 'func' raised an exception
|
||||
* of type 'EXC'
|
||||
* \param attempts number of attempts to execute 'func' before giving up
|
||||
* and reflecting the exception 'EXC' to the caller. If not
|
||||
* specified, attempt infinitely.
|
||||
*/
|
||||
template <typename EXC, typename FUNC, typename HANDLER>
|
||||
auto retry(FUNC func, HANDLER handler) -> decltype(func())
|
||||
auto retry(FUNC func, HANDLER handler, unsigned attempts = ~0U) -> decltype(func())
|
||||
{
|
||||
for (;;)
|
||||
for (unsigned i = 0; attempts == ~0U || i < attempts; i++)
|
||||
try { return func(); }
|
||||
catch (EXC) { handler(); }
|
||||
|
||||
throw EXC();
|
||||
}
|
||||
|
||||
|
||||
@ -64,6 +76,7 @@ struct Upgradeable_client : CLIENT
|
||||
|
||||
char buf[128];
|
||||
Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd", quota);
|
||||
|
||||
Genode::env()->parent()->upgrade(_cap, buf);
|
||||
}
|
||||
};
|
||||
@ -74,12 +87,235 @@ struct Genode::Expanding_ram_session_client : Upgradeable_client<Genode::Ram_ses
|
||||
Expanding_ram_session_client(Ram_session_capability cap)
|
||||
: Upgradeable_client<Genode::Ram_session_client>(cap) { }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, bool cached)
|
||||
Ram_dataspace_capability alloc(size_t size, bool cached = true)
|
||||
{
|
||||
return retry<Ram_session::Out_of_metadata>(
|
||||
[&] () { return Ram_session_client::alloc(size, cached); },
|
||||
[&] () { upgrade_ram(8*1024); });
|
||||
/*
|
||||
* If the RAM session runs out of quota, issue a resource request
|
||||
* to the parent and retry.
|
||||
*/
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
return retry<Ram_session::Quota_exceeded>(
|
||||
[&] () {
|
||||
/*
|
||||
* If the RAM session runs out of meta data, upgrade the
|
||||
* session quota and retry.
|
||||
*/
|
||||
return retry<Ram_session::Out_of_metadata>(
|
||||
[&] () { return Ram_session_client::alloc(size, cached); },
|
||||
[&] () { upgrade_ram(8*1024); });
|
||||
},
|
||||
[&] () {
|
||||
char buf[128];
|
||||
|
||||
/*
|
||||
* The RAM service withdraws the meta data for the allocator
|
||||
* from the RAM quota. In the worst case, a new slab block
|
||||
* may be needed. To cover the worst case, we need to take
|
||||
* this possible overhead into account when requesting
|
||||
* additional RAM quota from the parent.
|
||||
*
|
||||
* Because the worst case almost never happens, we request
|
||||
* a bit too much quota for the most time.
|
||||
*/
|
||||
enum { ALLOC_OVERHEAD = 1024U };
|
||||
Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd",
|
||||
size + ALLOC_OVERHEAD);
|
||||
env()->parent()->resource_request(buf);
|
||||
},
|
||||
NUM_ATTEMPTS);
|
||||
}
|
||||
|
||||
int transfer_quota(Ram_session_capability ram_session, size_t amount)
|
||||
{
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
int ret = -1;
|
||||
for (unsigned i = 0; i < NUM_ATTEMPTS; i++) {
|
||||
|
||||
ret = Ram_session_client::transfer_quota(ram_session, amount);
|
||||
if (ret != -3) break;
|
||||
|
||||
/*
|
||||
* The transfer failed because we don't have enough quota. Request
|
||||
* the needed amount from the parent.
|
||||
*
|
||||
* XXX Let transfer_quota throw 'Ram_session::Quota_exceeded'
|
||||
*/
|
||||
char buf[128];
|
||||
Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd", amount);
|
||||
env()->parent()->resource_request(buf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Emergency_ram_reserve
|
||||
{
|
||||
virtual void release() = 0;
|
||||
};
|
||||
|
||||
|
||||
class Genode::Expanding_parent_client : public Parent_client
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Signal handler state
|
||||
*
|
||||
* UNDEFINED - No signal handler is effective. If we issue a
|
||||
* resource request, use our built-in fallback
|
||||
* signal handler.
|
||||
* BLOCKING_DEFAULT - The fallback signal handler is effective.
|
||||
* When using this handler, we block for a
|
||||
* for a response to a resource request.
|
||||
* CUSTOM - A custom signal handler was registered. Calls
|
||||
* of 'resource_request' won't block.
|
||||
*/
|
||||
enum State { UNDEFINED, BLOCKING_DEFAULT, CUSTOM };
|
||||
State _state = { UNDEFINED };
|
||||
|
||||
/**
|
||||
* Lock used to serialize resource requests
|
||||
*/
|
||||
Lock _lock;
|
||||
|
||||
/**
|
||||
* Return signal context capability for the fallback signal handler
|
||||
*/
|
||||
Signal_context_capability _fallback_sig_cap();
|
||||
|
||||
/**
|
||||
* Block for resource response arriving at the fallback signal handler
|
||||
*/
|
||||
static void _wait_for_resource_response();
|
||||
|
||||
/**
|
||||
* Emergency RAM reserve for constructing the fallback signal handler
|
||||
*
|
||||
* See the comment of '_fallback_sig_cap()' in 'env/env.cc'.
|
||||
*/
|
||||
Emergency_ram_reserve &_emergency_ram_reserve;
|
||||
|
||||
public:
|
||||
|
||||
Expanding_parent_client(Parent_capability cap,
|
||||
Emergency_ram_reserve &emergency_ram_reserve)
|
||||
:
|
||||
Parent_client(cap), _emergency_ram_reserve(emergency_ram_reserve)
|
||||
{ }
|
||||
|
||||
|
||||
/**********************
|
||||
** Parent interface **
|
||||
**********************/
|
||||
|
||||
Session_capability session(Service_name const &name,
|
||||
Session_args const &args,
|
||||
Affinity const &affinity)
|
||||
{
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
return retry<Parent::Quota_exceeded>(
|
||||
[&] () { return Parent_client::session(name, args, affinity); },
|
||||
[&] () {
|
||||
|
||||
/*
|
||||
* Request amount of session quota from the parent.
|
||||
*
|
||||
* XXX We could deduce the available quota of our
|
||||
* own RAM session from the request.
|
||||
*/
|
||||
size_t const ram_quota =
|
||||
Arg_string::find_arg(args.string(), "ram_quota")
|
||||
.ulong_value(0);
|
||||
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "ram_quota=%zd", ram_quota);
|
||||
|
||||
resource_request(Resource_args(buf));
|
||||
},
|
||||
NUM_ATTEMPTS);
|
||||
}
|
||||
|
||||
void upgrade(Session_capability to_session, Upgrade_args const &args)
|
||||
{
|
||||
/*
|
||||
* If the upgrade fails, attempt to issue a resource request twice.
|
||||
*
|
||||
* If the default fallback for resource-available signals is used,
|
||||
* the first request will block until the resources are upgraded.
|
||||
* The second attempt to upgrade will succeed.
|
||||
*
|
||||
* If a custom handler is installed, the resource quest will return
|
||||
* immediately. The second upgrade attempt may fail too if the
|
||||
* parent handles the resource request asynchronously. In this
|
||||
* case, we escalate the problem to caller by propagating the
|
||||
* 'Parent::Quota_exceeded' exception. Now, it is the job of the
|
||||
* caller to issue (and respond to) a resource request.
|
||||
*/
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
retry<Parent::Quota_exceeded>(
|
||||
[&] () { Parent_client::upgrade(to_session, args); },
|
||||
[&] () { resource_request(Resource_args(args.string())); },
|
||||
NUM_ATTEMPTS);
|
||||
}
|
||||
|
||||
void resource_avail_sigh(Signal_context_capability sigh)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
/*
|
||||
* If signal hander gets de-installed, let the next call of
|
||||
* 'resource_request' install the fallback signal handler.
|
||||
*/
|
||||
if (_state == CUSTOM && !sigh.valid())
|
||||
_state = UNDEFINED;
|
||||
|
||||
/*
|
||||
* Forward information about a custom signal handler and remember
|
||||
* state to avoid blocking in 'resource_request'.
|
||||
*/
|
||||
if (sigh.valid()) {
|
||||
_state = CUSTOM;
|
||||
Parent_client::resource_avail_sigh(sigh);
|
||||
}
|
||||
}
|
||||
|
||||
void resource_request(Resource_args const &args)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
PLOG("resource_request: %s", args.string());
|
||||
|
||||
/*
|
||||
* Issue request but don't block if a custom signal handler is
|
||||
* installed.
|
||||
*/
|
||||
if (_state == CUSTOM) {
|
||||
Parent_client::resource_request(args);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install fallback signal handler not yet installed.
|
||||
*/
|
||||
if (_state == UNDEFINED) {
|
||||
Parent_client::resource_avail_sigh(_fallback_sig_cap());
|
||||
_state = BLOCKING_DEFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue resource request
|
||||
*/
|
||||
Parent_client::resource_request(args);
|
||||
|
||||
/*
|
||||
* Block until we get a response for the outstanding resource
|
||||
* request.
|
||||
*/
|
||||
if (_state == BLOCKING_DEFAULT)
|
||||
_wait_for_resource_response();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _PLATFORM_ENV_COMMON_H_ */
|
||||
|
5
base/src/base/env/reload_parent_cap.cc
vendored
5
base/src/base/env/reload_parent_cap.cc
vendored
@ -44,8 +44,9 @@ void Genode::Platform_env::reload_parent_cap(Native_capability::Dst dst,
|
||||
/*
|
||||
* Re-initialize 'Platform_env' members
|
||||
*/
|
||||
_parent_client = Parent_client(Genode::parent_cap());
|
||||
_resources = Resources(_parent_client);
|
||||
static_cast<Parent_client &>(_parent_client) = Parent_client(Genode::parent_cap());
|
||||
|
||||
_resources = Resources(_parent_client);
|
||||
|
||||
/*
|
||||
* Keep information about dynamically allocated memory but use the new
|
||||
|
Loading…
Reference in New Issue
Block a user