cpu: Rework cpuCompare* APIs

Both cpuCompare* APIs are renamed to virCPUCompare*. And they should now
work for any guest CPU definition, i.e., even for host-passthrough
(trivial) and host-model CPUs. The implementation in x86 driver is
enhanced to provide a hint about -noTSX Broadwell and Haswell models
when appropriate.

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
This commit is contained in:
Jiri Denemark 2016-08-09 13:26:53 +02:00
parent 81da062f0b
commit 7f127ded65
9 changed files with 126 additions and 80 deletions

View File

@ -91,8 +91,9 @@ cpuGetSubDriverByName(const char *name)
/** /**
* cpuCompareXML: * virCPUCompareXML:
* *
* @arch: CPU architecture
* @host: host CPU definition * @host: host CPU definition
* @xml: XML description of either guest or host CPU to be compared with @host * @xml: XML description of either guest or host CPU to be compared with @host
* @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE * @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE
@ -107,25 +108,31 @@ cpuGetSubDriverByName(const char *name)
* two CPUs are incompatible. * two CPUs are incompatible.
*/ */
virCPUCompareResult virCPUCompareResult
cpuCompareXML(virCPUDefPtr host, virCPUCompareXML(virArch arch,
const char *xml, virCPUDefPtr host,
bool failIncompatible) const char *xml,
bool failIncompatible)
{ {
xmlDocPtr doc = NULL; xmlDocPtr doc = NULL;
xmlXPathContextPtr ctxt = NULL; xmlXPathContextPtr ctxt = NULL;
virCPUDefPtr cpu = NULL; virCPUDefPtr cpu = NULL;
virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR; virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
VIR_DEBUG("host=%p, xml=%s", host, NULLSTR(xml)); VIR_DEBUG("arch=%s, host=%p, xml=%s",
virArchToString(arch), host, NULLSTR(xml));
if (!xml) {
virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing CPU definition"));
goto cleanup;
}
if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt))) if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt)))
goto cleanup; goto cleanup;
cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO); if (!(cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO)))
if (cpu == NULL)
goto cleanup; goto cleanup;
ret = cpuCompare(host, cpu, failIncompatible); ret = virCPUCompare(arch, host, cpu, failIncompatible);
cleanup: cleanup:
virCPUDefFree(cpu); virCPUDefFree(cpu);
@ -137,8 +144,9 @@ cpuCompareXML(virCPUDefPtr host,
/** /**
* cpuCompare: * virCPUCompare:
* *
* @arch: CPU architecture
* @host: host CPU definition * @host: host CPU definition
* @cpu: either guest or host CPU to be compared with @host * @cpu: either guest or host CPU to be compared with @host
* @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE * @failIncompatible: return an error instead of VIR_CPU_COMPARE_INCOMPATIBLE
@ -153,21 +161,23 @@ cpuCompareXML(virCPUDefPtr host,
* two CPUs are incompatible. * two CPUs are incompatible.
*/ */
virCPUCompareResult virCPUCompareResult
cpuCompare(virCPUDefPtr host, virCPUCompare(virArch arch,
virCPUDefPtr cpu, virCPUDefPtr host,
bool failIncompatible) virCPUDefPtr cpu,
bool failIncompatible)
{ {
struct cpuArchDriver *driver; struct cpuArchDriver *driver;
VIR_DEBUG("host=%p, cpu=%p", host, cpu); VIR_DEBUG("arch=%s, host=%p, cpu=%p",
virArchToString(arch), host, cpu);
if ((driver = cpuGetSubDriver(host->arch)) == NULL) if (!(driver = cpuGetSubDriver(arch)))
return VIR_CPU_COMPARE_ERROR; return VIR_CPU_COMPARE_ERROR;
if (driver->compare == NULL) { if (!driver->compare) {
virReportError(VIR_ERR_NO_SUPPORT, virReportError(VIR_ERR_NO_SUPPORT,
_("cannot compare CPUs of %s architecture"), _("cannot compare CPUs of %s architecture"),
virArchToString(host->arch)); virArchToString(arch));
return VIR_CPU_COMPARE_ERROR; return VIR_CPU_COMPARE_ERROR;
} }

View File

@ -45,7 +45,7 @@ struct _virCPUData {
typedef virCPUCompareResult typedef virCPUCompareResult
(*cpuArchCompare) (virCPUDefPtr host, (*virCPUArchCompare)(virCPUDefPtr host,
virCPUDefPtr cpu, virCPUDefPtr cpu,
bool failIncompatible); bool failIncompatible);
@ -116,7 +116,7 @@ struct cpuArchDriver {
const char *name; const char *name;
const virArch *arch; const virArch *arch;
unsigned int narch; unsigned int narch;
cpuArchCompare compare; virCPUArchCompare compare;
cpuArchDecode decode; cpuArchDecode decode;
cpuArchEncode encode; cpuArchEncode encode;
cpuArchDataFree free; cpuArchDataFree free;
@ -134,16 +134,17 @@ struct cpuArchDriver {
virCPUCompareResult virCPUCompareResult
cpuCompareXML(virCPUDefPtr host, virCPUCompareXML(virArch arch,
const char *xml, virCPUDefPtr host,
bool failIncompatible) const char *xml,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); bool failIncompatible);
virCPUCompareResult virCPUCompareResult
cpuCompare (virCPUDefPtr host, virCPUCompare(virArch arch,
virCPUDefPtr cpu, virCPUDefPtr host,
bool failIncompatible) virCPUDefPtr cpu,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); bool failIncompatible)
ATTRIBUTE_NONNULL(3);
int int
cpuDecode (virCPUDefPtr cpu, cpuDecode (virCPUDefPtr cpu,

View File

@ -112,9 +112,9 @@ armBaseline(virCPUDefPtr *cpus,
} }
static virCPUCompareResult static virCPUCompareResult
armCompare(virCPUDefPtr host ATTRIBUTE_UNUSED, virCPUarmCompare(virCPUDefPtr host ATTRIBUTE_UNUSED,
virCPUDefPtr cpu ATTRIBUTE_UNUSED, virCPUDefPtr cpu ATTRIBUTE_UNUSED,
bool failMessages ATTRIBUTE_UNUSED) bool failMessages ATTRIBUTE_UNUSED)
{ {
return VIR_CPU_COMPARE_IDENTICAL; return VIR_CPU_COMPARE_IDENTICAL;
} }
@ -123,7 +123,7 @@ struct cpuArchDriver cpuDriverArm = {
.name = "arm", .name = "arm",
.arch = archs, .arch = archs,
.narch = ARRAY_CARDINALITY(archs), .narch = ARRAY_CARDINALITY(archs),
.compare = armCompare, .compare = virCPUarmCompare,
.decode = NULL, .decode = NULL,
.encode = NULL, .encode = NULL,
.free = armDataFree, .free = armDataFree,

View File

@ -634,13 +634,24 @@ ppc64Compute(virCPUDefPtr host,
} }
static virCPUCompareResult static virCPUCompareResult
ppc64DriverCompare(virCPUDefPtr host, virCPUppc64Compare(virCPUDefPtr host,
virCPUDefPtr cpu, virCPUDefPtr cpu,
bool failIncompatible) bool failIncompatible)
{ {
virCPUCompareResult ret; virCPUCompareResult ret;
char *message = NULL; char *message = NULL;
if (!host || !host->model) {
if (failIncompatible) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
_("unknown host CPU"));
} else {
VIR_WARN("unknown host CPU");
ret = VIR_CPU_COMPARE_INCOMPATIBLE;
}
return -1;
}
ret = ppc64Compute(host, cpu, NULL, &message); ret = ppc64Compute(host, cpu, NULL, &message);
if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) { if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) {
@ -902,7 +913,7 @@ struct cpuArchDriver cpuDriverPPC64 = {
.name = "ppc64", .name = "ppc64",
.arch = archs, .arch = archs,
.narch = ARRAY_CARDINALITY(archs), .narch = ARRAY_CARDINALITY(archs),
.compare = ppc64DriverCompare, .compare = virCPUppc64Compare,
.decode = ppc64DriverDecode, .decode = ppc64DriverDecode,
.encode = NULL, .encode = NULL,
.free = ppc64DriverFree, .free = ppc64DriverFree,

View File

@ -1057,7 +1057,8 @@ x86ModelFromCPU(const virCPUDef *cpu,
policy != -1) policy != -1)
return x86ModelNew(); return x86ModelNew();
if (policy == VIR_CPU_FEATURE_REQUIRE || policy == -1) { if (cpu->model &&
(policy == VIR_CPU_FEATURE_REQUIRE || policy == -1)) {
if (!(model = x86ModelFind(map, cpu->model))) { if (!(model = x86ModelFind(map, cpu->model))) {
virReportError(VIR_ERR_INTERNAL_ERROR, virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown CPU model %s"), cpu->model); _("Unknown CPU model %s"), cpu->model);
@ -1519,12 +1520,6 @@ x86Compute(virCPUDefPtr host,
virArch arch; virArch arch;
size_t i; size_t i;
if (!cpu->model) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("no guest CPU model specified"));
return VIR_CPU_COMPARE_ERROR;
}
if (cpu->arch != VIR_ARCH_NONE) { if (cpu->arch != VIR_ARCH_NONE) {
bool found = false; bool found = false;
@ -1565,7 +1560,7 @@ x86Compute(virCPUDefPtr host,
} }
if (!(map = virCPUx86GetMap()) || if (!(map = virCPUx86GetMap()) ||
!(host_model = x86ModelFromCPU(host, map, VIR_CPU_FEATURE_REQUIRE)) || !(host_model = x86ModelFromCPU(host, map, -1)) ||
!(cpu_force = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORCE)) || !(cpu_force = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORCE)) ||
!(cpu_require = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_REQUIRE)) || !(cpu_require = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_REQUIRE)) ||
!(cpu_optional = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_OPTIONAL)) || !(cpu_optional = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_OPTIONAL)) ||
@ -1664,25 +1659,68 @@ x86Compute(virCPUDefPtr host,
static virCPUCompareResult static virCPUCompareResult
x86Compare(virCPUDefPtr host, virCPUx86Compare(virCPUDefPtr host,
virCPUDefPtr cpu, virCPUDefPtr cpu,
bool failIncompatible) bool failIncompatible)
{ {
virCPUCompareResult ret; virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
virCPUx86MapPtr map;
virCPUx86ModelPtr model = NULL;
char *message = NULL; char *message = NULL;
if (!host || !host->model) {
if (failIncompatible) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
_("unknown host CPU"));
} else {
VIR_WARN("unknown host CPU");
ret = VIR_CPU_COMPARE_INCOMPATIBLE;
}
goto cleanup;
}
ret = x86Compute(host, cpu, NULL, &message); ret = x86Compute(host, cpu, NULL, &message);
if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) { if (ret == VIR_CPU_COMPARE_INCOMPATIBLE) {
ret = VIR_CPU_COMPARE_ERROR; bool noTSX = false;
if (message) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message); if (STREQ_NULLABLE(cpu->model, "Haswell") ||
} else { STREQ_NULLABLE(cpu->model, "Broadwell")) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL); if (!(map = virCPUx86GetMap()))
goto cleanup;
if (!(model = x86ModelFromCPU(cpu, map, -1)))
goto cleanup;
noTSX = !x86FeatureInData("hle", &model->data, map) ||
!x86FeatureInData("rtm", &model->data, map);
}
if (failIncompatible) {
ret = VIR_CPU_COMPARE_ERROR;
if (message) {
if (noTSX) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE,
_("%s; try using '%s-noTSX' CPU model"),
message, cpu->model);
} else {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message);
}
} else {
if (noTSX) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE,
_("try using '%s-noTSX' CPU model"),
cpu->model);
} else {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL);
}
}
} }
} }
VIR_FREE(message);
cleanup:
VIR_FREE(message);
x86ModelFree(model);
return ret; return ret;
} }
@ -1693,6 +1731,12 @@ x86GuestData(virCPUDefPtr host,
virCPUDataPtr *data, virCPUDataPtr *data,
char **message) char **message)
{ {
if (!guest->model) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("no guest CPU model specified"));
return VIR_CPU_COMPARE_ERROR;
}
return x86Compute(host, guest, data, message); return x86Compute(host, guest, data, message);
} }
@ -2719,7 +2763,7 @@ struct cpuArchDriver cpuDriverX86 = {
.name = "x86", .name = "x86",
.arch = archs, .arch = archs,
.narch = ARRAY_CARDINALITY(archs), .narch = ARRAY_CARDINALITY(archs),
.compare = x86Compare, .compare = virCPUx86Compare,
.decode = x86DecodeCPUData, .decode = x86DecodeCPUData,
.encode = x86Encode, .encode = x86Encode,
.free = x86FreeCPUData, .free = x86FreeCPUData,

View File

@ -970,8 +970,6 @@ virSecretObjSetValueSize;
# cpu/cpu.h # cpu/cpu.h
cpuBaseline; cpuBaseline;
cpuBaselineXML; cpuBaselineXML;
cpuCompare;
cpuCompareXML;
cpuDataFormat; cpuDataFormat;
cpuDataFree; cpuDataFree;
cpuDataParse; cpuDataParse;
@ -981,6 +979,8 @@ cpuGetModels;
cpuGuestData; cpuGuestData;
cpuNodeData; cpuNodeData;
virCPUCheckFeature; virCPUCheckFeature;
virCPUCompare;
virCPUCompareXML;
virCPUDataCheckFeature; virCPUDataCheckFeature;
virCPUTranslate; virCPUTranslate;
virCPUUpdate; virCPUUpdate;

View File

@ -6369,18 +6369,8 @@ libxlConnectCompareCPU(virConnectPtr conn,
cfg = libxlDriverConfigGet(driver); cfg = libxlDriverConfigGet(driver);
if (!cfg->caps->host.cpu || ret = virCPUCompareXML(cfg->caps->host.arch, cfg->caps->host.cpu,
!cfg->caps->host.cpu->model) { xmlDesc, failIncompatible);
if (failIncompatible) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
_("cannot get host CPU capabilities"));
} else {
VIR_WARN("cannot get host CPU capabilities");
ret = VIR_CPU_COMPARE_INCOMPATIBLE;
}
} else {
ret = cpuCompareXML(cfg->caps->host.cpu, xmlDesc, failIncompatible);
}
virObjectUnref(cfg); virObjectUnref(cfg);
return ret; return ret;

View File

@ -12812,18 +12812,8 @@ qemuConnectCompareCPU(virConnectPtr conn,
if (!(caps = virQEMUDriverGetCapabilities(driver, false))) if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
goto cleanup; goto cleanup;
if (!caps->host.cpu || ret = virCPUCompareXML(caps->host.arch, caps->host.cpu,
!caps->host.cpu->model) { xmlDesc, failIncompatible);
if (failIncompatible) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
_("cannot get host CPU capabilities"));
} else {
VIR_WARN("cannot get host CPU capabilities");
ret = VIR_CPU_COMPARE_INCOMPATIBLE;
}
} else {
ret = cpuCompareXML(caps->host.cpu, xmlDesc, failIncompatible);
}
cleanup: cleanup:
virObjectUnref(caps); virObjectUnref(caps);

View File

@ -215,7 +215,7 @@ cpuTestCompare(const void *arg)
!(cpu = cpuTestLoadXML(data->arch, data->name))) !(cpu = cpuTestLoadXML(data->arch, data->name)))
goto cleanup; goto cleanup;
result = cpuCompare(host, cpu, false); result = virCPUCompare(host->arch, host, cpu, false);
if (data->result == VIR_CPU_COMPARE_ERROR) if (data->result == VIR_CPU_COMPARE_ERROR)
virResetLastError(); virResetLastError();
@ -360,7 +360,7 @@ cpuTestBaseline(const void *arg)
for (i = 0; i < ncpus; i++) { for (i = 0; i < ncpus; i++) {
virCPUCompareResult cmp; virCPUCompareResult cmp;
cmp = cpuCompare(cpus[i], baseline, false); cmp = virCPUCompare(cpus[i]->arch, cpus[i], baseline, false);
if (cmp != VIR_CPU_COMPARE_SUPERSET && if (cmp != VIR_CPU_COMPARE_SUPERSET &&
cmp != VIR_CPU_COMPARE_IDENTICAL) { cmp != VIR_CPU_COMPARE_IDENTICAL) {
VIR_TEST_VERBOSE("\nbaseline CPU is incompatible with CPU %zu\n", VIR_TEST_VERBOSE("\nbaseline CPU is incompatible with CPU %zu\n",