]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/x86/events/msr.c
Merge branches 'pm-cpufreq-fixes' and 'pm-cpuidle'
[karo-tx-linux.git] / arch / x86 / events / msr.c
1 #include <linux/perf_event.h>
2
3 enum perf_msr_id {
4         PERF_MSR_TSC                    = 0,
5         PERF_MSR_APERF                  = 1,
6         PERF_MSR_MPERF                  = 2,
7         PERF_MSR_PPERF                  = 3,
8         PERF_MSR_SMI                    = 4,
9         PERF_MSR_PTSC                   = 5,
10         PERF_MSR_IRPERF                 = 6,
11
12         PERF_MSR_EVENT_MAX,
13 };
14
15 static bool test_aperfmperf(int idx)
16 {
17         return boot_cpu_has(X86_FEATURE_APERFMPERF);
18 }
19
20 static bool test_ptsc(int idx)
21 {
22         return boot_cpu_has(X86_FEATURE_PTSC);
23 }
24
25 static bool test_irperf(int idx)
26 {
27         return boot_cpu_has(X86_FEATURE_IRPERF);
28 }
29
30 static bool test_intel(int idx)
31 {
32         if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
33             boot_cpu_data.x86 != 6)
34                 return false;
35
36         switch (boot_cpu_data.x86_model) {
37         case 30: /* 45nm Nehalem    */
38         case 26: /* 45nm Nehalem-EP */
39         case 46: /* 45nm Nehalem-EX */
40
41         case 37: /* 32nm Westmere    */
42         case 44: /* 32nm Westmere-EP */
43         case 47: /* 32nm Westmere-EX */
44
45         case 42: /* 32nm SandyBridge         */
46         case 45: /* 32nm SandyBridge-E/EN/EP */
47
48         case 58: /* 22nm IvyBridge       */
49         case 62: /* 22nm IvyBridge-EP/EX */
50
51         case 60: /* 22nm Haswell Core */
52         case 63: /* 22nm Haswell Server */
53         case 69: /* 22nm Haswell ULT */
54         case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */
55
56         case 61: /* 14nm Broadwell Core-M */
57         case 86: /* 14nm Broadwell Xeon D */
58         case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */
59         case 79: /* 14nm Broadwell Server */
60
61         case 55: /* 22nm Atom "Silvermont"                */
62         case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
63         case 76: /* 14nm Atom "Airmont"                   */
64                 if (idx == PERF_MSR_SMI)
65                         return true;
66                 break;
67
68         case 78: /* 14nm Skylake Mobile */
69         case 94: /* 14nm Skylake Desktop */
70                 if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
71                         return true;
72                 break;
73         }
74
75         return false;
76 }
77
78 struct perf_msr {
79         u64     msr;
80         struct  perf_pmu_events_attr *attr;
81         bool    (*test)(int idx);
82 };
83
84 PMU_EVENT_ATTR_STRING(tsc,    evattr_tsc,    "event=0x00");
85 PMU_EVENT_ATTR_STRING(aperf,  evattr_aperf,  "event=0x01");
86 PMU_EVENT_ATTR_STRING(mperf,  evattr_mperf,  "event=0x02");
87 PMU_EVENT_ATTR_STRING(pperf,  evattr_pperf,  "event=0x03");
88 PMU_EVENT_ATTR_STRING(smi,    evattr_smi,    "event=0x04");
89 PMU_EVENT_ATTR_STRING(ptsc,   evattr_ptsc,   "event=0x05");
90 PMU_EVENT_ATTR_STRING(irperf, evattr_irperf, "event=0x06");
91
92 static struct perf_msr msr[] = {
93         [PERF_MSR_TSC]    = { 0,                &evattr_tsc,    NULL,            },
94         [PERF_MSR_APERF]  = { MSR_IA32_APERF,   &evattr_aperf,  test_aperfmperf, },
95         [PERF_MSR_MPERF]  = { MSR_IA32_MPERF,   &evattr_mperf,  test_aperfmperf, },
96         [PERF_MSR_PPERF]  = { MSR_PPERF,        &evattr_pperf,  test_intel,      },
97         [PERF_MSR_SMI]    = { MSR_SMI_COUNT,    &evattr_smi,    test_intel,      },
98         [PERF_MSR_PTSC]   = { MSR_F15H_PTSC,    &evattr_ptsc,   test_ptsc,       },
99         [PERF_MSR_IRPERF] = { MSR_F17H_IRPERF,  &evattr_irperf, test_irperf,     },
100 };
101
102 static struct attribute *events_attrs[PERF_MSR_EVENT_MAX + 1] = {
103         NULL,
104 };
105
106 static struct attribute_group events_attr_group = {
107         .name = "events",
108         .attrs = events_attrs,
109 };
110
111 PMU_FORMAT_ATTR(event, "config:0-63");
112 static struct attribute *format_attrs[] = {
113         &format_attr_event.attr,
114         NULL,
115 };
116 static struct attribute_group format_attr_group = {
117         .name = "format",
118         .attrs = format_attrs,
119 };
120
121 static const struct attribute_group *attr_groups[] = {
122         &events_attr_group,
123         &format_attr_group,
124         NULL,
125 };
126
127 static int msr_event_init(struct perf_event *event)
128 {
129         u64 cfg = event->attr.config;
130
131         if (event->attr.type != event->pmu->type)
132                 return -ENOENT;
133
134         if (cfg >= PERF_MSR_EVENT_MAX)
135                 return -EINVAL;
136
137         /* unsupported modes and filters */
138         if (event->attr.exclude_user   ||
139             event->attr.exclude_kernel ||
140             event->attr.exclude_hv     ||
141             event->attr.exclude_idle   ||
142             event->attr.exclude_host   ||
143             event->attr.exclude_guest  ||
144             event->attr.sample_period) /* no sampling */
145                 return -EINVAL;
146
147         if (!msr[cfg].attr)
148                 return -EINVAL;
149
150         event->hw.idx = -1;
151         event->hw.event_base = msr[cfg].msr;
152         event->hw.config = cfg;
153
154         return 0;
155 }
156
157 static inline u64 msr_read_counter(struct perf_event *event)
158 {
159         u64 now;
160
161         if (event->hw.event_base)
162                 rdmsrl(event->hw.event_base, now);
163         else
164                 rdtscll(now);
165
166         return now;
167 }
168 static void msr_event_update(struct perf_event *event)
169 {
170         u64 prev, now;
171         s64 delta;
172
173         /* Careful, an NMI might modify the previous event value. */
174 again:
175         prev = local64_read(&event->hw.prev_count);
176         now = msr_read_counter(event);
177
178         if (local64_cmpxchg(&event->hw.prev_count, prev, now) != prev)
179                 goto again;
180
181         delta = now - prev;
182         if (unlikely(event->hw.event_base == MSR_SMI_COUNT))
183                 delta = sign_extend64(delta, 31);
184
185         local64_add(delta, &event->count);
186 }
187
188 static void msr_event_start(struct perf_event *event, int flags)
189 {
190         u64 now;
191
192         now = msr_read_counter(event);
193         local64_set(&event->hw.prev_count, now);
194 }
195
196 static void msr_event_stop(struct perf_event *event, int flags)
197 {
198         msr_event_update(event);
199 }
200
201 static void msr_event_del(struct perf_event *event, int flags)
202 {
203         msr_event_stop(event, PERF_EF_UPDATE);
204 }
205
206 static int msr_event_add(struct perf_event *event, int flags)
207 {
208         if (flags & PERF_EF_START)
209                 msr_event_start(event, flags);
210
211         return 0;
212 }
213
214 static struct pmu pmu_msr = {
215         .task_ctx_nr    = perf_sw_context,
216         .attr_groups    = attr_groups,
217         .event_init     = msr_event_init,
218         .add            = msr_event_add,
219         .del            = msr_event_del,
220         .start          = msr_event_start,
221         .stop           = msr_event_stop,
222         .read           = msr_event_update,
223         .capabilities   = PERF_PMU_CAP_NO_INTERRUPT,
224 };
225
226 static int __init msr_init(void)
227 {
228         int i, j = 0;
229
230         if (!boot_cpu_has(X86_FEATURE_TSC)) {
231                 pr_cont("no MSR PMU driver.\n");
232                 return 0;
233         }
234
235         /* Probe the MSRs. */
236         for (i = PERF_MSR_TSC + 1; i < PERF_MSR_EVENT_MAX; i++) {
237                 u64 val;
238
239                 /*
240                  * Virt sucks arse; you cannot tell if a R/O MSR is present :/
241                  */
242                 if (!msr[i].test(i) || rdmsrl_safe(msr[i].msr, &val))
243                         msr[i].attr = NULL;
244         }
245
246         /* List remaining MSRs in the sysfs attrs. */
247         for (i = 0; i < PERF_MSR_EVENT_MAX; i++) {
248                 if (msr[i].attr)
249                         events_attrs[j++] = &msr[i].attr->attr.attr;
250         }
251         events_attrs[j] = NULL;
252
253         perf_pmu_register(&pmu_msr, "msr", -1);
254
255         return 0;
256 }
257 device_initcall(msr_init);