qemu-block
[Top][All Lists]
Advanced

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

Re: [Qemu-block] [Qemu-devel] [PATCH 3/5] vmdk: Implement .bdrv_co_creat


From: Markus Armbruster
Subject: Re: [Qemu-block] [Qemu-devel] [PATCH 3/5] vmdk: Implement .bdrv_co_create callback
Date: Wed, 09 May 2018 20:59:40 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/25.3 (gnu/linux)

Fam Zheng <address@hidden> writes:

> On Wed, 05/09 14:41, Markus Armbruster wrote:
>> Beware, I'm only looking at QAPI-related aspects.
>> 
>> Fam Zheng <address@hidden> writes:
>> 
>> > This makes VMDK support x-blockdev-create. The implementation reuses the
>> > image creation code in vmdk_co_create_opts which now acceptes a callback
>> > pointer to "retrieve" BlockBackend pointers from the caller. This way we
>> > separate the logic between file/extent acquisition and initialization.
>> >
>> > The QAPI command parameters are mostly the same as the old create_opts
>> > except the dropped legacy @compat6 switch, which is redundant with
>> > @hwversion.
>> >
>> > Signed-off-by: Fam Zheng <address@hidden>
>> > ---
>> >  block/vmdk.c         | 481 
>> > +++++++++++++++++++++++++++++++++++++--------------
>> >  qapi/block-core.json |  67 ++++++-
>> >  2 files changed, 418 insertions(+), 130 deletions(-)
>> >
>> > diff --git a/block/vmdk.c b/block/vmdk.c
>> > index 083942f806..e16b04e26a 100644
>> > --- a/block/vmdk.c
>> > +++ b/block/vmdk.c
>> > @@ -1905,38 +1905,87 @@ static int filename_decompose(const char 
>> > *filename, char *path, char *prefix,
>> >      return VMDK_OK;
>> >  }
>> >  
>> > -static int coroutine_fn vmdk_co_create_opts(const char *filename, 
>> > QemuOpts *opts,
>> > -                                            Error **errp)
>> > +/* Stringify BlockdevVmdkSubformat enum in the faimiliar camelCase. */
>> > +static const char *vmdk_subformat_str(BlockdevVmdkSubformat subformat)
>> >  {
>> > -    int idx = 0;
>> > -    BlockBackend *new_blk = NULL;
>> > +    switch (subformat) {
>> > +    case BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE:
>> > +        return "monolithicSparse";
>> > +    case BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT:
>> > +        return "monolithicFlat";
>> > +    case BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT:
>> > +        return "twoGbMaxExtentFlat";
>> > +    case BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE:
>> > +        return "twoGbMaxExtentSparse";
>> > +    case BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED:
>> > +        return "streamOptimized";
>> > +    default:
>> > +        abort();
>> > +    }
>> > +}
>> 
>> I'd use an array instead of a switch.
>> 
>> The standard mapping from enum FOO to string is FOO_lookup[], commonly
>> used via qapi_enum_lookup().
>> 
>> vmdk_subformat_str() differs from BlockdevVmdkSubformat_lookup[] only in
>> case: the former is CamelCase ("monolithicSparse"), the latter is all
>> lower case (like "monolithicsparse"), because that's how it's spelled in
>> the QAPI schema.  By the way, QAPI naming conventions ask for hyphens,
>> like "monolithic-sparse".
>> 
>> Why do you need CamelCase?  Is it for an existing external interface?
>> 
>> If yes, should we use CamelCase in the schema?
>> 
>> Should we use hyphens, and have this function map hyphen followed by
>> lower case letter to upper case letter?
>
> "streamOptimized" is the exact style as spelled in the VMDK spec:
>
> https://www.vmware.com/support/developer/vddk/vmdk_50_technote.pdf
>
> so I want to stay as close as possible to it. In other words lower case
> "monolithicsparse" feels a bit better than hyphens "monolithic-sparse" in the
> QAPI interface.

To me it feels like a compromise that makes all parties unhappy: it
deviates from QAPI naming conventions (pain), and differs from VMDK's
spelling (for no gain: we still need extra conversion code).

>> > +
>> > +/*
>> > + * idx == 0: get or create the descriptor file (also the image file if in 
>> > a
>> > + *           non-split format.
>> > + * idx >= 1: get the n-th extent if in a split subformat
>> > + */
>> > +typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size,
>> > +                                               int idx,
>> > +                                               bool flat,
>> > +                                               bool split,
>> > +                                               bool compress,
>> > +                                               bool zeroed_grain,
>> > +                                               void *opaque,
>> > +                                               Error **errp);
>> > +
>> > +static void vmdk_desc_add_extent(GString *desc,
>> > +                                 const char *extent_line_fmt,
>> > +                                 int64_t size, const char *filename)
>> > +{
>> > +    char *desc_line = g_malloc0(BUF_SIZE);
>> > +    const char *basename = strrchr(filename, '/');
>> 
>> Blank line between declarations and statements, if you don't mind.
>
> OK!
>
>> 
>> > +    if (!basename) {
>> > +        basename = filename;
>> > +    } else {
>> > +        basename += 1;
>> > +    }
>> 
>> g_path_get_basename()?
>
> Good suggestion, thanks!
>
>> 
>> > +    snprintf(desc_line, BUF_SIZE, extent_line_fmt,
>> > +             DIV_ROUND_UP(size, BDRV_SECTOR_SIZE),
>> > +             basename);
>> > +    g_string_append(desc, desc_line);
>> > +    g_free(desc_line);
>> 
>> g_string_append_printf()?
>
> Yes!
>
>> 
>> > +}
>> > +
>> > +static int coroutine_fn vmdk_co_do_create(int64_t size,
>> > +                                          BlockdevVmdkSubformat subformat,
>> > +                                          BlockdevVmdkAdapterType 
>> > adapter_type,
>> > +                                          const char *backing_file,
>> > +                                          const char *hw_version,
>> > +                                          bool compat6,
>> > +                                          bool zeroed_grain,
>> > +                                          vmdk_create_extent_fn extent_fn,
>> > +                                          void *opaque,
>> > +                                          Error **errp)
>> > +{
>> > +    int extent_idx;
>> > +    BlockBackend *blk;
>> >      Error *local_err = NULL;
>> >      char *desc = NULL;
>> > -    int64_t total_size = 0, filesize;
>> > -    char *adapter_type = NULL;
>> > -    char *backing_file = NULL;
>> > -    char *hw_version = NULL;
>> > -    char *fmt = NULL;
>> >      int ret = 0;
>> >      bool flat, split, compress;
>> >      GString *ext_desc_lines;
>> > -    char *path = g_malloc0(PATH_MAX);
>> > -    char *prefix = g_malloc0(PATH_MAX);
>> > -    char *postfix = g_malloc0(PATH_MAX);
>> > -    char *desc_line = g_malloc0(BUF_SIZE);
>> > -    char *ext_filename = g_malloc0(PATH_MAX);
>> > -    char *desc_filename = g_malloc0(PATH_MAX);
>> >      const int64_t split_size = 0x80000000;  /* VMDK has constant split 
>> > size */
>> > -    const char *desc_extent_line;
>> > +    int64_t extent_size;
>> > +    int64_t created_size = 0;
>> > +    const char *extent_line_fmt;
>> >      char *parent_desc_line = g_malloc0(BUF_SIZE);
>> >      uint32_t parent_cid = 0xffffffff;
>> >      uint32_t number_heads = 16;
>> > -    bool zeroed_grain = false;
>> >      uint32_t desc_offset = 0, desc_len;
>> >      const char desc_template[] =
>> >          "# Disk DescriptorFile\n"
>> >          "version=1\n"
>> > -        "CID=%" PRIx32 "\n"
>> > +        "CID=%08" PRIx32 "\n"
>> 
>> How is this change related to the patch's purpose?
>
> It's unrelated. I forgot to remove it after testing: unifying the CID print
> length helps hexdump based compare of monolithicSparse headers.
>
>> 
>> >          "parentCID=%" PRIx32 "\n"
>> >          "createType=\"%s\"\n"
>> >          "%s"
>> > @@ -1955,71 +2004,35 @@ static int coroutine_fn vmdk_co_create_opts(const 
>> > char *filename, QemuOpts *opts
>> >  
>> >      ext_desc_lines = g_string_new(NULL);
>> >  
>> > -    if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, 
>> > errp)) {
>> > -        ret = -EINVAL;
>> > -        goto exit;
>> > -    }
>> >      /* Read out options */
>> > -    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
>> > -                          BDRV_SECTOR_SIZE);
>> > -    adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
>> > -    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
>> > -    hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION);
>> > -    if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) {
>> > -        if (strcmp(hw_version, "undefined")) {
>> > +    if (compat6) {
>> > +        if (hw_version) {
>> >              error_setg(errp,
>> >                         "compat6 cannot be enabled with hwversion set");
>> >              ret = -EINVAL;
>> >              goto exit;
>> >          }
>> > -        g_free(hw_version);
>> > -        hw_version = g_strdup("6");
>> > +        hw_version = "6";
>> >      }
>> > -    if (strcmp(hw_version, "undefined") == 0) {
>> > -        g_free(hw_version);
>> > -        hw_version = g_strdup("4");
>> > -    }
>> > -    fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
>> > -    if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false)) {
>> > -        zeroed_grain = true;
>> > +    if (!hw_version) {
>> > +        hw_version = "4";
>> >      }
>> >  
>> > -    if (!adapter_type) {
>> > -        adapter_type = g_strdup("ide");
>> > -    } else if (strcmp(adapter_type, "ide") &&
>> > -               strcmp(adapter_type, "buslogic") &&
>> > -               strcmp(adapter_type, "lsilogic") &&
>> > -               strcmp(adapter_type, "legacyESX")) {
>> > -        error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
>> > -        ret = -EINVAL;
>> > -        goto exit;
>> > -    }
>> > -    if (strcmp(adapter_type, "ide") != 0) {
>> > +    if (adapter_type != BLOCKDEV_VMDK_ADAPTER_TYPE_IDE) {
>> >          /* that's the number of heads with which vmware operates when
>> >             creating, exporting, etc. vmdk files with a non-ide adapter 
>> > type */
>> >          number_heads = 255;
>> >      }
>> > -    if (!fmt) {
>> > -        /* Default format to monolithicSparse */
>> > -        fmt = g_strdup("monolithicSparse");
>> > -    } else if (strcmp(fmt, "monolithicFlat") &&
>> > -               strcmp(fmt, "monolithicSparse") &&
>> > -               strcmp(fmt, "twoGbMaxExtentSparse") &&
>> > -               strcmp(fmt, "twoGbMaxExtentFlat") &&
>> > -               strcmp(fmt, "streamOptimized")) {
>> > -        error_setg(errp, "Unknown subformat: '%s'", fmt);
>> > -        ret = -EINVAL;
>> > -        goto exit;
>> > -    }
>> > -    split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
>> > -              strcmp(fmt, "twoGbMaxExtentSparse"));
>> > -    flat = !(strcmp(fmt, "monolithicFlat") &&
>> > -             strcmp(fmt, "twoGbMaxExtentFlat"));
>> > -    compress = !strcmp(fmt, "streamOptimized");
>> > +    split = (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT) ||
>> > +            (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE);
>> > +    flat = (subformat == BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT) ||
>> > +           (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT);
>> > +    compress = subformat == BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED;
>> > +
>> >      if (flat) {
>> > -        desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n";
>> > +        extent_line_fmt = "RW %" PRId64 " FLAT \"%s\" 0\n";
>> >      } else {
>> > -        desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n";
>> > +        extent_line_fmt = "RW %" PRId64 " SPARSE \"%s\"\n";
>> >      }
>> >      if (flat && backing_file) {
>> >          error_setg(errp, "Flat image can't have backing file");
>> > @@ -2031,10 +2044,34 @@ static int coroutine_fn vmdk_co_create_opts(const 
>> > char *filename, QemuOpts *opts
>> >          ret = -ENOTSUP;
>> >          goto exit;
>> >      }
>> > +
>> > +    /* Create extents */
>> > +    if (split) {
>> > +        extent_size = split_size;
>> > +    } else {
>> > +        extent_size = size;
>> > +    }
>> > +    if (!split && !flat) {
>> > +        created_size = extent_size;
>> > +    } else {
>> > +        created_size = 0;
>> > +    }
>> > +    /* Get the descriptor file BDS */
>> > +    blk = extent_fn(created_size, 0, flat, split, compress, zeroed_grain,
>> > +                    opaque, errp);
>> > +    if (!blk) {
>> > +        ret = -EIO;
>> > +        goto exit;
>> > +    }
>> > +    if (!split && !flat) {
>> > +        vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, 
>> > created_size,
>> > +                             blk_bs(blk)->filename);
>> > +    }
>> > +
>> >      if (backing_file) {
>> > -        BlockBackend *blk;
>> > +        BlockBackend *backing;
>> >          char *full_backing = g_new0(char, PATH_MAX);
>> > -        bdrv_get_full_backing_filename_from_filename(filename, 
>> > backing_file,
>> > +        
>> > bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename, 
>> > backing_file,
>> >                                                       full_backing, 
>> > PATH_MAX,
>> >                                                       &local_err);
>> >          if (local_err) {
>> > @@ -2044,93 +2081,65 @@ static int coroutine_fn vmdk_co_create_opts(const 
>> > char *filename, QemuOpts *opts
>> >              goto exit;
>> >          }
>> >  
>> > -        blk = blk_new_open(full_backing, NULL, NULL,
>> > -                           BDRV_O_NO_BACKING, errp);
>> > +        backing = blk_new_open(full_backing, NULL, NULL,
>> > +                               BDRV_O_NO_BACKING, errp);
>> >          g_free(full_backing);
>> > -        if (blk == NULL) {
>> > +        if (backing == NULL) {
>> >              ret = -EIO;
>> >              goto exit;
>> >          }
>> > -        if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) {
>> > -            blk_unref(blk);
>> > +        if (strcmp(blk_bs(backing)->drv->format_name, "vmdk")) {
>> > +            error_setg(errp, "Invalid backing file format: %s. Must be 
>> > vmdk",
>> > +                       blk_bs(backing)->drv->format_name);
>> > +            blk_unref(backing);
>> >              ret = -EINVAL;
>> >              goto exit;
>> >          }
>> > -        ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid);
>> > -        blk_unref(blk);
>> > +        ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid);
>> > +        blk_unref(backing);
>> >          if (ret) {
>> > +            error_setg(errp, "Failed to read parent CID");
>> >              goto exit;
>> >          }
>> >          snprintf(parent_desc_line, BUF_SIZE,
>> >                  "parentFileNameHint=\"%s\"", backing_file);
>> >      }
>> > -
>> > -    /* Create extents */
>> > -    filesize = total_size;
>> > -    while (filesize > 0) {
>> > -        int64_t size = filesize;
>> > -
>> > -        if (split && size > split_size) {
>> > -            size = split_size;
>> > -        }
>> > -        if (split) {
>> > -            snprintf(desc_filename, PATH_MAX, "%s-%c%03d%s",
>> > -                    prefix, flat ? 'f' : 's', ++idx, postfix);
>> > -        } else if (flat) {
>> > -            snprintf(desc_filename, PATH_MAX, "%s-flat%s", prefix, 
>> > postfix);
>> > -        } else {
>> > -            snprintf(desc_filename, PATH_MAX, "%s%s", prefix, postfix);
>> > -        }
>> > -        snprintf(ext_filename, PATH_MAX, "%s%s", path, desc_filename);
>> > -
>> > -        if (vmdk_create_extent(ext_filename, size,
>> > -                               flat, compress, zeroed_grain, NULL, opts, 
>> > errp)) {
>> > +    extent_idx = 1;
>> > +    while (created_size < size) {
>> > +        BlockBackend *extent_blk;
>> > +        int64_t cur_size = MIN(size - created_size, extent_size);
>> > +        extent_blk = extent_fn(cur_size, extent_idx, flat, split, 
>> > compress,
>> > +                               zeroed_grain, opaque, errp);
>> > +        if (!extent_blk) {
>> >              ret = -EINVAL;
>> >              goto exit;
>> >          }
>> > -        filesize -= size;
>> > -
>> > -        /* Format description line */
>> > -        snprintf(desc_line, BUF_SIZE,
>> > -                    desc_extent_line, size / BDRV_SECTOR_SIZE, 
>> > desc_filename);
>> > -        g_string_append(ext_desc_lines, desc_line);
>> > +        vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, cur_size,
>> > +                             blk_bs(extent_blk)->filename);
>> > +        created_size += cur_size;
>> > +        extent_idx++;
>> > +        blk_unref(extent_blk);
>> >      }
>> >      /* generate descriptor file */
>> >      desc = g_strdup_printf(desc_template,
>> >                             g_random_int(),
>> >                             parent_cid,
>> > -                           fmt,
>> > +                           vmdk_subformat_str(subformat),
>> >                             parent_desc_line,
>> >                             ext_desc_lines->str,
>> >                             hw_version,
>> > -                           total_size /
>> > +                           size /
>> >                                 (int64_t)(63 * number_heads * 
>> > BDRV_SECTOR_SIZE),
>> >                             number_heads,
>> > -                           adapter_type);
>> > +                           
>> > qapi_enum_lookup(&BlockdevVmdkAdapterType_lookup,
>> > +                                            adapter_type));
>> >      desc_len = strlen(desc);
>> >      /* the descriptor offset = 0x200 */
>> >      if (!split && !flat) {
>> >          desc_offset = 0x200;
>> > -    } else {
>> > -        ret = bdrv_create_file(filename, opts, &local_err);
>> > -        if (ret < 0) {
>> > -            error_propagate(errp, local_err);
>> > -            goto exit;
>> > -        }
>> >      }
>> >  
>> > -    new_blk = blk_new_open(filename, NULL, NULL,
>> > -                           BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
>> > -                           &local_err);
>> > -    if (new_blk == NULL) {
>> > -        error_propagate(errp, local_err);
>> > -        ret = -EIO;
>> > -        goto exit;
>> > -    }
>> > -
>> > -    blk_set_allow_write_beyond_eof(new_blk, true);
>> > -
>> > -    ret = blk_pwrite(new_blk, desc_offset, desc, desc_len, 0);
>> > +    ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0);
>> >      if (ret < 0) {
>> >          error_setg_errno(errp, -ret, "Could not write description");
>> >          goto exit;
>> > @@ -2138,12 +2147,148 @@ static int coroutine_fn vmdk_co_create_opts(const 
>> > char *filename, QemuOpts *opts
>> >      /* bdrv_pwrite write padding zeros to align to sector, we don't need 
>> > that
>> >       * for description file */
>> >      if (desc_offset == 0) {
>> > -        ret = blk_truncate(new_blk, desc_len, PREALLOC_MODE_OFF, errp);
>> > +        ret = blk_truncate(blk, desc_len, PREALLOC_MODE_OFF, errp);
>> >      }
>> >  exit:
>> > -    if (new_blk) {
>> > -        blk_unref(new_blk);
>> > +    if (blk) {
>> > +        blk_unref(blk);
>> >      }
>> > +    g_free(desc);
>> > +    g_free(parent_desc_line);
>> > +    g_string_free(ext_desc_lines, true);
>> > +    return ret;
>> > +}
>> > +
>> > +typedef struct {
>> > +    char *path;
>> > +    char *prefix;
>> > +    char *postfix;
>> > +    QemuOpts *opts;
>> > +} VMDKCreateOptsData;
>> > +
>> > +static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx,
>> > +                                            bool flat, bool split, bool 
>> > compress,
>> > +                                            bool zeroed_grain, void 
>> > *opaque,
>> > +                                            Error **errp)
>> > +{
>> > +    BlockBackend *blk = NULL;
>> > +    BlockDriverState *bs = NULL;
>> > +    VMDKCreateOptsData *data = opaque;
>> > +    char *ext_filename = NULL;
>> > +    char *rel_filename = NULL;
>> > +
>> > +    if (idx == 0) {
>> > +        rel_filename = g_strdup_printf("%s%s", data->prefix, 
>> > data->postfix);
>> > +    } else if (split) {
>> > +        rel_filename = g_strdup_printf("%s-%c%03d%s",
>> > +                                       data->prefix,
>> > +                                       flat ? 'f' : 's', idx, 
>> > data->postfix);
>> > +    } else {
>> > +        assert(idx == 1);
>> > +        rel_filename = g_strdup_printf("%s-flat%s", data->prefix, 
>> > data->postfix);
>> > +    }
>> > +
>> > +    ext_filename = g_strdup_printf("%s%s", data->path, rel_filename);
>> > +    g_free(rel_filename);
>> > +
>> > +    if (vmdk_create_extent(ext_filename, size,
>> > +                           flat, compress, zeroed_grain, &blk, data->opts,
>> > +                           errp)) {
>> > +        goto exit;
>> > +    }
>> > +    bdrv_unref(bs);
>> > +exit:
>> > +    g_free(ext_filename);
>> > +    return blk;
>> > +}
>> > +
>> > +static int coroutine_fn vmdk_co_create_opts(const char *filename, 
>> > QemuOpts *opts,
>> > +                                            Error **errp)
>> > +{
>> > +    Error *local_err = NULL;
>> > +    char *desc = NULL;
>> > +    int64_t total_size = 0;
>> > +    char *adapter_type = NULL;
>> > +    BlockdevVmdkAdapterType adapter_type_enum;
>> > +    char *backing_file = NULL;
>> > +    char *hw_version = NULL;
>> > +    char *fmt = NULL;
>> > +    BlockdevVmdkSubformat subformat;
>> > +    int ret = 0;
>> > +    char *path = g_malloc0(PATH_MAX);
>> > +    char *prefix = g_malloc0(PATH_MAX);
>> > +    char *postfix = g_malloc0(PATH_MAX);
>> > +    char *desc_line = g_malloc0(BUF_SIZE);
>> > +    char *ext_filename = g_malloc0(PATH_MAX);
>> > +    char *desc_filename = g_malloc0(PATH_MAX);
>> > +    char *parent_desc_line = g_malloc0(BUF_SIZE);
>> > +    bool zeroed_grain;
>> > +    bool compat6;
>> > +    int i;
>> > +    VMDKCreateOptsData data;
>> > +
>> > +    if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, 
>> > errp)) {
>> > +        ret = -EINVAL;
>> > +        goto exit;
>> > +    }
>> > +    /* Read out options */
>> > +    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
>> > +                          BDRV_SECTOR_SIZE);
>> > +    adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
>> > +    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
>> > +    hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION);
>> > +    compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false);
>> > +    if (strcmp(hw_version, "undefined") == 0) {
>> > +        g_free(hw_version);
>> > +        hw_version = g_strdup("4");
>> > +    }
>> > +    fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
>> > +    zeroed_grain = qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, 
>> > false);
>> > +
>> > +    if (adapter_type) {
>> > +        for (i = 0; i < strlen(adapter_type); ++i) {
>> > +            adapter_type[i] = qemu_tolower(adapter_type[i]);
>> > +        }
>> 
>> First, you convert to lower cases, and then...
>> 
>> > +        adapter_type_enum = 
>> > qapi_enum_parse_full(&BlockdevVmdkAdapterType_lookup,
>> > +                                                 adapter_type,
>> > +                                                 
>> > BLOCKDEV_VMDK_ADAPTER_TYPE_IDE,
>> > +                                                 true,
>> > +                                                 &local_err);
>> 
>> ... you parse case-insensitive.  Huh?
>
> I forgot to update this one after adding the qapi_enum_parse_full patch.
>
>> 
>> Which spellings did the old code accept?  As far as I can tell, exactly
>> "ide", "lsilogic", "buslogic", "legacyESX".  Are you sure we should
>> ignore case going forward?
>
> So this comes to the same point as subformat: could QAPI do camelCase as in
> "monolithicSparse" and "legacyESX"?

Yes, but you might have to add the type to the name-case-whitelist in
qapi-schema.json.

Additions to name-case-whitelist need a really good reason.  Making code
simpler could be one.

>> > +        if (local_err) {
>> > +            error_propagate(errp, local_err);
>> > +            ret = -EINVAL;
>> > +            goto exit;
>> > +        }
>> > +    } else {
>> > +        adapter_type_enum = BLOCKDEV_VMDK_ADAPTER_TYPE_IDE;
>> > +    }
>> > +
>> > +    if (!fmt) {
>> > +        /* Default format to monolithicSparse */
>> > +        subformat = BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE;
>> > +    } else {
>> > +        subformat = qapi_enum_parse_full(&BlockdevVmdkSubformat_lookup,
>> > +                                         fmt,
>> > +                                         
>> > BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE,
>> > +                                         true,
>> > +                                         &local_err);
>> > +        if (local_err) {
>> > +            error_propagate(errp, local_err);
>> > +            ret = -EINVAL;
>> > +            goto exit;
>> > +        }
>> 
>> Likewise: should we ignore case going forward?  The old code appears to
>> accept exactly "monolithicFlat", "monolithicSparse",
>> "twoGbMaxExtentSparse", "twoGbMaxExtentFlat", "streamOptimized".
>> 
>> > +    }
>> > +    data = (VMDKCreateOptsData){
>> > +        .prefix = prefix,
>> > +        .postfix = postfix,
>> > +        .path = path,
>> > +        .opts = opts,
>> > +    };
>> > +    ret = vmdk_co_do_create(total_size, subformat, adapter_type_enum,
>> > +                            backing_file, hw_version, compat6, 
>> > zeroed_grain,
>> > +                            vmdk_co_create_opts_cb, &data, errp);
>> > +
>> > +exit:
>> >      g_free(adapter_type);
>> >      g_free(backing_file);
>> >      g_free(hw_version);
>> > @@ -2156,7 +2301,84 @@ exit:
>> >      g_free(ext_filename);
>> >      g_free(desc_filename);
>> >      g_free(parent_desc_line);
>> > -    g_string_free(ext_desc_lines, true);
>> > +    return ret;
>> > +}
>> > +
>> > +static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
>> > +                                       bool flat, bool split, bool 
>> > compress,
>> > +                                       bool zeroed_grain, void *opaque,
>> > +                                       Error **errp)
>> > +{
>> > +    int ret;
>> > +    BlockDriverState *bs;
>> > +    BlockBackend *blk;
>> > +    BlockdevCreateOptionsVmdk *opts = opaque;
>> > +
>> > +    if (idx == 0) {
>> > +        bs = bdrv_open_blockdev_ref(opts->file, errp);
>> > +    } else {
>> > +        int i;
>> > +        BlockdevRefList *list = opts->extents;
>> > +        for (i = 1; i < idx; i++) {
>> > +            if (!list || !list->next) {
>> > +                error_setg(errp, "Extent [%d] not specified", i);
>> > +                return NULL;
>> > +            }
>> > +            list = list->next;
>> > +        }
>> > +        if (!list) {
>> > +            error_setg(errp, "Extent [%d] not specified", idx - 1);
>> > +            return NULL;
>> > +        }
>> > +        bs = bdrv_open_blockdev_ref(list->value, errp);
>> > +    }
>> > +    if (!bs) {
>> > +        return NULL;
>> > +    }
>> > +    blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | 
>> > BLK_PERM_RESIZE,
>> > +                  BLK_PERM_ALL);
>> > +    if (blk_insert_bs(blk, bs, errp)) {
>> > +        bdrv_unref(bs);
>> > +        return NULL;
>> > +    }
>> > +    blk_set_allow_write_beyond_eof(blk, true);
>> > +    bdrv_unref(bs);
>> > +
>> > +    ret = vmdk_init_extent(blk, size, flat, compress, zeroed_grain, errp);
>> > +    if (ret) {
>> > +        blk_unref(blk);
>> > +        blk = NULL;
>> > +    }
>> > +    return blk;
>> > +}
>> > +
>> > +static int coroutine_fn vmdk_co_create(BlockdevCreateOptions 
>> > *create_options,
>> > +                                       Error **errp)
>> > +{
>> > +    int ret;
>> > +    BlockdevCreateOptionsVmdk *opts;
>> > +
>> > +    opts = &create_options->u.vmdk;
>> > +
>> > +    /* Validate options */
>> > +    if (!QEMU_IS_ALIGNED(opts->size, BDRV_SECTOR_SIZE)) {
>> > +        error_setg(errp, "Image size must be a multiple of 512 bytes");
>> > +        ret = -EINVAL;
>> > +        goto out;
>> > +    }
>> > +
>> > +    ret = vmdk_co_do_create(opts->size,
>> > +                            opts->subformat,
>> > +                            opts->adapter_type,
>> > +                            opts->backing_file,
>> > +                            opts->hwversion,
>> > +                            false,
>> > +                            opts->zeroed_grain,
>> > +                            vmdk_co_create_cb,
>> > +                            opts, errp);
>> > +    return ret;
>> > +
>> > +out:
>> >      return ret;
>> >  }
>> >  
>> > @@ -2424,6 +2646,7 @@ static BlockDriver bdrv_vmdk = {
>> >      .bdrv_co_pwrite_zeroes        = vmdk_co_pwrite_zeroes,
>> >      .bdrv_close                   = vmdk_close,
>> >      .bdrv_co_create_opts          = vmdk_co_create_opts,
>> > +    .bdrv_co_create               = vmdk_co_create,
>> >      .bdrv_co_flush_to_disk        = vmdk_co_flush,
>> >      .bdrv_co_block_status         = vmdk_co_block_status,
>> >      .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
>> > diff --git a/qapi/block-core.json b/qapi/block-core.json
>> > index c50517bff3..df3903b54d 100644
>> > --- a/qapi/block-core.json
>> > +++ b/qapi/block-core.json
>> > @@ -3855,6 +3855,71 @@
>> >              'size':             'size',
>> >              '*cluster-size' :   'size' } }
>> >  
>> > +##
>> > +# @BlockdevVmdkSubformat:
>> > +#
>> > +# Subformat options for VMDK images
>> > +#
>> > +# @monolithicsparse: Single file image with sparse cluster allocation
>> > +# @monolithicflat: Single flat data image and a descriptor file
>> > +# @twogbmaxextentsparse: Data is split into 2GB (per virtual LBA) sparse 
>> > extent
>> > +#                        files, in addition to a descriptor file
>> > +# @twogbmaxextentflat: Data is split into 2GB (per virtual LBA) flat 
>> > extent
>> > +#                        files, in addition to a descriptor file
>> > +# @streamoptimized: Single file image sparse cluster allocation, 
>> > optimized for
>> > +#                   streaming over network.
>> > +#
>> > +# Since: 2.13
>> > +##
>> > +{ 'enum': 'BlockdevVmdkSubformat',
>> > +  'data': [ 'monolithicsparse', 'monolithicflat', 'twogbmaxextentsparse',
>> > +            'twogbmaxextentflat', 'streamoptimized'] }
>> 
>> alllowercasewithoutspacesisevenlesslegiblethanCamelCase.
>> THERESULTINGCIDENTIFIERSAREALLCAPSWITHOUTSPACESWHICHISEVENWORSE.
>> 
>> QAPI conventions ask for monolithic-sparse, monolithic-flat,
>> two-gb-max-extent-sparse and so forth.  Results in C enum identifiers
>> BLOCKDEV_VMDK_SUBFORMAT_MONOLITHIC_SPARSE,
>> BLOCKDEV_VMDK_SUBFORMAT_MONOLITHIC_FLAT,
>> BLOCKDEV_VMDK_SUBFORMAT_TWO_GB_MAX_EXTENT_SPARSE, ...
>> 
>> The existing external interface appears to ask for monolithicFlat,
>> monolithicSparse, twoGbMaxExtentSparse, ...  What's the best way to map
>> between these guys and a QAPI enum?
>
> It would be best if we can stick to monolithicSparse everywhere. Can we?

Please try.  The generated C identifiers will be ugly, but a bit of
ugliness is probably less bad than conversion code.

>> > +
>> > +##
>> > +# @BlockdevVmdkAdapterType:
>> > +#
>> > +# Adapter type info for VMDK images
>> > +#
>> > +# Since: 2.13
>> > +##
>> > +{ 'enum': 'BlockdevVmdkAdapterType',
>> > +  'data': [ 'ide', 'buslogic', 'lsilogic', 'legacyesx'] }
>> > +
>> > +##
>> > +# @BlockdevCreateOptionsVmdk:
>> > +#
>> > +# Driver specific image creation options for VMDK.
>> > +#
>> > +# @file         Where to store the new image file. This refers to the 
>> > image
>> > +#               file for monolithcSparse and streamOptimized format, or 
>> > the
>> > +#               descriptor file for other formats.
>> > +# @size         Size of the virtual disk in bytes
>> > +# @extents      Where to store the data extents. Required for 
>> > monolithcflat,
>> > +#               twoGbMaxExtentSparse and twoGbMaxExtentFlat formats. For
>> > +#               monolithicflat, only one entry is required; for
>> > +#               twoGbMaxExtent* formats, the number of entries required is
>> > +#               calculated as extent_number = virtual_size / 2GB.
>> > +# @subformat    The subformat of the VMDK image. Default: 
>> > "monolithicsparse".
>> > +# @backing-file The path of backing file. Default: no backing file is 
>> > used.
>> > +# @adapter-type The adapter type used to fill in the descriptor. Default: 
>> > ide.
>> > +# @hwversion    Hardware version. The meaningful options are "4" or "6".
>> 
>> Okay, these are the meaningfull options.  What are the meaningless ones?
>
> I don't know. Historically we've used '3', I have never seen '5'. VMware
> articles mention '7' [1]. This is not documented anywhere, so I'm only
> listing what has been used by QEMU.
>
> [1]: https://kb.vmware.com/s/article/1026254

What about

    # @hwversion    Hardware version.  Recognized values are "4" and "6".

>> 
>> > +#               Defaulted to "4".
>> 
>> More common phrasings are
>> 
>>     Default is "4"
>>     Defaults to "4"
>>     Default: "4"
>
> OK.
>
>> 
>> > +# @zeroed-grain Whether to enable zeroed-grain feature for sparse 
>> > subformats.
>> > +#               Default: false.
>> > +#
>> > +# Since: 2.13
>> > +##
>> > +{ 'struct': 'BlockdevCreateOptionsVmdk',
>> > +  'data': { 'file':             'BlockdevRef',
>> > +            'size':             'size',
>> > +            '*extents':          ['BlockdevRef'],
>> > +            '*subformat':       'BlockdevVmdkSubformat',
>> > +            '*backing-file':    'str',
>> > +            '*adapter-type':    'BlockdevVmdkAdapterType',
>> > +            '*hwversion':       'str',
>> > +            '*zeroed-grain':    'bool' } }
>> > +
>> > +
>> >  ##
>> >  # @SheepdogRedundancyType:
>> >  #
>> > @@ -4078,7 +4143,7 @@
>> >        'throttle':       'BlockdevCreateNotSupported',
>> >        'vdi':            'BlockdevCreateOptionsVdi',
>> >        'vhdx':           'BlockdevCreateOptionsVhdx',
>> > -      'vmdk':           'BlockdevCreateNotSupported',
>> > +      'vmdk':           'BlockdevCreateOptionsVmdk',
>> >        'vpc':            'BlockdevCreateOptionsVpc',
>> >        'vvfat':          'BlockdevCreateNotSupported',
>> >        'vxhs':           'BlockdevCreateNotSupported'
>
> Fam



reply via email to

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