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:
Norman Feske 2017-02-23 17:55:00 +01:00 committed by Christian Helmuth
parent 28b359703d
commit 23ad546a88
4 changed files with 147 additions and 49 deletions

View File

@ -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;
}

View File

@ -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>

View File

@ -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) {
/*

View File

@ -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;
}