Pre-checkpoint does a snapshot of a memory changes since previous pre-checkpoint, allowing to significantly decrease time of an actual checkpoint.
We need it to be able to live-migrate containers faster. Signed-off-by: Ruslan Kuprieiev <kupru...@gmail.com> --- src/lxc/criu.c | 20 ++++++++++++++ src/lxc/criu.h | 1 + src/lxc/lxc_checkpoint.c | 41 +++++++++++++++++++++++++++- src/lxc/lxccontainer.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-- src/lxc/lxccontainer.h | 15 ++++++++++- 5 files changed, 143 insertions(+), 4 deletions(-) diff --git a/src/lxc/criu.c b/src/lxc/criu.c index e939b37..a8fcc5f 100644 --- a/src/lxc/criu.c +++ b/src/lxc/criu.c @@ -70,6 +70,13 @@ void exec_criu(struct criu_opts *opts) /* --leave-running */ if (!opts->stop) static_args++; + } else if (strcmp(opts->action, "pre-dump") == 0) { + /* -t pid */ + static_args += 2; + + /* --prev-images-dir */ + if (opts->prev_dir) + static_args++; } else if (strcmp(opts->action, "restore") == 0) { /* --root $(lxc_mount_point) --restore-detached * --restore-sibling --pidfile $foo --cgroup-root $foo */ @@ -127,6 +134,11 @@ void exec_criu(struct criu_opts *opts) DECLARE_ARG("-o"); DECLARE_ARG(log); + if (opts->prev_dir) { + DECLARE_ARG("--prev-images-dir"); + DECLARE_ARG(opts->prev_dir); + } + if (opts->verbose) DECLARE_ARG("-vvvvvv"); @@ -140,6 +152,14 @@ void exec_criu(struct criu_opts *opts) DECLARE_ARG(pid); if (!opts->stop) DECLARE_ARG("--leave-running"); + } else if (strcmp(opts->action, "pre-dump") == 0) { + char pid[32]; + + if (sprintf(pid, "%d", opts->c->init_pid(opts->c)) < 0) + goto err; + + DECLARE_ARG("-t"); + DECLARE_ARG(pid); } else if (strcmp(opts->action, "restore") == 0) { void *m; int additional; diff --git a/src/lxc/criu.h b/src/lxc/criu.h index 1f65e47..e8ef24b 100644 --- a/src/lxc/criu.h +++ b/src/lxc/criu.h @@ -53,6 +53,7 @@ struct criu_opts { /* restore: the file to write the init process' pid into */ char *pidfile; const char *cgroup_path; + char *prev_dir; }; void exec_criu(struct criu_opts *opts); diff --git a/src/lxc/lxc_checkpoint.c b/src/lxc/lxc_checkpoint.c index 2e76c2e..d4ff786 100644 --- a/src/lxc/lxc_checkpoint.c +++ b/src/lxc/lxc_checkpoint.c @@ -36,6 +36,10 @@ static bool stop = false; static bool verbose = false; static bool do_restore = false; static bool daemonize_set = false; +static char *prev_checkpoint_dir = NULL; +static bool do_pre_checkpoint = false; + +#define OPT_PREV_DIR OPT_VERSION - 1 static const struct option my_longopts[] = { {"checkpoint-dir", required_argument, 0, 'D'}, @@ -44,6 +48,8 @@ static const struct option my_longopts[] = { {"restore", no_argument, 0, 'r'}, {"daemon", no_argument, 0, 'd'}, {"foreground", no_argument, 0, 'F'}, + {"pre-checkpoint", no_argument, 0, 'p'}, + {"prev-checkpoint-dir", required_argument, 0, OPT_PREV_DIR}, LXC_COMMON_OPTIONS }; @@ -91,6 +97,14 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg) args->daemonize = 0; daemonize_set = true; break; + case 'p': + do_pre_checkpoint = true; + break; + case OPT_PREV_DIR: + prev_checkpoint_dir = strdup(arg); + if (!prev_checkpoint_dir) + return -1; + break; } return 0; } @@ -107,7 +121,9 @@ lxc-checkpoint checkpoints and restores a container\n\ Options :\n\ -n, --name=NAME NAME for name of the container\n\ -r, --restore Restore container\n\ + -p, --pre-checkpoint Pre-checkpoint container\n\ -D, --checkpoint-dir=DIR directory to save the checkpoint in\n\ + --prev-checkpoint-dir=DIR directory with previous checkpoint(relative to -D)\n\ -v, --verbose Enable verbose criu logs\n\ Checkpoint options:\n\ -s, --stop Stop the container after checkpointing.\n\ @@ -131,7 +147,7 @@ bool checkpoint(struct lxc_container *c) return false; } - ret = c->checkpoint(c, checkpoint_dir, stop, verbose); + ret = c->checkpoint(c, checkpoint_dir, prev_checkpoint_dir, stop, verbose); lxc_container_put(c); if (!ret) { @@ -142,6 +158,27 @@ bool checkpoint(struct lxc_container *c) return true; } +bool pre_checkpoint(struct lxc_container *c) +{ + bool ret; + + if (!c->is_running(c)) { + fprintf(stderr, "%s not running, not pre-checkpointing.\n", my_args.name); + lxc_container_put(c); + return false; + } + + ret = c->pre_checkpoint(c, checkpoint_dir, prev_checkpoint_dir, verbose); + lxc_container_put(c); + + if (!ret) { + fprintf(stderr, "Pre-checkpointing %s failed.\n", my_args.name); + return false; + } + + return true; +} + bool restore_finalize(struct lxc_container *c) { bool ret = c->restore(c, checkpoint_dir, verbose); @@ -229,6 +266,8 @@ int main(int argc, char *argv[]) if (do_restore) ret = restore(c); + else if (do_pre_checkpoint) + ret = pre_checkpoint(c); else ret = checkpoint(c); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 1c103e8..5e15a1b 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -380,6 +380,16 @@ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \ return ret; \ } +#define WRAP_API_4(rettype, fnname, t1, t2, t3, t4) \ +static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3, t4 a4)\ +{ \ + rettype ret; \ + current_config = c ? c->lxc_conf : NULL; \ + ret = do_##fnname(c, a1, a2, a3, a4); \ + current_config = NULL; \ + return ret; \ +} + WRAP_API(bool, lxcapi_is_defined) static const char *do_lxcapi_state(struct lxc_container *c) @@ -3712,7 +3722,7 @@ static bool do_lxcapi_detach_interface(struct lxc_container *c, const char *ifna WRAP_API_2(bool, lxcapi_detach_interface, const char *, const char *) -static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) +static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, char *prev_dir, bool stop, bool verbose) { pid_t pid; int status; @@ -3745,6 +3755,7 @@ static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool os.action = "dump"; os.directory = directory; + os.prev_dir = prev_dir; os.c = c; os.stop = stop; os.verbose = verbose; @@ -3767,7 +3778,61 @@ static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool } } -WRAP_API_3(bool, lxcapi_checkpoint, char *, bool, bool) +WRAP_API_4(bool, lxcapi_checkpoint, char *, char *, bool, bool) + +static bool do_lxcapi_pre_checkpoint(struct lxc_container *c, char *directory, char *prev_dir, bool verbose) +{ + pid_t pid; + int status; + char path[PATH_MAX]; + + if (!criu_ok(c)) + return false; + + if (mkdir(directory, 0700) < 0 && errno != EEXIST) + return false; + + status = snprintf(path, sizeof(path), "%s/inventory.img", directory); + if (status < 0 || status >= sizeof(path)) + return false; + + if (access(path, F_OK) == 0) { + ERROR("please use a fresh directory for the dump directory\n"); + return false; + } + + pid = fork(); + if (pid < 0) + return false; + + if (pid == 0) { + struct criu_opts os; + + os.action = "pre-dump"; + os.directory = directory; + os.prev_dir = prev_dir; + os.c = c; + os.verbose = verbose; + + /* exec_criu() returning is an error */ + exec_criu(&os); + exit(1); + } else { + pid_t w = waitpid(pid, &status, 0); + if (w == -1) { + SYSERROR("waitpid"); + return false; + } + + if (WIFEXITED(status)) { + return !WEXITSTATUS(status); + } + + return false; + } +} + +WRAP_API_3(bool, lxcapi_pre_checkpoint, char *, char *, bool) static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool verbose) { @@ -3966,6 +4031,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->detach_interface = lxcapi_detach_interface; c->checkpoint = lxcapi_checkpoint; c->restore = lxcapi_restore; + c->pre_checkpoint = lxcapi_pre_checkpoint; return c; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index d60e19a..1faded2 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -773,7 +773,7 @@ struct lxc_container { * \return \c true on success, else \c false. * present at compile time). */ - bool (*checkpoint)(struct lxc_container *c, char *directory, bool stop, bool verbose); + bool (*checkpoint)(struct lxc_container *c, char *directory, char *prev_dir, bool stop, bool verbose); /*! * \brief Restore a container from a checkpoint. @@ -808,6 +808,19 @@ struct lxc_container { bool (*snapshot_destroy_all)(struct lxc_container *c); /* Post LXC-1.1 additions */ + + /*! + * \brief Pre-checkpoint a container. + * + * \param c Container. + * \param directory The directory to dump the container to. + * \param prev_dir The directory with previous dump. + * \param verbose Enable criu's verbose logs. + * + * \return \c true on success, else \c false. + * present at compile time). + */ + bool (*pre_checkpoint)(struct lxc_container *c, char *directory, char *prev_dir, bool verbose); }; /*! -- 2.1.0 _______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel