genode/repos/base/src/test/smp/main.cc

332 lines
9.5 KiB
C++

/*
* \brief Multiprocessor testsuite
* \author Alexander Boettcher
* \author Stefan Kalkowski
* \date 2013-07-19
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_ram_dataspace.h>
#include <base/component.h>
#include <base/heap.h>
#include <base/log.h>
#include <base/rpc_server.h>
#include <base/rpc_client.h>
namespace Genode {
static inline void print(Output &out, Affinity::Location location)
{
print(out, location.xpos(), ",", location.ypos());
}
}
/**
* Set up a server running on every CPU one Rpc_entrypoint
*/
namespace Mp_server_test {
/**
* Test session interface definition
*/
struct Session : Genode::Session
{
static const char *service_name() { return "MP_RPC_TEST"; }
enum { CAP_QUOTA = 2 };
GENODE_RPC(Rpc_test_untyped, void, test_untyped, unsigned);
GENODE_RPC(Rpc_test_cap, void, test_cap, Genode::Native_capability);
GENODE_RPC(Rpc_test_cap_reply, Genode::Native_capability,
test_cap_reply, Genode::Native_capability);
GENODE_RPC_INTERFACE(Rpc_test_untyped, Rpc_test_cap, Rpc_test_cap_reply);
};
struct Client : Genode::Rpc_client<Session>
{
Client(Genode::Capability<Session> cap) : Rpc_client<Session>(cap) { }
void test_untyped(unsigned value) { call<Rpc_test_untyped>(value); }
void test_cap(Genode::Native_capability cap) { call<Rpc_test_cap>(cap); }
Genode::Native_capability test_cap_reply(Genode::Native_capability cap) {
return call<Rpc_test_cap_reply>(cap); }
};
struct Component : Genode::Rpc_object<Session, Component>
{
/* Test to just sent plain words (untyped items) */
void test_untyped(unsigned);
/* Test to transfer a object capability during send */
void test_cap(Genode::Native_capability);
/* Test to transfer a object capability during send+reply */
Genode::Native_capability test_cap_reply(Genode::Native_capability);
};
typedef Genode::Capability<Session> Capability;
struct Cpu_compound
{
enum { STACK_SIZE = 2*1024*sizeof(long) };
Genode::Rpc_entrypoint rpc;
Component comp { };
Capability cap { rpc.manage(&comp) };
Client cli { cap };
Cpu_compound(Genode::Affinity::Location l, Genode::Env &env)
: rpc(&env.pd(), STACK_SIZE, "rpc en", true, l) {}
~Cpu_compound() { rpc.dissolve(&comp); }
};
/**
* Session implementation
*/
void Component::test_untyped(unsigned value) {
Genode::log("RPC: function ", __FUNCTION__, ": got value ", value);
}
void Component::test_cap(Genode::Native_capability cap) {
Genode::log("RPC: function ", __FUNCTION__, ": capability is valid ? ",
cap.valid() ? "yes" : "no", " - idx ", cap.local_name());
}
Genode::Native_capability Component::test_cap_reply(Genode::Native_capability cap) {
Genode::log("RPC: function ", __FUNCTION__, ": capability is valid ? ",
cap.valid() ? "yes" : "no", " - idx ", cap.local_name());
return cap;
}
static void execute(Genode::Env & env, Genode::Heap & heap,
Genode::Affinity::Space & cpus)
{
using namespace Genode;
log("RPC: --- test started ---");
Cpu_compound ** compounds = new (heap) Cpu_compound*[cpus.total()];
for (unsigned i = 0; i < cpus.total(); i++)
compounds[i] = new (heap) Cpu_compound(cpus.location_of_index(i), env);
/* Invoke RPC entrypoint on different CPUs */
for (unsigned i = 0; i < cpus.total(); i++) {
log("RPC: call server on CPU ", i);
compounds[i]->cli.test_untyped(i);
}
/* Transfer a capability to RPC Entrypoints on different CPUs */
for (unsigned i = 0; i < cpus.total(); i++) {
Native_capability cap = compounds[0]->cap;
log("RPC: call server on CPU ", i, " - transfer cap ", cap.local_name());
compounds[i]->cli.test_cap(cap);
}
/* Transfer a capability to RPC Entrypoints and back */
for (unsigned i = 0; i < cpus.total(); i++) {
Native_capability cap = compounds[0]->cap;
log("RPC: call server on CPU ", i, " - transfer cap ", cap.local_name());
Native_capability rcap = compounds[i]->cli.test_cap_reply(cap);
log("RPC: got from server on CPU ", i, " - received cap ", rcap.local_name());
}
/* clean up */
for (unsigned i = 0; i < cpus.total(); i++)
destroy(heap, compounds[i]);
destroy(heap, compounds);
log("RPC: --- test finished ---");
}
}
namespace Affinity_test {
enum {
STACK_SIZE = sizeof(long)*2048,
COUNT_VALUE = 10 * 1024 * 1024
};
struct Spinning_thread : Genode::Thread
{
Genode::Affinity::Location const location;
Genode::uint64_t volatile cnt;
Genode::Lock barrier;
void entry() override
{
barrier.unlock();
Genode::log("Affinity: thread started on CPU ",
location, " spinning...");
for (;;) cnt++;
}
Spinning_thread(Genode::Env &env, Location location)
: Genode::Thread(env, Name("spinning_thread"), STACK_SIZE, location,
Weight(), env.cpu()),
location(location), cnt(0ULL), barrier(Genode::Lock::LOCKED) {
start(); }
};
void execute(Genode::Env &env, Genode::Heap & heap,
Genode::Affinity::Space & cpus)
{
using namespace Genode;
log("Affinity: --- test started ---");
/* get some memory for the thread objects */
Spinning_thread ** threads = new (heap) Spinning_thread*[cpus.total()];
uint64_t * thread_cnt = new (heap) uint64_t[cpus.total()];
/* construct the thread objects */
for (unsigned i = 0; i < cpus.total(); i++)
threads[i] = new (heap)
Spinning_thread(env, cpus.location_of_index(i));
/* wait until all threads are up and running */
for (unsigned i = 0; i < cpus.total(); i++)
threads[i]->barrier.lock();
log("Affinity: Threads started on a different CPU each.");
log("Affinity: You may inspect them using the kernel debugger - if you have one.");
log("Affinity: Main thread monitors client threads and prints the status of them.");
log("Affinity: Legend : D - DEAD, A - ALIVE");
volatile uint64_t cnt = 0;
unsigned round = 0;
char const text_cpu[] = "Affinity: CPU: ";
char const text_round[] = "Affinity: Round %2u: ";
char * output_buffer = new (heap) char [sizeof(text_cpu) + 3 * cpus.total()];
for (; round < 11;) {
cnt++;
/* try to get a life sign by the main thread from the remote threads */
if (cnt % COUNT_VALUE == 0) {
char * output = output_buffer;
snprintf(output, sizeof(text_cpu), text_cpu);
output += sizeof(text_cpu) - 1;
for (unsigned i = 0; i < cpus.total(); i++) {
snprintf(output, 4, "%2u ", i);
output += 3;
}
log(Cstring(output_buffer));
output = output_buffer;
snprintf(output, sizeof(text_round), text_round, round);
output += sizeof(text_round) - 2;
for (unsigned i = 0; i < cpus.total(); i++) {
snprintf(output, 4, "%s ",
thread_cnt[i] == threads[i]->cnt ? " D" : " A");
output += 3;
thread_cnt[i] = threads[i]->cnt;
}
log(Cstring(output_buffer));
round ++;
}
}
for (unsigned i = 0; i < cpus.total(); i++)
destroy(heap, threads[i]);
destroy(heap, threads);
destroy(heap, thread_cnt);
log("Affinity: --- test finished ---");
}
}
namespace Tlb_shootdown_test {
struct Thread : Genode::Thread, Genode::Noncopyable
{
enum { STACK_SIZE = sizeof(long)*2048 };
unsigned cpu_idx;
volatile unsigned * values;
Genode::Lock barrier;
void entry() override
{
Genode::log("TLB: thread started on CPU ", cpu_idx);
values[cpu_idx] = 1;
barrier.unlock();
for (; values[cpu_idx] == 1;) ;
Genode::raw("Unforseeable crosstalk effect!");
}
Thread(Genode::Env &env, Location location, unsigned idx,
volatile unsigned * values)
: Genode::Thread(env, Name("tlb_thread"), STACK_SIZE, location,
Weight(), env.cpu()),
cpu_idx(idx), values(values), barrier(Genode::Lock::LOCKED) {
start(); }
/*
* Noncopyable
*/
Thread(Thread const&);
Thread &operator = (Thread const &);
};
void execute(Genode::Env &env, Genode::Heap & heap,
Genode::Affinity::Space & cpus)
{
using namespace Genode;
log("TLB: --- test started ---");
enum { DS_SIZE = 4096 };
Genode::Attached_ram_dataspace * ram_ds =
new (heap) Genode::Attached_ram_dataspace(env.ram(), env.rm(),
DS_SIZE);
/* get some memory for the thread objects */
Thread ** threads = new (heap) Thread*[cpus.total()];
/* construct the thread objects */
for (unsigned i = 1; i < cpus.total(); i++)
threads[i] = new (heap) Thread(env, cpus.location_of_index(i), i,
ram_ds->local_addr<volatile unsigned>());
/* wait until all threads are up and running */
for (unsigned i = 1; i < cpus.total(); i++) threads[i]->barrier.lock();
log("TLB: all threads are up and running...");
destroy(heap, ram_ds);
log("TLB: ram dataspace destroyed, all will fault...");
for (unsigned i = 1; i < cpus.total(); i++) destroy(heap, threads[i]);
destroy(heap, threads);
log("TLB: --- test finished ---");
}
}
void Component::construct(Genode::Env & env)
{
using namespace Genode;
log("--- SMP testsuite started ---");
Affinity::Space cpus = env.cpu().affinity_space();
log("Detected ", cpus.width(), "x", cpus.height(), " CPU",
cpus.total() > 1 ? "s." : ".");
Heap heap(env.ram(), env.rm());
Mp_server_test::execute(env, heap, cpus);
Affinity_test::execute(env, heap, cpus);
Tlb_shootdown_test::execute(env, heap, cpus);
log("--- SMP testsuite finished ---");
}