From cd0f3755bd04bb58089d756400f20c75550e54f2 Mon Sep 17 00:00:00 2001
From: tujipei <tujipei@huawei.com>
Date: Wed, 12 Jun 2024 12:02:00 +0800
Subject: [PATCH] libvirt: add get tmm memory info API and libvirtd RPC Add the
 get tmm memory info API into libvirt-host. Also should add the RPC calls into
 libvirtd for API calling.

Signed-off-by: tujipei <tujipei@huawei.com>
---
 include/libvirt/libvirt-host.h      |   2 +
 scripts/apibuild.py                 |   1 +
 scripts/check-aclrules.py           |   1 +
 src/driver-hypervisor.h             |   5 +
 src/libvirt-host.c                  |  35 +++++++
 src/libvirt_public.syms             |   1 +
 src/qemu/qemu_driver.c              | 139 ++++++++++++++++++++++++++++
 src/remote/remote_daemon_dispatch.c |  23 +++++
 src/remote/remote_driver.c          |  29 ++++++
 src/remote/remote_protocol.x        |  16 +++-
 10 files changed, 251 insertions(+), 1 deletion(-)

diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h
index 6972834175..e7272ccb20 100644
--- a/include/libvirt/libvirt-host.h
+++ b/include/libvirt/libvirt-host.h
@@ -820,5 +820,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 c98bcf6091..9b78754e5d 100755
--- a/scripts/apibuild.py
+++ b/scripts/apibuild.py
@@ -107,6 +107,7 @@ ignored_functions = {
     "virDomainMigrateConfirm3Params": "private function for migration",
     "virDomainMigratePrepareTunnel3Params": "private function for tunnelled migration",
     "virErrorCopyNew": "private",
+    "virConnectGetTmmMemoryInfo": "private function for tmm",
 }
 
 ignored_macros = {
diff --git a/scripts/check-aclrules.py b/scripts/check-aclrules.py
index e196f81de9..aa5a589c85 100755
--- a/scripts/check-aclrules.py
+++ b/scripts/check-aclrules.py
@@ -54,6 +54,7 @@ whitelist = {
     "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 82f808905d..e48b701365 100644
--- a/src/driver-hypervisor.h
+++ b/src/driver-hypervisor.h
@@ -1402,6 +1402,10 @@ typedef int
 typedef struct _virHypervisorDriver virHypervisorDriver;
 typedef virHypervisorDriver *virHypervisorDriverPtr;
 
+typedef char *
+(*virDrvConnectGetTmmMemoryInfo)(virConnectPtr conn,
+                                 bool detail);
+
 /**
  * _virHypervisorDriver:
  *
@@ -1664,4 +1668,5 @@ struct _virHypervisorDriver {
     virDrvDomainBackupGetXMLDesc domainBackupGetXMLDesc;
     virDrvDomainHotpatchManage domainHotpatchManage;
     virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc;
+    virDrvConnectGetTmmMemoryInfo connectGetTmmMemoryInfo;
 };
diff --git a/src/libvirt-host.c b/src/libvirt-host.c
index bc3d1d2803..d0750d28cc 100644
--- a/src/libvirt-host.c
+++ b/src/libvirt-host.c
@@ -1754,3 +1754,38 @@ 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.
+ */
+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 f006516208..284b7f7873 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -877,5 +877,6 @@ LIBVIRT_6.2.0 {
     global:
         virDomainHotpatchManage;
         virDomainStartDirtyRateCalc;
+        virConnectGetTmmMemoryInfo;
 } LIBVIRT_6.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 77a139c66b..1e3f63a39a 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -23385,6 +23385,144 @@ qemuDomainStartDirtyRateCalc(virDomainPtr dom,
     return ret;
 }
 
+static int
+qemuConnectTmmInfoListAppend(char ***targetInfoStrList,
+                             char **infoStrList,
+                             int targetNumaNum,
+                             int *startIndex,
+                             int maxListSize)
+{
+    char *numStart;
+    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) {
+            ret = virStringListAdd(targetInfoStrList, infoStrList[index]);
+            if (ret < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s [%d]",
+                               _("Failed to get add info list member"), index);
+                return ret;
+            }
+        } 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 = virStringSplit(baseMeminfo, "\n", 0);
+    char **slabInfoSplits = virStringSplit(slabInfo, "\n", 0);
+    char **resultStrList = NULL;
+    int numaSize, numaIndex, headNumaNode;
+    ssize_t meminfoListSize = virStringListLength((const char * const *)baseMeminfoSplits);
+    ssize_t slabInfoSize = virStringListLength((const char * const *)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;
+    }
+
+    ret = virStringListAdd(&resultStrList, baseMeminfoSplits[i++]);
+    if (ret < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s [%d]",
+                       _("Failed to get add base memory info list member"), (i - 1));
+        goto cleanup;
+    }
+
+    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(&resultStrList, baseMeminfoSplits, headNumaNode, &i, meminfoListSize);
+        if (ret < 0)
+            goto cleanup;
+        ret = qemuConnectTmmInfoListAppend(&resultStrList, slabInfoSplits, headNumaNode, &j, slabInfoSize);
+        if (ret < 0)
+            goto cleanup;
+    }
+
+    format = virStringListJoin((const char **)resultStrList, "\n");
+
+ cleanup:
+    virStringListFree(baseMeminfoSplits);
+    virStringListFree(slabInfoSplits);
+    virStringListFree(resultStrList);
+    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,
@@ -23627,6 +23765,7 @@ static virHypervisorDriver qemuHypervisorDriver = {
     .domainBackupGetXMLDesc = qemuDomainBackupGetXMLDesc, /* 6.0.0 */
     .domainHotpatchManage = qemuDomainHotpatchManage, /* 6.2.0 */
     .domainStartDirtyRateCalc = qemuDomainStartDirtyRateCalc, /* 6.2.0 */
+    .connectGetTmmMemoryInfo = qemuConnectGetTmmMemoryInfo, /* 6.2.0 */
 };
 
 
diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c
index 0bbcc05235..a9763cee7b 100644
--- a/src/remote/remote_daemon_dispatch.c
+++ b/src/remote/remote_daemon_dispatch.c
@@ -7268,6 +7268,29 @@ remoteDispatchNetworkPortGetParameters(virNetServerPtr server G_GNUC_UNUSED,
     return rv;
 }
 
+static int
+remoteDispatchConnectGetTmmMemoryInfo(virNetServerPtr server G_GNUC_UNUSED,
+                                      virNetServerClientPtr client,
+                                      virNetMessagePtr msg G_GNUC_UNUSED,
+                                      virNetMessageErrorPtr 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 9c272b4ff8..b597ae614c 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -8253,6 +8253,34 @@ remoteDomainGetGuestInfo(virDomainPtr dom,
     return rv;
 }
 
+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.
  * These can return NULL if underlying memory allocations fail,
@@ -8686,6 +8714,7 @@ static virHypervisorDriver hypervisor_driver = {
     .domainBackupGetXMLDesc = remoteDomainBackupGetXMLDesc, /* 6.0.0 */
     .domainHotpatchManage = remoteDomainHotpatchManage, /* 6.2.0 */
     .domainStartDirtyRateCalc = remoteDomainStartDirtyRateCalc, /* 6.2.0 */
+    .connectGetTmmMemoryInfo = remoteConnectGetTmmMemoryInfo /* 6.2.0 */
 };
 
 static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index d89cc1a087..f37bd332a1 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3789,6 +3789,14 @@ struct remote_domain_start_dirty_rate_calc_args {
     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. */
@@ -6698,5 +6706,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
 };
-- 
2.27.0

