Commit 13fc78b9 authored by Andrew Vagin's avatar Andrew Vagin Committed by Pavel Emelyanov

ptrace: say to parasite_stop_on_syscall where is we now

On restore parasite_stop_on_syscall() can be called after PTRACE_SYSCALL
and after a breakpoint. parasite_stop_on_syscall() must be called only
after PTRACE_SYSCALL, so all tests where is one process stuck.

Reported-by: Mr Jenkins
Signed-off-by: 's avatarAndrew Vagin <avagin@openvz.org>
Acked-by: 's avatarTycho Andersen <tycho.andersen@canonical.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent eda6b3d0
......@@ -1540,7 +1540,7 @@ static int restore_switch_stage(int next_stage)
return restore_wait_inprogress_tasks();
}
static int attach_to_tasks(bool root_seized)
static int attach_to_tasks(bool root_seized, enum trace_flags *flag)
{
struct pstree_item *item;
......@@ -1584,9 +1584,14 @@ static int attach_to_tasks(bool root_seized)
return -1;
/* A breakpoint was not set */
if (ret == 0 && ptrace(PTRACE_SYSCALL, pid, NULL, NULL)) {
pr_perror("Unable to start %d", pid);
return -1;
if (ret > 0)
*flag = TRACE_EXIT;
else {
*flag = TRACE_ENTER;
if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL)) {
pr_perror("Unable to start %d", pid);
return -1;
}
}
}
}
......@@ -1644,6 +1649,7 @@ static void ignore_kids(void)
static int restore_root_task(struct pstree_item *init)
{
enum trace_flags flag = TRACE_ALL;
int ret, fd;
fd = open("/proc", O_DIRECTORY | O_RDONLY);
......@@ -1760,13 +1766,14 @@ static int restore_root_task(struct pstree_item *init)
timing_stop(TIME_RESTORE);
ret = attach_to_tasks(root_as_sibling);
ret = attach_to_tasks(root_as_sibling, &flag);
pr_info("Restore finished successfully. Resuming tasks.\n");
futex_set_and_wake(&task_entries->start, CR_STATE_COMPLETE);
if (ret == 0)
ret = parasite_stop_on_syscall(task_entries->nr_threads, __NR_rt_sigreturn);
ret = parasite_stop_on_syscall(task_entries->nr_threads,
__NR_rt_sigreturn, flag);
/*
* finalize_restore() always detaches from processes and
......
......@@ -125,7 +125,19 @@ extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
struct vm_area_list *vma_area_list);
#endif
extern int parasite_stop_on_syscall(int tasks, int sys_nr);
/*
* The PTRACE_SYSCALL will trap task twice -- on
* enter into and on exit from syscall. If we trace
* a single task, we may skip half of all getregs
* calls -- on exit we don't need them.
*/
enum trace_flags {
TRACE_ALL,
TRACE_ENTER,
TRACE_EXIT,
};
extern int parasite_stop_on_syscall(int tasks, int sys_nr, enum trace_flags trace);
extern int parasite_unmap(struct parasite_ctl *ctl, unsigned long addr);
#endif /* __CR_PARASITE_SYSCALL_H__ */
......@@ -827,6 +827,7 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
pid_t pid = ctl->pid.real;
user_regs_struct_t regs;
int status, ret = 0;
enum trace_flags flag;
/* stop getting chld from parasite -- we're about to step-by-step it */
if (restore_child_handler())
......@@ -873,27 +874,20 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
ret = ptrace_set_breakpoint(pid, ctl->sigreturn_addr);
if (ret < 0)
return ret;
if (ret > 0) {
pid = wait4(pid, &status, __WALL, NULL);
if (pid == -1) {
pr_perror("wait4 failed");
return -1;
}
if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) {
pr_err("Task is in unexpected state: %x\n", status);
return -1;
if (ret > 0)
flag = TRACE_EXIT;
else {
/* Start tracing syscalls */
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
if (ret) {
pr_perror("ptrace");
return -1;
}
}
/* Start tracing syscalls */
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
if (ret) {
pr_perror("ptrace");
return -1;
flag = TRACE_ENTER;
}
if (parasite_stop_on_syscall(1, __NR_rt_sigreturn))
if (parasite_stop_on_syscall(1, __NR_rt_sigreturn, flag))
return -1;
/*
......@@ -905,28 +899,20 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
return 0;
}
#define TRACE_ALL 1
#define TRACE_ENTER 2
#define TRACE_EXIT 3
/*
* Trap tasks on the exit from the specified syscall
*
* tasks - number of processes, which should be trapped
* sys_nr - the required syscall number
*/
int parasite_stop_on_syscall(int tasks, const int sys_nr)
int parasite_stop_on_syscall(int tasks, const int sys_nr, enum trace_flags trace)
{
user_regs_struct_t regs;
int status, ret;
pid_t pid;
/*
* The PTRACE_SYSCALL will trap task twice -- on
* enter into and on exit from syscall. If we trace
* a single task, we may skip half of all getregs
* calls -- on exit we don't need them.
*/
int trace = (tasks == 1 ? TRACE_ENTER : TRACE_ALL);
if (tasks > 1)
trace = TRACE_ALL;
/* Stop all threads on the enter point in sys_rt_sigreturn */
while (tasks) {
......@@ -1061,7 +1047,7 @@ int parasite_unmap(struct parasite_ctl *ctl, unsigned long addr)
if (ret)
goto err;
ret = parasite_stop_on_syscall(1, __NR_munmap);
ret = parasite_stop_on_syscall(1, __NR_munmap, TRACE_ENTER);
if (restore_thread_ctx(pid, &ctl->orig))
ret = -1;
......
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