From 96ec8dcd8c5ac0459259cf0d40a163bcee668484 Mon Sep 17 00:00:00 2001 From: ikarosYuuki 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