From ecdbdef8ee78fc82ec39976d972797d02f4fe93a Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 4 Jan 2013 15:24:11 +0100 Subject: [PATCH] Loader: reflect faults to client With this patch, the loader installs an optional client-provided fault handler as default CPU exception handler and RM fault handler for all CPU and RM sessions of the loaded subsystem. This way, loader clients become able to respond to failures occuring within the subsystem. The new feature is provided via the added 'Loader::fault_handler' RPC function. The 'run/failsafe' test covers two cases related to the loader, which are faults produced by the immediate child of the loader and faults produced by indirect children. --- os/include/loader_session/client.h | 3 + os/include/loader_session/loader_session.h | 19 ++- os/run/failsafe.run | 12 +- os/src/server/loader/child.h | 52 +++--- os/src/server/loader/main.cc | 74 +++++++- os/src/test/failsafe/main.cc | 187 ++++++++++++++++++--- 6 files changed, 301 insertions(+), 46 deletions(-) diff --git a/os/include/loader_session/client.h b/os/include/loader_session/client.h index 98918bba68..56994742cf 100644 --- a/os/include/loader_session/client.h +++ b/os/include/loader_session/client.h @@ -41,6 +41,9 @@ namespace Loader { void view_ready_sigh(Signal_context_capability sigh) { call(sigh); } + void fault_sigh(Signal_context_capability sigh) { + call(sigh); } + void start(Name const &binary, Name const &label = "", Native_pd_args const &pd_args = Native_pd_args()) { call(binary, label, pd_args); } diff --git a/os/include/loader_session/loader_session.h b/os/include/loader_session/loader_session.h index 344ba0ec62..59f51080db 100644 --- a/os/include/loader_session/loader_session.h +++ b/os/include/loader_session/loader_session.h @@ -116,6 +116,20 @@ namespace Loader { */ virtual void view_ready_sigh(Signal_context_capability sigh) = 0; + /** + * Register signal handler notified when a failure occurs in the + * loaded subsystem. + * + * This signal is delivered if any child process of the subsystem + * produces an unresolvable page fault, exists, or triggers a CPU + * exception. (e.g., division by zero) For more information about + * the possible types of faults, please refer to the documentation of + * 'Rm_session::fault_handler' and 'Cpu_session::exception_handler'. + * + * This function should not be called after the 'start' function. + */ + virtual void fault_sigh(Signal_context_capability sigh) = 0; + /** * Start subsystem * @@ -152,6 +166,7 @@ namespace Loader { GENODE_RPC(Rpc_ram_quota, void, ram_quota, size_t); GENODE_RPC(Rpc_constrain_geometry, void, constrain_geometry, int, int); GENODE_RPC(Rpc_view_ready_sigh, void, view_ready_sigh, Signal_context_capability); + GENODE_RPC(Rpc_fault_sigh, void, fault_sigh, Signal_context_capability); GENODE_RPC_THROW(Rpc_start, void, start, GENODE_TYPE_LIST(Rom_module_does_not_exist), Name const &, Name const &, Native_pd_args const &); @@ -161,8 +176,8 @@ namespace Loader { GENODE_RPC_INTERFACE(Rpc_alloc_rom_module, Rpc_commit_rom_module, Rpc_ram_quota, Rpc_constrain_geometry, - Rpc_view_ready_sigh, Rpc_start, Rpc_view, - Rpc_view_geometry); + Rpc_view_ready_sigh, Rpc_fault_sigh, Rpc_start, + Rpc_view, Rpc_view_geometry); }; } diff --git a/os/run/failsafe.run b/os/run/failsafe.run index d46f3a5abb..ebb3ebce04 100644 --- a/os/run/failsafe.run +++ b/os/run/failsafe.run @@ -8,7 +8,7 @@ # Build # -build { core init test/failsafe test/segfault } +build { core init server/loader test/failsafe test/segfault } create_boot_directory @@ -31,6 +31,10 @@ install_config { + + + + @@ -42,7 +46,7 @@ install_config { # # generic modules -set boot_modules { core init test-failsafe test-segfault } +set boot_modules { core init loader test-failsafe test-segfault } build_boot_image $boot_modules @@ -50,5 +54,7 @@ build_boot_image $boot_modules # Execute test case # -run_genode_until "--- finished failsafe test ---.*\n" 10 +append qemu_args "-nographic -m 64" + +run_genode_until "--- finished failsafe test ---.*\n" 30 diff --git a/os/src/server/loader/child.h b/os/src/server/loader/child.h index 0aa18f06c7..f73c05e82b 100644 --- a/os/src/server/loader/child.h +++ b/os/src/server/loader/child.h @@ -47,8 +47,9 @@ namespace Loader { Rm_connection rm; Resources(char const *label, - Ram_session_client &ram_session_client, - size_t ram_quota) + Ram_session_client &ram_session_client, + size_t ram_quota, + Signal_context_capability fault_sigh) : ram(label), cpu(label) { /* deduce session costs from usable ram quota */ @@ -62,6 +63,13 @@ namespace Loader { ram.ref_account(ram_session_client); ram_session_client.transfer_quota(ram.cap(), ram_quota); + + /* + * Install CPU exception and RM fault handler assigned by + * the loader client via 'Loader_session::fault_handler'. + */ + cpu.exception_handler(Thread_capability(), fault_sigh); + rm.fault_handler(fault_sigh); } } _resources; @@ -69,6 +77,8 @@ namespace Loader { Service_registry &_parent_services; Service &_local_nitpicker_service; Service &_local_rom_service; + Service &_local_cpu_service; + Service &_local_rm_service; Rom_session_client _binary_rom_session; @@ -94,25 +104,30 @@ namespace Loader { public: - Child(char const *binary_name, - char const *label, - Native_pd_args const &pd_args, - Rpc_entrypoint &ep, - Ram_session_client &ram_session_client, - size_t ram_quota, - Service_registry &parent_services, - Service &local_rom_service, - Service &local_nitpicker_service, - int max_width, - int max_height) + Child(char const *binary_name, + char const *label, + Native_pd_args const &pd_args, + Rpc_entrypoint &ep, + Ram_session_client &ram_session_client, + size_t ram_quota, + Service_registry &parent_services, + Service &local_rom_service, + Service &local_cpu_service, + Service &local_rm_service, + Service &local_nitpicker_service, + Signal_context_capability fault_sigh, + int max_width, + int max_height) : _label(label), _pd_args(pd_args), _ep(ep), - _resources(_label.string, ram_session_client, ram_quota), + _resources(_label.string, ram_session_client, ram_quota, fault_sigh), _parent_services(parent_services), _local_nitpicker_service(local_nitpicker_service), _local_rom_service(local_rom_service), + _local_cpu_service(local_cpu_service), + _local_rm_service(local_rm_service), _binary_rom_session(_rom_session(binary_name)), _binary_policy("binary", _binary_rom_session.dataspace(), &_ep), _labeling_policy(_label.string), @@ -173,11 +188,10 @@ namespace Loader { if ((service = _binary_policy.resolve_session_request(name, args))) return service; - if (!strcmp(name, "Nitpicker")) - return &_local_nitpicker_service; - - if (!strcmp(name, "ROM")) - return &_local_rom_service; + if (!strcmp(name, "Nitpicker")) return &_local_nitpicker_service; + if (!strcmp(name, "ROM")) return &_local_rom_service; + if (!strcmp(name, "CPU")) return &_local_cpu_service; + if (!strcmp(name, "RM")) return &_local_rm_service; /* populate session-local parent service registry on demand */ service = _parent_services.find(name); diff --git a/os/src/server/loader/main.cc b/os/src/server/loader/main.cc index 0cdf94c97f..d6b33afe93 100644 --- a/os/src/server/loader/main.cc +++ b/os/src/server/loader/main.cc @@ -120,6 +120,54 @@ namespace Loader { void upgrade(Session_capability session, const char *) { } }; + /** + * Common base class of 'Local_cpu_service' and 'Local_rm_service' + */ + struct Intercepted_parent_service : Service + { + Signal_context_capability fault_sigh; + + Intercepted_parent_service(char const *name) : Service(name) { } + + void close(Session_capability session) + { + env()->parent()->close(session); + } + + void upgrade(Session_capability session, const char *) { } + }; + + /** + * Intercept CPU session requests to install default exception + * handler + */ + struct Local_cpu_service : Intercepted_parent_service + { + Local_cpu_service() : Intercepted_parent_service("CPU") { } + + Genode::Session_capability session(const char *args) + { + Capability cap = env()->parent()->session(args); + Cpu_session_client(cap).exception_handler(Thread_capability(), fault_sigh); + return cap; + } + }; + + /** + * Intercept RM session requests to install default fault handler + */ + struct Local_rm_service : Intercepted_parent_service + { + Local_rm_service() : Intercepted_parent_service("RM") { } + + Genode::Session_capability session(const char *args) + { + Capability cap = env()->parent()->session(args); + Rm_session_client(cap).fault_handler(fault_sigh); + return cap; + } + }; + struct Local_nitpicker_service : Service { Rpc_entrypoint &ep; @@ -172,7 +220,10 @@ namespace Loader { Service_registry _parent_services; Rom_module_registry _rom_modules; Local_rom_service _rom_service; + Local_cpu_service _cpu_service; + Local_rm_service _rm_service; Local_nitpicker_service _nitpicker_service; + Signal_context_capability _fault_sigh; Child *_child; /** @@ -244,6 +295,26 @@ namespace Loader { _nitpicker_service.view_ready_sigh = sigh; } + void fault_sigh(Signal_context_capability sigh) + { + /* + * CPU exception handler for CPU sessions originating from the + * subsystem. + */ + _cpu_service.fault_sigh = sigh; + + /* + * RM fault handler for RM sessions originating from the + * subsystem. + */ + _rm_service.fault_sigh = sigh; + + /* + * CPU exception and RM fault handler for the immediate child. + */ + _fault_sigh = sigh; + } + void start(Name const &binary_name, Name const &label, Genode::Native_pd_args const &pd_args) { @@ -261,7 +332,8 @@ namespace Loader { Child(binary_name.string(), label.string(), pd_args, _ep, _ram_session_client, ram_quota, _parent_services, _rom_service, - _nitpicker_service, _width, _height); + _cpu_service, _rm_service, _nitpicker_service, + _fault_sigh, _width, _height); } catch (Genode::Parent::Service_denied) { throw Rom_module_does_not_exist(); } diff --git a/os/src/test/failsafe/main.cc b/os/src/test/failsafe/main.cc index b82323912b..4143bbd2a1 100644 --- a/os/src/test/failsafe/main.cc +++ b/os/src/test/failsafe/main.cc @@ -19,8 +19,32 @@ #include #include #include +#include +/*************** + ** Utilities ** + ***************/ + +static void wait_for_signal_for_context(Genode::Signal_receiver &sig_rec, + Genode::Signal_context const &sig_ctx) +{ + Genode::Signal s = sig_rec.wait_for_signal(); + + if (s.num() && s.context() == &sig_ctx) { + PLOG("got exception for child"); + } else { + PERR("got unexpected signal while waiting for child"); + class Unexpected_signal { }; + throw Unexpected_signal(); + } +} + + +/****************************************************************** + ** Test for detecting the failure of an immediate child process ** + ******************************************************************/ + class Test_child : public Genode::Child_policy { private: @@ -40,18 +64,16 @@ class Test_child : public Genode::Child_policy ram.ref_account(env()->ram_session_cap()); env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA); - /* - * Register default exception handler by specifying an invalid - * thread capability. - */ + /* register default exception handler */ cpu.exception_handler(Thread_capability(), sigh); + + /* register handler for unresolvable page faults */ + rm.fault_handler(sigh); } } _resources; Genode::Rom_connection _elf; - - Genode::Child _child; - + Genode::Child _child; Genode::Parent_service _log_service; public: @@ -92,11 +114,11 @@ class Test_child : public Genode::Child_policy }; -int main(int argc, char **argv) +void failsafe_child_test() { using namespace Genode; - printf("--- failsafe test started ---\n"); + printf("-- exercise failure detection of immediate child --\n"); /* * Entry point used for serving the parent interface @@ -106,27 +128,26 @@ int main(int argc, char **argv) Rpc_entrypoint ep(&cap, STACK_SIZE, "child"); /* - * Signal receiver of CPU-session exception signals + * Signal receiver and signal context for signals originating from the + * children's CPU-session and RM session. */ - static Signal_receiver sig_rec; + Signal_receiver sig_rec; + Signal_context sig_ctx; + /* + * Iteratively start a faulting program and detect the faults + */ for (int i = 0; i < 5; i++) { PLOG("create child %d", i); - Signal_context sig_ctx; - Signal_context_capability exception_sigh = sig_rec.manage(&sig_ctx); + /* create and start child process */ + Test_child child(ep, "test-segfault", sig_rec.manage(&sig_ctx)); - Test_child child(ep, "test-segfault", exception_sigh); + PLOG("wait_for_signal"); - Signal s = sig_rec.wait_for_signal(); - if (s.num() && s.context() == &sig_ctx) { - PLOG("got exception for child %d", i); - } else { - PERR("got unexpected signal while waiting for child %d", i); - return -2; - } + wait_for_signal_for_context(sig_rec, sig_ctx); sig_rec.dissolve(&sig_ctx); @@ -137,6 +158,130 @@ int main(int argc, char **argv) */ } + printf("\n"); +} + + +/****************************************************************** + ** Test for detecting failures in a child started by the loader ** + ******************************************************************/ + +void failsafe_loader_child_test() +{ + using namespace Genode; + + printf("-- exercise failure detection of loaded child --\n"); + + /* + * Signal receiver and signal context for receiving faults originating from + * the loader subsystem. + */ + static Signal_receiver sig_rec; + Signal_context sig_ctx; + + for (int i = 0; i < 5; i++) { + + PLOG("create loader session %d", i); + + Loader::Connection loader(1024*1024); + + /* register fault handler at loader session */ + loader.fault_sigh(sig_rec.manage(&sig_ctx)); + + /* start subsystem */ + loader.start("test-segfault"); + + wait_for_signal_for_context(sig_rec, sig_ctx); + + sig_rec.dissolve(&sig_ctx); + } + + printf("\n"); +} + + +/*********************************************************************** + ** Test for detecting failures in a grandchild started by the loader ** + ***********************************************************************/ + +void failsafe_loader_grand_child_test() +{ + using namespace Genode; + + printf("-- exercise failure detection of loaded grand child --\n"); + + /* + * Signal receiver and signal context for receiving faults originating from + * the loader subsystem. + */ + static Signal_receiver sig_rec; + Signal_context sig_ctx; + + for (int i = 0; i < 5; i++) { + + PLOG("create loader session %d", i); + + Loader::Connection loader(2024*1024); + + /* + * Install init config for subsystem into the loader session + */ + char const *config = + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + + size_t config_size = strlen(config); + + Dataspace_capability config_ds = + loader.alloc_rom_module("config", config_size); + + char *config_ds_addr = env()->rm_session()->attach(config_ds); + memcpy(config_ds_addr, config, config_size); + env()->rm_session()->detach(config_ds_addr); + + loader.commit_rom_module("config"); + + /* register fault handler at loader session */ + loader.fault_sigh(sig_rec.manage(&sig_ctx)); + + /* start subsystem */ + loader.start("init", "init"); + + wait_for_signal_for_context(sig_rec, sig_ctx); + + sig_rec.dissolve(&sig_ctx); + } + + printf("\n"); +} + + +/****************** + ** Main program ** + ******************/ + +int main(int argc, char **argv) +{ + using namespace Genode; + + printf("--- failsafe test started ---\n"); + + failsafe_child_test(); + + failsafe_loader_child_test(); + + failsafe_loader_grand_child_test(); + printf("--- finished failsafe test ---\n"); return 0; }