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/x86/kernel/ds.c | |
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/x86/kernel/ds.c')
-rw-r--r-- | arch/x86/kernel/ds.c | 474 |
1 files changed, 367 insertions, 107 deletions
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); | ||