[libvirt] [PATCH 4/5 V5] qemu driver for virDomainGetCPUstats using cpuacct cgroup.
Eric Blake
eblake at redhat.com
Mon Feb 13 19:55:48 UTC 2012
On 02/09/2012 03:43 AM, 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
> * 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 | 157 ++++++++++++++++++++++++++++++++++++++++++++++++
> src/util/cgroup.c | 6 ++
> src/util/cgroup.h | 1 +
> 5 files changed, 168 insertions(+), 2 deletions(-)
>
> diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
> index 95428c1..db07b8a 100644
> --- a/src/qemu/qemu.conf
> +++ b/src/qemu/qemu.conf
> @@ -166,6 +166,7 @@
> # - 'memory' - use for memory tunables
> # - 'blkio' - use for block devices I/O tunables
> # - 'cpuset' - use for CPUs and memory nodes
> +# - cpuacct - use for CPUs statistics.
Needs consistent quoting with lines above.
>
> +
> +/* qemuDomainGetCPUStats() with start_cpu == -1 */
> +static int qemuDomainGetTotalcpuStats(virDomainPtr domain ATTRIBUTE_UNUSED,
> + virCgroupPtr group,
> + virTypedParameterPtr params,
> + int nparams,
> + unsigned int flags)
Formatting nit: these days, we are preferring to start new function
names on column 1, with the return type on the previous line, as in:
static int
qemuDomainGetTotalCpuStats(...)
Why are you passing an unused parameter through to a static function?
The only time that should happen is in a callback interface, but you are
directly calling this function, so I'd prune the parameter instead.
> +{
> + unsigned long long cpu_time;
> + int param_idx = 0;
> + int ret;
> +
> + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
No need to re-check flags; we already checked it up front in the primary
entry point. At which point, you probably don't need the flags
parameter in this function.
> +
> + if (nparams == 0) /* return supprted number of params */
s/supprted/supported/
> + return 1;
> + /* entry 0 is cputime */
> + ret = virCgroupGetCpuacctUsage(group, &cpu_time);
> + if (ret < 0) {
> + virReportSystemError(-ret, "%s", _("unable to get cpu account"));
> + return -1;
> + }
> +
> + virTypedParameterAssign(¶ms[param_idx], VIR_DOMAIN_CPU_STATS_CPUTIME,
> + VIR_TYPED_PARAM_ULLONG, cpu_time);
> + return param_idx + 1;
Why are you using the 'param_idx' variable, which is always 0? I'd
simplify this to just 'return 1;', and worry about things if we ever add
additional parameters later.
> +}
> +
> +static int qemuDomainGetPercpuStats(virDomainPtr domain,
> + virCgroupPtr group,
> + virTypedParameterPtr params,
> + unsigned int nparams,
> + int start_cpu,
> + unsigned int ncpus,
> + unsigned int flags)
> +{
> + virBitmapPtr map = NULL;
> + int rv = -1;
> + int i, max_id;
> + char *pos;
> + char *buf = NULL;
> + virTypedParameterPtr ent;
> + int param_idx;
> +
> + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
Again, why check flags here? It should have already been done in the
caller.
> + /* return the number of supported params ? */
> + if (nparams == 0 && ncpus != 0)
> + return 1; /* only cpu_time is supported */
> +
> + /* return percpu cputime in index 0 */
> + param_idx = 0;
> + /* to parse account file, we need "present" cpu map */
> + map = nodeGetCPUmap(domain->conn, &max_id, "present");
I'm wondering if you can just use virDomainCpuSetParse to get a cpumap,
and then use macros like VIR_CPU_USABLE in parsing that map, rather than
going through virBitmap.
> + if (!map)
> + return rv;
> +
> + if (ncpus == 0) { /* returns max cpu ID */
> + rv = max_id;
> + goto cleanup;
> + }
> + /* we get percpu cputime accoutning info. */
s/accoutning/accounting/
> + if (virCgroupGetCpuacctPercpuUsage(group, &buf))
> + goto cleanup;
> + pos = buf;
> +
> + if (max_id > start_cpu + ncpus - 1)
> + max_id = start_cpu + ncpus - 1;
> +
> + for (i = 0; i <= max_id; i++) {
> + bool exist;
> + unsigned long long cpu_time;
> +
> + if (virBitmapGetBit(map, i, &exist) < 0) {
> + qemuReportError(VIR_ERR_INTERNAL_ERROR, _("bitmap parse error"));
> + goto cleanup;
> + }
> + if (!exist)
> + 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;
> + ent = ¶ms[ (i - start_cpu) * nparams + param_idx];
> + virTypedParameterAssign(ent, VIR_DOMAIN_CPU_STATS_CPUTIME,
> + VIR_TYPED_PARAM_ULLONG, cpu_time);
> + }
> + rv = param_idx + 1;
This part looks right, even if we aren't incrementing param_idx until
some future date when we add a second parameter.
> +cleanup:
> + VIR_FREE(buf);
> + virBitmapFree(map);
> + return rv;
> +}
> +
> +
> +static int
> +qemuDomainGetCPUStats(virDomainPtr domain,
> + virTypedParameterPtr params,
> + unsigned int nparams,
> + int start_cpu,
> + unsigned int ncpus,
> + unsigned int flags)
> +{
> + struct qemud_driver *driver = domain->conn->privateData;
> + virCgroupPtr group = NULL;
> + virDomainObjPtr vm = NULL;
> + int ret = -1;
> + bool isActive;
> +
> + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
> +
> + qemuDriverLock(driver);
> +
> + vm = virDomainFindByUUID(&driver->domains, domain->uuid);
> + if (vm == NULL) {
> + qemuReportError(VIR_ERR_INTERNAL_ERROR,
> + _("No such domain %s"), domain->uuid);
> + goto cleanup;
> + }
> +
> + isActive = virDomainObjIsActive(vm);
> + if (!isActive) {
> + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
> + _("domain is not running"));
> + goto cleanup;
> + }
> +
> + if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPUACCT)) {
> + qemuReportError(VIR_ERR_OPERATION_INVALID,
> + "%s", _("cgroup CPUACCT controller is not mounted"));
> + goto cleanup;
> + }
> +
> + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) {
> + qemuReportError(VIR_ERR_INTERNAL_ERROR,
> + _("cannot find cgroup for domain %s"), vm->def->name);
> + goto cleanup;
> + }
> +
> + if (nparams == 0 && ncpus == 0) /* returns max cpu id */
> + ret = qemuDomainGetPercpuStats(domain, group, NULL, 0, 0, 0, flags);
> + else if (start_cpu == -1) /* get total */
> + ret = qemuDomainGetTotalcpuStats(domain, group, params, nparams, flags);
> + else
> + ret = qemuDomainGetPercpuStats(domain, group, params, nparams,
> + start_cpu, ncpus ,flags);
Formatting nit: s/ ,/, /
> +cleanup:
> + virCgroupFree(&group);
> + if (vm)
> + virDomainObjUnlock(vm);
> + qemuDriverUnlock(driver);
> + return ret;
> +}
> +
> static virDriver qemuDriver = {
> .no = VIR_DRV_QEMU,
> .name = "QEMU",
> @@ -12193,6 +12349,7 @@ static virDriver qemuDriver = {
> .domainGetDiskErrors = qemuDomainGetDiskErrors, /* 0.9.10 */
> .domainSetMetadata = qemuDomainSetMetadata, /* 0.9.10 */
> .domainGetMetadata = qemuDomainGetMetadata, /* 0.9.10 */
> + .domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.10 */
We've now missed 0.9.10, so this should be 0.9.11.
--
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/20120213/be0a9fa0/attachment-0001.sig>
More information about the libvir-list
mailing list