Blob Blame History Raw
From 2e68dbce9f8d49e57a0e16a202483b59e9497054 Mon Sep 17 00:00:00 2001
Message-Id: <2e68dbce9f8d49e57a0e16a202483b59e9497054@dist-git>
From: John Ferlan <jferlan@redhat.com>
Date: Thu, 13 Dec 2018 10:54:33 -0500
Subject: [PATCH] qemu: Add check for whether KVM nesting is enabled

https://bugzilla.redhat.com/show_bug.cgi?id=1645139

Support for nested KVM is handled via a kernel module configuration
parameters values for kvm_intel, kvm_amd, kvm_hv (PPC), or kvm (s390).
While it's possible to fetch the kmod config values via virKModConfig,
unfortunately that is the static value and we need to get the
current/dynamic value from the kernel file system.

So this patch adds a new API virHostKVMSupportsNesting that will
search the 3 kernel modules to get the nesting value and check if
it is 'Y' (or 'y' just in case) to return a true/false whether
the KVM kernel supports nesting.

We need to do this in order to handle cases where adjustments to
the value are made after libvirtd is started to force a refetch of
the latest QEMU capabilities since the correct CPU settings need
to be made for a guest to add the "vmx=on" to/for the guest config.

Signed-off-by: John Ferlan <jferlan@redhat.com>
ACKed-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit b183a75319b90d0af5512be513743e1eab950612)

NB:
Handled merge/build conflict/issue where VIR_AUTOFREE isn't defined
(or backported to RHEL git).  So rather than relying on the automatic
free of memory, prior to each possible return add a VIR_FREE. It was
that or adjust the logic to set a retval and use goto cleanup type
logic. This way just seemed cleaner.

Signed-off-by: John Ferlan <jferlan@redhat.com>
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
---
 src/qemu/qemu_capabilities.c | 58 ++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 57b1b99076..ba8c717e22 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -550,6 +550,7 @@ struct _virQEMUCaps {
     virObject parent;
 
     bool usedQMP;
+    bool kvmSupportsNesting;
 
     char *binary;
     time_t ctime;
@@ -1606,6 +1607,7 @@ virQEMUCapsPtr virQEMUCapsNewCopy(virQEMUCapsPtr qemuCaps)
         return NULL;
 
     ret->usedQMP = qemuCaps->usedQMP;
+    ret->kvmSupportsNesting = qemuCaps->kvmSupportsNesting;
 
     if (VIR_STRDUP(ret->binary, qemuCaps->binary) < 0)
         goto error;
@@ -3597,6 +3599,9 @@ virQEMUCapsLoadCache(virArch hostArch,
     virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_KVM);
     virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_QEMU);
 
+    if (virXPathBoolean("boolean(./kvmSupportsNesting)", ctxt) > 0)
+        qemuCaps->kvmSupportsNesting = true;
+
     ret = 0;
  cleanup:
     VIR_FREE(str);
@@ -3813,6 +3818,9 @@ virQEMUCapsFormatCache(virQEMUCapsPtr qemuCaps)
     if (qemuCaps->sevCapabilities)
         virQEMUCapsFormatSEVInfo(qemuCaps, &buf);
 
+    if (qemuCaps->kvmSupportsNesting)
+        virBufferAddLit(&buf, "<kvmSupportsNesting/>\n");
+
     virBufferAdjustIndent(&buf, -2);
     virBufferAddLit(&buf, "</qemuCaps>\n");
 
@@ -3853,6 +3861,45 @@ virQEMUCapsSaveFile(void *data,
 }
 
 
+/* Check the kernel module parameters 'nested' file to determine if enabled
+ *
+ *   Intel: 'kvm_intel' uses 'Y'
+ *   AMD:   'kvm_amd' uses '1'
+ *   PPC64: 'kvm_hv' uses 'Y'
+ *   S390:  'kvm' uses '1'
+ */
+static bool
+virQEMUCapsKVMSupportsNesting(void)
+{
+    static char const * const kmod[] = {"kvm_intel", "kvm_amd",
+                                        "kvm_hv", "kvm"};
+    char * value = NULL;
+    int rc;
+    size_t i;
+
+    for (i = 0; i < ARRAY_CARDINALITY(kmod); i++) {
+        VIR_FREE(value);
+        rc = virFileReadValueString(&value, "/sys/module/%s/parameters/nested",
+                                    kmod[i]);
+        if (rc == -2)
+            continue;
+        if (rc < 0) {
+            virResetLastError();
+            VIR_FREE(value);
+            return false;
+        }
+
+        if (value[0] == 'Y' || value[0] == 'y' || value[0] == '1') {
+            VIR_FREE(value);
+            return true;
+        }
+    }
+
+    VIR_FREE(value);
+    return false;
+}
+
+
 static bool
 virQEMUCapsIsValid(void *data,
                    void *privData)
@@ -3861,6 +3908,7 @@ virQEMUCapsIsValid(void *data,
     virQEMUCapsCachePrivPtr priv = privData;
     bool kvmUsable;
     struct stat sb;
+    bool kvmSupportsNesting;
 
     if (!qemuCaps->binary)
         return true;
@@ -3938,6 +3986,14 @@ virQEMUCapsIsValid(void *data,
                       qemuCaps->kernelVersion);
             return false;
         }
+
+        kvmSupportsNesting = virQEMUCapsKVMSupportsNesting();
+        if (kvmSupportsNesting != qemuCaps->kvmSupportsNesting) {
+            VIR_DEBUG("Outdated capabilities for '%s': kvm kernel nested "
+                      "value changed from %d",
+                     qemuCaps->binary, qemuCaps->kvmSupportsNesting);
+            return false;
+        }
     }
 
     return true;
@@ -4591,6 +4647,8 @@ virQEMUCapsNewForBinaryInternal(virArch hostArch,
 
         if (VIR_STRDUP(qemuCaps->kernelVersion, kernelVersion) < 0)
             goto error;
+
+        qemuCaps->kvmSupportsNesting = virQEMUCapsKVMSupportsNesting();
     }
 
  cleanup:
-- 
2.20.1