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.
This commit is contained in:
Norman Feske 2013-01-04 15:24:11 +01:00
parent b0c18d0362
commit ecdbdef8ee
6 changed files with 301 additions and 46 deletions

View File

@ -41,6 +41,9 @@ namespace Loader {
void view_ready_sigh(Signal_context_capability sigh) {
call<Rpc_view_ready_sigh>(sigh); }
void fault_sigh(Signal_context_capability sigh) {
call<Rpc_fault_sigh>(sigh); }
void start(Name const &binary, Name const &label = "",
Native_pd_args const &pd_args = Native_pd_args()) {
call<Rpc_start>(binary, label, pd_args); }

View File

@ -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);
};
}

View File

@ -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 {
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="loader">
<resource name="RAM" quantum="10M"/>
<provides> <service name="Loader"/> </provides>
</start>
<start name="test-failsafe">
<resource name="RAM" quantum="10M"/>
</start>
@ -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

View File

@ -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);

View File

@ -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<Cpu_session> cap = env()->parent()->session<Cpu_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<Rm_session> cap = env()->parent()->session<Rm_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(); }

View File

@ -19,8 +19,32 @@
#include <rom_session/connection.h>
#include <cpu_session/connection.h>
#include <cap_session/connection.h>
#include <loader_session/connection.h>
/***************
** 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 =
"<config>\n"
" <parent-provides>\n"
" <service name=\"ROM\"/>\n"
" <service name=\"LOG\"/>\n"
" </parent-provides>\n"
" <default-route>\n"
" <any-service> <parent/> <any-child/> </any-service>\n"
" </default-route>\n"
" <start name=\"test-segfault\">\n"
" <resource name=\"RAM\" quantum=\"10M\"/>\n"
" </start>\n"
"</config>";
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;
}