Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libcrun/container.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ static char *potentially_unsafe_annotations[] = {
"io.kubernetes.cri.container-type",
"run.oci.",
"org.criu.",
"krun.",
};

#define SYNC_SOCKET_MESSAGE_LEN(x, l) (offsetof (struct sync_socket_message_s, message) + l)
Expand Down
112 changes: 82 additions & 30 deletions src/libcrun/handlers/krun.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
/* libkrun has a hard-limit of 16 vCPUs per microVM. */
#define LIBKRUN_MAX_VCPUS 16

/* If the user doesn't configure the vCPU count, fallback to this value. */
#define LIBKRUN_DEFAULT_VCPUS 1

/* If the user doesn't configure the RAM amount, fallback to this value. */
#define LIBKRUN_DEFAULT_RAM_MIB 1024

/* crun dumps the container configuration into this file, which will be read by
* libkrun to set up the environment for the workload inside the microVM.
*/
Expand Down Expand Up @@ -184,68 +190,114 @@ libkrun_read_vm_config (yajl_val *config_tree, libcrun_error_t *err)
return 0;
}

/*
* Default to parsing the OCI annotations to find the specified microVM
* attribute. If the desired attribute is not found within the OCI annotations
* or the krun_vm.json file, then return a negative integer.
*
* The configuration precedence is as follows:
* OCI annotations -> krun_vm.json.
*/
static int
libkrun_configure_vm (uint32_t ctx_id, void *handle, bool *configured, yajl_val *config_tree, libcrun_error_t *err)
libkrun_parse_resource_configuration (yajl_val *config_tree, libcrun_container_t *container, const char *annotation, const char *path[])
{
char *val_str, *endptr;
int val = -1;
yajl_val val_json = NULL;

val_str = (char *) find_annotation (container, annotation);
if (val_str != NULL)
{
errno = 0;
val = (int) strtol (val_str, &endptr, 10);
if (errno != 0 || endptr == val_str || *endptr != '\0')
/* Annotations value is not a valid integer. */
error (EXIT_FAILURE, 0, "krun annotation %s value cannot be converted to an integer", annotation);

if (val <= 0)
error (EXIT_FAILURE, 0, "krun annotation %s value must be a positive integer", annotation);

return val;
}
else if (*config_tree != NULL)
{
val_json = yajl_tree_get (*config_tree, path, yajl_t_number);
if (! YAJL_IS_INTEGER (val_json))
error (EXIT_FAILURE, 0, "krun krun_vm.json %s value is not an integer", path[0]);

if (! (val = (int) YAJL_GET_INTEGER (val_json)))
error (EXIT_FAILURE, 0, "krun krun_vm.json %s value must be a positive integer", path[0]);
}

return val;
}

static int
libkrun_configure_vm (uint32_t ctx_id, void *handle, bool *configured, yajl_val *config_tree, libcrun_container_t *container, libcrun_error_t *err)
{
int32_t (*krun_set_vm_config) (uint32_t ctx_id, uint8_t num_vcpus, uint32_t ram_mib);
yajl_val cpus = NULL;
yajl_val ram_mib = NULL;
int cpus, ram_mib, ret;
const char *path_cpus[] = { "cpus", (const char *) 0 };
const char *path_ram_mib[] = { "ram_mib", (const char *) 0 };
int ret;

if (*config_tree == NULL)
return 0;
cpus = libkrun_parse_resource_configuration (config_tree, container, "krun.cpus", path_cpus);
if (cpus <= 0)
cpus = LIBKRUN_DEFAULT_VCPUS;

/* Try to configure an external kernel. If the configuration file doesn't
* specify a kernel, libkrun automatically fall back to using libkrunfw,
* if the library is present and was loaded while creating the context.
*/
ret = libkrun_configure_kernel (ctx_id, handle, config_tree, err);
if (UNLIKELY (ret))
return ret;

cpus = yajl_tree_get (*config_tree, path_cpus, yajl_t_number);
ram_mib = yajl_tree_get (*config_tree, path_ram_mib, yajl_t_number);
/* Both cpus and ram_mib must be present at the same time */
if (cpus == NULL || ram_mib == NULL || ! YAJL_IS_INTEGER (cpus) || ! YAJL_IS_INTEGER (ram_mib))
return 0;
ram_mib = libkrun_parse_resource_configuration (config_tree, container, "krun.ram_mib", path_ram_mib);
if (ram_mib <= 0)
ram_mib = LIBKRUN_DEFAULT_RAM_MIB;

krun_set_vm_config = dlsym (handle, "krun_set_vm_config");

if (krun_set_vm_config == NULL)
return crun_make_error (err, 0, "could not find symbol in the krun library");

ret = krun_set_vm_config (ctx_id, YAJL_GET_INTEGER (cpus), YAJL_GET_INTEGER (ram_mib));
ret = krun_set_vm_config (ctx_id, cpus, ram_mib);
if (UNLIKELY (ret < 0))
return crun_make_error (err, -ret, "could not set krun vm configuration");

if (*config_tree != NULL)
{
/* Try to configure an external kernel. If the configuration file doesn't
* specify a kernel, libkrun automatically fall back to using libkrunfw,
* if the library is present and was loaded while creating the context.
*/
ret = libkrun_configure_kernel (ctx_id, handle, config_tree, err);
if (UNLIKELY (ret))
return ret;
}

*configured = true;

return 0;
}

static int
libkrun_configure_flavor (void *cookie, yajl_val *config_tree, libcrun_error_t *err)
libkrun_configure_flavor (void *cookie, yajl_val *config_tree, libcrun_container_t *container, libcrun_error_t *err)
{
int ret, sev_indicated = 0, nitro_indicated = 0;
const char *path_flavor[] = { "flavor", (const char *) 0 };
struct krun_config *kconf = (struct krun_config *) cookie;
yajl_val val_flavor = NULL;
char *flavor = NULL;
const char *flavor = NULL;
void *close_handles[2];

close_handles[0] = NULL;
close_handles[1] = NULL;

// Read if the SEV flavor was indicated in the krun VM config.
val_flavor = yajl_tree_get (*config_tree, path_flavor, yajl_t_string);
if (val_flavor != NULL && YAJL_IS_STRING (val_flavor))
// Check if the user provided the krun variant through OCI annotations.
flavor = find_annotation (container, "krun.variant");
if (flavor == NULL && *config_tree != NULL)
{
flavor = YAJL_GET_STRING (val_flavor);
// If the user doesn't specify a variant via OCI annotations, check the krun VM config to see if the "flavor" field was populated.
val_flavor = yajl_tree_get (*config_tree, path_flavor, yajl_t_string);
if (val_flavor != NULL && YAJL_IS_STRING (val_flavor))
flavor = YAJL_GET_STRING (val_flavor);
}

// The SEV flavor will be used if the krun VM config indicates to use SEV
// within the "flavor" field.
if (flavor != NULL)
{
sev_indicated |= strcmp (flavor, KRUN_FLAVOR_SEV) == 0;
nitro_indicated |= strcmp (flavor, KRUN_FLAVOR_NITRO) == 0;
}
Expand Down Expand Up @@ -327,7 +379,7 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname
if (UNLIKELY (ret < 0))
error (EXIT_FAILURE, -ret, "libkrun VM config exists, but unable to parse");

ret = libkrun_configure_flavor (cookie, &config_tree, &err);
ret = libkrun_configure_flavor (cookie, &config_tree, container, &err);
if (UNLIKELY (ret < 0))
error (EXIT_FAILURE, -ret, "unable to configure libkrun flavor");

Expand Down Expand Up @@ -391,7 +443,7 @@ libkrun_exec (void *cookie, libcrun_container_t *container, const char *pathname
error (EXIT_FAILURE, -ret, "could not set enclave execution arguments");
}

ret = libkrun_configure_vm (ctx_id, handle, &configured, &config_tree, &err);
ret = libkrun_configure_vm (ctx_id, handle, &configured, &config_tree, container, &err);
if (UNLIKELY (ret))
{
libcrun_error_t *tmp_err = &err;
Expand Down
1 change: 1 addition & 0 deletions tests/test_oci_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def test_crun_features():
"io.kubernetes.cri.container-type",
"run.oci.",
"org.criu.",
"krun.",
]
}

Expand Down
Loading