[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v0 3/3] qcow2: add zstd cluster compression
From: |
Denis Plotnikov |
Subject: |
Re: [Qemu-devel] [PATCH v0 3/3] qcow2: add zstd cluster compression |
Date: |
Tue, 2 Jul 2019 12:33:34 +0000 |
Thanks for reviewing, I'll take into account the suggestions below and
send the next version of the series soon.
On 28.06.2019 14:57, Kevin Wolf wrote:
> Am 28.05.2019 um 16:37 hat Denis Plotnikov geschrieben:
>> zstd significantly reduces cluster compression time.
>> It provides better compression performance maintaining
>> the same level of compression ratio in comparison with
>> zlib, which, by the moment, has been the only compression
>> method available.
>>
>> The performance test results:
>> Test compresses and decompresses qemu qcow2 image with just
>> installed rhel-7.6 guest.
>> Image cluster size: 64K. Image on disk size: 2.2G
>>
>> The test was conducted with brd disk to reduce the influence
>> of disk subsystem to the test results.
>> The results is given in seconds.
>>
>> compress cmd:
>> time ./qemu-img convert -O qcow2 -c -o compression_type=[zlib|zstd]
>> src.img [zlib|zstd]_compressed.img
>> decompress cmd
>> time ./qemu-img convert -O qcow2
>> [zlib|zstd]_compressed.img uncompressed.img
>>
>> compression decompression
>> zlib zstd zlib zstd
>> ------------------------------------------------------------
>> real 65.5 16.3 (-75 %) 1.9 1.6 (-16 %)
>> user 65.0 15.8 5.3 2.5
>> sys 3.3 0.2 2.0 2.0
>>
>> Both ZLIB and ZSTD gave the same compression ratio: 1.57
>> compressed image size in both cases: 1.4G
>>
>> Signed-off-by: Denis Plotnikov <address@hidden>
>> ---
>> block/qcow2.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++
>> configure | 26 ++++++++++++++++
>> 2 files changed, 108 insertions(+)
>>
>> diff --git a/block/qcow2.c b/block/qcow2.c
>> index 90f15cc3c9..58901f9f79 100644
>> --- a/block/qcow2.c
>> +++ b/block/qcow2.c
>> @@ -26,6 +26,7 @@
>>
>> #define ZLIB_CONST
>> #include <zlib.h>
>> +#include <zstd.h>
>>
>> #include "block/block_int.h"
>> #include "block/qdict.h"
>> @@ -1553,6 +1554,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState
>> *bs, QDict *options,
>> case QCOW2_COMPRESSION_TYPE_ZLIB:
>> break;
>>
>> + case QCOW2_COMPRESSION_TYPE_ZSTD:
>> + break;
>
> If we don't intend to add any code here, why not just add another case
> label to the existing break?
>
>> default:
>> error_setg(errp, "Unknown compression type");
>> ret = -EINVAL;
>> @@ -3286,6 +3290,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options,
>> Error **errp)
>> * ZLIB shouldn't be here since it's the default
>> */
>> switch (qcow2_opts->compression_type) {
>> + case QCOW2_COMPRESSION_TYPE_ZSTD:
>> + break;
>> +
>> default:
>> error_setg_errno(errp, -EINVAL, "Unknown compression type");
>> goto out;
>> @@ -4113,6 +4120,73 @@ static ssize_t zlib_decompress(void *dest, size_t
>> dest_size,
>> return ret;
>> }
>>
>> +/*
>> + * zstd_compress()
>> + *
>> + * @dest - destination buffer, @dest_size bytes
>> + * @src - source buffer, @src_size bytes
>> + *
>> + * Returns: compressed size on success
>> + * -1 on any error
>> + */
>> +
>> +static ssize_t zstd_compress(void *dest, size_t dest_size,
>> + const void *src, size_t src_size)
>> +{
>> + /* steal some bytes to store compressed chunk size */
>> + size_t ret;
>
> This ends up in a file, so this needs to be a fixed number of bytes.
> size_t varies between different platforms, so it is not acceptable.
>
> If I understand correctly, the maximum for this is the cluster size, so
> uint32_t should be right.
>
> This isn't plain the zstd compression format any more, so it needs to be
> described in the qcow2 spec.
>
>> + size_t *c_size = dest;
>> + char *d_buf = dest;
>> + d_buf += sizeof(ret);
>
> char *d_bug = dest + sizeof(ret);
>
>> + dest_size -= sizeof(ret);
>
> We don't want to end up with an integer overflow, so before this:
>
> if (dest_size < sizeof(ret)) {
> return -ENOMEM;
> }
>
>> + ret = ZSTD_compress(d_buf, dest_size, src, src_size, 5);
>> +
>> + if (ZSTD_isError(ret)) {
>> + return -1;
>> + }
>
> Need an error code here, not just -1. In particular, we need to
> distinguish cases where the buffer was too small and uncompressed data
> should be written instead (ENOMEM) from real errors that should be
> returned to the caller (EIO).
>
>> +
>> + /* store the compressed chunk size in the very beginning of the buffer
>> */
>> + *c_size = ret;
>> +
>> + return ret + sizeof(ret);
>> +}
>> +
>> +/*
>> + * zstd_decompress()
>> + *
>> + * Decompress some data (not more than @src_size bytes) to produce exactly
>> + * @dest_size bytes.
>> + *
>> + * @dest - destination buffer, @dest_size bytes
>> + * @src - source buffer, @src_size bytes
>> + *
>> + * Returns: 0 on success
>> + * -1 on fail
>> + */
>> +
>> +static ssize_t zstd_decompress(void *dest, size_t dest_size,
>> + const void *src, size_t src_size)
>
> Indentation is off.
>
>> +{
>> + size_t ret;
>> + /*
>> + * zstd decompress wants to know the exact lenght of the data
>> + * for that purpose, zstd_compress stores the length in the
>> + * very beginning of the compressed buffer
>> + */
>> + const size_t *s_size = src;
>> + const char *s_buf = src;
>> + s_buf += sizeof(size_t);
>
> Single line: const char *s_buf = src + sizeof(size_t);
>
> Of course, size_t is wrong here, too. And above you used sizeof() on a
> variable and here it's on the type. I think we should stay consistent.
>
> You're lacking a check against src_size. A malicious image could make
> use read beyond the end of the buffer. (Also consider that src_size
> could be smaller than sizeof(size_t).)
>
>> + ret = ZSTD_decompress(dest, dest_size, s_buf, *s_size);
>> +
>> + if (ZSTD_isError(ret)) {
>> + return -1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> #define MAX_COMPRESS_THREADS 4
>>
>> typedef ssize_t (*Qcow2CompressFunc)(void *dest, size_t dest_size,
>> @@ -4189,6 +4263,10 @@ qcow2_co_compress(BlockDriverState *bs, void *dest,
>> size_t dest_size,
>> fn = zlib_compress;
>> break;
>>
>> + case QCOW2_COMPRESSION_TYPE_ZSTD:
>> + fn = zstd_compress;
>> + break;
>> +
>> default:
>> return -ENOTSUP;
>> }
>> @@ -4208,6 +4286,10 @@ qcow2_co_decompress(BlockDriverState *bs, void *dest,
>> size_t dest_size,
>> fn = zlib_decompress;
>> break;
>>
>> + case QCOW2_COMPRESSION_TYPE_ZSTD:
>> + fn = zstd_decompress;
>> + break;
>> +
>> default:
>> return -ENOTSUP;
>> }
>> diff --git a/configure b/configure
>> index 1c563a7027..c90716189c 100755
>> --- a/configure
>> +++ b/configure
>> @@ -433,6 +433,7 @@ opengl_dmabuf="no"
>> cpuid_h="no"
>> avx2_opt=""
>> zlib="yes"
>> +zstd="yes"
>
> This should be zstd="" so that a missing library will automatically
> disable it instead of producing an error. (Building QEMU without zlib is
> impossible, but building it without ZSTD should work.)
>
>> capstone=""
>> lzo=""
>> snappy=""
>> @@ -1317,6 +1318,8 @@ for opt do
>> ;;
>> --disable-zlib-test) zlib="no"
>> ;;
>> + --disable-zstd-test) zstd="no"
>> + ;;
>
> Instead of this one, after making the above change, options
> --disable-zstd and --enable-zstd should be introduced that set
> zstd="yes" (that actually does produce an error if it's not available)
> or zstd="no".
>
>> --disable-lzo) lzo="no"
>> ;;
>> --enable-lzo) lzo="yes"
>> @@ -3702,6 +3705,29 @@ EOF
>> fi
>> fi
>>
>> +#########################################
>> +# zstd check
>> +
>> +if test "$zstd" != "no" ; then
>> + if $pkg_config --exists libzstd; then
>> + zstd_cflags=$($pkg_config --cflags libzstd)
>> + zstd_libs=$($pkg_config --libs libzstd)
>> + QEMU_CFLAGS="$zstd_cflags $QEMU_CFLAGS"
>> + LIBS="$zstd_libs $LIBS"
>> + else
>> + cat > $TMPC << EOF
>> +#include <zstd.h>
>> +int main(void) { ZSTD_versionNumber(); return 0; }
>> +EOF
>> + if compile_prog "" "-lzstd" ; then
>> + LIBS="$LIBS -lzstd"
>> + else
>> + error_exit "zstd check failed" \
>> + "Make sure to have the zstd libs and headers installed."
>> + fi
>
> This needs to be changed, too, to get the desired behaviour. Model it
> after bzip2 or lzo support checks:
>
> if compile_prog "" "-lbz2" ; then
> bzip2="yes"
> else
> if test "$bzip2" = "yes"; then
> feature_not_found "libbzip2" "Install libbzip2 devel"
> fi
> bzip2="no"
> fi
>
>> + fi
>> +fi
>> +
>> ##########################################
>> # SHA command probe for modules
>> if test "$modules" = yes; then
>
> Kevin
>
--
Best,
Denis
- Re: [Qemu-devel] [PATCH v0 3/3] qcow2: add zstd cluster compression,
Denis Plotnikov <=