From a903049a1ababfe5e5daec54e432ef8bbb41721d Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Sat, 5 Oct 2013 19:07:34 +0200 Subject: [PATCH] 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. --- gems/run/terminal_mux.run | 11 +- os/src/app/cli_monitor/child.h | 303 +++++++++ os/src/app/cli_monitor/child_registry.h | 68 ++ os/src/app/cli_monitor/format_util.h | 112 ++++ os/src/app/cli_monitor/help_command.h | 28 + os/src/app/cli_monitor/kill_command.h | 75 +++ os/src/app/cli_monitor/line_editor.h | 30 +- os/src/app/cli_monitor/main.cc | 592 +++--------------- os/src/app/cli_monitor/process_arg_registry.h | 25 + os/src/app/cli_monitor/ram.h | 123 ++++ os/src/app/cli_monitor/ram_command.h | 123 ++++ os/src/app/cli_monitor/start_command.h | 216 +++++++ os/src/app/cli_monitor/status_command.h | 154 +++++ os/src/app/cli_monitor/table.h | 96 +++ os/src/app/cli_monitor/yield_command.h | 71 +++ 15 files changed, 1519 insertions(+), 508 deletions(-) create mode 100644 os/src/app/cli_monitor/child.h create mode 100644 os/src/app/cli_monitor/child_registry.h create mode 100644 os/src/app/cli_monitor/format_util.h create mode 100644 os/src/app/cli_monitor/help_command.h create mode 100644 os/src/app/cli_monitor/kill_command.h create mode 100644 os/src/app/cli_monitor/process_arg_registry.h create mode 100644 os/src/app/cli_monitor/ram.h create mode 100644 os/src/app/cli_monitor/ram_command.h create mode 100644 os/src/app/cli_monitor/start_command.h create mode 100644 os/src/app/cli_monitor/status_command.h create mode 100644 os/src/app/cli_monitor/table.h create mode 100644 os/src/app/cli_monitor/yield_command.h diff --git a/gems/run/terminal_mux.run b/gems/run/terminal_mux.run index 98121dc814..ba8319e086 100644 --- a/gems/run/terminal_mux.run +++ b/gems/run/terminal_mux.run @@ -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 { + + + + + @@ -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 } diff --git a/os/src/app/cli_monitor/child.h b/os/src/app/cli_monitor/child.h new file mode 100644 index 0000000000..06c7c82975 --- /dev/null +++ b/os/src/app/cli_monitor/child.h @@ -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 +#include +#include +#include + +class Child : public List::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_ */ diff --git a/os/src/app/cli_monitor/child_registry.h b/os/src/app/cli_monitor/child_registry.h new file mode 100644 index 0000000000..83b00f09ad --- /dev/null +++ b/os/src/app/cli_monitor/child_registry.h @@ -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 + +/* local includes */ +#include + +class Child_registry : public List +{ + 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_ */ diff --git a/os/src/app/cli_monitor/format_util.h b/os/src/app/cli_monitor/format_util.h new file mode 100644 index 0000000000..8dd0e8ba84 --- /dev/null +++ b/os/src/app/cli_monitor/format_util.h @@ -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 + + +/** + * 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_ */ diff --git a/os/src/app/cli_monitor/help_command.h b/os/src/app/cli_monitor/help_command.h new file mode 100644 index 0000000000..6289429496 --- /dev/null +++ b/os/src/app/cli_monitor/help_command.h @@ -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_ */ diff --git a/os/src/app/cli_monitor/kill_command.h b/os/src/app/cli_monitor/kill_command.h new file mode 100644 index 0000000000..ec1a0758ed --- /dev/null +++ b/os/src/app/cli_monitor/kill_command.h @@ -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 +#include + +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 &arguments() { return _process_args.list; } +}; + +#endif /* _KILL_COMMAND_H_ */ diff --git a/os/src/app/cli_monitor/line_editor.h b/os/src/app/cli_monitor/line_editor.h index 00bd647c89..0fb2d8895a 100644 --- a/os/src/app/cli_monitor/line_editor.h +++ b/os/src/app/cli_monitor/line_editor.h @@ -101,21 +101,25 @@ struct Command_line; */ struct Command : List::Element, Completable { - List _arguments; List _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 ¶meters() { return _parameters; } - List &arguments() { return _arguments; } + + /** + * To be overridden by commands that accept auto-completion of arguments + */ + virtual List &arguments() + { + static List 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(); } diff --git a/os/src/app/cli_monitor/main.cc b/os/src/app/cli_monitor/main.cc index d0335c3d22..3e9843ea94 100644 --- a/os/src/app/cli_monitor/main.cc +++ b/os/src/app/cli_monitor/main.cc @@ -12,499 +12,33 @@ */ /* Genode includes */ -#include #include #include #include -#include -#include -#include #include -#include /* local includes */ #include #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include -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::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 -{ - 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 '' 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 '' 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 '' 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 ®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) { /* 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; diff --git a/os/src/app/cli_monitor/process_arg_registry.h b/os/src/app/cli_monitor/process_arg_registry.h new file mode 100644 index 0000000000..2ec5b5cdce --- /dev/null +++ b/os/src/app/cli_monitor/process_arg_registry.h @@ -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 list; +}; + +#endif /* _PROCESS_ARG_REGISTRY_H_ */ diff --git a/os/src/app/cli_monitor/ram.h b/os/src/app/cli_monitor/ram.h new file mode 100644 index 0000000000..d7ab181905 --- /dev/null +++ b/os/src/app/cli_monitor/ram.h @@ -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_ */ diff --git a/os/src/app/cli_monitor/ram_command.h b/os/src/app/cli_monitor/ram_command.h new file mode 100644 index 0000000000..7f4d103ac2 --- /dev/null +++ b/os/src/app/cli_monitor/ram_command.h @@ -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 +#include + +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 &arguments() { return _process_args.list; } +}; + +#endif /* _RAM_COMMAND_H_ */ diff --git a/os/src/app/cli_monitor/start_command.h b/os/src/app/cli_monitor/start_command.h new file mode 100644 index 0000000000..665dce6f00 --- /dev/null +++ b/os/src/app/cli_monitor/start_command.h @@ -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 + +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 _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 '' 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 '' 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 '' 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 &arguments() { return _arguments; } +}; + +#endif /* _START_COMMAND_H_ */ diff --git a/os/src/app/cli_monitor/status_command.h b/os/src/app/cli_monitor/status_command.h new file mode 100644 index 0000000000..7b46db586e --- /dev/null +++ b/os/src/app/cli_monitor/status_command.h @@ -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 +#include + +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::print(terminal, child_info, num_children); + tprintf(terminal, "\n"); + } + } +}; + +#endif /* _STATUS_COMMAND_H_ */ diff --git a/os/src/app/cli_monitor/table.h b/os/src/app/cli_monitor/table.h new file mode 100644 index 0000000000..8510facf0d --- /dev/null +++ b/os/src/app/cli_monitor/table.h @@ -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 +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_ */ diff --git a/os/src/app/cli_monitor/yield_command.h b/os/src/app/cli_monitor/yield_command.h new file mode 100644 index 0000000000..13b99011a3 --- /dev/null +++ b/os/src/app/cli_monitor/yield_command.h @@ -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 +#include + +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 &arguments() { return _process_args.list; } +}; + +#endif /* _YIELD_COMMAND_H_ */