diff --git a/Makefile.am b/Makefile.am index b532a0037c..e10ff5cfac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,7 @@ libcrun_SOURCES = src/libcrun/utils.c \ src/libcrun/handlers/wasmedge.c \ src/libcrun/handlers/wasmer.c \ src/libcrun/handlers/wasmtime.c \ + src/libcrun/intelrdt.c \ src/libcrun/io_priority.c \ src/libcrun/linux.c \ src/libcrun/mount_flags.c \ @@ -147,12 +148,13 @@ EXTRA_DIST = COPYING COPYING.libcrun README.md NEWS SECURITY.md rpm/crun.spec au src/libcrun/custom-handler.h src/libcrun/io_priority.h \ src/libcrun/handlers/handler-utils.h \ src/libcrun/linux.h src/libcrun/utils.h src/libcrun/error.h src/libcrun/criu.h \ - src/libcrun/scheduler.h src/libcrun/status.h src/libcrun/terminal.h src/libcrun/mount_flags.h \ + src/libcrun/scheduler.h src/libcrun/status.h src/libcrun/terminal.h \ + src/libcrun/mount_flags.h src/libcrun/intelrdt.h \ crun.1.md crun.1 libcrun.lds \ krun.1.md krun.1 \ lua/luacrun.rockspec -UNIT_TESTS = tests/tests_libcrun_utils tests/tests_libcrun_errors +UNIT_TESTS = tests/tests_libcrun_utils tests/tests_libcrun_errors tests/tests_libcrun_intelrdt if ENABLE_CRUN bin_PROGRAMS = crun @@ -174,6 +176,11 @@ tests_tests_libcrun_utils_SOURCES = tests/tests_libcrun_utils.c tests_tests_libcrun_utils_LDADD = $(TESTS_LDADD) tests_tests_libcrun_utils_LDFLAGS = $(crun_LDFLAGS) +tests_tests_libcrun_intelrdt_CFLAGS = -I $(abs_top_builddir)/libocispec/src -I $(abs_top_srcdir)/libocispec/src -I $(abs_top_builddir)/src -I $(abs_top_srcdir)/src +tests_tests_libcrun_intelrdt_SOURCES = tests/tests_libcrun_intelrdt.c +tests_tests_libcrun_intelrdt_LDADD = $(TESTS_LDADD) +tests_tests_libcrun_intelrdt_LDFLAGS = $(crun_LDFLAGS) + tests_tests_libcrun_fuzzer_CFLAGS = -I $(abs_top_builddir)/libocispec/src -I $(abs_top_srcdir)/libocispec/src -I $(abs_top_builddir)/src -I $(abs_top_srcdir)/src tests_tests_libcrun_fuzzer_SOURCES = tests/tests_libcrun_fuzzer.c tests_tests_libcrun_fuzzer_LDADD = $(TESTS_LDADD) libocispec/libocispec.la $(maybe_libyajl.la) diff --git a/src/libcrun/container.c b/src/libcrun/container.c index 401b75ed6b..ed0fa29bb0 100644 --- a/src/libcrun/container.c +++ b/src/libcrun/container.c @@ -1696,6 +1696,13 @@ container_delete_internal (libcrun_context_t *context, runtime_spec_schema_confi } } + if (! is_empty_string (status.intelrdt)) + { + ret = libcrun_destroy_intelrdt (status.intelrdt, err); + if (UNLIKELY (ret < 0)) + crun_error_write_warning_and_release (context->output_handler_arg, &err); + } + if (status.cgroup_path) { ret = libcrun_cgroup_destroy (cgroup_status, err); @@ -1766,15 +1773,32 @@ write_container_status (libcrun_container_t *container, libcrun_context_t *conte { cleanup_free char *cwd = getcwd (NULL, 0); cleanup_free char *owner = get_user_name (geteuid ()); + cleanup_free char *intelrdt = NULL; char *external_descriptors = libcrun_get_external_descriptors (container); char *rootfs = container->container_def->root ? container->container_def->root->path : ""; char created[35]; + + if (container_has_intelrdt (container)) + { + bool explicit = false; + const char *tmp; + + tmp = libcrun_get_intelrdt_name (context->id, container, &explicit); + if (tmp == NULL) + return crun_make_error (err, 0, "internal error: cannot get intelrdt name"); + /* It is stored in the status only for cleanup purposes. Delete the group only + if it was not explicitly set. */ + if (! explicit) + intelrdt = xstrdup (tmp); + } + libcrun_container_status_t status = { .pid = pid, .rootfs = rootfs, .bundle = cwd, .created = created, .owner = owner, + .intelrdt = intelrdt, .systemd_cgroup = context->systemd_cgroup, .detached = context->detach, .external_descriptors = external_descriptors, @@ -2437,6 +2461,10 @@ libcrun_container_run_internal (libcrun_container_t *container, libcrun_context_ if (UNLIKELY (ret < 0)) goto fail; + ret = libcrun_apply_intelrdt (context->id, container, pid, LIBCRUN_INTELRDT_CREATE_UPDATE_MOVE, err); + if (UNLIKELY (ret < 0)) + goto fail; + /* sync send own pid. */ ret = TEMP_FAILURE_RETRY (write (sync_socket, &pid, sizeof (pid))); if (UNLIKELY (ret != sizeof (pid))) @@ -3094,7 +3122,7 @@ libcrun_container_state (libcrun_context_t *context, const char *id, FILE *out, ret = append_paths (&config_file, err, dir, "config.json", NULL); if (UNLIKELY (ret < 0)) - return ret; + goto exit; container = libcrun_container_load_from_file (config_file, err); if (UNLIKELY (container == NULL)) @@ -3540,7 +3568,7 @@ libcrun_container_exec_with_options (libcrun_context_t *context, const char *id, if (UNLIKELY (ret < 0)) return crun_make_error (err, errno, "prctl (PR_SET_DUMPABLE)"); - pid = libcrun_join_process (container, status.pid, &status, opts->cgroup, context->detach, + pid = libcrun_join_process (context, container, status.pid, &status, opts->cgroup, context->detach, process, process->terminal ? &terminal_fd : NULL, err); if (UNLIKELY (pid < 0)) return pid; @@ -3925,6 +3953,8 @@ libcrun_container_get_features (libcrun_context_t *context, struct features_info (*info)->linux.apparmor.enabled = true; (*info)->linux.selinux.enabled = true; + (*info)->linux.intel_rdt.enabled = true; + // Put the values for mount extensions (*info)->linux.mount_ext.idmap.enabled = true; @@ -4267,3 +4297,26 @@ libcrun_write_json_containers_list (libcrun_context_t *context, FILE *out, libcr return ret; } + +int +libcrun_container_update_intel_rdt (libcrun_context_t *context, const char *id, struct libcrun_intel_rdt_update *update, libcrun_error_t *err) +{ + cleanup_container libcrun_container_t *container = NULL; + cleanup_free char *config_file = NULL; + cleanup_free char *dir = NULL; + int ret; + + dir = libcrun_get_state_directory (context->state_root, id); + if (UNLIKELY (dir == NULL)) + return crun_make_error (err, 0, "cannot get state directory"); + + ret = append_paths (&config_file, err, dir, "config.json", NULL); + if (UNLIKELY (ret < 0)) + return ret; + + container = libcrun_container_load_from_file (config_file, err); + if (UNLIKELY (container == NULL)) + return crun_make_error (err, 0, "error loading config.json"); + + return libcrun_update_intel_rdt (id, container, update->l3_cache_schema, update->mem_bw_schema, err); +} diff --git a/src/libcrun/container.h b/src/libcrun/container.h index 8d87052a14..dc50dd0c13 100644 --- a/src/libcrun/container.h +++ b/src/libcrun/container.h @@ -126,6 +126,11 @@ struct idmap_info_s bool enabled; }; +struct intel_rdt_s +{ + bool enabled; +}; + struct mount_ext_info_s { struct idmap_info_s idmap; @@ -140,6 +145,7 @@ struct linux_info_s struct apparmor_info_s apparmor; struct selinux_info_s selinux; struct mount_ext_info_s mount_ext; + struct intel_rdt_s intel_rdt; }; struct annotations_info_s @@ -249,6 +255,15 @@ LIBCRUN_PUBLIC int libcrun_container_update_from_values (libcrun_context_t *cont struct libcrun_update_value_s *values, size_t len, libcrun_error_t *err); +struct libcrun_intel_rdt_update +{ + const char *l3_cache_schema; + const char *mem_bw_schema; +}; + +LIBCRUN_PUBLIC int libcrun_container_update_intel_rdt (libcrun_context_t *context, const char *id, + struct libcrun_intel_rdt_update *update, libcrun_error_t *err); + LIBCRUN_PUBLIC int libcrun_container_get_features (libcrun_context_t *context, struct features_info_s **info, libcrun_error_t *err); diff --git a/src/libcrun/intelrdt.c b/src/libcrun/intelrdt.c new file mode 100644 index 0000000000..db517ef753 --- /dev/null +++ b/src/libcrun/intelrdt.c @@ -0,0 +1,339 @@ +/* + * crun - OCI runtime written in C + * + * Copyright (C) 2023 Giuseppe Scrivano + * crun is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * crun is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with crun. If not, see . + */ + +#define _GNU_SOURCE + +#include +#include "linux.h" +#include "utils.h" +#include "intelrdt.h" +#include +#include +#include +#include + +#define INTEL_RDT_MOUNT_POINT "/sys/fs/resctrl" +#define SCHEMATA_FILE "schemata" +#define TASKS_FILE "tasks" +#define RDTGROUP_SUPER_MAGIC 0x7655821 + +static int +is_rdt_mounted (libcrun_error_t *err) +{ + struct statfs sfs; + int ret; + + ret = statfs (INTEL_RDT_MOUNT_POINT, &sfs); + if (ret < 0) + return crun_make_error (err, errno, "statfs `%s`", INTEL_RDT_MOUNT_POINT); + + return sfs.f_type == RDTGROUP_SUPER_MAGIC; +} + +static int +get_rdt_value (char **out, const char *l3_cache_schema, const char *mem_bw_schema) +{ + return xasprintf (out, "%s%s%s\n", l3_cache_schema ?: "", (l3_cache_schema && mem_bw_schema) ? "\n" : "", mem_bw_schema ?: ""); +} + +struct key_value +{ + int key; + int value; +}; + +static int +kv_cmp (const void *p1, const void *p2) +{ + const struct key_value *kv1 = p1; + const struct key_value *kv2 = p2; + + return kv1->key - kv2->key; +} + +static int +count_parts (const char *str) +{ + const char *it; + int ret = 0; + + for (it = str; *it; it++) + { + if (*it == ';' || *(it + 1) == '\0') + ret++; + } + return ret; +} + +static int +read_kv (const char *value, struct key_value *out) +{ + char *endptr; + + out->key = strtoll (value, &endptr, 10); + if (*endptr != '=') + return 1; + + endptr++; + out->value = strtoll (endptr, NULL, 16); + return 0; +} + +int +compare_rdt_configurations (const char *a, const char *b) +{ + cleanup_free struct key_value *kv = NULL; + size_t i, n_parts_a = 0, n_parts_b = 0; + cleanup_free char *a_copy = NULL; + cleanup_free char *b_copy = NULL; + const char *it; + char *end; + int ret; + + it = strchr (a, ':'); + a = it ? it + 1 : a; + + it = strchr (b, ':'); + b = it ? it + 1 : b; + + n_parts_a = count_parts (a); + n_parts_b = count_parts (b); + + if (n_parts_a != n_parts_b) + return 1; + + kv = xmalloc (sizeof (struct key_value) * (n_parts_a + 1)); + + end = a_copy = xstrdup (a); + i = 0; + while ((it = strsep (&end, ";"))) + { + if (it[0] == '\0') + break; + ret = read_kv (it, &(kv[i])); + if (ret) + return 1; + i++; + } + + qsort (kv, i, sizeof (struct key_value), kv_cmp); + + end = b_copy = xstrdup (b); + while ((it = strsep (&end, ";"))) + { + struct key_value key; + struct key_value *res; + + if (it[0] == '\0') + break; + + ret = read_kv (it, &key); + if (ret) + return 1; + + res = bsearch (&key, kv, n_parts_a, sizeof (struct key_value), kv_cmp); + if (res == NULL || res->value != key.value) + return 1; + } + + return 0; +} + +static int +validate_rdt_configuration (const char *name, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err) +{ + cleanup_free char *existing_content = NULL; + cleanup_free char *path = NULL; + char *it, *end; + int ret; + + ret = append_paths (&path, err, INTEL_RDT_MOUNT_POINT, name, SCHEMATA_FILE, NULL); + if (UNLIKELY (ret < 0)) + return ret; + + ret = read_all_file (path, &existing_content, NULL, err); + if (UNLIKELY (ret < 0)) + return ret; + + end = existing_content; + while ((it = strsep (&end, "\n"))) + { + ret = 0; + + if (mem_bw_schema && has_prefix (it, "MB:")) + ret = compare_rdt_configurations (it, mem_bw_schema); + + if (l3_cache_schema && has_prefix (it, "L3:")) + ret = compare_rdt_configurations (it, l3_cache_schema); + + if (ret) + return crun_make_error (err, 0, "the resctl group `%s` has a different configuration", name); + } + + return 0; +} + +static int +write_intelrdt_string (int fd, const char *file, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err) +{ + cleanup_free char *formatted = NULL; + int len, ret; + + len = get_rdt_value (&formatted, l3_cache_schema, mem_bw_schema); + if (len < 0) + return crun_make_error (err, errno, "internal error get_rdt_value"); + + ret = write (fd, formatted, len); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, errno, "write `%s`", file); + + return 0; +} + +/* filter any line in the l3_cache_schema that starts with MB:. */ +char * +intelrdt_clean_l3_cache_schema (const char *l3_cache_schema) +{ + size_t i, j, len = strlen (l3_cache_schema); + char *ret; + + ret = xmalloc (len + 1); + + for (i = 0, j = 0; i < len; i++) + { + if (l3_cache_schema[i] == 'M' && l3_cache_schema[i + 1] == 'B' && l3_cache_schema[i + 2] == ':') + { + i += 3; + while (l3_cache_schema[i] != '\n' && l3_cache_schema[i] != '\0') + i++; + continue; + } + ret[j++] = l3_cache_schema[i]; + } + ret[j] = '\0'; + return ret; +} + +int +resctl_create (const char *name, bool explicit_clos_id, bool *created, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err) +{ + cleanup_free char *cleaned_l3_cache_schema = NULL; + cleanup_free char *path = NULL; + int exist; + int ret; + + ret = is_rdt_mounted (err); + if (UNLIKELY (ret < 0)) + return ret; + if (ret == 0) + return crun_make_error (err, 0, "the resctl file system is not mounted"); + + ret = append_paths (&path, err, INTEL_RDT_MOUNT_POINT, name, NULL); + if (UNLIKELY (ret < 0)) + return ret; + + exist = crun_path_exists (path, err); + if (UNLIKELY (exist < 0)) + return exist; + + if (l3_cache_schema && strstr (l3_cache_schema, "MB:")) + l3_cache_schema = cleaned_l3_cache_schema = intelrdt_clean_l3_cache_schema (l3_cache_schema); + + /* If the closID was specified and both l3cache and bwSchema are unset, the group + must exist. */ + if (explicit_clos_id && l3_cache_schema == NULL && mem_bw_schema == NULL) + { + if (exist) + return 0; + + return crun_make_error (err, 0, "the resctl group `%s` does not exist", name); + } + + /* If the closID exists then it must match the specified configuration. */ + if (exist && (l3_cache_schema != NULL || mem_bw_schema != NULL)) + return validate_rdt_configuration (name, l3_cache_schema, mem_bw_schema, err); + + /* At this point, assume it was created. */ + *created = true; + + return crun_ensure_directory (path, 0755, true, err); +} + +int +resctl_move_task_to (const char *name, pid_t pid, libcrun_error_t *err) +{ + cleanup_free char *path = NULL; + char pid_str[32]; + int len; + int ret; + + ret = append_paths (&path, err, INTEL_RDT_MOUNT_POINT, name, TASKS_FILE, NULL); + if (UNLIKELY (ret < 0)) + return ret; + + len = sprintf (pid_str, "%d", pid); + + return write_file (path, pid_str, len, err); +} + +int +resctl_update (const char *name, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err) +{ + cleanup_free char *cleaned_l3_cache_schema = NULL; + cleanup_free char *path = NULL; + cleanup_close int fd = -1; + int ret; + + /* Nothing to do. */ + if (l3_cache_schema == NULL && mem_bw_schema == NULL) + return 0; + + ret = append_paths (&path, err, INTEL_RDT_MOUNT_POINT, name, SCHEMATA_FILE, NULL); + if (UNLIKELY (ret < 0)) + return ret; + + if (l3_cache_schema && strstr (l3_cache_schema, "MB:")) + l3_cache_schema = cleaned_l3_cache_schema = intelrdt_clean_l3_cache_schema (l3_cache_schema); + + fd = open (path, O_WRONLY | O_CLOEXEC); + if (UNLIKELY (fd < 0)) + return crun_make_error (err, errno, "open `%s`", path); + + ret = write_intelrdt_string (fd, path, l3_cache_schema, mem_bw_schema, err); + if (UNLIKELY (ret < 0)) + return ret; + + return 0; +} + +int +resctl_destroy (const char *name, libcrun_error_t *err) +{ + cleanup_free char *path = NULL; + int ret; + + ret = append_paths (&path, err, INTEL_RDT_MOUNT_POINT, name, NULL); + if (UNLIKELY (ret < 0)) + return ret; + + ret = rmdir (path); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, errno, "rmdir `%s`", path); + + return 0; +} diff --git a/src/libcrun/intelrdt.h b/src/libcrun/intelrdt.h new file mode 100644 index 0000000000..d3485a3d26 --- /dev/null +++ b/src/libcrun/intelrdt.h @@ -0,0 +1,32 @@ +/* + * crun - OCI runtime written in C + * + * Copyright (C) 2023 Giuseppe Scrivano + * crun is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * crun is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with crun. If not, see . + */ + +#ifndef INTEL_RDT_H +#define INTEL_RDT_H + +#include +#include +#include +#include "error.h" + +int resctl_create (const char *name, bool explicit_clos_id, bool *created, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err); +int resctl_move_task_to (const char *name, pid_t pid, libcrun_error_t *err); +int resctl_update (const char *name, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err); +int resctl_destroy (const char *name, libcrun_error_t *err); + +#endif diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c index 5c1b0d29c6..cc2050a40e 100644 --- a/src/libcrun/linux.c +++ b/src/libcrun/linux.c @@ -50,6 +50,7 @@ #include "status.h" #include "criu.h" #include "scheduler.h" +#include "intelrdt.h" #include "io_priority.h" #include @@ -4836,7 +4837,9 @@ libcrun_run_linux_container (libcrun_container_t *container, container_entrypoin } static int -join_process_parent_helper (runtime_spec_schema_config_schema_process *process, +join_process_parent_helper (libcrun_context_t *context, + libcrun_container_t *container, + runtime_spec_schema_config_schema_process *process, pid_t child_pid, int sync_socket_fd, libcrun_container_status_t *status, bool need_move_to_cgroup, const char *sub_cgroup, @@ -4894,6 +4897,10 @@ join_process_parent_helper (runtime_spec_schema_config_schema_process *process, return ret; } + ret = libcrun_apply_intelrdt (context->id, container, pid, LIBCRUN_INTELRDT_MOVE, err); + if (UNLIKELY (ret < 0)) + return ret; + ret = libcrun_set_io_priority (pid, process, err); if (UNLIKELY (ret < 0)) return ret; @@ -5071,10 +5078,15 @@ join_process_namespaces (libcrun_container_t *container, pid_t pid_to_join, libc } int -libcrun_join_process (libcrun_container_t *container, pid_t pid_to_join, - libcrun_container_status_t *status, const char *sub_cgroup, - int detach, runtime_spec_schema_config_schema_process *process, - int *terminal_fd, libcrun_error_t *err) +libcrun_join_process (libcrun_context_t *context, + libcrun_container_t *container, + pid_t pid_to_join, + libcrun_container_status_t *status, + const char *sub_cgroup, + int detach, + runtime_spec_schema_config_schema_process *process, + int *terminal_fd, + libcrun_error_t *err) { pid_t pid; int ret; @@ -5150,11 +5162,10 @@ libcrun_join_process (libcrun_container_t *container, pid_t pid_to_join, { close_and_reset (&sync_socket_fd[1]); sync_fd = sync_socket_fd[0]; - return join_process_parent_helper (process, - pid, sync_fd, status, - need_move_to_cgroup, - sub_cgroup, - terminal_fd, err); + return join_process_parent_helper (context, container, + process, pid, sync_fd, + status, need_move_to_cgroup, + sub_cgroup, terminal_fd, err); } close_and_reset (&sync_socket_fd[0]); @@ -5496,3 +5507,92 @@ libcrun_kill_linux (libcrun_container_status_t *status, int signal, libcrun_erro return 0; } + +const char * +libcrun_get_intelrdt_name (const char *ctr_name, libcrun_container_t *container, bool *explicit) +{ + runtime_spec_schema_config_schema *def = NULL; + + def = container->container_def; + + if (def == NULL || def->linux == NULL || def->linux->intel_rdt == NULL || def->linux->intel_rdt->clos_id == NULL) + { + if (explicit) + *explicit = false; + return ctr_name; + } + + if (explicit) + *explicit = true; + return def->linux->intel_rdt->clos_id; +} + +int +libcrun_apply_intelrdt (const char *ctr_name, libcrun_container_t *container, pid_t pid, int actions, libcrun_error_t *err) +{ + runtime_spec_schema_config_schema *def = NULL; + bool explicit = false; + bool created = false; + const char *name; + int ret; + + if (container) + def = container->container_def; + + if (def == NULL || def->linux == NULL || def->linux->intel_rdt == NULL) + return 0; + + name = libcrun_get_intelrdt_name (ctr_name, container, &explicit); + + if (actions & LIBCRUN_INTELRDT_CREATE) + { + ret = resctl_create (name, explicit, &created, def->linux->intel_rdt->l3cache_schema, def->linux->intel_rdt->mem_bw_schema, err); + if (UNLIKELY (ret < 0)) + return ret; + } + + if (actions & LIBCRUN_INTELRDT_UPDATE) + { + ret = resctl_update (name, def->linux->intel_rdt->l3cache_schema, def->linux->intel_rdt->mem_bw_schema, err); + if (UNLIKELY (ret < 0)) + goto fail; + } + + if (actions & LIBCRUN_INTELRDT_MOVE) + { + ret = resctl_move_task_to (name, pid, err); + if (UNLIKELY (ret < 0)) + goto fail; + } + + return 0; + +fail: + /* Cleanup only if the resctl was created as part of this call. */ + if (created) + { + libcrun_error_t tmp_err = NULL; + int tmp_ret; + + tmp_ret = resctl_destroy (name, &tmp_err); + if (tmp_ret < 0) + crun_error_release (&tmp_err); + } + return ret; +} + +int +libcrun_destroy_intelrdt (const char *name, libcrun_error_t *err) +{ + return resctl_destroy (name, err); +} + +int +libcrun_update_intel_rdt (const char *ctr_name, libcrun_container_t *container, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err) +{ + const char *name; + + name = libcrun_get_intelrdt_name (ctr_name, container, NULL); + + return resctl_update (name, l3_cache_schema, mem_bw_schema, err); +} diff --git a/src/libcrun/linux.h b/src/libcrun/linux.h index f8800c9228..98bbf5d47e 100644 --- a/src/libcrun/linux.h +++ b/src/libcrun/linux.h @@ -80,9 +80,9 @@ int libcrun_set_domainname (libcrun_container_t *container, libcrun_error_t *err int libcrun_set_oom (libcrun_container_t *container, libcrun_error_t *err); int libcrun_set_sysctl (libcrun_container_t *container, libcrun_error_t *err); int libcrun_set_terminal (libcrun_container_t *container, libcrun_error_t *err); -int libcrun_join_process (libcrun_container_t *container, pid_t pid_to_join, libcrun_container_status_t *status, - const char *cgroup, int detach, runtime_spec_schema_config_schema_process *process, - int *terminal_fd, libcrun_error_t *err); +int libcrun_join_process (libcrun_context_t *context, libcrun_container_t *container, pid_t pid_to_join, + libcrun_container_status_t *status, const char *cgroup, int detach, + runtime_spec_schema_config_schema_process *process, int *terminal_fd, libcrun_error_t *err); int libcrun_linux_container_update (libcrun_container_status_t *status, runtime_spec_schema_config_linux_resources *resources, libcrun_error_t *err); int libcrun_create_keyring (const char *name, const char *label, libcrun_error_t *err); @@ -116,4 +116,31 @@ int libcrun_create_dev (libcrun_container_t *container, int devfd, int parse_idmapped_mount_option (runtime_spec_schema_config_schema *def, bool is_uids, char *option, char **out, size_t *len, libcrun_error_t *err); +enum +{ + LIBCRUN_INTELRDT_CREATE = (1 << 0), + LIBCRUN_INTELRDT_UPDATE = (1 << 1), + LIBCRUN_INTELRDT_MOVE = (1 << 2), +}; + +#define LIBCRUN_INTELRDT_CREATE_UPDATE_MOVE (LIBCRUN_INTELRDT_CREATE | LIBCRUN_INTELRDT_UPDATE | LIBCRUN_INTELRDT_MOVE) + +static inline bool +container_has_intelrdt (libcrun_container_t *container) +{ + runtime_spec_schema_config_schema *def = NULL; + + def = container->container_def; + + return def != NULL && def->linux != NULL && def->linux->intel_rdt != NULL; +} + +const char *libcrun_get_intelrdt_name (const char *ctr_name, libcrun_container_t *container, bool *explicit); + +int libcrun_apply_intelrdt (const char *ctr_name, libcrun_container_t *container, pid_t pid, int actions, libcrun_error_t *err); + +int libcrun_destroy_intelrdt (const char *name, libcrun_error_t *err); + +int libcrun_update_intel_rdt (const char *ctr_name, libcrun_container_t *container, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err); + #endif diff --git a/src/libcrun/status.c b/src/libcrun/status.c index 375610c9d7..880024a0f2 100644 --- a/src/libcrun/status.c +++ b/src/libcrun/status.c @@ -237,6 +237,15 @@ libcrun_write_container_status (const char *state_root, const char *id, libcrun_ if (UNLIKELY (r != yajl_gen_status_ok)) goto yajl_error; + r = yajl_gen_string (gen, YAJL_STR ("intelrdt"), strlen ("intelrdt")); + if (UNLIKELY (r != yajl_gen_status_ok)) + goto yajl_error; + + tmp = status->intelrdt ? status->intelrdt : ""; + r = yajl_gen_string (gen, YAJL_STR (tmp), strlen (tmp)); + if (UNLIKELY (r != yajl_gen_status_ok)) + goto yajl_error; + r = yajl_gen_string (gen, YAJL_STR ("rootfs"), strlen ("rootfs")); if (UNLIKELY (r != yajl_gen_status_ok)) goto yajl_error; @@ -376,6 +385,11 @@ libcrun_read_container_status (libcrun_container_status_t *status, const char *s tmp = yajl_tree_get (tree, scope, yajl_t_string); status->scope = tmp ? xstrdup (YAJL_GET_STRING (tmp)) : NULL; } + { + const char *intelrdt[] = { "intelrdt", NULL }; + tmp = yajl_tree_get (tree, intelrdt, yajl_t_string); + status->intelrdt = tmp ? xstrdup (YAJL_GET_STRING (tmp)) : NULL; + } { const char *rootfs[] = { "rootfs", NULL }; tmp = yajl_tree_get (tree, rootfs, yajl_t_string); @@ -538,6 +552,7 @@ libcrun_free_container_status (libcrun_container_status_t *status) free (status->external_descriptors); free (status->created); free (status->scope); + free (status->intelrdt); free (status->owner); } diff --git a/src/libcrun/status.h b/src/libcrun/status.h index 28cb9d44e3..70f33139e7 100644 --- a/src/libcrun/status.h +++ b/src/libcrun/status.h @@ -39,6 +39,7 @@ struct libcrun_container_status_s char *rootfs; char *cgroup_path; char *scope; + char *intelrdt; int systemd_cgroup; char *created; int detached; diff --git a/src/oci_features.c b/src/oci_features.c index b13449b421..ff5d0fff0e 100644 --- a/src/oci_features.c +++ b/src/oci_features.c @@ -184,6 +184,15 @@ crun_features_add_mount_ext_info (yajl_gen json_gen, const struct linux_info_s * yajl_gen_map_close (json_gen); } +void +crun_features_add_intel_rdt (yajl_gen json_gen, const struct linux_info_s *linux) +{ + yajl_gen_string (json_gen, (const unsigned char *) "intelRdt", strlen ("intelRdt")); + yajl_gen_map_open (json_gen); + add_bool_to_json (json_gen, "enabled", linux->intel_rdt.enabled); + yajl_gen_map_close (json_gen); +} + void crun_features_add_linux_info (yajl_gen json_gen, const struct linux_info_s *linux) { @@ -197,6 +206,7 @@ crun_features_add_linux_info (yajl_gen json_gen, const struct linux_info_s *linu crun_features_add_apparmor_info (json_gen, linux); crun_features_add_selinux_info (json_gen, linux); crun_features_add_mount_ext_info (json_gen, linux); + crun_features_add_intel_rdt (json_gen, linux); yajl_gen_map_close (json_gen); } diff --git a/src/update.c b/src/update.c index 6710655fb9..e535da3fb9 100644 --- a/src/update.c +++ b/src/update.c @@ -56,6 +56,10 @@ enum PIDS_LIMIT, + /* not in the resources block. */ + L3_CACHE_SCHEMA, + MEM_BW_SCHEMA, + LAST_VALUE, }; @@ -100,6 +104,9 @@ set_value (int id, const char *value) values_len++; } +static char *l3_cache_schema; +static char *mem_bw_schema; + static struct argp_option options[] = { { "resources", 'r', "FILE", 0, "path to the file containing the resources to update", 0 }, { "blkio-weight", BLKIO_WEIGHT, "VALUE", 0, "Specifies per cgroup weight", 0 }, @@ -116,6 +123,8 @@ static struct argp_option options[] { "memory-reservation", MEMORY_RESERVATION, "VALUE", 0, "Memory reservation or soft_limit", 0 }, { "memory-swap", MEMORY_SWAP, "VALUE", 0, "Total memory usage", 0 }, { "pids-limit", PIDS_LIMIT, "VALUE", 0, "Maximum number of pids allowed in the container", 0 }, + { "l3-cache-schema", L3_CACHE_SCHEMA, "VALUE", 0, "The string of Intel RDT/CAT L3 cache schema", 0 }, + { "mem-bw-schema", MEM_BW_SCHEMA, "VALUE", 0, "The string of Intel RDT/MBA memory bandwidth schema", 0 }, { 0, } }; @@ -151,6 +160,14 @@ parse_opt (int key, char *arg, struct argp_state *state) set_value (key, argp_mandatory_argument (arg, state)); break; + case L3_CACHE_SCHEMA: + l3_cache_schema = argp_mandatory_argument (arg, state); + break; + + case MEM_BW_SCHEMA: + mem_bw_schema = argp_mandatory_argument (arg, state); + break; + default: return ARGP_ERR_UNKNOWN; } @@ -176,8 +193,27 @@ crun_command_update (struct crun_global_arguments *global_args, int argc, char * { ret = libcrun_container_update_from_values (&crun_context, argv[first_arg], values, values_len, err); free (values); - return ret; + if (ret < 0) + return ret; + } + else + { + ret = libcrun_container_update_from_file (&crun_context, argv[first_arg], resources, err); + if (ret < 0) + return ret; + } + + if (l3_cache_schema || mem_bw_schema) + { + struct libcrun_intel_rdt_update update = { + .l3_cache_schema = l3_cache_schema, + .mem_bw_schema = mem_bw_schema, + }; + + ret = libcrun_container_update_intel_rdt (&crun_context, argv[first_arg], &update, err); + if (ret < 0) + return ret; } - return libcrun_container_update_from_file (&crun_context, argv[first_arg], resources, err); + return 0; } diff --git a/tests/fuzzing/run-tests.sh b/tests/fuzzing/run-tests.sh index f8d3ec8df4..e375ef403a 100755 --- a/tests/fuzzing/run-tests.sh +++ b/tests/fuzzing/run-tests.sh @@ -38,3 +38,4 @@ run_test 4 $CORPUS/paths run_test 5 random-data run_test 6 $CORPUS/annotations run_test 7 $CORPUS/idmapped-mounts-option +run_test 8 $CORPUS/intelrdt diff --git a/tests/tests_libcrun_fuzzer.c b/tests/tests_libcrun_fuzzer.c index 995a95901c..df0dfbdf0a 100644 --- a/tests/tests_libcrun_fuzzer.c +++ b/tests/tests_libcrun_fuzzer.c @@ -36,6 +36,8 @@ static int test_mode = -1; +extern int compare_rdt_configurations (const char *a, const char *b); + static char * make_nul_terminated (uint8_t *buf, size_t len) { @@ -407,9 +409,17 @@ run_one_test (int mode, uint8_t *buf, size_t len) test_parse_idmapped_mounts (buf, len); break; + case 8: + { + cleanup_free char *a = make_nul_terminated (buf, len / 2); + cleanup_free char *b = make_nul_terminated (buf + len / 2, len / 2); + compare_rdt_configurations (a, b); + } + break; + /* ALL mode. */ case -1: - for (i = 0; i <= 5; i++) + for (i = 0; i <= 8; i++) run_one_test (i, buf, len); break; diff --git a/tests/tests_libcrun_intelrdt.c b/tests/tests_libcrun_intelrdt.c new file mode 100644 index 0000000000..ea29cf212c --- /dev/null +++ b/tests/tests_libcrun_intelrdt.c @@ -0,0 +1,104 @@ +/* + * crun - OCI runtime written in C + * + * Copyright (C) 2017, 2018, 2019, 2023 Giuseppe Scrivano + * crun is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * crun is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with crun. If not, see . + */ + +#include +#include +#include +#include +#include + +typedef int (*test) (); + +extern int compare_rdt_configurations (const char *a, const char *b); +extern char *intelrdt_clean_l3_cache_schema (const char *l3_cache_schema); + +static int +test_compare_rdt_configurations () +{ + if (compare_rdt_configurations ("L3:1=1f;0=7f0;", "L3:0=7f0;1=01f;")) + return 1; + if (compare_rdt_configurations ("L3:1=1f;0=7f0;", "L3:0=7f0;1=01f")) + return 1; + if (compare_rdt_configurations ("MB:0=20;1=70", "MB:0=20;1=70")) + return 1; + if (compare_rdt_configurations ("MB:0=20;1=70;", "0= 20;1= 70")) + return 1; + return 0; +} + +static int +test_intelrdt_clean_l3_cache_schema () +{ +#define COMPARE(X, Y) \ + do \ + { \ + char *res = intelrdt_clean_l3_cache_schema (X); \ + int r = strcmp (res, Y); \ + free (res); \ + if (r) \ + return 1; \ + } while (0) + + COMPARE ("L3:2=2e;1=8e1;", "L3:2=2e;1=8e1;"); + COMPARE ("L3:2=2e;1=8e1", "L3:2=2e;1=8e1"); + COMPARE ("L3:2=2e;1=8e1;\nMB:13", "L3:2=2e;1=8e1;\n"); + COMPARE ("MB:13\nL3:2=2e;1=8e1", "L3:2=2e;1=8e1"); + COMPARE ("L3:2=2e;1=8e1\nMB:foo1=bar1\n", "L3:2=2e;1=8e1\n"); + COMPARE ("L3:3=3d;2=9d2;", "L3:3=3d;2=9d2;"); + COMPARE ("L3:3=3d;2=9d2", "L3:3=3d;2=9d2"); + COMPARE ("L3:3=3d;2=9d2;\nMB:14", "L3:3=3d;2=9d2;\n"); + COMPARE ("MB:14\nL3:3=3d;2=9d2", "L3:3=3d;2=9d2"); + COMPARE ("L3:3=3d;2=9d2\nMB:foo2=bar2\n", "L3:3=3d;2=9d2\n"); + COMPARE ("L3:4=4c;3=ac3;", "L3:4=4c;3=ac3;"); + COMPARE ("L3:4=4c;3=ac3", "L3:4=4c;3=ac3"); + COMPARE ("L3:4=4c;3=ac3;\nMB:15", "L3:4=4c;3=ac3;\n"); + COMPARE ("MB:15\nL3:4=4c;3=ac3", "L3:4=4c;3=ac3"); + COMPARE ("L3:4=4c;3=ac3\nMB:foo3=bar3\n", "L3:4=4c;3=ac3\n"); + +#undef COMPARE + + return 0; +} + +static void +run_and_print_test_result (const char *name, int id, test t) +{ + int ret = t (); + if (ret == 0) + printf ("ok %d - %s\n", id, name); + else if (ret == 77) + printf ("ok %d - %s #SKIP\n", id, name); + else + printf ("not ok %d - %s\n", id, name); +} + +#define RUN_TEST(T) \ + do \ + { \ + run_and_print_test_result (#T, id++, T); \ + } while (0) + +int +main () +{ + int id = 1; + printf ("1..2\n"); + RUN_TEST (test_compare_rdt_configurations); + RUN_TEST (test_intelrdt_clean_l3_cache_schema); + return 0; +}