[CRIU] [PATCH 19/19] parasite: Introduce parasite daemon mode
Andrew Vagin
avagin at parallels.com
Wed Feb 27 03:15:53 EST 2013
On Tue, Feb 26, 2013 at 05:09:01PM +0400, Cyrill Gorcunov wrote:
>
> Parasite daemon mode represents a code blob which lives in
> dumpee space but listen to a channel from crtools driver.
>
> The synchronization is done by futexes, which live in tid_state_s
> structure and shared between crtools and every daemon thread
I am not sure about futex-s. I expect to see unix sockets here. When
crtoola crashes a unix socket are closed and the dumped task gets a
signal about that.
>
> +---------+
> | cmd | command for daemon to pass
> +---------+
> | ack | ack when daemon finished handling
> +---------+
> | ret | return code for errors
> +---------+
> ...
> +---------+
> | stack | per-thread daemon stack
> +---------+
>
> Thus a typical scenario of "talking" to parasite daemon looks like
>
> crtools daemon
> ------- ------
> set-and-wake on @cmd
> wake up on @cmd
> wait for "idle" on @cmd
> handle command
> set @ret
> wake crtools on @cmd with "idle"
> wait for @ack
> wake up on @cmd by "idle"
> set @ack and wake daemon on @ack
> wake on @ack
> sleep until next @cmd
>
> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
> parasite-syscall.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++------
> pie/parasite.c | 137 +++++++++++++++++++++++++++++++++++++--------------
> 2 files changed, 226 insertions(+), 51 deletions(-)
>
> diff --git a/parasite-syscall.c b/parasite-syscall.c
> index ebc168e..ce8ad11 100644
> --- a/parasite-syscall.c
> +++ b/parasite-syscall.c
> @@ -233,6 +233,45 @@ static int parasite_execute_trap(unsigned int cmd, struct parasite_ctl *ctl)
> return parasite_execute_trap_by_pid(cmd, ctl, ctl->pid);
> }
>
> +static struct tid_state_s *find_thread_state(struct parasite_ctl *ctl, pid_t global)
> +{
> + struct tid_state_s *s = ctl->tid_state_map;
> + unsigned int i;
> +
> + for (i = 0; i < ctl->nr_tid_states; i++) {
> + if (s[i].global == global)
> + return &s[i];
> + }
> +
> + pr_err("Can't find global tid %d\n", global);
> +
> + BUG();
> + return NULL;
> +}
> +
> +static int parasite_execute_daemon_by_pid(unsigned int cmd, struct parasite_ctl *ctl, pid_t pid)
> +{
> + struct tid_state_s *s;
> +
> + s = find_thread_state(ctl, pid);
> +
> + pr_debug("Wake daemon %d with command %d\n", pid, cmd);
> + futex_set_and_wake(&s->cmd, cmd);
> +
> + pr_debug("Wait for PARASITE_CMD_IDLE on %d\n", s->global);
> + futex_wait_until(&s->cmd, PARASITE_CMD_IDLE);
> +
> + pr_debug("Wake daemon with " __stringify_1(PARASITE_CMD_ACK) "\n");
> + futex_set_and_wake(&s->ack, PARASITE_CMD_ACK);
> +
> + return s->ret;
> +}
> +
> +static int parasite_execute_daemon(unsigned int cmd, struct parasite_ctl *ctl)
> +{
> + return parasite_execute_daemon_by_pid(cmd, ctl, ctl->pid);
> +}
> +
> static int munmap_seized(struct parasite_ctl *ctl, void *addr, size_t length)
> {
> unsigned long x;
> @@ -276,13 +315,50 @@ static int parasite_set_logfd(struct parasite_ctl *ctl, pid_t pid)
> a = parasite_args(ctl, struct parasite_log_args);
> a->log_level = log_get_loglevel();
>
> - ret = parasite_execute_trap(PARASITE_CMD_CFG_LOG, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_CFG_LOG, ctl);
> if (ret < 0)
> return ret;
>
> return 0;
> }
>
> +static int parasite_daemonize(struct parasite_ctl *ctl, pid_t pid)
> +{
> + user_regs_struct_t regs = ctl->regs_orig;
> + struct tid_state_s *s;
> +
> + *ctl->addr_cmd = PARASITE_CMD_DAEMONIZE;
> + parasite_setup_regs(ctl->parasite_ip, ®s);
> +
> + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s)) {
> + pr_perror("Can't set registers (pid: %d)", pid);
> + goto err;
> + }
> +
> + if (ptrace(PTRACE_CONT, pid, NULL, NULL)) {
> + pr_perror("Can't continue (pid: %d)\n", pid);
> + goto err;
> + }
> +
> + s = find_thread_state(ctl, pid);
> + pr_info("Wait for PARASITE_CMD_DAEMONIZE\n");
> + futex_wait_until(&s->cmd, PARASITE_CMD_DAEMONIZE);
> +
> + if (s->ret) {
> + pr_err("Can't switch parasite to daemon mode: %d (%d)\n",
> + pid, s->ret);
> + goto err;
> + }
> +
> + pr_info("Parasite has been switched to daemon mode: %d (%d)\n",
> + pid, s->ret);
> +
> + return 0;
> +
> +err:
> + return -1;
> +}
> +
> static int parasite_init(struct parasite_ctl *ctl, pid_t pid, int nr_threads)
> {
> struct parasite_init_args *args;
> @@ -368,6 +444,9 @@ static int parasite_init(struct parasite_ctl *ctl, pid_t pid, int nr_threads)
> goto err;
> }
>
> + if (parasite_daemonize(ctl, ctl->pid))
> + goto err;
> +
> ctl->tsock = sock;
> return 0;
> err:
> @@ -383,7 +462,7 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, struct pid *tid,
>
> args = parasite_args(ctl, struct parasite_dump_thread);
>
> - ret = parasite_execute_trap_by_pid(PARASITE_CMD_DUMP_THREAD, ctl, tid->real);
> + ret = parasite_execute_daemon_by_pid(PARASITE_CMD_DUMP_THREAD, ctl, tid->real);
>
> memcpy(&core->thread_core->blk_sigset, &args->blocked, sizeof(args->blocked));
> CORE_THREAD_ARCH_INFO(core)->clear_tid_addr = encode_pointer(args->tid_addr);
> @@ -401,7 +480,7 @@ int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_f
>
> args = parasite_args(ctl, struct parasite_dump_sa_args);
>
> - ret = parasite_execute_trap(PARASITE_CMD_DUMP_SIGACTS, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_DUMP_SIGACTS, ctl);
> if (ret < 0)
> return ret;
>
> @@ -444,7 +523,7 @@ int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_f
>
> args = parasite_args(ctl, struct parasite_dump_itimers_args);
>
> - ret = parasite_execute_trap(PARASITE_CMD_DUMP_ITIMERS, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_DUMP_ITIMERS, ctl);
> if (ret < 0)
> return ret;
>
> @@ -464,7 +543,7 @@ int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_mis
> struct parasite_dump_misc *ma;
>
> ma = parasite_args(ctl, struct parasite_dump_misc);
> - if (parasite_execute_trap(PARASITE_CMD_DUMP_MISC, ctl) < 0)
> + if (parasite_execute_daemon(PARASITE_CMD_DUMP_MISC, ctl) < 0)
> return -1;
>
> *misc = *ma;
> @@ -478,7 +557,7 @@ struct parasite_tty_args *parasite_dump_tty(struct parasite_ctl *ctl, int fd)
> p = parasite_args(ctl, struct parasite_tty_args);
> p->fd = fd;
>
> - if (parasite_execute_trap(PARASITE_CMD_DUMP_TTY, ctl) < 0)
> + if (parasite_execute_daemon(PARASITE_CMD_DUMP_TTY, ctl) < 0)
> return NULL;
>
> return p;
> @@ -489,7 +568,7 @@ int parasite_dump_creds(struct parasite_ctl *ctl, CredsEntry *ce)
> struct parasite_dump_creds *pc;
>
> pc = parasite_args(ctl, struct parasite_dump_creds);
> - if (parasite_execute_trap(PARASITE_CMD_DUMP_CREDS, ctl) < 0)
> + if (parasite_execute_daemon(PARASITE_CMD_DUMP_CREDS, ctl) < 0)
> return -1;
>
> ce->secbits = pc->secbits;
> @@ -526,7 +605,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a
> if (ret < 0)
> goto out;
>
> - ret = parasite_execute_trap(PARASITE_CMD_DUMPPAGES_INIT, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_DUMPPAGES_INIT, ctl);
> if (ret < 0) {
> pr_err("Dumping pages failed with %i\n", ret);
> goto out;
> @@ -564,7 +643,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a
> if (vma_area->vma.end > TASK_SIZE)
> continue;
>
> - ret = parasite_execute_trap(PARASITE_CMD_DUMPPAGES, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_DUMPPAGES, ctl);
> if (ret) {
> pr_err("Dumping pages failed with %d\n", ret);
> goto out_fini;
> @@ -587,7 +666,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_a
> ret = 0;
>
> out_fini:
> - parasite_execute_trap(PARASITE_CMD_DUMPPAGES_FINI, ctl);
> + parasite_execute_daemon(PARASITE_CMD_DUMPPAGES_FINI, ctl);
> out:
> pr_info("----------------------------------------\n");
>
> @@ -604,7 +683,7 @@ int parasite_drain_fds_seized(struct parasite_ctl *ctl,
> args = parasite_args_s(ctl, size);
> memcpy(args, dfds, size);
>
> - ret = parasite_execute_trap(PARASITE_CMD_DRAIN_FDS, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_DRAIN_FDS, ctl);
> if (ret) {
> pr_err("Parasite failed to drain descriptors\n");
> goto err;
> @@ -624,7 +703,7 @@ int parasite_get_proc_fd_seized(struct parasite_ctl *ctl)
> {
> int ret = -1, fd;
>
> - ret = parasite_execute_trap(PARASITE_CMD_GET_PROC_FD, ctl);
> + ret = parasite_execute_daemon(PARASITE_CMD_GET_PROC_FD, ctl);
> if (ret) {
> pr_err("Parasite failed to get proc fd\n");
> return ret;
> @@ -658,6 +737,13 @@ int parasite_init_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
> item->threads[i].real);
> break;
> }
> +
> + ret = parasite_daemonize(ctl, item->threads[i].real);
> + if (ret) {
> + pr_err("Can't switch thread to daemon mode %d\n",
> + item->threads[i].real);
> + break;
> + }
> }
>
> return ret;
> @@ -666,13 +752,14 @@ int parasite_init_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
> int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *item)
> {
> int ret = 0, i;
> + int status;
>
> for (i = 0; i < item->nr_threads; i++) {
> if (item->pid.real == item->threads[i].real)
> continue;
>
> - ret = parasite_execute_trap_by_pid(PARASITE_CMD_FINI_THREAD, ctl,
> - item->threads[i].real);
> + ret = parasite_execute_daemon_by_pid(PARASITE_CMD_FINI_THREAD, ctl,
> + item->threads[i].real);
> /*
> * Note the thread's fini() can be called even when not
> * all threads were init()'ed, say we're rolling back from
> @@ -689,6 +776,17 @@ int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
> pr_err("Can't fini thread in parasite %d\n",
> item->threads[i].real);
> break;
> + } else if (ret == -ENOENT)
> + continue;
> +
> + if (wait4(item->threads[i].real, &status, __WALL, NULL) != item->threads[i].real) {
> + pr_perror("Waited pid mismatch (pid: %d)", item->threads[i].real);
> + break;
> + }
> +
> + if (!WIFSTOPPED(status)) {
> + pr_err("Task is still running (pid: %d)\n", item->threads[i].real);
> + break;
> }
> }
>
> @@ -702,9 +800,21 @@ int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item)
> ctl->tsock = -1;
>
> if (ctl->parasite_ip) {
> + int status;
> +
> ctl->signals_blocked = 0;
> parasite_fini_threads_seized(ctl, item);
> - parasite_execute_trap(PARASITE_CMD_FINI, ctl);
> + parasite_execute_daemon(PARASITE_CMD_FINI, ctl);
> +
> + if (wait4(ctl->pid, &status, __WALL, NULL) != ctl->pid) {
> + pr_perror("Waited pid mismatch (pid: %d)", ctl->pid);
> + ret = -1;
> + }
> +
> + if (!WIFSTOPPED(status)) {
> + pr_err("Task is still running (pid: %d)\n", ctl->pid);
> + ret = -1;
> + }
> }
>
> if (ctl->tid_state_map) {
> diff --git a/pie/parasite.c b/pie/parasite.c
> index 37b4c9b..d791f07 100644
> --- a/pie/parasite.c
> +++ b/pie/parasite.c
> @@ -613,20 +613,111 @@ static int parasite_cfg_log(struct parasite_log_args *args)
> return ret;
> }
>
> -static int fini(void)
> +static void fini(struct tid_state_s *s)
> {
> - int ret;
> -
> - ret = fini_thread();
> -
> - sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state));
> + fini_thread();
> + brk_fini();
> log_set_fd(-1);
> sys_close(tsock);
> - brk_fini();
> +}
>
> - return ret;
> +static unsigned long noinline __used
> +__parasite_daemon_thread(void *args, struct tid_state_s *s, unsigned long oldstack)
> +{
> + while (1) {
> + unsigned int cmd;
> + unsigned int ret;
> +
> + futex_wait_while_lt(&s->cmd, PARASITE_CMD_DAEMONIZED);
> + cmd = futex_get(&s->cmd);
> +
> + switch (cmd) {
> + case PARASITE_CMD_FINI:
> + futex_set(&s->ack, PARASITE_CMD_IDLE);
> + futex_set_and_wake(&s->cmd, PARASITE_CMD_IDLE);
> + futex_wait_until(&s->ack, PARASITE_CMD_ACK);
> + fini(s);
> + return oldstack;
> + case PARASITE_CMD_FINI_THREAD:
> + futex_set(&s->ack, PARASITE_CMD_IDLE);
> + futex_set_and_wake(&s->cmd, PARASITE_CMD_IDLE);
> + futex_wait_until(&s->ack, PARASITE_CMD_ACK);
> + s->ret = fini_thread();
> + asm_trap();
> + break;
> + case PARASITE_CMD_CFG_LOG:
> + ret = parasite_cfg_log(args);
> + break;
> + case PARASITE_CMD_DUMPPAGES_INIT:
> + ret = dump_pages_init();
> + break;
> + case PARASITE_CMD_DUMPPAGES_FINI:
> + ret = dump_pages_fini();
> + break;
> + case PARASITE_CMD_DUMPPAGES:
> + ret = dump_pages(args);
> + break;
> + case PARASITE_CMD_DUMP_SIGACTS:
> + ret = dump_sigact(args);
> + break;
> + case PARASITE_CMD_DUMP_ITIMERS:
> + ret = dump_itimers(args);
> + break;
> + case PARASITE_CMD_DUMP_MISC:
> + ret = dump_misc(args);
> + break;
> + case PARASITE_CMD_DUMP_CREDS:
> + ret = dump_creds(args);
> + break;
> + case PARASITE_CMD_DUMP_THREAD:
> + ret = dump_thread(args);
> + break;
> + case PARASITE_CMD_DRAIN_FDS:
> + ret = drain_fds(args);
> + break;
> + case PARASITE_CMD_GET_PROC_FD:
> + ret = parasite_get_proc_fd();
> + break;
> + case PARASITE_CMD_DUMP_TTY:
> + ret = parasite_dump_tty(args);
> + break;
> + default:
> + pr_err("Unknown command in parasite daemon: %d\n", cmd);
> + ret = -1;
> + break;
> + }
> +
> + s->ret = ret;
> + futex_set(&s->ack, PARASITE_CMD_IDLE);
> + futex_set_and_wake(&s->cmd, PARASITE_CMD_IDLE);
> + futex_wait_until(&s->ack, PARASITE_CMD_ACK);
> + }
> +
> + return (long)oldstack;
> }
>
> +static int noinline parasite_daemon(void *args)
> +{
> + struct tid_state_s *s;
> + unsigned long new_sp = 0;
> +
> + s = find_thread_state(sys_gettid());
> + BUG_ON(!s);
> +
> + pr_info("Parasite entering daemon mode for %d\n", s->global);
> + s->ret = 0;
> + futex_set_and_wake(&s->cmd, PARASITE_CMD_DAEMONIZE);
> +
> + new_sp = (unsigned long)(void *)&s->stack[PARASITE_STACK_SIZE - 8];
> + call_daemon_thread(new_sp, args, s, __parasite_daemon_thread);
> +
> + pr_info("Parasite leaving daemon mode for %d\n", s->global);
> + s->ret = 0;
> + sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state));
> + asm_trap();
> + return 0;
> + }
> +
> int __used parasite_service(unsigned int cmd, void *args)
> {
> pr_info("Parasite cmd %d/%x process\n", cmd, cmd);
> @@ -636,34 +727,8 @@ int __used parasite_service(unsigned int cmd, void *args)
> return init(args);
> case PARASITE_CMD_INIT_THREAD:
> return init_thread(args);
> - case PARASITE_CMD_FINI:
> - return fini();
> - case PARASITE_CMD_FINI_THREAD:
> - return fini_thread();
> - case PARASITE_CMD_CFG_LOG:
> - return parasite_cfg_log(args);
> - case PARASITE_CMD_DUMPPAGES_INIT:
> - return dump_pages_init();
> - case PARASITE_CMD_DUMPPAGES_FINI:
> - return dump_pages_fini();
> - case PARASITE_CMD_DUMPPAGES:
> - return dump_pages(args);
> - case PARASITE_CMD_DUMP_SIGACTS:
> - return dump_sigact(args);
> - case PARASITE_CMD_DUMP_ITIMERS:
> - return dump_itimers(args);
> - case PARASITE_CMD_DUMP_MISC:
> - return dump_misc(args);
> - case PARASITE_CMD_DUMP_CREDS:
> - return dump_creds(args);
> - case PARASITE_CMD_DUMP_THREAD:
> - return dump_thread(args);
> - case PARASITE_CMD_DRAIN_FDS:
> - return drain_fds(args);
> - case PARASITE_CMD_GET_PROC_FD:
> - return parasite_get_proc_fd();
> - case PARASITE_CMD_DUMP_TTY:
> - return parasite_dump_tty(args);
> + case PARASITE_CMD_DAEMONIZE:
> + return parasite_daemon(args);
> }
>
> pr_err("Unknown command to parasite: %d\n", cmd);
More information about the CRIU
mailing list