cgroups: get controllers on the unified hierarchy

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
Christian Brauner 2018-01-31 16:25:11 +01:00
parent 2ddc66536b
commit d6337a5f9d
No known key found for this signature in database
GPG Key ID: 8EB056D53EECB12D
4 changed files with 327 additions and 103 deletions

View File

@ -78,7 +78,7 @@ struct hierarchy {
char *mountpoint; char *mountpoint;
char *base_cgroup; char *base_cgroup;
char *fullcgpath; char *fullcgpath;
bool is_cgroup_v2; int version;
}; };
/* /*
@ -98,14 +98,17 @@ struct cgfsng_handler_data {
char *name; /* container name */ char *name; /* container name */
/* per-container cgroup information */ /* per-container cgroup information */
struct lxc_cgroup cgroup_meta; struct lxc_cgroup cgroup_meta;
cgroup_layout_t cgroup_layout;
}; };
/* /*
* @hierarchies - a NULL-terminated array of struct hierarchy, one per * @hierarchies - a NULL-terminated array of struct hierarchy, one per
* hierarchy. No duplicates. First sufficient, writeable mounted * legacy hierarchy. No duplicates. First sufficient, writeable
* hierarchy wins * mounted hierarchy wins
*/ */
struct hierarchy **hierarchies; struct hierarchy **hierarchies;
struct hierarchy *unified;
cgroup_layout_t cgroup_layout;
/* /*
* @cgroup_use - a copy of the lxc.cgroup.use * @cgroup_use - a copy of the lxc.cgroup.use
@ -183,6 +186,7 @@ static bool string_in_list(char **list, const char *entry)
if (!list) if (!list)
return false; return false;
for (i = 0; list[i]; i++) for (i = 0; list[i]; i++)
if (strcmp(list[i], entry) == 0) if (strcmp(list[i], entry) == 0)
return true; return true;
@ -220,8 +224,6 @@ static void must_append_controller(char **klist, char **nlist, char ***clist, ch
copy = must_copy_string(entry); copy = must_copy_string(entry);
else if (string_in_list(klist, entry)) else if (string_in_list(klist, entry))
copy = must_copy_string(entry); copy = must_copy_string(entry);
else if (!strcmp(entry, "cgroup2"))
copy = must_copy_string(entry);
else else
copy = must_prefix_named(entry); copy = must_prefix_named(entry);
@ -250,10 +252,21 @@ struct hierarchy *get_hierarchy(const char *c)
if (!hierarchies) if (!hierarchies)
return NULL; return NULL;
for (i = 0; hierarchies[i]; i++) { for (i = 0; hierarchies[i]; i++) {
if (!c) {
/* This is the empty unified hierarchy. */
if (hierarchies[i]->controllers &&
!hierarchies[i]->controllers[0])
return hierarchies[i];
return NULL;
}
if (string_in_list(hierarchies[i]->controllers, c)) if (string_in_list(hierarchies[i]->controllers, c))
return hierarchies[i]; return hierarchies[i];
} }
return NULL; return NULL;
} }
@ -278,7 +291,7 @@ static void append_line(char **dest, size_t oldlen, char *new, size_t newlen)
} }
/* Slurp in a whole file */ /* Slurp in a whole file */
static char *read_file(char *fnam) static char *read_file(const char *fnam)
{ {
FILE *f; FILE *f;
char *line = NULL, *buf = NULL; char *line = NULL, *buf = NULL;
@ -713,12 +726,14 @@ static bool controller_list_is_dup(struct hierarchy **hlist, char **clist)
static bool controller_found(struct hierarchy **hlist, char *entry) static bool controller_found(struct hierarchy **hlist, char *entry)
{ {
int i; int i;
if (!hlist) if (!hlist)
return false; return false;
for (i = 0; hlist[i]; i++) for (i = 0; hlist[i]; i++)
if (string_in_list(hlist[i]->controllers, entry)) if (string_in_list(hlist[i]->controllers, entry))
return true; return true;
return false; return false;
} }
@ -757,12 +772,13 @@ static bool all_controllers_found(void)
* options. But we simply assume that the mountpoint must be * options. But we simply assume that the mountpoint must be
* /sys/fs/cgroup/controller-list * /sys/fs/cgroup/controller-list
*/ */
static char **get_controllers(char **klist, char **nlist, char *line, int type) static char **get_controllers_on_hybrid_layout(char **klist, char **nlist,
char *line, int type)
{ {
/* the fourth field is /sys/fs/cgroup/comma-delimited-controller-list */ /* the fourth field is /sys/fs/cgroup/comma-delimited-controller-list */
int i; int i;
char *dup, *p2, *tok; char *dup, *p2, *tok;
char *p = line, *saveptr = NULL; char *p = line, *saveptr = NULL, *sep = ",";
char **aret = NULL; char **aret = NULL;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
@ -778,6 +794,7 @@ static char **get_controllers(char **klist, char **nlist, char *line, int type)
CGFSNG_DEBUG("Found hierarchy not under /sys/fs/cgroup: \"%s\"\n", p); CGFSNG_DEBUG("Found hierarchy not under /sys/fs/cgroup: \"%s\"\n", p);
return NULL; return NULL;
} }
p += 15; p += 15;
p2 = strchr(p, ' '); p2 = strchr(p, ' ');
if (!p2) { if (!p2) {
@ -786,30 +803,60 @@ static char **get_controllers(char **klist, char **nlist, char *line, int type)
} }
*p2 = '\0'; *p2 = '\0';
/* cgroup v2 does not have separate mountpoints for controllers */ if (type == CGROUP_SUPER_MAGIC) {
if (type == CGROUP_V2) { /* strdup() here for v1 hierarchies. Otherwise strtok_r() will
must_append_controller(klist, nlist, &aret, "cgroup2"); * destroy mountpoints such as "/sys/fs/cgroup/cpu,cpuacct".
return aret;
}
/* strdup() here for v1 hierarchies. Otherwise strtok_r() will destroy
* mountpoints such as "/sys/fs/cgroup/cpu,cpuacct".
*/ */
dup = strdup(p); dup = strdup(p);
if (!dup) if (!dup)
return NULL; return NULL;
for (tok = strtok_r(dup, ",", &saveptr); tok; for (tok = strtok_r(dup, sep, &saveptr); tok;
tok = strtok_r(NULL, ",", &saveptr)) { tok = strtok_r(NULL, sep, &saveptr))
must_append_controller(klist, nlist, &aret, tok); must_append_controller(klist, nlist, &aret, tok);
}
free(dup); free(dup);
}
*p2 = ' ';
return aret; return aret;
} }
/* Add a controller to our list of hierarchies */ static char **cg_unified_make_empty_controller(void)
static void add_controller(char **clist, char *mountpoint, char *base_cgroup) {
int newentry;
char **aret = NULL;
newentry = append_null_to_list((void ***)&aret);
aret[newentry] = NULL;
return aret;
}
static char **cg_unified_get_controllers(const char *file)
{
char *buf, *tok;
char *saveptr = NULL, *sep = " \t\n";
char **aret = NULL;
buf = read_file(file);
if (!buf)
return NULL;
for (tok = strtok_r(buf, sep, &saveptr); tok;
tok = strtok_r(NULL, sep, &saveptr)) {
int newentry;
char *copy;
newentry = append_null_to_list((void ***)&aret);
copy = must_copy_string(tok);
aret[newentry] = copy;
}
free(buf);
return aret;
}
static struct hierarchy *add_hierarchy(char **clist, char *mountpoint,
char *base_cgroup, int type)
{ {
struct hierarchy *new; struct hierarchy *new;
int newentry; int newentry;
@ -819,26 +866,24 @@ static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
new->mountpoint = mountpoint; new->mountpoint = mountpoint;
new->base_cgroup = base_cgroup; new->base_cgroup = base_cgroup;
new->fullcgpath = NULL; new->fullcgpath = NULL;
new->version = type;
/* record if this is the cgroup v2 hierarchy */
if (clist && !strcmp(*clist, "cgroup2"))
new->is_cgroup_v2 = true;
else
new->is_cgroup_v2 = false;
newentry = append_null_to_list((void ***)&hierarchies); newentry = append_null_to_list((void ***)&hierarchies);
hierarchies[newentry] = new; hierarchies[newentry] = new;
return new;
} }
/* /*
* Get a copy of the mountpoint from @line, which is a line from * Get a copy of the mountpoint from @line, which is a line from
* /proc/self/mountinfo * /proc/self/mountinfo
*/ */
static char *get_mountpoint(char *line) static char *get_mountpoint_on_hybrid_layout(char *line)
{ {
int i; int i;
char *p = line, *sret; char *p2;
size_t len; size_t len;
char *p = line;
char *sret = NULL;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
p = strchr(p, ' '); p = strchr(p, ' ');
@ -846,7 +891,15 @@ static char *get_mountpoint(char *line)
return NULL; return NULL;
p++; p++;
} }
/* we've already stuck a \0 after the mountpoint */
if (strncmp(p, "/sys/fs/cgroup/", 15))
return NULL;
p2 = strchr(p + 15, ' ');
if (!p2)
return NULL;
*p2 = '\0';
len = strlen(p); len = strlen(p);
sret = must_alloc(len + 1); sret = must_alloc(len + 1);
memcpy(sret, p, len); memcpy(sret, p, len);
@ -897,6 +950,7 @@ static bool controller_in_clist(char *cgline, char *c)
if (strcmp(tok, c) == 0) if (strcmp(tok, c) == 0)
return true; return true;
} }
return false; return false;
} }
@ -904,24 +958,23 @@ static bool controller_in_clist(char *cgline, char *c)
* @basecginfo is a copy of /proc/$$/cgroup. Return the current * @basecginfo is a copy of /proc/$$/cgroup. Return the current
* cgroup for @controller * cgroup for @controller
*/ */
static char *get_current_cgroup(char *basecginfo, char *controller) static char *get_current_cgroup(char *basecginfo, char *controller, int type)
{ {
char *p = basecginfo; char *p = basecginfo;
bool is_cgroup_v2;
bool is_cgroup_v2_base_cgroup;
is_cgroup_v2 = !strcmp(controller, "cgroup2"); for (;;) {
while (true) { bool is_cgv2_base_cgroup = false;
is_cgroup_v2_base_cgroup = false;
/* cgroup v2 entry in "/proc/<pid>/cgroup": "0::/some/path" */ /* cgroup v2 entry in "/proc/<pid>/cgroup": "0::/some/path" */
if (is_cgroup_v2 && (*p == '0')) if ((type == CGROUP2_SUPER_MAGIC) && (*p == '0'))
is_cgroup_v2_base_cgroup = true; is_cgv2_base_cgroup = true;
p = strchr(p, ':'); p = strchr(p, ':');
if (!p) if (!p)
return NULL; return NULL;
p++; p++;
if (is_cgroup_v2_base_cgroup || controller_in_clist(p, controller)) {
if (is_cgv2_base_cgroup || (controller && controller_in_clist(p, controller))) {
p = strchr(p, ':'); p = strchr(p, ':');
if (!p) if (!p)
return NULL; return NULL;
@ -945,14 +998,16 @@ static void must_append_string(char ***list, char *entry)
(*list)[newentry] = copy; (*list)[newentry] = copy;
} }
static void get_existing_subsystems(char ***klist, char ***nlist) static int get_existing_subsystems(char ***klist, char ***nlist)
{ {
FILE *f; FILE *f;
char *line = NULL; char *line = NULL;
size_t len = 0; size_t len = 0;
if ((f = fopen("/proc/self/cgroup", "r")) == NULL) f = fopen("/proc/self/cgroup", "r");
return; if (!f)
return -1;
while (getline(&line, &len, f) != -1) { while (getline(&line, &len, f) != -1) {
char *p, *p2, *tok, *saveptr = NULL; char *p, *p2, *tok, *saveptr = NULL;
p = strchr(line, ':'); p = strchr(line, ':');
@ -987,6 +1042,7 @@ static void get_existing_subsystems(char ***klist, char ***nlist)
free(line); free(line);
fclose(f); fclose(f);
return 0;
} }
static void trim(char *s) static void trim(char *s)
@ -1054,82 +1110,125 @@ static void lxc_cgfsng_print_debuginfo(const struct cgfsng_handler_data *d)
* At startup, parse_hierarchies finds all the info we need about * At startup, parse_hierarchies finds all the info we need about
* cgroup mountpoints and current cgroups, and stores it in @d. * cgroup mountpoints and current cgroups, and stores it in @d.
*/ */
static bool parse_hierarchies(void) static bool cg_init_hybrid(void)
{ {
int ret;
char *basecginfo;
bool will_escape;
FILE *f; FILE *f;
char * line = NULL, *basecginfo;
char **klist = NULL, **nlist = NULL;
size_t len = 0; size_t len = 0;
char *line = NULL;
char **klist = NULL, **nlist = NULL;
/* /*
* Root spawned containers escape the current cgroup, so use init's * Root spawned containers escape the current cgroup, so use init's
* cgroups as our base in that case. * cgroups as our base in that case.
*/ */
if (geteuid()) will_escape = (geteuid() == 0);
basecginfo = read_file("/proc/self/cgroup"); if (will_escape)
else
basecginfo = read_file("/proc/1/cgroup"); basecginfo = read_file("/proc/1/cgroup");
else
basecginfo = read_file("/proc/self/cgroup");
if (!basecginfo) if (!basecginfo)
return false; return false;
if ((f = fopen("/proc/self/mountinfo", "r")) == NULL) { ret = get_existing_subsystems(&klist, &nlist);
if (ret < 0) {
CGFSNG_DEBUG("Failed to retrieve available cgroup v1 controllers\n");
free(basecginfo);
return false;
}
f = fopen("/proc/self/mountinfo", "r");
if (!f) {
CGFSNG_DEBUG("Failed to open \"/proc/self/mountinfo\"\n"); CGFSNG_DEBUG("Failed to open \"/proc/self/mountinfo\"\n");
return false; return false;
} }
get_existing_subsystems(&klist, &nlist);
if (lxc_cgfsng_debug) if (lxc_cgfsng_debug)
lxc_cgfsng_print_basecg_debuginfo(basecginfo, klist, nlist); lxc_cgfsng_print_basecg_debuginfo(basecginfo, klist, nlist);
/* we support simple cgroup mounts and lxcfs mounts */
while (getline(&line, &len, f) != -1) { while (getline(&line, &len, f) != -1) {
char **controller_list = NULL;
char *mountpoint, *base_cgroup;
bool writeable;
int type; int type;
bool writeable;
struct hierarchy *new;
char *mountpoint = NULL, *base_cgroup = NULL;
char **controller_list = NULL;
type = get_cgroup_version(line); type = get_cgroup_version(line);
if (type < 0) if (type == 0)
continue; continue;
controller_list = get_controllers(klist, nlist, line, type); if (type == CGROUP2_SUPER_MAGIC && unified)
if (!controller_list)
continue; continue;
if (controller_list_is_dup(hierarchies, controller_list)) { if (cgroup_layout == CGROUP_LAYOUT_UNKNOWN) {
free(controller_list); if (type == CGROUP2_SUPER_MAGIC)
continue; cgroup_layout = CGROUP_LAYOUT_UNIFIED;
else if (type == CGROUP_SUPER_MAGIC)
cgroup_layout = CGROUP_LAYOUT_LEGACY;
} else if (cgroup_layout == CGROUP_LAYOUT_UNIFIED) {
if (type == CGROUP_SUPER_MAGIC)
cgroup_layout = CGROUP_LAYOUT_HYBRID;
} else if (cgroup_layout == CGROUP_LAYOUT_LEGACY) {
if (type == CGROUP2_SUPER_MAGIC)
cgroup_layout = CGROUP_LAYOUT_HYBRID;
} }
mountpoint = get_mountpoint(line); controller_list = get_controllers_on_hybrid_layout(klist, nlist, line, type);
if (!controller_list && type == CGROUP_SUPER_MAGIC)
continue;
if (type == CGROUP_SUPER_MAGIC)
if (controller_list_is_dup(hierarchies, controller_list))
goto next;
mountpoint = get_mountpoint_on_hybrid_layout(line);
if (!mountpoint) { if (!mountpoint) {
CGFSNG_DEBUG("Failed parsing mountpoint from \"%s\"\n", line); CGFSNG_DEBUG("Failed parsing mountpoint from \"%s\"\n", line);
free_string_list(controller_list); goto next;
continue;
} }
base_cgroup = get_current_cgroup(basecginfo, controller_list[0]); if (type == CGROUP_SUPER_MAGIC)
base_cgroup = get_current_cgroup(basecginfo, controller_list[0], CGROUP_SUPER_MAGIC);
else
base_cgroup = get_current_cgroup(basecginfo, NULL, CGROUP2_SUPER_MAGIC);
if (!base_cgroup) { if (!base_cgroup) {
CGFSNG_DEBUG("Failed to find current cgroup for controller \"%s\"\n", controller_list[0]); CGFSNG_DEBUG("Failed to find current cgroup\n");
free_string_list(controller_list); goto next;
free(mountpoint);
continue;
} }
trim(base_cgroup); trim(base_cgroup);
prune_init_scope(base_cgroup); prune_init_scope(base_cgroup);
if (type == CGROUP_V2) if (type == CGROUP2_SUPER_MAGIC)
writeable = test_writeable_v2(mountpoint, base_cgroup); writeable = test_writeable_v2(mountpoint, base_cgroup);
else else
writeable = test_writeable_v1(mountpoint, base_cgroup); writeable = test_writeable_v1(mountpoint, base_cgroup);
if (!writeable) { if (!writeable)
goto next;
if (type == CGROUP2_SUPER_MAGIC) {
char *cgv2_ctrl_path;
cgv2_ctrl_path = must_make_path(mountpoint, base_cgroup,
"cgroup.controllers",
NULL);
controller_list = cg_unified_get_controllers(cgv2_ctrl_path);
free(cgv2_ctrl_path);
if (!controller_list)
controller_list = cg_unified_make_empty_controller();
}
new = add_hierarchy(controller_list, mountpoint, base_cgroup, type);
if (type == CGROUP2_SUPER_MAGIC && !unified)
unified = new;
continue;
next:
free_string_list(controller_list); free_string_list(controller_list);
free(mountpoint); free(mountpoint);
free(base_cgroup); free(base_cgroup);
continue;
}
add_controller(controller_list, mountpoint, base_cgroup);
} }
free_string_list(klist); free_string_list(klist);
@ -1154,9 +1253,106 @@ static bool parse_hierarchies(void)
return true; return true;
} }
static bool collect_hierarchy_info(void) static int cg_is_pure_unified(void) {
int ret;
struct statfs fs;
ret = statfs("/sys/fs/cgroup", &fs);
if (ret < 0)
return -ENOMEDIUM;
if (is_fs_type(&fs, CGROUP2_SUPER_MAGIC))
return CGROUP2_SUPER_MAGIC;
return 0;
}
/* Get current cgroup from /proc/self/cgroup for the cgroupfs v2 hierarchy. */
static char *cg_get_current_cgroup_unified(void)
{ {
char *basecginfo;
char *base_cgroup;
bool will_escape;
char *copy = NULL;
will_escape = (geteuid() == 0);
if (will_escape)
basecginfo = read_file("/proc/1/cgroup");
else
basecginfo = read_file("/proc/self/cgroup");
if (!basecginfo)
return NULL;
base_cgroup = strstr(basecginfo, "0::/");
if (!base_cgroup)
goto cleanup_on_err;
base_cgroup = base_cgroup + 3;
copy = copy_to_eol(base_cgroup);
if (!copy)
goto cleanup_on_err;
cleanup_on_err:
free(basecginfo);
if (copy)
trim(copy);
return copy;
}
static int cg_init_unified(void)
{
int ret;
char *mountpoint, *subtree_path;
char **delegatable;
char *base_cgroup = NULL;
ret = cg_is_pure_unified();
if (ret == -ENOMEDIUM)
return -ENOMEDIUM;
if (ret != CGROUP2_SUPER_MAGIC)
return 0;
base_cgroup = cg_get_current_cgroup_unified();
if (!base_cgroup)
return -EINVAL;
prune_init_scope(base_cgroup);
/* We assume that we have already been given controllers to delegate
* further down the hierarchy. If not it is up to the user to delegate
* them to us.
*/
mountpoint = must_copy_string("/sys/fs/cgroup");
subtree_path = must_make_path(mountpoint, base_cgroup,
"cgroup.subtree_control", NULL);
delegatable = cg_unified_get_controllers(subtree_path);
free(subtree_path);
if (!delegatable)
delegatable = cg_unified_make_empty_controller();
if (!delegatable[0])
CGFSNG_DEBUG("No controllers are enabled for delegation\n");
/* TODO: If the user requested specific controllers via lxc.cgroup.use
* we should verify here. The reason I'm not doing it right is that I'm
* not convinced that lxc.cgroup.use will be the future since it is a
* global property. I much rather have an option that lets you request
* controllers per container.
*/
add_hierarchy(delegatable, mountpoint, base_cgroup, CGROUP2_SUPER_MAGIC);
unified = hierarchies[0];
cgroup_layout = CGROUP_LAYOUT_UNIFIED;
return CGROUP2_SUPER_MAGIC;
}
static bool cg_init(void)
{
int ret;
const char *tmp; const char *tmp;
errno = 0; errno = 0;
tmp = lxc_global_config_value("lxc.cgroup.use"); tmp = lxc_global_config_value("lxc.cgroup.use");
if (!cgroup_use && errno != 0) { /* lxc.cgroup.use can be NULL */ if (!cgroup_use && errno != 0) { /* lxc.cgroup.use can be NULL */
@ -1165,7 +1361,14 @@ static bool collect_hierarchy_info(void)
} }
cgroup_use = must_copy_string(tmp); cgroup_use = must_copy_string(tmp);
return parse_hierarchies(); ret = cg_init_unified();
if (ret < 0)
return false;
if (ret == CGROUP2_SUPER_MAGIC)
return true;
return cg_init_hybrid();
} }
static void *cgfsng_init(struct lxc_handler *handler) static void *cgfsng_init(struct lxc_handler *handler)
@ -1196,6 +1399,16 @@ static void *cgfsng_init(struct lxc_handler *handler)
} }
d->cgroup_pattern = must_copy_string(cgroup_pattern); d->cgroup_pattern = must_copy_string(cgroup_pattern);
d->cgroup_layout = cgroup_layout;
if (d->cgroup_layout == CGROUP_LAYOUT_LEGACY)
TRACE("Running with legacy cgroup layout");
else if (d->cgroup_layout == CGROUP_LAYOUT_HYBRID)
TRACE("Running with hybrid cgroup layout");
else if (d->cgroup_layout == CGROUP_LAYOUT_UNIFIED)
TRACE("Running with unified cgroup layout");
else
WARN("Running with unknown cgroup layout");
if (lxc_cgfsng_debug) if (lxc_cgfsng_debug)
lxc_cgfsng_print_debuginfo(d); lxc_cgfsng_print_debuginfo(d);
@ -1343,7 +1556,7 @@ struct cgroup_ops *cgfsng_ops_init(void)
if (getenv("LXC_DEBUG_CGFSNG")) if (getenv("LXC_DEBUG_CGFSNG"))
lxc_cgfsng_debug = true; lxc_cgfsng_debug = true;
if (!collect_hierarchy_info()) if (!cg_init())
return NULL; return NULL;
return &cgfsng_ops; return &cgfsng_ops;
@ -1529,7 +1742,7 @@ static int chown_cgroup_wrapper(void *data)
WARN("Error chmoding %s: %s", path, strerror(errno)); WARN("Error chmoding %s: %s", path, strerror(errno));
free(fullpath); free(fullpath);
if (!hierarchies[i]->is_cgroup_v2) if (hierarchies[i]->version != CGROUP2_SUPER_MAGIC)
continue; continue;
fullpath = must_make_path(path, "cgroup.subtree_control", NULL); fullpath = must_make_path(path, "cgroup.subtree_control", NULL);
@ -1679,7 +1892,7 @@ static int mount_cgroup_cgns_supported(int type, struct hierarchy *h, const char
if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO) if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO)
flags |= MS_RDONLY; flags |= MS_RDONLY;
if (!h->is_cgroup_v2) { if (h->version != CGROUP2_SUPER_MAGIC) {
controllers = lxc_string_join(",", (const char **)h->controllers, false); controllers = lxc_string_join(",", (const char **)h->controllers, false);
if (!controllers) if (!controllers)
return -ENOMEM; return -ENOMEM;
@ -1902,25 +2115,33 @@ static bool cgfsng_get_hierarchies(int n, char ***out)
#define THAWED "THAWED" #define THAWED "THAWED"
#define THAWED_LEN (strlen(THAWED)) #define THAWED_LEN (strlen(THAWED))
/* TODO: If the unified cgroup hierarchy grows a freezer controller this needs
* to be adapted.
*/
static bool cgfsng_unfreeze(void *hdata) static bool cgfsng_unfreeze(void *hdata)
{ {
int ret;
char *fullpath; char *fullpath;
struct hierarchy *h = get_hierarchy("freezer"); struct hierarchy *h;
h = get_hierarchy("freezer");
if (!h) if (!h)
return false; return false;
fullpath = must_make_path(h->fullcgpath, "freezer.state", NULL); fullpath = must_make_path(h->fullcgpath, "freezer.state", NULL);
if (lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false) != 0) { ret = lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false);
free(fullpath); free(fullpath);
if (ret < 0)
return false; return false;
}
free(fullpath);
return true; return true;
} }
static const char *cgfsng_get_cgroup(void *hdata, const char *subsystem) static const char *cgfsng_get_cgroup(void *hdata, const char *subsystem)
{ {
struct hierarchy *h = get_hierarchy(subsystem); struct hierarchy *h;
h = get_hierarchy(subsystem);
if (!h) if (!h)
return NULL; return NULL;

View File

@ -32,6 +32,13 @@ struct lxc_handler;
struct lxc_conf; struct lxc_conf;
struct lxc_list; struct lxc_list;
typedef enum {
CGROUP_LAYOUT_UNKNOWN = -1,
CGROUP_LAYOUT_LEGACY = 0,
CGROUP_LAYOUT_HYBRID = 1,
CGROUP_LAYOUT_UNIFIED = 2,
} cgroup_layout_t;
typedef enum { typedef enum {
CGFS, CGFS,
CGMANAGER, CGMANAGER,

View File

@ -35,12 +35,12 @@
int get_cgroup_version(char *line) int get_cgroup_version(char *line)
{ {
if (is_cgroupfs_v1(line)) if (is_cgroupfs_v1(line))
return CGROUP_V1; return CGROUP_SUPER_MAGIC;
if (is_cgroupfs_v2(line)) if (is_cgroupfs_v2(line))
return CGROUP_V2; return CGROUP2_SUPER_MAGIC;
return -1; return 0;
} }
bool is_cgroupfs_v1(char *line) bool is_cgroupfs_v1(char *line)

View File

@ -28,10 +28,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#define CGROUP_V1 0
#define CGROUP_V2 1
#define LXCFS_CGROUP 2
/* Retrieve the cgroup version of a given entry from /proc/<pid>/mountinfo. */ /* Retrieve the cgroup version of a given entry from /proc/<pid>/mountinfo. */
extern int get_cgroup_version(char *line); extern int get_cgroup_version(char *line);