genode/repos/os/include/cli_monitor/child.h
Norman Feske 20f961cbd8 cli_monitor, launcher: handle exiting subsystems
Until now, the CLI monitor and the laucher allowed the user to explitly
kill subsystems but both used to ignore gracefully exiting subsystems.
It was the user's job to remove the remains of those subsystems. The
patch takes the burden of manually killing exited subsystems from the
user.

Fixes #1685
2015-09-30 15:48:12 +02:00

339 lines
8.8 KiB
C++

/*
* \brief Child handling
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__CLI_MONITOR__CHILD_H_
#define _INCLUDE__CLI_MONITOR__CHILD_H_
/* Genode includes */
#include <util/list.h>
#include <base/child.h>
#include <init/child_policy.h>
#include <os/child_policy_dynamic_rom.h>
#include <cpu_session/connection.h>
#include <rm_session/connection.h>
#include <pd_session/connection.h>
/* CLI-monitor includes */
#include <cli_monitor/ram.h>
class Child_base : public Genode::Child_policy
{
public:
/*
* XXX derive donated quota from information to be provided by
* the used 'Connection' interfaces
*/
enum { DONATED_RAM_QUOTA = 128*1024 };
class Quota_exceeded : public Genode::Exception { };
typedef Genode::String<128> Label;
typedef Genode::size_t size_t;
private:
Ram &_ram;
Label const _label;
struct Resources
{
Genode::Pd_connection pd;
Genode::Ram_connection ram;
Genode::Cpu_connection cpu;
Genode::Rm_connection rm;
Resources(const char *label, Genode::size_t ram_quota)
: pd(label), ram(label), cpu(label)
{
if (ram_quota > DONATED_RAM_QUOTA)
ram_quota -= DONATED_RAM_QUOTA;
else
throw Quota_exceeded();
ram.ref_account(Genode::env()->ram_session_cap());
if (Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota) != 0)
throw Quota_exceeded();
}
};
size_t _ram_quota;
size_t _ram_limit;
Resources _resources;
Genode::Service_registry _parent_services;
Genode::Rom_connection _binary_rom;
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
Genode::Rpc_entrypoint _entrypoint;
Init::Child_policy_enforce_labeling _labeling_policy;
Init::Child_policy_provide_rom_file _binary_policy;
Genode::Child_policy_dynamic_rom_file _config_policy;
Genode::Child _child;
/**
* If set to true, immediately withdraw resources yielded by the child
*/
bool _withdraw_on_yield_response = false;
/**
* Arguments of current resource request from the child
*/
Genode::Parent::Resource_args _resource_args;
Genode::Signal_context_capability _yield_response_sigh_cap;
Genode::Signal_context_capability _exit_sig_cap;
/* true if child is scheduled for destruction */
bool _exited = false;
public:
Child_base(Ram &ram,
char const *label,
char const *binary,
Genode::Cap_session &cap_session,
Genode::size_t ram_quota,
Genode::size_t ram_limit,
Genode::Signal_context_capability yield_response_sig_cap,
Genode::Signal_context_capability exit_sig_cap)
:
_ram(ram),
_label(label),
_ram_quota(ram_quota),
_ram_limit(ram_limit),
_resources(_label.string(), _ram_quota),
_binary_rom(binary, _label.string()),
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _label.string(), false),
_labeling_policy(_label.string()),
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
_config_policy("config", _entrypoint, &_resources.ram),
_child(_binary_rom.dataspace(), _resources.pd.cap(),
_resources.ram.cap(), _resources.cpu.cap(),
_resources.rm.cap(), &_entrypoint, this),
_yield_response_sigh_cap(yield_response_sig_cap),
_exit_sig_cap(exit_sig_cap)
{ }
Label label() const { return _label; }
void configure(char const *config, size_t config_len)
{
_config_policy.load(config, config_len);
}
void start()
{
_entrypoint.activate();
}
/**
* Issue yield request to the child
*/
void yield(size_t amount, bool greedy)
{
if (requested_ram_quota())
return; /* resource request in flight */
char buf[128];
Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd", amount);
_withdraw_on_yield_response = greedy;
_child.yield(buf);
}
/**
* Return amount of RAM currently requested by the child
*/
size_t requested_ram_quota() const
{
return Genode::Arg_string::find_arg(_resource_args.string(), "ram_quota").ulong_value(0);
}
/**
* Withdraw quota from the child
*
* \throw Ram::Transfer_quota_failed
*/
void withdraw_ram_quota(size_t amount)
{
if (!amount)
return;
_ram.withdraw_from(_resources.ram.cap(), amount);
_ram_quota -= amount;
}
/**
* Upgrade quota of child
*
* \throw Ram::Transfer_quota_failed
*/
void upgrade_ram_quota(size_t amount)
{
_ram.transfer_to(_resources.ram.cap(), amount);
_ram_quota += amount;
/* wake up child if resource request is in flight */
size_t const req = requested_ram_quota();
if (req && _resources.ram.avail() >= req) {
_child.notify_resource_avail();
/* clear request state */
_resource_args = Genode::Parent::Resource_args("");
}
}
/**
* Try to respond to a current resource request issued by the child
*
* This method evaluates the conditions, under which a resource
* request can be answered: There must be enough room between the
* current quota and the configured limit, and there must be enough
* slack memory available. If both conditions are met, the quota
* of the child gets upgraded.
*/
void try_response_to_resource_request()
{
size_t const req = requested_ram_quota();
if (!req)
return; /* no resource request in flight */
/*
* Respond to the current request if the requested quota fits
* within the limit and if there is enough free quota available.
*/
if (req <= _ram.status().avail && req + _ram_quota <= _ram_limit) {
try { upgrade_ram_quota(req); }
catch (Ram::Transfer_quota_failed) { }
}
}
/**
* Set limit for on-demand RAM quota expansion
*/
void ram_limit(size_t limit)
{
_ram_limit = limit;
try_response_to_resource_request();
}
struct Ram_status
{
size_t quota = 0, limit = 0, xfer = 0, used = 0, avail = 0, req = 0;
Ram_status() { }
Ram_status(size_t quota, size_t limit, size_t xfer, size_t used,
size_t avail, size_t req)
:
quota(quota), limit(limit), xfer(xfer), used(used),
avail(avail), req(req)
{ }
};
/**
* Return RAM quota status of the child
*
* XXX should be a const method, but the 'Ram_session' accessors
* are not const
*/
Ram_status ram_status()
{
return Ram_status(_ram_quota,
_ram_limit,
_ram_quota - _resources.ram.quota(),
_resources.ram.used(),
_resources.ram.avail(),
requested_ram_quota());
}
/**
* Return true if child exited and should be destructed
*/
bool exited() const { return _exited; }
/****************************
** Child_policy interface **
****************************/
const char *name() const { return _label.string(); }
Genode::Service *resolve_session_request(const char *service_name,
const char *args)
{
Genode::Service *service = 0;
/* check for binary file request */
if ((service = _binary_policy.resolve_session_request(service_name, args)))
return service;
/* check for config file request */
if ((service = _config_policy.resolve_session_request(service_name, args)))
return service;
/* fill parent service registry on demand */
if (!(service = _parent_services.find(service_name))) {
service = new (Genode::env()->heap())
Genode::Parent_service(service_name);
_parent_services.insert(service);
}
/* return parent service */
return service;
}
void filter_session_args(const char *service,
char *args, Genode::size_t args_len)
{
_labeling_policy.filter_session_args(service, args, args_len);
}
void yield_response()
{
if (_withdraw_on_yield_response) {
enum { RESERVE = 4*1024*1024 };
size_t amount = _resources.ram.avail() < RESERVE
? 0 : _resources.ram.avail() - RESERVE;
/* try to immediately withdraw freed-up resources */
try { withdraw_ram_quota(amount); }
catch (Ram::Transfer_quota_failed) { }
}
/* propagate yield-response signal */
Genode::Signal_transmitter(_yield_response_sigh_cap).submit();
}
void resource_request(Genode::Parent::Resource_args const &args)
{
_resource_args = args;
try_response_to_resource_request();
}
void exit(int exit_value) override
{
PINF("subsystem \"%s\" exited with value %d", name(), exit_value);
_exited = true;
/* trigger destruction of the child */
Genode::Signal_transmitter(_exit_sig_cap).submit();
}
};
#endif /* _INCLUDE__CLI_MONITOR__CHILD_H_ */