9288c12b创建于 2024年12月12日历史提交
From 96ec8dcd8c5ac0459259cf0d40a163bcee668484 Mon Sep 17 00:00:00 2001
From: ikarosYuuki <tujipei@huawei.com>
Date: Fri, 2 Aug 2024 14:18:11 +0800
Subject: [PATCH] Add the get tmm memory info API into libvirt-host. Also
 should add the RPC calls into libvirtd for API calling.

---
 include/libvirt/libvirt-host.h      |   2 +
 scripts/apibuild.py                 |   1 +
 scripts/check-aclrules.py           |   1 +
 src/driver-hypervisor.h             |   5 ++
 src/libvirt-host.c                  |  36 ++++++++
 src/libvirt_public.syms             |   1 +
 src/qemu/qemu_driver.c              | 128 ++++++++++++++++++++++++++++
 src/remote/remote_daemon_dispatch.c |  22 +++++
 src/remote/remote_driver.c          |  28 ++++++
 src/remote/remote_protocol.x        |  17 +++-
 tools/virsh-host.c                  |  98 +++++++++++++++++++++
 11 files changed, 338 insertions(+), 1 deletion(-)

diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h
index 3112f2b676..af7dede24e 100644
--- a/include/libvirt/libvirt-host.h
+++ b/include/libvirt/libvirt-host.h
@@ -1016,5 +1016,7 @@ int virNodeAllocPages(virConnectPtr conn,
                       unsigned int cellCount,
                       unsigned int flags);
 
+char *virConnectGetTmmMemoryInfo(virConnectPtr conn,
+                                 unsigned int detail);
 
 #endif /* LIBVIRT_HOST_H */
diff --git a/scripts/apibuild.py b/scripts/apibuild.py
index 3ecc3eadf7..f1cfa5aa0a 100755
--- a/scripts/apibuild.py
+++ b/scripts/apibuild.py
@@ -109,6 +109,7 @@ ignored_functions = {
     "virDomainMigrateConfirm3Params": "private function for migration",
     "virDomainMigratePrepareTunnel3Params": "private function for tunnelled migration",
     "virErrorCopyNew": "private",
+    "virConnectGetTmmMemoryInfo": "private function for tmm",
 }
 
 # The version in the .sym file might different from
diff --git a/scripts/check-aclrules.py b/scripts/check-aclrules.py
index e39dbd2ba8..e6bcf00b11 100755
--- a/scripts/check-aclrules.py
+++ b/scripts/check-aclrules.py
@@ -54,6 +54,7 @@ permitted = {
     "localOnly": True,
     "domainQemuAttach": True,
     "domainHotpatchManage": True,
+    "connectGetTmmMemoryInfo": True,
 }
 
 # XXX this vzDomainMigrateConfirm3Params looks
diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
index e54af0515f..619a091ffa 100644
--- a/src/driver-hypervisor.h
+++ b/src/driver-hypervisor.h
@@ -1457,6 +1457,10 @@ typedef char *
 
 typedef struct _virHypervisorDriver virHypervisorDriver;
 
+typedef char *
+(*virDrvConnectGetTmmMemoryInfo)(virConnectPtr conn,
+                                 bool detail);
+
 /**
  * _virHypervisorDriver:
  *
@@ -1728,4 +1732,5 @@ struct _virHypervisorDriver {
     virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc;
     virDrvDomainFDAssociate domainFDAssociate;
     virDrvDomainHotpatchManage domainHotpatchManage;
+    virDrvConnectGetTmmMemoryInfo connectGetTmmMemoryInfo;
 };
diff --git a/src/libvirt-host.c b/src/libvirt-host.c
index e67b36812e..e763d5c86c 100644
--- a/src/libvirt-host.c
+++ b/src/libvirt-host.c
@@ -1829,3 +1829,39 @@ virNodeGetSEVInfo(virConnectPtr conn,
     virDispatchError(conn);
     return -1;
 }
+
+/*
+ * virConnectGetTmmMemoryInfo:
+ * @conn: pointer to the hypervisor connection
+ * @detail: whether libvirtd return detailed tmm memory information;
+ * the default value is 0 which means don't return detailed tmm memory information.
+ *
+ * If Tmm enable, then will fill the cotents of string buffer with tmm memory information.
+ *
+ * Returns string ptr in case of success, and NULL in case of failure.
+ *
+ * Since: 9.7.0
+ */
+char *
+virConnectGetTmmMemoryInfo(virConnectPtr conn,
+                           unsigned int detail)
+{
+    VIR_DEBUG("conn=%p", conn);
+
+    virResetLastError();
+
+    virCheckConnectReturn(conn, NULL);
+
+    if (conn->driver->connectGetTmmMemoryInfo) {
+        char *ret;
+        ret = conn->driver->connectGetTmmMemoryInfo(conn, detail);
+        if (!ret)
+            goto error;
+        return ret;
+    }
+
+    virReportUnsupportedError();
+ error:
+    virDispatchError(conn);
+    return NULL;
+}
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 8b38fe9a5f..72efec0b61 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -941,6 +941,7 @@ LIBVIRT_9.7.0 {
     global:
         virNetworkGetMetadata;
         virNetworkSetMetadata;
+        virConnectGetTmmMemoryInfo;
 } LIBVIRT_9.0.0;
 
 # .... define new API here using predicted next version number ....
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index d7fb93b3b3..5a5aa28449 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -20010,6 +20010,133 @@ qemuDomainHotpatchManage(virDomainPtr domain,
     return ret;
 }
 
+static int
+qemuConnectTmmInfoListAppend(char **format,
+                             char **infoStrList,
+                             int targetNumaNum,
+                             int *startIndex,
+                             int maxListSize)
+{
+    char *numStart;
+    char *strPtr = NULL;
+    int numaNode, index, ret = 0;
+
+    for (index = *startIndex; index < maxListSize; index++) {
+        if (strlen(infoStrList[index]) == 0)
+            break;
+
+        numStart = strstr(infoStrList[index], "node ");
+        if (!numStart)
+            return -1;
+
+        virSkipToDigit((const char **)(&numStart));
+        ret = virStrToLong_i(numStart, &numStart, 10, &numaNode);
+        if (ret < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Failed to get current numa node"));
+            return ret;
+        }
+
+        if (numaNode == targetNumaNum) {
+            strPtr = *format;
+            *format = g_strconcat(*format, "\n", infoStrList[index], NULL);
+            free(strPtr);
+        } else {
+            break;
+        }
+    }
+
+    *startIndex = index;
+
+    return ret;
+}
+
+static char *
+qemuConnectTmmDetailInfoFormat(char *baseMeminfo,
+                               char *slabInfo)
+{
+    int ret, i = 0, j = 0;
+    char *numStart, *numListStart, *format = NULL;
+    char **baseMeminfoSplits = g_strsplit(baseMeminfo, "\n", 0);
+    char **slabInfoSplits = g_strsplit(slabInfo, "\n", 0);
+    int numaSize, numaIndex, headNumaNode;
+    ssize_t meminfoListSize = g_strv_length(baseMeminfoSplits);
+    ssize_t slabInfoSize = g_strv_length(slabInfoSplits);
+
+    numStart = strchr(baseMeminfoSplits[i], ':');
+    numListStart = strchr(baseMeminfoSplits[i], '(');
+    if (!numStart || !numListStart)
+        goto cleanup;
+
+    virSkipToDigit((const char **)(&numStart));
+    ret = virStrToLong_i(numStart, &numStart, 10, &numaSize);
+    if (ret < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed to get available numa size"));
+        goto cleanup;
+    }
+
+    format = g_strconcat(baseMeminfoSplits[i++], NULL);
+
+    virSkipToDigit((const char **)(&numListStart));
+    for (numaIndex = 0; *numListStart && numaIndex < numaSize; numaIndex++, numListStart++) {
+        ret = virStrToLong_i(numListStart, &numListStart, 10, &headNumaNode);
+        if (ret < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Failed to get current numa node"));
+            goto cleanup;
+        }
+
+        ret = qemuConnectTmmInfoListAppend(&format, baseMeminfoSplits, headNumaNode, &i, meminfoListSize);
+        if (ret < 0)
+            goto cleanup;
+        ret = qemuConnectTmmInfoListAppend(&format, slabInfoSplits, headNumaNode, &j, slabInfoSize);
+        if (ret < 0)
+            goto cleanup;
+    }
+
+cleanup:
+    g_strfreev(baseMeminfoSplits);
+    g_strfreev(slabInfoSplits);
+    return format;
+}
+
+static char *
+qemuConnectGetTmmMemoryInfo(virConnectPtr conn G_GNUC_UNUSED,
+                            bool detail)
+{
+    int maxLen = 10 * 1024;
+    char *meminfo = NULL;
+    g_autofree char *formatInfo = NULL;
+    g_autofree char *baseMeminfo = NULL;
+    g_autofree char *slabInfo = NULL;
+    g_autofree char *buddyInfo = NULL;
+
+    if (virFileReadAll("/sys/kernel/tmm/memory_info", maxLen, &baseMeminfo) < 0)
+        goto end;
+    if (detail && virFileReadAll("/sys/kernel/tmm/slab_info", maxLen, &slabInfo) < 0)
+        goto end;
+    if (detail && virFileReadAll("/sys/kernel/tmm/buddy_info", maxLen, &buddyInfo) < 0)
+        goto end;
+
+    if (detail) {
+        if (!virStringIsEmpty(baseMeminfo) && !virStringIsEmpty(slabInfo)) {
+            formatInfo = qemuConnectTmmDetailInfoFormat(baseMeminfo, slabInfo);
+            if (formatInfo == NULL)
+                goto end;
+        } else {
+            formatInfo = g_strdup_printf(_("%s%s"), baseMeminfo, slabInfo);
+        }
+
+        meminfo = g_strdup_printf(_("%s\n%s"), formatInfo, buddyInfo);
+    } else {
+        meminfo = g_steal_pointer(&baseMeminfo);
+    }
+
+end:
+    return meminfo;
+}
+
 static virHypervisorDriver qemuHypervisorDriver = {
     .name = QEMU_DRIVER_NAME,
     .connectURIProbe = qemuConnectURIProbe,
@@ -20260,6 +20387,7 @@ static virHypervisorDriver qemuHypervisorDriver = {
     .domainStartDirtyRateCalc = qemuDomainStartDirtyRateCalc, /* 7.2.0 */
     .domainSetLaunchSecurityState = qemuDomainSetLaunchSecurityState, /* 8.0.0 */
     .domainFDAssociate = qemuDomainFDAssociate, /* 9.0.0 */
+    .connectGetTmmMemoryInfo = qemuConnectGetTmmMemoryInfo, /* 9.0.0 */
 };
 
 
diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c
index 7542caa952..10f343843a 100644
--- a/src/remote/remote_daemon_dispatch.c
+++ b/src/remote/remote_daemon_dispatch.c
@@ -7125,6 +7125,28 @@ remoteDispatchNetworkPortGetParameters(virNetServer *server G_GNUC_UNUSED,
     return rv;
 }
 
+static int
+remoteDispatchConnectGetTmmMemoryInfo(virNetServer *server G_GNUC_UNUSED,
+                                      virNetServerClient *client,
+                                      virNetMessage *msg G_GNUC_UNUSED,
+                                      struct virNetMessageError *rerr,
+                                      remote_connect_get_tmm_memory_info_args *args,
+                                      remote_connect_get_tmm_memory_info_ret *ret)
+{
+    int rv = -1;
+    char *meminfo = NULL;
+    virConnectPtr conn = remoteGetHypervisorConn(client);
+
+    if (conn && (meminfo = virConnectGetTmmMemoryInfo(conn, args->detail))) {
+        rv = 0;
+        ret->meminfo = meminfo;
+    }
+
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+
+    return rv;
+}
 
 /*----- Helpers. -----*/
 
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 9350e811d6..4b9ad30ed6 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -7406,6 +7406,33 @@ remoteDomainFDAssociate(virDomainPtr domain,
     return 0;
 }
 
+static char *
+remoteConnectGetTmmMemoryInfo(virConnectPtr conn,
+                              bool detail)
+{
+    char *rv = NULL;
+    struct private_data *priv = conn->privateData;
+    remote_connect_get_tmm_memory_info_args args;
+    remote_connect_get_tmm_memory_info_ret ret;
+
+    remoteDriverLock(priv);
+
+    args.detail = detail;
+
+    memset(&ret, 0, sizeof(ret));
+
+    if (call(conn, priv, 0, REMOTE_PROC_CONNECT_GET_TMM_MEMORY_INFO,
+             (xdrproc_t)xdr_remote_connect_get_tmm_memory_info_args, (char *)&args,
+             (xdrproc_t)xdr_remote_connect_get_tmm_memory_info_ret, (char *)&ret) < 0) {
+        goto done;
+    }
+
+    rv = ret.meminfo;
+
+ done:
+    remoteDriverUnlock(priv);
+    return rv;
+}
 
 /* get_nonnull_domain and get_nonnull_network turn an on-wire
  * (name, uuid) pair into virDomainPtr or virNetworkPtr object.
@@ -7849,6 +7876,7 @@ static virHypervisorDriver hypervisor_driver = {
     .domainStartDirtyRateCalc = remoteDomainStartDirtyRateCalc, /* 7.2.0 */
     .domainSetLaunchSecurityState = remoteDomainSetLaunchSecurityState, /* 8.0.0 */
     .domainFDAssociate = remoteDomainFDAssociate, /* 9.0.0 */
+    .connectGetTmmMemoryInfo = remoteConnectGetTmmMemoryInfo /* 9.0.0 */
 };
 
 static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index eea11df2ea..39069ce207 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3973,6 +3973,15 @@ struct remote_domain_fd_associate_args {
     remote_nonnull_string name;
     unsigned int flags;
 };
+
+struct remote_connect_get_tmm_memory_info_args {
+    unsigned int detail;
+};
+
+struct remote_connect_get_tmm_memory_info_ret {
+    remote_nonnull_string meminfo;
+};
+
 /*----- Protocol. -----*/
 
 /* Define the program number, protocol version and procedure numbers here. */
@@ -7038,5 +7047,11 @@ enum remote_procedure {
      * @generate: both
      * @acl: domain:read
      */
-    REMOTE_PROC_DOMAIN_HOTPATCH_MANAGE = 800
+    REMOTE_PROC_DOMAIN_HOTPATCH_MANAGE = 800,
+
+    /**
+     * @generate: none
+     * @acl: connect:read
+     */
+    REMOTE_PROC_CONNECT_GET_TMM_MEMORY_INFO = 900
 };
diff --git a/tools/virsh-host.c b/tools/virsh-host.c
index 6c14be865f..7fdd6aed53 100644
--- a/tools/virsh-host.c
+++ b/tools/virsh-host.c
@@ -1826,6 +1826,98 @@ cmdHypervisorCPUBaseline(vshControl *ctl,
     return ret;
 }
 
+/*
+ * "securememinfo" command
+ */
+static const vshCmdInfo info_tmm[] = {
+    {.name = "help",
+     .data = N_("Interaction with the tmm")
+    },
+    {.name = "desc",
+     .data = N_("Call the host kernel dev which is provided for virsh to use receiving tmm informations.")
+    },
+    {.name = NULL}
+};
+
+static const vshCmdOptDef opts_tmm[] = {
+    {.name = "dev",
+     .type = VSH_OT_DATA,
+     .flags = VSH_OFLAG_REQ,
+     .help = N_("Device name of host kernel dev")
+    },
+    {.name = "detail",
+     .type = VSH_OT_BOOL,
+     .help = N_("print detailed info if this option contained in cmd")
+    },
+    {.name = NULL}
+};
+
+static bool
+virshGetTmmMemoryInfo(vshControl *ctl,
+                      const vshCmd *cmd)
+{
+    char *tmmMemoryInfo = NULL;
+    bool detail;
+    virshControl *priv = ctl->privData;
+
+    detail = vshCommandOptBool(cmd, "detail");
+    if (!(tmmMemoryInfo = virConnectGetTmmMemoryInfo(priv->conn, (unsigned int)detail))) {
+        vshError(ctl, _("Get tmm_memory_info failed"));
+        return false;
+    }
+
+    vshPrintExtra(ctl, _("%s"), tmmMemoryInfo);
+
+    VIR_FREE(tmmMemoryInfo);
+    return true;
+}
+
+typedef bool
+(*virshTmmFunc)(vshControl *ctl,
+                const vshCmd *cmd);
+
+struct _virshTmmFuncInfo {
+    const char *devName;
+    virshTmmFunc funcPtr;
+};
+
+typedef struct _virshTmmFuncInfo virshTmmFuncInfo;
+
+static virshTmmFuncInfo virshTmmFuncMap[] = {
+    {"tmm_memory_info", virshGetTmmMemoryInfo},
+};
+
+static bool
+virshTmmRunFunc(vshControl *ctl,
+                const char *devName,
+                const vshCmd *cmd)
+{
+    int funcIndex;
+
+    for (funcIndex = 0; funcIndex < sizeof(virshTmmFuncMap) / sizeof(virshTmmFuncInfo); funcIndex++) {
+        if (strcmp(devName, virshTmmFuncMap[funcIndex].devName) == 0) {
+            virshTmmFuncMap[funcIndex].funcPtr(ctl, cmd);
+            return true;
+        }
+    }
+
+    vshError(ctl, _("Invalid dev name"));
+    return false;
+}
+
+static bool
+cmdTmm(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *devName = NULL;
+
+    if (vshCommandOptStringReq(ctl, cmd, "dev", &devName) < 0)
+        return false;
+
+    if (!virshTmmRunFunc(ctl, devName, cmd))
+        return false;
+
+    return true;
+}
 
 const vshCmdDef hostAndHypervisorCmds[] = {
     {.name = "allocpages",
@@ -1960,5 +2052,11 @@ const vshCmdDef hostAndHypervisorCmds[] = {
      .info = info_version,
      .flags = 0
     },
+    {.name = "tmm",
+     .handler = cmdTmm,
+     .opts = opts_tmm,
+     .info = info_tmm,
+     .flags = 0
+    },
     {.name = NULL}
 };
-- 
2.41.0.windows.1