[CRIU] [PATCH 12/22] parasite: restore thread registers

Pavel Emelyanov xemul at parallels.com
Wed May 15 08:55:40 EDT 2013


On 05/08/2013 05:28 PM, Andrey Vagin wrote:
> crtools dump should not be destructive.

What was before this patch? What patch introduced the problem?

> We suppose that item->threads[0] is a thread leader.
> 
> Signed-off-by: Andrey Vagin <avagin at openvz.org>
> ---
>  arch/x86/crtools.c         |   2 +-
>  cr-dump.c                  |   4 +-
>  cr-exec.c                  |   4 +-
>  include/parasite-syscall.h |  19 +++++--
>  parasite-syscall.c         | 129 +++++++++++++++++++++++++--------------------
>  5 files changed, 93 insertions(+), 65 deletions(-)
> 
> diff --git a/arch/x86/crtools.c b/arch/x86/crtools.c
> index 284f2c0..1fc0b63 100644
> --- a/arch/x86/crtools.c
> +++ b/arch/x86/crtools.c
> @@ -86,7 +86,7 @@ int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
>  		unsigned long arg5,
>  		unsigned long arg6)
>  {
> -	user_regs_struct_t regs = ctl->regs_orig;
> +	user_regs_struct_t regs = ctl->threads[0].regs_orig;
>  	int err;
>  
>  	regs.ax  = (unsigned long)nr;
> diff --git a/cr-dump.c b/cr-dump.c
> index 2464a10..2ce4950 100644
> --- a/cr-dump.c
> +++ b/cr-dump.c
> @@ -1457,7 +1457,7 @@ static int dump_one_task(struct pstree_item *item)
>  		goto err;
>  	}
>  
> -	ret = parasite_cure_seized(parasite_ctl, item);
> +	ret = parasite_cure_seized(parasite_ctl);
>  	if (ret) {
>  		pr_err("Can't cure (pid: %d) from parasite\n", pid);
>  		goto err;
> @@ -1504,7 +1504,7 @@ err_free:
>  err_cure:
>  	close_cr_fdset(&cr_fdset);
>  err_cure_fdset:
> -	parasite_cure_seized(parasite_ctl, item);
> +	parasite_cure_seized(parasite_ctl);
>  	goto err;
>  }
>  
> diff --git a/cr-exec.c b/cr-exec.c
> index 1b73da0..c2e3bea 100644
> --- a/cr-exec.c
> +++ b/cr-exec.c
> @@ -110,7 +110,7 @@ int cr_exec(int pid, char **opt)
>  		goto out_unseize;
>  	}
>  
> -	ctl = parasite_prep_ctl(pid, &vmas);
> +	ctl = parasite_prep_ctl(pid, &vmas, 1);
>  	if (!ctl) {
>  		pr_err("Can't prep ctl %d\n", pid);
>  		goto out_unseize;
> @@ -120,7 +120,7 @@ int cr_exec(int pid, char **opt)
>  	if (ret < 0)
>  		pr_err("Can't execute syscall remotely\n");
>  
> -	parasite_cure_seized(ctl, NULL);
> +	parasite_cure_seized(ctl);
>  out_unseize:
>  	unseize_task(pid, prev_state);
>  out:
> diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h
> index 47dd8bb..9865014 100644
> --- a/include/parasite-syscall.h
> +++ b/include/parasite-syscall.h
> @@ -5,6 +5,13 @@
>  
>  #include "pstree.h"
>  
> +struct parasite_thread_ctl
> +{
> +	pid_t			tid;
> +	user_regs_struct_t	regs_orig;				/* original registers */
> +	bool			daemonized;
> +};
> +
>  /* parasite control block */
>  struct parasite_ctl {
>  	struct pid		pid;
> @@ -13,7 +20,6 @@ struct parasite_ctl {
>  	unsigned long		map_length;
>  
>  	unsigned long		parasite_ip;				/* service routine start ip */
> -	user_regs_struct_t	regs_orig;				/* original registers */
>  	unsigned long		syscall_ip;				/* entry point of infection */
>  	u8			code_orig[BUILTIN_SYSCALL_SIZE];
>  
> @@ -23,6 +29,9 @@ struct parasite_ctl {
>  	void			*addr_args;				/* address for arguments */
>  	unsigned long		args_size;
>  	int			tsock;					/* transport socket for transfering fds */
> +
> +	int			nr_threads;
> +	struct parasite_thread_ctl threads[0];
>  };
>  
>  struct cr_fdset;
> @@ -63,19 +72,21 @@ extern int parasite_drain_fds_seized(struct parasite_ctl *ctl,
>  extern int parasite_get_proc_fd_seized(struct parasite_ctl *ctl);
>  
>  struct pstree_item;
> -extern int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item);
> +extern int parasite_cure_seized(struct parasite_ctl *ctl);
>  extern struct parasite_ctl *parasite_infect_seized(pid_t pid,
>  						   struct pstree_item *item,
>  						   struct vm_area_list *vma_area_list,
>  						   struct parasite_drain_fd *dfds);
> -extern struct parasite_ctl *parasite_prep_ctl(pid_t pid, struct vm_area_list *vma_area_list);
> +extern struct parasite_ctl *parasite_prep_ctl(pid_t pid,
> +					      struct vm_area_list *vma_area_list,
> +					      unsigned int nr_threads);
>  extern int parasite_map_exchange(struct parasite_ctl *ctl, unsigned long size);
>  
>  extern struct parasite_tty_args *parasite_dump_tty(struct parasite_ctl *ctl, int fd);
>  
>  struct pstree_item;
>  extern int parasite_init_threads_seized(struct parasite_ctl *ctl, struct pstree_item *item);
> -extern int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *item);
> +extern int parasite_fini_threads_seized(struct parasite_ctl *ctl);
>  
>  int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
>  		unsigned long arg1,
> diff --git a/parasite-syscall.c b/parasite-syscall.c
> index 30af8e3..a0cc8a7 100644
> --- a/parasite-syscall.c
> +++ b/parasite-syscall.c
> @@ -126,7 +126,7 @@ retry_signal:
>  		 * and retry.
>  		 */
>  
> -		if (ptrace(PTRACE_SETREGS, pid, NULL, &ctl->regs_orig)) {
> +		if (ptrace(PTRACE_SETREGS, pid, NULL, &ctl->threads[0].regs_orig)) {
>  			pr_perror("Can't set registers (pid: %d)", pid);
>  			goto err;
>  		}
> @@ -169,7 +169,7 @@ retry_signal:
>  				pr_perror("Can't obtain registers (pid: %d)", pid);
>  				goto err;
>  			}
> -			ctl->regs_orig = r;
> +			ctl->threads[0].regs_orig = r;
>  		}
>  
>  		goto again;
> @@ -195,20 +195,12 @@ void *parasite_args_s(struct parasite_ctl *ctl, int args_size)
>  		ctl->addr_args;					\
>  	})
>  
> -static int parasite_execute_trap_by_pid(unsigned int cmd, struct parasite_ctl *ctl, pid_t pid)
> +static int parasite_execute_trap_by_pid(unsigned int cmd, struct parasite_ctl *ctl, int id)
>  {
> +	struct parasite_thread_ctl *thread = &ctl->threads[id];
> +	pid_t pid = thread->tid;
>  	int ret;
> -	user_regs_struct_t regs_orig, regs;
> -
> -	if (ctl->pid.real == pid)
> -		regs = ctl->regs_orig;
> -	else {
> -		if (ptrace(PTRACE_GETREGS, pid, NULL, &regs_orig)) {
> -			pr_perror("Can't obtain registers (pid: %d)", pid);
> -			return -1;
> -		}
> -		regs = regs_orig;
> -	}
> +	user_regs_struct_t regs = thread->regs_orig;
>  
>  	*ctl->addr_cmd = cmd;
>  
> @@ -221,20 +213,18 @@ static int parasite_execute_trap_by_pid(unsigned int cmd, struct parasite_ctl *c
>  	if (ret)
>  		pr_err("Parasite exited with %d\n", ret);
>  
> -	if (ctl->pid.real != pid) {
> -		pr_err("%d %d\n", ctl->pid.real, pid);
> -		if (ptrace(PTRACE_SETREGS, pid, NULL, &regs_orig)) {
> +	if (ctl->pid.real != pid)
> +		if (ptrace(PTRACE_SETREGS, pid, NULL, &thread->regs_orig)) {
>  			pr_perror("Can't restore registers (pid: %d)", pid);
>  			return -1;
>  		}
> -	}
>  
>  	return ret;
>  }
>  
> -int parasite_execute_trap(unsigned int cmd, struct parasite_ctl *ctl)
> +static int parasite_execute_trap(unsigned int cmd, struct parasite_ctl *ctl)
>  {
> -	return parasite_execute_trap_by_pid(cmd, ctl, ctl->pid.real);
> +	return parasite_execute_trap_by_pid(cmd, ctl, 0);
>  }
>  
>  static int __parasite_send_cmd(int sockfd, struct ctl_msg *m)
> @@ -451,11 +441,15 @@ err:
>  	return -1;
>  }
>  
> -static int parasite_daemonize(struct parasite_ctl *ctl, pid_t pid)
> +static int parasite_daemonize(struct parasite_ctl *ctl, int id)
>  {
> -	user_regs_struct_t regs = ctl->regs_orig;
> +	struct parasite_thread_ctl *thread = &ctl->threads[id];
> +	pid_t pid = thread->tid;
> +	user_regs_struct_t regs;
>  	struct ctl_msg m = { };
>  
> +	regs = thread->regs_orig;
> +
>  	*ctl->addr_cmd = PARASITE_CMD_DAEMONIZE;
>  	parasite_setup_regs(ctl->parasite_ip, &regs);
>  
> @@ -466,6 +460,7 @@ static int parasite_daemonize(struct parasite_ctl *ctl, pid_t pid)
>  
>  	if (ptrace(PTRACE_CONT, pid, NULL, NULL)) {
>  		pr_perror("Can't continue (pid: %d)\n", pid);
> +		ptrace(PTRACE_SETREGS, pid, NULL, thread->regs_orig);
>  		goto err;
>  	}
>  
> @@ -477,6 +472,7 @@ static int parasite_daemonize(struct parasite_ctl *ctl, pid_t pid)
>  		goto err;
>  	}
>  
> +	thread->daemonized = true;
>  	pr_info("Parasite %d has been switched to daemon mode\n", pid);
>  	return 0;
>  
> @@ -678,40 +674,51 @@ int parasite_init_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
>  
>  	args = parasite_args(ctl, struct parasite_init_args);
>  
> -	for (i = 0; i < item->nr_threads; i++) {
> +	for (i = 1; i < item->nr_threads; i++) {
>  		pid_t tid = item->threads[i].real;
>  
> -		if (item->pid.real == tid)
> -			continue;
> +		ctl->threads[i].tid = tid;
> +		ctl->nr_threads++;
>  
>  		args->real = tid;
> -		ret = parasite_execute_trap_by_pid(PARASITE_CMD_INIT_THREAD, ctl, tid);
> +
> +		ret = ptrace(PTRACE_GETREGS, tid, NULL, &ctl->threads[i].regs_orig);
>  		if (ret) {
> -			pr_err("Can't init thread in parasite %d\n", tid);
> -			break;
> +			pr_perror("Can't obtain registers (pid: %d)", tid);
> +			goto err;
>  		}
>  
> -		if (parasite_daemonize(ctl, tid))
> -			break;
> +		ret = parasite_execute_trap_by_pid(PARASITE_CMD_INIT_THREAD, ctl, i);
> +		if (ret) {
> +			pr_err("Can't init thread in parasite %d\n",
> +			       item->threads[i].real);
> +			goto err;
> +		}
> +
> +		if (parasite_daemonize(ctl, i))
> +			goto err;
>  	}
>  
> -	return ret;
> +	return 0;
> +err:
> +	return -1 ;
>  }
>  
> -int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *item)
> +int parasite_fini_threads_seized(struct parasite_ctl *ctl)
>  {
>  	struct parasite_init_args *args;
>  	int ret = 0, i, status;
>  
>  	args = parasite_args(ctl, struct parasite_init_args);
>  
> -	for (i = 0; i < item->nr_threads; i++) {
> -		if (item->pid.real == item->threads[i].real)
> -			continue;
> +	for (i = 1; i < ctl->nr_threads; i++) {
> +		pid_t tid = ctl->threads[i].tid;
> +
> +		if (!ctl->threads[i].daemonized)
> +			break;
>  
> -		args->real = item->threads[i].real;
> -		ret = parasite_execute_daemon_by_pid(PARASITE_CMD_FINI_THREAD, ctl,
> -					      item->threads[i].real);
> +		args->real = tid;
> +		ret = parasite_execute_daemon_by_pid(PARASITE_CMD_FINI_THREAD, ctl, tid);
>  		/*
>  		 * Note the thread's fini() can be called even when not
>  		 * all threads were init()'ed, say we're rolling back from
> @@ -725,23 +732,27 @@ int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
>  		 * would change the code logic.
>  		 */
>  		if (ret && ret != -ENOENT) {
> -			pr_err("Can't fini thread in parasite %d\n",
> -			       item->threads[i].real);
> +			pr_err("Can't fini thread in parasite %d\n", tid);
>  			break;
>  		} else if (ret == -ENOENT)
>  			continue;
>  
> -		pr_debug("Waiting for %d to trap\n", item->threads[i].real);
> -		if (wait4(item->threads[i].real, &status, __WALL, NULL) != item->threads[i].real) {
> -			pr_perror("Waited pid mismatch (pid: %d)", item->threads[i].real);
> +		pr_debug("Waiting for %d to trap\n", tid);
> +		if (wait4(tid, &status, __WALL, NULL) != tid) {
> +			pr_perror("Waited pid mismatch (pid: %d)", tid);
>  			break;
>  		}
>  
> -		pr_debug("Daemon %d exited trapping\n", item->threads[i].real);
> +		pr_debug("Daemon %d exited trapping\n", tid);
>  		if (!WIFSTOPPED(status)) {
> -			pr_err("Task is still running (pid: %d)\n", item->threads[i].real);
> +			pr_err("Task is still running (pid: %d)\n", tid);
>  			break;
>  		}
> +
> +		if (ptrace(PTRACE_SETREGS, tid, NULL, &ctl->threads[i].regs_orig)) {
> +			pr_perror("Can't restore registers (pid: %d)", tid);
> +			return -1;
> +		}
>  	}
>  
>  	return ret;
> @@ -771,13 +782,13 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
>  	return ret;
>  }
>  
> -int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item)
> +int parasite_cure_seized(struct parasite_ctl *ctl)
>  {
>  	int ret = 0;
>  
>  	if (ctl->parasite_ip) {
>  		ctl->signals_blocked = 0;
> -		ret = parasite_fini_threads_seized(ctl, item);
> +		ret = parasite_fini_threads_seized(ctl);
>  		parasite_fini_seized(ctl);
>  	}
>  
> @@ -803,40 +814,44 @@ int parasite_cure_seized(struct parasite_ctl *ctl, struct pstree_item *item)
>  		ret = -1;
>  	}
>  
> -	if (ptrace(PTRACE_SETREGS, ctl->pid.real, NULL, &ctl->regs_orig)) {
> +	if (ptrace(PTRACE_SETREGS, ctl->pid.real, NULL, &ctl->threads[0].regs_orig)) {
>  		pr_err("Can't restore registers (pid: %d)\n", ctl->pid.real);
>  		ret = -1;
>  	}
>  
> -	free(ctl);
> +	xfree(ctl);
>  	return ret;
>  }
>  
> -struct parasite_ctl *parasite_prep_ctl(pid_t pid, struct vm_area_list *vma_area_list)
> +struct parasite_ctl *parasite_prep_ctl(pid_t pid, struct vm_area_list *vma_area_list, unsigned int nr_threads)
>  {
>  	struct parasite_ctl *ctl = NULL;
>  	struct vma_area *vma_area;
>  
> +	BUG_ON(nr_threads == 0);
> +
>  	if (!arch_can_dump_task(pid))
>  		goto err;
>  
>  	/*
>  	 * Control block early setup.
>  	 */
> -	ctl = xzalloc(sizeof(*ctl));
> +	ctl = xzalloc(sizeof(*ctl) + nr_threads * sizeof(ctl->threads[0]));
>  	if (!ctl) {
>  		pr_err("Parasite control block allocation failed (pid: %d)\n", pid);
>  		goto err;
>  	}
>  
>  	ctl->tsock = -1;
> +	ctl->nr_threads = 1;
> +	ctl->threads[0].tid = pid;
>  
> -	if (ptrace(PTRACE_GETREGS, pid, NULL, &ctl->regs_orig)) {
> +	if (ptrace(PTRACE_GETREGS, pid, NULL, &ctl->threads[0].regs_orig)) {
>  		pr_err("Can't obtain registers (pid: %d)\n", pid);
>  		goto err;
>  	}
>  
> -	vma_area = get_vma_by_ip(&vma_area_list->h, REG_IP(ctl->regs_orig));
> +	vma_area = get_vma_by_ip(&vma_area_list->h, REG_IP(ctl->threads[0].regs_orig));
>  	if (!vma_area) {
>  		pr_err("No suitable VMA found to run parasite "
>  		       "bootstrap code (pid: %d)\n", pid);
> @@ -914,7 +929,9 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
>  	int ret;
>  	struct parasite_ctl *ctl;
>  
> -	ctl = parasite_prep_ctl(pid, vma_area_list);
> +	BUG_ON(item->threads[0].real != pid);
> +
> +	ctl = parasite_prep_ctl(pid, vma_area_list, item->nr_threads);
>  	if (!ctl)
>  		return NULL;
>  
> @@ -952,7 +969,7 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
>  		goto err_restore;
>  	}
>  
> -	if (parasite_daemonize(ctl, pid))
> +	if (parasite_daemonize(ctl, 0))
>  		goto err_restore;
>  
>  	ret = parasite_init_threads_seized(ctl, item);
> @@ -962,7 +979,7 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
>  	return ctl;
>  
>  err_restore:
> -	parasite_cure_seized(ctl, item);
> +	parasite_cure_seized(ctl);
>  	return NULL;
>  }
>  
> 




More information about the CRIU mailing list