[CRIU] [PATCH 07/24] parasite: Switch parasite to daemon mode

Andrey Vagin avagin at openvz.org
Wed May 22 16:08:08 EDT 2013


From: Cyrill Gorcunov <gorcunov at openvz.org>

Parasite daemon mode it quite tricky. One may consider
it as consisting of two parts

 - daemon mode for thread leader
 - daemon mode for regular threads

Thread leader daemon
--------------------

Once thread leader parasite code switched initialized,
it starts spinning on socket listening for commands
to handle.

If the command destination is the thread leader itself it
handles it and replies back the ack to the caller (iow
the main crtools code).

If the recepient is not thread leader but one of threads,
then thread leader wakes up the thread by futex and makes
it to handle the command waiting on futex for result. Once
result obtained, the ack is being sending back to caller.

Thread daemon
-------------

On initialization thread daemon starts waiting a command on futex.
The futex is triggered by thread leader daemon when command received.
Once command is received and handled, the result is reported back to
the thread leader daemon, which in turn send ack message.

Both thread-leader and regular threads require own stack to operate
on since they all are present in memory simultaneously. Thus we use
call_daemon_thread() helper which takes care of providing stack
to the callee.

TODO:

 - ARM requires own wrappers on damonize/trap low-level code,
   at moment x86-64 is only covered

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
 cr-dump.c                  |  19 ++--
 include/parasite-syscall.h |   8 +-
 mem.c                      |  17 ++--
 parasite-syscall.c         | 213 +++++++++++++++++++++++++++++++++-----
 pie/parasite.c             | 249 ++++++++++++++++++++++++++++++++++++++-------
 5 files changed, 428 insertions(+), 78 deletions(-)

diff --git a/cr-dump.c b/cr-dump.c
index 10df8df..eed6fe8 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -1144,6 +1144,8 @@ static int dump_signal_queue(pid_t tid, int fd, bool group)
 	siginfo_t siginfo[32]; /* One page or all non-rt signals */
 	int ret, i = 0, j, nr;
 
+	pr_debug("Dump %s signals of %d\n", group ? "shared" : "private", tid);
+
 	arg.nr = sizeof(siginfo) / sizeof(siginfo_t);
 	arg.flags = 0;
 	if (group)
@@ -1204,14 +1206,11 @@ static int dump_task_threads(struct parasite_ctl *parasite_ctl,
 
 	for (i = 0; i < item->nr_threads; i++) {
 		/* Leader is already dumped */
-		if (item->pid.real == item->threads[i].real)
+		if (item->pid.real == item->threads[i].real) {
 			item->threads[i].virt = item->pid.virt;
-		else {
-			if (dump_task_thread(parasite_ctl, &item->threads[i], item->core[i]))
-				return -1;
+			continue;
 		}
-
-		if (dump_thread_signals(&item->threads[i]))
+		if (dump_task_thread(parasite_ctl, &item->threads[i], item->core[i]))
 			return -1;
 	}
 
@@ -1387,7 +1386,7 @@ static int dump_one_task(struct pstree_item *item)
 	pid_t pid = item->pid.real;
 	struct vm_area_list vmas;
 	struct parasite_ctl *parasite_ctl;
-	int ret = -1;
+	int i, ret = -1;
 	struct parasite_dump_misc misc;
 	struct cr_fdset *cr_fdset = NULL;
 	struct parasite_drain_fd *dfds;
@@ -1555,6 +1554,12 @@ static int dump_one_task(struct pstree_item *item)
 		goto err_cure;
 	}
 
+	for (i = 0; i < item->nr_threads; i++) {
+		ret = dump_thread_signals(&item->threads[i]);
+		if (ret)
+			goto err;
+	}
+
 	close_cr_fdset(&cr_fdset);
 err:
 	close_pid_proc();
diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h
index e4a644f..df3df8f 100644
--- a/include/parasite-syscall.h
+++ b/include/parasite-syscall.h
@@ -35,8 +35,14 @@ extern int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdse
 extern int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset);
 
 void *parasite_args_s(struct parasite_ctl *ctl, int args_size);
-int parasite_execute_trap(unsigned int cmd, struct parasite_ctl *ctl);
+int parasite_execute_daemon(unsigned int cmd, struct parasite_ctl *ctl);
 int parasite_send_fd(struct parasite_ctl *ctl, int fd);
+int __parasite_execute_daemon_by_pid(unsigned int cmd,
+					    struct parasite_ctl *ctl,
+					    pid_t pid, bool wait_ack);
+int __parasite_execute_daemon_wait_ack(unsigned int cmd,
+					      struct parasite_ctl *ctl,
+					      pid_t pid);
 
 struct parasite_dump_misc;
 struct vm_area_list;
diff --git a/mem.c b/mem.c
index 16f5177..e792622 100644
--- a/mem.c
+++ b/mem.c
@@ -373,16 +373,19 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
 
 	args->off = 0;
 	list_for_each_entry(ppb, &pp->bufs, l) {
-		ret = parasite_send_fd(ctl, ppb->p[1]);
-		if (ret)
-			goto out_pp;
-
 		args->nr_segs = ppb->nr_segs;
 		args->nr_pages = ppb->pages_in;
 		pr_debug("PPB: %d pages %d segs %u pipe %d off\n",
 				args->nr_pages, args->nr_segs, ppb->pipe_size, args->off);
 
-		ret = parasite_execute_trap(PARASITE_CMD_DUMPPAGES, ctl);
+		ret = __parasite_execute_daemon_by_pid(PARASITE_CMD_DUMPPAGES, ctl, ctl->pid.real, false);
+		if (ret < 0)
+			goto out_pp;
+		ret = parasite_send_fd(ctl, ppb->p[1]);
+		if (ret)
+			goto out_pp;
+
+		ret = __parasite_execute_daemon_wait_ack(PARASITE_CMD_DUMPPAGES, ctl, ctl->pid.real);
 		if (ret < 0)
 			goto out_pp;
 
@@ -448,7 +451,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
 	 */
 
 	pargs->add_prot = PROT_READ;
-	ret = parasite_execute_trap(PARASITE_CMD_MPROTECT_VMAS, ctl);
+	ret = parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl);
 	if (ret) {
 		pr_err("Can't dump unprotect vmas with parasite\n");
 		return ret;
@@ -459,7 +462,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
 		pr_err("Can't dump page with parasite\n");
 
 	pargs->add_prot = 0;
-	ret = parasite_execute_trap(PARASITE_CMD_MPROTECT_VMAS, ctl);
+	ret = parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl);
 	if (ret) {
 		pr_err("Can't rollback unprotected vmas with parasite\n");
 		ret = -1;
diff --git a/parasite-syscall.c b/parasite-syscall.c
index 744b1d4..fc9375e 100644
--- a/parasite-syscall.c
+++ b/parasite-syscall.c
@@ -235,6 +235,100 @@ int parasite_execute_trap(unsigned int cmd, struct parasite_ctl *ctl)
 	return parasite_execute_trap_by_pid(cmd, ctl, ctl->pid.real);
 }
 
+static int __parasite_send_cmd(int sockfd, struct ctl_msg *m)
+{
+	int ret;
+
+	ret = send(sockfd, m, sizeof(*m), 0);
+	if (ret == -1) {
+		pr_perror("Failed to send command %d to daemon %d\n", m->cmd, m->id);
+		return -1;
+	} else if (ret != sizeof(*m)) {
+		pr_err("Message to daemon is trimmed (%d/%d)\n",
+		       (int)sizeof(*m), ret);
+		return -1;
+	}
+
+	pr_debug("Sent msg to daemon %d %d %d %d\n", m->id, m->cmd, m->ack, m->err);
+	return 0;
+}
+
+static int parasite_wait_ack(int sockfd, pid_t pid, unsigned int cmd, struct ctl_msg *m)
+{
+	int ret;
+
+	pr_debug("Wait for ack %d-%d on daemon socket\n", pid, cmd);
+
+	while (1) {
+		memzero(m, sizeof(*m));
+
+		ret = recv(sockfd, m, sizeof(*m), MSG_WAITALL);
+		if (ret == -1) {
+			pr_perror("Failed to read ack from %d", pid);
+			return -1;
+		} else if (ret != sizeof(*m)) {
+			pr_err("Message reply from daemon is trimmed (%d/%d)\n",
+			       (int)sizeof(*m), ret);
+			return -1;
+		}
+		pr_debug("Fetched ack: %d %d %d %d\n",
+			 m->id, m->cmd, m->ack, m->err);
+
+		if (m->id != pid || m->cmd != cmd || m->ack != cmd) {
+			pr_err("Communication error, this is not "
+			       "the ack we expected\n");
+			return -1;
+		}
+		return 0;
+	}
+
+	return -1;
+}
+
+int __parasite_execute_daemon_wait_ack(unsigned int cmd,
+					      struct parasite_ctl *ctl,
+					      pid_t pid)
+{
+	struct ctl_msg m;
+
+	if (parasite_wait_ack(ctl->tsock, pid, cmd, &m))
+		return -1;
+
+	if (m.err != 0) {
+		pr_err("Command %d for daemon %d failed with %d\n",
+		       cmd, pid, m.err);
+		return -1;
+	}
+
+	return 0;
+}
+
+int __parasite_execute_daemon_by_pid(unsigned int cmd,
+					    struct parasite_ctl *ctl,
+					    pid_t pid, bool wait_ack)
+{
+	struct ctl_msg m;
+
+	m = ctl_msg_cmd(pid, cmd);
+	if (__parasite_send_cmd(ctl->tsock, &m))
+		return -1;
+
+	if (wait_ack)
+		return __parasite_execute_daemon_wait_ack(cmd, ctl, pid);
+
+	return 0;
+}
+
+static int parasite_execute_daemon_by_pid(unsigned int cmd, struct parasite_ctl *ctl, pid_t pid)
+{
+	return __parasite_execute_daemon_by_pid(cmd, ctl, pid, true);
+}
+
+int parasite_execute_daemon(unsigned int cmd, struct parasite_ctl *ctl)
+{
+	return parasite_execute_daemon_by_pid(cmd, ctl, ctl->pid.real);
+}
+
 static int munmap_seized(struct parasite_ctl *ctl, void *addr, size_t length)
 {
 	unsigned long x;
@@ -355,6 +449,39 @@ err:
 	return -1;
 }
 
+static int parasite_daemonize(struct parasite_ctl *ctl, pid_t pid)
+{
+	user_regs_struct_t regs = ctl->regs_orig;
+	struct ctl_msg m = { };
+
+	*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;
+	}
+
+	pr_info("Wait for parasite being daemonized...\n");
+
+	if (parasite_wait_ack(ctl->tsock, pid, PARASITE_CMD_DAEMONIZE, &m)) {
+		pr_err("Can't switch parasite %d to daemon mode %d\n",
+		       pid, m.err);
+		goto err;
+	}
+
+	pr_info("Parasite %d has been switched to daemon mode\n", pid);
+	return 0;
+
+err:
+	return -1;
+}
+
 int parasite_dump_thread_seized(struct parasite_ctl *ctl, struct pid *tid,
 		CoreEntry *core)
 {
@@ -364,7 +491,7 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, struct pid *tid,
 	args = parasite_args(ctl, struct parasite_dump_thread);
 	args->real = tid->real;
 
-	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);
@@ -382,7 +509,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;
 
@@ -425,7 +552,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;
 
@@ -445,7 +572,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;
@@ -459,7 +586,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;
@@ -470,7 +597,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;
@@ -497,18 +624,19 @@ 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_by_pid(PARASITE_CMD_DRAIN_FDS, ctl,
+					       ctl->pid.real, false);
 	if (ret) {
 		pr_err("Parasite failed to drain descriptors\n");
 		goto err;
 	}
 
 	ret = recv_fds(ctl->tsock, lfds, dfds->nr_fds, opts);
-	if (ret) {
+	if (ret)
 		pr_err("Can't retrieve FDs from socket\n");
-		goto err;
-	}
 
+	ret |= __parasite_execute_daemon_wait_ack(PARASITE_CMD_DRAIN_FDS, ctl,
+						  ctl->pid.real);
 err:
 	return ret;
 }
@@ -517,17 +645,17 @@ 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_by_pid(PARASITE_CMD_GET_PROC_FD, ctl,
+					       ctl->pid.real, false);
 	if (ret) {
 		pr_err("Parasite failed to get proc fd\n");
 		return ret;
 	}
 
 	fd = recv_fd(ctl->tsock);
-	if (fd < 0) {
+	if (fd < 0)
 		pr_err("Can't retrieve FD from socket\n");
-		return fd;
-	}
+	__parasite_execute_daemon_wait_ack(PARASITE_CMD_DRAIN_FDS, ctl, ctl->pid.real);
 
 	return fd;
 }
@@ -540,17 +668,20 @@ 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++) {
-		if (item->pid.real == item->threads[i].real)
+		pid_t tid = item->threads[i].real;
+
+		if (item->pid.real == tid)
 			continue;
 
-		args->real = item->threads[i].real;
-		ret = parasite_execute_trap_by_pid(PARASITE_CMD_INIT_THREAD, ctl,
-					      item->threads[i].real);
+		args->real = tid;
+		ret = parasite_execute_trap_by_pid(PARASITE_CMD_INIT_THREAD, ctl, tid);
 		if (ret) {
-			pr_err("Can't init thread in parasite %d\n",
-			       item->threads[i].real);
+			pr_err("Can't init thread in parasite %d\n", tid);
 			break;
 		}
+
+		if (parasite_daemonize(ctl, tid))
+			break;
 	}
 
 	return ret;
@@ -559,7 +690,7 @@ 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)
 {
 	struct parasite_init_args *args;
-	int ret = 0, i;
+	int ret = 0, i, status;
 
 	args = parasite_args(ctl, struct parasite_init_args);
 
@@ -568,7 +699,7 @@ int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
 			continue;
 
 		args->real = item->threads[i].real;
-		ret = parasite_execute_trap_by_pid(PARASITE_CMD_FINI_THREAD, ctl,
+		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
@@ -586,6 +717,19 @@ 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;
+
+		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);
+			break;
+		}
+
+		pr_debug("Daemon %d exited trapping\n", item->threads[i].real);
+		if (!WIFSTOPPED(status)) {
+			pr_err("Task is still running (pid: %d)\n", item->threads[i].real);
+			break;
 		}
 	}
 
@@ -595,25 +739,39 @@ int parasite_fini_threads_seized(struct parasite_ctl *ctl, struct pstree_item *i
 static int parasite_fini_seized(struct parasite_ctl *ctl)
 {
 	struct parasite_init_args *args;
+	int status, ret = 0;
 
 	args = parasite_args(ctl, struct parasite_init_args);
 	args->real = ctl->pid.real;
 
-	return parasite_execute_trap(PARASITE_CMD_FINI, ctl);
+	args->real = ctl->pid.real;
+	__parasite_execute_daemon_by_pid(PARASITE_CMD_FINI, ctl, ctl->pid.real, false);
+
+	if (wait4(ctl->pid.real, &status, __WALL, NULL) != ctl->pid.real) {
+		pr_perror("Waited pid mismatch (pid: %d)", ctl->pid.real);
+		ret = -1;
+	}
+
+	if (!WIFSTOPPED(status)) {
+		pr_err("Task is still running (pid: %d)\n", ctl->pid.real);
+		ret = -1;
+	}
+
+	return ret;
 }
 
 int parasite_cure_remote(struct parasite_ctl *ctl, struct pstree_item *item)
 {
 	int ret = 0;
 
-	ctl->tsock = -1;
-
 	if (ctl->parasite_ip) {
 		ctl->signals_blocked = 0;
-		parasite_fini_threads_seized(ctl, item);
+		ret = parasite_fini_threads_seized(ctl, item);
 		parasite_fini_seized(ctl);
 	}
 
+	ctl->tsock = -1;
+
 	if (ctl->remote_map) {
 		if (munmap_seized(ctl, (void *)ctl->remote_map, ctl->map_length)) {
 			pr_err("munmap_seized failed (pid: %d)\n", ctl->pid.real);
@@ -801,6 +959,9 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
 		goto err_restore;
 	}
 
+	if (parasite_daemonize(ctl, pid))
+		goto err_restore;
+
 	ret = parasite_init_threads_seized(ctl, item);
 	if (ret)
 		goto err_restore;
diff --git a/pie/parasite.c b/pie/parasite.c
index 6f2328f..c2f721c 100644
--- a/pie/parasite.c
+++ b/pie/parasite.c
@@ -255,14 +255,8 @@ static int init_thread(struct parasite_init_args *args)
 	return ret;
 }
 
-static int fini_thread(struct parasite_init_args *args)
+static int fini_thread(struct tid_state_s *s)
 {
-	struct tid_state_s *s;
-
-	s = find_thread_state(args->real);
-	if (!s)
-		return -ENOENT;
-
 	if (s->use_sig_blocked)
 		return sys_sigprocmask(SIG_SETMASK, &s->sig_blocked,
 				       NULL, sizeof(k_rtsigset_t));
@@ -437,17 +431,220 @@ static int parasite_cfg_log(struct parasite_log_args *args)
 	return ret;
 }
 
-static int fini(struct parasite_init_args *args)
+static int fini(struct tid_state_s *s)
+{
+	log_set_fd(-1);
+
+	return fini_thread(s);
+}
+
+static int __parasite_daemon_reply_ack(unsigned int id, unsigned int cmd, int err)
+{
+	struct ctl_msg m;
+	int ret;
+
+	m = ctl_msg_ack(id, cmd, err);
+	ret = sys_sendto(tsock, &m, sizeof(m), 0, NULL, 0);
+	if (ret != sizeof(m)) {
+		pr_err("Sent only %d bytes while %d expected\n",
+			ret, (int)sizeof(m));
+		return -1;
+	}
+
+	pr_debug("__sent ack msg: %d %d %d %d\n",
+		 m.id, m.cmd, m.ack, m.err);
+
+	return 0;
+}
+
+static int __parasite_daemon_wait_msg(struct ctl_msg *m)
 {
 	int ret;
 
-	ret = fini_thread(args);
+	pr_debug("Daemon wais for command\n");
 
-	sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state));
-	log_set_fd(-1);
-	sys_close(tsock);
+	while (1) {
+		*m = (struct ctl_msg){ };
+		ret = sys_recvfrom(tsock, m, sizeof(*m), MSG_WAITALL, NULL, 0);
+		if (ret != sizeof(*m)) {
+			pr_err("Trimmed message received (%d/%d)\n",
+			       (int)sizeof(*m), ret);
+			return 0;
+		}
 
-	return ret;
+		pr_debug("__fetched msg: %d %d %d %d\n",
+			 m->id, m->cmd, m->ack, m->err);
+		return 0;
+	}
+
+	return -1;
+}
+
+static int __parasite_daemon_thread_wait_cmd(struct tid_state_s *s)
+{
+	futex_wait_while_lt(&s->cmd, PARASITE_CMD_DAEMONIZED);
+	return futex_get(&s->cmd);
+}
+
+static void __parasite_daemon_thread_ack(struct tid_state_s *s, int ret)
+{
+	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);
+}
+
+static unsigned long noinline __used
+__parasite_daemon_thread(void *args, struct tid_state_s *s, unsigned long oldstack)
+{
+	pr_debug("Running daemon thread %d\n", s->real);
+
+	/* Reply we're alive */
+	if (__parasite_daemon_reply_ack(s->real, PARASITE_CMD_DAEMONIZE, 0))
+		return oldstack;
+
+	while (1) {
+		int ret, cmd;
+
+		cmd = __parasite_daemon_thread_wait_cmd(s);
+
+		pr_debug("Command %d in daemon thread %d\n", cmd, s->real);
+
+		switch (cmd) {
+		case PARASITE_CMD_DUMP_THREAD:
+			ret = dump_thread(args);
+			break;
+		case PARASITE_CMD_FINI_THREAD:
+			__parasite_daemon_thread_ack(s, 0);
+			fini_thread(s);
+			return oldstack;
+		default:
+			pr_err("Unknown command in parasite daemon thread: %d\n", cmd);
+			ret = -1;
+			break;
+		}
+		__parasite_daemon_thread_ack(s, ret);
+	}
+
+	return oldstack;
+}
+
+static int __parasite_execute_thread(struct ctl_msg *m)
+{
+	struct tid_state_s *s = find_thread_state(m->id);
+	if (!s)
+		return -ENOENT;
+
+	pr_debug("Wake thread %d daemon with command %d\n", s->real, m->cmd);
+	futex_set_and_wake(&s->cmd, m->cmd);
+
+	pr_debug("Wait thread %d for PARASITE_CMD_IDLE\n", s->real);
+	futex_wait_until(&s->cmd, PARASITE_CMD_IDLE);
+
+	pr_debug("Wake thread %d daemon with " __stringify_1(PARASITE_CMD_ACK) "\n", s->real);
+	futex_set_and_wake(&s->ack, PARASITE_CMD_ACK);
+
+	return s->ret;
+}
+
+static unsigned long noinline __used
+__parasite_daemon_thread_leader(void *args, struct tid_state_s *s, unsigned long oldstack)
+{
+	struct ctl_msg m = { };
+	int ret = -1;
+
+	pr_debug("Running daemon thread leader %d\n", s->real);
+
+	/* Reply we're alive */
+	if (__parasite_daemon_reply_ack(s->real, PARASITE_CMD_DAEMONIZE, 0))
+		return oldstack;
+
+	while (1) {
+		if (__parasite_daemon_wait_msg(&m))
+			break;
+
+		switch (m.cmd) {
+		case PARASITE_CMD_FINI:
+			ret = fini(s);
+			sys_close(tsock);
+			/*
+			 * 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);
+			break;
+		case PARASITE_CMD_DUMPPAGES:
+			ret = dump_pages(args);
+			break;
+		case PARASITE_CMD_MPROTECT_VMAS:
+			ret = mprotect_vmas(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_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 thread leader: %d\n", m.cmd);
+			ret = -1;
+			break;
+		}
+
+		if (__parasite_daemon_reply_ack(m.id, m.cmd, ret))
+			break;
+	}
+
+	return oldstack;
+}
+
+static int noinline parasite_daemon(struct parasite_init_args *args)
+{
+	struct tid_state_s *s;
+	unsigned long new_sp = 0;
+	bool is_leader = false;
+
+	s = find_thread_state(args->real);
+	if (!s)
+		return -ENOENT;
+
+	if (s->real == thread_leader->real)
+		is_leader = true;
+
+	pr_info("Parasite entering daemon mode for %d\n", s->real);
+	new_sp = (unsigned long)(void *)&s->stack[PARASITE_STACK_SIZE - 8];
+
+	if (is_leader)
+		CALL_DAEMON_THREAD(new_sp, args, s, __parasite_daemon_thread_leader);
+	else
+		CALL_DAEMON_THREAD(new_sp, args, s, __parasite_daemon_thread);
+
+	pr_info("Parasite leaving daemon mode for %d\n", s->real);
+
+	if (is_leader)
+		sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state));
+
+	ASM_TRAP();
+	return 0;
 }
 
 int __used parasite_service(unsigned int cmd, void *args)
@@ -459,32 +656,10 @@ 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(args);
-	case PARASITE_CMD_FINI_THREAD:
-		return fini_thread(args);
 	case PARASITE_CMD_CFG_LOG:
 		return parasite_cfg_log(args);
-	case PARASITE_CMD_DUMPPAGES:
-		return dump_pages(args);
-	case PARASITE_CMD_MPROTECT_VMAS:
-		return mprotect_vmas(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);
-- 
1.8.2



More information about the CRIU mailing list