[libvirt] [PATCH 2/3 V7] qemu driver for virDomainGetCPUstats using cpuacct cgroup.

Eric Blake eblake at redhat.com
Wed Mar 7 00:55:20 UTC 2012


On 03/01/2012 07:54 PM, Lai Jiangshan wrote:
> From: KAMEZAWA Hiroyuki <kamezawa.hiroyu at jp.fujitsu.com>
> 
> * Now, only "cpu_time" is supported.
> * cpuacct cgroup is used for providing percpu cputime information.
> 
> * include/libvirt/libvirt.h.in  - defines VIR_DOMAIN_CPU_STATS_CPUTIME

Stale commit message; this change was committed earlier.

> * src/qemu/qemu.conf     - take care of cpuacct cgroup.
> * src/qemu/qemu_conf.c   - take care of cpuacct cgroup.
> * src/qemu/qemu_driver.c - added an interface
> * src/util/cgroup.c/h    - added interface for getting percpu cputime
> 
> Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu at jp.fujitsu.com>
> Signed-off-by: Lai Jiangshan <laijs at cn.fujitsu.com>
> ---
>  src/qemu/qemu.conf     |    3 +-
>  src/qemu/qemu_conf.c   |    3 +-
>  src/qemu/qemu_driver.c |  146 ++++++++++++++++++++++++++++++++++++++++++++++++
>  src/util/cgroup.c      |    6 ++
>  src/util/cgroup.h      |    1 +
>  5 files changed, 157 insertions(+), 2 deletions(-)
> 

> +++ b/src/qemu/qemu_driver.c
> @@ -12087,6 +12087,151 @@ cleanup:
>      return ret;
>  }
>  
> +/* qemuDomainGetCPUStats() with start_cpu == -1 */
> +static int
> +qemuDomainGetTotalcpuStats(virCgroupPtr group,
> +                          virTypedParameterPtr params,
> +                           int nparams)

Indentation.

> +static int qemuDomainGetPercpuStats(virDomainPtr domain,
> +                                    virCgroupPtr group,
> +                                    virTypedParameterPtr params,
> +                                    unsigned int nparams,
> +                                    int start_cpu,
> +                                    unsigned int ncpus)
> +{
> +    const char *map = NULL;

Same issue with 'const' as in 1/3.

> +    int rv = -1;
> +    int i, max_id;
> +    char *pos;
> +    char *buf = NULL;
> +    virTypedParameterPtr ent;
> +    int param_idx;
> +
> +    /* return the number of supported params ? */

The ? makes it look like you weren't sure.

> +    if (ncpus == 0) { /* returns max cpu ID */
> +        rv = max_id;
> +        goto cleanup;
> +    }

Actually, rv must be max_id + 1 (the size of the array, not the index of
the last element in the array).

> +    if (max_id > start_cpu + ncpus - 1)
> +        max_id = start_cpu + ncpus - 1;
> +
> +    for (i = 0; i <= max_id; i++) {
> +        unsigned long long cpu_time;

Hmm - start_cpu and ncpus are user-supplied values; their sum could
overflow, and we should not misbehave in that case.  If the user passes
a start_cpu of 1000000, it's probably better to return an error stating
that start_cpu is out of range, rather than returning 1 without
populating information.  On the other hand, if max_id is 3, and the user
passes start_cpu 3 and ncpus 2, it makes sense to return just the one
in-range entity, rather than erroring out because ncpus was too large.

> +        if (!map[i])
> +            continue;
> +        if (virStrToLong_ull(pos, &pos, 10, &cpu_time) < 0) {
> +            qemuReportError(VIR_ERR_INTERNAL_ERROR,
> +                            _("cpuacct parse error"));
> +            goto cleanup;
> +        }
> +        if (i < start_cpu)
> +            continue;

Hmm - this leaves the return struct unpopulated for offline cpus, but
the RPC protocol strips out holes on the sending side, and
reconstructing on the receiving side won't know where to restore holes.
 It is only safe to leave holes at the end of the array (if ncpus goes
beyond max_id), and we must explicitly populate 0 rather than skipping
offline cpus, if they are in range.

I confirmed that it also means we need to tweak the RPC code to allow
reconstruction with fewer elements than ncpus, in the case where ncpus
is too large.  But I will submit that as a separate patch.

> +
> +static int
> +qemuDomainGetCPUStats(virDomainPtr domain,
> +                virTypedParameterPtr params,
> +                unsigned int nparams,
> +                int start_cpu,
> +                unsigned int ncpus,
> +                unsigned int flags)
> +{

> +
> +    if (nparams == 0 && ncpus == 0) /* returns max cpu id */
> +        ret = qemuDomainGetPercpuStats(domain, group, NULL, 0, 0, 0);

If start_cpu is -1, then ncpus is non-zero; therefore, this conditional
could safely be deferred to be second, at which point it becomes redundant.

> +    else if (start_cpu == -1) /* get total */
> +        ret = qemuDomainGetTotalcpuStats(group, params, nparams);
> +    else
> +        ret = qemuDomainGetPercpuStats(domain, group, params, nparams,
> +                                       start_cpu, ncpus);

> +++ b/src/util/cgroup.c
> @@ -1555,6 +1555,12 @@ int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage)
>                                  "cpuacct.usage", usage);
>  }
>  
> +int virCgroupGetCpuacctPercpuUsage(virCgroupPtr group, char **usage)

Missing an export for this one.

ACK with this squashed in:

diff --git i/src/libvirt_private.syms w/src/libvirt_private.syms
index 1f58832..c44c617 100644
--- i/src/libvirt_private.syms
+++ w/src/libvirt_private.syms
@@ -74,16 +74,17 @@ virCgroupForDriver;
 virCgroupForVcpu;
 virCgroupFree;
 virCgroupGetBlkioWeight;
-virCgroupGetCpuShares;
 virCgroupGetCpuCfsPeriod;
 virCgroupGetCpuCfsQuota;
+virCgroupGetCpuShares;
+virCgroupGetCpuacctPercpuUsage;
 virCgroupGetCpuacctUsage;
 virCgroupGetCpusetMems;
 virCgroupGetFreezerState;
+virCgroupGetMemSwapHardLimit;
 virCgroupGetMemoryHardLimit;
 virCgroupGetMemorySoftLimit;
 virCgroupGetMemoryUsage;
-virCgroupGetMemSwapHardLimit;
 virCgroupKill;
 virCgroupKillPainfully;
 virCgroupKillRecursive;
@@ -92,15 +93,15 @@ virCgroupPathOfController;
 virCgroupRemove;
 virCgroupSetBlkioDeviceWeight;
 virCgroupSetBlkioWeight;
-virCgroupSetCpuShares;
 virCgroupSetCpuCfsPeriod;
 virCgroupSetCpuCfsQuota;
+virCgroupSetCpuShares;
 virCgroupSetCpusetMems;
 virCgroupSetFreezerState;
+virCgroupSetMemSwapHardLimit;
 virCgroupSetMemory;
 virCgroupSetMemoryHardLimit;
 virCgroupSetMemorySoftLimit;
-virCgroupSetMemSwapHardLimit;


 # command.h
diff --git i/src/qemu/qemu_driver.c w/src/qemu/qemu_driver.c
index 98b3c25..538a419 100644
--- i/src/qemu/qemu_driver.c
+++ w/src/qemu/qemu_driver.c
@@ -12098,7 +12098,7 @@ cleanup:
 /* qemuDomainGetCPUStats() with start_cpu == -1 */
 static int
 qemuDomainGetTotalcpuStats(virCgroupPtr group,
-                          virTypedParameterPtr params,
+                           virTypedParameterPtr params,
                            int nparams)
 {
     unsigned long long cpu_time;
@@ -12119,14 +12119,15 @@ qemuDomainGetTotalcpuStats(virCgroupPtr group,
     return 1;
 }

-static int qemuDomainGetPercpuStats(virDomainPtr domain,
-                                    virCgroupPtr group,
-                                    virTypedParameterPtr params,
-                                    unsigned int nparams,
-                                    int start_cpu,
-                                    unsigned int ncpus)
+static int
+qemuDomainGetPercpuStats(virDomainPtr domain,
+                         virCgroupPtr group,
+                         virTypedParameterPtr params,
+                         unsigned int nparams,
+                         int start_cpu,
+                         unsigned int ncpus)
 {
-    const char *map = NULL;
+    char *map = NULL;
     int rv = -1;
     int i, max_id;
     char *pos;
@@ -12134,7 +12135,7 @@ static int qemuDomainGetPercpuStats(virDomainPtr
domain,
     virTypedParameterPtr ent;
     int param_idx;

-    /* return the number of supported params ? */
+    /* return the number of supported params */
     if (nparams == 0 && ncpus != 0)
         return 1; /* only cpu_time is supported */

@@ -12146,23 +12147,31 @@ static int
qemuDomainGetPercpuStats(virDomainPtr domain,
         return rv;

     if (ncpus == 0) { /* returns max cpu ID */
-        rv = max_id;
+        rv = max_id + 1;
+        goto cleanup;
+    }
+
+    if (start_cpu > max_id) {
+        qemuReportError(VIR_ERR_INVALID_ARG,
+                        _("start_cpu %d larger than maximum of %d"),
+                        start_cpu, max_id);
         goto cleanup;
     }
+
     /* we get percpu cputime accounting info. */
     if (virCgroupGetCpuacctPercpuUsage(group, &buf))
         goto cleanup;
     pos = buf;

-    if (max_id > start_cpu + ncpus - 1)
+    if (max_id - start_cpu > ncpus - 1)
         max_id = start_cpu + ncpus - 1;

     for (i = 0; i <= max_id; i++) {
         unsigned long long cpu_time;

-        if (!map[i])
-            continue;
-        if (virStrToLong_ull(pos, &pos, 10, &cpu_time) < 0) {
+        if (!map[i]) {
+            cpu_time = 0;
+        } else if (virStrToLong_ull(pos, &pos, 10, &cpu_time) < 0) {
             qemuReportError(VIR_ERR_INTERNAL_ERROR,
                             _("cpuacct parse error"));
             goto cleanup;
@@ -12225,9 +12234,7 @@ qemuDomainGetCPUStats(virDomainPtr domain,
         goto cleanup;
     }

-    if (nparams == 0 && ncpus == 0) /* returns max cpu id */
-        ret = qemuDomainGetPercpuStats(domain, group, NULL, 0, 0, 0);
-    else if (start_cpu == -1) /* get total */
+    if (start_cpu == -1)
         ret = qemuDomainGetTotalcpuStats(group, params, nparams);
     else
         ret = qemuDomainGetPercpuStats(domain, group, params, nparams,

-- 
Eric Blake   eblake at redhat.com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 620 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20120306/97755e71/attachment-0001.sig>


More information about the libvir-list mailing list