From: "Daniel P. Berrange" <berrange(a)redhat.com>
If a cgroup controller is co-mounted with another, eg
/sys/fs/cgroup/cpu,cpuacct
Then it is a requirement that there exist symlinks at
/sys/fs/cgroup/cpu
/sys/fs/cgroup/cpuacct
pointing to the real mount point. Add support to virCgroupPtr
to detect and track these symlinks
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/util/vircgroup.c | 56 ++++++++++++++++++++++++++++++++++++++++++----
src/util/vircgrouppriv.h | 5 +++++
tests/vircgroupmock.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/vircgrouptest.c | 36 +++++++++++++++++++++++-------
4 files changed, 143 insertions(+), 12 deletions(-)
diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
index 6202614..14af16e 100644
--- a/src/util/vircgroup.c
+++ b/src/util/vircgroup.c
@@ -75,6 +75,7 @@ void virCgroupFree(virCgroupPtr *group)
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
VIR_FREE((*group)->controllers[i].mountPoint);
+ VIR_FREE((*group)->controllers[i].linkPoint);
VIR_FREE((*group)->controllers[i].placement);
}
@@ -114,6 +115,14 @@ static int virCgroupCopyMounts(virCgroupPtr group,
if (!group->controllers[i].mountPoint)
return -ENOMEM;
+
+ if (parent->controllers[i].linkPoint) {
+ group->controllers[i].linkPoint =
+ strdup(parent->controllers[i].linkPoint);
+
+ if (!group->controllers[i].linkPoint)
+ return -ENOMEM;
+ }
}
return 0;
}
@@ -157,9 +166,46 @@ static int virCgroupDetectMounts(virCgroupPtr group)
* first entry only
*/
if (typelen == len && STREQLEN(typestr, tmp, len) &&
- !group->controllers[i].mountPoint &&
- !(group->controllers[i].mountPoint = strdup(entry.mnt_dir)))
- goto no_memory;
+ !group->controllers[i].mountPoint) {
+ char *linksrc;
+ struct stat sb;
+ char *tmp2;
+
+ if (!(group->controllers[i].mountPoint = strdup(entry.mnt_dir)))
+ goto no_memory;
+
+ tmp2 = strrchr(entry.mnt_dir, '/');
+ if (!tmp2) {
+ errno = EINVAL;
+ goto error;
+ }
+ *tmp2 = '\0';
+ /* If it is a co-mount it has a filename like
"cpu,cpuacct"
+ * and we must identify the symlink path */
+ if (strchr(tmp2 + 1, ',')) {
+ if (virAsprintf(&linksrc, "%s/%s",
+ entry.mnt_dir, typestr) < 0)
+ goto no_memory;
+ *tmp2 = '/';
+
+ if (lstat(linksrc, &sb) < 0) {
+ if (errno == ENOENT) {
+ VIR_WARN("Controller %s co-mounted at %s is missing
symlink at %s",
+ typestr, entry.mnt_dir, linksrc);
+ VIR_FREE(linksrc);
+ } else {
+ goto error;
+ }
+ } else {
+ if (!S_ISLNK(sb.st_mode)) {
+ VIR_WARN("Expecting a symlink at %s for controller
%s",
+ linksrc, typestr);
+ } else {
+ group->controllers[i].linkPoint = linksrc;
+ }
+ }
+ }
+ }
tmp = next;
}
}
@@ -170,8 +216,10 @@ static int virCgroupDetectMounts(virCgroupPtr group)
return 0;
no_memory:
+ errno = ENOENT;
+error:
VIR_FORCE_FCLOSE(mounts);
- return -ENOMEM;
+ return -errno;
}
diff --git a/src/util/vircgrouppriv.h b/src/util/vircgrouppriv.h
index cc8cc0b..582be79 100644
--- a/src/util/vircgrouppriv.h
+++ b/src/util/vircgrouppriv.h
@@ -34,6 +34,11 @@
struct virCgroupController {
int type;
char *mountPoint;
+ /* If mountPoint holds several controllers co-mounted,
+ * then linkPoint is path of the symlink to the mountPoint
+ * for just the one controller
+ */
+ char *linkPoint;
char *placement;
};
diff --git a/tests/vircgroupmock.c b/tests/vircgroupmock.c
index e50f7e0..32f074b 100644
--- a/tests/vircgroupmock.c
+++ b/tests/vircgroupmock.c
@@ -32,6 +32,8 @@
static int (*realopen)(const char *path, int flags, ...);
static FILE *(*realfopen)(const char *path, const char *mode);
static int (*realaccess)(const char *path, int mode);
+static int (*reallstat)(const char *path, struct stat *sb);
+static int (*real__lxstat)(int ver, const char *path, struct stat *sb);
static int (*realmkdir)(const char *path, mode_t mode);
static char *fakesysfsdir;
@@ -314,8 +316,18 @@ static void init_syms(void)
} \
} while (0)
+#define LOAD_SYM_ALT(name1, name2) \
+ do { \
+ if (!(real ## name1 = dlsym(RTLD_NEXT, #name1)) && \
+ !(real ## name2 = dlsym(RTLD_NEXT, #name2))) { \
+ fprintf(stderr, "Cannot find real '%s' or '%s'
symbol\n", #name1, #name2); \
+ abort(); \
+ } \
+ } while (0)
+
LOAD_SYM(fopen);
LOAD_SYM(access);
+ LOAD_SYM_ALT(lstat, __lxstat);
LOAD_SYM(mkdir);
LOAD_SYM(open);
}
@@ -399,6 +411,52 @@ int access(const char *path, int mode)
return ret;
}
+int __lxstat(int ver, const char *path, struct stat *sb)
+{
+ int ret;
+
+ init_syms();
+
+ if (STRPREFIX(path, SYSFS_PREFIX)) {
+ init_sysfs();
+ char *newpath;
+ if (asprintf(&newpath, "%s/%s",
+ fakesysfsdir,
+ path + strlen(SYSFS_PREFIX)) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = real__lxstat(ver, newpath, sb);
+ free(newpath);
+ } else {
+ ret = real__lxstat(ver, path, sb);
+ }
+ return ret;
+}
+
+int lstat(const char *path, struct stat *sb)
+{
+ int ret;
+
+ init_syms();
+
+ if (STRPREFIX(path, SYSFS_PREFIX)) {
+ init_sysfs();
+ char *newpath;
+ if (asprintf(&newpath, "%s/%s",
+ fakesysfsdir,
+ path + strlen(SYSFS_PREFIX)) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = reallstat(newpath, sb);
+ free(newpath);
+ } else {
+ ret = reallstat(path, sb);
+ }
+ return ret;
+}
+
int mkdir(const char *path, mode_t mode)
{
int ret;
diff --git a/tests/vircgrouptest.c b/tests/vircgrouptest.c
index 4f76a06..4b8ca61 100644
--- a/tests/vircgrouptest.c
+++ b/tests/vircgrouptest.c
@@ -36,6 +36,7 @@
static int validateCgroup(virCgroupPtr cgroup,
const char *expectPath,
const char **expectMountPoint,
+ const char **expectLinkPoint,
const char **expectPlacement)
{
int i;
@@ -55,6 +56,14 @@ static int validateCgroup(virCgroupPtr cgroup,
virCgroupControllerTypeToString(i));
return -1;
}
+ if (STRNEQ_NULLABLE(expectLinkPoint[i],
+ cgroup->controllers[i].linkPoint)) {
+ fprintf(stderr, "Wrong link '%s', expected '%s' for
'%s'\n",
+ cgroup->controllers[i].linkPoint,
+ expectLinkPoint[i],
+ virCgroupControllerTypeToString(i));
+ return -1;
+ }
if (STRNEQ_NULLABLE(expectPlacement[i],
cgroup->controllers[i].placement)) {
fprintf(stderr, "Wrong placement '%s', expected '%s' for
'%s'\n",
@@ -87,6 +96,17 @@ const char *mountsFull[VIR_CGROUP_CONTROLLER_LAST] = {
[VIR_CGROUP_CONTROLLER_BLKIO] = "/not/really/sys/fs/cgroup/blkio",
};
+const char *links[VIR_CGROUP_CONTROLLER_LAST] = {
+ [VIR_CGROUP_CONTROLLER_CPU] = "/not/really/sys/fs/cgroup/cpu",
+ [VIR_CGROUP_CONTROLLER_CPUACCT] = "/not/really/sys/fs/cgroup/cpuacct",
+ [VIR_CGROUP_CONTROLLER_CPUSET] = NULL,
+ [VIR_CGROUP_CONTROLLER_MEMORY] = NULL,
+ [VIR_CGROUP_CONTROLLER_DEVICES] = NULL,
+ [VIR_CGROUP_CONTROLLER_FREEZER] = NULL,
+ [VIR_CGROUP_CONTROLLER_BLKIO] = NULL,
+};
+
+
static int testCgroupNewForSelf(const void *args ATTRIBUTE_UNUSED)
{
virCgroupPtr cgroup = NULL;
@@ -106,7 +126,7 @@ static int testCgroupNewForSelf(const void *args ATTRIBUTE_UNUSED)
goto cleanup;
}
- ret = validateCgroup(cgroup, "", mountsFull, placement);
+ ret = validateCgroup(cgroup, "", mountsFull, links, placement);
cleanup:
virCgroupFree(&cgroup);
@@ -168,14 +188,14 @@ static int testCgroupNewForDriver(const void *args
ATTRIBUTE_UNUSED)
fprintf(stderr, "Cannot create LXC cgroup: %d\n", -rv);
goto cleanup;
}
- ret = validateCgroup(cgroup, "libvirt/lxc", mountsSmall, placementSmall);
+ ret = validateCgroup(cgroup, "libvirt/lxc", mountsSmall, links,
placementSmall);
virCgroupFree(&cgroup);
if ((rv = virCgroupNewDriver("lxc", true, -1, &cgroup)) != 0) {
fprintf(stderr, "Cannot create LXC cgroup: %d\n", -rv);
goto cleanup;
}
- ret = validateCgroup(cgroup, "libvirt/lxc", mountsFull, placementFull);
+ ret = validateCgroup(cgroup, "libvirt/lxc", mountsFull, links,
placementFull);
cleanup:
virCgroupFree(&cgroup);
@@ -209,7 +229,7 @@ static int testCgroupNewForDriverDomain(const void *args
ATTRIBUTE_UNUSED)
goto cleanup;
}
- ret = validateCgroup(domaincgroup, "libvirt/lxc/wibble", mountsFull,
placement);
+ ret = validateCgroup(domaincgroup, "libvirt/lxc/wibble", mountsFull, links,
placement);
cleanup:
virCgroupFree(&drivercgroup);
@@ -272,14 +292,14 @@ static int testCgroupNewForPartition(const void *args
ATTRIBUTE_UNUSED)
fprintf(stderr, "Cannot create /virtualmachines cgroup: %d\n", -rv);
goto cleanup;
}
- ret = validateCgroup(cgroup, "/virtualmachines", mountsSmall,
placementSmall);
+ ret = validateCgroup(cgroup, "/virtualmachines", mountsSmall, links,
placementSmall);
virCgroupFree(&cgroup);
if ((rv = virCgroupNewPartition("/virtualmachines", true, -1, &cgroup))
!= 0) {
fprintf(stderr, "Cannot create /virtualmachines cgroup: %d\n", -rv);
goto cleanup;
}
- ret = validateCgroup(cgroup, "/virtualmachines", mountsFull,
placementFull);
+ ret = validateCgroup(cgroup, "/virtualmachines", mountsFull, links,
placementFull);
cleanup:
virCgroupFree(&cgroup);
@@ -324,7 +344,7 @@ static int testCgroupNewForPartitionNested(const void *args
ATTRIBUTE_UNUSED)
goto cleanup;
}
- ret = validateCgroup(cgroup, "/users/berrange", mountsFull,
placementFull);
+ ret = validateCgroup(cgroup, "/users/berrange", mountsFull, links,
placementFull);
cleanup:
virCgroupFree(&cgroup);
@@ -359,7 +379,7 @@ static int testCgroupNewForPartitionDomain(const void *args
ATTRIBUTE_UNUSED)
goto cleanup;
}
- ret = validateCgroup(domaincgroup, "/production/foo.lxc.libvirt",
mountsFull, placement);
+ ret = validateCgroup(domaincgroup, "/production/foo.lxc.libvirt",
mountsFull, links, placement);
cleanup:
virCgroupFree(&partitioncgroup);
--
1.8.1.4