qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v3 1/3] qcow2: introduce compression type featur


From: Denis Plotnikov
Subject: Re: [Qemu-devel] [PATCH v3 1/3] qcow2: introduce compression type feature
Date: Tue, 27 Aug 2019 14:52:53 +0000


On 27.08.2019 14:49, Vladimir Sementsov-Ogievskiy wrote:
> 19.08.2019 18:00, Denis Plotnikov wrote:
>> The patch adds some preparation parts for incompatible compression type
>> feature to QCOW2 header that indicates that *all* compressed clusters
>> must be (de)compressed using a certain compression type.
>>
>> It is implied that the compression type is set on the image creation and
>> can be changed only later by image conversion, thus compression type
>> defines the only compression algorithm used for the image.
>>
>> The goal of the feature is to add support of other compression algorithms
>> to qcow2. For example, ZSTD which is more effective on compression than ZLIB.
>> It works roughly 2x faster than ZLIB providing a comparable compression ratio
>> and therefore provides a performance advantage in backup scenarios.
>>
>> The default compression is ZLIB. Images created with ZLIB compression type
>> are backward compatible with older qemu versions.
>>
>> Signed-off-by: Denis Plotnikov <address@hidden>
>> ---
>>    block/qcow2.c             | 94 +++++++++++++++++++++++++++++++++++++++
>>    block/qcow2.h             | 26 ++++++++---
>>    docs/interop/qcow2.txt    | 19 +++++++-
>>    include/block/block_int.h |  1 +
>>    qapi/block-core.json      | 22 ++++++++-
>>    5 files changed, 152 insertions(+), 10 deletions(-)
>>
>> diff --git a/block/qcow2.c b/block/qcow2.c
>> index 039bdc2f7e..4e07b7e9ec 100644
>> --- a/block/qcow2.c
>> +++ b/block/qcow2.c
>> @@ -1197,6 +1197,32 @@ static int qcow2_update_options(BlockDriverState *bs, 
>> QDict *options,
>>        return ret;
>>    }
>>    
>> +static int check_compression_type(BDRVQcow2State *s, Error **errp)
>> +{
>> +    switch (s->compression_type) {
>> +    case QCOW2_COMPRESSION_TYPE_ZLIB:
>> +        break;
>> +
>> +    default:
>> +        error_setg(errp, "qcow2: unknown compression type: %u",
>> +                   s->compression_type);
>> +        return -ENOTSUP;
>> +    }
>> +
>> +    /*
>> +     * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB
>> +     * the incompatible feature flag must be set
>> +     */
>> +
>> +    if (s->compression_type != QCOW2_COMPRESSION_TYPE_ZLIB &&
>> +        !(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE)) {
>> +            error_setg(errp, "qcow2: Invalid compression type setting");
>> +            return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>    /* Called with s->lock held.  */
>>    static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict 
>> *options,
>>                                          int flags, Error **errp)
>> @@ -1312,6 +1338,35 @@ static int coroutine_fn 
>> qcow2_do_open(BlockDriverState *bs, QDict *options,
>>        s->compatible_features      = header.compatible_features;
>>        s->autoclear_features       = header.autoclear_features;
>>    
>> +    /*
>> +     * Handle compression type
>> +     * Older qcow2 images don't contain the compression type header.
>> +     * Distinguish them by the header length and use
>> +     * the only valid (default) compression type in that case
>> +     */
>> +    if (header.header_length > offsetof(QCowHeader, compression_type)) {
>> +        /* sanity check that we can read a compression type */
>> +        size_t min_len = offsetof(QCowHeader, compression_type) +
>> +                         sizeof(header.compression_type);
>> +        if (header.header_length < min_len) {
>> +            error_setg(errp,
>> +                       "Could not read compression type, "
>> +                       "qcow2 header is too short");
>> +            ret = -EINVAL;
>> +            goto fail;
>> +        }
>> +
>> +        header.compression_type = be32_to_cpu(header.compression_type);
>> +        s->compression_type = header.compression_type;
>> +    } else {
>> +        s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
>> +    }
>> +
>> +    ret = check_compression_type(s, errp);
>> +    if (ret) {
>> +        goto fail;
>> +    }
>> +
>>        if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
>>            void *feature_table = NULL;
>>            qcow2_read_extensions(bs, header.header_length, ext_end,
>> @@ -2516,6 +2571,12 @@ int qcow2_update_header(BlockDriverState *bs)
>>        total_size = bs->total_sectors * BDRV_SECTOR_SIZE;
>>        refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits 
>> - 3);
>>    
>> +    ret = check_compression_type(s, NULL);
>> +
>> +    if (ret) {
>> +        goto fail;
>> +    }
>> +
>>        *header = (QCowHeader) {
>>            /* Version 2 fields */
>>            .magic                  = cpu_to_be32(QCOW_MAGIC),
>> @@ -2538,6 +2599,7 @@ int qcow2_update_header(BlockDriverState *bs)
>>            .autoclear_features     = cpu_to_be64(s->autoclear_features),
>>            .refcount_order         = cpu_to_be32(s->refcount_order),
>>            .header_length          = cpu_to_be32(header_length),
>> +        .compression_type       = cpu_to_be32(s->compression_type),
>>        };
>>    
>>        /* For older versions, write a shorter header */
>> @@ -2635,6 +2697,11 @@ int qcow2_update_header(BlockDriverState *bs)
>>                    .bit  = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
>>                    .name = "lazy refcounts",
>>                },
>> +            {
>> +                .type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
>> +                .bit  = QCOW2_INCOMPAT_COMPRESSION_TYPE_BITNR,
>> +                .name = "compression type",
>> +            },
>>            };
>>    
>>            ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
>> @@ -3202,6 +3269,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, 
>> Error **errp)
>>            .refcount_table_offset      = cpu_to_be64(cluster_size),
>>            .refcount_table_clusters    = cpu_to_be32(1),
>>            .refcount_order             = cpu_to_be32(refcount_order),
>> +        .compression_type           = 
>> cpu_to_be32(QCOW2_COMPRESSION_TYPE_ZLIB),
>>            .header_length              = cpu_to_be32(sizeof(*header)),
>>        };
>>    
>> @@ -3221,6 +3289,24 @@ qcow2_co_create(BlockdevCreateOptions 
>> *create_options, Error **errp)
>>                cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW);
>>        }
>>    
>> +    if (qcow2_opts->has_compression_type &&
>> +        qcow2_opts->compression_type != QCOW2_COMPRESSION_TYPE_ZLIB) {
>> +
>> +        switch (qcow2_opts->compression_type) {
>> +        case QCOW2_COMPRESSION_TYPE_ZLIB:
> 
> a bit confusing, as it's unreachable because of if (), and because if we are 
> here we are going to
> do a wrong thing: to set incompatible feature (so, I firstly thought that 
> this is a bug and then
> looked above at if() condition)
Yes, there should be QCOW2_COMPRESSION_TYPE_ZSTD being added in later 
patches in the series
>> +            break;
>> +
>> +        default:
>> +            error_setg_errno(errp, -EINVAL, "Unknown compression type");
>> +            goto out;
>> +        }
>> +
>> +        header->compression_type = 
>> cpu_to_be32(qcow2_opts->compression_type);
>> +
>> +        header->incompatible_features |=
>> +            cpu_to_be64(QCOW2_INCOMPAT_COMPRESSION_TYPE);
> 
> cpu_to_be32 actually
true
> 
>> +    }
>> +
>>        ret = blk_pwrite(blk, 0, header, cluster_size, 0);
>>        g_free(header);
>>        if (ret < 0) {
>> @@ -3402,6 +3488,7 @@ static int coroutine_fn qcow2_co_create_opts(const 
>> char *filename, QemuOpts *opt
>>            { BLOCK_OPT_ENCRYPT,            BLOCK_OPT_ENCRYPT_FORMAT },
>>            { BLOCK_OPT_COMPAT_LEVEL,       "version" },
>>            { BLOCK_OPT_DATA_FILE_RAW,      "data-file-raw" },
>> +        { BLOCK_OPT_COMPRESSION_TYPE,   "compression-type" },
> 
> I think we don't need it. This array is commented as:
>    /* Change legacy command line options into QMP ones */
> 
> but compression-type is not a legacy option, it's a new one. Why to tolerate 
> old notation for it?
> 
>>            { NULL, NULL },
>>        };
>>    
>> @@ -4598,6 +4685,7 @@ static ImageInfoSpecific 
>> *qcow2_get_specific_info(BlockDriverState *bs,
>>                .data_file          = g_strdup(s->image_data_file),
>>                .has_data_file_raw  = has_data_file(bs),
>>                .data_file_raw      = data_file_is_raw(bs),
>> +            .compression_type   = s->compression_type,
>>            };
>>        } else {
>>            /* if this assertion fails, this probably means a new version was
>> @@ -5163,6 +5251,12 @@ static QemuOptsList qcow2_create_opts = {
>>                .help = "Width of a reference count entry in bits",
>>                .def_value_str = "16"
>>            },
>> +        {
>> +            .name = BLOCK_OPT_COMPRESSION_TYPE,
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "Compression method used for image clusters 
>> compression",
>> +            .def_value_str = "zlib"
>> +        },
>>            { /* end of list */ }
>>        }
>>    };
>> diff --git a/block/qcow2.h b/block/qcow2.h
>> index fc1b0d3c1e..9a241e4b9a 100644
>> --- a/block/qcow2.h
>> +++ b/block/qcow2.h
>> @@ -140,6 +140,7 @@ typedef struct QCowHeader {
>>    
>>        uint32_t refcount_order;
>>        uint32_t header_length;
>> +    uint32_t compression_type;
>>    } QEMU_PACKED QCowHeader;
>>    
>>    typedef struct QEMU_PACKED QCowSnapshotHeader {
>> @@ -203,16 +204,20 @@ enum {
>>    
>>    /* Incompatible feature bits */
>>    enum {
>> -    QCOW2_INCOMPAT_DIRTY_BITNR      = 0,
>> -    QCOW2_INCOMPAT_CORRUPT_BITNR    = 1,
>> -    QCOW2_INCOMPAT_DATA_FILE_BITNR  = 2,
>> -    QCOW2_INCOMPAT_DIRTY            = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
>> -    QCOW2_INCOMPAT_CORRUPT          = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR,
>> -    QCOW2_INCOMPAT_DATA_FILE        = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR,
>> +    QCOW2_INCOMPAT_DIRTY_BITNR            = 0,
>> +    QCOW2_INCOMPAT_CORRUPT_BITNR          = 1,
>> +    QCOW2_INCOMPAT_DATA_FILE_BITNR        = 2,
>> +    QCOW2_INCOMPAT_COMPRESSION_TYPE_BITNR = 3,
>> +    QCOW2_INCOMPAT_DIRTY                  = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
>> +    QCOW2_INCOMPAT_CORRUPT                = 1 << 
>> QCOW2_INCOMPAT_CORRUPT_BITNR,
>> +    QCOW2_INCOMPAT_DATA_FILE              = 1 << 
>> QCOW2_INCOMPAT_DATA_FILE_BITNR,
>> +    QCOW2_INCOMPAT_COMPRESSION_TYPE       =
>> +        1 << QCOW2_INCOMPAT_COMPRESSION_TYPE_BITNR,
>>    
>>        QCOW2_INCOMPAT_MASK             = QCOW2_INCOMPAT_DIRTY
>>                                        | QCOW2_INCOMPAT_CORRUPT
>> -                                    | QCOW2_INCOMPAT_DATA_FILE,
>> +                                    | QCOW2_INCOMPAT_DATA_FILE
>> +                                    | QCOW2_INCOMPAT_COMPRESSION_TYPE,
>>    };
>>    
>>    /* Compatible feature bits */
>> @@ -359,6 +364,13 @@ typedef struct BDRVQcow2State {
>>    
>>        bool metadata_preallocation_checked;
>>        bool metadata_preallocation;
>> +    /*
>> +     * Compression type used for the image. Default: 0 - ZLIB
>> +     * The image compression type is set on image creation.
>> +     * The only way to change the compression type is to convert the image
>> +     * with the desired compression type set
>> +     */
>> +    uint32_t compression_type;
>>    } BDRVQcow2State;
>>    
>>    typedef struct Qcow2COWRegion {
>> diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
>> index af5711e533..e1be8bd5c3 100644
>> --- a/docs/interop/qcow2.txt
>> +++ b/docs/interop/qcow2.txt
>> @@ -109,7 +109,12 @@ in the description of a field.
>>                                    An External Data File Name header 
>> extension may
>>                                    be present if this bit is set.
>>    
>> -                    Bits 3-63:  Reserved (set to 0)
>> +                    Bit 3:      Compression type bit. The bit must be set if
>> +                                the compression type differs from default 
>> of zlib.
>> +                                If the compression type is default the bit 
>> should
>> +                                be unset.
>> +
>> +                    Bits 4-63:  Reserved (set to 0)
>>    
>>             80 -  87:  compatible_features
>>                        Bitmask of compatible features. An implementation can
>> @@ -165,6 +170,18 @@ in the description of a field.
>>                        Length of the header structure in bytes. For version 2
>>                        images, the length is always assumed to be 72 bytes.
>>    
>> +        104 - 107:  compression_type
> 
> Why 4 bytes? 1 is enough and 2 are enough for sure. Or we need to align all 
> fields to 4 bytes?
Each field in the header is at least 4 bytes in size including "32-35 
crypt_method" which also is not expected to have a big variety of values 
(now it's 3). I'm not sure but there should be some kind of reason for that.

>> +                    Defines the compression method used for compressed 
>> clusters.
>> +                    A single compression type is applied to all compressed 
>> image
>> +                    clusters.
> 
> 
>> +                    The compression type is set on image creation only.
> 
> this sentence is not needed, why to abandon inplace conversion? And anyway, 
> it's not
> about specification of format.
But on the other hand, it makes the intentions clearer.>
> 
> 
>> +                    The default compression type is zlib (value: 0).
>> +                    When the compression type differs from the default
>> +                    the compression type bit (incompatible feature bit 3)
>> +                    must be set.
>> +                    Available compression type values:
>> +                        0: zlib <https://www.zlib.net/> (default)
>> +
>>    Directly after the image header, optional sections called header 
>> extensions can
>>    be stored. Each extension has a structure like the following:
>>    
>> diff --git a/include/block/block_int.h b/include/block/block_int.h
>> index 3aa1e832a8..4b254802e5 100644
>> --- a/include/block/block_int.h
>> +++ b/include/block/block_int.h
>> @@ -58,6 +58,7 @@
>>    #define BLOCK_OPT_REFCOUNT_BITS     "refcount_bits"
>>    #define BLOCK_OPT_DATA_FILE         "data_file"
>>    #define BLOCK_OPT_DATA_FILE_RAW     "data_file_raw"
>> +#define BLOCK_OPT_COMPRESSION_TYPE  "compression_type"
>>    
>>    #define BLOCK_PROBE_BUF_SIZE        512
>>    
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index 0d43d4f37c..2c002ca6a9 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -78,6 +78,8 @@
>>    #
>>    # @bitmaps: A list of qcow2 bitmap details (since 4.0)
>>    #
>> +# @compression-type: the image cluster compression method (since 4.2)
>> +#
>>    # Since: 1.7
>>    ##
>>    { 'struct': 'ImageInfoSpecificQCow2',
>> @@ -89,7 +91,8 @@
>>          '*corrupt': 'bool',
>>          'refcount-bits': 'int',
>>          '*encrypt': 'ImageInfoSpecificQCow2Encryption',
>> -      '*bitmaps': ['Qcow2BitmapInfo']
>> +      '*bitmaps': ['Qcow2BitmapInfo'],
>> +      'compression-type': 'Qcow2CompressionType'
>>      } }
>>    
>>    ##
>> @@ -4274,6 +4277,18 @@
>>      'data': [ 'v2', 'v3' ] }
>>    
>>    
>> +##
>> +# @Qcow2CompressionType:
>> +#
>> +# Compression type used in qcow2 image file
>> +#
>> +# @zlib:  zlib compression, see <http://zlib.net/>
>> +#
>> +# Since: 4.2
>> +##
>> +{ 'enum': 'Qcow2CompressionType',
>> +  'data': [ 'zlib' ] }
>> +
>>    ##
>>    # @BlockdevCreateOptionsQcow2:
>>    #
>> @@ -4297,6 +4312,8 @@
>>    #                   allowed values: off, falloc, full, metadata)
>>    # @lazy-refcounts   True if refcounts may be updated lazily (default: off)
>>    # @refcount-bits    Width of reference counts in bits (default: 16)
>> +# @compression-type The image cluster compression method
>> +#                   (default: zlib, since 4.2)
>>    #
>>    # Since: 2.12
>>    ##
>> @@ -4312,7 +4329,8 @@
>>                '*cluster-size':    'size',
>>                '*preallocation':   'PreallocMode',
>>                '*lazy-refcounts':  'bool',
>> -            '*refcount-bits':   'int' } }
>> +            '*refcount-bits':   'int',
>> +            '*compression-type': 'Qcow2CompressionType' } }
>>    
>>    ##
>>    # @BlockdevCreateOptionsQed:
>>
> 
> 

-- 
Best,
Denis

reply via email to

[Prev in Thread] Current Thread [Next in Thread]