[libvirt] [PATCH] cpu: Improve error reporting on incompatible CPUs

Jiri Denemark jdenemar at redhat.com
Thu Apr 19 13:45:37 UTC 2012


On Wed, Apr 18, 2012 at 15:19:53 +0200, Peter Krempa wrote:
> This patch modifies the CPU comparrison function to report the
> incompatibilities in more detail to ease identification of problems.
> 
> * src/cpu/cpu.h:
>     cpuGuestData(): Add argument to return detailed error message.
> * src/cpu/cpu.c:
>     cpuGuestData(): Add passthrough for error argument.
> * src/cpu/cpu_x86.c
>     x86FeatureNames(): Add function to convert a CPU definition to flag
>                        names.
>     x86Compute(): - Add error message parameter
>                   - Add macro for reporting detailed error messages.
>                   - Improve error reporting.
>                   - Simplify calculation of forbidden flags.
>     x86DataIteratorInit():
>     x86cpuidMatchAny(): Remove functions that are no longer needed.
> * src/qemu/qemu_command.c:
>     qemuBuildCpuArgStr(): - Modify for new function prototype
>                           - Add detailed error reports
>                           - Change error code on incompatible processors
>                             to VIR_ERR_CONFIG_UNSUPPORTED instead of
>                             internal error
> * tests/cputest.c:
>     cpuTestGuestData(): Modify for new function prototype
> ---
> Sample of error message:
> $ virsh start Bare
> error: Failed to start domain Bare
> error: unsupported configuration: guest and host CPU are not compatible: Host CPU does not provide required features: svm, avx
> 
> 
>  src/cpu/cpu.c           |    7 ++-
>  src/cpu/cpu.h           |    6 ++-
>  src/cpu/cpu_x86.c       |  123 +++++++++++++++++++++++++++++++----------------
>  src/qemu/qemu_command.c |   12 ++++-
>  tests/cputest.c         |    2 +-
>  5 files changed, 100 insertions(+), 50 deletions(-)
> 
> diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
> index 01c31bb..b8ccd24 100644
> --- a/src/cpu/cpu.c
> +++ b/src/cpu/cpu.c
> @@ -248,11 +248,12 @@ cpuNodeData(const char *arch)
>  virCPUCompareResult
>  cpuGuestData(virCPUDefPtr host,
>               virCPUDefPtr guest,
> -             union cpuData **data)
> +             union cpuData **data,
> +             char **msg)
>  {
>      struct cpuArchDriver *driver;
> 
> -    VIR_DEBUG("host=%p, guest=%p, data=%p", host, guest, data);
> +    VIR_DEBUG("host=%p, guest=%p, data=%p, msg=%p", host, guest, data, msg);
> 
>      if ((driver = cpuGetSubDriver(host->arch)) == NULL)
>          return VIR_CPU_COMPARE_ERROR;
> @@ -264,7 +265,7 @@ cpuGuestData(virCPUDefPtr host,
>          return VIR_CPU_COMPARE_ERROR;
>      }
> 
> -    return driver->guestData(host, guest, data);
> +    return driver->guestData(host, guest, data, msg);
>  }
> 
> 
> diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
> index 9f01f17..d7bc54e 100644
> --- a/src/cpu/cpu.h
> +++ b/src/cpu/cpu.h
> @@ -70,7 +70,8 @@ typedef union cpuData *
>  typedef virCPUCompareResult
>  (*cpuArchGuestData) (virCPUDefPtr host,
>                       virCPUDefPtr guest,
> -                     union cpuData **data);
> +                     union cpuData **data,
> +                     char **message);
> 
>  typedef virCPUDefPtr
>  (*cpuArchBaseline)  (virCPUDefPtr *cpus,
> @@ -138,7 +139,8 @@ cpuNodeData (const char *arch);
>  extern virCPUCompareResult
>  cpuGuestData(virCPUDefPtr host,
>               virCPUDefPtr guest,
> -             union cpuData **data);
> +             union cpuData **data,
> +             char **msg);
> 
>  extern char *
>  cpuBaselineXML(const char **xmlCPUs,
> diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c
> index 8d92649..e1500bf 100644
> --- a/src/cpu/cpu_x86.c
> +++ b/src/cpu/cpu_x86.c
> @@ -31,6 +31,7 @@
>  #include "cpu.h"
>  #include "cpu_map.h"
>  #include "cpu_x86.h"
> +#include "buf.h"
> 
> 
>  #define VIR_FROM_THIS VIR_FROM_CPU
> @@ -89,16 +90,6 @@ struct data_iterator {
>      { data, -1, false }
> 
> 
> -static void
> -x86DataIteratorInit(struct data_iterator *iter,
> -                    union cpuData *data)
> -{
> -    struct data_iterator init = DATA_ITERATOR_INIT(data);
> -
> -    *iter = init;
> -}
> -
> -
>  static int
>  x86cpuidMatch(const struct cpuX86cpuid *cpuid1,
>                const struct cpuX86cpuid *cpuid2)
> @@ -121,17 +112,6 @@ x86cpuidMatchMasked(const struct cpuX86cpuid *cpuid,
>  }
> 
> 
> -static int
> -x86cpuidMatchAny(const struct cpuX86cpuid *cpuid,
> -                 const struct cpuX86cpuid *mask)
> -{
> -    return ((cpuid->eax & mask->eax) ||
> -            (cpuid->ebx & mask->ebx) ||
> -            (cpuid->ecx & mask->ecx) ||
> -            (cpuid->edx & mask->edx));
> -}
> -
> -
>  static void
>  x86cpuidSetBits(struct cpuX86cpuid *cpuid,
>                  const struct cpuX86cpuid *mask)
> @@ -649,6 +629,34 @@ x86FeatureFind(const struct x86_map *map,
>  }
> 
> 
> +static char *
> +x86FeatureNames(const struct x86_map *map,
> +                const char *separator,
> +                union cpuData *data)
> +{
> +    virBuffer ret = VIR_BUFFER_INITIALIZER;
> +    bool first = true;
> +
> +    struct x86_feature *next_feature = map->features;
> +
> +    virBufferAdd(&ret, "", 0);
> +
> +    while (next_feature) {
> +        if (x86DataIsSubset(data, next_feature->data)) {
> +            if (!first)
> +                virBufferAdd(&ret, separator, -1);
> +            else
> +                first = false;
> +
> +            virBufferAdd(&ret, next_feature->name, -1);
> +        }
> +        next_feature = next_feature->next;
> +    }
> +
> +    return virBufferContentAndReset(&ret);
> +}
> +
> +
>  static int
>  x86FeatureLoad(xmlXPathContextPtr ctxt,
>                 struct x86_map *map)
> @@ -1115,10 +1123,34 @@ error:
>  }
> 
> 
> +/* A helper macro to exit the cpu computation function without writing
> + * redundant code:
> + * MSG: error message
> + * CPU_DEF: a union cpuData pointer with flags that are conflicting
> + * RET: return code to set
> + *
> + * This macro generates the error string outputs it into logs.
> + */
> +#define virX86CpuIncompatible(MSG, CPU_DEF)                             \
> +        do {                                                            \
> +            char *flagsStr = NULL;                                      \
> +            if (!(flagsStr = x86FeatureNames(map, ", ", (CPU_DEF))))    \
> +                goto no_memory;                                         \
> +            if (message &&                                              \
> +                virAsprintf(message, "%s: %s", _(MSG), flagsStr) < 0) { \
> +                VIR_FREE(flagsStr);                                     \
> +                goto no_memory;                                         \
> +            }                                                           \
> +            VIR_DEBUG("%s: %s", MSG, flagsStr);                         \
> +            VIR_FREE(flagsStr);                                         \
> +            ret = VIR_CPU_COMPARE_INCOMPATIBLE;                         \
> +        } while (0);

I can't think of any case where ";;" would be a problem but I'd remove the ';'
from the end of this macro anyway

> +
>  static virCPUCompareResult
>  x86Compute(virCPUDefPtr host,
>             virCPUDefPtr cpu,
> -           union cpuData **guest)
> +           union cpuData **guest,
> +           char **message)
>  {
>      struct x86_map *map = NULL;
>      struct x86_model *host_model = NULL;
> @@ -1129,8 +1161,6 @@ x86Compute(virCPUDefPtr host,
>      struct x86_model *cpu_forbid = NULL;
>      struct x86_model *diff = NULL;
>      struct x86_model *guest_model = NULL;
> -    struct data_iterator iter;
> -    const struct cpuX86cpuid *cpuid;
>      virCPUCompareResult ret;
>      enum compare_result result;
>      unsigned int i;
> @@ -1147,6 +1177,11 @@ x86Compute(virCPUDefPtr host,
> 
>          if (!found) {
>              VIR_DEBUG("CPU arch %s does not match host arch", cpu->arch);
> +            if (message &&
> +                virAsprintf(message,
> +                            _("CPU arch %s does not match host arch"),
> +                            cpu->arch) < 0)
> +                goto no_memory;
>              return VIR_CPU_COMPARE_INCOMPATIBLE;
>          }
>      }
> @@ -1155,6 +1190,12 @@ x86Compute(virCPUDefPtr host,
>          (!host->vendor || STRNEQ(cpu->vendor, host->vendor))) {
>          VIR_DEBUG("host CPU vendor does not match required CPU vendor %s",
>                    cpu->vendor);
> +        if (message &&
> +            virAsprintf(message,
> +                        _("host CPU vendor does not match required "
> +                          "CPU vendor %s"),
> +                        cpu->vendor) < 0)
> +            goto no_memory;
>          return VIR_CPU_COMPARE_INCOMPATIBLE;
>      }
> 
> @@ -1167,24 +1208,19 @@ x86Compute(virCPUDefPtr host,
>          !(cpu_forbid = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORBID)))
>          goto error;
> 
> -    x86DataIteratorInit(&iter, cpu_forbid->data);
> -    while ((cpuid = x86DataCpuidNext(&iter))) {
> -        const struct cpuX86cpuid *cpuid2;
> -
> -        cpuid2 = x86DataCpuid(host_model->data, cpuid->function);
> -        if (cpuid2 != NULL && x86cpuidMatchAny(cpuid2, cpuid)) {
> -            VIR_DEBUG("Host CPU provides forbidden features in CPUID function 0x%x",
> -                      cpuid->function);
> -            ret = VIR_CPU_COMPARE_INCOMPATIBLE;
> -            goto out;
> -        }
> +    x86DataIntersect(cpu_forbid->data, host_model->data);
> +    if (!x86DataIsEmpty(cpu_forbid->data)) {
> +        virX86CpuIncompatible("Host CPU provides forbidden features",

You need to mark the string as translatable with N_("...")

> +                              cpu_forbid->data);
> +        goto out;
>      }
> 
>      x86DataSubtract(cpu_require->data, cpu_disable->data);
>      result = x86ModelCompare(host_model, cpu_require);
>      if (result == SUBSET || result == UNRELATED) {
> -        VIR_DEBUG("Host CPU does not provide all required features");
> -        ret = VIR_CPU_COMPARE_INCOMPATIBLE;
> +        x86DataSubtract(cpu_require->data, host_model->data);
> +        virX86CpuIncompatible("Host CPU does not provide required features",

N_()

> +                              cpu_require->data);
>          goto out;
>      }
> 
> @@ -1204,8 +1240,9 @@ x86Compute(virCPUDefPtr host,
>      if (ret == VIR_CPU_COMPARE_SUPERSET
>          && cpu->type == VIR_CPU_TYPE_GUEST
>          && cpu->match == VIR_CPU_MATCH_STRICT) {
> -        VIR_DEBUG("Host CPU does not strictly match guest CPU");
> -        ret = VIR_CPU_COMPARE_INCOMPATIBLE;
> +        virX86CpuIncompatible("Host CPU does not strictly match guest CPU: "
> +                              "Extra features",

N_()

> +                              diff->data);
>          goto out;
>      }
> 
> @@ -1246,22 +1283,24 @@ error:
>      ret = VIR_CPU_COMPARE_ERROR;
>      goto out;
>  }
> +#undef virX86CpuIncompatible
> 
> 
>  static virCPUCompareResult
>  x86Compare(virCPUDefPtr host,
>             virCPUDefPtr cpu)
>  {
> -    return x86Compute(host, cpu, NULL);
> +    return x86Compute(host, cpu, NULL, NULL);
>  }
> 
> 
>  static virCPUCompareResult
>  x86GuestData(virCPUDefPtr host,
>               virCPUDefPtr guest,
> -             union cpuData **data)
> +             union cpuData **data,
> +             char **message)
>  {
> -    return x86Compute(host, guest, data);
> +    return x86Compute(host, guest, data, message);
>  }
> 
> 
> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
> index 8dedd80..45cd417 100644
> --- a/src/qemu/qemu_command.c
> +++ b/src/qemu/qemu_command.c
> @@ -3704,6 +3704,7 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver,
>      const char *default_model;
>      union cpuData *data = NULL;
>      bool have_cpu = false;
> +    char *compare_msg = NULL;
>      int ret = -1;
>      virBuffer buf = VIR_BUFFER_INITIALIZER;
>      int i;
> @@ -3740,11 +3741,17 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver,
>              cpuUpdate(cpu, host) < 0)
>              goto cleanup;
> 
> -        cmp = cpuGuestData(host, cpu, &data);
> +        cmp = cpuGuestData(host, cpu, &data, &compare_msg);
>          switch (cmp) {
>          case VIR_CPU_COMPARE_INCOMPATIBLE:
> -            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +            if (compare_msg) {
> +                qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                                _("guest and host CPU are not compatible: %s"),
> +                                compare_msg);
> +            } else {
> +                qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
>                              _("guest CPU is not compatible with host CPU"));
> +            }
>              /* fall through */
>          case VIR_CPU_COMPARE_ERROR:
>              goto cleanup;
> @@ -3848,6 +3855,7 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver,
>      ret = 0;
> 
>  cleanup:
> +    VIR_FREE(compare_msg);
>      if (host)
>          cpuDataFree(host->arch, data);
>      virCPUDefFree(guest);
> diff --git a/tests/cputest.c b/tests/cputest.c
> index 01db8f1..ccc17fd 100644
> --- a/tests/cputest.c
> +++ b/tests/cputest.c
> @@ -269,7 +269,7 @@ cpuTestGuestData(const void *arg)
>          !(cpu = cpuTestLoadXML(data->arch, data->name)))
>          goto cleanup;
> 
> -    cmpResult = cpuGuestData(host, cpu, &guestData);
> +    cmpResult = cpuGuestData(host, cpu, &guestData, NULL);
>      if (cmpResult == VIR_CPU_COMPARE_ERROR ||
>          cmpResult == VIR_CPU_COMPARE_INCOMPATIBLE)
>          goto cleanup;

ACK

Jirka




More information about the libvir-list mailing list