diff --git a/repos/os/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc index 0a6696b527..7096516fe1 100644 --- a/repos/os/src/lib/sandbox/child.cc +++ b/repos/os/src/lib/sandbox/child.cc @@ -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_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, diff --git a/repos/os/src/lib/sandbox/child.h b/repos/os/src/lib/sandbox/child.h index 10c7772b06..e192d9126f 100644 --- a/repos/os/src/lib/sandbox/child.h +++ b/repos/os/src/lib/sandbox/child.h @@ -64,6 +64,12 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup typedef Resource_limit_accessor Ram_limit_accessor; typedef Resource_limit_accessor Cap_limit_accessor; + typedef Resource_limit_accessor 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_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() { diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc index 90a8e556b5..ddfef7ae9e 100644 --- a/repos/os/src/lib/sandbox/library.cc +++ b/repos/os/src/lib/sandbox/library.cc @@ -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; diff --git a/repos/os/src/lib/sandbox/types.h b/repos/os/src/lib/sandbox/types.h index 5d8ee16c56..56fd8ddd0d 100644 --- a/repos/os/src/lib/sandbox/types.h +++ b/repos/os/src/lib/sandbox/types.h @@ -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 > Child_list; template