1 Star 0 Fork 38

EulerOSWander/criu

forked from src-openEuler/criu 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
0052-optimization-parallel-collecting-vmas.patch 13.56 KB
一键复制 编辑 原始数据 按行查看 历史
river 提交于 2022-04-13 15:05 +08:00 . criu: backport kinds of features/bugfix
From ade879e6ccdc4c74a1c153f0750d2cd87ec8a4ec Mon Sep 17 00:00:00 2001
From: Jingxian He <hejingxian@huawei.com>
Date: Tue, 30 Nov 2021 10:26:10 +0800
Subject: [PATCH 52/72] optimization: parallel collecting vmas
In order to improve criu dump performance, make the collecting vmas
operation parallel run with the other collecting operations.
In order to prevent the concurrency problem by `find_unused_fd`, only
the main root task will parallel.
Usage:
criu --parallel
Note:
Ensure criu can use multi-core, otherwise the performance will
deterioration.
Signed-off-by: fu.lin <fulin10@huawei.com>
Signed-off-by: hewenliang <hewenliang4@huawei.com>
Signed-off-by: Jingxian He <hejingxian@huawei.com>
---
criu/Makefile.crtools | 1 +
criu/Makefile.packages | 1 +
criu/config.c | 1 +
criu/cr-dump.c | 53 +++++++++++-----
criu/crtools.c | 1 +
criu/include/cr_options.h | 1 +
criu/include/pstree.h | 3 +
criu/include/taskqueue.h | 50 +++++++++++++++
criu/namespaces.c | 9 ++-
criu/taskqueue.c | 124 ++++++++++++++++++++++++++++++++++++++
10 files changed, 228 insertions(+), 16 deletions(-)
create mode 100644 criu/include/taskqueue.h
create mode 100644 criu/taskqueue.c
diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
index 3bb7c19..2ad0207 100644
--- a/criu/Makefile.crtools
+++ b/criu/Makefile.crtools
@@ -97,6 +97,7 @@ obj-y += exit-notify.o
obj-y += reserved-ports.o
obj-y += orphan-inode.o
obj-y += kmsg.o
+obj-y += taskqueue.o
obj-$(CONFIG_HAS_LIBBPF) += bpfmap.o
obj-$(CONFIG_COMPAT) += pie-util-vdso-elf32.o
CFLAGS_pie-util-vdso-elf32.o += -DCONFIG_VDSO_32
diff --git a/criu/Makefile.packages b/criu/Makefile.packages
index 13c346f..851489b 100644
--- a/criu/Makefile.packages
+++ b/criu/Makefile.packages
@@ -31,6 +31,7 @@ REQ-RPM-PKG-TEST-NAMES += $(PYTHON)-pyyaml
endif
export LIBS += -lprotobuf-c -ldl -lnl-3 -lsoccr -Lsoccr/ -lnet
+export LIBS += -lpthread
check-packages-failed:
$(warning Can not find some of the required libraries)
diff --git a/criu/config.c b/criu/config.c
index ae5f81e..fdbc5eb 100644
--- a/criu/config.c
+++ b/criu/config.c
@@ -709,6 +709,7 @@ int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd,
{ "share-dst-ports", required_argument, 0, 2000 },
{ "share-src-ports", required_argument, 0, 2001 },
{ "reserve-ports", required_argument, 0, 'P' },
+ BOOL_OPT("parallel", &opts.parallel),
{},
};
diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index a8ab61e..ee826c0 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -17,6 +17,7 @@
#include <sched.h>
#include <sys/resource.h>
+#include <sys/sysinfo.h>
#include "types.h"
#include "protobuf.h"
@@ -90,6 +91,7 @@
#include "notifier.h"
#include "files-chr.h"
#include "reserved-ports.h"
+#include "taskqueue.h"
/*
* Architectures can overwrite this function to restore register sets that
@@ -424,7 +426,7 @@ static int dump_pid_misc(pid_t pid, TaskCoreEntry *tc)
return 0;
}
-static int dump_filemap(struct vma_area *vma_area, int fd)
+int dump_filemap(struct vma_area *vma_area, int fd)
{
struct fd_parms p = FD_PARMS_INIT;
VmaEntry *vma = vma_area->e;
@@ -1504,7 +1506,7 @@ err_cure:
static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
{
pid_t pid = item->pid->real;
- struct vm_area_list vmas;
+ struct vm_area_list *vmas = NULL;
struct parasite_ctl *parasite_ctl;
int ret, exit_code = -1;
struct parasite_dump_misc misc;
@@ -1513,8 +1515,6 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
struct proc_posix_timers_stat proc_args;
struct mem_dump_ctl mdc;
- vm_area_list_init(&vmas);
-
pr_info("========================================\n");
pr_info("Dumping task (pid: %d)\n", pid);
pr_info("========================================\n");
@@ -1525,12 +1525,23 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
*/
return 0;
+ if (!opts.parallel || root_item->pid->real != item->pid->real ) {
+ vmas = xmalloc(sizeof(struct vm_area_list));
+ if (vmas == NULL) {
+ pr_err("xmalloc no memory\n");
+ return -1;
+ }
+ vm_area_list_init(vmas);
+ } else
+ vmas = item->maps_info.vmas;
+
pr_info("Obtaining task stat ... \n");
ret = parse_pid_stat(pid, &pps_buf);
if (ret < 0)
goto err;
- ret = collect_mappings(pid, &vmas, dump_filemap);
+ ret = (opts.parallel && root_item->pid->real == item->pid->real) ?
+ 0 : collect_mappings(pid, vmas, dump_filemap);
if (ret) {
pr_err("Collect mappings (pid: %d) failed with %d\n", pid, ret);
goto err;
@@ -1570,7 +1581,10 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
goto err;
}
- parasite_ctl = parasite_infect_seized(pid, item, &vmas);
+ if (opts.parallel && end_collect_mappings_thread(item))
+ goto err;
+
+ parasite_ctl = parasite_infect_seized(pid, item, vmas);
if (!parasite_ctl) {
pr_err("Can't infect (pid: %d) with parasite\n", pid);
goto err;
@@ -1600,13 +1614,13 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
goto err_cure_imgset;
}
- ret = parasite_fixup_vdso(parasite_ctl, pid, &vmas);
+ ret = parasite_fixup_vdso(parasite_ctl, pid, vmas);
if (ret) {
pr_err("Can't fixup vdso VMAs (pid: %d)\n", pid);
goto err_cure_imgset;
}
- ret = parasite_collect_aios(parasite_ctl, &vmas); /* FIXME -- merge with above */
+ ret = parasite_collect_aios(parasite_ctl, vmas); /* FIXME -- merge with above */
if (ret) {
pr_err("Failed to check aio rings (pid: %d)\n", pid);
goto err_cure_imgset;
@@ -1658,7 +1672,7 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
mdc.stat = &pps_buf;
mdc.parent_ie = parent_ie;
- ret = parasite_dump_pages_seized(item, &vmas, &mdc, parasite_ctl);
+ ret = parasite_dump_pages_seized(item, vmas, &mdc, parasite_ctl);
if (ret)
goto err_cure;
@@ -1719,7 +1733,7 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
goto err;
}
- ret = dump_task_mm(pid, &pps_buf, &misc, &vmas, cr_imgset);
+ ret = dump_task_mm(pid, &pps_buf, &misc, vmas, cr_imgset);
if (ret) {
pr_err("Dump mappings (pid: %d) failed with %d\n", pid, ret);
goto err;
@@ -1735,7 +1749,8 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
exit_code = 0;
err:
close_pid_proc();
- free_mappings(&vmas);
+ free_mappings(vmas);
+ free(vmas);
xfree(dfds);
return exit_code;
@@ -1893,6 +1908,9 @@ int cr_pre_dump_tasks(pid_t pid)
if (opts.dump_char_dev && parse_devname() < 0)
goto err;
+ if (opts.parallel && init_parallel_env() != 0)
+ goto err;
+
root_item = alloc_pstree_item();
if (!root_item)
goto err;
@@ -2107,6 +2125,13 @@ static int cr_dump_finish(int ret)
write_stats(DUMP_STATS);
pr_info("Dumping finished successfully\n");
}
+
+ /*
+ * Don't care threads' status and ignore unfree resources, use
+ * `exit_group()` to ensure exit all threads.
+ */
+ syscall(SYS_exit_group, post_dump_ret ? : (ret != 0));
+
return post_dump_ret ?: (ret != 0);
}
@@ -2203,13 +2228,13 @@ int cr_dump_tasks(pid_t pid)
if (collect_file_locks())
goto err;
- if (collect_namespaces(true) < 0)
- goto err;
-
glob_imgset = cr_glob_imgset_open(O_DUMP);
if (!glob_imgset)
goto err;
+ if (collect_namespaces(true) < 0)
+ goto err;
+
if (seccomp_collect_dump_filters() < 0)
goto err;
diff --git a/criu/crtools.c b/criu/crtools.c
index cc0a18f..c20b3b7 100644
--- a/criu/crtools.c
+++ b/criu/crtools.c
@@ -472,6 +472,7 @@ usage:
" --weak-file-check Allow file size and mod larger than dumping value\n"
" --file-locks-repair Use repair mode to dump and restore file locks\n"
" --reserve-ports Reserve src ports in kernel\n"
+ " --parallel Collect smaps parallel to accellrate dumping speed\n"
"\n"
"Check options:\n"
" Without options, \"criu check\" checks availability of absolutely required\n"
diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h
index 3b61c6b..6478d4d 100644
--- a/criu/include/cr_options.h
+++ b/criu/include/cr_options.h
@@ -201,6 +201,7 @@ struct cr_options {
char *share_dst_ports;
char *share_src_ports;
int reserve_ports;
+ int parallel;
};
extern struct cr_options opts;
diff --git a/criu/include/pstree.h b/criu/include/pstree.h
index 97bef11..87e4c47 100644
--- a/criu/include/pstree.h
+++ b/criu/include/pstree.h
@@ -1,6 +1,8 @@
#ifndef __CR_PSTREE_H__
#define __CR_PSTREE_H__
+#include "taskqueue.h"
+
#include "common/list.h"
#include "common/lock.h"
#include "pid.h"
@@ -31,6 +33,7 @@ struct pstree_item {
futex_t task_st;
unsigned long task_st_le_bits;
};
+ struct mappings_info maps_info;
};
static inline pid_t vpid(const struct pstree_item *i)
diff --git a/criu/include/taskqueue.h b/criu/include/taskqueue.h
new file mode 100644
index 0000000..16f9e3d
--- /dev/null
+++ b/criu/include/taskqueue.h
@@ -0,0 +1,50 @@
+#ifndef __CR_TASKQUEUE_H__
+#define __CR_TASKQUEUE_H__
+
+#include <stdbool.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include "vma.h"
+#include "pstree.h"
+
+#include "common/list.h"
+
+#define TASKQUEUE_HASH_SIZE 8
+
+struct taskqueue {
+ pthread_t task;
+ void *(*routine)(void *);
+ void *arg;
+ int result;
+};
+#define queue_task queue.task
+#define queue_routine queue.routine
+#define queue_arg queue.arg
+#define queue_result queue.result
+
+int init_parallel_env(void);
+
+static inline int taskqueue_create(struct taskqueue *queue)
+{
+ return pthread_create(&queue->task, NULL, queue->routine, queue->arg);
+}
+
+static inline int taskqueue_join(struct taskqueue *queue)
+{
+ return pthread_join(queue->task, NULL);
+}
+
+/* parallel collect smaps */
+struct mappings_info {
+ struct hlist_node hash;
+ pid_t pid;
+ struct vm_area_list *vmas;
+ dump_filemap_t dump_file;
+ struct taskqueue queue;
+};
+
+int start_collect_mappings_thread(void);
+int end_collect_mappings_thread(struct pstree_item *item);
+
+#endif /* __CR_TASKQUEUE_H__ */
diff --git a/criu/namespaces.c b/criu/namespaces.c
index 7fa5868..05e6732 100644
--- a/criu/namespaces.c
+++ b/criu/namespaces.c
@@ -28,6 +28,7 @@
#include "cgroup.h"
#include "fdstore.h"
#include "kerndat.h"
+#include "taskqueue.h"
#include "protobuf.h"
#include "util.h"
@@ -1607,11 +1608,15 @@ int collect_namespaces(bool for_dump)
{
int ret;
- ret = collect_user_namespaces(for_dump);
+ ret = collect_mnt_namespaces(for_dump);
if (ret < 0)
return ret;
- ret = collect_mnt_namespaces(for_dump);
+ /* need mnt info provided by `mntinfo` */
+ if (opts.parallel && start_collect_mappings_thread())
+ return -1;
+
+ ret = collect_user_namespaces(for_dump);
if (ret < 0)
return ret;
diff --git a/criu/taskqueue.c b/criu/taskqueue.c
new file mode 100644
index 0000000..1196a5e
--- /dev/null
+++ b/criu/taskqueue.c
@@ -0,0 +1,124 @@
+/*
+ * Target:
+ * parallel dump process
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/sysinfo.h>
+
+#include "pstree.h"
+#include "log.h"
+#include "taskqueue.h"
+
+/*
+ * Sometimes, only one cpu can be used which is bad for parallel routine.
+ * Therefore, set cpu affinity for criu routine.
+ */
+static int set_cpuaffinity(void)
+{
+ cpu_set_t *set;
+ int num_cpus = get_nprocs_conf();
+ size_t cpusetsize = CPU_ALLOC_SIZE(num_cpus);
+ int retval;
+
+ set = CPU_ALLOC(num_cpus);
+ memset(set, 0xff, cpusetsize);
+
+ retval = sched_setaffinity(getpid(), cpusetsize, set);
+ if (retval != 0)
+ pr_err("sched_setaffinity failed: %s\n", strerror(errno));
+
+ CPU_FREE(set);
+
+ return retval;
+}
+
+int init_parallel_env(void)
+{
+ return set_cpuaffinity();
+}
+
+static void *collect_mappings_routine(void *_arg)
+{
+ struct mappings_info *info = _arg;
+
+ info->queue_result = collect_mappings(info->pid, info->vmas, info->dump_file);
+ return NULL;
+}
+
+int dump_filemap(struct vma_area *vma_area, int fd); /* defined in criu/cr-dump.c */
+
+int start_collect_mappings_thread(void)
+{
+ struct pstree_item *pi;
+ struct mappings_info *info;
+
+ for_each_pstree_item(pi) {
+ /* disable parallel collect for non-root item because of the
+ * concurrence.
+ */
+ if (pi->pid->real != root_item->pid->real)
+ continue;
+
+ info = &pi->maps_info;
+
+ info->vmas = xmalloc(sizeof(struct vm_area_list));
+ if (info->vmas == NULL) {
+ pr_err("xzalloc vmas no memory\n");
+ return -1;
+ }
+ vm_area_list_init(info->vmas);
+
+ info->pid = pi->pid->real;
+ info->dump_file = dump_filemap;
+ info->queue_routine = collect_mappings_routine;
+ info->queue_arg = info;
+
+ pr_info("Start thread to collect %d mappings\n", info->pid);
+
+ if (taskqueue_create(&info->queue) < 0) {
+ pr_err("parallel_collect_mappings failed: %s\n", strerror(errno));
+ free(info->vmas);
+ /*
+ * Don't care other threads status, use `exit_group()`
+ * to ensure all threads exit.
+ */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int end_collect_mappings_thread(struct pstree_item *item)
+{
+ struct mappings_info *info = &item->maps_info;
+ int retval;
+
+ /* disable parallel collect for non-root item because of the
+ * concurrence.
+ */
+ if (root_item->pid->real != item->pid->real)
+ return 0;
+
+ retval = taskqueue_join(&info->queue);
+ if (retval != 0 || info->queue_result != 0) {
+ pr_err("taskqueue_join failed, retval %d(errno %d: %s),"
+ " queue_result: %d\n",
+ retval,
+ retval == 0 ? 0 : errno,
+ retval == 0 ? "nil" : strerror(errno),
+ info->queue_result);
+ retval = -1;
+ }
+
+ pr_info("End thread to collect %d mappings\n", info->pid);
+
+ /*
+ * Don't care other threads status, use `exit_group()` to ensure all
+ * threads exit.
+ */
+ return retval;
+}
--
2.34.1
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/euleroswander/criu.git
git@gitee.com:euleroswander/criu.git
euleroswander
criu
criu
master

搜索帮助