| From 55c524d38cafb5856220c76b9573efa6e22dd1f1 Mon Sep 17 00:00:00 2001 |
| From: Laszlo Ersek <lersek@redhat.com> |
| Date: Fri, 7 Nov 2014 17:18:01 +0100 |
| Subject: [PATCH 14/41] dump: add API to write dump pages |
| |
| Message-id: <1415380693-16593-15-git-send-email-lersek@redhat.com> |
| Patchwork-id: 62200 |
| O-Subject: [RHEL-7.1 qemu-kvm PATCH 14/26] dump: add API to write dump pages |
| Bugzilla: 1157798 |
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> |
| RH-Acked-by: Luiz Capitulino <lcapitulino@redhat.com> |
| RH-Acked-by: dgibson <dgibson@redhat.com> |
| |
| From: qiaonuohan <qiaonuohan@cn.fujitsu.com> |
| |
| functions are used to write page to vmcore. vmcore is written page by page. |
| page desc is used to store the information of a page, including a page's size, |
| offset, compression format, etc. |
| |
| Signed-off-by: Qiao Nuohan <qiaonuohan@cn.fujitsu.com> |
| Reviewed-by: Laszlo Ersek <lersek@redhat.com> |
| Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com> |
| (cherry picked from commit d12f57ec6640d36e380367a0ab6ab9f3f29b6d51) |
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> |
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> |
| |
| dump.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++ |
| include/sysemu/dump.h | 7 ++ |
| 2 files changed, 238 insertions(+) |
| |
| diff --git a/dump.c b/dump.c |
| index 926ab84..fc5530f 100644 |
| |
| |
| @@ -25,6 +25,14 @@ |
| #include "qapi/error.h" |
| #include "qmp-commands.h" |
| |
| +#include <zlib.h> |
| +#ifdef CONFIG_LZO |
| +#include <lzo/lzo1x.h> |
| +#endif |
| +#ifdef CONFIG_SNAPPY |
| +#include <snappy-c.h> |
| +#endif |
| + |
| static uint16_t cpu_convert_to_target16(uint16_t val, int endian) |
| { |
| if (endian == ELFDATA2LSB) { |
| @@ -1218,6 +1226,229 @@ static void free_data_cache(DataCache *data_cache) |
| g_free(data_cache->buf); |
| } |
| |
| +static size_t get_len_buf_out(size_t page_size, uint32_t flag_compress) |
| +{ |
| + size_t len_buf_out_zlib, len_buf_out_lzo, len_buf_out_snappy; |
| + size_t len_buf_out; |
| + |
| + /* init buf_out */ |
| + len_buf_out_zlib = len_buf_out_lzo = len_buf_out_snappy = 0; |
| + |
| + /* buf size for zlib */ |
| + len_buf_out_zlib = compressBound(page_size); |
| + |
| + /* buf size for lzo */ |
| +#ifdef CONFIG_LZO |
| + if (flag_compress & DUMP_DH_COMPRESSED_LZO) { |
| + if (lzo_init() != LZO_E_OK) { |
| + /* return 0 to indicate lzo is unavailable */ |
| + return 0; |
| + } |
| + } |
| + |
| + /* |
| + * LZO will expand incompressible data by a little amount. please check the |
| + * following URL to see the expansion calculation: |
| + * http://www.oberhumer.com/opensource/lzo/lzofaq.php |
| + */ |
| + len_buf_out_lzo = page_size + page_size / 16 + 64 + 3; |
| +#endif |
| + |
| +#ifdef CONFIG_SNAPPY |
| + /* buf size for snappy */ |
| + len_buf_out_snappy = snappy_max_compressed_length(page_size); |
| +#endif |
| + |
| + /* get the biggest that can store all kinds of compressed page */ |
| + len_buf_out = MAX(len_buf_out_zlib, |
| + MAX(len_buf_out_lzo, len_buf_out_snappy)); |
| + |
| + return len_buf_out; |
| +} |
| + |
| +/* |
| + * check if the page is all 0 |
| + */ |
| +static inline bool is_zero_page(const uint8_t *buf, size_t page_size) |
| +{ |
| + return buffer_is_zero(buf, page_size); |
| +} |
| + |
| +static int write_dump_pages(DumpState *s) |
| +{ |
| + int ret = 0; |
| + DataCache page_desc, page_data; |
| + size_t len_buf_out, size_out; |
| +#ifdef CONFIG_LZO |
| + lzo_bytep wrkmem = NULL; |
| +#endif |
| + uint8_t *buf_out = NULL; |
| + off_t offset_desc, offset_data; |
| + PageDescriptor pd, pd_zero; |
| + uint8_t *buf; |
| + int endian = s->dump_info.d_endian; |
| + GuestPhysBlock *block_iter = NULL; |
| + uint64_t pfn_iter; |
| + |
| + /* get offset of page_desc and page_data in dump file */ |
| + offset_desc = s->offset_page; |
| + offset_data = offset_desc + sizeof(PageDescriptor) * s->num_dumpable; |
| + |
| + prepare_data_cache(&page_desc, s, offset_desc); |
| + prepare_data_cache(&page_data, s, offset_data); |
| + |
| + /* prepare buffer to store compressed data */ |
| + len_buf_out = get_len_buf_out(s->page_size, s->flag_compress); |
| + if (len_buf_out == 0) { |
| + dump_error(s, "dump: failed to get length of output buffer.\n"); |
| + goto out; |
| + } |
| + |
| +#ifdef CONFIG_LZO |
| + wrkmem = g_malloc(LZO1X_1_MEM_COMPRESS); |
| +#endif |
| + |
| + buf_out = g_malloc(len_buf_out); |
| + |
| + /* |
| + * init zero page's page_desc and page_data, because every zero page |
| + * uses the same page_data |
| + */ |
| + pd_zero.size = cpu_convert_to_target32(s->page_size, endian); |
| + pd_zero.flags = cpu_convert_to_target32(0, endian); |
| + pd_zero.offset = cpu_convert_to_target64(offset_data, endian); |
| + pd_zero.page_flags = cpu_convert_to_target64(0, endian); |
| + buf = g_malloc0(s->page_size); |
| + ret = write_cache(&page_data, buf, s->page_size, false); |
| + g_free(buf); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page data(zero page).\n"); |
| + goto out; |
| + } |
| + |
| + offset_data += s->page_size; |
| + |
| + /* |
| + * dump memory to vmcore page by page. zero page will all be resided in the |
| + * first page of page section |
| + */ |
| + while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { |
| + /* check zero page */ |
| + if (is_zero_page(buf, s->page_size)) { |
| + ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), |
| + false); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page desc.\n"); |
| + goto out; |
| + } |
| + } else { |
| + /* |
| + * not zero page, then: |
| + * 1. compress the page |
| + * 2. write the compressed page into the cache of page_data |
| + * 3. get page desc of the compressed page and write it into the |
| + * cache of page_desc |
| + * |
| + * only one compression format will be used here, for |
| + * s->flag_compress is set. But when compression fails to work, |
| + * we fall back to save in plaintext. |
| + */ |
| + size_out = len_buf_out; |
| + if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) && |
| + (compress2(buf_out, (uLongf *)&size_out, buf, s->page_size, |
| + Z_BEST_SPEED) == Z_OK) && (size_out < s->page_size)) { |
| + pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_ZLIB, |
| + endian); |
| + pd.size = cpu_convert_to_target32(size_out, endian); |
| + |
| + ret = write_cache(&page_data, buf_out, size_out, false); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page data.\n"); |
| + goto out; |
| + } |
| +#ifdef CONFIG_LZO |
| + } else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) && |
| + (lzo1x_1_compress(buf, s->page_size, buf_out, |
| + (lzo_uint *)&size_out, wrkmem) == LZO_E_OK) && |
| + (size_out < s->page_size)) { |
| + pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_LZO, |
| + endian); |
| + pd.size = cpu_convert_to_target32(size_out, endian); |
| + |
| + ret = write_cache(&page_data, buf_out, size_out, false); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page data.\n"); |
| + goto out; |
| + } |
| +#endif |
| +#ifdef CONFIG_SNAPPY |
| + } else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) && |
| + (snappy_compress((char *)buf, s->page_size, |
| + (char *)buf_out, &size_out) == SNAPPY_OK) && |
| + (size_out < s->page_size)) { |
| + pd.flags = cpu_convert_to_target32( |
| + DUMP_DH_COMPRESSED_SNAPPY, endian); |
| + pd.size = cpu_convert_to_target32(size_out, endian); |
| + |
| + ret = write_cache(&page_data, buf_out, size_out, false); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page data.\n"); |
| + goto out; |
| + } |
| +#endif |
| + } else { |
| + /* |
| + * fall back to save in plaintext, size_out should be |
| + * assigned to s->page_size |
| + */ |
| + pd.flags = cpu_convert_to_target32(0, endian); |
| + size_out = s->page_size; |
| + pd.size = cpu_convert_to_target32(size_out, endian); |
| + |
| + ret = write_cache(&page_data, buf, s->page_size, false); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page data.\n"); |
| + goto out; |
| + } |
| + } |
| + |
| + /* get and write page desc here */ |
| + pd.page_flags = cpu_convert_to_target64(0, endian); |
| + pd.offset = cpu_convert_to_target64(offset_data, endian); |
| + offset_data += size_out; |
| + |
| + ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to write page desc.\n"); |
| + goto out; |
| + } |
| + } |
| + } |
| + |
| + ret = write_cache(&page_desc, NULL, 0, true); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to sync cache for page_desc.\n"); |
| + goto out; |
| + } |
| + ret = write_cache(&page_data, NULL, 0, true); |
| + if (ret < 0) { |
| + dump_error(s, "dump: failed to sync cache for page_data.\n"); |
| + goto out; |
| + } |
| + |
| +out: |
| + free_data_cache(&page_desc); |
| + free_data_cache(&page_data); |
| + |
| +#ifdef CONFIG_LZO |
| + g_free(wrkmem); |
| +#endif |
| + |
| + g_free(buf_out); |
| + |
| + return ret; |
| +} |
| + |
| static ram_addr_t get_start_block(DumpState *s) |
| { |
| GuestPhysBlock *block; |
| diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h |
| index 92a95e4..efab7a3 100644 |
| |
| |
| @@ -151,6 +151,13 @@ typedef struct DataCache { |
| off_t offset; /* offset of the file */ |
| } DataCache; |
| |
| +typedef struct QEMU_PACKED PageDescriptor { |
| + uint64_t offset; /* the offset of the page data*/ |
| + uint32_t size; /* the size of this dump page */ |
| + uint32_t flags; /* flags */ |
| + uint64_t page_flags; /* page flags */ |
| +} PageDescriptor; |
| + |
| struct GuestPhysBlockList; /* memory_mapping.h */ |
| int cpu_get_dump_info(ArchDumpInfo *info, |
| const struct GuestPhysBlockList *guest_phys_blocks); |
| -- |
| 1.8.3.1 |
| |