6 * Definition of the instance manager that knows about all
7 * redundant VCPUs as well as the fault observers.
9 * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
10 * economic rights: Technische Universität Dresden (Germany)
11 * This file is part of TUD:OS and distributed under the terms of the
12 * GNU General Public License 2.
13 * Please see the COPYING-GPL-2 file for details.
17 * Romain instance manager
22 #include "constants.h"
24 #include "thread_group.h"
25 //#include "app_loading"
26 #include "fault_observers"
34 #include "redundancy.h"
35 #include <l4/plr/measurements.h>
38 EXTERN_C void *split_handler_fn(void*);
39 #endif // SPLIT_HANDLING
44 * Map logical (=sequential) CPU numbers to the topology of the underlying
47 * Background: Fiasco.OC uses sequential CPU numbers to assign threads to CPUs. These
48 * numbers are assigned during bootup and their order depends on the order
49 * in which booted CPUs come up. This means, assigning two threads to CPUs
50 * 0 and 1 can in one case mean that they are running on the same core in
51 * different hyperthreads, while in the next run, they run on the different
52 * physical CPUs (or even NUMA domains, if we had those).
54 * The purpose of this map is to make the assignment less surprising, e.g.,
55 * for the same platform, assigning to two specific CPU IDs should always
56 * lead to the same performance.
61 std::vector<CPU_id> _fiasco_cpus;
63 static l4_umword_t fiasco_count_online_cpus()
65 l4_umword_t ret = 0, max = 0;
66 l4_sched_cpu_set_t cpus;
67 chksys(L4Re::Env::env()->scheduler()->info(&max, &cpus), "CPU count failed");
70 enter_kdebug("MAX > 31");
72 for (unsigned i = 0; i < max; ++i) {
73 if (cpus.map & (1 << i))
74 ret += (1 << cpus.granularity());
79 static void* topology_scanner(void* data)
81 l4_debugger_set_object_name(pthread_l4_cap(pthread_self()), "romain::topscan");
83 for (l4_umword_t cpu = 0; cpu < fiasco_count_online_cpus(); ++cpu) {
84 Romain::LogicalCPUMap *m = reinterpret_cast<Romain::LogicalCPUMap*>(data);
86 //INFO() << "CPU " << cpu;
87 L4::Cap<L4::Thread> thread = Pthread::L4::cap(pthread_self());
88 l4_sched_param_t sp = l4_sched_param(2);
89 sp.affinity = l4_sched_cpu_set(cpu, 0);
90 chksys(L4Re::Env::env()->scheduler()->run_thread(thread, sp));
92 m->_fiasco_cpus[cpu] = CPUID::current_apicid(cpu);
101 l4_mword_t ret = pthread_create(&p, 0, Romain::LogicalCPUMap::topology_scanner, this);
102 _check(ret != 0, "error creating topology scanner");
103 ret = pthread_join(p, 0);
105 for (l4_umword_t i = fiasco_count_online_cpus(); i < MAX_CPUS; ++i) {
106 _fiasco_cpus[i].apic_id = 0xDEADBEEF;
109 INFO() << "Topology scan completed.";
115 INFO() << "Virtual to Fiasco CPU map";
117 for (l4_umword_t i = 0; i < fiasco_count_online_cpus(); ++i) {
118 INFO() << "virt# " << i << " fiasco# " << _fiasco_cpus[i].fiasco_id
119 << " apic# " << std::hex << _fiasco_cpus[i].apic_id;
125 : _fiasco_cpus(MAX_CPUS)
127 /* Default: identity mapping if nothing else works */
128 for (l4_umword_t i = 0; i < MAX_CPUS; ++i) {
129 _fiasco_cpus[i].fiasco_id = i;
130 _fiasco_cpus[i].apic_id = i;
133 if (CPUID::have_cpuid() and
134 (CPUID::max_cpuid() >= CPUID::TOPOLOGY)) {
137 auto f_it = _fiasco_cpus.begin();
138 while ((*f_it).apic_id != 0xdeadbeef)
140 std::sort(_fiasco_cpus.begin(), f_it, CPU_id::comp_apic);
141 // If Fiasco-CPU ID 0 is in the second half, reverse the order to
142 // have the CPU0-Socket first
143 // XXX Hack! This requires real socket knowledge.
144 l4_umword_t logical0;
145 for (logical0 = 0; logical0 < MAX_CPUS; ++logical0) {
146 if (_fiasco_cpus[logical0].fiasco_id == 0)
149 if (logical0 >= CPUID::num_cpus() / 2) {
150 std::reverse(_fiasco_cpus.begin(), f_it);
155 //enter_kdebug("cpu");
159 l4_umword_t logicalToCPU(l4_umword_t log)
161 INFO() << log << " -> " << _fiasco_cpus[log].fiasco_id;
162 return _fiasco_cpus[log].fiasco_id;
167 * Set up and keep track of the running instances
169 class InstanceManager : public LogicalCPUMap
172 Romain::App_model *_am;
173 std::vector<Romain::App_instance *> _instances; // instance list
174 std::vector<Romain::Thread_group *> _threadgroups; // list of thread groups
176 Romain::Observer *_observer_list[Romain::MAX_OBSERVERS]; // exception observer list
177 //Romain::RedundancyCallback *_callback; // the callback for redundancy checking
178 l4_umword_t _num_observers;
180 char const *_name; // app name
181 l4_umword_t _num_inst; // number of instances to run
182 l4_umword_t _num_cpu; // number of available CPUs
184 l4_addr_t _init_eip; // initial EIP (from ELF)
185 l4_addr_t _init_esp; // initial ESP (from ELF)
186 l4_umword_t _gdt_min; // start of GDT entries
188 l4_umword_t _argc; // client argc
189 char const **_argv; // argv of the client
192 pthread_t _split_handler; // resilient core handler thread
193 #endif // SPLIT_HANDLING
196 bool _watchdog_enable;
197 int _watchdog_timeout;
199 Romain::Watchdog::SynchronizationMode _watchdog_mode;
201 Romain::Watchdog* _watchdog;
205 * Prepare handler stack by pushing 3 values:
207 * 1st parameter is an app_thread
208 * 2nd parameter is an app_instance
209 * 3rd parameter is a dummy for the location where usually the
210 * return address would be located
211 * 4th parameter is the thread group we are dealing with
213 l4_addr_t prepare_stack(l4_addr_t sp, Romain::App_instance *inst,
214 Romain::App_thread *thread, Romain::Thread_group* tgroup);
217 void configure_logflags(char *flags);
218 void configure_fault_observers();
219 void configure_redundancy();
220 void configure_logbuf(l4_mword_t size);
221 void configure_watchdog();
224 InstanceManager(l4_umword_t argc, char const **argv, l4_umword_t num_instances = 1);
228 * Prepare the replica instances (VCPUs)
230 void create_instances();
233 * Create a single replica thread along with
234 * its VCPU handler stack
236 Romain::App_thread* create_thread(l4_umword_t eip, l4_umword_t esp,
237 l4_umword_t inst, Romain::Thread_group *group);
240 * Create a group of replica threads, e.g., all replicas
241 * of a single original thread
243 Romain::Thread_group* create_thread_group(l4_umword_t eip, l4_umword_t esp,
244 std::string, l4_umword_t cap, l4_umword_t uid);
247 * Setup the VCPU handler stack.
249 * Puts fake parameters on the handler stack so that
250 * upon an event it looks like we got them passed as
253 void prepare_handler_stack(Romain::App_thread *t, Romain::App_instance* i);
256 * Start the instances
258 void run_instances();
261 void register_fault_observer(Romain::Observer* ob)
263 _check(_num_observers >= MAX_OBSERVERS, "no more observers");
264 _observer_list[_num_observers++] = ob;
268 Romain::Watchdog* watchdog() { return _watchdog; }
271 Romain::RedundancyCallback *redundancy() const { return _callback; }
274 void set_redundancy_callback(Romain::RedundancyCallback* cb)
276 _check(_callback != 0, "there is already a callback registered?");
282 void startup_notify(Romain::App_instance *i, Romain::App_thread *t,
283 Romain::Thread_group* tg, Romain::App_model *a) const
285 // XXX would need to be aware of observer list modifications
286 // -> for now, we assume that observers are not added/removed
287 // at runtime. otherwise we'd use sth. like an r/w lock with
288 // reader prioritization??
289 for (l4_umword_t idx = 0; idx < _num_observers; ++idx) {
290 DEBUG() << "[" << i->id() << "] startup notifying: " << _observer_list[idx]->name();
291 _observer_list[idx]->startup_notify(i,t,tg,a);
297 * Entry point for CPU exception handling.
299 * Iterates over the list of registered observers and calls
300 * every observer's notify() function. Observers may stop
301 * this iteration process. See Romain::Observer::ObserverReturnVal
303 Romain::Observer::ObserverReturnVal
304 fault_notify(Romain::App_instance *i, Romain::App_thread *t,
305 Romain::Thread_group* tg, Romain::App_model *a)
307 Romain::Observer::ObserverReturnVal v = Romain::Observer::Ignored;
308 // XXX would need to be aware of observer list modifications
309 // -> for now, we assume that observers are not added/removed
310 // at runtime. otherwise we'd use sth. like an r/w lock with
311 // reader prioritization??
312 for (l4_umword_t idx = 0; idx < _num_observers; ++idx) {
313 //DEBUG() << "[" << i->id() << "] notifying: " << _observer_list[idx]->name();
314 v = _observer_list[idx]->notify(i,t,tg,a);
316 case Romain::Observer::Finished:
317 case Romain::Observer::Replicatable:
318 //DEBUG() << "Fault handling done: " << std::hex << v;
328 void query_observer_status()
330 for (l4_umword_t idx = 0; idx < _num_observers; ++idx) {
331 //INFO() << "querying: " << _observer_list[idx]->name();
332 _observer_list[idx]->status();
337 l4_umword_t instance_count() const { return _num_inst; }
343 INFO() << BOLD_PURPLE << "Halting threads..." << NOCOLOR;
344 for (auto it = _threadgroups.begin(); it != _threadgroups.end(); ++it) {
348 INFO() << BOLD_PURPLE << "-------- Observer statistics -------" << NOCOLOR;
349 query_observer_status();
351 INFO() << BOLD_PURPLE << "-------- Replica statistics --------" << NOCOLOR;
352 for (auto tg : _threadgroups) {
353 INFO() << "Stats for thread group '" << tg->name << "'";
354 for (auto t : tg->threads) {
362 Romain::App_instance* instance(l4_umword_t idx)
363 { return _instances[idx]; }
365 static void VCPU_handler(Romain::InstanceManager *m, Romain::App_instance *i, Romain::App_thread *t, Romain::Thread_group* tg, Romain::App_model *a);
366 static void VCPU_startup(Romain::InstanceManager *m, Romain::App_instance *i, Romain::App_thread *t, Romain::Thread_group* tg, Romain::App_model *a);
371 * Read config from cfg file
376 extern Romain::InstanceManager* _the_instance_manager;