diff --git a/repos/base-linux/src/include/base/internal/local_parent.h b/repos/base-linux/src/include/base/internal/local_parent.h index 1923b47272..883a212edc 100644 --- a/repos/base-linux/src/include/base/internal/local_parent.h +++ b/repos/base-linux/src/include/base/internal/local_parent.h @@ -55,8 +55,8 @@ class Genode::Local_parent : public Expanding_parent_client ** Parent interface ** **********************/ - Session_capability session(Client::Id, Service_name const &, Session_args const &, - Affinity const & = Affinity()) override; + Session_result session(Client::Id, Service_name const &, Session_args const &, + Affinity const & = Affinity()) override; Close_result close(Client::Id) override; /** diff --git a/repos/base-linux/src/include/base/internal/platform.h b/repos/base-linux/src/include/base/internal/platform.h index 4aa569ba94..d48dfad17b 100644 --- a/repos/base-linux/src/include/base/internal/platform.h +++ b/repos/base-linux/src/include/base/internal/platform.h @@ -40,11 +40,15 @@ struct Genode::Platform Local_parent parent { _obtain_parent_cap(), rm, heap }; - Pd_session_capability pd_cap = - static_cap_cast(parent.session_cap(Parent::Env::pd())); + Capability pd_cap = + parent.session_cap(Parent::Env::pd()).convert>( + [&] (Capability cap) { return static_cap_cast(cap); }, + [&] (Parent::Session_cap_error) { return Capability(); }); - Cpu_session_capability cpu_cap = - static_cap_cast(parent.session_cap(Parent::Env::cpu())); + Capability cpu_cap = + parent.session_cap(Parent::Env::cpu()).convert>( + [&] (Capability cap) { return static_cap_cast(cap); }, + [&] (Parent::Session_cap_error) { return Capability(); }); Local_pd_session pd { parent, pd_cap }; diff --git a/repos/base-linux/src/lib/base/platform.cc b/repos/base-linux/src/lib/base/platform.cc index 7d08b6b469..53b1da83b3 100644 --- a/repos/base-linux/src/lib/base/platform.cc +++ b/repos/base-linux/src/lib/base/platform.cc @@ -58,10 +58,10 @@ bool Region_map_mmap::_dataspace_writeable(Dataspace_capability ds) ** Local_parent ** ******************/ -Session_capability Local_parent::session(Parent::Client::Id id, - Service_name const &service_name, - Session_args const &args, - Affinity const &affinity) +Parent::Session_result Local_parent::session(Parent::Client::Id id, + Service_name const &service_name, + Session_args const &args, + Affinity const &affinity) { if (strcmp(service_name.string(), Rm_session::service_name()) == 0) { @@ -87,7 +87,7 @@ Parent::Close_result Local_parent::close(Client::Id id) Capability rm = static_cap_cast(local_session.local_session_cap()); destroy(_alloc, Local_capability::deref(rm)); - return CLOSE_DONE; + return Parent::Close_result::DONE; }, [&] /* missing */ { return Parent_client::close(id); }); diff --git a/repos/base/include/base/child.h b/repos/base/include/base/child.h index 8a99561f05..a7cc559198 100644 --- a/repos/base/include/base/child.h +++ b/repos/base/include/base/child.h @@ -853,9 +853,9 @@ class Genode::Child : protected Rpc_object, void announce(Service_name const &) override; void session_sigh(Signal_context_capability) override; - Session_capability session(Client::Id, Service_name const &, - Session_args const &, Affinity const &) override; - Session_capability session_cap(Client::Id) override; + Session_result session(Client::Id, Service_name const &, + Session_args const &, Affinity const &) override; + Session_cap_result session_cap(Client::Id) override; Upgrade_result upgrade(Client::Id, Upgrade_args const &) override; Close_result close(Client::Id) override; void exit(int) override; diff --git a/repos/base/include/parent/client.h b/repos/base/include/parent/client.h index 5a6fc949c3..e160c4b938 100644 --- a/repos/base/include/parent/client.h +++ b/repos/base/include/parent/client.h @@ -33,13 +33,13 @@ struct Genode::Parent_client : Rpc_client void session_sigh(Signal_context_capability sigh) override { call(sigh); } - Session_capability session(Client::Id id, - Service_name const &service, - Session_args const &args, - Affinity const &affinity) override { + Session_result session(Client::Id id, + Service_name const &service, + Session_args const &args, + Affinity const &affinity) override { return call(id, service, args, affinity); } - Session_capability session_cap(Client::Id id) override { + Session_cap_result session_cap(Client::Id id) override { return call(id); } Upgrade_result upgrade(Client::Id to_session, Upgrade_args const &args) override { diff --git a/repos/base/include/parent/parent.h b/repos/base/include/parent/parent.h index c2e885e4f2..99f783c54c 100644 --- a/repos/base/include/parent/parent.h +++ b/repos/base/include/parent/parent.h @@ -14,7 +14,7 @@ #ifndef _INCLUDE__PARENT__PARENT_H_ #define _INCLUDE__PARENT__PARENT_H_ -#include +#include #include #include #include @@ -143,6 +143,16 @@ class Genode::Parent */ virtual void session_sigh(Signal_context_capability) = 0; + enum class Session_error { + OUT_OF_RAM, /* session RAM quota exceeds our resources */ + OUT_OF_CAPS, /* session CAP quota exceeds our resources */ + INSUFFICIENT_RAM_QUOTA, /* RAM donation does not suffice */ + INSUFFICIENT_CAP_QUOTA, /* CAP donation does not suffice */ + DENIED, /* parent or server denies request */ + }; + + using Session_result = Attempt, Session_error>; + /** * Create session to a service * @@ -151,39 +161,35 @@ class Genode::Parent * \param args session constructor arguments * \param affinity preferred CPU affinity for the session * - * \throw Service_denied parent denies session request - * \throw Insufficient_cap_quota donated cap quota does not suffice - * \throw Insufficient_ram_quota donated RAM quota does not suffice - * \throw Out_of_caps session CAP quota exceeds our resources - * \throw Out_of_ram session RAM quota exceeds our resources - * * \return session capability if the new session is immediately - * available, or an invalid capability + * available, or an invalid capability, or an error of + * type 'Session_error'. * * If the returned capability is invalid, the request is pending at the * server. The parent delivers a signal to the handler as registered * via 'session_sigh' once the server responded to the request. Now the * session capability can be picked up by calling 'session_cap'. */ - virtual Session_capability session(Client::Id id, - Service_name const &service_name, - Session_args const &args, - Affinity const &affinity = Affinity()) = 0; + virtual Session_result session(Client::Id id, + Service_name const &service_name, + Session_args const &args, + Affinity const &affinity = Affinity()) = 0; + + enum class Session_cap_error { INSUFFICIENT_RAM_QUOTA, + INSUFFICIENT_CAP_QUOTA, DENIED, }; + + using Session_cap_result = Attempt, Session_cap_error>; /** * Request session capability * - * \throw Service_denied - * \throw Insufficient_cap_quota - * \throw Insufficient_ram_quota - * * See 'session' for more documentation. * - * In the exception case, the parent implicitly closes the session. + * In the error case, the parent implicitly closes the session. */ - virtual Session_capability session_cap(Client::Id id) = 0; + virtual Session_cap_result session_cap(Client::Id id) = 0; - enum Upgrade_result { UPGRADE_DONE, UPGRADE_PENDING }; + enum class Upgrade_result { OK, PENDING, OUT_OF_RAM, OUT_OF_CAPS }; /** * Transfer our quota to the server that provides the specified session @@ -191,16 +197,13 @@ class Genode::Parent * \param id ID of recipient session * \param args description of the amount of quota to transfer * - * \throw Out_of_caps - * \throw Out_of_ram - * * The 'args' argument has the same principle format as the 'args' * argument of the 'session' operation. */ virtual Upgrade_result upgrade(Client::Id to_session, Upgrade_args const &args) = 0; - enum [[nodiscard]] Close_result { CLOSE_DONE, CLOSE_PENDING }; + enum class [[nodiscard]] Close_result { DONE, PENDING }; /** * Close session @@ -308,19 +311,11 @@ class Genode::Parent GENODE_RPC(Rpc_announce, void, announce, Service_name const &); GENODE_RPC(Rpc_session_sigh, void, session_sigh, Signal_context_capability); - GENODE_RPC_THROW(Rpc_session, Session_capability, session, - GENODE_TYPE_LIST(Service_denied, Out_of_caps, - Out_of_ram, Insufficient_cap_quota, - Insufficient_ram_quota), - Client::Id, Service_name const &, Session_args const &, - Affinity const &); - GENODE_RPC_THROW(Rpc_session_cap, Session_capability, session_cap, - GENODE_TYPE_LIST(Service_denied, Insufficient_cap_quota, - Insufficient_ram_quota), - Client::Id); - GENODE_RPC_THROW(Rpc_upgrade, Upgrade_result, upgrade, - GENODE_TYPE_LIST(Out_of_ram, Out_of_caps), - Client::Id, Upgrade_args const &); + GENODE_RPC(Rpc_session, Session_result, session, + Client::Id, Service_name const &, Session_args const &, + Affinity const &); + GENODE_RPC(Rpc_session_cap, Session_cap_result, session_cap, Client::Id); + GENODE_RPC(Rpc_upgrade, Upgrade_result, upgrade, Client::Id, Upgrade_args const &); GENODE_RPC(Rpc_close, Close_result, close, Client::Id); GENODE_RPC(Rpc_session_response, void, session_response, Server::Id, Session_response); 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 cc1bf9eb1b..af2fe56713 100644 --- a/repos/base/src/include/base/internal/expanding_parent_client.h +++ b/repos/base/src/include/base/internal/expanding_parent_client.h @@ -113,10 +113,10 @@ class Genode::Expanding_parent_client : public Parent_client } } - Session_capability session(Client::Id id, - Service_name const &name, - Session_args const &args, - Affinity const &affinity) override + Session_result session(Client::Id id, + Service_name const &name, + Session_args const &args, + Affinity const &affinity) override { return Parent_client::session(id, name, args, affinity); } @@ -130,39 +130,28 @@ class Genode::Expanding_parent_client : public Parent_client */ if (id == Env::pd()) { resource_request(Resource_args(args.string())); - return UPGRADE_DONE; + return Upgrade_result::OK; } /* - * 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 - * 'Out_of_ram' or 'Out_of_caps' exception. Now, it is the job of - * the caller to issue (and respond to) a resource request. + * If the upgrade fails, attempt to issue a resource request */ Session::Resources const amount = session_resources_from_args(args.string()); using Arg = String<64>; - return retry( - [&] { - return retry( - [&] { return Parent_client::upgrade(id, args); }, - [&] { - Arg cap_arg("cap_quota=", amount.cap_quota); - resource_request(Resource_args(cap_arg.string())); - }); - }, - [&] { + for (;;) { + Upgrade_result const result = Parent_client::upgrade(id, args); + if (result == Upgrade_result::OUT_OF_RAM) { Arg ram_arg("ram_quota=", amount.ram_quota); resource_request(Resource_args(ram_arg.string())); - }); + } + else if (result == Upgrade_result::OUT_OF_CAPS) { + Arg cap_arg("cap_quota=", amount.cap_quota); + resource_request(Resource_args(cap_arg.string())); + } + else + return result; + } } void resource_avail_sigh(Signal_context_capability sigh) override diff --git a/repos/base/src/include/base/internal/platform.h b/repos/base/src/include/base/internal/platform.h index 264284109e..8c94f3b825 100644 --- a/repos/base/src/include/base/internal/platform.h +++ b/repos/base/src/include/base/internal/platform.h @@ -33,7 +33,9 @@ struct Genode::Platform : Noncopyable template Capability _request(Parent::Client::Id id) { - return static_cap_cast(parent.session_cap(id)); + return parent.session_cap(id).convert>( + [&] (Capability cap) { return static_cap_cast(cap); }, + [&] (Parent::Session_cap_error) { return Capability(); }); } Expanding_pd_session_client pd { diff --git a/repos/base/src/lib/base/child.cc b/repos/base/src/lib/base/child.cc index 06dcc1b089..8f6fc60d7e 100644 --- a/repos/base/src/lib/base/child.cc +++ b/repos/base/src/lib/base/child.cc @@ -92,34 +92,26 @@ void Child::session_sigh(Signal_context_capability sigh) /** * Create session-state object for a dynamically created session * - * \throw Out_of_ram - * \throw Out_of_caps - * \throw Insufficient_cap_quota - * \throw Insufficient_ram_quota - * \throw Service_denied + * If successful, 'fn' is called with a new 'Session_state &' as argument. */ -Session_state & -create_session(Child_policy::Name const &child_name, Service &service, - Session_label const &label, Session::Diag diag, - Session_state::Factory &factory, Id_space &id_space, - Parent::Client::Id id, Session_state::Args const &args, - Affinity const &affinity) +Parent::Session_result +with_new_session(Child_policy::Name const &child_name, Service &service, + Session_label const &label, Session::Diag diag, + Session_state::Factory &factory, Id_space &id_space, + Parent::Client::Id id, Session_state::Args const &args, + Affinity const &affinity, + auto const &fn) { + using Error = Parent::Session_error; + + Error session_error = Error::DENIED; try { - return service.create_session(factory, id_space, id, label, diag, args, affinity); } - - catch (Insufficient_ram_quota) { - error(child_name, " requested session with insufficient RAM quota"); - throw; } - - catch (Insufficient_cap_quota) { - error(child_name, " requested session with insufficient cap quota"); - throw; } - - catch (Allocator::Out_of_memory) { - error(child_name, " session meta data could not be allocated"); - throw Out_of_ram(); } - + return fn(service.create_session(factory, id_space, id, label, diag, args, affinity)); + } + catch (Insufficient_ram_quota) { session_error = Error::INSUFFICIENT_RAM_QUOTA; } + catch (Insufficient_cap_quota) { session_error = Error::INSUFFICIENT_CAP_QUOTA; } + catch (Out_of_ram) { session_error = Error::OUT_OF_RAM; } + catch (Out_of_caps) { session_error = Error::OUT_OF_CAPS; } catch (Id_space::Conflicting_id) { error(child_name, " requested conflicting session ID ", id, " " @@ -129,17 +121,27 @@ create_session(Child_policy::Name const &child_name, Service &service, [&] (Session_state &session) { error("existing session: ", session); }, [&] /* missing */ { }); } - throw Service_denied(); + + if (session_error == Error::OUT_OF_RAM || session_error == Error::OUT_OF_CAPS) + error(child_name, " session meta data could not be allocated"); + + if (session_error == Error::INSUFFICIENT_RAM_QUOTA) + error(child_name, " requested session with insufficient RAM quota"); + + if (session_error == Error::INSUFFICIENT_CAP_QUOTA) + error(child_name, " requested session with insufficient cap quota"); + + return session_error; } -Session_capability Child::session(Parent::Client::Id id, - Parent::Service_name const &name, - Parent::Session_args const &args, - Affinity const &affinity) +Parent::Session_result Child::session(Parent::Client::Id id, + Parent::Service_name const &name, + Parent::Session_args const &args, + Affinity const &affinity) { if (!name.valid_string() || !args.valid_string() || _pd.closed()) - throw Service_denied(); + return Session_error::DENIED; char argbuf[Parent::Session_args::MAX_SIZE]; @@ -163,7 +165,7 @@ Session_capability Child::session(Parent::Client::Id id, size_t const keep_ram_quota = _session_factory.session_costs(); if (ram_quota.value < keep_ram_quota) - throw Insufficient_ram_quota(); + return Session_error::INSUFFICIENT_RAM_QUOTA; /* ram quota to be forwarded to the server */ Ram_quota const forward_ram_quota { ram_quota.value - keep_ram_quota }; @@ -171,100 +173,106 @@ Session_capability Child::session(Parent::Client::Id id, /* adjust the session information as presented to the server */ Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", (int)forward_ram_quota.value); - /* may throw a 'Service_denied' exception */ - Child_policy::Route route = - _policy.resolve_session_request(name.string(), label, - session_diag_from_args(argbuf)); - - Service &service = route.service; - - /* propagate diag flag */ - Arg_string::set_arg(argbuf, sizeof(argbuf), "diag", route.diag.enabled); - - Session_state &session = - create_session(_policy.name(), service, route.label, route.diag, - _session_factory, _id_space, id, argbuf, filtered_affinity); - - _policy.session_state_changed(); - - session.ready_callback = this; - session.closed_callback = this; - try { - Ram_transfer::Remote_account ref_ram_account { _policy.ref_pd(), _policy.ref_pd_cap() }; - Cap_transfer::Remote_account ref_cap_account { _policy.ref_pd(), _policy.ref_pd_cap() }; + /* may throw a 'Service_denied' exception */ + Child_policy::Route route = + _policy.resolve_session_request(name.string(), label, + session_diag_from_args(argbuf)); - Ram_transfer::Remote_account ram_account { pd(), pd_session_cap() }; - Cap_transfer::Remote_account cap_account { pd(), pd_session_cap() }; + Service &service = route.service; - /* transfer the quota donation from the child's account to ourself */ - Ram_transfer ram_donation_from_child(ram_quota, ram_account, ref_ram_account); - Cap_transfer cap_donation_from_child(cap_quota, cap_account, ref_cap_account); + /* propagate diag flag */ + Arg_string::set_arg(argbuf, sizeof(argbuf), "diag", route.diag.enabled); - /* transfer session quota from ourself to the service provider */ - Ram_transfer ram_donation_to_service(forward_ram_quota, ref_ram_account, service); - Cap_transfer cap_donation_to_service(cap_quota, ref_cap_account, service); + return with_new_session(_policy.name(), service, route.label, route.diag, + _session_factory, _id_space, id, argbuf, filtered_affinity, - /* finish transaction */ - ram_donation_from_child.acknowledge(); - cap_donation_from_child.acknowledge(); - ram_donation_to_service.acknowledge(); - cap_donation_to_service.acknowledge(); + [&] (Session_state &session) -> Session_result { + + _policy.session_state_changed(); + + session.ready_callback = this; + session.closed_callback = this; + + try { + Ram_transfer::Remote_account ref_ram_account { _policy.ref_pd(), _policy.ref_pd_cap() }; + Cap_transfer::Remote_account ref_cap_account { _policy.ref_pd(), _policy.ref_pd_cap() }; + + Ram_transfer::Remote_account ram_account { pd(), pd_session_cap() }; + Cap_transfer::Remote_account cap_account { pd(), pd_session_cap() }; + + /* transfer the quota donation from the child's account to ourself */ + Ram_transfer ram_donation_from_child(ram_quota, ram_account, ref_ram_account); + Cap_transfer cap_donation_from_child(cap_quota, cap_account, ref_cap_account); + + /* transfer session quota from ourself to the service provider */ + Ram_transfer ram_donation_to_service(forward_ram_quota, ref_ram_account, service); + Cap_transfer cap_donation_to_service(cap_quota, ref_cap_account, service); + + /* finish transaction */ + ram_donation_from_child.acknowledge(); + cap_donation_from_child.acknowledge(); + ram_donation_to_service.acknowledge(); + cap_donation_to_service.acknowledge(); + } + /* + * Release session meta data if one of the quota transfers went wrong. + */ + catch (Ram_transfer::Quota_exceeded) { + session.destroy(); + return Session_error::OUT_OF_RAM; + } + catch (Cap_transfer::Quota_exceeded) { + session.destroy(); + return Session_error::OUT_OF_CAPS; + } + + /* try to dispatch session request synchronously */ + service.initiate_request(session); + + if (session.phase == Session_state::SERVICE_DENIED) { + _revert_quota_and_destroy(session); + return Session_error::DENIED; + } + + if (session.phase == Session_state::INSUFFICIENT_RAM_QUOTA) { + _revert_quota_and_destroy(session); + return Session_error::INSUFFICIENT_RAM_QUOTA; + } + + if (session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) { + _revert_quota_and_destroy(session); + return Session_error::INSUFFICIENT_CAP_QUOTA; + } + + /* + * Copy out the session cap before we are potentially kicking off the + * asynchonous request handling at the server to avoid doule-read + * issues with the session.cap, which will be asynchronously assigned + * by the server side. + */ + Session_capability cap = session.cap; + + /* if request was not handled synchronously, kick off async operation */ + if (session.phase == Session_state::CREATE_REQUESTED) + service.wakeup(); + + if (cap.valid()) + session.phase = Session_state::CAP_HANDED_OUT; + + return cap; + } + ); } - /* - * Release session meta data if one of the quota transfers went wrong. - */ - catch (Ram_transfer::Quota_exceeded) { - session.destroy(); - throw Out_of_ram(); - } - catch (Cap_transfer::Quota_exceeded) { - session.destroy(); - throw Out_of_caps(); - } - - /* try to dispatch session request synchronously */ - service.initiate_request(session); - - if (session.phase == Session_state::SERVICE_DENIED) { - _revert_quota_and_destroy(session); - throw Service_denied(); - } - - if (session.phase == Session_state::INSUFFICIENT_RAM_QUOTA) { - _revert_quota_and_destroy(session); - throw Insufficient_ram_quota(); - } - - if (session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) { - _revert_quota_and_destroy(session); - throw Insufficient_cap_quota(); - } - - /* - * Copy out the session cap before we are potentially kicking off the - * asynchonous request handling at the server to avoid doule-read - * issues with the session.cap, which will be asynchronously assigned - * by the server side. - */ - Session_capability cap = session.cap; - - /* if request was not handled synchronously, kick off async operation */ - if (session.phase == Session_state::CREATE_REQUESTED) - service.wakeup(); - - if (cap.valid()) - session.phase = Session_state::CAP_HANDED_OUT; - - return cap; + catch (Service_denied) { return Session_error::DENIED; } } -Session_capability Child::session_cap(Client::Id id) +Parent::Session_cap_result Child::session_cap(Client::Id id) { return _id_space.apply(id, - [&] (Session_state &session) -> Session_capability { + [&] (Session_state &session) -> Session_cap_result { if (session.phase == Session_state::SERVICE_DENIED || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA @@ -280,9 +288,9 @@ Session_capability Child::session_cap(Client::Id id) _revert_quota_and_destroy(session); switch (phase) { - case Session_state::SERVICE_DENIED: throw Service_denied(); - case Session_state::INSUFFICIENT_RAM_QUOTA: throw Insufficient_ram_quota(); - case Session_state::INSUFFICIENT_CAP_QUOTA: throw Insufficient_cap_quota(); + case Session_state::SERVICE_DENIED: return Session_cap_error::DENIED; + case Session_state::INSUFFICIENT_RAM_QUOTA: return Session_cap_error::INSUFFICIENT_RAM_QUOTA; + case Session_state::INSUFFICIENT_CAP_QUOTA: return Session_cap_error::INSUFFICIENT_CAP_QUOTA; default: break; } } @@ -309,14 +317,16 @@ Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const { if (!args.valid_string()) { warning("no valid session-upgrade arguments"); - return UPGRADE_DONE; + return Upgrade_result::OK; } /* ignore suprious request that may arrive after 'close_all_sessions' */ if (_pd.closed()) - return UPGRADE_PENDING; + return Upgrade_result::PENDING; - Upgrade_result result = UPGRADE_PENDING; + Upgrade_result result = Upgrade_result::PENDING; + + bool session_state_changed = false; auto upgrade_session = [&] (Session_state &session) { @@ -350,6 +360,7 @@ Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const session.phase = Session_state::UPGRADE_REQUESTED; session.service().initiate_request(session); + session_state_changed = true; /* finish transaction */ ram_donation_from_child.acknowledge(); @@ -359,25 +370,27 @@ Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const } catch (Ram_transfer::Quota_exceeded) { warning(_policy.name(), ": RAM upgrade of ", session.service().name(), " failed"); - throw Out_of_ram(); + result = Upgrade_result::OUT_OF_RAM; + return; } catch (Cap_transfer::Quota_exceeded) { warning(_policy.name(), ": cap upgrade of ", session.service().name(), " failed"); - throw Out_of_caps(); + result = Upgrade_result::OUT_OF_CAPS; + return; } - if (session.phase == Session_state::CAP_HANDED_OUT) { - result = UPGRADE_DONE; + result = Upgrade_result::OK; _policy.session_state_changed(); return; } - session.service().wakeup(); }; _id_space.apply(id, upgrade_session, [&] /* missing */ { }); - _policy.session_state_changed(); + if (session_state_changed) + _policy.session_state_changed(); + return result; } @@ -440,7 +453,7 @@ Child::Close_result Child::_close(Session_state &session) || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA || session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) { _revert_quota_and_destroy(session); - return CLOSE_DONE; + return Close_result::DONE; } /* close session if alive */ @@ -457,14 +470,14 @@ Child::Close_result Child::_close(Session_state &session) if (session.phase == Session_state::CLOSED) { _revert_quota_and_destroy(session); - return CLOSE_DONE; + return Close_result::DONE; } _policy.session_state_changed(); session.service().wakeup(); - return CLOSE_PENDING; + return Close_result::PENDING; } @@ -472,11 +485,11 @@ Child::Close_result Child::close(Client::Id id) { /* refuse to close the child's initial sessions */ if (Parent::Env::session_id(id)) - return CLOSE_DONE; + return Close_result::DONE; return _id_space.apply(id, [&] (Session_state &session) -> Close_result { return _close(session); }, - [&] /* missing */ { return CLOSE_DONE; }); + [&] /* missing */ { return Close_result::DONE; }); } @@ -832,7 +845,7 @@ void Child::close_all_sessions() Close_result const close_result = close(Parent::Client::Id{id_value}); /* break infinte loop if CPU session is provided by a child */ - if (close_result != CLOSE_DONE) + if (close_result != Close_result::DONE) break; } @@ -887,7 +900,7 @@ void Child::close_all_sessions() Close_result const close_result = _close(session); - if (close_result == CLOSE_PENDING) + if (close_result == Close_result::PENDING) session.discard_id_at_client(); }; diff --git a/repos/base/src/lib/base/component.cc b/repos/base/src/lib/base/component.cc index ba4a132460..530df9ae3e 100644 --- a/repos/base/src/lib/base/component.cc +++ b/repos/base/src/lib/base/component.cc @@ -119,13 +119,36 @@ struct Genode::Component_env : Env Mutex::Guard guard(_mutex); - Session_capability cap = _parent.session(id, name, args, affinity); + return _parent.session(id, name, args, affinity).convert>( + [&] (Capability cap) -> Capability { + if (cap.valid()) + return cap; - if (cap.valid()) - return cap; + _block_for_session(); - _block_for_session(); - return _parent.session_cap(id); + return _parent.session_cap(id).convert>( + [&] (Capability cap) { return cap; }, + [&] (Parent::Session_cap_error const e) -> Capability { + using Error = Parent::Session_cap_error; + switch (e) { + case Error::INSUFFICIENT_RAM_QUOTA: throw Insufficient_ram_quota(); + case Error::INSUFFICIENT_CAP_QUOTA: throw Insufficient_cap_quota(); + case Error::DENIED: break; + } + throw Service_denied(); } + ); + }, + [&] (Parent::Session_error const e) -> Capability { + using Error = Parent::Session_error; + switch (e) { + case Error::OUT_OF_RAM: throw Out_of_ram(); + case Error::OUT_OF_CAPS: throw Out_of_caps(); + case Error::INSUFFICIENT_RAM_QUOTA: throw Insufficient_ram_quota(); + case Error::INSUFFICIENT_CAP_QUOTA: throw Insufficient_cap_quota(); + case Error::DENIED: break; + } + throw Service_denied(); + }); } Session_capability session(Parent::Service_name const &name, @@ -194,7 +217,7 @@ struct Genode::Component_env : Env { Mutex::Guard guard(_mutex); - if (_parent.upgrade(id, args) == Parent::UPGRADE_PENDING) + if (_parent.upgrade(id, args) == Parent::Upgrade_result::PENDING) _block_for_session(); } @@ -202,7 +225,7 @@ struct Genode::Component_env : Env { Mutex::Guard guard(_mutex); - if (_parent.close(id) == Parent::CLOSE_PENDING) + if (_parent.close(id) == Parent::Close_result::PENDING) _block_for_session(); } diff --git a/repos/base/src/lib/base/default_log.cc b/repos/base/src/lib/base/default_log.cc index 5cde155e02..ab84657471 100644 --- a/repos/base/src/lib/base/default_log.cc +++ b/repos/base/src/lib/base/default_log.cc @@ -34,8 +34,12 @@ namespace { { Log_session_client _client; - static Session_capability _cap(Parent &parent) { - return parent.session_cap(Parent::Env::log()); } + static Session_capability _cap(Parent &parent) + { + return parent.session_cap(Parent::Env::log()).convert>( + [&] (Capability cap) { return cap; }, + [&] (Parent::Session_cap_error) { return Capability(); }); + } Back_end(Parent &parent) : _client(reinterpret_cap_cast(_cap(parent))) { } diff --git a/repos/base/src/lib/ldso/include/file.h b/repos/base/src/lib/ldso/include/file.h index b57669217f..d9bab80e37 100644 --- a/repos/base/src/lib/ldso/include/file.h +++ b/repos/base/src/lib/ldso/include/file.h @@ -102,18 +102,22 @@ struct Linker::Elf_file : File Rom_dataspace_capability _rom_dataspace(Name const &name) { /* request the linker and binary from the component environment */ - Session_capability cap; + Parent::Session_cap_result cap = Parent::Session_cap_error::DENIED; if (name == binary_name()) cap = env.parent().session_cap(Parent::Env::binary()); if (name == linker_name()) cap = env.parent().session_cap(Parent::Env::linker()); - if (cap.valid()) { - Rom_session_client client(reinterpret_cap_cast(cap)); - return client.dataspace(); - } - rom_connection.construct(env, name.string()); - return rom_connection->dataspace(); + return cap.convert( + [&] (Capability cap) { + Rom_session_client client(reinterpret_cap_cast(cap)); + return client.dataspace(); + }, + [&] (Parent::Session_cap_error) { + rom_connection.construct(env, name.string()); + return rom_connection->dataspace(); + } + ); } void _allocate_region_within_linker_area(Name const &name) diff --git a/repos/os/src/lib/vfs/log_file_system.h b/repos/os/src/lib/vfs/log_file_system.h index 6cb6cfc311..9a394d0e85 100644 --- a/repos/os/src/lib/vfs/log_file_system.h +++ b/repos/os/src/lib/vfs/log_file_system.h @@ -33,15 +33,21 @@ class Vfs::Log_file_system : public Single_file_system Genode::Log_session & _log_session(Genode::Env &env) { + using namespace Genode; + if (_label.valid()) { _log_connection.construct(env, _label); return *_log_connection; - } else { - _log_client.construct( - Genode::reinterpret_cap_cast - (env.parent().session_cap(Genode::Parent::Env::log()))); - return *_log_client; } + + _log_client.construct( + env.parent().session_cap(Parent::Env::log()) + .convert>( + [&] (Capability cap) { + return static_cap_cast(cap); }, + [&] (Parent::Session_cap_error) { + return Capability(); })); + return *_log_client; } Genode::Log_session &_log;