diff options
author | Erik Bosman <ejbosman@cs.vu.nl> | 2008-04-13 18:24:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-04-19 13:19:55 -0400 |
commit | 529e25f646e08901a6dad5768f681efffd77225e (patch) | |
tree | a284d6511b6eef0ab83a3e9ada7a6ae300a33010 /arch/x86/kernel/process_32.c | |
parent | 8fb402bccf203ecca8f9e0202b8fd3c937dece6f (diff) |
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: Erik Bosman <ejbosman@cs.vu.nl>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel/process_32.c')
-rw-r--r-- | arch/x86/kernel/process_32.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 91e147b486dd..a3790a3f8a83 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/personality.h> | 36 | #include <linux/personality.h> |
37 | #include <linux/tick.h> | 37 | #include <linux/tick.h> |
38 | #include <linux/percpu.h> | 38 | #include <linux/percpu.h> |
39 | #include <linux/prctl.h> | ||
39 | 40 | ||
40 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
41 | #include <asm/pgtable.h> | 42 | #include <asm/pgtable.h> |
@@ -523,11 +524,11 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp) | |||
523 | } | 524 | } |
524 | EXPORT_SYMBOL_GPL(start_thread); | 525 | EXPORT_SYMBOL_GPL(start_thread); |
525 | 526 | ||
526 | #ifdef CONFIG_SECCOMP | ||
527 | static void hard_disable_TSC(void) | 527 | static void hard_disable_TSC(void) |
528 | { | 528 | { |
529 | write_cr4(read_cr4() | X86_CR4_TSD); | 529 | write_cr4(read_cr4() | X86_CR4_TSD); |
530 | } | 530 | } |
531 | |||
531 | void disable_TSC(void) | 532 | void disable_TSC(void) |
532 | { | 533 | { |
533 | preempt_disable(); | 534 | preempt_disable(); |
@@ -539,11 +540,47 @@ void disable_TSC(void) | |||
539 | hard_disable_TSC(); | 540 | hard_disable_TSC(); |
540 | preempt_enable(); | 541 | preempt_enable(); |
541 | } | 542 | } |
543 | |||
542 | static void hard_enable_TSC(void) | 544 | static void hard_enable_TSC(void) |
543 | { | 545 | { |
544 | write_cr4(read_cr4() & ~X86_CR4_TSD); | 546 | write_cr4(read_cr4() & ~X86_CR4_TSD); |
545 | } | 547 | } |
546 | #endif /* CONFIG_SECCOMP */ | 548 | |
549 | void enable_TSC(void) | ||
550 | { | ||
551 | preempt_disable(); | ||
552 | if (test_and_clear_thread_flag(TIF_NOTSC)) | ||
553 | /* | ||
554 | * Must flip the CPU state synchronously with | ||
555 | * TIF_NOTSC in the current running context. | ||
556 | */ | ||
557 | hard_enable_TSC(); | ||
558 | preempt_enable(); | ||
559 | } | ||
560 | |||
561 | int get_tsc_mode(unsigned long adr) | ||
562 | { | ||
563 | unsigned int val; | ||
564 | |||
565 | if (test_thread_flag(TIF_NOTSC)) | ||
566 | val = PR_TSC_SIGSEGV; | ||
567 | else | ||
568 | val = PR_TSC_ENABLE; | ||
569 | |||
570 | return put_user(val, (unsigned int __user *)adr); | ||
571 | } | ||
572 | |||
573 | int set_tsc_mode(unsigned int val) | ||
574 | { | ||
575 | if (val == PR_TSC_SIGSEGV) | ||
576 | disable_TSC(); | ||
577 | else if (val == PR_TSC_ENABLE) | ||
578 | enable_TSC(); | ||
579 | else | ||
580 | return -EINVAL; | ||
581 | |||
582 | return 0; | ||
583 | } | ||
547 | 584 | ||
548 | static noinline void | 585 | static noinline void |
549 | __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, | 586 | __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 | set_debugreg(next->debugreg7, 7); | 614 | set_debugreg(next->debugreg7, 7); |
578 | } | 615 | } |
579 | 616 | ||
580 | #ifdef CONFIG_SECCOMP | ||
581 | if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ | 617 | if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ |
582 | test_tsk_thread_flag(next_p, TIF_NOTSC)) { | 618 | test_tsk_thread_flag(next_p, TIF_NOTSC)) { |
583 | /* prev and next are different */ | 619 | /* prev and next are different */ |
@@ -586,7 +622,6 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, | |||
586 | else | 622 | else |
587 | hard_enable_TSC(); | 623 | hard_enable_TSC(); |
588 | } | 624 | } |
589 | #endif | ||
590 | 625 | ||
591 | #ifdef X86_BTS | 626 | #ifdef X86_BTS |
592 | if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS)) | 627 | if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS)) |