Commit 1ea1fdde authored by Alice Frosi's avatar Alice Frosi Committed by Pavel Emelyanov

compel: Save thread registers before executing parasite code

For dumping threads we execute parasite code before we collect the
floating point registers with get_task_regs().

The following describes the code path were this is done:

 dump_task_threads()
     for (i = 0; i < item->nr_threads; i++)
         dump_task_thread()
           parasite_dump_thread_seized()
             compel_prepare_thread()
                prepare_thread()

                   ### Get general purpose registers ###
                   ptrace_get_regs(ctx->regs)

             ### parasite code is executed in thread ###
             compel_run_in_thread(tctl, PARASITE_CMD_DUMP_THREAD)

             compel_get_thread_regs()

                 ### Get FP and VX registers ###
                 get_task_regs()

Since on s390 gcc uses floating point registers to cache general
purpose without the -msoft-float option the floating point
registers would have been clobbered after compel_run_in_thread().

With this patch we first save all thread registers with task_get_regs() and
then call the parasite code.

We introduce a new function arch_set_thread_regs() that restores the saved
registers for all threads. This is necessary for criu dump --leave-running,
--check-only, and error handling.

Pre-dump does not require to save the registers for the threads because
because only the main thread (task) is used for parsite code execution.

The above changes allow us to use all register sets in the parasite
code - therefore we can remove -msoft-float on s390.
Reviewed-by: 's avatarMichael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: 's avatarAlice Frosi <alice@linux.vnet.ibm.com>
Reviewed-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent 43ad1757
...@@ -68,11 +68,7 @@ endif ...@@ -68,11 +68,7 @@ endif
# #
# CFLAGS_PIE: # CFLAGS_PIE:
# #
# We assume that compel code does not change floating point registers. # Ensure with -fno-optimize-sibling-calls that we don't create GOT
# On s390 gcc uses fprs to cache gprs. Therefore disable floating point
# with -msoft-float.
#
# Also ensure with -fno-optimize-sibling-calls that we don't create GOT
# (Global Offset Table) relocations with gcc compilers that don't have # (Global Offset Table) relocations with gcc compilers that don't have
# commit "S/390: Fix 64 bit sibcall". # commit "S/390: Fix 64 bit sibcall".
ifeq ($(ARCH),s390) ifeq ($(ARCH),s390)
...@@ -80,7 +76,7 @@ ifeq ($(ARCH),s390) ...@@ -80,7 +76,7 @@ ifeq ($(ARCH),s390)
SRCARCH := s390 SRCARCH := s390
VDSO := y VDSO := y
DEFINES := -DCONFIG_S390 DEFINES := -DCONFIG_S390
CFLAGS_PIE := -msoft-float -fno-optimize-sibling-calls CFLAGS_PIE := -fno-optimize-sibling-calls
endif endif
export CFLAGS_PIE export CFLAGS_PIE
......
...@@ -148,32 +148,6 @@ int get_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs) ...@@ -148,32 +148,6 @@ int get_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs)
return 0; return 0;
} }
/*
* Set vector registers
*/
int set_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs)
{
struct iovec iov;
int rc;
if (!(fpregs->flags & USER_FPREGS_VXRS))
return 0;
iov.iov_base = &fpregs->vxrs_low;
iov.iov_len = sizeof(fpregs->vxrs_low);
rc = ptrace(PTRACE_SETREGSET, pid, NT_S390_VXRS_LOW, &iov);
if (rc) {
pr_perror("Couldn't set VXRS_LOW registers\n");
return rc;
}
iov.iov_base = &fpregs->vxrs_high;
iov.iov_len = sizeof(fpregs->vxrs_high);
rc = ptrace(PTRACE_SETREGSET, pid, NT_S390_VXRS_HIGH, &iov);
if (rc)
pr_perror("Couldn't set VXRS_HIGH registers\n");
return rc;
}
/* /*
* Prepare task registers for restart * Prepare task registers for restart
......
...@@ -22,6 +22,12 @@ ...@@ -22,6 +22,12 @@
#include "protobuf.h" #include "protobuf.h"
#include "images/core.pb-c.h" #include "images/core.pb-c.h"
#include "images/creds.pb-c.h" #include "images/creds.pb-c.h"
#include "ptrace.h"
#include "pstree.h"
#define NT_PRFPREG 2
#define NT_S390_VXRS_LOW 0x309
#define NT_S390_VXRS_HIGH 0x30a
/* /*
* Print general purpose and access registers * Print general purpose and access registers
...@@ -339,3 +345,106 @@ void arch_free_thread_info(CoreEntry *core) ...@@ -339,3 +345,106 @@ void arch_free_thread_info(CoreEntry *core)
xfree(CORE_THREAD_ARCH_INFO(core)); xfree(CORE_THREAD_ARCH_INFO(core));
CORE_THREAD_ARCH_INFO(core) = NULL; CORE_THREAD_ARCH_INFO(core) = NULL;
} }
/*
* Set regset for pid
*/
static int setregset(int pid, int set, const char *set_str, struct iovec *iov)
{
if (ptrace(PTRACE_SETREGSET, pid, set, iov) == 0)
return 0;
pr_perror("Couldn't set %s registers for pid %d", set_str, pid);
return -1;
}
/*
* Set floating point registers for pid from fpregs
*/
static int set_fp_regs(pid_t pid, user_fpregs_struct_t *fpregs)
{
struct iovec iov;
iov.iov_base = &fpregs->prfpreg;
iov.iov_len = sizeof(fpregs->prfpreg);
return setregset(pid, NT_PRFPREG, "PRFPREG", &iov);
}
/*
* Set vector registers
*/
static int set_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs)
{
struct iovec iov;
if (!(fpregs->flags & USER_FPREGS_VXRS))
return 0;
iov.iov_base = &fpregs->vxrs_low;
iov.iov_len = sizeof(fpregs->vxrs_low);
if (setregset(pid, NT_S390_VXRS_LOW, "S390_VXRS_LOW", &iov))
return -1;
iov.iov_base = &fpregs->vxrs_high;
iov.iov_len = sizeof(fpregs->vxrs_high);
return setregset(pid, NT_S390_VXRS_HIGH, "S390_VXRS_HIGH", &iov);
}
/*
* Restore registers for pid from core
*/
static int set_task_regs(pid_t pid, CoreEntry *core)
{
UserS390VxrsHighEntry *cvxrs_high;
UserS390VxrsLowEntry *cvxrs_low;
UserS390FpregsEntry *cfpregs;
user_fpregs_struct_t fpregs;
memset(&fpregs, 0, sizeof(fpregs));
/* Floating point registers */
cfpregs = CORE_THREAD_ARCH_INFO(core)->fpregs;
if (!cfpregs)
return -1;
fpregs.prfpreg.fpc = cfpregs->fpc;
memcpy(fpregs.prfpreg.fprs, cfpregs->fprs, sizeof(fpregs.prfpreg.fprs));
if (set_fp_regs(pid, &fpregs) < 0)
return -1;
/* Vector registers (optional) */
cvxrs_low = CORE_THREAD_ARCH_INFO(core)->vxrs_low;
if (!cvxrs_low)
return 0;
cvxrs_high = CORE_THREAD_ARCH_INFO(core)->vxrs_high;
if (!cvxrs_high)
return -1;
fpregs.flags |= USER_FPREGS_VXRS;
memcpy(&fpregs.vxrs_low, cvxrs_low->regs, sizeof(fpregs.vxrs_low));
memcpy(&fpregs.vxrs_high, cvxrs_high->regs, sizeof(fpregs.vxrs_high));
return set_vx_regs(pid, &fpregs);
}
/*
* Restore vector and floating point registers for all threads
*/
int arch_set_thread_regs(struct pstree_item *item)
{
int i;
for_each_pstree_item(item) {
if (item->pid->state == TASK_DEAD ||
item->pid->state == TASK_ZOMBIE ||
item->pid->state == TASK_HELPER)
continue;
for (i = 0; i < item->nr_threads; i++) {
if (item->threads[i]->state == TASK_DEAD ||
item->threads[i]->state == TASK_ZOMBIE)
continue;
if (set_task_regs(item->threads[i]->real,
item->core[i])) {
pr_perror("Not set registers for task %d",
item->threads[i]->real);
return -1;
}
}
}
return 0;
}
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include "asm/restorer.h" #include "asm/restorer.h"
#include "images/core.pb-c.h" #include "images/core.pb-c.h"
/* /*
* Load stack to %r15, return address in %r14 and argument 1 into %r2 * Load stack to %r15, return address in %r14 and argument 1 into %r2
*/ */
...@@ -25,5 +24,4 @@ ...@@ -25,5 +24,4 @@
#define core_get_tls(pcore, ptls) #define core_get_tls(pcore, ptls)
int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core); int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core);
#endif #endif
...@@ -83,6 +83,15 @@ ...@@ -83,6 +83,15 @@
#include "fault-injection.h" #include "fault-injection.h"
#include "dump.h" #include "dump.h"
/*
* Architectures can overwrite this function to restore register sets that
* are not covered by ptrace_set/get_regs().
*/
int __attribute__((weak)) arch_set_thread_regs(struct pstree_item *item)
{
return 0;
}
static char loc_buf[PAGE_SIZE]; static char loc_buf[PAGE_SIZE];
void free_mappings(struct vm_area_list *vma_area_list) void free_mappings(struct vm_area_list *vma_area_list)
...@@ -1641,6 +1650,7 @@ static int cr_dump_finish(int ret) ...@@ -1641,6 +1650,7 @@ static int cr_dump_finish(int ret)
delete_link_remaps(); delete_link_remaps();
clean_cr_time_mounts(); clean_cr_time_mounts();
} }
arch_set_thread_regs(root_item);
pstree_switch_state(root_item, pstree_switch_state(root_item,
(ret || post_dump_ret) ? (ret || post_dump_ret) ?
TASK_ALIVE : opts.final_state); TASK_ALIVE : opts.final_state);
......
...@@ -186,6 +186,11 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id, ...@@ -186,6 +186,11 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
tc->has_blk_sigset = true; tc->has_blk_sigset = true;
memcpy(&tc->blk_sigset, compel_thread_sigmask(tctl), sizeof(k_rtsigset_t)); memcpy(&tc->blk_sigset, compel_thread_sigmask(tctl), sizeof(k_rtsigset_t));
ret = compel_get_thread_regs(tctl, save_task_regs, core);
if (ret) {
pr_err("Can't obtain regs for thread %d\n", pid);
goto err_rth;
}
ret = compel_run_in_thread(tctl, PARASITE_CMD_DUMP_THREAD); ret = compel_run_in_thread(tctl, PARASITE_CMD_DUMP_THREAD);
if (ret) { if (ret) {
...@@ -199,12 +204,6 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id, ...@@ -199,12 +204,6 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
goto err_rth; goto err_rth;
} }
ret = compel_get_thread_regs(tctl, save_task_regs, core);
if (ret) {
pr_err("Can't obtain regs for thread %d\n", pid);
goto err_rth;
}
compel_release_thread(tctl); compel_release_thread(tctl);
tid->ns[0].virt = args->tid; tid->ns[0].virt = args->tid;
......
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