mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 16:35:28 +00:00
cli_monitor: Resource-balancing support
This patch introduces new commands for dynamically balancing RAM between subsystems. The 'status' command prints a table with the RAM status of each subsystem. The 'ram' command changes the quota or a quota limit of a given subsystem. The quota limit can be defined to allow the on-demand expansion of the quota. Finally, the 'yield' command can be used to instruct a subsystem to yield a specified amount of resources. For trying out the new commands, a so-called 'ram_eater' example has been added to the 'terminal_mux.run' scenario. This program simulates a subsystem with a growing demand for resources, yet with the capability to yield resources when instructed by the parent (i.e., cli_monitor). Besides implementing the new features, the patch splits the implementation of 'cli_monitor' into multiple files.
This commit is contained in:
parent
cbdad73dd7
commit
a903049a1a
@ -11,8 +11,8 @@ if {[have_spec linux]} { puts "Run script does not support Linux"; exit 0 }
|
|||||||
|
|
||||||
set build_components {
|
set build_components {
|
||||||
core init noux/minimal lib/libc_noux app/cli_monitor test/bomb test/signal
|
core init noux/minimal lib/libc_noux app/cli_monitor test/bomb test/signal
|
||||||
drivers/timer drivers/uart server/terminal_mux server/terminal_log
|
test/resource_yield drivers/timer drivers/uart server/terminal_mux
|
||||||
noux-pkg/vim
|
server/terminal_log noux-pkg/vim
|
||||||
}
|
}
|
||||||
|
|
||||||
build $build_components
|
build $build_components
|
||||||
@ -125,6 +125,11 @@ append config {
|
|||||||
<resource name="RAM" quantum="5M" />
|
<resource name="RAM" quantum="5M" />
|
||||||
<binary name="test-signal" />
|
<binary name="test-signal" />
|
||||||
</subsystem>
|
</subsystem>
|
||||||
|
<subsystem name="ram_eater" help="resource-yield test program">
|
||||||
|
<resource name="RAM" quantum="25M" />
|
||||||
|
<binary name="test-resource_yield" />
|
||||||
|
<config child="yes" expand="yes" />
|
||||||
|
</subsystem>
|
||||||
</config>
|
</config>
|
||||||
<route>
|
<route>
|
||||||
<any-service> <child name="terminal_mux" /> <any-child/> <parent/> </any-service>
|
<any-service> <child name="terminal_mux" /> <any-child/> <parent/> </any-service>
|
||||||
@ -143,7 +148,7 @@ install_config $config
|
|||||||
# generic modules
|
# generic modules
|
||||||
set boot_modules {
|
set boot_modules {
|
||||||
core init timer ld.lib.so noux terminal_mux terminal_log
|
core init timer ld.lib.so noux terminal_mux terminal_log
|
||||||
test-signal cli_monitor
|
test-signal cli_monitor test-resource_yield
|
||||||
libc.lib.so libm.lib.so libc_noux.lib.so libc_terminal.lib.so ncurses.lib.so
|
libc.lib.so libm.lib.so libc_noux.lib.so libc_terminal.lib.so ncurses.lib.so
|
||||||
vim.tar
|
vim.tar
|
||||||
}
|
}
|
||||||
|
303
os/src/app/cli_monitor/child.h
Normal file
303
os/src/app/cli_monitor/child.h
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/*
|
||||||
|
* \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 _CHILD_H_
|
||||||
|
#define _CHILD_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/list.h>
|
||||||
|
#include <base/child.h>
|
||||||
|
#include <ram_session/connection.h>
|
||||||
|
#include <cpu_session/connection.h>
|
||||||
|
|
||||||
|
class Child : public List<Child>::Element, 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 { };
|
||||||
|
|
||||||
|
Argument const argument;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Ram &_ram;
|
||||||
|
|
||||||
|
struct Label
|
||||||
|
{
|
||||||
|
enum { LABEL_MAX_LEN = 128 };
|
||||||
|
char buf[LABEL_MAX_LEN];
|
||||||
|
Label(char const *label) { strncpy(buf, label, sizeof(buf)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Label const _label;
|
||||||
|
|
||||||
|
struct Resources
|
||||||
|
{
|
||||||
|
Genode::Ram_connection ram;
|
||||||
|
Genode::Cpu_connection cpu;
|
||||||
|
Genode::Rm_connection rm;
|
||||||
|
|
||||||
|
Resources(const char *label, Genode::size_t ram_quota)
|
||||||
|
: 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;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Child(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)
|
||||||
|
:
|
||||||
|
argument(label, "subsystem"),
|
||||||
|
_ram(ram),
|
||||||
|
_label(label),
|
||||||
|
_ram_quota(ram_quota),
|
||||||
|
_ram_limit(ram_limit),
|
||||||
|
_resources(_label.buf, _ram_quota),
|
||||||
|
_binary_rom(binary, _label.buf),
|
||||||
|
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _label.buf, false),
|
||||||
|
_labeling_policy(_label.buf),
|
||||||
|
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
|
||||||
|
_config_policy("config", _entrypoint, &_resources.ram),
|
||||||
|
_child(_binary_rom.dataspace(),
|
||||||
|
_resources.ram.cap(), _resources.cpu.cap(),
|
||||||
|
_resources.rm.cap(), &_entrypoint, this),
|
||||||
|
_yield_response_sigh_cap(yield_response_sig_cap)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
_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 function 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 function, 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****************************
|
||||||
|
** Child_policy interface **
|
||||||
|
****************************/
|
||||||
|
|
||||||
|
const char *name() const { return _label.buf; }
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
/* try to immediately withdraw freed-up resources */
|
||||||
|
try { withdraw_ram_quota(_resources.ram.avail()); }
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _CHILD_H_ */
|
68
os/src/app/cli_monitor/child_registry.h
Normal file
68
os/src/app/cli_monitor/child_registry.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* \brief Registry of running children
|
||||||
|
* \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 _CHILD_REGISTRY_H_
|
||||||
|
#define _CHILD_REGISTRY_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <child.h>
|
||||||
|
|
||||||
|
class Child_registry : public List<Child>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if a child with the specified name already exists
|
||||||
|
*/
|
||||||
|
bool _child_name_exists(const char *label)
|
||||||
|
{
|
||||||
|
for (Child *child = first() ; child; child = child->next())
|
||||||
|
if (strcmp(child->name(), label) == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum { CHILD_NAME_MAX_LEN = 64 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce new unique child name
|
||||||
|
*/
|
||||||
|
void unique_child_name(const char *prefix, char *dst, int dst_len)
|
||||||
|
{
|
||||||
|
char buf[CHILD_NAME_MAX_LEN];
|
||||||
|
char suffix[8];
|
||||||
|
suffix[0] = 0;
|
||||||
|
|
||||||
|
for (int cnt = 1; true; cnt++) {
|
||||||
|
|
||||||
|
/* build program name composed of prefix and numeric suffix */
|
||||||
|
snprintf(buf, sizeof(buf), "%s%s", prefix, suffix);
|
||||||
|
|
||||||
|
/* if such a program name does not exist yet, we are happy */
|
||||||
|
if (!_child_name_exists(buf)) {
|
||||||
|
strncpy(dst, buf, dst_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* increase number of suffix */
|
||||||
|
snprintf(suffix, sizeof(suffix), ".%d", cnt + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _CHILD_REGISTRY_H_ */
|
112
os/src/app/cli_monitor/format_util.h
Normal file
112
os/src/app/cli_monitor/format_util.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* \brief Utilities for formatting output to terminal
|
||||||
|
* \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 _FORMAT_UTIL_H_
|
||||||
|
#define _FORMAT_UTIL_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <terminal_util.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print rational number with two fractional decimals
|
||||||
|
*/
|
||||||
|
static inline size_t format_number(char *dst, size_t len, size_t const value,
|
||||||
|
size_t const quotient, char const *unit)
|
||||||
|
{
|
||||||
|
size_t const integer = value / quotient;
|
||||||
|
size_t const n = snprintf(dst, len, "%zd.", integer);
|
||||||
|
size_t const remainder = ((value - (integer * quotient))*100) / quotient;
|
||||||
|
|
||||||
|
if (len == n) return n;
|
||||||
|
|
||||||
|
return n + snprintf(dst + n, len - n, "%s%zd%s",
|
||||||
|
remainder < 10 ? "0" : "", remainder, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print number of bytes using the best suitable unit
|
||||||
|
*/
|
||||||
|
static inline size_t format_bytes(char *dst, size_t len, size_t bytes)
|
||||||
|
{
|
||||||
|
enum { KB = 1024, MB = 1024*KB };
|
||||||
|
|
||||||
|
if (bytes > MB)
|
||||||
|
return format_number(dst, len, bytes, MB, " MiB");
|
||||||
|
|
||||||
|
if (bytes > KB)
|
||||||
|
return format_number(dst, len, bytes, KB, " KiB");
|
||||||
|
|
||||||
|
return snprintf(dst, len, "%zd bytes", bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print number in MiB, without unit
|
||||||
|
*/
|
||||||
|
static inline size_t format_mib(char *dst, size_t len, size_t bytes)
|
||||||
|
{
|
||||||
|
enum { KB = 1024, MB = 1024*KB };
|
||||||
|
|
||||||
|
return format_number(dst, len, bytes, MB , "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline size_t format_bytes(size_t bytes)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
return format_bytes(buf, sizeof(buf), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline size_t format_mib(size_t bytes)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
return format_mib(buf, sizeof(buf), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void tprint_bytes(Terminal::Session &terminal, size_t bytes)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
format_bytes(buf, sizeof(buf), bytes);
|
||||||
|
Terminal::tprintf(terminal, "%s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void tprint_mib(Terminal::Session &terminal, size_t bytes)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
format_mib(buf, sizeof(buf), bytes);
|
||||||
|
Terminal::tprintf(terminal, "%s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void tprint_status_bytes(Terminal::Session &terminal,
|
||||||
|
char const *label, size_t bytes)
|
||||||
|
{
|
||||||
|
Terminal::tprintf(terminal, label);
|
||||||
|
tprint_bytes(terminal, bytes);
|
||||||
|
Terminal::tprintf(terminal, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void tprint_padding(Terminal::Session &terminal, size_t pad, char c = ' ')
|
||||||
|
{
|
||||||
|
char const buf[2] = { c, 0 };
|
||||||
|
for (unsigned i = 0; i < pad; i++)
|
||||||
|
Terminal::tprintf(terminal, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _FORMAT_UTIL_H_ */
|
28
os/src/app/cli_monitor/help_command.h
Normal file
28
os/src/app/cli_monitor/help_command.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* \brief Help command
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-03-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _HELP_COMMAND_H_
|
||||||
|
#define _HELP_COMMAND_H_
|
||||||
|
|
||||||
|
struct Help_command : Command
|
||||||
|
{
|
||||||
|
Help_command() : Command("help", "brief help information") { }
|
||||||
|
|
||||||
|
void execute(Command_line &, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
tprintf(terminal, " Press [tab] for a list of commands.\n");
|
||||||
|
tprintf(terminal, " When given a command, press [tab] for a list of arguments.\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _HELP_COMMAND_H_ */
|
75
os/src/app/cli_monitor/kill_command.h
Normal file
75
os/src/app/cli_monitor/kill_command.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* \brief Kill command
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-03-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _KILL_COMMAND_H_
|
||||||
|
#define _KILL_COMMAND_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <child_registry.h>
|
||||||
|
#include <process_arg_registry.h>
|
||||||
|
|
||||||
|
struct Kill_command : Command
|
||||||
|
{
|
||||||
|
Child_registry &_children;
|
||||||
|
|
||||||
|
Process_arg_registry &_process_args;
|
||||||
|
|
||||||
|
void _destroy_child(Child *child, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
tprintf(terminal, "destroying subsystem '%s'\n", child->name());
|
||||||
|
_process_args.list.remove(&child->argument);
|
||||||
|
_children.remove(child);
|
||||||
|
Genode::destroy(Genode::env()->heap(), child);
|
||||||
|
}
|
||||||
|
|
||||||
|
Kill_command(Child_registry &children, Process_arg_registry &process_args)
|
||||||
|
:
|
||||||
|
Command("kill", "destroy subsystem"),
|
||||||
|
_children(children),
|
||||||
|
_process_args(process_args)
|
||||||
|
{
|
||||||
|
add_parameter(new Parameter("--all", Parameter::VOID, "kill all subsystems"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
bool const kill_all = cmd.parameter_exists("--all");
|
||||||
|
|
||||||
|
if (kill_all) {
|
||||||
|
for (Child *child = _children.first(); child; child = _children.first())
|
||||||
|
_destroy_child(child, terminal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char label[128];
|
||||||
|
label[0] = 0;
|
||||||
|
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||||
|
tprintf(terminal, "Error: no subsystem name specified\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lookup child by its unique name */
|
||||||
|
for (Child *child = _children.first(); child; child = child->next()) {
|
||||||
|
if (strcmp(child->name(), label) == 0) {
|
||||||
|
_destroy_child(child, terminal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Argument> &arguments() { return _process_args.list; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _KILL_COMMAND_H_ */
|
@ -101,21 +101,25 @@ struct Command_line;
|
|||||||
*/
|
*/
|
||||||
struct Command : List<Command>::Element, Completable
|
struct Command : List<Command>::Element, Completable
|
||||||
{
|
{
|
||||||
List<Argument> _arguments;
|
|
||||||
List<Parameter> _parameters;
|
List<Parameter> _parameters;
|
||||||
|
|
||||||
Command(char const *name, char const *short_help)
|
Command(char const *name, char const *short_help)
|
||||||
: Completable(name, short_help) { }
|
: Completable(name, short_help) { }
|
||||||
|
|
||||||
void add_parameter(Parameter *par) { _parameters.insert(par); }
|
void add_parameter(Parameter *par) { _parameters.insert(par); }
|
||||||
void add_argument (Argument *arg) { _arguments. insert(arg); }
|
|
||||||
|
|
||||||
void remove_argument(Argument *arg) { _arguments.remove(arg); }
|
|
||||||
|
|
||||||
char const *name_suffix() const { return ""; }
|
char const *name_suffix() const { return ""; }
|
||||||
|
|
||||||
List<Parameter> ¶meters() { return _parameters; }
|
List<Parameter> ¶meters() { return _parameters; }
|
||||||
List<Argument> &arguments() { return _arguments; }
|
|
||||||
|
/**
|
||||||
|
* To be overridden by commands that accept auto-completion of arguments
|
||||||
|
*/
|
||||||
|
virtual List<Argument> &arguments()
|
||||||
|
{
|
||||||
|
static List<Argument> empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void execute(Command_line &, Terminal::Session &terminal) = 0;
|
virtual void execute(Command_line &, Terminal::Session &terminal) = 0;
|
||||||
};
|
};
|
||||||
@ -713,11 +717,21 @@ class Line_editor
|
|||||||
Terminal::Session &terminal, Command_registry &commands)
|
Terminal::Session &terminal, Command_registry &commands)
|
||||||
:
|
:
|
||||||
_prompt(prompt), _prompt_len(strlen(prompt)),
|
_prompt(prompt), _prompt_len(strlen(prompt)),
|
||||||
_buf(buf), _buf_size(buf_size), _cursor_pos(0),
|
_buf(buf), _buf_size(buf_size),
|
||||||
_terminal(terminal), _commands(commands),
|
_terminal(terminal), _commands(commands)
|
||||||
_complete(false)
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset prompt to initial state after construction
|
||||||
|
*/
|
||||||
|
void reset()
|
||||||
{
|
{
|
||||||
_buf[0] = 0;
|
_buf[0] = 0;
|
||||||
|
_complete = false;
|
||||||
|
_cursor_pos = 0;
|
||||||
|
_seq_tracker = Seq_tracker();
|
||||||
_fresh_prompt();
|
_fresh_prompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,499 +12,33 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <base/child.h>
|
|
||||||
#include <init/child_policy.h>
|
#include <init/child_policy.h>
|
||||||
#include <os/config.h>
|
#include <os/config.h>
|
||||||
#include <os/child_policy_dynamic_rom.h>
|
#include <os/child_policy_dynamic_rom.h>
|
||||||
#include <ram_session/connection.h>
|
|
||||||
#include <cpu_session/connection.h>
|
|
||||||
#include <rm_session/connection.h>
|
|
||||||
#include <cap_session/connection.h>
|
#include <cap_session/connection.h>
|
||||||
#include <rom_session/connection.h>
|
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <line_editor.h>
|
#include <line_editor.h>
|
||||||
#include <command_line.h>
|
#include <command_line.h>
|
||||||
#include <terminal_util.h>
|
#include <format_util.h>
|
||||||
#include <extension.h>
|
#include <extension.h>
|
||||||
|
#include <ram.h>
|
||||||
|
#include <status_command.h>
|
||||||
|
#include <kill_command.h>
|
||||||
|
#include <start_command.h>
|
||||||
|
#include <help_command.h>
|
||||||
|
#include <yield_command.h>
|
||||||
|
#include <ram_command.h>
|
||||||
|
|
||||||
using Terminal::tprintf;
|
|
||||||
using Genode::Xml_node;
|
using Genode::Xml_node;
|
||||||
|
|
||||||
|
|
||||||
/***************
|
|
||||||
** Utilities **
|
|
||||||
***************/
|
|
||||||
|
|
||||||
static inline void tprint_bytes(Terminal::Session &terminal, size_t bytes)
|
|
||||||
{
|
|
||||||
enum { KB = 1024, MB = 1024*KB };
|
|
||||||
if (bytes > MB) {
|
|
||||||
size_t const mb = bytes / MB;
|
|
||||||
|
|
||||||
tprintf(terminal, "%zd.", mb);
|
|
||||||
size_t const remainder = bytes - (mb * MB);
|
|
||||||
|
|
||||||
tprintf(terminal, "%zd MiB", (remainder*100)/(MB));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes > KB) {
|
|
||||||
size_t const kb = bytes / KB;
|
|
||||||
|
|
||||||
tprintf(terminal, "%zd.", kb);
|
|
||||||
size_t const remainder = bytes - (kb * KB);
|
|
||||||
|
|
||||||
tprintf(terminal, "%zd KiB", (remainder*100)/(KB));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tprintf(terminal, "%zd bytes", bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void tprint_status_bytes(Terminal::Session &terminal,
|
|
||||||
char const *label, size_t bytes)
|
|
||||||
{
|
|
||||||
tprintf(terminal, label);
|
|
||||||
tprint_bytes(terminal, bytes);
|
|
||||||
tprintf(terminal, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void *operator new (size_t size)
|
inline void *operator new (size_t size)
|
||||||
{
|
{
|
||||||
return Genode::env()->heap()->alloc(size);
|
return Genode::env()->heap()->alloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/********************
|
|
||||||
** Child handling **
|
|
||||||
********************/
|
|
||||||
|
|
||||||
class Child : public List<Child>::Element, 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 { };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct Label
|
|
||||||
{
|
|
||||||
enum { LABEL_MAX_LEN = 128 };
|
|
||||||
char buf[LABEL_MAX_LEN];
|
|
||||||
Label(char const *label) { strncpy(buf, label, sizeof(buf)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
Label const _label;
|
|
||||||
|
|
||||||
Argument _kill_argument;
|
|
||||||
|
|
||||||
struct Resources
|
|
||||||
{
|
|
||||||
Genode::Ram_connection ram;
|
|
||||||
Genode::Cpu_connection cpu;
|
|
||||||
Genode::Rm_connection rm;
|
|
||||||
|
|
||||||
Resources(const char *label, Genode::size_t ram_quota)
|
|
||||||
: 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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Child(char const *label,
|
|
||||||
char const *binary,
|
|
||||||
Genode::Cap_session &cap_session,
|
|
||||||
Genode::size_t ram_quota)
|
|
||||||
:
|
|
||||||
_label(label),
|
|
||||||
_kill_argument(label, "subsystem"),
|
|
||||||
_resources(_label.buf, ram_quota),
|
|
||||||
_binary_rom(binary, _label.buf),
|
|
||||||
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _label.buf, false),
|
|
||||||
_labeling_policy(_label.buf),
|
|
||||||
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
|
|
||||||
_config_policy("config", _entrypoint, &_resources.ram),
|
|
||||||
_child(_binary_rom.dataspace(),
|
|
||||||
_resources.ram.cap(), _resources.cpu.cap(),
|
|
||||||
_resources.rm.cap(), &_entrypoint, this)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void configure(char const *config, size_t config_len)
|
|
||||||
{
|
|
||||||
_config_policy.load(config, config_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void start()
|
|
||||||
{
|
|
||||||
_entrypoint.activate();
|
|
||||||
}
|
|
||||||
|
|
||||||
Argument *kill_argument() { return &_kill_argument; }
|
|
||||||
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
** Child_policy interface **
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
const char *name() const { return _label.buf; }
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Child_registry : public List<Child>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if a child with the specified name already exists
|
|
||||||
*/
|
|
||||||
bool _child_name_exists(const char *label)
|
|
||||||
{
|
|
||||||
for (Child *child = first() ; child; child = child->next())
|
|
||||||
if (strcmp(child->name(), label) == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum { CHILD_NAME_MAX_LEN = 64 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce new unique child name
|
|
||||||
*/
|
|
||||||
void unique_child_name(const char *prefix, char *dst, int dst_len)
|
|
||||||
{
|
|
||||||
char buf[CHILD_NAME_MAX_LEN];
|
|
||||||
char suffix[8];
|
|
||||||
suffix[0] = 0;
|
|
||||||
|
|
||||||
for (int cnt = 1; true; cnt++) {
|
|
||||||
|
|
||||||
/* build program name composed of prefix and numeric suffix */
|
|
||||||
snprintf(buf, sizeof(buf), "%s%s", prefix, suffix);
|
|
||||||
|
|
||||||
/* if such a program name does not exist yet, we are happy */
|
|
||||||
if (!_child_name_exists(buf)) {
|
|
||||||
strncpy(dst, buf, dst_len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* increase number of suffix */
|
|
||||||
snprintf(suffix, sizeof(suffix), ".%d", cnt + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**************
|
|
||||||
** Commands **
|
|
||||||
**************/
|
|
||||||
|
|
||||||
struct Help_command : Command
|
|
||||||
{
|
|
||||||
Help_command() : Command("help", "brief help information") { }
|
|
||||||
|
|
||||||
void execute(Command_line &, Terminal::Session &terminal)
|
|
||||||
{
|
|
||||||
tprintf(terminal, " Press [tab] for a list of commands.\n");
|
|
||||||
tprintf(terminal, " When given a command, press [tab] for a list of arguments.\n");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Kill_command : Command
|
|
||||||
{
|
|
||||||
Child_registry &_children;
|
|
||||||
|
|
||||||
void _destroy_child(Child *child, Terminal::Session &terminal)
|
|
||||||
{
|
|
||||||
tprintf(terminal, "destroying subsystem '%s'\n", child->name());
|
|
||||||
remove_argument(child->kill_argument());
|
|
||||||
_children.remove(child);
|
|
||||||
Genode::destroy(Genode::env()->heap(), child);
|
|
||||||
}
|
|
||||||
|
|
||||||
Kill_command(Child_registry &children)
|
|
||||||
:
|
|
||||||
Command("kill", "destroy subsystem"),
|
|
||||||
_children(children)
|
|
||||||
{
|
|
||||||
add_parameter(new Parameter("--all", Parameter::VOID, "kill all subsystems"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
|
||||||
{
|
|
||||||
bool const kill_all = cmd.parameter_exists("--all");
|
|
||||||
|
|
||||||
if (kill_all) {
|
|
||||||
for (Child *child = _children.first(); child; child = _children.first())
|
|
||||||
_destroy_child(child, terminal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char label[128];
|
|
||||||
label[0] = 0;
|
|
||||||
if (cmd.argument(0, label, sizeof(label)) == false) {
|
|
||||||
tprintf(terminal, "Error: no configuration name specified\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* lookup child by its unique name */
|
|
||||||
for (Child *child = _children.first(); child; child = child->next()) {
|
|
||||||
if (strcmp(child->name(), label) == 0) {
|
|
||||||
_destroy_child(child, terminal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Start_command : Command
|
|
||||||
{
|
|
||||||
Child_registry &_children;
|
|
||||||
Genode::Cap_session &_cap;
|
|
||||||
Xml_node _config;
|
|
||||||
Kill_command &_kill_command;
|
|
||||||
|
|
||||||
Start_command(Genode::Cap_session &cap, Child_registry &children,
|
|
||||||
Xml_node config, Kill_command &kill_command)
|
|
||||||
:
|
|
||||||
Command("start", "create new subsystem"),
|
|
||||||
_children(children), _cap(cap), _config(config),
|
|
||||||
_kill_command(kill_command)
|
|
||||||
{
|
|
||||||
/* scan config for possible subsystem arguments */
|
|
||||||
try {
|
|
||||||
Xml_node node = _config.sub_node("subsystem");
|
|
||||||
for (;; node = node.next("subsystem")) {
|
|
||||||
|
|
||||||
char name[Parameter::NAME_MAX_LEN];
|
|
||||||
try { node.attribute("name").value(name, sizeof(name)); }
|
|
||||||
catch (Xml_node::Nonexistent_attribute) {
|
|
||||||
PWRN("Missing name in '<subsystem>' configuration");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *prefix = "config: ";
|
|
||||||
size_t const prefix_len = strlen(prefix);
|
|
||||||
|
|
||||||
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
|
|
||||||
strncpy(help, prefix, ~0);
|
|
||||||
try { node.attribute("help").value(help + prefix_len,
|
|
||||||
sizeof(help) - prefix_len); }
|
|
||||||
catch (Xml_node::Nonexistent_attribute) {
|
|
||||||
PWRN("Missing help in '<subsystem>' configuration");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_argument(new Argument(name, help));
|
|
||||||
}
|
|
||||||
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
|
|
||||||
|
|
||||||
add_parameter(new Parameter("--count", Parameter::NUMBER, "number of instances"));
|
|
||||||
add_parameter(new Parameter("--ram", Parameter::NUMBER, "RAM quota"));
|
|
||||||
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup subsystem in config
|
|
||||||
*/
|
|
||||||
Xml_node _subsystem_node(char const *name)
|
|
||||||
{
|
|
||||||
Xml_node node = _config.sub_node("subsystem");
|
|
||||||
for (;; node = node.next("subsystem")) {
|
|
||||||
if (node.attribute("name").has_value(name))
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
|
||||||
{
|
|
||||||
size_t count = 1;
|
|
||||||
Genode::Number_of_bytes ram = 0;
|
|
||||||
|
|
||||||
char name[128];
|
|
||||||
name[0] = 0;
|
|
||||||
if (cmd.argument(0, name, sizeof(name)) == false) {
|
|
||||||
tprintf(terminal, "Error: no configuration name specified\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char buf[128];
|
|
||||||
if (cmd.argument(1, buf, sizeof(buf))) {
|
|
||||||
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if a configuration for the subsystem exists */
|
|
||||||
try { _subsystem_node(name); }
|
|
||||||
catch (Xml_node::Nonexistent_sub_node) {
|
|
||||||
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read default RAM quota from config */
|
|
||||||
try {
|
|
||||||
Xml_node rsc = _subsystem_node(name).sub_node("resource");
|
|
||||||
for (;; rsc = rsc.next("resource")) {
|
|
||||||
if (rsc.attribute("name").has_value("RAM")) {
|
|
||||||
rsc.attribute("quantum").value(&ram);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (...) { }
|
|
||||||
|
|
||||||
cmd.parameter("--count", count);
|
|
||||||
cmd.parameter("--ram", ram);
|
|
||||||
|
|
||||||
bool const verbose = cmd.parameter_exists("--verbose");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine binary name
|
|
||||||
*
|
|
||||||
* Use subsystem name by default, override with '<binary>' declaration.
|
|
||||||
*/
|
|
||||||
char binary_name[128];
|
|
||||||
strncpy(binary_name, name, sizeof(binary_name));
|
|
||||||
try {
|
|
||||||
Xml_node bin = _subsystem_node(name).sub_node("binary");
|
|
||||||
bin.attribute("name").value(binary_name, sizeof(binary_name));
|
|
||||||
} catch (...) { }
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < count; i++) {
|
|
||||||
|
|
||||||
/* generate unique child name */
|
|
||||||
char label[Child_registry::CHILD_NAME_MAX_LEN];
|
|
||||||
_children.unique_child_name(name, label, sizeof(label));
|
|
||||||
|
|
||||||
tprintf(terminal, "starting new subsystem '%s'\n", label);
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
tprintf(terminal, " RAM quota: ");
|
|
||||||
tprint_bytes(terminal, ram);
|
|
||||||
tprintf(terminal,"\n");
|
|
||||||
tprintf(terminal, " binary: %s\n", binary_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Child *child = 0;
|
|
||||||
try {
|
|
||||||
child = new (Genode::env()->heap())
|
|
||||||
Child(label, binary_name, _cap, ram);
|
|
||||||
}
|
|
||||||
catch (Genode::Rom_connection::Rom_connection_failed) {
|
|
||||||
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
|
|
||||||
binary_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Child::Quota_exceeded) {
|
|
||||||
tprintf(terminal, "Error: insufficient memory, need ");
|
|
||||||
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
|
|
||||||
tprintf(terminal, ", have ");
|
|
||||||
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
|
|
||||||
tprintf(terminal, "\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Genode::Allocator::Out_of_memory) {
|
|
||||||
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* configure child */
|
|
||||||
try {
|
|
||||||
Xml_node config_node = _subsystem_node(name).sub_node("config");
|
|
||||||
child->configure(config_node.addr(), config_node.size());
|
|
||||||
if (verbose)
|
|
||||||
tprintf(terminal, " config: inline\n");
|
|
||||||
} catch (...) {
|
|
||||||
if (verbose)
|
|
||||||
tprintf(terminal, " config: none\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
_kill_command.add_argument(child->kill_argument());
|
|
||||||
_children.insert(child);
|
|
||||||
child->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Status_command : Command
|
|
||||||
{
|
|
||||||
Status_command() : Command("status", "show runtime status") { }
|
|
||||||
|
|
||||||
void execute(Command_line &, Terminal::Session &terminal)
|
|
||||||
{
|
|
||||||
Genode::Ram_session *ram = Genode::env()->ram_session();
|
|
||||||
|
|
||||||
tprint_status_bytes(terminal, " RAM quota: ", ram->quota());
|
|
||||||
tprint_status_bytes(terminal, " used: ", ram->used());
|
|
||||||
tprint_status_bytes(terminal, " avail: ", ram->avail());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/******************
|
/******************
|
||||||
** Main program **
|
** Main program **
|
||||||
******************/
|
******************/
|
||||||
@ -520,6 +54,21 @@ static inline Command *lookup_command(char const *buf, Command_registry ®istr
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static size_t ram_preservation_from_config()
|
||||||
|
{
|
||||||
|
Genode::Number_of_bytes ram_preservation = 0;
|
||||||
|
try {
|
||||||
|
Genode::Xml_node node =
|
||||||
|
Genode::config()->xml_node().sub_node("preservation");
|
||||||
|
|
||||||
|
if (node.attribute("name").has_value("RAM"))
|
||||||
|
node.attribute("quantum").value(&ram_preservation);
|
||||||
|
} catch (...) { }
|
||||||
|
|
||||||
|
return ram_preservation;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
/* look for dynamic linker */
|
/* look for dynamic linker */
|
||||||
@ -529,46 +78,55 @@ int main(int argc, char **argv)
|
|||||||
} catch (...) { }
|
} catch (...) { }
|
||||||
|
|
||||||
using Genode::Signal_context;
|
using Genode::Signal_context;
|
||||||
|
using Genode::Signal_context_capability;
|
||||||
using Genode::Signal_receiver;
|
using Genode::Signal_receiver;
|
||||||
|
|
||||||
try { Genode::config()->xml_node(); }
|
|
||||||
catch (...) {
|
|
||||||
PERR("Error: could not obtain configuration");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Genode::Cap_connection cap;
|
static Genode::Cap_connection cap;
|
||||||
static Terminal::Connection terminal;
|
static Terminal::Connection terminal;
|
||||||
static Command_registry commands;
|
static Command_registry commands;
|
||||||
static Child_registry children;
|
static Child_registry children;
|
||||||
|
static Process_arg_registry process_args;
|
||||||
|
|
||||||
/* initialize platform-specific commands */
|
/* initialize platform-specific commands */
|
||||||
init_extension(commands);
|
init_extension(commands);
|
||||||
|
|
||||||
/* initialize generic commands */
|
|
||||||
commands.insert(new Help_command);
|
|
||||||
Kill_command kill_command(children);
|
|
||||||
commands.insert(&kill_command);
|
|
||||||
commands.insert(new Start_command(cap, children,
|
|
||||||
Genode::config()->xml_node(),
|
|
||||||
kill_command));
|
|
||||||
commands.insert(new Status_command);
|
|
||||||
|
|
||||||
static Signal_receiver sig_rec;
|
static Signal_receiver sig_rec;
|
||||||
static Signal_context read_avail_sig_ctx;
|
static Signal_context read_avail_sig_ctx;
|
||||||
terminal.read_avail_sigh(sig_rec.manage(&read_avail_sig_ctx));
|
terminal.read_avail_sigh(sig_rec.manage(&read_avail_sig_ctx));
|
||||||
|
|
||||||
|
static Signal_context yield_response_sig_ctx;
|
||||||
|
static Signal_context_capability yield_response_sig_cap =
|
||||||
|
sig_rec.manage(&yield_response_sig_ctx);
|
||||||
|
|
||||||
|
static Signal_context yield_broadcast_sig_ctx;
|
||||||
|
static Signal_context resource_avail_sig_ctx;
|
||||||
|
|
||||||
|
static Ram ram(ram_preservation_from_config(),
|
||||||
|
sig_rec.manage(&yield_broadcast_sig_ctx),
|
||||||
|
sig_rec.manage(&resource_avail_sig_ctx));
|
||||||
|
|
||||||
|
/* initialize generic commands */
|
||||||
|
commands.insert(new Help_command);
|
||||||
|
Kill_command kill_command(children, process_args);
|
||||||
|
commands.insert(&kill_command);
|
||||||
|
commands.insert(new Start_command(ram, cap, children,
|
||||||
|
Genode::config()->xml_node(),
|
||||||
|
process_args,
|
||||||
|
yield_response_sig_cap));
|
||||||
|
commands.insert(new Status_command(ram, children));
|
||||||
|
commands.insert(new Yield_command(children, process_args));
|
||||||
|
commands.insert(new Ram_command(children, process_args));
|
||||||
|
|
||||||
|
enum { COMMAND_MAX_LEN = 1000 };
|
||||||
|
static char buf[COMMAND_MAX_LEN];
|
||||||
|
static Line_editor line_editor("genode> ", buf, sizeof(buf), terminal, commands);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
enum { COMMAND_MAX_LEN = 1000 };
|
/* block for event, e.g., the arrival of new user input */
|
||||||
static char buf[COMMAND_MAX_LEN];
|
Genode::Signal signal = sig_rec.wait_for_signal();
|
||||||
|
|
||||||
Line_editor line_editor("genode> ", buf, sizeof(buf), terminal, commands);
|
if (signal.context() == &read_avail_sig_ctx) {
|
||||||
|
|
||||||
while (!line_editor.is_complete()) {
|
|
||||||
|
|
||||||
/* block for event, e.g., the arrival of new user input */
|
|
||||||
sig_rec.wait_for_signal();
|
|
||||||
|
|
||||||
/* supply pending terminal input to line editor */
|
/* supply pending terminal input to line editor */
|
||||||
while (terminal.avail() && !line_editor.is_complete()) {
|
while (terminal.avail() && !line_editor.is_complete()) {
|
||||||
@ -578,12 +136,44 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signal.context() == &yield_response_sig_ctx
|
||||||
|
|| signal.context() == &resource_avail_sig_ctx) {
|
||||||
|
|
||||||
|
for (Child *child = children.first(); child; child = child->next())
|
||||||
|
child->try_response_to_resource_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.context() == &yield_broadcast_sig_ctx) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute argument of yield request to be broadcasted to all
|
||||||
|
* processes.
|
||||||
|
*/
|
||||||
|
size_t amount = 0;
|
||||||
|
|
||||||
|
/* amount needed to reach preservation limit */
|
||||||
|
Ram::Status ram_status = ram.status();
|
||||||
|
if (ram_status.avail < ram_status.preserve)
|
||||||
|
amount += ram_status.preserve - ram_status.avail;
|
||||||
|
|
||||||
|
/* sum of pending resource requests */
|
||||||
|
for (Child *child = children.first(); child; child = child->next())
|
||||||
|
amount += child->requested_ram_quota();
|
||||||
|
|
||||||
|
for (Child *child = children.first(); child; child = child->next())
|
||||||
|
child->yield(amount, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line_editor.is_complete())
|
||||||
|
continue;
|
||||||
|
|
||||||
Command *command = lookup_command(buf, commands);
|
Command *command = lookup_command(buf, commands);
|
||||||
if (!command) {
|
if (!command) {
|
||||||
Token cmd_name(buf);
|
Token cmd_name(buf);
|
||||||
tprintf(terminal, "Error: unknown command \"");
|
tprintf(terminal, "Error: unknown command \"");
|
||||||
terminal.write(cmd_name.start(), cmd_name.len());
|
terminal.write(cmd_name.start(), cmd_name.len());
|
||||||
tprintf(terminal, "\"\n");
|
tprintf(terminal, "\"\n");
|
||||||
|
line_editor.reset();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,9 +184,17 @@ int main(int argc, char **argv)
|
|||||||
tprintf(terminal, "Error: unexpected parameter \"");
|
tprintf(terminal, "Error: unexpected parameter \"");
|
||||||
terminal.write(unexpected.start(), unexpected.len());
|
terminal.write(unexpected.start(), unexpected.len());
|
||||||
tprintf(terminal, "\"\n");
|
tprintf(terminal, "\"\n");
|
||||||
|
line_editor.reset();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
command->execute(cmd_line, terminal);
|
command->execute(cmd_line, terminal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The command might result in a change of the RAM usage. Validate
|
||||||
|
* that the preservation is satisfied.
|
||||||
|
*/
|
||||||
|
ram.validate_preservation();
|
||||||
|
line_editor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
25
os/src/app/cli_monitor/process_arg_registry.h
Normal file
25
os/src/app/cli_monitor/process_arg_registry.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* \brief Registry of process names used as arguments
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-03-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _PROCESS_ARG_REGISTRY_H_
|
||||||
|
#define _PROCESS_ARG_REGISTRY_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry of arguments referring to the currently running processes
|
||||||
|
*/
|
||||||
|
struct Process_arg_registry
|
||||||
|
{
|
||||||
|
Genode::List<Argument> list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _PROCESS_ARG_REGISTRY_H_ */
|
123
os/src/app/cli_monitor/ram.h
Normal file
123
os/src/app/cli_monitor/ram.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* \brief RAM management
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-10-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _RAM_H_
|
||||||
|
#define _RAM_H_
|
||||||
|
|
||||||
|
class Ram
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Genode::Lock mutable _lock;
|
||||||
|
Genode::Ram_session &_ram = *Genode::env()->ram_session();
|
||||||
|
Genode::Ram_session_capability _ram_cap = Genode::env()->ram_session_cap();
|
||||||
|
Genode::Signal_context_capability _yield_sigh;
|
||||||
|
Genode::Signal_context_capability _resource_avail_sigh;
|
||||||
|
|
||||||
|
size_t _preserve;
|
||||||
|
|
||||||
|
void _validate_preservation()
|
||||||
|
{
|
||||||
|
if (_ram.avail() < _preserve)
|
||||||
|
Genode::Signal_transmitter(_yield_sigh).submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Status
|
||||||
|
{
|
||||||
|
size_t quota, used, avail, preserve;
|
||||||
|
Status(size_t quota, size_t used, size_t avail, size_t preserve)
|
||||||
|
: quota(quota), used(used), avail(avail), preserve(preserve) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
Ram(size_t preserve,
|
||||||
|
Genode::Signal_context_capability yield_sigh,
|
||||||
|
Genode::Signal_context_capability resource_avail_sigh)
|
||||||
|
:
|
||||||
|
_yield_sigh(yield_sigh), _preserve(preserve)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
size_t preserve() const
|
||||||
|
{
|
||||||
|
Genode::Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
return _preserve;
|
||||||
|
}
|
||||||
|
|
||||||
|
void preserve(size_t preserve)
|
||||||
|
{
|
||||||
|
Genode::Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
_preserve = preserve;
|
||||||
|
|
||||||
|
_validate_preservation();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status status() const
|
||||||
|
{
|
||||||
|
Genode::Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
return Status(_ram.quota(), _ram.used(), _ram.avail(), _preserve);
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate_preservation()
|
||||||
|
{
|
||||||
|
Genode::Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
_validate_preservation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception type
|
||||||
|
*/
|
||||||
|
class Transfer_quota_failed { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \throw Transfer_quota_failed
|
||||||
|
*/
|
||||||
|
void withdraw_from(Genode::Ram_session_capability from, size_t amount)
|
||||||
|
{
|
||||||
|
Genode::Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
int const ret =
|
||||||
|
Genode::Ram_session_client(from).transfer_quota(_ram_cap, amount);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
throw Transfer_quota_failed();
|
||||||
|
|
||||||
|
Genode::Signal_transmitter(_resource_avail_sigh).submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \throw Transfer_quota_failed
|
||||||
|
*/
|
||||||
|
void transfer_to(Genode::Ram_session_capability to, size_t amount)
|
||||||
|
{
|
||||||
|
Genode::Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
int const ret = _ram.transfer_quota(to, amount);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
throw Transfer_quota_failed();
|
||||||
|
|
||||||
|
_validate_preservation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return singleton object
|
||||||
|
*/
|
||||||
|
static Ram &ram();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _RAM_H_ */
|
123
os/src/app/cli_monitor/ram_command.h
Normal file
123
os/src/app/cli_monitor/ram_command.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* \brief RAM command
|
||||||
|
* \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 _RAM_COMMAND_H_
|
||||||
|
#define _RAM_COMMAND_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <child_registry.h>
|
||||||
|
#include <process_arg_registry.h>
|
||||||
|
|
||||||
|
struct Ram_command : Command
|
||||||
|
{
|
||||||
|
Child_registry &_children;
|
||||||
|
Process_arg_registry &_process_args;
|
||||||
|
|
||||||
|
Ram_command(Child_registry &children, Process_arg_registry &process_args)
|
||||||
|
:
|
||||||
|
Command("ram", "set RAM quota of subsystem"),
|
||||||
|
_children(children),
|
||||||
|
_process_args(process_args)
|
||||||
|
{
|
||||||
|
add_parameter(new Parameter("--quota", Parameter::NUMBER, "new RAM quota"));
|
||||||
|
add_parameter(new Parameter("--limit", Parameter::NUMBER, "on-demand quota limit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _set_quota(Terminal::Session &terminal, Child &child, size_t const new_quota)
|
||||||
|
{
|
||||||
|
size_t const old_quota = child.ram_status().quota;
|
||||||
|
|
||||||
|
if (new_quota > old_quota) {
|
||||||
|
|
||||||
|
size_t amount = new_quota - old_quota;
|
||||||
|
size_t const avail = Genode::env()->ram_session()->avail();
|
||||||
|
if (amount > avail) {
|
||||||
|
tprintf(terminal, "upgrade of '%s' exceeds available quota of ",
|
||||||
|
child.name());
|
||||||
|
tprint_bytes(terminal, avail);
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
amount = avail;
|
||||||
|
}
|
||||||
|
|
||||||
|
tprintf(terminal, "upgrading quota of '%s' to ", child.name());
|
||||||
|
tprint_bytes(terminal, old_quota + amount);
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
child.upgrade_ram_quota(amount); }
|
||||||
|
catch (Ram::Transfer_quota_failed) {
|
||||||
|
tprintf(terminal, "Error: transfer_quota failed\n"); }
|
||||||
|
|
||||||
|
} if (new_quota < old_quota) {
|
||||||
|
|
||||||
|
size_t amount = old_quota - new_quota;
|
||||||
|
size_t const avail = child.ram_status().avail;
|
||||||
|
|
||||||
|
if (amount > avail) {
|
||||||
|
tprintf(terminal, "withdrawal of ");
|
||||||
|
tprint_bytes(terminal, amount);
|
||||||
|
tprintf(terminal, " exceeds available quota of ");
|
||||||
|
tprint_bytes(terminal, avail);
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
amount = avail;
|
||||||
|
}
|
||||||
|
|
||||||
|
tprintf(terminal, "depleting quota of '%s' to ", child.name());
|
||||||
|
tprint_bytes(terminal, old_quota - amount);
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
child.withdraw_ram_quota(amount); }
|
||||||
|
catch (Ram::Transfer_quota_failed) {
|
||||||
|
tprintf(terminal, "Error: transfer_quota failed\n"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
char label[128];
|
||||||
|
label[0] = 0;
|
||||||
|
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||||
|
tprintf(terminal, "Error: no subsystem name specified\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lookup child by its unique name */
|
||||||
|
Child *child = _children.first();
|
||||||
|
for (; child; child = child->next())
|
||||||
|
if (strcmp(child->name(), label) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!child) {
|
||||||
|
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool const limit_specified = cmd.parameter_exists("--limit");
|
||||||
|
Genode::Number_of_bytes limit = 0;
|
||||||
|
if (limit_specified) {
|
||||||
|
cmd.parameter("--limit", limit);
|
||||||
|
child->ram_limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.parameter_exists("--quota")) {
|
||||||
|
Genode::Number_of_bytes quota = 0;
|
||||||
|
cmd.parameter("--quota", quota);
|
||||||
|
_set_quota(terminal, *child, quota);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Argument> &arguments() { return _process_args.list; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _RAM_COMMAND_H_ */
|
216
os/src/app/cli_monitor/start_command.h
Normal file
216
os/src/app/cli_monitor/start_command.h
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* \brief Start command
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-03-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _START_COMMAND_H_
|
||||||
|
#define _START_COMMAND_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
struct Start_command : Command
|
||||||
|
{
|
||||||
|
typedef Genode::Xml_node Xml_node;
|
||||||
|
typedef Genode::Signal_context_capability Signal_context_capability;
|
||||||
|
|
||||||
|
Ram &_ram;
|
||||||
|
Child_registry &_children;
|
||||||
|
Genode::Cap_session &_cap;
|
||||||
|
Xml_node _config;
|
||||||
|
Process_arg_registry &_process_args;
|
||||||
|
List<Argument> _arguments;
|
||||||
|
Signal_context_capability _yield_response_sigh_cap;
|
||||||
|
|
||||||
|
Start_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
|
||||||
|
Xml_node config, Process_arg_registry &process_args,
|
||||||
|
Signal_context_capability yield_response_sigh_cap)
|
||||||
|
:
|
||||||
|
Command("start", "create new subsystem"),
|
||||||
|
_ram(ram), _children(children), _cap(cap), _config(config),
|
||||||
|
_process_args(process_args),
|
||||||
|
_yield_response_sigh_cap(yield_response_sigh_cap)
|
||||||
|
{
|
||||||
|
/* scan config for possible subsystem arguments */
|
||||||
|
try {
|
||||||
|
Xml_node node = _config.sub_node("subsystem");
|
||||||
|
for (;; node = node.next("subsystem")) {
|
||||||
|
|
||||||
|
char name[Parameter::NAME_MAX_LEN];
|
||||||
|
try { node.attribute("name").value(name, sizeof(name)); }
|
||||||
|
catch (Xml_node::Nonexistent_attribute) {
|
||||||
|
PWRN("Missing name in '<subsystem>' configuration");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const *prefix = "config: ";
|
||||||
|
size_t const prefix_len = strlen(prefix);
|
||||||
|
|
||||||
|
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
|
||||||
|
strncpy(help, prefix, ~0);
|
||||||
|
try { node.attribute("help").value(help + prefix_len,
|
||||||
|
sizeof(help) - prefix_len); }
|
||||||
|
catch (Xml_node::Nonexistent_attribute) {
|
||||||
|
PWRN("Missing help in '<subsystem>' configuration");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_arguments.insert(new Argument(name, help));
|
||||||
|
}
|
||||||
|
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
|
||||||
|
|
||||||
|
add_parameter(new Parameter("--count", Parameter::NUMBER, "number of instances"));
|
||||||
|
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
|
||||||
|
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
|
||||||
|
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup subsystem in config
|
||||||
|
*/
|
||||||
|
Xml_node _subsystem_node(char const *name)
|
||||||
|
{
|
||||||
|
Xml_node node = _config.sub_node("subsystem");
|
||||||
|
for (;; node = node.next("subsystem")) {
|
||||||
|
if (node.attribute("name").has_value(name))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
size_t count = 1;
|
||||||
|
Genode::Number_of_bytes ram = 0;
|
||||||
|
Genode::Number_of_bytes ram_limit = 0;
|
||||||
|
|
||||||
|
char name[128];
|
||||||
|
name[0] = 0;
|
||||||
|
if (cmd.argument(0, name, sizeof(name)) == false) {
|
||||||
|
tprintf(terminal, "Error: no configuration name specified\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[128];
|
||||||
|
if (cmd.argument(1, buf, sizeof(buf))) {
|
||||||
|
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if a configuration for the subsystem exists */
|
||||||
|
try { _subsystem_node(name); }
|
||||||
|
catch (Xml_node::Nonexistent_sub_node) {
|
||||||
|
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read default RAM quota from config */
|
||||||
|
try {
|
||||||
|
Xml_node rsc = _subsystem_node(name).sub_node("resource");
|
||||||
|
for (;; rsc = rsc.next("resource")) {
|
||||||
|
if (rsc.attribute("name").has_value("RAM")) {
|
||||||
|
rsc.attribute("quantum").value(&ram);
|
||||||
|
|
||||||
|
if (rsc.has_attribute("limit"))
|
||||||
|
rsc.attribute("limit").value(&ram_limit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (...) { }
|
||||||
|
|
||||||
|
cmd.parameter("--count", count);
|
||||||
|
cmd.parameter("--ram", ram);
|
||||||
|
cmd.parameter("--ram-limit", ram_limit);
|
||||||
|
|
||||||
|
size_t preserve_ram = 1*1024*1024;
|
||||||
|
if (ram + preserve_ram > Genode::env()->ram_session()->avail()) {
|
||||||
|
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool const verbose = cmd.parameter_exists("--verbose");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine binary name
|
||||||
|
*
|
||||||
|
* Use subsystem name by default, override with '<binary>' declaration.
|
||||||
|
*/
|
||||||
|
char binary_name[128];
|
||||||
|
strncpy(binary_name, name, sizeof(binary_name));
|
||||||
|
try {
|
||||||
|
Xml_node bin = _subsystem_node(name).sub_node("binary");
|
||||||
|
bin.attribute("name").value(binary_name, sizeof(binary_name));
|
||||||
|
} catch (...) { }
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < count; i++) {
|
||||||
|
|
||||||
|
/* generate unique child name */
|
||||||
|
char label[Child_registry::CHILD_NAME_MAX_LEN];
|
||||||
|
_children.unique_child_name(name, label, sizeof(label));
|
||||||
|
|
||||||
|
tprintf(terminal, "starting new subsystem '%s'\n", label);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
tprintf(terminal, " RAM quota: ");
|
||||||
|
tprint_bytes(terminal, ram);
|
||||||
|
tprintf(terminal,"\n");
|
||||||
|
if (ram_limit) {
|
||||||
|
tprintf(terminal, " RAM limit: ");
|
||||||
|
tprint_bytes(terminal, ram_limit);
|
||||||
|
tprintf(terminal,"\n");
|
||||||
|
}
|
||||||
|
tprintf(terminal, " binary: %s\n", binary_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Child *child = 0;
|
||||||
|
try {
|
||||||
|
child = new (Genode::env()->heap())
|
||||||
|
Child(_ram, label, binary_name, _cap, ram, ram_limit,
|
||||||
|
_yield_response_sigh_cap);
|
||||||
|
}
|
||||||
|
catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||||
|
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
|
||||||
|
binary_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Child::Quota_exceeded) {
|
||||||
|
tprintf(terminal, "Error: insufficient memory, need ");
|
||||||
|
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
|
||||||
|
tprintf(terminal, ", have ");
|
||||||
|
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Genode::Allocator::Out_of_memory) {
|
||||||
|
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* configure child */
|
||||||
|
try {
|
||||||
|
Xml_node config_node = _subsystem_node(name).sub_node("config");
|
||||||
|
child->configure(config_node.addr(), config_node.size());
|
||||||
|
if (verbose)
|
||||||
|
tprintf(terminal, " config: inline\n");
|
||||||
|
} catch (...) {
|
||||||
|
if (verbose)
|
||||||
|
tprintf(terminal, " config: none\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
_process_args.list.insert(&child->argument);
|
||||||
|
_children.insert(child);
|
||||||
|
child->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Argument> &arguments() { return _arguments; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _START_COMMAND_H_ */
|
154
os/src/app/cli_monitor/status_command.h
Normal file
154
os/src/app/cli_monitor/status_command.h
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* \brief Status command
|
||||||
|
* \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 _STATUS_COMMAND_H_
|
||||||
|
#define _STATUS_COMMAND_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <table.h>
|
||||||
|
#include <child_registry.h>
|
||||||
|
|
||||||
|
struct Status_command : Command
|
||||||
|
{
|
||||||
|
Child_registry &_children;
|
||||||
|
Ram &_ram;
|
||||||
|
|
||||||
|
Status_command(Ram &ram, Child_registry &children)
|
||||||
|
:
|
||||||
|
Command("status", "show runtime status"),
|
||||||
|
_children(children), _ram(ram)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void execute(Command_line &, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
using Terminal::tprintf;
|
||||||
|
|
||||||
|
Ram::Status const ram_status = _ram.status();
|
||||||
|
|
||||||
|
tprint_status_bytes(terminal, " RAM quota: ", ram_status.quota);
|
||||||
|
tprint_status_bytes(terminal, " used: ", ram_status.used);
|
||||||
|
tprint_status_bytes(terminal, " avail: ", ram_status.avail);
|
||||||
|
tprint_status_bytes(terminal, " preserve: ", ram_status.preserve);
|
||||||
|
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
|
||||||
|
struct Child_info
|
||||||
|
{
|
||||||
|
enum Column { NAME, QUOTA, LIMIT, XFER, USED, AVAIL, STATUS };
|
||||||
|
|
||||||
|
constexpr static size_t num_columns() { return STATUS + 1; }
|
||||||
|
|
||||||
|
char const *name = 0;
|
||||||
|
Child::Ram_status ram_status;
|
||||||
|
|
||||||
|
static char const *label(Column column)
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case NAME: return "process";
|
||||||
|
case QUOTA: return "quota";
|
||||||
|
case LIMIT: return "limit";
|
||||||
|
case XFER: return "xfer";
|
||||||
|
case USED: return "alloc";
|
||||||
|
case AVAIL: return "avail";
|
||||||
|
case STATUS: return "status";
|
||||||
|
};
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len(Column column) const
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case NAME: return strlen(name);
|
||||||
|
case QUOTA: return format_mib(ram_status.quota);
|
||||||
|
case LIMIT:
|
||||||
|
return ram_status.limit ? format_mib(ram_status.limit) : 0;
|
||||||
|
|
||||||
|
case XFER: return format_mib(ram_status.xfer);
|
||||||
|
case USED: return format_mib(ram_status.used);
|
||||||
|
case AVAIL: return format_mib(ram_status.avail);
|
||||||
|
case STATUS:
|
||||||
|
{
|
||||||
|
size_t const req = ram_status.req;
|
||||||
|
return req ? strlen("req ") + format_mib(req) : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool left_aligned(Column column)
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case NAME: return true;
|
||||||
|
case QUOTA: return false;
|
||||||
|
case LIMIT: return false;
|
||||||
|
case XFER: return false;
|
||||||
|
case USED: return false;
|
||||||
|
case AVAIL: return false;
|
||||||
|
case STATUS: return true;
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_cell(Terminal::Session &terminal, Column column)
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case NAME: tprintf(terminal, "%s", name); break;
|
||||||
|
case QUOTA: tprint_mib(terminal, ram_status.quota); break;
|
||||||
|
case LIMIT:
|
||||||
|
|
||||||
|
if (ram_status.limit)
|
||||||
|
tprint_mib(terminal, ram_status.limit);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XFER: tprint_mib(terminal, ram_status.xfer); break;
|
||||||
|
case USED: tprint_mib(terminal, ram_status.used); break;
|
||||||
|
case AVAIL: tprint_mib(terminal, ram_status.avail); break;
|
||||||
|
case STATUS:
|
||||||
|
if (ram_status.req) {
|
||||||
|
tprintf(terminal, "req ");
|
||||||
|
tprint_mib(terminal, ram_status.req);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Child_info() { }
|
||||||
|
Child_info(char const *name, Child::Ram_status ram_status)
|
||||||
|
:
|
||||||
|
name(name), ram_status(ram_status)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take snapshot of child information.
|
||||||
|
*/
|
||||||
|
size_t num_children = 0;
|
||||||
|
for (Child *c = _children.first(); c; c = c->next())
|
||||||
|
num_children++;
|
||||||
|
|
||||||
|
Child_info child_info[num_children];
|
||||||
|
unsigned i = 0;
|
||||||
|
for (Child *c = _children.first(); c && i < num_children; c = c->next(), i++)
|
||||||
|
child_info[i] = Child_info(c->name(), c->ram_status());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print table
|
||||||
|
*/
|
||||||
|
if (num_children) {
|
||||||
|
Table<Child_info>::print(terminal, child_info, num_children);
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _STATUS_COMMAND_H_ */
|
96
os/src/app/cli_monitor/table.h
Normal file
96
os/src/app/cli_monitor/table.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* \brief Utility for printing a table to the terminal
|
||||||
|
* \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 _TABLE_H_
|
||||||
|
#define _TABLE_H_
|
||||||
|
|
||||||
|
template <typename TI>
|
||||||
|
class Table
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
static void _print_cell(TI &info, Terminal::Session &terminal,
|
||||||
|
typename TI::Column column, size_t column_size)
|
||||||
|
{
|
||||||
|
size_t const padding = column_size - info.len(column);
|
||||||
|
|
||||||
|
if (!TI::left_aligned(column))
|
||||||
|
tprint_padding(terminal, padding);
|
||||||
|
|
||||||
|
info.print_cell(terminal, column);
|
||||||
|
|
||||||
|
if (TI::left_aligned(column))
|
||||||
|
tprint_padding(terminal, padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print centered title of table column
|
||||||
|
*/
|
||||||
|
static void _print_label(Terminal::Session &terminal,
|
||||||
|
typename TI::Column column, size_t column_size)
|
||||||
|
{
|
||||||
|
size_t const padding = column_size - strlen(TI::label(column));
|
||||||
|
size_t const left_padding = padding / 2;
|
||||||
|
|
||||||
|
tprint_padding(terminal, left_padding);
|
||||||
|
tprintf(terminal, "%s", TI::label(column));
|
||||||
|
tprint_padding(terminal, padding - left_padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void print(Terminal::Session &terminal, TI info[], unsigned num_rows)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Determine formatting of table
|
||||||
|
*/
|
||||||
|
size_t column_size[TI::num_columns()];
|
||||||
|
for (unsigned i = 0; i < TI::num_columns(); i++)
|
||||||
|
column_size[i] = strlen(TI::label((typename TI::Column)i));
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_rows; i++) {
|
||||||
|
for (unsigned j = 0; j < TI::num_columns(); j++)
|
||||||
|
column_size[j] = max(column_size[j],
|
||||||
|
info[i].len((typename TI::Column)j));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print table
|
||||||
|
*/
|
||||||
|
tprintf(terminal, " ");
|
||||||
|
for (unsigned j = 0; j < TI::num_columns(); j++) {
|
||||||
|
_print_label(terminal, (typename TI::Column)j, column_size[j]);
|
||||||
|
if (j < TI::num_columns() - 1) tprintf(terminal, " | ");
|
||||||
|
}
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
|
||||||
|
tprintf(terminal, " ");
|
||||||
|
for (unsigned j = 0; j < TI::num_columns(); j++) {
|
||||||
|
for (unsigned i = 0; i < column_size[j]; i++)
|
||||||
|
tprintf(terminal, "-");
|
||||||
|
if (j < TI::num_columns() - 1) tprintf(terminal, "-+-");
|
||||||
|
}
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_rows; i++) {
|
||||||
|
tprintf(terminal, " ");
|
||||||
|
for (unsigned j = 0; j < TI::num_columns(); j++) {
|
||||||
|
_print_cell(info[i], terminal, (typename TI::Column)j, column_size[j]);
|
||||||
|
if (j < TI::num_columns() - 1) tprintf(terminal, " | ");
|
||||||
|
}
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _TABLE_H_ */
|
71
os/src/app/cli_monitor/yield_command.h
Normal file
71
os/src/app/cli_monitor/yield_command.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* \brief Yield command
|
||||||
|
* \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 _YIELD_COMMAND_H_
|
||||||
|
#define _YIELD_COMMAND_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <child_registry.h>
|
||||||
|
#include <process_arg_registry.h>
|
||||||
|
|
||||||
|
struct Yield_command : Command
|
||||||
|
{
|
||||||
|
Child_registry &_children;
|
||||||
|
Process_arg_registry &_process_args;
|
||||||
|
|
||||||
|
Yield_command(Child_registry &children, Process_arg_registry &process_args)
|
||||||
|
:
|
||||||
|
Command("yield", "instruct subsystem to yield resources"),
|
||||||
|
_children(children),
|
||||||
|
_process_args(process_args)
|
||||||
|
{
|
||||||
|
add_parameter(new Parameter("--ram", Parameter::NUMBER, "RAM quota to free"));
|
||||||
|
add_parameter(new Parameter("--greedy", Parameter::VOID, "withdraw yielded RAM quota"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||||
|
{
|
||||||
|
char label[128];
|
||||||
|
label[0] = 0;
|
||||||
|
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||||
|
tprintf(terminal, "Error: no subsystem name specified\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::Number_of_bytes ram = 0;
|
||||||
|
cmd.parameter("--ram", ram);
|
||||||
|
|
||||||
|
bool const greedy = cmd.parameter_exists("--greedy");
|
||||||
|
|
||||||
|
/* lookup child by its unique name */
|
||||||
|
Child *child = _children.first();
|
||||||
|
for (; child; child = child->next())
|
||||||
|
if (strcmp(child->name(), label) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!child) {
|
||||||
|
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
child->yield(ram, greedy);
|
||||||
|
|
||||||
|
tprintf(terminal, "requesting '%s' to yield ", child->name());
|
||||||
|
tprint_bytes(terminal, ram);
|
||||||
|
tprintf(terminal, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Argument> &arguments() { return _process_args.list; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _YIELD_COMMAND_H_ */
|
Loading…
x
Reference in New Issue
Block a user