1
0
mirror of https://github.com/opencontainers/runc.git synced 2026-02-05 18:45:28 +01:00
Files
Kir Kolyshkin 10ca66bff5 runc exec: implement CPU affinity
As per
- https://github.com/opencontainers/runtime-spec/pull/1253
- https://github.com/opencontainers/runtime-spec/pull/1261

CPU affinity can be set in two ways:
1. When creating/starting a container, in config.json's
   Process.ExecCPUAffinity, which is when applied to all execs.
2. When running an exec, in process.json's CPUAffinity, which
   applied to a given exec and overrides the value from (1).

Add some basic tests.

Note that older kernels (RHEL8, Ubuntu 20.04) change CPU affinity of a
process to that of a container's cgroup, as soon as it is moved to that
cgroup, while newer kernels (Ubuntu 24.04, Fedora 41) don't do that.

Because of the above,
 - it's impossible to really test initial CPU affinity without adding
   debug logging to libcontainer/nsenter;
 - for older kernels, there can be a brief moment when exec's affinity
   is different than either initial or final affinity being set;
 - exec's final CPU affinity, if not specified, can be different
   depending on the kernel, therefore we don't test it.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2025-03-02 19:17:41 -08:00

89 lines
1.7 KiB
C

#define _GNU_SOURCE
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "log.h"
#include "getenv.h"
static const char *level_str[] = { "panic", "fatal", "error", "warning", "info", "debug", "trace" };
int logfd = -1;
static int loglevel = DEBUG;
extern char *escape_json_string(char *str);
void setup_logpipe(void)
{
int i;
i = getenv_int("_LIBCONTAINER_LOGPIPE");
if (i < 0) {
/* We are not runc init, or log pipe was not provided. */
return;
}
logfd = i;
i = getenv_int("_LIBCONTAINER_LOGLEVEL");
if (i < 0)
return;
loglevel = i;
}
bool log_enabled_for(int level)
{
return (logfd >= 0 && level <= loglevel);
}
/* Defined in nsexec.c */
extern int current_stage;
void write_log(int level, const char *format, ...)
{
char *message = NULL, *stage = NULL, *json = NULL;
va_list args;
int ret;
if (!log_enabled_for(level))
return;
va_start(args, format);
ret = vasprintf(&message, format, args);
va_end(args);
if (ret < 0) {
message = NULL;
goto out;
}
message = escape_json_string(message);
if (current_stage < 0) {
stage = strdup("nsexec");
if (stage == NULL)
goto out;
} else {
ret = asprintf(&stage, "nsexec-%d", current_stage);
if (ret < 0) {
stage = NULL;
goto out;
}
}
ret = asprintf(&json, "{\"level\":\"%s\", \"msg\": \"%s[%d]: %s\"}\n",
level_str[level], stage, getpid(), message);
if (ret < 0) {
json = NULL;
goto out;
}
/* This logging is on a best-effort basis. In case of a short or failed
* write there is nothing we can do, so just ignore write() errors.
*/
ssize_t __attribute__((unused)) __res = write(logfd, json, ret);
out:
free(message);
free(stage);
free(json);
}