Commit a533820b authored by Pavel Emelyanov's avatar Pavel Emelyanov

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: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 094330c3
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include "kerndat.h" #include "kerndat.h"
#include "stats.h" #include "stats.h"
#include "mem.h" #include "mem.h"
#include "page-pipe.h"
#include "asm/dump.h" #include "asm/dump.h"
...@@ -1366,6 +1367,65 @@ err: ...@@ -1366,6 +1367,65 @@ err:
return ret; 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, &parasite_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(&parasite_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) static int dump_one_task(struct pstree_item *item)
{ {
pid_t pid = item->pid.real; pid_t pid = item->pid.real;
...@@ -1472,7 +1532,7 @@ static int dump_one_task(struct pstree_item *item) ...@@ -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) if (ret)
goto err_cure; goto err_cure;
...@@ -1551,6 +1611,64 @@ err_cure_fdset: ...@@ -1551,6 +1611,64 @@ err_cure_fdset:
goto err; 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) int cr_dump_tasks(pid_t pid)
{ {
struct pstree_item *item; struct pstree_item *item;
......
...@@ -308,6 +308,23 @@ int main(int argc, char *argv[]) ...@@ -308,6 +308,23 @@ int main(int argc, char *argv[])
return cr_dump_tasks(tree_id); 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 (!strcmp(argv[optind], "restore")) {
if (tree_id) if (tree_id)
pr_warn("Using -t with criu restore is obsoleted\n"); pr_warn("Using -t with criu restore is obsoleted\n");
......
...@@ -224,6 +224,7 @@ extern struct cr_fdset *glob_fdset; ...@@ -224,6 +224,7 @@ extern struct cr_fdset *glob_fdset;
extern struct cr_options opts; extern struct cr_options opts;
int cr_dump_tasks(pid_t pid); 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_restore_tasks(struct cr_options *opts);
int cr_show(struct cr_options *opts, int pid); int cr_show(struct cr_options *opts, int pid);
int convert_to_elf(char *elf_path, int fd_core); int convert_to_elf(char *elf_path, int fd_core);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
struct vm_area_list; struct vm_area_list;
unsigned int dump_pages_args_size(struct vm_area_list *vmas); unsigned int dump_pages_args_size(struct vm_area_list *vmas);
struct parasite_ctl; struct parasite_ctl;
struct page_pipe;
int parasite_dump_pages_seized(struct parasite_ctl *ctl, 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 #endif
...@@ -23,6 +23,9 @@ struct parasite_ctl { ...@@ -23,6 +23,9 @@ struct parasite_ctl {
void *addr_args; /* address for arguments */ void *addr_args; /* address for arguments */
unsigned long args_size; unsigned long args_size;
int tsock; /* transport socket for transfering fds */ int tsock; /* transport socket for transfering fds */
struct list_head pre_list;
struct page_pipe *mem_pp;
}; };
struct cr_fdset; struct cr_fdset;
......
...@@ -286,7 +286,8 @@ static struct parasite_dump_pages_args *prep_dump_pages_args(struct parasite_ctl ...@@ -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, static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
struct parasite_dump_pages_args *args, 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; u64 *map;
int pagemap; int pagemap;
...@@ -294,7 +295,6 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -294,7 +295,6 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
struct page_pipe_buf *ppb; struct page_pipe_buf *ppb;
struct vma_area *vma_area; struct vma_area *vma_area;
int ret = -1; int ret = -1;
struct page_xfer xfer;
struct mem_snap_ctx *snap; struct mem_snap_ctx *snap;
pr_info("\n"); pr_info("\n");
...@@ -358,6 +358,11 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -358,6 +358,11 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
args->off += args->nr_segs; args->off += args->nr_segs;
} }
if (pp_ret)
*pp_ret = pp;
else {
struct page_xfer xfer;
timing_start(TIME_MEMWRITE); timing_start(TIME_MEMWRITE);
ret = open_page_xfer(&xfer, CR_FD_PAGEMAP, ctl->pid.virt); ret = open_page_xfer(&xfer, CR_FD_PAGEMAP, ctl->pid.virt);
if (ret < 0) if (ret < 0)
...@@ -367,8 +372,11 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -367,8 +372,11 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
xfer.close(&xfer); xfer.close(&xfer);
timing_stop(TIME_MEMWRITE); timing_stop(TIME_MEMWRITE);
}
task_reset_dirty_track(ctl->pid.real); task_reset_dirty_track(ctl->pid.real);
out_pp: out_pp:
if (ret || !pp_ret)
destroy_page_pipe(pp); destroy_page_pipe(pp);
out_close: out_close:
close(pagemap); close(pagemap);
...@@ -384,7 +392,7 @@ out: ...@@ -384,7 +392,7 @@ out:
} }
int parasite_dump_pages_seized(struct parasite_ctl *ctl, 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; int ret;
struct parasite_dump_pages_args *pargs; struct parasite_dump_pages_args *pargs;
...@@ -398,7 +406,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -398,7 +406,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
return ret; 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) if (ret)
pr_err("Can't dump page with parasite\n"); pr_err("Can't dump page with parasite\n");
......
...@@ -730,6 +730,7 @@ static unsigned long parasite_args_size(struct vm_area_list *vmas, struct parasi ...@@ -730,6 +730,7 @@ static unsigned long parasite_args_size(struct vm_area_list *vmas, struct parasi
{ {
unsigned long size = PARASITE_ARG_SIZE_MIN; unsigned long size = PARASITE_ARG_SIZE_MIN;
if (dfds)
size = max(size, (unsigned long)drain_fds_size(dfds)); size = max(size, (unsigned long)drain_fds_size(dfds));
size = max(size, (unsigned long)dump_pages_args_size(vmas)); size = max(size, (unsigned long)dump_pages_args_size(vmas));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment