Blame SOURCES/kvm-migration-detect-compression-and-decompression-error.patch

383d26
From 231178d9b06a3d2bba1e7695071957671d7c08a1 Mon Sep 17 00:00:00 2001
383d26
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
383d26
Date: Fri, 22 Jun 2018 18:59:51 +0200
383d26
Subject: [PATCH 12/57] migration: detect compression and decompression errors
383d26
383d26
RH-Author: Dr. David Alan Gilbert <dgilbert@redhat.com>
383d26
Message-id: <20180622190005.21297-5-dgilbert@redhat.com>
383d26
Patchwork-id: 80996
383d26
O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 04/18] migration: detect compression and decompression errors
383d26
Bugzilla: 1584139
383d26
RH-Acked-by: Peter Xu <peterx@redhat.com>
383d26
RH-Acked-by: Juan Quintela <quintela@redhat.com>
383d26
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
383d26
383d26
From: Xiao Guangrong <xiaoguangrong@tencent.com>
383d26
383d26
Currently the page being compressed is allowed to be updated by
383d26
the VM on the source QEMU, correspondingly the destination QEMU
383d26
just ignores the decompression error. However, we completely miss
383d26
the chance to catch real errors, then the VM is corrupted silently
383d26
383d26
To make the migration more robuster, we copy the page to a buffer
383d26
first to avoid it being written by VM, then detect and handle the
383d26
errors of both compression and decompression errors properly
383d26
383d26
Reviewed-by: Peter Xu <peterx@redhat.com>
383d26
Signed-off-by: Xiao Guangrong <xiaoguangrong@tencent.com>
383d26
Message-Id: <20180330075128.26919-5-xiaoguangrong@tencent.com>
383d26
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
383d26
(cherry picked from commit 34ab9e9743aeaf265929d930747f101fa5c76fea)
383d26
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
383d26
---
383d26
 migration/qemu-file.c |  4 ++--
383d26
 migration/ram.c       | 56 +++++++++++++++++++++++++++++++++++----------------
383d26
 2 files changed, 41 insertions(+), 19 deletions(-)
383d26
383d26
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
383d26
index bafe3a0..0463f4c 100644
383d26
--- a/migration/qemu-file.c
383d26
+++ b/migration/qemu-file.c
383d26
@@ -710,9 +710,9 @@ ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream,
383d26
     blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t),
383d26
                               blen, p, size);
383d26
     if (blen < 0) {
383d26
-        error_report("Compress Failed!");
383d26
-        return 0;
383d26
+        return -1;
383d26
     }
383d26
+
383d26
     qemu_put_be32(f, blen);
383d26
     if (f->ops->writev_buffer) {
383d26
         add_to_iovec(f, f->buf + f->buf_index, blen, false);
383d26
diff --git a/migration/ram.c b/migration/ram.c
383d26
index be89cd8..cd6d98a 100644
383d26
--- a/migration/ram.c
383d26
+++ b/migration/ram.c
383d26
@@ -269,7 +269,10 @@ struct CompressParam {
383d26
     QemuCond cond;
383d26
     RAMBlock *block;
383d26
     ram_addr_t offset;
383d26
+
383d26
+    /* internally used fields */
383d26
     z_stream stream;
383d26
+    uint8_t *originbuf;
383d26
 };
383d26
 typedef struct CompressParam CompressParam;
383d26
 
383d26
@@ -296,13 +299,14 @@ static QemuCond comp_done_cond;
383d26
 /* The empty QEMUFileOps will be used by file in CompressParam */
383d26
 static const QEMUFileOps empty_ops = { };
383d26
 
383d26
+static QEMUFile *decomp_file;
383d26
 static DecompressParam *decomp_param;
383d26
 static QemuThread *decompress_threads;
383d26
 static QemuMutex decomp_done_lock;
383d26
 static QemuCond decomp_done_cond;
383d26
 
383d26
 static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
383d26
-                                ram_addr_t offset);
383d26
+                                ram_addr_t offset, uint8_t *source_buf);
383d26
 
383d26
 static void *do_data_compress(void *opaque)
383d26
 {
383d26
@@ -318,7 +322,8 @@ static void *do_data_compress(void *opaque)
383d26
             param->block = NULL;
383d26
             qemu_mutex_unlock(&param->mutex);
383d26
 
383d26
-            do_compress_ram_page(param->file, &param->stream, block, offset);
383d26
+            do_compress_ram_page(param->file, &param->stream, block, offset,
383d26
+                                 param->originbuf);
383d26
 
383d26
             qemu_mutex_lock(&comp_done_lock);
383d26
             param->done = true;
383d26
@@ -370,6 +375,7 @@ static void compress_threads_save_cleanup(void)
383d26
         qemu_mutex_destroy(&comp_param[i].mutex);
383d26
         qemu_cond_destroy(&comp_param[i].cond);
383d26
         deflateEnd(&comp_param[i].stream);
383d26
+        g_free(comp_param[i].originbuf);
383d26
         qemu_fclose(comp_param[i].file);
383d26
         comp_param[i].file = NULL;
383d26
     }
383d26
@@ -394,8 +400,14 @@ static int compress_threads_save_setup(void)
383d26
     qemu_cond_init(&comp_done_cond);
383d26
     qemu_mutex_init(&comp_done_lock);
383d26
     for (i = 0; i < thread_count; i++) {
383d26
+        comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
383d26
+        if (!comp_param[i].originbuf) {
383d26
+            goto exit;
383d26
+        }
383d26
+
383d26
         if (deflateInit(&comp_param[i].stream,
383d26
                         migrate_compress_level()) != Z_OK) {
383d26
+            g_free(comp_param[i].originbuf);
383d26
             goto exit;
383d26
         }
383d26
 
383d26
@@ -1054,7 +1066,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage)
383d26
 }
383d26
 
383d26
 static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
383d26
-                                ram_addr_t offset)
383d26
+                                ram_addr_t offset, uint8_t *source_buf)
383d26
 {
383d26
     RAMState *rs = ram_state;
383d26
     int bytes_sent, blen;
383d26
@@ -1062,7 +1074,14 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
383d26
 
383d26
     bytes_sent = save_page_header(rs, f, block, offset |
383d26
                                   RAM_SAVE_FLAG_COMPRESS_PAGE);
383d26
-    blen = qemu_put_compression_data(f, stream, p, TARGET_PAGE_SIZE);
383d26
+
383d26
+    /*
383d26
+     * copy it to a internal buffer to avoid it being modified by VM
383d26
+     * so that we can catch up the error during compression and
383d26
+     * decompression
383d26
+     */
383d26
+    memcpy(source_buf, p, TARGET_PAGE_SIZE);
383d26
+    blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE);
383d26
     if (blen < 0) {
383d26
         bytes_sent = 0;
383d26
         qemu_file_set_error(migrate_get_current()->to_dst_file, blen);
383d26
@@ -2556,7 +2575,7 @@ static void *do_data_decompress(void *opaque)
383d26
     DecompressParam *param = opaque;
383d26
     unsigned long pagesize;
383d26
     uint8_t *des;
383d26
-    int len;
383d26
+    int len, ret;
383d26
 
383d26
     qemu_mutex_lock(&param->mutex);
383d26
     while (!param->quit) {
383d26
@@ -2567,13 +2586,13 @@ static void *do_data_decompress(void *opaque)
383d26
             qemu_mutex_unlock(&param->mutex);
383d26
 
383d26
             pagesize = TARGET_PAGE_SIZE;
383d26
-            /* qemu_uncompress_data() will return failed in some case,
383d26
-             * especially when the page is dirtied when doing the compression,
383d26
-             * it's not a problem because the dirty page will be retransferred
383d26
-             * and uncompress() won't break the data in other pages.
383d26
-             */
383d26
-            qemu_uncompress_data(&param->stream, des, pagesize, param->compbuf,
383d26
-                                 len);
383d26
+
383d26
+            ret = qemu_uncompress_data(&param->stream, des, pagesize,
383d26
+                                       param->compbuf, len);
383d26
+            if (ret < 0) {
383d26
+                error_report("decompress data failed");
383d26
+                qemu_file_set_error(decomp_file, ret);
383d26
+            }
383d26
 
383d26
             qemu_mutex_lock(&decomp_done_lock);
383d26
             param->done = true;
383d26
@@ -2590,12 +2609,12 @@ static void *do_data_decompress(void *opaque)
383d26
     return NULL;
383d26
 }
383d26
 
383d26
-static void wait_for_decompress_done(void)
383d26
+static int wait_for_decompress_done(void)
383d26
 {
383d26
     int idx, thread_count;
383d26
 
383d26
     if (!migrate_use_compression()) {
383d26
-        return;
383d26
+        return 0;
383d26
     }
383d26
 
383d26
     thread_count = migrate_decompress_threads();
383d26
@@ -2606,6 +2625,7 @@ static void wait_for_decompress_done(void)
383d26
         }
383d26
     }
383d26
     qemu_mutex_unlock(&decomp_done_lock);
383d26
+    return qemu_file_get_error(decomp_file);
383d26
 }
383d26
 
383d26
 static void compress_threads_load_cleanup(void)
383d26
@@ -2646,9 +2666,10 @@ static void compress_threads_load_cleanup(void)
383d26
     g_free(decomp_param);
383d26
     decompress_threads = NULL;
383d26
     decomp_param = NULL;
383d26
+    decomp_file = NULL;
383d26
 }
383d26
 
383d26
-static int compress_threads_load_setup(void)
383d26
+static int compress_threads_load_setup(QEMUFile *f)
383d26
 {
383d26
     int i, thread_count;
383d26
 
383d26
@@ -2661,6 +2682,7 @@ static int compress_threads_load_setup(void)
383d26
     decomp_param = g_new0(DecompressParam, thread_count);
383d26
     qemu_mutex_init(&decomp_done_lock);
383d26
     qemu_cond_init(&decomp_done_cond);
383d26
+    decomp_file = f;
383d26
     for (i = 0; i < thread_count; i++) {
383d26
         if (inflateInit(&decomp_param[i].stream) != Z_OK) {
383d26
             goto exit;
383d26
@@ -2720,7 +2742,7 @@ static void decompress_data_with_multi_threads(QEMUFile *f,
383d26
  */
383d26
 static int ram_load_setup(QEMUFile *f, void *opaque)
383d26
 {
383d26
-    if (compress_threads_load_setup()) {
383d26
+    if (compress_threads_load_setup(f)) {
383d26
         return -1;
383d26
     }
383d26
 
383d26
@@ -3075,7 +3097,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
383d26
         }
383d26
     }
383d26
 
383d26
-    wait_for_decompress_done();
383d26
+    ret |= wait_for_decompress_done();
383d26
     rcu_read_unlock();
383d26
     trace_ram_load_complete(ret, seq_iter);
383d26
     return ret;
383d26
-- 
383d26
1.8.3.1
383d26