NOVA: extend cpu_session with synchronous pause

The kernel provides a "recall" feature issued on threads to force a thread into
an exception. In the exception the current state of the thread can be obtained
and its execution can be halted/paused.

However, the recall exception is only delivered when the next time the thread
would leave the kernel. That means the delivery is asynchronous and Genode has
to wait until the exception triggered.

Waiting for the exception can either be done in the cpu_session service or
outside the service in the protection domain of the caller.

It turned out that waiting inside the cpu_service is prone to deadlock the
system. The cpu_session interface is one of many session interfaces handled by
the same thread inside Core.

Deadlock situation:
* The caller (thread_c) to pause some thread_p manages to establish the call
  to the cpu_session thread_s of Core but get be interrupted before issuing
  the actual pause (recall) command.
* Now the - to be recalled thread_p - is scheduled and tries to invoke another
  service of Core, like making log output.
* Since the Core thread_s is handling the session request of thread_c, the
  kernel uses the timeslice of thread_p to help to finish the request handled
  by thread_s.
* Thread_s issues the actual pause/recall on thread_p and blocks inside Core
  to wait for the recall exception to be issued.
* thread_p will leave not the kernel before finishing it actual IPC with
  thread_s which is blocked waiting for thread_p.

That is the reason why the waiting/blocking for the recall exception taking
place must be done on NOVA in the context of the caller (thread_1).

Introduce a pause_sync call to the cpu_session which returns a semaphore
capability to the caller. The caller blocks on the semaphore and is woken up
when the pager of thread_p receives the recall exception with the state of
thread_p.
This commit is contained in:
Alexander Boettcher 2012-08-24 09:29:54 +02:00 committed by Norman Feske
parent 7595823146
commit 841a1fd579
7 changed files with 51 additions and 14 deletions

View File

@ -11,17 +11,21 @@
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__NOVA_CPU_SESSION__CLIENT_H_
#define _INCLUDE__NOVA_CPU_SESSION__CLIENT_H_
#ifndef _INCLUDE__CPU_SESSION__CLIENT_H_
#define _INCLUDE__CPU_SESSION__CLIENT_H_
#include <base/rpc_client.h>
#include <base/thread.h>
#include <cpu_session/capability.h>
#include <cpu_session/client.h>
#include <nova_cpu_session/nova_cpu_session.h>
namespace Genode {
struct Nova_cpu_session_client : Rpc_client<Nova_cpu_session>
struct Cpu_session_client : Rpc_client<Nova_cpu_session>
{
explicit Nova_cpu_session_client(Cpu_session_capability session)
explicit Cpu_session_client(Cpu_session_capability session)
: Rpc_client<Nova_cpu_session>(static_cap_cast<Nova_cpu_session>(session)) { }
Thread_capability create_thread(Name const &name, addr_t utcb = 0) {
@ -46,8 +50,17 @@ namespace Genode {
int start(Thread_capability thread, addr_t ip, addr_t sp) {
return call<Rpc_start>(thread, ip, sp); }
void pause(Thread_capability thread) {
call<Rpc_pause>(thread); }
void pause(Thread_capability thread)
{
Native_capability block = call<Rpc_pause_sync>(thread);
if (!block.valid())
return;
Nova::sm_ctrl(block.local_name(), Nova::SEMAPHORE_DOWN);
Nova::revoke(Nova::Obj_crd(block.local_name(), 0));
cap_selector_allocator()->free(block.local_name(), 0);
}
void resume(Thread_capability thread) {
call<Rpc_resume>(thread); }
@ -75,8 +88,12 @@ namespace Genode {
exc_base, vcpu);
}
private:
Native_capability pause_sync(Thread_capability target) {
return Native_capability::invalid_cap(); }
};
}
#endif /* _INCLUDE__NOVA_CPU_SESSION__CLIENT_H_ */
#endif /* _INCLUDE__CPU_SESSION__CLIENT_H_ */

View File

@ -14,12 +14,12 @@
#ifndef _INCLUDE__NOVA_CPU_SESSION__CONNECTION_H_
#define _INCLUDE__NOVA_CPU_SESSION__CONNECTION_H_
#include <nova_cpu_session/client.h>
#include <cpu_session/client.h>
#include <base/connection.h>
namespace Genode {
struct Nova_cpu_connection : Connection<Cpu_session>, Nova_cpu_session_client
struct Nova_cpu_connection : Connection<Cpu_session>, Cpu_session_client
{
/**
* Constructor
@ -33,7 +33,7 @@ namespace Genode {
Connection<Cpu_session>(
session("priority=0x%lx, ram_quota=32K, label=\"%s\"",
priority, label)),
Nova_cpu_session_client(cap()) { }
Cpu_session_client(cap()) { }
};
}

View File

@ -29,6 +29,9 @@ namespace Genode {
virtual
Native_capability native_cap(Thread_capability cap) = 0;
virtual
Native_capability pause_sync(Thread_capability) = 0;
/*********************
** RPC declaration **
*********************/
@ -37,9 +40,11 @@ namespace Genode {
Thread_capability, addr_t, addr_t, addr_t, bool);
GENODE_RPC(Rpc_native_cap, Native_capability, native_cap,
Thread_capability);
GENODE_RPC(Rpc_pause_sync, Native_capability, pause_sync,
Thread_capability);
GENODE_RPC_INTERFACE_INHERIT(Cpu_session, Rpc_native_cap,
Rpc_start_exc_base_vcpu);
Rpc_start_exc_base_vcpu, Rpc_pause_sync);
};
}

View File

@ -40,3 +40,13 @@ Cpu_session_component::start_exc_base_vcpu(Thread_capability thread_cap,
return thread->platform_thread()->start((void *)ip, (void *)sp,
exc_base, vcpu);
}
Native_capability
Cpu_session_component::pause_sync(Thread_capability target_thread_cap)
{
Cpu_thread_component *thread = _lookup_thread(target_thread_cap);
if (!thread || !thread->platform_thread())
return Native_capability::invalid_cap();
return thread->platform_thread()->pause();
}

View File

@ -150,6 +150,7 @@ namespace Genode {
int start_exc_base_vcpu(Thread_capability, addr_t,
addr_t, addr_t, bool);
Native_capability native_cap(Thread_capability);
Native_capability pause_sync(Thread_capability);
};
}

View File

@ -75,7 +75,7 @@ namespace Genode {
/**
* Pause this thread
*/
void pause();
Native_capability pause();
/**
* Resume this thread

View File

@ -200,7 +200,11 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu)
}
void Platform_thread::pause() { PDBG("not implemented"); }
Native_capability Platform_thread::pause()
{
PDBG("not implemented");
return Native_capability::invalid_cap();
}
void Platform_thread::resume()