diff --git a/Makefile.am b/Makefile.am index fe2128238f..cc72d475fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,6 +67,7 @@ libcrun_SOURCES = src/libcrun/utils.c \ src/libcrun/linux.c \ src/libcrun/mount_flags.c \ src/libcrun/scheduler.c \ + src/libcrun/mempolicy.c \ src/libcrun/seccomp.c \ src/libcrun/seccomp_notify.c \ src/libcrun/signals.c \ @@ -160,7 +161,7 @@ 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/scheduler.h src/libcrun/mempolicy.h src/libcrun/status.h src/libcrun/terminal.h \ src/libcrun/mount_flags.h src/libcrun/intelrdt.h src/libcrun/ring_buffer.h src/libcrun/string_map.h \ src/libcrun/net_device.h \ crun.1.md crun.1 libcrun.lds \ @@ -225,6 +226,7 @@ PYTHON_TESTS = tests/test_capabilities.py \ tests/test_hostname.py \ tests/test_limits.py \ tests/test_oci_features.py \ + tests/test_mempolicy.py \ tests/test_mounts.py \ tests/test_paths.py \ tests/test_pid.py \ diff --git a/src/libcrun/container.c b/src/libcrun/container.c index 0d21b9e310..103f0c06db 100644 --- a/src/libcrun/container.c +++ b/src/libcrun/container.c @@ -23,6 +23,7 @@ #include "container.h" #include "utils.h" #include "seccomp.h" +#include "mempolicy.h" #ifdef HAVE_SECCOMP # include #endif @@ -167,6 +168,22 @@ static char *archs[] = { "SCMP_ARCH_X86_64" }; +static char *mempolicy_modes[] = { + "MPOL_DEFAULT", + "MPOL_PREFERRED", + "MPOL_BIND", + "MPOL_INTERLEAVE", + "MPOL_LOCAL", + "MPOL_PREFERRED_MANY", + "MPOL_WEIGHTED_INTERLEAVE" +}; + +static char *mempolicy_flags[] = { + "MPOL_F_NUMA_BALANCING", + "MPOL_F_RELATIVE_NODES", + "MPOL_F_STATIC_NODES" +}; + static const char spec_file[] = "\ {\n\ \"ociVersion\": \"1.0.0\",\n\ @@ -2768,6 +2785,10 @@ libcrun_container_run_internal (libcrun_container_t *container, libcrun_context_ umask (0); + ret = libcrun_set_mempolicy (def, err); + if (UNLIKELY (ret < 0)) + return ret; + ret = setup_seccomp (container, seccomp_bpf_data, &seccomp_gen_ctx, &seccomp_fd, err); if (UNLIKELY (ret < 0)) return ret; @@ -4324,6 +4345,8 @@ libcrun_container_get_features (libcrun_context_t *context, struct features_info size_t num_unsafe_annotations = sizeof (potentially_unsafe_annotations) / sizeof (potentially_unsafe_annotations[0]); cleanup_free char **capabilities = NULL; size_t num_capabilities = 0; + size_t num_mempolicy_modes = sizeof (mempolicy_modes) / sizeof (mempolicy_modes[0]); + size_t num_mempolicy_flags = sizeof (mempolicy_flags) / sizeof (mempolicy_flags[0]); *info = xmalloc0 (sizeof (struct features_info_s)); @@ -4374,6 +4397,9 @@ libcrun_container_get_features (libcrun_context_t *context, struct features_info (*info)->linux.net_devices.enabled = true; + populate_array_field (&((*info)->linux.memory_policy.mode), mempolicy_modes, num_mempolicy_modes); + populate_array_field (&((*info)->linux.memory_policy.flags), mempolicy_flags, num_mempolicy_flags); + // Put the values for mount extensions (*info)->linux.mount_ext.idmap.enabled = true; diff --git a/src/libcrun/container.h b/src/libcrun/container.h index d6772017fd..00fac9e2f1 100644 --- a/src/libcrun/container.h +++ b/src/libcrun/container.h @@ -149,6 +149,12 @@ struct mount_ext_info_s struct idmap_info_s idmap; }; +struct memory_policy_info_s +{ + char **mode; + char **flags; +}; + struct linux_info_s { char **namespaces; @@ -160,6 +166,7 @@ struct linux_info_s struct mount_ext_info_s mount_ext; struct intel_rdt_s intel_rdt; struct net_devices_s net_devices; + struct memory_policy_info_s memory_policy; }; struct annotations_info_s diff --git a/src/libcrun/mempolicy.c b/src/libcrun/mempolicy.c new file mode 100644 index 0000000000..abaf045a3d --- /dev/null +++ b/src/libcrun/mempolicy.c @@ -0,0 +1,154 @@ +/* + * crun - OCI runtime written in C + * + * Copyright (C) 2017, 2018, 2019 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 "linux.h" +#include "utils.h" +#include + +#include +#include + +/* follow runc go implementation and redefine everything here */ +/* Policies */ +#define MPOL_DEFAULT 0 +#define MPOL_PREFERRED 1 +#define MPOL_BIND 2 +#define MPOL_INTERLEAVE 3 +#define MPOL_LOCAL 4 +#define MPOL_PREFERRED_MANY 5 +#define MPOL_WEIGHTED_INTERLEAVE 6 + +/* Flags for set_mempolicy, specified in mode */ +#define MPOL_F_NUMA_BALANCING (1 << 13) +#define MPOL_F_RELATIVE_NODES (1 << 14) +#define MPOL_F_STATIC_NODES (1 << 15) + +typedef struct +{ + const char *name; + int value; +} str2int_map_t; + +/* update mpol_mode_map based on numaif.h MPOL_MAX + * the warn in mempolicy.c will indicate that an update is required. + * MPOL_WEIGHTED_INTERLEAVE has been introduced in MPOL_MAX 7 (kernel 6.9+) + * and some distros still has older kernel interfaces */ +str2int_map_t mpol_mode_map[] = { + { "MPOL_DEFAULT", MPOL_DEFAULT }, + { "MPOL_PREFERRED", MPOL_PREFERRED }, + { "MPOL_BIND", MPOL_BIND }, + { "MPOL_INTERLEAVE", MPOL_INTERLEAVE }, + { "MPOL_LOCAL", MPOL_LOCAL }, + { "MPOL_PREFERRED_MANY", MPOL_PREFERRED_MANY }, + { "MPOL_WEIGHTED_INTERLEAVE", MPOL_WEIGHTED_INTERLEAVE }, + { NULL, -1 } +}; + +/* flags cannot be tracked the same way as mode */ +str2int_map_t mpol_flag_map[] = { + { "MPOL_F_NUMA_BALANCING", MPOL_F_NUMA_BALANCING }, + { "MPOL_F_RELATIVE_NODES", MPOL_F_RELATIVE_NODES }, + { "MPOL_F_STATIC_NODES", MPOL_F_STATIC_NODES }, + { NULL, -1 } +}; + +#define MAX_NUMA_NODES 4096 + +static int +mpol_str2int (const char *str, const str2int_map_t *map) +{ + int idx = 0; + + while (map[idx].name != NULL) + { + if (! strcmp (map[idx].name, str)) + return map[idx].value; + + idx++; + } + + errno = EINVAL; + return -1; +} + +int +libcrun_set_mempolicy (runtime_spec_schema_config_schema *def, libcrun_error_t *err) +{ + runtime_spec_schema_config_linux_memory_policy *memory_policy = NULL; + int mpol_mode = 0; + int mpol_flag = 0; + size_t i = 0; + unsigned long nmask[MAX_NUMA_NODES / (sizeof (unsigned long) * 8)]; + unsigned long *nmask_final = NULL; + int ret = 0; + + if (def->linux == NULL || def->linux->memory_policy == NULL) + { + libcrun_debug ("no linux numa mempolicy configuration found"); + return ret; + } + + libcrun_debug ("Initializing linux numa mempolicy"); + + memory_policy = def->linux->memory_policy; + + libcrun_debug ("Validating linux numa mempolicy"); + /* validate memory policy mode */ + if (! memory_policy->mode) + return crun_make_error (err, EINVAL, "linux NUMA mempolicy mode is missing from the configuration"); + + libcrun_debug ("Validating mode: %s", memory_policy->mode); + mpol_mode = mpol_str2int (memory_policy->mode, mpol_mode_map); + if (mpol_mode < 0) + return crun_make_error (err, EINVAL, "requested linux NUMA mempolicy mode '%s' is unknown", memory_policy->mode); + + /* validating memory policy flags */ + libcrun_debug ("Validating mode flags: %zu configured", memory_policy->flags_len); + for (i = 0; i < memory_policy->flags_len; i++) + { + libcrun_debug ("Validating mode flag: %s", memory_policy->flags[i]); + mpol_flag = mpol_str2int (memory_policy->flags[i], mpol_flag_map); + if (mpol_flag < 0) + return crun_make_error (err, EINVAL, "requested linux NUMA mempolicy flag '%s' is unknown", memory_policy->flags[i]); + mpol_mode = mpol_mode | mpol_flag; + } + + /* kernel will take care of validating the nodes */ + if (memory_policy->nodes) + { + cleanup_free char *bitmask = NULL; + size_t bitmask_size; + + ret = cpuset_string_to_bitmask (memory_policy->nodes, &bitmask, &bitmask_size, err); + if (UNLIKELY (ret < 0)) + return ret; + + if (bitmask_size > sizeof (nmask)) + return crun_make_error (err, EINVAL, "requested NUMA bitmask bigger than kernel supported bitmask"); + + nmask_final = nmask; + memset (nmask_final, 0, sizeof (nmask)); + memcpy (nmask_final, bitmask, bitmask_size); + } + + if (syscall (__NR_set_mempolicy, mpol_mode, nmask_final, nmask_final ? MAX_NUMA_NODES - 1 : 0) < 0) + return crun_make_error (err, errno, "set_mempolicy"); + + return ret; +} diff --git a/src/libcrun/mempolicy.h b/src/libcrun/mempolicy.h new file mode 100644 index 0000000000..db00e231e7 --- /dev/null +++ b/src/libcrun/mempolicy.h @@ -0,0 +1,27 @@ +/* + * crun - OCI runtime written in C + * + * Copyright (C) 2017, 2018, 2019, 2021 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 MEMPOLICY_H +#define MEMPOLICY_H +#include +#include "error.h" +#include "container.h" +#include "status.h" + +int libcrun_set_mempolicy (runtime_spec_schema_config_schema *def, libcrun_error_t *err); + +#endif diff --git a/src/oci_features.c b/src/oci_features.c index 4bae598a75..32126b72a4 100644 --- a/src/oci_features.c +++ b/src/oci_features.c @@ -150,6 +150,21 @@ crun_features_add_seccomp_info (yajl_gen json_gen, const struct linux_info_s *li yajl_gen_map_close (json_gen); } +void +crun_features_add_mempolicy_info (yajl_gen json_gen, const struct linux_info_s *linux) +{ + yajl_gen_string (json_gen, (const unsigned char *) "memoryPolicy", strlen ("memoryPolicy")); + yajl_gen_map_open (json_gen); + + if (linux->memory_policy.mode) + add_array_to_json (json_gen, "modes", linux->memory_policy.mode); + + if (linux->memory_policy.flags) + add_array_to_json (json_gen, "flags", linux->memory_policy.flags); + + yajl_gen_map_close (json_gen); +} + void crun_features_add_apparmor_info (yajl_gen json_gen, const struct linux_info_s *linux) { @@ -219,6 +234,7 @@ crun_features_add_linux_info (yajl_gen json_gen, const struct linux_info_s *linu crun_features_add_mount_ext_info (json_gen, linux); crun_features_add_intel_rdt (json_gen, linux); crun_features_add_net_devices (json_gen, linux); + crun_features_add_mempolicy_info (json_gen, linux); yajl_gen_map_close (json_gen); } diff --git a/tests/test_mempolicy.py b/tests/test_mempolicy.py new file mode 100755 index 0000000000..367fe8675a --- /dev/null +++ b/tests/test_mempolicy.py @@ -0,0 +1,478 @@ +#!/bin/env python3 +# crun - OCI runtime written in C +# +# Copyright (C) 2017, 2018, 2019 Giuseppe Scrivano +# crun is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with crun. If not, see . + +import subprocess +import sys +import json +import os +from tests_utils import * + +def check_numa_interleave(): + return os.path.exists("/sys/kernel/mm/mempolicy/weighted_interleave") + +def check_numa_hw(): + return os.path.exists("/proc/self/numa_maps") + +def check_mempolicy_prerequisites(need_interleave=False): + """Check all prerequisites for numa mempolicy tests. Returns 77 (skip) if not met, 0 if OK""" + if not check_numa_hw(): + sys.stderr.write("# numa missing\n") + return 77 + if need_interleave and not check_numa_interleave(): + sys.stderr.write("# interleave missing\n") + return 77 + +def test_mempolicy_no_conf(): + """Test numa mempolicy without configuration""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bad_mode(): + """Test numa mempolicy with bad mode""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "BAD_MODE" } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bad_flag(): + """Test numa mempolicy with bad flag""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED", "flags": ["BADFLAG"] } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_numa_balancing_flag(): + """Test numa mempolicy preferred with numa_balancing flag""" + if check_mempolicy_prerequisites(need_interleave=True): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED", "flags": ["MPOL_F_NUMA_BALANCING"] } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_static_relative_nodes_flags(): + """Test numa mempolicy preferred with numa_balancing flag""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED", "flags": ["MPOL_F_RELATIVE_NODES", "MPOL_F_STATIC_NODES"] } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_no_nodes(): + """Test numa mempolicy without nodes configuration""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_INTERLEAVE" } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bad_nodes_string(): + """Test numa mempolicy without nodes configuration""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED", "nodes": "bad" } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bad_nodes_number(): + """Test numa mempolicy without nodes configuration""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED", "nodes": "10284838" } + + cid = None + try: + _, cid = run_and_get_output(conf, command='run') + sys.stderr.write("# unexpected success\n") + return -1 + except: + pass + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_default_mode(): + """Test numa mempolicy default mode""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_DEFAULT" } + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " default " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' default ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_local_mode(): + """Test numa mempolicy local mode""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_LOCAL" } + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " local " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' local ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bind_mode(): + """Test numa mempolicy bind mode""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_BIND", "nodes": "0" } + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " bind:0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' bind:0 ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bind_mode_balancing(): + """Test numa mempolicy bind mode balancing""" + if check_mempolicy_prerequisites(need_interleave=True): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_BIND", "nodes": "0", "flags": ["MPOL_F_NUMA_BALANCING"]} + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " bind=balancing:0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' bind=balancing:0 ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_bind_mode_balancing_relative(): + """Test numa mempolicy bind mode balancing with relative nodes""" + if check_mempolicy_prerequisites(need_interleave=True): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_BIND", "nodes": "0", "flags": ["MPOL_F_NUMA_BALANCING", "MPOL_F_RELATIVE_NODES"]} + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " bind=relative|balancing:0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' bind=relative|balancing:0 ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_preferred_mode_static(): + """Test numa mempolicy preferred mode with static nodes""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED", "nodes": "0", "flags": ["MPOL_F_STATIC_NODES"]} + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " prefer=static:0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' prefer=static:0 ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_preferred_many_mode(): + """Test numa mempolicy preferred many mode with all nodes""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_PREFERRED_MANY", "nodes": "0" } + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " prefer (many):0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' prefer (many):0 ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_interleave_mode(): + """Test numa mempolicy interleave mode""" + if check_mempolicy_prerequisites(): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_INTERLEAVE", "nodes": "0" } + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " interleave:0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' interleave:0 ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +def test_mempolicy_weighted_interleave_mode(): + """Test numa mempolicy weighted interleave mode""" + if check_mempolicy_prerequisites(need_interleave=True): + return 77 + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps'] + add_all_namespaces(conf) + conf['linux']['memoryPolicy'] = { "mode": "MPOL_WEIGHTED_INTERLEAVE", "nodes": "0" } + + cid = None + try: + out, cid = run_and_get_output(conf, command='run') + if " weighted interleave:0 " not in out.splitlines()[1]: + sys.stderr.write("# Unable to find ' weighted interleave ' in /proc/self/numa_maps\n") + sys.stderr.write(out) + return -1 + except Exception as e: + sys.stderr.write("# Test failed with exception: %s\n" % str(e)) + return -1 + finally: + if cid is not None: + run_crun_command(["delete", "-f", cid]) + + return 0 + +all_tests = { + "mempolicy-no-conf": test_mempolicy_no_conf, + "mempolicy-bad-mode": test_mempolicy_bad_mode, + "mempolicy-bad-flag": test_mempolicy_bad_flag, + "mempolicy-numa-balancing-flag": test_mempolicy_numa_balancing_flag, + "mempolicy-static-relative-nodes-flags": test_mempolicy_static_relative_nodes_flags, + "mempolicy-no-nodes": test_mempolicy_no_nodes, + "mempolicy-bad-nodes-string": test_mempolicy_bad_nodes_string, + "mempolicy-bad-nodes-number": test_mempolicy_bad_nodes_number, + "mempolicy-default-mode": test_mempolicy_default_mode, + "mempolicy-local-mode": test_mempolicy_local_mode, + "mempolicy-bind-mode": test_mempolicy_bind_mode, + "mempolicy-bind-mode-balancing": test_mempolicy_bind_mode_balancing, + "mempolicy-bind-mode-balancing-relative": test_mempolicy_bind_mode_balancing_relative, + "mempolicy-preferred-mode-static": test_mempolicy_preferred_mode_static, + "mempolicy-preferred-many-mode-all-nodes": test_mempolicy_preferred_many_mode, + "mempolicy-interleave-mode": test_mempolicy_interleave_mode, + "mempolicy-weighted-interleave-mode": test_mempolicy_weighted_interleave_mode, +} + +if __name__ == "__main__": + tests_main(all_tests)