VIR_CPU_TYPE_AUTO : detect the input xml to tell is guest or host cpu model definition
VIR_CPU_TYPE_GUEST : guest cpu model means the cpu conf define from domain xml
VIR_CPU_TYPE_HOST : host cpu model means the cpu conf load from host capabilities xml
So the could focus on formatFallback.
Verification is required, if you use a custom mode without a cpu model is not allowed, because custom means you need specify a collections of cpu features and custom features of the subset.
1 2 3 4 5
if (!def->model && def->mode == VIR_CPU_MODE_CUSTOM && def->nfeatures) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Non-empty feature list specified without CPU model")); return-1; }
while define model, need to get a fallback value for guest cpu
The virtual CPU will claim the feature is supported regardless of it being supported by host CPU.
require
Guest creation will fail unless the feature is supported by the host CPU or the hypervisor is able to emulate it.
optional
The feature will be supported by virtual CPU if and only if it is supported by host CPU.
disable
The feature will not be supported by virtual CPU.
forbid
Guest creation will fail if the feature is supported by host CPU.
virCPUDefFormatBuf is used by capabilities.c which collects host features from host capabilities xml. But now we need to check the code in domain_capabilities.c
typedef virCPUDef *virCPUDefPtr; struct _virCPUDef { int type; /* enum virCPUType */ int mode; /* enum virCPUMode */ int match; /* enum virCPUMatch */ virCPUCheck check; virArch arch; char *model; char *vendor_id; /* vendor id returned by CPUID in the guest */ int fallback; /* enum virCPUFallback */ char *vendor; unsignedint microcodeVersion; unsignedint sockets; unsignedint cores; unsignedint threads; size_t nfeatures; size_t nfeatures_max; virCPUFeatureDefPtr features; virCPUCacheDefPtr cache; };
nfeatures will be set in _virCPUDef and supported features are parsed from domain xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* * Parses CPU definition XML from a node pointed to by @xpath. If @xpath is * NULL, the current node of @ctxt is used (i.e., it is a shortcut to "."). * * Missing <cpu> element in the XML document is not considered an error unless * @xpath is NULL in which case the function expects it was provided with a * valid <cpu> element already. In other words, the function returns success * and sets @cpu to NULL if @xpath is not NULL and the node pointed to by * @xpath is not found. * * Returns 0 on success, -1 on error. */ int virCPUDefParseXML(xmlXPathContextPtr ctxt, constchar *xpath, virCPUType type, virCPUDefPtr *cpu)
Finally, qemu_command.c would use those features to qemu commandline:
for (i = 0; i < cpu->nfeatures; i++) { if (STREQ("rtm", cpu->features[i].name)) rtm = true; if (STREQ("hle", cpu->features[i].name)) hle = true;
switch ((virCPUFeaturePolicy) cpu->features[i].policy) { case VIR_CPU_FEATURE_FORCE: case VIR_CPU_FEATURE_REQUIRE: if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) virBufferAsprintf(buf, ",%s=on", cpu->features[i].name); else virBufferAsprintf(buf, ",+%s", cpu->features[i].name); break;
case VIR_CPU_FEATURE_DISABLE: case VIR_CPU_FEATURE_FORBID: if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) virBufferAsprintf(buf, ",%s=off", cpu->features[i].name); else virBufferAsprintf(buf, ",-%s", cpu->features[i].name); break;
case VIR_CPU_FEATURE_OPTIONAL: case VIR_CPU_FEATURE_LAST: break; } }
like -cpu ... feature1=on,feature2=off to make those features take effects.
oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]); if (oc == NULL) { error_report("unable to find CPU model '%s'", model_pieces[0]); g_strfreev(model_pieces); exit(EXIT_FAILURE); }
An object from oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);will return a cpu object class which support parse features. See following code:
eq = strchr(featurestr, '='); if (eq) { *eq++ = 0; val = eq; } else { val = "on"; }
feat2prop(featurestr); name = featurestr;
if (g_list_find_custom(plus_features, name, compare_string)) { warn_report("Ambiguous CPU model string. " "Don't mix both \"+%s\" and \"%s=%s\"", name, name, val); ambiguous = true; } if (g_list_find_custom(minus_features, name, compare_string)) { warn_report("Ambiguous CPU model string. " "Don't mix both \"-%s\" and \"%s=%s\"", name, name, val); ambiguous = true; }
/* Special case: */ if (!strcmp(name, "tsc-freq")) { int ret; uint64_t tsc_freq;
ret = qemu_strtosz_metric(val, NULL, &tsc_freq); if (ret < 0 || tsc_freq > INT64_MAX) { error_setg(errp, "bad numerical value %s", val); return; } snprintf(num, sizeof(num), "%" PRId64, tsc_freq); val = num; name = "tsc-frequency"; }
/* Check for missing features that may prevent the CPU class from * running using the current machine and accelerator. */ staticvoidx86_cpu_class_check_missing_features(X86CPUClass *xcc, strList **missing_feats) { X86CPU *xc; Error *err = NULL; strList **next = missing_feats;
x86_cpu_expand_features(xc, &err); if (err) { /* Errors at x86_cpu_expand_features should never happen, * but in case it does, just report the model as not * runnable at all using the "type" property. */ strList *new = g_new0(strList, 1); new->value = g_strdup("type"); *next = new; next = &new->next; }
/* * Finishes initialization of CPUID data, filters CPU feature * words based on host availability of each feature. * * Returns: 0 if all flags are supported by the host, non-zero otherwise. */ staticvoidx86_cpu_filter_features(X86CPU *cpu, bool verbose) { CPUX86State *env = &cpu->env; FeatureWord w; constchar *prefix = NULL;
if (verbose) { prefix = accel_uses_host_cpuid() ? "host doesn't support requested feature" : "TCG doesn't support requested feature"; }
for (w = 0; w < FEATURE_WORDS; w++) { uint64_t host_feat = x86_cpu_get_supported_feature_word(w, false); uint64_t requested_features = env->features[w]; uint64_t unavailable_features = requested_features & ~host_feat; mark_unavailable_features(cpu, w, unavailable_features, prefix); }
the hypervisor guest status = false value changed as expected.
Linux drawbacks
Read the usage about X86_FEATURE_HYPERVISOR in linux kernel. Some drawbacks can be found in kernel code directly.
From qspintlock.h :
1 2 3 4 5 6 7 8 9 10 11 12 13
/* * RHEL7 specific: * To provide backward compatibility with pre-7.4 kernel modules that * inlines the ticket spinlock unlock code. The virt_spin_lock() function * will have to recognize both a lock value of 0 or _Q_UNLOCKED_VAL as * being in an unlocked state. */ staticinlineboolvirt_spin_lock(struct qspinlock *lock) { int lockval;
if (!static_cpu_has(X86_FEATURE_HYPERVISOR)) returnfalse;
/* * Don't enable ART in a VM, non-stop TSC required, * and the TSC counter resets must not occur asynchronously. */ if (boot_cpu_has(X86_FEATURE_HYPERVISOR) || !boot_cpu_has(X86_FEATURE_NONSTOP_TSC) || art_to_tsc_denominator < ART_MIN_DENOMINATOR || tsc_async_resets) return;
Always run timer will be started which actually should not be enabled.
From apic.c:
1 2 3
if (!boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER) || boot_cpu_has(X86_FEATURE_HYPERVISOR)) return;
From mshyperv.c:
1 2
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR)) return0;
Can not detect if run on hyperv.
From radeon_device :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/** * radeon_device_is_virtual - check if we are running is a virtual environment * * Check if the asic has been passed through to a VM (all asics). * Used at driver startup. * Returns true if virtual or false if not. */ boolradeon_device_is_virtual(void) { #ifdef CONFIG_X86 return boot_cpu_has(X86_FEATURE_HYPERVISOR); #else returnfalse; #endif }
Radeon gpu will not detect it is running as guest.
For kernel it may failed to detect that it is running over hypervisor. So related performance improvement changed won’t be applied so there will be a performance drop for those guests.
So does the userspace application also can not do specific things without knowing it is running in virtual machine.