genode/base-nova/src/core/include/cpu_session_component.h

213 lines
6.5 KiB
C
Raw Normal View History

2012-08-08 17:12:10 +02:00
/*
* \brief Core-specific instance of the CPU session/thread interfaces
* \author Christian Helmuth
\author Norman Feske
2012-08-08 17:12:10 +02:00
* \author Alexander Boettcher
* \date 2006-07-17
*/
/*
2013-01-10 21:44:47 +01:00
* Copyright (C) 2006-2013 Genode Labs GmbH
2012-08-08 17:12:10 +02:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_
/* Genode includes */
#include <util/list.h>
#include <base/allocator_guard.h>
#include <base/tslab.h>
#include <base/lock.h>
#include <base/pager.h>
#include <base/rpc_server.h>
#include <nova_cpu_session/nova_cpu_session.h>
/* core includes */
#include <platform_thread.h>
#include <trace/control_area.h>
#include <trace/source_registry.h>
2012-08-08 17:12:10 +02:00
namespace Genode {
/**
* RPC interface of CPU thread
*
* We make 'Cpu_thread' a RPC object only to be able to lookup CPU threads
* from thread capabilities supplied as arguments to CPU-session functions.
* A CPU thread does not provide an actual RPC interface.
*/
struct Cpu_thread
{
GENODE_RPC_INTERFACE();
};
class Cpu_thread_component : public Rpc_object<Cpu_thread>,
public List<Cpu_thread_component>::Element
{
public:
typedef Trace::Session_label Session_label;
typedef Trace::Thread_name Thread_name;
2012-08-08 17:12:10 +02:00
private:
Thread_name const _name;
Platform_thread _platform_thread;
bool _bound; /* pd binding flag */
Signal_context_capability _sigh; /* exception handler */
unsigned const _trace_control_index;
Trace::Source _trace_source;
2012-08-08 17:12:10 +02:00
public:
Cpu_thread_component(Session_label const &label,
Thread_name const &name,
unsigned priority, addr_t utcb,
Signal_context_capability sigh,
unsigned trace_control_index,
Trace::Control &trace_control)
:
_name(name),
_platform_thread(name.string(), priority, utcb), _bound(false),
_sigh(sigh), _trace_control_index(trace_control_index),
_trace_source(label, _name, trace_control)
{
update_exception_sigh();
}
2012-08-08 17:12:10 +02:00
/************************
** Accessor functions **
************************/
Platform_thread *platform_thread() { return &_platform_thread; }
bool bound() const { return _bound; }
void bound(bool b) { _bound = b; }
Trace::Source *trace_source() { return &_trace_source; }
void sigh(Signal_context_capability sigh)
{
_sigh = sigh;
update_exception_sigh();
}
/**
* Propagate exception handler to platform thread
*/
void update_exception_sigh();
/**
* Return index within the CPU-session's trace control area
*/
unsigned trace_control_index() const { return _trace_control_index; }
2012-08-08 17:12:10 +02:00
};
class Cpu_session_component : public Rpc_object<Nova_cpu_session>
{
public:
typedef Cpu_thread_component::Session_label Session_label;
2012-08-08 17:12:10 +02:00
private:
/**
* Allocator used for managing the CPU threads associated with the
* CPU session
*/
typedef Tslab<Cpu_thread_component, 1024> Cpu_thread_allocator;
Session_label _label;
2012-08-08 17:12:10 +02:00
Rpc_entrypoint *_thread_ep;
Pager_entrypoint *_pager_ep;
Allocator_guard _md_alloc; /* guarded meta-data allocator */
Cpu_thread_allocator _thread_alloc; /* meta-data allocator */
Lock _thread_alloc_lock; /* protect allocator access */
2012-08-08 17:12:10 +02:00
List<Cpu_thread_component> _thread_list;
Lock _thread_list_lock; /* protect thread list */
unsigned _priority; /* priority of threads
created with this
session */
Affinity::Location _location; /* CPU affinity of this
session */
Trace::Source_registry &_trace_sources;
Trace::Control_area _trace_control_area;
/**
* Exception handler that will be invoked unless overridden by a
* call of 'Cpu_session::exception_handler'.
*/
Signal_context_capability _default_exception_handler;
2012-08-08 17:12:10 +02:00
/**
* Raw thread-killing functionality
*
* This function is called from the 'kill_thread' function and
* the destructor. Each these functions grab the list lock
* by themselves and call this function to perform the actual
* killing.
*/
void _unsynchronized_kill_thread(Cpu_thread_component *thread);
public:
/**
* Constructor
*/
Cpu_session_component(Rpc_entrypoint *thread_ep,
Pager_entrypoint *pager_ep,
Allocator *md_alloc,
Trace::Source_registry &trace_sources,
const char *args, Affinity const &affinity);
2012-08-08 17:12:10 +02:00
/**
* Destructor
*/
~Cpu_session_component();
/**
* Register quota donation at allocator guard
*/
void upgrade_ram_quota(size_t ram_quota) { _md_alloc.upgrade(ram_quota); }
2012-08-08 17:12:10 +02:00
/***************************
** CPU session interface **
***************************/
Thread_capability create_thread(Name const &, addr_t);
Ram_dataspace_capability utcb(Thread_capability thread);
void kill_thread(Thread_capability);
int set_pager(Thread_capability, Pager_capability);
int start(Thread_capability, addr_t, addr_t);
void pause(Thread_capability thread_cap);
void resume(Thread_capability thread_cap);
void single_step(Thread_capability thread_cap, bool enable);
2012-08-08 17:12:10 +02:00
void cancel_blocking(Thread_capability);
int name(Thread_capability, char *, size_t);
Thread_state state(Thread_capability);
void state(Thread_capability, Thread_state const &);
2012-08-08 17:12:10 +02:00
void exception_handler(Thread_capability, Signal_context_capability);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
Dataspace_capability trace_control();
unsigned trace_control_index(Thread_capability);
Dataspace_capability trace_buffer(Thread_capability);
Dataspace_capability trace_policy(Thread_capability);
2012-08-08 17:12:10 +02:00
/******************************
2012-08-08 17:12:10 +02:00
** NOVA specific extensions **
******************************/
2012-08-08 17:12:10 +02:00
Native_capability native_cap(Thread_capability);
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.
2012-08-24 09:29:54 +02:00
Native_capability pause_sync(Thread_capability);
2012-08-08 17:12:10 +02:00
};
}
#endif /* _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ */