mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
Currently, in the target process, device-related restore operations and other restore operations almost run sequentially. When the target process executes the corresponding CRIU hook functions, it can't perform other restore operations. However, for GPU applications, some device restore operations have no logical dependencies on other common restore operations and can be parallelized with other operations to speed up the process. Instead of launching a thread in child processes for parallelization, this patch chooses to add a new hook, `POST_FORKING`, in the main CRIU process to handle these restore operations. This is because the restoration of memory state in the restore blob is one of the most time-consuming parts of all restore logic. The main CRIU process can easily parallelize these operations, whereas parallelizing in threads within child processes is challenging. - POST_FORKING *POST_FORKING: Hook to enable the main CRIU process to perform some restore operations of plugins. Signed-off-by: Yanning Yang <yangyanning@sjtu.edu.cn>
271 lines
6 KiB
C
271 lines
6 KiB
C
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include "cr_options.h"
|
|
#include "common/compiler.h"
|
|
#include "xmalloc.h"
|
|
#include "plugin.h"
|
|
#include "servicefd.h"
|
|
#include "common/list.h"
|
|
#include "log.h"
|
|
|
|
cr_plugin_ctl_t cr_plugin_ctl = {
|
|
.head.next = &cr_plugin_ctl.head,
|
|
.head.prev = &cr_plugin_ctl.head,
|
|
};
|
|
|
|
/*
|
|
* If we met old version of a plugin, selfgenerate a plugin descriptor for it.
|
|
*/
|
|
static cr_plugin_desc_t *cr_gen_plugin_desc(void *h, char *path)
|
|
{
|
|
cr_plugin_desc_t *d;
|
|
|
|
d = xzalloc(sizeof(*d));
|
|
if (!d)
|
|
return NULL;
|
|
|
|
d->name = xstrdup(path);
|
|
d->max_hooks = CR_PLUGIN_HOOK__MAX;
|
|
d->version = CRIU_PLUGIN_VERSION_OLD;
|
|
|
|
pr_warn("Generating dynamic descriptor for plugin `%s'."
|
|
"Won't work in next version of the program."
|
|
"Please update your plugin.\n",
|
|
path);
|
|
|
|
#define __assign_hook(__hook, __name) \
|
|
do { \
|
|
void *name; \
|
|
name = dlsym(h, __name); \
|
|
if (name) \
|
|
d->hooks[CR_PLUGIN_HOOK__##__hook] = name; \
|
|
} while (0)
|
|
|
|
__assign_hook(DUMP_UNIX_SK, "cr_plugin_dump_unix_sk");
|
|
__assign_hook(RESTORE_UNIX_SK, "cr_plugin_restore_unix_sk");
|
|
__assign_hook(DUMP_EXT_FILE, "cr_plugin_dump_file");
|
|
__assign_hook(RESTORE_EXT_FILE, "cr_plugin_restore_file");
|
|
__assign_hook(DUMP_EXT_MOUNT, "cr_plugin_dump_ext_mount");
|
|
__assign_hook(RESTORE_EXT_MOUNT, "cr_plugin_restore_ext_mount");
|
|
__assign_hook(DUMP_EXT_LINK, "cr_plugin_dump_ext_link");
|
|
__assign_hook(HANDLE_DEVICE_VMA, "cr_plugin_handle_device_vma");
|
|
__assign_hook(UPDATE_VMA_MAP, "cr_plugin_update_vma_map");
|
|
__assign_hook(RESUME_DEVICES_LATE, "cr_plugin_resume_devices_late");
|
|
__assign_hook(PAUSE_DEVICES, "cr_plugin_pause_devices");
|
|
__assign_hook(CHECKPOINT_DEVICES, "cr_plugin_checkpoint_devices");
|
|
__assign_hook(POST_FORKING, "cr_plugin_post_forking");
|
|
|
|
#undef __assign_hook
|
|
|
|
d->init = dlsym(h, "cr_plugin_init");
|
|
d->exit = dlsym(h, "cr_plugin_fini");
|
|
|
|
return d;
|
|
}
|
|
|
|
static void show_plugin_desc(cr_plugin_desc_t *d)
|
|
{
|
|
size_t i;
|
|
|
|
pr_debug("Plugin \"%s\" (version %u hooks %u)\n", d->name, d->version, d->max_hooks);
|
|
for (i = 0; i < d->max_hooks; i++) {
|
|
if (d->hooks[i])
|
|
pr_debug("\t%4zu -> %p\n", i, d->hooks[i]);
|
|
}
|
|
}
|
|
|
|
static int verify_plugin(cr_plugin_desc_t *d)
|
|
{
|
|
if (d->version > CRIU_PLUGIN_VERSION) {
|
|
pr_debug("Plugin %s has version %x while max %x supported\n", d->name, d->version, CRIU_PLUGIN_VERSION);
|
|
return -1;
|
|
}
|
|
|
|
if (d->max_hooks > CR_PLUGIN_HOOK__MAX) {
|
|
pr_debug("Plugin %s has %u assigned while max %u supported\n", d->name, d->max_hooks,
|
|
CR_PLUGIN_HOOK__MAX);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int criu_get_image_dir(void)
|
|
{
|
|
return get_service_fd(IMG_FD_OFF);
|
|
}
|
|
|
|
static int cr_lib_load(int stage, char *path)
|
|
{
|
|
cr_plugin_desc_t *d;
|
|
plugin_desc_t *this;
|
|
size_t i;
|
|
void *h;
|
|
bool allocated = false;
|
|
|
|
h = dlopen(path, RTLD_LAZY);
|
|
if (h == NULL) {
|
|
pr_err("Unable to load %s: %s\n", path, dlerror());
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Load plugin descriptor. If plugin is too old -- create
|
|
* dynamic plugin descriptor. In most cases this won't
|
|
* be a common operation and plugins are not supposed to
|
|
* be changing own format frequently.
|
|
*/
|
|
d = dlsym(h, "CR_PLUGIN_DESC");
|
|
if (!d) {
|
|
d = cr_gen_plugin_desc(h, path);
|
|
if (!d) {
|
|
pr_err("Can't load plugin %s\n", path);
|
|
goto error_close;
|
|
}
|
|
allocated = true;
|
|
}
|
|
|
|
this = xzalloc(sizeof(*this));
|
|
if (!this)
|
|
goto error_close;
|
|
|
|
if (verify_plugin(d)) {
|
|
pr_err("Corrupted plugin %s\n", path);
|
|
goto error_free;
|
|
}
|
|
|
|
this->d = d;
|
|
this->dlhandle = h;
|
|
INIT_LIST_HEAD(&this->list);
|
|
|
|
for (i = 0; i < d->max_hooks; i++)
|
|
INIT_LIST_HEAD(&this->link[i]);
|
|
|
|
list_add_tail(&this->list, &cr_plugin_ctl.head);
|
|
show_plugin_desc(d);
|
|
|
|
if (d->init && d->init(stage)) {
|
|
pr_err("Failed in init(%d) of \"%s\"\n", stage, d->name);
|
|
list_del(&this->list);
|
|
goto error_free;
|
|
}
|
|
|
|
/*
|
|
* Chain hooks into appropriate places for
|
|
* fast handler access.
|
|
*/
|
|
for (i = 0; i < d->max_hooks; i++) {
|
|
if (!d->hooks[i])
|
|
continue;
|
|
list_add_tail(&this->link[i], &cr_plugin_ctl.hook_chain[i]);
|
|
}
|
|
|
|
return 0;
|
|
|
|
error_free:
|
|
xfree(this);
|
|
error_close:
|
|
dlclose(h);
|
|
if (allocated)
|
|
xfree(d);
|
|
return -1;
|
|
}
|
|
|
|
void cr_plugin_fini(int stage, int ret)
|
|
{
|
|
plugin_desc_t *this, *tmp;
|
|
|
|
list_for_each_entry_safe(this, tmp, &cr_plugin_ctl.head, list) {
|
|
void *h = this->dlhandle;
|
|
size_t i;
|
|
|
|
list_del(&this->list);
|
|
if (this->d->exit)
|
|
this->d->exit(stage, ret);
|
|
|
|
for (i = 0; i < this->d->max_hooks; i++) {
|
|
if (!list_empty(&this->link[i]))
|
|
list_del(&this->link[i]);
|
|
}
|
|
|
|
if (this->d->version == CRIU_PLUGIN_VERSION_OLD)
|
|
xfree(this->d);
|
|
dlclose(h);
|
|
}
|
|
}
|
|
|
|
int cr_plugin_init(int stage)
|
|
{
|
|
int exit_code = -1;
|
|
char *path;
|
|
size_t i;
|
|
DIR *d;
|
|
|
|
INIT_LIST_HEAD(&cr_plugin_ctl.head);
|
|
for (i = 0; i < ARRAY_SIZE(cr_plugin_ctl.hook_chain); i++)
|
|
INIT_LIST_HEAD(&cr_plugin_ctl.hook_chain[i]);
|
|
|
|
if (opts.libdir == NULL) {
|
|
path = getenv("CRIU_LIBS_DIR");
|
|
if (path)
|
|
SET_CHAR_OPTS(libdir, path);
|
|
else {
|
|
if (access(CR_PLUGIN_DEFAULT, F_OK))
|
|
return 0;
|
|
|
|
SET_CHAR_OPTS(libdir, CR_PLUGIN_DEFAULT);
|
|
}
|
|
}
|
|
|
|
d = opendir(opts.libdir);
|
|
if (d == NULL) {
|
|
pr_perror("Unable to open directory %s", opts.libdir);
|
|
return -1;
|
|
}
|
|
|
|
while (1) {
|
|
char path[PATH_MAX];
|
|
struct dirent *de;
|
|
int len;
|
|
|
|
errno = 0;
|
|
de = readdir(d);
|
|
if (de == NULL) {
|
|
if (errno == 0)
|
|
break;
|
|
pr_perror("Unable to read the libraries directory");
|
|
goto err;
|
|
}
|
|
|
|
len = strlen(de->d_name);
|
|
|
|
if (len < 3 || strncmp(de->d_name + len - 3, ".so", 3))
|
|
continue;
|
|
|
|
if (snprintf(path, sizeof(path), "%s/%s", opts.libdir, de->d_name) >= sizeof(path)) {
|
|
pr_err("Unable to build plugin path\n");
|
|
goto err;
|
|
}
|
|
|
|
if (cr_lib_load(stage, path))
|
|
goto err;
|
|
}
|
|
|
|
if (stage == CR_PLUGIN_STAGE__RESTORE && check_inventory_plugins())
|
|
goto err;
|
|
|
|
exit_code = 0;
|
|
err:
|
|
closedir(d);
|
|
|
|
if (exit_code)
|
|
cr_plugin_fini(stage, exit_code);
|
|
|
|
return exit_code;
|
|
}
|