Merge pull request #2117 from brauner/2018-01-26/cgroup_v2_support

cgroups: add unified hierarchy support
This commit is contained in:
Serge Hallyn 2018-02-06 13:19:10 -06:00 committed by GitHub
commit 3fb8253d91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 805 additions and 218 deletions

View File

@ -1330,17 +1330,31 @@ dev/null proc/kcore none bind,relative 0 0
<variablelist>
<varlistentry>
<term>
<option>lxc.cgroup.[subsystem name]</option>
<option>lxc.cgroup.[controller name]</option>
</term>
<listitem>
<para>
specify the control group value to be set. The
subsystem name is the literal name of the control group
subsystem. The permitted names and the syntax of their
values is not dictated by LXC, instead it depends on the
features of the Linux kernel running at the time the
container is started,
eg. <option>lxc.cgroup.cpuset.cpus</option>
Specify the control group value to be set on a legacy cgroup
hierarchy. The controller name is the literal name of the control
group. The permitted names and the syntax of their values is not
dictated by LXC, instead it depends on the features of the Linux
kernel running at the time the container is started, eg.
<option>lxc.cgroup.cpuset.cpus</option>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>lxc.cgroup2.[controller name]</option>
</term>
<listitem>
<para>
Specify the control group value to be set on the unified cgroup
shierarchy. The controller name is the literal name of the control
group. The permitted names and the syntax of their values is not
dictated by LXC, instead it depends on the features of the Linux
kernel running at the time the container is started, eg.
<option>lxc.cgroup2.memory.high</option>
</para>
</listitem>
</varlistentry>

View File

@ -2525,14 +2525,14 @@ static bool cgfs_unfreeze(void *hdata)
return ret == 0;
}
static bool cgroupfs_setup_limits(void *hdata, struct lxc_list *cgroup_conf,
static bool cgroupfs_setup_limits(void *hdata, struct lxc_conf *conf,
bool with_devices)
{
struct cgfs_data *d = hdata;
if (!d)
return false;
return do_setup_cgroup_limits(d, cgroup_conf, with_devices) == 0;
return do_setup_cgroup_limits(d, &conf->cgroup, with_devices) == 0;
}
static bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid)

File diff suppressed because it is too large Load Diff

View File

@ -1479,11 +1479,12 @@ static bool cgm_unfreeze(void *hdata)
return ret;
}
static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
static bool cgm_setup_limits(void *hdata, struct lxc_conf *conf, bool do_devices)
{
struct cgm_data *d = hdata;
struct lxc_list *iterator, *sorted_cgroup_settings, *next;
struct lxc_cgroup *cg;
struct lxc_list *cgroup_settings = &conf->cgroup;
bool ret = false;
if (lxc_list_empty(cgroup_settings))

View File

@ -150,7 +150,7 @@ bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
{
if (ops)
return ops->setup_limits(handler->cgroup_data,
&handler->conf->cgroup, with_devices);
handler->conf, with_devices);
return false;
}

View File

@ -32,6 +32,13 @@ struct lxc_handler;
struct lxc_conf;
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 {
CGFS,
CGMANAGER,
@ -53,7 +60,7 @@ struct cgroup_ops {
int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath);
int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
bool (*unfreeze)(void *hdata);
bool (*setup_limits)(void *hdata, struct lxc_list *cgroup_conf, bool with_devices);
bool (*setup_limits)(void *hdata, struct lxc_conf *conf, bool with_devices);
bool (*chown)(void *hdata, struct lxc_conf *conf);
bool (*attach)(const char *name, const char *lxcpath, pid_t pid);
bool (*mount_cgroup)(void *hdata, const char *root, int type);
@ -73,7 +80,8 @@ extern bool cgroup_enter(struct lxc_handler *handler);
extern void cgroup_cleanup(struct lxc_handler *handler);
extern bool cgroup_create_legacy(struct lxc_handler *handler);
extern int cgroup_nrtasks(struct lxc_handler *handler);
extern const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem);
extern const char *cgroup_get_cgroup(struct lxc_handler *handler,
const char *subsystem);
extern bool cgroup_escape();
extern int cgroup_num_hierarchies();
extern bool cgroup_get_hierarchies(int i, char ***out);

View File

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

View File

@ -28,10 +28,6 @@
#include <stdbool.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. */
extern int get_cgroup_version(char *line);

View File

@ -445,11 +445,16 @@ char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath,
struct lxc_cmd_rr cmd = {
.req = {
.cmd = LXC_CMD_GET_CGROUP,
.datalen = strlen(subsystem) + 1,
.data = subsystem,
.datalen = 0,
},
};
cmd.req.data = subsystem;
cmd.req.datalen = 0;
if (subsystem)
cmd.req.datalen = strlen(subsystem) + 1;
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
if (ret < 0)
return NULL;
@ -469,10 +474,10 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
const char *path;
struct lxc_cmd_rsp rsp;
if (req->datalen < 1)
return -1;
path = cgroup_get_cgroup(handler, req->data);
if (req->datalen > 0)
path = cgroup_get_cgroup(handler, req->data);
else
path = cgroup_get_cgroup(handler, NULL);
if (!path)
return -1;

View File

@ -2555,6 +2555,7 @@ struct lxc_conf *lxc_conf_init(void)
}
new->logfd = -1;
lxc_list_init(&new->cgroup);
lxc_list_init(&new->cgroup2);
lxc_list_init(&new->network);
lxc_list_init(&new->mount_list);
lxc_list_init(&new->caps);
@ -3446,23 +3447,38 @@ int lxc_clear_config_keepcaps(struct lxc_conf *c)
return 0;
}
int lxc_clear_cgroups(struct lxc_conf *c, const char *key)
int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version)
{
struct lxc_list *it,*next;
bool all = false;
char *global_token, *namespaced_token;
struct lxc_list *it, *next, *list;
const char *k = NULL;
bool all = false;
if (strcmp(key, "lxc.cgroup") == 0)
if (version == CGROUP2_SUPER_MAGIC) {
global_token = "lxc.cgroup2";
namespaced_token = "lxc.cgroup2.";
list = &c->cgroup2;
} else if (version == CGROUP_SUPER_MAGIC) {
global_token = "lxc.cgroup";
namespaced_token = "lxc.cgroup.";
list = &c->cgroup;
} else {
return -1;
}
if (strcmp(key, global_token) == 0)
all = true;
else if (strncmp(key, "lxc.cgroup.", sizeof("lxc.cgroup.") - 1) == 0)
k = key + sizeof("lxc.cgroup.") - 1;
else if (strncmp(key, namespaced_token, sizeof(namespaced_token) - 1) == 0)
k = key + sizeof(namespaced_token) - 1;
else
return -1;
lxc_list_for_each_safe(it, &c->cgroup, next) {
lxc_list_for_each_safe(it, list, next) {
struct lxc_cgroup *cg = it->elem;
if (!all && strcmp(cg->subsystem, k) != 0)
continue;
lxc_list_del(it);
free(cg->subsystem);
free(cg->value);
@ -3680,7 +3696,8 @@ void lxc_conf_free(struct lxc_conf *conf)
lxc_seccomp_free(conf);
lxc_clear_config_caps(conf);
lxc_clear_config_keepcaps(conf);
lxc_clear_cgroups(conf, "lxc.cgroup");
lxc_clear_cgroups(conf, "lxc.cgroup", CGROUP_SUPER_MAGIC);
lxc_clear_cgroups(conf, "lxc.cgroup2", CGROUP2_SUPER_MAGIC);
lxc_clear_hooks(conf, "lxc.hook");
lxc_clear_mount_entries(conf);
lxc_clear_idmaps(conf);

View File

@ -52,6 +52,8 @@ typedef void * scmp_filter_ctx;
* programmer to specify the right subsystem.
* @subsystem : the targeted subsystem
* @value : the value to set
* @version : The version of the cgroup filesystem on which the controller
* resides.
*
* @controllers : The controllers to use for this container.
* @dir : The name of the directory containing the container's cgroup.
@ -61,6 +63,7 @@ struct lxc_cgroup {
union {
/* information about a specific controller */
struct /* controller */ {
int version;
char *subsystem;
char *value;
};
@ -282,7 +285,10 @@ struct lxc_conf {
int reboot;
signed long personality;
struct utsname *utsname;
struct lxc_list cgroup;
struct {
struct lxc_list cgroup;
struct lxc_list cgroup2;
};
struct {
struct lxc_list id_map;
@ -433,7 +439,7 @@ extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
extern int lxc_clear_config_caps(struct lxc_conf *c);
extern int lxc_clear_config_keepcaps(struct lxc_conf *c);
extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key);
extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version);
extern int lxc_clear_mount_entries(struct lxc_conf *c);
extern int lxc_clear_automounts(struct lxc_conf *c);
extern int lxc_clear_hooks(struct lxc_conf *c, const char *key);

View File

@ -81,6 +81,7 @@ lxc_config_define(apparmor_profile);
lxc_config_define(cap_drop);
lxc_config_define(cap_keep);
lxc_config_define(cgroup_controller);
lxc_config_define(cgroup2_controller);
lxc_config_define(cgroup_dir);
lxc_config_define(console_logfile);
lxc_config_define(console_rotate);
@ -153,6 +154,7 @@ static struct lxc_config_t config[] = {
{ "lxc.autodev", false, set_config_autodev, get_config_autodev, clr_config_autodev, },
{ "lxc.cap.drop", false, set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, },
{ "lxc.cap.keep", false, set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, },
{ "lxc.cgroup2", false, set_config_cgroup2_controller, get_config_cgroup2_controller, clr_config_cgroup2_controller, },
{ "lxc.cgroup.dir", false, set_config_cgroup_dir, get_config_cgroup_dir, clr_config_cgroup_dir, },
{ "lxc.cgroup", false, set_config_cgroup_controller, get_config_cgroup_controller, clr_config_cgroup_controller, },
{ "lxc.console.buffer.logfile", false, set_config_console_buffer_logfile, get_config_console_buffer_logfile, clr_config_console_buffer_logfile, },
@ -1374,28 +1376,33 @@ static int set_config_signal_stop(const char *key, const char *value,
return 0;
}
static int set_config_cgroup_controller(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
static int __set_config_cgroup_controller(const char *key, const char *value,
struct lxc_conf *lxc_conf, int version)
{
char *subkey;
char *token = "lxc.cgroup.";
const char *subkey, *token;
size_t token_len;
struct lxc_list *cglist = NULL;
struct lxc_cgroup *cgelem = NULL;
if (lxc_config_value_empty(value))
return lxc_clear_cgroups(lxc_conf, key);
return lxc_clear_cgroups(lxc_conf, key, version);
subkey = strstr(key, token);
if (!subkey)
return -1;
if (version == CGROUP2_SUPER_MAGIC) {
token = "lxc.cgroup2.";
token_len = 12;
} else if (version == CGROUP_SUPER_MAGIC) {
token = "lxc.cgroup.";
token_len = 11;
} else {
return -EINVAL;
}
if (!strlen(subkey))
return -1;
if (strncmp(key, token, token_len) != 0)
return -EINVAL;
if (strlen(subkey) == strlen(token))
return -1;
subkey += strlen(token);
subkey = key + token_len;
if (*subkey == '\0')
return -EINVAL;
cglist = malloc(sizeof(*cglist));
if (!cglist)
@ -1407,14 +1414,21 @@ static int set_config_cgroup_controller(const char *key, const char *value,
memset(cgelem, 0, sizeof(*cgelem));
cgelem->subsystem = strdup(subkey);
cgelem->value = strdup(value);
if (!cgelem->subsystem || !cgelem->value)
if (!cgelem->subsystem)
goto out;
cglist->elem = cgelem;
cgelem->value = strdup(value);
if (!cgelem->value)
goto out;
lxc_list_add_tail(&lxc_conf->cgroup, cglist);
cgelem->version = version;
lxc_list_add_elem(cglist, cgelem);
if (version == CGROUP2_SUPER_MAGIC)
lxc_list_add_tail(&lxc_conf->cgroup2, cglist);
else
lxc_list_add_tail(&lxc_conf->cgroup, cglist);
return 0;
@ -1429,6 +1443,21 @@ out:
return -1;
}
static int set_config_cgroup_controller(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
return __set_config_cgroup_controller(key, value, lxc_conf,
CGROUP_SUPER_MAGIC);
}
static int set_config_cgroup2_controller(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
return __set_config_cgroup_controller(key, value, lxc_conf,
CGROUP2_SUPER_MAGIC);
}
static int set_config_cgroup_dir(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
@ -2910,11 +2939,14 @@ static int get_config_selinux_context(const char *key, char *retv, int inlen,
* If you ask for 'lxc.cgroup", then all cgroup entries will be printed, in
* 'lxc.cgroup.subsystem.key = value' format.
*/
static int get_config_cgroup_controller(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
static int __get_config_cgroup_controller(const char *key, char *retv,
int inlen, struct lxc_conf *c,
int version)
{
struct lxc_list *it;
int len;
size_t namespaced_token_len;
char *global_token, *namespaced_token;
struct lxc_list *it;
int fulllen = 0;
bool get_all = false;
@ -2923,10 +2955,22 @@ static int get_config_cgroup_controller(const char *key, char *retv, int inlen,
else
memset(retv, 0, inlen);
if (!strcmp(key, "lxc.cgroup"))
if (version == CGROUP2_SUPER_MAGIC) {
global_token = "lxc.cgroup2";
namespaced_token = "lxc.cgroup2.";
namespaced_token_len = sizeof("lxc.cgroup2.") - 1;;
} else if (version == CGROUP_SUPER_MAGIC) {
global_token = "lxc.cgroup";
namespaced_token = "lxc.cgroup.";
namespaced_token_len = sizeof("lxc.cgroup.") - 1;;
} else {
return -1;
}
if (strcmp(key, global_token) == 0)
get_all = true;
else if (!strncmp(key, "lxc.cgroup.", 11))
key += 11;
else if (strncmp(key, namespaced_token, namespaced_token_len) == 0)
key += namespaced_token_len;
else
return -1;
@ -2934,8 +2978,11 @@ static int get_config_cgroup_controller(const char *key, char *retv, int inlen,
struct lxc_cgroup *cg = it->elem;
if (get_all) {
strprint(retv, inlen, "lxc.cgroup.%s = %s\n",
cg->subsystem, cg->value);
if (version != cg->version)
continue;
strprint(retv, inlen, "%s.%s = %s\n",
global_token, cg->subsystem, cg->value);
} else if (!strcmp(cg->subsystem, key)) {
strprint(retv, inlen, "%s\n", cg->value);
}
@ -2944,6 +2991,20 @@ static int get_config_cgroup_controller(const char *key, char *retv, int inlen,
return fulllen;
}
static int get_config_cgroup_controller(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
return __get_config_cgroup_controller(key, retv, inlen, c,
CGROUP_SUPER_MAGIC);
}
static int get_config_cgroup2_controller(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
return __get_config_cgroup_controller(key, retv, inlen, c,
CGROUP2_SUPER_MAGIC);
}
static int get_config_cgroup_dir(const char *key, char *retv, int inlen,
struct lxc_conf *lxc_conf, void *data)
{
@ -3632,7 +3693,13 @@ static inline int clr_config_selinux_context(const char *key,
static inline int clr_config_cgroup_controller(const char *key,
struct lxc_conf *c, void *data)
{
return lxc_clear_cgroups(c, key);
return lxc_clear_cgroups(c, key, CGROUP_SUPER_MAGIC);
}
static inline int clr_config_cgroup2_controller(const char *key,
struct lxc_conf *c, void *data)
{
return lxc_clear_cgroups(c, key, CGROUP2_SUPER_MAGIC);
}
static int clr_config_cgroup_dir(const char *key, struct lxc_conf *lxc_conf,

View File

@ -2307,6 +2307,33 @@ char *must_make_path(const char *first, ...)
return dest;
}
char *must_append_path(char *first, ...)
{
char *cur;
size_t full_len;
va_list args;
char *dest = first;
full_len = strlen(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;

View File

@ -86,6 +86,14 @@
#define CAP_SYS_ADMIN 21
#endif
#ifndef CGROUP_SUPER_MAGIC
#define CGROUP_SUPER_MAGIC 0x27e0eb
#endif
#ifndef CGROUP2_SUPER_MAGIC
#define CGROUP2_SUPER_MAGIC 0x63677270
#endif
/* Useful macros */
/* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */
#define LXC_NUMSTRLEN64 21
@ -529,7 +537,8 @@ extern int run_command(char *buf, size_t buf_size, int (*child_fn)(void *),
/* Concatenate all passed-in strings into one path. Do not fail. If any piece
* is not prefixed with '/', add a '/'.
*/
extern char *must_make_path(const char *first, ...) __attribute__((sentinel));
__attribute__((sentinel)) extern char *must_make_path(const char *first, ...);
__attribute__((sentinel)) extern char *must_append_path(char *first, ...);
/* return copy of string @entry; do not fail. */
extern char *must_copy_string(const char *entry);