cgroups: refactor cgroup handling

This replaces the constructor implementation of cgroup handling with a simpler, thread-safe on-demand model of cgroup driver initialization. Making the cgroup initialization code run in a constructor means that each time the shared library gets mapped the cgroup parsing code gets run. That's unnecessary overhead. It also feels to me that this is only accidently thread-safe because constructors are only run once. But should threads actually end up manipulating or freeing memory that is file-global to cgfsng.c we'd be screwed. Now, I might be wrong here but the cleaner implementation is to allocate a cgroup driver on demand whenever we need it. Take the chance and rework the cgroup_ops interface to make the functions it wants to have implemented a lot cleaner. Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent e37266ab
...@@ -1272,10 +1272,17 @@ int lxc_attach(const char *name, const char *lxcpath, ...@@ -1272,10 +1272,17 @@ int lxc_attach(const char *name, const char *lxcpath,
/* Attach to cgroup, if requested. */ /* Attach to cgroup, if requested. */
if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
if (!cgroup_attach(name, lxcpath, pid)) struct cgroup_ops *cgroup_ops;
cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops)
goto on_error;
if (!cgroup_ops->attach(cgroup_ops, name, lxcpath, pid))
goto on_error; goto on_error;
TRACE("Moved intermediate process %d into container's "
"cgroups", pid); cgroup_exit(cgroup_ops);
TRACE("Moved intermediate process %d into container's cgroups", pid);
} }
/* Setup /proc limits */ /* Setup /proc limits */
......
...@@ -32,180 +32,61 @@ ...@@ -32,180 +32,61 @@
lxc_log_define(lxc_cgroup, lxc); lxc_log_define(lxc_cgroup, lxc);
static struct cgroup_ops *ops = NULL;
extern struct cgroup_ops *cgfsng_ops_init(void); extern struct cgroup_ops *cgfsng_ops_init(void);
__attribute__((constructor)) void cgroup_ops_init(void) struct cgroup_ops *cgroup_init(struct lxc_handler *handler)
{ {
if (ops) { struct cgroup_ops *cgroup_ops;
INFO("Running with %s in version %s", ops->driver, ops->version);
return;
}
DEBUG("cgroup_init");
ops = cgfsng_ops_init();
if (ops)
INFO("Initialized cgroup driver %s", ops->driver);
}
bool cgroup_init(struct lxc_handler *handler)
{
if (handler->cgroup_data) {
ERROR("cgroup_init called on already initialized handler");
return true;
}
if (ops) {
INFO("cgroup driver %s initing for %s", ops->driver, handler->name);
handler->cgroup_data = ops->init(handler);
}
return handler->cgroup_data != NULL; cgroup_ops = cgfsng_ops_init();
} if (!cgroup_ops) {
ERROR("Failed to initialize cgroup driver");
void cgroup_destroy(struct lxc_handler *handler) return NULL;
{
if (ops) {
ops->destroy(handler->cgroup_data, handler->conf);
handler->cgroup_data = NULL;
} }
}
/* Create the container cgroups for all requested controllers. */
bool cgroup_create(struct lxc_handler *handler)
{
if (ops)
return ops->create(handler->cgroup_data);
return false;
}
/* Enter the container init into its new cgroups for all requested controllers. */
bool cgroup_enter(struct lxc_handler *handler)
{
if (ops)
return ops->enter(handler->cgroup_data, handler->pid);
return false;
}
bool cgroup_create_legacy(struct lxc_handler *handler)
{
if (ops && ops->create_legacy)
return ops->create_legacy(handler->cgroup_data, handler->pid);
return true;
}
const char *cgroup_get_cgroup(struct lxc_handler *handler,
const char *subsystem)
{
if (ops)
return ops->get_cgroup(handler->cgroup_data, subsystem);
if (!cgroup_ops->data_init(cgroup_ops))
return NULL; return NULL;
}
bool cgroup_escape(struct lxc_handler *handler)
{
if (ops)
return ops->escape(handler->cgroup_data);
return false;
}
int cgroup_num_hierarchies(void)
{
if (!ops)
return -1;
return ops->num_hierarchies();
}
bool cgroup_get_hierarchies(int n, char ***out) TRACE("Initialized cgroup driver %s", cgroup_ops->driver);
{
if (!ops)
return false;
return ops->get_hierarchies(n, out); if (cgroup_ops->cgroup_layout == CGROUP_LAYOUT_LEGACY)
} TRACE("Running with legacy cgroup layout");
else if (cgroup_ops->cgroup_layout == CGROUP_LAYOUT_HYBRID)
bool cgroup_unfreeze(struct lxc_handler *handler) TRACE("Running with hybrid cgroup layout");
{ else if (cgroup_ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED)
if (ops) TRACE("Running with unified cgroup layout");
return ops->unfreeze(handler->cgroup_data);
return false;
}
bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
{
if (ops)
return ops->setup_limits(handler->cgroup_data,
handler->conf, with_devices);
return false;
}
bool cgroup_chown(struct lxc_handler *handler)
{
if (ops && ops->chown)
return ops->chown(handler->cgroup_data, handler->conf);
return true;
}
bool cgroup_mount(const char *root, struct lxc_handler *handler, int type)
{
if (ops)
return ops->mount_cgroup(handler, root, type);
return false;
}
int cgroup_nrtasks(struct lxc_handler *handler)
{
if (ops) {
if (ops->nrtasks)
return ops->nrtasks(handler->cgroup_data);
else else
WARN("cgroup driver \"%s\" doesn't implement nrtasks", ops->driver); WARN("Running with unknown cgroup layout");
}
return -1; return cgroup_ops;
} }
bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid) void cgroup_exit(struct cgroup_ops *ops)
{ {
if (ops) struct hierarchy **it;
return ops->attach(name, lxcpath, pid);
return false; if (!ops)
} return;
int lxc_cgroup_set(const char *filename, const char *value, const char *name, free(ops->cgroup_use);
const char *lxcpath) free(ops->cgroup_pattern);
{ free(ops->container_cgroup);
if (ops)
return ops->set(filename, value, name, lxcpath);
return -1; for (it = ops->hierarchies; it && *it; it++) {
} char **ctrlr;
int lxc_cgroup_get(const char *filename, char *value, size_t len, for (ctrlr = (*it)->controllers; ctrlr && *ctrlr; ctrlr++)
const char *name, const char *lxcpath) free(*ctrlr);
{ free((*it)->controllers);
if (ops)
return ops->get(filename, value, len, name, lxcpath);
return -1; free((*it)->mountpoint);
} free((*it)->base_cgroup);
free((*it)->fullcgpath);
free(*it);
}
free(ops->hierarchies);
void cgroup_disconnect(void) return;
{
if (ops && ops->disconnect)
ops->disconnect();
} }
#define INIT_SCOPE "/init.scope" #define INIT_SCOPE "/init.scope"
......
...@@ -39,48 +39,114 @@ typedef enum { ...@@ -39,48 +39,114 @@ typedef enum {
CGROUP_LAYOUT_UNIFIED = 2, CGROUP_LAYOUT_UNIFIED = 2,
} cgroup_layout_t; } cgroup_layout_t;
/* A descriptor for a mounted hierarchy
*
* @controllers
* - legacy hierarchy
* Either NULL, or a null-terminated list of all the co-mounted controllers.
* - unified hierarchy
* Either NULL, or a null-terminated list of all enabled controllers.
*
* @mountpoint
* - The mountpoint we will use.
* - legacy hierarchy
* It will be either /sys/fs/cgroup/controller or
* /sys/fs/cgroup/controllerlist.
* - unified hierarchy
* It will either be /sys/fs/cgroup or /sys/fs/cgroup/<mountpoint-name>
* depending on whether this is a hybrid cgroup layout (mix of legacy and
* unified hierarchies) or a pure unified cgroup layout.
*
* @base_cgroup
* - The cgroup under which the container cgroup path
* is created. This will be either the caller's cgroup (if not root), or
* init's cgroup (if root).
*
* @fullcgpath
* - The full path to the containers cgroup.
*
* @version
* - legacy hierarchy
* If the hierarchy is a legacy hierarchy this will be set to
* CGROUP_SUPER_MAGIC.
* - unified hierarchy
* If the hierarchy is a legacy hierarchy this will be set to
* CGROUP2_SUPER_MAGIC.
*/
struct hierarchy {
char **controllers;
char *mountpoint;
char *base_cgroup;
char *fullcgpath;
int version;
};
struct cgroup_ops { struct cgroup_ops {
/* string constant */
const char *driver; const char *driver;
/* string constant */
const char *version; const char *version;
void *(*init)(struct lxc_handler *handler); /* What controllers is the container supposed to use. */
void (*destroy)(void *hdata, struct lxc_conf *conf); char *cgroup_use;
bool (*create)(void *hdata); char *cgroup_pattern;
bool (*enter)(void *hdata, pid_t pid); char *container_cgroup;
bool (*create_legacy)(void *hdata, pid_t pid);
const char *(*get_cgroup)(void *hdata, const char *subsystem); /* @hierarchies
bool (*escape)(); * - A NULL-terminated array of struct hierarchy, one per legacy
int (*num_hierarchies)(); * hierarchy. No duplicates. First sufficient, writeable mounted
bool (*get_hierarchies)(int n, char ***out); * hierarchy wins.
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); struct hierarchy **hierarchies;
bool (*unfreeze)(void *hdata); struct hierarchy *unified;
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); * @cgroup_layout
bool (*mount_cgroup)(void *hdata, const char *root, int type); * - What cgroup layout the container is running with.
int (*nrtasks)(void *hdata); * - CGROUP_LAYOUT_UNKNOWN
void (*disconnect)(void); * The cgroup layout could not be determined. This should be treated
* as an error condition.
* - CGROUP_LAYOUT_LEGACY
* The container is running with all controllers mounted into legacy
* cgroup hierarchies.
* - CGROUP_LAYOUT_HYBRID
* The container is running with at least one controller mounted
* into a legacy cgroup hierarchy and a mountpoint for the unified
* hierarchy. The unified hierarchy can be empty (no controllers
* enabled) or non-empty (controllers enabled).
* - CGROUP_LAYOUT_UNIFIED
* The container is running on a pure unified cgroup hierarchy. The
* unified hierarchy can be empty (no controllers enabled) or
* non-empty (controllers enabled).
*/
cgroup_layout_t cgroup_layout;
bool (*data_init)(struct cgroup_ops *ops);
void (*destroy)(struct cgroup_ops *ops, struct lxc_handler *handler);
bool (*create)(struct cgroup_ops *ops, struct lxc_handler *handler);
bool (*enter)(struct cgroup_ops *ops, pid_t pid);
const char *(*get_cgroup)(struct cgroup_ops *ops, const char *controller);
bool (*escape)(const struct cgroup_ops *ops);
int (*num_hierarchies)(struct cgroup_ops *ops);
bool (*get_hierarchies)(struct cgroup_ops *ops, int n, char ***out);
int (*set)(struct cgroup_ops *ops, const char *filename,
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 (*unfreeze)(struct cgroup_ops *ops);
bool (*setup_limits)(struct cgroup_ops *ops, struct lxc_conf *conf,
bool with_devices);
bool (*chown)(struct cgroup_ops *ops, struct lxc_conf *conf);
bool (*attach)(struct cgroup_ops *ops, const char *name,
const char *lxcpath, pid_t pid);
bool (*mount)(struct cgroup_ops *ops, struct lxc_handler *handler,
const char *root, int type);
int (*nrtasks)(struct cgroup_ops *ops);
}; };
extern bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid); extern struct cgroup_ops *cgroup_init(struct lxc_handler *handler);
extern bool cgroup_mount(const char *root, struct lxc_handler *handler, int type); extern void cgroup_exit(struct cgroup_ops *ops);
extern void cgroup_destroy(struct lxc_handler *handler);
extern bool cgroup_init(struct lxc_handler *handler);
extern bool cgroup_create(struct lxc_handler *handler);
extern bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices);
extern bool cgroup_chown(struct lxc_handler *handler);
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 bool cgroup_escape();
extern int cgroup_num_hierarchies();
extern bool cgroup_get_hierarchies(int i, char ***out);
extern bool cgroup_unfreeze(struct lxc_handler *handler);
extern void cgroup_disconnect(void);
extern void prune_init_scope(char *cg); extern void prune_init_scope(char *cg);
extern bool is_crucial_cgroup_subsystem(const char *s); extern bool is_crucial_cgroup_subsystem(const char *s);
......
...@@ -473,11 +473,12 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, ...@@ -473,11 +473,12 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
{ {
const char *path; const char *path;
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
if (req->datalen > 0) if (req->datalen > 0)
path = cgroup_get_cgroup(handler, req->data); path = cgroup_ops->get_cgroup(cgroup_ops, req->data);
else else
path = cgroup_get_cgroup(handler, NULL); path = cgroup_ops->get_cgroup(cgroup_ops, NULL);
if (!path) if (!path)
return -1; return -1;
...@@ -637,6 +638,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, ...@@ -637,6 +638,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
{ {
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
int stopsignal = SIGKILL; int stopsignal = SIGKILL;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
if (handler->conf->stopsignal) if (handler->conf->stopsignal)
stopsignal = handler->conf->stopsignal; stopsignal = handler->conf->stopsignal;
...@@ -648,7 +650,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, ...@@ -648,7 +650,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
* lxc_unfreeze() would do another cmd (GET_CGROUP) which would * lxc_unfreeze() would do another cmd (GET_CGROUP) which would
* deadlock us. * deadlock us.
*/ */
if (cgroup_unfreeze(handler)) if (cgroup_ops->unfreeze(cgroup_ops))
return 0; return 0;
ERROR("Failed to unfreeze container \"%s\"", handler->name); ERROR("Failed to unfreeze container \"%s\"", handler->name);
......
...@@ -757,7 +757,10 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha ...@@ -757,7 +757,10 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
if (flags & LXC_AUTO_CGROUP_FORCE) if (flags & LXC_AUTO_CGROUP_FORCE)
cg_flags |= LXC_AUTO_CGROUP_FORCE; cg_flags |= LXC_AUTO_CGROUP_FORCE;
if (!cgroup_mount(conf->rootfs.path ? conf->rootfs.mount : "", handler, cg_flags)) { if (!handler->cgroup_ops->mount(handler->cgroup_ops,
handler,
conf->rootfs.path ? conf->rootfs.mount : "",
cg_flags)) {
SYSERROR("Failed to mount \"/sys/fs/cgroup\""); SYSERROR("Failed to mount \"/sys/fs/cgroup\"");
return -1; return -1;
} }
......
...@@ -171,7 +171,7 @@ static int cmp_version(const char *v1, const char *v2) ...@@ -171,7 +171,7 @@ static int cmp_version(const char *v1, const char *v2)
return -1; return -1;
} }
static void exec_criu(struct criu_opts *opts) static void exec_criu(struct cgroup_ops *cgroup_ops, struct criu_opts *opts)
{ {
char **argv, log[PATH_MAX]; char **argv, log[PATH_MAX];
int static_args = 23, argc = 0, i, ret; int static_args = 23, argc = 0, i, ret;
...@@ -190,7 +190,7 @@ static void exec_criu(struct criu_opts *opts) ...@@ -190,7 +190,7 @@ static void exec_criu(struct criu_opts *opts)
* /actual/ root cgroup so that lxcfs thinks criu has enough rights to * /actual/ root cgroup so that lxcfs thinks criu has enough rights to
* see all cgroups. * see all cgroups.
*/ */
if (!cgroup_escape()) { if (!cgroup_ops->escape(cgroup_ops)) {
ERROR("failed to escape cgroups"); ERROR("failed to escape cgroups");
return; return;
} }
...@@ -248,8 +248,8 @@ static void exec_criu(struct criu_opts *opts) ...@@ -248,8 +248,8 @@ static void exec_criu(struct criu_opts *opts)
return; return;
} }
if (cgroup_num_hierarchies() > 0) if (cgroup_ops->num_hierarchies(cgroup_ops) > 0)
static_args += 2 * cgroup_num_hierarchies(); static_args += 2 * cgroup_ops->num_hierarchies(cgroup_ops);
if (opts->user->verbose) if (opts->user->verbose)
static_args++; static_args++;
...@@ -306,11 +306,11 @@ static void exec_criu(struct criu_opts *opts) ...@@ -306,11 +306,11 @@ static void exec_criu(struct criu_opts *opts)
DECLARE_ARG("-o"); DECLARE_ARG("-o");
DECLARE_ARG(log); DECLARE_ARG(log);
for (i = 0; i < cgroup_num_hierarchies(); i++) { for (i = 0; i < cgroup_ops->num_hierarchies(cgroup_ops); i++) {
char **controllers = NULL, *fullname; char **controllers = NULL, *fullname;
char *path, *tmp; char *path, *tmp;
if (!cgroup_get_hierarchies(i, &controllers)) { if (!cgroup_ops->get_hierarchies(cgroup_ops, i, &controllers)) {
ERROR("failed to get hierarchy %d", i); ERROR("failed to get hierarchy %d", i);
goto err; goto err;
} }
...@@ -328,7 +328,7 @@ static void exec_criu(struct criu_opts *opts) ...@@ -328,7 +328,7 @@ static void exec_criu(struct criu_opts *opts)
} else { } else {
const char *p; const char *p;
p = cgroup_get_cgroup(opts->handler, controllers[0]); p = cgroup_ops->get_cgroup(cgroup_ops, controllers[0]);
if (!p) { if (!p) {
ERROR("failed to get cgroup path for %s", controllers[0]); ERROR("failed to get cgroup path for %s", controllers[0]);
goto err; goto err;
...@@ -937,6 +937,7 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_ ...@@ -937,6 +937,7 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_
struct lxc_handler *handler; struct lxc_handler *handler;
int status = 0; int status = 0;
int pipes[2] = {-1, -1}; int pipes[2] = {-1, -1};
struct cgroup_ops *cgroup_ops;
/* Try to detach from the current controlling tty if it exists. /* Try to detach from the current controlling tty if it exists.
* Othwerise, lxc_init (via lxc_console) will attach the container's * Othwerise, lxc_init (via lxc_console) will attach the container's
...@@ -958,12 +959,12 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_ ...@@ -958,12 +959,12 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_
if (lxc_init(c->name, handler) < 0) if (lxc_init(c->name, handler) < 0)
goto out; goto out;
if (!cgroup_init(handler)) { cgroup_ops = cgroup_init(NULL);
ERROR("failed initing cgroups"); if (!cgroup_ops)
goto out_fini_handler; goto out_fini_handler;
} handler->cgroup_ops = cgroup_ops;
if (!cgroup_create(handler)) { if (!cgroup_ops->create(cgroup_ops, handler)) {
ERROR("failed creating groups"); ERROR("failed creating groups");
goto out_fini_handler; goto out_fini_handler;
} }
...@@ -1052,7 +1053,7 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_ ...@@ -1052,7 +1053,7 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_
os.console_name = c->lxc_conf->console.name; os.console_name = c->lxc_conf->console.name;
/* exec_criu() returning is an error */ /* exec_criu() returning is an error */
exec_criu(&os); exec_criu(cgroup_ops, &os);
umount(rootfs->mount); umount(rootfs->mount);
rmdir(rootfs->mount); rmdir(rootfs->mount);
goto out_fini_handler; goto out_fini_handler;
...@@ -1253,16 +1254,21 @@ static bool do_dump(struct lxc_container *c, char *mode, struct migrate_opts *op ...@@ -1253,16 +1254,21 @@ static bool do_dump(struct lxc_container *c, char *mode, struct migrate_opts *op
if (pid == 0) { if (pid == 0) {
struct criu_opts os; struct criu_opts os;
struct lxc_handler h; struct lxc_handler h;
struct cgroup_ops *cgroup_ops;
close(criuout[0]); close(criuout[0]);
lxc_zero_handler(&h); lxc_zero_handler(&h);
h.name = c->name; h.name = c->name;
if (!cgroup_init(&h)) {
cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops) {
ERROR("failed to cgroup_init()"); ERROR("failed to cgroup_init()");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
return -1;
} }
h.cgroup_ops = cgroup_ops;
os.pipefd = criuout[1]; os.pipefd = criuout[1];
os.action = mode; os.action = mode;
...@@ -1278,7 +1284,7 @@ static bool do_dump(struct lxc_container *c, char *mode, struct migrate_opts *op ...@@ -1278,7 +1284,7 @@ static bool do_dump(struct lxc_container *c, char *mode, struct migrate_opts *op
} }
/* exec_criu() returning is an error */ /* exec_criu() returning is an error */
exec_criu(&os); exec_criu(cgroup_ops, &os);
free(criu_version); free(criu_version);
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} else { } else {
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h> #include <sys/param.h>
#include "cgroup.h"
#include "commands.h" #include "commands.h"
#include "error.h" #include "error.h"
#include "log.h" #include "log.h"
...@@ -45,8 +46,14 @@ lxc_state_t freezer_state(const char *name, const char *lxcpath) ...@@ -45,8 +46,14 @@ lxc_state_t freezer_state(const char *name, const char *lxcpath)
{ {
int ret; int ret;
char v[100]; char v[100];
struct cgroup_ops *cgroup_ops;
ret = lxc_cgroup_get("freezer.state", v, sizeof(v), name, lxcpath); cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops)
return -1;
ret = cgroup_ops->get(cgroup_ops, "freezer.state", v, sizeof(v), name, lxcpath);
cgroup_exit(cgroup_ops);
if (ret < 0) if (ret < 0)
return -1; return -1;
...@@ -60,19 +67,26 @@ static int do_freeze_thaw(bool freeze, const char *name, const char *lxcpath) ...@@ -60,19 +67,26 @@ static int do_freeze_thaw(bool freeze, const char *name, const char *lxcpath)
{ {
int ret; int ret;
char v[100]; char v[100];
struct cgroup_ops *cgroup_ops;
const char *state = freeze ? "FROZEN" : "THAWED"; const char *state = freeze ? "FROZEN" : "THAWED";
size_t state_len = 6; size_t state_len = 6;
lxc_state_t new_state = freeze ? FROZEN : THAWED; lxc_state_t new_state = freeze ? FROZEN : THAWED;
ret = lxc_cgroup_set("freezer.state", state, name, lxcpath); cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops)
return -1;
ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name, lxcpath);
if (ret < 0) { if (ret < 0) {
cgroup_exit(cgroup_ops);
ERROR("Failed to freeze %s", name); ERROR("Failed to freeze %s", name);
return -1; return -1;
} }
for (;;) { for (;;) {
ret = lxc_cgroup_get("freezer.state", v, sizeof(v), name, lxcpath); ret = cgroup_ops->get(cgroup_ops, "freezer.state", v, sizeof(v), name, lxcpath);
if (ret < 0) { if (ret < 0) {
cgroup_exit(cgroup_ops);
ERROR("Failed to get freezer state of %s", name); ERROR("Failed to get freezer state of %s", name);
return -1; return -1;
} }
...@@ -82,6 +96,7 @@ static int do_freeze_thaw(bool freeze, const char *name, const char *lxcpath) ...@@ -82,6 +96,7 @@ static int do_freeze_thaw(bool freeze, const char *name, const char *lxcpath)
ret = strncmp(v, state, state_len); ret = strncmp(v, state, state_len);
if (ret == 0) { if (ret == 0) {
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;
......
...@@ -98,29 +98,6 @@ extern int lxc_unfreeze(const char *name, const char *lxcpath); ...@@ -98,29 +98,6 @@ extern int lxc_unfreeze(const char *name, const char *lxcpath);
extern lxc_state_t lxc_state(const char *name, const char *lxcpath); extern lxc_state_t lxc_state(const char *name, const char *lxcpath);
/* /*
* Set a specified value for a specified subsystem. The specified
* subsystem must be fully specified, eg. "cpu.shares"
* @filename : the cgroup attribute filename
* @value : the value to be set
* @name : the name of the container
* @lxcpath : lxc config path for container
* Returns 0 on success, < 0 otherwise
*/
extern int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath);
/*
* Get a specified value for a specified subsystem. The specified
* subsystem must be fully specified, eg. "cpu.shares"
* @filename : the cgroup attribute filename
* @value : the value to be set
* @len : the len of the value variable
* @name : the name of the container
* @lxcpath : lxc config path for container
* Returns the number of bytes read, < 0 on error
*/
extern int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
/*
* Create and return a new lxccontainer struct. * Create and return a new lxccontainer struct.
*/ */
extern struct lxc_container *lxc_container_new(const char *name, const char *configpath); extern struct lxc_container *lxc_container_new(const char *name, const char *configpath);
......
...@@ -3141,6 +3141,7 @@ WRAP_API_1(bool, lxcapi_set_config_path, const char *) ...@@ -3141,6 +3141,7 @@ WRAP_API_1(bool, lxcapi_set_config_path, const char *)
static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value) static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value)
{ {
int ret; int ret;
struct cgroup_ops *cgroup_ops;
if (!c) if (!c)
return false; return false;
...@@ -3148,12 +3149,19 @@ static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsy ...@@ -3148,12 +3149,19 @@ static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsy
if (is_stopped(c)) if (is_stopped(c))
return false; return false;
cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops)
return false;
if (container_disk_lock(c)) if (container_disk_lock(c))
return false; return false;
ret = lxc_cgroup_set(subsys, value, c->name, c->config_path); ret = cgroup_ops->set(cgroup_ops, subsys, value, c->name, c->config_path);
container_disk_unlock(c); container_disk_unlock(c);
cgroup_exit(cgroup_ops);
return ret == 0; return ret == 0;
} }
...@@ -3162,6 +3170,7 @@ WRAP_API_2(bool, lxcapi_set_cgroup_item, const char *, const char *) ...@@ -3162,6 +3170,7 @@ WRAP_API_2(bool, lxcapi_set_cgroup_item, const char *, const char *)
static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen)
{ {
int ret; int ret;
struct cgroup_ops *cgroup_ops;
if (!c) if (!c)
return -1; return -1;
...@@ -3169,12 +3178,20 @@ static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys ...@@ -3169,12 +3178,20 @@ static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys
if (is_stopped(c)) if (is_stopped(c))
return -1; return -1;
cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops)
return -1;
if (container_disk_lock(c)) if (container_disk_lock(c))
return -1; return -1;
ret = lxc_cgroup_get(subsys, retv, inlen, c->name, c->config_path); ret = cgroup_ops->get(cgroup_ops, subsys, retv, inlen, c->name,
c->config_path);
container_disk_unlock(c); container_disk_unlock(c);
cgroup_exit(cgroup_ops);
return ret; return ret;
} }
......
...@@ -849,6 +849,13 @@ int lxc_init(const char *name, struct lxc_handler *handler) ...@@ -849,6 +849,13 @@ int lxc_init(const char *name, struct lxc_handler *handler)
} }
TRACE("Chowned console"); TRACE("Chowned console");
handler->cgroup_ops = cgroup_init(handler);
if (!handler->cgroup_ops) {
ERROR("Failed to initialize cgroup driver");
goto out_restore_sigmask;
}
TRACE("Initialized cgroup driver");
INFO("Container \"%s\" is initialized", name); INFO("Container \"%s\" is initialized", name);
return 0; return 0;
...@@ -871,6 +878,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler) ...@@ -871,6 +878,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
struct lxc_list *cur, *next; struct lxc_list *cur, *next;
char *namespaces[LXC_NS_MAX + 1]; char *namespaces[LXC_NS_MAX + 1];
size_t namespace_count = 0; size_t namespace_count = 0;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
/* The STOPPING state is there for future cleanup code which can take /* The STOPPING state is there for future cleanup code which can take
* awhile. * awhile.
...@@ -935,7 +943,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler) ...@@ -935,7 +943,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
while (namespace_count--) while (namespace_count--)
free(namespaces[namespace_count]); free(namespaces[namespace_count]);
cgroup_destroy(handler); cgroup_ops->destroy(cgroup_ops, handler);
cgroup_exit(cgroup_ops);
if (handler->conf->reboot == 0) { if (handler->conf->reboot == 0) {
/* For all new state clients simply close the command socket. /* For all new state clients simply close the command socket.
...@@ -1506,8 +1515,9 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1506,8 +1515,9 @@ static int lxc_spawn(struct lxc_handler *handler)
struct lxc_list *id_map; struct lxc_list *id_map;
const char *name = handler->name; const char *name = handler->name;
const char *lxcpath = handler->lxcpath; const char *lxcpath = handler->lxcpath;
bool cgroups_connected = false, share_ns = false; bool share_ns = false;
struct lxc_conf *conf = handler->conf; struct lxc_conf *conf = handler->conf;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
id_map = &conf->id_map; id_map = &conf->id_map;
wants_to_map_ids = !lxc_list_empty(id_map); wants_to_map_ids = !lxc_list_empty(id_map);
...@@ -1567,14 +1577,7 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1567,14 +1577,7 @@ static int lxc_spawn(struct lxc_handler *handler)
} }
} }
if (!cgroup_init(handler)) { if (!cgroup_ops->create(cgroup_ops, handler)) {
ERROR("Failed initializing cgroup support");
goto out_delete_net;
}
cgroups_connected = true;
if (!cgroup_create(handler)) {
ERROR("Failed creating cgroups"); ERROR("Failed creating cgroups");
goto out_delete_net; goto out_delete_net;
} }
...@@ -1663,15 +1666,15 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1663,15 +1666,15 @@ static int lxc_spawn(struct lxc_handler *handler)
if (ret < 0) if (ret < 0)
goto out_delete_net; goto out_delete_net;
if (!cgroup_setup_limits(handler, false)) { if (!cgroup_ops->setup_limits(cgroup_ops, handler->conf, false)) {
ERROR("Failed to setup cgroup limits for container \"%s\"", name); ERROR("Failed to setup cgroup limits for container \"%s\"", name);
goto out_delete_net; goto out_delete_net;
} }
if (!cgroup_enter(handler)) if (!cgroup_ops->enter(cgroup_ops, handler->pid))
goto out_delete_net; goto out_delete_net;
if (!cgroup_chown(handler)) if (!cgroup_ops->chown(cgroup_ops, handler->conf))
goto out_delete_net; goto out_delete_net;
/* Now we're ready to preserve the network namespace */ /* Now we're ready to preserve the network namespace */
...@@ -1736,15 +1739,12 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1736,15 +1739,12 @@ static int lxc_spawn(struct lxc_handler *handler)
if (ret < 0) if (ret < 0)
goto out_delete_net; goto out_delete_net;
if (!cgroup_setup_limits(handler, true)) { if (!cgroup_ops->setup_limits(cgroup_ops, handler->conf, true)) {
ERROR("Failed to setup legacy device cgroup controller limits"); ERROR("Failed to setup legacy device cgroup controller limits");
goto out_delete_net; goto out_delete_net;
} }
TRACE("Set up legacy device cgroup controller limits"); TRACE("Set up legacy device cgroup controller limits");
cgroup_disconnect();
cgroups_connected = false;
if (handler->ns_clone_flags & CLONE_NEWCGROUP) { if (handler->ns_clone_flags & CLONE_NEWCGROUP) {
/* Now we're ready to preserve the cgroup namespace */ /* Now we're ready to preserve the cgroup namespace */
ret = lxc_try_preserve_ns(handler->pid, "cgroup"); ret = lxc_try_preserve_ns(handler->pid, "cgroup");
...@@ -1821,9 +1821,6 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1821,9 +1821,6 @@ static int lxc_spawn(struct lxc_handler *handler)
return 0; return 0;
out_delete_net: out_delete_net:
if (cgroups_connected)
cgroup_disconnect();
if (handler->ns_clone_flags & CLONE_NEWNET) if (handler->ns_clone_flags & CLONE_NEWNET)
lxc_delete_network(handler); lxc_delete_network(handler);
......
...@@ -132,6 +132,8 @@ struct lxc_handler { ...@@ -132,6 +132,8 @@ struct lxc_handler {
* true. * true.
*/ */
int exit_status; int exit_status;
struct cgroup_ops *cgroup_ops;
}; };
struct execute_args { struct execute_args {
......
...@@ -53,6 +53,7 @@ static int test_running_container(const char *lxcpath, ...@@ -53,6 +53,7 @@ static int test_running_container(const char *lxcpath,
char *cgrelpath; char *cgrelpath;
char relpath[PATH_MAX+1]; char relpath[PATH_MAX+1];
char value[NAME_MAX], value_save[NAME_MAX]; char value[NAME_MAX], value_save[NAME_MAX];
struct cgroup_ops *cgroup_ops;
sprintf(relpath, "%s/%s", group ? group : "lxc", name); sprintf(relpath, "%s/%s", group ? group : "lxc", name);
...@@ -75,36 +76,41 @@ static int test_running_container(const char *lxcpath, ...@@ -75,36 +76,41 @@ static int test_running_container(const char *lxcpath,
goto err3; goto err3;
} }
cgroup_ops = cgroup_init(NULL);
if (!cgroup_ops)
goto err3;
/* test get/set value using memory.soft_limit_in_bytes file */ /* test get/set value using memory.soft_limit_in_bytes file */
ret = lxc_cgroup_get("memory.soft_limit_in_bytes", value, sizeof(value), ret = cgroup_ops->get(cgroup_ops, "memory.soft_limit_in_bytes", value,
c->name, c->config_path); sizeof(value), c->name, c->config_path);
if (ret < 0) { if (ret < 0) {
TSTERR("lxc_cgroup_get failed"); TSTERR("cgroup_get failed");
goto err3; goto err3;
} }
strcpy(value_save, value); strcpy(value_save, value);
ret = lxc_cgroup_set("memory.soft_limit_in_bytes", "512M", c->name, c->config_path); ret = cgroup_ops->set(cgroup_ops, "memory.soft_limit_in_bytes", "512M",
c->name, c->config_path);
if (ret < 0) { if (ret < 0) {
TSTERR("lxc_cgroup_set failed %d %d", ret, errno); TSTERR("cgroup_set failed %d %d", ret, errno);
goto err3; goto err3;
} }
ret = lxc_cgroup_get("memory.soft_limit_in_bytes", value, sizeof(value), ret = cgroup_ops->get(cgroup_ops, "memory.soft_limit_in_bytes", value,
c->name, c->config_path); sizeof(value), c->name, c->config_path);
if (ret < 0) { if (ret < 0) {
TSTERR("lxc_cgroup_get failed"); TSTERR("cgroup_get failed");
goto err3; goto err3;
} }
if (strcmp(value, "536870912\n")) { if (strcmp(value, "536870912\n")) {
TSTERR("lxc_cgroup_set_bypath failed to set value >%s<", value); TSTERR("cgroup_set_bypath failed to set value >%s<", value);
goto err3; goto err3;
} }
/* restore original value */ /* restore original value */
ret = lxc_cgroup_set("memory.soft_limit_in_bytes", value_save, ret = cgroup_ops->set(cgroup_ops, "memory.soft_limit_in_bytes",
c->name, c->config_path); value_save, c->name, c->config_path);
if (ret < 0) { if (ret < 0) {
TSTERR("lxc_cgroup_set failed"); TSTERR("cgroup_set failed");
goto err3; goto err3;
} }
......
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