genode/base-pistachio/src/core/platform_thread.cc
Norman Feske 5fe29e8e4a Express affinities via Cartesian coordinates
This patch introduces new types for expressing CPU affinities. Instead
of dealing with physical CPU numbers, affinities are expressed as
rectangles in a grid of virtual CPU nodes. This clears the way to
conveniently assign sets of adjacent CPUs to subsystems, each of them
managing their respective viewport of the coordinate space.

By using 2D Cartesian coordinates, the locality of CPU nodes can be
modeled for different topologies such as SMP (simple Nx1 grid), grids of
NUMA nodes, or ring topologies.
2013-08-13 17:08:24 +02:00

261 lines
6.0 KiB
C++

/*
* \brief Pistachio thread facility
* \author Julian Stecklina
* \date 2008-03-19
*/
/*
* Copyright (C) 2008-2013 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.
*/
/* Genode includes */
#include <base/printf.h>
#include <util/string.h>
#include <pistachio/thread_helper.h>
#include <pistachio/kip.h>
/* core includes */
#include <platform_pd.h>
#include <platform_thread.h>
/* Pistachio includes */
namespace Pistachio
{
#include <l4/types.h>
#include <l4/thread.h>
#include <l4/schedule.h>
#include <l4/sigma0.h>
};
using namespace Genode;
using namespace Pistachio;
static const bool verbose = false;
static const bool verbose2 = true;
#define PT_DBG(args...) if (verbose) { PDBG(args); } else { }
void Platform_thread::affinity(Affinity::Location location)
{
_location = location;
unsigned const cpu_no = location.xpos();
if (cpu_no >= L4_NumProcessors(get_kip())) {
PERR("Invalid processor number.");
return;
}
if (L4_Set_ProcessorNo(_l4_thread_id, cpu_no) == 0)
PERR("Error setting processor number.");
}
Affinity::Location Platform_thread::affinity()
{
return _location;
}
int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no)
{
L4_ThreadId_t thread = _l4_thread_id;
L4_ThreadId_t pager = _pager ? _pager->cap().dst() : L4_nilthread;
/* XXX should always be the root task */
L4_ThreadId_t preempter = L4_Myself();
PT_DBG("Trying to Platform_thread::start the thread '%s'.", _name);
if (verbose2)
printf("thread '%s' has id 0x%08lx (task = 0x%x, thread = 0x%x)\n",
_name, thread.raw, _platform_pd->pd_id(), _thread_id);
if (_thread_id == THREAD_INVALID) {
PERR("Trying to start a thread with invalid ID.");
return -1;
}
L4_Word_t utcb_location = _platform_pd->_utcb_location(_thread_id);
PT_DBG("New thread's utcb at %08lx.", utcb_location);
PT_DBG("Attaching thread to address space 0x%08lx.",
_platform_pd->_l4_task_id.raw);
PT_DBG("sp = %p, ip = %p", sp, ip);
int ret = L4_ThreadControl(thread, _platform_pd->_l4_task_id,
preempter, L4_Myself(), (void *)utcb_location);
PT_DBG("L4_ThreadControl() = %d", ret);
if (ret != 1) {
PERR("Error code = 0x%08lx", L4_ErrorCode());
PERR("L4_ThreadControl failed.");
return -2;
}
/* set real pager */
ret = L4_ThreadControl(thread, _platform_pd->_l4_task_id,
L4_nilthread, pager, (void *)-1);
if (ret != 1) {
PERR("Error code = 0x%08lx", L4_ErrorCode());
PERR("Setting pager failed.");
return -3;
}
/* get the thread running on the right cpu */
affinity(Affinity::Location(cpu_no, 0));
/* assign priority */
if (!L4_Set_Priority(thread,
Cpu_session::scale_priority(DEFAULT_PRIORITY, _priority)))
PWRN("Could not set thread prioritry to default");
/* send start message */
L4_Msg_t msg;
L4_Clear(&msg);
L4_Append(&msg, (L4_Word_t)ip);
L4_Append(&msg, (L4_Word_t)sp);
L4_Load(&msg);
L4_MsgTag_t tag = L4_Send(thread);
if (L4_IpcFailed(tag)) {
PERR("Starting thread failed. (IPC error)");
return -4;
}
PT_DBG("Done starting thread.");
return 0;
}
void Platform_thread::pause()
{
PDBG("not implemented");
}
void Platform_thread::resume()
{
PDBG("not implemented");
}
void Platform_thread::bind(int thread_id, L4_ThreadId_t l4_thread_id,
Platform_pd *pd)
{
_thread_id = thread_id;
_l4_thread_id = l4_thread_id;
_platform_pd = pd;
}
void Platform_thread::unbind()
{
PT_DBG("Killing thread 0x%08lx.", _l4_thread_id.raw);
L4_Word_t res = L4_ThreadControl(_l4_thread_id, L4_nilthread,
L4_nilthread, L4_nilthread, (void *)-1);
if (res != 1)
PERR("Deleting thread 0x%08lx failed. Continuing...", _l4_thread_id.raw);
_thread_id = THREAD_INVALID;
_l4_thread_id = L4_nilthread;
_platform_pd = 0;
}
void Platform_thread::state(Thread_state)
{
PDBG("Not implemented");
throw Cpu_session::State_access_failed();
}
Thread_state Platform_thread::state()
{
Thread_state s;
L4_Word_t dummy;
L4_ThreadId_t dummy_tid;
L4_Word_t ip, sp;
enum {
DELIVER = 1 << 9,
};
L4_ExchangeRegisters(_l4_thread_id,
DELIVER,
0, 0, 0, 0, L4_nilthread,
&dummy, &sp, &ip, &dummy, &dummy,
&dummy_tid);
s.ip = ip;
s.sp = sp;
return s;
}
void Platform_thread::cancel_blocking()
{
L4_Word_t dummy;
L4_ThreadId_t dummy_tid;
/*
* XXX: This implementation is not safe because it only cancels
* a currently executed blocking operation but it has no
* effect when the thread is executing user code and going
* to block soon. To solve this issue, we would need signalling
* semantics, which means that we flag the thread to being
* canceled the next time it enters the kernel.
*/
/* control flags for 'L4_ExchangeRegisters' */
enum {
CANCEL_SEND = 1 << 2,
CANCEL_RECV = 1 << 1,
CANCEL_IPC = CANCEL_SEND | CANCEL_RECV,
USER_DEFINED_HANDLE = 1 << 6,
RESUME = 1 << 8,
};
/* reset value for the thread's user-defined handle */
enum { USER_DEFINED_HANDLE_ZERO = 0 };
L4_ExchangeRegisters(_l4_thread_id,
CANCEL_IPC | RESUME | USER_DEFINED_HANDLE,
0, 0, 0, USER_DEFINED_HANDLE_ZERO, L4_nilthread,
&dummy, &dummy, &dummy, &dummy, &dummy,
&dummy_tid);
}
Weak_ptr<Address_space> Platform_thread::address_space()
{
return _platform_pd->Address_space::weak_ptr();
}
Platform_thread::Platform_thread(const char *name, unsigned prio, addr_t, int id)
: _thread_id(id), _l4_thread_id(L4_nilthread), _priority(prio), _pager(0)
{
strncpy(_name, name, sizeof(_name));
}
Platform_thread::~Platform_thread()
{
/*
* We inform our protection domain about thread destruction, which will end up in
* Thread::unbind()
*/
if (_platform_pd)
_platform_pd->unbind_thread(this);
}