]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
conf: Report default hyperv values in domain capabilities
authorMichal Privoznik <mprivozn@redhat.com>
Tue, 30 Sep 2025 13:05:10 +0000 (15:05 +0200)
committerMichal Privoznik <mprivozn@redhat.com>
Wed, 15 Oct 2025 08:04:11 +0000 (10:04 +0200)
So far the set of available Hyper-V enlightenments are reported
in domain capabilities. Well, some enlightenments are more than
just simple on/off switch. For instance, the 'spinlocks'
enlightenment expects a number, or 'vendor_id' expects a string.

All of these have some default values (at least in QEMU) and are
used when the passthrough mode is set.

Allow querying these defaults in domain capabilities XML.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
docs/formatdomaincaps.rst
src/conf/domain_capabilities.c
src/conf/domain_capabilities.h
src/conf/schemas/domaincaps.rng
src/libvirt_private.syms
src/qemu/qemu_capabilities.c

index 4eb8211b09c33bb0bd8620fb5e9b287f1853745e..32dc09d41efa92425c4d42d6a87015c747f5aedb 100644 (file)
@@ -869,7 +869,10 @@ Hyper-V Enlightenments
 Report which features improving behavior of guests running Microsoft Windows
 are supported. The ``features`` enum corresponds to the ``<hyperv/>`` element
 (well, its children) as documented in `Hypervisor features
-<formatdomain.html#hypervisor-features>`__.
+<formatdomain.html#hypervisor-features>`__. The ``defaults`` element then
+contains child elements describing default values as reported by hypervisor,
+e.h. whether direct or extended TLB flushes are available. :since:`(since
+11.9.0)`
 
 Launch security
 ^^^^^^^^^^^^^^^
index f29c4e0515dc6319e7b57769fab4c6eea2036575..422b68c08548430ae406112413bce7ef51bd1f05 100644 (file)
@@ -92,6 +92,32 @@ virSGXCapabilitiesFree(virSGXCapability *cap)
 }
 
 
+void
+virDomainCapsFeatureHypervFree(virDomainCapsFeatureHyperv *cap)
+{
+    if (!cap)
+        return;
+
+    g_free(cap->vendor_id);
+    g_free(cap);
+}
+
+
+virDomainCapsFeatureHyperv *
+virDomainCapsFeatureHypervCopy(virDomainCapsFeatureHyperv *cap)
+{
+    virDomainCapsFeatureHyperv *ret = NULL;
+
+    if (!cap)
+        return NULL;
+
+    ret = g_memdup2(cap, sizeof(virDomainCapsFeatureHyperv));
+    ret->vendor_id = g_strdup(cap->vendor_id);
+
+    return ret;
+}
+
+
 static void
 virDomainCapsDispose(void *obj)
 {
@@ -105,7 +131,7 @@ virDomainCapsDispose(void *obj)
     virCPUDefFree(caps->cpu.hostModel);
     virSEVCapabilitiesFree(caps->sev);
     virSGXCapabilitiesFree(caps->sgx);
-    g_free(caps->hyperv);
+    virDomainCapsFeatureHypervFree(caps->hyperv);
 
     values = &caps->os.loader.values;
     for (i = 0; i < values->nvalues; i++)
@@ -791,6 +817,8 @@ static void
 virDomainCapsFeatureHypervFormat(virBuffer *buf,
                                  const virDomainCapsFeatureHyperv *hyperv)
 {
+    virBuffer defaults = VIR_BUFFER_INIT_CHILD(buf);
+
     if (!hyperv)
         return;
 
@@ -798,6 +826,37 @@ virDomainCapsFeatureHypervFormat(virBuffer *buf,
 
     ENUM_PROCESS(hyperv, features, virDomainHypervTypeToString);
 
+    virBufferAdjustIndent(&defaults, 2);
+
+    if (VIR_DOMAIN_CAPS_ENUM_IS_SET(hyperv->features, VIR_DOMAIN_HYPERV_SPINLOCKS) &&
+        hyperv->spinlocks != 0) {
+        virBufferAsprintf(&defaults, "<spinlocks>%u</spinlocks>\n", hyperv->spinlocks);
+    }
+
+    if (VIR_DOMAIN_CAPS_ENUM_IS_SET(hyperv->features, VIR_DOMAIN_HYPERV_STIMER) &&
+        hyperv->stimer_direct != VIR_TRISTATE_SWITCH_ABSENT) {
+        virBufferAsprintf(&defaults, "<stimer_direct>%s</stimer_direct>\n",
+                          virTristateSwitchTypeToString(hyperv->stimer_direct));
+    }
+
+    if (VIR_DOMAIN_CAPS_ENUM_IS_SET(hyperv->features, VIR_DOMAIN_HYPERV_TLBFLUSH)) {
+        if (hyperv->tlbflush_direct != VIR_TRISTATE_SWITCH_ABSENT) {
+            virBufferAsprintf(&defaults, "<tlbflush_direct>%s</tlbflush_direct>\n",
+                              virTristateSwitchTypeToString(hyperv->tlbflush_direct));
+        }
+
+        if (hyperv->tlbflush_extended != VIR_TRISTATE_SWITCH_ABSENT) {
+            virBufferAsprintf(&defaults, "<tlbflush_extended>%s</tlbflush_extended>\n",
+                              virTristateSwitchTypeToString(hyperv->tlbflush_extended));
+        }
+    }
+
+    if (VIR_DOMAIN_CAPS_ENUM_IS_SET(hyperv->features, VIR_DOMAIN_HYPERV_VENDOR_ID)) {
+        virBufferEscapeString(&defaults, "<vendor_id>%s</vendor_id>\n", hyperv->vendor_id);
+    }
+
+    virXMLFormatElement(buf, "defaults", NULL, &defaults);
+
     FORMAT_EPILOGUE(hyperv);
 }
 
index 43141dbdd576d8a0c89d1e9f743555091c6de98f..437981c71103906196ec1c70e851e91ba53c07ad 100644 (file)
@@ -163,6 +163,11 @@ typedef struct _virDomainCapsFeatureHyperv virDomainCapsFeatureHyperv;
 struct _virDomainCapsFeatureHyperv {
     virTristateBool supported;
     virDomainCapsEnum features; /* Info about supported virDomainHyperv features */
+    unsigned int spinlocks;
+    virTristateSwitch stimer_direct;
+    virTristateSwitch tlbflush_direct;
+    virTristateSwitch tlbflush_extended;
+    char *vendor_id;
 };
 
 STATIC_ASSERT_ENUM(VIR_DOMAIN_LAUNCH_SECURITY_LAST);
@@ -378,3 +383,9 @@ void
 virSGXCapabilitiesFree(virSGXCapability *capabilities);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSGXCapability, virSGXCapabilitiesFree);
+
+void virDomainCapsFeatureHypervFree(virDomainCapsFeatureHyperv *capabilities);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainCapsFeatureHyperv, virDomainCapsFeatureHypervFree);
+
+virDomainCapsFeatureHyperv *
+virDomainCapsFeatureHypervCopy(virDomainCapsFeatureHyperv *cap);
index 7edae54931df58e06271a4afa3e18420427be7d5..8d0380951da6b035377d0a2b650f08cd9dd3442a 100644 (file)
     <element name="hyperv">
       <ref name="supported"/>
       <ref name="enum"/>
+      <optional>
+        <element name="defaults">
+          <optional>
+            <element name="spinlocks">
+              <ref name="unsignedInt"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="stimer_direct">
+              <ref name="virOnOff"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="tlbflush_direct">
+              <ref name="virOnOff"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="tlbflush_extended">
+              <ref name="virOnOff"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="vendor_id">
+              <text/>
+            </element>
+          </optional>
+        </element>
+      </optional>
     </element>
   </define>
 
index 1a4f47aabc80eac5367311f614f9cf8def8fbbd7..26776dff2ab571bb12588151dd70018b4956d3ed 100644 (file)
@@ -217,6 +217,8 @@ virDomainCapsCPUUsableTypeFromString;
 virDomainCapsCPUUsableTypeToString;
 virDomainCapsEnumClear;
 virDomainCapsEnumSet;
+virDomainCapsFeatureHypervCopy;
+virDomainCapsFeatureHypervFree;
 virDomainCapsFormat;
 virDomainCapsNew;
 virSEVCapabilitiesFree;
index 5b485d7bfbdce9bfa7ef4df2d378a0f5b6bec880..2ccddbfcaa959ec09b54bf5842acff8212b1f0e9 100644 (file)
@@ -2070,8 +2070,7 @@ virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
     if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SGX_EPC))
         virQEMUCapsSGXInfoCopy(&ret->sgxCapabilities, qemuCaps->sgxCapabilities);
 
-    ret->hypervCapabilities = g_memdup2(qemuCaps->hypervCapabilities,
-                                        sizeof(virDomainCapsFeatureHyperv));
+    ret->hypervCapabilities = virDomainCapsFeatureHypervCopy(qemuCaps->hypervCapabilities);
 
     return g_steal_pointer(&ret);
 }
@@ -2113,7 +2112,7 @@ void virQEMUCapsDispose(void *obj)
     virSEVCapabilitiesFree(qemuCaps->sevCapabilities);
     virSGXCapabilitiesFree(qemuCaps->sgxCapabilities);
 
-    g_free(qemuCaps->hypervCapabilities);
+    virDomainCapsFeatureHypervFree(qemuCaps->hypervCapabilities);
 
     virQEMUCapsAccelClear(&qemuCaps->kvm);
     virQEMUCapsAccelClear(&qemuCaps->hvf);
@@ -3138,7 +3137,7 @@ static int
 virQEMUCapsProbeHypervCapabilities(virQEMUCaps *qemuCaps,
                                    qemuMonitorCPUModelInfo *fullQEMU)
 {
-    g_autofree virDomainCapsFeatureHyperv *hvcaps = NULL;
+    g_autoptr(virDomainCapsFeatureHyperv) hvcaps = NULL;
     size_t i;
 
     if (!fullQEMU)
@@ -4494,7 +4493,7 @@ static int
 virQEMUCapsParseHypervCapabilities(virQEMUCaps *qemuCaps,
                                    xmlXPathContextPtr ctxt)
 {
-    g_autofree virDomainCapsFeatureHyperv *hvcaps = NULL;
+    g_autoptr(virDomainCapsFeatureHyperv) hvcaps = NULL;
     xmlNodePtr n = NULL;
     g_autofree xmlNodePtr *capNodes = NULL;
     int ncapNodes;
@@ -6930,8 +6929,7 @@ static void
 virQEMUCapsFillDomainFeatureHypervCaps(virQEMUCaps *qemuCaps,
                                        virDomainCaps *domCaps)
 {
-    domCaps->hyperv = g_memdup2(qemuCaps->hypervCapabilities,
-                                sizeof(virDomainCapsFeatureHyperv));
+    domCaps->hyperv = virDomainCapsFeatureHypervCopy(qemuCaps->hypervCapabilities);
 }