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 | |
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>
-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 | ||||
-rw-r--r-- | kernel/trace/trace_hw_branches.c | 193 |
5 files changed, 492 insertions, 246 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 | ||
diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c index 8b2109a6c61c..50565d8cd2ed 100644 --- a/kernel/trace/trace_hw_branches.c +++ b/kernel/trace/trace_hw_branches.c | |||
@@ -4,7 +4,6 @@ | |||
4 | * Copyright (C) 2008-2009 Intel Corporation. | 4 | * Copyright (C) 2008-2009 Intel Corporation. |
5 | * Markus Metzger <markus.t.metzger@gmail.com>, 2008-2009 | 5 | * Markus Metzger <markus.t.metzger@gmail.com>, 2008-2009 |
6 | */ | 6 | */ |
7 | #include <linux/spinlock.h> | ||
8 | #include <linux/kallsyms.h> | 7 | #include <linux/kallsyms.h> |
9 | #include <linux/debugfs.h> | 8 | #include <linux/debugfs.h> |
10 | #include <linux/ftrace.h> | 9 | #include <linux/ftrace.h> |
@@ -21,168 +20,113 @@ | |||
21 | 20 | ||
22 | #define BTS_BUFFER_SIZE (1 << 13) | 21 | #define BTS_BUFFER_SIZE (1 << 13) |
23 | 22 | ||
24 | /* | ||
25 | * The tracer lock protects the below per-cpu tracer array. | ||
26 | * It needs to be held to: | ||
27 | * - start tracing on all cpus | ||
28 | * - stop tracing on all cpus | ||
29 | * - start tracing on a single hotplug cpu | ||
30 | * - stop tracing on a single hotplug cpu | ||
31 | * - read the trace from all cpus | ||
32 | * - read the trace from a single cpu | ||
33 | */ | ||
34 | static DEFINE_SPINLOCK(bts_tracer_lock); | ||
35 | static DEFINE_PER_CPU(struct bts_tracer *, tracer); | 23 | static DEFINE_PER_CPU(struct bts_tracer *, tracer); |
36 | static DEFINE_PER_CPU(unsigned char[BTS_BUFFER_SIZE], buffer); | 24 | static DEFINE_PER_CPU(unsigned char[BTS_BUFFER_SIZE], buffer); |
37 | 25 | ||
38 | #define this_tracer per_cpu(tracer, smp_processor_id()) | 26 | #define this_tracer per_cpu(tracer, smp_processor_id()) |
39 | #define this_buffer per_cpu(buffer, smp_processor_id()) | ||
40 | 27 | ||
41 | static int trace_hw_branches_enabled __read_mostly; | 28 | static int trace_hw_branches_enabled __read_mostly; |
42 | static int trace_hw_branches_suspended __read_mostly; | 29 | static int trace_hw_branches_suspended __read_mostly; |
43 | static struct trace_array *hw_branch_trace __read_mostly; | 30 | static struct trace_array *hw_branch_trace __read_mostly; |
44 | 31 | ||
45 | 32 | ||
46 | /* | 33 | static void bts_trace_init_cpu(int cpu) |
47 | * Initialize the tracer for the current cpu. | ||
48 | * The argument is ignored. | ||
49 | * | ||
50 | * pre: bts_tracer_lock must be locked. | ||
51 | */ | ||
52 | static void bts_trace_init_cpu(void *arg) | ||
53 | { | 34 | { |
54 | if (this_tracer) | 35 | per_cpu(tracer, cpu) = |
55 | ds_release_bts(this_tracer); | 36 | ds_request_bts_cpu(cpu, per_cpu(buffer, cpu), BTS_BUFFER_SIZE, |
37 | NULL, (size_t)-1, BTS_KERNEL); | ||
56 | 38 | ||
57 | this_tracer = ds_request_bts(NULL, this_buffer, BTS_BUFFER_SIZE, | 39 | if (IS_ERR(per_cpu(tracer, cpu))) |
58 | NULL, (size_t)-1, BTS_KERNEL); | 40 | per_cpu(tracer, cpu) = NULL; |
59 | if (IS_ERR(this_tracer)) { | ||
60 | this_tracer = NULL; | ||
61 | return; | ||
62 | } | ||
63 | } | 41 | } |
64 | 42 | ||
65 | static int bts_trace_init(struct trace_array *tr) | 43 | static int bts_trace_init(struct trace_array *tr) |
66 | { | 44 | { |
67 | int cpu, avail; | 45 | int cpu; |
68 | |||
69 | spin_lock(&bts_tracer_lock); | ||
70 | 46 | ||
71 | hw_branch_trace = tr; | 47 | hw_branch_trace = tr; |
48 | trace_hw_branches_enabled = 0; | ||
72 | 49 | ||
73 | on_each_cpu(bts_trace_init_cpu, NULL, 1); | 50 | get_online_cpus(); |
74 | 51 | for_each_online_cpu(cpu) { | |
75 | /* Check on how many cpus we could enable tracing */ | 52 | bts_trace_init_cpu(cpu); |
76 | avail = 0; | ||
77 | for_each_online_cpu(cpu) | ||
78 | if (per_cpu(tracer, cpu)) | ||
79 | avail++; | ||
80 | 53 | ||
81 | trace_hw_branches_enabled = (avail ? 1 : 0); | 54 | if (likely(per_cpu(tracer, cpu))) |
55 | trace_hw_branches_enabled = 1; | ||
56 | } | ||
82 | trace_hw_branches_suspended = 0; | 57 | trace_hw_branches_suspended = 0; |
83 | 58 | put_online_cpus(); | |
84 | spin_unlock(&bts_tracer_lock); | ||
85 | |||
86 | 59 | ||
87 | /* If we could not enable tracing on a single cpu, we fail. */ | 60 | /* If we could not enable tracing on a single cpu, we fail. */ |
88 | return avail ? 0 : -EOPNOTSUPP; | 61 | return trace_hw_branches_enabled ? 0 : -EOPNOTSUPP; |
89 | } | ||
90 | |||
91 | /* | ||
92 | * Release the tracer for the current cpu. | ||
93 | * The argument is ignored. | ||
94 | * | ||
95 | * pre: bts_tracer_lock must be locked. | ||
96 | */ | ||
97 | static void bts_trace_release_cpu(void *arg) | ||
98 | { | ||
99 | if (this_tracer) { | ||
100 | ds_release_bts(this_tracer); | ||
101 | this_tracer = NULL; | ||
102 | } | ||
103 | } | 62 | } |
104 | 63 | ||
105 | static void bts_trace_reset(struct trace_array *tr) | 64 | static void bts_trace_reset(struct trace_array *tr) |
106 | { | 65 | { |
107 | spin_lock(&bts_tracer_lock); | 66 | int cpu; |
108 | 67 | ||
109 | on_each_cpu(bts_trace_release_cpu, NULL, 1); | 68 | get_online_cpus(); |
69 | for_each_online_cpu(cpu) { | ||
70 | if (likely(per_cpu(tracer, cpu))) { | ||
71 | ds_release_bts(per_cpu(tracer, cpu)); | ||
72 | per_cpu(tracer, cpu) = NULL; | ||
73 | } | ||
74 | } | ||
110 | trace_hw_branches_enabled = 0; | 75 | trace_hw_branches_enabled = 0; |
111 | trace_hw_branches_suspended = 0; | 76 | trace_hw_branches_suspended = 0; |
112 | 77 | put_online_cpus(); | |
113 | spin_unlock(&bts_tracer_lock); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Resume tracing on the current cpu. | ||
118 | * The argument is ignored. | ||
119 | * | ||
120 | * pre: bts_tracer_lock must be locked. | ||
121 | */ | ||
122 | static void bts_trace_resume_cpu(void *arg) | ||
123 | { | ||
124 | if (this_tracer) | ||
125 | ds_resume_bts(this_tracer); | ||
126 | } | 78 | } |
127 | 79 | ||
128 | static void bts_trace_start(struct trace_array *tr) | 80 | static void bts_trace_start(struct trace_array *tr) |
129 | { | 81 | { |
130 | spin_lock(&bts_tracer_lock); | 82 | int cpu; |
131 | 83 | ||
132 | on_each_cpu(bts_trace_resume_cpu, NULL, 1); | 84 | get_online_cpus(); |
85 | for_each_online_cpu(cpu) | ||
86 | if (likely(per_cpu(tracer, cpu))) | ||
87 | ds_resume_bts(per_cpu(tracer, cpu)); | ||
133 | trace_hw_branches_suspended = 0; | 88 | trace_hw_branches_suspended = 0; |
134 | 89 | put_online_cpus(); | |
135 | spin_unlock(&bts_tracer_lock); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Suspend tracing on the current cpu. | ||
140 | * The argument is ignored. | ||
141 | * | ||
142 | * pre: bts_tracer_lock must be locked. | ||
143 | */ | ||
144 | static void bts_trace_suspend_cpu(void *arg) | ||
145 | { | ||
146 | if (this_tracer) | ||
147 | ds_suspend_bts(this_tracer); | ||
148 | } | 90 | } |
149 | 91 | ||
150 | static void bts_trace_stop(struct trace_array *tr) | 92 | static void bts_trace_stop(struct trace_array *tr) |
151 | { | 93 | { |
152 | spin_lock(&bts_tracer_lock); | 94 | int cpu; |
153 | 95 | ||
154 | on_each_cpu(bts_trace_suspend_cpu, NULL, 1); | 96 | get_online_cpus(); |
97 | for_each_online_cpu(cpu) | ||
98 | if (likely(per_cpu(tracer, cpu))) | ||
99 | ds_suspend_bts(per_cpu(tracer, cpu)); | ||
155 | trace_hw_branches_suspended = 1; | 100 | trace_hw_branches_suspended = 1; |
156 | 101 | put_online_cpus(); | |
157 | spin_unlock(&bts_tracer_lock); | ||
158 | } | 102 | } |
159 | 103 | ||
160 | static int __cpuinit bts_hotcpu_handler(struct notifier_block *nfb, | 104 | static int __cpuinit bts_hotcpu_handler(struct notifier_block *nfb, |
161 | unsigned long action, void *hcpu) | 105 | unsigned long action, void *hcpu) |
162 | { | 106 | { |
163 | unsigned int cpu = (unsigned long)hcpu; | 107 | int cpu = (long)hcpu; |
164 | |||
165 | spin_lock(&bts_tracer_lock); | ||
166 | |||
167 | if (!trace_hw_branches_enabled) | ||
168 | goto out; | ||
169 | 108 | ||
170 | switch (action) { | 109 | switch (action) { |
171 | case CPU_ONLINE: | 110 | case CPU_ONLINE: |
172 | case CPU_DOWN_FAILED: | 111 | case CPU_DOWN_FAILED: |
173 | smp_call_function_single(cpu, bts_trace_init_cpu, NULL, 1); | 112 | /* The notification is sent with interrupts enabled. */ |
174 | 113 | if (trace_hw_branches_enabled) { | |
175 | if (trace_hw_branches_suspended) | 114 | bts_trace_init_cpu(cpu); |
176 | smp_call_function_single(cpu, bts_trace_suspend_cpu, | 115 | |
177 | NULL, 1); | 116 | if (trace_hw_branches_suspended && |
117 | likely(per_cpu(tracer, cpu))) | ||
118 | ds_suspend_bts(per_cpu(tracer, cpu)); | ||
119 | } | ||
178 | break; | 120 | break; |
121 | |||
179 | case CPU_DOWN_PREPARE: | 122 | case CPU_DOWN_PREPARE: |
180 | smp_call_function_single(cpu, bts_trace_release_cpu, NULL, 1); | 123 | /* The notification is sent with interrupts enabled. */ |
181 | break; | 124 | if (likely(per_cpu(tracer, cpu))) { |
125 | ds_release_bts(per_cpu(tracer, cpu)); | ||
126 | per_cpu(tracer, cpu) = NULL; | ||
127 | } | ||
182 | } | 128 | } |
183 | 129 | ||
184 | out: | ||
185 | spin_unlock(&bts_tracer_lock); | ||
186 | return NOTIFY_DONE; | 130 | return NOTIFY_DONE; |
187 | } | 131 | } |
188 | 132 | ||
@@ -274,7 +218,7 @@ static void trace_bts_at(const struct bts_trace *trace, void *at) | |||
274 | /* | 218 | /* |
275 | * Collect the trace on the current cpu and write it into the ftrace buffer. | 219 | * Collect the trace on the current cpu and write it into the ftrace buffer. |
276 | * | 220 | * |
277 | * pre: bts_tracer_lock must be locked | 221 | * pre: tracing must be suspended on the current cpu |
278 | */ | 222 | */ |
279 | static void trace_bts_cpu(void *arg) | 223 | static void trace_bts_cpu(void *arg) |
280 | { | 224 | { |
@@ -291,10 +235,9 @@ static void trace_bts_cpu(void *arg) | |||
291 | if (unlikely(!this_tracer)) | 235 | if (unlikely(!this_tracer)) |
292 | return; | 236 | return; |
293 | 237 | ||
294 | ds_suspend_bts(this_tracer); | ||
295 | trace = ds_read_bts(this_tracer); | 238 | trace = ds_read_bts(this_tracer); |
296 | if (!trace) | 239 | if (!trace) |
297 | goto out; | 240 | return; |
298 | 241 | ||
299 | for (at = trace->ds.top; (void *)at < trace->ds.end; | 242 | for (at = trace->ds.top; (void *)at < trace->ds.end; |
300 | at += trace->ds.size) | 243 | at += trace->ds.size) |
@@ -303,18 +246,27 @@ static void trace_bts_cpu(void *arg) | |||
303 | for (at = trace->ds.begin; (void *)at < trace->ds.top; | 246 | for (at = trace->ds.begin; (void *)at < trace->ds.top; |
304 | at += trace->ds.size) | 247 | at += trace->ds.size) |
305 | trace_bts_at(trace, at); | 248 | trace_bts_at(trace, at); |
306 | |||
307 | out: | ||
308 | ds_resume_bts(this_tracer); | ||
309 | } | 249 | } |
310 | 250 | ||
311 | static void trace_bts_prepare(struct trace_iterator *iter) | 251 | static void trace_bts_prepare(struct trace_iterator *iter) |
312 | { | 252 | { |
313 | spin_lock(&bts_tracer_lock); | 253 | int cpu; |
314 | 254 | ||
255 | get_online_cpus(); | ||
256 | for_each_online_cpu(cpu) | ||
257 | if (likely(per_cpu(tracer, cpu))) | ||
258 | ds_suspend_bts(per_cpu(tracer, cpu)); | ||
259 | /* | ||
260 | * We need to collect the trace on the respective cpu since ftrace | ||
261 | * implicitly adds the record for the current cpu. | ||
262 | * Once that is more flexible, we could collect the data from any cpu. | ||
263 | */ | ||
315 | on_each_cpu(trace_bts_cpu, iter->tr, 1); | 264 | on_each_cpu(trace_bts_cpu, iter->tr, 1); |
316 | 265 | ||
317 | spin_unlock(&bts_tracer_lock); | 266 | for_each_online_cpu(cpu) |
267 | if (likely(per_cpu(tracer, cpu))) | ||
268 | ds_resume_bts(per_cpu(tracer, cpu)); | ||
269 | put_online_cpus(); | ||
318 | } | 270 | } |
319 | 271 | ||
320 | static void trace_bts_close(struct trace_iterator *iter) | 272 | static void trace_bts_close(struct trace_iterator *iter) |
@@ -324,12 +276,11 @@ static void trace_bts_close(struct trace_iterator *iter) | |||
324 | 276 | ||
325 | void trace_hw_branch_oops(void) | 277 | void trace_hw_branch_oops(void) |
326 | { | 278 | { |
327 | spin_lock(&bts_tracer_lock); | 279 | if (this_tracer) { |
328 | 280 | ds_suspend_bts_noirq(this_tracer); | |
329 | if (trace_hw_branches_enabled) | ||
330 | trace_bts_cpu(hw_branch_trace); | 281 | trace_bts_cpu(hw_branch_trace); |
331 | 282 | ds_resume_bts_noirq(this_tracer); | |
332 | spin_unlock(&bts_tracer_lock); | 283 | } |
333 | } | 284 | } |
334 | 285 | ||
335 | struct tracer bts_tracer __read_mostly = | 286 | struct tracer bts_tracer __read_mostly = |