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.
This commit is contained in:
Norman Feske 2013-08-07 22:16:58 +02:00
parent fa7329a3e6
commit 5fe29e8e4a
49 changed files with 445 additions and 239 deletions

View File

@ -134,12 +134,12 @@ namespace Genode {
/**
* Set the executing CPU for this thread
*/
void affinity(unsigned cpu);
void affinity(Affinity::Location) { }
/**
* Get the executing CPU for this thread
*/
unsigned affinity();
Affinity::Location affinity() { return Affinity::Location(); }
/**
* Get thread name

View File

@ -27,19 +27,6 @@ using namespace Genode;
using namespace Codezero;
void Platform_thread::affinity(unsigned int cpu_no)
{
PDBG("'%s' not yet implemented", __PRETTY_FUNCTION__);
}
unsigned Platform_thread::affinity()
{
PDBG("'%s' not yet implemented", __PRETTY_FUNCTION__);
return 0;
}
int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no)
{
Native_thread_id pager = _pager ? _pager->cap().dst() : THREAD_INVALID;

View File

@ -121,12 +121,12 @@ namespace Genode {
*
* SMP is not supported on L4/Fiasco.
*/
void affinity(unsigned) { }
void affinity(Affinity::Location) { }
/**
* Request the affinity of this thread
*/
unsigned affinity() { return 0; }
Affinity::Location affinity() { return Affinity::Location(); }
/**
* Return the address space to which the thread is bound

View File

@ -130,7 +130,7 @@ static void _core_pager_loop()
}
Platform::Sigma0::Sigma0() : Pager_object(0, 0)
Platform::Sigma0::Sigma0() : Pager_object(0, Affinity::Location())
{
cap(reinterpret_cap_cast<Cpu_thread>(Native_capability(Fiasco::sigma0_threadid, 0)));
}
@ -145,7 +145,7 @@ Platform::Sigma0 *Platform::sigma0()
Platform::Core_pager::Core_pager(Platform_pd *core_pd)
:
Platform_thread("core.pager"), Pager_object(0, 0)
Platform_thread("core.pager"), Pager_object(0, Affinity::Location())
{
Platform_thread::pager(sigma0());

View File

@ -67,11 +67,11 @@ namespace Genode {
void single_step(Thread_capability thread, bool enable) {
call<Rpc_single_step>(thread, enable); }
unsigned num_cpus() const {
return call<Rpc_num_cpus>(); }
Affinity::Space affinity_space() const {
return call<Rpc_affinity_space>(); }
void affinity(Thread_capability thread, unsigned cpu) {
call<Rpc_affinity>(thread, cpu); }
void affinity(Thread_capability thread, Affinity::Location location) {
call<Rpc_affinity>(thread, location); }
void enable_vcpu(Thread_capability cap, addr_t vcpu_state) {
call<Rpc_enable_vcpu>(cap, vcpu_state); }

View File

@ -159,8 +159,8 @@ namespace Genode {
Thread_state state(Thread_capability);
void state(Thread_capability, Thread_state const &);
void exception_handler(Thread_capability, Signal_context_capability);
unsigned num_cpus() const;
void affinity(Thread_capability, unsigned);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
/***********************************

View File

@ -159,7 +159,7 @@ namespace Genode {
addr_t vm_start() const { return _vm_start; }
size_t vm_size() const { return _vm_size; }
Rom_fs *rom_fs() { return &_rom_fs; }
unsigned num_cpus() const;
Affinity::Space affinity_space() const;
void wait_for_exit();
};

View File

@ -52,6 +52,8 @@ namespace Genode {
Pager_object *_pager_obj;
unsigned _prio;
Affinity::Location _location;
void _create_thread(void);
void _finalize_construction(const char *name);
bool _in_syscall(Fiasco::l4_umword_t flags);
@ -136,12 +138,12 @@ namespace Genode {
/**
* Set the executing CPU for this thread
*/
void affinity(unsigned cpu);
void affinity(Affinity::Location location);
/**
* Get the executing CPU for this thread
*/
unsigned affinity();
Affinity::Location affinity();
/**
* Return the address space to which the thread is bound

View File

@ -121,7 +121,7 @@ static void _core_pager_loop()
}
Platform::Sigma0::Sigma0(Cap_index* i) : Pager_object(0, 0)
Platform::Sigma0::Sigma0(Cap_index* i) : Pager_object(0, Affinity::Location())
{
/*
* We use the Pager_object here in a slightly different manner,
@ -132,7 +132,7 @@ Platform::Sigma0::Sigma0(Cap_index* i) : Pager_object(0, 0)
Platform::Core_pager::Core_pager(Platform_pd *core_pd, Sigma0 *sigma0)
: Platform_thread("core.pager"), Pager_object(0, 0)
: Platform_thread("core.pager"), Pager_object(0, Affinity::Location())
{
Platform_thread::pager(sigma0);
@ -512,8 +512,8 @@ void Platform::wait_for_exit()
}
unsigned Platform::num_cpus() const {
Affinity::Space Platform::affinity_space() const
{
using namespace Genode;
using namespace Fiasco;
@ -531,7 +531,11 @@ unsigned Platform::num_cpus() const {
if ((cpus.map >> i) & 0x1)
cpus_online ++;
return cpus_online;
/*
* Currently, we do not gather any information about the topology of CPU
* nodes but just return a one-dimensional affinity space.
*/
return Affinity::Space(cpus_online, 1);
}

View File

@ -198,8 +198,12 @@ void Platform_thread::cancel_blocking()
}
void Platform_thread::affinity(unsigned cpu)
void Platform_thread::affinity(Affinity::Location location)
{
_location = location;
int const cpu = location.xpos();
l4_sched_param_t params = l4_sched_param(_prio);
params.affinity = l4_sched_cpu_set(cpu, 0, 1);
l4_msgtag_t tag = l4_scheduler_run_thread(L4_BASE_SCHEDULER_CAP,
@ -209,10 +213,9 @@ void Platform_thread::affinity(unsigned cpu)
}
unsigned Platform_thread::affinity()
Affinity::Location Platform_thread::affinity()
{
PERR("'%s' not yet implemented", __PRETTY_FUNCTION__);
return 0;
return _location;
}

View File

@ -151,18 +151,12 @@ namespace Genode {
/**
* Set the executing CPU for this thread
*/
void affinity(unsigned cpu) {
kernel_log() << __PRETTY_FUNCTION__ << ": not implemented\n"; };
void affinity(Affinity::Location) { }
/**
* Get the executing CPU for this thread
*/
unsigned affinity()
{
kernel_log() << __PRETTY_FUNCTION__ << ": not implemented\n";
return 0;
};
Affinity::Location affinity() { return Affinity::Location(); };
/**
* Return the address space to which the thread is bound

View File

@ -60,11 +60,11 @@ namespace Genode {
void single_step(Thread_capability thread, bool enable) {
call<Rpc_single_step>(thread, enable); }
unsigned num_cpus() const {
return call<Rpc_num_cpus>(); }
Affinity::Space affinity_space() const {
return call<Rpc_affinity_space>(); }
void affinity(Thread_capability thread, unsigned cpu) {
call<Rpc_affinity>(thread, cpu); }
void affinity(Thread_capability thread, Affinity::Location location) {
call<Rpc_affinity>(thread, location); }
/*****************************

View File

@ -150,8 +150,8 @@ namespace Genode {
Thread_state state(Thread_capability);
void state(Thread_capability, Thread_state const &);
void exception_handler(Thread_capability, Signal_context_capability);
unsigned num_cpus() const;
void affinity(Thread_capability, unsigned);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
/*******************************

View File

@ -114,7 +114,7 @@ namespace Genode {
}
const char *name() { return _name; }
void affinity(unsigned) { }
void affinity(Affinity::Location) { }
/**
* Register process ID and thread ID of thread

View File

@ -81,7 +81,7 @@ namespace Genode {
static Nova::Utcb * _check_handler(Thread_base *&, Pager_object *&);
public:
Pager_object(unsigned long badge, unsigned affinity);
Pager_object(unsigned long badge, Affinity::Location location);
virtual ~Pager_object();

View File

@ -76,17 +76,16 @@ namespace Genode {
Native_capability native_cap(Thread_capability cap) {
return call<Rpc_native_cap>(cap); }
unsigned num_cpus() const {
return call<Rpc_num_cpus>(); }
Affinity::Space affinity_space() const {
return call<Rpc_affinity_space>(); }
void affinity(Thread_capability thread, unsigned cpu) {
call<Rpc_affinity>(thread, cpu); }
void affinity(Thread_capability thread, Affinity::Location location) {
call<Rpc_affinity>(thread, location); }
private:
Native_capability pause_sync(Thread_capability target) {
return Native_capability::invalid_cap(); }
};
}

View File

@ -261,7 +261,7 @@ static uint8_t create_portal(addr_t pt, addr_t pd, addr_t ec, Mtd mtd,
return res;
}
Pager_object::Pager_object(unsigned long badge, unsigned affinity)
Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
: Thread_base("pager:", PF_HANDLER_STACK_SIZE), _badge(badge)
{
class Create_exception_pt_failed { };
@ -280,7 +280,8 @@ Pager_object::Pager_object(unsigned long badge, unsigned affinity)
_state.sel_client_ec = Native_thread::INVALID_INDEX;
/* tell thread starting code on which CPU to let run the pager */
*reinterpret_cast<addr_t *>(stack_top()) = affinity;
reinterpret_cast<Affinity::Location *>(stack_top())[-1] = location;
/* creates local EC */
Thread_base::start();

View File

@ -199,7 +199,7 @@ void Rpc_entrypoint::activate()
Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
const char *name, bool start_on_construction,
unsigned affinity)
Affinity::Location location)
:
Thread_base(name, stack_size),
_curr_obj(start_on_construction ? 0 : (Rpc_object_base *)~0UL),
@ -222,9 +222,9 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
if (env()->cpu_session()->set_pager(_thread_cap, _pager_cap))
throw Cpu_session::Thread_creation_failed();
/* place new thread on the specified CPU - ~0UL means default CPU */
if (affinity != ~0UL)
env()->cpu_session()->affinity(_thread_cap, affinity);
/* place new thread on the specified CPU, if specified */
if (location.valid())
env()->cpu_session()->affinity(_thread_cap, location);
addr_t thread_sp = (addr_t)&_context->stack[-4];
@ -260,7 +260,7 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
} else {
/* tell thread starting code to use a specific CPU */
*reinterpret_cast<addr_t *>(stack_top()) = affinity;
reinterpret_cast<Affinity::Location *>(stack_top())[-1] = location;
/*
* Required for core threads (creates local EC)

View File

@ -156,8 +156,8 @@ namespace Genode {
Thread_state state(Thread_capability);
void state(Thread_capability, Thread_state const &);
void exception_handler(Thread_capability, Signal_context_capability);
unsigned num_cpus() const;
void affinity(Thread_capability, unsigned);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
/******************************

View File

@ -43,7 +43,7 @@ namespace Genode {
size_t _vm_size;
/* available CPUs */
unsigned _cpus;
Affinity::Space _cpus;
addr_t _map_page(addr_t const phys_page, addr_t const pages,
bool const extra_page);
@ -74,7 +74,8 @@ namespace Genode {
void wait_for_exit();
bool supports_unmap() { return true; }
unsigned num_cpus() const { return _cpus; }
Affinity::Space affinity_space() const { return _cpus; }
/*******************

View File

@ -36,13 +36,13 @@ namespace Genode {
Pager_object *_pager;
addr_t _id_base;
addr_t _sel_exc_base;
unsigned _cpu_no;
Affinity::Location _location;
bool _is_main_thread;
bool _is_vcpu;
char _name[Thread_base::Context::NAME_LEN];
addr_t _sel_ec() { return _id_base; }
addr_t _sel_sc() { return _id_base + 1; }
addr_t _sel_ec() const { return _id_base; }
addr_t _sel_sc() const { return _id_base + 1; }
public:
@ -129,12 +129,12 @@ namespace Genode {
/**
* Set the executing CPU for this thread
*/
void affinity(unsigned cpu);
void affinity(Affinity::Location location);
/**
* Get the executing CPU for this thread
*/
unsigned affinity();
Affinity::Location affinity();
/**
* Get thread name

View File

@ -287,15 +287,23 @@ static void init_core_page_fault_handler()
Platform::Platform() :
_io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()),
_irq_alloc(core_mem_alloc()),
_vm_base(0x1000), _vm_size(0), _cpus(1)
_vm_base(0x1000), _vm_size(0), _cpus(Affinity::Space(1,1))
{
Hip *hip = (Hip *)__initial_sp;
/* check for right API version */
if (hip->api_version != 6)
nova_die();
/* determine number of available CPUs */
_cpus = hip->cpus();
/*
* Determine number of available CPUs
*
* XXX As of now, we assume a one-dimensional affinity space, ignoring
* the y component of the affinity location. When adding support
* for two-dimensional affinity spaces, look out and adjust the use of
* 'Platform_thread::_location' in 'platform_thread.cc'. Also look
* at the 'Thread_base::start' function in core/thread_start.cc.
*/
_cpus = Affinity::Space(hip->cpus(), 1);
/* register UTCB of main thread */
__main_thread_utcb = (Utcb *)(__initial_sp - get_page_size());
@ -339,8 +347,8 @@ Platform::Platform() :
if (verbose_boot_info) {
printf("Hypervisor %s VMX\n", hip->has_feature_vmx() ? "features" : "does not feature");
printf("Hypervisor %s SVM\n", hip->has_feature_svm() ? "features" : "does not feature");
printf("Hypervisor reports %u CPU%c - boot CPU is %lu\n",
_cpus, _cpus > 1 ? 's' : ' ', boot_cpu());
printf("Hypervisor reports %ux%u CPU%c - boot CPU is %lu\n",
_cpus.width(), _cpus.height(), _cpus.total() > 1 ? 's' : ' ', boot_cpu());
}
/* initialize core allocators */

View File

@ -35,18 +35,18 @@ using namespace Genode;
** Platform thread **
*********************/
void Platform_thread::affinity(unsigned int cpu_no)
void Platform_thread::affinity(Affinity::Location location)
{
if (_sel_exc_base != Native_thread::INVALID_INDEX) {
PERR("Failure - affinity of thread could not be set");
return;
}
_cpu_no = cpu_no;
_location = location;
}
unsigned Platform_thread::affinity() { return _cpu_no; }
Affinity::Location Platform_thread::affinity() { return _location; }
int Platform_thread::start(void *ip, void *sp)
@ -88,7 +88,7 @@ int Platform_thread::start(void *ip, void *sp)
/* ip == 0 means that caller will use the thread as worker */
bool thread_global = ip;
res = create_ec(_sel_ec(), _pd->pd_sel(), _cpu_no, utcb,
res = create_ec(_sel_ec(), _pd->pd_sel(), _location.xpos(), utcb,
initial_sp, _sel_exc_base, thread_global);
if (res != Nova::NOVA_OK) {
revoke(Obj_crd(sm, 0));
@ -182,7 +182,7 @@ int Platform_thread::start(void *ip, void *sp)
/* create first thread in task */
enum { THREAD_GLOBAL = true };
res = create_ec(_sel_ec(), pd_sel, _cpu_no, pd_utcb, 0, 0,
res = create_ec(_sel_ec(), pd_sel, _location.xpos(), pd_utcb, 0, 0,
THREAD_GLOBAL);
if (res != NOVA_OK) {
PERR("create_ec returned %d", res);
@ -326,7 +326,7 @@ Weak_ptr<Address_space> Platform_thread::address_space()
Platform_thread::Platform_thread(const char *name, unsigned, int thread_id)
:
_pd(0), _pager(0), _id_base(cap_selector_allocator()->alloc(1)),
_sel_exc_base(Native_thread::INVALID_INDEX), _cpu_no(boot_cpu()),
_sel_exc_base(Native_thread::INVALID_INDEX), _location(boot_cpu(), 0),
_is_main_thread(false), _is_vcpu(false)
{
strncpy(_name, name, sizeof(_name));

View File

@ -82,15 +82,15 @@ void Thread_base::start()
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
Utcb * utcb_obj = reinterpret_cast<Utcb *>(&_context->utcb);
addr_t pd_sel = Platform_pd::pd_core_sel();
addr_t cpu_no = *reinterpret_cast<addr_t *>(stack_top());
Affinity::Location location = reinterpret_cast<Affinity::Location *>(stack_top())[-1];
/* server code sets this value */
if (cpu_no == ~0UL)
cpu_no = boot_cpu();
if (!location.valid())
location = Affinity::Location(boot_cpu(), 0);
/* create local EC */
enum { LOCAL_THREAD = false };
uint8_t res = create_ec(_tid.ec_sel, pd_sel, cpu_no,
uint8_t res = create_ec(_tid.ec_sel, pd_sel, location.xpos(),
utcb, sp, _tid.exc_pt_sel, LOCAL_THREAD);
if (res != NOVA_OK) {
PERR("create_ec returned %d", res);

View File

@ -140,12 +140,12 @@ namespace Genode {
/**
* Set the executing CPU for this thread
*/
void affinity(unsigned cpu);
void affinity(Affinity::Location) { }
/**
* Request the affinity of this thread
*/
unsigned affinity();
Affinity::Location affinity() { return Affinity::Location(); }
/*****************************

View File

@ -36,19 +36,6 @@ using namespace Genode;
using namespace Okl4;
void Platform_thread::affinity(unsigned int cpu_no)
{
PERR("'%s' not yet implemented", __PRETTY_FUNCTION__);
}
unsigned Platform_thread::affinity()
{
PERR("'%s' not yet implemented", __PRETTY_FUNCTION__);
return 0;
}
int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no)
{
if (!_platform_pd) {

View File

@ -150,8 +150,15 @@ namespace Genode {
Rom_fs *rom_fs() { return &_rom_fs; }
void wait_for_exit();
unsigned num_cpus() const {
return L4_NumProcessors(Pistachio::get_kip()); }
Affinity::Space affinity_space() const
{
/*
* Ignore topology of CPU nodes, just return a one-dimensional
* affinity space.
*/
return Affinity::Space(L4_NumProcessors(Pistachio::get_kip()), 1);
}
};
}

View File

@ -45,6 +45,8 @@ namespace Genode {
unsigned _priority; /* thread priority */
Pager_object *_pager;
Affinity::Location _location;
public:
enum { THREAD_INVALID = -1 }; /* invalid thread number */
@ -140,12 +142,12 @@ namespace Genode {
/**
* Set the executing CPU for this thread
*/
void affinity(unsigned cpu);
void affinity(Affinity::Location location);
/**
* Request the affinity of this thread
*/
unsigned affinity();
Affinity::Location affinity();
/**********************************

View File

@ -199,7 +199,7 @@ static void _core_pager_loop()
}
Platform::Sigma0::Sigma0() : Pager_object(0, 0)
Platform::Sigma0::Sigma0() : Pager_object(0, Affinity::Location())
{
cap(Native_capability(Pistachio::get_sigma0(), 0));
}
@ -214,7 +214,7 @@ Platform::Sigma0 *Platform::sigma0()
Platform::Core_pager::Core_pager(Platform_pd *core_pd)
:
Platform_thread("core.pager"), Pager_object(0, 0)
Platform_thread("core.pager"), Pager_object(0, Affinity::Location())
{
Platform_thread::pager(sigma0());

View File

@ -39,8 +39,12 @@ static const bool verbose2 = true;
#define PT_DBG(args...) if (verbose) { PDBG(args); } else { }
void Platform_thread::affinity(unsigned int cpu_no)
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;
@ -51,10 +55,9 @@ void Platform_thread::affinity(unsigned int cpu_no)
}
unsigned Platform_thread::affinity()
Affinity::Location Platform_thread::affinity()
{
PERR("'%s' not yet implemented", __PRETTY_FUNCTION__);
return 0;
return _location;
}
@ -105,7 +108,7 @@ int Platform_thread::start(void *ip, void *sp, unsigned int cpu_no)
}
/* get the thread running on the right cpu */
affinity(cpu_no);
affinity(Affinity::Location(cpu_no, 0));
/* assign priority */
if (!L4_Set_Priority(thread,

View File

@ -0,0 +1,167 @@
/*
* \brief Representation of CPU affinities
* \author Norman Feske
* \date 2013-08-07
*/
/*
* Copyright (C) 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.
*/
#ifndef _INCLUDE__BASE__AFFINITY_H_
#define _INCLUDE__BASE__AFFINITY_H_
namespace Genode {
/**
* Affinity to CPU nodes
*
* The entity of CPU nodes is expected to form a grid where the Euclidean
* distance between nodes roughly correlate to the locality of their
* respective resources. Closely interacting processes are supposed to
* perform best when using nodes close to each other. To allow a relatively
* simple specification of such constraints, the affinity of a subsystem
* (e.g., a process) to CPU nodes is expressed as a rectangle within the
* grid of available CPU nodes. The dimensions of the grid are represented
* by 'Affinity::Space'. The rectangle within the grid is represented by
* 'Affinity::Location'.
*/
class Affinity
{
public:
class Location;
/**
* Bounds of the affinity name space
*
* An 'Affinity::Space' defines the bounds of a Cartesian
* coordinate space that expresses the entity of available CPU
* nodes. The dimension values do not necessarily correspond to
* physical CPU numbers. They solely represent the range the
* 'Affinity::Location' is relative to.
*/
class Space
{
private:
unsigned _width, _height;
public:
Space() : _width(0), _height(0) { }
/**
* Construct a two-dimensional affinity space
*/
Space(unsigned width, unsigned height)
: _width(width), _height(height) { }
/**
* Constuct one-dimensional affinity space
*/
Space(unsigned size) : _width(size), _height(1) { }
unsigned width() const { return _width; }
unsigned height() const { return _height; }
unsigned total() const { return _width*_height; }
/**
* Return location of a single CPU of specified index
*/
inline Location location_of_index(int index);
};
/**
* Location within 'Space'
*/
class Location
{
private:
int _xpos, _ypos;
unsigned _width, _height;
public:
/**
* Default constructor creates invalid location
*/
Location() : _xpos(0), _ypos(0), _width(0), _height(0) { }
/**
* Constructor to express the affinity to a single CPU
*/
Location(int xpos, unsigned ypos)
: _xpos(xpos), _ypos(ypos), _width(1), _height(1) { }
/**
* Constructor to express the affinity to a set of CPUs
*/
Location(int xpos, int ypos, unsigned width, unsigned height)
: _xpos(xpos), _ypos(ypos), _width(width), _height(height) { }
int xpos() const { return _xpos; }
int ypos() const { return _ypos; }
unsigned width() const { return _width; }
unsigned height() const { return _height; }
bool valid() const { return _width*_height > 0; }
};
private:
Space _space;
Location _location;
Affinity(Space const &space, Location const &location)
: _space(space), _location(location) { }
Affinity() { }
Space space() const { return _space; }
Location location() const { return _location; }
/**
* Return location scaled to specified affinity space
*/
Location scale_to(Space const &space) const
{
if (_space.total() == 0)
return Location();
/*
* Calculate coordinates of rectangle corners
*
* P1 is the upper left corner, inside the rectangle.
* P2 is the lower right corner, outside the rectangle.
*/
int const x1 = _location.xpos(),
y1 = _location.ypos(),
x2 = _location.width() + x1,
y2 = _location.height() + y1;
/* scale corner positions */
int const scaled_x1 = (x1*space.width()) / _space.width(),
scaled_y1 = (y1*space.height()) / _space.height(),
scaled_x2 = (x2*space.width()) / _space.width(),
scaled_y2 = (y2*space.height()) / _space.height();
/* make sure to not scale the location size to zero */
return Location(scaled_x1, scaled_y1,
max(scaled_x2 - scaled_x1, 1),
max(scaled_y2 - scaled_y1, 1));
}
};
Affinity::Location Affinity::Space::location_of_index(int index)
{
return Location(index % _width, index / _width, 1, 1);
}
}
#endif /* _INCLUDE__BASE__AFFINITY_H_ */

View File

@ -58,7 +58,14 @@ namespace Genode {
*/
Thread_state state;
Pager_object(unsigned long badge, unsigned affinity) : _badge(badge) { }
/**
* Constructor
*
* \param location affinity of paged thread to physical CPU
*/
Pager_object(unsigned long badge, Affinity::Location location)
: _badge(badge) { }
virtual ~Pager_object() { }
unsigned long badge() const { return _badge; }

View File

@ -316,10 +316,11 @@ namespace Genode {
* point
* \param stack_size stack size of entrypoint thread
* \param name name of entrypoint thread
* \param location CPU affinity
*/
Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
char const *name, bool start_on_construction = true,
unsigned affinity = ~0U);
Affinity::Location location = Affinity::Location());
~Rpc_entrypoint();

View File

@ -60,11 +60,11 @@ namespace Genode {
void single_step(Thread_capability thread, bool enable) {
call<Rpc_single_step>(thread, enable); }
unsigned num_cpus() const {
return call<Rpc_num_cpus>(); }
Affinity::Space affinity_space() const {
return call<Rpc_affinity_space>(); }
void affinity(Thread_capability thread, unsigned cpu) {
call<Rpc_affinity>(thread, cpu); }
void affinity(Thread_capability thread, Affinity::Location location) {
call<Rpc_affinity>(thread, location); }
};
}

View File

@ -34,6 +34,7 @@
#include <base/thread_state.h>
#include <base/rpc_args.h>
#include <base/signal.h>
#include <base/affinity.h>
#include <thread/capability.h>
#include <pager/capability.h>
#include <session/session.h>
@ -173,17 +174,23 @@ namespace Genode {
virtual void single_step(Thread_capability, bool) {}
/**
* Return number of CPUs available via the CPU session
* Return affinity space of CPU nodes available to the CPU session
*
* The dimension of the affinity space as returned by this function
* represent the physical CPUs that are available.
*/
virtual unsigned num_cpus() const = 0;
virtual Affinity::Space affinity_space() const = 0;
/**
* Assign thread to a CPU
* Define affinity of thread to one or multiple CPU nodes
*
* The 'cpu' argument is a CPU index starting at 0. It must be
* smaller than the value returned by 'num_cpus()'.
* In the normal case, a thread is assigned to a single CPU.
* Specifying more than one CPU node is supposed to principally
* allow a CPU service to balance the load of threads among
* multiple CPUs.
*/
virtual void affinity(Thread_capability thread, unsigned cpu) = 0;
virtual void affinity(Thread_capability thread,
Affinity::Location affinity) = 0;
/**
* Translate generic priority value to kernel-specific priority levels
@ -238,8 +245,8 @@ namespace Genode {
GENODE_RPC(Rpc_exception_handler, void, exception_handler,
Thread_capability, Signal_context_capability);
GENODE_RPC(Rpc_single_step, void, single_step, Thread_capability, bool);
GENODE_RPC(Rpc_num_cpus, unsigned, num_cpus);
GENODE_RPC(Rpc_affinity, void, affinity, Thread_capability, unsigned);
GENODE_RPC(Rpc_affinity_space, Affinity::Space, affinity_space);
GENODE_RPC(Rpc_affinity, void, affinity, Thread_capability, Affinity::Location);
/*
* 'GENODE_RPC_INTERFACE' declaration done manually
@ -261,7 +268,7 @@ namespace Genode {
Meta::Type_tuple<Rpc_get_state,
Meta::Type_tuple<Rpc_exception_handler,
Meta::Type_tuple<Rpc_single_step,
Meta::Type_tuple<Rpc_num_cpus,
Meta::Type_tuple<Rpc_affinity_space,
Meta::Type_tuple<Rpc_affinity,
Meta::Empty>
> > > > > > > > > > > > > Rpc_functions;

View File

@ -4,7 +4,7 @@
# \author Alexander Boettcher
#
if {![have_spec nova] && ![have_spec foc]} {
if {[have_spec platform_pbxa9] || (![have_spec nova] && ![have_spec foc])} {
puts "Platform is unsupported."
exit 0
}
@ -33,9 +33,11 @@ install_config {
build_boot_image "core init test-affinity"
if {[is_qemu_available]} {
set want_cpus 4
set rounds "05"
append qemu_args "-nographic -m 64 -smp $want_cpus,cores=$want_cpus "
set want_cpus_x 4
set want_cpus_y 1
set want_cpus_total [expr $want_cpus_x*$want_cpus_y]
set rounds "03"
append qemu_args "-nographic -m 64 -smp $want_cpus_total,cores=$want_cpus_total "
} else {
set rounds "10"
if {[have_spec x86]} { set rounds "40" }
@ -43,12 +45,13 @@ if {[is_qemu_available]} {
run_genode_until "Round $rounds:.*\n" 90
set cpus [regexp -inline {Detected [0-9]+ CPU[ s].*\n} $output]
set cpus [regexp -inline {[0-9]+} $cpus]
set cpus [regexp -inline {Detected [0-9x]+ CPU[ s]\.} $output]
set cpus [regexp -all -inline {[0-9]+} $cpus]
set cpus [expr [lindex $cpus 0] * [lindex $cpus 1]]
if {[is_qemu_available]} {
if {$want_cpus != $cpus} {
puts "CPU count is not as expected: $want_cpus != $cpus"
if {$want_cpus_total != $cpus} {
puts "CPU count is not as expected: $want_cpus_total != $cpus"
exit 1;
}
}

View File

@ -4,7 +4,7 @@
# \author Alexander Boettcher
#
if {![have_spec nova] && ![have_spec foc]} {
if {[have_spec platform_pbxa9] || (![have_spec nova] && ![have_spec foc])} {
puts "Platform is unsupported."
exit 0
}
@ -38,10 +38,11 @@ if {[is_qemu_available]} {
}
# run the test
run_genode_until {\[init -\> test-server-mp\] done.*\n} 20
run_genode_until {\[init -\> test-server-mp\] done.*\n} 60
set cpus [regexp -inline {Detected [0-9]+ CPU[ s].*\n} $output]
set cpus [regexp -inline {[0-9]+} $cpus]
set cpus [regexp -inline {Detected [0-9x]+ CPU[ s]\.} $output]
set cpus [regexp -all -inline {[0-9]+} $cpus]
set cpus [expr [lindex $cpus 0] * [lindex $cpus 1]]
if {[is_qemu_available]} {
if {$want_cpus != $cpus} {
@ -59,7 +60,7 @@ unify_output {\- received cap [a-f0-9]+} "- received cap UNIFIED"
compare_output_to {
[init -> test-server-mp] --- test-mp_server started ---
[init -> test-server-mp] Detected 2 CPUs.
[init -> test-server-mp] Detected 2x1 CPUs.
[init -> test-server-mp] call server on CPU 0
[init -> test-server-mp] function test_untyped: got value 0
[init -> test-server-mp] call server on CPU 1

View File

@ -14,6 +14,7 @@
#include <base/rpc_server.h>
#include <base/rpc_client.h>
#include <base/blocking.h>
#include <base/env.h>
using namespace Genode;
@ -99,7 +100,7 @@ bool Rpc_entrypoint::is_myself() const
Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
char const *name, bool start_on_construction,
unsigned affinity)
Affinity::Location location)
:
Thread_base(name, stack_size),
_cap(Untyped_capability()),
@ -107,6 +108,10 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
_delay_exit(Lock::LOCKED),
_cap_session(cap_session)
{
/* set CPU affinity, if specified */
if (location.valid())
env()->cpu_session()->affinity(Thread_base::cap(), location);
Thread_base::start();
_block_until_cap_valid();

View File

@ -180,18 +180,19 @@ Cpu_session_component::exception_handler(Thread_capability thread_cap,
}
unsigned Cpu_session_component::num_cpus() const
Affinity::Space Cpu_session_component::affinity_space() const
{
return platform()->num_cpus();
return platform()->affinity_space();
}
void Cpu_session_component::affinity(Thread_capability thread_cap, unsigned cpu)
void Cpu_session_component::affinity(Thread_capability thread_cap,
Affinity::Location location)
{
Object_pool<Cpu_thread_component>::Guard thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return;
thread->platform_thread()->affinity(cpu);
thread->platform_thread()->affinity(location);
}

View File

@ -149,8 +149,8 @@ namespace Genode {
Thread_state state(Thread_capability);
void state(Thread_capability, Thread_state const &);
void exception_handler(Thread_capability, Signal_context_capability);
unsigned num_cpus() const;
void affinity(Thread_capability, unsigned);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
};
}

View File

@ -18,6 +18,7 @@
/* Genode includes */
#include <thread/capability.h>
#include <base/allocator.h>
#include <base/affinity.h>
/* core includes */
#include <rom_fs.h>
@ -91,8 +92,13 @@ namespace Genode {
/**
* Return number of physical CPUs present in the platform
*
* The default implementation returns a single CPU.
*/
virtual unsigned num_cpus() const { return 1; }
virtual Affinity::Space affinity_space() const
{
return Affinity::Space(1);
}
};

View File

@ -197,13 +197,13 @@ namespace Genode {
* \param session RM session to which the client belongs
* \param badge pager-object badge used of identifying the client
* when a page-fault occurs
* \param affinity cpu affinity
* \param location affinity to physical CPU
*/
Rm_client(Rm_session_component *session, unsigned long badge,
Weak_ptr<Address_space> &address_space,
unsigned affinity)
Affinity::Location location)
:
Pager_object(badge, affinity), Rm_member(session),
Pager_object(badge, location), Rm_member(session),
Rm_faulter(this), _address_space(address_space) { }
int pager(Ipc_pager &pager);

View File

@ -605,7 +605,7 @@ void Rm_session_component::detach(Local_addr local_addr)
Pager_capability Rm_session_component::add_client(Thread_capability thread)
{
unsigned long badge;
unsigned affinity;
Affinity::Location location;
Weak_ptr<Address_space> address_space;
{
@ -616,8 +616,9 @@ Pager_capability Rm_session_component::add_client(Thread_capability thread)
/* determine identification of client when faulting */
badge = cpu_thread->platform_thread()->pager_object_badge();
/* determine cpu affinity of client thread */
affinity = cpu_thread->platform_thread()->affinity();
location = cpu_thread->platform_thread()->affinity();
address_space = cpu_thread->platform_thread()->address_space();
if (!Locked_ptr<Address_space>(address_space).is_valid())
@ -628,7 +629,7 @@ Pager_capability Rm_session_component::add_client(Thread_capability thread)
Lock::Guard lock_guard(_lock);
Rm_client *cl;
try { cl = new(&_client_slab) Rm_client(this, badge, address_space, affinity); }
try { cl = new(&_client_slab) Rm_client(this, badge, address_space, location); }
catch (Allocator::Out_of_memory) { throw Out_of_metadata(); }
catch (Cpu_session::Thread_creation_failed) { throw Out_of_metadata(); }
catch (Thread_base::Stack_alloc_failed) { throw Out_of_metadata(); }

View File

@ -22,7 +22,7 @@ enum { STACK_SIZE = sizeof(long)*1024, COUNT_VALUE = 10 * 1024 * 1024 };
struct Spinning_thread : Genode::Thread<STACK_SIZE>
{
unsigned const _cpu_number;
Genode::Affinity::Location const _location;
Genode::uint64_t volatile cnt;
@ -32,7 +32,8 @@ struct Spinning_thread : Genode::Thread<STACK_SIZE>
{
barrier.unlock();
PINF("thread started on CPU %u, spinning...", _cpu_number);
PINF("thread started on CPU %d,%d, spinning...",
_location.xpos(), _location.ypos());
unsigned round = 0;
@ -41,18 +42,18 @@ struct Spinning_thread : Genode::Thread<STACK_SIZE>
/* show a life sign every now and then... */
if (cnt % COUNT_VALUE == 0) {
PINF("thread on CPU %u keeps counting - round %u...\n",
_cpu_number, round++);
PINF("thread on CPU %d,%d keeps counting - round %u...\n",
_location.xpos(), _location.ypos(), round++);
}
}
}
Spinning_thread(unsigned cpu_number, char const *name)
Spinning_thread(Genode::Affinity::Location location, char const *name)
:
Genode::Thread<STACK_SIZE>(name), _cpu_number(cpu_number), cnt(0ULL),
Genode::Thread<STACK_SIZE>(name), _location(location), cnt(0ULL),
barrier(Genode::Lock::LOCKED)
{
Genode::env()->cpu_session()->affinity(Thread_base::cap(), cpu_number);
Genode::env()->cpu_session()->affinity(Thread_base::cap(), location);
start();
}
};
@ -64,19 +65,21 @@ int main(int argc, char **argv)
printf("--- test-affinity started ---\n");
unsigned cpus = env()->cpu_session()->num_cpus();
printf("Detected %u CPU%c.\n", cpus, cpus > 1 ? 's' : ' ');
Affinity::Space cpus = env()->cpu_session()->affinity_space();
printf("Detected %ux%u CPU%s\n",
cpus.width(), cpus.height(), cpus.total() > 1 ? "s." : ".");
/* get some memory for the thread objects */
Spinning_thread ** threads = new (env()->heap()) Spinning_thread*[cpus];
uint64_t * thread_cnt = new (env()->heap()) uint64_t[cpus];
Spinning_thread ** threads = new (env()->heap()) Spinning_thread*[cpus.total()];
uint64_t * thread_cnt = new (env()->heap()) uint64_t[cpus.total()];
/* construct the thread objects */
for (unsigned i = 0; i < cpus; i++)
threads[i] = new (env()->heap()) Spinning_thread(i, "thread");
for (unsigned i = 0; i < cpus.total(); i++)
threads[i] = new (env()->heap())
Spinning_thread(cpus.location_of_index(i), "thread");
/* wait until all threads are up and running */
for (unsigned i = 0; i < cpus; i++)
for (unsigned i = 0; i < cpus.total(); i++)
threads[i]->barrier.lock();
printf("Threads started on a different CPU each.\n");
@ -89,7 +92,7 @@ int main(int argc, char **argv)
char const text_cpu[] = " CPU: ";
char const text_round[] = "Round %2u: ";
char * output_buffer = new (env()->heap()) char [sizeof(text_cpu) + 3 * cpus];
char * output_buffer = new (env()->heap()) char [sizeof(text_cpu) + 3 * cpus.total()];
for (;;) {
cnt++;
@ -99,7 +102,7 @@ int main(int argc, char **argv)
char * output = output_buffer;
snprintf(output, sizeof(text_cpu), text_cpu);
output += sizeof(text_cpu) - 1;
for (unsigned i = 0; i < cpus; i++) {
for (unsigned i = 0; i < cpus.total(); i++) {
snprintf(output, 4, "%2u ", i);
output += 3;
}
@ -109,7 +112,7 @@ int main(int argc, char **argv)
snprintf(output, sizeof(text_round), text_round, round);
output += sizeof(text_round) - 2;
for (unsigned i = 0; i < cpus; i++) {
for (unsigned i = 0; i < cpus.total(); i++) {
snprintf(output, 4, "%s ",
thread_cnt[i] == threads[i]->cnt ? " D" : " A");
output += 3;

View File

@ -89,43 +89,44 @@ int main(int argc, char **argv)
printf("--- test-mp_server started ---\n");
unsigned cpus = env()->cpu_session()->num_cpus();
printf("Detected %u CPU%c.\n", cpus, cpus > 1 ? 's' : ' ');
Affinity::Space cpus = env()->cpu_session()->affinity_space();
printf("Detected %ux%u CPU%s\n",
cpus.width(), cpus.height(), cpus.total() > 1 ? "s." : ".");
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
Rpc_entrypoint ** eps = new (env()->heap()) Rpc_entrypoint*[cpus];
for (unsigned i = 0; i < cpus; i++)
Rpc_entrypoint ** eps = new (env()->heap()) Rpc_entrypoint*[cpus.total()];
for (unsigned i = 0; i < cpus.total(); i++)
eps[i] = new (env()->heap()) Rpc_entrypoint(&cap, STACK_SIZE, "rpc en",
true, i);
true, cpus.location_of_index(i));
/* XXX using the same object and putting it to different queues fails XXX */
Test::Component * components = new (env()->heap()) Test::Component[cpus];
Test::Component * components = new (env()->heap()) Test::Component[cpus.total()];
Test::Capability * caps = new (env()->heap()) Test::Capability[cpus];
for (unsigned i = 0; i < cpus; i++)
Test::Capability * caps = new (env()->heap()) Test::Capability[cpus.total()];
for (unsigned i = 0; i < cpus.total(); i++)
caps[i] = eps[i]->manage(&components[i]);
Test::Client ** clients = new (env()->heap()) Test::Client*[cpus];
for (unsigned i = 0; i < cpus; i++)
Test::Client ** clients = new (env()->heap()) Test::Client*[cpus.total()];
for (unsigned i = 0; i < cpus.total(); i++)
clients[i] = new (env()->heap()) Test::Client(caps[i]);
/* Test: Invoke RPC entrypoint on different CPUs */
for (unsigned i = 0; i < cpus; i++) {
for (unsigned i = 0; i < cpus.total(); i++) {
printf("call server on CPU %u\n", i);
clients[i]->test_untyped(i);
}
/* Test: Transfer a capability to RPC Entrypoints on different CPUs */
for (unsigned i = 0; i < cpus; i++) {
for (unsigned i = 0; i < cpus.total(); i++) {
Native_capability cap = caps[0];
printf("call server on CPU %u - transfer cap %lx\n", i, cap.local_name());
clients[i]->test_cap(cap);
}
/* Test: Transfer a capability to RPC Entrypoints and back */
for (unsigned i = 0; i < cpus; i++) {
for (unsigned i = 0; i < cpus.total(); i++) {
Native_capability cap = caps[0];
printf("call server on CPU %u - transfer cap %lx\n", i, cap.local_name());
Native_capability rcap = clients[i]->test_cap_reply(cap);

View File

@ -81,14 +81,18 @@ namespace L4lx {
Genode::addr_t sp() {
return ((Genode::addr_t)&_context->stack[-4]) & ~0xf; }
Genode::addr_t ip() { return (Genode::addr_t)_func; }
Fiasco::l4_utcb_t *utcb() { return _context->utcb; };
Timer::Connection* timer() { return &_timer; }
void set_affinity(unsigned i) {
vcpu_connection()->affinity(_thread_cap, i); }
void set_affinity(unsigned i)
{
vcpu_connection()->affinity(_thread_cap,
Genode::Affinity::Location(i, 0));
}
};
}

View File

@ -191,15 +191,16 @@ void Cpu_session_component::single_step(Thread_capability thread_cap, bool enabl
}
unsigned Cpu_session_component::num_cpus() const
Affinity::Space Cpu_session_component::affinity_space() const
{
return _parent_cpu_session.num_cpus();
return _parent_cpu_session.affinity_space();
}
void Cpu_session_component::affinity(Thread_capability thread_cap, unsigned cpu)
void Cpu_session_component::affinity(Thread_capability thread_cap,
Affinity::Location location)
{
_parent_cpu_session.affinity(thread_cap, cpu);
_parent_cpu_session.affinity(thread_cap, location);
}

View File

@ -70,8 +70,8 @@ class Cpu_session_component : public Rpc_object<Cpu_session>
void exception_handler(Thread_capability thread,
Signal_context_capability handler);
void single_step(Thread_capability thread, bool enable);
unsigned num_cpus() const;
void affinity(Thread_capability, unsigned);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
};
#endif /* _CPU_SESSION_COMPONENT_H_ */

View File

@ -129,11 +129,11 @@ namespace Noux {
void single_step(Thread_capability thread, bool enable) {
_cpu.single_step(thread, enable); }
unsigned num_cpus() const {
return _cpu.num_cpus(); }
Affinity::Space affinity_space() const {
return _cpu.affinity_space(); }
void affinity(Thread_capability thread, unsigned cpu) {
_cpu.affinity(thread, cpu); }
void affinity(Thread_capability thread, Affinity::Location location) {
_cpu.affinity(thread, location); }
};
}