sandbox: allow for customized PD access

By default, the sandbox uses the Env::pd() as reference PD session of
the sandbox children.

However, to accomodate use cases where the interplay of the reference
PD session and the child's address space needs to be intercepted, this
patch adds a constructor that takes an interface for the controlled
access of PD intrinsics as argument.

Issue #4917
This commit is contained in:
Norman Feske 2023-05-09 11:34:51 +02:00
parent 30b70da6c1
commit f2153f9b2f
5 changed files with 155 additions and 21 deletions

View File

@ -44,6 +44,30 @@ class Genode::Sandbox : Noncopyable
virtual void handle_sandbox_state() = 0;
};
/**
* Interface for accessing the PD intrinsics relevant to differentiate
* the regular use of core's PD service from a locally implemented PD
* service.
*/
struct Pd_intrinsics : Interface, Noncopyable
{
struct Intrinsics
{
Pd_session &ref_pd;
Capability<Pd_session> ref_pd_cap;
Cpu_session &ref_cpu;
Capability<Cpu_session> ref_cpu_cap;
Region_map &address_space;
};
struct Fn : Interface { virtual void call(Intrinsics &) const = 0; };
/**
* Call 'Fn' with the 'Intrinsics' that apply for the specified PD
*/
virtual void with_intrinsics(Capability<Pd_session>, Pd_session &, Fn const &) = 0;
};
private:
friend class Local_service_base;
@ -60,6 +84,15 @@ class Genode::Sandbox : Noncopyable
Sandbox(Env &, State_handler &);
/**
* Constructor designated for monitoring PD-session operations
*
* The 'Pd_intrinsics' argument allows for the customization of the
* reference PD session used for quota transfers between the sandboxed
* children and the local runtime.
*/
Sandbox(Env &, State_handler &, Pd_intrinsics &);
void apply_config(Xml_node const &);
/**
@ -196,6 +229,7 @@ struct Genode::Sandbox::Local_service : private Local_service_base
Local_service(Sandbox &sandbox, Wakeup &wakeup)
: Local_service_base(sandbox, ST::service_name(), wakeup) { }
using Local_session = ST;
using Upgrade_response = Local_service_base::Upgrade_response;
using Close_response = Local_service_base::Close_response;
using Request = Local_service_base::Request;

View File

@ -6,4 +6,6 @@ _ZN6Genode7Sandbox18Local_service_baseC1ERS0_RKNS_6StringILm32EEERNS1_6WakeupE T
_ZN6Genode7Sandbox18Local_service_baseC2ERS0_RKNS_6StringILm32EEERNS1_6WakeupE T
_ZN6Genode7SandboxC1ERNS_3EnvERNS0_13State_handlerE T
_ZN6Genode7SandboxC2ERNS_3EnvERNS0_13State_handlerE T
_ZN6Genode7SandboxC1ERNS_3EnvERNS0_13State_handlerERNS0_13Pd_intrinsicsE T
_ZN6Genode7SandboxC2ERNS_3EnvERNS0_13State_handlerERNS0_13Pd_intrinsicsE T
_ZNK6Genode7Sandbox21generate_state_reportERNS_13Xml_generatorE T

View File

@ -443,8 +443,6 @@ Sandbox::Child::Sample_state_result Sandbox::Child::sample_state()
void Sandbox::Child::init(Pd_session &session, Pd_session_capability cap)
{
session.ref_account(_env.pd_session_cap());
size_t const initial_session_costs =
session_alloc_batch_size()*_child.session_factory().session_costs();
@ -472,13 +470,20 @@ void Sandbox::Child::init(Pd_session &session, Pd_session_capability cap)
cap_quota = avail_caps;
}
try { _env.pd().transfer_quota(cap, cap_quota); }
catch (Out_of_caps) {
error(name(), ": unable to initialize cap quota of PD"); }
_with_pd_intrinsics([&] (Pd_intrinsics::Intrinsics &intrinsics) {
try { _env.pd().transfer_quota(cap, ram_quota); }
catch (Out_of_ram) {
error(name(), ": unable to initialize RAM quota of PD"); }
_ref_pd_cap = intrinsics.ref_pd_cap;
session.ref_account(intrinsics.ref_pd_cap);
try { intrinsics.ref_pd.transfer_quota(cap, cap_quota); }
catch (Out_of_caps) {
error(name(), ": unable to initialize cap quota of PD"); }
try { intrinsics.ref_pd.transfer_quota(cap, ram_quota); }
catch (Out_of_ram) {
error(name(), ": unable to initialize RAM quota of PD"); }
});
}
@ -491,9 +496,11 @@ void Sandbox::Child::init(Cpu_session &session, Cpu_session_capability cap)
warning(name(), ": configured CPU quota of ", assigned, " exceeds "
"available quota, proceeding with a quota of ", effective);
session.ref_account(_env.cpu_session_cap());
_with_pd_intrinsics([&] (Pd_intrinsics::Intrinsics &intrinsics) {
session.ref_account(intrinsics.ref_cpu_cap); });
_cpu_quota_transfer.transfer_cpu_quota(cap, effective);
_cpu_quota_transfer.transfer_cpu_quota(_child.pd_session_cap(), _child.pd(),
cap, effective);
}
@ -745,7 +752,8 @@ Sandbox::Child::Child(Env &env,
Affinity::Space const &affinity_space,
Registry<Parent_service> &parent_services,
Registry<Routed_service> &child_services,
Registry<Local_service> &local_services)
Registry<Local_service> &local_services,
Pd_intrinsics &pd_intrinsics)
:
_env(env), _alloc(alloc), _verbose(verbose), _id(id),
_report_update_trigger(report_update_trigger),
@ -761,6 +769,7 @@ Sandbox::Child::Child(Env &env,
_heartbeat_enabled(start_node.has_sub_node("heartbeat")),
_resources(_resources_from_start_node(start_node, prio_levels, affinity_space,
default_caps_accessor.default_caps())),
_pd_intrinsics(pd_intrinsics),
_parent_services(parent_services),
_child_services(child_services),
_local_services(local_services),

View File

@ -68,11 +68,35 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
struct Cpu_quota_transfer : Interface
{
virtual void transfer_cpu_quota(Cpu_session_capability, Cpu_quota) = 0;
virtual void transfer_cpu_quota(Capability<Pd_session>, Pd_session &,
Capability<Cpu_session>, Cpu_quota) = 0;
};
enum class Sample_state_result { CHANGED, UNCHANGED };
/*
* Helper for passing lambda functions as 'Pd_intrinsics::Fn'
*/
using Pd_intrinsics = Genode::Sandbox::Pd_intrinsics;
template <typename PD_SESSION, typename FN>
static void with_pd_intrinsics(Pd_intrinsics &pd_intrinsics,
Capability<Pd_session> cap, PD_SESSION &pd,
FN const &fn)
{
struct Impl : Pd_intrinsics::Fn
{
using Intrinsics = Pd_intrinsics::Intrinsics;
FN const &_fn;
Impl(FN const &fn) : _fn(fn) { }
void call(Intrinsics &intrinsics) const override { _fn(intrinsics); }
};
pd_intrinsics.with_intrinsics(cap, pd, Impl { fn });
}
private:
friend class Child_registry;
@ -280,6 +304,16 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Ram_quota _configured_ram_quota() const;
Cap_quota _configured_cap_quota() const;
Pd_intrinsics &_pd_intrinsics;
template <typename FN>
void _with_pd_intrinsics(FN const &fn)
{
with_pd_intrinsics(_pd_intrinsics, _child.pd_session_cap(), _child.pd(), fn);
}
Capability<Pd_session> _ref_pd_cap { }; /* defined by 'init' */
using Local_service = Genode::Sandbox::Local_service_base;
Registry<Parent_service> &_parent_services;
@ -293,7 +327,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Child &_child;
Dynamic_rom_session _session { _child._env.ep().rpc_ep(),
_child.ref_pd(), _child._env.rm(),
_child._env.ram(), _child._env.rm(),
*this };
Service::Single_session_factory _factory { _session };
@ -557,7 +591,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Affinity::Space const &affinity_space,
Registry<Parent_service> &parent_services,
Registry<Routed_service> &child_services,
Registry<Local_service> &local_services);
Registry<Local_service> &local_services,
Pd_intrinsics &pd_intrinsics);
virtual ~Child();
@ -680,8 +715,17 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Child_policy::Name name() const override { return _unique_name; }
Pd_session &ref_pd() override { return _env.pd(); }
Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); }
Pd_session &ref_pd() override
{
Pd_session *_ref_pd_ptr = nullptr;
_with_pd_intrinsics([&] (Pd_intrinsics::Intrinsics &intrinsics) {
_ref_pd_ptr = &intrinsics.ref_pd; });
return *_ref_pd_ptr;
}
Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
Ram_allocator &session_md_ram() override { return _env.ram(); }
void init(Pd_session &, Pd_session_capability) override;
void init(Cpu_session &, Cpu_session_capability) override;
@ -732,6 +776,12 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
bool initiate_env_sessions() const override { return false; }
void _with_address_space(Pd_session &, With_address_space_fn const &fn) override
{
_with_pd_intrinsics([&] (Pd_intrinsics::Intrinsics &intrinsics) {
fn.call(intrinsics.address_space); });
}
void yield_response() override
{
apply_downgrade();

View File

@ -54,6 +54,8 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
Env &_env;
Heap &_heap;
Pd_intrinsics &_pd_intrinsics;
Registry<Parent_service> _parent_services { };
Registry<Routed_service> _child_services { };
Registry<Local_service> &_local_services;
@ -143,7 +145,8 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
/**
* Child::Cpu_quota_transfer interface
*/
void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override
void transfer_cpu_quota(Capability<Pd_session> pd_cap, Pd_session &pd,
Capability<Cpu_session> cpu, Cpu_quota quota) override
{
Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) };
@ -154,7 +157,8 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
size_t const fraction =
Cpu_session::quota_lim_upscale(quota.percent, remaining.percent);
_env.cpu().transfer_quota(cap, fraction);
Child::with_pd_intrinsics(_pd_intrinsics, pd_cap, pd, [&] (auto &intrinsics) {
intrinsics.ref_cpu.transfer_quota(cpu, fraction); });
_transferred_cpu.percent += quota.percent;
}
@ -247,11 +251,38 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
return *new (_heap) Parent_service(_parent_services, _env, name);
}
/**
* Default way of using the 'Env::pd' as the child's 'ref_pd' and accessing
* the child's address space via RPC.
*/
struct Default_pd_intrinsics : Pd_intrinsics
{
Env &_env;
void with_intrinsics(Capability<Pd_session>, Pd_session &pd, Fn const &fn) override
{
Region_map_client region_map(pd.address_space());
Intrinsics intrinsics { _env.pd(), _env.pd_session_cap(),
_env.cpu(), _env.cpu_session_cap(), region_map };
fn.call(intrinsics);
}
Default_pd_intrinsics(Env &env) : _env(env) { }
} _default_pd_intrinsics { _env };
Library(Env &env, Heap &heap, Registry<Local_service> &local_services,
State_handler &state_handler, Pd_intrinsics &pd_intrinsics)
:
_env(env), _heap(heap), _pd_intrinsics(pd_intrinsics),
_local_services(local_services), _state_reporter(_env, *this, state_handler)
{ }
Library(Env &env, Heap &heap, Registry<Local_service> &local_services,
State_handler &state_handler)
:
_env(env), _heap(heap), _local_services(local_services),
_state_reporter(_env, *this, state_handler)
Library(env, heap, local_services, state_handler, _default_pd_intrinsics)
{ }
void apply_config(Xml_node const &);
@ -341,7 +372,8 @@ bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const
Child::Id { ++_child_cnt }, _state_reporter,
start_node, *this, *this, _children, *this, *this, *this, *this,
_prio_levels, _effective_affinity_space(),
_parent_services, _child_services, _local_services);
_parent_services, _child_services, _local_services,
_pd_intrinsics);
_children.insert(&child);
_avail_cpu.percent -= min(_avail_cpu.percent, child.cpu_quota().percent);
@ -627,6 +659,13 @@ void Genode::Sandbox::generate_state_report(Xml_generator &xml) const
}
Genode::Sandbox::Sandbox(Env &env, State_handler &state_handler, Pd_intrinsics &pd_intrinsics)
:
_heap(env.ram(), env.rm()),
_library(*new (_heap) Library(env, _heap, _local_services, state_handler, pd_intrinsics))
{ }
Genode::Sandbox::Sandbox(Env &env, State_handler &state_handler)
:
_heap(env.ram(), env.rm()),