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 | 97 ++++++++++++++++++++++++++++++++++++++++++++++ src/nodeinfo.h | 4 ++ src/qemu/qemu_driver.c | 1 + src/uml/uml_driver.c | 1 + 6 files changed, 105 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4cb8dda..5e55cf1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -689,6 +689,7 @@ virNodeDeviceObjUnlock; # nodeinfo.h nodeCapsInitNUMA; +nodeGetMemoryStats; nodeGetCellsFreeMemory; nodeGetFreeMemory; nodeGetInfo; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 9e09c95..41b6260 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2754,6 +2754,7 @@ static virDriver lxcDriver = { .domainGetSchedulerParameters = lxcGetSchedulerParameters, /* 0.5.0 */ .domainSetSchedulerParameters = lxcSetSchedulerParameters, /* 0.5.0 */ .domainInterfaceStats = lxcDomainInterfaceStats, /* 0.7.3 */ + .nodeGetMemoryStats = nodeGetMemoryStats, /* 0.9.2 */ .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 f55c83e..63d7822 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -57,11 +57,17 @@ #ifdef __linux__ # define CPUINFO_PATH "/proc/cpuinfo" # define CPU_SYS_PATH "/sys/devices/system/cpu" +# define MEMINFO_PATH "/proc/meminfo" + +# define LINUX_NB_MEMORY_STATS 4 /* NB, this is not static as we need to call it from the testsuite */ int linuxNodeInfoCPUPopulate(FILE *cpuinfo, virNodeInfoPtr nodeinfo, bool need_hyperthreads); +int linuxNodeGetMemoryStats(FILE *meminfo, + 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 @@ -378,6 +384,71 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo, return 0; } +int linuxNodeGetMemoryStats(FILE *meminfo, + virMemoryStatsPtr params, + int *nparams) +{ + int ret = -1; + int i = 0, j = 0; + 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 ((*nparams) == 0) { + /* Current number of memory stats supported by linux */ + *nparams = LINUX_NB_MEMORY_STATS; + ret = 0; + goto cleanup; + } + + if ((*nparams) != LINUX_NB_MEMORY_STATS) { + nodeReportError(VIR_ERR_INVALID_ARG, + "%s", _("Invalid stats count")); + goto cleanup; + } + + while (fgets(line, sizeof(line), meminfo) != NULL) { + char *buf = line; + + 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[i++]; + + 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; + break; + } + } + } + + ret = 0; + goto cleanup; + + nodeReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no \'cpu \' line found")); + +cleanup: + return ret; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -416,6 +487,32 @@ int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { #endif } +int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, + virMemoryStatsPtr params, + int *nparams, + unsigned int flags ATTRIBUTE_UNUSED) +{ +#ifdef __linux__ + { + int ret; + FILE *meminfo = fopen(MEMINFO_PATH, "r"); + if (!meminfo) { + virReportSystemError(errno, + _("cannot open %s"), MEMINFO_PATH); + return -1; + } + ret = linuxNodeGetMemoryStats(meminfo, 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 88bac6c..6e69f76 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -28,6 +28,10 @@ # include "capabilities.h" int nodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo); +int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, + virMemoryStatsPtr params, + int *nparams, + unsigned int flags ATTRIBUTE_UNUSED); int nodeCapsInitNUMA(virCapsPtr caps); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 576393e..cc930c8 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7734,6 +7734,7 @@ static virDriver qemuDriver = { .domainBlockPeek = qemudDomainBlockPeek, /* 0.4.4 */ .domainMemoryPeek = qemudDomainMemoryPeek, /* 0.4.4 */ .domainGetBlockInfo = qemuDomainGetBlockInfo, /* 0.8.1 */ + .nodeGetMemoryStats = nodeGetMemoryStats, /* 0.9.2 */ .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 536cd8c..6987d05 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2218,6 +2218,7 @@ static virDriver umlDriver = { .domainGetAutostart = umlDomainGetAutostart, /* 0.5.0 */ .domainSetAutostart = umlDomainSetAutostart, /* 0.5.0 */ .domainBlockPeek = umlDomainBlockPeek, /* 0.5.0 */ + .nodeGetMemoryStats = nodeGetMemoryStats, /* 0.9.2 */ .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