[libvirt] [PATCHv2 05/11] qemu_monitor: CPUModelExpansion on both model name and properties

Jiri Denemark jdenemar at redhat.com
Thu Jul 12 16:26:32 UTC 2018


On Mon, Jul 09, 2018 at 22:56:49 -0500, Chris Venteicher wrote:
> Send both model name and a set of features/properties to QEMU for
> expansion rather than just the model name.
> 
> Required to expand name+props models of the form computed by baseline
> into fully expanded (all props/features listed) output.

This patch is doing too much at once and is quite hard to review.

> ---
>  src/qemu/qemu_capabilities.c | 42 +++++++++++++++++-----
>  src/qemu/qemu_monitor.c      | 38 ++++++++++++++++----
>  src/qemu/qemu_monitor.h      |  5 ++-
>  src/qemu/qemu_monitor_json.c | 69 ++++++++++++++++++++++++------------
>  src/qemu/qemu_monitor_json.h |  7 ++--
>  tests/cputest.c              |  7 +++-
>  6 files changed, 122 insertions(+), 46 deletions(-)
> 
> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
> index 3d78e2e29b..72ab012a78 100644
> --- a/src/qemu/qemu_capabilities.c
> +++ b/src/qemu/qemu_capabilities.c
> @@ -2343,23 +2343,32 @@ virQEMUCapsProbeQMPHostCPU(virQEMUCapsPtr qemuCaps,
>      qemuMonitorCPUModelInfoPtr modelInfo = NULL;
>      qemuMonitorCPUModelInfoPtr nonMigratable = NULL;
>      virHashTablePtr hash = NULL;
> -    const char *model;
> +    const char *model_name;

Why? If we really want to do this, it should go into a separate patch
explaining why it is needed.

>      qemuMonitorCPUModelExpansionType type;
>      virDomainVirtType virtType;
>      virQEMUCapsHostCPUDataPtr cpuData;
>      int ret = -1;
> +    int err = -1;

We usually call such variable 'rc' and leave it uninitialized.

>  
>      if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION))
>          return 0;
>  
>      if (tcg || !virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
>          virtType = VIR_DOMAIN_VIRT_QEMU;
> -        model = "max";
> +        model_name = "max";
>      } else {
>          virtType = VIR_DOMAIN_VIRT_KVM;
> -        model = "host";
> +        model_name = "host";
>      }
>  
> +    if ((VIR_ALLOC(modelInfo) < 0) ||
> +        (VIR_ALLOC(nonMigratable) < 0))
> +        goto cleanup;
> +
> +    if ((qemuMonitorCPUModelInfoInit(model_name, modelInfo) < 0) ||
> +        (qemuMonitorCPUModelInfoInit(model_name, nonMigratable) < 0))
> +        goto cleanup;

Redundant parentheses. But anyway, VIR_ALLOC +
qemuMonitorCPUModelInfoInit combo should be replaced with a single
qemuMonitorCPUModelInfoNew function which would do both. As a bonus
point, you could never end up with a non-NULL modelInfo structure with
name == NULL.

> +
>      cpuData = virQEMUCapsGetHostCPUData(qemuCaps, virtType);
>  
>      /* Some x86_64 features defined in cpu_map.xml use spelling which differ
> @@ -2372,16 +2381,31 @@ virQEMUCapsProbeQMPHostCPU(virQEMUCapsPtr qemuCaps,
>      else
>          type = QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC;
>  
> -    if (qemuMonitorGetCPUModelExpansion(mon, type, model, true, &modelInfo) < 0)
> +    if ((err = qemuMonitorGetCPUModelExpansion(mon, type, true, modelInfo)) < 0)
>          goto cleanup;
>  
> -    /* Try to check migratability of each feature. */
> -    if (modelInfo &&
> -        qemuMonitorGetCPUModelExpansion(mon, type, model, false,
> -                                        &nonMigratable) < 0)
> +    if (err == 1) {
> +        ret = 0;      /* Qemu can't do expansion 1, exit without error */
> +        goto cleanup; /* We don't have info so don't update cpuData->info */
> +    }

It's not very clear to me why qemuMonitorGetCPUModelExpansion needs to
report 1. A separate patch with an explanation would be better.

> +
> +    if ((err = qemuMonitorGetCPUModelExpansion(mon, type, false, nonMigratable)) < 0)
>          goto cleanup;
>  
> -    if (nonMigratable) {
> +    /* Try to check migratability of each feature */
> +    /* Expansion 1 sets migratable features true
> +     * Expansion 2 sets migratable and non-migratable features true
> +     *             (non-migratable set true only in some archs like X86)
> +     *
> +     * If delta between Expansion 1 and 2 exists...
> +     * - both migratable and non-migratable features set prop->value = true
> +     * - migratable features set prop->migatable = VIR_TRISTATE_BOOL_YES
> +     * - non-migratable features set prop->migatable = VIR_TRISTATE_BOOL_NO
> +     */
> +    if (err == 0) {
> +        /* Expansion 2 succeded
> +         * Qemu expanded both migratable and nonMigratable features */

This comment seems redundant. It's pretty clear from the code that both
expansions were successful at this point.

> +
>          qemuMonitorCPUPropertyPtr prop;
>          qemuMonitorCPUPropertyPtr nmProp;
>          size_t i;
> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
> index 2d9297c3a7..91b946c8b4 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -3619,20 +3619,46 @@ qemuMonitorCPUDefInfoFree(qemuMonitorCPUDefInfoPtr cpu)
>  }
>  
>  
> +/*
> + * type static:
> + *   Expand to static base model + delta property changes
> + *   Returned model is invariant and migration safe
> + *
> + *   model_info->name = base model name
> + *   model_info->props = features to +/- to base model to achive model_name
> + *
> + * type full:
> + *   Expand model to enumerate all properties
> + *   Returned model isn't guaranteed to be invariant or migration safe.
> + *
> + *   model_info->name = base model name
> + *   model_info->props = features to +/- to empty set to achive model_name
> + *
> + * type static_full:
> + *   Expand to static base model + delta property changes (pass 0)
> + *   Expand model to enumerate all properties (pass 1)
> + *   Returned model is invariant and migration safe
> + *
> + *   model_info->name = base model name
> + *   model_info->props = features to +/- to empty set to achive model_name
> + *
> + * migratable_only:
> + *   true: QEMU excludes non-migratable features
> + *   false: QEMU includes non-migratable features for some archs like X86
> + */

Function description in our public API format and using real
qemuMonitorCPUModelExpansionType values would be a lot better.

>  int
>  qemuMonitorGetCPUModelExpansion(qemuMonitorPtr mon,
>                                  qemuMonitorCPUModelExpansionType type,
> -                                const char *model_name,
> -                                bool migratable,
> -                                qemuMonitorCPUModelInfoPtr *model_info)
> +                                bool migratable_only,

Why do you need to rename migratable as migratable_only? The migratable
bool selects whether the CPU model returned by QEMU has to be migratable
or not. In any case it would be a separate change.

> +                                qemuMonitorCPUModelInfoPtr model_info)

If you keep the double pointer there, you could remove some hacks and
comments to explain these hacks in the JSON monitor implementation. And
you wouldn't need to introduce qemuMonitorCPUModelInfoFreeContents.

>  {
>      VIR_DEBUG("type=%d model_name=%s migratable=%d",
> -              type, model_name, migratable);
> +              type, (model_info ? NULLSTR(model_info->name):"NULL"),

The function is not supposed to be called with model_info == NULL, the
check here is useless. Not to mention that if model_info was NULL,
qemuMonitorJSONGetCPUModelExpansion would segfault anyway.

And I believe the model_info->name can't be NULL either.

All this would of course change with the double pointer and additionally
this function should check a proper value was passed in.

> +              migratable_only);
>  
>      QEMU_CHECK_MONITOR(mon);
>  
> -    return qemuMonitorJSONGetCPUModelExpansion(mon, type, model_name,
> -                                               migratable, model_info);
> +    return qemuMonitorJSONGetCPUModelExpansion(mon, type, migratable_only, model_info);
>  }
>  
>  
> diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
> index 0b84a91fbc..6b4b527512 100644
> --- a/src/qemu/qemu_monitor.h
> +++ b/src/qemu/qemu_monitor.h
> @@ -1016,9 +1016,8 @@ typedef enum {
>  
>  int qemuMonitorGetCPUModelExpansion(qemuMonitorPtr mon,
>                                      qemuMonitorCPUModelExpansionType type,
> -                                    const char *model_name,
> -                                    bool migratable,
> -                                    qemuMonitorCPUModelInfoPtr *model_info);
> +                                    bool migratable_only,
> +                                    qemuMonitorCPUModelInfoPtr model_info);
>  
>  void qemuMonitorCPUModelInfoFree(qemuMonitorCPUModelInfoPtr model_info);
>  void qemuMonitorCPUModelInfoFreeContents(qemuMonitorCPUModelInfoPtr model_info);
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index 90d43eee97..9b681f4592 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -5390,8 +5390,11 @@ qemuMonitorJSONBuildCPUModelInfoToJSON(qemuMonitorCPUModelInfoPtr model)
>          }
>      }
>  
> -    ignore_value(virJSONValueObjectCreate(&model_json, "s:name", model->name,
> -                                          "a:props", &cpu_props, NULL));
> +    if (model->nprops > 0)
> +        ignore_value(virJSONValueObjectCreate(&model_json, "s:name", model->name,
> +                                              "a:props", &cpu_props, NULL));
> +    else
> +        ignore_value(virJSONValueObjectCreate(&model_json, "s:name", model->name, NULL));

Just don't fill in cpu_props at all and then you can use "A:props" to
cover both cases. Anyway, this can be done in a separate patch.

>  
>   cleanup:
>      virJSONValueFree(cpu_props);
> @@ -5440,38 +5443,52 @@ qemuMonitorJSONBuildCPUModelInfoFromJSON(virJSONValuePtr cpu_model)
>      return model;
>  }
>  
> +
> +/* return:
> + * -1 - Execution Failure
> + *  0 - Success
> + *  1 - Qemu unable to do expansion leaving "model" unmodified
> + */
>  int
>  qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
>                                      qemuMonitorCPUModelExpansionType type,
> -                                    const char *model_name,
> -                                    bool migratable,
> -                                    qemuMonitorCPUModelInfoPtr *model_info)
> +                                    bool migratable_only,
> +                                    qemuMonitorCPUModelInfoPtr model)
>  {
>      int ret = -1;
> -    virJSONValuePtr model = NULL;
> -    virJSONValuePtr props = NULL;
> +    virJSONValuePtr json_model = NULL;
>      virJSONValuePtr cmd = NULL;
>      virJSONValuePtr reply = NULL;
>      virJSONValuePtr data;
>      virJSONValuePtr cpu_model;
> +    qemuMonitorCPUModelInfoPtr expanded_model = NULL;
> +    qemuMonitorCPUModelInfoPtr model_info = NULL;
>      const char *typeStr = "";
>  
> -    *model_info = NULL;
> +    if (!(model_info = qemuMonitorCPUModelInfoCopy(model)))
> +        return -1;
> +
> +    qemuMonitorCPUModelInfoFreeContents(model);

We usually don't touch output parameters until we know the function
succeeded. Double pointer would let you comply with this.

>  
> -    if (!(model = virJSONValueNewObject()))
> -        goto cleanup;
> +    if (!migratable_only) {
> +        /* Add property to input CPUModelInfo causing QEMU to include
> +         * non-migratable properties for some architectures like X86 */

This comment doesn't really belong here. It's the caller's decision to
run this command with appropriate arguments for each architecture.

>  
> -    if (virJSONValueObjectAppendString(model, "name", model_name) < 0)
> -        goto cleanup;
> +        qemuMonitorCPUProperty prop;
> +        prop.type = QEMU_MONITOR_CPU_PROPERTY_BOOLEAN;
> +        prop.value.boolean = false;
> +        prop.migratable = false;
> +
> +        if (VIR_STRDUP(prop.name, "migratable") < 0)
> +            goto cleanup;
>  
> -    if (!migratable) {
> -        if (!(props = virJSONValueNewObject()) ||
> -            virJSONValueObjectAppendBoolean(props, "migratable", false) < 0 ||
> -            virJSONValueObjectAppend(model, "props", props) < 0)
> +        if (VIR_APPEND_ELEMENT(model_info->props, model_info->nprops, prop) < 0)

You'd leak prop.name here.

>              goto cleanup;
> -        props = NULL;
>      }
>  
> +    if (!(json_model = qemuMonitorJSONBuildCPUModelInfoToJSON(model_info)))
> +        goto cleanup;
> +
>   retry:
>      switch (type) {
>      case QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC:
> @@ -5486,7 +5503,7 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
>  
>      if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-expansion",
>                                             "s:type", typeStr,
> -                                           "a:model", &model,
> +                                           "a:model", &json_model,
>                                             NULL)))
>          goto cleanup;
>  
> @@ -5498,7 +5515,7 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
>       * guest architecture or it is not supported in the host environment.
>       */
>      if (qemuMonitorJSONHasError(reply, "GenericError")) {
> -        ret = 0;
> +        ret = 1;
>          goto cleanup;
>      }
>  
> @@ -5517,7 +5534,9 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
>       * on the result of the initial "static" expansion.
>       */
>      if (type == QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL) {
> -        if (!(model = virJSONValueCopy(cpu_model)))
> +        virJSONValueFree(json_model);
> +
> +        if (!(json_model = virJSONValueCopy(cpu_model)))
>              goto cleanup;
>  
>          virJSONValueFree(cmd);
> @@ -5526,16 +5545,20 @@ qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
>          goto retry;
>      }
>  
> -    if (!(*model_info = qemuMonitorJSONBuildCPUModelInfoFromJSON(cpu_model)))
> +    if (!(expanded_model = qemuMonitorJSONBuildCPUModelInfoFromJSON(cpu_model)))
>          goto cleanup;
>  
> +    *model = *expanded_model; /* overwrite contents */
> +
>      ret = 0;
>  
>   cleanup:
> +    VIR_FREE(expanded_model); /* Free structure but not reused contents */
> +    qemuMonitorCPUModelInfoFreeContents(model_info);
> +
>      virJSONValueFree(cmd);
>      virJSONValueFree(reply);
> -    virJSONValueFree(model);
> -    virJSONValueFree(props);
> +    virJSONValueFree(json_model);
>      return ret;
>  }
>  
> diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
> index 73e1cb6ace..9950483c5c 100644
> --- a/src/qemu/qemu_monitor_json.h
> +++ b/src/qemu/qemu_monitor_json.h
> @@ -362,10 +362,9 @@ int qemuMonitorJSONGetCPUDefinitions(qemuMonitorPtr mon,
>  
>  int qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon,
>                                          qemuMonitorCPUModelExpansionType type,
> -                                        const char *model_name,
> -                                        bool migratable,
> -                                        qemuMonitorCPUModelInfoPtr *model_info)
> -    ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(5);
> +                                        bool migratable_only,
> +                                        qemuMonitorCPUModelInfoPtr model_info)
> +    ATTRIBUTE_NONNULL(4);
>  
>  int qemuMonitorJSONGetCPUModelBaseline(qemuMonitorPtr mon,
>                                         qemuMonitorCPUModelInfoPtr model_a,
> diff --git a/tests/cputest.c b/tests/cputest.c
> index baf2b3c648..27727aa29e 100644
> --- a/tests/cputest.c
> +++ b/tests/cputest.c
> @@ -495,9 +495,14 @@ cpuTestMakeQEMUCaps(const struct data *data)
>      if (!(testMon = qemuMonitorTestNewFromFile(json, driver.xmlopt, true)))
>          goto error;
>  
> +    if ((VIR_ALLOC(model) < 0) ||
> +        (qemuMonitorCPUModelInfoInit("host", model) < 0))
> +        goto cleanup;
> +
> +

One empty line would be enough.

>      if (qemuMonitorGetCPUModelExpansion(qemuMonitorTestGetMonitor(testMon),
>                                          QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC,
> -                                        "host", true, &model) < 0)
> +                                        true, model) < 0)
>          goto error;
>  
>      if (!(qemuCaps = virQEMUCapsNew()))

Jirka




More information about the libvir-list mailing list