aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/ds.c
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2009-04-03 10:43:33 -0400
committerIngo Molnar <mingo@elte.hu>2009-04-07 07:36:10 -0400
commitcac94f979326212831c0ea44ed9ea1622b4f4e93 (patch)
tree716fbff126d60586fa1a1adf32b2ecf08b4d2a77 /arch/x86/kernel/ds.c
parent2e8844e13ab73f1107aea4317a53ff5879f2e1d7 (diff)
x86, bts: fix race when bts tracer is removed
When the bts tracer is removed while the traced task is running, the write to clear the bts tracer pointer races with context switch code. Read the tracer once during a context switch. When a new tracer is installed, the bts tracer is set in the ds context before the tracer is initialized in order to claim the context for that tracer. This may result in write accesses using an uninitialized trace configuration when scheduling timestamps have been requested. Store active tracing flags separately and only set active flags after the tracing configuration has been initialized. Reviewed-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Markus Metzger <markus.t.metzger@intel.com> Cc: roland@redhat.com Cc: eranian@googlemail.com Cc: juan.villacis@intel.com Cc: ak@linux.jf.intel.com LKML-Reference: <20090403144548.881338000@intel.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/ds.c')
-rw-r--r--arch/x86/kernel/ds.c58
1 files changed, 37 insertions, 21 deletions
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c
index b1d6e1f502fa..c730155bf54d 100644
--- a/arch/x86/kernel/ds.c
+++ b/arch/x86/kernel/ds.c
@@ -89,6 +89,9 @@ struct bts_tracer {
89 89
90 /* Buffer overflow notification function: */ 90 /* Buffer overflow notification function: */
91 bts_ovfl_callback_t ovfl; 91 bts_ovfl_callback_t ovfl;
92
93 /* Active flags affecting trace collection. */
94 unsigned int flags;
92}; 95};
93 96
94struct pebs_tracer { 97struct pebs_tracer {
@@ -799,6 +802,8 @@ void ds_suspend_bts(struct bts_tracer *tracer)
799 if (!tracer) 802 if (!tracer)
800 return; 803 return;
801 804
805 tracer->flags = 0;
806
802 task = tracer->ds.context->task; 807 task = tracer->ds.context->task;
803 808
804 if (!task || (task == current)) 809 if (!task || (task == current))
@@ -820,6 +825,8 @@ void ds_resume_bts(struct bts_tracer *tracer)
820 if (!tracer) 825 if (!tracer)
821 return; 826 return;
822 827
828 tracer->flags = tracer->trace.ds.flags;
829
823 task = tracer->ds.context->task; 830 task = tracer->ds.context->task;
824 831
825 control = ds_cfg.ctl[dsf_bts]; 832 control = ds_cfg.ctl[dsf_bts];
@@ -1037,43 +1044,52 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
1037 } 1044 }
1038} 1045}
1039 1046
1047static inline void ds_take_timestamp(struct ds_context *context,
1048 enum bts_qualifier qualifier,
1049 struct task_struct *task)
1050{
1051 struct bts_tracer *tracer = context->bts_master;
1052 struct bts_struct ts;
1053
1054 /* Prevent compilers from reading the tracer pointer twice. */
1055 barrier();
1056
1057 if (!tracer || !(tracer->flags & BTS_TIMESTAMPS))
1058 return;
1059
1060 memset(&ts, 0, sizeof(ts));
1061 ts.qualifier = qualifier;
1062 ts.variant.timestamp.jiffies = jiffies_64;
1063 ts.variant.timestamp.pid = task->pid;
1064
1065 bts_write(tracer, &ts);
1066}
1067
1040/* 1068/*
1041 * Change the DS configuration from tracing prev to tracing next. 1069 * Change the DS configuration from tracing prev to tracing next.
1042 */ 1070 */
1043void ds_switch_to(struct task_struct *prev, struct task_struct *next) 1071void ds_switch_to(struct task_struct *prev, struct task_struct *next)
1044{ 1072{
1045 struct ds_context *prev_ctx = prev->thread.ds_ctx; 1073 struct ds_context *prev_ctx = prev->thread.ds_ctx;
1046 struct ds_context *next_ctx = next->thread.ds_ctx; 1074 struct ds_context *next_ctx = next->thread.ds_ctx;
1075 unsigned long debugctlmsr = next->thread.debugctlmsr;
1076
1077 /* Make sure all data is read before we start. */
1078 barrier();
1047 1079
1048 if (prev_ctx) { 1080 if (prev_ctx) {
1049 update_debugctlmsr(0); 1081 update_debugctlmsr(0);
1050 1082
1051 if (prev_ctx->bts_master && 1083 ds_take_timestamp(prev_ctx, bts_task_departs, prev);
1052 (prev_ctx->bts_master->trace.ds.flags & BTS_TIMESTAMPS)) {
1053 struct bts_struct ts = {
1054 .qualifier = bts_task_departs,
1055 .variant.timestamp.jiffies = jiffies_64,
1056 .variant.timestamp.pid = prev->pid
1057 };
1058 bts_write(prev_ctx->bts_master, &ts);
1059 }
1060 } 1084 }
1061 1085
1062 if (next_ctx) { 1086 if (next_ctx) {
1063 if (next_ctx->bts_master && 1087 ds_take_timestamp(next_ctx, bts_task_arrives, next);
1064 (next_ctx->bts_master->trace.ds.flags & BTS_TIMESTAMPS)) {
1065 struct bts_struct ts = {
1066 .qualifier = bts_task_arrives,
1067 .variant.timestamp.jiffies = jiffies_64,
1068 .variant.timestamp.pid = next->pid
1069 };
1070 bts_write(next_ctx->bts_master, &ts);
1071 }
1072 1088
1073 wrmsrl(MSR_IA32_DS_AREA, (unsigned long)next_ctx->ds); 1089 wrmsrl(MSR_IA32_DS_AREA, (unsigned long)next_ctx->ds);
1074 } 1090 }
1075 1091
1076 update_debugctlmsr(next->thread.debugctlmsr); 1092 update_debugctlmsr(debugctlmsr);
1077} 1093}
1078 1094
1079void ds_copy_thread(struct task_struct *tsk, struct task_struct *father) 1095void ds_copy_thread(struct task_struct *tsk, struct task_struct *father)