9ae3a8
From 7cc714c354fc4dbfaeca66156ca331f57bf9653e Mon Sep 17 00:00:00 2001
9ae3a8
From: Laszlo Ersek <lersek@redhat.com>
9ae3a8
Date: Fri, 7 Nov 2014 17:18:03 +0100
9ae3a8
Subject: [PATCH 16/41] dump: make kdump-compressed format available for
9ae3a8
 'dump-guest-memory'
9ae3a8
9ae3a8
Message-id: <1415380693-16593-17-git-send-email-lersek@redhat.com>
9ae3a8
Patchwork-id: 62202
9ae3a8
O-Subject: [RHEL-7.1 qemu-kvm PATCH 16/26] dump: make kdump-compressed format available for 'dump-guest-memory'
9ae3a8
Bugzilla: 1157798
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Luiz Capitulino <lcapitulino@redhat.com>
9ae3a8
RH-Acked-by: dgibson <dgibson@redhat.com>
9ae3a8
9ae3a8
From: qiaonuohan <qiaonuohan@cn.fujitsu.com>
9ae3a8
9ae3a8
Make monitor command 'dump-guest-memory' be able to dump in kdump-compressed
9ae3a8
format. The command's usage:
9ae3a8
9ae3a8
  dump [-p] protocol [begin] [length] [format]
9ae3a8
9ae3a8
'format' is used to specified the format of vmcore and can be:
9ae3a8
1. 'elf': ELF format, without compression
9ae3a8
2. 'kdump-zlib': kdump-compressed format, with zlib-compressed
9ae3a8
3. 'kdump-lzo': kdump-compressed format, with lzo-compressed
9ae3a8
4. 'kdump-snappy': kdump-compressed format, with snappy-compressed
9ae3a8
Without 'format' being set, it is same as 'elf'. And if non-elf format is
9ae3a8
specified, paging and filter is not allowed.
9ae3a8
9ae3a8
Note:
9ae3a8
  1. The kdump-compressed format is readable only with the crash utility and
9ae3a8
     makedumpfile, and it can be smaller than the ELF format because of the
9ae3a8
     compression support.
9ae3a8
  2. The kdump-compressed format is the 6th edition.
9ae3a8
9ae3a8
Signed-off-by: Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
9ae3a8
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
9ae3a8
(cherry picked from commit b53ccc30c40df52d192e469a86c188a8649c6df3)
9ae3a8
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 dump.c           | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
9ae3a8
 hmp.c            |   5 ++-
9ae3a8
 qapi-schema.json |  25 ++++++++++-
9ae3a8
 qmp-commands.hx  |   7 ++-
9ae3a8
 4 files changed, 158 insertions(+), 10 deletions(-)
9ae3a8
9ae3a8
diff --git a/dump.c b/dump.c
9ae3a8
index fc5530f..c0d5877 100644
9ae3a8
--- a/dump.c
9ae3a8
+++ b/dump.c
9ae3a8
@@ -1449,6 +1449,64 @@ out:
9ae3a8
     return ret;
9ae3a8
 }
9ae3a8
 
9ae3a8
+static int create_kdump_vmcore(DumpState *s)
9ae3a8
+{
9ae3a8
+    int ret;
9ae3a8
+
9ae3a8
+    /*
9ae3a8
+     * the kdump-compressed format is:
9ae3a8
+     *                                               File offset
9ae3a8
+     *  +------------------------------------------+ 0x0
9ae3a8
+     *  |    main header (struct disk_dump_header) |
9ae3a8
+     *  |------------------------------------------+ block 1
9ae3a8
+     *  |    sub header (struct kdump_sub_header)  |
9ae3a8
+     *  |------------------------------------------+ block 2
9ae3a8
+     *  |            1st-dump_bitmap               |
9ae3a8
+     *  |------------------------------------------+ block 2 + X blocks
9ae3a8
+     *  |            2nd-dump_bitmap               | (aligned by block)
9ae3a8
+     *  |------------------------------------------+ block 2 + 2 * X blocks
9ae3a8
+     *  |  page desc for pfn 0 (struct page_desc)  | (aligned by block)
9ae3a8
+     *  |  page desc for pfn 1 (struct page_desc)  |
9ae3a8
+     *  |                    :                     |
9ae3a8
+     *  |------------------------------------------| (not aligned by block)
9ae3a8
+     *  |         page data (pfn 0)                |
9ae3a8
+     *  |         page data (pfn 1)                |
9ae3a8
+     *  |                    :                     |
9ae3a8
+     *  +------------------------------------------+
9ae3a8
+     */
9ae3a8
+
9ae3a8
+    ret = write_start_flat_header(s->fd);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        dump_error(s, "dump: failed to write start flat header.\n");
9ae3a8
+        return -1;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = write_dump_header(s);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        return -1;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = write_dump_bitmap(s);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        return -1;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = write_dump_pages(s);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        return -1;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = write_end_flat_header(s->fd);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        dump_error(s, "dump: failed to write end flat header.\n");
9ae3a8
+        return -1;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    dump_completed(s);
9ae3a8
+
9ae3a8
+    return 0;
9ae3a8
+}
9ae3a8
+
9ae3a8
 static ram_addr_t get_start_block(DumpState *s)
9ae3a8
 {
9ae3a8
     GuestPhysBlock *block;
9ae3a8
@@ -1485,7 +1543,8 @@ static void get_max_mapnr(DumpState *s)
9ae3a8
     s->max_mapnr = paddr_to_pfn(last_block->target_end, s->page_shift);
9ae3a8
 }
9ae3a8
 
9ae3a8
-static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
9ae3a8
+static int dump_init(DumpState *s, int fd, bool has_format,
9ae3a8
+                     DumpGuestMemoryFormat format, bool paging, bool has_filter,
9ae3a8
                      int64_t begin, int64_t length, Error **errp)
9ae3a8
 {
9ae3a8
     CPUArchState *env;
9ae3a8
@@ -1493,6 +1552,11 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
9ae3a8
     Error *err = NULL;
9ae3a8
     int ret;
9ae3a8
 
9ae3a8
+    /* kdump-compressed is conflict with paging and filter */
9ae3a8
+    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
9ae3a8
+        assert(!paging && !has_filter);
9ae3a8
+    }
9ae3a8
+
9ae3a8
     if (runstate_is_running()) {
9ae3a8
         vm_stop(RUN_STATE_SAVE_VM);
9ae3a8
         s->resume = true;
9ae3a8
@@ -1563,6 +1627,28 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
9ae3a8
     tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), s->page_size);
9ae3a8
     s->len_dump_bitmap = tmp * s->page_size;
9ae3a8
 
9ae3a8
+    /* init for kdump-compressed format */
9ae3a8
+    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
9ae3a8
+        switch (format) {
9ae3a8
+        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB:
9ae3a8
+            s->flag_compress = DUMP_DH_COMPRESSED_ZLIB;
9ae3a8
+            break;
9ae3a8
+
9ae3a8
+        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO:
9ae3a8
+            s->flag_compress = DUMP_DH_COMPRESSED_LZO;
9ae3a8
+            break;
9ae3a8
+
9ae3a8
+        case DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY:
9ae3a8
+            s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY;
9ae3a8
+            break;
9ae3a8
+
9ae3a8
+        default:
9ae3a8
+            s->flag_compress = 0;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        return 0;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     if (s->has_filter) {
9ae3a8
         memory_mapping_filter(&s->list, s->begin, s->length);
9ae3a8
     }
9ae3a8
@@ -1622,14 +1708,25 @@ cleanup:
9ae3a8
 }
9ae3a8
 
9ae3a8
 void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
9ae3a8
-                           int64_t begin, bool has_length, int64_t length,
9ae3a8
-                           Error **errp)
9ae3a8
+                           int64_t begin, bool has_length,
9ae3a8
+                           int64_t length, bool has_format,
9ae3a8
+                           DumpGuestMemoryFormat format, Error **errp)
9ae3a8
 {
9ae3a8
     const char *p;
9ae3a8
     int fd = -1;
9ae3a8
     DumpState *s;
9ae3a8
     int ret;
9ae3a8
 
9ae3a8
+    /*
9ae3a8
+     * kdump-compressed format need the whole memory dumped, so paging or
9ae3a8
+     * filter is not supported here.
9ae3a8
+     */
9ae3a8
+    if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) &&
9ae3a8
+        (paging || has_begin || has_length)) {
9ae3a8
+        error_setg(errp, "kdump-compressed format doesn't support paging or "
9ae3a8
+                         "filter");
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
     if (has_begin && !has_length) {
9ae3a8
         error_set(errp, QERR_MISSING_PARAMETER, "length");
9ae3a8
         return;
9ae3a8
@@ -1639,6 +1736,21 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
9ae3a8
         return;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    /* check whether lzo/snappy is supported */
9ae3a8
+#ifndef CONFIG_LZO
9ae3a8
+    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) {
9ae3a8
+        error_setg(errp, "kdump-lzo is not available now");
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+#endif
9ae3a8
+
9ae3a8
+#ifndef CONFIG_SNAPPY
9ae3a8
+    if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY) {
9ae3a8
+        error_setg(errp, "kdump-snappy is not available now");
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+#endif
9ae3a8
+
9ae3a8
 #if !defined(WIN32)
9ae3a8
     if (strstart(file, "fd:", &p)) {
9ae3a8
         fd = monitor_get_fd(cur_mon, p, errp);
9ae3a8
@@ -1663,14 +1775,21 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
9ae3a8
 
9ae3a8
     s = g_malloc0(sizeof(DumpState));
9ae3a8
 
9ae3a8
-    ret = dump_init(s, fd, paging, has_begin, begin, length, errp);
9ae3a8
+    ret = dump_init(s, fd, has_format, format, paging, has_begin,
9ae3a8
+                    begin, length, errp);
9ae3a8
     if (ret < 0) {
9ae3a8
         g_free(s);
9ae3a8
         return;
9ae3a8
     }
9ae3a8
 
9ae3a8
-    if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
9ae3a8
-        error_set(errp, QERR_IO_ERROR);
9ae3a8
+    if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
9ae3a8
+        if (create_kdump_vmcore(s) < 0 && !error_is_set(s->errp)) {
9ae3a8
+            error_set(errp, QERR_IO_ERROR);
9ae3a8
+        }
9ae3a8
+    } else {
9ae3a8
+        if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
9ae3a8
+            error_set(errp, QERR_IO_ERROR);
9ae3a8
+        }
9ae3a8
     }
9ae3a8
 
9ae3a8
     g_free(s);
9ae3a8
diff --git a/hmp.c b/hmp.c
9ae3a8
index b723b26..1805926 100644
9ae3a8
--- a/hmp.c
9ae3a8
+++ b/hmp.c
9ae3a8
@@ -1195,8 +1195,11 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
9ae3a8
     const char *file = qdict_get_str(qdict, "filename");
9ae3a8
     bool has_begin = qdict_haskey(qdict, "begin");
9ae3a8
     bool has_length = qdict_haskey(qdict, "length");
9ae3a8
+    /* kdump-compressed format is not supported for HMP */
9ae3a8
+    bool has_format = false;
9ae3a8
     int64_t begin = 0;
9ae3a8
     int64_t length = 0;
9ae3a8
+    enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
9ae3a8
     char *prot;
9ae3a8
 
9ae3a8
     if (has_begin) {
9ae3a8
@@ -1209,7 +1212,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
9ae3a8
     prot = g_strconcat("file:", file, NULL);
9ae3a8
 
9ae3a8
     qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
9ae3a8
-                          &errp);
9ae3a8
+                          has_format, dump_format, &errp);
9ae3a8
     hmp_handle_error(mon, &errp);
9ae3a8
     g_free(prot);
9ae3a8
 }
9ae3a8
diff --git a/qapi-schema.json b/qapi-schema.json
9ae3a8
index d3d4e57..8f81c76 100644
9ae3a8
--- a/qapi-schema.json
9ae3a8
+++ b/qapi-schema.json
9ae3a8
@@ -2578,6 +2578,24 @@
9ae3a8
 { 'command': 'device_del', 'data': {'id': 'str'} }
9ae3a8
 
9ae3a8
 ##
9ae3a8
+# @DumpGuestMemoryFormat:
9ae3a8
+#
9ae3a8
+# An enumeration of guest-memory-dump's format.
9ae3a8
+#
9ae3a8
+# @elf: elf format
9ae3a8
+#
9ae3a8
+# @kdump-zlib: kdump-compressed format with zlib-compressed
9ae3a8
+#
9ae3a8
+# @kdump-lzo: kdump-compressed format with lzo-compressed
9ae3a8
+#
9ae3a8
+# @kdump-snappy: kdump-compressed format with snappy-compressed
9ae3a8
+#
9ae3a8
+# Since: 2.0
9ae3a8
+##
9ae3a8
+{ 'enum': 'DumpGuestMemoryFormat',
9ae3a8
+  'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] }
9ae3a8
+
9ae3a8
+##
9ae3a8
 # @dump-guest-memory
9ae3a8
 #
9ae3a8
 # Dump guest's memory to vmcore. It is a synchronous operation that can take
9ae3a8
@@ -2613,13 +2631,18 @@
9ae3a8
 #          want to dump all guest's memory, please specify the start @begin
9ae3a8
 #          and @length
9ae3a8
 #
9ae3a8
+# @format: #optional if specified, the format of guest memory dump. But non-elf
9ae3a8
+#          format is conflict with paging and filter, ie. @paging, @begin and
9ae3a8
+#          @length is not allowed to be specified with non-elf @format at the
9ae3a8
+#          same time (since 2.0)
9ae3a8
+#
9ae3a8
 # Returns: nothing on success
9ae3a8
 #
9ae3a8
 # Since: 1.2
9ae3a8
 ##
9ae3a8
 { 'command': 'dump-guest-memory',
9ae3a8
   'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
9ae3a8
-            '*length': 'int' } }
9ae3a8
+            '*length': 'int', '*format': 'DumpGuestMemoryFormat' } }
9ae3a8
 
9ae3a8
 ##
9ae3a8
 # @netdev_add:
9ae3a8
diff --git a/qmp-commands.hx b/qmp-commands.hx
9ae3a8
index e164ff8..61aa3bf 100644
9ae3a8
--- a/qmp-commands.hx
9ae3a8
+++ b/qmp-commands.hx
9ae3a8
@@ -885,8 +885,8 @@ EQMP
9ae3a8
 
9ae3a8
     {
9ae3a8
         .name       = "dump-guest-memory",
9ae3a8
-        .args_type  = "paging:b,protocol:s,begin:i?,end:i?",
9ae3a8
-        .params     = "-p protocol [begin] [length]",
9ae3a8
+        .args_type  = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
9ae3a8
+        .params     = "-p protocol [begin] [length] [format]",
9ae3a8
         .help       = "dump guest memory to file",
9ae3a8
         .user_print = monitor_user_noop,
9ae3a8
         .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory,
9ae3a8
@@ -907,6 +907,9 @@ Arguments:
9ae3a8
            with length together (json-int)
9ae3a8
 - "length": the memory size, in bytes. It's optional, and should be specified
9ae3a8
             with begin together (json-int)
9ae3a8
+- "format": the format of guest memory dump. It's optional, and can be
9ae3a8
+            elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will
9ae3a8
+            conflict with paging and filter, ie. begin and length (json-string)
9ae3a8
 
9ae3a8
 Example:
9ae3a8
 
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8