virNodeGetMemoryStats: Implement linux support
Signed-off-by: Minoru Usui <usui(a)mxm.nes.nec.co.jp>
---
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