virNodeGetMemoryStats: Implement linux support Signed-off-by: Minoru Usui <usui@xxxxxxxxxxxxxxxxx> --- src/libvirt_private.syms | 1 + src/lxc/lxc_driver.c | 1 + src/nodeinfo.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++ src/nodeinfo.h | 5 ++ src/qemu/qemu_driver.c | 1 + src/uml/uml_driver.c | 1 + 6 files changed, 170 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a8c77f2..4685e81 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -732,6 +732,7 @@ virNodeDeviceObjUnlock; # nodeinfo.h nodeCapsInitNUMA; nodeGetCPUStats; +nodeGetMemoryStats; nodeGetCellsFreeMemory; nodeGetFreeMemory; nodeGetInfo; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 3286154..daf93f2 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2780,6 +2780,7 @@ static virDriver lxcDriver = { .domainSetSchedulerParametersFlags = lxcSetSchedulerParametersFlags, /* 0.9.2 */ .domainInterfaceStats = lxcDomainInterfaceStats, /* 0.7.3 */ .nodeGetCPUStats = nodeGetCPUStats, /* 0.9.3 */ + .nodeGetMemoryStats = nodeGetMemoryStats, /* 0.9.3 */ .nodeGetCellsFreeMemory = nodeGetCellsFreeMemory, /* 0.6.5 */ .nodeGetFreeMemory = nodeGetFreeMemory, /* 0.6.5 */ .domainEventRegister = lxcDomainEventRegister, /* 0.7.0 */ diff --git a/src/nodeinfo.c b/src/nodeinfo.c index 39afd0e..29afd1f 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -58,8 +58,12 @@ # define CPUINFO_PATH "/proc/cpuinfo" # define CPU_SYS_PATH "/sys/devices/system/cpu" # define PROCSTAT_PATH "/proc/stat" +# define MEMINFO_PATH "/proc/meminfo" +# define NODE_SYS_PATH "/sys/devices/system/node" # define LINUX_NB_CPU_STATS 4 +# define LINUX_NB_MEMORY_STATS_ALL 4 +# define LINUX_NB_MEMORY_STATS_CELL 2 /* NB, this is not static as we need to call it from the testsuite */ int linuxNodeInfoCPUPopulate(FILE *cpuinfo, @@ -70,6 +74,10 @@ int linuxNodeGetCPUStats(FILE *procstat, int cpuNum, virCPUStatsPtr params, int *nparams); +int linuxNodeGetMemoryStats(FILE *meminfo, + int cellNum, + virMemoryStatsPtr params, + int *nparams); /* Return the positive decimal contents of the given * CPU_SYS_PATH/cpu%u/FILE, or -1 on error. If MISSING_OK and the @@ -488,6 +496,112 @@ int linuxNodeGetCPUStats(FILE *procstat, cleanup: return ret; } + +int linuxNodeGetMemoryStats(FILE *meminfo, + int cellNum, + virMemoryStatsPtr params, + int *nparams) +{ + int ret = -1; + int i = 0, j = 0, k = 0; + int found = 0; + int nr_param; + char line[1024]; + char meminfo_hdr[VIR_MEMORY_STATS_FIELD_LENGTH]; + unsigned long val; + struct field_conv { + const char *meminfo_hdr; // meminfo header + const char *field; // MemoryStats field name + } field_conv[] = { + {"MemTotal:", VIR_MEMORY_STATS_TOTAL}, + {"MemFree:", VIR_MEMORY_STATS_FREE}, + {"Buffers:", VIR_MEMORY_STATS_BUFFERS}, + {"Cached:", VIR_MEMORY_STATS_CACHED}, + {NULL, NULL} + }; + + if (cellNum == VIR_MEMORY_STATS_ALL_CELLS) { + nr_param = LINUX_NB_MEMORY_STATS_ALL; + } else { + nr_param = LINUX_NB_MEMORY_STATS_CELL; + } + + if ((*nparams) == 0) { + /* Current number of memory stats supported by linux */ + *nparams = nr_param; + ret = 0; + goto cleanup; + } + + if ((*nparams) != nr_param) { + nodeReportError(VIR_ERR_INVALID_ARG, + "%s", _("Invalid stats count")); + goto cleanup; + } + + while (fgets(line, sizeof(line), meminfo) != NULL) { + char *buf = line; + + if (STRPREFIX(buf, "Node ")) { + /* + * /sys/devices/system/node/nodeX/meminfo format is below. + * So, skip prefix "Node XX ". + * + * Node 0 MemTotal: 8386980 kB + * Node 0 MemFree: 5300920 kB + * : + */ + char *p; + + p = buf; + for (i = 0; i < 2; i++) { + p = strchr(p, ' '); + if (p == NULL) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("no prefix found")); + goto cleanup; + } + p++; + } + buf = p; + } + + if (sscanf(buf, "%s %lu kB", meminfo_hdr, &val) < 2) { + continue; + } + + for (j = 0; field_conv[j].meminfo_hdr != NULL; j++) { + struct field_conv *convp = &field_conv[j]; + + if (STREQ(meminfo_hdr, convp->meminfo_hdr)) { + virMemoryStatsPtr param = ¶ms[k++]; + + if (virStrcpyStatic(param->field, convp->field) == NULL) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Field kernel memory too long for destination")); + goto cleanup; + } + param->value = val; + found++; + break; + } + } + if (found >= nr_param) { + break; + } + } + + if (found == 0) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("no available memory line found")); + goto cleanup; + } + + ret = 0; + +cleanup: + return ret; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -554,6 +668,53 @@ int nodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED, #endif } +int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, + int cellNum, + virMemoryStatsPtr params, + int *nparams, + unsigned int flags ATTRIBUTE_UNUSED) +{ +#ifdef __linux__ + { + int ret; + char meminfo_path[1024]; + FILE *meminfo; + + if (cellNum == VIR_MEMORY_STATS_ALL_CELLS) { + sprintf(meminfo_path, MEMINFO_PATH); + } else { + if (numa_available() < 0) { + nodeReportError(VIR_ERR_NO_SUPPORT, + "%s", _("NUMA not supported on this host")); + return -1; + } + + if (cellNum > numa_max_node()) { + nodeReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid cell number")); + return -1; + } + + sprintf(meminfo_path, "%s/node%d/meminfo", NODE_SYS_PATH, cellNum); + } + meminfo = fopen(meminfo_path, "r"); + + if (!meminfo) { + virReportSystemError(errno, + _("cannot open %s"), MEMINFO_PATH); + return -1; + } + ret = linuxNodeGetMemoryStats(meminfo, cellNum, params, nparams); + VIR_FORCE_FCLOSE(meminfo); + + return ret; + } +#else + nodeReportError(VIR_ERR_NO_SUPPORT, "%s", + _("node memory stats not implemented on this platform")); + return -1; +#endif +} + #if HAVE_NUMACTL # if LIBNUMA_API_VERSION <= 1 # define NUMA_MAX_N_CPUS 4096 diff --git a/src/nodeinfo.h b/src/nodeinfo.h index 361e3e5..1ebcdb1 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -35,6 +35,11 @@ int nodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED, virCPUStatsPtr params, int *nparams, unsigned int flags ATTRIBUTE_UNUSED); +int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, + int cellNum, + virMemoryStatsPtr params, + int *nparams, + unsigned int flags ATTRIBUTE_UNUSED); int nodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems, int startCell, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 108a37f..ce93bf1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8045,6 +8045,7 @@ static virDriver qemuDriver = { .domainMemoryPeek = qemudDomainMemoryPeek, /* 0.4.4 */ .domainGetBlockInfo = qemuDomainGetBlockInfo, /* 0.8.1 */ .nodeGetCPUStats = nodeGetCPUStats, /* 0.9.3 */ + .nodeGetMemoryStats = nodeGetMemoryStats, /* 0.9.3 */ .nodeGetCellsFreeMemory = nodeGetCellsFreeMemory, /* 0.4.4 */ .nodeGetFreeMemory = nodeGetFreeMemory, /* 0.4.4 */ .domainEventRegister = qemuDomainEventRegister, /* 0.5.0 */ diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 471277d..80b51dd 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2219,6 +2219,7 @@ static virDriver umlDriver = { .domainSetAutostart = umlDomainSetAutostart, /* 0.5.0 */ .domainBlockPeek = umlDomainBlockPeek, /* 0.5.0 */ .nodeGetCPUStats = nodeGetCPUStats, /* 0.9.3 */ + .nodeGetMemoryStats = nodeGetMemoryStats, /* 0.9.3 */ .nodeGetCellsFreeMemory = nodeGetCellsFreeMemory, /* 0.5.0 */ .nodeGetFreeMemory = nodeGetFreeMemory, /* 0.5.0 */ .isEncrypted = umlIsEncrypted, /* 0.7.3 */ -- 1.7.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list