Skip to content

Commit

Permalink
Merge pull request #3694 from brauner/2021-02-24/fixes_2
Browse files Browse the repository at this point in the history
commands: rework and add LXC_CMD_GET_CGROUP_FD and LXC_CMD_GET_LIMIT_CGROUP_FD
  • Loading branch information
stgraber committed Feb 24, 2021
2 parents 5dc90af + 7e85a2c commit 2ed9052
Show file tree
Hide file tree
Showing 13 changed files with 564 additions and 202 deletions.
2 changes: 2 additions & 0 deletions src/lxc/Makefile.am
Expand Up @@ -18,6 +18,7 @@ noinst_HEADERS = api_extensions.h \
confile_utils.h \
criu.h \
error.h \
error_utils.h \
file_utils.h \
../include/netns_ifaddrs.h \
initutils.h \
Expand Down Expand Up @@ -117,6 +118,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \
criu.c criu.h \
error.c error.h \
execute.c \
error_utils.h \
freezer.c \
file_utils.c file_utils.h \
../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \
Expand Down
2 changes: 1 addition & 1 deletion src/lxc/attach.c
Expand Up @@ -1641,7 +1641,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
ret = cgroup_attach(conf, name, lxcpath, pid);
if (ret) {
call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
if (ret != -ENOCGROUP2 && ret != -ENOSYS) {
if (!ERRNO_IS_NOT_SUPPORTED(ret)) {
SYSERROR("Failed to attach cgroup");
goto on_error;
}
Expand Down
162 changes: 123 additions & 39 deletions src/lxc/cgroups/cgfsng.c
Expand Up @@ -40,6 +40,7 @@
#include "commands_utils.h"
#include "conf.h"
#include "config.h"
#include "error_utils.h"
#include "log.h"
#include "macro.h"
#include "mainloop.h"
Expand Down Expand Up @@ -103,7 +104,7 @@ static bool string_in_list(char **list, const char *entry)
/* Given a handler's cgroup data, return the struct hierarchy for the controller
* @c, or NULL if there is none.
*/
static struct hierarchy *get_hierarchy(struct cgroup_ops *ops, const char *controller)
static struct hierarchy *get_hierarchy(const struct cgroup_ops *ops, const char *controller)
{
if (!ops->hierarchies)
return log_trace_errno(NULL, errno, "There are no useable cgroup controllers");
Expand Down Expand Up @@ -147,6 +148,38 @@ static struct hierarchy *get_hierarchy(struct cgroup_ops *ops, const char *contr
return ret_set_errno(NULL, ENOENT);
}

int prepare_cgroup_fd(const struct cgroup_ops *ops, struct cgroup_fd *fd, bool limit)
{
int dfd;
const struct hierarchy *h;

h = get_hierarchy(ops, fd->controller);
if (!h)
return ret_errno(ENOENT);

/*
* The client requested that the controller must be in a specific
* cgroup version.
*/
if (fd->type != 0 && fd->type != h->fs_type)
return ret_errno(EINVAL);

if (limit)
dfd = h->dfd_con;
else
dfd = h->dfd_lim;
if (dfd < 0)
return ret_errno(EBADF);

fd->layout = ops->cgroup_layout;
fd->type = h->fs_type;
if (fd->type == UNIFIED_HIERARCHY)
fd->utilities = h->utilities;
fd->fd = dfd;

return 0;
}

/* Taken over modified from the kernel sources. */
#define NBITS 32 /* bits in uint32_t */
#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
Expand Down Expand Up @@ -2057,8 +2090,8 @@ __cgfsng_ops static const char *cgfsng_get_cgroup(struct cgroup_ops *ops,
return cgfsng_get_cgroup_do(ops, controller, false);
}

__cgfsng_ops static const char *cgfsng_get_limiting_cgroup(struct cgroup_ops *ops,
const char *controller)
__cgfsng_ops static const char *cgfsng_get_limit_cgroup(struct cgroup_ops *ops,
const char *controller)
{
return cgfsng_get_cgroup_do(ops, controller, true);
}
Expand Down Expand Up @@ -2369,7 +2402,7 @@ __cgfsng_ops static int cgfsng_get(struct cgroup_ops *ops, const char *filename,
if (p)
*p = '\0';

path = lxc_cmd_get_limiting_cgroup_path(name, lxcpath, controller);
path = lxc_cmd_get_limit_cgroup_path(name, lxcpath, controller);
/* not running */
if (!path)
return -1;
Expand Down Expand Up @@ -2531,7 +2564,7 @@ __cgfsng_ops static int cgfsng_set(struct cgroup_ops *ops,
return 0;
}

path = lxc_cmd_get_limiting_cgroup_path(name, lxcpath, controller);
path = lxc_cmd_get_limit_cgroup_path(name, lxcpath, controller);
/* not running */
if (!path)
return -1;
Expand Down Expand Up @@ -3336,7 +3369,7 @@ struct cgroup_ops *cgroup_ops_init(struct lxc_conf *conf)
cgfsng_ops->chown = cgfsng_chown;
cgfsng_ops->mount = cgfsng_mount;
cgfsng_ops->devices_activate = cgfsng_devices_activate;
cgfsng_ops->get_limiting_cgroup = cgfsng_get_limiting_cgroup;
cgfsng_ops->get_limit_cgroup = cgfsng_get_limit_cgroup;

cgfsng_ops->criu_escape = cgfsng_criu_escape;
cgfsng_ops->criu_num_hierarchies = cgfsng_criu_num_hierarchies;
Expand Down Expand Up @@ -3377,12 +3410,11 @@ static int __cgroup_attach_many(const struct lxc_conf *conf, const char *name,
{
call_cleaner(put_cgroup_ctx) struct cgroup_ctx *ctx = &(struct cgroup_ctx){};
int ret;
char pidstr[INTTYPE_TO_STRLEN(pid_t)];
size_t idx;
ssize_t pidstr_len;
char pidstr[INTTYPE_TO_STRLEN(pid_t)];

ret = lxc_cmd_get_cgroup_ctx(name, lxcpath, NULL, true,
sizeof(struct cgroup_ctx), ctx);
ret = lxc_cmd_get_cgroup_ctx(name, lxcpath, sizeof(struct cgroup_ctx), ctx);
if (ret < 0)
return ret_errno(ENOSYS);

Expand All @@ -3406,7 +3438,7 @@ static int __cgroup_attach_many(const struct lxc_conf *conf, const char *name,
if (idx == 0)
return syserrno_set(-ENOENT, "Failed to attach to cgroups");

TRACE("Attached to %s cgroup layout", cgroup_layout_name(ctx->cgroup_layout));
TRACE("Attached to %s cgroup layout", cgroup_layout_name(ctx->layout));
return 0;
}

Expand All @@ -3420,7 +3452,7 @@ static int __cgroup_attach_unified(const struct lxc_conf *conf, const char *name

dfd_unified = lxc_cmd_get_cgroup2_fd(name, lxcpath);
if (dfd_unified < 0)
return ret_errno(ENOCGROUP2);
return ret_errno(ENOSYS);

return __unified_attach_fd(conf, dfd_unified, pid);
}
Expand All @@ -3432,65 +3464,117 @@ int cgroup_attach(const struct lxc_conf *conf, const char *name,

ret = __cgroup_attach_many(conf, name, lxcpath, pid);
if (ret < 0) {
if (ret != -ENOSYS)
if (!ERRNO_IS_NOT_SUPPORTED(ret))
return ret;

ret = __cgroup_attach_unified(conf, name, lxcpath, pid);
if (ret < 0 && ERRNO_IS_NOT_SUPPORTED(ret))
return ret_errno(ENOSYS);
}

return ret;
}

/* Connects to command socket therefore isn't callable from command handler. */
int cgroup_get(const char *name, const char *lxcpath,
const char *filename, char *buf, size_t len)
int cgroup_get(const char *name, const char *lxcpath, const char *key, char *buf, size_t len)
{
__do_close int unified_fd = -EBADF;
ssize_t ret;
__do_close int dfd = -EBADF;
struct cgroup_fd fd = {
.fd = -EBADF,
};
size_t len_controller;
int ret;

if (is_empty_string(filename) || is_empty_string(name) ||
is_empty_string(lxcpath))
if (is_empty_string(name) || is_empty_string(lxcpath) ||
is_empty_string(key))
return ret_errno(EINVAL);

if ((buf && !len) || (len && !buf))
return ret_errno(EINVAL);

unified_fd = lxc_cmd_get_limiting_cgroup2_fd(name, lxcpath);
if (unified_fd < 0)
return ret_errno(ENOCGROUP2);
len_controller = strcspn(key, ".");
len_controller++; /* Don't forget the \0 byte. */
if (len_controller >= MAX_CGROUP_ROOT_NAMELEN)
return ret_errno(EINVAL);
(void)strlcpy(fd.controller, key, len_controller);

ret = lxc_read_try_buf_at(unified_fd, filename, buf, len);
if (ret < 0)
SYSERROR("Failed to read cgroup value");
ret = lxc_cmd_get_limit_cgroup_fd(name, lxcpath, sizeof(struct cgroup_fd), &fd);
if (ret < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(ret))
return ret;

dfd = lxc_cmd_get_limit_cgroup2_fd(name, lxcpath);
if (dfd < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(ret))
return ret;

return ret_errno(ENOSYS);
}
fd.type = UNIFIED_HIERARCHY;
fd.fd = move_fd(dfd);
}
dfd = move_fd(fd.fd);

TRACE("Reading %s from %s cgroup hierarchy", key, cgroup_hierarchy_name(fd.type));

if (fd.type == UNIFIED_HIERARCHY && strequal(fd.controller, "devices"))
return ret_errno(EOPNOTSUPP);
else
ret = lxc_read_try_buf_at(dfd, key, buf, len);

return ret;
}

/* Connects to command socket therefore isn't callable from command handler. */
int cgroup_set(const char *name, const char *lxcpath,
const char *filename, const char *value)
int cgroup_set(const char *name, const char *lxcpath, const char *key, const char *value)
{
__do_close int unified_fd = -EBADF;
ssize_t ret;
__do_close int dfd = -EBADF;
struct cgroup_fd fd = {
.fd = -EBADF,
};
size_t len_controller;
int ret;

if (is_empty_string(filename) || is_empty_string(value) ||
is_empty_string(name) || is_empty_string(lxcpath))
if (is_empty_string(name) || is_empty_string(lxcpath) ||
is_empty_string(key) || is_empty_string(value))
return ret_errno(EINVAL);

unified_fd = lxc_cmd_get_limiting_cgroup2_fd(name, lxcpath);
if (unified_fd < 0)
return ret_errno(ENOCGROUP2);
len_controller = strcspn(key, ".");
len_controller++; /* Don't forget the \0 byte. */
if (len_controller >= MAX_CGROUP_ROOT_NAMELEN)
return ret_errno(EINVAL);
(void)strlcpy(fd.controller, key, len_controller);

ret = lxc_cmd_get_limit_cgroup_fd(name, lxcpath, sizeof(struct cgroup_fd), &fd);
if (ret < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(ret))
return ret;

dfd = lxc_cmd_get_limit_cgroup2_fd(name, lxcpath);
if (dfd < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(ret))
return ret;

if (strnequal(filename, "devices.", STRLITERALLEN("devices."))) {
return ret_errno(ENOSYS);
}
fd.type = UNIFIED_HIERARCHY;
fd.fd = move_fd(dfd);
}
dfd = move_fd(fd.fd);

TRACE("Setting %s to %s in %s cgroup hierarchy", key, value, cgroup_hierarchy_name(fd.type));

if (fd.type == UNIFIED_HIERARCHY && strequal(fd.controller, "devices")) {
struct device_item device = {};

ret = device_cgroup_rule_parse(&device, filename, value);
ret = device_cgroup_rule_parse(&device, key, value);
if (ret < 0)
return log_error_errno(-1, EINVAL, "Failed to parse device string %s=%s", filename, value);
return log_error_errno(-1, EINVAL, "Failed to parse device string %s=%s",
key, value);

ret = lxc_cmd_add_bpf_device_cgroup(name, lxcpath, &device);
} else {
ret = lxc_writeat(unified_fd, filename, value, strlen(value));
ret = lxc_writeat(dfd, key, value, strlen(value));
}

return ret;
Expand Down Expand Up @@ -3553,7 +3637,7 @@ int cgroup_freeze(const char *name, const char *lxcpath, int timeout)
if (is_empty_string(name) || is_empty_string(lxcpath))
return ret_errno(EINVAL);

unified_fd = lxc_cmd_get_limiting_cgroup2_fd(name, lxcpath);
unified_fd = lxc_cmd_get_limit_cgroup2_fd(name, lxcpath);
if (unified_fd < 0)
return ret_errno(ENOCGROUP2);

Expand All @@ -3578,7 +3662,7 @@ int cgroup_unfreeze(const char *name, const char *lxcpath, int timeout)
if (is_empty_string(name) || is_empty_string(lxcpath))
return ret_errno(EINVAL);

unified_fd = lxc_cmd_get_limiting_cgroup2_fd(name, lxcpath);
unified_fd = lxc_cmd_get_limit_cgroup2_fd(name, lxcpath);
if (unified_fd < 0)
return ret_errno(ENOCGROUP2);

Expand Down
41 changes: 34 additions & 7 deletions src/lxc/cgroups/cgroup.h
Expand Up @@ -52,19 +52,44 @@ static inline const char *cgroup_layout_name(cgroup_layout_t layout)
}

typedef enum {
LEGACY_HIERARCHY = CGROUP_SUPER_MAGIC,
LEGACY_HIERARCHY = CGROUP_SUPER_MAGIC,
UNIFIED_HIERARCHY = CGROUP2_SUPER_MAGIC,
} cgroupfs_type_magic_t;

static inline const char *cgroup_hierarchy_name(cgroupfs_type_magic_t type)
{
switch (type) {
case LEGACY_HIERARCHY:
return "legacy";
case UNIFIED_HIERARCHY:
return "unified";
}

return "unknown";
}

#define DEVICES_CONTROLLER (1U << 0)
#define FREEZER_CONTROLLER (1U << 1)

/*
* This is the maximum length of a cgroup controller in the kernel.
* This includes the \0 byte.
*/
#define MAX_CGROUP_ROOT_NAMELEN 64

/* That's plenty of hierarchies. */
#define CGROUP_CTX_MAX_FD 20
// BUILD_BUG_ON(CGROUP_CTX_MAX_FD > KERNEL_SCM_MAX_FD);

struct cgroup_fd {
__s32 layout;
__u32 utilities;
__s32 type;
__s32 fd;
char controller[MAX_CGROUP_ROOT_NAMELEN];
} __attribute__((aligned(8)));

struct cgroup_ctx {
__s32 cgroup_layout;
__s32 layout;
__u32 utilities;
__u32 fd_len;
__s32 fd[CGROUP_CTX_MAX_FD];
Expand Down Expand Up @@ -248,7 +273,7 @@ struct cgroup_ops {
bool (*monitor_delegate_controllers)(struct cgroup_ops *ops);
bool (*payload_delegate_controllers)(struct cgroup_ops *ops);
void (*finalize)(struct cgroup_ops *ops);
const char *(*get_limiting_cgroup)(struct cgroup_ops *ops, const char *controller);
const char *(*get_limit_cgroup)(struct cgroup_ops *ops, const char *controller);
};

__hidden extern struct cgroup_ops *cgroup_init(struct lxc_conf *conf);
Expand All @@ -259,9 +284,9 @@ define_cleanup_function(struct cgroup_ops *, cgroup_exit);
__hidden extern int cgroup_attach(const struct lxc_conf *conf, const char *name,
const char *lxcpath, pid_t pid);
__hidden extern int cgroup_get(const char *name, const char *lxcpath,
const char *filename, char *buf, size_t len);
const char *key, char *buf, size_t len);
__hidden extern int cgroup_set(const char *name, const char *lxcpath,
const char *filename, const char *value);
const char *key, const char *value);
__hidden extern int cgroup_freeze(const char *name, const char *lxcpath, int timeout);
__hidden extern int cgroup_unfreeze(const char *name, const char *lxcpath, int timeout);
__hidden extern int __cgroup_unfreeze(int unified_fd, int timeout);
Expand Down Expand Up @@ -311,11 +336,13 @@ static inline int prepare_cgroup_ctx(struct cgroup_ops *ops,
return ret_errno(ENOENT);

ctx->fd_len = idx;
ctx->cgroup_layout = ops->cgroup_layout;
ctx->layout = ops->cgroup_layout;
if (ops->unified && ops->unified->dfd_con > 0)
ctx->utilities = ops->unified->utilities;

return 0;
}
__hidden extern int prepare_cgroup_fd(const struct cgroup_ops *ops,
struct cgroup_fd *fd, bool limit);

#endif /* __LXC_CGROUP_H */

0 comments on commit 2ed9052

Please sign in to comment.