Commit 529e25f6 authored by Erik Bosman's avatar Erik Bosman Committed by Ingo Molnar

x86: implement prctl PR_GET_TSC and PR_SET_TSC

This patch implements the PR_GET_TSC and PR_SET_TSC prctl()
commands on the x86 platform (both 32 and 64 bit.) These
commands control the ability to read the timestamp counter
from userspace (the RDTSC instruction.)

While the RDTSC instuction is a useful profiling tool,
it is also the source of some non-determinism in ring-3.
For deterministic replay applications it is useful to be
able to trap and emulate (and record the outcome of) this
instruction.

This patch uses code earlier used to disable the timestamp
counter for the SECCOMP framework. A side-effect of this
patch is that the SECCOMP environment will now also disable
the timestamp counter on x86_64 due to the addition of the
TIF_NOTSC define on this platform.

The code which enables/disables the RDTSC instruction during
context switches is in the __switch_to_xtra function, which
already handles other unusual conditions, so normal
performance should not have to suffer from this change.
Signed-off-by: default avatarErik Bosman <ejbosman@cs.vu.nl>
Acked-by: default avatarArjan van de Ven  <arjan@linux.intel.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 8fb402bc
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/prctl.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -523,11 +524,11 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp) ...@@ -523,11 +524,11 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
} }
EXPORT_SYMBOL_GPL(start_thread); EXPORT_SYMBOL_GPL(start_thread);
#ifdef CONFIG_SECCOMP
static void hard_disable_TSC(void) static void hard_disable_TSC(void)
{ {
write_cr4(read_cr4() | X86_CR4_TSD); write_cr4(read_cr4() | X86_CR4_TSD);
} }
void disable_TSC(void) void disable_TSC(void)
{ {
preempt_disable(); preempt_disable();
...@@ -539,11 +540,47 @@ void disable_TSC(void) ...@@ -539,11 +540,47 @@ void disable_TSC(void)
hard_disable_TSC(); hard_disable_TSC();
preempt_enable(); preempt_enable();
} }
static void hard_enable_TSC(void) static void hard_enable_TSC(void)
{ {
write_cr4(read_cr4() & ~X86_CR4_TSD); write_cr4(read_cr4() & ~X86_CR4_TSD);
} }
#endif /* CONFIG_SECCOMP */
void enable_TSC(void)
{
preempt_disable();
if (test_and_clear_thread_flag(TIF_NOTSC))
/*
* Must flip the CPU state synchronously with
* TIF_NOTSC in the current running context.
*/
hard_enable_TSC();
preempt_enable();
}
int get_tsc_mode(unsigned long adr)
{
unsigned int val;
if (test_thread_flag(TIF_NOTSC))
val = PR_TSC_SIGSEGV;
else
val = PR_TSC_ENABLE;
return put_user(val, (unsigned int __user *)adr);
}
int set_tsc_mode(unsigned int val)
{
if (val == PR_TSC_SIGSEGV)
disable_TSC();
else if (val == PR_TSC_ENABLE)
enable_TSC();
else
return -EINVAL;
return 0;
}
static noinline void static noinline void
__switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
...@@ -577,7 +614,6 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, ...@@ -577,7 +614,6 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
set_debugreg(next->debugreg7, 7); set_debugreg(next->debugreg7, 7);
} }
#ifdef CONFIG_SECCOMP
if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
test_tsk_thread_flag(next_p, TIF_NOTSC)) { test_tsk_thread_flag(next_p, TIF_NOTSC)) {
/* prev and next are different */ /* prev and next are different */
...@@ -586,7 +622,6 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, ...@@ -586,7 +622,6 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
else else
hard_enable_TSC(); hard_enable_TSC();
} }
#endif
#ifdef X86_BTS #ifdef X86_BTS
if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS)) if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS))
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <linux/prctl.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -535,6 +536,64 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp) ...@@ -535,6 +536,64 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
} }
EXPORT_SYMBOL_GPL(start_thread); EXPORT_SYMBOL_GPL(start_thread);
static void hard_disable_TSC(void)
{
write_cr4(read_cr4() | X86_CR4_TSD);
}
void disable_TSC(void)
{
preempt_disable();
if (!test_and_set_thread_flag(TIF_NOTSC))
/*
* Must flip the CPU state synchronously with
* TIF_NOTSC in the current running context.
*/
hard_disable_TSC();
preempt_enable();
}
static void hard_enable_TSC(void)
{
write_cr4(read_cr4() & ~X86_CR4_TSD);
}
void enable_TSC(void)
{
preempt_disable();
if (test_and_clear_thread_flag(TIF_NOTSC))
/*
* Must flip the CPU state synchronously with
* TIF_NOTSC in the current running context.
*/
hard_enable_TSC();
preempt_enable();
}
int get_tsc_mode(unsigned long adr)
{
unsigned int val;
if (test_thread_flag(TIF_NOTSC))
val = PR_TSC_SIGSEGV;
else
val = PR_TSC_ENABLE;
return put_user(val, (unsigned int __user *)adr);
}
int set_tsc_mode(unsigned int val)
{
if (val == PR_TSC_SIGSEGV)
disable_TSC();
else if (val == PR_TSC_ENABLE)
enable_TSC();
else
return -EINVAL;
return 0;
}
/* /*
* This special macro can be used to load a debugging register * This special macro can be used to load a debugging register
*/ */
...@@ -572,6 +631,15 @@ static inline void __switch_to_xtra(struct task_struct *prev_p, ...@@ -572,6 +631,15 @@ static inline void __switch_to_xtra(struct task_struct *prev_p,
loaddebug(next, 7); loaddebug(next, 7);
} }
if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
test_tsk_thread_flag(next_p, TIF_NOTSC)) {
/* prev and next are different */
if (test_tsk_thread_flag(next_p, TIF_NOTSC))
hard_disable_TSC();
else
hard_enable_TSC();
}
if (test_tsk_thread_flag(next_p, TIF_IO_BITMAP)) { if (test_tsk_thread_flag(next_p, TIF_IO_BITMAP)) {
/* /*
* Copy the relevant range of the IO bitmap. * Copy the relevant range of the IO bitmap.
......
...@@ -918,4 +918,11 @@ extern void start_thread(struct pt_regs *regs, unsigned long new_ip, ...@@ -918,4 +918,11 @@ extern void start_thread(struct pt_regs *regs, unsigned long new_ip,
#define KSTK_EIP(task) (task_pt_regs(task)->ip) #define KSTK_EIP(task) (task_pt_regs(task)->ip)
/* Get/set a process' ability to use the timestamp counter instruction */
#define GET_TSC_CTL(adr) get_tsc_mode((adr))
#define SET_TSC_CTL(val) set_tsc_mode((val))
extern int get_tsc_mode(unsigned long adr);
extern int set_tsc_mode(unsigned int val);
#endif #endif
...@@ -126,6 +126,7 @@ static inline struct thread_info *stack_thread_info(void) ...@@ -126,6 +126,7 @@ static inline struct thread_info *stack_thread_info(void)
#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */ #define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */
#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */ #define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */
#define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */ #define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */
#define TIF_NOTSC 28 /* TSC is not accessible in userland */
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
...@@ -147,6 +148,7 @@ static inline struct thread_info *stack_thread_info(void) ...@@ -147,6 +148,7 @@ static inline struct thread_info *stack_thread_info(void)
#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR) #define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR) #define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
#define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS) #define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS)
#define _TIF_NOTSC (1 << TIF_NOTSC)
/* work to do on interrupt/exception return */ /* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \ #define _TIF_WORK_MASK \
...@@ -160,7 +162,7 @@ static inline struct thread_info *stack_thread_info(void) ...@@ -160,7 +162,7 @@ static inline struct thread_info *stack_thread_info(void)
/* flags to check in __switch_to() */ /* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \ #define _TIF_WORK_CTXSW \
(_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_BTS_TRACE_TS) (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_BTS_TRACE_TS|_TIF_NOTSC)
#define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW #define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG)
......
...@@ -18,6 +18,7 @@ extern unsigned int cpu_khz; ...@@ -18,6 +18,7 @@ extern unsigned int cpu_khz;
extern unsigned int tsc_khz; extern unsigned int tsc_khz;
extern void disable_TSC(void); extern void disable_TSC(void);
extern void enable_TSC(void);
static inline cycles_t get_cycles(void) static inline cycles_t get_cycles(void)
{ {
......
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