[CRIU] [PATCH RFC] parasite: don't daemonize non-leader threads

Andrey Vagin avagin at openvz.org
Fri May 24 17:33:55 EDT 2013


For dumping thread we need to execute only one command, so its
demonization is overload.

This patch blocks all signals and dumps all thread related parameters in
PARASITE_CMD_INIT_THREAD, then registers are dumped for this thread and
the last step is unblocking thread signals.

This patch simplifies a parasite code, because it removes execution of
commands for threads.

Don't commit this patch. It's just proof of concept.

Thanks Pavel for this idea.

Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
 include/parasite-syscall.h |   7 +-
 include/parasite.h         |   4 +-
 parasite-syscall.c         | 212 +++++++++++++++++++--------------------------
 pie/parasite.c             | 197 ++++++++++-------------------------------
 4 files changed, 140 insertions(+), 280 deletions(-)

diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h
index 3babeb8..2f29ce3 100644
--- a/include/parasite-syscall.h
+++ b/include/parasite-syscall.h
@@ -12,12 +12,9 @@ struct parasite_thread_ctl
 
 	bool			daemonized;
 
-	k_rtsigset_t		sig_blocked;
 	bool			use_sig_blocked;
 
 	void			*rstack;
-	struct rt_sigframe	*sigframe;
-	struct rt_sigframe	*rsigframe;				/* address in a parasite */
 };
 
 /* parasite control block */
@@ -39,6 +36,10 @@ struct parasite_ctl {
 	struct list_head	pre_list;
 	struct page_pipe	*mem_pp;
 
+	k_rtsigset_t		sig_blocked;
+	struct rt_sigframe	*sigframe;
+	struct rt_sigframe	*rsigframe;				/* address in a parasite */
+
 	int			nr_threads;
 	struct parasite_thread_ctl threads[0];
 };
diff --git a/include/parasite.h b/include/parasite.h
index d26b130..3b54fea 100644
--- a/include/parasite.h
+++ b/include/parasite.h
@@ -72,8 +72,6 @@ struct parasite_init_args {
 	int			p_addr_len;
 	struct sockaddr_un	p_addr;
 
-	int			nr_threads;
-
 	k_rtsigset_t		sig_blocked;
 
 	struct rt_sigframe	*sigframe;
@@ -152,7 +150,7 @@ struct parasite_dump_thread {
 	int			id;
 	unsigned int		*tid_addr;
 	pid_t			tid;
-	k_rtsigset_t		blocked;
+	k_rtsigset_t		sig_blocked;
 	u32			tls;
 };
 
diff --git a/parasite-syscall.c b/parasite-syscall.c
index f44f406..3b91be1 100644
--- a/parasite-syscall.c
+++ b/parasite-syscall.c
@@ -388,8 +388,7 @@ static int parasite_init(struct parasite_ctl *ctl, pid_t pid, int nr_threads)
 	pr_info("Putting tsock into pid %d\n", pid);
 	args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid());
 	args->p_addr_len = gen_parasite_saddr(&args->p_addr, pid);
-	args->nr_threads = nr_threads;
-	args->sigframe = ctl->threads[0].rsigframe;
+	args->sigframe = ctl->rsigframe;
 	args->id = 0;
 
 	if (sock == -1) {
@@ -437,7 +436,7 @@ static int parasite_init(struct parasite_ctl *ctl, pid_t pid, int nr_threads)
 		goto err;
 	}
 
-	ctl->threads[0].sig_blocked = args->sig_blocked;
+	ctl->sig_blocked = args->sig_blocked;
 	ctl->threads[0].use_sig_blocked = true;
 
 	if (connect(sock, (struct sockaddr *)&args->p_addr, args->p_addr_len) < 0) {
@@ -499,12 +498,49 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
 				struct pid *tid, CoreEntry *core)
 {
 	struct parasite_dump_thread *args;
+	pid_t pid = tid->real;
 	int ret;
 
 	args = parasite_args(ctl, struct parasite_dump_thread);
 	args->id = id;
 
-	ret = parasite_execute_daemon_by_id(PARASITE_CMD_DUMP_THREAD, ctl, id);
+	if (id == 0)
+		ret = parasite_execute_daemon_by_id(PARASITE_CMD_DUMP_THREAD, ctl, id);
+	else {
+		user_regs_struct_t *regs_orig = &ctl->threads[id].regs_orig;
+		ctl->threads[id].tid = pid;
+
+		args->id = id;
+
+		ret = ptrace(PTRACE_GETREGS, pid, NULL, regs_orig);
+		if (ret) {
+			pr_perror("Can't obtain registers (pid: %d)", pid);
+			return -1;
+		}
+
+		ret = parasite_execute_trap_by_id(PARASITE_CMD_INIT_THREAD, ctl, id);
+		if (ret) {
+			pr_err("Can't init thread in parasite %d\n", pid);
+			return -1;
+		}
+
+		ctl->threads[id].use_sig_blocked = true;
+
+		ret = get_task_regs(pid, *regs_orig, core);
+		if (ret)
+			pr_err("Can't obtain regs for thread %d\n", pid);
+
+		if (parasite_execute_trap_by_id(PARASITE_CMD_FINI_THREAD, ctl, id)) {
+			pr_err("Can't init thread in parasite %d\n", pid);
+			return -1;
+		}
+		if (ret)
+			return -1;
+
+		memcpy(&core->thread_core->blk_sigset,
+			&args->sig_blocked, sizeof(k_rtsigset_t));
+		core->thread_core->has_blk_sigset = true;
+	}
 
 	CORE_THREAD_ARCH_INFO(core)->clear_tid_addr = encode_pointer(args->tid_addr);
 	tid->virt = args->tid;
@@ -793,85 +829,33 @@ int parasite_get_proc_fd_seized(struct parasite_ctl *ctl)
 	return fd;
 }
 
-int parasite_init_threads_seized(struct parasite_ctl *ctl, struct pstree_item *item)
-{
-	struct parasite_init_args *args;
-	int ret = 0, i;
-
-	args = parasite_args(ctl, struct parasite_init_args);
-
-	for (i = 1; i < item->nr_threads; i++) {
-		pid_t tid = item->threads[i].real;
-		user_regs_struct_t *regs_orig = &ctl->threads[i].regs_orig;
-
-		ctl->threads[i].tid = tid;
-		ctl->nr_threads++;
-
-		args->id = i;
-		args->sigframe = ctl->threads[i].rsigframe;
-
-		ret = ptrace(PTRACE_GETREGS, tid, NULL, regs_orig);
-		if (ret) {
-			pr_perror("Can't obtain registers (pid: %d)", tid);
-			goto err;
-		}
-
-		ret = parasite_execute_trap_by_id(PARASITE_CMD_INIT_THREAD, ctl, i);
-		if (ret) {
-			pr_err("Can't init thread in parasite %d\n", tid);
-			goto err;
-		}
-
-		ret = get_task_regs(tid, *regs_orig, item->core[i]);
-		if (ret) {
-			pr_err("Can't obtain regs for thread %d\n", tid);
-			goto err;
-		}
-
-		ctl->threads[i].sig_blocked = args->sig_blocked;
-		ctl->threads[i].use_sig_blocked = true;
-
-		if (parasite_daemonize(ctl, i))
-			goto err;
-	}
-
-	return 0;
-err:
-	return -1 ;
-}
-
 static int parasite_fini_seized(struct parasite_ctl *ctl)
 {
-	int status, ret = 0, i, nr = 0, nr_dmnz = 0;
-
-	/* Start to trace syscalls for each thread */
-	for (i = 0; i < ctl->nr_threads; i++) {
-		pid_t pid = ctl->threads[i].tid;
+	int status, ret = 0;
+	pid_t pid = ctl->threads[0].tid;
 
-		if (!ctl->threads[i].daemonized)
-			break;
-
-		ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
+	if (!ctl->threads[0].daemonized)
+		return 0;
 
-		pr_debug("Waiting for %d to trap\n", pid);
-		if (wait4(pid, &status, __WALL, NULL) != pid) {
-			pr_perror("Waited pid mismatch (pid: %d)", pid);
-			return -1;
-		}
+	/* Start to trace syscalls for each thread */
+	ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
 
-		pr_debug("Daemon %d exited trapping\n", pid);
-		if (!WIFSTOPPED(status)) {
-			pr_err("Task is still running (pid: %d)\n", pid);
-			return -1;
-		}
+	pr_debug("Waiting for %d to trap\n", pid);
+	if (wait4(pid, &status, __WALL, NULL) != pid) {
+		pr_perror("Waited pid mismatch (pid: %d)", pid);
+		return -1;
+	}
 
-		ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
-		if (ret) {
-			pr_perror("ptrace");
-			return -1;
-		}
+	pr_debug("Daemon %d exited trapping\n", pid);
+	if (!WIFSTOPPED(status)) {
+		pr_err("Task is still running (pid: %d)\n", pid);
+		return -1;
+	}
 
-		nr_dmnz++;
+	ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
+	if (ret) {
+		pr_perror("ptrace");
+		return -1;
 	}
 
 	ret = __parasite_execute_daemon_by_id(PARASITE_CMD_FINI, ctl, 0, false);
@@ -902,11 +886,8 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
 
 		pr_debug("%d is going to execute the syscall %lx\n", pid, regs.orig_ax);
 		if (regs.orig_ax == __NR_rt_sigreturn) {
-			nr++;
 			pr_debug("%d was stopped\n", pid);
-			if (nr == nr_dmnz)
-				break;
-			continue;
+			break;
 		}
 
 		ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
@@ -917,30 +898,23 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
 	}
 
 	/* Stop all threads on the exit point from sys_rt_sigreturn */
-	for (i = 0; i < ctl->nr_threads; i++) {
-		pid_t pid = ctl->threads[i].tid;
-
-		if (!ctl->threads[i].daemonized)
-			break;
-
-		ctl->threads[i].use_sig_blocked = false;
+	ctl->threads[0].use_sig_blocked = false;
 
-		ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
-		if (ret) {
-			pr_perror("ptrace");
-			return -1;
-		}
+	ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
+	if (ret) {
+		pr_perror("ptrace");
+		return -1;
+	}
 
-		if (wait4(pid, &status, __WALL, NULL) != pid) {
-			pr_perror("wait4 failed");
-			return -1;
-		}
+	if (wait4(pid, &status, __WALL, NULL) != pid) {
+		pr_perror("wait4 failed");
+		return -1;
+	}
 
-		pr_debug("Trap %d\n", pid);
-		if (!WIFSTOPPED(status)) {
-			pr_err("%d\n", status);
-			return -1;
-		}
+	pr_debug("Trap %d\n", pid);
+	if (!WIFSTOPPED(status)) {
+		pr_err("%d\n", status);
+		return -1;
 	}
 
 	return ret;
@@ -1128,7 +1102,7 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
 
 	ctl->args_size = parasite_args_size(vma_area_list, dfds);
 	ret = parasite_map_exchange(ctl, parasite_size + ctl->args_size +
-					 item->nr_threads * RESTORE_STACK_SIGFRAME +
+					 RESTORE_STACK_SIGFRAME +
 					 item->nr_threads * PARASITE_STACK_SIZE);
 	if (ret)
 		goto err_restore;
@@ -1142,14 +1116,18 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
 	ctl->addr_args		= parasite_sym(ctl->local_map, __export_parasite_args);
 
 	p = parasite_size + ctl->args_size;
+
+	ctl->rsigframe	= ctl->remote_map + p;
+	ctl->sigframe	= ctl->local_map  + p;
+
+	p += RESTORE_STACK_SIGFRAME;
+
 	for (i = 0; i < item->nr_threads; i++) {
 		struct parasite_thread_ctl *thread = &ctl->threads[i];
 
 		thread->rstack		= ctl->remote_map + p;
-		thread->rsigframe	= ctl->remote_map + p + PARASITE_STACK_SIZE;
-		thread->sigframe	= ctl->local_map  + p + PARASITE_STACK_SIZE;
 
-		p += PARASITE_STACK_SIZE + RESTORE_STACK_SIGFRAME;
+		p += PARASITE_STACK_SIZE;
 	}
 
 	ret = parasite_init(ctl, pid, item->nr_threads);
@@ -1170,28 +1148,14 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
 		goto err_restore;
 	}
 
-	if (parasite_daemonize(ctl, 0))
-		goto err_restore;
+	memcpy(&item->core[0]->tc->blk_sigset,
+		&ctl->sig_blocked, sizeof(k_rtsigset_t));
 
-	ret = parasite_init_threads_seized(ctl, item);
-	if (ret)
+	if (construct_sigframe(ctl->sigframe, ctl->rsigframe, item->core[0]))
 		goto err_restore;
 
-	for (i = 0; i < item->nr_threads; i++) {
-		struct parasite_thread_ctl *thread = &ctl->threads[i];
-
-		if (i == 0)
-			memcpy(&item->core[i]->tc->blk_sigset,
-				&thread->sig_blocked, sizeof(k_rtsigset_t));
-		else {
-			memcpy(&item->core[i]->thread_core->blk_sigset,
-				&thread->sig_blocked, sizeof(k_rtsigset_t));
-			item->core[i]->thread_core->has_blk_sigset = true;
-		}
-
-		if (construct_sigframe(thread->sigframe, thread->rsigframe, item->core[i]))
-			goto err_restore;
-	}
+	if (parasite_daemonize(ctl, 0))
+		goto err_restore;
 
 	return ctl;
 
diff --git a/pie/parasite.c b/pie/parasite.c
index 3e01cc7..0a94895 100644
--- a/pie/parasite.c
+++ b/pie/parasite.c
@@ -20,22 +20,7 @@
 
 static int tsock = -1;
 
-static struct tid_state_s {
-	int		id;
-
-	futex_t		cmd;
-	int		ret;
-
-	struct rt_sigframe *sigframe;
-} *tid_state;
-
-static unsigned int nr_tid_state;
-static unsigned int next_tid_state;
-
-#define TID_STATE_SIZE(n)	\
-	(ALIGN(sizeof(struct tid_state_s) * n, PAGE_SIZE))
-
-#define thread_leader	(&tid_state[0])
+static struct rt_sigframe *sigframe;
 
 #ifndef SPLICE_F_GIFT
 #define SPLICE_F_GIFT	0x08
@@ -192,17 +177,12 @@ static int dump_thread(struct parasite_dump_thread *args)
 	return 0;
 }
 
-static int init_thread(struct parasite_init_args *args)
+static int init_thread(struct parasite_dump_thread *args)
 {
 	k_rtsigset_t to_block;
+	pid_t tid = sys_gettid();
 	int ret;
 
-	if (args->id != next_tid_state)
-		return -EINVAL;
-
-	if (next_tid_state >= nr_tid_state)
-		return -ENOMEM;
-
 	ksigfillset(&to_block);
 	ret = sys_sigprocmask(SIG_SETMASK, &to_block,
 			      &args->sig_blocked,
@@ -210,72 +190,60 @@ static int init_thread(struct parasite_init_args *args)
 	if (ret)
 		return -1;
 
-	tid_state[next_tid_state].id = next_tid_state;
-	tid_state[next_tid_state].sigframe = args->sigframe;
+	ret = sys_prctl(PR_GET_TID_ADDRESS, (unsigned long) &args->tid_addr, 0, 0, 0);
+	if (ret)
+		goto err;
 
-	futex_set(&tid_state[next_tid_state].cmd, PARASITE_CMD_IDLE);
+	args->tid = tid;
+	args->tls = arch_get_tls();
 
-	next_tid_state++;
 
 	return ret;
+err:
+	sys_sigprocmask(SIG_SETMASK, &args->sig_blocked,
+				NULL, sizeof(k_rtsigset_t));
+	return ret;
 }
 
-static void __parasite_daemon_thread_ack(struct tid_state_s *s, int ret)
+static int fini_thread(struct parasite_dump_thread *args)
 {
-	s->ret = ret;
-	futex_set_and_wake(&s->cmd, PARASITE_CMD_IDLE);
-}
-
-static int fini_thread(struct tid_state_s *s)
-{
-	unsigned long new_sp;
-
-	new_sp = (long)s->sigframe + SIGFRAME_OFFSET;
-	pr_debug("%ld: new_sp=%lx ip %lx\n", sys_gettid(),
-		 new_sp, s->sigframe->uc.uc_mcontext.rip);
-
-	__parasite_daemon_thread_ack(s, 0);
-
-	ARCH_RT_SIGRETURN(new_sp);
-
-	BUG();
-
-	return 0;
+	return sys_sigprocmask(SIG_SETMASK, &args->sig_blocked,
+				NULL, sizeof(k_rtsigset_t));
 }
 
 static int init(struct parasite_init_args *args)
 {
+	k_rtsigset_t to_block;
 	int ret;
 
-	if (!args->nr_threads)
-		return -EINVAL;
-
-	tid_state = (void *)sys_mmap(NULL, TID_STATE_SIZE(args->nr_threads),
-				     PROT_READ | PROT_WRITE,
-				     MAP_PRIVATE | MAP_ANONYMOUS,
-				     -1, 0);
-	if ((unsigned long)tid_state > TASK_SIZE)
-		return -ENOMEM;
-
-	nr_tid_state = args->nr_threads;
+	sigframe = args->sigframe;
 
-	ret = init_thread(args);
-	if (ret < 0)
-		return ret;
+	ksigfillset(&to_block);
+	ret = sys_sigprocmask(SIG_SETMASK, &to_block,
+			      &args->sig_blocked,
+			      sizeof(k_rtsigset_t));
+	if (ret)
+		return -1;
 
 	tsock = sys_socket(PF_UNIX, SOCK_DGRAM, 0);
-	if (tsock < 0)
-		return tsock;
+	if (tsock < 0) {
+		ret = tsock;
+		goto err;
+	}
 
 	ret = sys_bind(tsock, (struct sockaddr *) &args->p_addr, args->p_addr_len);
 	if (ret < 0)
-		return ret;
+		goto err;
 
 	ret = sys_connect(tsock, (struct sockaddr *)&args->h_addr, args->h_addr_len);
 	if (ret < 0)
-		return ret;
+		goto err;
 
 	return 0;
+err:
+	sys_sigprocmask(SIG_SETMASK, &args->sig_blocked,
+				NULL, sizeof(k_rtsigset_t));
+	return ret;
 }
 
 static char proc_mountpoint[] = "proc.crtools";
@@ -467,80 +435,18 @@ static int __parasite_daemon_wait_msg(struct ctl_msg *m)
 	return -1;
 }
 
-static int __parasite_daemon_thread_wait_cmd(struct tid_state_s *s)
-{
-	futex_wait_while_eq(&s->cmd, PARASITE_CMD_IDLE);
-	return futex_get(&s->cmd);
-}
-
-static void noinline __used
-__parasite_daemon_thread(void *args, struct tid_state_s *s)
-{
-	pr_debug("Running daemon thread %d\n", s->id);
-
-	/* Reply we're alive */
-	if (__parasite_daemon_reply_ack(s->id, PARASITE_CMD_DAEMONIZE, 0))
-		return;
-
-	while (1) {
-		int ret, cmd;
-
-		cmd = __parasite_daemon_thread_wait_cmd(s);
-
-		pr_debug("Command %d in daemon thread %d\n", cmd, s->id);
-
-		switch (cmd) {
-		case PARASITE_CMD_DUMP_THREAD:
-			ret = dump_thread(args);
-			break;
-		case PARASITE_CMD_FINI_THREAD:
-			fini_thread(s);
-			return;
-		default:
-			pr_err("Unknown command in parasite daemon thread: %d\n", cmd);
-			ret = -1;
-			break;
-		}
-		__parasite_daemon_thread_ack(s, ret);
-	}
-
-	pr_err("The thread %d trys to escape!!!", s->id);
-	BUG();
-	return;
-}
-
-static int __parasite_execute_thread(struct ctl_msg *m)
-{
-	struct tid_state_s *s = &tid_state[m->id];
-
-	pr_debug("Wake thread %d daemon with command %d\n", s->id, m->cmd);
-	futex_set_and_wake(&s->cmd, m->cmd);
-
-	pr_debug("Wait thread %d for PARASITE_CMD_IDLE\n", s->id);
-	futex_wait_until(&s->cmd, PARASITE_CMD_IDLE);
-
-	return s->ret;
-}
-
-static int fini(struct tid_state_s *s)
+static int fini()
 {
 	unsigned long new_sp;
-	int i;
-
-	for (i = 1; i < next_tid_state; i++) {
-		struct ctl_msg m = {.cmd = PARASITE_CMD_FINI_THREAD, .id = i};
-		__parasite_execute_thread(&m);
-	}
 
-	new_sp = (long)s->sigframe + SIGFRAME_OFFSET;
+			pr_debug("xxx\n");
+	new_sp = (long)sigframe + SIGFRAME_OFFSET;
 	pr_debug("%ld: new_sp=%lx ip %lx\n", sys_gettid(),
-		  new_sp, s->sigframe->uc.uc_mcontext.rip);
+		  new_sp, sigframe->uc.uc_mcontext.rip);
 
 	sys_close(tsock);
 	log_set_fd(-1);
 
-	sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state));
-
 	ARCH_RT_SIGRETURN(new_sp);
 
 	BUG();
@@ -549,7 +455,7 @@ static int fini(struct tid_state_s *s)
 }
 
 static void noinline __used
-__parasite_daemon_thread_leader(void *args, struct tid_state_s *s)
+__parasite_daemon_thread_leader(void *args)
 {
 	struct ctl_msg m = { };
 	int ret = -1;
@@ -566,17 +472,14 @@ __parasite_daemon_thread_leader(void *args, struct tid_state_s *s)
 
 		switch (m.cmd) {
 		case PARASITE_CMD_FINI:
-			ret = fini(s);
-			sys_close(tsock);
+			pr_debug("xxx\n");
+			fini();
 			/*
 			 * No ACK here since we're getting out.
 			 */
 			break;
-		case PARASITE_CMD_FINI_THREAD:
-			ret = __parasite_execute_thread(&m);
-			break;
 		case PARASITE_CMD_DUMP_THREAD:
-			ret = __parasite_execute_thread(&m);
+			ret = dump_thread(args);
 			break;
 		case PARASITE_CMD_DUMPPAGES:
 			ret = dump_pages(args);
@@ -619,26 +522,18 @@ __parasite_daemon_thread_leader(void *args, struct tid_state_s *s)
 	}
 
 out:
-	fini(&tid_state[0]);
+	fini();
 
 	return;
 }
 
 static int noinline parasite_daemon(struct parasite_init_args *args)
 {
-	struct tid_state_s *s;
-	bool is_leader = (args->id == 0);
-
-	s = &tid_state[args->id];
-
-	pr_info("Parasite entering daemon mode for %d\n", s->id);
+	pr_info("Parasite entering daemon mode\n");
 
-	if (is_leader)
-		__parasite_daemon_thread_leader(args, s);
-	else
-		__parasite_daemon_thread(args, s);
+	__parasite_daemon_thread_leader(args);
 
-	pr_info("Parasite leaving daemon mode for %d\n", s->id);
+	pr_info("Parasite leaving daemon mode\n");
 
 	return 0;
 }
@@ -652,6 +547,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_THREAD:
+		return fini_thread(args);
 	case PARASITE_CMD_CFG_LOG:
 		return parasite_cfg_log(args);
 	case PARASITE_CMD_DAEMONIZE:
-- 
1.8.2



More information about the CRIU mailing list