26ba25
From d9c3b12460d40900dbc2c0f4b6b03886488178d6 Mon Sep 17 00:00:00 2001
26ba25
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
26ba25
Date: Wed, 1 Aug 2018 13:55:06 +0100
26ba25
Subject: [PATCH 02/21] migration: stop compression to allocate and free memory
26ba25
 frequently
26ba25
26ba25
RH-Author: Dr. David Alan Gilbert <dgilbert@redhat.com>
26ba25
Message-id: <20180801135522.11658-3-dgilbert@redhat.com>
26ba25
Patchwork-id: 81579
26ba25
O-Subject: [qemu-kvm RHEL8/virt212 PATCH 02/18] migration: stop compression to allocate and free memory frequently
26ba25
Bugzilla: 1594384
26ba25
RH-Acked-by: Peter Xu <peterx@redhat.com>
26ba25
RH-Acked-by: John Snow <jsnow@redhat.com>
26ba25
RH-Acked-by: Juan Quintela <quintela@redhat.com>
26ba25
26ba25
From: Xiao Guangrong <xiaoguangrong@tencent.com>
26ba25
26ba25
Current code uses compress2() to compress memory which manages memory
26ba25
internally, that causes huge memory is allocated and freed very
26ba25
frequently
26ba25
26ba25
More worse, frequently returning memory to kernel will flush TLBs
26ba25
and trigger invalidation callbacks on mmu-notification which
26ba25
interacts with KVM MMU, that dramatically reduce the performance
26ba25
of VM
26ba25
26ba25
So, we maintain the memory by ourselves and reuse it for each
26ba25
compression
26ba25
26ba25
Reviewed-by: Peter Xu <peterx@redhat.com>
26ba25
Reviewed-by: Jiang Biao <jiang.biao2@zte.com.cn>
26ba25
Signed-off-by: Xiao Guangrong <xiaoguangrong@tencent.com>
26ba25
Message-Id: <20180330075128.26919-3-xiaoguangrong@tencent.com>
26ba25
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
26ba25
(cherry picked from commit dcaf446ebda5d87e05eb41cdbafb7ae4a7cc4a62)
26ba25
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
26ba25
---
26ba25
 migration/qemu-file.c | 39 ++++++++++++++++++++++++++++++++-------
26ba25
 migration/qemu-file.h |  6 ++++--
26ba25
 migration/ram.c       | 41 ++++++++++++++++++++++++++++++++---------
26ba25
 3 files changed, 68 insertions(+), 18 deletions(-)
26ba25
26ba25
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
26ba25
index bb63c77..bafe3a0 100644
26ba25
--- a/migration/qemu-file.c
26ba25
+++ b/migration/qemu-file.c
26ba25
@@ -658,8 +658,32 @@ uint64_t qemu_get_be64(QEMUFile *f)
26ba25
     return v;
26ba25
 }
26ba25
 
26ba25
-/* Compress size bytes of data start at p with specific compression
26ba25
- * level and store the compressed data to the buffer of f.
26ba25
+/* return the size after compression, or negative value on error */
26ba25
+static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len,
26ba25
+                              const uint8_t *source, size_t source_len)
26ba25
+{
26ba25
+    int err;
26ba25
+
26ba25
+    err = deflateReset(stream);
26ba25
+    if (err != Z_OK) {
26ba25
+        return -1;
26ba25
+    }
26ba25
+
26ba25
+    stream->avail_in = source_len;
26ba25
+    stream->next_in = (uint8_t *)source;
26ba25
+    stream->avail_out = dest_len;
26ba25
+    stream->next_out = dest;
26ba25
+
26ba25
+    err = deflate(stream, Z_FINISH);
26ba25
+    if (err != Z_STREAM_END) {
26ba25
+        return -1;
26ba25
+    }
26ba25
+
26ba25
+    return stream->next_out - dest;
26ba25
+}
26ba25
+
26ba25
+/* Compress size bytes of data start at p and store the compressed
26ba25
+ * data to the buffer of f.
26ba25
  *
26ba25
  * When f is not writable, return -1 if f has no space to save the
26ba25
  * compressed data.
26ba25
@@ -667,9 +691,8 @@ uint64_t qemu_get_be64(QEMUFile *f)
26ba25
  * do fflush first, if f still has no space to save the compressed
26ba25
  * data, return -1.
26ba25
  */
26ba25
-
26ba25
-ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
26ba25
-                                  int level)
26ba25
+ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream,
26ba25
+                                  const uint8_t *p, size_t size)
26ba25
 {
26ba25
     ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t);
26ba25
 
26ba25
@@ -683,8 +706,10 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
26ba25
             return -1;
26ba25
         }
26ba25
     }
26ba25
-    if (compress2(f->buf + f->buf_index + sizeof(int32_t), (uLongf *)&blen,
26ba25
-                  (Bytef *)p, size, level) != Z_OK) {
26ba25
+
26ba25
+    blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t),
26ba25
+                              blen, p, size);
26ba25
+    if (blen < 0) {
26ba25
         error_report("Compress Failed!");
26ba25
         return 0;
26ba25
     }
26ba25
diff --git a/migration/qemu-file.h b/migration/qemu-file.h
26ba25
index f4f356a..2ccfcfb 100644
26ba25
--- a/migration/qemu-file.h
26ba25
+++ b/migration/qemu-file.h
26ba25
@@ -25,6 +25,8 @@
26ba25
 #ifndef MIGRATION_QEMU_FILE_H
26ba25
 #define MIGRATION_QEMU_FILE_H
26ba25
 
26ba25
+#include <zlib.h>
26ba25
+
26ba25
 /* Read a chunk of data from a file at the given position.  The pos argument
26ba25
  * can be ignored if the file is only be used for streaming.  The number of
26ba25
  * bytes actually read should be returned.
26ba25
@@ -132,8 +134,8 @@ bool qemu_file_is_writable(QEMUFile *f);
26ba25
 
26ba25
 size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset);
26ba25
 size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size);
26ba25
-ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
26ba25
-                                  int level);
26ba25
+ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream,
26ba25
+                                  const uint8_t *p, size_t size);
26ba25
 int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src);
26ba25
 
26ba25
 /*
26ba25
diff --git a/migration/ram.c b/migration/ram.c
26ba25
index f27038a..7d3b1da 100644
26ba25
--- a/migration/ram.c
26ba25
+++ b/migration/ram.c
26ba25
@@ -269,6 +269,7 @@ struct CompressParam {
26ba25
     QemuCond cond;
26ba25
     RAMBlock *block;
26ba25
     ram_addr_t offset;
26ba25
+    z_stream stream;
26ba25
 };
26ba25
 typedef struct CompressParam CompressParam;
26ba25
 
26ba25
@@ -299,7 +300,7 @@ static QemuThread *decompress_threads;
26ba25
 static QemuMutex decomp_done_lock;
26ba25
 static QemuCond decomp_done_cond;
26ba25
 
26ba25
-static int do_compress_ram_page(QEMUFile *f, RAMBlock *block,
26ba25
+static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
26ba25
                                 ram_addr_t offset);
26ba25
 
26ba25
 static void *do_data_compress(void *opaque)
26ba25
@@ -316,7 +317,7 @@ static void *do_data_compress(void *opaque)
26ba25
             param->block = NULL;
26ba25
             qemu_mutex_unlock(&param->mutex);
26ba25
 
26ba25
-            do_compress_ram_page(param->file, block, offset);
26ba25
+            do_compress_ram_page(param->file, &param->stream, block, offset);
26ba25
 
26ba25
             qemu_mutex_lock(&comp_done_lock);
26ba25
             param->done = true;
26ba25
@@ -357,10 +358,19 @@ static void compress_threads_save_cleanup(void)
26ba25
     terminate_compression_threads();
26ba25
     thread_count = migrate_compress_threads();
26ba25
     for (i = 0; i < thread_count; i++) {
26ba25
+        /*
26ba25
+         * we use it as a indicator which shows if the thread is
26ba25
+         * properly init'd or not
26ba25
+         */
26ba25
+        if (!comp_param[i].file) {
26ba25
+            break;
26ba25
+        }
26ba25
         qemu_thread_join(compress_threads + i);
26ba25
-        qemu_fclose(comp_param[i].file);
26ba25
         qemu_mutex_destroy(&comp_param[i].mutex);
26ba25
         qemu_cond_destroy(&comp_param[i].cond);
26ba25
+        deflateEnd(&comp_param[i].stream);
26ba25
+        qemu_fclose(comp_param[i].file);
26ba25
+        comp_param[i].file = NULL;
26ba25
     }
26ba25
     qemu_mutex_destroy(&comp_done_lock);
26ba25
     qemu_cond_destroy(&comp_done_cond);
26ba25
@@ -370,12 +380,12 @@ static void compress_threads_save_cleanup(void)
26ba25
     comp_param = NULL;
26ba25
 }
26ba25
 
26ba25
-static void compress_threads_save_setup(void)
26ba25
+static int compress_threads_save_setup(void)
26ba25
 {
26ba25
     int i, thread_count;
26ba25
 
26ba25
     if (!migrate_use_compression()) {
26ba25
-        return;
26ba25
+        return 0;
26ba25
     }
26ba25
     thread_count = migrate_compress_threads();
26ba25
     compress_threads = g_new0(QemuThread, thread_count);
26ba25
@@ -383,6 +393,11 @@ static void compress_threads_save_setup(void)
26ba25
     qemu_cond_init(&comp_done_cond);
26ba25
     qemu_mutex_init(&comp_done_lock);
26ba25
     for (i = 0; i < thread_count; i++) {
26ba25
+        if (deflateInit(&comp_param[i].stream,
26ba25
+                        migrate_compress_level()) != Z_OK) {
26ba25
+            goto exit;
26ba25
+        }
26ba25
+
26ba25
         /* comp_param[i].file is just used as a dummy buffer to save data,
26ba25
          * set its ops to empty.
26ba25
          */
26ba25
@@ -395,6 +410,11 @@ static void compress_threads_save_setup(void)
26ba25
                            do_data_compress, comp_param + i,
26ba25
                            QEMU_THREAD_JOINABLE);
26ba25
     }
26ba25
+    return 0;
26ba25
+
26ba25
+exit:
26ba25
+    compress_threads_save_cleanup();
26ba25
+    return -1;
26ba25
 }
26ba25
 
26ba25
 /* Multiple fd's */
26ba25
@@ -1032,7 +1052,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage)
26ba25
     return pages;
26ba25
 }
26ba25
 
26ba25
-static int do_compress_ram_page(QEMUFile *f, RAMBlock *block,
26ba25
+static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block,
26ba25
                                 ram_addr_t offset)
26ba25
 {
26ba25
     RAMState *rs = ram_state;
26ba25
@@ -1041,8 +1061,7 @@ static int do_compress_ram_page(QEMUFile *f, RAMBlock *block,
26ba25
 
26ba25
     bytes_sent = save_page_header(rs, f, block, offset |
26ba25
                                   RAM_SAVE_FLAG_COMPRESS_PAGE);
26ba25
-    blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE,
26ba25
-                                     migrate_compress_level());
26ba25
+    blen = qemu_put_compression_data(f, stream, p, TARGET_PAGE_SIZE);
26ba25
     if (blen < 0) {
26ba25
         bytes_sent = 0;
26ba25
         qemu_file_set_error(migrate_get_current()->to_dst_file, blen);
26ba25
@@ -2215,9 +2234,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
26ba25
     RAMState **rsp = opaque;
26ba25
     RAMBlock *block;
26ba25
 
26ba25
+    if (compress_threads_save_setup()) {
26ba25
+        return -1;
26ba25
+    }
26ba25
+
26ba25
     /* migration has already setup the bitmap, reuse it. */
26ba25
     if (!migration_in_colo_state()) {
26ba25
         if (ram_init_all(rsp) != 0) {
26ba25
+            compress_threads_save_cleanup();
26ba25
             return -1;
26ba25
         }
26ba25
     }
26ba25
@@ -2237,7 +2261,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
26ba25
     }
26ba25
 
26ba25
     rcu_read_unlock();
26ba25
-    compress_threads_save_setup();
26ba25
 
26ba25
     ram_control_before_iterate(f, RAM_CONTROL_SETUP);
26ba25
     ram_control_after_iterate(f, RAM_CONTROL_SETUP);
26ba25
-- 
26ba25
1.8.3.1
26ba25