virNodeGetCPUStats: Implement linux support Signed-off-by: Minoru Usui <usui@xxxxxxxxxxxxxxxxx> --- src/libvirt_private.syms | 1 + src/lxc/lxc_driver.c | 1 + src/nodeinfo.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ src/nodeinfo.h | 6 ++- src/qemu/qemu_driver.c | 1 + src/uml/uml_driver.c | 1 + 6 files changed, 147 insertions(+), 1 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e6ab870..a8c77f2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -731,6 +731,7 @@ virNodeDeviceObjUnlock; # nodeinfo.h nodeCapsInitNUMA; +nodeGetCPUStats; nodeGetCellsFreeMemory; nodeGetFreeMemory; nodeGetInfo; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 8eb87a2..3286154 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2779,6 +2779,7 @@ static virDriver lxcDriver = { .domainSetSchedulerParameters = lxcSetSchedulerParameters, /* 0.5.0 */ .domainSetSchedulerParametersFlags = lxcSetSchedulerParametersFlags, /* 0.9.2 */ .domainInterfaceStats = lxcDomainInterfaceStats, /* 0.7.3 */ + .nodeGetCPUStats = nodeGetCPUStats, /* 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 f55c83e..39afd0e 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -57,12 +57,20 @@ #ifdef __linux__ # define CPUINFO_PATH "/proc/cpuinfo" # define CPU_SYS_PATH "/sys/devices/system/cpu" +# define PROCSTAT_PATH "/proc/stat" + +# define LINUX_NB_CPU_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 linuxNodeGetCPUStats(FILE *procstat, + int cpuNum, + virCPUStatsPtr 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 * file could not be found, return 1 instead of an error; this is @@ -378,6 +386,108 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo, return 0; } +#define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / sysconf(_SC_CLK_TCK)) +#define CPU_HEADER_LEN 8 + +int linuxNodeGetCPUStats(FILE *procstat, + int cpuNum, + virCPUStatsPtr params, + int *nparams) +{ + int ret = -1; + char line[1024]; + unsigned long long usr, ni, sys, idle, iowait; + unsigned long long irq, softirq, steal, guest, guest_nice; + char cpu_header[CPU_HEADER_LEN]; + + if ((*nparams) == 0) { + /* Current number of cpu stats supported by linux */ + *nparams = LINUX_NB_CPU_STATS; + ret = 0; + goto cleanup; + } + + if ((*nparams) != LINUX_NB_CPU_STATS) { + nodeReportError(VIR_ERR_INVALID_ARG, + "%s", _("Invalid parameter count")); + goto cleanup; + } + + if (cpuNum == VIR_CPU_STATS_ALL_CPUS) { + strcpy(cpu_header, "cpu"); + } else { + sprintf(cpu_header, "cpu%d", cpuNum); + } + + while (fgets(line, sizeof(line), procstat) != NULL) { + char *buf = line; + + if (STRPREFIX(buf, cpu_header)) { /* aka logical CPU time */ + int i; + + if (sscanf(buf, + "%*s %llu %llu %llu %llu %llu" // user ~ iowait + "%llu %llu %llu %llu %llu", // irq ~ guest_nice + &usr, &ni, &sys, &idle, &iowait, + &irq, &softirq, &steal, &guest, &guest_nice) < 4) { + continue; + } + + for (i = 0; i < *nparams; i++) { + virCPUStatsPtr param = ¶ms[i]; + + switch (i) { + case 0: /* fill kernel cpu time here */ + if (virStrcpyStatic(param->field, VIR_CPU_STATS_KERNEL)== NULL) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Field kernel cpu time too long for destination")); + goto cleanup; + } + param->value = (sys + irq + softirq) * TICK_TO_NSEC; + break; + + case 1: /* fill user cpu time here */ + if (virStrcpyStatic(param->field, VIR_CPU_STATS_USER) == NULL) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Field kernel cpu time too long for destination")); + goto cleanup; + } + param->value = (usr + ni) * TICK_TO_NSEC; + break; + + case 2: /* fill idle cpu time here */ + if (virStrcpyStatic(param->field, VIR_CPU_STATS_IDLE) == NULL) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Field kernel cpu time too long for destination")); + goto cleanup; + } + param->value = idle * TICK_TO_NSEC; + break; + + case 3: /* fill iowait cpu time here */ + if (virStrcpyStatic(param->field, VIR_CPU_STATS_IOWAIT) == NULL) { + nodeReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Field kernel cpu time too long for destination")); + goto cleanup; + } + param->value = iowait * TICK_TO_NSEC; + break; + + default: + break; + /* should not hit here */ + } + } + ret = 0; + goto cleanup; + } + } + + nodeReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid cpu number")); + +cleanup: + return ret; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -416,6 +526,34 @@ int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { #endif } +int nodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED, + int cpuNum, + virCPUStatsPtr params, + int *nparams, + unsigned int flags ATTRIBUTE_UNUSED) +{ + +#ifdef __linux__ + { + int ret; + FILE *procstat = fopen(PROCSTAT_PATH, "r"); + if (!procstat) { + virReportSystemError(errno, + _("cannot open %s"), PROCSTAT_PATH); + return -1; + } + ret = linuxNodeGetCPUStats(procstat, cpuNum, params, nparams); + VIR_FORCE_FCLOSE(procstat); + + return ret; + } +#else + nodeReportError(VIR_ERR_NO_SUPPORT, "%s", + _("node CPU 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..361e3e5 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -30,7 +30,11 @@ int nodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo); int nodeCapsInitNUMA(virCapsPtr caps); - +int nodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED, + int cpuNum, + virCPUStatsPtr 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 5632d62..108a37f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8044,6 +8044,7 @@ static virDriver qemuDriver = { .domainBlockPeek = qemudDomainBlockPeek, /* 0.4.4 */ .domainMemoryPeek = qemudDomainMemoryPeek, /* 0.4.4 */ .domainGetBlockInfo = qemuDomainGetBlockInfo, /* 0.8.1 */ + .nodeGetCPUStats = nodeGetCPUStats, /* 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 536cd8c..471277d 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 */ + .nodeGetCPUStats = nodeGetCPUStats, /* 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