[lxc-devel] [lxc/master] cgroups: handle hybrid cgroup layouts

brauner on Github lxc-bot at linuxcontainers.org
Wed Jul 26 13:48:03 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 430 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170726/ae6f83d3/attachment.bin>
-------------- next part --------------
From 04ad7ffe2a42fb2fa2e78e694990b385fd2dd5e0 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 26 Jul 2017 14:57:35 +0200
Subject: [PATCH 1/2] utils: move helpers from cgfsng.c to utils.{c,h}

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/cgroups/cgfsng.c | 53 ------------------------------------------------
 src/lxc/utils.c          | 47 ++++++++++++++++++++++++++++++++++++++++++
 src/lxc/utils.h          | 11 ++++++++++
 3 files changed, 58 insertions(+), 53 deletions(-)

diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c
index 1192d575f..b3f4ca742 100644
--- a/src/lxc/cgroups/cgfsng.c
+++ b/src/lxc/cgroups/cgfsng.c
@@ -118,36 +118,12 @@ static void free_string_list(char **clist)
 	}
 }
 
-/* Re-alllocate a pointer, do not fail */
-static void *must_realloc(void *orig, size_t sz)
-{
-	void *ret;
-
-	do {
-		ret = realloc(orig, sz);
-	} while (!ret);
-	return ret;
-}
-
 /* Allocate a pointer, do not fail */
 static void *must_alloc(size_t sz)
 {
 	return must_realloc(NULL, sz);
 }
 
-/* return copy of string @entry;  do not fail. */
-static char *must_copy_string(const char *entry)
-{
-	char *ret;
-
-	if (!entry)
-		return NULL;
-	do {
-		ret = strdup(entry);
-	} while (!ret);
-	return ret;
-}
-
 /*
  * This is a special case - return a copy of @entry
  * prepending 'name='.  I.e. turn systemd into name=systemd.
@@ -259,8 +235,6 @@ struct hierarchy *get_hierarchy(const char *c)
 	return NULL;
 }
 
-static char *must_make_path(const char *first, ...) __attribute__((sentinel));
-
 #define BATCH_SIZE 50
 static void batch_realloc(char **mem, size_t oldlen, size_t newlen)
 {
@@ -1187,33 +1161,6 @@ static void *cgfsng_init(const char *name)
 	return NULL;
 }
 
-/*
- * Concatenate all passed-in strings into one path.  Do not fail.  If any piece is
- * not prefixed with '/', add a '/'.
- */
-static char *must_make_path(const char *first, ...)
-{
-	va_list args;
-	char *cur, *dest;
-	size_t full_len = strlen(first);
-
-	dest = must_copy_string(first);
-
-	va_start(args, first);
-	while ((cur = va_arg(args, char *)) != NULL) {
-		full_len += strlen(cur);
-		if (cur[0] != '/')
-			full_len++;
-		dest = must_realloc(dest, full_len + 1);
-		if (cur[0] != '/')
-			strcat(dest, "/");
-		strcat(dest, cur);
-	}
-	va_end(args);
-
-	return dest;
-}
-
 static int cgroup_rmdir(char *dirname)
 {
 	struct dirent *direntp;
diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index 5b61cba00..f89c837d5 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -2337,3 +2337,50 @@ int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args)
 
 	return fret;
 }
+
+char *must_make_path(const char *first, ...)
+{
+	va_list args;
+	char *cur, *dest;
+	size_t full_len = strlen(first);
+
+	dest = must_copy_string(first);
+
+	va_start(args, first);
+	while ((cur = va_arg(args, char *)) != NULL) {
+		full_len += strlen(cur);
+		if (cur[0] != '/')
+			full_len++;
+		dest = must_realloc(dest, full_len + 1);
+		if (cur[0] != '/')
+			strcat(dest, "/");
+		strcat(dest, cur);
+	}
+	va_end(args);
+
+	return dest;
+}
+
+char *must_copy_string(const char *entry)
+{
+	char *ret;
+
+	if (!entry)
+		return NULL;
+	do {
+		ret = strdup(entry);
+	} while (!ret);
+
+	return ret;
+}
+
+void *must_realloc(void *orig, size_t sz)
+{
+	void *ret;
+
+	do {
+		ret = realloc(orig, sz);
+	} while (!ret);
+
+	return ret;
+}
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 916ee56a6..3465e6a6f 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -375,4 +375,15 @@ int lxc_unstack_mountpoint(const char *path, bool lazy);
  */
 int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args);
 
+/* Concatenate all passed-in strings into one path. Do not fail. If any piece
+ * is not prefixed with '/', add a '/'.
+ */
+char *must_make_path(const char *first, ...) __attribute__((sentinel));
+
+/* return copy of string @entry;  do not fail. */
+char *must_copy_string(const char *entry);
+
+/* Re-alllocate a pointer, do not fail */
+void *must_realloc(void *orig, size_t sz);
+
 #endif /* __LXC_UTILS_H */

From 6328fd9c05d2730182eb738ffd320de716f062bb Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 26 Jul 2017 15:15:27 +0200
Subject: [PATCH 2/2] cgroups: handle hybrid cgroup layouts

Closes #1669.
Closes #1678.
Relates to https://github.com/systemd/systemd/issues/6408.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/Makefile.am            |  2 +
 src/lxc/cgroups/cgfsng.c       | 81 ++++++++++++++++++++++-----------------
 src/lxc/cgroups/cgroup_utils.c | 86 ++++++++++++++++++++++++++++++++++++++++++
 src/lxc/cgroups/cgroup_utils.h | 48 +++++++++++++++++++++++
 4 files changed, 183 insertions(+), 34 deletions(-)
 create mode 100644 src/lxc/cgroups/cgroup_utils.c
 create mode 100644 src/lxc/cgroups/cgroup_utils.h

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index f659f3736..a55103ec5 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -19,6 +19,7 @@ noinst_HEADERS = \
 	bdev/lxczfs.h \
 	bdev/storage_utils.h \
 	cgroups/cgroup.h \
+	cgroups/cgroup_utils.h \
 	caps.h \
 	conf.h \
 	confile.h \
@@ -90,6 +91,7 @@ liblxc_la_SOURCES = \
 	bdev/storage_utils.c bdev/storage_utils.h \
 	cgroups/cgfs.c \
 	cgroups/cgfsng.c \
+	cgroups/cgroup_utils.c cgroups/cgroup_utils.h \
 	cgroups/cgroup.c cgroups/cgroup.h \
 	commands.c commands.h \
 	commands_utils.c commands_utils.h \
diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c
index b3f4ca742..5ad24d1e5 100644
--- a/src/lxc/cgroups/cgfsng.c
+++ b/src/lxc/cgroups/cgfsng.c
@@ -49,6 +49,7 @@
 
 #include "bdev.h"
 #include "cgroup.h"
+#include "cgroup_utils.h"
 #include "commands.h"
 #include "log.h"
 #include "utils.h"
@@ -72,6 +73,7 @@ struct hierarchy {
 	char *mountpoint;
 	char *base_cgroup;
 	char *fullcgpath;
+	bool is_cgroup_v2;
 };
 
 /*
@@ -600,7 +602,8 @@ static bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname)
 	}
 
 	clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL);
-	if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */
+	/* unified hierarchy doesn't have clone_children */
+	if (!file_exists(clonechildrenpath)) {
 		free(clonechildrenpath);
 		free(cgpath);
 		return true;
@@ -742,10 +745,14 @@ static bool is_lxcfs(const char *line)
  */
 static char **get_controllers(char **klist, char **nlist, char *line)
 {
-	// the fourth field is /sys/fs/cgroup/comma-delimited-controller-list
+	/* the fourth field is /sys/fs/cgroup/comma-delimited-controller-list */
 	int i;
 	char *p = line, *p2, *tok, *saveptr = NULL;
 	char **aret = NULL;
+	bool is_cgroup_v2;
+
+	/* handle cgroup v2 */
+	is_cgroup_v2 = is_cgroupfs_v2(line);
 
 	for (i = 0; i < 4; i++) {
 		p = strchr(p, ' ');
@@ -768,6 +775,13 @@ static char **get_controllers(char **klist, char **nlist, char *line)
 		return NULL;
 	}
 	*p2 = '\0';
+
+	/* cgroup v2 does not have separate mountpoints for controllers */
+	if (is_cgroup_v2) {
+		must_append_controller(klist, nlist, &aret, "cgroup2");
+		return aret;
+	}
+
 	for (tok = strtok_r(p, ",", &saveptr); tok;
 			tok = strtok_r(NULL, ",", &saveptr)) {
 		must_append_controller(klist, nlist, &aret, tok);
@@ -776,15 +790,6 @@ static char **get_controllers(char **klist, char **nlist, char *line)
 	return aret;
 }
 
-/* return true if the fstype is cgroup */
-static bool is_cgroupfs(char *line)
-{
-	char *p = strstr(line, " - ");
-	if (!p)
-		return false;
-	return strncmp(p, " - cgroup ", 10) == 0;
-}
-
 /* Add a controller to our list of hierarchies */
 static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
 {
@@ -797,6 +802,12 @@ static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
 	new->base_cgroup = base_cgroup;
 	new->fullcgpath = NULL;
 
+	/* record if this is the cgroup v2 hierarchy */
+	if (!strcmp(base_cgroup, "cgroup2"))
+		new->is_cgroup_v2 = true;
+	else
+		new->is_cgroup_v2 = false;
+
 	newentry = append_null_to_list((void ***)&hierarchies);
 	hierarchies[newentry] = new;
 }
@@ -878,13 +889,21 @@ static bool controller_in_clist(char *cgline, char *c)
 static char *get_current_cgroup(char *basecginfo, char *controller)
 {
 	char *p = basecginfo;
+	bool is_cgroup_v2;
+	bool is_cgroup_v2_base_cgroup;
+
+	is_cgroup_v2 = !strcmp(controller, "cgroup2");
+	while (true) {
+		is_cgroup_v2_base_cgroup = false;
+		/* cgroup v2 entry in "/proc/<pid>/cgroup": "0::/some/path" */
+		if (is_cgroup_v2 && (*p == '0'))
+			is_cgroup_v2_base_cgroup = true;
 
-	while (1) {
 		p = strchr(p, ':');
 		if (!p)
 			return NULL;
 		p++;
-		if (controller_in_clist(p, controller)) {
+		if (is_cgroup_v2_base_cgroup || controller_in_clist(p, controller)) {
 			p = strchr(p, ':');
 			if (!p)
 				return NULL;
@@ -899,20 +918,6 @@ static char *get_current_cgroup(char *basecginfo, char *controller)
 	}
 }
 
-/*
- * Given a hierarchy @mountpoint and base @path, verify that we can create
- * directories underneath it.
- */
-static bool test_writeable(char *mountpoint, char *path)
-{
-	char *fullpath = must_make_path(mountpoint, path, NULL);
-	int ret;
-
-	ret = access(fullpath, W_OK);
-	free(fullpath);
-	return ret == 0;
-}
-
 static void must_append_string(char ***list, char *entry)
 {
 	int newentry = append_null_to_list((void ***)list);
@@ -941,16 +946,17 @@ static void get_existing_subsystems(char ***klist, char ***nlist)
 			continue;
 		*p2 = '\0';
 
-		/* If we have a mixture between cgroup v1 and cgroup v2
-		 * hierarchies, then /proc/self/cgroup contains entries of the
-		 * form:
+		/* If the kernel has cgroup v2 support, then /proc/self/cgroup
+		 * contains an entry of the form:
 		 *
 		 *	0::/some/path
 		 *
-		 * We need to skip those.
+		 * In this case we use "cgroup2" as controller name.
 		 */
-		if ((p2 - p) == 0)
+		if ((p2 - p) == 0) {
+			must_append_string(klist, "cgroup2");
 			continue;
+		}
 
 		for (tok = strtok_r(p, ",", &saveptr); tok;
 				tok = strtok_r(NULL, ",", &saveptr)) {
@@ -1058,8 +1064,10 @@ static bool parse_hierarchies(void)
 	while (getline(&line, &len, f) != -1) {
 		char **controller_list = NULL;
 		char *mountpoint, *base_cgroup;
+		bool is_cgroup_v2, writeable;
 
-		if (!is_lxcfs(line) && !is_cgroupfs(line))
+		is_cgroup_v2 = is_cgroupfs_v2(line);
+		if (!is_lxcfs(line) && !is_cgroupfs_v1(line) && !is_cgroup_v2)
 			continue;
 
 		controller_list = get_controllers(klist, nlist, line);
@@ -1085,9 +1093,14 @@ static bool parse_hierarchies(void)
 			free(mountpoint);
 			continue;
 		}
+
 		trim(base_cgroup);
 		prune_init_scope(base_cgroup);
-		if (!test_writeable(mountpoint, base_cgroup)) {
+		if (is_cgroup_v2)
+			writeable = test_writeable_v2(mountpoint, base_cgroup);
+		else
+			writeable = test_writeable_v1(mountpoint, base_cgroup);
+		if (!writeable) {
 			free_string_list(controller_list);
 			free(mountpoint);
 			free(base_cgroup);
diff --git a/src/lxc/cgroups/cgroup_utils.c b/src/lxc/cgroups/cgroup_utils.c
new file mode 100644
index 000000000..c09ba1688
--- /dev/null
+++ b/src/lxc/cgroups/cgroup_utils.c
@@ -0,0 +1,86 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2017 Canonical Ltd.
+ *
+ * Authors:
+ * Serge Hallyn <serge.hallyn at ubuntu.com>
+ * Christian Brauner <christian.brauner at ubuntu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cgroup_utils.h"
+#include "utils.h"
+
+bool is_cgroupfs_v1(char *line)
+{
+	char *p = strstr(line, " - ");
+	if (!p)
+		return false;
+	return strncmp(p, " - cgroup ", 10) == 0;
+}
+
+bool is_cgroupfs_v2(char *line)
+{
+	char *p = strstr(line, " - ");
+	if (!p)
+		return false;
+
+	return strncmp(p, " - cgroup2 ", 11) == 0;
+}
+
+bool test_writeable_v1(char *mountpoint, char *path)
+{
+	char *fullpath = must_make_path(mountpoint, path, NULL);
+	int ret;
+
+	ret = access(fullpath, W_OK);
+	free(fullpath);
+	return ret == 0;
+}
+
+bool test_writeable_v2(char *mountpoint, char *path)
+{
+	/* In order to move ourselves into an appropriate sub-cgroup we need to
+	 * have write access to the parent cgroup's "cgroup.procs" file, i.e. we
+	 * need to have write access to the our current cgroups's "cgroup.procs"
+	 * file.
+	 */
+	int ret;
+	char *cgroup_path, *cgroup_procs_file;
+
+	cgroup_path = must_make_path(mountpoint, path, NULL);
+	cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL);
+
+	ret = access(cgroup_path, W_OK);
+	free(cgroup_path);
+	if (ret < 0) {
+		free(cgroup_procs_file);
+		return false;
+	}
+
+	ret = access(cgroup_procs_file, W_OK);
+	free(cgroup_procs_file);
+
+	return ret == 0;
+}
diff --git a/src/lxc/cgroups/cgroup_utils.h b/src/lxc/cgroups/cgroup_utils.h
new file mode 100644
index 000000000..49aae8567
--- /dev/null
+++ b/src/lxc/cgroups/cgroup_utils.h
@@ -0,0 +1,48 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2017 Canonical Ltd.
+ *
+ * Authors:
+ * Serge Hallyn <serge.hallyn at ubuntu.com>
+ * Christian Brauner <christian.brauner at ubuntu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __LXC_CGROUP_UTILS_H
+#define __LXC_CGROUP_UTILS_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+/* Check if given entry from /proc/<pid>/mountinfo is a cgroupfs v1 mount. */
+extern bool is_cgroupfs_v1(char *line);
+
+/* Check if given entry from /proc/<pid>/mountinfo is a cgroupfs v2 mount. */
+extern bool is_cgroupfs_v2(char *line);
+
+/* Given a v1 hierarchy @mountpoint and base @path, verify that we can create
+ * directories underneath it.
+ */
+extern bool test_writeable_v1(char *mountpoint, char *path);
+
+/* Given a v2 hierarchy @mountpoint and base @path, verify that we can create
+ * directories underneath it and that we have write access to the cgroup's
+ * "cgroup.procs" file.
+ */
+extern bool test_writeable_v2(char *mountpoint, char *path);
+
+#endif /* __LXC_CGROUP_UTILS_H */


More information about the lxc-devel mailing list