All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: Juan Quintela <quintela@redhat.com>
Cc: "Fam Zheng" <fam@euphon.net>,
	"Laurent Vivier" <lvivier@redhat.com>,
	"Thomas Huth" <thuth@redhat.com>,
	"Daniel P. Berrangé" <berrange@redhat.com>,
	"Eduardo Habkost" <ehabkost@redhat.com>,
	"Philippe Mathieu-Daudé" <philmd@redhat.com>,
	qemu-devel@nongnu.org, "Markus Armbruster" <armbru@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Alex Bennée" <alex.bennee@linaro.org>
Subject: Re: [PATCH v6 5/8] multifd: Add zlib compression multifd support
Date: Fri, 14 Feb 2020 15:23:15 +0000	[thread overview]
Message-ID: <20200214152315.GJ3283@work-vm> (raw)
In-Reply-To: <20200213211709.59065-6-quintela@redhat.com>

* Juan Quintela (quintela@redhat.com) wrote:
> Signed-off-by: Juan Quintela <quintela@redhat.com>
> Acked-by: Markus Armbruster <armbru@redhat.com>

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

> ---
>  hw/core/qdev-properties.c    |   2 +-
>  migration/Makefile.objs      |   1 +
>  migration/multifd-zlib.c     | 325 +++++++++++++++++++++++++++++++++++
>  migration/multifd.c          |   6 +
>  migration/multifd.h          |   4 +
>  qapi/migration.json          |   3 +-
>  tests/qtest/migration-test.c |   6 +
>  7 files changed, 345 insertions(+), 2 deletions(-)
>  create mode 100644 migration/multifd-zlib.c
> 
> diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
> index fa7edac020..db2a7abfb2 100644
> --- a/hw/core/qdev-properties.c
> +++ b/hw/core/qdev-properties.c
> @@ -645,7 +645,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = {
>  const PropertyInfo qdev_prop_multifd_compression = {
>      .name = "MultiFDCompression",
>      .description = "multifd_compression values, "
> -                   "none",
> +                   "none/zlib",
>      .enum_table = &MultiFDCompression_lookup,
>      .get = get_enum,
>      .set = set_enum,
> diff --git a/migration/Makefile.objs b/migration/Makefile.objs
> index d3623d5f9b..0308caa5c5 100644
> --- a/migration/Makefile.objs
> +++ b/migration/Makefile.objs
> @@ -8,6 +8,7 @@ common-obj-y += xbzrle.o postcopy-ram.o
>  common-obj-y += qjson.o
>  common-obj-y += block-dirty-bitmap.o
>  common-obj-y += multifd.o
> +common-obj-y += multifd-zlib.o
>  
>  common-obj-$(CONFIG_RDMA) += rdma.o
>  
> diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c
> new file mode 100644
> index 0000000000..ab4ba75d75
> --- /dev/null
> +++ b/migration/multifd-zlib.c
> @@ -0,0 +1,325 @@
> +/*
> + * Multifd zlib compression implementation
> + *
> + * Copyright (c) 2020 Red Hat Inc
> + *
> + * Authors:
> + *  Juan Quintela <quintela@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include <zlib.h>
> +#include "qemu/rcu.h"
> +#include "exec/target_page.h"
> +#include "qapi/error.h"
> +#include "migration.h"
> +#include "trace.h"
> +#include "multifd.h"
> +
> +struct zlib_data {
> +    /* stream for compression */
> +    z_stream zs;
> +    /* compressed buffer */
> +    uint8_t *zbuff;
> +    /* size of compressed buffer */
> +    uint32_t zbuff_len;
> +};
> +
> +/* Multifd zlib compression */
> +
> +/**
> + * zlib_send_setup: setup send side
> + *
> + * Setup each channel with zlib compression.
> + *
> + * Returns 0 for success or -1 for error
> + *
> + * @p: Params for the channel that we are using
> + * @errp: pointer to an error
> + */
> +static int zlib_send_setup(MultiFDSendParams *p, Error **errp)
> +{
> +    uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
> +    struct zlib_data *z = g_malloc0(sizeof(struct zlib_data));
> +    z_stream *zs = &z->zs;
> +
> +    zs->zalloc = Z_NULL;
> +    zs->zfree = Z_NULL;
> +    zs->opaque = Z_NULL;
> +    if (deflateInit(zs, migrate_multifd_zlib_level()) != Z_OK) {
> +        g_free(z);
> +        error_setg(errp, "multifd %d: deflate init failed", p->id);
> +        return -1;
> +    }
> +    /* We will never have more than page_count pages */
> +    z->zbuff_len = page_count * qemu_target_page_size();
> +    z->zbuff_len *= 2;
> +    z->zbuff = g_try_malloc(z->zbuff_len);
> +    if (!z->zbuff) {
> +        deflateEnd(&z->zs);
> +        g_free(z);
> +        error_setg(errp, "multifd %d: out of memory for zbuff", p->id);
> +        return -1;
> +    }
> +    p->data = z;
> +    return 0;
> +}
> +
> +/**
> + * zlib_send_cleanup: cleanup send side
> + *
> + * Close the channel and return memory.
> + *
> + * @p: Params for the channel that we are using
> + */
> +static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp)
> +{
> +    struct zlib_data *z = p->data;
> +
> +    deflateEnd(&z->zs);
> +    g_free(z->zbuff);
> +    z->zbuff = NULL;
> +    g_free(p->data);
> +    p->data = NULL;
> +}
> +
> +/**
> + * zlib_send_prepare: prepare date to be able to send
> + *
> + * Create a compressed buffer with all the pages that we are going to
> + * send.
> + *
> + * Returns 0 for success or -1 for error
> + *
> + * @p: Params for the channel that we are using
> + * @used: number of pages used
> + */
> +static int zlib_send_prepare(MultiFDSendParams *p, uint32_t used, Error **errp)
> +{
> +    struct iovec *iov = p->pages->iov;
> +    struct zlib_data *z = p->data;
> +    z_stream *zs = &z->zs;
> +    uint32_t out_size = 0;
> +    int ret;
> +    uint32_t i;
> +
> +    for (i = 0; i < used; i++) {
> +        uint32_t available = z->zbuff_len - out_size;
> +        int flush = Z_NO_FLUSH;
> +
> +        if (i == used - 1) {
> +            flush = Z_SYNC_FLUSH;
> +        }
> +
> +        zs->avail_in = iov[i].iov_len;
> +        zs->next_in = iov[i].iov_base;
> +
> +        zs->avail_out = available;
> +        zs->next_out = z->zbuff + out_size;
> +
> +        /*
> +         * Welcome to deflate semantics
> +         *
> +         * We need to loop while:
> +         * - return is Z_OK
> +         * - there are stuff to be compressed
> +         * - there are output space free
> +         */
> +        do {
> +            ret = deflate(zs, flush);
> +        } while (ret == Z_OK && zs->avail_in && zs->avail_out);
> +        if (ret == Z_OK && zs->avail_in) {
> +            error_setg(errp, "multifd %d: deflate failed to compress all input",
> +                       p->id);
> +            return -1;
> +        }
> +        if (ret != Z_OK) {
> +            error_setg(errp, "multifd %d: deflate returned %d instead of Z_OK",
> +                       p->id, ret);
> +            return -1;
> +        }
> +        out_size += available - zs->avail_out;
> +    }
> +    p->next_packet_size = out_size;
> +    p->flags |= MULTIFD_FLAG_ZLIB;
> +
> +    return 0;
> +}
> +
> +/**
> + * zlib_send_write: do the actual write of the data
> + *
> + * Do the actual write of the comprresed buffer.
> + *
> + * Returns 0 for success or -1 for error
> + *
> + * @p: Params for the channel that we are using
> + * @used: number of pages used
> + * @errp: pointer to an error
> + */
> +static int zlib_send_write(MultiFDSendParams *p, uint32_t used, Error **errp)
> +{
> +    struct zlib_data *z = p->data;
> +
> +    return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size,
> +                                 errp);
> +}
> +
> +/**
> + * zlib_recv_setup: setup receive side
> + *
> + * Create the compressed channel and buffer.
> + *
> + * Returns 0 for success or -1 for error
> + *
> + * @p: Params for the channel that we are using
> + * @errp: pointer to an error
> + */
> +static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp)
> +{
> +    uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
> +    struct zlib_data *z = g_malloc0(sizeof(struct zlib_data));
> +    z_stream *zs = &z->zs;
> +
> +    p->data = z;
> +    zs->zalloc = Z_NULL;
> +    zs->zfree = Z_NULL;
> +    zs->opaque = Z_NULL;
> +    zs->avail_in = 0;
> +    zs->next_in = Z_NULL;
> +    if (inflateInit(zs) != Z_OK) {
> +        error_setg(errp, "multifd %d: inflate init failed", p->id);
> +        return -1;
> +    }
> +    /* We will never have more than page_count pages */
> +    z->zbuff_len = page_count * qemu_target_page_size();
> +    /* We know compression "could" use more space */
> +    z->zbuff_len *= 2;
> +    z->zbuff = g_try_malloc(z->zbuff_len);
> +    if (!z->zbuff) {
> +        inflateEnd(zs);
> +        error_setg(errp, "multifd %d: out of memory for zbuff", p->id);
> +        return -1;
> +    }
> +    return 0;
> +}
> +
> +/**
> + * zlib_recv_cleanup: setup receive side
> + *
> + * For no compression this function does nothing.
> + *
> + * @p: Params for the channel that we are using
> + */
> +static void zlib_recv_cleanup(MultiFDRecvParams *p)
> +{
> +    struct zlib_data *z = p->data;
> +
> +    inflateEnd(&z->zs);
> +    g_free(z->zbuff);
> +    z->zbuff = NULL;
> +    g_free(p->data);
> +    p->data = NULL;
> +}
> +
> +/**
> + * zlib_recv_pages: read the data from the channel into actual pages
> + *
> + * Read the compressed buffer, and uncompress it into the actual
> + * pages.
> + *
> + * Returns 0 for success or -1 for error
> + *
> + * @p: Params for the channel that we are using
> + * @used: number of pages used
> + * @errp: pointer to an error
> + */
> +static int zlib_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
> +{
> +    struct zlib_data *z = p->data;
> +    z_stream *zs = &z->zs;
> +    uint32_t in_size = p->next_packet_size;
> +    /* we measure the change of total_out */
> +    uint32_t out_size = zs->total_out;
> +    uint32_t expected_size = used * qemu_target_page_size();
> +    uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
> +    int ret;
> +    int i;
> +
> +    if (flags != MULTIFD_FLAG_ZLIB) {
> +        error_setg(errp, "multifd %d: flags received %x flags expected %x",
> +                   p->id, flags, MULTIFD_FLAG_ZLIB);
> +        return -1;
> +    }
> +    ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp);
> +
> +    if (ret != 0) {
> +        return ret;
> +    }
> +
> +    zs->avail_in = in_size;
> +    zs->next_in = z->zbuff;
> +
> +    for (i = 0; i < used; i++) {
> +        struct iovec *iov = &p->pages->iov[i];
> +        int flush = Z_NO_FLUSH;
> +        unsigned long start = zs->total_out;
> +
> +        if (i == used - 1) {
> +            flush = Z_SYNC_FLUSH;
> +        }
> +
> +        zs->avail_out = iov->iov_len;
> +        zs->next_out = iov->iov_base;
> +
> +        /*
> +         * Welcome to inflate semantics
> +         *
> +         * We need to loop while:
> +         * - return is Z_OK
> +         * - there are input available
> +         * - we haven't completed a full page
> +         */
> +        do {
> +            ret = inflate(zs, flush);
> +        } while (ret == Z_OK && zs->avail_in
> +                             && (zs->total_out - start) < iov->iov_len);
> +        if (ret == Z_OK && (zs->total_out - start) < iov->iov_len) {
> +            error_setg(errp, "multifd %d: inflate generated too few output",
> +                       p->id);
> +            return -1;
> +        }
> +        if (ret != Z_OK) {
> +            error_setg(errp, "multifd %d: inflate returned %d instead of Z_OK",
> +                       p->id, ret);
> +            return -1;
> +        }
> +    }
> +    out_size = zs->total_out - out_size;
> +    if (out_size != expected_size) {
> +        error_setg(errp, "multifd %d: packet size received %d size expected %d",
> +                   p->id, out_size, expected_size);
> +        return -1;
> +    }
> +    return 0;
> +}
> +
> +static MultiFDMethods multifd_zlib_ops = {
> +    .send_setup = zlib_send_setup,
> +    .send_cleanup = zlib_send_cleanup,
> +    .send_prepare = zlib_send_prepare,
> +    .send_write = zlib_send_write,
> +    .recv_setup = zlib_recv_setup,
> +    .recv_cleanup = zlib_recv_cleanup,
> +    .recv_pages = zlib_recv_pages
> +};
> +
> +static void multifd_zlib_register(void)
> +{
> +    multifd_register_ops(MULTIFD_COMPRESSION_ZLIB, &multifd_zlib_ops);
> +}
> +
> +migration_init(multifd_zlib_register);
> diff --git a/migration/multifd.c b/migration/multifd.c
> index 97433e5135..cb6a4a3ab8 100644
> --- a/migration/multifd.c
> +++ b/migration/multifd.c
> @@ -166,6 +166,12 @@ static MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = {
>      [MULTIFD_COMPRESSION_NONE] = &multifd_nocomp_ops,
>  };
>  
> +void multifd_register_ops(int method, MultiFDMethods *ops)
> +{
> +    assert(0 < method && method < MULTIFD_COMPRESSION__MAX);
> +    multifd_ops[method] = ops;
> +}
> +
>  static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp)
>  {
>      MultiFDInit_t msg = {};
> diff --git a/migration/multifd.h b/migration/multifd.h
> index 54075ffa7d..c6dad7b990 100644
> --- a/migration/multifd.h
> +++ b/migration/multifd.h
> @@ -23,12 +23,14 @@ void multifd_recv_sync_main(void);
>  void multifd_send_sync_main(QEMUFile *f);
>  int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset);
>  
> +/* Multifd Compression flags */
>  #define MULTIFD_FLAG_SYNC (1 << 0)
>  
>  /* We reserve 3 bits for compression methods */
>  #define MULTIFD_FLAG_COMPRESSION_MASK (7 << 1)
>  /* we need to be compatible. Before compression value was 0 */
>  #define MULTIFD_FLAG_NOCOMP (0 << 1)
> +#define MULTIFD_FLAG_ZLIB (1 << 1)
>  
>  /* This value needs to be a multiple of qemu_target_page_size() */
>  #define MULTIFD_PACKET_SIZE (512 * 1024)
> @@ -161,5 +163,7 @@ typedef struct {
>      int (*recv_pages)(MultiFDRecvParams *p, uint32_t used, Error **errp);
>  } MultiFDMethods;
>  
> +void multifd_register_ops(int method, MultiFDMethods *ops);
> +
>  #endif
>  
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 0845d926f2..860609231b 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -494,12 +494,13 @@
>  # An enumeration of multifd compression methods.
>  #
>  # @none: no compression.
> +# @zlib: use zlib compression method.
>  #
>  # Since: 5.0
>  #
>  ##
>  { 'enum': 'MultiFDCompression',
> -  'data': [ 'none' ] }
> +  'data': [ 'none', 'zlib' ] }
>  
>  ##
>  # @MigrationParameter:
> diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
> index f84c157285..90c26e879f 100644
> --- a/tests/qtest/migration-test.c
> +++ b/tests/qtest/migration-test.c
> @@ -1323,6 +1323,11 @@ static void test_multifd_tcp_none(void)
>      test_multifd_tcp("none");
>  }
>  
> +static void test_multifd_tcp_zlib(void)
> +{
> +    test_multifd_tcp("zlib");
> +}
> +
>  /*
>   * This test does:
>   *  source               target
> @@ -1486,6 +1491,7 @@ int main(int argc, char **argv)
>      qtest_add_func("/migration/auto_converge", test_migrate_auto_converge);
>      qtest_add_func("/migration/multifd/tcp/none", test_multifd_tcp_none);
>      qtest_add_func("/migration/multifd/tcp/cancel", test_multifd_tcp_cancel);
> +    qtest_add_func("/migration/multifd/tcp/zlib", test_multifd_tcp_zlib);
>  
>      ret = g_test_run();
>  
> -- 
> 2.24.1
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK



  reply	other threads:[~2020-02-14 15:30 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-13 21:17 [PATCH v6 0/8] Multifd Migration Compression Juan Quintela
2020-02-13 21:17 ` [PATCH v6 1/8] multifd: Add multifd-compression parameter Juan Quintela
2020-02-13 21:17 ` [PATCH v6 2/8] migration: Add support for modules Juan Quintela
2020-02-13 21:17 ` [PATCH v6 3/8] multifd: Make no compression operations into its own structure Juan Quintela
2020-02-14 10:11   ` Dr. David Alan Gilbert
2020-02-13 21:17 ` [PATCH v6 4/8] multifd: Add multifd-zlib-level parameter Juan Quintela
2020-02-13 21:17 ` [PATCH v6 5/8] multifd: Add zlib compression multifd support Juan Quintela
2020-02-14 15:23   ` Dr. David Alan Gilbert [this message]
2020-02-13 21:17 ` [PATCH v6 6/8] configure: Enable test and libs for zstd Juan Quintela
2020-02-13 21:17 ` [PATCH v6 7/8] multifd: Add multifd-zstd-level parameter Juan Quintela
2020-02-27 20:53   ` Peter Xu
2020-02-13 21:17 ` [PATCH v6 8/8] multifd: Add zstd compression multifd support Juan Quintela

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200214152315.GJ3283@work-vm \
    --to=dgilbert@redhat.com \
    --cc=alex.bennee@linaro.org \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=fam@euphon.net \
    --cc=lvivier@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=philmd@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@redhat.com \
    --cc=thuth@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.