]> xenbits.xensource.com Git - libvirt.git/commitdiff
virNodeGetCPUStats: Implement linux support
authorMinoru Usui <usui@mxm.nes.nec.co.jp>
Tue, 7 Jun 2011 01:02:55 +0000 (10:02 +0900)
committerEric Blake <eblake@redhat.com>
Tue, 14 Jun 2011 21:57:26 +0000 (15:57 -0600)
Signed-off-by: Minoru Usui <usui@mxm.nes.nec.co.jp>
src/libvirt_private.syms
src/lxc/lxc_driver.c
src/nodeinfo.c
src/nodeinfo.h
src/qemu/qemu_driver.c
src/uml/uml_driver.c

index 737cd31deaf4fabca441f3111d8092f900bff036..32523a07875b0117c28a90dbdeaf7a8c5724aa84 100644 (file)
@@ -733,6 +733,7 @@ virNodeDeviceObjUnlock;
 
 # nodeinfo.h
 nodeCapsInitNUMA;
+nodeGetCPUStats;
 nodeGetCellsFreeMemory;
 nodeGetFreeMemory;
 nodeGetInfo;
index 9ef75f5f2f5ca26fb2d92bebeabea7399577d093..338ea7304a41d37f8e818729f3000669055ec58f 100644 (file)
@@ -2885,6 +2885,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 */
index 1fe6ec9984635608865bac051bec04ff4a2288fb..bdf8f8a7b0161c84b7a645b54d2d7c30cdcfb9a4 100644 (file)
 #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);
 
+static 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
@@ -376,6 +384,107 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
     return 0;
 }
 
+# define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / sysconf(_SC_CLK_TCK))
+
+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[3 + INT_BUFSIZE_BOUND(cpuNum)];
+
+    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 {
+        snprintf(cpu_header, sizeof(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 = &params[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) {
@@ -414,6 +523,35 @@ int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) {
 #endif
 }
 
+int nodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED,
+                    int cpuNum,
+                    virCPUStatsPtr params,
+                    int *nparams,
+                    unsigned int flags)
+{
+    virCheckFlags(0, -1);
+
+#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
index 88bac6cf1107ddcb841125f1609d0e26780da785..361e3e5d9c3e444423dbd1fef5a46a6a5db0db42 100644 (file)
 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,
index e18728a38108a7ddd34b65e6b4549fe391261e1a..890769bc3cb5aa605f44d58f9bad0bdd614ad9df 100644 (file)
@@ -8092,6 +8092,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 */
index 4427e95c96d250df0c8e9913f09793e0643f7161..59b910ae5a472fe34734579d3e0d094160ea33c9 100644 (file)
@@ -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 */