mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-22 10:21:04 +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 namespace Genode;
|
||||||
using Genode::size_t;
|
using Genode::size_t;
|
||||||
using Genode::strlen;
|
using Genode::strlen;
|
||||||
|
|
||||||
|
struct Ram_quota { size_t value; };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,18 +113,6 @@ namespace Init {
|
|||||||
catch (...) { return Location(0, 0, space.width(), space.height()); }
|
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
|
* 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 Id { unsigned value; };
|
||||||
|
|
||||||
struct Default_route_accessor
|
struct Default_route_accessor { virtual Xml_node default_route() = 0; };
|
||||||
{
|
|
||||||
virtual Xml_node default_route() = 0;
|
struct Ram_limit_accessor { virtual Ram_quota ram_limit() = 0; };
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -437,6 +426,8 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
|
|
||||||
Default_route_accessor &_default_route_accessor;
|
Default_route_accessor &_default_route_accessor;
|
||||||
|
|
||||||
|
Ram_limit_accessor &_ram_limit_accessor;
|
||||||
|
|
||||||
Name_registry &_name_registry;
|
Name_registry &_name_registry;
|
||||||
|
|
||||||
typedef String<64> Name;
|
typedef String<64> Name;
|
||||||
@ -495,7 +486,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
size_t &ram_quota,
|
size_t &ram_quota,
|
||||||
size_t &cpu_quota_pc,
|
size_t &cpu_quota_pc,
|
||||||
bool &constrain_phys,
|
bool &constrain_phys,
|
||||||
size_t const ram_avail,
|
Ram_quota const ram_limit,
|
||||||
Verbose const &verbose)
|
Verbose const &verbose)
|
||||||
{
|
{
|
||||||
cpu_quota_pc = 0;
|
cpu_quota_pc = 0;
|
||||||
@ -521,11 +512,11 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
* If the configured RAM quota exceeds our own quota, we donate
|
* If the configured RAM quota exceeds our own quota, we donate
|
||||||
* all remaining quota to the child.
|
* all remaining quota to the child.
|
||||||
*/
|
*/
|
||||||
if (ram_quota > ram_avail) {
|
if (ram_quota > ram_limit.value) {
|
||||||
ram_quota = ram_avail;
|
ram_quota = ram_limit.value;
|
||||||
|
|
||||||
if (verbose.enabled())
|
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 prio_levels_log2;
|
||||||
long priority;
|
long priority;
|
||||||
Affinity affinity;
|
Affinity affinity;
|
||||||
size_t ram_quota;
|
size_t assigned_ram_quota;
|
||||||
|
size_t effective_ram_quota;
|
||||||
size_t cpu_quota_pc;
|
size_t cpu_quota_pc;
|
||||||
bool constrain_phys;
|
bool constrain_phys;
|
||||||
|
|
||||||
Resources(Xml_node start_node, long prio_levels,
|
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)
|
Verbose const &verbose)
|
||||||
:
|
:
|
||||||
Read_quota(start_node, ram_quota, cpu_quota_pc,
|
Read_quota(start_node, assigned_ram_quota, cpu_quota_pc,
|
||||||
constrain_phys, ram_avail, verbose),
|
constrain_phys, ram_limit, verbose),
|
||||||
prio_levels_log2(log2(prio_levels)),
|
prio_levels_log2(log2(prio_levels)),
|
||||||
priority(read_priority(start_node, prio_levels)),
|
priority(read_priority(start_node, prio_levels)),
|
||||||
affinity(affinity_space,
|
affinity(affinity_space,
|
||||||
read_affinity_location(affinity_space, start_node))
|
read_affinity_location(affinity_space, start_node))
|
||||||
{
|
{
|
||||||
/* deduce session costs from usable ram quota */
|
effective_ram_quota = Genode::Child::effective_ram_quota(assigned_ram_quota);
|
||||||
ram_quota = Genode::Child::effective_ram_quota(ram_quota);
|
|
||||||
}
|
}
|
||||||
} _resources;
|
} _resources;
|
||||||
|
|
||||||
@ -684,6 +675,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
Xml_node start_node,
|
Xml_node start_node,
|
||||||
Default_route_accessor &default_route_accessor,
|
Default_route_accessor &default_route_accessor,
|
||||||
Name_registry &name_registry,
|
Name_registry &name_registry,
|
||||||
|
Ram_limit_accessor &ram_limit_accessor,
|
||||||
long prio_levels,
|
long prio_levels,
|
||||||
Affinity::Space const &affinity_space,
|
Affinity::Space const &affinity_space,
|
||||||
Registry<Parent_service> &parent_services,
|
Registry<Parent_service> &parent_services,
|
||||||
@ -694,24 +686,25 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
_list_element(this),
|
_list_element(this),
|
||||||
_start_node(_alloc, start_node),
|
_start_node(_alloc, start_node),
|
||||||
_default_route_accessor(default_route_accessor),
|
_default_route_accessor(default_route_accessor),
|
||||||
|
_ram_limit_accessor(ram_limit_accessor),
|
||||||
_name_registry(name_registry),
|
_name_registry(name_registry),
|
||||||
_unique_name(start_node, name_registry),
|
_unique_name(start_node, name_registry),
|
||||||
_binary_name(_binary_name_from_xml(start_node, _unique_name)),
|
_binary_name(_binary_name_from_xml(start_node, _unique_name)),
|
||||||
_resources(start_node, prio_levels, affinity_space,
|
_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),
|
_parent_services(parent_services),
|
||||||
_child_services(child_services),
|
_child_services(child_services),
|
||||||
_session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()),
|
_session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()),
|
||||||
_priority_policy(_resources.prio_levels_log2, _resources.priority),
|
_priority_policy(_resources.prio_levels_log2, _resources.priority),
|
||||||
_ram_session_policy(_resources.constrain_phys)
|
_ram_session_policy(_resources.constrain_phys)
|
||||||
{
|
{
|
||||||
if (_resources.ram_quota == 0)
|
if (_resources.effective_ram_quota == 0)
|
||||||
warning("no valid RAM resource for child "
|
warning("no valid RAM resource for child "
|
||||||
"\"", _unique_name, "\"");
|
"\"", _unique_name, "\"");
|
||||||
|
|
||||||
if (_verbose.enabled()) {
|
if (_verbose.enabled()) {
|
||||||
log("child \"", _unique_name, "\"");
|
log("child \"", _unique_name, "\"");
|
||||||
log(" RAM quota: ", _resources.ram_quota);
|
log(" RAM quota: ", _resources.effective_ram_quota);
|
||||||
log(" ELF binary: ", _binary_name);
|
log(" ELF binary: ", _binary_name);
|
||||||
log(" priority: ", _resources.priority);
|
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(); }
|
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()
|
void initiate_env_ram_session()
|
||||||
{
|
{
|
||||||
if (_state == STATE_INITIAL) {
|
if (_state == STATE_INITIAL) {
|
||||||
@ -775,7 +770,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
/* check for completeness of the child's environment */
|
/* check for completeness of the child's environment */
|
||||||
if (_verbose.enabled())
|
if (_verbose.enabled())
|
||||||
_child.for_each_session([&] (Session_state const &session) {
|
_child.for_each_session([&] (Session_state const &session) {
|
||||||
if (session.phase != Session_state::AVAILABLE)
|
if (!session.alive())
|
||||||
warning(name(), ": incomplete environment ",
|
warning(name(), ": incomplete environment ",
|
||||||
session.service().name(), " session "
|
session.service().name(), " session "
|
||||||
"(", session.label(), ")"); });
|
"(", session.label(), ")"); });
|
||||||
@ -937,7 +932,15 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
void init(Ram_session &session, Ram_session_capability cap) override
|
void init(Ram_session &session, Ram_session_capability cap) override
|
||||||
{
|
{
|
||||||
session.ref_account(_env.ram_session_cap());
|
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
|
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")
|
Arg_string::find_arg(args.string(), "ram_quota")
|
||||||
.ulong_value(0);
|
.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");
|
warning("cannot respond to resource request - out of memory");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -234,6 +234,40 @@ append config {
|
|||||||
<expect_log string="[init -> application] config 2: Version B"/>
|
<expect_log string="[init -> application] config 2: Version B"/>
|
||||||
<sleep ms="100"/>
|
<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>
|
</config>
|
||||||
<route>
|
<route>
|
||||||
<service name="Report"> <child name="report_rom"/> </service>
|
<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;
|
Env &_env;
|
||||||
|
|
||||||
@ -395,6 +396,24 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor
|
|||||||
|
|
||||||
unsigned _child_cnt = 0;
|
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 _handle_resource_avail() { }
|
||||||
|
|
||||||
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const
|
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();
|
_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 */
|
/* create new children */
|
||||||
try {
|
try {
|
||||||
_config.xml().for_each_sub_node("start", [&] (Xml_node start_node) {
|
_config.xml().for_each_sub_node("start", [&] (Xml_node start_node) {
|
||||||
@ -604,15 +641,29 @@ void Init::Main::_handle_config()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (used_ram.value > avail_ram.value) {
|
||||||
|
error("RAM exhausted while starting childen");
|
||||||
|
throw Ram_session::Alloc_failed();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_children.insert(new (_heap)
|
Init::Child &child = *new (_heap)
|
||||||
Init::Child(_env, _heap, *_verbose,
|
Init::Child(_env, _heap, *_verbose,
|
||||||
Init::Child::Id { ++_child_cnt },
|
Init::Child::Id { ++_child_cnt }, _state_reporter,
|
||||||
_state_reporter,
|
start_node, *this, _children, *this,
|
||||||
start_node, *this,
|
read_prio_levels(_config.xml()),
|
||||||
_children, read_prio_levels(_config.xml()),
|
|
||||||
read_affinity_space(_config.xml()),
|
read_affinity_space(_config.xml()),
|
||||||
_parent_services, _child_services));
|
_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) {
|
catch (Rom_connection::Rom_connection_failed) {
|
||||||
/*
|
/*
|
||||||
|
@ -45,11 +45,21 @@ static inline bool Test::xml_attribute_matches(Xml_node condition, Xml_node node
|
|||||||
typedef String<64> Value;
|
typedef String<64> Value;
|
||||||
|
|
||||||
Name const name = condition.attribute_value("name", Name());
|
Name const name = condition.attribute_value("name", Name());
|
||||||
Value const value = condition.attribute_value("value", Value());
|
|
||||||
|
|
||||||
|
if (condition.has_attribute("value")) {
|
||||||
|
Value const value = condition.attribute_value("value", Value());
|
||||||
return node.attribute_value(name.string(), 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if 'node' has expected content
|
* Return true if 'node' has expected content
|
||||||
|
Loading…
x
Reference in New Issue
Block a user