Unverified Commit 29e4eb31 by Serge Hallyn Committed by GitHub

Merge pull request #1891 from brauner/2017-10-31/cgfsng_fixes

cgroups/cgfsng: fixes, features, and improved cgroup2 handling
parents ce8cab74 0d8e40c6
...@@ -6,7 +6,6 @@ lxc.tty.max = 6 ...@@ -6,7 +6,6 @@ lxc.tty.max = 6
# Set the halt/stop signals # Set the halt/stop signals
lxc.signal.halt=SIGRTMIN+4 lxc.signal.halt=SIGRTMIN+4
lxc.signal.stop=SIGRTMIN+14
# Uncomment to disable creating tty devices subdirectory in /dev # Uncomment to disable creating tty devices subdirectory in /dev
# lxc.tty.dir = # lxc.tty.dir =
......
...@@ -204,7 +204,7 @@ static void must_append_controller(char **klist, char **nlist, char ***clist, ch ...@@ -204,7 +204,7 @@ static void must_append_controller(char **klist, char **nlist, char ***clist, ch
char *copy; char *copy;
if (string_in_list(klist, entry) && string_in_list(nlist, entry)) { if (string_in_list(klist, entry) && string_in_list(nlist, entry)) {
ERROR("Refusing to use ambiguous controller '%s'", entry); ERROR("Refusing to use ambiguous controller \"%s\"", entry);
ERROR("It is both a named and kernel subsystem"); ERROR("It is both a named and kernel subsystem");
return; return;
} }
...@@ -215,6 +215,8 @@ static void must_append_controller(char **klist, char **nlist, char ***clist, ch ...@@ -215,6 +215,8 @@ 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);
...@@ -724,29 +726,22 @@ static bool all_controllers_found(void) ...@@ -724,29 +726,22 @@ static bool all_controllers_found(void)
struct hierarchy ** hlist = hierarchies; struct hierarchy ** hlist = hierarchies;
if (!controller_found(hlist, "freezer")) { if (!controller_found(hlist, "freezer")) {
ERROR("no freezer controller mountpoint found"); ERROR("No freezer controller mountpoint found");
return false; return false;
} }
if (!cgroup_use) if (!cgroup_use)
return true; return true;
for (p = strtok_r(cgroup_use, ",", &saveptr); p; for (p = strtok_r(cgroup_use, ",", &saveptr); p;
p = strtok_r(NULL, ",", &saveptr)) { p = strtok_r(NULL, ",", &saveptr)) {
if (!controller_found(hlist, p)) { if (!controller_found(hlist, p)) {
ERROR("no %s controller mountpoint found", p); ERROR("No %s controller mountpoint found", p);
return false; return false;
} }
} }
return true;
}
/* Return true if the fs type is fuse.lxcfs */ return true;
static bool is_lxcfs(const char *line)
{
char *p = strstr(line, " - ");
if (!p)
return false;
return strncmp(p, " - fuse.lxcfs ", 14) == 0;
} }
/* /*
...@@ -756,16 +751,13 @@ static bool is_lxcfs(const char *line) ...@@ -756,16 +751,13 @@ static bool is_lxcfs(const char *line)
* 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) static char **get_controllers(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 *p = line, *p2, *tok, *saveptr = NULL; char *dup, *p2, *tok;
char *p = line, *saveptr = NULL;
char **aret = NULL; char **aret = NULL;
bool is_cgroup_v2;
/* handle cgroup v2 */
is_cgroup_v2 = is_cgroupfs_v2(line);
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
p = strchr(p, ' '); p = strchr(p, ' ');
...@@ -777,29 +769,37 @@ static char **get_controllers(char **klist, char **nlist, char *line) ...@@ -777,29 +769,37 @@ static char **get_controllers(char **klist, char **nlist, char *line)
return NULL; return NULL;
/* note - if we change how mountinfo works, then our caller /* note - if we change how mountinfo works, then our caller
* will need to verify /sys/fs/cgroup/ in this field */ * will need to verify /sys/fs/cgroup/ in this field */
if (strncmp(p, "/sys/fs/cgroup/", 15) != 0) { if (strncmp(p, "/sys/fs/cgroup/", 15)) {
INFO("cgfsng: found hierarchy not under /sys/fs/cgroup: \"%s\"", p); INFO("Found hierarchy not under /sys/fs/cgroup: \"%s\"", p);
return NULL; return NULL;
} }
p += 15; p += 15;
p2 = strchr(p, ' '); p2 = strchr(p, ' ');
if (!p2) { if (!p2) {
ERROR("corrupt mountinfo"); ERROR("Corrupt mountinfo");
return NULL; return NULL;
} }
*p2 = '\0'; *p2 = '\0';
/* cgroup v2 does not have separate mountpoints for controllers */ /* cgroup v2 does not have separate mountpoints for controllers */
if (is_cgroup_v2) { if (type == CGROUP_V2) {
must_append_controller(klist, nlist, &aret, "cgroup2"); must_append_controller(klist, nlist, &aret, "cgroup2");
return aret; return aret;
} }
for (tok = strtok_r(p, ",", &saveptr); tok; /* strdup() here for v1 hierarchies. Otherwise strtok_r() will destroy
* mountpoints such as "/sys/fs/cgroup/cpu,cpuacct".
*/
dup = strdup(p);
if (!dup)
return NULL;
for (tok = strtok_r(dup, ",", &saveptr); tok;
tok = strtok_r(NULL, ",", &saveptr)) { tok = strtok_r(NULL, ",", &saveptr)) {
must_append_controller(klist, nlist, &aret, tok); must_append_controller(klist, nlist, &aret, tok);
} }
free(dup);
return aret; return aret;
} }
...@@ -1010,15 +1010,15 @@ static void lxc_cgfsng_print_hierarchies() ...@@ -1010,15 +1010,15 @@ static void lxc_cgfsng_print_hierarchies()
int i; int i;
if (!hierarchies) { if (!hierarchies) {
printf(" No hierarchies found."); printf(" No hierarchies found\n");
return; return;
} }
printf(" Hierarchies:\n"); printf(" Hierarchies:\n");
for (i = 0, it = hierarchies; it && *it; it++, i++) { for (i = 0, it = hierarchies; it && *it; it++, i++) {
char **cit; char **cit;
int j; int j;
printf(" %d: base_cgroup %s\n", i, (*it)->base_cgroup ? (*it)->base_cgroup : "(null)"); printf(" %d: base_cgroup: %s\n", i, (*it)->base_cgroup ? (*it)->base_cgroup : "(null)");
printf(" mountpoint %s\n", (*it)->mountpoint ? (*it)->mountpoint : "(null)"); printf(" mountpoint: %s\n", (*it)->mountpoint ? (*it)->mountpoint : "(null)");
printf(" controllers:\n"); printf(" controllers:\n");
for (j = 0, cit = (*it)->controllers; cit && *cit; cit++, j++) for (j = 0, cit = (*it)->controllers; cit && *cit; cit++, j++)
printf(" %d: %s\n", j, *cit); printf(" %d: %s\n", j, *cit);
...@@ -1068,7 +1068,7 @@ static bool parse_hierarchies(void) ...@@ -1068,7 +1068,7 @@ static bool parse_hierarchies(void)
return false; return false;
if ((f = fopen("/proc/self/mountinfo", "r")) == NULL) { if ((f = fopen("/proc/self/mountinfo", "r")) == NULL) {
SYSERROR("Failed opening /proc/self/mountinfo"); SYSERROR("Failed to open \"/proc/self/mountinfo\"");
return false; return false;
} }
...@@ -1081,13 +1081,14 @@ static bool parse_hierarchies(void) ...@@ -1081,13 +1081,14 @@ static bool parse_hierarchies(void)
while (getline(&line, &len, f) != -1) { while (getline(&line, &len, f) != -1) {
char **controller_list = NULL; char **controller_list = NULL;
char *mountpoint, *base_cgroup; char *mountpoint, *base_cgroup;
bool is_cgroup_v2, writeable; bool writeable;
int type;
is_cgroup_v2 = is_cgroupfs_v2(line); type = get_cgroup_version(line);
if (!is_lxcfs(line) && !is_cgroupfs_v1(line) && !is_cgroup_v2) if (type < 0)
continue; continue;
controller_list = get_controllers(klist, nlist, line); controller_list = get_controllers(klist, nlist, line, type);
if (!controller_list) if (!controller_list)
continue; continue;
...@@ -1098,14 +1099,14 @@ static bool parse_hierarchies(void) ...@@ -1098,14 +1099,14 @@ static bool parse_hierarchies(void)
mountpoint = get_mountpoint(line); mountpoint = get_mountpoint(line);
if (!mountpoint) { if (!mountpoint) {
ERROR("Error reading mountinfo: bad line '%s'", line); ERROR("Failed parsing mountpoint from \"%s\"", line);
free_string_list(controller_list); free_string_list(controller_list);
continue; continue;
} }
base_cgroup = get_current_cgroup(basecginfo, controller_list[0]); base_cgroup = get_current_cgroup(basecginfo, controller_list[0]);
if (!base_cgroup) { if (!base_cgroup) {
ERROR("Failed to find current cgroup for controller '%s'", controller_list[0]); ERROR("Failed to find current cgroup for controller \"%s\"", controller_list[0]);
free_string_list(controller_list); free_string_list(controller_list);
free(mountpoint); free(mountpoint);
continue; continue;
...@@ -1113,7 +1114,7 @@ static bool parse_hierarchies(void) ...@@ -1113,7 +1114,7 @@ static bool parse_hierarchies(void)
trim(base_cgroup); trim(base_cgroup);
prune_init_scope(base_cgroup); prune_init_scope(base_cgroup);
if (is_cgroup_v2) if (type == CGROUP_V2)
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);
...@@ -1142,10 +1143,8 @@ static bool parse_hierarchies(void) ...@@ -1142,10 +1143,8 @@ static bool parse_hierarchies(void)
/* verify that all controllers in cgroup.use and all crucial /* verify that all controllers in cgroup.use and all crucial
* controllers are accounted for * controllers are accounted for
*/ */
if (!all_controllers_found()) { if (!all_controllers_found())
INFO("cgfsng: not all controllers were find, deferring to cgfs driver");
return false; return false;
}
return true; return true;
} }
...@@ -1156,7 +1155,7 @@ static bool collect_hierarchy_info(void) ...@@ -1156,7 +1155,7 @@ static bool collect_hierarchy_info(void)
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 */
SYSERROR("cgfsng: error reading list of cgroups to use"); ERROR("Failed to retrieve list of cgroups to use");
return false; return false;
} }
cgroup_use = must_copy_string(tmp); cgroup_use = must_copy_string(tmp);
...@@ -1176,6 +1175,8 @@ static void *cgfsng_init(struct lxc_handler *handler) ...@@ -1176,6 +1175,8 @@ static void *cgfsng_init(struct lxc_handler *handler)
d->name = must_copy_string(handler->name); d->name = must_copy_string(handler->name);
/* copy per-container cgroup information */ /* copy per-container cgroup information */
d->cgroup_meta.dir = NULL;
d->cgroup_meta.controllers = NULL;
if (handler->conf) { if (handler->conf) {
d->cgroup_meta.dir = must_copy_string(handler->conf->cgroup_meta.dir); d->cgroup_meta.dir = must_copy_string(handler->conf->cgroup_meta.dir);
d->cgroup_meta.controllers = must_copy_string(handler->conf->cgroup_meta.controllers); d->cgroup_meta.controllers = must_copy_string(handler->conf->cgroup_meta.controllers);
...@@ -1507,7 +1508,7 @@ static int chown_cgroup_wrapper(void *data) ...@@ -1507,7 +1508,7 @@ static int chown_cgroup_wrapper(void *data)
return 0; return 0;
} }
static bool cgfsns_chown(void *hdata, struct lxc_conf *conf) static bool cgfsng_chown(void *hdata, struct lxc_conf *conf)
{ {
struct cgfsng_handler_data *d = hdata; struct cgfsng_handler_data *d = hdata;
struct chown_data wrap; struct chown_data wrap;
...@@ -1617,27 +1618,36 @@ do_secondstage_mounts_if_needed(int type, struct hierarchy *h, ...@@ -1617,27 +1618,36 @@ do_secondstage_mounts_if_needed(int type, struct hierarchy *h,
return 0; return 0;
} }
static int mount_cgroup_cgns_supported(struct hierarchy *h, const char *controllerpath) static int mount_cgroup_cgns_supported(int type, struct hierarchy *h, const char *controllerpath)
{ {
int ret; int ret;
char *controllers = NULL; char *controllers = NULL;
char *type = "cgroup2"; char *fstype = "cgroup2";
unsigned long flags = 0;
flags |= MS_NOSUID;
flags |= MS_NOEXEC;
flags |= MS_NODEV;
flags |= MS_RELATIME;
if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO)
flags |= MS_RDONLY;
if (!h->is_cgroup_v2) { if (!h->is_cgroup_v2) {
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;
type = "cgroup"; fstype = "cgroup";
} }
ret = mount("cgroup", controllerpath, type, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RELATIME, controllers); ret = mount("cgroup", controllerpath, fstype, flags, controllers);
free(controllers); free(controllers);
if (ret < 0) { if (ret < 0) {
SYSERROR("Failed to mount %s with cgroup filesystem type %s", controllerpath, type); SYSERROR("Failed to mount %s with cgroup filesystem type %s", controllerpath, fstype);
return -1; return -1;
} }
DEBUG("Mounted %s with cgroup filesystem type %s", controllerpath, type); DEBUG("Mounted %s with cgroup filesystem type %s", controllerpath, fstype);
return 0; return 0;
} }
...@@ -1701,7 +1711,7 @@ static bool cgfsng_mount(void *hdata, const char *root, int type) ...@@ -1701,7 +1711,7 @@ static bool cgfsng_mount(void *hdata, const char *root, int type)
* will not have CAP_SYS_ADMIN after it has started we * will not have CAP_SYS_ADMIN after it has started we
* need to mount the cgroups manually. * need to mount the cgroups manually.
*/ */
r = mount_cgroup_cgns_supported(h, controllerpath); r = mount_cgroup_cgns_supported(type, h, controllerpath);
free(controllerpath); free(controllerpath);
if (r < 0) if (r < 0)
goto bad; goto bad;
...@@ -2152,7 +2162,7 @@ static struct cgroup_ops cgfsng_ops = { ...@@ -2152,7 +2162,7 @@ static struct cgroup_ops cgfsng_ops = {
.setup_limits = cgfsng_setup_limits, .setup_limits = cgfsng_setup_limits,
.name = "cgroupfs-ng", .name = "cgroupfs-ng",
.attach = cgfsng_attach, .attach = cgfsng_attach,
.chown = cgfsns_chown, .chown = cgfsng_chown,
.mount_cgroup = cgfsng_mount, .mount_cgroup = cgfsng_mount,
.nrtasks = cgfsng_nrtasks, .nrtasks = cgfsng_nrtasks,
.driver = CGFSNG, .driver = CGFSNG,
......
...@@ -32,6 +32,17 @@ ...@@ -32,6 +32,17 @@
#include "cgroup_utils.h" #include "cgroup_utils.h"
#include "utils.h" #include "utils.h"
int get_cgroup_version(char *line)
{
if (is_cgroupfs_v1(line))
return CGROUP_V1;
if (is_cgroupfs_v2(line))
return CGROUP_V2;
return -1;
}
bool is_cgroupfs_v1(char *line) bool is_cgroupfs_v1(char *line)
{ {
char *p = strstr(line, " - "); char *p = strstr(line, " - ");
...@@ -67,20 +78,39 @@ bool test_writeable_v2(char *mountpoint, char *path) ...@@ -67,20 +78,39 @@ bool test_writeable_v2(char *mountpoint, char *path)
* file. * file.
*/ */
int ret; int ret;
char *cgroup_path, *cgroup_procs_file; char *cgroup_path, *cgroup_procs_file, *cgroup_threads_file;
cgroup_path = must_make_path(mountpoint, path, NULL); cgroup_path = must_make_path(mountpoint, path, NULL);
cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL); cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL);
ret = access(cgroup_path, W_OK); ret = access(cgroup_path, W_OK);
free(cgroup_path);
if (ret < 0) { if (ret < 0) {
free(cgroup_path);
free(cgroup_procs_file); free(cgroup_procs_file);
return false; return false;
} }
ret = access(cgroup_procs_file, W_OK); ret = access(cgroup_procs_file, W_OK);
free(cgroup_procs_file); free(cgroup_procs_file);
if (ret < 0) {
free(cgroup_path);
return false;
}
/* Newer versions of cgroup2 now also require write access to the
* "cgroup.threads" file.
*/
cgroup_threads_file = must_make_path(cgroup_path, "cgroup.threads", NULL);
free(cgroup_path);
if (!file_exists(cgroup_threads_file)) {
free(cgroup_threads_file);
return true;
}
ret = access(cgroup_threads_file, W_OK);
free(cgroup_threads_file);
if (ret < 0)
return false;
return ret == 0; return ret == 0;
} }
...@@ -28,6 +28,13 @@ ...@@ -28,6 +28,13 @@
#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. */
extern int get_cgroup_version(char *line);
/* Check if given entry from /proc/<pid>/mountinfo is a cgroupfs v1 mount. */ /* Check if given entry from /proc/<pid>/mountinfo is a cgroupfs v1 mount. */
extern bool is_cgroupfs_v1(char *line); extern bool is_cgroupfs_v1(char *line);
......
...@@ -264,7 +264,7 @@ static int log_append_logfile(const struct lxc_log_appender *appender, ...@@ -264,7 +264,7 @@ static int log_append_logfile(const struct lxc_log_appender *appender,
{ {
char buffer[LXC_LOG_BUFFER_SIZE]; char buffer[LXC_LOG_BUFFER_SIZE];
char date_time[LXC_LOG_TIME_SIZE]; char date_time[LXC_LOG_TIME_SIZE];
int n; int n, ret;
int fd_to_use = -1; int fd_to_use = -1;
#ifndef NO_LXC_CONF #ifndef NO_LXC_CONF
...@@ -295,8 +295,13 @@ static int log_append_logfile(const struct lxc_log_appender *appender, ...@@ -295,8 +295,13 @@ static int log_append_logfile(const struct lxc_log_appender *appender,
if (n < 0) if (n < 0)
return n; return n;
if ((size_t)n < (sizeof(buffer) - 1)) if ((size_t)n < (sizeof(buffer) - 1)) {
n += vsnprintf(buffer + n, sizeof(buffer) - n, event->fmt, *event->vap); ret = vsnprintf(buffer + n, sizeof(buffer) - n, event->fmt, *event->vap);
if (ret < 0)
return 0;
n += ret;
}
if ((size_t)n >= sizeof(buffer)) if ((size_t)n >= sizeof(buffer))
n = sizeof(buffer) - 1; n = sizeof(buffer) - 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment