]> rtime.felk.cvut.cz Git - l4.git/blobdiff - l4/pkg/plr/server/src/manager.cc
update
[l4.git] / l4 / pkg / plr / server / src / manager.cc
index bf2cd138e32c3ec135f2ff1d091446efa1c84939..4d8c38d2fa4f7ed57d9ee437e800393479e421ab 100644 (file)
@@ -3,7 +3,7 @@
  *
  *     Instance manager implementation.
  *
- * (c) 2011 Björn Döbel <doebel@os.inf.tu-dresden.de>,
+ * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
  *     economic rights: Technische Universität Dresden (Germany)
  * This file is part of TUD:OS and distributed under the terms of the
  * GNU General Public License 2.
 #include "configuration"
 
 #include <l4/sys/segment.h>
+#include <l4/re/mem_alloc>
+#include <l4/re/rm>
+#include <l4/re/env>
+#include <l4/re/dataspace>
+#include <l4/re/util/cap_alloc>
+#include <l4/plr/uu.h>
 
 #define MSG() DEBUGf(Romain::Log::Manager)
+#include "fault_handlers/syscalls_factory.h"
 
 Romain::Configuration Romain::globalconfig;
 
+
 L4_INLINE unsigned countbits(long v)
 {
        v = v - ((v >> 1) & 0x55555555);                         // reuse input as temporary
@@ -27,30 +35,34 @@ L4_INLINE unsigned countbits(long v)
        return ((v + ((v >> 4) & 0xF0F0F0F)) * 0x1010101) >> 24; // count
 }
 
+
 L4_INLINE l4_umword_t count_online_cpus()
 {
-       l4_umword_t ret;
-       l4_sched_cpu_set_t set = l4_sched_cpu_set(0, 0);
-       if (l4_error(L4Re::Env::env()->scheduler()->info(&ret, &set)) < 0) {
+       l4_umword_t maxcpu = 0;
+       l4_sched_cpu_set_t cpuonline = l4_sched_cpu_set(0, 0);
+       if (l4_error(L4Re::Env::env()->scheduler()->info(&maxcpu, &cpuonline)) < 0) {
                ERROR() << "reading CPU info";
        }
-       ret = countbits(set.map);
 
-       INFO() << "Found " << ret << " CPUs.";
-       return ret;
+       INFO() << "Online " << countbits(cpuonline.map) << " / MAX " << maxcpu;
+
+       return countbits(cpuonline.map) > maxcpu ? maxcpu : countbits(cpuonline.map);
 }
 
+
 Romain::InstanceManager::InstanceManager(unsigned int argc,
                                          char const **argv,
                                          unsigned num_instances)
-       : _am(0),
+       : LogicalCPUMap(),
+         _am(0),
          _instances(),
-         _callback(0),
+         _threadgroups(),
          _num_observers(0),
          _num_inst(num_instances),
          _num_cpu(1),
          _argc(argc), // XXX: remove
-         _argv(argv)  // XXX: remove
+         _argv(argv), // XXX: remove
+         _logBuf(0)
 {
        configure();
 
@@ -128,20 +140,34 @@ void Romain::InstanceManager::configure_fault_observers()
         * First, register those observers that don't interfere
         * with anyone else and get notified all the time.
         */
+       DEBUG() << "[observer] vcpu state.";
        BoolObserverConfig("general:print_vcpu_state",
                           this, "vcpu_state");
+       DEBUG() << "[observer] trap limit.";
        ObserverConfig(this, "trap_limit");
 
        /*
-        * We always need the system call observer.
+        * Always needed -- slightly ordered by the number of
+        * calls they are expected to see, so that we minimize
+        * the amount of unnecessary observer callbacks.
         */
-       ObserverConfig(this, "syscalls");
+       DEBUG() << "[observer] page faults.";
        ObserverConfig(this, "pagefaults");
+       DEBUG() << "[observer] syscalls.";
+       ObserverConfig(this, "syscalls");
+       DEBUG() << "[observer] threads.";
+       BoolObserverConfig("general:threads", this, "threads");
+       DEBUG() << "[observer] trap.";
        ObserverConfig(this, "trap");
 
+       DEBUG() << "[observer] simpledbg.";
        StringObserverConfig("general:debug", this);
+       DEBUG() << "[observer] intercept-kip.";
        BoolObserverConfig("general:intercept_kip", this, "kip-time");
+       DEBUG() << "[observer] swifi.";
        BoolObserverConfig("general:swifi", this, "swifi");
+       DEBUG() << "[observer] logreplica.";
+       BoolObserverConfig("general:logreplica", this, "replicalog");
 }
 
 
@@ -152,19 +178,31 @@ void Romain::InstanceManager::configure_redundancy()
        INFO() << "red: '" << redundancy << "'";
        if (strcmp(redundancy, "none") == 0) {
                _num_inst = 1;
-               set_redundancy_callback(new NoRed());
        } else if (strcmp(redundancy, "dual") == 0) {
                _num_inst = 2;
-               set_redundancy_callback(new DMR(2));
        } else if (strcmp(redundancy, "triple") == 0) {
                _num_inst = 3;
-               set_redundancy_callback(new DMR(3));
        } else {
                ERROR() << "Invalid redundancy setting: " << redundancy;
                enter_kdebug("Invalid redundancy setting");
        }
 }
 
+
+void Romain::InstanceManager::configure_logbuf(int sizeMB)
+{
+       INFO() << "Log buffer size: " << sizeMB << " MB requested.";
+       unsigned size_in_bytes = sizeMB << 20;
+
+       L4::Cap<L4Re::Dataspace> ds;
+
+       l4_addr_t addr = Romain::Region_map::allocate_and_attach(&ds, size_in_bytes, 0, L4_SUPERPAGESHIFT);
+    INFO() << "Log buffer attached to 0x" << std::hex << addr;
+
+    _logBuf->set_buffer(reinterpret_cast<unsigned char*>(addr), size_in_bytes);
+}
+
+
 /*
  * Romain ini file settings
  * =====================
@@ -204,6 +242,34 @@ void Romain::InstanceManager::configure_redundancy()
  *       - gdb        -> GDB stub logging
  *       - all        -> everything
  *
+ *  logbuf [int] (-1)
+ *       - establish a log buffer with the given size in MB
+ *       - runtime events are logged into this buffer and can later
+ *         be dumped for postprocessing -> this is an alternative to
+ *         printing a lot of stuff to the serial console
+ *
+ *  logcpu [int]
+ *       - event generation needs a global timestamp. On real SMP hardware
+ *         CPUs disagree on their local TSC values. As a workaround, we start
+ *         a dedicated thread that busily writes its local TSC to a global timer
+ *         variable that is then read by everyone else. This of course requires
+ *         the thread to solely run on a dedicated CPU. This option sets the
+ *         respective CPU #.
+ *
+ *  logrdtsc [bool] (false)
+ *       - use local TSC instead of global time stamp counter for event timestamps
+ *         -> use on Qemu where a dedicated timestamp thread does not work properly
+ *
+ *  logreplica [bool] (false)
+ *       - assign each replica a log buffer (mapped to REPLICA_LOG_ADDRESS)
+ *
+ *  logtimeout [int] (-1)
+ *       - run the replicated app for N seconds, then halt all threads and
+ *         print replica stats (as if on termination)
+ *
+ *  replicalogsize [int] (-1)
+ *       - buffser size for the replica-specific log buffer
+ *
  *  swifi [bool] (false)
  *       - Perform fault injection experiments, details are configured
  *         in the [swifi] section.
@@ -272,6 +338,39 @@ void Romain::InstanceManager::configure_redundancy()
  */
 void Romain::InstanceManager::configure()
 {
+#define USE_SHARABLE_TIMESTAMP 1
+
+       int logMB = ConfigIntValue("general:logbuf");
+
+#if USE_SHARABLE_TIMESTAMP
+       _logBuf = new Measurements::EventBuf(true);
+       L4::Cap<L4Re::Dataspace> tsds;
+       l4_addr_t ts_addr = Romain::Region_map::allocate_and_attach(&tsds, L4_PAGESIZE);
+       l4_touch_ro((void*)ts_addr, L4_PAGESIZE);
+       _logBuf->set_tsc_buffer(reinterpret_cast<l4_uint64_t*>(ts_addr));
+#else
+       _logBuf = new Measurements::EventBuf();
+#endif
+       if (logMB != -1) {
+               configure_logbuf(logMB);
+       }
+
+       Log::logLocalTSC = ConfigBoolValue("general:logrdtsc", false);
+
+       /*
+        * These modes are exclusive: either we use the local TSC _xor_ we start a
+        * timer thread on a dedicated CPU.
+        */
+       if (!Log::logLocalTSC) {
+               int logCPU = ConfigIntValue("general:logcpu");
+               if (logCPU != -1) {
+                       INFO() << "Starting counter thread on CPU " << logCPU;
+                       INFO() << "Timestamp @ 0x" << std::hex << (l4_addr_t)_logBuf->timestamp;
+                       Measurements::EventBuf::launchTimerThread((l4_addr_t)_logBuf->timestamp,
+                                                                 logCPU);
+               }
+       }
+
        char *log = strdup(ConfigStringValue("general:log", "none"));
        configure_logflags(log);
        
@@ -283,6 +382,30 @@ void Romain::InstanceManager::configure()
 }
 
 
+void Romain::InstanceManager::logdump()
+{
+       int logMB = ConfigIntValue("general:logbuf");
+       if (logMB != -1) {
+               char const *filename = "sampledump.txt";
+
+               unsigned oldest = _logBuf->oldest();
+               unsigned dump_start, dump_size;
+
+               if (oldest == 0) { // half-full -> dump from 0 to index
+                       dump_start = 0;
+                       dump_size  = _logBuf->index * sizeof(Measurements::GenericEvent);
+               } else { // buffer completely full -> dump full size starting from oldest entry
+                       dump_start = oldest * sizeof(Measurements::GenericEvent);
+                       dump_size  = _logBuf->size * sizeof(Measurements::GenericEvent);
+               }
+
+               uu_dumpz_ringbuffer(filename, _logBuf->buffer,
+                                   _logBuf->size * sizeof(Measurements::GenericEvent),
+                                   dump_start, dump_size);
+       }
+}
+
+
 /*
  * Prepare the stack that is used by the fault handler whenever a
  * VCPU enters the master task.
@@ -292,11 +415,13 @@ void Romain::InstanceManager::configure()
  */
 l4_addr_t Romain::InstanceManager::prepare_stack(l4_addr_t sp,
                                                  Romain::App_instance *inst,
-                                                 Romain::App_thread *thread)
+                                                 Romain::App_thread *thread,
+                                                 Romain::Thread_group *tgroup)
 {
        Romain::Stack st(sp);
 
        st.push(_am);
+       st.push(tgroup);
        st.push(thread);
        st.push(inst);
        st.push(this);
@@ -315,34 +440,132 @@ void Romain::InstanceManager::create_instances()
 }
 
 
-void Romain::InstanceManager::run_instances()
+Romain::App_thread*
+Romain::InstanceManager::create_thread(l4_umword_t eip, l4_umword_t esp,
+                                       unsigned instance_id, Romain::Thread_group *group)
 {
-       for (unsigned i = 0; i < _num_inst; ++i) {
-               Romain::App_thread *at = new Romain::App_thread(_init_eip, _init_esp,
+               Romain::App_thread *at = new Romain::App_thread(eip, esp,
                                                          reinterpret_cast<l4_addr_t>(VCPU_handler),
                                                          reinterpret_cast<l4_addr_t>(VCPU_startup)
                );
 
-               at->setup_gdt(_am->stack()->target_top() - 4, 4);
+               /*
+                * Set up the VCPU handler thread. It has been allocated in
+                * App_thread's constructor.
+                */
+               DEBUG() << "prepare: " << (void*)at->handler_sp();
+               at->handler_sp(prepare_stack(at->handler_sp(),
+                                            _instances[instance_id], at, group));
+
+               /*
+                * phys. CPU assignment, currently done by mapping instances to dedicated
+                * physical CPUs
+                */
+               if (_num_cpu > 1) {
+                       INFO() << instance_id << " " << (instance_id+1) % _num_cpu << " " << _num_cpu;
+                       
+                       unsigned logCPU = 1;
+
+                       /* XXX REPLICAS PER CPU XXX */
+                       //logCPU = logicalToCPU(group->uid % _num_cpu);
+
+                       /* XXX INSTANCES PER CPU XXX */
+                       //logCPU = logicalToCPU((instance_id + 1) % _num_cpu);
+
+                       /* XXX OVERLAPPING REPLICAS XXX */
+                       //logCPU = logicalToCPU((group->uid + instance_id) % _num_cpu);
+
+                       /* XXX RANDOM PLACEMENT XXX */
+                       //logCPU = logicalToCPU(random() % _num_cpu);
+                       
+                       /* XXX Threads assigned RR to CPUs */
+                       static int threadcount = 0;
+                       logCPU = logicalToCPU(threadcount % _num_cpu);
+                       threadcount++;
+
+                       /* XXX The hard-coded placement map */
+                       //int cpumap[12] = {0, 1, 2,
+                       //                  0, 0, 0,
+                       //                  3, 4, 5,
+                       //                  0, 0, 0};
+                       //logCPU = logicalToCPU(cpumap[threadcount]);
+                       //threadcount++;
+
+                       at->cpu(logCPU);
+               } else {
+                       at->cpu(0);
+               }
+
+               return at;
+}
+
+
+Romain::Thread_group *
+Romain::InstanceManager::create_thread_group(l4_umword_t eip, l4_umword_t esp, std::string n,
+                                             unsigned cap, unsigned uid)
+{
+       Romain::Thread_group *group = new Romain::Thread_group(n, cap, uid);
+       group->set_redundancy_callback(new DMR(_num_inst));
+
+       for (unsigned i = 0; i < _num_inst; ++i) {
+               /*
+                * Thread creation
+                */
+               Romain::App_thread *at = create_thread(eip, esp, i, group);
+               group->add_replica(at);
+       }
+
+       _threadgroups.push_back(group);
+       return group;
+}
+
 
+void Romain::InstanceManager::run_instances()
+{
+       Romain::Thread_group *group = create_thread_group(_init_eip, _init_esp, "init",
+                                                                                                         Romain::FIRST_REPLICA_CAP, 0);
+       DEBUG() << "created group object @ " << (void*)group;
+       theObjectFactory.register_thread_group(group, Romain::FIRST_REPLICA_CAP);
+
+       _check(group->threads.size() != _num_inst, "not enough threads created?");
+
+       for (unsigned i = 0; i < _num_inst; ++i) {
+
+               App_thread *at = group->threads[i];
+
+               /*
+                * Stack setup
+                */
+               at->thread_sp((l4_addr_t)_am->stack()->relocate(_am->stack()->ptr()));
+
+               /*
+                * The initial UTCB address is on top of the app's stack. This location
+                * is used for the first GDT entry, which L4Re later uses to find the
+                * thread's UTCB address.
+                */
+               at->setup_utcb_segdesc(_am->stack()->target_top() - 4, 4);
+
+               /*
+                * Establish UTCB mapping
+                */
                Romain::Region_handler &rh = const_cast<Romain::Region_handler&>(
                                                  _am->rm()->find(_am->prog_info()->utcbs_start)->second);
                _check(_am->rm()->copy_existing_mapping(rh, 0, i) != true,
                       "could not create UTCB copy");
                at->remote_utcb(rh.local_region(i).start());
 
-               DEBUG() << "prepare: " << (void*)at->handler_sp();
-               at->handler_sp(prepare_stack(at->handler_sp(), _instances[i], at));
-               at->thread_sp((l4_addr_t)_am->stack()->relocate(_am->stack()->ptr()));
-               if (_num_cpu > 1) {
-                       INFO() << i << " " << (i+1) % _num_cpu << " " << _num_cpu;
-                       at->cpu((i+1) % _num_cpu);
-               } else {
-                       at->cpu(0);
-               }
-
-               startup_notify(_instances[i], at, _am);
+               /*
+                * Notfiy handlers about an instance that has started
+                */
+               startup_notify(_instances[i], at, group, _am);
 
+               /*
+                * Start the thread itself
+                */
+               at->vcpu()->r()->sp = at->thread_sp();
                at->start();
+               at->commit_client_gdt();
        }
+
+       group->activate();
 }