From 9cba459958a7418616ed60e7cc5ffd8556404a30 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Sun, 19 Feb 2017 10:31:50 +0100 Subject: [PATCH] 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. --- repos/base/include/base/child.h | 45 +++++----- repos/base/include/base/local_connection.h | 39 ++++++--- repos/base/include/base/service.h | 7 ++ repos/base/include/base/session_state.h | 33 +++++++- repos/base/include/base/slab.h | 8 ++ .../base/include/io_mem_session/connection.h | 2 +- .../base/include/io_port_session/connection.h | 2 +- repos/base/include/irq_session/connection.h | 2 +- repos/base/include/parent/parent.h | 4 +- repos/base/include/rom_session/connection.h | 2 +- repos/base/include/root/component.h | 6 +- repos/base/include/trace_session/connection.h | 2 +- repos/base/src/core/main.cc | 2 + .../base/internal/expanding_parent_client.h | 22 +---- repos/base/src/lib/base/child.cc | 83 ++++++++++++++----- repos/base/src/lib/base/component.cc | 63 ++++++++++++-- repos/base/src/lib/base/root_proxy.cc | 2 +- repos/base/src/lib/base/session_state.cc | 2 + repos/demo/include/launchpad/launchpad.h | 10 ++- repos/demo/src/lib/launchpad/launchpad.cc | 12 ++- .../include/hello_session/connection.h | 2 +- .../os/include/audio_in_session/connection.h | 2 +- .../os/include/audio_out_session/connection.h | 2 +- repos/os/include/block_session/connection.h | 2 +- repos/os/include/init/child.h | 21 +++-- repos/os/include/input_session/connection.h | 4 +- .../os/include/os/child_policy_dynamic_rom.h | 1 + repos/os/include/os/slave.h | 1 + .../os/include/platform_session/connection.h | 4 +- repos/os/include/report_session/connection.h | 4 +- repos/os/include/rtc_session/connection.h | 4 +- .../os/include/terminal_session/connection.h | 2 +- repos/os/include/timer_session/connection.h | 4 +- repos/os/run/report_rom.run | 2 +- repos/os/src/init/main.cc | 2 +- 35 files changed, 275 insertions(+), 130 deletions(-) diff --git a/repos/base/include/base/child.h b/repos/base/include/base/child.h index c23683c5c..b6dab9deb 100644 --- a/repos/base/include/base/child.h +++ b/repos/base/include/base/child.h @@ -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, /* sessions opened by the child */ Id_space _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, void _try_construct_env_dependent_members(); - /* heap for child-specific allocations using the child's quota */ - Constructible _heap; - - /* factory for dynamically created session-state objects */ - Constructible _session_factory; - Constructible _initial_thread; struct Process @@ -584,11 +596,6 @@ class Genode::Child : protected Rpc_object, 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, 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 diff --git a/repos/base/include/base/local_connection.h b/repos/base/include/base/local_connection.h index c45b917cc..2fbb7ce32 100644 --- a/repos/base/include/base/local_connection.h +++ b/repos/base/include/base/local_connection.h @@ -35,7 +35,7 @@ struct Genode::Local_connection_base : Noncopyable protected: - Session_state _session_state; + Constructible _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 cap() const { - return reinterpret_cap_cast(_session_state.cap); + return reinterpret_cap_cast(_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_state.local_ptr); + if (_session_state->local_ptr) + return *static_cast(_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(); } diff --git a/repos/base/include/base/service.h b/repos/base/include/base/service.h index e9207e3dd..200df961a 100644 --- a/repos/base/include/base/service.h +++ b/repos/base/include/base/service.h @@ -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: diff --git a/repos/base/include/base/session_state.h b/repos/base/include/base/session_state.h index 6ef8b6666..2f956e067 100644 --- a/repos/base/include/base/session_state.h +++ b/repos/base/include/base/session_state.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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 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); } }; diff --git a/repos/base/include/base/slab.h b/repos/base/include/base/slab.h index b4a7be613..9f16664e4 100644 --- a/repos/base/include/base/slab.h +++ b/repos/base/include/base/slab.h @@ -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 * diff --git a/repos/base/include/io_mem_session/connection.h b/repos/base/include/io_mem_session/connection.h index 7ac62ff53..779e92a7b 100644 --- a/repos/base/include/io_mem_session/connection.h +++ b/repos/base/include/io_mem_session/connection.h @@ -30,7 +30,7 @@ struct Genode::Io_mem_connection : Connection, Io_mem_session_cl Capability _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"); } diff --git a/repos/base/include/io_port_session/connection.h b/repos/base/include/io_port_session/connection.h index 1a2af2edf..31eaf1749 100644 --- a/repos/base/include/io_port_session/connection.h +++ b/repos/base/include/io_port_session/connection.h @@ -30,7 +30,7 @@ struct Genode::Io_port_connection : Connection, */ Capability _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); } diff --git a/repos/base/include/irq_session/connection.h b/repos/base/include/irq_session/connection.h index 3c961ac6a..4a8cf21c0 100644 --- a/repos/base/include/irq_session/connection.h +++ b/repos/base/include/irq_session/connection.h @@ -32,7 +32,7 @@ struct Genode::Irq_connection : Connection, 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); } diff --git a/repos/base/include/parent/parent.h b/repos/base/include/parent/parent.h index 76c20c2d5..9e598d4d4 100644 --- a/repos/base/include/parent/parent.h +++ b/repos/base/include/parent/parent.h @@ -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 diff --git a/repos/base/include/rom_session/connection.h b/repos/base/include/rom_session/connection.h index 2eed9c43c..0dd66f0d9 100644 --- a/repos/base/include/rom_session/connection.h +++ b/repos/base/include/rom_session/connection.h @@ -28,7 +28,7 @@ class Genode::Rom_connection : public Connection, class Rom_connection_failed : public Parent::Exception { }; - enum { RAM_QUOTA = 4096UL }; + enum { RAM_QUOTA = 6*1024UL }; private: diff --git a/repos/base/include/root/component.h b/repos/base/include/root/component.h index d3041cf4e..81ef7d2d4 100644 --- a/repos/base/include/root/component.h +++ b/repos/base/include/root/component.h @@ -130,8 +130,9 @@ class Genode::Root_component : public Rpc_object >, 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 >, { try { return _create(args, affinity); } + catch (Root::Quota_exceeded) { throw Service::Quota_exceeded(); } catch (...) { throw typename Local_service::Factory::Denied(); } } diff --git a/repos/base/include/trace_session/connection.h b/repos/base/include/trace_session/connection.h index e8d14b184..233b40cd3 100644 --- a/repos/base/include/trace_session/connection.h +++ b/repos/base/include/trace_session/connection.h @@ -35,7 +35,7 @@ struct Genode::Trace::Connection : Genode::Connection, { 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); } /** diff --git a/repos/base/src/core/main.cc b/repos/base/src/core/main.cc index 1ba2580ed..fa4d9724b 100644 --- a/repos/base/src/core/main.cc +++ b/repos/base/src/core/main.cc @@ -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; } }; diff --git a/repos/base/src/include/base/internal/expanding_parent_client.h b/repos/base/src/include/base/internal/expanding_parent_client.h index 6ef975c2f..c34751388 100644 --- a/repos/base/src/include/base/internal/expanding_parent_client.h +++ b/repos/base/src/include/base/internal/expanding_parent_client.h @@ -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( - [&] () { 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 diff --git a/repos/base/src/lib/base/child.cc b/repos/base/src/lib/base/child.cc index 4dc2052ea..d9351af9c 100644 --- a/repos/base/src/lib/base/child.cc +++ b/repos/base/src/lib/base/child.cc @@ -142,9 +142,15 @@ void Child::session_sigh(Signal_context_capability sigh) * needs asynchronous handling. */ _id_space.for_each([&] (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(); } diff --git a/repos/base/src/lib/base/component.cc b/repos/base/src/lib/base/component.cc index afc190547..0e0b7dff4 100644 --- a/repos/base/src/lib/base/component.cc +++ b/repos/base/src/lib/base/component.cc @@ -13,6 +13,7 @@ */ /* Genode includes */ +#include #include #include #include @@ -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([&] () { + + 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 diff --git a/repos/base/src/lib/base/root_proxy.cc b/repos/base/src/lib/base/root_proxy.cc index 23472ab72..78d485bd7 100644 --- a/repos/base/src/lib/base/root_proxy.cc +++ b/repos/base/src/lib/base/root_proxy.cc @@ -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); } } diff --git a/repos/base/src/lib/base/session_state.cc b/repos/base/src/lib/base/session_state.cc index 330335285..3d31e9111 100644 --- a/repos/base/src/lib/base/session_state.cc +++ b/repos/base/src/lib/base/session_state.cc @@ -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: diff --git a/repos/demo/include/launchpad/launchpad.h b/repos/demo/include/launchpad/launchpad.h index 619584784..f424b2065 100644 --- a/repos/demo/include/launchpad/launchpad.h +++ b/repos/demo/include/launchpad/launchpad.h @@ -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); diff --git a/repos/demo/src/lib/launchpad/launchpad.cc b/repos/demo/src/lib/launchpad/launchpad.cc index e3d4356a4..d1c7a1407 100644 --- a/repos/demo/src/lib/launchpad/launchpad.cc +++ b/repos/demo/src/lib/launchpad/launchpad.cc @@ -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); diff --git a/repos/hello_tutorial/include/hello_session/connection.h b/repos/hello_tutorial/include/hello_session/connection.h index dffb9a393..cce2b502f 100644 --- a/repos/hello_tutorial/include/hello_session/connection.h +++ b/repos/hello_tutorial/include/hello_session/connection.h @@ -26,7 +26,7 @@ struct Hello::Connection : Genode::Connection, Session_client : /* create session */ Genode::Connection(env, session(env.parent(), - "ram_quota=4K")), + "ram_quota=6K")), /* initialize RPC interface */ Session_client(cap()) { } diff --git a/repos/os/include/audio_in_session/connection.h b/repos/os/include/audio_in_session/connection.h index 3977be629..dbde2e0fb 100644 --- a/repos/os/include/audio_in_session/connection.h +++ b/repos/os/include/audio_in_session/connection.h @@ -31,7 +31,7 @@ struct Audio_in::Connection : Genode::Connection, Audio_in::Session_cli Capability _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); } /** diff --git a/repos/os/include/audio_out_session/connection.h b/repos/os/include/audio_out_session/connection.h index e12df105b..ca3a2b298 100644 --- a/repos/os/include/audio_out_session/connection.h +++ b/repos/os/include/audio_out_session/connection.h @@ -31,7 +31,7 @@ struct Audio_out::Connection : Genode::Connection, Audio_out::Session_c Capability _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); } /** diff --git a/repos/os/include/block_session/connection.h b/repos/os/include/block_session/connection.h index 2713e2ae2..d82196894 100644 --- a/repos/os/include/block_session/connection.h +++ b/repos/os/include/block_session/connection.h @@ -31,7 +31,7 @@ struct Block::Connection : Genode::Connection, 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); } /** diff --git a/repos/os/include/init/child.h b/repos/os/include/init/child.h index 1dc75e7b3..d9639207f 100644 --- a/repos/os/include/init/child.h +++ b/repos/os/include/init/child.h @@ -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_services, Registry &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); }); } /** diff --git a/repos/os/include/input_session/connection.h b/repos/os/include/input_session/connection.h index c395f593c..d7982d764 100644 --- a/repos/os/include/input_session/connection.h +++ b/repos/os/include/input_session/connection.h @@ -27,7 +27,7 @@ struct Input::Connection : Genode::Connection, Session_client * \noapi */ Capability _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_client Connection() __attribute__((deprecated)) : Genode::Connection( - session(*Genode::env_deprecated()->parent(), "ram_quota=16K")), + session(*Genode::env_deprecated()->parent(), "ram_quota=18K")), Session_client(*Genode::env_deprecated()->rm_session(), cap()) { } }; diff --git a/repos/os/include/os/child_policy_dynamic_rom.h b/repos/os/include/os/child_policy_dynamic_rom.h index 7790a6232..c957236b5 100644 --- a/repos/os/include/os/child_policy_dynamic_rom.h +++ b/repos/os/include/os/child_policy_dynamic_rom.h @@ -194,6 +194,7 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object, 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: diff --git a/repos/os/include/os/slave.h b/repos/os/include/os/slave.h index 29662a2fe..a48181eac 100644 --- a/repos/os/include/os/slave.h +++ b/repos/os/include/os/slave.h @@ -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: diff --git a/repos/os/include/platform_session/connection.h b/repos/os/include/platform_session/connection.h index f02780806..ab7967d80 100644 --- a/repos/os/include/platform_session/connection.h +++ b/repos/os/include/platform_session/connection.h @@ -27,7 +27,7 @@ struct Platform::Connection : Genode::Connection, Client * Constructor */ Connection(Genode::Env &env) - : Genode::Connection(env, session(env.parent(), "ram_quota=4K")), + : Genode::Connection(env, session(env.parent(), "ram_quota=6K")), Client(cap()) { } /** @@ -38,7 +38,7 @@ struct Platform::Connection : Genode::Connection, Client * argument instead */ Connection() __attribute__((deprecated)) - : Genode::Connection(session("ram_quota=4K")), + : Genode::Connection(session("ram_quota=6K")), Client(cap()) { } }; diff --git a/repos/os/include/report_session/connection.h b/repos/os/include/report_session/connection.h index 1498a012e..58b519855 100644 --- a/repos/os/include/report_session/connection.h +++ b/repos/os/include/report_session/connection.h @@ -22,7 +22,7 @@ namespace Report { struct Connection; } struct Report::Connection : Genode::Connection, 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_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); } /** diff --git a/repos/os/include/rtc_session/connection.h b/repos/os/include/rtc_session/connection.h index 4836f0cd8..6c1791ff4 100644 --- a/repos/os/include/rtc_session/connection.h +++ b/repos/os/include/rtc_session/connection.h @@ -28,7 +28,7 @@ struct Rtc::Connection : Genode::Connection, Session_client */ Connection(Genode::Env &env) : - Genode::Connection(env, session(env.parent(), "ram_quota=4K")), + Genode::Connection(env, session(env.parent(), "ram_quota=8K")), Session_client(cap()) { } @@ -41,7 +41,7 @@ struct Rtc::Connection : Genode::Connection, Session_client */ Connection() __attribute__((deprecated)) : - Genode::Connection(session("ram_quota=4K")), + Genode::Connection(session("ram_quota=8K")), Session_client(cap()) { } }; diff --git a/repos/os/include/terminal_session/connection.h b/repos/os/include/terminal_session/connection.h index dd682e55e..555ac13e1 100644 --- a/repos/os/include/terminal_session/connection.h +++ b/repos/os/include/terminal_session/connection.h @@ -51,7 +51,7 @@ struct Terminal::Connection : Genode::Connection, Session_client : Genode::Connection(env, session(env.parent(), "ram_quota=%ld, label=\"%s\"", - 2*4096, label)), + 10*1024, label)), Session_client(env.rm(), cap()) { wait_for_connection(cap()); diff --git a/repos/os/include/timer_session/connection.h b/repos/os/include/timer_session/connection.h index e7a81f3b4..b3aad1df8 100644 --- a/repos/os/include/timer_session/connection.h +++ b/repos/os/include/timer_session/connection.h @@ -40,7 +40,7 @@ class Timer::Connection : public Genode::Connection, public Session_cli */ Connection(Genode::Env &env, char const *label = "") : - Genode::Connection(env, session(env.parent(), "ram_quota=8K, label=\"%s\"", label)), + Genode::Connection(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, public Session_cli */ Connection() __attribute__((deprecated)) : - Genode::Connection(session("ram_quota=8K")), + Genode::Connection(session("ram_quota=10K")), Session_client(cap()) { /* register default signal handler */ diff --git a/repos/os/run/report_rom.run b/repos/os/run/report_rom.run index 039bb4230..871d4cf30 100644 --- a/repos/os/run/report_rom.run +++ b/repos/os/run/report_rom.run @@ -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 --- } diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc index 985462415..7f19d54b4 100644 --- a/repos/os/src/init/main.cc +++ b/repos/os/src/init/main.cc @@ -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,