mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-26 08:51:08 +00:00
eba9c15746
The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
351 lines
9.5 KiB
C++
351 lines
9.5 KiB
C++
/*
|
|
* \brief Child handling
|
|
* \author Norman Feske
|
|
* \date 2013-10-05
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2013-2017 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
#ifndef _INCLUDE__CLI_MONITOR__CHILD_H_
|
|
#define _INCLUDE__CLI_MONITOR__CHILD_H_
|
|
|
|
/* Genode includes */
|
|
#include <util/list.h>
|
|
#include <base/registry.h>
|
|
#include <base/child.h>
|
|
#include <init/child_policy.h>
|
|
#include <os/child_policy_dynamic_rom.h>
|
|
#include <cpu_session/connection.h>
|
|
#include <pd_session/connection.h>
|
|
#include <base/session_label.h>
|
|
|
|
/* CLI-monitor includes */
|
|
#include <cli_monitor/ram.h>
|
|
|
|
namespace Cli_monitor { class Child_base; }
|
|
|
|
|
|
class Cli_monitor::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::size_t size_t;
|
|
typedef Genode::Cap_quota Cap_quota;
|
|
|
|
typedef Genode::Registered<Genode::Parent_service> Parent_service;
|
|
|
|
private:
|
|
|
|
Ram &_ram;
|
|
|
|
Genode::Allocator &_alloc;
|
|
|
|
Genode::Session_label const _label;
|
|
Binary_name const _binary_name;
|
|
|
|
Genode::Pd_session_capability _ref_pd_cap;
|
|
Genode::Pd_session &_ref_pd;
|
|
|
|
Genode::Ram_session_capability _ref_ram_cap;
|
|
Genode::Ram_session &_ref_ram;
|
|
|
|
Cap_quota _cap_quota;
|
|
|
|
size_t _ram_quota;
|
|
size_t _ram_limit;
|
|
|
|
struct Parent_services : Genode::Registry<Parent_service>
|
|
{
|
|
Genode::Allocator &_alloc;
|
|
Parent_services(Genode::Allocator &alloc) : _alloc(alloc) { }
|
|
~Parent_services()
|
|
{
|
|
for_each([&] (Parent_service &s) { Genode::destroy(_alloc, &s); });
|
|
}
|
|
} _parent_services { _alloc };
|
|
|
|
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
|
|
Genode::Rpc_entrypoint _entrypoint;
|
|
|
|
Genode::Child_policy_dynamic_rom_file _config_policy;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
Genode::Child _child;
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param ref_ram used as reference account for the child'd RAM
|
|
* session and for allocating the backing store
|
|
* for the child's configuration
|
|
* \param alloc allocator used to fill parent-service registry
|
|
* on demand
|
|
*/
|
|
Child_base(Ram &ram,
|
|
Genode::Allocator &alloc,
|
|
Name const &label,
|
|
Binary_name const &binary_name,
|
|
Genode::Pd_session &ref_pd,
|
|
Genode::Pd_session_capability ref_pd_cap,
|
|
Genode::Ram_session &ref_ram,
|
|
Genode::Ram_session_capability ref_ram_cap,
|
|
Genode::Region_map &local_rm,
|
|
Cap_quota cap_quota,
|
|
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), _alloc(alloc),
|
|
_label(label), _binary_name(binary_name),
|
|
_ref_pd_cap (ref_pd_cap), _ref_pd (ref_pd),
|
|
_ref_ram_cap(ref_ram_cap), _ref_ram(ref_ram),
|
|
_cap_quota(cap_quota), _ram_quota(ram_quota), _ram_limit(ram_limit),
|
|
_entrypoint(&ref_pd, ENTRYPOINT_STACK_SIZE, _label.string(), false),
|
|
_config_policy(local_rm, "config", _entrypoint, &ref_ram),
|
|
_yield_response_sigh_cap(yield_response_sig_cap),
|
|
_exit_sig_cap(exit_sig_cap),
|
|
_child(local_rm, _entrypoint, *this)
|
|
{ }
|
|
|
|
Genode::Session_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=%ld", 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(_child.ram_session_cap(), amount);
|
|
_ram_quota -= amount;
|
|
}
|
|
|
|
/**
|
|
* Upgrade quota of child
|
|
*
|
|
* \throw Ram::Transfer_quota_failed
|
|
*/
|
|
void upgrade_ram_quota(size_t amount)
|
|
{
|
|
_ram.transfer_to(_child.ram_session_cap(), amount);
|
|
_ram_quota += amount;
|
|
|
|
/* wake up child if resource request is in flight */
|
|
size_t const req = requested_ram_quota();
|
|
if (req && _child.ram().avail_ram().value >= 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 - _child.ram().ram_quota().value,
|
|
_child.ram().used_ram().value,
|
|
_child.ram().avail_ram().value,
|
|
requested_ram_quota());
|
|
}
|
|
|
|
/**
|
|
* Return true if child exited and should be destructed
|
|
*/
|
|
bool exited() const { return _exited; }
|
|
|
|
|
|
/****************************
|
|
** Child_policy interface **
|
|
****************************/
|
|
|
|
Name name() const override { return _label; }
|
|
Binary_name binary_name() const override { return _binary_name; }
|
|
|
|
Genode::Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
|
|
Genode::Pd_session &ref_pd() override { return _ref_pd; }
|
|
|
|
void init(Genode::Pd_session &session, Genode::Pd_session_capability cap) override
|
|
{
|
|
session.ref_account(_ref_pd_cap);
|
|
_ref_pd.transfer_quota(cap, _cap_quota);
|
|
_ref_pd.transfer_quota(cap, Genode::Ram_quota{_ram_quota});
|
|
}
|
|
|
|
Genode::Service &resolve_session_request(Genode::Service::Name const &name,
|
|
Genode::Session_state::Args const &args) override
|
|
{
|
|
Genode::Service *service = nullptr;
|
|
|
|
/* check for config file request */
|
|
if ((service = _config_policy.resolve_session_request(name.string(), args.string())))
|
|
return *service;
|
|
|
|
/* populate session-local parent service registry on demand */
|
|
_parent_services.for_each([&] (Parent_service &s) {
|
|
if (s.name() == name)
|
|
service = &s; });
|
|
|
|
if (service)
|
|
return *service;
|
|
|
|
return *new (_alloc) Parent_service(_parent_services, name);
|
|
}
|
|
|
|
void yield_response()
|
|
{
|
|
if (_withdraw_on_yield_response) {
|
|
enum { RESERVE = 4*1024*1024 };
|
|
|
|
size_t amount = _child.ram().avail_ram().value < RESERVE
|
|
? 0 : _child.ram().avail_ram().value - 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
|
|
{
|
|
Genode::log("subsystem \"", name(), "\" exited with value ", exit_value);
|
|
_exited = true;
|
|
|
|
/* trigger destruction of the child */
|
|
Genode::Signal_transmitter(_exit_sig_cap).submit();
|
|
}
|
|
};
|
|
|
|
#endif /* _INCLUDE__CLI_MONITOR__CHILD_H_ */
|