From 5f09f10d760b81c103861a10851a017f13b01ff3 Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Sat, 30 Jan 2021 14:57:54 +0800 Subject: [PATCH 1/9] migration: Add multi-thread compress method A multi-thread compress method parameter is added to hold the method we are going to use. By default the 'zlib' method is used to maintain the compatibility as before. Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang (cherry picked from commit 9b2dc3992aa6c6526852ad0c50d86f2034927a07) --- ...ion-Add-multi-thread-compress-method.patch | 365 ++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 migration-Add-multi-thread-compress-method.patch diff --git a/migration-Add-multi-thread-compress-method.patch b/migration-Add-multi-thread-compress-method.patch new file mode 100644 index 00000000..e900a729 --- /dev/null +++ b/migration-Add-multi-thread-compress-method.patch @@ -0,0 +1,365 @@ +From b0cabc67e16d9b4e1e749b0359dd8f3874e0968d Mon Sep 17 00:00:00 2001 +From: Zeyu Jin +Date: Sat, 30 Jan 2021 14:57:54 +0800 +Subject: [PATCH] migration: Add multi-thread compress method + +A multi-thread compress method parameter is added to hold the method we +are going to use. By default the 'zlib' method is used to maintain the +compatibility as before. + +Signed-off-by: Zeyu Jin +Signed-off-by: Ying Fang +--- + hw/core/qdev-prop-internal.h | 18 ++++++++++++++++++ + hw/core/qdev-properties-system.c | 13 +++++++++++++ + hw/core/qdev-properties.c | 14 +++++++++++--- + include/hw/qdev-properties.h | 4 ++++ + migration/migration.c | 15 +++++++++++++++ + migration/qemu-file.c | 9 +++++++++ + monitor/hmp-cmds.c | 13 +++++++++++++ + qapi/migration.json | 26 +++++++++++++++++++++++++- + 8 files changed, 108 insertions(+), 4 deletions(-) + create mode 100644 hw/core/qdev-prop-internal.h + +diff --git a/hw/core/qdev-prop-internal.h b/hw/core/qdev-prop-internal.h +new file mode 100644 +index 0000000000..a4a7eaf078 +--- /dev/null ++++ b/hw/core/qdev-prop-internal.h +@@ -0,0 +1,18 @@ ++/* ++ * qdev property parsing ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HW_CORE_QDEV_PROP_INTERNAL_H ++#define HW_CORE_QDEV_PROP_INTERNAL_H ++ ++void get_enum(Object *obj, Visitor *v, const char *name, void *opaque, ++ Error **errp); ++void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, ++ Error **errp); ++ ++void set_default_value_enum(Object *obj, const Property *prop); ++ ++#endif +diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c +index ba412dd2ca..67ed89b406 100644 +--- a/hw/core/qdev-properties-system.c ++++ b/hw/core/qdev-properties-system.c +@@ -15,6 +15,7 @@ + #include "hw/qdev.h" + #include "qapi/error.h" + #include "qapi/qmp/qerror.h" ++#include "qapi/qapi-types-migration.h" + #include "sysemu/block-backend.h" + #include "sysemu/blockdev.h" + #include "hw/block/block.h" +@@ -23,6 +24,7 @@ + #include "chardev/char-fe.h" + #include "sysemu/iothread.h" + #include "sysemu/tpm_backend.h" ++#include "qdev-prop-internal.h" + + static void get_pointer(Object *obj, Visitor *v, Property *prop, + char *(*print)(void *ptr), +@@ -399,3 +401,14 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) + } + nd->instantiated = 1; + } ++ ++/* --- CompressMethod --- */ ++const PropertyInfo qdev_prop_compress_method = { ++ .name = "CompressMethod", ++ .description = "multi-thread compression method, " ++ "zlib", ++ .enum_table = &CompressMethod_lookup, ++ .get = get_enum, ++ .set = set_enum, ++ .set_default_value = set_default_value_enum, ++}; +diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c +index 81c97f48a7..709f9e0f9d 100644 +--- a/hw/core/qdev-properties.c ++++ b/hw/core/qdev-properties.c +@@ -11,6 +11,7 @@ + #include "qapi/visitor.h" + #include "chardev/char.h" + #include "qemu/uuid.h" ++#include "qdev-prop-internal.h" + + void qdev_prop_set_after_realize(DeviceState *dev, const char *name, + Error **errp) +@@ -46,7 +47,7 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) + return ptr; + } + +-static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque, ++void get_enum(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) + { + DeviceState *dev = DEVICE(obj); +@@ -56,7 +57,7 @@ static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque, + visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); + } + +-static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, ++void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) + { + DeviceState *dev = DEVICE(obj); +@@ -71,7 +72,7 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, + visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); + } + +-static void set_default_value_enum(Object *obj, const Property *prop) ++void set_default_value_enum(Object *obj, const Property *prop) + { + object_property_set_str(obj, + qapi_enum_lookup(prop->info->enum_table, +@@ -79,6 +80,13 @@ static void set_default_value_enum(Object *obj, const Property *prop) + prop->name, &error_abort); + } + ++const PropertyInfo qdev_prop_enum = { ++ .name = "enum", ++ .get = get_enum, ++ .set = set_enum, ++ .set_default_value = set_default_value_enum, ++}; ++ + /* Bit */ + + static uint32_t qdev_get_prop_mask(Property *prop) +diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h +index 1eae5ab056..a22a532eb8 100644 +--- a/include/hw/qdev-properties.h ++++ b/include/hw/qdev-properties.h +@@ -23,6 +23,7 @@ extern const PropertyInfo qdev_prop_tpm; + extern const PropertyInfo qdev_prop_ptr; + extern const PropertyInfo qdev_prop_macaddr; + extern const PropertyInfo qdev_prop_on_off_auto; ++extern const PropertyInfo qdev_prop_compress_method; + extern const PropertyInfo qdev_prop_losttickpolicy; + extern const PropertyInfo qdev_prop_blockdev_on_error; + extern const PropertyInfo qdev_prop_bios_chs_trans; +@@ -205,6 +206,9 @@ extern const PropertyInfo qdev_prop_pcie_link_width; + DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) + #define DEFINE_PROP_ON_OFF_AUTO(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_on_off_auto, OnOffAuto) ++#define DEFINE_PROP_COMPRESS_METHOD(_n, _s, _f, _d) \ ++ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_compress_method, \ ++ CompressMethod) + #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ + LostTickPolicy) +diff --git a/migration/migration.c b/migration/migration.c +index 0e396f22b4..c79bf09269 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -71,6 +71,7 @@ + #define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 + /*0: means nocompress, 1: best speed, ... 9: best compress ratio */ + #define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 ++#define DEFAULT_MIGRATE_COMPRESS_METHOD COMPRESS_METHOD_ZLIB + /* Define default autoconverge cpu throttle migration parameters */ + #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 + #define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10 +@@ -748,6 +749,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) + params->compress_wait_thread = s->parameters.compress_wait_thread; + params->has_decompress_threads = true; + params->decompress_threads = s->parameters.decompress_threads; ++ params->has_compress_method = true; ++ params->compress_method = s->parameters.compress_method; + params->has_cpu_throttle_initial = true; + params->cpu_throttle_initial = s->parameters.cpu_throttle_initial; + params->has_cpu_throttle_increment = true; +@@ -1250,6 +1253,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, + dest->decompress_threads = params->decompress_threads; + } + ++ if (params->has_compress_method) { ++ dest->compress_method = params->compress_method; ++ } ++ + if (params->has_cpu_throttle_initial) { + dest->cpu_throttle_initial = params->cpu_throttle_initial; + } +@@ -1331,6 +1338,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) + s->parameters.decompress_threads = params->decompress_threads; + } + ++ if (params->has_compress_method) { ++ s->parameters.compress_method = params->compress_method; ++ } ++ + if (params->has_cpu_throttle_initial) { + s->parameters.cpu_throttle_initial = params->cpu_throttle_initial; + } +@@ -3436,6 +3447,9 @@ static Property migration_properties[] = { + DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, + parameters.decompress_threads, + DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), ++ DEFINE_PROP_COMPRESS_METHOD("compress-method", MigrationState, ++ parameters.compress_method, ++ DEFAULT_MIGRATE_COMPRESS_METHOD), + DEFINE_PROP_UINT8("x-cpu-throttle-initial", MigrationState, + parameters.cpu_throttle_initial, + DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL), +@@ -3535,6 +3549,7 @@ static void migration_instance_init(Object *obj) + params->has_compress_level = true; + params->has_compress_threads = true; + params->has_decompress_threads = true; ++ params->has_compress_method = true; + params->has_cpu_throttle_initial = true; + params->has_cpu_throttle_increment = true; + params->has_max_bandwidth = true; +diff --git a/migration/qemu-file.c b/migration/qemu-file.c +index cd96d04e9a..be0d6c8ca8 100644 +--- a/migration/qemu-file.c ++++ b/migration/qemu-file.c +@@ -382,6 +382,15 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size, + } + } + ++static void add_buf_to_iovec(QEMUFile *f, size_t len) ++{ ++ add_to_iovec(f, f->buf + f->buf_index, len, false); ++ f->buf_index += len; ++ if (f->buf_index == IO_BUF_SIZE) { ++ qemu_fflush(f); ++ } ++} ++ + void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, + bool may_free) + { +diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c +index fc5d6b92c4..e5a7a88ba2 100644 +--- a/monitor/hmp-cmds.c ++++ b/monitor/hmp-cmds.c +@@ -41,6 +41,7 @@ + #include "qapi/qapi-commands-tpm.h" + #include "qapi/qapi-commands-ui.h" + #include "qapi/qapi-visit-net.h" ++#include "qapi/qapi-visit-migration.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qerror.h" + #include "qapi/string-input-visitor.h" +@@ -426,6 +427,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) + MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), + params->decompress_threads); + assert(params->has_cpu_throttle_initial); ++ monitor_printf(mon, "%s: %s\n", ++ MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_METHOD), ++ CompressMethod_str(params->compress_method)); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), + params->cpu_throttle_initial); +@@ -1756,6 +1760,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) + MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); + uint64_t valuebw = 0; + uint64_t cache_size; ++ CompressMethod compress_method; + Error *err = NULL; + int val, ret; + +@@ -1781,6 +1786,14 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) + p->has_decompress_threads = true; + visit_type_int(v, param, &p->decompress_threads, &err); + break; ++ case MIGRATION_PARAMETER_COMPRESS_METHOD: ++ p->has_compress_method = true; ++ visit_type_CompressMethod(v, param, &compress_method, &err); ++ if (err) { ++ break; ++ } ++ p->compress_method = compress_method; ++ break; + case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: + p->has_cpu_throttle_initial = true; + visit_type_int(v, param, &p->cpu_throttle_initial, &err); +diff --git a/qapi/migration.json b/qapi/migration.json +index 6844ddfab3..b0e8c493ee 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -482,6 +482,19 @@ + ## + { 'command': 'query-migrate-capabilities', 'returns': ['MigrationCapabilityStatus']} + ++## ++# @CompressMethod: ++# ++# An enumeration of multi-thread compression methods. ++# ++# @zlib: use zlib compression method. ++# ++# Since: 5.0 ++# ++## ++{ 'enum': 'CompressMethod', ++ 'data': [ 'zlib' ] } ++ + ## + # @MigrationParameter: + # +@@ -518,6 +531,9 @@ + # compression, so set the decompress-threads to the number about 1/4 + # of compress-threads is adequate. + # ++# @compress-method: Which multi-thread compression method to use. ++# Defaults to none. (Since 5.0) ++# + # @cpu-throttle-initial: Initial percentage of time guest cpus are throttled + # when migration auto-converge is activated. The + # default value is 20. (Since 2.7) +@@ -586,7 +602,7 @@ + 'data': ['announce-initial', 'announce-max', + 'announce-rounds', 'announce-step', + 'compress-level', 'compress-threads', 'decompress-threads', +- 'compress-wait-thread', ++ 'compress-wait-thread', 'compress-method', + 'cpu-throttle-initial', 'cpu-throttle-increment', + 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', + 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', +@@ -620,6 +636,9 @@ + # + # @decompress-threads: decompression thread count + # ++# @compress-method: Set compression method to use in multi-thread compression. ++# Defaults to none. (Since 5.0) ++# + # @cpu-throttle-initial: Initial percentage of time guest cpus are + # throttled when migration auto-converge is activated. + # The default value is 20. (Since 2.7) +@@ -695,6 +714,7 @@ + '*compress-threads': 'int', + '*compress-wait-thread': 'bool', + '*decompress-threads': 'int', ++ '*compress-method': 'CompressMethod', + '*cpu-throttle-initial': 'int', + '*cpu-throttle-increment': 'int', + '*tls-creds': 'StrOrNull', +@@ -753,6 +773,9 @@ + # + # @decompress-threads: decompression thread count + # ++# @compress-method: Which multi-thread compression method to use. ++# Defaults to none. (Since 5.0) ++# + # @cpu-throttle-initial: Initial percentage of time guest cpus are + # throttled when migration auto-converge is activated. + # (Since 2.7) +@@ -828,6 +851,7 @@ + '*compress-threads': 'uint8', + '*compress-wait-thread': 'bool', + '*decompress-threads': 'uint8', ++ '*compress-method': 'CompressMethod', + '*cpu-throttle-initial': 'uint8', + '*cpu-throttle-increment': 'uint8', + '*tls-creds': 'str', +-- +2.27.0 + -- Gitee From 93511d92a67675b727798347e71da075c9b301ca Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Sat, 30 Jan 2021 15:21:17 +0800 Subject: [PATCH 2/9] migration: Refactoring multi-thread compress migration Code refactor for the compression procedure which includes: 1. Move qemu_compress_data and qemu_put_compression_data from qemu-file.c to ram.c, for the reason that most part of the code logical has nothing to do with qemu-file. Besides, the decompression code is located at ram.c only. 2. Simplify the function input arguments for compression and decompression. Wrap the input into the param structure which already exists. This change also makes the function much more flexible for other compression methods. Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang (cherry picked from commit e5ce7b7f34495e80731b098946bad97ff8571313) --- ...oring-multi-thread-compress-migratio.patch | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 migration-Refactoring-multi-thread-compress-migratio.patch diff --git a/migration-Refactoring-multi-thread-compress-migratio.patch b/migration-Refactoring-multi-thread-compress-migratio.patch new file mode 100644 index 00000000..d3ab4d0d --- /dev/null +++ b/migration-Refactoring-multi-thread-compress-migratio.patch @@ -0,0 +1,305 @@ +From 524d8cee48006918cf181f2817e4ec3ce5a3bb12 Mon Sep 17 00:00:00 2001 +From: Zeyu Jin +Date: Sat, 30 Jan 2021 15:21:17 +0800 +Subject: [PATCH] migration: Refactoring multi-thread compress migration + +Code refactor for the compression procedure which includes: + +1. Move qemu_compress_data and qemu_put_compression_data from qemu-file.c to +ram.c, for the reason that most part of the code logical has nothing to do +with qemu-file. Besides, the decompression code is located at ram.c only. + +2. Simplify the function input arguments for compression and decompression. +Wrap the input into the param structure which already exists. This change also +makes the function much more flexible for other compression methods. + +Signed-off-by: Zeyu Jin +Signed-off-by: Ying Fang +--- + migration/qemu-file.c | 78 ++++++--------------------------------- + migration/qemu-file.h | 4 +- + migration/ram.c | 85 +++++++++++++++++++++++++++++++------------ + 3 files changed, 75 insertions(+), 92 deletions(-) + +diff --git a/migration/qemu-file.c b/migration/qemu-file.c +index be0d6c8ca8..3bba694ed4 100644 +--- a/migration/qemu-file.c ++++ b/migration/qemu-file.c +@@ -695,72 +695,6 @@ uint64_t qemu_get_be64(QEMUFile *f) + return v; + } + +-/* return the size after compression, or negative value on error */ +-static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, +- const uint8_t *source, size_t source_len) +-{ +- int err; +- +- err = deflateReset(stream); +- if (err != Z_OK) { +- return -1; +- } +- +- stream->avail_in = source_len; +- stream->next_in = (uint8_t *)source; +- stream->avail_out = dest_len; +- stream->next_out = dest; +- +- err = deflate(stream, Z_FINISH); +- if (err != Z_STREAM_END) { +- return -1; +- } +- +- return stream->next_out - dest; +-} +- +-/* Compress size bytes of data start at p and store the compressed +- * data to the buffer of f. +- * +- * When f is not writable, return -1 if f has no space to save the +- * compressed data. +- * When f is wirtable and it has no space to save the compressed data, +- * do fflush first, if f still has no space to save the compressed +- * data, return -1. +- */ +-ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, +- const uint8_t *p, size_t size) +-{ +- ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); +- +- if (blen < compressBound(size)) { +- if (!qemu_file_is_writable(f)) { +- return -1; +- } +- qemu_fflush(f); +- blen = IO_BUF_SIZE - sizeof(int32_t); +- if (blen < compressBound(size)) { +- return -1; +- } +- } +- +- blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), +- blen, p, size); +- if (blen < 0) { +- return -1; +- } +- +- qemu_put_be32(f, blen); +- if (f->ops->writev_buffer) { +- add_to_iovec(f, f->buf + f->buf_index, blen, false); +- } +- f->buf_index += blen; +- if (f->buf_index == IO_BUF_SIZE) { +- qemu_fflush(f); +- } +- return blen + sizeof(int32_t); +-} +- + /* Put the data in the buffer of f_src to the buffer of f_des, and + * then reset the buf_index of f_src to 0. + */ +@@ -820,3 +754,15 @@ void qemu_file_set_blocking(QEMUFile *f, bool block) + f->ops->set_blocking(f->opaque, block); + } + } ++ ++ssize_t qemu_put_compress_start(QEMUFile *f, uint8_t **dest_ptr) ++{ ++ *dest_ptr = f->buf + f->buf_index + sizeof(int32_t); ++ return IO_BUF_SIZE - f->buf_index - sizeof(int32_t); ++} ++ ++void qemu_put_compress_end(QEMUFile *f, unsigned int v) ++{ ++ qemu_put_be32(f, v); ++ add_buf_to_iovec(f, v); ++} +diff --git a/migration/qemu-file.h b/migration/qemu-file.h +index 5de9fa2e96..6570e53e13 100644 +--- a/migration/qemu-file.h ++++ b/migration/qemu-file.h +@@ -134,8 +134,6 @@ bool qemu_file_is_writable(QEMUFile *f); + + size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); + size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); +-ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, +- const uint8_t *p, size_t size); + int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); + + /* +@@ -162,6 +160,8 @@ void ram_control_before_iterate(QEMUFile *f, uint64_t flags); + void ram_control_after_iterate(QEMUFile *f, uint64_t flags); + void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data); + ++ssize_t qemu_put_compress_start(QEMUFile *f, uint8_t **dest_ptr); ++void qemu_put_compress_end(QEMUFile *f, unsigned int v); + /* Whenever this is found in the data stream, the flags + * will be passed to ram_control_load_hook in the incoming-migration + * side. This lets before_ram_iterate/after_ram_iterate add +diff --git a/migration/ram.c b/migration/ram.c +index 92ce1a53e7..f78a681ca2 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -449,26 +449,22 @@ static QemuThread *decompress_threads; + static QemuMutex decomp_done_lock; + static QemuCond decomp_done_cond; + +-static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, +- ram_addr_t offset, uint8_t *source_buf); ++static bool do_compress_ram_page(CompressParam *param, RAMBlock *block); + + static void *do_data_compress(void *opaque) + { + CompressParam *param = opaque; + RAMBlock *block; +- ram_addr_t offset; + bool zero_page; + + qemu_mutex_lock(¶m->mutex); + while (!param->quit) { + if (param->block) { + block = param->block; +- offset = param->offset; + param->block = NULL; + qemu_mutex_unlock(¶m->mutex); + +- zero_page = do_compress_ram_page(param->file, ¶m->stream, +- block, offset, param->originbuf); ++ zero_page = do_compress_ram_page(param, block); + + qemu_mutex_lock(&comp_done_lock); + param->done = true; +@@ -2212,28 +2208,73 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, + return 1; + } + +-static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, +- ram_addr_t offset, uint8_t *source_buf) ++/* ++ * Compress size bytes of data start at p and store the compressed ++ * data to the buffer of f. ++ * ++ * Since the file is dummy file with empty_ops, return -1 if f has no space to ++ * save the compressed data. ++ */ ++static ssize_t qemu_put_compression_data(CompressParam *param, size_t size) ++{ ++ int err; ++ uint8_t *dest = NULL; ++ z_stream *stream = ¶m->stream; ++ uint8_t *p = param->originbuf; ++ QEMUFile *f = f = param->file; ++ ssize_t blen = qemu_put_compress_start(f, &dest); ++ ++ if (blen < compressBound(size)) { ++ return -1; ++ } ++ ++ err = deflateReset(stream); ++ if (err != Z_OK) { ++ return -1; ++ } ++ ++ stream->avail_in = size; ++ stream->next_in = p; ++ stream->avail_out = blen; ++ stream->next_out = dest; ++ ++ err = deflate(stream, Z_FINISH); ++ if (err != Z_STREAM_END) { ++ return -1; ++ } ++ ++ blen = stream->next_out - dest; ++ if (blen < 0) { ++ return -1; ++ } ++ ++ qemu_put_compress_end(f, blen); ++ return blen + sizeof(int32_t); ++} ++ ++static bool do_compress_ram_page(CompressParam *param, RAMBlock *block) + { + RAMState *rs = ram_state; ++ ram_addr_t offset = param->offset; + uint8_t *p = block->host + (offset & TARGET_PAGE_MASK); + bool zero_page = false; + int ret; + +- if (save_zero_page_to_file(rs, f, block, offset)) { ++ if (save_zero_page_to_file(rs, param->file, block, offset)) { + zero_page = true; + goto exit; + } + +- save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); ++ save_page_header(rs, param->file, block, ++ offset | RAM_SAVE_FLAG_COMPRESS_PAGE); + + /* + * copy it to a internal buffer to avoid it being modified by VM + * so that we can catch up the error during compression and + * decompression + */ +- memcpy(source_buf, p, TARGET_PAGE_SIZE); +- ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); ++ memcpy(param->originbuf, p, TARGET_PAGE_SIZE); ++ ret = qemu_put_compression_data(param, TARGET_PAGE_SIZE); + if (ret < 0) { + qemu_file_set_error(migrate_get_current()->to_dst_file, ret); + error_report("compressed data failed!"); +@@ -3926,19 +3967,20 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) + + /* return the size after decompression, or negative value on error */ + static int +-qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, +- const uint8_t *source, size_t source_len) ++qemu_uncompress_data(DecompressParam *param, uint8_t *dest, size_t pagesize) + { + int err; + ++ z_stream *stream = ¶m->stream; ++ + err = inflateReset(stream); + if (err != Z_OK) { + return -1; + } + +- stream->avail_in = source_len; +- stream->next_in = (uint8_t *)source; +- stream->avail_out = dest_len; ++ stream->avail_in = param->len; ++ stream->next_in = param->compbuf; ++ stream->avail_out = pagesize; + stream->next_out = dest; + + err = inflate(stream, Z_NO_FLUSH); +@@ -3952,22 +3994,17 @@ qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, + static void *do_data_decompress(void *opaque) + { + DecompressParam *param = opaque; +- unsigned long pagesize; + uint8_t *des; +- int len, ret; ++ int ret; + + qemu_mutex_lock(¶m->mutex); + while (!param->quit) { + if (param->des) { + des = param->des; +- len = param->len; + param->des = 0; + qemu_mutex_unlock(¶m->mutex); + +- pagesize = TARGET_PAGE_SIZE; +- +- ret = qemu_uncompress_data(¶m->stream, des, pagesize, +- param->compbuf, len); ++ ret = qemu_uncompress_data(param, des, TARGET_PAGE_SIZE); + if (ret < 0 && migrate_get_current()->decompress_error_check) { + error_report("decompress data failed"); + qemu_file_set_error(decomp_file, ret); +-- +2.27.0 + -- Gitee From 62e03aa7d974c43063eec75a8c84849653968de3 Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Sat, 30 Jan 2021 15:57:31 +0800 Subject: [PATCH 3/9] migration: Add multi-thread compress ops Add the MigrationCompressOps and MigrationDecompressOps structures to make the compression method configurable for multi-thread compression migration. Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang (cherry picked from commit 70bc9a5a345334f626b23cb6add3fac65507911a) --- migration-Add-multi-thread-compress-ops.patch | 442 ++++++++++++++++++ 1 file changed, 442 insertions(+) create mode 100644 migration-Add-multi-thread-compress-ops.patch diff --git a/migration-Add-multi-thread-compress-ops.patch b/migration-Add-multi-thread-compress-ops.patch new file mode 100644 index 00000000..043d9f9b --- /dev/null +++ b/migration-Add-multi-thread-compress-ops.patch @@ -0,0 +1,442 @@ +From 99fddf2ffeefc99ab15b3428dbd2b46476be3e7e Mon Sep 17 00:00:00 2001 +From: Zeyu Jin +Date: Sat, 30 Jan 2021 15:57:31 +0800 +Subject: [PATCH] migration: Add multi-thread compress ops + +Add the MigrationCompressOps and MigrationDecompressOps structures to make +the compression method configurable for multi-thread compression migration. + +Signed-off-by: Zeyu Jin +Signed-off-by: Ying Fang +--- + migration/migration.c | 9 ++ + migration/migration.h | 1 + + migration/ram.c | 269 ++++++++++++++++++++++++++++++------------ + 3 files changed, 201 insertions(+), 78 deletions(-) + +diff --git a/migration/migration.c b/migration/migration.c +index c79bf09269..67425fde7a 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -2143,6 +2143,15 @@ int migrate_decompress_threads(void) + return s->parameters.decompress_threads; + } + ++CompressMethod migrate_compress_method(void) ++{ ++ MigrationState *s; ++ ++ s = migrate_get_current(); ++ ++ return s->parameters.compress_method; ++} ++ + bool migrate_dirty_bitmaps(void) + { + MigrationState *s; +diff --git a/migration/migration.h b/migration/migration.h +index f2bd4ebe33..4aa72297fc 100644 +--- a/migration/migration.h ++++ b/migration/migration.h +@@ -319,6 +319,7 @@ int migrate_compress_level(void); + int migrate_compress_threads(void); + int migrate_compress_wait_thread(void); + int migrate_decompress_threads(void); ++CompressMethod migrate_compress_method(void); + bool migrate_use_events(void); + bool migrate_postcopy_blocktime(void); + +diff --git a/migration/ram.c b/migration/ram.c +index f78a681ca2..3ed808a4ca 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -417,6 +417,9 @@ struct CompressParam { + /* internally used fields */ + z_stream stream; + uint8_t *originbuf; ++ ++ /* for zlib compression */ ++ z_stream stream; + }; + typedef struct CompressParam CompressParam; + +@@ -428,12 +431,29 @@ struct DecompressParam { + void *des; + uint8_t *compbuf; + int len; ++ ++ /* for zlib compression */ + z_stream stream; + }; + typedef struct DecompressParam DecompressParam; + ++typedef struct { ++ int (*save_setup)(CompressParam *param); ++ void (*save_cleanup)(CompressParam *param); ++ ssize_t (*compress_data)(CompressParam *param, size_t size); ++} MigrationCompressOps; ++ ++typedef struct { ++ int (*load_setup)(DecompressParam *param); ++ void (*load_cleanup)(DecompressParam *param); ++ int (*decompress_data)(DecompressParam *param, uint8_t *dest, size_t size); ++ int (*check_len)(int len); ++} MigrationDecompressOps; ++ + static CompressParam *comp_param; + static QemuThread *compress_threads; ++static MigrationCompressOps *compress_ops; ++static MigrationDecompressOps *decompress_ops; + /* comp_done_cond is used to wake up the migration thread when + * one of the compression threads has finished the compression. + * comp_done_lock is used to co-work with comp_done_cond. +@@ -451,6 +471,157 @@ static QemuCond decomp_done_cond; + + static bool do_compress_ram_page(CompressParam *param, RAMBlock *block); + ++static int zlib_save_setup(CompressParam *param) ++{ ++ if (deflateInit(¶m->stream, ++ migrate_compress_level()) != Z_OK) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static ssize_t zlib_compress_data(CompressParam *param, size_t size) ++ ++ int err; ++ uint8_t *dest = NULL; ++ z_stream *stream = ¶m->stream; ++ uint8_t *p = param->originbuf; ++ QEMUFile *f = f = param->file; ++ ssize_t blen = qemu_put_compress_start(f, &dest); ++ ++ if (blen < compressBound(size)) { ++ return -1; ++ } ++ ++ err = deflateReset(stream); ++ if (err != Z_OK) { ++ return -1; ++ } ++ ++ stream->avail_in = size; ++ stream->next_in = p; ++ stream->avail_out = blen; ++ stream->next_out = dest; ++ ++ err = deflate(stream, Z_FINISH); ++ if (err != Z_STREAM_END) { ++ return -1; ++ } ++ ++ blen = stream->next_out - dest; ++ if (blen < 0) { ++ return -1; ++ } ++ ++ qemu_put_compress_end(f, blen); ++ return blen + sizeof(int32_t); ++} ++ ++static void zlib_save_cleanup(CompressParam *param) ++{ ++ deflateEnd(¶m->stream); ++} ++ ++static int zlib_load_setup(DecompressParam *param) ++{ ++ if (inflateInit(¶m->stream) != Z_OK) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++zlib_decompress_data(DecompressParam *param, uint8_t *dest, size_t size) ++{ ++ int err; ++ ++ z_stream *stream = ¶m->stream; ++ ++ err = inflateReset(stream); ++ if (err != Z_OK) { ++ return -1; ++ } ++ ++ stream->avail_in = param->len; ++ stream->next_in = param->compbuf; ++ stream->avail_out = size; ++ stream->next_out = dest; ++ ++ err = inflate(stream, Z_NO_FLUSH); ++ if (err != Z_STREAM_END) { ++ return -1; ++ } ++ ++ return stream->total_out; ++} ++ ++static void zlib_load_cleanup(DecompressParam *param) ++{ ++ inflateEnd(¶m->stream); ++} ++ ++static int zlib_check_len(int len) ++{ ++ return len < 0 || len > compressBound(TARGET_PAGE_SIZE); ++} ++ ++static int set_compress_ops(void) ++{ ++ compress_ops = g_new0(MigrationCompressOps, 1); ++ ++ switch (migrate_compress_method()) { ++ case COMPRESS_METHOD_ZLIB: ++ compress_ops->save_setup = zlib_save_setup; ++ compress_ops->save_cleanup = zlib_save_cleanup; ++ compress_ops->compress_data = zlib_compress_data; ++ break; ++ default: ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int set_decompress_ops(void) ++{ ++ decompress_ops = g_new0(MigrationDecompressOps, 1); ++ ++ switch (migrate_compress_method()) { ++ case COMPRESS_METHOD_ZLIB: ++ decompress_ops->load_setup = zlib_load_setup; ++ decompress_ops->load_cleanup = zlib_load_cleanup; ++ decompress_ops->decompress_data = zlib_decompress_data; ++ decompress_ops->check_len = zlib_check_len; ++ break; ++ default: ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void clean_compress_ops(void) ++{ ++ compress_ops->save_setup = NULL; ++ compress_ops->save_cleanup = NULL; ++ compress_ops->compress_data = NULL; ++ ++ g_free(compress_ops); ++ compress_ops = NULL; ++} ++ ++static void clean_decompress_ops(void) ++{ ++ decompress_ops->load_setup = NULL; ++ decompress_ops->load_cleanup = NULL; ++ decompress_ops->decompress_data = NULL; ++ ++ g_free(decompress_ops); ++ decompress_ops = NULL; ++} ++ + static void *do_data_compress(void *opaque) + { + CompressParam *param = opaque; +@@ -508,7 +679,7 @@ static void compress_threads_save_cleanup(void) + qemu_thread_join(compress_threads + i); + qemu_mutex_destroy(&comp_param[i].mutex); + qemu_cond_destroy(&comp_param[i].cond); +- deflateEnd(&comp_param[i].stream); ++ compress_ops->save_cleanup(&comp_param[i]); + g_free(comp_param[i].originbuf); + qemu_fclose(comp_param[i].file); + comp_param[i].file = NULL; +@@ -519,6 +690,7 @@ static void compress_threads_save_cleanup(void) + g_free(comp_param); + compress_threads = NULL; + comp_param = NULL; ++ clean_compress_ops(); + } + + static int compress_threads_save_setup(void) +@@ -528,6 +700,12 @@ static int compress_threads_save_setup(void) + if (!migrate_use_compression()) { + return 0; + } ++ ++ if (set_compress_ops() < 0) { ++ clean_compress_ops(); ++ return -1; ++ } ++ + thread_count = migrate_compress_threads(); + compress_threads = g_new0(QemuThread, thread_count); + comp_param = g_new0(CompressParam, thread_count); +@@ -539,8 +717,7 @@ static int compress_threads_save_setup(void) + goto exit; + } + +- if (deflateInit(&comp_param[i].stream, +- migrate_compress_level()) != Z_OK) { ++ if (compress_ops->save_setup(&comp_param[i]) < 0) { + g_free(comp_param[i].originbuf); + goto exit; + } +@@ -2208,50 +2385,6 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, + return 1; + } + +-/* +- * Compress size bytes of data start at p and store the compressed +- * data to the buffer of f. +- * +- * Since the file is dummy file with empty_ops, return -1 if f has no space to +- * save the compressed data. +- */ +-static ssize_t qemu_put_compression_data(CompressParam *param, size_t size) +-{ +- int err; +- uint8_t *dest = NULL; +- z_stream *stream = ¶m->stream; +- uint8_t *p = param->originbuf; +- QEMUFile *f = f = param->file; +- ssize_t blen = qemu_put_compress_start(f, &dest); +- +- if (blen < compressBound(size)) { +- return -1; +- } +- +- err = deflateReset(stream); +- if (err != Z_OK) { +- return -1; +- } +- +- stream->avail_in = size; +- stream->next_in = p; +- stream->avail_out = blen; +- stream->next_out = dest; +- +- err = deflate(stream, Z_FINISH); +- if (err != Z_STREAM_END) { +- return -1; +- } +- +- blen = stream->next_out - dest; +- if (blen < 0) { +- return -1; +- } +- +- qemu_put_compress_end(f, blen); +- return blen + sizeof(int32_t); +-} +- + static bool do_compress_ram_page(CompressParam *param, RAMBlock *block) + { + RAMState *rs = ram_state; +@@ -2274,7 +2407,7 @@ static bool do_compress_ram_page(CompressParam *param, RAMBlock *block) + * decompression + */ + memcpy(param->originbuf, p, TARGET_PAGE_SIZE); +- ret = qemu_put_compression_data(param, TARGET_PAGE_SIZE); ++ ret = compress_ops->compress_data(param, TARGET_PAGE_SIZE); + if (ret < 0) { + qemu_file_set_error(migrate_get_current()->to_dst_file, ret); + error_report("compressed data failed!"); +@@ -3965,32 +4098,6 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) + } + } + +-/* return the size after decompression, or negative value on error */ +-static int +-qemu_uncompress_data(DecompressParam *param, uint8_t *dest, size_t pagesize) +-{ +- int err; +- +- z_stream *stream = ¶m->stream; +- +- err = inflateReset(stream); +- if (err != Z_OK) { +- return -1; +- } +- +- stream->avail_in = param->len; +- stream->next_in = param->compbuf; +- stream->avail_out = pagesize; +- stream->next_out = dest; +- +- err = inflate(stream, Z_NO_FLUSH); +- if (err != Z_STREAM_END) { +- return -1; +- } +- +- return stream->total_out; +-} +- + static void *do_data_decompress(void *opaque) + { + DecompressParam *param = opaque; +@@ -4004,7 +4111,7 @@ static void *do_data_decompress(void *opaque) + param->des = 0; + qemu_mutex_unlock(¶m->mutex); + +- ret = qemu_uncompress_data(param, des, TARGET_PAGE_SIZE); ++ ret = decompress_ops->decompress_data(param, des, TARGET_PAGE_SIZE); + if (ret < 0 && migrate_get_current()->decompress_error_check) { + error_report("decompress data failed"); + qemu_file_set_error(decomp_file, ret); +@@ -4074,7 +4181,7 @@ static void compress_threads_load_cleanup(void) + qemu_thread_join(decompress_threads + i); + qemu_mutex_destroy(&decomp_param[i].mutex); + qemu_cond_destroy(&decomp_param[i].cond); +- inflateEnd(&decomp_param[i].stream); ++ decompress_ops->load_cleanup(&decomp_param[i]); + g_free(decomp_param[i].compbuf); + decomp_param[i].compbuf = NULL; + } +@@ -4083,6 +4190,7 @@ static void compress_threads_load_cleanup(void) + decompress_threads = NULL; + decomp_param = NULL; + decomp_file = NULL; ++ clean_decompress_ops(); + } + + static int compress_threads_load_setup(QEMUFile *f) +@@ -4093,6 +4201,11 @@ static int compress_threads_load_setup(QEMUFile *f) + return 0; + } + ++ if (set_decompress_ops() < 0) { ++ clean_decompress_ops(); ++ return -1; ++ } ++ + thread_count = migrate_decompress_threads(); + decompress_threads = g_new0(QemuThread, thread_count); + decomp_param = g_new0(DecompressParam, thread_count); +@@ -4100,7 +4213,7 @@ static int compress_threads_load_setup(QEMUFile *f) + qemu_cond_init(&decomp_done_cond); + decomp_file = f; + for (i = 0; i < thread_count; i++) { +- if (inflateInit(&decomp_param[i].stream) != Z_OK) { ++ if (decompress_ops->load_setup(&decomp_param[i]) < 0) { + goto exit; + } + +@@ -4642,7 +4755,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) + + case RAM_SAVE_FLAG_COMPRESS_PAGE: + len = qemu_get_be32(f); +- if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { ++ if (decompress_ops->check_len(len)) { + error_report("Invalid compressed data length: %d", len); + ret = -EINVAL; + break; +-- +2.27.0 + -- Gitee From cfcc552f676143827ad9ff68905a6b87f0e5b550 Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Sat, 30 Jan 2021 16:15:10 +0800 Subject: [PATCH 4/9] migration: Add zstd support in multi-thread compression This patch enables zstd option in multi-thread compression. Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang (cherry picked from commit e865b3f1cf7db224d22dda57c5a7a97665c5d0fa) --- ...td-support-in-multi-thread-compressi.patch | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 migration-Add-zstd-support-in-multi-thread-compressi.patch diff --git a/migration-Add-zstd-support-in-multi-thread-compressi.patch b/migration-Add-zstd-support-in-multi-thread-compressi.patch new file mode 100644 index 00000000..a84bb368 --- /dev/null +++ b/migration-Add-zstd-support-in-multi-thread-compressi.patch @@ -0,0 +1,231 @@ +From 54a1b546e0bd0cc41669bf7ade806c6c777c96ad Mon Sep 17 00:00:00 2001 +From: Zeyu Jin +Date: Sat, 30 Jan 2021 16:15:10 +0800 +Subject: [PATCH] migration: Add zstd support in multi-thread compression + +This patch enables zstd option in multi-thread compression. + +Signed-off-by: Zeyu Jin +Signed-off-by: Ying Fang +--- + hw/core/qdev-properties-system.c | 2 +- + migration/ram.c | 130 ++++++++++++++++++++++++++++++- + qapi/migration.json | 2 +- + 3 files changed, 130 insertions(+), 4 deletions(-) + +diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c +index 67ed89b406..6d48903c87 100644 +--- a/hw/core/qdev-properties-system.c ++++ b/hw/core/qdev-properties-system.c +@@ -406,7 +406,7 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) + const PropertyInfo qdev_prop_compress_method = { + .name = "CompressMethod", + .description = "multi-thread compression method, " +- "zlib", ++ "zlib/zstd", + .enum_table = &CompressMethod_lookup, + .get = get_enum, + .set = set_enum, +diff --git a/migration/ram.c b/migration/ram.c +index 3ed808a4ca..ba1e729c39 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -59,6 +59,10 @@ + #include "savevm.h" + #include "qemu/iov.h" + ++#ifdef CONFIG_ZSTD ++#include ++#include ++#endif + /***********************************************************/ + /* ram save/restore */ + +@@ -415,11 +419,16 @@ struct CompressParam { + ram_addr_t offset; + + /* internally used fields */ +- z_stream stream; + uint8_t *originbuf; + + /* for zlib compression */ + z_stream stream; ++ ++#ifdef CONFIG_ZSTD ++ ZSTD_CStream *zstd_cs; ++ ZSTD_inBuffer in; ++ ZSTD_outBuffer out; ++#endif + }; + typedef struct CompressParam CompressParam; + +@@ -434,6 +443,11 @@ struct DecompressParam { + + /* for zlib compression */ + z_stream stream; ++#ifdef CONFIG_ZSTD ++ ZSTD_DStream *zstd_ds; ++ ZSTD_inBuffer in; ++ ZSTD_outBuffer out; ++#endif + }; + typedef struct DecompressParam DecompressParam; + +@@ -482,7 +496,7 @@ static int zlib_save_setup(CompressParam *param) + } + + static ssize_t zlib_compress_data(CompressParam *param, size_t size) +- ++{ + int err; + uint8_t *dest = NULL; + z_stream *stream = ¶m->stream; +@@ -567,6 +581,103 @@ static int zlib_check_len(int len) + return len < 0 || len > compressBound(TARGET_PAGE_SIZE); + } + ++#ifdef CONFIG_ZSTD ++static int zstd_save_setup(CompressParam *param) ++{ ++ int res; ++ param->zstd_cs = ZSTD_createCStream(); ++ if (!param->zstd_cs) { ++ return -1; ++ } ++ res = ZSTD_initCStream(param->zstd_cs, migrate_compress_level()); ++ if (ZSTD_isError(res)) { ++ return -1; ++ } ++ return 0; ++} ++static void zstd_save_cleanup(CompressParam *param) ++{ ++ ZSTD_freeCStream(param->zstd_cs); ++ param->zstd_cs = NULL; ++} ++static ssize_t zstd_compress_data(CompressParam *param, size_t size) ++{ ++ int ret; ++ uint8_t *dest = NULL; ++ uint8_t *p = param->originbuf; ++ QEMUFile *f = f = param->file; ++ ssize_t blen = qemu_put_compress_start(f, &dest); ++ if (blen < ZSTD_compressBound(size)) { ++ return -1; ++ } ++ param->out.dst = dest; ++ param->out.size = blen; ++ param->out.pos = 0; ++ param->in.src = p; ++ param->in.size = size; ++ param->in.pos = 0; ++ do { ++ ret = ZSTD_compressStream2(param->zstd_cs, ¶m->out, ++ ¶m->in, ZSTD_e_end); ++ } while (ret > 0 && (param->in.size - param->in.pos > 0) ++ && (param->out.size - param->out.pos > 0)); ++ if (ret > 0 && (param->in.size - param->in.pos > 0)) { ++ return -1; ++ } ++ if (ZSTD_isError(ret)) { ++ return -1; ++ } ++ blen = param->out.pos; ++ qemu_put_compress_end(f, blen); ++ return blen + sizeof(int32_t); ++} ++ ++static int zstd_load_setup(DecompressParam *param) ++{ ++ int ret; ++ param->zstd_ds = ZSTD_createDStream(); ++ if (!param->zstd_ds) { ++ return -1; ++ } ++ ret = ZSTD_initDStream(param->zstd_ds); ++ if (ZSTD_isError(ret)) { ++ return -1; ++ } ++ return 0; ++} ++static void zstd_load_cleanup(DecompressParam *param) ++{ ++ ZSTD_freeDStream(param->zstd_ds); ++ param->zstd_ds = NULL; ++} ++static int ++zstd_decompress_data(DecompressParam *param, uint8_t *dest, size_t size) ++{ ++ int ret; ++ param->out.dst = dest; ++ param->out.size = size; ++ param->out.pos = 0; ++ param->in.src = param->compbuf; ++ param->in.size = param->len; ++ param->in.pos = 0; ++ do { ++ ret = ZSTD_decompressStream(param->zstd_ds, ¶m->out, ¶m->in); ++ } while (ret > 0 && (param->in.size - param->in.pos > 0) ++ && (param->out.size - param->out.pos > 0)); ++ if (ret > 0 && (param->in.size - param->in.pos > 0)) { ++ return -1; ++ } ++ if (ZSTD_isError(ret)) { ++ return -1; ++ } ++ return ret; ++} ++static int zstd_check_len(int len) ++{ ++ return len < 0 || len > ZSTD_compressBound(TARGET_PAGE_SIZE); ++} ++#endif ++ + static int set_compress_ops(void) + { + compress_ops = g_new0(MigrationCompressOps, 1); +@@ -577,6 +688,13 @@ static int set_compress_ops(void) + compress_ops->save_cleanup = zlib_save_cleanup; + compress_ops->compress_data = zlib_compress_data; + break; ++#ifdef CONFIG_ZSTD ++ case COMPRESS_METHOD_ZSTD: ++ compress_ops->save_setup = zstd_save_setup; ++ compress_ops->save_cleanup = zstd_save_cleanup; ++ compress_ops->compress_data = zstd_compress_data; ++ break; ++#endif + default: + return -1; + } +@@ -595,6 +713,14 @@ static int set_decompress_ops(void) + decompress_ops->decompress_data = zlib_decompress_data; + decompress_ops->check_len = zlib_check_len; + break; ++#ifdef CONFIG_ZSTD ++ case COMPRESS_METHOD_ZSTD: ++ decompress_ops->load_setup = zstd_load_setup; ++ decompress_ops->load_cleanup = zstd_load_cleanup; ++ decompress_ops->decompress_data = zstd_decompress_data; ++ decompress_ops->check_len = zstd_check_len; ++ break; ++#endif + default: + return -1; + } +diff --git a/qapi/migration.json b/qapi/migration.json +index b0e8c493ee..587ef65872 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -493,7 +493,7 @@ + # + ## + { 'enum': 'CompressMethod', +- 'data': [ 'zlib' ] } ++ 'data': [ 'zlib', { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] } + + ## + # @MigrationParameter: +-- +2.27.0 + -- Gitee From b3cb876f2b45c54471d8d03bac6a4f497a49acb7 Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Sat, 30 Jan 2021 16:23:15 +0800 Subject: [PATCH 5/9] migration: Add compress_level sanity check Zlib compression has level from 1 to 9. However Zstd compression has level from 1 to 22 (level >= 20 not recommanded). Let's do sanity check here to make sure a vaild compress_level is given by user. Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang (cherry picked from commit 1154612ba182ef7d373b4ec30c374a5b8d978fea) --- ...tion-Add-compress_level-sanity-check.patch | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 migration-Add-compress_level-sanity-check.patch diff --git a/migration-Add-compress_level-sanity-check.patch b/migration-Add-compress_level-sanity-check.patch new file mode 100644 index 00000000..8513384f --- /dev/null +++ b/migration-Add-compress_level-sanity-check.patch @@ -0,0 +1,67 @@ +From 90c8ce0b3bcf4a3140bc4b500da9b55a694e1bde Mon Sep 17 00:00:00 2001 +From: Zeyu Jin +Date: Sat, 30 Jan 2021 16:23:15 +0800 +Subject: [PATCH] migration: Add compress_level sanity check + +Zlib compression has level from 1 to 9. However Zstd compression has level +from 1 to 22 (level >= 20 not recommanded). Let's do sanity check here +to make sure a vaild compress_level is given by user. + +Signed-off-by: Zeyu Jin +Signed-off-by: Ying Fang +--- + migration/migration.c | 32 ++++++++++++++++++++++++++++---- + 1 file changed, 28 insertions(+), 4 deletions(-) + +diff --git a/migration/migration.c b/migration/migration.c +index 67425fde7a..17a5c16c79 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -1111,16 +1111,40 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, + } + } + ++static bool compress_level_check(MigrationParameters *params, Error **errp) ++{ ++ switch (params->compress_method) { ++ case COMPRESS_METHOD_ZLIB: ++ if (params->compress_level > 9 || params->compress_level < 1) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", ++ "a value in the range of 0 to 9 for Zlib method"); ++ return false; ++ } ++ break; ++#ifdef CONFIG_ZSTD ++ case COMPRESS_METHOD_ZSTD: ++ if (params->compress_level > 19 || params->compress_level < 1) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", ++ "a value in the range of 1 to 19 for Zstd method"); ++ return false; ++ } ++ break; ++#endif ++ default: ++ error_setg(errp, "Checking compress_level failed for unknown reason"); ++ return false; ++ } ++ ++ return true; ++} ++ + /* + * Check whether the parameters are valid. Error will be put into errp + * (if provided). Return true if valid, otherwise false. + */ + static bool migrate_params_check(MigrationParameters *params, Error **errp) + { +- if (params->has_compress_level && +- (params->compress_level > 9)) { +- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", +- "is invalid, it should be in the range of 0 to 9"); ++ if (params->has_compress_level && !compress_level_check(params, errp)) { + return false; + } + +-- +2.27.0 + -- Gitee From 3a06a27b2b36738cba232272fdab2014afaf5375 Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Sat, 30 Jan 2021 16:36:47 +0800 Subject: [PATCH 6/9] doc: Update multi-thread compression doc Modify the doc to fit the previous changes. Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang (cherry picked from commit 928670c540014f1629fe89b5b4b0c82a30c49b99) --- doc-Update-multi-thread-compression-doc.patch | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 doc-Update-multi-thread-compression-doc.patch diff --git a/doc-Update-multi-thread-compression-doc.patch b/doc-Update-multi-thread-compression-doc.patch new file mode 100644 index 00000000..33ef835a --- /dev/null +++ b/doc-Update-multi-thread-compression-doc.patch @@ -0,0 +1,85 @@ +From 642df85795097017e9370a9721f702cbec50c173 Mon Sep 17 00:00:00 2001 +From: Zeyu Jin +Date: Sat, 30 Jan 2021 16:36:47 +0800 +Subject: [PATCH] doc: Update multi-thread compression doc + +Modify the doc to fit the previous changes. + +Signed-off-by: Zeyu Jin +Signed-off-by: Ying Fang +--- + docs/multi-thread-compression.txt | 31 ++++++++++++++++++------------- + 1 file changed, 18 insertions(+), 13 deletions(-) + +diff --git a/docs/multi-thread-compression.txt b/docs/multi-thread-compression.txt +index bb88c6bdf1..d429963cb0 100644 +--- a/docs/multi-thread-compression.txt ++++ b/docs/multi-thread-compression.txt +@@ -33,14 +33,15 @@ thread compression can be used to accelerate the compression process. + + The decompression speed of Zlib is at least 4 times as quick as + compression, if the source and destination CPU have equal speed, +-keeping the compression thread count 4 times the decompression +-thread count can avoid resource waste. ++and you choose Zlib as compression method, keeping the compression ++thread count 4 times the decompression thread count can avoid resource waste. + + Compression level can be used to control the compression speed and the +-compression ratio. High compression ratio will take more time, level 0 +-stands for no compression, level 1 stands for the best compression +-speed, and level 9 stands for the best compression ratio. Users can +-select a level number between 0 and 9. ++compression ratio. High compression ratio will take more time, ++level 1 stands for the best compression speed, and higher level means higher ++compression ration. For Zlib, users can select a level number between 0 and 9, ++where level 0 stands for no compression. For Zstd, users can select a ++level number between 1 and 22. + + + When to use the multiple thread compression in live migration +@@ -116,16 +117,19 @@ to support the multiple thread compression migration: + 2. Activate compression on the source: + {qemu} migrate_set_capability compress on + +-3. Set the compression thread count on source: ++3. Set the compression method: ++ {qemu} migrate_set_parameter compress_method zstd ++ ++4. Set the compression thread count on source: + {qemu} migrate_set_parameter compress_threads 12 + +-4. Set the compression level on the source: ++5. Set the compression level on the source: + {qemu} migrate_set_parameter compress_level 1 + +-5. Set the decompression thread count on destination: ++6. Set the decompression thread count on destination: + {qemu} migrate_set_parameter decompress_threads 3 + +-6. Start outgoing migration: ++7. Start outgoing migration: + {qemu} migrate -d tcp:destination.host:4444 + {qemu} info migrate + Capabilities: ... compress: on +@@ -136,6 +140,7 @@ The following are the default settings: + compress_threads: 8 + decompress_threads: 2 + compress_level: 1 (which means best speed) ++ compress_method: zlib + + So, only the first two steps are required to use the multiple + thread compression in migration. You can do more if the default +@@ -143,7 +148,7 @@ settings are not appropriate. + + TODO + ==== +-Some faster (de)compression method such as LZ4 and Quicklz can help +-to reduce the CPU consumption when doing (de)compression. If using +-these faster (de)compression method, less (de)compression threads ++Comparing to Zlib, Some faster (de)compression method such as LZ4 ++and Quicklz can help to reduce the CPU consumption when doing (de)compression. ++If using these faster (de)compression method, less (de)compression threads + are needed when doing the migration. +-- +2.27.0 + -- Gitee From faa56f29bdc9bf4e90ed8ab0c959a253002d0bd8 Mon Sep 17 00:00:00 2001 From: "Huawei Technologies Co., Ltd" Date: Wed, 3 Feb 2021 17:33:44 +0800 Subject: [PATCH 7/9] configure: Enable test and libs for zstd configure: Enable test and libs for zstd Add it to several build systems to make testing good. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Jiajie Li (cherry picked from commit 086515ab472266a3776c80311ba6248b7b3f889b) --- configure-Enable-test-and-libs-for-zstd.patch | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 configure-Enable-test-and-libs-for-zstd.patch diff --git a/configure-Enable-test-and-libs-for-zstd.patch b/configure-Enable-test-and-libs-for-zstd.patch new file mode 100644 index 00000000..bf900cf3 --- /dev/null +++ b/configure-Enable-test-and-libs-for-zstd.patch @@ -0,0 +1,121 @@ +From 5a79ccd388ee09dc1db93d26791d1e4a6b2ced47 Mon Sep 17 00:00:00 2001 +From: Juan Quintela +Date: Wed, 3 Feb 2021 17:33:44 +0800 +Subject: [PATCH] configure: Enable test and libs for zstd + +configure: Enable test and libs for zstd +Add it to several build systems to make testing good. + +Signed-off-by: Juan Quintela +Reviewed-by: Dr. David Alan Gilbert + +Signed-off-by: Jiajie Li +--- + .gitlab-ci.yml | 1 + + .travis.yml | 1 + + configure | 30 ++++++++++++++++++++++++++++++ + 3 files changed, 32 insertions(+) + +diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml +index c63bf2f822..3d9b7f9262 100644 +--- a/.gitlab-ci.yml ++++ b/.gitlab-ci.yml +@@ -16,6 +16,7 @@ build-system2: + script: + - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev + libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev ++ libzstd-dev + - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu + microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu + sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu" +diff --git a/.travis.yml b/.travis.yml +index caf0a1f8fa..f3fe04fba9 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -35,6 +35,7 @@ addons: + - liburcu-dev + - libusb-1.0-0-dev + - libvte-2.91-dev ++ - libzstd-dev + - sparse + - uuid-dev + - gcovr +diff --git a/configure b/configure +index 714e7fb6a1..577533e9ed 100755 +--- a/configure ++++ b/configure +@@ -446,6 +446,7 @@ lzo="" + snappy="" + bzip2="" + lzfse="" ++zstd="" + guest_agent="" + guest_agent_with_vss="no" + guest_agent_ntddscsi="no" +@@ -1358,6 +1359,10 @@ for opt do + ;; + --disable-lzfse) lzfse="no" + ;; ++ --disable-zstd) zstd="no" ++ ;; ++ --enable-zstd) zstd="yes" ++ ;; + --enable-guest-agent) guest_agent="yes" + ;; + --disable-guest-agent) guest_agent="no" +@@ -1812,6 +1817,8 @@ disabled with --disable-FEATURE, default is enabled if available: + (for reading bzip2-compressed dmg images) + lzfse support of lzfse compression library + (for reading lzfse-compressed dmg images) ++ zstd support for zstd compression library ++ (for migration compression) + seccomp seccomp support + coroutine-pool coroutine freelist (better performance) + glusterfs GlusterFS backend +@@ -2407,6 +2414,24 @@ EOF + fi + fi + ++########################################## ++# zstd check ++ ++if test "$zstd" != "no" ; then ++ if $pkg_config --exist libzstd ; then ++ zstd_cflags="$($pkg_config --cflags libzstd)" ++ zstd_libs="$($pkg_config --libs libzstd)" ++ LIBS="$zstd_libs $LIBS" ++ QEMU_CFLAGS="$QEMU_CFLAGS $zstd_cflags" ++ zstd="yes" ++ else ++ if test "$zstd" = "yes" ; then ++ feature_not_found "libzstd" "Install libzstd devel" ++ fi ++ zstd="no" ++ fi ++fi ++ + ########################################## + # libseccomp check + +@@ -6460,6 +6485,7 @@ echo "lzo support $lzo" + echo "snappy support $snappy" + echo "bzip2 support $bzip2" + echo "lzfse support $lzfse" ++echo "zstd support $zstd" + echo "NUMA host support $numa" + echo "libxml2 $libxml2" + echo "tcmalloc support $tcmalloc" +@@ -7024,6 +7050,10 @@ if test "$lzfse" = "yes" ; then + echo "LZFSE_LIBS=-llzfse" >> $config_host_mak + fi + ++if test "$zstd" = "yes" ; then ++ echo "CONFIG_ZSTD=y" >> $config_host_mak ++fi ++ + if test "$libiscsi" = "yes" ; then + echo "CONFIG_LIBISCSI=m" >> $config_host_mak + echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak +-- +2.27.0 + -- Gitee From 80426cea688563a45eef6f82712ed3dbb8b69590 Mon Sep 17 00:00:00 2001 From: Euler Robot Date: Thu, 4 Feb 2021 16:27:33 +0800 Subject: [PATCH 8/9] spec: Update patch and changelog with !69 migration: Add multi-thread compress method migration: Refactoring multi-thread compress migration migration: Add multi-thread compress ops migration: Add zstd support in multi-thread compression migration: Add compress_level sanity check doc: Update multi-thread compression doc configure: Enable test and libs for zstd Signed-off-by: Alex Chen (cherry picked from commit ef3cb17353f5affaf1fe40f0499dfc7c9c1863de) --- qemu.spec | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/qemu.spec b/qemu.spec index f546abe7..b5dd2eb4 100644 --- a/qemu.spec +++ b/qemu.spec @@ -299,6 +299,13 @@ Patch0286: block-Add-error-retry-param-setting.patch Patch0287: virtio-blk-Refactor-the-code-that-processes-queued-r.patch Patch0288: virtio-blk-On-restart-process-queued-requests-in-the.patch Patch0289: virtio_blk-Add-support-for-retry-on-errors.patch +Patch0290: migration-Add-multi-thread-compress-method.patch +Patch0291: migration-Refactoring-multi-thread-compress-migratio.patch +Patch0292: migration-Add-multi-thread-compress-ops.patch +Patch0293: migration-Add-zstd-support-in-multi-thread-compressi.patch +Patch0294: migration-Add-compress_level-sanity-check.patch +Patch0295: doc-Update-multi-thread-compression-doc.patch +Patch0296: configure-Enable-test-and-libs-for-zstd.patch BuildRequires: flex BuildRequires: bison @@ -678,6 +685,15 @@ getent passwd qemu >/dev/null || \ %endif %changelog +* Thu Feb 04 2021 Huawei Technologies Co., Ltd +- migration: Add multi-thread compress method +- migration: Refactoring multi-thread compress migration +- migration: Add multi-thread compress ops +- migration: Add zstd support in multi-thread compression +- migration: Add compress_level sanity check +- doc: Update multi-thread compression doc +- configure: Enable test and libs for zstd + * Sat Jan 30 2021 Huawei Technologies Co., Ltd - scsi-bus: Refactor the code that retries requests - scsi-disk: Add support for retry on errors -- Gitee From eed637ffab95ca345df440c80a0942ec70943bf7 Mon Sep 17 00:00:00 2001 From: Euler Robot Date: Thu, 4 Feb 2021 16:28:08 +0800 Subject: [PATCH 9/9] spec: Update release version with !69 increase release verison by one Signed-off-by: Euler Robot (cherry picked from commit 99eb051c6892dddcfb1e2aac48b9d6cf5bb24f82) --- qemu.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index b5dd2eb4..e7816389 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,6 +1,6 @@ Name: qemu Version: 4.1.0 -Release: 44 +Release: 45 Epoch: 2 Summary: QEMU is a generic and open source machine emulator and virtualizer License: GPLv2 and BSD and MIT and CC-BY-SA-4.0 -- Gitee