Merge pull request #3120 from brauner/2019-08-15/cgroup2_freezer

cgroups: support cgroup2 freezer
This commit is contained in:
Stéphane Graber 2019-08-27 14:01:41 -06:00 committed by GitHub
commit b14cd8ac4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 38 deletions

View File

@ -43,6 +43,8 @@
#include <grp.h> #include <grp.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/types.h> #include <linux/types.h>
#include <poll.h>
#include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -1793,8 +1795,23 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC) else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC)
type = LXC_AUTO_CGROUP_FULL_MIXED; type = LXC_AUTO_CGROUP_FULL_MIXED;
/* Mount tmpfs */ if (ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED) {
tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL); __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", ret = safe_mount(NULL, tmpfspath, "tmpfs",
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME,
"size=10240k,mode=755", root); "size=10240k,mode=755", root);
@ -1958,30 +1975,107 @@ __cgfsng_ops static bool cgfsng_get_hierarchies(struct cgroup_ops *ops, int n, c
return true; return true;
} }
#define THAWED "THAWED" static bool poll_file_ready(int lfd)
#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)
{ {
int ret; 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; struct hierarchy *h;
if (ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) {
h = get_hierarchy(ops, "freezer"); h = get_hierarchy(ops, "freezer");
if (!h) if (!h)
return false; return false;
fullpath = must_make_path(h->container_full_path, "freezer.state", NULL); fullpath = must_make_path(h->container_full_path,
ret = lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false, 0666); "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, "cgroup.freeze", NULL);
ret = lxc_write_to_file(fullpath, "1", 1, false, 0666);
if (ret < 0) if (ret < 0)
return false; return false;
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; 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, __cgfsng_ops static const char *cgfsng_get_cgroup(struct cgroup_ops *ops,
const char *controller) const char *controller)
{ {
@ -2767,6 +2861,7 @@ struct cgroup_ops *cgfsng_ops_init(struct lxc_conf *conf)
cgfsng_ops->get_cgroup = cgfsng_get_cgroup; cgfsng_ops->get_cgroup = cgfsng_get_cgroup;
cgfsng_ops->get = cgfsng_get; cgfsng_ops->get = cgfsng_get;
cgfsng_ops->set = cgfsng_set; cgfsng_ops->set = cgfsng_set;
cgfsng_ops->freeze = cgfsng_freeze;
cgfsng_ops->unfreeze = cgfsng_unfreeze; cgfsng_ops->unfreeze = cgfsng_unfreeze;
cgfsng_ops->setup_limits = cgfsng_setup_limits; cgfsng_ops->setup_limits = cgfsng_setup_limits;
cgfsng_ops->driver = "cgfsng"; cgfsng_ops->driver = "cgfsng";

View File

@ -154,6 +154,7 @@ struct cgroup_ops {
const char *value, const char *name, const char *lxcpath); const char *value, const char *name, const char *lxcpath);
int (*get)(struct cgroup_ops *ops, const char *filename, char *value, int (*get)(struct cgroup_ops *ops, const char *filename, char *value,
size_t len, const char *name, const char *lxcpath); size_t len, const char *name, const char *lxcpath);
bool (*freeze)(struct cgroup_ops *ops);
bool (*unfreeze)(struct cgroup_ops *ops); bool (*unfreeze)(struct cgroup_ops *ops);
bool (*setup_limits)(struct cgroup_ops *ops, struct lxc_conf *conf, bool (*setup_limits)(struct cgroup_ops *ops, struct lxc_conf *conf,
bool with_devices); bool with_devices);

View File

@ -62,17 +62,20 @@ static int do_freeze_thaw(bool freeze, struct lxc_conf *conf, const char *name,
if (!cgroup_ops) if (!cgroup_ops)
return -1; return -1;
ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name, lxcpath); if (cgroup_ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) {
ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name,
lxcpath);
if (ret < 0) { if (ret < 0) {
cgroup_exit(cgroup_ops); cgroup_exit(cgroup_ops);
ERROR("Failed to %s %s", ERROR("Failed to %s %s",
(new_state == FROZEN ? "freeze" : "unfreeze"), name); (new_state == FROZEN ? "freeze" : "unfreeze"),
name);
return -1; return -1;
} }
for (;;) { for (;;) {
ret = cgroup_ops->get(cgroup_ops, "freezer.state", v, sizeof(v), ret = cgroup_ops->get(cgroup_ops, "freezer.state", v,
name, lxcpath); sizeof(v), name, lxcpath);
if (ret < 0) { if (ret < 0) {
cgroup_exit(cgroup_ops); cgroup_exit(cgroup_ops);
ERROR("Failed to get freezer state of %s", name); ERROR("Failed to get freezer state of %s", name);
@ -85,7 +88,8 @@ static int do_freeze_thaw(bool freeze, struct lxc_conf *conf, const char *name,
ret = strncmp(v, state, state_len); ret = strncmp(v, state, state_len);
if (ret == 0) { if (ret == 0) {
cgroup_exit(cgroup_ops); cgroup_exit(cgroup_ops);
lxc_cmd_serve_state_clients(name, lxcpath, new_state); lxc_cmd_serve_state_clients(name, lxcpath,
new_state);
lxc_monitor_send_state(name, new_state, lxcpath); lxc_monitor_send_state(name, new_state, lxcpath);
return 0; return 0;
} }
@ -94,6 +98,15 @@ static int do_freeze_thaw(bool freeze, struct lxc_conf *conf, const char *name,
} }
} }
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) int lxc_freeze(struct lxc_conf *conf, const char *name, const char *lxcpath)
{ {
lxc_cmd_serve_state_clients(name, lxcpath, FREEZING); lxc_cmd_serve_state_clients(name, lxcpath, FREEZING);