[CRIU] [PATCH 12/22] parasite: restore thread registers
Pavel Emelyanov
xemul at parallels.com
Wed May 15 08:55:40 EDT 2013
On 05/08/2013 05:28 PM, Andrey Vagin wrote:
> crtools dump should not be destructive.
What was before this patch? What patch introduced the problem?
> 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, ®s_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, ®s_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, ®s);
>
> @@ -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;
> }
>
>
More information about the CRIU
mailing list