b03b5ee4创建于 2024年4月12日历史提交
From 48da26004ea0222cc8819e097a004980662ef3eb Mon Sep 17 00:00:00 2001
From: jiang-dawei15 <jiangdawei15@huawei.com>
Date: Wed, 26 Jan 2022 15:18:10 +0800
Subject: [PATCH] hotpatch: virsh support autoload mode

---
 include/libvirt/libvirt-domain.h |   1 +
 src/qemu/qemu_conf.c             |   9 +++
 src/qemu/qemu_conf.h             |   2 +
 src/qemu/qemu_driver.c           |   5 ++
 src/qemu/qemu_hotpatch.c         | 120 +++++++++++++++++++++++++++++++
 src/qemu/qemu_hotpatch.h         |   3 +
 src/qemu/qemu_process.c          |   7 ++
 tools/virsh-domain.c             |   5 +-
 8 files changed, 150 insertions(+), 2 deletions(-)

diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 96e62deac3..6120c6cea7 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -6427,6 +6427,7 @@ typedef enum {
     VIR_DOMAIN_HOTPATCH_APPLY,    /* Apply hotpatch (Since: 6.2.0) */
     VIR_DOMAIN_HOTPATCH_UNAPPLY,  /* Unapply hotpatch (Since: 6.2.0) */
     VIR_DOMAIN_HOTPATCH_QUERY,    /* Query hotpatch (Since: 6.2.0) */
+    VIR_DOMAIN_HOTPATCH_AUTOLOAD, /* Autoload hotpatch (Since: 6.2.0) */
 
 # ifdef VIR_ENUM_SENTINELS
     VIR_DOMAIN_HOTPATCH_LAST      /* Last index (Since: 6.2.0) */
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 513b5ebb1e..30343d3d12 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1064,6 +1064,12 @@ virQEMUDriverConfigLoadCapsFiltersEntry(virQEMUDriverConfig *cfg,
     return 0;
 }
 
+static int
+virQEMUDriverConfigLoadHotpatchPathEntry(virQEMUDriverConfig *cfg,
+                                        virConf *conf)
+{
+    return virConfGetValueString(conf, "hotpatch_path", &cfg->hotpatchPath);
+}
 
 int virQEMUDriverConfigLoadFile(virQEMUDriverConfig *cfg,
                                 const char *filename,
@@ -1136,6 +1142,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfig *cfg,
     if (virQEMUDriverConfigLoadCapsFiltersEntry(cfg, conf) < 0)
         return -1;
 
+    if (virQEMUDriverConfigLoadHotpatchPathEntry(cfg, conf) < 0)
+        return -1;
+
     return 0;
 }
 
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 1a3ba3a0fb..8034ec7885 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -231,6 +231,8 @@ struct _virQEMUDriverConfig {
     char *deprecationBehavior;
 
     virQEMUSchedCore schedCore;
+
+    char *hotpatchPath;
 };
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virQEMUDriverConfig, virObjectUnref);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 05cc0db3ae..6b07bcc8dc 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -19948,6 +19948,7 @@ qemuDomainHotpatchManage(virDomainPtr domain,
     virQEMUDriver *driver = domain->conn->privateData;
     char *ret = NULL;
     size_t len;
+    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
 
     virCheckFlags(0, NULL);
 
@@ -19976,6 +19977,10 @@ qemuDomainHotpatchManage(virDomainPtr domain,
         ret = qemuDomainHotpatchQuery(vm);
         break;
 
+    case VIR_DOMAIN_HOTPATCH_AUTOLOAD:
+        ret = qemuDomainHotpatchAutoload(vm, cfg->hotpatchPath);
+        break;
+
     default:
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                        _("Unknow hotpatch action"));
diff --git a/src/qemu/qemu_hotpatch.c b/src/qemu/qemu_hotpatch.c
index 31ef5bb7f2..0259ae76c8 100644
--- a/src/qemu/qemu_hotpatch.c
+++ b/src/qemu/qemu_hotpatch.c
@@ -25,6 +25,8 @@
 #include "virerror.h"
 #include "virfile.h"
 #include "virlog.h"
+#include "virbuffer.h"
+#include "virstring.h"
 #include "vircommand.h"
 #include "qemu/qemu_domain.h"
 #include "qemu_hotpatch.h"
@@ -32,6 +34,7 @@
 #define LIBCARE_CTL "libcare-ctl"
 #define LIBCARE_ERROR_NUMBER 255
 #define MAX_PATCHID_LEN 8
+#define MAX_FILE_SIZE (1024*1024)
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -204,3 +207,120 @@ qemuDomainHotpatchUnapply(virDomainObj *vm,
     VIR_FREE(output);
     return NULL;
 }
+
+char *
+qemuDomainHotpatchAutoload(virDomainObj *vm, char *hotpatch_path)
+{
+    g_auto(GStrv) applied_patches = NULL;
+    g_auto(GStrv) lines = NULL;
+    g_autofree char *applied_patch = NULL;
+    g_autofree char *libvirtd_conf = NULL;
+    g_autofree char *patch_conf = NULL;
+    g_autofree char *buf = NULL;
+    char *ret = NULL;
+    int i, j, len;
+
+    if (hotpatch_path == NULL) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("Invalid hotpatch path."));
+        return NULL;
+    }
+
+    /* get hotpatch info from Patch.conf */
+    patch_conf = g_strdup_printf("%s/Patch.conf", hotpatch_path);
+    if ((len = virFileReadAll(patch_conf, MAX_FILE_SIZE, &buf)) < 0) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("Failed to read Patch.conf file."));
+        return NULL;
+    }
+    if (len > 0)
+        buf[len-1] = '\0';
+
+    lines = g_strsplit(buf, "\n", 0);
+    if (!lines)
+        return NULL;
+
+    /* get domain hotpatch infomation */
+    applied_patch = qemuDomainHotpatchQuery(vm);
+    if (!applied_patch)
+        return NULL;
+
+    applied_patches = g_strsplit(applied_patch, "\n", 0);
+    if (!applied_patches)
+        return NULL;
+
+    /* load all hotpatch which are listed in Patch.conf one by one */
+    for (i = 0; lines[i] != NULL; i++) {
+        g_auto(GStrv) patch_info = NULL;
+        g_autofree char *kpatch_dir = NULL;
+        g_autofree char *file_path = NULL;
+        struct dirent *de;
+        g_autoptr(DIR) dh = NULL;
+        int direrr;
+
+        if (!strstr(lines[i], "QEMU-"))
+            continue;
+
+        patch_info = g_strsplit(lines[i], " ", 0);
+        if (!patch_info)
+            continue;
+
+        /* skip already applied patch */
+        if (strstr(applied_patch, patch_info[2]))
+            continue;
+
+        /* get the kpatch file name */
+        kpatch_dir = g_strdup_printf("%s/%s", hotpatch_path, patch_info[1]);
+        if (!kpatch_dir || !virFileExists(kpatch_dir))
+            return NULL;
+
+        if (virDirOpen(&dh, kpatch_dir) < 0)
+            return NULL;
+        if ((direrr = virDirRead(dh, &de, kpatch_dir)) > 0) {
+            GStatBuf sb;
+
+            file_path = g_strdup_printf("%s/%s", kpatch_dir, de->d_name);
+            if (g_lstat(file_path, &sb) < 0) {
+                virReportSystemError(errno, _("Cannot access '%s'"),
+                                    file_path);
+                return NULL;
+            }
+        }
+
+        if (qemuDomainHotpatchApply(vm, file_path) == NULL) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("failed to apply the hotpatch."));
+            return NULL;
+        }
+    }
+
+    /* unload the hotpatch which are not listed in Patch.conf */
+    for (i = 0; applied_patches[i] != NULL; i++) {
+        const char *patch_id = NULL;
+        bool is_need_unload = true;
+
+        if (!strstr(applied_patches[i], "Patch id"))
+            continue;
+
+        patch_id = strstr(applied_patches[i], ":") + 1;
+        virSkipSpaces(&patch_id);
+
+        for (j = 0; lines[j] != NULL; j++) {
+            if (!strstr(lines[j], "QEMU-"))
+                continue;
+            if (strstr(lines[j], patch_id)) {
+                is_need_unload = false;
+                break;
+            }
+        }
+        if (is_need_unload == true)
+            if (qemuDomainHotpatchUnapply(vm, patch_id) == NULL) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("failed to unapply the hotpatch."));
+                return NULL;
+        }
+    }
+
+    ret = g_strdup_printf("Hotpatch autoload successfully.\n");
+    return ret;
+}
diff --git a/src/qemu/qemu_hotpatch.h b/src/qemu/qemu_hotpatch.h
index 3cf22f7fc4..7cab4787c6 100644
--- a/src/qemu/qemu_hotpatch.h
+++ b/src/qemu/qemu_hotpatch.h
@@ -34,3 +34,6 @@ qemuDomainHotpatchApply(virDomainObj *vm,
 char *
 qemuDomainHotpatchUnapply(virDomainObj *vm,
                           const char *id);
+
+char *
+qemuDomainHotpatchAutoload(virDomainObj *vm, char *path_config);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 318f9f6182..41e9660ecd 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -61,6 +61,7 @@
 #include "qemu_backup.h"
 #include "qemu_dbus.h"
 #include "qemu_snapshot.h"
+#include "qemu_hotpatch.h"
 
 #include "cpu/cpu.h"
 #include "cpu/cpu_x86.h"
@@ -7595,6 +7596,7 @@ qemuProcessLaunch(virConnectPtr conn,
     g_autofree int *nicindexes = NULL;
     unsigned long long maxMemLock = 0;
     bool incomingMigrationExtDevices = false;
+    g_autofree char *autoLoadStatus = NULL;
 
     VIR_DEBUG("conn=%p driver=%p vm=%p name=%s id=%d asyncJob=%d "
               "incoming.uri=%s "
@@ -7926,6 +7928,11 @@ qemuProcessLaunch(virConnectPtr conn,
     if (qemuProcessDeleteThreadContextHelper(vm, asyncJob) < 0)
         goto cleanup;
 
+    /* Autoload hotpatch */
+    if ((autoLoadStatus = qemuDomainHotpatchAutoload(vm, cfg->hotpatchPath)) == NULL) {
+        VIR_WARN("Failed to autoload the hotpatch for %s.", vm->def->name);
+    }
+
     ret = 0;
 
  cleanup:
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index d88ac3cca6..89bd737f19 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -13574,7 +13574,7 @@ static const vshCmdOptDef opts_hotpatch[] = {
     {.name = "action",
      .type = VSH_OT_DATA,
      .flags = VSH_OFLAG_REQ,
-     .help = N_("hotpatch action, choose from <apply>, <unapply> and <query>")
+     .help = N_("hotpatch action, choose from <apply>, <unapply>, <query> and <autoload>")
     },
     {.name = "patch",
      .type = VSH_OT_STRING,
@@ -13593,7 +13593,8 @@ VIR_ENUM_IMPL(virDomainHotpatchAction,
               "none",
               "apply",
               "unapply",
-              "query");
+              "query",
+              "autoload");
 
 static bool
 cmdHotpatch(vshControl *ctl,
-- 
2.27.0