commands: add LXC_CMD_GET_CGROUP_FD and LXC_CMD_GET_LIMIT_CGROUP_FD

and port cgroup_get() and cgroup_set(). This means no more useless cgroup driver initialization on ever get or set. Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 5d8caeb9
...@@ -104,7 +104,7 @@ static bool string_in_list(char **list, const char *entry) ...@@ -104,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 /* Given a handler's cgroup data, return the struct hierarchy for the controller
* @c, or NULL if there is none. * @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) if (!ops->hierarchies)
return log_trace_errno(NULL, errno, "There are no useable cgroup controllers"); return log_trace_errno(NULL, errno, "There are no useable cgroup controllers");
...@@ -148,6 +148,38 @@ static struct hierarchy *get_hierarchy(struct cgroup_ops *ops, const char *contr ...@@ -148,6 +148,38 @@ static struct hierarchy *get_hierarchy(struct cgroup_ops *ops, const char *contr
return ret_set_errno(NULL, ENOENT); 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. */ /* Taken over modified from the kernel sources. */
#define NBITS 32 /* bits in uint32_t */ #define NBITS 32 /* bits in uint32_t */
#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) #define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
...@@ -3442,55 +3474,105 @@ int cgroup_attach(const struct lxc_conf *conf, const char *name, ...@@ -3442,55 +3474,105 @@ int cgroup_attach(const struct lxc_conf *conf, const char *name,
} }
/* Connects to command socket therefore isn't callable from command handler. */ /* Connects to command socket therefore isn't callable from command handler. */
int cgroup_get(const char *name, const char *lxcpath, int cgroup_get(const char *name, const char *lxcpath, const char *key, char *buf, size_t len)
const char *filename, char *buf, size_t len)
{ {
__do_close int unified_fd = -EBADF; __do_close int dfd = -EBADF;
ssize_t ret; struct cgroup_fd fd = {
.fd = -EBADF,
};
size_t len_controller;
int ret;
if (is_empty_string(filename) || is_empty_string(name) || if (is_empty_string(name) || is_empty_string(lxcpath) ||
is_empty_string(lxcpath)) is_empty_string(key))
return ret_errno(EINVAL); return ret_errno(EINVAL);
if ((buf && !len) || (len && !buf)) if ((buf && !len) || (len && !buf))
return ret_errno(EINVAL); return ret_errno(EINVAL);
unified_fd = lxc_cmd_get_limit_cgroup2_fd(name, lxcpath); len_controller = strcspn(key, ".");
if (unified_fd < 0) len_controller++; /* Don't forget the \0 byte. */
return ret_errno(ENOSYS); 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); ret = lxc_cmd_get_limit_cgroup_fd(name, lxcpath, sizeof(struct cgroup_fd), &fd);
if (ret < 0) if (ret < 0) {
SYSERROR("Failed to read cgroup value"); 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; return ret;
} }
/* Connects to command socket therefore isn't callable from command handler. */ /* Connects to command socket therefore isn't callable from command handler. */
int cgroup_set(const char *name, const char *lxcpath, int cgroup_set(const char *name, const char *lxcpath, const char *key, const char *value)
const char *filename, const char *value)
{ {
__do_close int unified_fd = -EBADF; __do_close int dfd = -EBADF;
ssize_t ret; struct cgroup_fd fd = {
.fd = -EBADF,
};
size_t len_controller;
int ret;
if (is_empty_string(filename) || is_empty_string(value) || if (is_empty_string(name) || is_empty_string(lxcpath) ||
is_empty_string(name) || is_empty_string(lxcpath)) is_empty_string(key) || is_empty_string(value))
return ret_errno(EINVAL); return ret_errno(EINVAL);
unified_fd = lxc_cmd_get_limit_cgroup2_fd(name, lxcpath); len_controller = strcspn(key, ".");
if (unified_fd < 0) len_controller++; /* Don't forget the \0 byte. */
return ret_errno(ENOSYS); 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 = {}; struct device_item device = {};
ret = device_cgroup_rule_parse(&device, filename, value); ret = device_cgroup_rule_parse(&device, key, value);
if (ret < 0) 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); ret = lxc_cmd_add_bpf_device_cgroup(name, lxcpath, &device);
} else { } else {
ret = lxc_writeat(unified_fd, filename, value, strlen(value)); ret = lxc_writeat(dfd, key, value, strlen(value));
} }
return ret; return ret;
......
...@@ -52,16 +52,41 @@ static inline const char *cgroup_layout_name(cgroup_layout_t layout) ...@@ -52,16 +52,41 @@ static inline const char *cgroup_layout_name(cgroup_layout_t layout)
} }
typedef enum { typedef enum {
LEGACY_HIERARCHY = CGROUP_SUPER_MAGIC, LEGACY_HIERARCHY = CGROUP_SUPER_MAGIC,
UNIFIED_HIERARCHY = CGROUP2_SUPER_MAGIC, UNIFIED_HIERARCHY = CGROUP2_SUPER_MAGIC,
} cgroupfs_type_magic_t; } 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 DEVICES_CONTROLLER (1U << 0)
#define FREEZER_CONTROLLER (1U << 1) #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. */ /* That's plenty of hierarchies. */
#define CGROUP_CTX_MAX_FD 20 #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 { struct cgroup_ctx {
__s32 layout; __s32 layout;
...@@ -259,9 +284,9 @@ define_cleanup_function(struct cgroup_ops *, cgroup_exit); ...@@ -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, __hidden extern int cgroup_attach(const struct lxc_conf *conf, const char *name,
const char *lxcpath, pid_t pid); const char *lxcpath, pid_t pid);
__hidden extern int cgroup_get(const char *name, const char *lxcpath, __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, __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_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(const char *name, const char *lxcpath, int timeout);
__hidden extern int __cgroup_unfreeze(int unified_fd, int timeout); __hidden extern int __cgroup_unfreeze(int unified_fd, int timeout);
...@@ -317,5 +342,7 @@ static inline int prepare_cgroup_ctx(struct cgroup_ops *ops, ...@@ -317,5 +342,7 @@ static inline int prepare_cgroup_ctx(struct cgroup_ops *ops,
return 0; return 0;
} }
__hidden extern int prepare_cgroup_fd(const struct cgroup_ops *ops,
struct cgroup_fd *fd, bool limit);
#endif /* __LXC_CGROUP_H */ #endif /* __LXC_CGROUP_H */
...@@ -140,7 +140,8 @@ __hidden extern int lxc_cmd_freeze(const char *name, const char *lxcpath, int ti ...@@ -140,7 +140,8 @@ __hidden extern int lxc_cmd_freeze(const char *name, const char *lxcpath, int ti
__hidden extern int lxc_cmd_unfreeze(const char *name, const char *lxcpath, int timeout); __hidden extern int lxc_cmd_unfreeze(const char *name, const char *lxcpath, int timeout);
__hidden extern int lxc_cmd_get_cgroup2_fd(const char *name, const char *lxcpath); __hidden extern int lxc_cmd_get_cgroup2_fd(const char *name, const char *lxcpath);
__hidden extern int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath, __hidden extern int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath,
const char *controller, cgroupfs_type_magic_t type); size_t size_ret_fd,
struct cgroup_fd *ret_fd);
__hidden extern char *lxc_cmd_get_limit_cgroup_path(const char *name, __hidden extern char *lxc_cmd_get_limit_cgroup_path(const char *name,
const char *lxcpath, const char *lxcpath,
const char *subsystem); const char *subsystem);
...@@ -148,8 +149,8 @@ __hidden extern int lxc_cmd_get_limit_cgroup2_fd(const char *name, ...@@ -148,8 +149,8 @@ __hidden extern int lxc_cmd_get_limit_cgroup2_fd(const char *name,
const char *lxcpath); const char *lxcpath);
__hidden extern int lxc_cmd_get_limit_cgroup_fd(const char *name, __hidden extern int lxc_cmd_get_limit_cgroup_fd(const char *name,
const char *lxcpath, const char *lxcpath,
const char *controller, size_t size_ret_fd,
cgroupfs_type_magic_t type); struct cgroup_fd *ret_fd);
__hidden extern int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath); __hidden extern int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath);
#endif /* __commands_h */ #endif /* __commands_h */
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