| 
									
										
										
										
											2011-08-30 20:41:05 -03:00
										 |  |  | #include <linux/perf_event.h>
 | 
					
						
							| 
									
										
										
										
											2012-02-29 14:57:32 +01:00
										 |  |  | #include <linux/export.h>
 | 
					
						
							| 
									
										
										
										
											2011-08-30 20:41:05 -03:00
										 |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2011-09-27 10:00:40 -07:00
										 |  |  | #include <asm/apicdef.h>
 | 
					
						
							| 
									
										
										
										
											2011-08-30 20:41:05 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "perf_event.h"
 | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-29 13:09:53 +02:00
										 |  |  | static __initconst const u64 amd_hw_cache_event_ids | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 				[PERF_COUNT_HW_CACHE_MAX] | 
					
						
							|  |  |  | 				[PERF_COUNT_HW_CACHE_OP_MAX] | 
					
						
							|  |  |  | 				[PERF_COUNT_HW_CACHE_RESULT_MAX] = | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |  [ C(L1D) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses        */ | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:53 +02:00
										 |  |  | 		[ C(RESULT_MISS)   ] = 0x0141, /* Data Cache Misses          */ | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x0267, /* Data Prefetcher :attempts  */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0x0167, /* Data Prefetcher :cancelled */ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							|  |  |  |  [ C(L1I ) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x0080, /* Instruction cache fetches  */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0x0081, /* Instruction cache misses   */ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x014B, /* Prefetch Instructions :Load */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							|  |  |  |  [ C(LL  ) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x037D, /* Requests to L2 Cache :IC+DC */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0x037E, /* L2 Cache Misses : IC+DC     */ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x017F, /* L2 Fill/Writeback           */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							|  |  |  |  [ C(DTLB) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses        */ | 
					
						
							| 
									
										
										
										
											2010-10-15 15:15:01 +02:00
										 |  |  | 		[ C(RESULT_MISS)   ] = 0x0746, /* L1_DTLB_AND_L2_DLTB_MISS.ALL */ | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							|  |  |  |  [ C(ITLB) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x0080, /* Instruction fecthes        */ | 
					
						
							| 
									
										
										
										
											2010-10-15 15:15:01 +02:00
										 |  |  | 		[ C(RESULT_MISS)   ] = 0x0385, /* L1_ITLB_AND_L2_ITLB_MISS.ALL */ | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							|  |  |  |  [ C(BPU ) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0x00c2, /* Retired Branch Instr.      */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0x00c3, /* Retired Mispredicted BI    */ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							| 
									
										
										
										
											2011-04-22 23:37:06 +02:00
										 |  |  |  [ C(NODE) ] = { | 
					
						
							|  |  |  | 	[ C(OP_READ) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = 0xb8e9, /* CPU Request to Memory, l+r */ | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = 0x98e9, /* CPU Request to Memory, r   */ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_WRITE) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[ C(OP_PREFETCH) ] = { | 
					
						
							|  |  |  | 		[ C(RESULT_ACCESS) ] = -1, | 
					
						
							|  |  |  | 		[ C(RESULT_MISS)   ] = -1, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |  }, | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * AMD Performance Monitor K7 and later. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static const u64 amd_perfmon_event_map[] = | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-04-29 14:17:19 +02:00
										 |  |  |   [PERF_COUNT_HW_CPU_CYCLES]			= 0x0076, | 
					
						
							|  |  |  |   [PERF_COUNT_HW_INSTRUCTIONS]			= 0x00c0, | 
					
						
							|  |  |  |   [PERF_COUNT_HW_CACHE_REFERENCES]		= 0x0080, | 
					
						
							|  |  |  |   [PERF_COUNT_HW_CACHE_MISSES]			= 0x0081, | 
					
						
							|  |  |  |   [PERF_COUNT_HW_BRANCH_INSTRUCTIONS]		= 0x00c2, | 
					
						
							|  |  |  |   [PERF_COUNT_HW_BRANCH_MISSES]			= 0x00c3, | 
					
						
							|  |  |  |   [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND]	= 0x00d0, /* "Decoder empty" event */ | 
					
						
							|  |  |  |   [PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= 0x00d1, /* "Dispatch stalls" event */ | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u64 amd_pmu_event_map(int hw_event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return amd_perfmon_event_map[hw_event]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:27 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Previously calculated offsets | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static unsigned int event_offsets[X86_PMC_IDX_MAX] __read_mostly; | 
					
						
							|  |  |  | static unsigned int count_offsets[X86_PMC_IDX_MAX] __read_mostly; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Legacy CPUs: | 
					
						
							|  |  |  |  *   4 counters starting at 0xc0010000 each offset by 1 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * CPUs with core performance counter extensions: | 
					
						
							|  |  |  |  *   6 counters starting at 0xc0010200 each offset by 2 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline int amd_pmu_addr_offset(int index, bool eventsel) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-04-15 12:21:22 -05:00
										 |  |  | 	int offset; | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:27 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!index) | 
					
						
							|  |  |  | 		return index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (eventsel) | 
					
						
							|  |  |  | 		offset = event_offsets[index]; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		offset = count_offsets[index]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (offset) | 
					
						
							|  |  |  | 		return offset; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-15 12:21:22 -05:00
										 |  |  | 	if (!cpu_has_perfctr_core) | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:27 -06:00
										 |  |  | 		offset = index; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		offset = index << 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (eventsel) | 
					
						
							|  |  |  | 		event_offsets[index] = offset; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		count_offsets[index] = offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return offset; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:29 -06:00
										 |  |  | static int amd_core_hw_config(struct perf_event *event) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-10-05 14:01:17 +02:00
										 |  |  | 	if (event->attr.exclude_host && event->attr.exclude_guest) | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * When HO == GO == 1 the hardware treats that as GO == HO == 0 | 
					
						
							|  |  |  | 		 * and will count in both modes. We don't want to count in that | 
					
						
							|  |  |  | 		 * case so we emulate no-counting by setting US = OS = 0. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		event->hw.config &= ~(ARCH_PERFMON_EVENTSEL_USR | | 
					
						
							|  |  |  | 				      ARCH_PERFMON_EVENTSEL_OS); | 
					
						
							|  |  |  | 	else if (event->attr.exclude_host) | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:26 -06:00
										 |  |  | 		event->hw.config |= AMD64_EVENTSEL_GUESTONLY; | 
					
						
							| 
									
										
										
										
											2011-10-05 14:01:17 +02:00
										 |  |  | 	else if (event->attr.exclude_guest) | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:26 -06:00
										 |  |  | 		event->hw.config |= AMD64_EVENTSEL_HOSTONLY; | 
					
						
							| 
									
										
										
										
											2011-10-05 14:01:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:29 -06:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-03-30 17:00:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * AMD64 events are detected based on their event codes. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ((hwc->config >> 24) & 0x0f00) | (hwc->config & 0x00ff); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | static inline int amd_is_nb_event(struct hw_perf_event *hwc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return (hwc->config & 0xe0) == 0xe0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | static inline int amd_has_nb(struct cpu_hw_events *cpuc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct amd_nb *nb = cpuc->amd_nb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nb && nb->nb_id != -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:29 -06:00
										 |  |  | static int amd_pmu_hw_config(struct perf_event *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* pass precise event sampling to ibs: */ | 
					
						
							|  |  |  | 	if (event->attr.precise_ip && get_ibs_caps()) | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (has_branch_stack(event)) | 
					
						
							|  |  |  | 		return -EOPNOTSUPP; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = x86_pmu_hw_config(event); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (event->attr.type == PERF_TYPE_RAW) | 
					
						
							|  |  |  | 		event->hw.config |= event->attr.config & AMD64_RAW_EVENT_MASK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return amd_core_hw_config(event); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:25 -06:00
										 |  |  | static void __amd_put_nb_event_constraints(struct cpu_hw_events *cpuc, | 
					
						
							|  |  |  | 					   struct perf_event *event) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct amd_nb *nb = cpuc->amd_nb; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * need to scan whole list because event may not have | 
					
						
							|  |  |  | 	 * been assigned during scheduling | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * no race condition possible because event can only | 
					
						
							|  |  |  | 	 * be removed on one CPU at a time AND PMU is disabled | 
					
						
							|  |  |  | 	 * when we come here | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2010-03-29 18:36:50 +02:00
										 |  |  | 	for (i = 0; i < x86_pmu.num_counters; i++) { | 
					
						
							| 
									
										
										
										
											2012-04-05 18:24:42 +02:00
										 |  |  | 		if (cmpxchg(nb->owners + i, event, NULL) == event) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  /*
 | 
					
						
							|  |  |  |   * AMD64 NorthBridge events need special treatment because | 
					
						
							|  |  |  |   * counter access needs to be synchronized across all cores | 
					
						
							|  |  |  |   * of a package. Refer to BKDG section 3.12 | 
					
						
							|  |  |  |   * | 
					
						
							|  |  |  |   * NB events are events measuring L3 cache, Hypertransport | 
					
						
							|  |  |  |   * traffic. They are identified by an event code >= 0xe00. | 
					
						
							|  |  |  |   * They measure events on the NorthBride which is shared | 
					
						
							|  |  |  |   * by all cores on a package. NB events are counted on a | 
					
						
							|  |  |  |   * shared set of counters. When a NB event is programmed | 
					
						
							|  |  |  |   * in a counter, the data actually comes from a shared | 
					
						
							|  |  |  |   * counter. Thus, access to those counters needs to be | 
					
						
							|  |  |  |   * synchronized. | 
					
						
							|  |  |  |   * | 
					
						
							|  |  |  |   * We implement the synchronization such that no two cores | 
					
						
							|  |  |  |   * can be measuring NB events using the same counters. Thus, | 
					
						
							|  |  |  |   * we maintain a per-NB allocation table. The available slot | 
					
						
							|  |  |  |   * is propagated using the event_constraint structure. | 
					
						
							|  |  |  |   * | 
					
						
							|  |  |  |   * We provide only one choice for each NB event based on | 
					
						
							|  |  |  |   * the fact that only NB events have restrictions. Consequently, | 
					
						
							|  |  |  |   * if a counter is available, there is a guarantee the NB event | 
					
						
							|  |  |  |   * will be assigned to it. If no slot is available, an empty | 
					
						
							|  |  |  |   * constraint is returned and scheduling will eventually fail | 
					
						
							|  |  |  |   * for this event. | 
					
						
							|  |  |  |   * | 
					
						
							|  |  |  |   * Note that all cores attached the same NB compete for the same | 
					
						
							|  |  |  |   * counters to host NB events, this is why we use atomic ops. Some | 
					
						
							|  |  |  |   * multi-chip CPUs may have more than one NB. | 
					
						
							|  |  |  |   * | 
					
						
							|  |  |  |   * Given that resources are allocated (cmpxchg), they must be | 
					
						
							|  |  |  |   * eventually freed for others to use. This is accomplished by | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:25 -06:00
										 |  |  |   * calling __amd_put_nb_event_constraints() | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  |   * | 
					
						
							|  |  |  |   * Non NB events are not impacted by this restriction. | 
					
						
							|  |  |  |   */ | 
					
						
							|  |  |  | static struct event_constraint * | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:25 -06:00
										 |  |  | __amd_get_nb_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event, | 
					
						
							|  |  |  | 			       struct event_constraint *c) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct hw_perf_event *hwc = &event->hw; | 
					
						
							|  |  |  | 	struct amd_nb *nb = cpuc->amd_nb; | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:24 -06:00
										 |  |  | 	struct perf_event *old; | 
					
						
							|  |  |  | 	int idx, new = -1; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:29 -06:00
										 |  |  | 	if (!c) | 
					
						
							|  |  |  | 		c = &unconstrained; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cpuc->is_fake) | 
					
						
							|  |  |  | 		return c; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * detect if already present, if so reuse | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * cannot merge with actual allocation | 
					
						
							|  |  |  | 	 * because of possible holes | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * event can already be present yet not assigned (in hwc->idx) | 
					
						
							|  |  |  | 	 * because of successive calls to x86_schedule_events() from | 
					
						
							|  |  |  | 	 * hw_perf_group_sched_in() without hw_perf_enable() | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:25 -06:00
										 |  |  | 	for_each_set_bit(idx, c->idxmsk, x86_pmu.num_counters) { | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:24 -06:00
										 |  |  | 		if (new == -1 || hwc->idx == idx) | 
					
						
							|  |  |  | 			/* assign free slot, prefer hwc->idx */ | 
					
						
							|  |  |  | 			old = cmpxchg(nb->owners + idx, NULL, event); | 
					
						
							|  |  |  | 		else if (nb->owners[idx] == event) | 
					
						
							|  |  |  | 			/* event already present */ | 
					
						
							|  |  |  | 			old = event; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (old && old != event) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* reassign to this slot */ | 
					
						
							|  |  |  | 		if (new != -1) | 
					
						
							|  |  |  | 			cmpxchg(nb->owners + new, event, NULL); | 
					
						
							|  |  |  | 		new = idx; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* already present, reuse */ | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:24 -06:00
										 |  |  | 		if (old == event) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:24 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (new == -1) | 
					
						
							|  |  |  | 		return &emptyconstraint; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &nb->event_constraints[new]; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-25 08:56:17 +01:00
										 |  |  | static struct amd_nb *amd_alloc_nb(int cpu) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct amd_nb *nb; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-01 18:52:05 +01:00
										 |  |  | 	nb = kmalloc_node(sizeof(struct amd_nb), GFP_KERNEL | __GFP_ZERO, | 
					
						
							|  |  |  | 			  cpu_to_node(cpu)); | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	if (!nb) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-25 08:56:17 +01:00
										 |  |  | 	nb->nb_id = -1; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * initialize all possible NB constraints | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2010-03-29 18:36:50 +02:00
										 |  |  | 	for (i = 0; i < x86_pmu.num_counters; i++) { | 
					
						
							| 
									
										
										
										
											2010-03-02 21:16:55 +01:00
										 |  |  | 		__set_bit(i, nb->event_constraints[i].idxmsk); | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 		nb->event_constraints[i].weight = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nb; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | static int amd_pmu_cpu_prepare(int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	WARN_ON_ONCE(cpuc->amd_nb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (boot_cpu_data.x86_max_cores < 2) | 
					
						
							|  |  |  | 		return NOTIFY_OK; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-25 08:56:17 +01:00
										 |  |  | 	cpuc->amd_nb = amd_alloc_nb(cpu); | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 	if (!cpuc->amd_nb) | 
					
						
							|  |  |  | 		return NOTIFY_BAD; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NOTIFY_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void amd_pmu_cpu_starting(int cpu) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 	struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); | 
					
						
							|  |  |  | 	struct amd_nb *nb; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	int i, nb_id; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:26 -06:00
										 |  |  | 	cpuc->perf_ctr_virt_mask = AMD64_EVENTSEL_HOSTONLY; | 
					
						
							| 
									
										
										
										
											2012-02-29 14:57:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	if (boot_cpu_data.x86_max_cores < 2) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nb_id = amd_get_nb_id(cpu); | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 	WARN_ON_ONCE(nb_id == BAD_APICID); | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for_each_online_cpu(i) { | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 		nb = per_cpu(cpu_hw_events, i).amd_nb; | 
					
						
							|  |  |  | 		if (WARN_ON_ONCE(!nb)) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 		if (nb->nb_id == nb_id) { | 
					
						
							| 
									
										
										
										
											2011-07-22 13:41:54 +02:00
										 |  |  | 			cpuc->kfree_on_online = cpuc->amd_nb; | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 			cpuc->amd_nb = nb; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cpuc->amd_nb->nb_id = nb_id; | 
					
						
							|  |  |  | 	cpuc->amd_nb->refcnt++; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | static void amd_pmu_cpu_dead(int cpu) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct cpu_hw_events *cpuhw; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (boot_cpu_data.x86_max_cores < 2) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpuhw = &per_cpu(cpu_hw_events, cpu); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-21 21:51:51 +01:00
										 |  |  | 	if (cpuhw->amd_nb) { | 
					
						
							| 
									
										
										
										
											2010-03-23 19:31:15 +01:00
										 |  |  | 		struct amd_nb *nb = cpuhw->amd_nb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (nb->nb_id == -1 || --nb->refcnt == 0) | 
					
						
							|  |  |  | 			kfree(nb); | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-21 21:51:51 +01:00
										 |  |  | 		cpuhw->amd_nb = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:25 -06:00
										 |  |  | static struct event_constraint * | 
					
						
							|  |  |  | amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * if not NB event or no NB, then no constraints | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!(amd_has_nb(cpuc) && amd_is_nb_event(&event->hw))) | 
					
						
							|  |  |  | 		return &unconstrained; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-15 12:21:22 -05:00
										 |  |  | 	return __amd_get_nb_event_constraints(cpuc, event, NULL); | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:25 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void amd_put_event_constraints(struct cpu_hw_events *cpuc, | 
					
						
							|  |  |  | 				      struct perf_event *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (amd_has_nb(cpuc) && amd_is_nb_event(&event->hw)) | 
					
						
							|  |  |  | 		__amd_put_nb_event_constraints(cpuc, event); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-15 20:09:14 +01:00
										 |  |  | PMU_FORMAT_ATTR(event,	"config:0-7,32-35"); | 
					
						
							|  |  |  | PMU_FORMAT_ATTR(umask,	"config:8-15"	); | 
					
						
							|  |  |  | PMU_FORMAT_ATTR(edge,	"config:18"	); | 
					
						
							|  |  |  | PMU_FORMAT_ATTR(inv,	"config:23"	); | 
					
						
							|  |  |  | PMU_FORMAT_ATTR(cmask,	"config:24-31"	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *amd_format_attr[] = { | 
					
						
							|  |  |  | 	&format_attr_event.attr, | 
					
						
							|  |  |  | 	&format_attr_umask.attr, | 
					
						
							|  |  |  | 	&format_attr_edge.attr, | 
					
						
							|  |  |  | 	&format_attr_inv.attr, | 
					
						
							|  |  |  | 	&format_attr_cmask.attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | /* AMD Family 15h */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define AMD_EVENT_TYPE_MASK	0x000000F0ULL
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define AMD_EVENT_FP		0x00000000ULL ... 0x00000010ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_LS		0x00000020ULL ... 0x00000030ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_DC		0x00000040ULL ... 0x00000050ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_CU		0x00000060ULL ... 0x00000070ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_IC_DE		0x00000080ULL ... 0x00000090ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_EX_LS		0x000000C0ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_DE		0x000000D0ULL
 | 
					
						
							|  |  |  | #define AMD_EVENT_NB		0x000000E0ULL ... 0x000000F0ULL
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * AMD family 15h event code/PMC mappings: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * type = event_code & 0x0F0: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 0x000	FP	PERF_CTL[5:3] | 
					
						
							|  |  |  |  * 0x010	FP	PERF_CTL[5:3] | 
					
						
							|  |  |  |  * 0x020	LS	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x030	LS	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x040	DC	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x050	DC	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x060	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x070	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x080	IC/DE	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x090	IC/DE	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x0A0	--- | 
					
						
							|  |  |  |  * 0x0B0	--- | 
					
						
							|  |  |  |  * 0x0C0	EX/LS	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x0D0	DE	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x0E0	NB	NB_PERF_CTL[3:0] | 
					
						
							|  |  |  |  * 0x0F0	NB	NB_PERF_CTL[3:0] | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Exceptions: | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:54 +02:00
										 |  |  |  * 0x000	FP	PERF_CTL[3], PERF_CTL[5:3] (*) | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  |  * 0x003	FP	PERF_CTL[3] | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:54 +02:00
										 |  |  |  * 0x004	FP	PERF_CTL[3], PERF_CTL[5:3] (*) | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  |  * 0x00B	FP	PERF_CTL[3] | 
					
						
							|  |  |  |  * 0x00D	FP	PERF_CTL[3] | 
					
						
							|  |  |  |  * 0x023	DE	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x02D	LS	PERF_CTL[3] | 
					
						
							|  |  |  |  * 0x02E	LS	PERF_CTL[3,0] | 
					
						
							| 
									
										
										
										
											2012-05-18 12:40:42 +02:00
										 |  |  |  * 0x031	LS	PERF_CTL[2:0] (**) | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  |  * 0x043	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x045	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x046	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x054	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x055	CU	PERF_CTL[2:0] | 
					
						
							|  |  |  |  * 0x08F	IC	PERF_CTL[0] | 
					
						
							|  |  |  |  * 0x187	DE	PERF_CTL[0] | 
					
						
							|  |  |  |  * 0x188	DE	PERF_CTL[0] | 
					
						
							|  |  |  |  * 0x0DB	EX	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x0DC	LS	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x0DD	LS	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x0DE	LS	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x0DF	LS	PERF_CTL[5:0] | 
					
						
							| 
									
										
										
										
											2012-05-18 12:40:42 +02:00
										 |  |  |  * 0x1C0	EX	PERF_CTL[5:3] | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  |  * 0x1D6	EX	PERF_CTL[5:0] | 
					
						
							|  |  |  |  * 0x1D8	EX	PERF_CTL[5:0] | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:54 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-05-18 12:40:42 +02:00
										 |  |  |  * (*)  depending on the umask all FPU counters may be used | 
					
						
							|  |  |  |  * (**) only one unitmask enabled at a time | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct event_constraint amd_f15_PMC0  = EVENT_CONSTRAINT(0, 0x01, 0); | 
					
						
							|  |  |  | static struct event_constraint amd_f15_PMC20 = EVENT_CONSTRAINT(0, 0x07, 0); | 
					
						
							|  |  |  | static struct event_constraint amd_f15_PMC3  = EVENT_CONSTRAINT(0, 0x08, 0); | 
					
						
							| 
									
										
										
										
											2011-11-18 12:35:22 +01:00
										 |  |  | static struct event_constraint amd_f15_PMC30 = EVENT_CONSTRAINT_OVERLAP(0, 0x09, 0); | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | static struct event_constraint amd_f15_PMC50 = EVENT_CONSTRAINT(0, 0x3F, 0); | 
					
						
							|  |  |  | static struct event_constraint amd_f15_PMC53 = EVENT_CONSTRAINT(0, 0x38, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct event_constraint * | 
					
						
							|  |  |  | amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *event) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:54 +02:00
										 |  |  | 	struct hw_perf_event *hwc = &event->hw; | 
					
						
							|  |  |  | 	unsigned int event_code = amd_get_event_code(hwc); | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (event_code & AMD_EVENT_TYPE_MASK) { | 
					
						
							|  |  |  | 	case AMD_EVENT_FP: | 
					
						
							|  |  |  | 		switch (event_code) { | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:54 +02:00
										 |  |  | 		case 0x000: | 
					
						
							|  |  |  | 			if (!(hwc->config & 0x0000F000ULL)) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			if (!(hwc->config & 0x00000F00ULL)) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			return &amd_f15_PMC3; | 
					
						
							|  |  |  | 		case 0x004: | 
					
						
							|  |  |  | 			if (hweight_long(hwc->config & ARCH_PERFMON_EVENTSEL_UMASK) <= 1) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			return &amd_f15_PMC3; | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 		case 0x003: | 
					
						
							|  |  |  | 		case 0x00B: | 
					
						
							|  |  |  | 		case 0x00D: | 
					
						
							|  |  |  | 			return &amd_f15_PMC3; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-04-16 02:27:54 +02:00
										 |  |  | 		return &amd_f15_PMC53; | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	case AMD_EVENT_LS: | 
					
						
							|  |  |  | 	case AMD_EVENT_DC: | 
					
						
							|  |  |  | 	case AMD_EVENT_EX_LS: | 
					
						
							|  |  |  | 		switch (event_code) { | 
					
						
							|  |  |  | 		case 0x023: | 
					
						
							|  |  |  | 		case 0x043: | 
					
						
							|  |  |  | 		case 0x045: | 
					
						
							|  |  |  | 		case 0x046: | 
					
						
							|  |  |  | 		case 0x054: | 
					
						
							|  |  |  | 		case 0x055: | 
					
						
							|  |  |  | 			return &amd_f15_PMC20; | 
					
						
							|  |  |  | 		case 0x02D: | 
					
						
							|  |  |  | 			return &amd_f15_PMC3; | 
					
						
							|  |  |  | 		case 0x02E: | 
					
						
							|  |  |  | 			return &amd_f15_PMC30; | 
					
						
							| 
									
										
										
										
											2012-05-18 12:40:42 +02:00
										 |  |  | 		case 0x031: | 
					
						
							|  |  |  | 			if (hweight_long(hwc->config & ARCH_PERFMON_EVENTSEL_UMASK) <= 1) | 
					
						
							|  |  |  | 				return &amd_f15_PMC20; | 
					
						
							|  |  |  | 			return &emptyconstraint; | 
					
						
							|  |  |  | 		case 0x1C0: | 
					
						
							|  |  |  | 			return &amd_f15_PMC53; | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return &amd_f15_PMC50; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case AMD_EVENT_CU: | 
					
						
							|  |  |  | 	case AMD_EVENT_IC_DE: | 
					
						
							|  |  |  | 	case AMD_EVENT_DE: | 
					
						
							|  |  |  | 		switch (event_code) { | 
					
						
							|  |  |  | 		case 0x08F: | 
					
						
							|  |  |  | 		case 0x187: | 
					
						
							|  |  |  | 		case 0x188: | 
					
						
							|  |  |  | 			return &amd_f15_PMC0; | 
					
						
							|  |  |  | 		case 0x0DB ... 0x0DF: | 
					
						
							|  |  |  | 		case 0x1D6: | 
					
						
							|  |  |  | 		case 0x1D8: | 
					
						
							|  |  |  | 			return &amd_f15_PMC50; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return &amd_f15_PMC20; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case AMD_EVENT_NB: | 
					
						
							| 
									
										
										
										
											2013-04-15 12:21:22 -05:00
										 |  |  | 		/* moved to perf_event_amd_uncore.c */ | 
					
						
							|  |  |  | 		return &emptyconstraint; | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return &emptyconstraint; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-10 14:53:14 +02:00
										 |  |  | static ssize_t amd_event_sysfs_show(char *page, u64 config) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u64 event = (config & ARCH_PERFMON_EVENTSEL_EVENT) | | 
					
						
							|  |  |  | 		    (config & AMD64_EVENTSEL_EVENT) >> 24; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return x86_event_sysfs_show(page, config, event); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | static __initconst const struct x86_pmu amd_pmu = { | 
					
						
							|  |  |  | 	.name			= "AMD", | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	.handle_irq		= x86_pmu_handle_irq, | 
					
						
							|  |  |  | 	.disable_all		= x86_pmu_disable_all, | 
					
						
							|  |  |  | 	.enable_all		= x86_pmu_enable_all, | 
					
						
							|  |  |  | 	.enable			= x86_pmu_enable_event, | 
					
						
							|  |  |  | 	.disable		= x86_pmu_disable_event, | 
					
						
							|  |  |  | 	.hw_config		= amd_pmu_hw_config, | 
					
						
							|  |  |  | 	.schedule_events	= x86_schedule_events, | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	.eventsel		= MSR_K7_EVNTSEL0, | 
					
						
							|  |  |  | 	.perfctr		= MSR_K7_PERFCTR0, | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:27 -06:00
										 |  |  | 	.addr_offset            = amd_pmu_addr_offset, | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	.event_map		= amd_pmu_event_map, | 
					
						
							|  |  |  | 	.max_events		= ARRAY_SIZE(amd_perfmon_event_map), | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	.num_counters		= AMD64_NUM_COUNTERS, | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	.cntval_bits		= 48, | 
					
						
							|  |  |  | 	.cntval_mask		= (1ULL << 48) - 1, | 
					
						
							|  |  |  | 	.apic			= 1, | 
					
						
							|  |  |  | 	/* use highest bit to detect overflow */ | 
					
						
							|  |  |  | 	.max_period		= (1ULL << 47) - 1, | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	.get_event_constraints	= amd_get_event_constraints, | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	.put_event_constraints	= amd_put_event_constraints, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	.format_attrs		= amd_format_attr, | 
					
						
							| 
									
										
										
										
											2012-10-10 14:53:14 +02:00
										 |  |  | 	.events_sysfs_show	= amd_event_sysfs_show, | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | 	.cpu_prepare		= amd_pmu_cpu_prepare, | 
					
						
							| 
									
										
										
										
											2012-02-29 14:57:32 +01:00
										 |  |  | 	.cpu_starting		= amd_pmu_cpu_starting, | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	.cpu_dead		= amd_pmu_cpu_dead, | 
					
						
							| 
									
										
										
										
											2011-02-02 17:36:12 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | static int __init amd_core_pmu_init(void) | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 	if (!cpu_has_perfctr_core) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (boot_cpu_data.x86) { | 
					
						
							|  |  |  | 	case 0x15: | 
					
						
							|  |  |  | 		pr_cont("Fam15h "); | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 		x86_pmu.get_event_constraints = amd_get_event_constraints_f15h; | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		pr_err("core perfctr but no constraints; unknown hardware!\n"); | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If core performance counter extensions exists, we must use | 
					
						
							|  |  |  | 	 * MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 	 * amd_pmu_addr_offset(). | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	x86_pmu.eventsel	= MSR_F15H_PERF_CTL; | 
					
						
							|  |  |  | 	x86_pmu.perfctr		= MSR_F15H_PERF_CTR; | 
					
						
							|  |  |  | 	x86_pmu.num_counters	= AMD64_NUM_COUNTERS_CORE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 	pr_cont("core perfctr, "); | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-30 20:41:05 -03:00
										 |  |  | __init int amd_pmu_init(void) | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 	/* Performance-monitoring supported from K7 and later: */ | 
					
						
							|  |  |  | 	if (boot_cpu_data.x86 < 6) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-20 20:46:35 +02:00
										 |  |  | 	x86_pmu = amd_pmu; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-21 13:05:37 +02:00
										 |  |  | 	ret = amd_core_pmu_init(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2010-02-26 12:05:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Events are common for all AMDs */ | 
					
						
							|  |  |  | 	memcpy(hw_cache_event_ids, amd_hw_cache_event_ids, | 
					
						
							|  |  |  | 	       sizeof(hw_cache_event_ids)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-02-29 14:57:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | void amd_pmu_enable_virt(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpuc->perf_ctr_virt_mask = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Reload all events */ | 
					
						
							|  |  |  | 	x86_pmu_disable_all(); | 
					
						
							|  |  |  | 	x86_pmu_enable_all(0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(amd_pmu_enable_virt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void amd_pmu_disable_virt(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We only mask out the Host-only bit so that host-only counting works | 
					
						
							|  |  |  | 	 * when SVM is disabled. If someone sets up a guest-only counter when | 
					
						
							|  |  |  | 	 * SVM is disabled the Guest-only bits still gets set and the counter | 
					
						
							|  |  |  | 	 * will not count anything. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-02-06 11:26:26 -06:00
										 |  |  | 	cpuc->perf_ctr_virt_mask = AMD64_EVENTSEL_HOSTONLY; | 
					
						
							| 
									
										
										
										
											2012-02-29 14:57:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Reload all events */ | 
					
						
							|  |  |  | 	x86_pmu_disable_all(); | 
					
						
							|  |  |  | 	x86_pmu_enable_all(0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(amd_pmu_disable_virt); |