diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2009-04-03 10:43:40 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-07 07:36:20 -0400 |
commit | de79f54f5347ad7ec6ff55ccbb6d4ab2a21f6a93 (patch) | |
tree | dfd3f000600b942a545cbc8acd2f2e67f4518015 /arch | |
parent | 35bb7600c17762bb129588c1877d2717fe325289 (diff) |
x86, bts, hw-branch-tracer: add _noirq variants to the debug store interface
The hw-branch-tracer uses debug store functions from an on_each_cpu()
context, which is simply wrong since the functions may sleep.
Add _noirq variants for most functions, which may be called with
interrupts disabled.
Separate per-cpu and per-task tracing and allow per-cpu tracing to be
controlled from any cpu.
Make the hw-branch-tracer use the new debug store interface, synchronize
with hotplug cpu event using get/put_online_cpus(), and remove the
unnecessary spinlock.
Make the ptrace bts and the ds selftest code use the new interface.
Defer the ds selftest.
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Cc: roland@redhat.com
Cc: eranian@googlemail.com
Cc: oleg@redhat.com
Cc: juan.villacis@intel.com
Cc: ak@linux.jf.intel.com
LKML-Reference: <20090403144555.658136000@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/ds.h | 57 | ||||
-rw-r--r-- | arch/x86/kernel/ds.c | 474 | ||||
-rw-r--r-- | arch/x86/kernel/ds_selftest.c | 9 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 5 |
4 files changed, 420 insertions, 125 deletions
diff --git a/arch/x86/include/asm/ds.h b/arch/x86/include/asm/ds.h index 772f141afb9a..413e127e567d 100644 --- a/arch/x86/include/asm/ds.h +++ b/arch/x86/include/asm/ds.h | |||
@@ -15,8 +15,8 @@ | |||
15 | * - buffer allocation (memory accounting) | 15 | * - buffer allocation (memory accounting) |
16 | * | 16 | * |
17 | * | 17 | * |
18 | * Copyright (C) 2007-2008 Intel Corporation. | 18 | * Copyright (C) 2007-2009 Intel Corporation. |
19 | * Markus Metzger <markus.t.metzger@intel.com>, 2007-2008 | 19 | * Markus Metzger <markus.t.metzger@intel.com>, 2007-2009 |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifndef _ASM_X86_DS_H | 22 | #ifndef _ASM_X86_DS_H |
@@ -83,8 +83,10 @@ enum ds_feature { | |||
83 | * The interrupt threshold is independent from the overflow callback | 83 | * The interrupt threshold is independent from the overflow callback |
84 | * to allow users to use their own overflow interrupt handling mechanism. | 84 | * to allow users to use their own overflow interrupt handling mechanism. |
85 | * | 85 | * |
86 | * task: the task to request recording for; | 86 | * The function might sleep. |
87 | * NULL for per-cpu recording on the current cpu | 87 | * |
88 | * task: the task to request recording for | ||
89 | * cpu: the cpu to request recording for | ||
88 | * base: the base pointer for the (non-pageable) buffer; | 90 | * base: the base pointer for the (non-pageable) buffer; |
89 | * size: the size of the provided buffer in bytes | 91 | * size: the size of the provided buffer in bytes |
90 | * ovfl: pointer to a function to be called on buffer overflow; | 92 | * ovfl: pointer to a function to be called on buffer overflow; |
@@ -93,19 +95,28 @@ enum ds_feature { | |||
93 | * -1 if no interrupt threshold is requested. | 95 | * -1 if no interrupt threshold is requested. |
94 | * flags: a bit-mask of the above flags | 96 | * flags: a bit-mask of the above flags |
95 | */ | 97 | */ |
96 | extern struct bts_tracer *ds_request_bts(struct task_struct *task, | 98 | extern struct bts_tracer *ds_request_bts_task(struct task_struct *task, |
97 | void *base, size_t size, | 99 | void *base, size_t size, |
98 | bts_ovfl_callback_t ovfl, | 100 | bts_ovfl_callback_t ovfl, |
99 | size_t th, unsigned int flags); | 101 | size_t th, unsigned int flags); |
100 | extern struct pebs_tracer *ds_request_pebs(struct task_struct *task, | 102 | extern struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size, |
101 | void *base, size_t size, | 103 | bts_ovfl_callback_t ovfl, |
102 | pebs_ovfl_callback_t ovfl, | 104 | size_t th, unsigned int flags); |
103 | size_t th, unsigned int flags); | 105 | extern struct pebs_tracer *ds_request_pebs_task(struct task_struct *task, |
106 | void *base, size_t size, | ||
107 | pebs_ovfl_callback_t ovfl, | ||
108 | size_t th, unsigned int flags); | ||
109 | extern struct pebs_tracer *ds_request_pebs_cpu(int cpu, | ||
110 | void *base, size_t size, | ||
111 | pebs_ovfl_callback_t ovfl, | ||
112 | size_t th, unsigned int flags); | ||
104 | 113 | ||
105 | /* | 114 | /* |
106 | * Release BTS or PEBS resources | 115 | * Release BTS or PEBS resources |
107 | * Suspend and resume BTS or PEBS tracing | 116 | * Suspend and resume BTS or PEBS tracing |
108 | * | 117 | * |
118 | * Must be called with irq's enabled. | ||
119 | * | ||
109 | * tracer: the tracer handle returned from ds_request_~() | 120 | * tracer: the tracer handle returned from ds_request_~() |
110 | */ | 121 | */ |
111 | extern void ds_release_bts(struct bts_tracer *tracer); | 122 | extern void ds_release_bts(struct bts_tracer *tracer); |
@@ -115,6 +126,28 @@ extern void ds_release_pebs(struct pebs_tracer *tracer); | |||
115 | extern void ds_suspend_pebs(struct pebs_tracer *tracer); | 126 | extern void ds_suspend_pebs(struct pebs_tracer *tracer); |
116 | extern void ds_resume_pebs(struct pebs_tracer *tracer); | 127 | extern void ds_resume_pebs(struct pebs_tracer *tracer); |
117 | 128 | ||
129 | /* | ||
130 | * Release BTS or PEBS resources | ||
131 | * Suspend and resume BTS or PEBS tracing | ||
132 | * | ||
133 | * Cpu tracers must call this on the traced cpu. | ||
134 | * Task tracers must call ds_release_~_noirq() for themselves. | ||
135 | * | ||
136 | * May be called with irq's disabled. | ||
137 | * | ||
138 | * Returns 0 if successful; | ||
139 | * -EPERM if the cpu tracer does not trace the current cpu. | ||
140 | * -EPERM if the task tracer does not trace itself. | ||
141 | * | ||
142 | * tracer: the tracer handle returned from ds_request_~() | ||
143 | */ | ||
144 | extern int ds_release_bts_noirq(struct bts_tracer *tracer); | ||
145 | extern int ds_suspend_bts_noirq(struct bts_tracer *tracer); | ||
146 | extern int ds_resume_bts_noirq(struct bts_tracer *tracer); | ||
147 | extern int ds_release_pebs_noirq(struct pebs_tracer *tracer); | ||
148 | extern int ds_suspend_pebs_noirq(struct pebs_tracer *tracer); | ||
149 | extern int ds_resume_pebs_noirq(struct pebs_tracer *tracer); | ||
150 | |||
118 | 151 | ||
119 | /* | 152 | /* |
120 | * The raw DS buffer state as it is used for BTS and PEBS recording. | 153 | * The raw DS buffer state as it is used for BTS and PEBS recording. |
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index 2071b992c35c..21a3852abf68 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c | |||
@@ -245,60 +245,50 @@ struct ds_context { | |||
245 | struct pebs_tracer *pebs_master; | 245 | struct pebs_tracer *pebs_master; |
246 | 246 | ||
247 | /* Use count: */ | 247 | /* Use count: */ |
248 | unsigned long count; | 248 | unsigned long count; |
249 | 249 | ||
250 | /* Pointer to the context pointer field: */ | 250 | /* Pointer to the context pointer field: */ |
251 | struct ds_context **this; | 251 | struct ds_context **this; |
252 | 252 | ||
253 | /* The traced task; NULL for current cpu: */ | 253 | /* The traced task; NULL for cpu tracing: */ |
254 | struct task_struct *task; | 254 | struct task_struct *task; |
255 | }; | ||
256 | 255 | ||
257 | static DEFINE_PER_CPU(struct ds_context *, system_context_array); | 256 | /* The traced cpu; only valid if task is NULL: */ |
257 | int cpu; | ||
258 | }; | ||
258 | 259 | ||
259 | #define system_context per_cpu(system_context_array, smp_processor_id()) | 260 | static DEFINE_PER_CPU(struct ds_context *, cpu_context); |
260 | 261 | ||
261 | 262 | ||
262 | static inline struct ds_context *ds_get_context(struct task_struct *task) | 263 | static struct ds_context *ds_get_context(struct task_struct *task, int cpu) |
263 | { | 264 | { |
264 | struct ds_context **p_context = | 265 | struct ds_context **p_context = |
265 | (task ? &task->thread.ds_ctx : &system_context); | 266 | (task ? &task->thread.ds_ctx : &per_cpu(cpu_context, cpu)); |
266 | struct ds_context *context = NULL; | 267 | struct ds_context *context = NULL; |
267 | struct ds_context *new_context = NULL; | 268 | struct ds_context *new_context = NULL; |
268 | unsigned long irq; | ||
269 | 269 | ||
270 | /* | 270 | /* Chances are small that we already have a context. */ |
271 | * Chances are small that we already have a context. | 271 | new_context = kzalloc(sizeof(*new_context), GFP_KERNEL); |
272 | * | ||
273 | * Contexts for per-cpu tracing are allocated using | ||
274 | * smp_call_function(). We must not sleep. | ||
275 | */ | ||
276 | new_context = kzalloc(sizeof(*new_context), GFP_ATOMIC); | ||
277 | if (!new_context) | 272 | if (!new_context) |
278 | return NULL; | 273 | return NULL; |
279 | 274 | ||
280 | spin_lock_irqsave(&ds_lock, irq); | 275 | spin_lock_irq(&ds_lock); |
281 | 276 | ||
282 | context = *p_context; | 277 | context = *p_context; |
283 | if (!context) { | 278 | if (likely(!context)) { |
284 | context = new_context; | 279 | context = new_context; |
285 | 280 | ||
286 | context->this = p_context; | 281 | context->this = p_context; |
287 | context->task = task; | 282 | context->task = task; |
283 | context->cpu = cpu; | ||
288 | context->count = 0; | 284 | context->count = 0; |
289 | 285 | ||
290 | if (task) | ||
291 | set_tsk_thread_flag(task, TIF_DS_AREA_MSR); | ||
292 | |||
293 | if (!task || (task == current)) | ||
294 | wrmsrl(MSR_IA32_DS_AREA, (unsigned long)context->ds); | ||
295 | |||
296 | *p_context = context; | 286 | *p_context = context; |
297 | } | 287 | } |
298 | 288 | ||
299 | context->count++; | 289 | context->count++; |
300 | 290 | ||
301 | spin_unlock_irqrestore(&ds_lock, irq); | 291 | spin_unlock_irq(&ds_lock); |
302 | 292 | ||
303 | if (context != new_context) | 293 | if (context != new_context) |
304 | kfree(new_context); | 294 | kfree(new_context); |
@@ -306,7 +296,7 @@ static inline struct ds_context *ds_get_context(struct task_struct *task) | |||
306 | return context; | 296 | return context; |
307 | } | 297 | } |
308 | 298 | ||
309 | static inline void ds_put_context(struct ds_context *context) | 299 | static void ds_put_context(struct ds_context *context) |
310 | { | 300 | { |
311 | struct task_struct *task; | 301 | struct task_struct *task; |
312 | unsigned long irq; | 302 | unsigned long irq; |
@@ -328,8 +318,15 @@ static inline void ds_put_context(struct ds_context *context) | |||
328 | if (task) | 318 | if (task) |
329 | clear_tsk_thread_flag(task, TIF_DS_AREA_MSR); | 319 | clear_tsk_thread_flag(task, TIF_DS_AREA_MSR); |
330 | 320 | ||
331 | if (!task || (task == current)) | 321 | /* |
332 | wrmsrl(MSR_IA32_DS_AREA, 0); | 322 | * We leave the (now dangling) pointer to the DS configuration in |
323 | * the DS_AREA msr. This is as good or as bad as replacing it with | ||
324 | * NULL - the hardware would crash if we enabled tracing. | ||
325 | * | ||
326 | * This saves us some problems with having to write an msr on a | ||
327 | * different cpu while preventing others from doing the same for the | ||
328 | * next context for that same cpu. | ||
329 | */ | ||
333 | 330 | ||
334 | spin_unlock_irqrestore(&ds_lock, irq); | 331 | spin_unlock_irqrestore(&ds_lock, irq); |
335 | 332 | ||
@@ -340,6 +337,31 @@ static inline void ds_put_context(struct ds_context *context) | |||
340 | kfree(context); | 337 | kfree(context); |
341 | } | 338 | } |
342 | 339 | ||
340 | static void ds_install_ds_area(struct ds_context *context) | ||
341 | { | ||
342 | unsigned long ds; | ||
343 | |||
344 | ds = (unsigned long)context->ds; | ||
345 | |||
346 | /* | ||
347 | * There is a race between the bts master and the pebs master. | ||
348 | * | ||
349 | * The thread/cpu access is synchronized via get/put_cpu() for | ||
350 | * task tracing and via wrmsr_on_cpu for cpu tracing. | ||
351 | * | ||
352 | * If bts and pebs are collected for the same task or same cpu, | ||
353 | * the same confiuration is written twice. | ||
354 | */ | ||
355 | if (context->task) { | ||
356 | get_cpu(); | ||
357 | if (context->task == current) | ||
358 | wrmsrl(MSR_IA32_DS_AREA, ds); | ||
359 | set_tsk_thread_flag(context->task, TIF_DS_AREA_MSR); | ||
360 | put_cpu(); | ||
361 | } else | ||
362 | wrmsr_on_cpu(context->cpu, MSR_IA32_DS_AREA, | ||
363 | (u32)((u64)ds), (u32)((u64)ds >> 32)); | ||
364 | } | ||
343 | 365 | ||
344 | /* | 366 | /* |
345 | * Call the tracer's callback on a buffer overflow. | 367 | * Call the tracer's callback on a buffer overflow. |
@@ -622,6 +644,7 @@ static void ds_init_ds_trace(struct ds_trace *trace, enum ds_qualifier qual, | |||
622 | * The value for 'no threshold' is -1, which will set the | 644 | * The value for 'no threshold' is -1, which will set the |
623 | * threshold outside of the buffer, just like we want it. | 645 | * threshold outside of the buffer, just like we want it. |
624 | */ | 646 | */ |
647 | ith *= ds_cfg.sizeof_rec[qual]; | ||
625 | trace->ith = (void *)(buffer + size - ith); | 648 | trace->ith = (void *)(buffer + size - ith); |
626 | 649 | ||
627 | trace->flags = flags; | 650 | trace->flags = flags; |
@@ -630,7 +653,7 @@ static void ds_init_ds_trace(struct ds_trace *trace, enum ds_qualifier qual, | |||
630 | 653 | ||
631 | static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace, | 654 | static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace, |
632 | enum ds_qualifier qual, struct task_struct *task, | 655 | enum ds_qualifier qual, struct task_struct *task, |
633 | void *base, size_t size, size_t th, unsigned int flags) | 656 | int cpu, void *base, size_t size, size_t th) |
634 | { | 657 | { |
635 | struct ds_context *context; | 658 | struct ds_context *context; |
636 | int error; | 659 | int error; |
@@ -643,7 +666,7 @@ static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace, | |||
643 | if (!base) | 666 | if (!base) |
644 | goto out; | 667 | goto out; |
645 | 668 | ||
646 | /* We require some space to do alignment adjustments below. */ | 669 | /* We need space for alignment adjustments in ds_init_ds_trace(). */ |
647 | error = -EINVAL; | 670 | error = -EINVAL; |
648 | if (size < (DS_ALIGNMENT + ds_cfg.sizeof_rec[qual])) | 671 | if (size < (DS_ALIGNMENT + ds_cfg.sizeof_rec[qual])) |
649 | goto out; | 672 | goto out; |
@@ -660,25 +683,27 @@ static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace, | |||
660 | tracer->size = size; | 683 | tracer->size = size; |
661 | 684 | ||
662 | error = -ENOMEM; | 685 | error = -ENOMEM; |
663 | context = ds_get_context(task); | 686 | context = ds_get_context(task, cpu); |
664 | if (!context) | 687 | if (!context) |
665 | goto out; | 688 | goto out; |
666 | tracer->context = context; | 689 | tracer->context = context; |
667 | 690 | ||
668 | ds_init_ds_trace(trace, qual, base, size, th, flags); | 691 | /* |
692 | * Defer any tracer-specific initialization work for the context until | ||
693 | * context ownership has been clarified. | ||
694 | */ | ||
669 | 695 | ||
670 | error = 0; | 696 | error = 0; |
671 | out: | 697 | out: |
672 | return error; | 698 | return error; |
673 | } | 699 | } |
674 | 700 | ||
675 | struct bts_tracer *ds_request_bts(struct task_struct *task, | 701 | static struct bts_tracer *ds_request_bts(struct task_struct *task, int cpu, |
676 | void *base, size_t size, | 702 | void *base, size_t size, |
677 | bts_ovfl_callback_t ovfl, size_t th, | 703 | bts_ovfl_callback_t ovfl, size_t th, |
678 | unsigned int flags) | 704 | unsigned int flags) |
679 | { | 705 | { |
680 | struct bts_tracer *tracer; | 706 | struct bts_tracer *tracer; |
681 | unsigned long irq; | ||
682 | int error; | 707 | int error; |
683 | 708 | ||
684 | /* Buffer overflow notification is not yet implemented. */ | 709 | /* Buffer overflow notification is not yet implemented. */ |
@@ -690,42 +715,46 @@ struct bts_tracer *ds_request_bts(struct task_struct *task, | |||
690 | if (error < 0) | 715 | if (error < 0) |
691 | goto out; | 716 | goto out; |
692 | 717 | ||
693 | /* | ||
694 | * Per-cpu tracing is typically requested using smp_call_function(). | ||
695 | * We must not sleep. | ||
696 | */ | ||
697 | error = -ENOMEM; | 718 | error = -ENOMEM; |
698 | tracer = kzalloc(sizeof(*tracer), GFP_ATOMIC); | 719 | tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); |
699 | if (!tracer) | 720 | if (!tracer) |
700 | goto out_put_tracer; | 721 | goto out_put_tracer; |
701 | tracer->ovfl = ovfl; | 722 | tracer->ovfl = ovfl; |
702 | 723 | ||
724 | /* Do some more error checking and acquire a tracing context. */ | ||
703 | error = ds_request(&tracer->ds, &tracer->trace.ds, | 725 | error = ds_request(&tracer->ds, &tracer->trace.ds, |
704 | ds_bts, task, base, size, th, flags); | 726 | ds_bts, task, cpu, base, size, th); |
705 | if (error < 0) | 727 | if (error < 0) |
706 | goto out_tracer; | 728 | goto out_tracer; |
707 | 729 | ||
708 | 730 | /* Claim the bts part of the tracing context we acquired above. */ | |
709 | spin_lock_irqsave(&ds_lock, irq); | 731 | spin_lock_irq(&ds_lock); |
710 | 732 | ||
711 | error = -EPERM; | 733 | error = -EPERM; |
712 | if (tracer->ds.context->bts_master) | 734 | if (tracer->ds.context->bts_master) |
713 | goto out_unlock; | 735 | goto out_unlock; |
714 | tracer->ds.context->bts_master = tracer; | 736 | tracer->ds.context->bts_master = tracer; |
715 | 737 | ||
716 | spin_unlock_irqrestore(&ds_lock, irq); | 738 | spin_unlock_irq(&ds_lock); |
717 | 739 | ||
740 | /* | ||
741 | * Now that we own the bts part of the context, let's complete the | ||
742 | * initialization for that part. | ||
743 | */ | ||
744 | ds_init_ds_trace(&tracer->trace.ds, ds_bts, base, size, th, flags); | ||
745 | ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_bts); | ||
746 | ds_install_ds_area(tracer->ds.context); | ||
718 | 747 | ||
719 | tracer->trace.read = bts_read; | 748 | tracer->trace.read = bts_read; |
720 | tracer->trace.write = bts_write; | 749 | tracer->trace.write = bts_write; |
721 | 750 | ||
722 | ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_bts); | 751 | /* Start tracing. */ |
723 | ds_resume_bts(tracer); | 752 | ds_resume_bts(tracer); |
724 | 753 | ||
725 | return tracer; | 754 | return tracer; |
726 | 755 | ||
727 | out_unlock: | 756 | out_unlock: |
728 | spin_unlock_irqrestore(&ds_lock, irq); | 757 | spin_unlock_irq(&ds_lock); |
729 | ds_put_context(tracer->ds.context); | 758 | ds_put_context(tracer->ds.context); |
730 | out_tracer: | 759 | out_tracer: |
731 | kfree(tracer); | 760 | kfree(tracer); |
@@ -735,13 +764,27 @@ struct bts_tracer *ds_request_bts(struct task_struct *task, | |||
735 | return ERR_PTR(error); | 764 | return ERR_PTR(error); |
736 | } | 765 | } |
737 | 766 | ||
738 | struct pebs_tracer *ds_request_pebs(struct task_struct *task, | 767 | struct bts_tracer *ds_request_bts_task(struct task_struct *task, |
739 | void *base, size_t size, | 768 | void *base, size_t size, |
740 | pebs_ovfl_callback_t ovfl, size_t th, | 769 | bts_ovfl_callback_t ovfl, |
741 | unsigned int flags) | 770 | size_t th, unsigned int flags) |
771 | { | ||
772 | return ds_request_bts(task, 0, base, size, ovfl, th, flags); | ||
773 | } | ||
774 | |||
775 | struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size, | ||
776 | bts_ovfl_callback_t ovfl, | ||
777 | size_t th, unsigned int flags) | ||
778 | { | ||
779 | return ds_request_bts(NULL, cpu, base, size, ovfl, th, flags); | ||
780 | } | ||
781 | |||
782 | static struct pebs_tracer *ds_request_pebs(struct task_struct *task, int cpu, | ||
783 | void *base, size_t size, | ||
784 | pebs_ovfl_callback_t ovfl, size_t th, | ||
785 | unsigned int flags) | ||
742 | { | 786 | { |
743 | struct pebs_tracer *tracer; | 787 | struct pebs_tracer *tracer; |
744 | unsigned long irq; | ||
745 | int error; | 788 | int error; |
746 | 789 | ||
747 | /* Buffer overflow notification is not yet implemented. */ | 790 | /* Buffer overflow notification is not yet implemented. */ |
@@ -753,37 +796,43 @@ struct pebs_tracer *ds_request_pebs(struct task_struct *task, | |||
753 | if (error < 0) | 796 | if (error < 0) |
754 | goto out; | 797 | goto out; |
755 | 798 | ||
756 | /* | ||
757 | * Per-cpu tracing is typically requested using smp_call_function(). | ||
758 | * We must not sleep. | ||
759 | */ | ||
760 | error = -ENOMEM; | 799 | error = -ENOMEM; |
761 | tracer = kzalloc(sizeof(*tracer), GFP_ATOMIC); | 800 | tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); |
762 | if (!tracer) | 801 | if (!tracer) |
763 | goto out_put_tracer; | 802 | goto out_put_tracer; |
764 | tracer->ovfl = ovfl; | 803 | tracer->ovfl = ovfl; |
765 | 804 | ||
805 | /* Do some more error checking and acquire a tracing context. */ | ||
766 | error = ds_request(&tracer->ds, &tracer->trace.ds, | 806 | error = ds_request(&tracer->ds, &tracer->trace.ds, |
767 | ds_pebs, task, base, size, th, flags); | 807 | ds_pebs, task, cpu, base, size, th); |
768 | if (error < 0) | 808 | if (error < 0) |
769 | goto out_tracer; | 809 | goto out_tracer; |
770 | 810 | ||
771 | spin_lock_irqsave(&ds_lock, irq); | 811 | /* Claim the pebs part of the tracing context we acquired above. */ |
812 | spin_lock_irq(&ds_lock); | ||
772 | 813 | ||
773 | error = -EPERM; | 814 | error = -EPERM; |
774 | if (tracer->ds.context->pebs_master) | 815 | if (tracer->ds.context->pebs_master) |
775 | goto out_unlock; | 816 | goto out_unlock; |
776 | tracer->ds.context->pebs_master = tracer; | 817 | tracer->ds.context->pebs_master = tracer; |
777 | 818 | ||
778 | spin_unlock_irqrestore(&ds_lock, irq); | 819 | spin_unlock_irq(&ds_lock); |
779 | 820 | ||
821 | /* | ||
822 | * Now that we own the pebs part of the context, let's complete the | ||
823 | * initialization for that part. | ||
824 | */ | ||
825 | ds_init_ds_trace(&tracer->trace.ds, ds_pebs, base, size, th, flags); | ||
780 | ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); | 826 | ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); |
827 | ds_install_ds_area(tracer->ds.context); | ||
828 | |||
829 | /* Start tracing. */ | ||
781 | ds_resume_pebs(tracer); | 830 | ds_resume_pebs(tracer); |
782 | 831 | ||
783 | return tracer; | 832 | return tracer; |
784 | 833 | ||
785 | out_unlock: | 834 | out_unlock: |
786 | spin_unlock_irqrestore(&ds_lock, irq); | 835 | spin_unlock_irq(&ds_lock); |
787 | ds_put_context(tracer->ds.context); | 836 | ds_put_context(tracer->ds.context); |
788 | out_tracer: | 837 | out_tracer: |
789 | kfree(tracer); | 838 | kfree(tracer); |
@@ -793,16 +842,26 @@ struct pebs_tracer *ds_request_pebs(struct task_struct *task, | |||
793 | return ERR_PTR(error); | 842 | return ERR_PTR(error); |
794 | } | 843 | } |
795 | 844 | ||
796 | void ds_release_bts(struct bts_tracer *tracer) | 845 | struct pebs_tracer *ds_request_pebs_task(struct task_struct *task, |
846 | void *base, size_t size, | ||
847 | pebs_ovfl_callback_t ovfl, | ||
848 | size_t th, unsigned int flags) | ||
797 | { | 849 | { |
798 | struct task_struct *task; | 850 | return ds_request_pebs(task, 0, base, size, ovfl, th, flags); |
851 | } | ||
799 | 852 | ||
800 | if (!tracer) | 853 | struct pebs_tracer *ds_request_pebs_cpu(int cpu, void *base, size_t size, |
801 | return; | 854 | pebs_ovfl_callback_t ovfl, |
855 | size_t th, unsigned int flags) | ||
856 | { | ||
857 | return ds_request_pebs(NULL, cpu, base, size, ovfl, th, flags); | ||
858 | } | ||
802 | 859 | ||
803 | task = tracer->ds.context->task; | 860 | static void ds_free_bts(struct bts_tracer *tracer) |
861 | { | ||
862 | struct task_struct *task; | ||
804 | 863 | ||
805 | ds_suspend_bts(tracer); | 864 | task = tracer->ds.context->task; |
806 | 865 | ||
807 | WARN_ON_ONCE(tracer->ds.context->bts_master != tracer); | 866 | WARN_ON_ONCE(tracer->ds.context->bts_master != tracer); |
808 | tracer->ds.context->bts_master = NULL; | 867 | tracer->ds.context->bts_master = NULL; |
@@ -817,9 +876,69 @@ void ds_release_bts(struct bts_tracer *tracer) | |||
817 | kfree(tracer); | 876 | kfree(tracer); |
818 | } | 877 | } |
819 | 878 | ||
879 | void ds_release_bts(struct bts_tracer *tracer) | ||
880 | { | ||
881 | might_sleep(); | ||
882 | |||
883 | if (!tracer) | ||
884 | return; | ||
885 | |||
886 | ds_suspend_bts(tracer); | ||
887 | ds_free_bts(tracer); | ||
888 | } | ||
889 | |||
890 | int ds_release_bts_noirq(struct bts_tracer *tracer) | ||
891 | { | ||
892 | struct task_struct *task; | ||
893 | unsigned long irq; | ||
894 | int error; | ||
895 | |||
896 | if (!tracer) | ||
897 | return 0; | ||
898 | |||
899 | task = tracer->ds.context->task; | ||
900 | |||
901 | local_irq_save(irq); | ||
902 | |||
903 | error = -EPERM; | ||
904 | if (!task && | ||
905 | (tracer->ds.context->cpu != smp_processor_id())) | ||
906 | goto out; | ||
907 | |||
908 | error = -EPERM; | ||
909 | if (task && (task != current)) | ||
910 | goto out; | ||
911 | |||
912 | ds_suspend_bts_noirq(tracer); | ||
913 | ds_free_bts(tracer); | ||
914 | |||
915 | error = 0; | ||
916 | out: | ||
917 | local_irq_restore(irq); | ||
918 | return error; | ||
919 | } | ||
920 | |||
921 | static void update_task_debugctlmsr(struct task_struct *task, | ||
922 | unsigned long debugctlmsr) | ||
923 | { | ||
924 | task->thread.debugctlmsr = debugctlmsr; | ||
925 | |||
926 | get_cpu(); | ||
927 | if (task == current) | ||
928 | update_debugctlmsr(debugctlmsr); | ||
929 | |||
930 | if (task->thread.debugctlmsr) | ||
931 | set_tsk_thread_flag(task, TIF_DEBUGCTLMSR); | ||
932 | else | ||
933 | clear_tsk_thread_flag(task, TIF_DEBUGCTLMSR); | ||
934 | put_cpu(); | ||
935 | } | ||
936 | |||
820 | void ds_suspend_bts(struct bts_tracer *tracer) | 937 | void ds_suspend_bts(struct bts_tracer *tracer) |
821 | { | 938 | { |
822 | struct task_struct *task; | 939 | struct task_struct *task; |
940 | unsigned long debugctlmsr; | ||
941 | int cpu; | ||
823 | 942 | ||
824 | if (!tracer) | 943 | if (!tracer) |
825 | return; | 944 | return; |
@@ -827,29 +946,60 @@ void ds_suspend_bts(struct bts_tracer *tracer) | |||
827 | tracer->flags = 0; | 946 | tracer->flags = 0; |
828 | 947 | ||
829 | task = tracer->ds.context->task; | 948 | task = tracer->ds.context->task; |
949 | cpu = tracer->ds.context->cpu; | ||
830 | 950 | ||
831 | if (!task || (task == current)) | 951 | WARN_ON(!task && irqs_disabled()); |
832 | update_debugctlmsr(get_debugctlmsr() & ~BTS_CONTROL); | ||
833 | 952 | ||
834 | if (task) { | 953 | debugctlmsr = (task ? |
835 | task->thread.debugctlmsr &= ~BTS_CONTROL; | 954 | task->thread.debugctlmsr : |
955 | get_debugctlmsr_on_cpu(cpu)); | ||
956 | debugctlmsr &= ~BTS_CONTROL; | ||
836 | 957 | ||
837 | if (!task->thread.debugctlmsr) | 958 | if (task) |
838 | clear_tsk_thread_flag(task, TIF_DEBUGCTLMSR); | 959 | update_task_debugctlmsr(task, debugctlmsr); |
839 | } | 960 | else |
961 | update_debugctlmsr_on_cpu(cpu, debugctlmsr); | ||
840 | } | 962 | } |
841 | 963 | ||
842 | void ds_resume_bts(struct bts_tracer *tracer) | 964 | int ds_suspend_bts_noirq(struct bts_tracer *tracer) |
843 | { | 965 | { |
844 | struct task_struct *task; | 966 | struct task_struct *task; |
845 | unsigned long control; | 967 | unsigned long debugctlmsr, irq; |
968 | int cpu, error = 0; | ||
846 | 969 | ||
847 | if (!tracer) | 970 | if (!tracer) |
848 | return; | 971 | return 0; |
849 | 972 | ||
850 | tracer->flags = tracer->trace.ds.flags; | 973 | tracer->flags = 0; |
851 | 974 | ||
852 | task = tracer->ds.context->task; | 975 | task = tracer->ds.context->task; |
976 | cpu = tracer->ds.context->cpu; | ||
977 | |||
978 | local_irq_save(irq); | ||
979 | |||
980 | error = -EPERM; | ||
981 | if (!task && (cpu != smp_processor_id())) | ||
982 | goto out; | ||
983 | |||
984 | debugctlmsr = (task ? | ||
985 | task->thread.debugctlmsr : | ||
986 | get_debugctlmsr()); | ||
987 | debugctlmsr &= ~BTS_CONTROL; | ||
988 | |||
989 | if (task) | ||
990 | update_task_debugctlmsr(task, debugctlmsr); | ||
991 | else | ||
992 | update_debugctlmsr(debugctlmsr); | ||
993 | |||
994 | error = 0; | ||
995 | out: | ||
996 | local_irq_restore(irq); | ||
997 | return error; | ||
998 | } | ||
999 | |||
1000 | static unsigned long ds_bts_control(struct bts_tracer *tracer) | ||
1001 | { | ||
1002 | unsigned long control; | ||
853 | 1003 | ||
854 | control = ds_cfg.ctl[dsf_bts]; | 1004 | control = ds_cfg.ctl[dsf_bts]; |
855 | if (!(tracer->trace.ds.flags & BTS_KERNEL)) | 1005 | if (!(tracer->trace.ds.flags & BTS_KERNEL)) |
@@ -857,25 +1007,77 @@ void ds_resume_bts(struct bts_tracer *tracer) | |||
857 | if (!(tracer->trace.ds.flags & BTS_USER)) | 1007 | if (!(tracer->trace.ds.flags & BTS_USER)) |
858 | control |= ds_cfg.ctl[dsf_bts_user]; | 1008 | control |= ds_cfg.ctl[dsf_bts_user]; |
859 | 1009 | ||
860 | if (task) { | 1010 | return control; |
861 | task->thread.debugctlmsr |= control; | ||
862 | set_tsk_thread_flag(task, TIF_DEBUGCTLMSR); | ||
863 | } | ||
864 | |||
865 | if (!task || (task == current)) | ||
866 | update_debugctlmsr(get_debugctlmsr() | control); | ||
867 | } | 1011 | } |
868 | 1012 | ||
869 | void ds_release_pebs(struct pebs_tracer *tracer) | 1013 | void ds_resume_bts(struct bts_tracer *tracer) |
870 | { | 1014 | { |
871 | struct task_struct *task; | 1015 | struct task_struct *task; |
1016 | unsigned long debugctlmsr; | ||
1017 | int cpu; | ||
872 | 1018 | ||
873 | if (!tracer) | 1019 | if (!tracer) |
874 | return; | 1020 | return; |
875 | 1021 | ||
1022 | tracer->flags = tracer->trace.ds.flags; | ||
1023 | |||
876 | task = tracer->ds.context->task; | 1024 | task = tracer->ds.context->task; |
1025 | cpu = tracer->ds.context->cpu; | ||
877 | 1026 | ||
878 | ds_suspend_pebs(tracer); | 1027 | WARN_ON(!task && irqs_disabled()); |
1028 | |||
1029 | debugctlmsr = (task ? | ||
1030 | task->thread.debugctlmsr : | ||
1031 | get_debugctlmsr_on_cpu(cpu)); | ||
1032 | debugctlmsr |= ds_bts_control(tracer); | ||
1033 | |||
1034 | if (task) | ||
1035 | update_task_debugctlmsr(task, debugctlmsr); | ||
1036 | else | ||
1037 | update_debugctlmsr_on_cpu(cpu, debugctlmsr); | ||
1038 | } | ||
1039 | |||
1040 | int ds_resume_bts_noirq(struct bts_tracer *tracer) | ||
1041 | { | ||
1042 | struct task_struct *task; | ||
1043 | unsigned long debugctlmsr, irq; | ||
1044 | int cpu, error = 0; | ||
1045 | |||
1046 | if (!tracer) | ||
1047 | return 0; | ||
1048 | |||
1049 | tracer->flags = tracer->trace.ds.flags; | ||
1050 | |||
1051 | task = tracer->ds.context->task; | ||
1052 | cpu = tracer->ds.context->cpu; | ||
1053 | |||
1054 | local_irq_save(irq); | ||
1055 | |||
1056 | error = -EPERM; | ||
1057 | if (!task && (cpu != smp_processor_id())) | ||
1058 | goto out; | ||
1059 | |||
1060 | debugctlmsr = (task ? | ||
1061 | task->thread.debugctlmsr : | ||
1062 | get_debugctlmsr()); | ||
1063 | debugctlmsr |= ds_bts_control(tracer); | ||
1064 | |||
1065 | if (task) | ||
1066 | update_task_debugctlmsr(task, debugctlmsr); | ||
1067 | else | ||
1068 | update_debugctlmsr(debugctlmsr); | ||
1069 | |||
1070 | error = 0; | ||
1071 | out: | ||
1072 | local_irq_restore(irq); | ||
1073 | return error; | ||
1074 | } | ||
1075 | |||
1076 | static void ds_free_pebs(struct pebs_tracer *tracer) | ||
1077 | { | ||
1078 | struct task_struct *task; | ||
1079 | |||
1080 | task = tracer->ds.context->task; | ||
879 | 1081 | ||
880 | WARN_ON_ONCE(tracer->ds.context->pebs_master != tracer); | 1082 | WARN_ON_ONCE(tracer->ds.context->pebs_master != tracer); |
881 | tracer->ds.context->pebs_master = NULL; | 1083 | tracer->ds.context->pebs_master = NULL; |
@@ -886,16 +1088,68 @@ void ds_release_pebs(struct pebs_tracer *tracer) | |||
886 | kfree(tracer); | 1088 | kfree(tracer); |
887 | } | 1089 | } |
888 | 1090 | ||
1091 | void ds_release_pebs(struct pebs_tracer *tracer) | ||
1092 | { | ||
1093 | might_sleep(); | ||
1094 | |||
1095 | if (!tracer) | ||
1096 | return; | ||
1097 | |||
1098 | ds_suspend_pebs(tracer); | ||
1099 | ds_free_pebs(tracer); | ||
1100 | } | ||
1101 | |||
1102 | int ds_release_pebs_noirq(struct pebs_tracer *tracer) | ||
1103 | { | ||
1104 | struct task_struct *task; | ||
1105 | unsigned long irq; | ||
1106 | int error; | ||
1107 | |||
1108 | if (!tracer) | ||
1109 | return 0; | ||
1110 | |||
1111 | task = tracer->ds.context->task; | ||
1112 | |||
1113 | local_irq_save(irq); | ||
1114 | |||
1115 | error = -EPERM; | ||
1116 | if (!task && | ||
1117 | (tracer->ds.context->cpu != smp_processor_id())) | ||
1118 | goto out; | ||
1119 | |||
1120 | error = -EPERM; | ||
1121 | if (task && (task != current)) | ||
1122 | goto out; | ||
1123 | |||
1124 | ds_suspend_pebs_noirq(tracer); | ||
1125 | ds_free_pebs(tracer); | ||
1126 | |||
1127 | error = 0; | ||
1128 | out: | ||
1129 | local_irq_restore(irq); | ||
1130 | return error; | ||
1131 | } | ||
1132 | |||
889 | void ds_suspend_pebs(struct pebs_tracer *tracer) | 1133 | void ds_suspend_pebs(struct pebs_tracer *tracer) |
890 | { | 1134 | { |
891 | 1135 | ||
892 | } | 1136 | } |
893 | 1137 | ||
1138 | int ds_suspend_pebs_noirq(struct pebs_tracer *tracer) | ||
1139 | { | ||
1140 | return 0; | ||
1141 | } | ||
1142 | |||
894 | void ds_resume_pebs(struct pebs_tracer *tracer) | 1143 | void ds_resume_pebs(struct pebs_tracer *tracer) |
895 | { | 1144 | { |
896 | 1145 | ||
897 | } | 1146 | } |
898 | 1147 | ||
1148 | int ds_resume_pebs_noirq(struct pebs_tracer *tracer) | ||
1149 | { | ||
1150 | return 0; | ||
1151 | } | ||
1152 | |||
899 | const struct bts_trace *ds_read_bts(struct bts_tracer *tracer) | 1153 | const struct bts_trace *ds_read_bts(struct bts_tracer *tracer) |
900 | { | 1154 | { |
901 | if (!tracer) | 1155 | if (!tracer) |
@@ -1004,26 +1258,6 @@ ds_configure(const struct ds_configuration *cfg, | |||
1004 | printk(KERN_INFO "[ds] pebs not available\n"); | 1258 | printk(KERN_INFO "[ds] pebs not available\n"); |
1005 | } | 1259 | } |
1006 | 1260 | ||
1007 | if (ds_cfg.sizeof_rec[ds_bts]) { | ||
1008 | int error; | ||
1009 | |||
1010 | error = ds_selftest_bts(); | ||
1011 | if (error) { | ||
1012 | WARN(1, "[ds] selftest failed. disabling bts.\n"); | ||
1013 | ds_cfg.sizeof_rec[ds_bts] = 0; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | if (ds_cfg.sizeof_rec[ds_pebs]) { | ||
1018 | int error; | ||
1019 | |||
1020 | error = ds_selftest_pebs(); | ||
1021 | if (error) { | ||
1022 | WARN(1, "[ds] selftest failed. disabling pebs.\n"); | ||
1023 | ds_cfg.sizeof_rec[ds_pebs] = 0; | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | printk(KERN_INFO "[ds] sizes: address: %u bit, ", | 1261 | printk(KERN_INFO "[ds] sizes: address: %u bit, ", |
1028 | 8 * ds_cfg.sizeof_ptr_field); | 1262 | 8 * ds_cfg.sizeof_ptr_field); |
1029 | printk("bts/pebs record: %u/%u bytes\n", | 1263 | printk("bts/pebs record: %u/%u bytes\n", |
@@ -1127,3 +1361,29 @@ void ds_copy_thread(struct task_struct *tsk, struct task_struct *father) | |||
1127 | void ds_exit_thread(struct task_struct *tsk) | 1361 | void ds_exit_thread(struct task_struct *tsk) |
1128 | { | 1362 | { |
1129 | } | 1363 | } |
1364 | |||
1365 | static __init int ds_selftest(void) | ||
1366 | { | ||
1367 | if (ds_cfg.sizeof_rec[ds_bts]) { | ||
1368 | int error; | ||
1369 | |||
1370 | error = ds_selftest_bts(); | ||
1371 | if (error) { | ||
1372 | WARN(1, "[ds] selftest failed. disabling bts.\n"); | ||
1373 | ds_cfg.sizeof_rec[ds_bts] = 0; | ||
1374 | } | ||
1375 | } | ||
1376 | |||
1377 | if (ds_cfg.sizeof_rec[ds_pebs]) { | ||
1378 | int error; | ||
1379 | |||
1380 | error = ds_selftest_pebs(); | ||
1381 | if (error) { | ||
1382 | WARN(1, "[ds] selftest failed. disabling pebs.\n"); | ||
1383 | ds_cfg.sizeof_rec[ds_pebs] = 0; | ||
1384 | } | ||
1385 | } | ||
1386 | |||
1387 | return 0; | ||
1388 | } | ||
1389 | device_initcall(ds_selftest); | ||
diff --git a/arch/x86/kernel/ds_selftest.c b/arch/x86/kernel/ds_selftest.c index 8c46fbf38c46..e5a263c8a14c 100644 --- a/arch/x86/kernel/ds_selftest.c +++ b/arch/x86/kernel/ds_selftest.c | |||
@@ -10,11 +10,12 @@ | |||
10 | 10 | ||
11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
12 | #include <linux/string.h> | 12 | #include <linux/string.h> |
13 | #include <linux/smp.h> | ||
13 | 14 | ||
14 | #include <asm/ds.h> | 15 | #include <asm/ds.h> |
15 | 16 | ||
16 | 17 | ||
17 | #define DS_SELFTEST_BUFFER_SIZE 1021 /* Intentionally chose an odd size. */ | 18 | #define BUFFER_SIZE 1021 /* Intentionally chose an odd size. */ |
18 | 19 | ||
19 | 20 | ||
20 | static int ds_selftest_bts_consistency(const struct bts_trace *trace) | 21 | static int ds_selftest_bts_consistency(const struct bts_trace *trace) |
@@ -125,12 +126,12 @@ int ds_selftest_bts(void) | |||
125 | struct bts_tracer *tracer; | 126 | struct bts_tracer *tracer; |
126 | int error = 0; | 127 | int error = 0; |
127 | void *top; | 128 | void *top; |
128 | unsigned char buffer[DS_SELFTEST_BUFFER_SIZE]; | 129 | unsigned char buffer[BUFFER_SIZE]; |
129 | 130 | ||
130 | printk(KERN_INFO "[ds] bts selftest..."); | 131 | printk(KERN_INFO "[ds] bts selftest..."); |
131 | 132 | ||
132 | tracer = ds_request_bts(NULL, buffer, DS_SELFTEST_BUFFER_SIZE, | 133 | tracer = ds_request_bts_cpu(smp_processor_id(), buffer, BUFFER_SIZE, |
133 | NULL, (size_t)-1, BTS_KERNEL); | 134 | NULL, (size_t)-1, BTS_KERNEL); |
134 | if (IS_ERR(tracer)) { | 135 | if (IS_ERR(tracer)) { |
135 | error = PTR_ERR(tracer); | 136 | error = PTR_ERR(tracer); |
136 | tracer = NULL; | 137 | tracer = NULL; |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 7c21d1e8cae7..adbb24322d8f 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -800,8 +800,9 @@ static int ptrace_bts_config(struct task_struct *child, | |||
800 | if (cfg.flags & PTRACE_BTS_O_SCHED) | 800 | if (cfg.flags & PTRACE_BTS_O_SCHED) |
801 | flags |= BTS_TIMESTAMPS; | 801 | flags |= BTS_TIMESTAMPS; |
802 | 802 | ||
803 | context->tracer = ds_request_bts(child, context->buffer, context->size, | 803 | context->tracer = |
804 | NULL, (size_t)-1, flags); | 804 | ds_request_bts_task(child, context->buffer, context->size, |
805 | NULL, (size_t)-1, flags); | ||
805 | if (unlikely(IS_ERR(context->tracer))) { | 806 | if (unlikely(IS_ERR(context->tracer))) { |
806 | int error = PTR_ERR(context->tracer); | 807 | int error = PTR_ERR(context->tracer); |
807 | 808 | ||