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

Andrey Vagin avagin at openvz.org
Wed May 8 09:28:39 EDT 2013


crtools dump should not be destructive.

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;
 }
 
-- 
1.8.2



More information about the CRIU mailing list