]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/manager.cc
update
[l4.git] / l4 / pkg / plr / server / src / manager.cc
1 /*
2  * manager.cc --
3  *
4  *     Instance manager implementation.
5  *
6  * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
7  *     economic rights: Technische Universität Dresden (Germany)
8  * This file is part of TUD:OS and distributed under the terms of the
9  * GNU General Public License 2.
10  * Please see the COPYING-GPL-2 file for details.
11  */
12
13 #include "manager"
14 #include "app_loading"
15 #include "configuration"
16
17 #include <l4/sys/segment.h>
18 #include <l4/re/mem_alloc>
19 #include <l4/re/rm>
20 #include <l4/re/env>
21 #include <l4/re/dataspace>
22 #include <l4/re/util/cap_alloc>
23 #include <l4/plr/uu.h>
24
25 #define MSG() DEBUGf(Romain::Log::Manager)
26 #include "fault_handlers/syscalls_factory.h"
27
28 Romain::Configuration Romain::globalconfig;
29
30
31 L4_INLINE unsigned countbits(long v)
32 {
33         v = v - ((v >> 1) & 0x55555555);                         // reuse input as temporary
34         v = (v & 0x33333333) + ((v >> 2) & 0x33333333);          // temp
35         return ((v + ((v >> 4) & 0xF0F0F0F)) * 0x1010101) >> 24; // count
36 }
37
38
39 L4_INLINE l4_umword_t count_online_cpus()
40 {
41         l4_umword_t maxcpu = 0;
42         l4_sched_cpu_set_t cpuonline = l4_sched_cpu_set(0, 0);
43         if (l4_error(L4Re::Env::env()->scheduler()->info(&maxcpu, &cpuonline)) < 0) {
44                 ERROR() << "reading CPU info";
45         }
46
47         INFO() << "Online " << countbits(cpuonline.map) << " / MAX " << maxcpu;
48
49         return countbits(cpuonline.map) > maxcpu ? maxcpu : countbits(cpuonline.map);
50 }
51
52
53 Romain::InstanceManager::InstanceManager(unsigned int argc,
54                                          char const **argv,
55                                          unsigned num_instances)
56         : LogicalCPUMap(),
57           _am(0),
58           _instances(),
59           _threadgroups(),
60           _num_observers(0),
61           _num_inst(num_instances),
62           _num_cpu(1),
63           _argc(argc), // XXX: remove
64           _argv(argv), // XXX: remove
65           _logBuf(0)
66 {
67         configure();
68
69         _gdt_min = fiasco_gdt_get_entry_offset(L4_INVALID_CAP, l4_utcb());
70         MSG() << "GDT MIN: " << _gdt_min;
71
72         _num_cpu = count_online_cpus();
73         /*
74          * initial parameter is argv for the client program, this means
75          * *argv is the file name to load.
76          */
77         _name = *argv;
78
79         _am = new Romain::App_model(_name, argc, argv);
80         Romain::Elf_Ldr loader(_am);
81         //loader.load();
82         loader.launch();
83
84         _init_eip = _am->prog_info()->entry;
85         _init_esp = _am->prog_info()->stack_addr;
86         INFO() << "Program entry point at 0x" << std::hex << _init_eip;
87         INFO() << "              stack at 0x" << std::hex << _init_esp;
88
89 #if SPLIT_HANDLING
90         int res = pthread_create(&_split_handler, 0, split_handler_fn, this);
91         _check(res != 0, "could not create split handler thread");
92 #endif
93 }
94
95
96 void Romain::InstanceManager::configure_logflags(char *flags)
97 {
98         printf("flags %p\n", flags);
99         if (!flags) {
100                 Romain::Log::logFlags = 0;
101         } else {
102                 unsigned max = strlen(flags);
103                 for (unsigned j = 0; j < max; ++j) {
104                         if (flags[j] == ',') flags[j] = 0;
105                 }
106
107                 char const *c = flags;
108                 while (c <= flags + max) {
109                         printf("  %s\n", c);
110                         if ((strcmp(c, "mem") == 0) || (strcmp(c, "memory") == 0)) {
111                                 Romain::Log::logFlags |= Romain::Log::Memory;
112                         } else if (strcmp(c, "emulator") == 0) {
113                                 Romain::Log::logFlags |= Romain::Log::Emulator;
114                         } else if (strcmp(c, "manager") == 0) {
115                                 Romain::Log::logFlags |= Romain::Log::Manager;
116                         } else if (strcmp(c, "faults") == 0) {
117                                 Romain::Log::logFlags |= Romain::Log::Faults;
118                         } else if (strcmp(c, "redundancy") == 0) {
119                                 Romain::Log::logFlags |= Romain::Log::Redundancy;
120                         } else if (strcmp(c, "loader") == 0) {
121                                 Romain::Log::logFlags |= Romain::Log::Loader;
122                         } else if (strcmp(c, "swifi") == 0) {
123                                 Romain::Log::logFlags |= Romain::Log::Swifi;
124                         } else if (strcmp(c, "gdb") == 0) {
125                                 Romain::Log::logFlags |= Romain::Log::Gdb;
126                         } else if (strcmp(c, "all") == 0) {
127                                 Romain::Log::logFlags = Romain::Log::All;
128                         }
129
130                         c += (strlen(c)+1);
131                 }
132                 printf("Flags: %08lx\n", Romain::Log::logFlags);
133         }
134 }
135
136
137 void Romain::InstanceManager::configure_fault_observers()
138 {
139         /*
140          * First, register those observers that don't interfere
141          * with anyone else and get notified all the time.
142          */
143         DEBUG() << "[observer] vcpu state.";
144         BoolObserverConfig("general:print_vcpu_state",
145                            this, "vcpu_state");
146         DEBUG() << "[observer] trap limit.";
147         ObserverConfig(this, "trap_limit");
148
149         /*
150          * Always needed -- slightly ordered by the number of
151          * calls they are expected to see, so that we minimize
152          * the amount of unnecessary observer callbacks.
153          */
154         DEBUG() << "[observer] page faults.";
155         ObserverConfig(this, "pagefaults");
156         DEBUG() << "[observer] syscalls.";
157         ObserverConfig(this, "syscalls");
158         DEBUG() << "[observer] threads.";
159         BoolObserverConfig("general:threads", this, "threads");
160         DEBUG() << "[observer] trap.";
161         ObserverConfig(this, "trap");
162
163         DEBUG() << "[observer] simpledbg.";
164         StringObserverConfig("general:debug", this);
165         DEBUG() << "[observer] intercept-kip.";
166         BoolObserverConfig("general:intercept_kip", this, "kip-time");
167         DEBUG() << "[observer] swifi.";
168         BoolObserverConfig("general:swifi", this, "swifi");
169         DEBUG() << "[observer] logreplica.";
170         BoolObserverConfig("general:logreplica", this, "replicalog");
171 }
172
173
174 void Romain::InstanceManager::configure_redundancy()
175 {
176         char const *redundancy = ConfigStringValue("general:redundancy");
177         if (!redundancy) redundancy = "none";
178         INFO() << "red: '" << redundancy << "'";
179         if (strcmp(redundancy, "none") == 0) {
180                 _num_inst = 1;
181         } else if (strcmp(redundancy, "dual") == 0) {
182                 _num_inst = 2;
183         } else if (strcmp(redundancy, "triple") == 0) {
184                 _num_inst = 3;
185         } else {
186                 ERROR() << "Invalid redundancy setting: " << redundancy;
187                 enter_kdebug("Invalid redundancy setting");
188         }
189 }
190
191
192 void Romain::InstanceManager::configure_logbuf(int sizeMB)
193 {
194         INFO() << "Log buffer size: " << sizeMB << " MB requested.";
195         unsigned size_in_bytes = sizeMB << 20;
196
197         L4::Cap<L4Re::Dataspace> ds;
198
199         l4_addr_t addr = Romain::Region_map::allocate_and_attach(&ds, size_in_bytes,
200                                                                                                                          0, 0, L4_SUPERPAGESHIFT);
201     INFO() << "Log buffer attached to 0x" << std::hex << addr;
202
203     memset((void*)addr, 0, size_in_bytes);
204     _logBuf->set_buffer(reinterpret_cast<unsigned char*>(addr), size_in_bytes);
205 }
206
207
208 /*
209  * Romain ini file settings
210  * =====================
211  *
212  *  'general' section
213  *  -----------------
214  *
215  *  The 'general' section determines which fault handlers are registered.
216  *
217  *  print_vcpu_state [bool]
218  *     - Registers a handler printing the state of a VCPU upon every
219  *       fault entry
220  *
221  *  debug [string = {simple,gdb}]
222  *     - Configures a debugger stub. 'simple' refers to builtin debugging,
223  *       'gdb' starts a gdb stub. Further configuration for the debuggers
224  *       is done in separate INI sections.
225  *
226  *  page_fault_handling [string = {ro}]
227  *     - Specify the way in which paging is done.
228  *       'ro' means that client memory is mapped read-only and write
229  *       accesses to the respective regions are emulated.
230  *
231  *  redundancy [string = {dual, triple}]
232  *     - configure the number of replicas that are started
233  *
234  *
235  *  log [string list]
236  *     - comma-separated list of strings for configuring logging
237  *     - available flags are:
238  *       - mem|memory -> memory management
239  *       - emulator   -> instruction emulation
240  *       - manager    -> replica management
241  *       - faults     -> generic fault entry path
242  *       - redundancy -> DMR/TMR-specific logs
243  *       - swifi      -> fault injetion
244  *       - gdb        -> GDB stub logging
245  *       - all        -> everything
246  *
247  *  logbuf [int] (-1)
248  *       - establish a log buffer with the given size in MB
249  *       - runtime events are logged into this buffer and can later
250  *         be dumped for postprocessing -> this is an alternative to
251  *         printing a lot of stuff to the serial console
252  *
253  *  logcpu [int]
254  *       - event generation needs a global timestamp. On real SMP hardware
255  *         CPUs disagree on their local TSC values. As a workaround, we start
256  *         a dedicated thread that busily writes its local TSC to a global timer
257  *         variable that is then read by everyone else. This of course requires
258  *         the thread to solely run on a dedicated CPU. This option sets the
259  *         respective CPU #.
260  *
261  *  logrdtsc [bool] (false)
262  *       - use local TSC instead of global time stamp counter for event timestamps
263  *         -> use on Qemu where a dedicated timestamp thread does not work properly
264  *
265  *  logreplica [bool] (false)
266  *       - assign each replica a log buffer (mapped to REPLICA_LOG_ADDRESS)
267  *
268  *  logtimeout [int] (-1)
269  *       - run the replicated app for N seconds, then halt all threads and
270  *         print replica stats (as if on termination)
271  *
272  *  replicalogsize [int] (-1)
273  *       - buffser size for the replica-specific log buffer
274  *
275  *  swifi [bool] (false)
276  *       - Perform fault injection experiments, details are configured
277  *         in the [swifi] section.
278  *
279  *  kip-time [bool] (false)
280  *       - Turn on/off KIP timer access. This is used to turn replica
281  *         accesses to the clock field of the KIP into traps (by placing
282  *         software breakpoints on specifically configured instructions).
283  *         Use this, if your application needs clock info from the KIP.
284  *
285  *  max_traps [int] (-1)
286  *       - Handle a maximum amount of traps before terminating the 
287  *         replicated application. Use as a debugging aid.
288  *
289  *  print_time [bool] (true)
290  *       - include timing information in printed output.
291  *
292  *  'gdbstub' section
293  *  -----------------
294  *
295  *  This section configures the behavior of the GDB stub.
296  *
297  *  port [int]
298  *     - Configures the GDB stub to use a TCP/IP connection and wait
299  *       for a remote GDB to connect on the port specified. If this
300  *       option is _not_ set, the GDB stub will try to use a serial
301  *       connection through COM2.
302  *
303  *  XXX make COM port configurable
304  *
305  *  'simpledbg' section
306  *  -------------------
307  *
308  *  This section configures Romain's builtin debugger, which is programmed through
309  *  INI file commands only and performs a narrow range of debugging tasks only.
310  *
311  *  singlestep [int]
312  *     - Patches an INT3 breakpoint to the given address. Then executes the program
313  *       until the breakpoint is hit and thereafter switches to single-stepping
314  *       mode.
315  *
316  *  'kip-time' section
317  *  ------------------
318  *
319  *  The KIP-time instrumentation needs a list of addresses that point to
320  *  KIP->clock accessing instructions. These are supplied as a comma-separated
321  *  list of hex values for the target command.
322  *
323  *  target [comma-separated list of hex addresses]
324  *
325  *  'swifi' section
326  *  ---------------
327  *
328  *  Configures fault-injection experiments that are performed on replicas.
329  *  By default, SWIFI currently injects faults into replica #0.
330  *
331  *  - target [hex]
332  *    specifies an address to place a breakpoint on. Upon hitting this
333  *    BP, a SWIFI injection is performed.
334  *
335  *  - inject [string]
336  *    specifies what kind of injection to perform when hitting the BP.
337  *    Available values:
338  *      - 'gpr' -> flip a random bit in a randomly selected
339  *                 general-purpose register
340  */
341 void Romain::InstanceManager::configure()
342 {
343 #define USE_SHARABLE_TIMESTAMP 1
344
345         int logMB = ConfigIntValue("general:logbuf");
346
347 #if USE_SHARABLE_TIMESTAMP
348         _logBuf = new Measurements::EventBuf(true);
349         L4::Cap<L4Re::Dataspace> tsds;
350         l4_addr_t ts_addr = Romain::Region_map::allocate_and_attach(&tsds, L4_PAGESIZE);
351         l4_touch_ro((void*)ts_addr, L4_PAGESIZE);
352         _logBuf->set_tsc_buffer(reinterpret_cast<l4_uint64_t*>(ts_addr));
353 #else
354         _logBuf = new Measurements::EventBuf();
355 #endif
356         if (logMB != -1) {
357                 configure_logbuf(logMB);
358         }
359
360         Log::logLocalTSC = ConfigBoolValue("general:logrdtsc", false);
361
362         /*
363          * These modes are exclusive: either we use the local TSC _xor_ we start a
364          * timer thread on a dedicated CPU.
365          */
366         if (!Log::logLocalTSC) {
367                 int logCPU = ConfigIntValue("general:logcpu");
368                 if (logCPU != -1) {
369                         INFO() << "Starting counter thread on CPU " << logCPU;
370                         INFO() << "Timestamp @ 0x" << std::hex << (l4_addr_t)_logBuf->timestamp;
371                         Measurements::EventBuf::launchTimerThread((l4_addr_t)_logBuf->timestamp,
372                                                                   logCPU);
373                 }
374         }
375
376         char *log = strdup(ConfigStringValue("general:log", "none"));
377         configure_logflags(log);
378         
379         Log::withtime = ConfigBoolValue("general:print_time", true);
380
381         configure_fault_observers();
382         configure_redundancy();
383         free(log);
384 }
385
386
387 void Romain::InstanceManager::logdump()
388 {
389         int logMB = ConfigIntValue("general:logbuf");
390         if (logMB != -1) {
391                 char const *filename = "sampledump.txt";
392
393                 unsigned oldest = _logBuf->oldest();
394                 unsigned dump_start, dump_size;
395
396                 if (oldest == 0) { // half-full -> dump from 0 to index
397                         dump_start = 0;
398                         dump_size  = _logBuf->index * sizeof(Measurements::GenericEvent);
399                 } else { // buffer completely full -> dump full size starting from oldest entry
400                         dump_start = oldest * sizeof(Measurements::GenericEvent);
401                         dump_size  = _logBuf->size * sizeof(Measurements::GenericEvent);
402                 }
403
404                 uu_dumpz_ringbuffer(filename, _logBuf->buffer,
405                                     _logBuf->size * sizeof(Measurements::GenericEvent),
406                                     dump_start, dump_size);
407         }
408 }
409
410
411 /*
412  * Prepare the stack that is used by the fault handler whenever a
413  * VCPU enters the master task.
414  *
415  * This pushes relevant pointers to the stack so that the handler
416  * functions can use them as parameters.
417  */
418 l4_addr_t Romain::InstanceManager::prepare_stack(l4_addr_t sp,
419                                                  Romain::App_instance *inst,
420                                                  Romain::App_thread *thread,
421                                                  Romain::Thread_group *tgroup)
422 {
423         Romain::Stack st(sp);
424
425         st.push(_am);
426         st.push(tgroup);
427         st.push(thread);
428         st.push(inst);
429         st.push(this);
430         st.push(0); // this would be the return address, but
431                     // handlers return by vcpu_resume()
432
433         return st.sp();
434 }
435
436
437 void Romain::InstanceManager::create_instances()
438 {
439         for (unsigned i = 0; i < _num_inst; ++i) {
440                 _instances.push_back(new Romain::App_instance(_name, i));
441         }
442 }
443
444
445 Romain::App_thread*
446 Romain::InstanceManager::create_thread(l4_umword_t eip, l4_umword_t esp,
447                                        unsigned instance_id, Romain::Thread_group *group)
448 {
449                 Romain::App_thread *at = new Romain::App_thread(eip, esp,
450                                                           reinterpret_cast<l4_addr_t>(VCPU_handler),
451                                                           reinterpret_cast<l4_addr_t>(VCPU_startup)
452                 );
453
454                 /*
455                  * Set up the VCPU handler thread. It has been allocated in
456                  * App_thread's constructor.
457                  */
458                 DEBUG() << "prepare: " << (void*)at->handler_sp();
459                 at->handler_sp(prepare_stack(at->handler_sp(),
460                                              _instances[instance_id], at, group));
461
462                 /*
463                  * phys. CPU assignment, currently done by mapping instances to dedicated
464                  * physical CPUs
465                  */
466                 if (_num_cpu > 1) {
467                         INFO() << instance_id << " " << (instance_id+1) % _num_cpu << " " << _num_cpu;
468                         
469                         unsigned logCPU = 1;
470
471                         /* XXX REPLICAS PER CPU XXX */
472                         //logCPU = logicalToCPU(group->uid % _num_cpu);
473
474                         /* XXX INSTANCES PER CPU XXX */
475                         //logCPU = logicalToCPU((instance_id + 1) % _num_cpu);
476
477                         /* XXX OVERLAPPING REPLICAS XXX */
478                         //logCPU = logicalToCPU((group->uid + instance_id) % _num_cpu);
479
480                         /* XXX RANDOM PLACEMENT XXX */
481                         //logCPU = logicalToCPU(random() % _num_cpu);
482                         
483                         /* XXX Threads assigned RR to CPUs */
484                         static int threadcount = 0;
485                         //logCPU = logicalToCPU(threadcount % _num_cpu);
486                         //threadcount++;
487
488                         /* XXX The hard-coded placement map:
489                          * Manual optimization for pthreads applications. In our scenarios,
490                          * pthreads starts a manager as the second thread and this manager
491                          * often does nothing. Therefore, instead of placing each idle manager
492                          * replica on its own CPU, we simply put them onto CPU0 where they
493                          * don't hurt anyone.
494                          */
495                         int cpumap[3][15] = { // single -> 1:1 mapping
496                                               {0, 1, 2,
497                                                3, 4, 5,
498                                                6, 7, 8,
499                                                9, 10, 11,
500                                                0, 1, 2},
501                                                                   // DMR
502                                               {0, 1, 0,
503                                                0, 2, 3,
504                                                4, 5, 6,
505                                                7, 8, 9,
506                                               10, 11, 0},
507                                                                   // TMR
508                                               {0, 1, 2,
509                                                0, 0, 0,
510                                                3, 4, 5,
511                                                6, 7, 8,
512                                                9, 10, 11}
513                                              };
514                         logCPU = logicalToCPU(cpumap[instance_count()-1][threadcount]);
515                         threadcount++;
516
517                         at->cpu(logCPU);
518                 } else {
519                         at->cpu(0);
520                 }
521
522                 return at;
523 }
524
525
526 Romain::Thread_group *
527 Romain::InstanceManager::create_thread_group(l4_umword_t eip, l4_umword_t esp, std::string n,
528                                              unsigned cap, unsigned uid)
529 {
530         Romain::Thread_group *group = new Romain::Thread_group(n, cap, uid);
531         group->set_redundancy_callback(new DMR(_num_inst));
532
533         for (unsigned i = 0; i < _num_inst; ++i) {
534                 /*
535                  * Thread creation
536                  */
537                 Romain::App_thread *at = create_thread(eip, esp, i, group);
538                 group->add_replica(at);
539         }
540
541         _threadgroups.push_back(group);
542         return group;
543 }
544
545
546 void Romain::InstanceManager::run_instances()
547 {
548         Romain::Thread_group *group = create_thread_group(_init_eip, _init_esp, "init",
549                                                                                                           Romain::FIRST_REPLICA_CAP, 0);
550         DEBUG() << "created group object @ " << (void*)group;
551         theObjectFactory.register_thread_group(group, Romain::FIRST_REPLICA_CAP);
552
553         _check(group->threads.size() != _num_inst, "not enough threads created?");
554
555         for (unsigned i = 0; i < _num_inst; ++i) {
556
557                 App_thread *at = group->threads[i];
558
559                 /*
560                  * Stack setup
561                  */
562                 at->thread_sp((l4_addr_t)_am->stack()->relocate(_am->stack()->ptr()));
563
564                 /*
565                  * The initial UTCB address is on top of the app's stack. This location
566                  * is used for the first GDT entry, which L4Re later uses to find the
567                  * thread's UTCB address.
568                  */
569                 at->setup_utcb_segdesc(_am->stack()->target_top() - 4, 4);
570
571                 /*
572                  * Establish UTCB mapping
573                  */
574                 Romain::Region_handler &rh = const_cast<Romain::Region_handler&>(
575                                                   _am->rm()->find(_am->prog_info()->utcbs_start)->second);
576                 _check(_am->rm()->copy_existing_mapping(rh, 0, i) != true,
577                        "could not create UTCB copy");
578                 at->remote_utcb(rh.local_region(i).start());
579
580                 /*
581                  * Notfiy handlers about an instance that has started
582                  */
583                 startup_notify(_instances[i], at, group, _am);
584
585                 /*
586                  * Start the thread itself
587                  */
588                 at->vcpu()->r()->sp = at->thread_sp();
589                 at->start();
590                 at->commit_client_gdt();
591         }
592
593         group->activate();
594 }