mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-24 07:46:42 +00:00
base: remove Child::heap
This patch improves the accounting for the backing store of session-state meta data. Originally, the session state used to be allocated by a child-local heap partition fed from the child's RAM session. However, whereas this approach was somehow practical from a runtime's (parent's) point of view, the child component could not count on the quota in its own RAM session. I.e., if the Child::heap grew at the parent side, the child's RAM session would magically diminish. This caused two problems. First, it violates assumptions of components like init that carefully manage their RAM resources (and giving most of them away their children). Second, if a child transfers most of its RAM session quota to another RAM session (like init does), the child's RAM session may actually not allow the parent's heap to grow, which is a very difficult error condition to deal with. In the new version, there is no Child::heap anymore. Instead, session states are allocated from the runtime's RAM session. In order to let children pay for these costs, the parent withdraws the local session costs from the session quota donated from the child when the child initiates a new session. Hence, in principle, all components on the route of the session request take a small bite from the session quota to pay for their local book keeping Consequently, the session quota that ends up at the server may become depleted more or less, depending on the route. In the case where the remaining quota is insufficient for the server, the server responds with 'QUOTA_EXCEEDED'. Since this behavior must generally be expected, this patch equips the client-side 'Env::session' implementation with the ability to re-issue session requests with successively growing quota donations. For several of core's services (ROM, IO_MEM, IRQ), the default session quota has now increased by 2 KiB, which should suffice for session requests to up to 3 hops as is the common case for most run scripts. For longer routes, the retry mechanism as described above comes into effect. For the time being, we give a warning whenever the server-side quota check triggers the retry mechanism. The warning may eventually be removed at a later stage.
This commit is contained in:
parent
641fb08b5f
commit
9cba459958
@ -192,6 +192,14 @@ struct Genode::Child_policy
|
||||
*/
|
||||
virtual void session_state_changed() { }
|
||||
|
||||
/**
|
||||
* Granularity of allocating the backing store for session meta data
|
||||
*
|
||||
* Session meta data is allocated from 'ref_ram'. The first batch of
|
||||
* session-state objects is allocated at child-construction time.
|
||||
*/
|
||||
virtual size_t session_alloc_batch_size() const { return 16; }
|
||||
|
||||
/**
|
||||
* Return region map for the child's address space
|
||||
*
|
||||
@ -301,6 +309,16 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
/* sessions opened by the child */
|
||||
Id_space<Client> _id_space;
|
||||
|
||||
/* allocator used for dynamically created session state objects */
|
||||
Sliced_heap _session_md_alloc { _policy.ref_ram(), _local_rm };
|
||||
|
||||
Session_state::Factory::Batch_size const
|
||||
_session_batch_size { _policy.session_alloc_batch_size() };
|
||||
|
||||
/* factory for dynamically created session-state objects */
|
||||
Session_state::Factory _session_factory { _session_md_alloc,
|
||||
_session_batch_size };
|
||||
|
||||
typedef Session_state::Args Args;
|
||||
|
||||
static Child_policy::Route _resolve_session_request(Child_policy &,
|
||||
@ -313,12 +331,6 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
|
||||
void _try_construct_env_dependent_members();
|
||||
|
||||
/* heap for child-specific allocations using the child's quota */
|
||||
Constructible<Heap> _heap;
|
||||
|
||||
/* factory for dynamically created session-state objects */
|
||||
Constructible<Session_state::Factory> _session_factory;
|
||||
|
||||
Constructible<Initial_thread> _initial_thread;
|
||||
|
||||
struct Process
|
||||
@ -584,11 +596,6 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
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(); }
|
||||
@ -597,24 +604,10 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
Cpu_session &cpu() { return _cpu.session(); }
|
||||
Pd_session &pd() { return _pd .session(); }
|
||||
|
||||
/**
|
||||
* Exception type
|
||||
*/
|
||||
class Inactive : Exception { };
|
||||
|
||||
/**
|
||||
* Request factory for creating session-state objects
|
||||
*
|
||||
* \throw Inactive factory cannot by provided because the child it
|
||||
* not yet completely initialized.
|
||||
*/
|
||||
Session_state::Factory &session_factory()
|
||||
{
|
||||
if (_session_factory.constructed())
|
||||
return *_session_factory;
|
||||
|
||||
throw Inactive();
|
||||
}
|
||||
Session_state::Factory &session_factory() { return _session_factory; }
|
||||
|
||||
/**
|
||||
* Instruct the child to yield resources
|
||||
|
@ -35,7 +35,7 @@ struct Genode::Local_connection_base : Noncopyable
|
||||
|
||||
protected:
|
||||
|
||||
Session_state _session_state;
|
||||
Constructible<Session_state> _session_state;
|
||||
|
||||
private:
|
||||
|
||||
@ -59,18 +59,31 @@ struct Genode::Local_connection_base : Noncopyable
|
||||
Parent::Client::Id id,
|
||||
Args const &args, Affinity const &affinity,
|
||||
size_t ram_quota)
|
||||
:
|
||||
_session_state(service, id_space, id, label_from_args(args.string()),
|
||||
_init_args(args, ram_quota), affinity)
|
||||
{
|
||||
_session_state.service().initiate_request(_session_state);
|
||||
enum { NUM_ATTEMPTS = 10 };
|
||||
for (unsigned i = 0; i < NUM_ATTEMPTS; i++) {
|
||||
_session_state.construct(service, id_space, id,
|
||||
label_from_args(args.string()),
|
||||
_init_args(args, ram_quota), affinity);
|
||||
|
||||
_session_state->service().initiate_request(*_session_state);
|
||||
|
||||
if (_session_state->phase == Session_state::QUOTA_EXCEEDED)
|
||||
ram_quota += 4096;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (_session_state->phase == Session_state::QUOTA_EXCEEDED)
|
||||
warning("giving up to increase session quota for ", service.name(), " session "
|
||||
"after ", (int)NUM_ATTEMPTS, " attempts");
|
||||
}
|
||||
|
||||
~Local_connection_base()
|
||||
{
|
||||
if (_session_state.alive()) {
|
||||
_session_state.phase = Session_state::CLOSE_REQUESTED;
|
||||
_session_state.service().initiate_request(_session_state);
|
||||
if (_session_state->alive()) {
|
||||
_session_state->phase = Session_state::CLOSE_REQUESTED;
|
||||
_session_state->service().initiate_request(*_session_state);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -89,7 +102,7 @@ class Genode::Local_connection : Local_connection_base
|
||||
|
||||
Capability<SESSION> cap() const
|
||||
{
|
||||
return reinterpret_cap_cast<SESSION>(_session_state.cap);
|
||||
return reinterpret_cap_cast<SESSION>(_session_state->cap);
|
||||
}
|
||||
|
||||
SESSION &session()
|
||||
@ -99,15 +112,15 @@ class Genode::Local_connection : Local_connection_base
|
||||
* RAM session, we return the reference to the corresponding
|
||||
* component object, which can be called directly.
|
||||
*/
|
||||
if (_session_state.local_ptr)
|
||||
return *static_cast<SESSION *>(_session_state.local_ptr);
|
||||
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. We construct the client object if
|
||||
* we have a valid session capability.
|
||||
*/
|
||||
if (!_client.constructed() && _session_state.cap.valid())
|
||||
if (!_client.constructed() && _session_state->cap.valid())
|
||||
_client.construct(cap());
|
||||
|
||||
if (_client.constructed())
|
||||
@ -117,7 +130,7 @@ class Genode::Local_connection : Local_connection_base
|
||||
* 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(), ") "
|
||||
error(SESSION::service_name(), " session (", _session_state->args(), ") "
|
||||
"unavailable");
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
@ -130,7 +130,10 @@ class Genode::Local_service : public Service
|
||||
class Denied : Exception { };
|
||||
|
||||
/**
|
||||
* Create session
|
||||
*
|
||||
* \throw Denied
|
||||
* \throw Quota_exceeded
|
||||
*/
|
||||
virtual SESSION &create(Args const &, Affinity) = 0;
|
||||
|
||||
@ -200,6 +203,8 @@ class Genode::Local_service : public Service
|
||||
}
|
||||
catch (typename Factory::Denied) {
|
||||
session.phase = Session_state::INVALID_ARGS; }
|
||||
catch (Quota_exceeded) {
|
||||
session.phase = Session_state::QUOTA_EXCEEDED; }
|
||||
|
||||
break;
|
||||
|
||||
@ -225,6 +230,7 @@ class Genode::Local_service : public Service
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::QUOTA_EXCEEDED:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
@ -317,6 +323,7 @@ class Genode::Parent_service : public Service
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::QUOTA_EXCEEDED:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <util/list.h>
|
||||
#include <util/reconstructible.h>
|
||||
#include <session/capability.h>
|
||||
#include <base/slab.h>
|
||||
#include <base/id_space.h>
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
@ -78,6 +79,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server,
|
||||
|
||||
enum Phase { CREATE_REQUESTED,
|
||||
INVALID_ARGS,
|
||||
QUOTA_EXCEEDED,
|
||||
AVAILABLE,
|
||||
CAP_HANDED_OUT,
|
||||
UPGRADE_REQUESTED,
|
||||
@ -182,6 +184,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server,
|
||||
|
||||
case CREATE_REQUESTED:
|
||||
case INVALID_ARGS:
|
||||
case QUOTA_EXCEEDED:
|
||||
case CLOSED:
|
||||
return false;
|
||||
|
||||
@ -234,17 +237,34 @@ class Genode::Session_state::Factory : Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Allocator &_md_alloc;
|
||||
size_t const _batch_size;
|
||||
|
||||
Slab _slab;
|
||||
|
||||
public:
|
||||
|
||||
struct Batch_size { size_t value; };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param md_alloc meta-data allocator used for allocating
|
||||
* 'Session_state' objects
|
||||
*
|
||||
* \param batch granularity of allocating blocks at 'md_alloc',
|
||||
* must be greater than 0
|
||||
*/
|
||||
Factory(Allocator &md_alloc) : _md_alloc(md_alloc) { }
|
||||
Factory(Allocator &md_alloc, Batch_size batch)
|
||||
:
|
||||
_batch_size(batch.value),
|
||||
/*
|
||||
* The calculation of 'block_size' is just an approximation as
|
||||
* a slab block contains a few bytes of meta data in addition
|
||||
* to the actual slab entries.
|
||||
*/
|
||||
_slab(sizeof(Session_state), sizeof(Session_state)*_batch_size,
|
||||
nullptr, &md_alloc)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Create a new session-state object
|
||||
@ -256,11 +276,16 @@ class Genode::Session_state::Factory : Noncopyable
|
||||
template <typename... ARGS>
|
||||
Session_state &create(ARGS &&... args)
|
||||
{
|
||||
Session_state &session = *new (_md_alloc) Session_state(args...);
|
||||
Session_state &session = *new (_slab) Session_state(args...);
|
||||
session.owner(*this);
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of bytes consumed per session
|
||||
*/
|
||||
size_t session_costs() const { return _slab.overhead(sizeof(Session_state)); }
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
@ -270,7 +295,7 @@ class Genode::Session_state::Factory : Noncopyable
|
||||
*/
|
||||
friend class Session_state;
|
||||
|
||||
void _destroy(Session_state &session) { Genode::destroy(_md_alloc, &session); }
|
||||
void _destroy(Session_state &session) { Genode::destroy(_slab, &session); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -101,6 +101,14 @@ class Genode::Slab : public Allocator
|
||||
*/
|
||||
~Slab();
|
||||
|
||||
/**
|
||||
* Return number of bytes consumed per slab entry
|
||||
*
|
||||
* The function takes the slab-internal meta-data needs and the actual
|
||||
* slab entry into account.
|
||||
*/
|
||||
static size_t entry_costs(size_t slab_size, size_t block_size);
|
||||
|
||||
/**
|
||||
* Add new slab block as backing store
|
||||
*
|
||||
|
@ -30,7 +30,7 @@ struct Genode::Io_mem_connection : Connection<Io_mem_session>, Io_mem_session_cl
|
||||
Capability<Io_mem_session> _session(Parent &parent, addr_t base, size_t size,
|
||||
bool write_combined)
|
||||
{
|
||||
return session("ram_quota=4K, base=0x%p, size=0x%lx, wc=%s",
|
||||
return session("ram_quota=6K, base=0x%p, size=0x%lx, wc=%s",
|
||||
base, size, write_combined ? "yes" : "no");
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ struct Genode::Io_port_connection : Connection<Io_port_session>,
|
||||
*/
|
||||
Capability<Io_port_session> _session(Parent &parent, unsigned base, unsigned size)
|
||||
{
|
||||
return session(parent, "ram_quota=4K, io_port_base=%u, io_port_size=%u",
|
||||
return session(parent, "ram_quota=6K, io_port_base=%u, io_port_size=%u",
|
||||
base, size);
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ struct Genode::Irq_connection : Connection<Irq_session>, Irq_session_client
|
||||
Irq_session::Polarity polarity,
|
||||
Genode::addr_t device_config_phys)
|
||||
{
|
||||
return session("ram_quota=4K, irq_number=%u, irq_trigger=%u, "
|
||||
return session("ram_quota=6K, irq_number=%u, irq_trigger=%u, "
|
||||
" irq_polarity=%u, device_config_phys=0x%lx",
|
||||
irq, trigger, polarity, device_config_phys);
|
||||
}
|
||||
|
@ -183,6 +183,8 @@ class Genode::Parent
|
||||
* Request session capability
|
||||
*
|
||||
* \throw Service_denied
|
||||
* \throw Quota_exceeded session quota does not suffice for
|
||||
* the creation of the new session
|
||||
*
|
||||
* In the exception case, the parent implicitly closes the session.
|
||||
*/
|
||||
@ -215,7 +217,7 @@ class Genode::Parent
|
||||
* Interface for providing services
|
||||
*/
|
||||
|
||||
enum Session_response { SESSION_OK, SESSION_CLOSED, INVALID_ARGS };
|
||||
enum Session_response { SESSION_OK, SESSION_CLOSED, INVALID_ARGS, QUOTA_EXCEEDED };
|
||||
|
||||
/**
|
||||
* Set state of a session provided by the child service
|
||||
|
@ -28,7 +28,7 @@ class Genode::Rom_connection : public Connection<Rom_session>,
|
||||
|
||||
class Rom_connection_failed : public Parent::Exception { };
|
||||
|
||||
enum { RAM_QUOTA = 4096UL };
|
||||
enum { RAM_QUOTA = 6*1024UL };
|
||||
|
||||
private:
|
||||
|
||||
|
@ -130,8 +130,9 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
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);
|
||||
warning("insufficient ram quota "
|
||||
"for ", SESSION_TYPE::service_name(), " session, "
|
||||
"provided=", ram_quota, ", required=", needed);
|
||||
throw Root::Quota_exceeded();
|
||||
}
|
||||
|
||||
@ -269,6 +270,7 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
|
||||
{
|
||||
try {
|
||||
return _create(args, affinity); }
|
||||
catch (Root::Quota_exceeded) { throw Service::Quota_exceeded(); }
|
||||
catch (...) {
|
||||
throw typename Local_service<SESSION_TYPE>::Factory::Denied(); }
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ struct Genode::Trace::Connection : Genode::Connection<Genode::Trace::Session>,
|
||||
{
|
||||
return session(parent,
|
||||
"ram_quota=%lu, arg_buffer_size=%lu, parent_levels=%u",
|
||||
ram_quota, arg_buffer_size, parent_levels);
|
||||
ram_quota + 2048, arg_buffer_size, parent_levels);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,6 +189,8 @@ class Core_child : public Child_policy
|
||||
|
||||
Ram_session &ref_ram() { return _core_ram; }
|
||||
Ram_session_capability ref_ram_cap() const { return _core_ram_cap; }
|
||||
|
||||
size_t session_alloc_batch_size() const override { return 128; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -92,27 +92,7 @@ class Genode::Expanding_parent_client : public Parent_client
|
||||
Session_args const &args,
|
||||
Affinity const &affinity) override
|
||||
{
|
||||
enum { NUM_ATTEMPTS = 2 };
|
||||
return retry<Parent::Quota_exceeded>(
|
||||
[&] () { return Parent_client::session(id, 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=%lu", ram_quota);
|
||||
|
||||
resource_request(Resource_args(buf));
|
||||
},
|
||||
NUM_ATTEMPTS);
|
||||
return Parent_client::session(id, name, args, affinity);
|
||||
}
|
||||
|
||||
Upgrade_result upgrade(Client::Id id, Upgrade_args const &args) override
|
||||
|
@ -142,9 +142,15 @@ void Child::session_sigh(Signal_context_capability sigh)
|
||||
* 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(); });
|
||||
|
||||
if (session.phase == Session_state::AVAILABLE ||
|
||||
session.phase == Session_state::QUOTA_EXCEEDED ||
|
||||
session.phase == Session_state::INVALID_ARGS) {
|
||||
|
||||
if (sigh.valid() && session.async_client_notify)
|
||||
Signal_transmitter(sigh).submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -164,6 +170,10 @@ create_session(Child_policy::Name const &child_name, Service &service,
|
||||
try {
|
||||
return service.create_session(factory, id_space, id, label, args, affinity);
|
||||
}
|
||||
catch (Service::Quota_exceeded) {
|
||||
error(child_name, " requested session with insufficient session quota");
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
catch (Allocator::Out_of_memory) {
|
||||
error("could not allocate session meta data for child ", child_name);
|
||||
throw Parent::Quota_exceeded();
|
||||
@ -230,12 +240,27 @@ Session_capability Child::session(Parent::Client::Id id,
|
||||
/* filter session affinity */
|
||||
Affinity const filtered_affinity = _policy.filter_session_affinity(affinity);
|
||||
|
||||
size_t const ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0);
|
||||
|
||||
/* portion of quota to keep for ourself to maintain the session meta data */
|
||||
size_t const keep_ram_quota = _session_factory.session_costs();
|
||||
|
||||
if (ram_quota < keep_ram_quota)
|
||||
throw Parent::Quota_exceeded();
|
||||
|
||||
/* ram quota to be forwarded to the server */
|
||||
size_t const forward_ram_quota = ram_quota - keep_ram_quota;
|
||||
|
||||
/* adjust the session information as presented to the server */
|
||||
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota",
|
||||
forward_ram_quota);
|
||||
|
||||
/* may throw a 'Parent::Service_denied' exception */
|
||||
Child_policy::Route route = _resolve_session_request(_policy, name.string(), argbuf);
|
||||
Service &service = route.service;
|
||||
|
||||
Session_state &session =
|
||||
create_session(_policy.name(), service, route.label, *_session_factory,
|
||||
create_session(_policy.name(), service, route.label, _session_factory,
|
||||
_id_space, id, argbuf, filtered_affinity);
|
||||
|
||||
_policy.session_state_changed();
|
||||
@ -243,14 +268,12 @@ Session_capability Child::session(Parent::Client::Id id,
|
||||
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(argbuf, "ram_quota").ulong_value(0);
|
||||
|
||||
try {
|
||||
/* transfer the quota donation from the child's account 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(),
|
||||
Transfer donation_to_service(forward_ram_quota, _policy.ref_ram_cap(),
|
||||
service.ram());
|
||||
|
||||
/* try to dispatch session request synchronously */
|
||||
@ -261,6 +284,11 @@ Session_capability Child::session(Parent::Client::Id id,
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
if (session.phase == Session_state::QUOTA_EXCEEDED) {
|
||||
_revert_quota_and_destroy(session);
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
|
||||
/* finish transaction */
|
||||
donation_from_child.acknowledge();
|
||||
donation_to_service.acknowledge();
|
||||
@ -298,7 +326,10 @@ Session_capability Child::session_cap(Client::Id id)
|
||||
|
||||
auto lamda = [&] (Session_state &session) {
|
||||
|
||||
if (session.phase == Session_state::INVALID_ARGS) {
|
||||
if (session.phase == Session_state::INVALID_ARGS
|
||||
|| session.phase == Session_state::QUOTA_EXCEEDED) {
|
||||
|
||||
Session_state::Phase const phase = session.phase;
|
||||
|
||||
/*
|
||||
* Implicity discard the session request when delivering an
|
||||
@ -306,7 +337,11 @@ Session_capability Child::session_cap(Client::Id id)
|
||||
* of the session ID at the child anyway.
|
||||
*/
|
||||
_revert_quota_and_destroy(session);
|
||||
throw Parent::Service_denied();
|
||||
|
||||
if (phase == Session_state::INVALID_ARGS)
|
||||
throw Parent::Service_denied();
|
||||
else
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
|
||||
if (!session.alive())
|
||||
@ -390,8 +425,14 @@ void Child::_revert_quota_and_destroy(Session_state &session)
|
||||
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(),
|
||||
/*
|
||||
* Transfer session quota from ourself to the client (our child). In
|
||||
* addition to the quota returned from the server, we also return the
|
||||
* quota that we preserved for locally storing the session meta data
|
||||
* ('session_costs').
|
||||
*/
|
||||
Transfer donation_to_client(session.donated_ram_quota() +
|
||||
_session_factory.session_costs(),
|
||||
_policy.ref_ram_cap(), ram_session_cap());
|
||||
/* finish transaction */
|
||||
donation_from_service.acknowledge();
|
||||
@ -411,7 +452,8 @@ 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) {
|
||||
if (session.phase == Session_state::INVALID_ARGS
|
||||
|| session.phase == Session_state::QUOTA_EXCEEDED) {
|
||||
_revert_quota_and_destroy(session);
|
||||
return CLOSE_DONE;
|
||||
}
|
||||
@ -501,6 +543,12 @@ void Child::session_response(Server::Id id, Session_response response)
|
||||
session.ready_callback->session_ready(session);
|
||||
break;
|
||||
|
||||
case Parent::QUOTA_EXCEEDED:
|
||||
session.phase = Session_state::QUOTA_EXCEEDED;
|
||||
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;
|
||||
@ -638,9 +686,6 @@ void Child::_try_construct_env_dependent_members()
|
||||
_policy.init(_cpu.session(), _cpu.cap());
|
||||
_policy.init(_pd.session(), _pd.cap());
|
||||
|
||||
_heap.construct(&_ram.session(), &_local_rm);
|
||||
_session_factory.construct(*_heap);
|
||||
|
||||
try {
|
||||
_initial_thread.construct(_cpu.session(), _pd.cap(), "initial");
|
||||
_process.construct(_binary.session().dataspace(), _linker_dataspace(),
|
||||
@ -733,13 +778,9 @@ Child::~Child()
|
||||
|
||||
/*
|
||||
* Make sure to destroy the users of the child's environment sessions
|
||||
* before destructing those sessions. E.g., as the environment RAM session
|
||||
* provides the backing store for the '_heap', we must not destroy the heap
|
||||
* after the RAM session.
|
||||
* before destructing those sessions.
|
||||
*/
|
||||
_process.destruct();
|
||||
_initial_thread.destruct();
|
||||
_session_factory.destruct();
|
||||
_heap.destruct();
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/retry.h>
|
||||
#include <base/component.h>
|
||||
#include <base/connection.h>
|
||||
#include <base/service.h>
|
||||
@ -108,12 +109,64 @@ namespace {
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Session_capability cap = _parent.session(id, name, args, affinity);
|
||||
if (cap.valid())
|
||||
return cap;
|
||||
/*
|
||||
* Since we account for the backing store for session meta data on
|
||||
* the route between client and server, the session quota provided
|
||||
* by the client may become successively diminished by intermediate
|
||||
* components, prompting the server to deny the session request.
|
||||
*
|
||||
* If the session creation failed due to insufficient session
|
||||
* quota, we try to repeatedly increase the quota up to
|
||||
* 'NUM_ATTEMPTS'.
|
||||
*/
|
||||
enum { NUM_ATTEMPTS = 10 };
|
||||
|
||||
_block_for_session();
|
||||
return _parent.session_cap(id);
|
||||
/* extract session quota as specified by the 'Connection' */
|
||||
char argbuf[Parent::Session_args::MAX_SIZE];
|
||||
strncpy(argbuf, args.string(), sizeof(argbuf));
|
||||
size_t ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0);
|
||||
|
||||
return retry<Parent::Quota_exceeded>([&] () {
|
||||
|
||||
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota",
|
||||
String<32>(Number_of_bytes(ram_quota)).string());
|
||||
|
||||
Session_capability cap =
|
||||
_parent.session(id, name, Parent::Session_args(argbuf), affinity);
|
||||
|
||||
if (cap.valid())
|
||||
return cap;
|
||||
|
||||
_block_for_session();
|
||||
return _parent.session_cap(id);
|
||||
},
|
||||
[&] () {
|
||||
/*
|
||||
* If our RAM session has less quota available than the
|
||||
* session quota, the session-quota transfer failed. In
|
||||
* this case, we try to recover by issuing a resource
|
||||
* request to the parent.
|
||||
*
|
||||
* Otherwise, the session-quota transfer succeeded but
|
||||
* the request was denied by the server.
|
||||
*/
|
||||
if (ram_quota > ram().avail()) {
|
||||
|
||||
/* issue resource request */
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "ram_quota=%lu", ram_quota);
|
||||
|
||||
_parent.resource_request(Parent::Resource_args(buf));
|
||||
} else {
|
||||
ram_quota += 4096;
|
||||
}
|
||||
|
||||
}, NUM_ATTEMPTS);
|
||||
|
||||
warning("giving up to increase session quota for ", name.string(), " session "
|
||||
"after ", (int)NUM_ATTEMPTS, " attempts");
|
||||
|
||||
throw Parent::Quota_exceeded();
|
||||
}
|
||||
|
||||
void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override
|
||||
|
@ -186,7 +186,7 @@ void Root_proxy::_handle_session_request(Xml_node request)
|
||||
catch (Root::Invalid_args) {
|
||||
_env.parent().session_response(id, Parent::INVALID_ARGS); }
|
||||
catch (Root::Quota_exceeded) {
|
||||
_env.parent().session_response(id, Parent::INVALID_ARGS); }
|
||||
_env.parent().session_response(id, Parent::QUOTA_EXCEEDED); }
|
||||
catch (Root::Unavailable) {
|
||||
_env.parent().session_response(id, Parent::INVALID_ARGS); }
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ struct Formatted_phase
|
||||
switch (_phase) {
|
||||
case State::CREATE_REQUESTED: print(output, "CREATE_REQUESTED"); break;
|
||||
case State::INVALID_ARGS: print(output, "INVALID_ARGS"); break;
|
||||
case State::QUOTA_EXCEEDED: print(output, "QUOTA_EXCEEDED"); break;
|
||||
case State::AVAILABLE: print(output, "AVAILABLE"); break;
|
||||
case State::CAP_HANDED_OUT: print(output, "CAP_HANDED_OUT"); break;
|
||||
case State::UPGRADE_REQUESTED: print(output, "UPGRADE_REQUESTED"); break;
|
||||
@ -86,6 +87,7 @@ void Session_state::generate_session_request(Xml_generator &xml) const
|
||||
break;
|
||||
|
||||
case INVALID_ARGS:
|
||||
case QUOTA_EXCEEDED:
|
||||
case AVAILABLE:
|
||||
case CAP_HANDED_OUT:
|
||||
case CLOSED:
|
||||
|
@ -51,6 +51,8 @@ class Launchpad_child : public Genode::Child_policy,
|
||||
|
||||
Genode::Env &_env;
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
|
||||
Genode::Ram_session_capability _ref_ram_cap;
|
||||
Genode::Ram_session_client _ref_ram { _ref_ram_cap };
|
||||
Genode::size_t const _ram_quota;
|
||||
@ -88,6 +90,7 @@ class Launchpad_child : public Genode::Child_policy,
|
||||
public:
|
||||
|
||||
Launchpad_child(Genode::Env &env,
|
||||
Genode::Allocator &alloc,
|
||||
Genode::Session_label const &label,
|
||||
Binary_name const &elf_name,
|
||||
Genode::size_t ram_quota,
|
||||
@ -96,7 +99,8 @@ class Launchpad_child : public Genode::Child_policy,
|
||||
Genode::Dataspace_capability config_ds)
|
||||
:
|
||||
_name(label), _elf_name(elf_name),
|
||||
_env(env), _ref_ram_cap(env.ram_session_cap()), _ram_quota(ram_quota),
|
||||
_env(env), _alloc(alloc),
|
||||
_ref_ram_cap(env.ram_session_cap()), _ram_quota(ram_quota),
|
||||
_parent_services(parent_services),
|
||||
_child_services(child_services),
|
||||
_session_requester(env.ep().rpc_ep(), _env.ram(), _env.rm()),
|
||||
@ -112,7 +116,7 @@ class Launchpad_child : public Genode::Child_policy,
|
||||
_child_services.for_each(
|
||||
[&] (Child_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space()))
|
||||
Genode::destroy(_child.heap(), &service); });
|
||||
Genode::destroy(_alloc, &service); });
|
||||
}
|
||||
|
||||
|
||||
@ -188,7 +192,7 @@ class Launchpad_child : public Genode::Child_policy,
|
||||
return;
|
||||
}
|
||||
|
||||
new (_child.heap())
|
||||
new (_alloc)
|
||||
Child_service(_child_services, _session_requester.id_space(),
|
||||
_child.session_factory(), service_name,
|
||||
_child.ram_session_cap(), *this);
|
||||
|
@ -154,7 +154,15 @@ Launchpad_child *Launchpad::start_child(Launchpad_child::Name const &binary_name
|
||||
|
||||
if (ram_quota > _env.ram().avail()) {
|
||||
error("child's ram quota is higher than our available quota, using available quota");
|
||||
ram_quota = _env.ram().avail() - 256*1000;
|
||||
|
||||
size_t const avail = _env.ram().avail();
|
||||
size_t const preserved = 256*1024;
|
||||
|
||||
if (avail < preserved) {
|
||||
error("giving up, our own quota is too low (", avail, ")");
|
||||
return 0;
|
||||
}
|
||||
ram_quota = avail - preserved;
|
||||
}
|
||||
|
||||
size_t metadata_size = 4096*16 + sizeof(Launchpad_child);
|
||||
@ -168,7 +176,7 @@ Launchpad_child *Launchpad::start_child(Launchpad_child::Name const &binary_name
|
||||
|
||||
try {
|
||||
Launchpad_child *c = new (&_sliced_heap)
|
||||
Launchpad_child(_env, unique_name, binary_name, ram_quota,
|
||||
Launchpad_child(_env, _heap, unique_name, binary_name, ram_quota,
|
||||
_parent_services, _child_services, config_ds);
|
||||
|
||||
Lock::Guard lock_guard(_children_lock);
|
||||
|
@ -26,7 +26,7 @@ struct Hello::Connection : Genode::Connection<Session>, Session_client
|
||||
:
|
||||
/* create session */
|
||||
Genode::Connection<Hello::Session>(env, session(env.parent(),
|
||||
"ram_quota=4K")),
|
||||
"ram_quota=6K")),
|
||||
|
||||
/* initialize RPC interface */
|
||||
Session_client(cap()) { }
|
||||
|
@ -31,7 +31,7 @@ struct Audio_in::Connection : Genode::Connection<Session>, Audio_in::Session_cli
|
||||
Capability<Audio_in::Session> _session(Genode::Parent &parent, char const *channel)
|
||||
{
|
||||
return session(parent, "ram_quota=%ld, channel=\"%s\"",
|
||||
2*4096 + sizeof(Stream), channel);
|
||||
10*1024 + sizeof(Stream), channel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ struct Audio_out::Connection : Genode::Connection<Session>, Audio_out::Session_c
|
||||
Capability<Audio_out::Session> _session(Genode::Parent &parent, char const *channel)
|
||||
{
|
||||
return session(parent, "ram_quota=%ld, channel=\"%s\"",
|
||||
2*4096 + sizeof(Stream), channel);
|
||||
2*4096 + 2048 + sizeof(Stream), channel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ struct Block::Connection : Genode::Connection<Session>, Session_client
|
||||
char const *label, Genode::size_t tx_buf_size)
|
||||
{
|
||||
return session(parent, "ram_quota=%ld, tx_buf_size=%ld, label=\"%s\"",
|
||||
3*4096 + tx_buf_size, tx_buf_size, label);
|
||||
14*1024 + tx_buf_size, tx_buf_size, label);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,6 +339,8 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
|
||||
Env &_env;
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Verbose const &_verbose;
|
||||
|
||||
Id const _id;
|
||||
@ -424,9 +426,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
|
||||
/*
|
||||
* If the configured RAM quota exceeds our own quota, we donate
|
||||
* all remaining quota to the child but we need to count in
|
||||
* our allocation of the child meta data from the heap.
|
||||
* Hence, we preserve some of our own quota.
|
||||
* all remaining quota to the child.
|
||||
*/
|
||||
if (ram_quota > ram_avail) {
|
||||
ram_quota = ram_avail;
|
||||
@ -506,12 +506,18 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator solely used for configuration-dependent
|
||||
* allocations. It is not used for allocations on behalf
|
||||
* of the child's behavior.
|
||||
*
|
||||
*
|
||||
* \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(Env &env,
|
||||
Allocator &alloc,
|
||||
Verbose const &verbose,
|
||||
Id id,
|
||||
Report_update_trigger &report_update_trigger,
|
||||
@ -523,7 +529,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
Registry<Parent_service> &parent_services,
|
||||
Registry<Routed_service> &child_services)
|
||||
:
|
||||
_env(env), _verbose(verbose), _id(id),
|
||||
_env(env), _alloc(alloc), _verbose(verbose), _id(id),
|
||||
_report_update_trigger(report_update_trigger),
|
||||
_list_element(this),
|
||||
_start_node(start_node),
|
||||
@ -567,24 +573,21 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
if (_verbose.enabled())
|
||||
log(" provides service ", Cstring(name));
|
||||
|
||||
new (_child.heap())
|
||||
new (_alloc)
|
||||
Routed_service(child_services, this->name(),
|
||||
_session_requester.id_space(),
|
||||
_child.session_factory(),
|
||||
name, _child.ram_session_cap(), *this);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Xml_node::Nonexistent_sub_node) { }
|
||||
catch (Genode::Child::Inactive) {
|
||||
error(this->name(), ": incomplete environment at construction time"); }
|
||||
}
|
||||
|
||||
virtual ~Child()
|
||||
{
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space()))
|
||||
destroy(_child.heap(), &service); });
|
||||
destroy(_alloc, &service); });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,7 @@ struct Input::Connection : Genode::Connection<Session>, Session_client
|
||||
* \noapi
|
||||
*/
|
||||
Capability<Input::Session> _session(Genode::Parent &parent, char const *label) {
|
||||
return session(parent, "ram_quota=16K, label=\"%s\"", label); }
|
||||
return session(parent, "ram_quota=18K, label=\"%s\"", label); }
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -48,7 +48,7 @@ struct Input::Connection : Genode::Connection<Session>, Session_client
|
||||
Connection() __attribute__((deprecated))
|
||||
:
|
||||
Genode::Connection<Input::Session>(
|
||||
session(*Genode::env_deprecated()->parent(), "ram_quota=16K")),
|
||||
session(*Genode::env_deprecated()->parent(), "ram_quota=18K")),
|
||||
Session_client(*Genode::env_deprecated()->rm_session(), cap())
|
||||
{ }
|
||||
};
|
||||
|
@ -194,6 +194,7 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object<Rom_session>,
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::QUOTA_EXCEEDED:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
|
@ -213,6 +213,7 @@ class Genode::Slave::Connection_base
|
||||
break;
|
||||
|
||||
case Session_state::INVALID_ARGS:
|
||||
case Session_state::QUOTA_EXCEEDED:
|
||||
case Session_state::AVAILABLE:
|
||||
case Session_state::CAP_HANDED_OUT:
|
||||
case Session_state::CLOSED:
|
||||
|
@ -27,7 +27,7 @@ struct Platform::Connection : Genode::Connection<Session>, Client
|
||||
* Constructor
|
||||
*/
|
||||
Connection(Genode::Env &env)
|
||||
: Genode::Connection<Session>(env, session(env.parent(), "ram_quota=4K")),
|
||||
: Genode::Connection<Session>(env, session(env.parent(), "ram_quota=6K")),
|
||||
Client(cap()) { }
|
||||
|
||||
/**
|
||||
@ -38,7 +38,7 @@ struct Platform::Connection : Genode::Connection<Session>, Client
|
||||
* argument instead
|
||||
*/
|
||||
Connection() __attribute__((deprecated))
|
||||
: Genode::Connection<Session>(session("ram_quota=4K")),
|
||||
: Genode::Connection<Session>(session("ram_quota=6K")),
|
||||
Client(cap()) { }
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace Report { struct Connection; }
|
||||
|
||||
struct Report::Connection : Genode::Connection<Session>, Session_client
|
||||
{
|
||||
enum { RAM_QUOTA = 4*4096 }; /* value used for 'Slave::Connection' */
|
||||
enum { RAM_QUOTA = 6*4096 }; /* value used for 'Slave::Connection' */
|
||||
|
||||
/**
|
||||
* Issue session request
|
||||
@ -33,7 +33,7 @@ struct Report::Connection : Genode::Connection<Session>, Session_client
|
||||
char const *label, size_t buffer_size)
|
||||
{
|
||||
return session(parent, "label=\"%s\", ram_quota=%ld, buffer_size=%zd",
|
||||
label, 2*4096 + buffer_size, buffer_size);
|
||||
label, 10*1024 + buffer_size, buffer_size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,7 +28,7 @@ struct Rtc::Connection : Genode::Connection<Session>, Session_client
|
||||
*/
|
||||
Connection(Genode::Env &env)
|
||||
:
|
||||
Genode::Connection<Rtc::Session>(env, session(env.parent(), "ram_quota=4K")),
|
||||
Genode::Connection<Rtc::Session>(env, session(env.parent(), "ram_quota=8K")),
|
||||
Session_client(cap())
|
||||
{ }
|
||||
|
||||
@ -41,7 +41,7 @@ struct Rtc::Connection : Genode::Connection<Session>, Session_client
|
||||
*/
|
||||
Connection() __attribute__((deprecated))
|
||||
:
|
||||
Genode::Connection<Rtc::Session>(session("ram_quota=4K")),
|
||||
Genode::Connection<Rtc::Session>(session("ram_quota=8K")),
|
||||
Session_client(cap())
|
||||
{ }
|
||||
};
|
||||
|
@ -51,7 +51,7 @@ struct Terminal::Connection : Genode::Connection<Session>, Session_client
|
||||
:
|
||||
Genode::Connection<Session>(env, session(env.parent(),
|
||||
"ram_quota=%ld, label=\"%s\"",
|
||||
2*4096, label)),
|
||||
10*1024, label)),
|
||||
Session_client(env.rm(), cap())
|
||||
{
|
||||
wait_for_connection(cap());
|
||||
|
@ -40,7 +40,7 @@ class Timer::Connection : public Genode::Connection<Session>, public Session_cli
|
||||
*/
|
||||
Connection(Genode::Env &env, char const *label = "")
|
||||
:
|
||||
Genode::Connection<Session>(env, session(env.parent(), "ram_quota=8K, label=\"%s\"", label)),
|
||||
Genode::Connection<Session>(env, session(env.parent(), "ram_quota=10K, label=\"%s\"", label)),
|
||||
Session_client(cap())
|
||||
{
|
||||
/* register default signal handler */
|
||||
@ -56,7 +56,7 @@ class Timer::Connection : public Genode::Connection<Session>, public Session_cli
|
||||
*/
|
||||
Connection() __attribute__((deprecated))
|
||||
:
|
||||
Genode::Connection<Session>(session("ram_quota=8K")),
|
||||
Genode::Connection<Session>(session("ram_quota=10K")),
|
||||
Session_client(cap())
|
||||
{
|
||||
/* register default signal handler */
|
||||
|
@ -70,7 +70,7 @@ compare_output_to {
|
||||
[init -> test-report_rom] Reporter: start reporting (while the ROM client still listens)
|
||||
[init -> test-report_rom] ROM client: wait for update notification
|
||||
[init -> test-report_rom] ROM client: try to open the same report again
|
||||
[init -> test-report_rom] Error: Report-session creation failed (label="brightness", ram_quota=12288, buffer_size=4096)
|
||||
[init -> test-report_rom] Error: Report-session creation failed (label="brightness", ram_quota=14336, buffer_size=4096)
|
||||
[init -> test-report_rom] ROM client: catched Parent::Service_denied - OK
|
||||
[init -> test-report_rom] --- test-report_rom finished ---
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ void Init::Main::_handle_config()
|
||||
|
||||
try {
|
||||
_children.insert(new (_heap)
|
||||
Init::Child(_env, *_verbose,
|
||||
Init::Child(_env, _heap, *_verbose,
|
||||
Init::Child::Id { ++_child_cnt },
|
||||
_state_reporter,
|
||||
start_node, default_route_node,
|
||||
|
Loading…
Reference in New Issue
Block a user