mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 00:51:28 +07:00
d31626f70b
Currently, when we have a process using the transactional memory facilities on POWER8 (that is, the processor is in transactional or suspended state), and the process enters the kernel and the kernel then uses the floating-point or vector (VMX/Altivec) facility, we end up corrupting the user-visible FP/VMX/VSX state. This happens, for example, if a page fault causes a copy-on-write operation, because the copy_page function will use VMX to do the copy on POWER8. The test program below demonstrates the bug. The bug happens because when FP/VMX state for a transactional process is stored in the thread_struct, we store the checkpointed state in .fp_state/.vr_state and the transactional (current) state in .transact_fp/.transact_vr. However, when the kernel wants to use FP/VMX, it calls enable_kernel_fp() or enable_kernel_altivec(), which saves the current state in .fp_state/.vr_state. Furthermore, when we return to the user process we return with FP/VMX/VSX disabled. The next time the process uses FP/VMX/VSX, we don't know which set of state (the current register values, .fp_state/.vr_state, or .transact_fp/.transact_vr) we should be using, since we have no way to tell if we are still in the same transaction, and if not, whether the previous transaction succeeded or failed. Thus it is necessary to strictly adhere to the rule that if FP has been enabled at any point in a transaction, we must keep FP enabled for the user process with the current transactional state in the FP registers, until we detect that it is no longer in a transaction. Similarly for VMX; once enabled it must stay enabled until the process is no longer transactional. In order to keep this rule, we add a new thread_info flag which we test when returning from the kernel to userspace, called TIF_RESTORE_TM. This flag indicates that there is FP/VMX/VSX state to be restored before entering userspace, and when it is set the .tm_orig_msr field in the thread_struct indicates what state needs to be restored. The restoration is done by restore_tm_state(). The TIF_RESTORE_TM bit is set by new giveup_fpu/altivec_maybe_transactional helpers, which are called from enable_kernel_fp/altivec, giveup_vsx, and flush_fp/altivec_to_thread instead of giveup_fpu/altivec. The other thing to be done is to get the transactional FP/VMX/VSX state from .fp_state/.vr_state when doing reclaim, if that state has been saved there by giveup_fpu/altivec_maybe_transactional. Having done this, we set the FP/VMX bit in the thread's MSR after reclaim to indicate that that part of the state is now valid (having been reclaimed from the processor's checkpointed state). Finally, in the signal handling code, we move the clearing of the transactional state bits in the thread's MSR a bit earlier, before calling flush_fp_to_thread(), so that we don't unnecessarily set the TIF_RESTORE_TM bit. This is the test program: /* Michael Neuling 4/12/2013 * * See if the altivec state is leaked out of an aborted transaction due to * kernel vmx copy loops. * * gcc -m64 htm_vmxcopy.c -o htm_vmxcopy * */ /* We don't use all of these, but for reference: */ int main(int argc, char *argv[]) { long double vecin = 1.3; long double vecout; unsigned long pgsize = getpagesize(); int i; int fd; int size = pgsize*16; char tmpfile[] = "/tmp/page_faultXXXXXX"; char buf[pgsize]; char *a; uint64_t aborted = 0; fd = mkstemp(tmpfile); assert(fd >= 0); memset(buf, 0, pgsize); for (i = 0; i < size; i += pgsize) assert(write(fd, buf, pgsize) == pgsize); unlink(tmpfile); a = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); assert(a != MAP_FAILED); asm __volatile__( "lxvd2x 40,0,%[vecinptr] ; " // set 40 to initial value TBEGIN "beq 3f ;" TSUSPEND "xxlxor 40,40,40 ; " // set 40 to 0 "std 5, 0(%[map]) ;" // cause kernel vmx copy page TABORT TRESUME TEND "li %[res], 0 ;" "b 5f ;" "3: ;" // Abort handler "li %[res], 1 ;" "5: ;" "stxvd2x 40,0,%[vecoutptr] ; " : [res]"=r"(aborted) : [vecinptr]"r"(&vecin), [vecoutptr]"r"(&vecout), [map]"r"(a) : "memory", "r0", "r3", "r4", "r5", "r6", "r7"); if (aborted && (vecin != vecout)){ printf("FAILED: vector state leaked on abort %f != %f\n", (double)vecin, (double)vecout); exit(1); } munmap(a, size); close(fd); printf("PASSED!\n"); return 0; } Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
199 lines
6.1 KiB
C
199 lines
6.1 KiB
C
/* thread_info.h: PowerPC low-level thread information
|
|
* adapted from the i386 version by Paul Mackerras
|
|
*
|
|
* Copyright (C) 2002 David Howells (dhowells@redhat.com)
|
|
* - Incorporating suggestions made by Linus Torvalds and Dave Miller
|
|
*/
|
|
|
|
#ifndef _ASM_POWERPC_THREAD_INFO_H
|
|
#define _ASM_POWERPC_THREAD_INFO_H
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
/* We have 8k stacks on ppc32 and 16k on ppc64 */
|
|
|
|
#if defined(CONFIG_PPC64)
|
|
#define THREAD_SHIFT 14
|
|
#elif defined(CONFIG_PPC_256K_PAGES)
|
|
#define THREAD_SHIFT 15
|
|
#else
|
|
#define THREAD_SHIFT 13
|
|
#endif
|
|
|
|
#define THREAD_SIZE (1 << THREAD_SHIFT)
|
|
|
|
#ifdef CONFIG_PPC64
|
|
#define CURRENT_THREAD_INFO(dest, sp) clrrdi dest, sp, THREAD_SHIFT
|
|
#else
|
|
#define CURRENT_THREAD_INFO(dest, sp) rlwinm dest, sp, 0, 0, 31-THREAD_SHIFT
|
|
#endif
|
|
|
|
#ifndef __ASSEMBLY__
|
|
#include <linux/cache.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/page.h>
|
|
#include <linux/stringify.h>
|
|
|
|
/*
|
|
* low level task data.
|
|
*/
|
|
struct thread_info {
|
|
struct task_struct *task; /* main task structure */
|
|
struct exec_domain *exec_domain; /* execution domain */
|
|
int cpu; /* cpu we're on */
|
|
int preempt_count; /* 0 => preemptable,
|
|
<0 => BUG */
|
|
struct restart_block restart_block;
|
|
unsigned long local_flags; /* private flags for thread */
|
|
|
|
/* low level flags - has atomic operations done on it */
|
|
unsigned long flags ____cacheline_aligned_in_smp;
|
|
};
|
|
|
|
/*
|
|
* macros/functions for gaining access to the thread information structure
|
|
*/
|
|
#define INIT_THREAD_INFO(tsk) \
|
|
{ \
|
|
.task = &tsk, \
|
|
.exec_domain = &default_exec_domain, \
|
|
.cpu = 0, \
|
|
.preempt_count = INIT_PREEMPT_COUNT, \
|
|
.restart_block = { \
|
|
.fn = do_no_restart_syscall, \
|
|
}, \
|
|
.flags = 0, \
|
|
}
|
|
|
|
#define init_thread_info (init_thread_union.thread_info)
|
|
#define init_stack (init_thread_union.stack)
|
|
|
|
#define THREAD_SIZE_ORDER (THREAD_SHIFT - PAGE_SHIFT)
|
|
|
|
/* how to get the thread information struct from C */
|
|
static inline struct thread_info *current_thread_info(void)
|
|
{
|
|
register unsigned long sp asm("r1");
|
|
|
|
/* gcc4, at least, is smart enough to turn this into a single
|
|
* rlwinm for ppc32 and clrrdi for ppc64 */
|
|
return (struct thread_info *)(sp & ~(THREAD_SIZE-1));
|
|
}
|
|
|
|
#endif /* __ASSEMBLY__ */
|
|
|
|
/*
|
|
* thread information flag bit numbers
|
|
*/
|
|
#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
|
|
#define TIF_SIGPENDING 1 /* signal pending */
|
|
#define TIF_NEED_RESCHED 2 /* rescheduling necessary */
|
|
#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling
|
|
TIF_NEED_RESCHED */
|
|
#define TIF_32BIT 4 /* 32 bit binary */
|
|
#define TIF_RESTORE_TM 5 /* need to restore TM FP/VEC/VSX */
|
|
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
|
|
#define TIF_SINGLESTEP 8 /* singlestepping active */
|
|
#define TIF_NOHZ 9 /* in adaptive nohz mode */
|
|
#define TIF_SECCOMP 10 /* secure computing */
|
|
#define TIF_RESTOREALL 11 /* Restore all regs (implies NOERROR) */
|
|
#define TIF_NOERROR 12 /* Force successful syscall return */
|
|
#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */
|
|
#define TIF_UPROBE 14 /* breakpointed or single-stepping */
|
|
#define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */
|
|
#define TIF_EMULATE_STACK_STORE 16 /* Is an instruction emulation
|
|
for stack store? */
|
|
#define TIF_MEMDIE 17 /* is terminating due to OOM killer */
|
|
#if defined(CONFIG_PPC64)
|
|
#define TIF_ELF2ABI 18 /* function descriptors must die! */
|
|
#endif
|
|
|
|
/* as above, but as bit values */
|
|
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
|
|
#define _TIF_SIGPENDING (1<<TIF_SIGPENDING)
|
|
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
|
|
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
|
|
#define _TIF_32BIT (1<<TIF_32BIT)
|
|
#define _TIF_RESTORE_TM (1<<TIF_RESTORE_TM)
|
|
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
|
|
#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
|
|
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
|
|
#define _TIF_RESTOREALL (1<<TIF_RESTOREALL)
|
|
#define _TIF_NOERROR (1<<TIF_NOERROR)
|
|
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
|
|
#define _TIF_UPROBE (1<<TIF_UPROBE)
|
|
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
|
|
#define _TIF_EMULATE_STACK_STORE (1<<TIF_EMULATE_STACK_STORE)
|
|
#define _TIF_NOHZ (1<<TIF_NOHZ)
|
|
#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
|
|
_TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT | \
|
|
_TIF_NOHZ)
|
|
|
|
#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
|
|
_TIF_NOTIFY_RESUME | _TIF_UPROBE | \
|
|
_TIF_RESTORE_TM)
|
|
#define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR)
|
|
|
|
/* Bits in local_flags */
|
|
/* Don't move TLF_NAPPING without adjusting the code in entry_32.S */
|
|
#define TLF_NAPPING 0 /* idle thread enabled NAP mode */
|
|
#define TLF_SLEEPING 1 /* suspend code enabled SLEEP mode */
|
|
#define TLF_RESTORE_SIGMASK 2 /* Restore signal mask in do_signal */
|
|
#define TLF_LAZY_MMU 3 /* tlb_batch is active */
|
|
#define TLF_RUNLATCH 4 /* Is the runlatch enabled? */
|
|
|
|
#define _TLF_NAPPING (1 << TLF_NAPPING)
|
|
#define _TLF_SLEEPING (1 << TLF_SLEEPING)
|
|
#define _TLF_RESTORE_SIGMASK (1 << TLF_RESTORE_SIGMASK)
|
|
#define _TLF_LAZY_MMU (1 << TLF_LAZY_MMU)
|
|
#define _TLF_RUNLATCH (1 << TLF_RUNLATCH)
|
|
|
|
#ifndef __ASSEMBLY__
|
|
#define HAVE_SET_RESTORE_SIGMASK 1
|
|
static inline void set_restore_sigmask(void)
|
|
{
|
|
struct thread_info *ti = current_thread_info();
|
|
ti->local_flags |= _TLF_RESTORE_SIGMASK;
|
|
WARN_ON(!test_bit(TIF_SIGPENDING, &ti->flags));
|
|
}
|
|
static inline void clear_restore_sigmask(void)
|
|
{
|
|
current_thread_info()->local_flags &= ~_TLF_RESTORE_SIGMASK;
|
|
}
|
|
static inline bool test_restore_sigmask(void)
|
|
{
|
|
return current_thread_info()->local_flags & _TLF_RESTORE_SIGMASK;
|
|
}
|
|
static inline bool test_and_clear_restore_sigmask(void)
|
|
{
|
|
struct thread_info *ti = current_thread_info();
|
|
if (!(ti->local_flags & _TLF_RESTORE_SIGMASK))
|
|
return false;
|
|
ti->local_flags &= ~_TLF_RESTORE_SIGMASK;
|
|
return true;
|
|
}
|
|
|
|
static inline bool test_thread_local_flags(unsigned int flags)
|
|
{
|
|
struct thread_info *ti = current_thread_info();
|
|
return (ti->local_flags & flags) != 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PPC64
|
|
#define is_32bit_task() (test_thread_flag(TIF_32BIT))
|
|
#else
|
|
#define is_32bit_task() (1)
|
|
#endif
|
|
|
|
#if defined(CONFIG_PPC64)
|
|
#define is_elf2_task() (test_thread_flag(TIF_ELF2ABI))
|
|
#else
|
|
#define is_elf2_task() (0)
|
|
#endif
|
|
|
|
#endif /* !__ASSEMBLY__ */
|
|
|
|
#endif /* __KERNEL__ */
|
|
|
|
#endif /* _ASM_POWERPC_THREAD_INFO_H */
|