[PATCH 0/2] Extend hyperv host-model mode
Turns out, KubeVirt wants host-model mode for hyperv enlightenments to allow finer control over each enlightenment. For instance, set different number of spinlock than QEMU's default. Fair enough. Michal Prívozník (2): conf: Parse hyperv features even for host-model qemu: Wire up new hyperv host-model mode behavior docs/formatdomain.rst | 3 +++ src/conf/domain_conf.c | 3 +-- src/conf/schemas/domaincommon.rng | 8 +++---- src/qemu/qemu_process.c | 24 ++++++++++++------- .../hyperv-host-model.x86_64-latest.args | 2 +- .../hyperv-host-model.x86_64-latest.xml | 6 ++++- tests/qemuxmlconfdata/hyperv-host-model.xml | 6 ++++- 7 files changed, 35 insertions(+), 17 deletions(-) -- 2.52.0
From: Michal Privoznik <mprivozn@redhat.com> Since some hyperv features might be already enabled/disabled when entering qemuProcessEnableDomainFeatures() only those which are not set in domain XML (i.e. are VIR_TRISTATE_SWITCH_ABSENT) should be modified. Furthermore, some features are not a simple on/off switch, but a number or a string even. Well, that doesn't matter really as the logic for setting them is the same: only set their value iff they are not already set. Resolves: https://issues.redhat.com/browse/RHEL-148219 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_process.c | 24 ++++++++++++------- .../hyperv-host-model.x86_64-latest.args | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c5b2a5fda8..27aa1896d4 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6960,30 +6960,38 @@ qemuProcessEnableDomainFeatures(virDomainObj *vm) } for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { + virTristateSwitch origState = vm->def->hyperv.features[i]; + if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(hv->features, i)) continue; - vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ON; + if (vm->def->hyperv.features[i] == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ON; if (i == VIR_DOMAIN_HYPERV_SPINLOCKS) { if (hv->spinlocks != 0) { - vm->def->hyperv.spinlocks = hv->spinlocks; + if (vm->def->hyperv.spinlocks == 0) + vm->def->hyperv.spinlocks = hv->spinlocks; } else { - vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ABSENT; + vm->def->hyperv.features[i] = origState; } } else if (i == VIR_DOMAIN_HYPERV_STIMER) { - vm->def->hyperv.stimer_direct = hv->stimer_direct; + if (vm->def->hyperv.stimer_direct == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.stimer_direct = hv->stimer_direct; /* Both hv-stimer and hv-stimer-direct require hv-time which is * expose as a timer. Enable it. */ qemuProcessMaybeAddHypervTimer(vm->def); } else if (i == VIR_DOMAIN_HYPERV_TLBFLUSH) { - vm->def->hyperv.tlbflush_direct = hv->tlbflush_direct; - vm->def->hyperv.tlbflush_extended = hv->tlbflush_extended; + if (vm->def->hyperv.tlbflush_direct == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.tlbflush_direct = hv->tlbflush_direct; + if (vm->def->hyperv.tlbflush_extended == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.tlbflush_extended = hv->tlbflush_extended; } else if (i == VIR_DOMAIN_HYPERV_VENDOR_ID) { if (hv->vendor_id) { - vm->def->hyperv.vendor_id = g_strdup(hv->vendor_id); + if (!vm->def->hyperv.vendor_id) + vm->def->hyperv.vendor_id = g_strdup(hv->vendor_id); } else { - vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ABSENT; + vm->def->hyperv.features[i] = origState; } } } diff --git a/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.args b/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.args index 58502ff51e..d1f2326da1 100644 --- a/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.args +++ b/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.args @@ -12,7 +12,7 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ -object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ -machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=on \ -accel tcg \ --cpu 'qemu64,hv-time=on,hv-relaxed=on,hv-vapic=on,hv-spinlocks=0xfff,hv-vpindex=on,hv-runtime=on,hv-synic=on,hv-stimer=on,hv-stimer-direct=on,hv-reset=on,hv-vendor-id=Linux KVM Hv,hv-frequencies=on,hv-reenlightenment=on,hv-tlbflush=on,hv-tlbflush-direct=on,hv-tlbflush-ext=on,hv-ipi=on,hv-avic=on,hv-emsr-bitmap=on,hv-xmm-input=on' \ +-cpu 'qemu64,hv-time=on,hv-relaxed=on,hv-vapic=on,hv-spinlocks=0x2000,hv-vpindex=on,hv-runtime=on,hv-synic=on,hv-stimer=on,hv-stimer-direct=on,hv-reset=on,hv-vendor-id=Linux KVM Hv,hv-frequencies=on,hv-reenlightenment=on,hv-tlbflush=on,hv-tlbflush-direct=on,hv-tlbflush-ext=on,hv-ipi=on,hv-avic=on,hv-emsr-bitmap=on' \ -m size=219136k \ -object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ -overcommit mem-lock=off \ -- 2.52.0
On Wed, Feb 11, 2026 at 12:50:24 +0100, Michal Privoznik via Devel wrote:
From: Michal Privoznik <mprivozn@redhat.com>
Since some hyperv features might be already enabled/disabled when entering qemuProcessEnableDomainFeatures() only those which are not set in domain XML (i.e. are VIR_TRISTATE_SWITCH_ABSENT) should be modified. Furthermore, some features are not a simple on/off switch, but a number or a string even. Well, that doesn't matter really as the logic for setting them is the same: only set their value iff they are not already set.
Resolves: https://issues.redhat.com/browse/RHEL-148219 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_process.c | 24 ++++++++++++------- .../hyperv-host-model.x86_64-latest.args | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c5b2a5fda8..27aa1896d4 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6960,30 +6960,38 @@ qemuProcessEnableDomainFeatures(virDomainObj *vm) }
for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { + virTristateSwitch origState = vm->def->hyperv.features[i]; + if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(hv->features, i)) continue;
- vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ON; + if (vm->def->hyperv.features[i] == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ON;
[...]
if (i == VIR_DOMAIN_HYPERV_SPINLOCKS) { if (hv->spinlocks != 0) { - vm->def->hyperv.spinlocks = hv->spinlocks; + if (vm->def->hyperv.spinlocks == 0) + vm->def->hyperv.spinlocks = hv->spinlocks;
This feature is parsed as: case VIR_DOMAIN_HYPERV_SPINLOCKS: if (value != VIR_TRISTATE_SWITCH_ON) break; if (virXMLPropUIntDefault(node, "retries", 0, VIR_XML_PROP_NONE, &def->hyperv.spinlocks, UINT_MAX) < 0) { return -1; } if (def->hyperv.spinlocks < 0xFFF) { virReportError(VIR_ERR_XML_ERROR, "%s", _("HyperV spinlock retry count must be at least 4095")); return -1; } Thus the value will never be 0 if it was enabled in the XML. Practically though I don't think it matters because the user in such case set what they wanted. To make it more obvious that this would happen I'd probably skip the attempt to set anything if origState of this feature is not _ABSENT. In fact IMO none of the settings should be auto-filled if the user provided config of the feature.
} else { - vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ABSENT; + vm->def->hyperv.features[i] = origState; } } else if (i == VIR_DOMAIN_HYPERV_STIMER) { - vm->def->hyperv.stimer_direct = hv->stimer_direct; + if (vm->def->hyperv.stimer_direct == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.stimer_direct = hv->stimer_direct; /* Both hv-stimer and hv-stimer-direct require hv-time which is * expose as a timer. Enable it. */ qemuProcessMaybeAddHypervTimer(vm->def); } else if (i == VIR_DOMAIN_HYPERV_TLBFLUSH) { - vm->def->hyperv.tlbflush_direct = hv->tlbflush_direct; - vm->def->hyperv.tlbflush_extended = hv->tlbflush_extended; + if (vm->def->hyperv.tlbflush_direct == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.tlbflush_direct = hv->tlbflush_direct; + if (vm->def->hyperv.tlbflush_extended == VIR_TRISTATE_SWITCH_ABSENT) + vm->def->hyperv.tlbflush_extended = hv->tlbflush_extended; } else if (i == VIR_DOMAIN_HYPERV_VENDOR_ID) { if (hv->vendor_id) { - vm->def->hyperv.vendor_id = g_strdup(hv->vendor_id); + if (!vm->def->hyperv.vendor_id) + vm->def->hyperv.vendor_id = g_strdup(hv->vendor_id);
e.g. this feature can't exist without having the 'value' field populated by the user. So [1] could then be: if (vm->def->hyperv.features[i] == VIR_TRISTATE_SWITCH_ABSENT) { vm->def->hyperv.features[i] = VIR_TRISTATE_SWITCH_ON; } else { /* if the user provided already config for this we skip the * auto-population code */ continue; } and the rest would then require no change at all.
From: Michal Privoznik <mprivozn@redhat.com> As it turns out, some users of the hyperv "host-model" mode might want to override the hypervisor defaults. For instance disable a feature that's on by default, or vice versa. Currently, this is not possible because as soon as our XML parser sees the "host-model" mode it exits early and skips parsing of individual features (for "custom" mode). Well, do not return early and parse the rest. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.rst | 3 +++ src/conf/domain_conf.c | 3 +-- src/conf/schemas/domaincommon.rng | 8 ++++---- tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.xml | 6 +++++- tests/qemuxmlconfdata/hyperv-host-model.xml | 6 +++++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 87644ad42a..31eba779c1 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2197,6 +2197,9 @@ are: enlightenments are supported by hypervisor and expands them on domain startup into the live XML. In a sense, this is similar to ``host-model`` CPU mode (See `CPU model and topology`_). :since:`Since 11.9.0` + It is also possible to set features, like in ``custom`` mode. These are + then left untouched and no expansion is done for them. :since:`Since + 12.1.0` The ``mode`` attribute can be omitted and will default to ``custom``. diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e88dc62520..aee7de9669 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17220,8 +17220,7 @@ virDomainFeaturesHyperVDefParse(virDomainDef *def, def->features[VIR_DOMAIN_FEATURE_HYPERV] = mode; - if (mode == VIR_DOMAIN_HYPERV_MODE_PASSTHROUGH || - mode == VIR_DOMAIN_HYPERV_MODE_HOST_MODEL) + if (mode == VIR_DOMAIN_HYPERV_MODE_PASSTHROUGH) return 0; node = xmlFirstElementChild(node); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index e659900687..c2b886dbdc 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -8080,13 +8080,13 @@ <attribute name="mode"> <value>passthrough</value> </attribute> - <attribute name="mode"> - <value>host-model</value> - </attribute> <group> <optional> <attribute name="mode"> - <value>custom</value> + <choice> + <value>custom</value> + <value>host-model</value> + </choice> </attribute> </optional> <interleave> diff --git a/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.xml b/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.xml index 453a43b3c9..9535cee02a 100644 --- a/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.xml +++ b/tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.xml @@ -10,7 +10,11 @@ </os> <features> <acpi/> - <hyperv mode='host-model'/> + <hyperv mode='host-model'> + <relaxed state='on'/> + <spinlocks state='on' retries='8192'/> + <xmm_input state='off'/> + </hyperv> </features> <cpu mode='custom' match='exact' check='none'> <model fallback='forbid'>qemu64</model> diff --git a/tests/qemuxmlconfdata/hyperv-host-model.xml b/tests/qemuxmlconfdata/hyperv-host-model.xml index fae00d86dd..473a41892d 100644 --- a/tests/qemuxmlconfdata/hyperv-host-model.xml +++ b/tests/qemuxmlconfdata/hyperv-host-model.xml @@ -10,7 +10,11 @@ </os> <features> <acpi/> - <hyperv mode='host-model'/> + <hyperv mode='host-model'> + <relaxed state='on'/> + <spinlocks state='on' retries='8192'/> + <xmm_input state='off'/> + </hyperv> </features> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> -- 2.52.0
On Wed, Feb 11, 2026 at 12:50:23 +0100, Michal Privoznik via Devel wrote:
From: Michal Privoznik <mprivozn@redhat.com>
As it turns out, some users of the hyperv "host-model" mode might want to override the hypervisor defaults. For instance disable a feature that's on by default, or vice versa. Currently, this is not possible because as soon as our XML parser sees the "host-model" mode it exits early and skips parsing of individual features (for "custom" mode). Well, do not return early and parse the rest.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.rst | 3 +++ src/conf/domain_conf.c | 3 +-- src/conf/schemas/domaincommon.rng | 8 ++++---- tests/qemuxmlconfdata/hyperv-host-model.x86_64-latest.xml | 6 +++++- tests/qemuxmlconfdata/hyperv-host-model.xml | 6 +++++- 5 files changed, 18 insertions(+), 8 deletions(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
participants (2)
-
Michal Privoznik -
Peter Krempa