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:
Norman Feske 2013-10-05 19:07:34 +02:00
parent cbdad73dd7
commit a903049a1a
15 changed files with 1519 additions and 508 deletions

View File

@ -11,8 +11,8 @@ if {[have_spec linux]} { puts "Run script does not support Linux"; exit 0 }
set build_components {
core init noux/minimal lib/libc_noux app/cli_monitor test/bomb test/signal
drivers/timer drivers/uart server/terminal_mux server/terminal_log
noux-pkg/vim
test/resource_yield drivers/timer drivers/uart server/terminal_mux
server/terminal_log noux-pkg/vim
}
build $build_components
@ -125,6 +125,11 @@ append config {
<resource name="RAM" quantum="5M" />
<binary name="test-signal" />
</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>
<route>
<any-service> <child name="terminal_mux" /> <any-child/> <parent/> </any-service>
@ -143,7 +148,7 @@ install_config $config
# generic modules
set boot_modules {
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
vim.tar
}

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View File

@ -101,21 +101,25 @@ struct Command_line;
*/
struct Command : List<Command>::Element, Completable
{
List<Argument> _arguments;
List<Parameter> _parameters;
Command(char const *name, char const *short_help)
: Completable(name, short_help) { }
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 ""; }
List<Parameter> &parameters() { 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;
};
@ -713,11 +717,21 @@ class Line_editor
Terminal::Session &terminal, Command_registry &commands)
:
_prompt(prompt), _prompt_len(strlen(prompt)),
_buf(buf), _buf_size(buf_size), _cursor_pos(0),
_terminal(terminal), _commands(commands),
_complete(false)
_buf(buf), _buf_size(buf_size),
_terminal(terminal), _commands(commands)
{
reset();
}
/**
* Reset prompt to initial state after construction
*/
void reset()
{
_buf[0] = 0;
_complete = false;
_cursor_pos = 0;
_seq_tracker = Seq_tracker();
_fresh_prompt();
}

View File

@ -12,499 +12,33 @@
*/
/* Genode includes */
#include <base/child.h>
#include <init/child_policy.h>
#include <os/config.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 <rom_session/connection.h>
/* local includes */
#include <line_editor.h>
#include <command_line.h>
#include <terminal_util.h>
#include <format_util.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;
/***************
** 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)
{
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 **
******************/
@ -520,6 +54,21 @@ static inline Command *lookup_command(char const *buf, Command_registry &registr
}
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)
{
/* look for dynamic linker */
@ -529,46 +78,55 @@ int main(int argc, char **argv)
} catch (...) { }
using Genode::Signal_context;
using Genode::Signal_context_capability;
using Genode::Signal_receiver;
try { Genode::config()->xml_node(); }
catch (...) {
PERR("Error: could not obtain configuration");
return -1;
}
static Genode::Cap_connection cap;
static Terminal::Connection terminal;
static Command_registry commands;
static Child_registry children;
static Process_arg_registry process_args;
/* initialize platform-specific 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_context 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 (;;) {
enum { COMMAND_MAX_LEN = 1000 };
static char buf[COMMAND_MAX_LEN];
/* block for event, e.g., the arrival of new user input */
Genode::Signal signal = sig_rec.wait_for_signal();
Line_editor line_editor("genode> ", buf, sizeof(buf), terminal, commands);
while (!line_editor.is_complete()) {
/* block for event, e.g., the arrival of new user input */
sig_rec.wait_for_signal();
if (signal.context() == &read_avail_sig_ctx) {
/* supply pending terminal input to line editor */
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);
if (!command) {
Token cmd_name(buf);
tprintf(terminal, "Error: unknown command \"");
terminal.write(cmd_name.start(), cmd_name.len());
tprintf(terminal, "\"\n");
line_editor.reset();
continue;
}
@ -594,9 +184,17 @@ int main(int argc, char **argv)
tprintf(terminal, "Error: unexpected parameter \"");
terminal.write(unexpected.start(), unexpected.len());
tprintf(terminal, "\"\n");
line_editor.reset();
continue;
}
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;

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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_ */