Commit 595b772a authored by Andrey Vagin's avatar Andrey Vagin Committed by Pavel Emelyanov

parasite: Switch parasite to daemon mode (v2)

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

v2: remove PARASITE_CMD_DAEMONIZED and s->ack
    parasite: use a propper command for getting ack
Fixed-by: 's avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 86d4c1ed
...@@ -10,6 +10,8 @@ struct parasite_thread_ctl ...@@ -10,6 +10,8 @@ struct parasite_thread_ctl
pid_t tid; pid_t tid;
user_regs_struct_t regs_orig; /* original registers */ user_regs_struct_t regs_orig; /* original registers */
bool daemonized;
void *rstack; void *rstack;
}; };
...@@ -45,8 +47,13 @@ extern int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdse ...@@ -45,8 +47,13 @@ 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); 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); 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_send_fd(struct parasite_ctl *ctl, int fd);
int __parasite_execute_daemon_by_id(unsigned int cmd,
struct parasite_ctl *ctl,
int id, bool wait_ack);
int __parasite_execute_daemon_wait_ack(unsigned int cmd,
struct parasite_ctl *ctl, int id);
struct parasite_dump_misc; struct parasite_dump_misc;
struct vm_area_list; struct vm_area_list;
......
...@@ -368,16 +368,19 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -368,16 +368,19 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
args->off = 0; args->off = 0;
list_for_each_entry(ppb, &pp->bufs, l) { 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_segs = ppb->nr_segs;
args->nr_pages = ppb->pages_in; args->nr_pages = ppb->pages_in;
pr_debug("PPB: %d pages %d segs %u pipe %d off\n", pr_debug("PPB: %d pages %d segs %u pipe %d off\n",
args->nr_pages, args->nr_segs, ppb->pipe_size, args->off); args->nr_pages, args->nr_segs, ppb->pipe_size, args->off);
ret = parasite_execute_trap(PARASITE_CMD_DUMPPAGES, ctl); ret = __parasite_execute_daemon_by_id(PARASITE_CMD_DUMPPAGES, ctl, 0, 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, 0);
if (ret < 0) if (ret < 0)
goto out_pp; goto out_pp;
...@@ -443,7 +446,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -443,7 +446,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
*/ */
pargs->add_prot = PROT_READ; 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) { if (ret) {
pr_err("Can't dump unprotect vmas with parasite\n"); pr_err("Can't dump unprotect vmas with parasite\n");
return ret; return ret;
...@@ -454,7 +457,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl, ...@@ -454,7 +457,7 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
pr_err("Can't dump page with parasite\n"); pr_err("Can't dump page with parasite\n");
pargs->add_prot = 0; pargs->add_prot = 0;
ret = parasite_execute_trap(PARASITE_CMD_MPROTECT_VMAS, ctl); ret = parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl);
if (ret) { if (ret) {
pr_err("Can't rollback unprotected vmas with parasite\n"); pr_err("Can't rollback unprotected vmas with parasite\n");
ret = -1; ret = -1;
......
This diff is collapsed.
...@@ -224,10 +224,8 @@ static int init_thread(struct parasite_init_args *args) ...@@ -224,10 +224,8 @@ static int init_thread(struct parasite_init_args *args)
return ret; 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 = &tid_state[args->id];
if (s->use_sig_blocked) if (s->use_sig_blocked)
return sys_sigprocmask(SIG_SETMASK, &s->sig_blocked, return sys_sigprocmask(SIG_SETMASK, &s->sig_blocked,
NULL, sizeof(k_rtsigset_t)); NULL, sizeof(k_rtsigset_t));
...@@ -417,17 +415,208 @@ static int parasite_check_vdso_mark(struct parasite_vdso_vma_entry *args) ...@@ -417,17 +415,208 @@ static int parasite_check_vdso_mark(struct parasite_vdso_vma_entry *args)
return 0; return 0;
} }
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; int ret;
ret = fini_thread(args); 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;
}
sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state)); pr_debug("__sent ack msg: %d %d %d %d\n",
log_set_fd(-1); m.id, m.cmd, m.ack, m.err);
sys_close(tsock);
return ret; return 0;
}
static int __parasite_daemon_wait_msg(struct ctl_msg *m)
{
int ret;
pr_debug("Daemon wais for command\n");
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;
}
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_eq(&s->cmd, PARASITE_CMD_IDLE);
return futex_get(&s->cmd);
}
static void __parasite_daemon_thread_ack(struct tid_state_s *s, int ret)
{
s->ret = ret;
futex_set_and_wake(&s->cmd, PARASITE_CMD_IDLE);
}
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:
__parasite_daemon_thread_ack(s, 0);
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);
}
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 void noinline __used
__parasite_daemon_thread_leader(void *args, struct tid_state_s *s)
{
struct ctl_msg m = { };
int ret = -1;
pr_debug("Running daemon thread leader\n");
/* Reply we're alive */
if (__parasite_daemon_reply_ack(0, PARASITE_CMD_DAEMONIZE, 0))
return;
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;
case PARASITE_CMD_CHECK_VDSO_MARK:
ret = parasite_check_vdso_mark(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;
}
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);
if (is_leader)
__parasite_daemon_thread_leader(args, s);
else
__parasite_daemon_thread(args, s);
pr_info("Parasite leaving daemon mode for %d\n", s->id);
if (is_leader)
sys_munmap(tid_state, TID_STATE_SIZE(nr_tid_state));
return 0;
} }
int __used parasite_service(unsigned int cmd, void *args) int __used parasite_service(unsigned int cmd, void *args)
...@@ -439,34 +628,10 @@ int __used parasite_service(unsigned int cmd, void *args) ...@@ -439,34 +628,10 @@ int __used parasite_service(unsigned int cmd, void *args)
return init(args); return init(args);
case PARASITE_CMD_INIT_THREAD: case PARASITE_CMD_INIT_THREAD:
return init_thread(args); 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: case PARASITE_CMD_CFG_LOG:
return parasite_cfg_log(args); return parasite_cfg_log(args);
case PARASITE_CMD_DUMPPAGES: case PARASITE_CMD_DAEMONIZE:
return dump_pages(args); return parasite_daemon(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_CHECK_VDSO_MARK:
return parasite_check_vdso_mark(args);
} }
pr_err("Unknown command to parasite: %d\n", cmd); pr_err("Unknown command to parasite: %d\n", cmd);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment