[libvirt] [PATCH 0/6] Add virNodeGetMemoryStats() API

Hi, everyone. I wrote new API called virNodeGetMemoryStats(). It returns total/free/buffer/cached memory of the node from /proc/meminfo. The user I/F is like virNodeGetCPUStats(). Previous discussion is here. (See about virNodeGetFreeMemory() part.) http://www.mail-archive.com/libvir-list@redhat.com/msg32553.html This patch series based on latest git tree. If my virNodeGetCPUStats() patch series applied, I'll rebase on it. Minoru Usui (6): virNodeGetMemoryStats: Expose new API virNodeGetMemoryStats: Define internal driver API virNodeGetMemoryStats: Implement public API virNodeGetMemoryStats: Implement remote protocol virNodeGetMemoryStats: Implement virsh support virNodeGetMemoryStats: Implement linux support daemon/remote.c | 76 +++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 65 +++++++++++++++++++++++++++- src/driver.h | 8 +++ src/libvirt.c | 81 +++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/libvirt_public.syms | 1 + src/lxc/lxc_driver.c | 1 + src/nodeinfo.c | 97 ++++++++++++++++++++++++++++++++++++++++++ src/nodeinfo.h | 4 ++ src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 63 +++++++++++++++++++++++++++ src/remote/remote_protocol.x | 21 +++++++++- src/uml/uml_driver.c | 1 + tools/virsh.c | 51 ++++++++++++++++++++++ tools/virsh.pod | 4 ++ 15 files changed, 473 insertions(+), 2 deletions(-) -- Minoru Usui <usui@mxm.nes.nec.co.jp>

virNodeGetMemoryStats: Expose new API Signed-off-by: Minoru Usui <usui@mxm.nes.nec.co.jp> --- include/libvirt/libvirt.h.in | 65 +++++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 1 + 2 files changed, 65 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..69e2255 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -280,6 +280,58 @@ struct _virNodeInfo { unsigned int threads;/* number of threads per core */ }; +/** + * VIR_MEMORY_STATS_FIELD_LENGTH: + * + * Macro providing the field length of virMemoryStats + */ +#define VIR_MEMORY_STATS_FIELD_LENGTH 80 + +/** + * VIR_MEMORY_STATS_TOTAL: + * + * Macro for the total memory: it represents the maximum memory. + */ + +#define VIR_MEMORY_STATS_TOTAL "total" + +/** + * VIR_MEMORY_STATS_FREE: + * + * Macro for the free memory: On linux, it includes buffer and cached memory. + */ + +#define VIR_MEMORY_STATS_FREE "free" + +/** + * VIR_MEMORY_STATS_BUFFERS: + * + * Macro for the buffer memory: + */ + +#define VIR_MEMORY_STATS_BUFFERS "buffers" + +/** + * VIR_MEMORY_STATS_CACHED: + * + * Macro for the cached memory: + */ + +#define VIR_MEMORY_STATS_CACHED "cached" + +/** + * virMemoryStats: + * + * a virMemoryStats is a structure filled by virNodeGetMemoryStats() + * and providing the information of the memory of the Node. + */ + +typedef struct _virMemoryStats virMemoryStats; + +struct _virMemoryStats { + char field[VIR_MEMORY_STATS_FIELD_LENGTH]; + unsigned long long value; +}; /* Common data types shared among interfaces with name/type/value lists. */ @@ -565,6 +617,14 @@ int virDomainMigrateSetMaxSpeed(virDomainPtr domain, typedef virNodeInfo *virNodeInfoPtr; /** + * virMemoryStatsPtr: + * + * a virMemoryStatsPtr is a pointer to a virMemoryStats structure. + */ + +typedef virMemoryStats *virMemoryStatsPtr; + +/** * virConnectFlags * * Flags when opening a connection to a hypervisor @@ -697,7 +757,10 @@ int virConnectGetMaxVcpus (virConnectPtr conn, int virNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); char * virConnectGetCapabilities (virConnectPtr conn); - +int virNodeGetMemoryStats(virConnectPtr conn, + virMemoryStatsPtr params, + int *nparams, + unsigned int flags); unsigned long long virNodeGetFreeMemory (virConnectPtr conn); int virNodeGetSecurityModel (virConnectPtr conn, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..277978e 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,7 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; + virNodeGetMemoryStats; } LIBVIRT_0.9.0; # .... define new API here using predicted next version number .... -- 1.7.1

virNodeGetMemoryStats: Define internal driver API Signed-off-by: Minoru Usui <usui@mxm.nes.nec.co.jp> --- src/driver.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 450dd53..daced6b 100644 --- a/src/driver.h +++ b/src/driver.h @@ -364,6 +364,13 @@ typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr; typedef int + (*virDrvNodeGetMemoryStats) + (virConnectPtr conn, + virMemoryStatsPtr params, + int *nparams, + unsigned int flags); + +typedef int (*virDrvNodeGetCellsFreeMemory) (virConnectPtr conn, unsigned long long *freeMems, @@ -694,6 +701,7 @@ struct _virDriver { virDrvDomainBlockPeek domainBlockPeek; virDrvDomainMemoryPeek domainMemoryPeek; virDrvDomainGetBlockInfo domainGetBlockInfo; + virDrvNodeGetMemoryStats nodeGetMemoryStats; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory nodeGetFreeMemory; virDrvDomainEventRegister domainEventRegister; -- 1.7.1

virNodeGetMemoryStats: Implement public API Signed-off-by: Minoru Usui <usui@mxm.nes.nec.co.jp> --- src/libvirt.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 81 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..2205ebb 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -4967,6 +4967,87 @@ error: } /** + * virNodeGetMemoryStats: + * @conn: pointer to the hypervisor connection. + * @params: pointer to node memory stats objects + * @nparams: number of node memory stats (this value should be same or + * less than the number of stats supported) + * @flags: currently unused, for future extension. always pass 0. + * + * This function provides memory stats of the node. + * The @params array will be filled with the values equal to the number of + * stats suggested by @nparams + * + * As the value of @nparams is dynamic, call the API setting @nparams to 0 and + * @params as NULL, the API returns the number of parameters supported by the + * HV by updating @nparams on SUCCESS. The caller should then allocate @params + * array, i.e. (sizeof(@virMemoryStats) * @nparams) bytes and call + * the API again. + * + * Here is the sample code snippet: + * + * if ((virNodeGetMemoryStats(conn, NULL, &nparams, 0) == 0) && + * (nparams != 0)) { + * params = vshMalloc(ctl, sizeof(virMemoryStats) * nparams); + * memset(params, 0, sizeof(virMemoryStats) * nparams); + * if (virNodeGetMemoryStats(conn, params, &nparams, 0)) { + * vshError(ctl, "%s", _("Unable to get node memory stats")); + * goto error; + * } + * } + * + * This function doesn't requires privileged access to the hypervisor. + * This function expects the caller to allocate the @params. + * + * Memory Stats: + * + * VIR_MEMORY_STATS_TOTAL: + * The total memory usage.(KB) + * VIR_MEMORY_STATS_FREE: + * The free memory usage.(KB) + * On linux, this usage includes buffers and cached. + * VIR_MEMORY_STATS_BUFFERS: + * The buffers memory usage.(KB) + * VIR_MEMORY_STATS_CACHED: + * The cached memory usage.(KB) + * + * Returns -1 in case of error, 0 in case of success. + */ +int virNodeGetMemoryStats (virConnectPtr conn, + virMemoryStatsPtr params, + int *nparams, unsigned int flags) +{ + VIR_DEBUG("conn=%p, params=%p, nparams=%d, flags=%u", + conn, params, nparams ? *nparams : -1, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if ((nparams == NULL) || (*nparams < 0)) { + virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->nodeGetMemoryStats) { + int ret; + ret = conn->driver->nodeGetMemoryStats (conn, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** * virNodeGetFreeMemory: * @conn: pointer to the hypervisor connection * -- 1.7.1

virNodeGetMemoryStats: Implement remote protocol Signed-off-by: Minoru Usui <usui@mxm.nes.nec.co.jp> --- daemon/remote.c | 76 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 63 ++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 21 +++++++++++- 3 files changed, 159 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 80783b3..190e213 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1788,6 +1788,82 @@ cleanup: return rv; } +static int +remoteDispatchNodeGetMemoryStats (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_node_get_memory_stats_args *args, + remote_node_get_memory_stats_ret *ret) +{ + virMemoryStatsPtr params = NULL; + int i; + int nparams = args->nparams; + unsigned int flags; + int rv = -1; + + if (!conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + flags = args->flags; + + if (nparams > REMOTE_NODE_MEMORY_STATS_MAX) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large")); + goto cleanup; + } + if (VIR_ALLOC_N(params, nparams) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virNodeGetMemoryStats(conn, params, &nparams, flags) < 0) + goto cleanup; + + /* In this case, we need to send back the number of parameters + * supported + */ + if (args->nparams == 0) { + ret->nparams = nparams; + goto success; + } + + /* Serialise the memory parameters. */ + ret->params.params_len = nparams; + if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0) + goto no_memory; + + for (i = 0; i < nparams; ++i) { + /* remoteDispatchClientRequest will free this: */ + ret->params.params_val[i].field = strdup(params[i].field); + if (ret->params.params_val[i].field == NULL) + goto no_memory; + + ret->params.params_val[i].value = params[i].value; + } + +success: + rv = 0; + +cleanup: + if (rv < 0) { + remoteDispatchError(rerr); + if (ret->params.params_val) { + for (i = 0; i < nparams; i++) + VIR_FREE(ret->params.params_val[i].field); + VIR_FREE(ret->params.params_val); + } + } + VIR_FREE(params); + return rv; + +no_memory: + virReportOOMError(); + goto cleanup; +} + /*-------------------------------------------------------------*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1691dab..475fd74 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1694,6 +1694,68 @@ done: } static int +remoteNodeGetMemoryStats (virConnectPtr conn, + virMemoryStatsPtr params, int *nparams, + unsigned int flags) +{ + int rv = -1; + remote_node_get_memory_stats_args args; + remote_node_get_memory_stats_ret ret; + int i = -1; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.nparams = *nparams; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_MEMORY_STATS, + (xdrproc_t) xdr_remote_node_get_memory_stats_args, (char *) &args, + (xdrproc_t) xdr_remote_node_get_memory_stats_ret, (char *) &ret) == -1) + goto done; + + /* Check the length of the returned list carefully. */ + if (ret.params.params_len > REMOTE_NODE_MEMORY_STATS_MAX || + ret.params.params_len > *nparams) { + remoteError(VIR_ERR_RPC, "%s", + _("remoteNodeGetMemoryStats: " + "returned number of stats exceeds limit")); + goto cleanup; + } + /* Handle the case when the caller does not know the number of stats + * and is asking for the number of stats supported + */ + if (*nparams == 0) { + *nparams = ret.nparams; + rv = 0; + goto cleanup; + } + + *nparams = ret.params.params_len; + + /* Deserialise the result. */ + for (i = 0; i < *nparams; ++i) { + if (virStrcpyStatic(params[i].field, ret.params.params_val[i].field) == NULL) { + remoteError(VIR_ERR_INTERNAL_ERROR, + _("Stats %s too big for destination"), + ret.params.params_val[i].field); + goto cleanup; + } + params[i].value = ret.params.params_val[i].value; + } + + rv = 0; + +cleanup: + xdr_free ((xdrproc_t) xdr_remote_node_get_memory_stats_ret, + (char *) &ret); +done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems, int startCell, @@ -6823,6 +6885,7 @@ static virDriver remote_driver = { .domainBlockPeek = remoteDomainBlockPeek, /* 0.4.2 */ .domainMemoryPeek = remoteDomainMemoryPeek, /* 0.4.2 */ .domainGetBlockInfo = remoteDomainGetBlockInfo, /* 0.8.1 */ + .nodeGetMemoryStats = remoteNodeGetMemoryStats, /* 0.9.2 */ .nodeGetCellsFreeMemory = remoteNodeGetCellsFreeMemory, /* 0.3.3 */ .nodeGetFreeMemory = remoteNodeGetFreeMemory, /* 0.3.3 */ .domainEventRegister = remoteDomainEventRegister, /* 0.5.0 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f0da95d..be8ee75 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -134,6 +134,9 @@ const REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX = 16; /* Upper limit on list of memory parameters. */ const REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX = 16; +/* Upper limit on list of node memory stats. */ +const REMOTE_NODE_MEMORY_STATS_MAX = 16; + /* Upper limit on number of NUMA cells */ const REMOTE_NODE_MAX_CELLS = 1024; @@ -359,6 +362,11 @@ struct remote_memory_param { remote_memory_param_value value; }; +struct remote_node_get_memory_stats { + remote_nonnull_string field; + unsigned hyper value; +}; + /*----- Calls. -----*/ /* For each call we may have a 'remote_CALL_args' and 'remote_CALL_ret' @@ -440,6 +448,16 @@ struct remote_get_capabilities_ret { remote_nonnull_string capabilities; }; +struct remote_node_get_memory_stats_args { + int nparams; + unsigned int flags; +}; + +struct remote_node_get_memory_stats_ret { + remote_node_get_memory_stats params<REMOTE_NODE_MEMORY_STATS_MAX>; + int nparams; +}; + struct remote_node_get_cells_free_memory_args { int startCell; int maxCells; @@ -2291,7 +2309,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3 = 216, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_FINISH3 = 217, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3 = 218, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219, /* skipgen skipgen */ + REMOTE_PROC_NODE_GET_MEMORY_STATS = 220 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? -- 1.7.1

virNodeGetMemoryStats: Implement virsh support Add nodecpustats subcommand to virsh. This subcommand prints below output. [Linux] # build/tools/virsh nodememstats total : 8058876 kB free : 5042384 kB buffers : 832728 kB cached : 776736 kB Signed-off-by: Minoru Usui <usui@mxm.nes.nec.co.jp> --- tools/virsh.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++++ 2 files changed, 55 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index de49489..9e57b27 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3490,6 +3490,56 @@ cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } /* + * "nodememstats" command + */ +static const vshCmdInfo info_nodememstats[] = { + {"help", N_("Prints memory stats of the node.")}, + {"desc", N_("Returns memory stats of the node.(KB)")}, + {NULL, NULL} +}; + +static bool +cmdNodememstats(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED) +{ + int nparams = 0; + unsigned int i = 0; + virMemoryStatsPtr params = NULL; + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + /* get the number of memory parameters */ + if (virNodeGetMemoryStats(ctl->conn, NULL, &nparams, 0) != 0) { + vshError(ctl, "%s", + _("Unable to get number of memory stats")); + goto cleanup; + } + + if (nparams == 0) { + /* nothing to output */ + ret = true; + goto cleanup; + } + + /* now go get all the memory parameters */ + params = vshCalloc(ctl, nparams, sizeof(*params)); + if (virNodeGetMemoryStats(ctl->conn, params, &nparams, 0) != 0) { + vshError(ctl, "%s", _("Unable to get memory stats")); + goto cleanup; + } + + for (i = 0; i < nparams; i++) + vshPrint(ctl, "%-15s: %20llu kB\n", params[i].field, params[i].value); + + ret = true; + + cleanup: + VIR_FREE(params); + return ret; +} + +/* * "capabilities" command */ static const vshCmdInfo info_capabilities[] = { @@ -10998,6 +11048,7 @@ static const vshCmdDef hostAndHypervisorCmds[] = { {"freecell", cmdFreecell, opts_freecell, info_freecell, 0}, {"hostname", cmdHostname, NULL, info_hostname, 0}, {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0}, + {"nodememstats", cmdNodememstats, NULL, info_nodememstats, 0}, {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, info_qemu_monitor_command, 0}, {"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index ef01f41..bafc6f2 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -237,6 +237,10 @@ Print the XML representation of the hypervisor sysinfo, if available. Returns basic information about the node, like number and type of CPU, and size of the physical memory. +=item B<nodememstats> + +Returns memory stats of the node. + =item B<capabilities> Print an XML document describing the capabilities of the hypervisor -- 1.7.1

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

On Wed, May 25, 2011 at 05:53:02PM +0900, Minoru Usui wrote:
Hi, everyone.
I wrote new API called virNodeGetMemoryStats(). It returns total/free/buffer/cached memory of the node from /proc/meminfo. The user I/F is like virNodeGetCPUStats().
Previous discussion is here. (See about virNodeGetFreeMemory() part.)
http://www.mail-archive.com/libvir-list@redhat.com/msg32553.html
This patch series based on latest git tree. If my virNodeGetCPUStats() patch series applied, I'll rebase on it.
As with your patches for CPUStats, I think the code in this series looks good, but have a question about the API design. I think we should add a parameter to the API to allow a query of memory data for individual NUMA cells int virNodeGetMemoryStats(virConnectPtr conn, int cellNum, virMemoryStatsPtr params, int *nparams, unsigned int flags); And a constant to use if wanting cummulative data for all cells #define VIR_NODE_MEMORY_STATS_ALL_CELLS -1 So to get memory stats for each NUMA cell virNodeGetMemoryStats(conn, 0, params, nparams, flags); virNodeGetMemoryStats(conn, 1, params, nparams, flags); virNodeGetMemoryStats(conn, 2, params, nparams, flags); virNodeGetMemoryStats(conn, 3, params, nparams, flags); Or to get memory statas for the whole host virNodeGetMemoryStats(conn, VIR_NODE_MEMORY_STATS_ALL_CELLS, params, nparams, flags); You currently use /proc/meminfo to get the host memory stats. The per-NUMA node data is available in similar format at the files: /sys/devices/system/node/*/meminfo Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Minoru Usui