diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c index c29c0958e..44ec5a0be 100644 --- a/src/lxc/cgroups/cgfsng.c +++ b/src/lxc/cgroups/cgfsng.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -1759,8 +1761,8 @@ static inline int cg_mount_cgroup_full(int type, struct hierarchy *h, } __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops, - struct lxc_handler *handler, - const char *root, int type) + struct lxc_handler *handler, + const char *root, int type) { __do_free char *tmpfspath = NULL; int ret; @@ -1793,8 +1795,23 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops, else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC) type = LXC_AUTO_CGROUP_FULL_MIXED; - /* Mount tmpfs */ - tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL); + if (ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED) { + __do_free char *unified_path = NULL; + + unified_path = must_make_path(root, "/sys/fs/cgroup", NULL); + if (has_cgns && wants_force_mount) { + /* If cgroup namespaces are supported but the container + * will not have CAP_SYS_ADMIN after it has started we + * need to mount the cgroups manually. + */ + return cg_mount_in_cgroup_namespace(type, ops->unified, + unified_path) == 0; + } + + return cg_mount_cgroup_full(type, ops->unified, unified_path) == 0; + } + + /* mount tmpfs */ ret = safe_mount(NULL, tmpfspath, "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "size=10240k,mode=755", root); @@ -1958,28 +1975,105 @@ __cgfsng_ops static bool cgfsng_get_hierarchies(struct cgroup_ops *ops, int n, c return true; } -#define THAWED "THAWED" -#define THAWED_LEN (strlen(THAWED)) - -/* TODO: If the unified cgroup hierarchy grows a freezer controller this needs - * to be adapted. - */ -__cgfsng_ops static bool cgfsng_unfreeze(struct cgroup_ops *ops) +static bool poll_file_ready(int lfd) { int ret; - __do_free char *fullpath = NULL; + struct pollfd pfd = { + .fd = lfd, + .events = POLLIN, + .revents = 0, + }; + +again: + ret = poll(&pfd, 1, 60000); + if (ret < 0) { + if (errno == EINTR) + goto again; + + SYSERROR("Failed to poll() on file descriptor"); + return false; + } + + return (pfd.revents & POLLIN); +} + +__cgfsng_ops static bool cgfsng_freeze(struct cgroup_ops *ops) +{ + int ret; + __do_close_prot_errno int fd = -EBADF; + __do_free char *events_file = NULL, *fullpath = NULL, *line = NULL; + __do_fclose FILE *f = NULL; struct hierarchy *h; - h = get_hierarchy(ops, "freezer"); + if (ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) { + h = get_hierarchy(ops, "freezer"); + if (!h) + return false; + + fullpath = must_make_path(h->container_full_path, + "freezer.state", NULL); + return lxc_write_to_file(fullpath, "FROZEN", + STRLITERALLEN("FROZEN"), false, + 0666) == 0; + } + + h = ops->unified; if (!h) return false; - fullpath = must_make_path(h->container_full_path, "freezer.state", NULL); - ret = lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false, 0666); + fullpath = must_make_path(h->container_full_path, "cgroup.freeze", NULL); + ret = lxc_write_to_file(fullpath, "1", 1, false, 0666); if (ret < 0) return false; - return true; + events_file = + must_make_path(h->container_full_path, "cgroup.events", NULL); + fd = open(events_file, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return false; + + f = fdopen(fd, "re"); + if (!f) + return false; + move_fd(fd); + + for (int i = 0; i < 10 && poll_file_ready(fd); i++) { + size_t len; + + while (getline(&line, &len, f) != -1) { + if (strcmp(line, "frozen 1") == 0) + return true; + } + + fseek(f, 0, SEEK_SET); + }; + + return false; +} + +__cgfsng_ops static bool cgfsng_unfreeze(struct cgroup_ops *ops) +{ + __do_free char *fullpath = NULL; + struct hierarchy *h; + + if (ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) { + h = get_hierarchy(ops, "freezer"); + if (!h) + return false; + + fullpath = must_make_path(h->container_full_path, + "freezer.state", NULL); + return lxc_write_to_file(fullpath, "THAWED", + STRLITERALLEN("THAWED"), false, + 0666) == 0; + } + + h = ops->unified; + if (!h) + return false; + + fullpath = must_make_path(h->container_full_path, "cgroup.freeze", NULL); + return lxc_write_to_file(fullpath, "0", 1, false, 0666) == 0; } __cgfsng_ops static const char *cgfsng_get_cgroup(struct cgroup_ops *ops, @@ -2767,6 +2861,7 @@ struct cgroup_ops *cgfsng_ops_init(struct lxc_conf *conf) cgfsng_ops->get_cgroup = cgfsng_get_cgroup; cgfsng_ops->get = cgfsng_get; cgfsng_ops->set = cgfsng_set; + cgfsng_ops->freeze = cgfsng_freeze; cgfsng_ops->unfreeze = cgfsng_unfreeze; cgfsng_ops->setup_limits = cgfsng_setup_limits; cgfsng_ops->driver = "cgfsng"; diff --git a/src/lxc/cgroups/cgroup.h b/src/lxc/cgroups/cgroup.h index b5d4c42bc..f3f0f6726 100644 --- a/src/lxc/cgroups/cgroup.h +++ b/src/lxc/cgroups/cgroup.h @@ -154,6 +154,7 @@ struct cgroup_ops { const char *value, const char *name, const char *lxcpath); int (*get)(struct cgroup_ops *ops, const char *filename, char *value, size_t len, const char *name, const char *lxcpath); + bool (*freeze)(struct cgroup_ops *ops); bool (*unfreeze)(struct cgroup_ops *ops); bool (*setup_limits)(struct cgroup_ops *ops, struct lxc_conf *conf, bool with_devices); diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c index 953d9d8b8..1843f2a60 100644 --- a/src/lxc/freezer.c +++ b/src/lxc/freezer.c @@ -62,36 +62,49 @@ static int do_freeze_thaw(bool freeze, struct lxc_conf *conf, const char *name, if (!cgroup_ops) return -1; - ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name, lxcpath); - if (ret < 0) { - cgroup_exit(cgroup_ops); - ERROR("Failed to %s %s", - (new_state == FROZEN ? "freeze" : "unfreeze"), name); - return -1; - } - - for (;;) { - ret = cgroup_ops->get(cgroup_ops, "freezer.state", v, sizeof(v), - name, lxcpath); + if (cgroup_ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) { + ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name, + lxcpath); if (ret < 0) { cgroup_exit(cgroup_ops); - ERROR("Failed to get freezer state of %s", name); + ERROR("Failed to %s %s", + (new_state == FROZEN ? "freeze" : "unfreeze"), + name); return -1; } - v[sizeof(v) - 1] = '\0'; - v[lxc_char_right_gc(v, strlen(v))] = '\0'; + for (;;) { + ret = cgroup_ops->get(cgroup_ops, "freezer.state", v, + sizeof(v), name, lxcpath); + if (ret < 0) { + cgroup_exit(cgroup_ops); + ERROR("Failed to get freezer state of %s", name); + return -1; + } - ret = strncmp(v, state, state_len); - if (ret == 0) { - cgroup_exit(cgroup_ops); - lxc_cmd_serve_state_clients(name, lxcpath, new_state); - lxc_monitor_send_state(name, new_state, lxcpath); - return 0; + v[sizeof(v) - 1] = '\0'; + v[lxc_char_right_gc(v, strlen(v))] = '\0'; + + ret = strncmp(v, state, state_len); + if (ret == 0) { + cgroup_exit(cgroup_ops); + lxc_cmd_serve_state_clients(name, lxcpath, + new_state); + lxc_monitor_send_state(name, new_state, lxcpath); + return 0; + } + + sleep(1); } - - sleep(1); } + + ret = cgroup_ops->freeze(cgroup_ops); + cgroup_exit(cgroup_ops); + if (ret < 0) + return error_log_errno(-1, "Failed to %s container", + freeze ? "freeze" : "unfreeze"); + + return 0; } int lxc_freeze(struct lxc_conf *conf, const char *name, const char *lxcpath)