From 6328fd9c05d2730182eb738ffd320de716f062bb Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 26 Jul 2017 15:15:27 +0200 Subject: [PATCH] cgroups: handle hybrid cgroup layouts Closes #1669. Closes #1678. Relates to https://github.com/systemd/systemd/issues/6408. Signed-off-by: Christian Brauner --- 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//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 + * Christian Brauner + * + * 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 +#include +#include +#include + +#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 + * Christian Brauner + * + * 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 +#include + +/* Check if given entry from /proc//mountinfo is a cgroupfs v1 mount. */ +extern bool is_cgroupfs_v1(char *line); + +/* Check if given entry from /proc//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 */ -- 2.47.2