From patchwork Tue Feb 2 20:49:12 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Nutaro X-Patchwork-Id: 8196281 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id E4E8BBEEE5 for ; Tue, 2 Feb 2016 22:17:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D237C20270 for ; Tue, 2 Feb 2016 22:17:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B8DDE2026D for ; Tue, 2 Feb 2016 22:17:08 +0000 (UTC) Received: from localhost ([::1]:59723 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aQjFz-0006IJ-WC for patchwork-qemu-devel@patchwork.kernel.org; Tue, 02 Feb 2016 17:17:08 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50145) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aQhsy-0001un-EB for qemu-devel@nongnu.org; Tue, 02 Feb 2016 15:49:19 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aQhsv-0002hj-MV for qemu-devel@nongnu.org; Tue, 02 Feb 2016 15:49:16 -0500 Received: from mail-ig0-x22e.google.com ([2607:f8b0:4001:c05::22e]:35649) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aQhsv-0002hf-BW for qemu-devel@nongnu.org; Tue, 02 Feb 2016 15:49:13 -0500 Received: by mail-ig0-x22e.google.com with SMTP id t15so70415835igr.0 for ; Tue, 02 Feb 2016 12:49:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to:content-type; bh=HAnyMK24un2+ycm168jmwEiPS+cYVA/MWWDHXvVb5FA=; b=Jt0HCDXdnZWgAt1icP0oYFH9JmIf3Knu9yBJjlkhceJoA6eQYRl2fdoRx80uR7mXMT t3EdTpT0sBXwYIXfK+L1H6L4+FvFoVj2tKsHMTK+iRf8yG+aCSHe/F9BOLeYz5MZ/p+d ommzptAJwTwS98WT/lWBgUVJXy6ixmvD8Lqmw/eTncuoHiFojp1w7HOmNDER7O7DUP+k 4ZyB7hgyC3EUcJVzLKWU6DJpo4UjFTyNQpJzttOkiAA451nuLIhYCxz0feeDmW9g+Lst GrXqXaZddD/K4j+E6++bgYVwkNJn333tDd9o5IX6FX7bWVOYYRnGXD3r+51GcfoYrW93 t3XQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to :content-type; bh=HAnyMK24un2+ycm168jmwEiPS+cYVA/MWWDHXvVb5FA=; b=TtzEK9lHeZQCIRVe36TLzb+TfYT0wWMufq6kE4LBQfhXJLwL0zFZhbQm03IbMM1BfC UDg/2dfJ4GFd/Jmr2acsxn+rjcW98etdUzcma9S0+IQwwsV3kr3FXndfXFBKeqKLnpGo M40VLV6GoZGFjxws3jGMjQ+bmgOFT+YRN46mYPoXh26IPKmxv4DjZc5gvxk5SCobwqoP MzW77acuIN6DIKM6xl3ZsUErbCbdF+TvCUQrVy8TeC7sd3AdGmH0ebb05e0V1U9ZiU5M BZFb9hVODtyyg/5Rtf+Hq96/WCC8NPFj99b6Xa/bTK0/ToWiqcOJtuPRPLYsOJAyTNzh AywA== X-Gm-Message-State: AG10YOSo0eaPJYEfxmZiieE7PlJKlrUHe0/D38B4nhz9xUaUbiS0VV9u74GLDaK54nwtEKGyPxcSsJaf/45+vQ== MIME-Version: 1.0 X-Received: by 10.50.36.35 with SMTP id n3mr18123221igj.73.1454446152375; Tue, 02 Feb 2016 12:49:12 -0800 (PST) Received: by 10.36.200.8 with HTTP; Tue, 2 Feb 2016 12:49:12 -0800 (PST) Date: Tue, 2 Feb 2016 15:49:12 -0500 Message-ID: From: James Nutaro To: qemu-devel@nongnu.org X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2607:f8b0:4001:c05::22e X-Mailman-Approved-At: Tue, 02 Feb 2016 17:16:57 -0500 Subject: [Qemu-devel] [PATCH] Added code for synchronzing the internal simulation clock with an external simulation clock X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, HTML_MESSAGE, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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. Signed-off-by: Jim Nutaro (nutaro@gmail.com) From 0e0d352be2f91ed2df3526bc55753acfa97509e9 Mon Sep 17 00:00:00 2001 From: "James J. Nutaro" 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 --- 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 if (icount_opts) { 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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();