Skip to contentSkip to conversation

qmp: extend QMP to provide read/write access to physical memory

PATCH Superseded Bryan D. Payne 4 participants 32 messages Nov 26, 2014 – Dec 12, 2014 Kbuild

lore
Share & tools
lore
#0 lore raw

cover qmp: extend QMP to provide read/write access to physical memory

Summary:
This patch improves Qemu support for virtual machine introspection.

Background:
Virtual machine introspection (VMI) is a technique where one accesses the memory of a (usually) paused guest. This access is typically used to perform security checks, debugging, or malware analysis. The LibVMI project provides and open source library that simplifies VMI programming. LibVMI supports both Xen and KVM environments.

Under KVM, LibVMI can work on systems today (albeit slowly) using the human monitor command functionality to extract memory with the xp command. This access is too slow for performance sensitive applications, so the LibVMI project has created and maintained a QEMU patch that enables faster access. We have used this patch for about 3 years now and it appears to be working nicely for our community.

The patch in this email is an updated version of the LibVMI patch that aims to conform to the Qemu coding guidelines. It is my hope that we can include this in Qemu so that LibVMI users can leverage this faster access without needing to do custom Qemu builds on their KVM systems.

Bryan D. Payne (1):
qmp: extend QMP to provide read/write access to physical memory

 Makefile.target |   2 +-
 memory-access.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory-access.h |  11 ++++
 monitor.c       |  10 +++
 qmp-commands.hx |  27 ++++++++
 5 files changed, 249 insertions(+), 1 deletion(-)
 create mode 100644 memory-access.c
 create mode 100644 memory-access.h
Signature · 1 line
1.9.1
#1 lore raw

PATCH 1/1 qmp: extend QMP to provide read/write access to physical memory

This patch adds a new QMP command that sets up a domain socket. This socket can then be used for fast read/write access to the guest's physical memory. The key benefit to this system over existing solutions is speed. Using this patch, guest memory can be copied out at a rate of ~200MB/sec, depending on the hardware. Existing solutions only achieve a small fraction of this speed.

---
 Makefile.target |   2 +-
 memory-access.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory-access.h |  11 ++++
 monitor.c       |  10 +++
 qmp-commands.hx |  27 ++++++++
 5 files changed, 249 insertions(+), 1 deletion(-)
 create mode 100644 memory-access.c
 create mode 100644 memory-access.h
Makefile.target +1 -1
index e9ff1ee..4b3cd99 100644
@@ -127,7 +127,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 += qtest.o bootdevice.o
+obj-y += qtest.o bootdevice.o memory-access.o
 obj-y += hw/
 obj-$(CONFIG_FDT) += device_tree.o
 obj-$(CONFIG_KVM) += kvm-all.o
memory-access.c new file +200 -0

200 lines not on this page · raw message

memory-access.h new file +11 -0
new file mode 100644
index 0000000..cb5fe33
@@ -0,0 +1,11 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (c) 2014 Bryan D. Payne ([email protected])
+ */
+#ifndef MEMORY_ACCESS_H
+#define MEMORY_ACCESS_H
+
+int memory_access_start(const char *path);
+
+#endif /* MEMORY_ACCESS_H */
monitor.c +10 -0
index fa00594..35e276a 100644
@@ -72,6 +72,7 @@
 #include "block/qapi.h"
 #include "qapi/qmp-event.h"
 #include "qapi-event.h"
+#include "memory-access.h"
 
 /* for pic/irq_info */
 #if defined(TARGET_SPARC)
@@ -1371,6 +1372,15 @@ static void do_print(Monitor *mon, const QDict *qdict)
     monitor_printf(mon, "\n");
 }
 
+static int do_physical_memory_access(Monitor *mon,
+                                     const QDict *qdict,
+                                     QObject **ret_data)
+{
+    const char *path = qdict_get_str(qdict, "path");
+    memory_access_start(path);
+    return 0;
+}
+
 static void do_sum(Monitor *mon, const QDict *qdict)
 {
     uint32_t addr;
qmp-commands.hx +27 -0
index 718dd92..ebe0e2a 100644
@@ -609,6 +609,33 @@ Example:
 EQMP
 
     {
+        .name       = "pmemaccess",
+        .args_type  = "path:s",
+        .params     = "path",
+        .help       = "mount guest physical memory image at 'path'",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = do_physical_memory_access,
+    },
+
+SQMP
+pmemaccess
+----------
+
+Mount guest physical memory image at 'path'.
+
+Arguments:
+
+- "path": mount point path (json-string)
+
+Example:
+
+-> { "execute": "pmemaccess",
+             "arguments": { "path": "/tmp/guestname" } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "migrate",
         .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
         .mhandler.cmd_new = qmp_marshal_input_migrate,
Signature · 1 line
1.9.1
#2 lore raw
Quoted 7 lines · Bryan D. Payne

This patch adds a new QMP command that sets up a domain socket. This socket can then be used for fast read/write access to the guest's physical memory. The key benefit to this system over existing solutions is speed. Using this patch, guest memory can be copied out at a rate of ~200MB/sec, depending on the hardware. Existing solutions only achieve a small fraction of this speed.

Quoted 7 lines
---
 Makefile.target |   2 +-
 memory-access.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory-access.h |  11 ++++
 monitor.c       |  10 +++
 qmp-commands.hx |  27 ++++++++

Where is the *.json file that adds the QMP command contract?

Quoted 17 lines

+++ b/qmp-commands.hx

qmp-commands.hx +12 -0
@@ -609,6 +609,33 @@ Example:
 EQMP
 
     {
+        .name       = "pmemaccess",
+        .args_type  = "path:s",
+        .params     = "path",
+        .help       = "mount guest physical memory image at 'path'",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = do_physical_memory_access,
+    },
+
+SQMP
+pmemaccess
+----------
+

qmp-commands.hx Example: · line 633

Quoted 10 lines
qmp-commands.hx +10 -0
+Mount guest physical memory image at 'path'.
+
+Arguments:
+
+- "path": mount point path (json-string)
+
+Example:
+
+-> { "execute": "pmemaccess",
+             "arguments": { "path": "/tmp/guestname" } }

Sounds like you are missing this entry (probably best to put it in the top-level qapi-schema.json):

{ 'command': 'pmemaccess', 'data': { 'path': 'str' } }

as well as documentation that mentions it is targetted for addition in 2.3.

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#3 lore raw

PATCH v2 1/1 qmp: extend QMP to provide read/write access to physical memory

In reply to Bryan D. Payne

Thanks for the feedback Eric, I've updated the patch.

v2 changes:
- added QMP command contract to qapi-schema.json
- corrected some comments
- rewired QMP command to use schema code

#4 lore raw

PATCH 1/1 qmp: extend QMP to provide read/write access to physical memory

This patch adds a new QMP command that sets up a domain socket. This socket can then be used for fast read/write access to the guest's physical memory. The key benefit to this system over existing solutions is speed. Using this patch, guest memory can be copied out at a rate of ~200MB/sec, depending on the hardware. Existing solutions only achieve a small fraction of this speed.

---
 Makefile.target  |   2 +-
 memory-access.c  | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory-access.h  |  11 +++
 monitor.c        |   6 ++
 qapi-schema.json |  12 ++++
 qmp-commands.hx  |  28 ++++++++
 6 files changed, 258 insertions(+), 1 deletion(-)
 create mode 100644 memory-access.c
 create mode 100644 memory-access.h
Makefile.target +1 -1
index e9ff1ee..4b3cd99 100644
@@ -127,7 +127,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 += qtest.o bootdevice.o
+obj-y += qtest.o bootdevice.o memory-access.o
 obj-y += hw/
 obj-$(CONFIG_FDT) += device_tree.o
 obj-$(CONFIG_KVM) += kvm-all.o
memory-access.c new file +200 -0

200 lines not on this page · raw message

memory-access.h new file +11 -0
new file mode 100644
index 0000000..cb5fe33
@@ -0,0 +1,11 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (c) 2014 Bryan D. Payne ([email protected])
+ */
+#ifndef MEMORY_ACCESS_H
+#define MEMORY_ACCESS_H
+
+int memory_access_start(const char *path);
+
+#endif /* MEMORY_ACCESS_H */
monitor.c +6 -0
index fa00594..19724b8 100644
@@ -72,6 +72,7 @@
 #include "block/qapi.h"
 #include "qapi/qmp-event.h"
 #include "qapi-event.h"
+#include "memory-access.h"
 
 /* for pic/irq_info */
 #if defined(TARGET_SPARC)
@@ -1371,6 +1372,11 @@ static void do_print(Monitor *mon, const QDict *qdict)
     monitor_printf(mon, "\n");
 }
 
+void qmp_pmemaccess(const char *path, Error **err)
+{
+    memory_access_start(path);
+}
+
 static void do_sum(Monitor *mon, const QDict *qdict)
 {
     uint32_t addr;
qapi-schema.json +12 -0
index 9ffdcf8..37a6657 100644
@@ -3515,3 +3515,15 @@
 # Since: 2.1
 ##
 { 'command': 'rtc-reset-reinjection' }
+
+##
+# @pmemaccess
+#
+# This command enables access to guest physical memory using
+# a simple protocol over a UNIX domain socket.
+#
+# @path Location to use for the UNIX domain socket
+#
+# Since: 2.3
+##
+{ 'command': 'pmemaccess', 'data': { 'path': 'str' } }
qmp-commands.hx +28 -0
index 718dd92..fab1322 100644
@@ -609,6 +609,34 @@ Example:
 EQMP
 
     {
+        .name       = "pmemaccess",
+        .args_type  = "path:s",
+        .params     = "path",
+        .help       = "access to guest memory via domain socket at 'path'",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = qmp_marshal_input_pmemaccess,
+    },
+
+SQMP
+pmemaccess
+----------
+
+This command enables access to guest physical memory using a simple protocol
+over a UNIX domain socket.
+
+Arguments:
+
+- "path": path for domain socket (json-string)
+
+Example:
+
+-> { "execute": "pmemaccess",
+             "arguments": { "path": "/tmp/guestname" } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "migrate",
         .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
         .mhandler.cmd_new = qmp_marshal_input_migrate,
Signature · 1 line
1.9.1
#5 lore raw
Quoted 6 lines · Bryan D. Payne

This patch adds a new QMP command that sets up a domain socket. This socket can then be used for fast read/write access to the guest's physical memory. The key benefit to this system over existing solutions is speed. Using this patch, guest memory can be copied out at a rate of ~200MB/sec, depending on the hardware. Existing solutions only achieve a small fraction of this speed.

Out of curiosity, what are existing solutions?

Quoted 50 lines
---
 Makefile.target  |   2 +-
 memory-access.c  | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory-access.h  |  11 +++
 monitor.c        |   6 ++
 qapi-schema.json |  12 ++++
 qmp-commands.hx  |  28 ++++++++
 6 files changed, 258 insertions(+), 1 deletion(-)
 create mode 100644 memory-access.c
 create mode 100644 memory-access.h
Makefile.target +1 -1
index e9ff1ee..4b3cd99 100644
@@ -127,7 +127,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 += qtest.o bootdevice.o
+obj-y += qtest.o bootdevice.o memory-access.o
 obj-y += hw/
 obj-$(CONFIG_FDT) += device_tree.o
 obj-$(CONFIG_KVM) += kvm-all.o
memory-access.c new file +18 -0
new file mode 100644
index 0000000..f696d7b
@@ -0,0 +1,200 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (c) 2014 Bryan D. Payne ([email protected])
+ */
+
+#include "memory-access.h"
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "config.h"
+
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>

memory-access.c line 28

Quoted 10 lines
memory-access.c new file +10 -0
+#include <sys/un.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdint.h>
+
+struct request {
+    uint8_t type;      /* 0 quit, 1 read, 2 write, ... rest reserved */
+    uint64_t address;  /* address to read from OR write to */
+    uint64_t length;   /* number of bytes to read OR write */
+};

Please add QEMU_PACKED to this structure, and probably name it QEMUMARequest, for name collision avoidance and CamelCase convension.

memory-access.c line 35

Quoted 7 lines
memory-access.c new file +7 -0
+
+static uint64_t
+connection_read_memory(uint64_t user_paddr, void *buf, uint64_t user_len)
+{
+    hwaddr paddr = (hwaddr) user_paddr;
+    hwaddr len = (hwaddr) user_len;
+    void *guestmem = cpu_physical_memory_map(paddr, &len, 0);

Accessing guest memory from another thread is not safe without holding the
lock: qemu_mutex_lock_iothread is needed before processing a request, and unlock it after responding, to let guest continue.

Quoted 33 lines
memory-access.c new file +33 -0
+    if (!guestmem) {
+        return 0;
+    }
+    memcpy(buf, guestmem, len);
+    cpu_physical_memory_unmap(guestmem, len, 0, len);
+
+    return len;
+}
+
+static uint64_t
+connection_write_memory(uint64_t user_paddr,
+                        const void *buf,
+                        uint64_t user_len)
+{
+    hwaddr paddr = (hwaddr) user_paddr;
+    hwaddr len = (hwaddr) user_len;
+    void *guestmem = cpu_physical_memory_map(paddr, &len, 1);
+    if (!guestmem) {
+        return 0;
+    }
+    memcpy(guestmem, buf, len);
+    cpu_physical_memory_unmap(guestmem, len, 0, len);
+
+    return len;
+}
+
+static void
+send_success_ack(int connection_fd)
+{
+    uint8_t success = 1;
+    int nbytes = write(connection_fd, &success, 1);
+    if (nbytes != 1) {
+        printf("QemuMemoryAccess: failed to send success ack\n");

memory-access.c line 78

Quoted 10 lines
memory-access.c new file +10 -0
+    }
+}
+
+static void
+send_fail_ack(int connection_fd)
+{
+    uint8_t fail = 0;
+    int nbytes = write(connection_fd, &fail, 1);
+    if (nbytes != 1) {
+        printf("QemuMemoryAccess: failed to send fail ack\n");

fprintf(stderr ?

Quoted 7 lines
memory-access.c new file +7 -0
+    }
+}
+
+static void
+connection_handler(int connection_fd)
+{
+    int nbytes;

memory-access.c line 95

Quoted 10 lines
memory-access.c new file +10 -0
+    struct request req;
+
+    while (1) {
+        /* client request should match the struct request format */
+        nbytes = read(connection_fd, &req, sizeof(struct request));
+        if (nbytes != sizeof(struct request)) {
+            /* error */
+            send_fail_ack(connection_fd);
+            continue;
+        } else if (req.type == 0) {

I suggest using macro names for enum instead of magic values 0, 1, 2.

memory-access.c line 100

Quoted 5 lines
memory-access.c new file +5 -0
+            /* request to quit, goodbye */
+            break;
+        } else if (req.type == 1) {
+            /* request to read */
+            char *buf = g_malloc(req.length + 1);

Is there a limit for r/w length? We could abort if req.length is too big.

Quoted 6 lines
memory-access.c new file +6 -0
+            nbytes = connection_read_memory(req.address, buf, req.length);
+            if (nbytes != req.length) {
+                /* read failure, return failure message */
+                buf[req.length] = 0; /* set last byte to 0 for failure */
+                nbytes = write(connection_fd, buf, 1);
+            } else {

memory-access.c line 116

Quoted 10 lines
memory-access.c new file +10 -0
+                /* read success, return bytes */
+                buf[req.length] = 1; /* set last byte to 1 for success */
+                nbytes = write(connection_fd, buf, nbytes + 1);
+            }
+            g_free(buf);
+        } else if (req.type == 2) {
+            /* request to write */
+            void *write_buf = g_malloc(req.length);
+            nbytes = read(connection_fd, &write_buf, req.length);
+            if (nbytes != req.length) {

Is short read possible here?

memory-access.c line 119

Quoted 3 lines
memory-access.c new file +3 -0
+                /* failed reading the message to write */
+                send_fail_ack(connection_fd);
+            } else{

s/else{/else {

Quoted 34 lines
memory-access.c new file +34 -0
+                /* do the write */
+                nbytes = connection_write_memory(req.address,
+                                                 write_buf,
+                                                 req.length);
+                if (nbytes == req.length) {
+                    send_success_ack(connection_fd);
+                } else {
+                    send_fail_ack(connection_fd);
+                }
+            }
+            g_free(write_buf);
+        } else {
+            /* unknown command */
+            printf("QemuMemoryAccess: ignoring unknown command (%d)\n",
+                   req.type);
+            send_fail_ack(connection_fd);
+        }
+    }
+
+    close(connection_fd);
+}
+
+static void *
+memory_access_thread(void *path)
+{
+    struct sockaddr_un address;
+    int socket_fd, connection_fd, path_len;
+    socklen_t address_length;
+
+    socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+    if (socket_fd < 0) {
+        printf("QemuMemoryAccess: socket failed\n");
+        goto error_exit;
+    }

memory-access.c line 163

Quoted 10 lines
memory-access.c new file +10 -0
+
+    unlink(path); /* handle unlikely case that this temp file exists */
+    memset(&address, 0, sizeof(struct sockaddr_un));
+    address.sun_family = AF_UNIX;
+    path_len = sprintf(address.sun_path, "%s", (char *) path);
+    address_length = sizeof(address.sun_family) + path_len;
+
+    if (bind(socket_fd, (struct sockaddr *) &address, address_length) != 0) {
+        printf("QemuMemoryAccess: bind failed\n");
+        goto error_exit;

socket_fd is left open on error.

Quoted 18 lines
memory-access.c new file +18 -0
+    }
+    if (listen(socket_fd, 0) != 0) {
+        printf("QemuMemoryAccess: listen failed\n");
+        goto error_exit;
+    }
+
+    connection_fd = accept(socket_fd,
+                           (struct sockaddr *) &address,
+                           &address_length);
+    connection_handler(connection_fd);
+
+    close(socket_fd);
+    unlink(path);
+error_exit:
+    g_free(path);
+    return NULL;
+}
+

memory-access.c line 191

Quoted 10 lines
memory-access.c new file +10 -0
+int
+memory_access_start(const char *path)
+{
+    pthread_t thread;
+    sigset_t set, oldset;
+    int ret;
+
+    /* create a copy of path that we can safely use */
+    char *pathcopy = g_malloc(strlen(path) + 1);
+    memcpy(pathcopy, path, strlen(path) + 1);

char *pathcopy = g_strdup(path);

Quoted 82 lines
memory-access.c new file +9 -0
+
+    /* start the thread */
+    sigfillset(&set);
+    pthread_sigmask(SIG_SETMASK, &set, &oldset);
+    ret = pthread_create(&thread, NULL, memory_access_thread, pathcopy);
+    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+
+    return ret;
+}
memory-access.h new file +11 -0
new file mode 100644
index 0000000..cb5fe33
@@ -0,0 +1,11 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (c) 2014 Bryan D. Payne ([email protected])
+ */
+#ifndef MEMORY_ACCESS_H
+#define MEMORY_ACCESS_H
+
+int memory_access_start(const char *path);
+
+#endif /* MEMORY_ACCESS_H */
monitor.c +6 -0
index fa00594..19724b8 100644
@@ -72,6 +72,7 @@
 #include "block/qapi.h"
 #include "qapi/qmp-event.h"
 #include "qapi-event.h"
+#include "memory-access.h"
 
 /* for pic/irq_info */
 #if defined(TARGET_SPARC)
@@ -1371,6 +1372,11 @@ static void do_print(Monitor *mon, const QDict *qdict)
     monitor_printf(mon, "\n");
 }
 
+void qmp_pmemaccess(const char *path, Error **err)
+{
+    memory_access_start(path);
+}
+
 static void do_sum(Monitor *mon, const QDict *qdict)
 {
     uint32_t addr;
qapi-schema.json +12 -0
index 9ffdcf8..37a6657 100644
@@ -3515,3 +3515,15 @@
 # Since: 2.1
 ##
 { 'command': 'rtc-reset-reinjection' }
+
+##
+# @pmemaccess
+#
+# This command enables access to guest physical memory using
+# a simple protocol over a UNIX domain socket.
+#
+# @path Location to use for the UNIX domain socket
+#
+# Since: 2.3
+##
+{ 'command': 'pmemaccess', 'data': { 'path': 'str' } }
qmp-commands.hx +4 -0
index 718dd92..fab1322 100644
@@ -609,6 +609,34 @@ Example:
 EQMP
 
     {
+        .name       = "pmemaccess",
+        .args_type  = "path:s",
+        .params     = "path",
+        .help       = "access to guest memory via domain socket at 'path'",

qmp-commands.hx Example: · line 625

Quoted 10 lines
qmp-commands.hx +10 -0
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = qmp_marshal_input_pmemaccess,
+    },
+
+SQMP
+pmemaccess
+----------
+
+This command enables access to guest physical memory using a simple protocol
+over a UNIX domain socket.

Please document the protocol, as it's the most important part of this command.

Fam

Quoted 21 lines
qmp-commands.hx +14 -0
+
+Arguments:
+
+- "path": path for domain socket (json-string)
+
+Example:
+
+-> { "execute": "pmemaccess",
+             "arguments": { "path": "/tmp/guestname" } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "migrate",
         .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
         .mhandler.cmd_new = qmp_marshal_input_migrate,
Signature · 1 line
1.9.1
#6 lore raw
Quoted 3 lines

Out of curiosity, what are existing solutions?

Basically just attaching gdb and pulling memory out manually (or writing a program to do the same).

Quoted 10 lines

+struct request {

memory-access.c line 28

Quoted 4 lines
memory-access.c new file +4 -0
+    uint8_t type;      /* 0 quit, 1 read, 2 write, ... rest reserved */
+    uint64_t address;  /* address to read from OR write to */
+    uint64_t length;   /* number of bytes to read OR write */
+};

Please add QEMU_PACKED to this structure, and probably name it QEMUMARequest,
for name collision avoidance and CamelCase convension.

How critical is QEMU_PACKED? This changes the layout of the struct which means that existing clients (LibVMI) would need to change their code as well.

-bryan

#7 lore raw
Quoted 14 lines · Bryan D. Payne
Quoted 3 lines

Out of curiosity, what are existing solutions?

Basically just attaching gdb and pulling memory out manually (or writing a program to do the same).

Quoted 6 lines

+struct request {

Quoted 4 lines
memory-access.c new file +4 -0
+    uint8_t type;      /* 0 quit, 1 read, 2 write, ... rest reserved */
+    uint64_t address;  /* address to read from OR write to */
+    uint64_t length;   /* number of bytes to read OR write */
+};
Quoted 8 lines
Quoted 4 lines

Please add QEMU_PACKED to this structure, and probably name it QEMUMARequest,
for name collision avoidance and CamelCase convension.

How critical is QEMU_PACKED? This changes the layout of the struct which means that existing clients (LibVMI) would need to change their code as well.

It is critical as a transport data structure. You have to define a byte-by-byte layout (concerning endianness and padding) and use padding fields together with QEMU_PACKED, so the representation is not dependent on alignment:

http://en.wikipedia.org/wiki/Data_structure_alignment

For example:

typedef struct {
    union {
        uint8_t type;
        uint64_t padding;
    };
    uint64_t address;
    uint64_t length;
} QEMU_PACKED QEMUMARequest;

It also means that client need to use the same endianness with us, but there is no way to query it.

An alternative might be carry endianness information in the protocol negotiation or in request.

Fam

#8 lore raw
Quoted 6 lines

It is critical as a transport data structure. You have to define a byte-by-byte
layout (concerning endianness and padding) and use padding fields together with
QEMU_PACKED, so the representation is not dependent on alignment

This makes sense for network protocols. But, in this case, the protocol is always taking placing over a unix socket on the localhost. This is why I wasn't sure that it was necessary.

Just to be clear... the QMP connection could go over the network, but its only purpose is to set up the unix socket on the local machine (meaning that doing it over the network is kind of pointless, even if it is possible). Once the unix socket is setup, the memory access protocol happens over that socket.

Thoughts?

-bryan

#9 lore raw
Quoted 12 lines · Bryan D. Payne
Quoted 6 lines

It is critical as a transport data structure. You have to define a byte-by-byte
layout (concerning endianness and padding) and use padding fields together with
QEMU_PACKED, so the representation is not dependent on alignment

This makes sense for network protocols. But, in this case, the protocol is always taking placing over a unix socket on the localhost. This is why I wasn't sure that it was necessary.

Quoted 5 lines

Just to be clear... the QMP connection could go over the network, but its only purpose is to set up the unix socket on the local machine (meaning that doing it over the network is kind of pointless, even if it is possible). Once the unix socket is setup, the memory access protocol happens over that socket.

This doesn't stop the client from using a different alignment than we expect. It's necessary to be explicit as a binary protocol.

Fam

#10 lore raw
Quoted 5 lines

This doesn't stop the client from using a different alignment than we expect.
It's necessary to be explicit as a binary protocol.

Ok, I'll move ahead with packing the data and sort out the backwards compat issues on the client side.
-bryan

#11 lore raw
Quoted 8 lines · Bryan D. Payne
Quoted 5 lines

This doesn't stop the client from using a different alignment than we expect.
It's necessary to be explicit as a binary protocol.

Ok, I'll move ahead with packing the data and sort out the backwards compat issues on the client side.

Or use a backwards compatible packed structure. :)

Fam

#12 lore raw
In reply to Bryan D. Payne
Quoted 6 lines · Bryan D. Payne
Quoted 3 lines

Out of curiosity, what are existing solutions?

Basically just attaching gdb and pulling memory out manually (or writing a program to do the same).

Can you explain again why the existing commands to read guest memory (from the top of my head: dump-guest-memory, memsave, pmemsave) are insufficient? How does your solution improve on them? What exactly can it do what these commands can't? What exactly can't it do what these commands can?

I feel we need to understand the answers to these questions to sensibly evolve the API in this area.

[...]

#13 lore raw
Quoted 9 lines

Can you explain again why the existing commands to read guest memory (from the top of my head: dump-guest-memory, memsave, pmemsave) are insufficient? How does your solution improve on them? What exactly can it do what these commands can't? What exactly can't it do what these commands can?

I feel we need to understand the answers to these questions to sensibly evolve the API in this area.

Certainly. The main issue with this series of commands is that they dump the memory to a file on disk. What I'm trying to facilitate here is an application that monitors the guest memory in real time while the guest is running (likely with brief pauses during memory analysis periods). Writing all of this data to disk, and then reading it back again for the analysis application is several orders of magnitude too slow for these types of applications.

This new method that I'm proposing here keeps everything in memory, which makes it much faster.

Tldr; existing solutions are suitable for forensic analysis, whereas I'm looking to solve the runtime analysis problem.

-bryan

#14 lore raw
Quoted 19 lines · Bryan D. Payne
Quoted 9 lines

Can you explain again why the existing commands to read guest memory (from the top of my head: dump-guest-memory, memsave, pmemsave) are insufficient? How does your solution improve on them? What exactly can it do what these commands can't? What exactly can't it do what these commands can?

I feel we need to understand the answers to these questions to sensibly evolve the API in this area.

Certainly. The main issue with this series of commands is that they dump the memory to a file on disk. What I'm trying to facilitate here is an application that monitors the guest memory in real time while the guest is running (likely with brief pauses during memory analysis periods). Writing all of this data to disk, and then reading it back again for the analysis application is several orders of magnitude too slow for these types of applications.

Quoted 2 lines

This new method that I'm proposing here keeps everything in memory, which makes it much faster.

Actually, it sends it through a UNIX domain socket. Still I/O, just different I/O. "Faster" is plausible, but by how much I can't say without measurements. "Several orders of magnitude" needs evidence.

I think you could make another supporting argument: the socket is easier to use than futzing around with files.

Quoted 2 lines

Tldr; existing solutions are suitable for forensic analysis, whereas I'm looking to solve the runtime analysis problem.

Understood.

Based on your qapi-schema.json change only, I figure your new command sets up a special-purpose monitor-like thing on a newly created a UNIX domain socket. This monitor-like thing talks its own language, which is so far undocmented.

Why restrict this to a UNIX domain socket? In other places involving byte streams we use a QEMU character device.

Can you explain why you need a separate stream instead of embedding the conversation in QMP? We already have such embedded I/O: ringbuf-read, ringbuf-write. Yes, it got multiple documented bugs, but that's because it was badly done, not because such a thing couldn't be done nicely.

If you add a separate stream talking its own language, you get to document the language. A file in docs/ should do. The QMP command documentation should point to it.

#15 lore raw
Quoted 5 lines

Actually, it sends it through a UNIX domain socket. Still I/O, just different I/O. "Faster" is plausible, but by how much I can't say without measurements. "Several orders of magnitude" needs evidence.

I've been testing introspection performance on both Xen and KVM for over 5 years now, and I'm pretty comfortable with this statement. Bottom line is that sending lots of data through a disk is much slower than sending lots of data through memory (for example, see Figure 3 in
http://queue.acm.org/detail.cfm?id=1563874, which shows about 5 orders of magnitude difference for random access data@). A UNIX domain socket is just going through memory.

I have other Qemu modifications that can reduce the use of the memory bus by using shared memory instead. This provides another big bump in performance. However, this work is still in earlier stages.

Based on your qapi-schema.json change only, I figure your new command

Quoted 4 lines

sets up a special-purpose monitor-like thing on a newly created a UNIX domain socket. This monitor-like thing talks its own language, which is so far undocmented.

Correct. Per request from one of the reviewers in this thread, I have an updated patch that has full documentation on this protocol. I've been waiting to push that as I try to sort out some of the remaining discussion in this thread, though.

Quoted 8 lines

Why restrict this to a UNIX domain socket? In other places involving byte streams we use a QEMU character device.

Can you explain why you need a separate stream instead of embedding the conversation in QMP? We already have such embedded I/O: ringbuf-read, ringbuf-write. Yes, it got multiple documented bugs, but that's because it was badly done, not because such a thing couldn't be done nicely.

I think that it could be done in QMP. I would want to validate the performance of that approach though as there will be some extra overhead. I would also be concerned about access to this data over a network.

For security reasons, it feels better to me to restrict this feature to someone that is on the local machine. Using a UNIX domain socket forces this constraint. I don't believe that is the case with QMP, but please correct me if I'm wrong about that.

If you add a separate stream talking its own language, you get to

Quoted 3 lines

document the language. A file in docs/ should do. The QMP command documentation should point to it.

My updated patch documents it as a comment directly in the C source, as that is what I felt a previous reviewer was asking for. Is docs more appropriate or ??

Cheers,
-bryan

#16 lore raw
Quoted 6 lines · Bryan D. Payne
Quoted 5 lines

Actually, it sends it through a UNIX domain socket. Still I/O, just different I/O. "Faster" is plausible, but by how much I can't say without measurements. "Several orders of magnitude" needs evidence.

Quoted 7 lines

I've been testing introspection performance on both Xen and KVM for over 5 years now, and I'm pretty comfortable with this statement. Bottom line is that sending lots of data through a disk is much slower than sending lots of data through memory (for example, see Figure 3 in
http://queue.acm.org/detail.cfm?id=1563874, which shows about 5 orders of magnitude difference for random access data@). A UNIX domain socket is just going through memory.

By "evidence", I mean actual numbers for actual QEMU code. Nothing sophisticated, just use your new interface in a way you consider relevant for your own use case, then approximate this use with existing interfaces. The approximation can be very rough. For instance, showing that doing the whole job with your approach is a much faster than doing a necessary part of the job with existing commands would do.

Quoted 11 lines

I have other Qemu modifications that can reduce the use of the memory bus by using shared memory instead. This provides another big bump in performance. However, this work is still in earlier stages.

Based on your qapi-schema.json change only, I figure your new command

Quoted 4 lines

sets up a special-purpose monitor-like thing on a newly created a UNIX domain socket. This monitor-like thing talks its own language, which is so far undocmented.

Quoted 4 lines

Correct. Per request from one of the reviewers in this thread, I have an updated patch that has full documentation on this protocol. I've been waiting to push that as I try to sort out some of the remaining discussion in this thread, though.

Makes sense.

Quoted 11 lines
Quoted 8 lines

Why restrict this to a UNIX domain socket? In other places involving byte streams we use a QEMU character device.

Can you explain why you need a separate stream instead of embedding the conversation in QMP? We already have such embedded I/O: ringbuf-read, ringbuf-write. Yes, it got multiple documented bugs, but that's because it was badly done, not because such a thing couldn't be done nicely.

I think that it could be done in QMP. I would want to validate the performance of that approach though as there will be some extra overhead.

The QMP overhead could be too high. QMP is control plane, not data plane. How much data do you envisage flowing here?

In theory, even the character device overhead could be too high. Measuring it shouldn't be too hard.

Quoted 6 lines

I would also be concerned about access to this data over a network.

For security reasons, it feels better to me to restrict this feature to someone that is on the local machine. Using a UNIX domain socket forces this constraint. I don't believe that is the case with QMP, but please correct me if I'm wrong about that.

If you control a QMP monitor, you own the guest. If you set up your QMP monitor in a way that permits untrusted users to control it, you're giving away the guest. Adding more commands to access guest memory doesn't really make this worse. It could perhaps make p0wning more efficient.

Quoted 8 lines

If you add a separate stream talking its own language, you get to

Quoted 3 lines

document the language. A file in docs/ should do. The QMP command documentation should point to it.

My updated patch documents it as a comment directly in the C source, as that is what I felt a previous reviewer was asking for. Is docs more appropriate or ??

I feel code comment is find for internal interfaces, but for external interfaces, a separate interface document is more appropriate.

#17 lore raw
Quoted 8 lines

By "evidence", I mean actual numbers for actual QEMU code. Nothing sophisticated, just use your new interface in a way you consider relevant for your own use case, then approximate this use with existing interfaces. The approximation can be very rough. For instance, showing that doing the whole job with your approach is a much faster than doing a necessary part of the job with existing commands would do.

Sure, I can put together some numbers to help with this discussion.

Quoted 6 lines

The QMP overhead could be too high. QMP is control plane, not data plane. How much data do you envisage flowing here?

In theory, even the character device overhead could be too high. Measuring it shouldn't be too hard.

This discussion has me thinking about whether QMP would be viable. I think I'll take a little time to explore that approach in a little more depth before proceeding here. I can report back with what I find.

If you control a QMP monitor, you own the guest.

Good point. So I'll explore the performance aspects and let that drive the decision.

Quoted 3 lines

I feel code comment is find for internal interfaces, but for external interfaces, a separate interface document is more appropriate.

Sounds good.

Cheers,
-bryan

#18 lore raw
Quoted 10 lines

By "evidence", I mean actual numbers for actual QEMU code. Nothing

Quoted 6 lines

sophisticated, just use your new interface in a way you consider relevant for your own use case, then approximate this use with existing interfaces. The approximation can be very rough. For instance, showing that doing the whole job with your approach is a much faster than doing a necessary part of the job with existing commands would do.

Sure, I can put together some numbers to help with this discussion.

I've done some performance testing on my laptop. I have setup LibVMI to access KVM guest memory using one of three different options:

QMP/pmemsave (dump bytes to a file and then read that back into memory) HMC/xp command (dump bytes via human monitor command and repack the output to binary form)
QMP/pmemaccess (access bytes through a Unix socket, this is the patch we are discussing in this thread)

For each of these I measured the time required to translate a guest kernel virtual address into a guest physical address. This is done with a 64-bit Ubuntu Trusty guest and the addresses are translated by walking the page tables in the guest using the selected memory access technique.

You can see the graph here:
http://cl.ly/image/322a3s0h1V05

For those that prefer text, here's the numbers (in microseconds):
QMP/pmemsave: 77706
HMC/xp command: 92552
QMP/pmemaccess: 95

As you can see, the technique proposed in this patch is about 3 orders of magnitude faster than the options available in Qemu today. I will also note that this is for a _single_ virtual address translation. A typical introspection application will be doing much more work than this, which will only compound the problem.

Hopefully this helps to explain why I believe that this is an improvement to the existing mechanisms. Please let me know if you have any questions about these results.

I'll continue some exploration of doing the pmemaccess bulk data transfer over QMP, instead of the UNIX socket. And I'll report back here once I've come up with some thoughts on the best way to proceed.

Cheers,
-bryan

#19 lore raw
Quoted 39 lines · Bryan D. Payne
Quoted 10 lines

By "evidence", I mean actual numbers for actual QEMU code. Nothing

Quoted 6 lines

sophisticated, just use your new interface in a way you consider relevant for your own use case, then approximate this use with existing interfaces. The approximation can be very rough. For instance, showing that doing the whole job with your approach is a much faster than doing a necessary part of the job with existing commands would do.

Sure, I can put together some numbers to help with this discussion.

I've done some performance testing on my laptop. I have setup LibVMI to access KVM guest memory using one of three different options:

QMP/pmemsave (dump bytes to a file and then read that back into memory) HMC/xp command (dump bytes via human monitor command and repack the output to binary form)
QMP/pmemaccess (access bytes through a Unix socket, this is the patch we are discussing in this thread)

For each of these I measured the time required to translate a guest kernel virtual address into a guest physical address. This is done with a 64-bit Ubuntu Trusty guest and the addresses are translated by walking the page tables in the guest using the selected memory access technique.

You can see the graph here:
http://cl.ly/image/322a3s0h1V05

For those that prefer text, here's the numbers (in microseconds):
QMP/pmemsave: 77706
HMC/xp command: 92552
QMP/pmemaccess: 95

As you can see, the technique proposed in this patch is about 3 orders of magnitude faster than the options available in Qemu today. I will also note that this is for a _single_ virtual address translation. A typical introspection application will be doing much more work than this, which will only compound the problem.

Quoted 8 lines

Hopefully this helps to explain why I believe that this is an improvement to the existing mechanisms. Please let me know if you have any questions about these results.

I'll continue some exploration of doing the pmemaccess bulk data transfer over QMP, instead of the UNIX socket. And I'll report back here once I've come up with some thoughts on the best way to proceed.

Look good. I believe QMP will be in between, and if it doesn't work as well, could you also try to use QEMU's char dev instead of limit this to unix socket?

Fam

#20 lore raw
Quoted 6 lines

Look good. I believe QMP will be in between, and if it doesn't work as well,
could you also try to use QEMU's char dev instead of limit this to unix socket?

Sure, I'll look into that as well.
-bryan

#21 lore raw
In reply to Fam Zheng
Quoted 6 lines
Quoted 5 lines

For those that prefer text, here's the numbers (in microseconds):
QMP/pmemsave: 77706
HMC/xp command: 92552
QMP/pmemaccess: 95

I completed a proof of concept implementation for doing this memory access completely over QMP. Basically, it works much like pmemsave except instead of sending the data to a file it does a base64 encode and sends it back over the QMP connection. Interestingly, my testing shows that this is slightly slower than the pmemsave option. Running the same test as before (virtual address translation), I get 88063 microsecs on average. So I don't believe this option is viable.

Look good. I believe QMP will be in between, and if it doesn't work as well,

Quoted 2 lines

could you also try to use QEMU's char dev instead of limit this to unix socket?

I'm moving forward to try the Qemu chardev approach now. I haven't working much with this construct before, so any pointers are appreciated. From what I'm seeing, it looks like the user would create a chardev using one of the QMP @chardev* commands? The schema doesn't indicate that these commands return the chardev id, which seems odd as I was then assuming that one could obtain the id and pass this into the pmemaccess QMP command. Thoughts?

-bryan

#22 lore raw
Quoted 7 lines · Bryan D. Payne
Quoted 6 lines
Quoted 5 lines

For those that prefer text, here's the numbers (in microseconds):
QMP/pmemsave: 77706
HMC/xp command: 92552
QMP/pmemaccess: 95

Quoted 7 lines

I completed a proof of concept implementation for doing this memory access completely over QMP. Basically, it works much like pmemsave except instead of sending the data to a file it does a base64 encode and sends it back over the QMP connection. Interestingly, my testing shows that this is slightly slower than the pmemsave option. Running the same test as before (virtual address translation), I get 88063 microsecs on average. So I don't believe this option is viable.

How did you setup your QMP, and how does the QMP transactions look like in your test? Is it Unix domain socket? If the # of requests are in a high rate, I think the overhead may come from QMP framework, mostly event loop
synchronization: QMP messages are not processed if the device emulation code is running.

Also did you try to compress the memory data with zlib, to compensate for the base64 encoding inflation?

Quoted 7 lines

Look good. I believe QMP will be in between, and if it doesn't work as well,

Quoted 2 lines

could you also try to use QEMU's char dev instead of limit this to unix socket?

Quoted 7 lines

I'm moving forward to try the Qemu chardev approach now. I haven't working much with this construct before, so any pointers are appreciated. From what I'm seeing, it looks like the user would create a chardev using one of the QMP @chardev* commands? The schema doesn't indicate that these commands return the chardev id, which seems odd as I was then assuming that one could obtain the id and pass this into the pmemaccess QMP command. Thoughts?

The chardev will be assigned an "id" by user, as the paremeter of chardev-add, and later the user will pass id to pmemaccess.

In pmemaccess implementation, we will find the chardev by qemu_chr_find, and read/write with qemu_chr_* functions. You can take examples from hw/char/* for devices' usage.

Fam

#23 lore raw
In reply to Bryan D. Payne
Quoted 11 lines · Bryan D. Payne

This patch adds a new QMP command that sets up a domain socket. This socket can then be used for fast read/write access to the guest's physical memory. The key benefit to this system over existing solutions is speed. Using this patch, guest memory can be copied out at a rate of ~200MB/sec, depending on the hardware. Existing solutions only achieve a small fraction of this speed.

qapi-schema.json +2 -0
+
+##

qapi-schema.json line 3529

Quoted 10 lines
qapi-schema.json +10 -0
+# @pmemaccess
+#
+# This command enables access to guest physical memory using
+# a simple protocol over a UNIX domain socket.
+#
+# @path Location to use for the UNIX domain socket
+#
+# Since: 2.3
+##
+{ 'command': 'pmemaccess', 'data': { 'path': 'str' } }

In addition to Fam's review, I have a question - does this code properly use qemu_open() so that I can use 'add-fd' to pass in a pre-opened socket fd into fdset 1, then call pmemaccess with '/dev/fdset/1'? If not, can you please fix it to allow this usage?

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#24 lore raw
Quoted 6 lines

In addition to Fam's review, I have a question - does this code properly use qemu_open() so that I can use 'add-fd' to pass in a pre-opened socket fd into fdset 1, then call pmemaccess with '/dev/fdset/1'? If not, can you please fix it to allow this usage?

I'm not familiar with this setup. Could you point me to an example of where this is done properly somewhere else in the Qemu code base? Once I figure this out, I'll be able to post the v3 patch.

Thanks,
-bryan

#25 lore raw
Quoted 10 lines · Bryan D. Payne
Quoted 6 lines

In addition to Fam's review, I have a question - does this code properly use qemu_open() so that I can use 'add-fd' to pass in a pre-opened socket fd into fdset 1, then call pmemaccess with '/dev/fdset/1'? If not, can you please fix it to allow this usage?

I'm not familiar with this setup. Could you point me to an example of where this is done properly somewhere else in the Qemu code base? Once I figure this out, I'll be able to post the v3 patch.

Off the top of my head, I know the -tpm command line options (related to the 'query-tpm' QMP command) do this; look at hw/tpm/tpm_passthrough.c for that implementation.

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#26 lore raw
Quoted 5 lines

Off the top of my head, I know the -tpm command line options (related to the 'query-tpm' QMP command) do this; look at hw/tpm/tpm_passthrough.c for that implementation.

Thanks, I'll check that out.

Cheers,
-bryan

#27 lore raw
In reply to Eric Blake
Quoted 4 lines

Off the top of my head, I know the -tpm command line options (related to the 'query-tpm' QMP command) do this; look at hw/tpm/tpm_passthrough.c for that implementation.

So now I do see what you are talking about. But I don't think it applies to this patch. I'm not using qemu_open in this patch.

-bryan

#28 lore raw
Quoted 8 lines · Bryan D. Payne
Quoted 4 lines

Off the top of my head, I know the -tpm command line options (related to the 'query-tpm' QMP command) do this; look at hw/tpm/tpm_passthrough.c for that implementation.

So now I do see what you are talking about. But I don't think it applies to this patch. I'm not using qemu_open in this patch.

But my argument is that you SHOULD be using qemu_open, so that someone can have the option of passing in a pre-opened fd rather than having qemu open it. It matters for cases like libvirt where qemu is running under restricted privileges, and it is easier to have libvirt pass in a pre-opened fd than to figure out how to give qemu the privileges it needs to open the file itself. Even if you don't think your feature will be exposed by libvirt any time soon, it's still worth designing things to be flexible from the beginning.

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#29 lore raw
In reply to Bryan D. Payne
Quoted 6 lines · Bryan D. Payne

Thanks for the feedback Eric, I've updated the patch.

v2 changes:
- added QMP command contract to qapi-schema.json
- corrected some comments
- rewired QMP command to use schema code

When sending a v2, it's best to send it as a new top-level thread, instead of buried in-reply-to an existing thread. Also, for a single patch, a cover letter is not strictly necessary (the information you gave here can instead be given after the --- separator of the one-and-only patch email). Cover letters are mandatory only for multi-patch series.

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#30 lore raw

Ok thanks for the advice, I'll adjust for v3. This is (clearly!) my first contribution to Qemu so I'm still learning how you guys operate.

Cheers,
-bryan

Quoted 20 lines · Eric Blake
Quoted 6 lines · Bryan D. Payne

Thanks for the feedback Eric, I've updated the patch.

v2 changes:
- added QMP command contract to qapi-schema.json
- corrected some comments
- rewired QMP command to use schema code

When sending a v2, it's best to send it as a new top-level thread, instead of buried in-reply-to an existing thread. Also, for a single patch, a cover letter is not strictly necessary (the information you gave here can instead be given after the --- separator of the one-and-only patch email). Cover letters are mandatory only for multi-patch series.

--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org

#31 lore raw
Quoted 2 lines · Bryan D. Payne

Ok thanks for the advice, I'll adjust for v3. This is (clearly!) my first contribution to Qemu so I'm still learning how you guys operate.

Great. Looking forward to the next version. BTW please try to use inline replying.

Fam

Quoted 26 lines

Cheers,
-bryan

Quoted 20 lines · Eric Blake
Quoted 6 lines · Bryan D. Payne

Thanks for the feedback Eric, I've updated the patch.

v2 changes:
- added QMP command contract to qapi-schema.json
- corrected some comments
- rewired QMP command to use schema code

When sending a v2, it's best to send it as a new top-level thread, instead of buried in-reply-to an existing thread. Also, for a single patch, a cover letter is not strictly necessary (the information you gave here can instead be given after the --- separator of the one-and-only patch email). Cover letters are mandatory only for multi-patch series.

--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org