Skip to content

Commit

Permalink
cgroup: add support for unified cgroup v2
Browse files Browse the repository at this point in the history
add support for: opencontainers/runtime-spec#1040

Signed-off-by: Giuseppe Scrivano <[email protected]>
  • Loading branch information
giuseppe committed Aug 19, 2020
1 parent 3fb312b commit 6232ab8
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 8 deletions.
83 changes: 82 additions & 1 deletion src/libcrun/cgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ write_controller_file (const char *path, int controllers_to_enable, libcrun_erro
libcrun_error_t tmp_err = NULL;
int exists;

exists = crun_path_exists (subtree_control, err);
exists = crun_path_exists (subtree_control, &tmp_err);
if (UNLIKELY (exists < 0))
{
crun_error_release (&tmp_err);
Expand Down Expand Up @@ -2755,6 +2755,79 @@ update_cgroup_v1_resources (runtime_spec_schema_config_linux_resources *resource
return 0;
}

static int
write_unified_resources (int cgroup_dirfd, runtime_spec_schema_config_linux_resources *resources, libcrun_error_t *err)
{
size_t i;
int ret;

for (i = 0; i < resources->unified->len; i++)
{
size_t len;

if (strchr (resources->unified->keys[i], '/'))
return crun_make_error (err, 0, "key `%s` must be a file name without any slash", resources->unified->keys[i]);

len = strlen (resources->unified->values[i]);
ret = write_file_at (cgroup_dirfd, resources->unified->keys[i], resources->unified->values[i], len, err);
if (UNLIKELY (ret < 0))
{
errno = crun_error_get_errno (err);

/* If the file is not found, try to give a more meaningful error message. */
if (errno == ENOENT || errno == EPERM || errno == EACCES)
{
cleanup_free char *controllers = NULL;
libcrun_error_t tmp_err = NULL;
cleanup_free char *key = NULL;
char *saveptr = NULL;
bool found = false;
const char *token;
char *it;

/* Check if the specified controller is enabled. */
key = xstrdup (resources->unified->keys[i]);

it = strchr (key, '.');
if (it == NULL)
{
crun_error_release (err);
return crun_make_error (err, 0, "the specified key has not the form CONTROLLER.VALUE `%s`", resources->unified->keys[i]);
}
*it = '\0';

/* cgroup. files are not part of a controller. Return the original error. */
if (strcmp (key, "cgroup") == 0)
return ret;

/* If the cgroup.controllers file cannot be read, return the original error. */
if (read_all_file_at (cgroup_dirfd, "cgroup.controllers", &controllers, NULL, &tmp_err) < 0)
{
crun_error_release (&tmp_err);
return ret;
}
for (token = strtok_r (controllers, " \n", &saveptr); token; token = strtok_r (NULL, " \n", &saveptr))
{
if (strcmp (token, key) == 0)
{
found = true;
break;
}
}
if (! found)
{
crun_error_release (err);
return crun_make_error (err, 0, "the requested controller `%s` is not available", key);
}
}
return ret;
}
}

return 0;
}


static int
update_cgroup_v2_resources (runtime_spec_schema_config_linux_resources *resources, char *path, libcrun_error_t *err)
{
Expand Down Expand Up @@ -2828,6 +2901,14 @@ update_cgroup_v2_resources (runtime_spec_schema_config_linux_resources *resource
return ret;
}

/* Write unified resources if any. They have higher precedence and override any previous setting. */
if (resources->unified)
{
ret = write_unified_resources (cgroup_dirfd, resources, err);
if (UNLIKELY (ret < 0))
return ret;
}

return 0;
}

Expand Down
18 changes: 13 additions & 5 deletions src/libcrun/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -746,20 +746,28 @@ read_all_fd (int fd, const char *description, char **out, size_t *len, libcrun_e
}

int
read_all_file (const char *path, char **out, size_t *len, libcrun_error_t *err)
read_all_file_at (int dirfd, const char *path, char **out, size_t *len, libcrun_error_t *err)
{
cleanup_close int fd;

if (strcmp (path, "-") == 0)
path = "/dev/stdin";

fd = TEMP_FAILURE_RETRY (open (path, O_RDONLY));
fd = TEMP_FAILURE_RETRY (openat (dirfd, path, O_RDONLY));
if (UNLIKELY (fd < 0))
return crun_make_error (err, errno, "error opening file `%s`", path);

return read_all_fd (fd, path, out, len, err);
}

int
read_all_file (const char *path, char **out, size_t *len, libcrun_error_t *err)
{
cleanup_close int fd = -1;

if (strcmp (path, "-") == 0)
path = "/dev/stdin";

return read_all_file_at (AT_FDCWD, path, out, len, err);
}

int
open_unix_domain_client_socket (const char *path, int dgram, libcrun_error_t *err)
{
Expand Down
2 changes: 2 additions & 0 deletions src/libcrun/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ int read_all_fd (int fd, const char *description, char **out, size_t *len, libcr

int read_all_file (const char *path, char **out, size_t *len, libcrun_error_t *err);

int read_all_file_at (int dirfd, const char *path, char **out, size_t *len, libcrun_error_t *err);

int open_unix_domain_client_socket (const char *path, int dgram, libcrun_error_t *err);

int open_unix_domain_socket (const char *path, int dgram, libcrun_error_t *err);
Expand Down
84 changes: 84 additions & 0 deletions tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
import sys
from tests_utils import *


def is_cgroup_v2_unified():
return subprocess.check_output("stat -c%T -f /sys/fs/cgroup".split()).decode("utf-8").strip() == "cgroup2fs"

def test_resources_pid_limit():
if os.getuid() != 0:
return 77
Expand All @@ -42,8 +46,88 @@ def test_resources_pid_limit():
return -1
return 0

def test_resources_unified_invalid_controller():
if not is_cgroup_v2_unified() or os.geteuid() != 0:
return 77

conf = base_config()
add_all_namespaces(conf, cgroupns=True)
conf['process']['args'] = ['/init', 'pause']

conf['linux']['resources'] = {}
conf['linux']['resources']['unified'] = {
"foo.bar": "doesntmatter"
}
cid = None
try:
out, cid = run_and_get_output(conf, command='run', detach=True)
# must raise an exception, fail if it doesn't.
return -1
except Exception as e:
if 'the requested controller `foo` is not available' in e.stdout.decode("utf-8").strip():
return 0
return -1
finally:
if cid is not None:
run_crun_command(["delete", "-f", cid])
return 0

def test_resources_unified_invalid_key():
if not is_cgroup_v2_unified() or os.geteuid() != 0:
return 77

conf = base_config()
add_all_namespaces(conf, cgroupns=True)
conf['process']['args'] = ['/init', 'pause']

conf['linux']['resources'] = {}
conf['linux']['resources']['unified'] = {
"NOT-A-VALID-KEY": "doesntmatter"
}
cid = None
try:
out, cid = run_and_get_output(conf, command='run', detach=True)
# must raise an exception, fail if it doesn't.
return -1
except Exception as e:
if 'the specified key has not the form CONTROLLER.VALUE `NOT-A-VALID-KEY`' in e.stdout.decode("utf-8").strip():
return 0
return -1
finally:
if cid is not None:
run_crun_command(["delete", "-f", cid])
return 0

def test_resources_unified():
if not is_cgroup_v2_unified() or os.geteuid() != 0:
return 77

conf = base_config()
add_all_namespaces(conf, cgroupns=True)
conf['process']['args'] = ['/init', 'pause']

conf['linux']['resources'] = {}
conf['linux']['resources']['unified'] = {
"memory.high": "1073741824"
}
cid = None
try:
_, cid = run_and_get_output(conf, command='run', detach=True)
out = run_crun_command(["exec", cid, "/init", "cat", "/sys/fs/cgroup/memory.high"])
if "1073741824" not in out:
return -1
finally:
if cid is not None:
run_crun_command(["delete", "-f", cid])
return 0



all_tests = {
"resources-pid-limit" : test_resources_pid_limit,
"resources-unified" : test_resources_unified,
"resources-unified-invalid-controller" : test_resources_unified_invalid_controller,
"resources-unified-invalid-key" : test_resources_unified_invalid_key,
}

if __name__ == "__main__":
Expand Down
7 changes: 5 additions & 2 deletions tests/tests_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,14 @@ def parse_proc_status(content):
r[k] = v.strip()
return r

def add_all_namespaces(conf):
def add_all_namespaces(conf, cgroupns=False):
has = {}
for i in conf['linux']['namespaces']:
has[i['type']] = i['type']
for i in ['pid', 'user', 'ipc', 'uts', 'network']:
namespaces = ['pid', 'user', 'ipc', 'uts', 'network']
if cgroupns:
namespaces = namespaces + ["cgroup"]
for i in namespaces:
if i not in has:
conf['linux']['namespaces'].append({"type" : i})

Expand Down

0 comments on commit 6232ab8

Please sign in to comment.