hw: restrict processor broadcast to TLB flushing

Removes the generic processor broadcast function call. By now, that call
was used for cross processor TLB maintance operations only. When core/kernel
gets its memory mapped on demand, and unmapped again, the previous cross
processor flush routine doesn't work anymore, because of a hen-egg problem.
The previous cross processor broadcast is realized using a thread constructed
by core running on top of each processor core. When constructing threads in
core, a dataspace for its thread context is constructed. Each constructed
RAM dataspace gets attached, zeroed out, and detached again. The detach
routine requires a TLB flush operation executed on each processor core.

Instead of executing a thread on each processor core, now a thread waiting
for a global TLB flush is removed from the scheduler queue, and gets attached
to a TLB flush queue of each processor. The processor local queue gets checked
whenever the kernel is entered. The last processor, which executed the TLB
flush, re-attaches the blocked thread to its scheduler queue again.

To ease uo the above described mechanism, a platform thread is now directly
associated with a platform pd object, instead of just associate it with the
kernel pd's id.

Ref #723
This commit is contained in:
Stefan Kalkowski 2014-04-28 20:36:00 +02:00 committed by Norman Feske
parent b888a26d57
commit 34b18e9da2
14 changed files with 161 additions and 295 deletions

View File

@ -1,6 +1,7 @@
/*
* \brief Platform specific part of a Genode protection domain
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-02-12
*/
@ -45,14 +46,22 @@ namespace Genode
*/
class Platform_pd : public Address_space
{
unsigned _id;
Native_capability _parent;
Native_thread_id _main_thread;
char const * const _label;
Tlb * _tlb;
protected:
unsigned _id;
Native_capability _parent;
Native_thread_id _main_thread;
char const * const _label;
Tlb * _tlb;
public:
/**
* Constructor for core pd
*/
Platform_pd(Tlb * tlb)
: _main_thread(0), _label("core"), _tlb(tlb) { }
/**
* Constructor
*/
@ -95,9 +104,9 @@ namespace Genode
{
/* annotate that we've got a main thread from now on */
_main_thread = t->id();
return t->join_pd(_id, 1, Address_space::weak_ptr());
return t->join_pd(this, 1, Address_space::weak_ptr());
}
return t->join_pd(_id, 0, Address_space::weak_ptr());
return t->join_pd(this, 0, Address_space::weak_ptr());
}
/**
@ -118,6 +127,7 @@ namespace Genode
** Accessors **
***************/
unsigned const id() { return _id; }
char const * const label() { return _label; }

View File

@ -1,6 +1,7 @@
/*
* \brief Userland interface for the management of kernel thread-objects
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-02-02
*/
@ -42,7 +43,7 @@ namespace Genode {
enum { LABEL_MAX_LEN = 32 };
size_t _stack_size;
unsigned _pd_id;
Platform_pd * _pd;
Weak_ptr<Address_space> _address_space;
unsigned _id;
Rm_client * _rm_client;
@ -81,11 +82,9 @@ namespace Genode {
* Constructor for core threads
*
* \param stack_size initial size of the stack
* \param pd_id kernel name of targeted protection domain
* \param label debugging label
*/
Platform_thread(size_t const stack_size,
unsigned const pd_id, const char * const label);
Platform_thread(size_t const stack_size, const char * const label);
/**
* Constructor for threads outside of core
@ -105,14 +104,14 @@ namespace Genode {
/**
* Join a protection domain
*
* \param pd_id kernel name of targeted protection domain
* \param pd platform pd object pointer
* \param main_thread wether thread is the first in protection domain
* \param address_space corresponding Genode address space
*
* \retval 0 succeeded
* \retval -1 failed
*/
int join_pd(unsigned const pd_id, bool const main_thread,
int join_pd(Platform_pd * const pd, bool const main_thread,
Weak_ptr<Address_space> address_space);
/**
@ -179,7 +178,7 @@ namespace Genode {
Pager_object * pager();
unsigned pd_id() const { return _pd_id; }
Platform_pd * pd() const { return _pd; }
Native_thread_id id() const { return _id; }

View File

@ -1,207 +0,0 @@
/*
* \brief Utility to execute a function on all available processors
* \author Martin Stein
* \date 2014-03-07
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PROCESSOR_BROADCAST_H_
#define _PROCESSOR_BROADCAST_H_
/* Genode includes */
#include <base/thread.h>
#include <base/signal.h>
namespace Genode {
enum { PROCESSOR_BROADCAST_RECEIVER_STACK_SIZE = 4 * 1024 };
/**
* Functionality that can be broadcasted on all available processors
*/
class Processor_broadcast_operation;
/**
* Wrapper for the signalling between broadcast and receiver
*/
class Processor_broadcast_signal;
/**
* Processor local receiver of broadcasted functions
*/
class Processor_broadcast_receiver;
/**
* Executes a function on all available processors
*/
class Processor_broadcast;
/**
* Return broadcast singleton
*/
Processor_broadcast * processor_broadcast();
}
class Genode::Processor_broadcast_operation
{
public:
typedef void (*Entry)(void * const);
private:
Entry const _entry;
void * const _data;
public:
/**
* Constructor
*
* \param entry entry to the operation code
* \param data pointer to operation specific input/output data
*/
Processor_broadcast_operation(Entry const entry, void * const data)
:
_entry(entry), _data(data)
{ }
/**
* Execute operation processor-locally
*/
void execute() const { _entry(_data); }
};
class Genode::Processor_broadcast_signal
{
private:
Signal_context _context;
Signal_receiver _receiver;
Signal_transmitter _transmitter;
public:
/**
* Constructor
*/
Processor_broadcast_signal()
:
_transmitter(_receiver.manage(&_context))
{ }
/**
* Submit the signal
*/
void submit() { _transmitter.submit(); }
/**
* Wait for signal submission
*/
void await() { _receiver.wait_for_signal(); }
};
class Genode::Processor_broadcast_receiver
:
public Thread<PROCESSOR_BROADCAST_RECEIVER_STACK_SIZE>
{
private:
typedef Processor_broadcast_operation Operation;
typedef Processor_broadcast_signal Signal;
Operation const * _operation;
Signal _start;
Signal _end;
public:
/**
* Constructor
*/
Processor_broadcast_receiver() : Thread("processor_broadcast") { }
/**
* Start receiver on a specific processor
*
* \param processor_id kernel name of targeted processor
*/
void init(unsigned const processor_id)
{
Thread::utcb()->core_start_info()->init(processor_id);
Thread::start();
}
/**
* Start remote execution of an operation
*
* \param operation desired operation
*/
void start_executing(Operation const * const operation)
{
_operation = operation;
_start.submit();
}
/**
* Wait until the remote execution of the current operation is done
*/
void end_executing() { _end.await(); }
/*****************
** Thread_base **
*****************/
void entry()
{
while (1) {
_start.await();
_operation->execute();
_end.submit();
}
}
};
class Genode::Processor_broadcast
{
private:
typedef Processor_broadcast_operation Operation;
typedef Processor_broadcast_receiver Receiver;
Receiver _receiver[PROCESSORS];
public:
/**
* Constructor
*/
Processor_broadcast()
{
for (unsigned i = 0; i < PROCESSORS; i++) { _receiver[i].init(i); }
}
/**
* Execute operation on all available processors
*
* \param operation desired operation
*/
void execute(Operation const * const operation)
{
for (unsigned i = 0; i < PROCESSORS; i++) {
_receiver[i].start_executing(operation);
}
for (unsigned i = 0; i < PROCESSORS; i++) {
_receiver[i].end_executing();
}
}
};
#endif /* _PROCESSOR_BROADCAST_H_ */

View File

@ -1,6 +1,7 @@
/*
* \brief Singlethreaded minimalistic kernel
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2011-10-20
*
* This kernel is the only code except the mode transition PIC, that runs in
@ -92,34 +93,28 @@ namespace Kernel
/**
* Static kernel PD that describes core
*/
Pd * core()
Pd * core_pd()
{
/**
* Core protection-domain
*/
class Core_pd : public Pd
struct Core_pd : public Platform_pd, public Pd
{
public:
/**
* Constructor
*/
Core_pd(Tlb * const tlb, Platform_pd * const platform_pd)
:
Pd(tlb, platform_pd)
{ }
/**
* Constructor
*/
Core_pd(Tlb * const tlb)
: Platform_pd(tlb),
Pd(tlb, this)
{
Platform_pd::_id = Pd::id();
}
};
constexpr int tlb_align = 1 << Core_tlb::ALIGNM_LOG2;
Core_tlb * core_tlb = unmanaged_singleton<Core_tlb, tlb_align>();
Core_pd * core_pd = unmanaged_singleton<Core_pd>(core_tlb, nullptr);
return core_pd;
return unmanaged_singleton<Core_pd>(core_tlb);
}
/**
* Get core attributes
*/
unsigned core_id() { return core()->id(); }
/**
* Return wether an interrupt is private to the kernel
*
@ -189,8 +184,8 @@ extern "C" void init_kernel_uniprocessor()
************************************************************************/
/* calculate in advance as needed later when data writes aren't allowed */
core_tlb_base = core()->tlb()->base();
core_pd_id = core_id();
core_tlb_base = core_pd()->tlb()->base();
core_pd_id = core_pd()->id();
/* initialize all processor objects */
processor_pool();
@ -275,7 +270,7 @@ extern "C" void init_kernel_multiprocessor()
_main_thread_utcb->start_info()->init(t.id(), Genode::Native_capability());
t.ip = (addr_t)CORE_MAIN;;
t.sp = (addr_t)s + STACK_SIZE;
t.init(processor_pool()->processor(processor_id), core(), &utcb, 1);
t.init(processor_pool()->processor(processor_id), core_pd(), &utcb, 1);
/* initialize interrupt objects */
static Genode::uint8_t _irqs[Pic::MAX_INTERRUPT_ID * sizeof(Irq)];
@ -312,6 +307,9 @@ extern "C" void kernel()
Processor_client * const old_occupant = scheduler->occupant();
old_occupant->exception(processor_id);
/* check for TLB maintainance requirements */
processor->flush_tlb();
/*
* The processor local as well as remote exception-handling may have
* changed the scheduling of the local activities. Hence we must update the
@ -338,5 +336,5 @@ Kernel::Cpu_context::Cpu_context()
_init(STACK_SIZE);
sp = (addr_t)kernel_stack;
ip = (addr_t)kernel;
core()->admit(this);
core_pd()->admit(this);
}

View File

@ -1,6 +1,7 @@
/*
* \brief Singlethreaded minimalistic kernel
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2013-09-30
*/
@ -14,6 +15,8 @@
#ifndef _KERNEL__KERNEL_H_
#define _KERNEL__KERNEL_H_
namespace Kernel { unsigned core_id(); }
#include <kernel/pd.h>
namespace Kernel { Pd * core_pd(); }
#endif /* _KERNEL__KERNEL_H_ */

View File

@ -1,6 +1,7 @@
/*
* \brief A multiplexable common instruction processor
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2014-01-14
*/
@ -13,6 +14,7 @@
/* core includes */
#include <kernel/processor.h>
#include <kernel/thread.h>
#include <kernel/irq.h>
#include <pic.h>
#include <timer.h>
@ -59,6 +61,25 @@ void Kernel::Processor_client::_interrupt(unsigned const processor_id)
void Kernel::Processor_client::_schedule() { __processor->schedule(this); }
void Kernel::Processor_client::tlb_to_flush(unsigned pd_id)
{
/* initialize pd and reference counter, and remove client from scheduler */
_flush_tlb_pd_id = pd_id;
_flush_tlb_ref_cnt = PROCESSORS;
_unschedule();
}
void Kernel::Processor_client::flush_tlb_by_id()
{
Processor::flush_tlb_by_pid(_flush_tlb_pd_id);
/* if reference counter reaches zero, add client to scheduler again */
if (--_flush_tlb_ref_cnt == 0)
_schedule();
}
void Kernel::Processor::schedule(Processor_client * const client)
{
if (_id != executing_id()) {
@ -99,3 +120,28 @@ void Kernel::Processor_client::_yield()
assert(__processor->id() == Processor::executing_id());
__processor->scheduler()->yield_occupation();
}
void Kernel::Processor::flush_tlb(Processor_client * const client)
{
/* find the last working item in the TLB work queue */
Genode::List_element<Processor_client> * last = _ipi_scheduler.first();
while (last && last->next()) last = last->next();
/* insert new work item at the end of the work list */
_ipi_scheduler.insert(&client->_flush_tlb_li, last);
/* enforce kernel entry of the corresponding processor */
pic()->trigger_ip_interrupt(_id);
}
void Kernel::Processor::flush_tlb()
{
/* iterate through the list of TLB work items, and proceed them */
for (Genode::List_element<Processor_client> * cli = _ipi_scheduler.first(); cli;
cli = _ipi_scheduler.first()) {
cli->object()->flush_tlb_by_id();
_ipi_scheduler.remove(cli);
}
}

View File

@ -1,6 +1,7 @@
/*
* \brief A multiplexable common instruction processor
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2014-01-14
*/
@ -18,6 +19,8 @@
#include <processor_driver.h>
#include <kernel/scheduler.h>
#include <util/list.h>
namespace Kernel
{
/**
@ -44,6 +47,14 @@ class Kernel::Processor_client : public Processor_scheduler::Item
protected:
using List_item = Genode::List_element<Processor_client>;
List_item _flush_tlb_li; /* TLB maintainance work list item */
unsigned _flush_tlb_pd_id; /* id of pd that TLB entries are flushed */
unsigned _flush_tlb_ref_cnt; /* reference counter */
friend class Processor;
/**
* Handle an interrupt exception that occured during execution
*
@ -92,6 +103,18 @@ class Kernel::Processor_client : public Processor_scheduler::Item
*/
virtual void proceed(unsigned const processor_id) = 0;
/**
* Sets the pd id, which TLB entries should be flushed
*
* \param pd_id protection domain kernel object's id
*/
void tlb_to_flush(unsigned pd_id);
/**
* Flush TLB entries requested by this client on the current processor
*/
void flush_tlb_by_id();
/**
* Constructor
*
@ -101,7 +124,8 @@ class Kernel::Processor_client : public Processor_scheduler::Item
Processor_client(Processor * const processor, Priority const priority)
:
Processor_scheduler::Item(priority),
__processor(processor)
__processor(processor),
_flush_tlb_li(this)
{ }
/**
@ -118,8 +142,11 @@ class Kernel::Processor : public Processor_driver
{
private:
using Ipi_scheduler = Genode::List<Genode::List_element<Processor_client> >;
unsigned const _id;
Processor_scheduler _scheduler;
Ipi_scheduler _ipi_scheduler;
bool _ip_interrupt_pending;
public:
@ -157,6 +184,19 @@ class Kernel::Processor : public Processor_driver
void schedule(Processor_client * const client);
/**
* Add processor client to the TLB maintainance queue of the processor
*
* \param client targeted client
*/
void flush_tlb(Processor_client * const client);
/**
* Perform outstanding TLB maintainance work
*/
void flush_tlb();
/***************
** Accessors **
***************/

View File

@ -1,6 +1,7 @@
/*
* \brief Provide a processor object for every available processor
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2014-01-14
*/
@ -18,15 +19,11 @@
#include <unmanaged_singleton.h>
/* core includes */
#include <kernel/kernel.h>
#include <kernel/thread.h>
namespace Kernel
{
/**
* Return kernel name of core domain
*/
Pd * core();
/**
* Thread that consumes processor time if no other thread is available
*/
@ -75,7 +72,7 @@ class Kernel::Idle_thread : public Thread
{
ip = (addr_t)&_main;
sp = (addr_t)&_stack[STACK_SIZE];
init(processor, core(), 0, 0);
init(processor, core_pd(), 0, 0);
}
};

View File

@ -1,6 +1,7 @@
/*
* \brief Kernel backend for execution contexts in userland
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2013-09-15
*/
@ -31,7 +32,7 @@ typedef Genode::Thread_state Thread_state;
unsigned Thread::pd_id() const { return _pd ? _pd->id() : 0; }
bool Thread::_core() const { return pd_id() == core_id(); }
bool Thread::_core() const { return pd_id() == core_pd()->id(); }
void Thread::_signal_context_kill_pending()
{
@ -536,8 +537,11 @@ void Thread::_call_access_thread_regs()
void Thread::_call_update_pd()
{
/* update hardware caches */
Processor::flush_tlb_by_pid(user_arg_1());
tlb_to_flush(user_arg_1());
/* inform other processors */
for (unsigned i = 0; i < PROCESSORS; i++)
Kernel::processor_pool()->processor(i)->flush_tlb(this);
}

View File

@ -1,6 +1,7 @@
/*
* \brief Thread facility
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-02-12
*/
@ -13,12 +14,14 @@
/* core includes */
#include <platform_thread.h>
#include <platform_pd.h>
#include <core_env.h>
#include <rm_session_component.h>
using namespace Genode;
/* kernel includes */
#include <kernel/kernel.h>
namespace Kernel { unsigned core_id(); }
using namespace Genode;
void Platform_thread::_init() { }
@ -30,7 +33,7 @@ bool Platform_thread::_attaches_utcb_by_itself()
* virtual context area by itself, as it is done for other threads
* through a sub RM-session.
*/
return _pd_id == Kernel::core_id() || !_main_thread;
return _pd == Kernel::core_pd()->platform_pd() || !_main_thread;
}
@ -52,7 +55,7 @@ Platform_thread::~Platform_thread()
}
}
/* free UTCB */
if (_pd_id == Kernel::core_id()) {
if (_pd == Kernel::core_pd()->platform_pd()) {
Range_allocator * const ram = platform()->ram_alloc();
ram->free(_utcb_phys, sizeof(Native_utcb));
} else {
@ -76,11 +79,10 @@ Platform_thread::~Platform_thread()
Platform_thread::Platform_thread(size_t const stack_size,
unsigned const pd_id,
const char * const label)
:
_stack_size(stack_size),
_pd_id(pd_id),
_pd(Kernel::core_pd()->platform_pd()),
_rm_client(0),
_utcb_virt(0),
_main_thread(0)
@ -114,7 +116,7 @@ Platform_thread::Platform_thread(const char * const label,
addr_t const utcb)
:
_stack_size(0),
_pd_id(0),
_pd(nullptr),
_rm_client(0),
_utcb_virt((Native_utcb *)utcb),
_main_thread(0)
@ -147,16 +149,16 @@ Platform_thread::Platform_thread(const char * const label,
}
int Platform_thread::join_pd(unsigned const pd_id, bool const main_thread,
int Platform_thread::join_pd(Platform_pd * pd, bool const main_thread,
Weak_ptr<Address_space> address_space)
{
/* check if thread is already in another protection domain */
if (_pd_id && _pd_id != pd_id) {
if (_pd && _pd != pd) {
PERR("thread already in another protection domain");
return -1;
}
/* join protection domain */
_pd_id = pd_id;
_pd = pd;
_main_thread = main_thread;
_address_space = address_space;
return 0;
@ -206,7 +208,7 @@ int Platform_thread::start(void * const ip, void * const sp)
/* start executing new thread */
_utcb_phys->start_info()->init(_id, _utcb);
_tlb = Kernel::start_thread(_id, processor_id, _pd_id, _utcb_phys);
_tlb = Kernel::start_thread(_id, processor_id, _pd->id(), _utcb_phys);
if (!_tlb) {
PERR("failed to start thread");
return -1;

View File

@ -1,24 +0,0 @@
/*
* \brief Utility to execute a function on all available processors
* \author Martin Stein
* \date 2014-03-07
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* core includes */
#include <processor_broadcast.h>
using namespace Genode;
Processor_broadcast * Genode::processor_broadcast()
{
static Processor_broadcast s;
return &s;
}

View File

@ -1,6 +1,7 @@
/*
* \brief RM- and pager implementations specific for base-hw and core
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-02-12
*/
@ -19,7 +20,6 @@
#include <platform.h>
#include <platform_pd.h>
#include <platform_thread.h>
#include <processor_broadcast.h>
#include <tlb.h>
using namespace Genode;
@ -58,9 +58,7 @@ void Rm_client::unmap(addr_t, addr_t virt_base, size_t size)
tlb->remove_region(virt_base, size);
/* update translation caches of all processors */
Update_pd_data data { pt->pd_id() };
Processor_broadcast_operation const operation(update_pd, &data);
processor_broadcast()->execute(&operation);
Kernel::update_pd(pt->pd()->id());
/* try to get back released memory from the translation table */
regain_ram_from_tlb(tlb);

View File

@ -54,7 +54,6 @@ SRC_CC += console.cc \
kernel/irq.cc \
kernel/processor.cc \
kernel/processor_pool.cc \
processor_broadcast.cc \
rm_session_support.cc \
trustzone.cc \
pager.cc \

View File

@ -19,6 +19,7 @@
/* core includes */
#include <platform.h>
#include <platform_thread.h>
#include <kernel/kernel.h>
using namespace Genode;
@ -56,7 +57,7 @@ void Thread_base::_thread_start()
Thread_base::Thread_base(const char * const label, size_t const stack_size, Type)
{
_tid.platform_thread = new (platform()->core_mem_alloc())
Platform_thread(stack_size, Kernel::core_id(), label);
Platform_thread(stack_size, label);
}