qemu-trivial
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-trivial] [PATCH] Added code for synchronzing the internal simulati


From: James Nutaro
Subject: [Qemu-trivial] [PATCH] Added code for synchronzing the internal simulation clock with an external simulation clock
Date: Wed, 13 Jan 2016 10:35:30 -0500


This patch adds an interface via UNIX shared memory for pacing the execution of QEMU to match an external simulation clock. The aim is to allow QEMU to be used as a module inside of a larger simulation system.

The body of the patch is below.

Jim Nutaro (address@hidden)

From 0e0d352be2f91ed2df3526bc55753acfa97509e9 Mon Sep 17 00:00:00 2001
From: "James J. Nutaro" <address@hidden>
Date: Mon, 9 Nov 2015 12:36:06 -0500
Subject: [PATCH] QQQ is a module for pacing the rate of advance of the
 computer clock in reference to an external simulation clock. The basic
 approach used here is adapted from QBox from Green Socs (hence the name, Q?
 Q? Q? - qqq!). Its intended use is to incorporate a complete software stack
 into constructive simulation models. The solution proposed here assumes that
 devices (NIC cards, serial ports, and other low level hardware) will be
 modeled within QEMU and connected to via a UNIX socket or some other
 appropriate mechanism. Other pieces of equipment (e.g., electric motors,
 communication networks, quad coptors, robotic arms, ...) would be modeled in
 some other simulation tool. Hence, I assume that the problem of exchanging
 data can be solved using existing mechanisms within QEMU and that the only
 remaining problem is time synchronization. This module addresses the time
 synchronization problem.

There is an example of how to use this module in the
adevs simulation package at http://sourceforge.net/projects/adevs/

The mode of operation is as follows:

The simulator creates a shared memory of size sizeof(int) that will be
used to exchange time advance information and writes a negative value
to that shared memory segment. Then the simulator forks a process
to execute qemu. Qemu attaches to this shared memory segment using
a key that includes the PID of the forked child (see qqq.c for the
expected shared memory key).

Qemu waits for a positive value to appear in the shared memory. Meanwhile
the simulator writes a positive value to indicate the maximum time
advance that it allows to qemu. When qemu receives this positive value
it schedules a timer event for time advance microseconds in the future.
A value of zero indicates that qemu should exit.

Now the simulator spins on the shared memory value waiting for it to become
negative. When qemu executes the scheduled timer event, qemu writes a
negative value indicating the actual time advanced. At this time, the simulator
is able to run again and this cycle is repeated until the simulation ends.

Authors:
  James Nutaro <address@hidden>
---
 Makefile.target          |   2 +-
 cpus.c                   |   9 ++++-
 include/qemu/thread.h    |   8 ++++
 qqq.c                    | 101 +++++++++++++++++++++++++++++++++++++++++++++++
 qqq.h                    |  67 +++++++++++++++++++++++++++++++
 util/qemu-thread-posix.c |  14 +++++++
 vl.c                     |   5 +++
 7 files changed, 204 insertions(+), 2 deletions(-)
 create mode 100644 qqq.c
 create mode 100644 qqq.h

diff --git a/Makefile.target b/Makefile.target
index 962d004..8956afe 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -131,7 +131,7 @@ endif #CONFIG_BSD_USER
 #########################################################
 # System emulator target
 ifdef CONFIG_SOFTMMU
-obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
+obj-y += arch_init.o qqq.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
 obj-y += qtest.o bootdevice.o
 obj-y += hw/
 obj-$(CONFIG_KVM) += kvm-all.o
diff --git a/cpus.c b/cpus.c
index 877bd70..b040285 100644
--- a/cpus.c
+++ b/cpus.c
@@ -44,6 +44,8 @@
 #include "hw/nmi.h"
 #include "sysemu/replay.h"
 
+#include "qqq.h"
+
 #ifndef _WIN32
 #include "qemu/compatfd.h"
 #endif
@@ -998,7 +1000,12 @@ static void qemu_tcg_wait_io_event(CPUState *cpu)
        /* Start accounting real time to the virtual clock if the CPUs
           are idle.  */
         qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
-        qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
+       /* If qqq is running then this wait must timeout so that the
+        * simulation does not become stuck when the cpu idles */
+        if (qqq_enabled())
+            qemu_cond_wait_timeout_ns(cpu->halt_cond, &qemu_global_mutex, 10000);
+        else
+            qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
     }
 
     while (iothread_requesting_mutex) {
diff --git a/include/qemu/thread.h b/include/qemu/thread.h
index 5114ec8..c0f6009 100644
--- a/include/qemu/thread.h
+++ b/include/qemu/thread.h
@@ -36,6 +36,14 @@ void qemu_cond_destroy(QemuCond *cond);
 void qemu_cond_signal(QemuCond *cond);
 void qemu_cond_broadcast(QemuCond *cond);
 void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
+/* This defaults to qemu_cond_wait() on Windows */
+#ifndef _WIN32
+void qemu_cond_wait_timeout_ns(QemuCond *cond, QemuMutex *mutex, int64_t timeout_ns);
+#else
+void qemu_cond_wait_timeout_ns(QemuCond *cond, QemuMutex *mutex, int64_t timeout_ns) {
+  qemu_cond_wait(cond,mutex);
+}
+#endif
 
 void qemu_sem_init(QemuSemaphore *sem, int init);
 void qemu_sem_post(QemuSemaphore *sem);
diff --git a/qqq.c b/qqq.c
new file mode 100644
index 0000000..5c2a786
--- /dev/null
+++ b/qqq.c
@@ -0,0 +1,101 @@
+
+/* This is a Linux only feature */
+
+#ifndef _WIN32
+
+#include "qemu/main-loop.h"
+#include "qemu/timer.h"
+#include "qemu/thread.h"
+#include "sysemu/cpus.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <pthread.h>
+#include "qqq.h"
+
+/* Shared memory data */
+static void* shm = NULL;
+static int64_t t = 0;
+static QEMUTimer* sync_timer = NULL;
+
+static void write_mem_value(int val)
+{
+  /* I AM ASSUMING THE MEMORY WRITE WILL BE ATOMIC AND WILL
+   * NOT BE CACHED */
+  (*((volatile int*)shm)) = val;
+}
+
+static int read_mem_value(void)
+{
+  /* I AM ASSUMING THE MEMORY READ WILL BE ATOMIC AND WILL
+   * NOT BE CACHED */
+  return (*((volatile int*)shm));
+}
+
+static void schedule_next_event(void)
+{
+  int time_advance;
+  /* Get the time advance allowed by the simulator. This is provided
+   * as a positive value that is written by the simulator to the
+   * shared memory location. */
+  while ((time_advance = read_mem_value()) < 0);
+  /* A zero time advance is an instruction to shutdown */
+  if (time_advance == 0)
+  {
+    munmap(shm,sizeof(int));
+    exit(0);
+  }
+  /* Schedule the next syncronization point */
+  timer_mod(sync_timer,t+time_advance);
+}
+
+static void sync_func(void* data)
+{
+  /* Report the actual elapsed time. The value written will be
+   * -(elapsed time), and the simulator will recognized the negative
+   * value as a signal that the write has been done. */
+  int64_t tnow = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
+  int usecs = tnow-t;
+  write_mem_value((usecs > 0) ? -usecs : -1);
+  /* Update our time of last event */
+  t = tnow;
+  /* Schedule the next event */
+  schedule_next_event();
+}
+
+bool qqq_enabled(void) { return (shm != NULL); }
+
+void setup_qqq(void)
+{
+  char shm_key[100];
+  /* Attach to the shared memory region */
+  sprintf(shm_key,"/qemu_%d",getpid());
+  errno = 0;
+  int fd = shm_open(shm_key,O_RDWR,S_IRWXU);
+  if (fd == -1)
+  {
+    perror("Could not open shared memory, qqq not enabled");
+    return;
+  }
+  shm = mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
+  if (shm == NULL)
+  {
+    perror("Could not map shared memory, qqq not enabled");
+    shm_unlink(shm_key);
+    return;
+  }
+  /* Done with the shared memory file handle */
+  close(fd);
+  shm_unlink(shm_key);
+  /* Start the timer to ensure time warps advance the clock */
+  sync_timer = timer_new_us(QEMU_CLOCK_VIRTUAL,sync_func,NULL);
+  /* Get the time advance that is requested by the simulation */
+  schedule_next_event();
+}
+
+#endif
diff --git a/qqq.h b/qqq.h
new file mode 100644
index 0000000..687a9f3
--- /dev/null
+++ b/qqq.h
@@ -0,0 +1,67 @@
+/*
+ * A module for pacing the rate of advance of the computer clock
+ * in reference to an external simulation clock. The basic approach
+ * used here is adapted from QBox from Green Socs (hence the name, Q?
+ * Q? Q? - qqq!). The basic mode of operation is as follows:
+ *
+ * The simulator creates a shared memory of size sizeof(int) that will be
+ * used to exchange time advance information, writes a negative value
+ * to that shared memory segment, and then forks a process
+ * to execute qemu. Qemu attaches to this shared memory segment using
+ * a key that includes the PID of the forked child (see qqq.c for the
+ * expected shared memory key).
+ *
+ * Qemu waits for a positive to appear in the shared memory. Meanwhile
+ * the simulator writes a positive value to indicate the maximum time
+ * advance that it allows to qemu. When qemu receives this positive value
+ * it schedules a timer event for time advance microseconds in the future.
+ *
+ * Now the simulator spins on the shared memory value waiting for it to become
+ * negative. When qemu executes the scheduled timer event, qemu writes a
+ * negative value indicating the actual time advanced. At this time, the simulator
+ * is able to run again and this cycle is repeated until the simulation ends.
+ *
+ * Authors:
+ *   James Nutaro <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *  
+ */
+#ifndef QQQ_H
+#define QQQ_H
+
+/* This is a Linux only feature */
+
+#ifdef _WIN32
+
+/* Windows functions that do nothing */
+static inline bool qqq_enabled(void) { return false; }
+static inline void setup_qqq(void){}
+
+#else
+
+/* The Linux implementation */
+
+#include "qemu/main-loop.h"
+#include "qemu/timer.h"
+#include "qemu/thread.h"
+#include "sysemu/cpus.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <pthread.h>
+
+void setup_qqq(void);
+/* Returns true if qqq is enabled and false otherwise.
+ * It will be enabled only if setup_qqq() is able to
+ * attach to the shared memory segment. */
+bool qqq_enabled(void);
+
+#endif
+#endif
diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c
index dbd8094..c6396c4 100644
--- a/util/qemu-thread-posix.c
+++ b/util/qemu-thread-posix.c
@@ -134,6 +134,20 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
         error_exit(err, __func__);
 }
 
+void qemu_cond_wait_timeout_ns(QemuCond *cond, QemuMutex *mutex, int64_t timeout_ns)
+{
+    static const long ns_sec = 1000000000LL;
+    struct timeval tv;
+    struct timespec ts;
+    int err;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec + (timeout_ns + tv.tv_usec*1000) / ns_sec;
+    ts.tv_nsec = (timeout_ns + tv.tv_usec*1000) % ns_sec;
+    err = pthread_cond_timedwait(&cond->cond, &mutex->lock, &ts);
+    if (err != 0 && err != ETIMEDOUT)
+        error_exit(err, __func__);
+}
+
 void qemu_sem_init(QemuSemaphore *sem, int init)
 {
     int rc;
diff --git a/vl.c b/vl.c
index 21e8876..8a2facc 100644
--- a/vl.c
+++ b/vl.c
@@ -125,6 +125,8 @@ int main(int argc, char **argv)
 #include "sysemu/replay.h"
 #include "qapi/qmp/qerror.h"
 
+#include "qqq.h"
+
 #define MAX_VIRTIO_CONSOLES 1
 #define MAX_SCLP_CONSOLES 1
 
@@ -4402,6 +4404,9 @@ int main(int argc, char **argv, char **envp)
     /* spice needs the timers to be initialized by this point */
     qemu_spice_init();
 #endif
+    /* try to setup the qqq interface for syncing advance of the virtual clock
+     * with an external simulator */
+    setup_qqq();
 
     cpu_ticks_init();
     if (icount_opts) {
--
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]