perf,x86: fix kernel crash with PEBS/BTS after suspend/resume
This patch fixes a kernel crash when using precise sampling (PEBS) after a suspend/resume. Turns out the CPU notifier code is not invoked on CPU0 (BP). Therefore, the DS_AREA (used by PEBS) is not restored properly by the kernel and keeps it power-on/resume value of 0 causing any PEBS measurement to crash when running on CPU0. The workaround is to add a hook in the actual resume code to restore the DS Area MSR value. It is invoked for all CPUS. So for all but CPU0, the DS_AREA will be restored twice but this is harmless. Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Stephane Eranian <eranian@google.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
					parent
					
						
							
								a2362d2476
							
						
					
				
			
			
				commit
				
					
						1d9d8639c0
					
				
			
		
					 3 changed files with 12 additions and 0 deletions
				
			
		|  | @ -729,3 +729,11 @@ void intel_ds_init(void) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void perf_restore_debug_store(void) | ||||||
|  | { | ||||||
|  | 	if (!x86_pmu.bts && !x86_pmu.pebs) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	init_debug_store_on_cpu(smp_processor_id()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| #include <linux/suspend.h> | #include <linux/suspend.h> | ||||||
| #include <linux/export.h> | #include <linux/export.h> | ||||||
| #include <linux/smp.h> | #include <linux/smp.h> | ||||||
|  | #include <linux/perf_event.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/pgtable.h> | #include <asm/pgtable.h> | ||||||
| #include <asm/proto.h> | #include <asm/proto.h> | ||||||
|  | @ -228,6 +229,7 @@ static void __restore_processor_state(struct saved_context *ctxt) | ||||||
| 	do_fpu_end(); | 	do_fpu_end(); | ||||||
| 	x86_platform.restore_sched_clock_state(); | 	x86_platform.restore_sched_clock_state(); | ||||||
| 	mtrr_bp_restore(); | 	mtrr_bp_restore(); | ||||||
|  | 	perf_restore_debug_store(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Needed by apm.c */ | /* Needed by apm.c */ | ||||||
|  |  | ||||||
|  | @ -758,6 +758,7 @@ extern void perf_event_enable(struct perf_event *event); | ||||||
| extern void perf_event_disable(struct perf_event *event); | extern void perf_event_disable(struct perf_event *event); | ||||||
| extern int __perf_event_disable(void *info); | extern int __perf_event_disable(void *info); | ||||||
| extern void perf_event_task_tick(void); | extern void perf_event_task_tick(void); | ||||||
|  | extern void perf_restore_debug_store(void); | ||||||
| #else | #else | ||||||
| static inline void | static inline void | ||||||
| perf_event_task_sched_in(struct task_struct *prev, | perf_event_task_sched_in(struct task_struct *prev, | ||||||
|  | @ -797,6 +798,7 @@ static inline void perf_event_enable(struct perf_event *event)		{ } | ||||||
| static inline void perf_event_disable(struct perf_event *event)		{ } | static inline void perf_event_disable(struct perf_event *event)		{ } | ||||||
| static inline int __perf_event_disable(void *info)			{ return -1; } | static inline int __perf_event_disable(void *info)			{ return -1; } | ||||||
| static inline void perf_event_task_tick(void)				{ } | static inline void perf_event_task_tick(void)				{ } | ||||||
|  | static inline void perf_restore_debug_store(void)			{ } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define perf_output_put(handle, x) perf_output_copy((handle), &(x), sizeof(x)) | #define perf_output_put(handle, x) perf_output_copy((handle), &(x), sizeof(x)) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Stephane Eranian
				Stephane Eranian