virt-host-validate: rewrite cgroup detection to use util/vircgroup

This removes code duplication and simplifies cgroup detection.
As a drawback we will not have separate messages to enable cgroup
controller in kernel or to mount it.  On the other side the rewrite
adds support for cgroup v2.

The kernel config support was wrong because it was parsing
'/proc/self/cgroup' instead of '/proc/cgroups/' file.

The mount suggestion is removed as well because it will not work
with cgroup v2.

Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
This commit is contained in:
Pavel Hrdina 2018-09-29 21:37:22 +02:00
parent a26de856f9
commit 0f4d7daa8c
4 changed files with 53 additions and 194 deletions

View File

@ -24,12 +24,10 @@
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#ifdef HAVE_MNTENT_H
# include <mntent.h>
#endif /* HAVE_MNTENT_H */
#include <sys/stat.h> #include <sys/stat.h>
#include "viralloc.h" #include "viralloc.h"
#include "vircgroup.h"
#include "virfile.h" #include "virfile.h"
#include "virt-host-validate-common.h" #include "virt-host-validate-common.h"
#include "virstring.h" #include "virstring.h"
@ -288,152 +286,50 @@ int virHostValidateLinuxKernel(const char *hvname,
} }
} }
#ifdef __linux__
static int virHostValidateCGroupSupport(const char *hvname, int virHostValidateCGroupControllers(const char *hvname,
const char *cg_name, int controllers,
virHostValidateLevel level, virHostValidateLevel level)
const char *config_name)
{ {
virHostMsgCheck(hvname, "for cgroup '%s' controller support", cg_name); virCgroupPtr group = NULL;
FILE *fp = fopen("/proc/self/cgroup", "r"); int ret = 0;
size_t len = 0; size_t i;
char *line = NULL;
ssize_t ret;
bool matched = false;
if (!fp) if (virCgroupNewSelf(&group) < 0)
goto error; return -1;
while ((ret = getline(&line, &len, fp)) >= 0 && !matched) { for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
char **cgroups; int flag = 1 << i;
char *start; const char *cg_name = virCgroupControllerTypeToString(i);
char *end;
size_t ncgroups;
size_t i;
/* Each line in this file looks like if (!(controllers & flag))
*
* 4:cpu,cpuacct:/machine.slice/machine-qemu\x2dtest.scope/emulator
*
* Since multiple cgroups can be part of the same line and some cgroup
* names can appear as part of other cgroup names (eg. 'cpu' is a
* prefix for both 'cpuacct' and 'cpuset'), it's not enough to simply
* check whether the cgroup name is present somewhere inside the file.
*
* Moreover, there's nothing stopping the cgroup name from appearing
* in an unrelated mount point name as well */
/* Look for the first colon.
* The part we're interested in starts right after it */
if (!(start = strchr(line, ':')))
continue;
start++;
/* Look for the second colon.
* The part we're interested in ends exactly there */
if (!(end = strchr(start, ':')))
continue;
*end = '\0';
if (!(cgroups = virStringSplitCount(start, ",", 0, &ncgroups)))
continue; continue;
/* Look for the matching cgroup */ virHostMsgCheck(hvname, "for cgroup '%s' controller support", cg_name);
for (i = 0; i < ncgroups; i++) {
if (STREQ(cgroups[i], cg_name)) if (!virCgroupHasController(group, i)) {
matched = true; ret = -1;
virHostMsgFail(level, "Enable '%s' in kernel Kconfig file or "
"mount/enable cgroup controller in your system",
cg_name);
} else {
virHostMsgPass();
} }
virStringListFreeCount(cgroups, ncgroups);
} }
VIR_FREE(line); virCgroupFree(&group);
VIR_FORCE_FCLOSE(fp);
if (!matched)
goto error;
virHostMsgPass(); return ret;
return 0;
error:
VIR_FREE(line);
virHostMsgFail(level, "Enable CONFIG_%s in kernel Kconfig file", config_name);
return -1;
} }
#else /* !__linux__ */
#ifdef HAVE_MNTENT_H int virHostValidateCGroupControllers(const char *hvname,
static int virHostValidateCGroupMount(const char *hvname, int controllers,
const char *cg_name, virHostValidateLevel level)
virHostValidateLevel level)
{ {
virHostMsgCheck(hvname, "for cgroup '%s' controller mount-point", cg_name);
FILE *fp = setmntent("/proc/mounts", "r");
struct mntent ent;
char mntbuf[1024];
bool matched = false;
if (!fp)
goto error;
while (getmntent_r(fp, &ent, mntbuf, sizeof(mntbuf)) && !matched) {
char **opts;
size_t nopts;
size_t i;
/* Ignore non-cgroup mounts */
if (STRNEQ(ent.mnt_type, "cgroup"))
continue;
if (!(opts = virStringSplitCount(ent.mnt_opts, ",", 0, &nopts)))
continue;
/* Look for a mount option matching the cgroup name */
for (i = 0; i < nopts; i++) {
if (STREQ(opts[i], cg_name))
matched = true;
}
virStringListFreeCount(opts, nopts);
}
endmntent(fp);
if (!matched)
goto error;
virHostMsgPass();
return 0;
error:
virHostMsgFail(level, "Mount '%s' cgroup controller (suggested at /sys/fs/cgroup/%s)",
cg_name, cg_name);
return -1;
}
#else /* ! HAVE_MNTENT_H */
static int virHostValidateCGroupMount(const char *hvname,
const char *cg_name,
virHostValidateLevel level)
{
virHostMsgCheck(hvname, "for cgroup '%s' controller mount-point", cg_name);
virHostMsgFail(level, "%s", "This platform does not support cgroups"); virHostMsgFail(level, "%s", "This platform does not support cgroups");
return -1; return -1;
} }
#endif /* ! HAVE_MNTENT_H */ #endif /* !__linux__ */
int virHostValidateCGroupController(const char *hvname,
const char *cg_name,
virHostValidateLevel level,
const char *config_name)
{
if (virHostValidateCGroupSupport(hvname,
cg_name,
level,
config_name) < 0)
return -1;
if (virHostValidateCGroupMount(hvname,
cg_name,
level) < 0)
return -1;
return 0;
}
int virHostValidateIOMMU(const char *hvname, int virHostValidateIOMMU(const char *hvname,
virHostValidateLevel level) virHostValidateLevel level)

View File

@ -77,10 +77,9 @@ int virHostValidateNamespace(const char *hvname,
virHostValidateLevel level, virHostValidateLevel level,
const char *hint); const char *hint);
int virHostValidateCGroupController(const char *hvname, int virHostValidateCGroupControllers(const char *hvname,
const char *cg_name, int controllers,
virHostValidateLevel level, virHostValidateLevel level);
const char *config_name);
int virHostValidateIOMMU(const char *hvname, int virHostValidateIOMMU(const char *hvname,
virHostValidateLevel level); virHostValidateLevel level);

View File

@ -23,6 +23,7 @@
#include "virt-host-validate-lxc.h" #include "virt-host-validate-lxc.h"
#include "virt-host-validate-common.h" #include "virt-host-validate-common.h"
#include "vircgroup.h"
int virHostValidateLXC(void) int virHostValidateLXC(void)
{ {
@ -63,35 +64,16 @@ int virHostValidateLXC(void)
_("User namespace support is recommended")) < 0) _("User namespace support is recommended")) < 0)
ret = -1; ret = -1;
if (virHostValidateCGroupController("LXC", "memory", if (virHostValidateCGroupControllers("LXC",
VIR_HOST_VALIDATE_FAIL, (1 << VIR_CGROUP_CONTROLLER_MEMORY) |
"MEMCG") < 0) (1 << VIR_CGROUP_CONTROLLER_CPU) |
ret = -1; (1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
(1 << VIR_CGROUP_CONTROLLER_CPUSET) |
if (virHostValidateCGroupController("LXC", "cpu", (1 << VIR_CGROUP_CONTROLLER_DEVICES) |
VIR_HOST_VALIDATE_FAIL, (1 << VIR_CGROUP_CONTROLLER_BLKIO),
"CGROUP_CPU") < 0) VIR_HOST_VALIDATE_FAIL) < 0) {
ret = -1;
if (virHostValidateCGroupController("LXC", "cpuacct",
VIR_HOST_VALIDATE_FAIL,
"CGROUP_CPUACCT") < 0)
ret = -1;
if (virHostValidateCGroupController("LXC", "cpuset",
VIR_HOST_VALIDATE_FAIL,
"CPUSETS") < 0)
ret = -1;
if (virHostValidateCGroupController("LXC", "devices",
VIR_HOST_VALIDATE_FAIL,
"CGROUP_DEVICE") < 0)
ret = -1;
if (virHostValidateCGroupController("LXC", "blkio",
VIR_HOST_VALIDATE_FAIL,
"BLK_CGROUP") < 0)
ret = -1; ret = -1;
}
#if WITH_FUSE #if WITH_FUSE
if (virHostValidateDeviceExists("LXC", "/sys/fs/fuse/connections", if (virHostValidateDeviceExists("LXC", "/sys/fs/fuse/connections",

View File

@ -26,6 +26,7 @@
#include "virt-host-validate-common.h" #include "virt-host-validate-common.h"
#include "virarch.h" #include "virarch.h"
#include "virbitmap.h" #include "virbitmap.h"
#include "vircgroup.h"
int virHostValidateQEMU(void) int virHostValidateQEMU(void)
{ {
@ -96,35 +97,16 @@ int virHostValidateQEMU(void)
_("Load the 'tun' module to enable networking for QEMU guests")) < 0) _("Load the 'tun' module to enable networking for QEMU guests")) < 0)
ret = -1; ret = -1;
if (virHostValidateCGroupController("QEMU", "memory", if (virHostValidateCGroupControllers("QEMU",
VIR_HOST_VALIDATE_WARN, (1 << VIR_CGROUP_CONTROLLER_MEMORY) |
"MEMCG") < 0) (1 << VIR_CGROUP_CONTROLLER_CPU) |
ret = -1; (1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
(1 << VIR_CGROUP_CONTROLLER_CPUSET) |
if (virHostValidateCGroupController("QEMU", "cpu", (1 << VIR_CGROUP_CONTROLLER_DEVICES) |
VIR_HOST_VALIDATE_WARN, (1 << VIR_CGROUP_CONTROLLER_BLKIO),
"CGROUP_CPU") < 0) VIR_HOST_VALIDATE_WARN) < 0) {
ret = -1;
if (virHostValidateCGroupController("QEMU", "cpuacct",
VIR_HOST_VALIDATE_WARN,
"CGROUP_CPUACCT") < 0)
ret = -1;
if (virHostValidateCGroupController("QEMU", "cpuset",
VIR_HOST_VALIDATE_WARN,
"CPUSETS") < 0)
ret = -1;
if (virHostValidateCGroupController("QEMU", "devices",
VIR_HOST_VALIDATE_WARN,
"CGROUP_DEVICES") < 0)
ret = -1;
if (virHostValidateCGroupController("QEMU", "blkio",
VIR_HOST_VALIDATE_WARN,
"BLK_CGROUP") < 0)
ret = -1; ret = -1;
}
if (virHostValidateIOMMU("QEMU", if (virHostValidateIOMMU("QEMU",
VIR_HOST_VALIDATE_WARN) < 0) VIR_HOST_VALIDATE_WARN) < 0)