[libvirt] [PATCH v2 1/2] BSD: implement nodeGetCPUStats

Roman Bogorodskiy bogorodskiy at gmail.com
Sun Jan 19 17:55:45 UTC 2014


Implementation obtains CPU usage information using
kern.cp_time and kern.cp_times sysctl(8)s and reports
CPU utilization.
---
 include/libvirt/libvirt.h.in |   8 ++++
 src/nodeinfo.c               | 104 +++++++++++++++++++++++++++++++++++++++++++
 tools/virsh-host.c           |  11 ++++-
 3 files changed, 121 insertions(+), 2 deletions(-)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 5bdb2bc..e8cdbbf 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -692,6 +692,14 @@ typedef enum {
 #define VIR_NODE_CPU_STATS_IOWAIT "iowait"
 
 /**
+ * VIR_NODE_CPU_STATS_INTR:
+ *
+ * The cumulative interrupt CPU time,
+ * since the node booting up (in nanoseconds).
+ */
+#define VIR_NODE_CPU_STATS_INTR "intr"
+
+/**
  * VIR_NODE_CPU_STATS_UTILIZATION:
  *
  * The CPU utilization of a node.
diff --git a/src/nodeinfo.c b/src/nodeinfo.c
index 05bc038..fd2f8c8 100644
--- a/src/nodeinfo.c
+++ b/src/nodeinfo.c
@@ -34,8 +34,10 @@
 #include "conf/domain_conf.h"
 
 #if defined(__FreeBSD__) || defined(__APPLE__)
+# include <sys/time.h>
 # include <sys/types.h>
 # include <sys/sysctl.h>
+# include <sys/resource.h>
 #endif
 
 #include "c-ctype.h"
@@ -99,8 +101,108 @@ appleFreebsdNodeGetMemorySize(unsigned long *memory)
 #endif /* defined(__FreeBSD__) || defined(__APPLE__) */
 
 #ifdef __FreeBSD__
+# define BSD_CPU_STATS_ALL 4
 # define BSD_MEMORY_STATS_ALL 4
 
+# define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / (stathz ? stathz : hz))
+
+static int
+freebsdNodeGetCPUStats(int cpuNum,
+                       virNodeCPUStatsPtr params,
+                       int *nparams)
+{
+    const char *sysctl_name;
+    long *cpu_times;
+    struct clockinfo clkinfo;
+    size_t i, j, cpu_times_size, clkinfo_size;
+    int cpu_times_num, offset, hz, stathz, ret = -1;
+    struct field_cpu_map {
+        const char *field;
+        int idx[CPUSTATES];
+    } cpu_map[] = {
+        {VIR_NODE_CPU_STATS_KERNEL, {CP_SYS}},
+        {VIR_NODE_CPU_STATS_USER, {CP_USER, CP_NICE}},
+        {VIR_NODE_CPU_STATS_IDLE, {CP_IDLE}},
+        {VIR_NODE_CPU_STATS_INTR, {CP_INTR}},
+        {NULL, {0}}
+    };
+
+    if ((*nparams) == 0) {
+        *nparams = BSD_CPU_STATS_ALL;
+        return 0;
+    }
+
+    if ((*nparams) != BSD_CPU_STATS_ALL) {
+        virReportInvalidArg(*nparams,
+                            _("nparams in %s must be equal to %d"),
+                            __FUNCTION__, BSD_CPU_STATS_ALL);
+        return -1;
+    }
+
+    clkinfo_size = sizeof(clkinfo);
+    if (sysctlbyname("kern.clockrate", &clkinfo, &clkinfo_size, NULL, 0) < 0) {
+        virReportSystemError(errno,
+                             _("sysctl failed for '%s'"),
+                             "kern.clockrate");
+        return -1;
+    }
+
+    stathz = clkinfo.stathz;
+    hz = clkinfo.hz;
+
+    if (cpuNum == VIR_NODE_CPU_STATS_ALL_CPUS) {
+        sysctl_name = "kern.cp_time";
+        cpu_times_num = 1;
+        offset = 0;
+    } else {
+        sysctl_name = "kern.cp_times";
+        cpu_times_num = appleFreebsdNodeGetCPUCount();
+
+        if (cpuNum >= cpu_times_num) {
+            virReportInvalidArg(cpuNum,
+                                _("Invalid cpuNum in %s"),
+                                __FUNCTION__);
+            return -1;
+        }
+
+        offset = cpu_times_num * CPUSTATES;
+    }
+
+    cpu_times_size = sizeof(long) * cpu_times_num * CPUSTATES;
+
+    if (VIR_ALLOC_N(cpu_times, cpu_times_num * CPUSTATES) < 0)
+        goto cleanup;
+
+    if (sysctlbyname(sysctl_name, cpu_times, &cpu_times_size, NULL, 0) < 0) {
+        virReportSystemError(errno,
+                             _("sysctl failed for '%s'"),
+                             sysctl_name);
+        goto cleanup;
+    }
+
+    for (i = 0; cpu_map[i].field != NULL; i++) {
+        virNodeCPUStatsPtr param = &params[i];
+
+        if (virStrcpyStatic(param->field, cpu_map[i].field) == NULL) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Field '%s' too long for destination"),
+                           cpu_map[i].field);
+            goto cleanup;
+        }
+
+        param->value = 0;
+        for (j = 0; j < ARRAY_CARDINALITY(cpu_map[i].idx); j++)
+            param->value += cpu_times[offset + cpu_map[i].idx[j]] * TICK_TO_NSEC;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(cpu_times);
+
+    return ret;
+}
+
 static int
 freebsdNodeGetMemoryStats(virNodeMemoryStatsPtr params,
                                int *nparams)
@@ -1066,6 +1168,8 @@ int nodeGetCPUStats(int cpuNum ATTRIBUTE_UNUSED,
 
         return ret;
     }
+#elif defined(__FreeBSD__)
+    return freebsdNodeGetCPUStats(cpuNum, params, nparams);
 #else
     virReportError(VIR_ERR_NO_SUPPORT, "%s",
                    _("node CPU stats not implemented on this platform"));
diff --git a/tools/virsh-host.c b/tools/virsh-host.c
index 1d1bb97..ac41177 100644
--- a/tools/virsh-host.c
+++ b/tools/virsh-host.c
@@ -347,9 +347,10 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
         unsigned long long sys;
         unsigned long long idle;
         unsigned long long iowait;
+        unsigned long long intr;
         unsigned long long util;
     } cpu_stats[2];
-    double user_time, sys_time, idle_time, iowait_time, total_time;
+    double user_time, sys_time, idle_time, iowait_time, intr_time, total_time;
     double usage;
 
     if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
@@ -390,6 +391,8 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
                 cpu_stats[i].idle = value;
             } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
                 cpu_stats[i].iowait = value;
+            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_INTR)) {
+                cpu_stats[i].intr = value;
             } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
                 cpu_stats[i].util = value;
                 flag_utilization = true;
@@ -406,6 +409,7 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
             vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
             vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
             vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
+            vshPrint(ctl, "%-15s %20llu\n", _("intr:"), cpu_stats[0].intr);
         }
     } else {
         if (flag_utilization) {
@@ -418,7 +422,8 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
             sys_time    = cpu_stats[1].sys    - cpu_stats[0].sys;
             idle_time   = cpu_stats[1].idle   - cpu_stats[0].idle;
             iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
-            total_time  = user_time + sys_time + idle_time + iowait_time;
+            intr_time   = cpu_stats[1].intr   - cpu_stats[0].intr;
+            total_time  = user_time + sys_time + idle_time + iowait_time + intr_time;
 
             usage = (user_time + sys_time) / total_time * 100;
 
@@ -432,6 +437,8 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
                      _("idle:"), idle_time     / total_time * 100);
             vshPrint(ctl, "%-15s %5.1lf%%\n",
                      _("iowait:"), iowait_time   / total_time * 100);
+            vshPrint(ctl, "%-15s %5.1lf%%\n",
+                     _("intr:"), intr_time         / total_time * 100);
         }
     }
 
-- 
1.8.4.3




More information about the libvir-list mailing list