mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 09:46:20 +00:00
init: make RAM preservation configurable
This patch improves the accuracy of init's quota-saturation feature (handing out all slack quota to a child by specifying an overly high RAM quota for the child) and makes the RAM preserved by init configurable. The preservation is specified as follows: ! <config> ! ... ! <resource name="RAM" preserve="1M"/> ! ... ! </config> If not specified, init has a reasonable default of 160K (on 32 bit) and 320K (on 64 bit).
This commit is contained in:
parent
28b359703d
commit
23ad546a88
@ -38,6 +38,8 @@ namespace Init {
|
||||
using namespace Genode;
|
||||
using Genode::size_t;
|
||||
using Genode::strlen;
|
||||
|
||||
struct Ram_quota { size_t value; };
|
||||
}
|
||||
|
||||
|
||||
@ -111,18 +113,6 @@ namespace Init {
|
||||
catch (...) { return Location(0, 0, space.width(), space.height()); }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return amount of RAM that is currently unused
|
||||
*/
|
||||
static inline size_t avail_slack_ram_quota(size_t ram_avail)
|
||||
{
|
||||
size_t const preserve = 148*1024;
|
||||
|
||||
return ram_avail > preserve ? ram_avail - preserve : 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return sub string of label with the leading child name stripped out
|
||||
*
|
||||
@ -407,10 +397,9 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
*/
|
||||
struct Id { unsigned value; };
|
||||
|
||||
struct Default_route_accessor
|
||||
{
|
||||
virtual Xml_node default_route() = 0;
|
||||
};
|
||||
struct Default_route_accessor { virtual Xml_node default_route() = 0; };
|
||||
|
||||
struct Ram_limit_accessor { virtual Ram_quota ram_limit() = 0; };
|
||||
|
||||
private:
|
||||
|
||||
@ -437,6 +426,8 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
|
||||
Default_route_accessor &_default_route_accessor;
|
||||
|
||||
Ram_limit_accessor &_ram_limit_accessor;
|
||||
|
||||
Name_registry &_name_registry;
|
||||
|
||||
typedef String<64> Name;
|
||||
@ -491,12 +482,12 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
|
||||
struct Read_quota
|
||||
{
|
||||
Read_quota(Xml_node start_node,
|
||||
size_t &ram_quota,
|
||||
size_t &cpu_quota_pc,
|
||||
bool &constrain_phys,
|
||||
size_t const ram_avail,
|
||||
Verbose const &verbose)
|
||||
Read_quota(Xml_node start_node,
|
||||
size_t &ram_quota,
|
||||
size_t &cpu_quota_pc,
|
||||
bool &constrain_phys,
|
||||
Ram_quota const ram_limit,
|
||||
Verbose const &verbose)
|
||||
{
|
||||
cpu_quota_pc = 0;
|
||||
constrain_phys = false;
|
||||
@ -521,11 +512,11 @@ 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.
|
||||
*/
|
||||
if (ram_quota > ram_avail) {
|
||||
ram_quota = ram_avail;
|
||||
if (ram_quota > ram_limit.value) {
|
||||
ram_quota = ram_limit.value;
|
||||
|
||||
if (verbose.enabled())
|
||||
warn_insuff_quota(ram_avail);
|
||||
warn_insuff_quota(ram_limit.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -538,23 +529,23 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
long prio_levels_log2;
|
||||
long priority;
|
||||
Affinity affinity;
|
||||
size_t ram_quota;
|
||||
size_t assigned_ram_quota;
|
||||
size_t effective_ram_quota;
|
||||
size_t cpu_quota_pc;
|
||||
bool constrain_phys;
|
||||
|
||||
Resources(Xml_node start_node, long prio_levels,
|
||||
Affinity::Space const &affinity_space, size_t ram_avail,
|
||||
Affinity::Space const &affinity_space, Ram_quota ram_limit,
|
||||
Verbose const &verbose)
|
||||
:
|
||||
Read_quota(start_node, ram_quota, cpu_quota_pc,
|
||||
constrain_phys, ram_avail, verbose),
|
||||
Read_quota(start_node, assigned_ram_quota, cpu_quota_pc,
|
||||
constrain_phys, ram_limit, verbose),
|
||||
prio_levels_log2(log2(prio_levels)),
|
||||
priority(read_priority(start_node, prio_levels)),
|
||||
affinity(affinity_space,
|
||||
read_affinity_location(affinity_space, start_node))
|
||||
{
|
||||
/* deduce session costs from usable ram quota */
|
||||
ram_quota = Genode::Child::effective_ram_quota(ram_quota);
|
||||
effective_ram_quota = Genode::Child::effective_ram_quota(assigned_ram_quota);
|
||||
}
|
||||
} _resources;
|
||||
|
||||
@ -684,6 +675,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
Xml_node start_node,
|
||||
Default_route_accessor &default_route_accessor,
|
||||
Name_registry &name_registry,
|
||||
Ram_limit_accessor &ram_limit_accessor,
|
||||
long prio_levels,
|
||||
Affinity::Space const &affinity_space,
|
||||
Registry<Parent_service> &parent_services,
|
||||
@ -694,24 +686,25 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
_list_element(this),
|
||||
_start_node(_alloc, start_node),
|
||||
_default_route_accessor(default_route_accessor),
|
||||
_ram_limit_accessor(ram_limit_accessor),
|
||||
_name_registry(name_registry),
|
||||
_unique_name(start_node, name_registry),
|
||||
_binary_name(_binary_name_from_xml(start_node, _unique_name)),
|
||||
_resources(start_node, prio_levels, affinity_space,
|
||||
avail_slack_ram_quota(_env.ram().avail()), _verbose),
|
||||
ram_limit_accessor.ram_limit(), _verbose),
|
||||
_parent_services(parent_services),
|
||||
_child_services(child_services),
|
||||
_session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()),
|
||||
_priority_policy(_resources.prio_levels_log2, _resources.priority),
|
||||
_ram_session_policy(_resources.constrain_phys)
|
||||
{
|
||||
if (_resources.ram_quota == 0)
|
||||
if (_resources.effective_ram_quota == 0)
|
||||
warning("no valid RAM resource for child "
|
||||
"\"", _unique_name, "\"");
|
||||
|
||||
if (_verbose.enabled()) {
|
||||
log("child \"", _unique_name, "\"");
|
||||
log(" RAM quota: ", _resources.ram_quota);
|
||||
log(" RAM quota: ", _resources.effective_ram_quota);
|
||||
log(" ELF binary: ", _binary_name);
|
||||
log(" priority: ", _resources.priority);
|
||||
}
|
||||
@ -758,6 +751,8 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
*/
|
||||
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
||||
|
||||
Ram_quota ram_quota() const { return Ram_quota { _resources.assigned_ram_quota }; }
|
||||
|
||||
void initiate_env_ram_session()
|
||||
{
|
||||
if (_state == STATE_INITIAL) {
|
||||
@ -775,7 +770,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
/* check for completeness of the child's environment */
|
||||
if (_verbose.enabled())
|
||||
_child.for_each_session([&] (Session_state const &session) {
|
||||
if (session.phase != Session_state::AVAILABLE)
|
||||
if (!session.alive())
|
||||
warning(name(), ": incomplete environment ",
|
||||
session.service().name(), " session "
|
||||
"(", session.label(), ")"); });
|
||||
@ -937,7 +932,15 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
void init(Ram_session &session, Ram_session_capability cap) override
|
||||
{
|
||||
session.ref_account(_env.ram_session_cap());
|
||||
_env.ram().transfer_quota(cap, _resources.ram_quota);
|
||||
|
||||
size_t const initial_session_costs =
|
||||
session_alloc_batch_size()*_child.session_factory().session_costs();
|
||||
|
||||
size_t const transfer_ram = _resources.effective_ram_quota > initial_session_costs
|
||||
? _resources.effective_ram_quota - initial_session_costs
|
||||
: 0;
|
||||
if (transfer_ram)
|
||||
_env.ram().transfer_quota(cap, transfer_ram);
|
||||
}
|
||||
|
||||
void init(Cpu_session &session, Cpu_session_capability cap) override
|
||||
@ -1161,7 +1164,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
||||
Arg_string::find_arg(args.string(), "ram_quota")
|
||||
.ulong_value(0);
|
||||
|
||||
if (avail_slack_ram_quota(_env.ram().avail()) < requested_ram_quota) {
|
||||
if (_ram_limit_accessor.ram_limit().value < requested_ram_quota) {
|
||||
warning("cannot respond to resource request - out of memory");
|
||||
return;
|
||||
}
|
||||
|
@ -234,6 +234,40 @@ append config {
|
||||
<expect_log string="[init -> application] config 2: Version B"/>
|
||||
<sleep ms="100"/>
|
||||
|
||||
|
||||
<message string="test RAM preservation"/>
|
||||
<init_config>
|
||||
<report init_ram="yes"/>
|
||||
<resource name="RAM" preserve="2M"/>
|
||||
<parent-provides>
|
||||
<service name="ROM"/> <service name="RAM"/>
|
||||
<service name="CPU"/> <service name="PD"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<start name="regular">
|
||||
<binary name="dummy"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config> <log string="regular component started"/> </config>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
<start name="greedy">
|
||||
<binary name="dummy"/>
|
||||
<resource name="RAM" quantum="1G"/>
|
||||
<config> <log string="greedy component started"/> </config>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
</init_config>
|
||||
<sleep ms="250"/>
|
||||
<!-- wait until both children are started -->
|
||||
<expect_init_state>
|
||||
<node name="child"> <attribute name="name" value="regular"/> </node>
|
||||
<node name="child"> <attribute name="name" value="greedy"/> </node>
|
||||
</expect_init_state>
|
||||
<expect_init_state>
|
||||
<node name="ram"> <attribute name="avail" higher="2M"/> </node>
|
||||
</expect_init_state>
|
||||
<sleep ms="100"/>
|
||||
|
||||
</config>
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
|
@ -377,7 +377,8 @@ class Init::State_reporter : public Report_update_trigger
|
||||
};
|
||||
|
||||
|
||||
struct Init::Main : State_reporter::Producer, Child::Default_route_accessor
|
||||
struct Init::Main : State_reporter::Producer, Child::Default_route_accessor,
|
||||
Child::Ram_limit_accessor
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
@ -395,6 +396,24 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor
|
||||
|
||||
unsigned _child_cnt = 0;
|
||||
|
||||
static Ram_quota _preserved_ram_from_config(Xml_node config)
|
||||
{
|
||||
Number_of_bytes preserve { 40*sizeof(long)*1024 };
|
||||
|
||||
config.for_each_sub_node("resource", [&] (Xml_node node) {
|
||||
if (node.attribute_value("name", String<16>()) == "RAM")
|
||||
preserve = node.attribute_value("preserve", preserve); });
|
||||
|
||||
return Ram_quota { preserve };
|
||||
}
|
||||
|
||||
Ram_quota _ram_limit { 0 };
|
||||
|
||||
/**
|
||||
* Child::Ram_limit_accessor interface
|
||||
*/
|
||||
Ram_quota ram_limit() override { return _ram_limit; }
|
||||
|
||||
void _handle_resource_avail() { }
|
||||
|
||||
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const
|
||||
@ -591,6 +610,24 @@ void Init::Main::_handle_config()
|
||||
|
||||
_destroy_abandoned_parent_services();
|
||||
|
||||
Ram_quota const preserved_ram = _preserved_ram_from_config(_config.xml());
|
||||
|
||||
Ram_quota avail_ram { _env.ram().avail() };
|
||||
|
||||
if (preserved_ram.value > avail_ram.value) {
|
||||
error("RAM preservation exceeds available memory");
|
||||
return;
|
||||
}
|
||||
|
||||
/* deduce preserved quota from available quota */
|
||||
avail_ram = Ram_quota { avail_ram.value - preserved_ram.value };
|
||||
|
||||
/* initial RAM limit before starting new children */
|
||||
_ram_limit = Ram_quota { avail_ram.value };
|
||||
|
||||
/* variable used to track the RAM taken by new started children */
|
||||
Ram_quota used_ram { 0 };
|
||||
|
||||
/* create new children */
|
||||
try {
|
||||
_config.xml().for_each_sub_node("start", [&] (Xml_node start_node) {
|
||||
@ -604,15 +641,29 @@ void Init::Main::_handle_config()
|
||||
return;
|
||||
}
|
||||
|
||||
if (used_ram.value > avail_ram.value) {
|
||||
error("RAM exhausted while starting childen");
|
||||
throw Ram_session::Alloc_failed();
|
||||
}
|
||||
|
||||
try {
|
||||
_children.insert(new (_heap)
|
||||
Init::Child(_env, _heap, *_verbose,
|
||||
Init::Child::Id { ++_child_cnt },
|
||||
_state_reporter,
|
||||
start_node, *this,
|
||||
_children, read_prio_levels(_config.xml()),
|
||||
read_affinity_space(_config.xml()),
|
||||
_parent_services, _child_services));
|
||||
Init::Child &child = *new (_heap)
|
||||
Init::Child(_env, _heap, *_verbose,
|
||||
Init::Child::Id { ++_child_cnt }, _state_reporter,
|
||||
start_node, *this, _children, *this,
|
||||
read_prio_levels(_config.xml()),
|
||||
read_affinity_space(_config.xml()),
|
||||
_parent_services, _child_services);
|
||||
_children.insert(&child);
|
||||
|
||||
/* account for the start XML node buffered in the child */
|
||||
size_t const metadata_overhead = start_node.size()
|
||||
+ sizeof(Init::Child);
|
||||
/* track used memory and RAM limit */
|
||||
used_ram = Ram_quota { used_ram.value
|
||||
+ child.ram_quota().value
|
||||
+ metadata_overhead };
|
||||
_ram_limit = Ram_quota { avail_ram.value - used_ram.value };
|
||||
}
|
||||
catch (Rom_connection::Rom_connection_failed) {
|
||||
/*
|
||||
|
@ -44,10 +44,20 @@ static inline bool Test::xml_attribute_matches(Xml_node condition, Xml_node node
|
||||
typedef String<32> Name;
|
||||
typedef String<64> Value;
|
||||
|
||||
Name const name = condition.attribute_value("name", Name());
|
||||
Value const value = condition.attribute_value("value", Value());
|
||||
Name const name = condition.attribute_value("name", Name());
|
||||
|
||||
return node.attribute_value(name.string(), Value()) == value;
|
||||
if (condition.has_attribute("value")) {
|
||||
Value const value = condition.attribute_value("value", Value());
|
||||
return node.attribute_value(name.string(), Value()) == value;
|
||||
}
|
||||
|
||||
if (condition.has_attribute("higher")) {
|
||||
size_t const value = condition.attribute_value("higher", Number_of_bytes());
|
||||
return (size_t)node.attribute_value(name.string(), Number_of_bytes()) > value;
|
||||
}
|
||||
|
||||
error("missing condition in <attribute> node");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user