diff --git a/repos/libports/run/sequence.run b/repos/libports/run/sequence.run new file mode 100644 index 0000000000..e15d63b973 --- /dev/null +++ b/repos/libports/run/sequence.run @@ -0,0 +1,118 @@ +# +# \brief Test of sequence utility +# \author Emery Hemingway +# \date 2017-08-09 +# + +# +# Build +# + +build { app/sequence test/libc } + +create_boot_directory + +import_from_depot \ + genodelabs/src/[base_src] \ + genodelabs/src/init \ + genodelabs/src/vfs \ + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +build_boot_image { + sequence + libc.lib.so libm.lib.so posix.lib.so + test-libc +} + +# +# Execute test case +# + +append qemu_args " -nographic " +run_genode_until {.*child "sequence" exited with exit value 0.*} 30 + +# vi: set ft=tcl : diff --git a/repos/os/src/app/sequence/README b/repos/os/src/app/sequence/README new file mode 100644 index 0000000000..aabf3b804c --- /dev/null +++ b/repos/os/src/app/sequence/README @@ -0,0 +1,26 @@ +This directory contains a simple utility to serialize the execution of +multiple components. It is configured with "start" XML nodes much like +_init_ however this component relies on its parent to manage all routing +and resources, with the exception of a "config" ROM. No services are +propagated from children to the parent. + +A sample configuration to start a consumer after a producer has exited: + +! +! +! +! +! +! +! +! +! ... +! +! +! +! +! ... +! +! +! +! diff --git a/repos/os/src/app/sequence/main.cc b/repos/os/src/app/sequence/main.cc new file mode 100644 index 0000000000..bd7ee931c6 --- /dev/null +++ b/repos/os/src/app/sequence/main.cc @@ -0,0 +1,241 @@ +/* + * \brief Utility to sequence component execution + * \author Emery Hemingway + * \date 2017-08-09 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include +#include +#include + +namespace Sequence { + using namespace Genode; + + struct Child; + struct Main; +} + + +struct Sequence::Child : Genode::Child_policy +{ + Genode::Env &_env; + + Heap _services_heap { _env.pd(), _env.rm() }; + + Xml_node const _start_node; + + Name const _name = _start_node.attribute_value("name", Name()); + + Binary_name _start_binary() + { + Binary_name name; + try { + _start_node.sub_node("binary").attribute("name").value(&name); + return name != "" ? name : _name; + } + catch (...) { return _name; } + } + + Binary_name const _binary_name = _start_binary(); + + Child_policy_dynamic_rom_file _config_policy { + _env.rm(), "config", _env.ep().rpc_ep(), &_env.pd() }; + + class Parent_service : public Genode::Parent_service + { + private: + + Registry::Element _reg_elem; + + public: + + Parent_service(Registry ®istry, Env &env, + Service::Name const &name) + : + Genode::Parent_service(env, name), _reg_elem(registry, *this) + { } + }; + + Registry _parent_services; + + Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this }; + + /* queue a child reload from the async Parent interface */ + Signal_transmitter _exit_transmitter; + + bool _have_config = _start_node.has_sub_node("config"); + + Child(Genode::Env &env, + Xml_node const &start_node, + Signal_context_capability exit_handler) + : + _env(env), + _start_node(start_node), + _exit_transmitter(exit_handler) + { + if (_have_config) { + Xml_node config_node = start_node.sub_node("config"); + _config_policy.load(config_node.addr(), config_node.size()); + } + } + + ~Child() + { + _parent_services.for_each([&] (Parent_service &service) { + destroy(_services_heap, &service); }); + } + + + /**************************** + ** Child_policy interface ** + ****************************/ + + Name name() const override { return _name; } + + Binary_name binary_name() const override { return _binary_name; } + + /** + * Provide a "config" ROM if configured to do so, + * otherwise forward directly to the parent. + */ + Service &resolve_session_request(Service::Name const &name, + Session_state::Args const &args) + { + if (_have_config) { + Service *s = _config_policy.resolve_session_request( + name.string(), args.string()); + if (s) + return *s; + } + + return *new (_services_heap) Parent_service(_parent_services, _env, name); + } + + /** + * Only a single child is managed at a time so + * no additional PD management is required. + */ + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + + /** + * If the exit is successful then queue a child reload via a signal, + * otherwise exit this parent component. + */ + void exit(int exit_value) override + { + if (exit_value == 0) + _exit_transmitter.submit(); + else { + error("child \"", name(), "\" exited with exit value ", exit_value); + _env.parent().exit(exit_value); + sleep_forever(); + } + } + + /** + * TODO: respond to yield_response by withdrawing + * child quota and informing our parent. + */ + + /** + * Upgrade child quotas from our quotas, + * otherwise request more quota from our parent. + */ + void resource_request(Parent::Resource_args const &args) override + { + Ram_quota ram = ram_quota_from_args(args.string()); + Cap_quota caps = cap_quota_from_args(args.string()); + + Pd_session_capability pd_cap = _child.pd_session_cap(); + + /* XXX: pretty simplistic math here */ + + if (ram.value) { + Ram_quota avail = _env.pd().avail_ram(); + if (avail.value > ram.value) { + ref_pd().transfer_quota(pd_cap, ram); + } else { + ref_pd().transfer_quota(pd_cap, Ram_quota{avail.value >> 1}); + _env.parent().resource_request(args); + } + } + + if (caps.value) { + Cap_quota avail = _env.pd().avail_caps(); + if (avail.value > caps.value) { + ref_pd().transfer_quota(pd_cap, caps); + } else { + ref_pd().transfer_quota(pd_cap, Cap_quota{avail.value >> 1}); + _env.parent().resource_request(args); + } + } + + _child.notify_resource_avail(); + } + + /** + * Initialize the child Protection Domain session with half of + * the initial quotas of this parent component. + */ + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, Cap_quota{_env.pd().avail_caps().value >> 1}); + ref_pd().transfer_quota(pd_cap, Ram_quota{_env.pd().avail_ram().value >> 1}); + } +}; + + +struct Sequence::Main +{ + Genode::Env &env; + + Constructible child; + + Attached_rom_dataspace config_rom { env, "config" }; + + Xml_node const config_xml = config_rom.xml(); + + int next_xml_index = 0; + + void start_next_child(); + + Signal_handler
exit_handler { + env.ep(), *this, &Main::start_next_child }; + + Main(Genode::Env &e) : env(e) { + start_next_child(); } +}; + + +void Sequence::Main::start_next_child() +{ + if (child.constructed()) + child.destruct(); + + try { while (true) { + Xml_node sub_node = config_xml.sub_node(next_xml_index++); + if (sub_node.type() != "start") + continue; + child.construct(env, sub_node, exit_handler); + break; + } } + + catch (Xml_node::Nonexistent_sub_node) { + env.parent().exit(0); } +} + + +void Component::construct(Genode::Env &env) { + static Sequence::Main main(env); } diff --git a/repos/os/src/app/sequence/target.mk b/repos/os/src/app/sequence/target.mk new file mode 100644 index 0000000000..e297230392 --- /dev/null +++ b/repos/os/src/app/sequence/target.mk @@ -0,0 +1,3 @@ +TARGET = sequence +SRC_CC = main.cc +LIBS = base