mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-09 12:43:43 +00:00
cgroups: get controllers on the unified hierarchy
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
parent
2ddc66536b
commit
d6337a5f9d
@ -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;
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user