mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06:00
qemu: add per-vcpu delay stats
This patch adds delay time (steal time inside guest) to libvirt domain per-vcpu stats. Delay time is an important performance metric. It is a consequence of the overloaded CPU. Knowledge of the delay time of a virtual machine helps to understand if it is affected and estimate the impact. As a result, it is possible to react exactly when needed and rebalance the load between hosts. This is used by cloud providers to provide quality of service, especially when the CPU is oversubscribed. It's more convenient to work with this metric in a context of a libvirt domain. Any monitoring software may use this information. Signed-off-by: Aleksei Zakharov <zaharov@selectel.ru> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
9b2f6c1030
commit
4719ec15e9
@ -2295,6 +2295,10 @@ When selecting the *--state* group the following fields are returned:
|
|||||||
* ``vcpu.<num>.halted`` - virtual CPU <num> is halted: yes or
|
* ``vcpu.<num>.halted`` - virtual CPU <num> is halted: yes or
|
||||||
no (may indicate the processor is idle or even disabled,
|
no (may indicate the processor is idle or even disabled,
|
||||||
depending on the architecture)
|
depending on the architecture)
|
||||||
|
* ``vcpu.<num>.delay`` - time the vCPU <num> thread was enqueued by the
|
||||||
|
host scheduler, but was waiting in the queue instead of running.
|
||||||
|
Exposed to the VM as a steal time.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*--interface* returns:
|
*--interface* returns:
|
||||||
|
@ -11693,6 +11693,10 @@ virConnectGetDomainCapabilities(virConnectPtr conn,
|
|||||||
* "vcpu.<num>.halted" - virtual CPU <num> is halted, may indicate the
|
* "vcpu.<num>.halted" - virtual CPU <num> is halted, may indicate the
|
||||||
* processor is idle or even disabled, depending
|
* processor is idle or even disabled, depending
|
||||||
* on the architecture)
|
* on the architecture)
|
||||||
|
* "vcpu.<num>.delay" - time the vCPU <num> thread was enqueued by the
|
||||||
|
* host scheduler, but was waiting in the queue
|
||||||
|
* instead of running. Exposed to the VM as a steal
|
||||||
|
* time.
|
||||||
*
|
*
|
||||||
* VIR_DOMAIN_STATS_INTERFACE:
|
* VIR_DOMAIN_STATS_INTERFACE:
|
||||||
* Return network interface statistics (from domain point of view).
|
* Return network interface statistics (from domain point of view).
|
||||||
|
@ -1333,6 +1333,37 @@ static char *qemuConnectGetCapabilities(virConnectPtr conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuGetSchedstatDelay(unsigned long long *cpudelay,
|
||||||
|
pid_t pid,
|
||||||
|
pid_t tid)
|
||||||
|
{
|
||||||
|
g_autofree char *path = NULL;
|
||||||
|
g_autofree char *buf = NULL;
|
||||||
|
|
||||||
|
if (tid)
|
||||||
|
path = g_strdup_printf("/proc/%d/task/%d/schedstat", (int)pid, (int)tid);
|
||||||
|
else
|
||||||
|
path = g_strdup_printf("/proc/%d/schedstat", (int)pid);
|
||||||
|
|
||||||
|
/* This file might not exist (needs CONFIG_SCHED_INFO) */
|
||||||
|
if (!virFileExists(path))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (virFileReadAll(path, 1024, &buf) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%*u %llu", cpudelay) != 1) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Unable to parse schedstat info at '%s'"),
|
||||||
|
path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuGetSchedInfo(unsigned long long *cpuWait,
|
qemuGetSchedInfo(unsigned long long *cpuWait,
|
||||||
pid_t pid, pid_t tid)
|
pid_t pid, pid_t tid)
|
||||||
@ -1470,6 +1501,7 @@ static int
|
|||||||
qemuDomainHelperGetVcpus(virDomainObjPtr vm,
|
qemuDomainHelperGetVcpus(virDomainObjPtr vm,
|
||||||
virVcpuInfoPtr info,
|
virVcpuInfoPtr info,
|
||||||
unsigned long long *cpuwait,
|
unsigned long long *cpuwait,
|
||||||
|
unsigned long long *cpudelay,
|
||||||
int maxinfo,
|
int maxinfo,
|
||||||
unsigned char *cpumaps,
|
unsigned char *cpumaps,
|
||||||
int maplen)
|
int maplen)
|
||||||
@ -1529,6 +1561,11 @@ qemuDomainHelperGetVcpus(virDomainObjPtr vm,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpudelay) {
|
||||||
|
if (qemuGetSchedstatDelay(&(cpudelay[ncpuinfo]), vm->pid, vcpupid) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ncpuinfo++;
|
ncpuinfo++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4873,7 +4910,7 @@ qemuDomainGetVcpus(virDomainPtr dom,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qemuDomainHelperGetVcpus(vm, info, NULL, maxinfo, cpumaps, maplen);
|
ret = qemuDomainHelperGetVcpus(vm, info, NULL, NULL, maxinfo, cpumaps, maplen);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
virDomainObjEndAPI(&vm);
|
virDomainObjEndAPI(&vm);
|
||||||
@ -17928,6 +17965,7 @@ qemuDomainGetStatsVcpu(virQEMUDriverPtr driver,
|
|||||||
int ret = -1;
|
int ret = -1;
|
||||||
virVcpuInfoPtr cpuinfo = NULL;
|
virVcpuInfoPtr cpuinfo = NULL;
|
||||||
g_autofree unsigned long long *cpuwait = NULL;
|
g_autofree unsigned long long *cpuwait = NULL;
|
||||||
|
g_autofree unsigned long long *cpudelay = NULL;
|
||||||
|
|
||||||
if (virTypedParamListAddUInt(params, virDomainDefGetVcpus(dom->def),
|
if (virTypedParamListAddUInt(params, virDomainDefGetVcpus(dom->def),
|
||||||
"vcpu.current") < 0)
|
"vcpu.current") < 0)
|
||||||
@ -17939,6 +17977,7 @@ qemuDomainGetStatsVcpu(virQEMUDriverPtr driver,
|
|||||||
|
|
||||||
cpuinfo = g_new0(virVcpuInfo, virDomainDefGetVcpus(dom->def));
|
cpuinfo = g_new0(virVcpuInfo, virDomainDefGetVcpus(dom->def));
|
||||||
cpuwait = g_new0(unsigned long long, virDomainDefGetVcpus(dom->def));
|
cpuwait = g_new0(unsigned long long, virDomainDefGetVcpus(dom->def));
|
||||||
|
cpudelay = g_new0(unsigned long long, virDomainDefGetVcpus(dom->def));
|
||||||
|
|
||||||
if (HAVE_JOB(privflags) && virDomainObjIsActive(dom) &&
|
if (HAVE_JOB(privflags) && virDomainObjIsActive(dom) &&
|
||||||
qemuDomainRefreshVcpuHalted(driver, dom, QEMU_ASYNC_JOB_NONE) < 0) {
|
qemuDomainRefreshVcpuHalted(driver, dom, QEMU_ASYNC_JOB_NONE) < 0) {
|
||||||
@ -17947,7 +17986,7 @@ qemuDomainGetStatsVcpu(virQEMUDriverPtr driver,
|
|||||||
virResetLastError();
|
virResetLastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qemuDomainHelperGetVcpus(dom, cpuinfo, cpuwait,
|
if (qemuDomainHelperGetVcpus(dom, cpuinfo, cpuwait, cpudelay,
|
||||||
virDomainDefGetVcpus(dom->def),
|
virDomainDefGetVcpus(dom->def),
|
||||||
NULL, 0) < 0) {
|
NULL, 0) < 0) {
|
||||||
virResetLastError();
|
virResetLastError();
|
||||||
@ -17972,6 +18011,10 @@ qemuDomainGetStatsVcpu(virQEMUDriverPtr driver,
|
|||||||
"vcpu.%u.wait", cpuinfo[i].number) < 0)
|
"vcpu.%u.wait", cpuinfo[i].number) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
if (virTypedParamListAddULLong(params, cpudelay[i],
|
||||||
|
"vcpu.%u.delay", cpuinfo[i].number) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
/* state below is extracted from the individual vcpu structs */
|
/* state below is extracted from the individual vcpu structs */
|
||||||
if (!(vcpu = virDomainDefGetVcpu(dom->def, cpuinfo[i].number)))
|
if (!(vcpu = virDomainDefGetVcpu(dom->def, cpuinfo[i].number)))
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
Reference in New Issue
Block a user