[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, &regs);
> +
> +	if (ptrace(PTRACE_SETREGS, pid, NULL, &regs)) {
> +		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