mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-31 06:15:24 +00:00
dump: Introduce the pre-dump action
With this action criu will seize tasks, grab all its memory into page-pipes, rest dirty tracker and will then release them. Writing the memory from page-pipes would occur after tasks are unfreezed and thus the frozen time should become reasonably small. When pre-dump is in action, the dirty tracking is forcedly turned off as well as tasks are resumed afterwards, not killed, by default. This is a prerequisite for iterative migration. Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
120
cr-dump.c
120
cr-dump.c
@@ -61,6 +61,7 @@
|
||||
#include "kerndat.h"
|
||||
#include "stats.h"
|
||||
#include "mem.h"
|
||||
#include "page-pipe.h"
|
||||
|
||||
#include "asm/dump.h"
|
||||
|
||||
@@ -1366,6 +1367,65 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pre_dump_one_task(struct pstree_item *item, struct list_head *ctls)
|
||||
{
|
||||
pid_t pid = item->pid.real;
|
||||
struct vm_area_list vmas;
|
||||
struct parasite_ctl *parasite_ctl;
|
||||
int ret = -1;
|
||||
struct parasite_dump_misc misc;
|
||||
|
||||
pr_info("========================================\n");
|
||||
pr_info("Pre-dumping task (pid: %d)\n", pid);
|
||||
pr_info("========================================\n");
|
||||
|
||||
if (item->state == TASK_STOPPED) {
|
||||
pr_warn("Stopped tasks are not supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (item->state == TASK_DEAD)
|
||||
return 0;
|
||||
|
||||
ret = collect_mappings(pid, &vmas);
|
||||
if (ret) {
|
||||
pr_err("Collect mappings (pid: %d) failed with %d\n", pid, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
parasite_ctl = parasite_infect_seized(pid, item, &vmas, NULL);
|
||||
if (!parasite_ctl) {
|
||||
pr_err("Can't infect (pid: %d) with parasite\n", pid);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = parasite_dump_misc_seized(parasite_ctl, &misc);
|
||||
if (ret) {
|
||||
pr_err("Can't dump misc (pid: %d)\n", pid);
|
||||
goto err_cure;
|
||||
}
|
||||
|
||||
parasite_ctl->pid.virt = item->pid.virt = misc.pid;
|
||||
|
||||
ret = parasite_dump_pages_seized(parasite_ctl, &vmas, ¶site_ctl->mem_pp);
|
||||
if (ret)
|
||||
goto err_cure;
|
||||
|
||||
if (parasite_cure_remote(parasite_ctl, item))
|
||||
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
||||
list_add_tail(¶site_ctl->pre_list, ctls);
|
||||
err_free:
|
||||
free_mappings(&vmas);
|
||||
err:
|
||||
return ret;
|
||||
|
||||
err_cure:
|
||||
if (parasite_cure_seized(parasite_ctl, item))
|
||||
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
static int dump_one_task(struct pstree_item *item)
|
||||
{
|
||||
pid_t pid = item->pid.real;
|
||||
@@ -1472,7 +1532,7 @@ static int dump_one_task(struct pstree_item *item)
|
||||
}
|
||||
}
|
||||
|
||||
ret = parasite_dump_pages_seized(parasite_ctl, &vmas);
|
||||
ret = parasite_dump_pages_seized(parasite_ctl, &vmas, NULL);
|
||||
if (ret)
|
||||
goto err_cure;
|
||||
|
||||
@@ -1551,6 +1611,64 @@ err_cure_fdset:
|
||||
goto err;
|
||||
}
|
||||
|
||||
int cr_pre_dump_tasks(pid_t pid)
|
||||
{
|
||||
struct pstree_item *item;
|
||||
int ret = -1;
|
||||
LIST_HEAD(ctls);
|
||||
struct parasite_ctl *ctl, *n;
|
||||
|
||||
if (kerndat_init())
|
||||
goto err;
|
||||
|
||||
if (connect_to_page_server())
|
||||
goto err;
|
||||
|
||||
if (collect_pstree(pid))
|
||||
goto err;
|
||||
|
||||
for_each_pstree_item(item)
|
||||
if (pre_dump_one_task(item, &ctls))
|
||||
goto err;
|
||||
|
||||
ret = 0;
|
||||
err:
|
||||
pstree_switch_state(root_item,
|
||||
ret ? TASK_ALIVE : opts.final_state);
|
||||
free_pstree(root_item);
|
||||
|
||||
timing_stop(TIME_FROZEN);
|
||||
|
||||
pr_info("Pre-dumping tasks' memory\n");
|
||||
list_for_each_entry_safe(ctl, n, &ctls, pre_list) {
|
||||
struct page_xfer xfer;
|
||||
|
||||
pr_info("\tPre-dumping %d\n", ctl->pid.virt);
|
||||
timing_start(TIME_MEMWRITE);
|
||||
ret = open_page_xfer(&xfer, CR_FD_PAGEMAP, ctl->pid.virt);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = page_xfer_dump_pages(&xfer, ctl->mem_pp, 0);
|
||||
|
||||
xfer.close(&xfer);
|
||||
timing_stop(TIME_MEMWRITE);
|
||||
|
||||
destroy_page_pipe(ctl->mem_pp);
|
||||
list_del(&ctl->pre_list);
|
||||
parasite_cure_local(ctl);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
pr_err("Pre-dumping FAILED.\n");
|
||||
else {
|
||||
write_stats(DUMP_STATS);
|
||||
pr_info("Pre-dumping finished successfully\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cr_dump_tasks(pid_t pid)
|
||||
{
|
||||
struct pstree_item *item;
|
||||
|
17
crtools.c
17
crtools.c
@@ -308,6 +308,23 @@ int main(int argc, char *argv[])
|
||||
return cr_dump_tasks(tree_id);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[optind], "pre-dump")) {
|
||||
if (!tree_id)
|
||||
goto opt_pid_missing;
|
||||
|
||||
if (!opts.track_mem) {
|
||||
pr_info("Enforcing memory tracking for pre-dump.\n");
|
||||
opts.track_mem = true;
|
||||
}
|
||||
|
||||
if (opts.final_state == TASK_DEAD) {
|
||||
pr_info("Enforcing tasks run after pre-dump.\n");
|
||||
opts.final_state = TASK_ALIVE;
|
||||
}
|
||||
|
||||
return cr_pre_dump_tasks(tree_id);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[optind], "restore")) {
|
||||
if (tree_id)
|
||||
pr_warn("Using -t with criu restore is obsoleted\n");
|
||||
|
@@ -224,6 +224,7 @@ extern struct cr_fdset *glob_fdset;
|
||||
extern struct cr_options opts;
|
||||
|
||||
int cr_dump_tasks(pid_t pid);
|
||||
int cr_pre_dump_tasks(pid_t pid);
|
||||
int cr_restore_tasks(struct cr_options *opts);
|
||||
int cr_show(struct cr_options *opts, int pid);
|
||||
int convert_to_elf(char *elf_path, int fd_core);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
struct vm_area_list;
|
||||
unsigned int dump_pages_args_size(struct vm_area_list *vmas);
|
||||
struct parasite_ctl;
|
||||
struct page_pipe;
|
||||
int parasite_dump_pages_seized(struct parasite_ctl *ctl,
|
||||
struct vm_area_list *vma_area_list);
|
||||
struct vm_area_list *vma_area_list, struct page_pipe **pp);
|
||||
#endif
|
||||
|
@@ -23,6 +23,9 @@ struct parasite_ctl {
|
||||
void *addr_args; /* address for arguments */
|
||||
unsigned long args_size;
|
||||
int tsock; /* transport socket for transfering fds */
|
||||
|
||||
struct list_head pre_list;
|
||||
struct page_pipe *mem_pp;
|
||||
};
|
||||
|
||||
struct cr_fdset;
|
||||
|
32
mem.c
32
mem.c
@@ -286,7 +286,8 @@ static struct parasite_dump_pages_args *prep_dump_pages_args(struct parasite_ctl
|
||||
|
||||
static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
|
||||
struct parasite_dump_pages_args *args,
|
||||
struct vm_area_list *vma_area_list)
|
||||
struct vm_area_list *vma_area_list,
|
||||
struct page_pipe **pp_ret)
|
||||
{
|
||||
u64 *map;
|
||||
int pagemap;
|
||||
@@ -294,7 +295,6 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
|
||||
struct page_pipe_buf *ppb;
|
||||
struct vma_area *vma_area;
|
||||
int ret = -1;
|
||||
struct page_xfer xfer;
|
||||
struct mem_snap_ctx *snap;
|
||||
|
||||
pr_info("\n");
|
||||
@@ -358,18 +358,26 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
|
||||
args->off += args->nr_segs;
|
||||
}
|
||||
|
||||
timing_start(TIME_MEMWRITE);
|
||||
ret = open_page_xfer(&xfer, CR_FD_PAGEMAP, ctl->pid.virt);
|
||||
if (ret < 0)
|
||||
goto out_pp;
|
||||
if (pp_ret)
|
||||
*pp_ret = pp;
|
||||
else {
|
||||
struct page_xfer xfer;
|
||||
|
||||
ret = page_xfer_dump_pages(&xfer, pp, 0);
|
||||
timing_start(TIME_MEMWRITE);
|
||||
ret = open_page_xfer(&xfer, CR_FD_PAGEMAP, ctl->pid.virt);
|
||||
if (ret < 0)
|
||||
goto out_pp;
|
||||
|
||||
ret = page_xfer_dump_pages(&xfer, pp, 0);
|
||||
|
||||
xfer.close(&xfer);
|
||||
timing_stop(TIME_MEMWRITE);
|
||||
}
|
||||
|
||||
xfer.close(&xfer);
|
||||
timing_stop(TIME_MEMWRITE);
|
||||
task_reset_dirty_track(ctl->pid.real);
|
||||
out_pp:
|
||||
destroy_page_pipe(pp);
|
||||
if (ret || !pp_ret)
|
||||
destroy_page_pipe(pp);
|
||||
out_close:
|
||||
close(pagemap);
|
||||
out_free:
|
||||
@@ -384,7 +392,7 @@ out:
|
||||
}
|
||||
|
||||
int parasite_dump_pages_seized(struct parasite_ctl *ctl,
|
||||
struct vm_area_list *vma_area_list)
|
||||
struct vm_area_list *vma_area_list, struct page_pipe **pp)
|
||||
{
|
||||
int ret;
|
||||
struct parasite_dump_pages_args *pargs;
|
||||
@@ -398,7 +406,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = __parasite_dump_pages_seized(ctl, pargs, vma_area_list);
|
||||
ret = __parasite_dump_pages_seized(ctl, pargs, vma_area_list, pp);
|
||||
if (ret)
|
||||
pr_err("Can't dump page with parasite\n");
|
||||
|
||||
|
@@ -730,7 +730,8 @@ static unsigned long parasite_args_size(struct vm_area_list *vmas, struct parasi
|
||||
{
|
||||
unsigned long size = PARASITE_ARG_SIZE_MIN;
|
||||
|
||||
size = max(size, (unsigned long)drain_fds_size(dfds));
|
||||
if (dfds)
|
||||
size = max(size, (unsigned long)drain_fds_size(dfds));
|
||||
size = max(size, (unsigned long)dump_pages_args_size(vmas));
|
||||
|
||||
return size;
|
||||
|
Reference in New Issue
Block a user