qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [RFC][PATCH v2] add realtime option


From: Satoru Moriya
Subject: [Qemu-devel] [RFC][PATCH v2] add realtime option
Date: Fri, 21 Dec 2012 22:46:35 +0000

Changelog
v1 -> v2
 - add RFC tag again
 - change semantics as follows
   - set event threads' priority to maxprio
   - set vcpu threads' priority to maxprio - 1
 - isolate all the posix stuff and put them into os_prioritize() in
   os-posix.c/qemu-os-win32.h to avoid breaking win32 build
 - introduce qemu_init_realtime(), qemu_realtime_is_enable and
   qemu_realtime_get_parameters() and struct QemuRealtimeInfo to
   keep realtime option and remove related global variables in vl.c
 - add other benchmark(qpid-latency-test) result

We have some plans to migrate old enterprise/control systems which
require low latency (msec order) to kvm virtualized environment. In
order to satify the requirements, this patch adds realtime option to qemu:

-realtime maxprio=<prio>,policy=<pol>

This option change the scheduling policy and priority to realtime
one (event threads: maxprio, vcpu threads: maxprio - 1) and mlock all
qemu and guest memory.

Of course, we need more improvements to keep latency low in qemu
virtualized environment and this is a first step. OTOH, we can
meet the requirement of our first migration project with this patch.

[ Note ]
This version doesn't support vhost, vpnc and linux-aio.

These are some basic performance results:

Host : 4 core, 4GB
Guest: 1 core, 512MB

Benchmark: qpid-latency-test
http://qpid.apache.org/
https://access.redhat.com/knowledge/docs/en-US/Red_Hat_Enterprise_MRG/2/html/Messaging_Installation_and_Configuration_Guide/qpid_latency_test.html

 Command:
 - qemu
   $ qemu -smp 1 -m 512 -enable-kvm -netdev tap,id=hostnet1 -device
     virtio-net-pci,netdev=hostnet1 -drive file=vm.img,if=virtio
     (-realtime maxprio=99,policy=fifo)

 - benchmark
   $ chrt -f 99 qpid-latency-test --tcp-nodelay --rate 10000 -b <server>

 Results: worst latency (msec) from 100 run
  - no load
    1. normal qemu  : 17.468400
    2. chrt qemu(*) : 10.019900
    3. realtime qemu:  8.048370

  - load (iperf, server:vm, client:other physical sercer)
    4. normal qemu  : 26.711100
    5. chrt qemu    :  8.485140
    6. realtime qemu: 10.176700

 (*) $ chrt -f -p 99 <event_thread_tid>
     $ chrt -f -p 98 <vcpu_thread_tid>

Any comments are welcome.

Regards,
Satoru

Signed-off-by: Satoru Moriya <address@hidden>
---
 cpus.c                    | 17 +++++++++++++++++
 include/qemu/thread.h     |  4 ++++
 include/sysemu/os-posix.h |  1 +
 include/sysemu/os-win32.h |  1 +
 os-posix.c                | 48 +++++++++++++++++++++++++++++++++++++++++++++++
 qemu-config.c             | 16 ++++++++++++++++
 qemu-options.hx           |  9 +++++++++
 qemu-thread-posix.c       | 27 ++++++++++++++++++++++++++
 qemu-thread-win32.c       | 13 +++++++++++++
 vl.c                      | 33 ++++++++++++++++++++++++++++++++
 10 files changed, 169 insertions(+)

diff --git a/cpus.c b/cpus.c
index 4a7782a..a049970 100644
--- a/cpus.c
+++ b/cpus.c
@@ -734,6 +734,9 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
     CPUArchState *env = arg;
     CPUState *cpu = ENV_GET_CPU(env);
     int r;
+    int rt_policy, rt_priority;
+    struct sched_param sp;
+
 
     qemu_mutex_lock(&qemu_global_mutex);
     qemu_thread_get_self(cpu->thread);
@@ -746,6 +749,20 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
         exit(1);
     }
 
+    if (qemu_realtime_is_enabled()) {
+        qemu_realtime_get_parameters(&rt_policy, &rt_priority);
+        /*
+         * vcpu threads' priority must be set to event thread priority -1
+         * to avoid starvation.
+         */
+        sp.sched_priority = rt_priority - 1;
+        r = sched_setscheduler(0, rt_policy, &sp);
+        if (r < 0) {
+            perror("Setting realtime policy failed");
+            exit(1);
+        }
+    }
+
     qemu_kvm_init_cpu_signals(env);
 
     /* signal CPU creation */
diff --git a/include/qemu/thread.h b/include/qemu/thread.h
index c02404b..3d8b3d2 100644
--- a/include/qemu/thread.h
+++ b/include/qemu/thread.h
@@ -53,4 +53,8 @@ void qemu_thread_get_self(QemuThread *thread);
 bool qemu_thread_is_self(QemuThread *thread);
 void qemu_thread_exit(void *retval);
 
+void qemu_init_realtime(int, int);
+bool qemu_realtime_is_enabled(void);
+void qemu_realtime_get_parameters(int *, int *);
+
 #endif
diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h
index 7f198e4..e5995b0 100644
--- a/include/sysemu/os-posix.h
+++ b/include/sysemu/os-posix.h
@@ -31,6 +31,7 @@ void os_set_proc_name(const char *s);
 void os_setup_signal_handling(void);
 void os_daemonize(void);
 void os_setup_post(void);
+void os_prioritize(const char *, int);
 
 typedef struct timeval qemu_timeval;
 #define qemu_gettimeofday(tp) gettimeofday(tp, NULL)
diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h
index d0e9234..946b566 100644
--- a/include/sysemu/os-win32.h
+++ b/include/sysemu/os-win32.h
@@ -78,6 +78,7 @@ static inline void os_daemonize(void) {}
 static inline void os_setup_post(void) {}
 void os_set_line_buffering(void);
 static inline void os_set_proc_name(const char *dummy) {}
+static inline void os_prioritize(const char *pol, int prio) {}
 
 #if !defined(EPROTONOSUPPORT)
 # define EPROTONOSUPPORT EINVAL
diff --git a/os-posix.c b/os-posix.c
index 5c64518..8fe0fa2 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -33,12 +33,14 @@
 #include <pwd.h>
 #include <grp.h>
 #include <libgen.h>
+#include <sched.h>
 
 /* Needed early for CONFIG_BSD etc. */
 #include "config-host.h"
 #include "sysemu/sysemu.h"
 #include "net/slirp.h"
 #include "qemu-options.h"
+#include "qemu-thread.h"
 
 #ifdef CONFIG_LINUX
 #include <sys/prctl.h>
@@ -363,3 +365,49 @@ bool is_daemonized(void)
 {
     return daemonize;
 }
+
+void os_prioritize(const char *rt_sched_policy, int max_sched_priority)
+{
+    int rt_pol, sys_min_prio, sys_max_prio;
+
+    if (rt_sched_policy) {
+        if (!strcmp(rt_sched_policy, "rr")) {
+            rt_pol = SCHED_RR;
+        } else if (!strcmp(rt_sched_policy, "fifo")) {
+            rt_pol = SCHED_FIFO;
+        } else {
+            fprintf(stderr, "qemu: invalid option value '%s'\n",
+                    rt_sched_policy);
+            exit(1);
+        }
+    } else {
+        rt_pol = SCHED_RR;
+    }
+
+    sys_min_prio = sched_get_priority_min(rt_pol);
+    sys_max_prio = sched_get_priority_max(rt_pol);
+
+    if (max_sched_priority < sys_min_prio + 1) {
+        /*
+         * We set event threads' priority to max_sched_priorty and
+         * vcpu threads' to max_sched_priority - 1 in order to avoid
+         * starvation. So, it must be > sys_min_prio + 1.
+         */
+        fprintf(stderr, "qemu: invalid option maxprio=%d. It must be >= %d\n",
+                max_sched_priority, sys_min_prio + 1);
+        exit(1);
+    }
+
+    if (sys_max_prio < max_sched_priority) {
+        fprintf(stderr, "qemu: invalid option maxprio=%d. It must be <= %d\n",
+                max_sched_priority, sys_max_prio);
+        exit(1);
+    }
+
+    qemu_init_realtime(rt_pol, max_sched_priority);
+
+    if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+        perror("mlockall");
+        exit(1);
+    }
+}
diff --git a/qemu-config.c b/qemu-config.c
index 2188c3e..b945d07 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -647,6 +647,21 @@ static QemuOptsList qemu_object_opts = {
     },
 };
 
+static QemuOptsList qemu_realtime_opts = {
+    .name = "realtime",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_realtime_opts.head),
+    .desc = {
+        {
+            .name = "maxprio",
+            .type = QEMU_OPT_NUMBER,
+        }, {
+            .name = "policy",
+            .type = QEMU_OPT_STRING,
+        },
+        { /* end of list */ }
+    },
+};
+
 static QemuOptsList *vm_config_groups[32] = {
     &qemu_drive_opts,
     &qemu_chardev_opts,
@@ -664,6 +679,7 @@ static QemuOptsList *vm_config_groups[32] = {
     &qemu_sandbox_opts,
     &qemu_add_fd_opts,
     &qemu_object_opts,
+    &qemu_realtime_opts,
     NULL,
 };
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 9df0cde..968a20a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2447,6 +2447,15 @@ STEXI
 Do not start CPU at startup (you must type 'c' in the monitor).
 ETEXI
 
+DEF("realtime", HAS_ARG, QEMU_OPTION_realtime,
+    "-realtime maxprio=prio[,policy=pol]\n",
+    QEMU_ARCH_ALL)
+STEXI
address@hidden -realtime address@hidden,address@hidden
address@hidden -realtime
+run qemu as realtime process with priority @var{prio} and policy @var{pol}.
+ETEXI
+
 DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \
     "-gdb dev        wait for gdb connection on 'dev'\n", QEMU_ARCH_ALL)
 STEXI
diff --git a/qemu-thread-posix.c b/qemu-thread-posix.c
index 7be292e..10a97cc 100644
--- a/qemu-thread-posix.c
+++ b/qemu-thread-posix.c
@@ -22,6 +22,15 @@
 #include <sys/time.h>
 #include "qemu/thread.h"
 
+struct QemuRealtimeInfo {
+    bool is_realtime;
+    int policy;
+    int max_priority;
+};
+typedef struct QemuRealtimeInfo QemuRealtimeInfo;
+
+static QemuRealtimeInfo rt_info;
+
 static void error_exit(int err, const char *msg)
 {
     fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
@@ -324,3 +333,21 @@ void *qemu_thread_join(QemuThread *thread)
     }
     return ret;
 }
+
+void qemu_init_realtime(int rt_sched_policy, int max_sched_priority)
+{
+    rt_info.is_realtime = true;
+    rt_info.policy = rt_sched_policy;
+    rt_info.max_priority = max_sched_priority;
+}
+
+bool qemu_realtime_is_enabled(void)
+{
+    return rt_info.is_realtime;
+}
+
+void qemu_realtime_get_parameters(int *policy, int *max_priority)
+{
+    *policy = rt_info.policy;
+    *max_priority = rt_info.max_priority;
+}
diff --git a/qemu-thread-win32.c b/qemu-thread-win32.c
index 8037b39..3beebcf 100644
--- a/qemu-thread-win32.c
+++ b/qemu-thread-win32.c
@@ -369,3 +369,16 @@ bool qemu_thread_is_self(QemuThread *thread)
 {
     return GetCurrentThreadId() == thread->tid;
 }
+
+void qemu_init_realtime(int rt_sched_policy, int max_sched_priority)
+{
+}
+
+bool qemu_realtime_is_enabled(void)
+{
+    return false;
+}
+
+void qemu_realtime_get_parameters(int *policy, int *max_priority)
+{
+}
diff --git a/vl.c b/vl.c
index e6a8d89..c310587 100644
--- a/vl.c
+++ b/vl.c
@@ -29,6 +29,7 @@
 #include <sys/time.h>
 #include <zlib.h>
 #include "qemu/bitmap.h"
+#include "qemu-thread.h"
 
 /* Needed early for CONFIG_BSD etc. */
 #include "config-host.h"
@@ -1148,6 +1149,17 @@ static void smp_parse(const char *optarg)
         max_cpus = smp_cpus;
 }
 
+static void configure_realtime(QemuOpts *opts)
+{
+    const char *pol;
+    int prio;
+
+    pol = qemu_opt_get(opts, "policy");
+    prio = qemu_opt_get_number(opts, "maxprio", 1);
+
+    os_prioritize(pol, prio);
+}
+
 /***********************************************************/
 /* USB devices */
 
@@ -1754,9 +1766,22 @@ static void main_loop(void)
 {
     bool nonblocking;
     int last_io = 0;
+    int rt_policy, rt_priority;
+    struct sched_param sp;
 #ifdef CONFIG_PROFILER
     int64_t ti;
 #endif
+
+    if (qemu_realtime_is_enabled()) {
+        qemu_realtime_get_parameters(&rt_policy, &rt_priority);
+
+        sp.sched_priority = rt_priority;;
+        if (sched_setscheduler(0, rt_policy, &sp) < 0) {
+            perror("Setting realtime policy failed");
+            exit(1);
+        }
+    }
+
     do {
         nonblocking = !kvm_enabled() && last_io > 0;
 #ifdef CONFIG_PROFILER
@@ -2758,6 +2783,14 @@ int main(int argc, char **argv, char **envp)
                 }
                 numa_add(optarg);
                 break;
+            case QEMU_OPTION_realtime:
+                opts = qemu_opts_parse(qemu_find_opts("realtime"), optarg, 0);
+                if (!opts) {
+                    fprintf(stderr, "parse error: %s\n", optarg);
+                    exit(1);
+                }
+                configure_realtime(opts);
+                break;
             case QEMU_OPTION_display:
                 display_type = select_display(optarg);
                 break;
-- 
1.7.11.7



reply via email to

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