mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 23:42:32 +00:00
sandbox: improve CPU-quota accounting
The existing assignment of CPU quotas did not anticipate the dynamic reconfiguration of init. It merely tracked the available CPU quota by deducing the consumed amount from a global variable but never replenished the value. This worked for static scenarios but failed in situations where components are dynamically re-started. So far this deficiency remained detected because CPU quotas were not used in highly dynamic systems like Sculpt OS. However, this has recently changed by commit "sculpt: assign CPU quotas". The patch improves the accounting by mirroring the existing handling of RAM and cap quotas. Note that the CPU-quota accounting is still rather limited. In particular the dynamic rebalancing is not yet supported. Issue #4445
This commit is contained in:
parent
d182b20705
commit
a9022d8451
@ -484,20 +484,16 @@ void Sandbox::Child::init(Pd_session &session, Pd_session_capability cap)
|
||||
|
||||
void Sandbox::Child::init(Cpu_session &session, Cpu_session_capability cap)
|
||||
{
|
||||
static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
|
||||
size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100);
|
||||
size_t need_adj = 0;
|
||||
Cpu_quota const assigned = _resources.assigned_cpu_quota;
|
||||
Cpu_quota const effective = _effective_cpu_quota;
|
||||
|
||||
if (assigned.percent > effective.percent)
|
||||
warning(name(), ": configured CPU quota of ", assigned, " exceeds "
|
||||
"available quota, proceeding with a quota of ", effective);
|
||||
|
||||
if (need > avail || avail == 0) {
|
||||
warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100));
|
||||
need_adj = Cpu_session::quota_lim_upscale(100, 100);
|
||||
avail = 0;
|
||||
} else {
|
||||
need_adj = Cpu_session::quota_lim_upscale(need, avail);
|
||||
avail -= need;
|
||||
}
|
||||
session.ref_account(_env.cpu_session_cap());
|
||||
_env.cpu().transfer_quota(cap, need_adj);
|
||||
|
||||
_cpu_quota_transfer.transfer_cpu_quota(cap, effective);
|
||||
}
|
||||
|
||||
|
||||
@ -743,6 +739,8 @@ Sandbox::Child::Child(Env &env,
|
||||
Name_registry &name_registry,
|
||||
Ram_limit_accessor &ram_limit_accessor,
|
||||
Cap_limit_accessor &cap_limit_accessor,
|
||||
Cpu_limit_accessor &cpu_limit_accessor,
|
||||
Cpu_quota_transfer &cpu_quota_transfer,
|
||||
Prio_levels prio_levels,
|
||||
Affinity::Space const &affinity_space,
|
||||
Registry<Parent_service> &parent_services,
|
||||
@ -757,6 +755,8 @@ Sandbox::Child::Child(Env &env,
|
||||
_default_caps_accessor(default_caps_accessor),
|
||||
_ram_limit_accessor(ram_limit_accessor),
|
||||
_cap_limit_accessor(cap_limit_accessor),
|
||||
_cpu_limit_accessor(cpu_limit_accessor),
|
||||
_cpu_quota_transfer(cpu_quota_transfer),
|
||||
_name_registry(name_registry),
|
||||
_heartbeat_enabled(start_node.has_sub_node("heartbeat")),
|
||||
_resources(_resources_from_start_node(start_node, prio_levels, affinity_space,
|
||||
|
@ -64,6 +64,12 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
|
||||
typedef Resource_limit_accessor<Ram_quota> Ram_limit_accessor;
|
||||
typedef Resource_limit_accessor<Cap_quota> Cap_limit_accessor;
|
||||
typedef Resource_limit_accessor<Cpu_quota> Cpu_limit_accessor;
|
||||
|
||||
struct Cpu_quota_transfer : Interface
|
||||
{
|
||||
virtual void transfer_cpu_quota(Cpu_session_capability, Cpu_quota) = 0;
|
||||
};
|
||||
|
||||
enum class Sample_state_result { CHANGED, UNCHANGED };
|
||||
|
||||
@ -145,6 +151,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
Default_caps_accessor &_default_caps_accessor;
|
||||
Ram_limit_accessor &_ram_limit_accessor;
|
||||
Cap_limit_accessor &_cap_limit_accessor;
|
||||
Cpu_limit_accessor &_cpu_limit_accessor;
|
||||
Cpu_quota_transfer &_cpu_quota_transfer;
|
||||
|
||||
Name_registry &_name_registry;
|
||||
|
||||
@ -206,7 +214,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
Affinity affinity;
|
||||
Ram_quota assigned_ram_quota;
|
||||
Cap_quota assigned_cap_quota;
|
||||
size_t cpu_quota_pc;
|
||||
Cpu_quota assigned_cpu_quota;
|
||||
|
||||
Ram_quota effective_ram_quota() const
|
||||
{
|
||||
@ -240,8 +248,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
Affinity::Space const &affinity_space,
|
||||
Cap_quota default_cap_quota)
|
||||
{
|
||||
size_t cpu_quota_pc = 0;
|
||||
Number_of_bytes ram_bytes = 0;
|
||||
unsigned cpu_percent = 0;
|
||||
Number_of_bytes ram_bytes = 0;
|
||||
|
||||
size_t caps = start_node.attribute_value("caps", default_cap_quota.value);
|
||||
|
||||
@ -250,17 +258,14 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
typedef String<8> Name;
|
||||
Name const name = rsc.attribute_value("name", Name());
|
||||
|
||||
if (name == "RAM") {
|
||||
if (name == "RAM")
|
||||
ram_bytes = rsc.attribute_value("quantum", ram_bytes);
|
||||
}
|
||||
|
||||
if (name == "CPU") {
|
||||
cpu_quota_pc = rsc.attribute_value("quantum", 0UL);
|
||||
}
|
||||
if (name == "CPU")
|
||||
cpu_percent = rsc.attribute_value("quantum", 0U);
|
||||
|
||||
if (name == "CAP") {
|
||||
if (name == "CAP")
|
||||
caps = rsc.attribute_value("quantum", 0UL);
|
||||
}
|
||||
});
|
||||
|
||||
return Resources { log2(prio_levels.value),
|
||||
@ -269,7 +274,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
affinity_location_from_xml(affinity_space, start_node)),
|
||||
Ram_quota { ram_bytes },
|
||||
Cap_quota { caps },
|
||||
cpu_quota_pc };
|
||||
Cpu_quota { cpu_percent } };
|
||||
}
|
||||
|
||||
Resources _resources;
|
||||
@ -342,6 +347,10 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
long const _prio_levels_log2 { _resources.prio_levels_log2 };
|
||||
long const _priority { _resources.priority };
|
||||
|
||||
Cpu_quota const _effective_cpu_quota {
|
||||
min(_cpu_limit_accessor.resource_limit(Cpu_quota{}).percent,
|
||||
_resources.assigned_cpu_quota.percent) };
|
||||
|
||||
/**
|
||||
* If set to true, the child is allowed to do system management,
|
||||
* e.g., constrain physical RAM allocations.
|
||||
@ -527,9 +536,6 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
* allocations on behalf of the child's
|
||||
* behavior.
|
||||
*
|
||||
* \param ram_limit maximum amount of RAM to be consumed at
|
||||
* creation time.
|
||||
*
|
||||
* \param ram_limit_accessor interface for querying the available
|
||||
* RAM, used for dynamic RAM balancing at
|
||||
* runtime.
|
||||
@ -547,6 +553,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
Name_registry &name_registry,
|
||||
Ram_limit_accessor &ram_limit_accessor,
|
||||
Cap_limit_accessor &cap_limit_accessor,
|
||||
Cpu_limit_accessor &cpu_limit_accessor,
|
||||
Cpu_quota_transfer &cpu_quota_transfer,
|
||||
Prio_levels prio_levels,
|
||||
Affinity::Space const &affinity_space,
|
||||
Registry<Parent_service> &parent_services,
|
||||
@ -564,6 +572,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
|
||||
|
||||
Ram_quota ram_quota() const { return _resources.assigned_ram_quota; }
|
||||
Cap_quota cap_quota() const { return _resources.assigned_cap_quota; }
|
||||
Cpu_quota cpu_quota() const { return _effective_cpu_quota; }
|
||||
|
||||
void try_start()
|
||||
{
|
||||
|
@ -27,6 +27,8 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
|
||||
::Sandbox::Child::Default_caps_accessor,
|
||||
::Sandbox::Child::Ram_limit_accessor,
|
||||
::Sandbox::Child::Cap_limit_accessor,
|
||||
::Sandbox::Child::Cpu_limit_accessor,
|
||||
::Sandbox::Child::Cpu_quota_transfer,
|
||||
::Sandbox::Start_model::Factory,
|
||||
::Sandbox::Parent_provides_model::Factory
|
||||
{
|
||||
@ -44,6 +46,7 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
|
||||
using Prio_levels = ::Sandbox::Prio_levels;
|
||||
using Ram_info = ::Sandbox::Ram_info;
|
||||
using Cap_info = ::Sandbox::Cap_info;
|
||||
using Cpu_quota = ::Sandbox::Cpu_quota;
|
||||
using Config_model = ::Sandbox::Config_model;
|
||||
using Start_model = ::Sandbox::Start_model;
|
||||
using Preservation = ::Sandbox::Preservation;
|
||||
@ -90,6 +93,9 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
|
||||
|
||||
unsigned _child_cnt = 0;
|
||||
|
||||
Cpu_quota _avail_cpu { .percent = 100 };
|
||||
Cpu_quota _transferred_cpu { .percent = 0 };
|
||||
|
||||
Ram_quota _avail_ram() const
|
||||
{
|
||||
Ram_quota avail_ram = _env.pd().avail_ram();
|
||||
@ -129,6 +135,30 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
|
||||
*/
|
||||
Cap_quota resource_limit(Cap_quota const &) const override { return _avail_caps(); }
|
||||
|
||||
/**
|
||||
* Child::Cpu_limit_accessor interface
|
||||
*/
|
||||
Cpu_quota resource_limit(Cpu_quota const &) const override { return _avail_cpu; }
|
||||
|
||||
/**
|
||||
* Child::Cpu_quota_transfer interface
|
||||
*/
|
||||
void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override
|
||||
{
|
||||
Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) };
|
||||
|
||||
/* prevent division by zero in 'quota_lim_upscale' */
|
||||
if (remaining.percent == 0)
|
||||
return;
|
||||
|
||||
size_t const fraction =
|
||||
Cpu_session::quota_lim_upscale(quota.percent, remaining.percent);
|
||||
|
||||
_env.cpu().transfer_quota(cap, fraction);
|
||||
|
||||
_transferred_cpu.percent += quota.percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* State_reporter::Producer interface
|
||||
*/
|
||||
@ -256,7 +286,15 @@ void Genode::Sandbox::Library::_destroy_abandoned_children()
|
||||
/* destroy child once all environment sessions are gone */
|
||||
if (child.env_sessions_closed()) {
|
||||
_children.remove(&child);
|
||||
|
||||
Cpu_quota const child_cpu_quota = child.cpu_quota();
|
||||
|
||||
destroy(_heap, &child);
|
||||
|
||||
/* replenish available CPU quota */
|
||||
_avail_cpu.percent += child_cpu_quota.percent;
|
||||
_transferred_cpu.percent -= min(_transferred_cpu.percent,
|
||||
child_cpu_quota.percent);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -301,11 +339,13 @@ bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const
|
||||
Child &child = *new (_heap)
|
||||
Child(_env, _heap, *_verbose,
|
||||
Child::Id { ++_child_cnt }, _state_reporter,
|
||||
start_node, *this, *this, _children,
|
||||
*this, *this, _prio_levels, _effective_affinity_space(),
|
||||
start_node, *this, *this, _children, *this, *this, *this, *this,
|
||||
_prio_levels, _effective_affinity_space(),
|
||||
_parent_services, _child_services, _local_services);
|
||||
_children.insert(&child);
|
||||
|
||||
_avail_cpu.percent -= min(_avail_cpu.percent, child.cpu_quota().percent);
|
||||
|
||||
if (start_node.has_sub_node("provides"))
|
||||
_server_appeared_or_disappeared = true;
|
||||
|
||||
|
@ -28,6 +28,13 @@ namespace Sandbox {
|
||||
|
||||
struct Prio_levels { long value; };
|
||||
|
||||
struct Cpu_quota
|
||||
{
|
||||
unsigned percent;
|
||||
|
||||
void print(Output &out) const { Genode::print(out, percent, "%"); }
|
||||
};
|
||||
|
||||
typedef List<List_element<Child> > Child_list;
|
||||
|
||||
template <typename T>
|
||||
|
Loading…
Reference in New Issue
Block a user