Skip to content

Commit ebfeb36

Browse files
committed
[WIP] just add basic plumbing around
Signed-off-by: Fabio M. Di Nitto <[email protected]>
1 parent e24ed69 commit ebfeb36

File tree

7 files changed

+245
-1
lines changed

7 files changed

+245
-1
lines changed

Makefile.am

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ libcrun_SOURCES = src/libcrun/utils.c \
6767
src/libcrun/linux.c \
6868
src/libcrun/mount_flags.c \
6969
src/libcrun/scheduler.c \
70+
src/libcrun/mempolicy.c \
7071
src/libcrun/seccomp.c \
7172
src/libcrun/seccomp_notify.c \
7273
src/libcrun/signals.c \
@@ -160,7 +161,7 @@ EXTRA_DIST = COPYING COPYING.libcrun README.md NEWS SECURITY.md rpm/crun.spec au
160161
src/libcrun/custom-handler.h src/libcrun/io_priority.h \
161162
src/libcrun/handlers/handler-utils.h \
162163
src/libcrun/linux.h src/libcrun/utils.h src/libcrun/error.h src/libcrun/criu.h \
163-
src/libcrun/scheduler.h src/libcrun/status.h src/libcrun/terminal.h \
164+
src/libcrun/scheduler.h src/libcrun/mempolicy.h src/libcrun/status.h src/libcrun/terminal.h \
164165
src/libcrun/mount_flags.h src/libcrun/intelrdt.h src/libcrun/ring_buffer.h src/libcrun/string_map.h \
165166
src/libcrun/net_device.h \
166167
crun.1.md crun.1 libcrun.lds \
@@ -225,6 +226,7 @@ PYTHON_TESTS = tests/test_capabilities.py \
225226
tests/test_hostname.py \
226227
tests/test_limits.py \
227228
tests/test_oci_features.py \
229+
tests/test_mempolicy.py \
228230
tests/test_mounts.py \
229231
tests/test_paths.py \
230232
tests/test_pid.py \

src/libcrun/container.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "container.h"
2424
#include "utils.h"
2525
#include "seccomp.h"
26+
#include "mempolicy.h"
2627
#ifdef HAVE_SECCOMP
2728
# include <seccomp.h>
2829
#endif
@@ -2805,6 +2806,10 @@ libcrun_container_run_internal (libcrun_container_t *container, libcrun_context_
28052806
if (UNLIKELY (ret < 0))
28062807
return ret;
28072808

2809+
ret = libcrun_set_mempolicy (def, err);
2810+
if (UNLIKELY (ret < 0))
2811+
return ret;
2812+
28082813
ret = libcrun_configure_handler (container_args.context->handler_manager,
28092814
container_args.context,
28102815
container,

src/libcrun/error.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,23 @@ libcrun_warning (const char *msg, ...)
480480
va_end (args_list);
481481
}
482482

483+
#ifdef HAVE_NUMA
484+
/* override libnuma internal numa_warn implementation
485+
* that is defined as WEAK to allow cunsumers to define
486+
* their own behavior.
487+
* symbol has to be public for linker to use our version
488+
* and allow to convert numa messages into libcrun messages */
489+
LIBCRUN_PUBLIC
490+
void
491+
numa_warn(int number, char *msg, ...)
492+
{
493+
va_list args_list;
494+
va_start (args_list, msg);
495+
write_log (0, LIBCRUN_VERBOSITY_WARNING, msg, args_list);
496+
va_end (args_list);
497+
}
498+
#endif
499+
483500
void
484501
libcrun_error (int errno_, const char *msg, ...)
485502
{

src/libcrun/linux.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "status.h"
5151
#include "criu.h"
5252
#include "scheduler.h"
53+
#include "mempolicy.h"
5354
#include "intelrdt.h"
5455
#include "io_priority.h"
5556
#include "net_device.h"

src/libcrun/mempolicy.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* crun - OCI runtime written in C
3+
*
4+
* Copyright (C) 2017, 2018, 2019 Giuseppe Scrivano <[email protected]>
5+
* crun is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation; either version 2.1 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* crun is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with crun. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include <config.h>
20+
#include "linux.h"
21+
#include "utils.h"
22+
#include <ocispec/runtime_spec_schema_config_schema.h>
23+
24+
#ifdef HAVE_NUMA
25+
#include <numa.h>
26+
#include <numaif.h>
27+
28+
#define CRUN_NUMA_API_VERSION 2 /* numa.h LIBNUMA_API_VERSION at the time of writing */
29+
#define CRUN_NUMA_MPOL_MAX 7 /* numaif.h MPOL_MAX at the time of writing */
30+
31+
#ifndef LIBNUMA_API_VERSION
32+
#error "Unable to determine libnuma api version"
33+
#else
34+
#if LIBNUMA_API_VERSION != CRUN_NUMA_API_VERSION
35+
#warning "This code was written with libnuma API version 2. numa.h reports a different version"
36+
#endif
37+
#if MPOL_MAX != CRUN_NUMA_MPOL_MAX
38+
#warning "This code was written with MPOL_MAX = 7. numaif.h reports a different value"
39+
#endif
40+
#endif
41+
42+
typedef struct {
43+
const char *name;
44+
int value;
45+
} str2int_map_t;
46+
47+
/* update mpol_mode_map based on numaif.h MPOL_MAX
48+
* the warn above will indicate that an update is required. */
49+
static str2int_map_t mpol_mode_map[] = {
50+
{ "MpolDefault", MPOL_DEFAULT },
51+
{ "MpolPreferred", MPOL_PREFERRED },
52+
{ "MpolBind", MPOL_BIND },
53+
{ "MpolInterleave", MPOL_INTERLEAVE },
54+
{ "MpolLocal", MPOL_LOCAL },
55+
{ "MpolPreferredMany", MPOL_PREFERRED_MANY },
56+
{ "MpolWeightedInterleave", MPOL_WEIGHTED_INTERLEAVE },
57+
{ NULL, -1 }
58+
};
59+
60+
/* flags cannot be tracked the same way as mode */
61+
static str2int_map_t mpol_flag_map[] = {
62+
{ "MpolFNumaBalancing", MPOL_F_NUMA_BALANCING },
63+
{ "MpolFRelativeNodes", MPOL_F_RELATIVE_NODES },
64+
{ "MpolFStaticNodes", MPOL_F_STATIC_NODES },
65+
{ NULL, -1 }
66+
};
67+
68+
static int mpol_str2int(const char *mode, const str2int_map_t *map)
69+
{
70+
int idx = 0;
71+
72+
while (map[idx].name != NULL) {
73+
if (!strcmp(map[idx].name, mode)) {
74+
return map[idx].value;
75+
}
76+
idx++;
77+
}
78+
79+
errno = EINVAL;
80+
return -1;
81+
}
82+
#endif
83+
84+
int
85+
libcrun_set_mempolicy (runtime_spec_schema_config_schema *def, libcrun_error_t *err)
86+
{
87+
#ifdef HAVE_NUMA
88+
runtime_spec_schema_config_linux_memory_policy *memory_policy = NULL;
89+
int mpol_mode = MPOL_DEFAULT;
90+
int mpol_flags = 0;
91+
struct bitmask *nodemask = NULL;
92+
93+
libcrun_debug("Initializing linux numa mempolicy");
94+
95+
if (def->linux && def->linux->memory_policy) {
96+
memory_policy = def->linux->memory_policy;
97+
98+
libcrun_debug("Validating linux numa mempolicy");
99+
100+
/* validate memory policy mode */
101+
if (!memory_policy->mode) {
102+
return crun_make_error(err, EINVAL, "linux numa mempolicy mode is missing from the configuration");
103+
}
104+
libcrun_debug("Validating mode: %s", memory_policy->mode);
105+
mpol_mode = mpol_str2int(memory_policy->mode, mpol_mode_map);
106+
if (mpol_mode < 0) {
107+
return crun_make_error(err, EINVAL, "Requested linux numa mempolicy mode '%s' is unknown", memory_policy->mode);
108+
}
109+
110+
/* validate memory nodes */
111+
if (!memory_policy->nodes) {
112+
return crun_make_error(err, EINVAL, "linux numa mempolicy nodes is missing from the configuration");
113+
}
114+
libcrun_debug("Validating nodes: %s", memory_policy->nodes);
115+
/* validation is done by libnuma based on hw environment
116+
* and numa_warn symbol is overridden in error.c to access
117+
* write_log */
118+
nodemask = numa_parse_nodestring_all(memory_policy->nodes);
119+
if (!nodemask) {
120+
return crun_make_error(err, EINVAL, "numa_parse_nodestring_all validation failed");
121+
}
122+
123+
libcrun_debug("Checking hardware numa availability");
124+
if (numa_available() < 0) {
125+
return crun_make_error(err, ENOENT, "linux numa not supported on current hardware");
126+
}
127+
} else {
128+
libcrun_debug("no linux numa mempolicy configuration found");
129+
}
130+
#endif
131+
return 0;
132+
}

src/libcrun/mempolicy.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* crun - OCI runtime written in C
3+
*
4+
* Copyright (C) 2017, 2018, 2019, 2021 Giuseppe Scrivano <[email protected]>
5+
* crun is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation; either version 2.1 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* crun is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with crun. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#ifndef MEMPOLICY_H
19+
#define MEMPOLICY_H
20+
#include <config.h>
21+
#include "error.h"
22+
#include "container.h"
23+
#include "status.h"
24+
25+
int libcrun_set_mempolicy (runtime_spec_schema_config_schema *def, libcrun_error_t *err);
26+
27+
#endif

tests/test_mempolicy.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/env python3
2+
# crun - OCI runtime written in C
3+
#
4+
# Copyright (C) 2017, 2018, 2019 Giuseppe Scrivano <[email protected]>
5+
# crun is free software; you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation; either version 2 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# crun is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with crun. If not, see <http://www.gnu.org/licenses/>.
17+
18+
import subprocess
19+
import sys
20+
import json
21+
import os
22+
from tests_utils import *
23+
24+
def check_mempolicy_prerequisites():
25+
"""Check all prerequisites for numa mempolicy tests. Returns 77 (skip) if not met, 0 if OK"""
26+
# TODO: stub entry for now.
27+
# there is no numa python bindings packaged for most distros.
28+
# the only way would be to call numactl shell and parse the output or check /proc/self/numa_maps
29+
return 0
30+
31+
def test_mempolicy():
32+
"""Test numa mempolicy"""
33+
ret = check_mempolicy_prerequisites()
34+
if ret != 0:
35+
return ret
36+
37+
conf = base_config()
38+
conf['process']['args'] = ['/init', 'cat', '/proc/self/numa_maps']
39+
add_all_namespaces(conf)
40+
conf['linux']['memoryPolicy'] = { "mode": "MpolDefault", "nodes": "all" }
41+
42+
cid = None
43+
try:
44+
out, cid = run_and_get_output(conf, command='run', debug=True)
45+
print(out)
46+
except Exception as e:
47+
sys.stderr.write("# Test failed with exception: %s\n" % str(e))
48+
return -1
49+
finally:
50+
if cid is not None:
51+
run_crun_command(["delete", "-f", cid])
52+
53+
return 0
54+
55+
all_tests = {
56+
"mempolicy": test_mempolicy,
57+
}
58+
59+
if __name__ == "__main__":
60+
tests_main(all_tests)

0 commit comments

Comments
 (0)