1218cfe4eSBorislav Petkov /*
2218cfe4eSBorislav Petkov * Performance events - AMD IBS
3218cfe4eSBorislav Petkov *
4218cfe4eSBorislav Petkov * Copyright (C) 2011 Advanced Micro Devices, Inc., Robert Richter
5218cfe4eSBorislav Petkov *
6218cfe4eSBorislav Petkov * For licencing details see kernel-base/COPYING
7218cfe4eSBorislav Petkov */
8218cfe4eSBorislav Petkov
9218cfe4eSBorislav Petkov #include <linux/perf_event.h>
10eb008eb6SPaul Gortmaker #include <linux/init.h>
11eb008eb6SPaul Gortmaker #include <linux/export.h>
12218cfe4eSBorislav Petkov #include <linux/pci.h>
13218cfe4eSBorislav Petkov #include <linux/ptrace.h>
14218cfe4eSBorislav Petkov #include <linux/syscore_ops.h>
15e6017571SIngo Molnar #include <linux/sched/clock.h>
16218cfe4eSBorislav Petkov
17218cfe4eSBorislav Petkov #include <asm/apic.h>
18218cfe4eSBorislav Petkov
1927f6d22bSBorislav Petkov #include "../perf_event.h"
20218cfe4eSBorislav Petkov
21218cfe4eSBorislav Petkov static u32 ibs_caps;
22218cfe4eSBorislav Petkov
23218cfe4eSBorislav Petkov #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
24218cfe4eSBorislav Petkov
25218cfe4eSBorislav Petkov #include <linux/kprobes.h>
26218cfe4eSBorislav Petkov #include <linux/hardirq.h>
27218cfe4eSBorislav Petkov
28218cfe4eSBorislav Petkov #include <asm/nmi.h>
296a371bafSKim Phillips #include <asm/amd-ibs.h>
30218cfe4eSBorislav Petkov
31218cfe4eSBorislav Petkov #define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT)
32218cfe4eSBorislav Petkov #define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT
33218cfe4eSBorislav Petkov
3485dc6002SPeter Zijlstra
3585dc6002SPeter Zijlstra /*
3685dc6002SPeter Zijlstra * IBS states:
3785dc6002SPeter Zijlstra *
3885dc6002SPeter Zijlstra * ENABLED; tracks the pmu::add(), pmu::del() state, when set the counter is taken
3985dc6002SPeter Zijlstra * and any further add()s must fail.
4085dc6002SPeter Zijlstra *
4185dc6002SPeter Zijlstra * STARTED/STOPPING/STOPPED; deal with pmu::start(), pmu::stop() state but are
4285dc6002SPeter Zijlstra * complicated by the fact that the IBS hardware can send late NMIs (ie. after
4385dc6002SPeter Zijlstra * we've cleared the EN bit).
4485dc6002SPeter Zijlstra *
4585dc6002SPeter Zijlstra * In order to consume these late NMIs we have the STOPPED state, any NMI that
4685dc6002SPeter Zijlstra * happens after we've cleared the EN state will clear this bit and report the
4785dc6002SPeter Zijlstra * NMI handled (this is fundamentally racy in the face or multiple NMI sources,
4885dc6002SPeter Zijlstra * someone else can consume our BIT and our NMI will go unhandled).
4985dc6002SPeter Zijlstra *
5085dc6002SPeter Zijlstra * And since we cannot set/clear this separate bit together with the EN bit,
5185dc6002SPeter Zijlstra * there are races; if we cleared STARTED early, an NMI could land in
5285dc6002SPeter Zijlstra * between clearing STARTED and clearing the EN bit (in fact multiple NMIs
5385dc6002SPeter Zijlstra * could happen if the period is small enough), and consume our STOPPED bit
5485dc6002SPeter Zijlstra * and trigger streams of unhandled NMIs.
5585dc6002SPeter Zijlstra *
5685dc6002SPeter Zijlstra * If, however, we clear STARTED late, an NMI can hit between clearing the
5785dc6002SPeter Zijlstra * EN bit and clearing STARTED, still see STARTED set and process the event.
5885dc6002SPeter Zijlstra * If this event will have the VALID bit clear, we bail properly, but this
5985dc6002SPeter Zijlstra * is not a given. With VALID set we can end up calling pmu::stop() again
6085dc6002SPeter Zijlstra * (the throttle logic) and trigger the WARNs in there.
6185dc6002SPeter Zijlstra *
6285dc6002SPeter Zijlstra * So what we do is set STOPPING before clearing EN to avoid the pmu::stop()
6385dc6002SPeter Zijlstra * nesting, and clear STARTED late, so that we have a well defined state over
6485dc6002SPeter Zijlstra * the clearing of the EN bit.
6585dc6002SPeter Zijlstra *
6685dc6002SPeter Zijlstra * XXX: we could probably be using !atomic bitops for all this.
6785dc6002SPeter Zijlstra */
6885dc6002SPeter Zijlstra
69218cfe4eSBorislav Petkov enum ibs_states {
70218cfe4eSBorislav Petkov IBS_ENABLED = 0,
71218cfe4eSBorislav Petkov IBS_STARTED = 1,
72218cfe4eSBorislav Petkov IBS_STOPPING = 2,
7385dc6002SPeter Zijlstra IBS_STOPPED = 3,
74218cfe4eSBorislav Petkov
75218cfe4eSBorislav Petkov IBS_MAX_STATES,
76218cfe4eSBorislav Petkov };
77218cfe4eSBorislav Petkov
78218cfe4eSBorislav Petkov struct cpu_perf_ibs {
79218cfe4eSBorislav Petkov struct perf_event *event;
80218cfe4eSBorislav Petkov unsigned long state[BITS_TO_LONGS(IBS_MAX_STATES)];
81218cfe4eSBorislav Petkov };
82218cfe4eSBorislav Petkov
83218cfe4eSBorislav Petkov struct perf_ibs {
84218cfe4eSBorislav Petkov struct pmu pmu;
85218cfe4eSBorislav Petkov unsigned int msr;
86218cfe4eSBorislav Petkov u64 config_mask;
87218cfe4eSBorislav Petkov u64 cnt_mask;
88218cfe4eSBorislav Petkov u64 enable_mask;
89218cfe4eSBorislav Petkov u64 valid_mask;
90218cfe4eSBorislav Petkov u64 max_period;
91218cfe4eSBorislav Petkov unsigned long offset_mask[1];
92218cfe4eSBorislav Petkov int offset_max;
93221bfce5SKim Phillips unsigned int fetch_count_reset_broken : 1;
9426db2e0cSKim Phillips unsigned int fetch_ignore_if_zero_rip : 1;
95218cfe4eSBorislav Petkov struct cpu_perf_ibs __percpu *pcpu;
96218cfe4eSBorislav Petkov
97218cfe4eSBorislav Petkov u64 (*get_count)(u64 config);
98218cfe4eSBorislav Petkov };
99218cfe4eSBorislav Petkov
100218cfe4eSBorislav Petkov static int
perf_event_set_period(struct hw_perf_event * hwc,u64 min,u64 max,u64 * hw_period)101218cfe4eSBorislav Petkov perf_event_set_period(struct hw_perf_event *hwc, u64 min, u64 max, u64 *hw_period)
102218cfe4eSBorislav Petkov {
103218cfe4eSBorislav Petkov s64 left = local64_read(&hwc->period_left);
104218cfe4eSBorislav Petkov s64 period = hwc->sample_period;
105218cfe4eSBorislav Petkov int overflow = 0;
106218cfe4eSBorislav Petkov
107218cfe4eSBorislav Petkov /*
108218cfe4eSBorislav Petkov * If we are way outside a reasonable range then just skip forward:
109218cfe4eSBorislav Petkov */
110218cfe4eSBorislav Petkov if (unlikely(left <= -period)) {
111218cfe4eSBorislav Petkov left = period;
112218cfe4eSBorislav Petkov local64_set(&hwc->period_left, left);
113218cfe4eSBorislav Petkov hwc->last_period = period;
114218cfe4eSBorislav Petkov overflow = 1;
115218cfe4eSBorislav Petkov }
116218cfe4eSBorislav Petkov
117218cfe4eSBorislav Petkov if (unlikely(left < (s64)min)) {
118218cfe4eSBorislav Petkov left += period;
119218cfe4eSBorislav Petkov local64_set(&hwc->period_left, left);
120218cfe4eSBorislav Petkov hwc->last_period = period;
121218cfe4eSBorislav Petkov overflow = 1;
122218cfe4eSBorislav Petkov }
123218cfe4eSBorislav Petkov
124218cfe4eSBorislav Petkov /*
125218cfe4eSBorislav Petkov * If the hw period that triggers the sw overflow is too short
126218cfe4eSBorislav Petkov * we might hit the irq handler. This biases the results.
127218cfe4eSBorislav Petkov * Thus we shorten the next-to-last period and set the last
128218cfe4eSBorislav Petkov * period to the max period.
129218cfe4eSBorislav Petkov */
130218cfe4eSBorislav Petkov if (left > max) {
131218cfe4eSBorislav Petkov left -= max;
132218cfe4eSBorislav Petkov if (left > max)
133218cfe4eSBorislav Petkov left = max;
134218cfe4eSBorislav Petkov else if (left < min)
135218cfe4eSBorislav Petkov left = min;
136218cfe4eSBorislav Petkov }
137218cfe4eSBorislav Petkov
138218cfe4eSBorislav Petkov *hw_period = (u64)left;
139218cfe4eSBorislav Petkov
140218cfe4eSBorislav Petkov return overflow;
141218cfe4eSBorislav Petkov }
142218cfe4eSBorislav Petkov
143218cfe4eSBorislav Petkov static int
perf_event_try_update(struct perf_event * event,u64 new_raw_count,int width)144218cfe4eSBorislav Petkov perf_event_try_update(struct perf_event *event, u64 new_raw_count, int width)
145218cfe4eSBorislav Petkov {
146218cfe4eSBorislav Petkov struct hw_perf_event *hwc = &event->hw;
147218cfe4eSBorislav Petkov int shift = 64 - width;
148218cfe4eSBorislav Petkov u64 prev_raw_count;
149218cfe4eSBorislav Petkov u64 delta;
150218cfe4eSBorislav Petkov
151218cfe4eSBorislav Petkov /*
152218cfe4eSBorislav Petkov * Careful: an NMI might modify the previous event value.
153218cfe4eSBorislav Petkov *
154218cfe4eSBorislav Petkov * Our tactic to handle this is to first atomically read and
155218cfe4eSBorislav Petkov * exchange a new raw count - then add that new-prev delta
156218cfe4eSBorislav Petkov * count to the generic event atomically:
157218cfe4eSBorislav Petkov */
158218cfe4eSBorislav Petkov prev_raw_count = local64_read(&hwc->prev_count);
1594c1c9deaSUros Bizjak if (!local64_try_cmpxchg(&hwc->prev_count,
1604c1c9deaSUros Bizjak &prev_raw_count, new_raw_count))
161218cfe4eSBorislav Petkov return 0;
162218cfe4eSBorislav Petkov
163218cfe4eSBorislav Petkov /*
164218cfe4eSBorislav Petkov * Now we have the new raw value and have updated the prev
165218cfe4eSBorislav Petkov * timestamp already. We can now calculate the elapsed delta
166218cfe4eSBorislav Petkov * (event-)time and add that to the generic event.
167218cfe4eSBorislav Petkov *
168218cfe4eSBorislav Petkov * Careful, not all hw sign-extends above the physical width
169218cfe4eSBorislav Petkov * of the count.
170218cfe4eSBorislav Petkov */
171218cfe4eSBorislav Petkov delta = (new_raw_count << shift) - (prev_raw_count << shift);
172218cfe4eSBorislav Petkov delta >>= shift;
173218cfe4eSBorislav Petkov
174218cfe4eSBorislav Petkov local64_add(delta, &event->count);
175218cfe4eSBorislav Petkov local64_sub(delta, &hwc->period_left);
176218cfe4eSBorislav Petkov
177218cfe4eSBorislav Petkov return 1;
178218cfe4eSBorislav Petkov }
179218cfe4eSBorislav Petkov
180218cfe4eSBorislav Petkov static struct perf_ibs perf_ibs_fetch;
181218cfe4eSBorislav Petkov static struct perf_ibs perf_ibs_op;
182218cfe4eSBorislav Petkov
get_ibs_pmu(int type)183218cfe4eSBorislav Petkov static struct perf_ibs *get_ibs_pmu(int type)
184218cfe4eSBorislav Petkov {
185218cfe4eSBorislav Petkov if (perf_ibs_fetch.pmu.type == type)
186218cfe4eSBorislav Petkov return &perf_ibs_fetch;
187218cfe4eSBorislav Petkov if (perf_ibs_op.pmu.type == type)
188218cfe4eSBorislav Petkov return &perf_ibs_op;
189218cfe4eSBorislav Petkov return NULL;
190218cfe4eSBorislav Petkov }
191218cfe4eSBorislav Petkov
192218cfe4eSBorislav Petkov /*
1932fad201fSRavi Bangoria * core pmu config -> IBS config
194218cfe4eSBorislav Petkov *
195218cfe4eSBorislav Petkov * perf record -a -e cpu-cycles:p ... # use ibs op counting cycle count
196218cfe4eSBorislav Petkov * perf record -a -e r076:p ... # same as -e cpu-cycles:p
197218cfe4eSBorislav Petkov * perf record -a -e r0C1:p ... # use ibs op counting micro-ops
198218cfe4eSBorislav Petkov *
199218cfe4eSBorislav Petkov * IbsOpCntCtl (bit 19) of IBS Execution Control Register (IbsOpCtl,
200218cfe4eSBorislav Petkov * MSRC001_1033) is used to select either cycle or micro-ops counting
201218cfe4eSBorislav Petkov * mode.
202218cfe4eSBorislav Petkov */
core_pmu_ibs_config(struct perf_event * event,u64 * config)2032fad201fSRavi Bangoria static int core_pmu_ibs_config(struct perf_event *event, u64 *config)
204218cfe4eSBorislav Petkov {
205218cfe4eSBorislav Petkov switch (event->attr.type) {
206218cfe4eSBorislav Petkov case PERF_TYPE_HARDWARE:
207218cfe4eSBorislav Petkov switch (event->attr.config) {
208218cfe4eSBorislav Petkov case PERF_COUNT_HW_CPU_CYCLES:
209218cfe4eSBorislav Petkov *config = 0;
210218cfe4eSBorislav Petkov return 0;
211218cfe4eSBorislav Petkov }
212218cfe4eSBorislav Petkov break;
213218cfe4eSBorislav Petkov case PERF_TYPE_RAW:
214218cfe4eSBorislav Petkov switch (event->attr.config) {
215218cfe4eSBorislav Petkov case 0x0076:
216218cfe4eSBorislav Petkov *config = 0;
217218cfe4eSBorislav Petkov return 0;
218218cfe4eSBorislav Petkov case 0x00C1:
219218cfe4eSBorislav Petkov *config = IBS_OP_CNT_CTL;
220218cfe4eSBorislav Petkov return 0;
221218cfe4eSBorislav Petkov }
222218cfe4eSBorislav Petkov break;
223218cfe4eSBorislav Petkov default:
224218cfe4eSBorislav Petkov return -ENOENT;
225218cfe4eSBorislav Petkov }
226218cfe4eSBorislav Petkov
227218cfe4eSBorislav Petkov return -EOPNOTSUPP;
228218cfe4eSBorislav Petkov }
229218cfe4eSBorislav Petkov
2302fad201fSRavi Bangoria /*
2312fad201fSRavi Bangoria * The rip of IBS samples has skid 0. Thus, IBS supports precise
2322fad201fSRavi Bangoria * levels 1 and 2 and the PERF_EFLAGS_EXACT is set. In rare cases the
2332fad201fSRavi Bangoria * rip is invalid when IBS was not able to record the rip correctly.
2342fad201fSRavi Bangoria * We clear PERF_EFLAGS_EXACT and take the rip from pt_regs then.
2352fad201fSRavi Bangoria */
forward_event_to_ibs(struct perf_event * event)2362fad201fSRavi Bangoria int forward_event_to_ibs(struct perf_event *event)
2372fad201fSRavi Bangoria {
2382fad201fSRavi Bangoria u64 config = 0;
2392fad201fSRavi Bangoria
2402fad201fSRavi Bangoria if (!event->attr.precise_ip || event->attr.precise_ip > 2)
2412fad201fSRavi Bangoria return -EOPNOTSUPP;
2422fad201fSRavi Bangoria
2432fad201fSRavi Bangoria if (!core_pmu_ibs_config(event, &config)) {
2442fad201fSRavi Bangoria event->attr.type = perf_ibs_op.pmu.type;
2452fad201fSRavi Bangoria event->attr.config = config;
2462fad201fSRavi Bangoria }
2472fad201fSRavi Bangoria return -ENOENT;
2482fad201fSRavi Bangoria }
2492fad201fSRavi Bangoria
2507c212823SRavi Bangoria /*
2517c212823SRavi Bangoria * Grouping of IBS events is not possible since IBS can have only
2527c212823SRavi Bangoria * one event active at any point in time.
2537c212823SRavi Bangoria */
validate_group(struct perf_event * event)2547c212823SRavi Bangoria static int validate_group(struct perf_event *event)
2557c212823SRavi Bangoria {
2567c212823SRavi Bangoria struct perf_event *sibling;
2577c212823SRavi Bangoria
2587c212823SRavi Bangoria if (event->group_leader == event)
2597c212823SRavi Bangoria return 0;
2607c212823SRavi Bangoria
2617c212823SRavi Bangoria if (event->group_leader->pmu == event->pmu)
2627c212823SRavi Bangoria return -EINVAL;
2637c212823SRavi Bangoria
2647c212823SRavi Bangoria for_each_sibling_event(sibling, event->group_leader) {
2657c212823SRavi Bangoria if (sibling->pmu == event->pmu)
2667c212823SRavi Bangoria return -EINVAL;
2677c212823SRavi Bangoria }
2687c212823SRavi Bangoria return 0;
2697c212823SRavi Bangoria }
2707c212823SRavi Bangoria
perf_ibs_init(struct perf_event * event)271218cfe4eSBorislav Petkov static int perf_ibs_init(struct perf_event *event)
272218cfe4eSBorislav Petkov {
273218cfe4eSBorislav Petkov struct hw_perf_event *hwc = &event->hw;
274218cfe4eSBorislav Petkov struct perf_ibs *perf_ibs;
275218cfe4eSBorislav Petkov u64 max_cnt, config;
2767c212823SRavi Bangoria int ret;
277218cfe4eSBorislav Petkov
278218cfe4eSBorislav Petkov perf_ibs = get_ibs_pmu(event->attr.type);
2792fad201fSRavi Bangoria if (!perf_ibs)
2802fad201fSRavi Bangoria return -ENOENT;
2812fad201fSRavi Bangoria
282218cfe4eSBorislav Petkov config = event->attr.config;
283218cfe4eSBorislav Petkov
284218cfe4eSBorislav Petkov if (event->pmu != &perf_ibs->pmu)
285218cfe4eSBorislav Petkov return -ENOENT;
286218cfe4eSBorislav Petkov
287218cfe4eSBorislav Petkov if (config & ~perf_ibs->config_mask)
288218cfe4eSBorislav Petkov return -EINVAL;
289218cfe4eSBorislav Petkov
2907c212823SRavi Bangoria ret = validate_group(event);
2917c212823SRavi Bangoria if (ret)
2927c212823SRavi Bangoria return ret;
2937c212823SRavi Bangoria
294218cfe4eSBorislav Petkov if (hwc->sample_period) {
295218cfe4eSBorislav Petkov if (config & perf_ibs->cnt_mask)
296218cfe4eSBorislav Petkov /* raw max_cnt may not be set */
297218cfe4eSBorislav Petkov return -EINVAL;
298218cfe4eSBorislav Petkov if (!event->attr.sample_freq && hwc->sample_period & 0x0f)
299218cfe4eSBorislav Petkov /*
300218cfe4eSBorislav Petkov * lower 4 bits can not be set in ibs max cnt,
301218cfe4eSBorislav Petkov * but allowing it in case we adjust the
302218cfe4eSBorislav Petkov * sample period to set a frequency.
303218cfe4eSBorislav Petkov */
304218cfe4eSBorislav Petkov return -EINVAL;
305218cfe4eSBorislav Petkov hwc->sample_period &= ~0x0FULL;
306218cfe4eSBorislav Petkov if (!hwc->sample_period)
307218cfe4eSBorislav Petkov hwc->sample_period = 0x10;
308218cfe4eSBorislav Petkov } else {
309218cfe4eSBorislav Petkov max_cnt = config & perf_ibs->cnt_mask;
310218cfe4eSBorislav Petkov config &= ~perf_ibs->cnt_mask;
311218cfe4eSBorislav Petkov event->attr.sample_period = max_cnt << 4;
312218cfe4eSBorislav Petkov hwc->sample_period = event->attr.sample_period;
313218cfe4eSBorislav Petkov }
314218cfe4eSBorislav Petkov
315218cfe4eSBorislav Petkov if (!hwc->sample_period)
316218cfe4eSBorislav Petkov return -EINVAL;
317218cfe4eSBorislav Petkov
318218cfe4eSBorislav Petkov /*
319218cfe4eSBorislav Petkov * If we modify hwc->sample_period, we also need to update
320218cfe4eSBorislav Petkov * hwc->last_period and hwc->period_left.
321218cfe4eSBorislav Petkov */
322218cfe4eSBorislav Petkov hwc->last_period = hwc->sample_period;
323218cfe4eSBorislav Petkov local64_set(&hwc->period_left, hwc->sample_period);
324218cfe4eSBorislav Petkov
325218cfe4eSBorislav Petkov hwc->config_base = perf_ibs->msr;
326218cfe4eSBorislav Petkov hwc->config = config;
327218cfe4eSBorislav Petkov
328218cfe4eSBorislav Petkov return 0;
329218cfe4eSBorislav Petkov }
330218cfe4eSBorislav Petkov
perf_ibs_set_period(struct perf_ibs * perf_ibs,struct hw_perf_event * hwc,u64 * period)331218cfe4eSBorislav Petkov static int perf_ibs_set_period(struct perf_ibs *perf_ibs,
332218cfe4eSBorislav Petkov struct hw_perf_event *hwc, u64 *period)
333218cfe4eSBorislav Petkov {
334218cfe4eSBorislav Petkov int overflow;
335218cfe4eSBorislav Petkov
336218cfe4eSBorislav Petkov /* ignore lower 4 bits in min count: */
337218cfe4eSBorislav Petkov overflow = perf_event_set_period(hwc, 1<<4, perf_ibs->max_period, period);
338218cfe4eSBorislav Petkov local64_set(&hwc->prev_count, 0);
339218cfe4eSBorislav Petkov
340218cfe4eSBorislav Petkov return overflow;
341218cfe4eSBorislav Petkov }
342218cfe4eSBorislav Petkov
get_ibs_fetch_count(u64 config)343218cfe4eSBorislav Petkov static u64 get_ibs_fetch_count(u64 config)
344218cfe4eSBorislav Petkov {
3456a371bafSKim Phillips union ibs_fetch_ctl fetch_ctl = (union ibs_fetch_ctl)config;
3466a371bafSKim Phillips
3476a371bafSKim Phillips return fetch_ctl.fetch_cnt << 4;
348218cfe4eSBorislav Petkov }
349218cfe4eSBorislav Petkov
get_ibs_op_count(u64 config)350218cfe4eSBorislav Petkov static u64 get_ibs_op_count(u64 config)
351218cfe4eSBorislav Petkov {
3526a371bafSKim Phillips union ibs_op_ctl op_ctl = (union ibs_op_ctl)config;
353218cfe4eSBorislav Petkov u64 count = 0;
354218cfe4eSBorislav Petkov
355680d6963SKim Phillips /*
356680d6963SKim Phillips * If the internal 27-bit counter rolled over, the count is MaxCnt
357680d6963SKim Phillips * and the lower 7 bits of CurCnt are randomized.
358680d6963SKim Phillips * Otherwise CurCnt has the full 27-bit current counter value.
359680d6963SKim Phillips */
3606a371bafSKim Phillips if (op_ctl.op_val) {
3616a371bafSKim Phillips count = op_ctl.opmaxcnt << 4;
3628b0bed7dSKim Phillips if (ibs_caps & IBS_CAPS_OPCNTEXT)
3636a371bafSKim Phillips count += op_ctl.opmaxcnt_ext << 20;
3648b0bed7dSKim Phillips } else if (ibs_caps & IBS_CAPS_RDWROPCNT) {
3656a371bafSKim Phillips count = op_ctl.opcurcnt;
3668b0bed7dSKim Phillips }
367218cfe4eSBorislav Petkov
368218cfe4eSBorislav Petkov return count;
369218cfe4eSBorislav Petkov }
370218cfe4eSBorislav Petkov
371218cfe4eSBorislav Petkov static void
perf_ibs_event_update(struct perf_ibs * perf_ibs,struct perf_event * event,u64 * config)372218cfe4eSBorislav Petkov perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event,
373218cfe4eSBorislav Petkov u64 *config)
374218cfe4eSBorislav Petkov {
375218cfe4eSBorislav Petkov u64 count = perf_ibs->get_count(*config);
376218cfe4eSBorislav Petkov
377218cfe4eSBorislav Petkov /*
378218cfe4eSBorislav Petkov * Set width to 64 since we do not overflow on max width but
379218cfe4eSBorislav Petkov * instead on max count. In perf_ibs_set_period() we clear
380218cfe4eSBorislav Petkov * prev count manually on overflow.
381218cfe4eSBorislav Petkov */
382218cfe4eSBorislav Petkov while (!perf_event_try_update(event, count, 64)) {
383218cfe4eSBorislav Petkov rdmsrl(event->hw.config_base, *config);
384218cfe4eSBorislav Petkov count = perf_ibs->get_count(*config);
385218cfe4eSBorislav Petkov }
386218cfe4eSBorislav Petkov }
387218cfe4eSBorislav Petkov
perf_ibs_enable_event(struct perf_ibs * perf_ibs,struct hw_perf_event * hwc,u64 config)388218cfe4eSBorislav Petkov static inline void perf_ibs_enable_event(struct perf_ibs *perf_ibs,
389218cfe4eSBorislav Petkov struct hw_perf_event *hwc, u64 config)
390218cfe4eSBorislav Petkov {
391221bfce5SKim Phillips u64 tmp = hwc->config | config;
392221bfce5SKim Phillips
393221bfce5SKim Phillips if (perf_ibs->fetch_count_reset_broken)
394221bfce5SKim Phillips wrmsrl(hwc->config_base, tmp & ~perf_ibs->enable_mask);
395221bfce5SKim Phillips
396221bfce5SKim Phillips wrmsrl(hwc->config_base, tmp | perf_ibs->enable_mask);
397218cfe4eSBorislav Petkov }
398218cfe4eSBorislav Petkov
399218cfe4eSBorislav Petkov /*
400218cfe4eSBorislav Petkov * Erratum #420 Instruction-Based Sampling Engine May Generate
401218cfe4eSBorislav Petkov * Interrupt that Cannot Be Cleared:
402218cfe4eSBorislav Petkov *
403218cfe4eSBorislav Petkov * Must clear counter mask first, then clear the enable bit. See
404218cfe4eSBorislav Petkov * Revision Guide for AMD Family 10h Processors, Publication #41322.
405218cfe4eSBorislav Petkov */
perf_ibs_disable_event(struct perf_ibs * perf_ibs,struct hw_perf_event * hwc,u64 config)406218cfe4eSBorislav Petkov static inline void perf_ibs_disable_event(struct perf_ibs *perf_ibs,
407218cfe4eSBorislav Petkov struct hw_perf_event *hwc, u64 config)
408218cfe4eSBorislav Petkov {
409218cfe4eSBorislav Petkov config &= ~perf_ibs->cnt_mask;
410e431e79bSKim Phillips if (boot_cpu_data.x86 == 0x10)
411218cfe4eSBorislav Petkov wrmsrl(hwc->config_base, config);
412218cfe4eSBorislav Petkov config &= ~perf_ibs->enable_mask;
413218cfe4eSBorislav Petkov wrmsrl(hwc->config_base, config);
414218cfe4eSBorislav Petkov }
415218cfe4eSBorislav Petkov
416218cfe4eSBorislav Petkov /*
417218cfe4eSBorislav Petkov * We cannot restore the ibs pmu state, so we always needs to update
418218cfe4eSBorislav Petkov * the event while stopping it and then reset the state when starting
419218cfe4eSBorislav Petkov * again. Thus, ignoring PERF_EF_RELOAD and PERF_EF_UPDATE flags in
420218cfe4eSBorislav Petkov * perf_ibs_start()/perf_ibs_stop() and instead always do it.
421218cfe4eSBorislav Petkov */
perf_ibs_start(struct perf_event * event,int flags)422218cfe4eSBorislav Petkov static void perf_ibs_start(struct perf_event *event, int flags)
423218cfe4eSBorislav Petkov {
424218cfe4eSBorislav Petkov struct hw_perf_event *hwc = &event->hw;
425218cfe4eSBorislav Petkov struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
426218cfe4eSBorislav Petkov struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
4278b0bed7dSKim Phillips u64 period, config = 0;
428218cfe4eSBorislav Petkov
429218cfe4eSBorislav Petkov if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
430218cfe4eSBorislav Petkov return;
431218cfe4eSBorislav Petkov
432218cfe4eSBorislav Petkov WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
433218cfe4eSBorislav Petkov hwc->state = 0;
434218cfe4eSBorislav Petkov
435218cfe4eSBorislav Petkov perf_ibs_set_period(perf_ibs, hwc, &period);
4368b0bed7dSKim Phillips if (perf_ibs == &perf_ibs_op && (ibs_caps & IBS_CAPS_OPCNTEXT)) {
4378b0bed7dSKim Phillips config |= period & IBS_OP_MAX_CNT_EXT_MASK;
4388b0bed7dSKim Phillips period &= ~IBS_OP_MAX_CNT_EXT_MASK;
4398b0bed7dSKim Phillips }
4408b0bed7dSKim Phillips config |= period >> 4;
4418b0bed7dSKim Phillips
4425a50f529SPeter Zijlstra /*
44385dc6002SPeter Zijlstra * Set STARTED before enabling the hardware, such that a subsequent NMI
44485dc6002SPeter Zijlstra * must observe it.
4455a50f529SPeter Zijlstra */
446218cfe4eSBorislav Petkov set_bit(IBS_STARTED, pcpu->state);
4475a50f529SPeter Zijlstra clear_bit(IBS_STOPPING, pcpu->state);
4488b0bed7dSKim Phillips perf_ibs_enable_event(perf_ibs, hwc, config);
449218cfe4eSBorislav Petkov
450218cfe4eSBorislav Petkov perf_event_update_userpage(event);
451218cfe4eSBorislav Petkov }
452218cfe4eSBorislav Petkov
perf_ibs_stop(struct perf_event * event,int flags)453218cfe4eSBorislav Petkov static void perf_ibs_stop(struct perf_event *event, int flags)
454218cfe4eSBorislav Petkov {
455218cfe4eSBorislav Petkov struct hw_perf_event *hwc = &event->hw;
456218cfe4eSBorislav Petkov struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
457218cfe4eSBorislav Petkov struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
458218cfe4eSBorislav Petkov u64 config;
459218cfe4eSBorislav Petkov int stopping;
460218cfe4eSBorislav Petkov
46185dc6002SPeter Zijlstra if (test_and_set_bit(IBS_STOPPING, pcpu->state))
46285dc6002SPeter Zijlstra return;
46385dc6002SPeter Zijlstra
4645a50f529SPeter Zijlstra stopping = test_bit(IBS_STARTED, pcpu->state);
465218cfe4eSBorislav Petkov
466218cfe4eSBorislav Petkov if (!stopping && (hwc->state & PERF_HES_UPTODATE))
467218cfe4eSBorislav Petkov return;
468218cfe4eSBorislav Petkov
469218cfe4eSBorislav Petkov rdmsrl(hwc->config_base, config);
470218cfe4eSBorislav Petkov
471218cfe4eSBorislav Petkov if (stopping) {
4725a50f529SPeter Zijlstra /*
47385dc6002SPeter Zijlstra * Set STOPPED before disabling the hardware, such that it
4745a50f529SPeter Zijlstra * must be visible to NMIs the moment we clear the EN bit,
4755a50f529SPeter Zijlstra * at which point we can generate an !VALID sample which
4765a50f529SPeter Zijlstra * we need to consume.
4775a50f529SPeter Zijlstra */
47885dc6002SPeter Zijlstra set_bit(IBS_STOPPED, pcpu->state);
479218cfe4eSBorislav Petkov perf_ibs_disable_event(perf_ibs, hwc, config);
4805a50f529SPeter Zijlstra /*
4815a50f529SPeter Zijlstra * Clear STARTED after disabling the hardware; if it were
4825a50f529SPeter Zijlstra * cleared before an NMI hitting after the clear but before
4835a50f529SPeter Zijlstra * clearing the EN bit might think it a spurious NMI and not
4845a50f529SPeter Zijlstra * handle it.
4855a50f529SPeter Zijlstra *
4865a50f529SPeter Zijlstra * Clearing it after, however, creates the problem of the NMI
4875a50f529SPeter Zijlstra * handler seeing STARTED but not having a valid sample.
4885a50f529SPeter Zijlstra */
4895a50f529SPeter Zijlstra clear_bit(IBS_STARTED, pcpu->state);
490218cfe4eSBorislav Petkov WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
491218cfe4eSBorislav Petkov hwc->state |= PERF_HES_STOPPED;
492218cfe4eSBorislav Petkov }
493218cfe4eSBorislav Petkov
494218cfe4eSBorislav Petkov if (hwc->state & PERF_HES_UPTODATE)
495218cfe4eSBorislav Petkov return;
496218cfe4eSBorislav Petkov
497218cfe4eSBorislav Petkov /*
498218cfe4eSBorislav Petkov * Clear valid bit to not count rollovers on update, rollovers
499218cfe4eSBorislav Petkov * are only updated in the irq handler.
500218cfe4eSBorislav Petkov */
501218cfe4eSBorislav Petkov config &= ~perf_ibs->valid_mask;
502218cfe4eSBorislav Petkov
503218cfe4eSBorislav Petkov perf_ibs_event_update(perf_ibs, event, &config);
504218cfe4eSBorislav Petkov hwc->state |= PERF_HES_UPTODATE;
505218cfe4eSBorislav Petkov }
506218cfe4eSBorislav Petkov
perf_ibs_add(struct perf_event * event,int flags)507218cfe4eSBorislav Petkov static int perf_ibs_add(struct perf_event *event, int flags)
508218cfe4eSBorislav Petkov {
509218cfe4eSBorislav Petkov struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
510218cfe4eSBorislav Petkov struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
511218cfe4eSBorislav Petkov
512218cfe4eSBorislav Petkov if (test_and_set_bit(IBS_ENABLED, pcpu->state))
513218cfe4eSBorislav Petkov return -ENOSPC;
514218cfe4eSBorislav Petkov
515218cfe4eSBorislav Petkov event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
516218cfe4eSBorislav Petkov
517218cfe4eSBorislav Petkov pcpu->event = event;
518218cfe4eSBorislav Petkov
519218cfe4eSBorislav Petkov if (flags & PERF_EF_START)
520218cfe4eSBorislav Petkov perf_ibs_start(event, PERF_EF_RELOAD);
521218cfe4eSBorislav Petkov
522218cfe4eSBorislav Petkov return 0;
523218cfe4eSBorislav Petkov }
524218cfe4eSBorislav Petkov
perf_ibs_del(struct perf_event * event,int flags)525218cfe4eSBorislav Petkov static void perf_ibs_del(struct perf_event *event, int flags)
526218cfe4eSBorislav Petkov {
527218cfe4eSBorislav Petkov struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
528218cfe4eSBorislav Petkov struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
529218cfe4eSBorislav Petkov
530218cfe4eSBorislav Petkov if (!test_and_clear_bit(IBS_ENABLED, pcpu->state))
531218cfe4eSBorislav Petkov return;
532218cfe4eSBorislav Petkov
533218cfe4eSBorislav Petkov perf_ibs_stop(event, PERF_EF_UPDATE);
534218cfe4eSBorislav Petkov
535218cfe4eSBorislav Petkov pcpu->event = NULL;
536218cfe4eSBorislav Petkov
537218cfe4eSBorislav Petkov perf_event_update_userpage(event);
538218cfe4eSBorislav Petkov }
539218cfe4eSBorislav Petkov
perf_ibs_read(struct perf_event * event)540218cfe4eSBorislav Petkov static void perf_ibs_read(struct perf_event *event) { }
541218cfe4eSBorislav Petkov
5422a7a7e65SRavi Bangoria /*
5432a7a7e65SRavi Bangoria * We need to initialize with empty group if all attributes in the
5442a7a7e65SRavi Bangoria * group are dynamic.
5452a7a7e65SRavi Bangoria */
5462a7a7e65SRavi Bangoria static struct attribute *attrs_empty[] = {
5472a7a7e65SRavi Bangoria NULL,
5482a7a7e65SRavi Bangoria };
5492a7a7e65SRavi Bangoria
5502a7a7e65SRavi Bangoria static struct attribute_group empty_format_group = {
5512a7a7e65SRavi Bangoria .name = "format",
5522a7a7e65SRavi Bangoria .attrs = attrs_empty,
5532a7a7e65SRavi Bangoria };
5542a7a7e65SRavi Bangoria
555838de1d8SRavi Bangoria static struct attribute_group empty_caps_group = {
556838de1d8SRavi Bangoria .name = "caps",
557838de1d8SRavi Bangoria .attrs = attrs_empty,
558838de1d8SRavi Bangoria };
559838de1d8SRavi Bangoria
5602a7a7e65SRavi Bangoria static const struct attribute_group *empty_attr_groups[] = {
5612a7a7e65SRavi Bangoria &empty_format_group,
562838de1d8SRavi Bangoria &empty_caps_group,
5632a7a7e65SRavi Bangoria NULL,
5642a7a7e65SRavi Bangoria };
5652a7a7e65SRavi Bangoria
566218cfe4eSBorislav Petkov PMU_FORMAT_ATTR(rand_en, "config:57");
567218cfe4eSBorislav Petkov PMU_FORMAT_ATTR(cnt_ctl, "config:19");
568ba5d35b4SRavi Bangoria PMU_EVENT_ATTR_STRING(l3missonly, fetch_l3missonly, "config:59");
569ba5d35b4SRavi Bangoria PMU_EVENT_ATTR_STRING(l3missonly, op_l3missonly, "config:16");
570838de1d8SRavi Bangoria PMU_EVENT_ATTR_STRING(zen4_ibs_extensions, zen4_ibs_extensions, "1");
571ba5d35b4SRavi Bangoria
572ba5d35b4SRavi Bangoria static umode_t
zen4_ibs_extensions_is_visible(struct kobject * kobj,struct attribute * attr,int i)573ba5d35b4SRavi Bangoria zen4_ibs_extensions_is_visible(struct kobject *kobj, struct attribute *attr, int i)
574ba5d35b4SRavi Bangoria {
575ba5d35b4SRavi Bangoria return ibs_caps & IBS_CAPS_ZEN4 ? attr->mode : 0;
576ba5d35b4SRavi Bangoria }
577218cfe4eSBorislav Petkov
5782a7a7e65SRavi Bangoria static struct attribute *rand_en_attrs[] = {
579218cfe4eSBorislav Petkov &format_attr_rand_en.attr,
580218cfe4eSBorislav Petkov NULL,
581218cfe4eSBorislav Petkov };
582218cfe4eSBorislav Petkov
583ba5d35b4SRavi Bangoria static struct attribute *fetch_l3missonly_attrs[] = {
584ba5d35b4SRavi Bangoria &fetch_l3missonly.attr.attr,
585ba5d35b4SRavi Bangoria NULL,
586ba5d35b4SRavi Bangoria };
587ba5d35b4SRavi Bangoria
588838de1d8SRavi Bangoria static struct attribute *zen4_ibs_extensions_attrs[] = {
589838de1d8SRavi Bangoria &zen4_ibs_extensions.attr.attr,
590838de1d8SRavi Bangoria NULL,
591838de1d8SRavi Bangoria };
592838de1d8SRavi Bangoria
5932a7a7e65SRavi Bangoria static struct attribute_group group_rand_en = {
5942a7a7e65SRavi Bangoria .name = "format",
5952a7a7e65SRavi Bangoria .attrs = rand_en_attrs,
5962a7a7e65SRavi Bangoria };
5972a7a7e65SRavi Bangoria
598ba5d35b4SRavi Bangoria static struct attribute_group group_fetch_l3missonly = {
599ba5d35b4SRavi Bangoria .name = "format",
600ba5d35b4SRavi Bangoria .attrs = fetch_l3missonly_attrs,
601ba5d35b4SRavi Bangoria .is_visible = zen4_ibs_extensions_is_visible,
602ba5d35b4SRavi Bangoria };
603ba5d35b4SRavi Bangoria
604838de1d8SRavi Bangoria static struct attribute_group group_zen4_ibs_extensions = {
605838de1d8SRavi Bangoria .name = "caps",
606838de1d8SRavi Bangoria .attrs = zen4_ibs_extensions_attrs,
607838de1d8SRavi Bangoria .is_visible = zen4_ibs_extensions_is_visible,
608838de1d8SRavi Bangoria };
609838de1d8SRavi Bangoria
6102a7a7e65SRavi Bangoria static const struct attribute_group *fetch_attr_groups[] = {
6112a7a7e65SRavi Bangoria &group_rand_en,
612838de1d8SRavi Bangoria &empty_caps_group,
6132a7a7e65SRavi Bangoria NULL,
6142a7a7e65SRavi Bangoria };
6152a7a7e65SRavi Bangoria
616ba5d35b4SRavi Bangoria static const struct attribute_group *fetch_attr_update[] = {
617ba5d35b4SRavi Bangoria &group_fetch_l3missonly,
618838de1d8SRavi Bangoria &group_zen4_ibs_extensions,
619ba5d35b4SRavi Bangoria NULL,
620ba5d35b4SRavi Bangoria };
621ba5d35b4SRavi Bangoria
6222a7a7e65SRavi Bangoria static umode_t
cnt_ctl_is_visible(struct kobject * kobj,struct attribute * attr,int i)6232a7a7e65SRavi Bangoria cnt_ctl_is_visible(struct kobject *kobj, struct attribute *attr, int i)
6242a7a7e65SRavi Bangoria {
6252a7a7e65SRavi Bangoria return ibs_caps & IBS_CAPS_OPCNT ? attr->mode : 0;
6262a7a7e65SRavi Bangoria }
6272a7a7e65SRavi Bangoria
6282a7a7e65SRavi Bangoria static struct attribute *cnt_ctl_attrs[] = {
6292a7a7e65SRavi Bangoria &format_attr_cnt_ctl.attr,
6302a7a7e65SRavi Bangoria NULL,
6312a7a7e65SRavi Bangoria };
6322a7a7e65SRavi Bangoria
633ba5d35b4SRavi Bangoria static struct attribute *op_l3missonly_attrs[] = {
634ba5d35b4SRavi Bangoria &op_l3missonly.attr.attr,
635ba5d35b4SRavi Bangoria NULL,
636ba5d35b4SRavi Bangoria };
637ba5d35b4SRavi Bangoria
6382a7a7e65SRavi Bangoria static struct attribute_group group_cnt_ctl = {
6392a7a7e65SRavi Bangoria .name = "format",
6402a7a7e65SRavi Bangoria .attrs = cnt_ctl_attrs,
6412a7a7e65SRavi Bangoria .is_visible = cnt_ctl_is_visible,
6422a7a7e65SRavi Bangoria };
6432a7a7e65SRavi Bangoria
644ba5d35b4SRavi Bangoria static struct attribute_group group_op_l3missonly = {
645ba5d35b4SRavi Bangoria .name = "format",
646ba5d35b4SRavi Bangoria .attrs = op_l3missonly_attrs,
647ba5d35b4SRavi Bangoria .is_visible = zen4_ibs_extensions_is_visible,
648ba5d35b4SRavi Bangoria };
649ba5d35b4SRavi Bangoria
6502a7a7e65SRavi Bangoria static const struct attribute_group *op_attr_update[] = {
6512a7a7e65SRavi Bangoria &group_cnt_ctl,
652ba5d35b4SRavi Bangoria &group_op_l3missonly,
653838de1d8SRavi Bangoria &group_zen4_ibs_extensions,
654218cfe4eSBorislav Petkov NULL,
655218cfe4eSBorislav Petkov };
656218cfe4eSBorislav Petkov
657218cfe4eSBorislav Petkov static struct perf_ibs perf_ibs_fetch = {
658218cfe4eSBorislav Petkov .pmu = {
65930093056SRavi Bangoria .task_ctx_nr = perf_hw_context,
660218cfe4eSBorislav Petkov
661218cfe4eSBorislav Petkov .event_init = perf_ibs_init,
662218cfe4eSBorislav Petkov .add = perf_ibs_add,
663218cfe4eSBorislav Petkov .del = perf_ibs_del,
664218cfe4eSBorislav Petkov .start = perf_ibs_start,
665218cfe4eSBorislav Petkov .stop = perf_ibs_stop,
666218cfe4eSBorislav Petkov .read = perf_ibs_read,
6672ff40250SAndrew Murray .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
668218cfe4eSBorislav Petkov },
669218cfe4eSBorislav Petkov .msr = MSR_AMD64_IBSFETCHCTL,
670218cfe4eSBorislav Petkov .config_mask = IBS_FETCH_CONFIG_MASK,
671218cfe4eSBorislav Petkov .cnt_mask = IBS_FETCH_MAX_CNT,
672218cfe4eSBorislav Petkov .enable_mask = IBS_FETCH_ENABLE,
673218cfe4eSBorislav Petkov .valid_mask = IBS_FETCH_VAL,
674218cfe4eSBorislav Petkov .max_period = IBS_FETCH_MAX_CNT << 4,
675218cfe4eSBorislav Petkov .offset_mask = { MSR_AMD64_IBSFETCH_REG_MASK },
676218cfe4eSBorislav Petkov .offset_max = MSR_AMD64_IBSFETCH_REG_COUNT,
677218cfe4eSBorislav Petkov
678218cfe4eSBorislav Petkov .get_count = get_ibs_fetch_count,
679218cfe4eSBorislav Petkov };
680218cfe4eSBorislav Petkov
681218cfe4eSBorislav Petkov static struct perf_ibs perf_ibs_op = {
682218cfe4eSBorislav Petkov .pmu = {
68330093056SRavi Bangoria .task_ctx_nr = perf_hw_context,
684218cfe4eSBorislav Petkov
685218cfe4eSBorislav Petkov .event_init = perf_ibs_init,
686218cfe4eSBorislav Petkov .add = perf_ibs_add,
687218cfe4eSBorislav Petkov .del = perf_ibs_del,
688218cfe4eSBorislav Petkov .start = perf_ibs_start,
689218cfe4eSBorislav Petkov .stop = perf_ibs_stop,
690218cfe4eSBorislav Petkov .read = perf_ibs_read,
691f11dd0d8SKim Phillips .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
692218cfe4eSBorislav Petkov },
693218cfe4eSBorislav Petkov .msr = MSR_AMD64_IBSOPCTL,
694218cfe4eSBorislav Petkov .config_mask = IBS_OP_CONFIG_MASK,
695e431e79bSKim Phillips .cnt_mask = IBS_OP_MAX_CNT | IBS_OP_CUR_CNT |
696e431e79bSKim Phillips IBS_OP_CUR_CNT_RAND,
697218cfe4eSBorislav Petkov .enable_mask = IBS_OP_ENABLE,
698218cfe4eSBorislav Petkov .valid_mask = IBS_OP_VAL,
699218cfe4eSBorislav Petkov .max_period = IBS_OP_MAX_CNT << 4,
700218cfe4eSBorislav Petkov .offset_mask = { MSR_AMD64_IBSOP_REG_MASK },
701218cfe4eSBorislav Petkov .offset_max = MSR_AMD64_IBSOP_REG_COUNT,
702218cfe4eSBorislav Petkov
703218cfe4eSBorislav Petkov .get_count = get_ibs_op_count,
704218cfe4eSBorislav Petkov };
705218cfe4eSBorislav Petkov
perf_ibs_get_mem_op(union ibs_op_data3 * op_data3,struct perf_sample_data * data)7067c10dd0aSRavi Bangoria static void perf_ibs_get_mem_op(union ibs_op_data3 *op_data3,
7077c10dd0aSRavi Bangoria struct perf_sample_data *data)
7087c10dd0aSRavi Bangoria {
7097c10dd0aSRavi Bangoria union perf_mem_data_src *data_src = &data->data_src;
7107c10dd0aSRavi Bangoria
7117c10dd0aSRavi Bangoria data_src->mem_op = PERF_MEM_OP_NA;
7127c10dd0aSRavi Bangoria
7137c10dd0aSRavi Bangoria if (op_data3->ld_op)
7147c10dd0aSRavi Bangoria data_src->mem_op = PERF_MEM_OP_LOAD;
7157c10dd0aSRavi Bangoria else if (op_data3->st_op)
7167c10dd0aSRavi Bangoria data_src->mem_op = PERF_MEM_OP_STORE;
7177c10dd0aSRavi Bangoria }
7187c10dd0aSRavi Bangoria
7197c10dd0aSRavi Bangoria /*
7207c10dd0aSRavi Bangoria * Processors having CPUID_Fn8000001B_EAX[11] aka IBS_CAPS_ZEN4 has
7217c10dd0aSRavi Bangoria * more fine granular DataSrc encodings. Others have coarse.
7227c10dd0aSRavi Bangoria */
perf_ibs_data_src(union ibs_op_data2 * op_data2)7237c10dd0aSRavi Bangoria static u8 perf_ibs_data_src(union ibs_op_data2 *op_data2)
7247c10dd0aSRavi Bangoria {
7257c10dd0aSRavi Bangoria if (ibs_caps & IBS_CAPS_ZEN4)
7267c10dd0aSRavi Bangoria return (op_data2->data_src_hi << 3) | op_data2->data_src_lo;
7277c10dd0aSRavi Bangoria
7287c10dd0aSRavi Bangoria return op_data2->data_src_lo;
7297c10dd0aSRavi Bangoria }
7307c10dd0aSRavi Bangoria
7318bfc20baSNamhyung Kim #define L(x) (PERF_MEM_S(LVL, x) | PERF_MEM_S(LVL, HIT))
7328bfc20baSNamhyung Kim #define LN(x) PERF_MEM_S(LVLNUM, x)
7338bfc20baSNamhyung Kim #define REM PERF_MEM_S(REMOTE, REMOTE)
7348bfc20baSNamhyung Kim #define HOPS(x) PERF_MEM_S(HOPS, x)
7358bfc20baSNamhyung Kim
7368bfc20baSNamhyung Kim static u64 g_data_src[8] = {
7378bfc20baSNamhyung Kim [IBS_DATA_SRC_LOC_CACHE] = L(L3) | L(REM_CCE1) | LN(ANY_CACHE) | HOPS(0),
7388bfc20baSNamhyung Kim [IBS_DATA_SRC_DRAM] = L(LOC_RAM) | LN(RAM),
7398bfc20baSNamhyung Kim [IBS_DATA_SRC_REM_CACHE] = L(REM_CCE2) | LN(ANY_CACHE) | REM | HOPS(1),
7408bfc20baSNamhyung Kim [IBS_DATA_SRC_IO] = L(IO) | LN(IO),
7418bfc20baSNamhyung Kim };
7428bfc20baSNamhyung Kim
7438bfc20baSNamhyung Kim #define RMT_NODE_BITS (1 << IBS_DATA_SRC_DRAM)
7448bfc20baSNamhyung Kim #define RMT_NODE_APPLICABLE(x) (RMT_NODE_BITS & (1 << x))
7458bfc20baSNamhyung Kim
7468bfc20baSNamhyung Kim static u64 g_zen4_data_src[32] = {
7478bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_LOC_CACHE] = L(L3) | LN(L3),
7488bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_NEAR_CCX_CACHE] = L(REM_CCE1) | LN(ANY_CACHE) | REM | HOPS(0),
7498bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_DRAM] = L(LOC_RAM) | LN(RAM),
7508bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_FAR_CCX_CACHE] = L(REM_CCE2) | LN(ANY_CACHE) | REM | HOPS(1),
7518bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_PMEM] = LN(PMEM),
7528bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_IO] = L(IO) | LN(IO),
7538bfc20baSNamhyung Kim [IBS_DATA_SRC_EXT_EXT_MEM] = LN(CXL),
7548bfc20baSNamhyung Kim };
7558bfc20baSNamhyung Kim
7568bfc20baSNamhyung Kim #define ZEN4_RMT_NODE_BITS ((1 << IBS_DATA_SRC_EXT_DRAM) | \
7578bfc20baSNamhyung Kim (1 << IBS_DATA_SRC_EXT_PMEM) | \
7588bfc20baSNamhyung Kim (1 << IBS_DATA_SRC_EXT_EXT_MEM))
7598bfc20baSNamhyung Kim #define ZEN4_RMT_NODE_APPLICABLE(x) (ZEN4_RMT_NODE_BITS & (1 << x))
7608bfc20baSNamhyung Kim
perf_ibs_get_mem_lvl(union ibs_op_data2 * op_data2,union ibs_op_data3 * op_data3,struct perf_sample_data * data)7618bfc20baSNamhyung Kim static __u64 perf_ibs_get_mem_lvl(union ibs_op_data2 *op_data2,
7627c10dd0aSRavi Bangoria union ibs_op_data3 *op_data3,
7637c10dd0aSRavi Bangoria struct perf_sample_data *data)
7647c10dd0aSRavi Bangoria {
7657c10dd0aSRavi Bangoria union perf_mem_data_src *data_src = &data->data_src;
7667c10dd0aSRavi Bangoria u8 ibs_data_src = perf_ibs_data_src(op_data2);
7677c10dd0aSRavi Bangoria
7687c10dd0aSRavi Bangoria data_src->mem_lvl = 0;
7698bfc20baSNamhyung Kim data_src->mem_lvl_num = 0;
7707c10dd0aSRavi Bangoria
7717c10dd0aSRavi Bangoria /*
7727c10dd0aSRavi Bangoria * DcMiss, L2Miss, DataSrc, DcMissLat etc. are all invalid for Uncached
7737c10dd0aSRavi Bangoria * memory accesses. So, check DcUcMemAcc bit early.
7747c10dd0aSRavi Bangoria */
7758bfc20baSNamhyung Kim if (op_data3->dc_uc_mem_acc && ibs_data_src != IBS_DATA_SRC_EXT_IO)
7768bfc20baSNamhyung Kim return L(UNC) | LN(UNC);
7777c10dd0aSRavi Bangoria
7787c10dd0aSRavi Bangoria /* L1 Hit */
7798bfc20baSNamhyung Kim if (op_data3->dc_miss == 0)
7808bfc20baSNamhyung Kim return L(L1) | LN(L1);
7817c10dd0aSRavi Bangoria
7827c10dd0aSRavi Bangoria /* L2 Hit */
7837c10dd0aSRavi Bangoria if (op_data3->l2_miss == 0) {
7847c10dd0aSRavi Bangoria /* Erratum #1293 */
7857c10dd0aSRavi Bangoria if (boot_cpu_data.x86 != 0x19 || boot_cpu_data.x86_model > 0xF ||
7868bfc20baSNamhyung Kim !(op_data3->sw_pf || op_data3->dc_miss_no_mab_alloc))
7878bfc20baSNamhyung Kim return L(L2) | LN(L2);
7887c10dd0aSRavi Bangoria }
7897c10dd0aSRavi Bangoria
7907c10dd0aSRavi Bangoria /*
7917c10dd0aSRavi Bangoria * OP_DATA2 is valid only for load ops. Skip all checks which
7927c10dd0aSRavi Bangoria * uses OP_DATA2[DataSrc].
7937c10dd0aSRavi Bangoria */
7947c10dd0aSRavi Bangoria if (data_src->mem_op != PERF_MEM_OP_LOAD)
7957c10dd0aSRavi Bangoria goto check_mab;
7967c10dd0aSRavi Bangoria
7977c10dd0aSRavi Bangoria if (ibs_caps & IBS_CAPS_ZEN4) {
7988bfc20baSNamhyung Kim u64 val = g_zen4_data_src[ibs_data_src];
7997c10dd0aSRavi Bangoria
8008bfc20baSNamhyung Kim if (!val)
8018bfc20baSNamhyung Kim goto check_mab;
8027c10dd0aSRavi Bangoria
8038bfc20baSNamhyung Kim /* HOPS_1 because IBS doesn't provide remote socket detail */
8048bfc20baSNamhyung Kim if (op_data2->rmt_node && ZEN4_RMT_NODE_APPLICABLE(ibs_data_src)) {
8058bfc20baSNamhyung Kim if (ibs_data_src == IBS_DATA_SRC_EXT_DRAM)
8068bfc20baSNamhyung Kim val = L(REM_RAM1) | LN(RAM) | REM | HOPS(1);
8077c10dd0aSRavi Bangoria else
8088bfc20baSNamhyung Kim val |= REM | HOPS(1);
8097c10dd0aSRavi Bangoria }
8107c10dd0aSRavi Bangoria
8118bfc20baSNamhyung Kim return val;
8128bfc20baSNamhyung Kim } else {
8138bfc20baSNamhyung Kim u64 val = g_data_src[ibs_data_src];
8148bfc20baSNamhyung Kim
8158bfc20baSNamhyung Kim if (!val)
8168bfc20baSNamhyung Kim goto check_mab;
8178bfc20baSNamhyung Kim
8188bfc20baSNamhyung Kim /* HOPS_1 because IBS doesn't provide remote socket detail */
8198bfc20baSNamhyung Kim if (op_data2->rmt_node && RMT_NODE_APPLICABLE(ibs_data_src)) {
8208bfc20baSNamhyung Kim if (ibs_data_src == IBS_DATA_SRC_DRAM)
8218bfc20baSNamhyung Kim val = L(REM_RAM1) | LN(RAM) | REM | HOPS(1);
8228bfc20baSNamhyung Kim else
8238bfc20baSNamhyung Kim val |= REM | HOPS(1);
8247c10dd0aSRavi Bangoria }
8257c10dd0aSRavi Bangoria
8268bfc20baSNamhyung Kim return val;
8277c10dd0aSRavi Bangoria }
8287c10dd0aSRavi Bangoria
8297c10dd0aSRavi Bangoria check_mab:
8307c10dd0aSRavi Bangoria /*
8317c10dd0aSRavi Bangoria * MAB (Miss Address Buffer) Hit. MAB keeps track of outstanding
8327c10dd0aSRavi Bangoria * DC misses. However, such data may come from any level in mem
8337c10dd0aSRavi Bangoria * hierarchy. IBS provides detail about both MAB as well as actual
8347c10dd0aSRavi Bangoria * DataSrc simultaneously. Prioritize DataSrc over MAB, i.e. set
8357c10dd0aSRavi Bangoria * MAB only when IBS fails to provide DataSrc.
8367c10dd0aSRavi Bangoria */
8378bfc20baSNamhyung Kim if (op_data3->dc_miss_no_mab_alloc)
8388bfc20baSNamhyung Kim return L(LFB) | LN(LFB);
8397c10dd0aSRavi Bangoria
8408bfc20baSNamhyung Kim /* Don't set HIT with NA */
8418bfc20baSNamhyung Kim return PERF_MEM_S(LVL, NA) | LN(NA);
8427c10dd0aSRavi Bangoria }
8437c10dd0aSRavi Bangoria
perf_ibs_cache_hit_st_valid(void)8447c10dd0aSRavi Bangoria static bool perf_ibs_cache_hit_st_valid(void)
8457c10dd0aSRavi Bangoria {
8467c10dd0aSRavi Bangoria /* 0: Uninitialized, 1: Valid, -1: Invalid */
8477c10dd0aSRavi Bangoria static int cache_hit_st_valid;
8487c10dd0aSRavi Bangoria
8497c10dd0aSRavi Bangoria if (unlikely(!cache_hit_st_valid)) {
8507c10dd0aSRavi Bangoria if (boot_cpu_data.x86 == 0x19 &&
8517c10dd0aSRavi Bangoria (boot_cpu_data.x86_model <= 0xF ||
8527c10dd0aSRavi Bangoria (boot_cpu_data.x86_model >= 0x20 &&
8537c10dd0aSRavi Bangoria boot_cpu_data.x86_model <= 0x5F))) {
8547c10dd0aSRavi Bangoria cache_hit_st_valid = -1;
8557c10dd0aSRavi Bangoria } else {
8567c10dd0aSRavi Bangoria cache_hit_st_valid = 1;
8577c10dd0aSRavi Bangoria }
8587c10dd0aSRavi Bangoria }
8597c10dd0aSRavi Bangoria
8607c10dd0aSRavi Bangoria return cache_hit_st_valid == 1;
8617c10dd0aSRavi Bangoria }
8627c10dd0aSRavi Bangoria
perf_ibs_get_mem_snoop(union ibs_op_data2 * op_data2,struct perf_sample_data * data)8637c10dd0aSRavi Bangoria static void perf_ibs_get_mem_snoop(union ibs_op_data2 *op_data2,
8647c10dd0aSRavi Bangoria struct perf_sample_data *data)
8657c10dd0aSRavi Bangoria {
8667c10dd0aSRavi Bangoria union perf_mem_data_src *data_src = &data->data_src;
8677c10dd0aSRavi Bangoria u8 ibs_data_src;
8687c10dd0aSRavi Bangoria
8697c10dd0aSRavi Bangoria data_src->mem_snoop = PERF_MEM_SNOOP_NA;
8707c10dd0aSRavi Bangoria
8717c10dd0aSRavi Bangoria if (!perf_ibs_cache_hit_st_valid() ||
8727c10dd0aSRavi Bangoria data_src->mem_op != PERF_MEM_OP_LOAD ||
8737c10dd0aSRavi Bangoria data_src->mem_lvl & PERF_MEM_LVL_L1 ||
8747c10dd0aSRavi Bangoria data_src->mem_lvl & PERF_MEM_LVL_L2 ||
8757c10dd0aSRavi Bangoria op_data2->cache_hit_st)
8767c10dd0aSRavi Bangoria return;
8777c10dd0aSRavi Bangoria
8787c10dd0aSRavi Bangoria ibs_data_src = perf_ibs_data_src(op_data2);
8797c10dd0aSRavi Bangoria
8807c10dd0aSRavi Bangoria if (ibs_caps & IBS_CAPS_ZEN4) {
8817c10dd0aSRavi Bangoria if (ibs_data_src == IBS_DATA_SRC_EXT_LOC_CACHE ||
8827c10dd0aSRavi Bangoria ibs_data_src == IBS_DATA_SRC_EXT_NEAR_CCX_CACHE ||
8837c10dd0aSRavi Bangoria ibs_data_src == IBS_DATA_SRC_EXT_FAR_CCX_CACHE)
8847c10dd0aSRavi Bangoria data_src->mem_snoop = PERF_MEM_SNOOP_HITM;
8857c10dd0aSRavi Bangoria } else if (ibs_data_src == IBS_DATA_SRC_LOC_CACHE) {
8867c10dd0aSRavi Bangoria data_src->mem_snoop = PERF_MEM_SNOOP_HITM;
8877c10dd0aSRavi Bangoria }
8887c10dd0aSRavi Bangoria }
8897c10dd0aSRavi Bangoria
perf_ibs_get_tlb_lvl(union ibs_op_data3 * op_data3,struct perf_sample_data * data)8907c10dd0aSRavi Bangoria static void perf_ibs_get_tlb_lvl(union ibs_op_data3 *op_data3,
8917c10dd0aSRavi Bangoria struct perf_sample_data *data)
8927c10dd0aSRavi Bangoria {
8937c10dd0aSRavi Bangoria union perf_mem_data_src *data_src = &data->data_src;
8947c10dd0aSRavi Bangoria
8957c10dd0aSRavi Bangoria data_src->mem_dtlb = PERF_MEM_TLB_NA;
8967c10dd0aSRavi Bangoria
8977c10dd0aSRavi Bangoria if (!op_data3->dc_lin_addr_valid)
8987c10dd0aSRavi Bangoria return;
8997c10dd0aSRavi Bangoria
9007c10dd0aSRavi Bangoria if (!op_data3->dc_l1tlb_miss) {
9017c10dd0aSRavi Bangoria data_src->mem_dtlb = PERF_MEM_TLB_L1 | PERF_MEM_TLB_HIT;
9027c10dd0aSRavi Bangoria return;
9037c10dd0aSRavi Bangoria }
9047c10dd0aSRavi Bangoria
9057c10dd0aSRavi Bangoria if (!op_data3->dc_l2tlb_miss) {
9067c10dd0aSRavi Bangoria data_src->mem_dtlb = PERF_MEM_TLB_L2 | PERF_MEM_TLB_HIT;
9077c10dd0aSRavi Bangoria return;
9087c10dd0aSRavi Bangoria }
9097c10dd0aSRavi Bangoria
9107c10dd0aSRavi Bangoria data_src->mem_dtlb = PERF_MEM_TLB_L2 | PERF_MEM_TLB_MISS;
9117c10dd0aSRavi Bangoria }
9127c10dd0aSRavi Bangoria
perf_ibs_get_mem_lock(union ibs_op_data3 * op_data3,struct perf_sample_data * data)9137c10dd0aSRavi Bangoria static void perf_ibs_get_mem_lock(union ibs_op_data3 *op_data3,
9147c10dd0aSRavi Bangoria struct perf_sample_data *data)
9157c10dd0aSRavi Bangoria {
9167c10dd0aSRavi Bangoria union perf_mem_data_src *data_src = &data->data_src;
9177c10dd0aSRavi Bangoria
9187c10dd0aSRavi Bangoria data_src->mem_lock = PERF_MEM_LOCK_NA;
9197c10dd0aSRavi Bangoria
9207c10dd0aSRavi Bangoria if (op_data3->dc_locked_op)
9217c10dd0aSRavi Bangoria data_src->mem_lock = PERF_MEM_LOCK_LOCKED;
9227c10dd0aSRavi Bangoria }
9237c10dd0aSRavi Bangoria
9247c10dd0aSRavi Bangoria #define ibs_op_msr_idx(msr) (msr - MSR_AMD64_IBSOPCTL)
9257c10dd0aSRavi Bangoria
perf_ibs_get_data_src(struct perf_ibs_data * ibs_data,struct perf_sample_data * data,union ibs_op_data2 * op_data2,union ibs_op_data3 * op_data3)9267c10dd0aSRavi Bangoria static void perf_ibs_get_data_src(struct perf_ibs_data *ibs_data,
9277c10dd0aSRavi Bangoria struct perf_sample_data *data,
9287c10dd0aSRavi Bangoria union ibs_op_data2 *op_data2,
9297c10dd0aSRavi Bangoria union ibs_op_data3 *op_data3)
9307c10dd0aSRavi Bangoria {
9318bfc20baSNamhyung Kim union perf_mem_data_src *data_src = &data->data_src;
9328bfc20baSNamhyung Kim
9338bfc20baSNamhyung Kim data_src->val |= perf_ibs_get_mem_lvl(op_data2, op_data3, data);
9347c10dd0aSRavi Bangoria perf_ibs_get_mem_snoop(op_data2, data);
9357c10dd0aSRavi Bangoria perf_ibs_get_tlb_lvl(op_data3, data);
9367c10dd0aSRavi Bangoria perf_ibs_get_mem_lock(op_data3, data);
9377c10dd0aSRavi Bangoria }
9387c10dd0aSRavi Bangoria
perf_ibs_get_op_data2(struct perf_ibs_data * ibs_data,union ibs_op_data3 * op_data3)9397c10dd0aSRavi Bangoria static __u64 perf_ibs_get_op_data2(struct perf_ibs_data *ibs_data,
9407c10dd0aSRavi Bangoria union ibs_op_data3 *op_data3)
9417c10dd0aSRavi Bangoria {
9427c10dd0aSRavi Bangoria __u64 val = ibs_data->regs[ibs_op_msr_idx(MSR_AMD64_IBSOPDATA2)];
9437c10dd0aSRavi Bangoria
9447c10dd0aSRavi Bangoria /* Erratum #1293 */
9457c10dd0aSRavi Bangoria if (boot_cpu_data.x86 == 0x19 && boot_cpu_data.x86_model <= 0xF &&
9467c10dd0aSRavi Bangoria (op_data3->sw_pf || op_data3->dc_miss_no_mab_alloc)) {
9477c10dd0aSRavi Bangoria /*
9487c10dd0aSRavi Bangoria * OP_DATA2 has only two fields on Zen3: DataSrc and RmtNode.
9497c10dd0aSRavi Bangoria * DataSrc=0 is 'No valid status' and RmtNode is invalid when
9507c10dd0aSRavi Bangoria * DataSrc=0.
9517c10dd0aSRavi Bangoria */
9527c10dd0aSRavi Bangoria val = 0;
9537c10dd0aSRavi Bangoria }
9547c10dd0aSRavi Bangoria return val;
9557c10dd0aSRavi Bangoria }
9567c10dd0aSRavi Bangoria
perf_ibs_parse_ld_st_data(__u64 sample_type,struct perf_ibs_data * ibs_data,struct perf_sample_data * data)9577c10dd0aSRavi Bangoria static void perf_ibs_parse_ld_st_data(__u64 sample_type,
9587c10dd0aSRavi Bangoria struct perf_ibs_data *ibs_data,
9597c10dd0aSRavi Bangoria struct perf_sample_data *data)
9607c10dd0aSRavi Bangoria {
9617c10dd0aSRavi Bangoria union ibs_op_data3 op_data3;
9627c10dd0aSRavi Bangoria union ibs_op_data2 op_data2;
9636b2ae495SRavi Bangoria union ibs_op_data op_data;
9647c10dd0aSRavi Bangoria
9657c10dd0aSRavi Bangoria data->data_src.val = PERF_MEM_NA;
9667c10dd0aSRavi Bangoria op_data3.val = ibs_data->regs[ibs_op_msr_idx(MSR_AMD64_IBSOPDATA3)];
9677c10dd0aSRavi Bangoria
9687c10dd0aSRavi Bangoria perf_ibs_get_mem_op(&op_data3, data);
9697c10dd0aSRavi Bangoria if (data->data_src.mem_op != PERF_MEM_OP_LOAD &&
9707c10dd0aSRavi Bangoria data->data_src.mem_op != PERF_MEM_OP_STORE)
9717c10dd0aSRavi Bangoria return;
9727c10dd0aSRavi Bangoria
9737c10dd0aSRavi Bangoria op_data2.val = perf_ibs_get_op_data2(ibs_data, &op_data3);
9747c10dd0aSRavi Bangoria
9757c10dd0aSRavi Bangoria if (sample_type & PERF_SAMPLE_DATA_SRC) {
9767c10dd0aSRavi Bangoria perf_ibs_get_data_src(ibs_data, data, &op_data2, &op_data3);
9777c10dd0aSRavi Bangoria data->sample_flags |= PERF_SAMPLE_DATA_SRC;
9787c10dd0aSRavi Bangoria }
9796b2ae495SRavi Bangoria
9806b2ae495SRavi Bangoria if (sample_type & PERF_SAMPLE_WEIGHT_TYPE && op_data3.dc_miss &&
9816b2ae495SRavi Bangoria data->data_src.mem_op == PERF_MEM_OP_LOAD) {
9826b2ae495SRavi Bangoria op_data.val = ibs_data->regs[ibs_op_msr_idx(MSR_AMD64_IBSOPDATA)];
9836b2ae495SRavi Bangoria
9846b2ae495SRavi Bangoria if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) {
9856b2ae495SRavi Bangoria data->weight.var1_dw = op_data3.dc_miss_lat;
9866b2ae495SRavi Bangoria data->weight.var2_w = op_data.tag_to_ret_ctr;
9876b2ae495SRavi Bangoria } else if (sample_type & PERF_SAMPLE_WEIGHT) {
9886b2ae495SRavi Bangoria data->weight.full = op_data3.dc_miss_lat;
9896b2ae495SRavi Bangoria }
9906b2ae495SRavi Bangoria data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE;
9916b2ae495SRavi Bangoria }
992cb2bb85fSRavi Bangoria
993cb2bb85fSRavi Bangoria if (sample_type & PERF_SAMPLE_ADDR && op_data3.dc_lin_addr_valid) {
994cb2bb85fSRavi Bangoria data->addr = ibs_data->regs[ibs_op_msr_idx(MSR_AMD64_IBSDCLINAD)];
995cb2bb85fSRavi Bangoria data->sample_flags |= PERF_SAMPLE_ADDR;
996cb2bb85fSRavi Bangoria }
9975b26af6dSRavi Bangoria
9985b26af6dSRavi Bangoria if (sample_type & PERF_SAMPLE_PHYS_ADDR && op_data3.dc_phy_addr_valid) {
9995b26af6dSRavi Bangoria data->phys_addr = ibs_data->regs[ibs_op_msr_idx(MSR_AMD64_IBSDCPHYSAD)];
10005b26af6dSRavi Bangoria data->sample_flags |= PERF_SAMPLE_PHYS_ADDR;
10015b26af6dSRavi Bangoria }
10027c10dd0aSRavi Bangoria }
10037c10dd0aSRavi Bangoria
perf_ibs_get_offset_max(struct perf_ibs * perf_ibs,u64 sample_type,int check_rip)10047c10dd0aSRavi Bangoria static int perf_ibs_get_offset_max(struct perf_ibs *perf_ibs, u64 sample_type,
10057c10dd0aSRavi Bangoria int check_rip)
10067c10dd0aSRavi Bangoria {
10077c10dd0aSRavi Bangoria if (sample_type & PERF_SAMPLE_RAW ||
10087c10dd0aSRavi Bangoria (perf_ibs == &perf_ibs_op &&
10096b2ae495SRavi Bangoria (sample_type & PERF_SAMPLE_DATA_SRC ||
1010cb2bb85fSRavi Bangoria sample_type & PERF_SAMPLE_WEIGHT_TYPE ||
10115b26af6dSRavi Bangoria sample_type & PERF_SAMPLE_ADDR ||
10125b26af6dSRavi Bangoria sample_type & PERF_SAMPLE_PHYS_ADDR)))
10137c10dd0aSRavi Bangoria return perf_ibs->offset_max;
10147c10dd0aSRavi Bangoria else if (check_rip)
10157c10dd0aSRavi Bangoria return 3;
10167c10dd0aSRavi Bangoria return 1;
10177c10dd0aSRavi Bangoria }
10187c10dd0aSRavi Bangoria
perf_ibs_handle_irq(struct perf_ibs * perf_ibs,struct pt_regs * iregs)1019218cfe4eSBorislav Petkov static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
1020218cfe4eSBorislav Petkov {
1021218cfe4eSBorislav Petkov struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
1022218cfe4eSBorislav Petkov struct perf_event *event = pcpu->event;
1023d2753e6bSThomas Gleixner struct hw_perf_event *hwc;
1024218cfe4eSBorislav Petkov struct perf_sample_data data;
1025218cfe4eSBorislav Petkov struct perf_raw_record raw;
1026218cfe4eSBorislav Petkov struct pt_regs regs;
1027218cfe4eSBorislav Petkov struct perf_ibs_data ibs_data;
1028218cfe4eSBorislav Petkov int offset, size, check_rip, offset_max, throttle = 0;
1029218cfe4eSBorislav Petkov unsigned int msr;
10308b0bed7dSKim Phillips u64 *buf, *config, period, new_config = 0;
1031218cfe4eSBorislav Petkov
1032218cfe4eSBorislav Petkov if (!test_bit(IBS_STARTED, pcpu->state)) {
10335a50f529SPeter Zijlstra fail:
1034218cfe4eSBorislav Petkov /*
1035218cfe4eSBorislav Petkov * Catch spurious interrupts after stopping IBS: After
1036218cfe4eSBorislav Petkov * disabling IBS there could be still incoming NMIs
1037218cfe4eSBorislav Petkov * with samples that even have the valid bit cleared.
1038218cfe4eSBorislav Petkov * Mark all this NMIs as handled.
1039218cfe4eSBorislav Petkov */
104085dc6002SPeter Zijlstra if (test_and_clear_bit(IBS_STOPPED, pcpu->state))
10415a50f529SPeter Zijlstra return 1;
10425a50f529SPeter Zijlstra
10435a50f529SPeter Zijlstra return 0;
1044218cfe4eSBorislav Petkov }
1045218cfe4eSBorislav Petkov
1046d2753e6bSThomas Gleixner if (WARN_ON_ONCE(!event))
1047d2753e6bSThomas Gleixner goto fail;
1048d2753e6bSThomas Gleixner
1049d2753e6bSThomas Gleixner hwc = &event->hw;
1050218cfe4eSBorislav Petkov msr = hwc->config_base;
1051218cfe4eSBorislav Petkov buf = ibs_data.regs;
1052218cfe4eSBorislav Petkov rdmsrl(msr, *buf);
1053218cfe4eSBorislav Petkov if (!(*buf++ & perf_ibs->valid_mask))
10545a50f529SPeter Zijlstra goto fail;
1055218cfe4eSBorislav Petkov
1056218cfe4eSBorislav Petkov config = &ibs_data.regs[0];
1057218cfe4eSBorislav Petkov perf_ibs_event_update(perf_ibs, event, config);
1058218cfe4eSBorislav Petkov perf_sample_data_init(&data, 0, hwc->last_period);
1059218cfe4eSBorislav Petkov if (!perf_ibs_set_period(perf_ibs, hwc, &period))
1060218cfe4eSBorislav Petkov goto out; /* no sw counter overflow */
1061218cfe4eSBorislav Petkov
1062218cfe4eSBorislav Petkov ibs_data.caps = ibs_caps;
1063218cfe4eSBorislav Petkov size = 1;
1064218cfe4eSBorislav Petkov offset = 1;
1065218cfe4eSBorislav Petkov check_rip = (perf_ibs == &perf_ibs_op && (ibs_caps & IBS_CAPS_RIPINVALIDCHK));
10667c10dd0aSRavi Bangoria
10677c10dd0aSRavi Bangoria offset_max = perf_ibs_get_offset_max(perf_ibs, event->attr.sample_type, check_rip);
10687c10dd0aSRavi Bangoria
1069218cfe4eSBorislav Petkov do {
1070218cfe4eSBorislav Petkov rdmsrl(msr + offset, *buf++);
1071218cfe4eSBorislav Petkov size++;
1072218cfe4eSBorislav Petkov offset = find_next_bit(perf_ibs->offset_mask,
1073218cfe4eSBorislav Petkov perf_ibs->offset_max,
1074218cfe4eSBorislav Petkov offset + 1);
1075218cfe4eSBorislav Petkov } while (offset < offset_max);
1076218cfe4eSBorislav Petkov /*
107736e1be8aSKim Phillips * Read IbsBrTarget, IbsOpData4, and IbsExtdCtl separately
1078218cfe4eSBorislav Petkov * depending on their availability.
1079218cfe4eSBorislav Petkov * Can't add to offset_max as they are staggered
1080218cfe4eSBorislav Petkov */
108136e1be8aSKim Phillips if (event->attr.sample_type & PERF_SAMPLE_RAW) {
108236e1be8aSKim Phillips if (perf_ibs == &perf_ibs_op) {
1083218cfe4eSBorislav Petkov if (ibs_caps & IBS_CAPS_BRNTRGT) {
1084218cfe4eSBorislav Petkov rdmsrl(MSR_AMD64_IBSBRTARGET, *buf++);
1085218cfe4eSBorislav Petkov size++;
1086218cfe4eSBorislav Petkov }
1087218cfe4eSBorislav Petkov if (ibs_caps & IBS_CAPS_OPDATA4) {
1088218cfe4eSBorislav Petkov rdmsrl(MSR_AMD64_IBSOPDATA4, *buf++);
1089218cfe4eSBorislav Petkov size++;
1090218cfe4eSBorislav Petkov }
1091218cfe4eSBorislav Petkov }
109236e1be8aSKim Phillips if (perf_ibs == &perf_ibs_fetch && (ibs_caps & IBS_CAPS_FETCHCTLEXTD)) {
109336e1be8aSKim Phillips rdmsrl(MSR_AMD64_ICIBSEXTDCTL, *buf++);
109436e1be8aSKim Phillips size++;
109536e1be8aSKim Phillips }
109636e1be8aSKim Phillips }
1097218cfe4eSBorislav Petkov ibs_data.size = sizeof(u64) * size;
1098218cfe4eSBorislav Petkov
1099218cfe4eSBorislav Petkov regs = *iregs;
1100218cfe4eSBorislav Petkov if (check_rip && (ibs_data.regs[2] & IBS_RIP_INVALID)) {
1101218cfe4eSBorislav Petkov regs.flags &= ~PERF_EFLAGS_EXACT;
1102218cfe4eSBorislav Petkov } else {
110326db2e0cSKim Phillips /* Workaround for erratum #1197 */
110426db2e0cSKim Phillips if (perf_ibs->fetch_ignore_if_zero_rip && !(ibs_data.regs[1]))
110526db2e0cSKim Phillips goto out;
110626db2e0cSKim Phillips
1107218cfe4eSBorislav Petkov set_linear_ip(®s, ibs_data.regs[1]);
1108218cfe4eSBorislav Petkov regs.flags |= PERF_EFLAGS_EXACT;
1109218cfe4eSBorislav Petkov }
1110218cfe4eSBorislav Petkov
1111218cfe4eSBorislav Petkov if (event->attr.sample_type & PERF_SAMPLE_RAW) {
11127e3f977eSDaniel Borkmann raw = (struct perf_raw_record){
11137e3f977eSDaniel Borkmann .frag = {
11147e3f977eSDaniel Borkmann .size = sizeof(u32) + ibs_data.size,
11157e3f977eSDaniel Borkmann .data = ibs_data.data,
11167e3f977eSDaniel Borkmann },
11177e3f977eSDaniel Borkmann };
1118*c0dbecb2SYabin Cui perf_sample_save_raw_data(&data, event, &raw);
1119218cfe4eSBorislav Petkov }
1120218cfe4eSBorislav Petkov
11217c10dd0aSRavi Bangoria if (perf_ibs == &perf_ibs_op)
11227c10dd0aSRavi Bangoria perf_ibs_parse_ld_st_data(event->attr.sample_type, &ibs_data, &data);
11237c10dd0aSRavi Bangoria
11243d47083bSRavi Bangoria /*
11253d47083bSRavi Bangoria * rip recorded by IbsOpRip will not be consistent with rsp and rbp
11263d47083bSRavi Bangoria * recorded as part of interrupt regs. Thus we need to use rip from
11273d47083bSRavi Bangoria * interrupt regs while unwinding call stack.
11283d47083bSRavi Bangoria */
112931046500SNamhyung Kim if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN)
113031046500SNamhyung Kim perf_sample_save_callchain(&data, event, iregs);
11313d47083bSRavi Bangoria
1132218cfe4eSBorislav Petkov throttle = perf_event_overflow(event, &data, ®s);
1133218cfe4eSBorislav Petkov out:
11340f4cd769SKim Phillips if (throttle) {
11350158b83fSPeter Zijlstra perf_ibs_stop(event, 0);
11360f4cd769SKim Phillips } else {
11378b0bed7dSKim Phillips if (perf_ibs == &perf_ibs_op) {
11388b0bed7dSKim Phillips if (ibs_caps & IBS_CAPS_OPCNTEXT) {
11398b0bed7dSKim Phillips new_config = period & IBS_OP_MAX_CNT_EXT_MASK;
11408b0bed7dSKim Phillips period &= ~IBS_OP_MAX_CNT_EXT_MASK;
11418b0bed7dSKim Phillips }
11428b0bed7dSKim Phillips if ((ibs_caps & IBS_CAPS_RDWROPCNT) && (*config & IBS_OP_CNT_CTL))
11438b0bed7dSKim Phillips new_config |= *config & IBS_OP_CUR_CNT_RAND;
11448b0bed7dSKim Phillips }
11458b0bed7dSKim Phillips new_config |= period >> 4;
11460f4cd769SKim Phillips
11478b0bed7dSKim Phillips perf_ibs_enable_event(perf_ibs, hwc, new_config);
11480f4cd769SKim Phillips }
1149218cfe4eSBorislav Petkov
1150218cfe4eSBorislav Petkov perf_event_update_userpage(event);
1151218cfe4eSBorislav Petkov
1152218cfe4eSBorislav Petkov return 1;
1153218cfe4eSBorislav Petkov }
1154218cfe4eSBorislav Petkov
1155218cfe4eSBorislav Petkov static int
perf_ibs_nmi_handler(unsigned int cmd,struct pt_regs * regs)1156218cfe4eSBorislav Petkov perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
1157218cfe4eSBorislav Petkov {
1158c2872d38SPeter Zijlstra u64 stamp = sched_clock();
1159218cfe4eSBorislav Petkov int handled = 0;
1160218cfe4eSBorislav Petkov
1161218cfe4eSBorislav Petkov handled += perf_ibs_handle_irq(&perf_ibs_fetch, regs);
1162218cfe4eSBorislav Petkov handled += perf_ibs_handle_irq(&perf_ibs_op, regs);
1163218cfe4eSBorislav Petkov
1164218cfe4eSBorislav Petkov if (handled)
1165218cfe4eSBorislav Petkov inc_irq_stat(apic_perf_irqs);
1166218cfe4eSBorislav Petkov
1167c2872d38SPeter Zijlstra perf_sample_event_took(sched_clock() - stamp);
1168c2872d38SPeter Zijlstra
1169218cfe4eSBorislav Petkov return handled;
1170218cfe4eSBorislav Petkov }
1171218cfe4eSBorislav Petkov NOKPROBE_SYMBOL(perf_ibs_nmi_handler);
1172218cfe4eSBorislav Petkov
perf_ibs_pmu_init(struct perf_ibs * perf_ibs,char * name)1173218cfe4eSBorislav Petkov static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name)
1174218cfe4eSBorislav Petkov {
1175218cfe4eSBorislav Petkov struct cpu_perf_ibs __percpu *pcpu;
1176218cfe4eSBorislav Petkov int ret;
1177218cfe4eSBorislav Petkov
1178218cfe4eSBorislav Petkov pcpu = alloc_percpu(struct cpu_perf_ibs);
1179218cfe4eSBorislav Petkov if (!pcpu)
1180218cfe4eSBorislav Petkov return -ENOMEM;
1181218cfe4eSBorislav Petkov
1182218cfe4eSBorislav Petkov perf_ibs->pcpu = pcpu;
1183218cfe4eSBorislav Petkov
1184218cfe4eSBorislav Petkov ret = perf_pmu_register(&perf_ibs->pmu, name, -1);
1185218cfe4eSBorislav Petkov if (ret) {
1186218cfe4eSBorislav Petkov perf_ibs->pcpu = NULL;
1187218cfe4eSBorislav Petkov free_percpu(pcpu);
1188218cfe4eSBorislav Petkov }
1189218cfe4eSBorislav Petkov
1190218cfe4eSBorislav Petkov return ret;
1191218cfe4eSBorislav Petkov }
1192218cfe4eSBorislav Petkov
perf_ibs_fetch_init(void)1193ba5d35b4SRavi Bangoria static __init int perf_ibs_fetch_init(void)
1194218cfe4eSBorislav Petkov {
1195221bfce5SKim Phillips /*
1196221bfce5SKim Phillips * Some chips fail to reset the fetch count when it is written; instead
1197221bfce5SKim Phillips * they need a 0-1 transition of IbsFetchEn.
1198221bfce5SKim Phillips */
1199221bfce5SKim Phillips if (boot_cpu_data.x86 >= 0x16 && boot_cpu_data.x86 <= 0x18)
1200221bfce5SKim Phillips perf_ibs_fetch.fetch_count_reset_broken = 1;
1201221bfce5SKim Phillips
120226db2e0cSKim Phillips if (boot_cpu_data.x86 == 0x19 && boot_cpu_data.x86_model < 0x10)
120326db2e0cSKim Phillips perf_ibs_fetch.fetch_ignore_if_zero_rip = 1;
120426db2e0cSKim Phillips
1205ba5d35b4SRavi Bangoria if (ibs_caps & IBS_CAPS_ZEN4)
1206ba5d35b4SRavi Bangoria perf_ibs_fetch.config_mask |= IBS_FETCH_L3MISSONLY;
1207ba5d35b4SRavi Bangoria
12082a7a7e65SRavi Bangoria perf_ibs_fetch.pmu.attr_groups = fetch_attr_groups;
1209ba5d35b4SRavi Bangoria perf_ibs_fetch.pmu.attr_update = fetch_attr_update;
12102a7a7e65SRavi Bangoria
1211ba5d35b4SRavi Bangoria return perf_ibs_pmu_init(&perf_ibs_fetch, "ibs_fetch");
1212ba5d35b4SRavi Bangoria }
1213218cfe4eSBorislav Petkov
perf_ibs_op_init(void)1214ba5d35b4SRavi Bangoria static __init int perf_ibs_op_init(void)
1215ba5d35b4SRavi Bangoria {
12162a7a7e65SRavi Bangoria if (ibs_caps & IBS_CAPS_OPCNT)
1217218cfe4eSBorislav Petkov perf_ibs_op.config_mask |= IBS_OP_CNT_CTL;
12188b0bed7dSKim Phillips
12198b0bed7dSKim Phillips if (ibs_caps & IBS_CAPS_OPCNTEXT) {
12208b0bed7dSKim Phillips perf_ibs_op.max_period |= IBS_OP_MAX_CNT_EXT_MASK;
12218b0bed7dSKim Phillips perf_ibs_op.config_mask |= IBS_OP_MAX_CNT_EXT_MASK;
12228b0bed7dSKim Phillips perf_ibs_op.cnt_mask |= IBS_OP_MAX_CNT_EXT_MASK;
12238b0bed7dSKim Phillips }
12248b0bed7dSKim Phillips
1225ba5d35b4SRavi Bangoria if (ibs_caps & IBS_CAPS_ZEN4)
1226ba5d35b4SRavi Bangoria perf_ibs_op.config_mask |= IBS_OP_L3MISSONLY;
1227ba5d35b4SRavi Bangoria
12282a7a7e65SRavi Bangoria perf_ibs_op.pmu.attr_groups = empty_attr_groups;
12292a7a7e65SRavi Bangoria perf_ibs_op.pmu.attr_update = op_attr_update;
12302a7a7e65SRavi Bangoria
1231ba5d35b4SRavi Bangoria return perf_ibs_pmu_init(&perf_ibs_op, "ibs_op");
1232ba5d35b4SRavi Bangoria }
1233ba5d35b4SRavi Bangoria
perf_event_ibs_init(void)1234ba5d35b4SRavi Bangoria static __init int perf_event_ibs_init(void)
1235ba5d35b4SRavi Bangoria {
1236ba5d35b4SRavi Bangoria int ret;
1237ba5d35b4SRavi Bangoria
1238ba5d35b4SRavi Bangoria ret = perf_ibs_fetch_init();
1239ba5d35b4SRavi Bangoria if (ret)
1240ba5d35b4SRavi Bangoria return ret;
1241ba5d35b4SRavi Bangoria
1242ba5d35b4SRavi Bangoria ret = perf_ibs_op_init();
124339b2ca75SRavi Bangoria if (ret)
124439b2ca75SRavi Bangoria goto err_op;
1245218cfe4eSBorislav Petkov
124639b2ca75SRavi Bangoria ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs");
124739b2ca75SRavi Bangoria if (ret)
124839b2ca75SRavi Bangoria goto err_nmi;
124939b2ca75SRavi Bangoria
1250218cfe4eSBorislav Petkov pr_info("perf: AMD IBS detected (0x%08x)\n", ibs_caps);
125139b2ca75SRavi Bangoria return 0;
125239b2ca75SRavi Bangoria
125339b2ca75SRavi Bangoria err_nmi:
125439b2ca75SRavi Bangoria perf_pmu_unregister(&perf_ibs_op.pmu);
125539b2ca75SRavi Bangoria free_percpu(perf_ibs_op.pcpu);
125639b2ca75SRavi Bangoria perf_ibs_op.pcpu = NULL;
125739b2ca75SRavi Bangoria err_op:
125839b2ca75SRavi Bangoria perf_pmu_unregister(&perf_ibs_fetch.pmu);
125939b2ca75SRavi Bangoria free_percpu(perf_ibs_fetch.pcpu);
126039b2ca75SRavi Bangoria perf_ibs_fetch.pcpu = NULL;
126139b2ca75SRavi Bangoria
126239b2ca75SRavi Bangoria return ret;
1263218cfe4eSBorislav Petkov }
1264218cfe4eSBorislav Petkov
1265218cfe4eSBorislav Petkov #else /* defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) */
1266218cfe4eSBorislav Petkov
perf_event_ibs_init(void)126739b2ca75SRavi Bangoria static __init int perf_event_ibs_init(void)
126839b2ca75SRavi Bangoria {
126939b2ca75SRavi Bangoria return 0;
127039b2ca75SRavi Bangoria }
1271218cfe4eSBorislav Petkov
1272218cfe4eSBorislav Petkov #endif
1273218cfe4eSBorislav Petkov
1274218cfe4eSBorislav Petkov /* IBS - apic initialization, for perf and oprofile */
1275218cfe4eSBorislav Petkov
__get_ibs_caps(void)1276218cfe4eSBorislav Petkov static __init u32 __get_ibs_caps(void)
1277218cfe4eSBorislav Petkov {
1278218cfe4eSBorislav Petkov u32 caps;
1279218cfe4eSBorislav Petkov unsigned int max_level;
1280218cfe4eSBorislav Petkov
1281218cfe4eSBorislav Petkov if (!boot_cpu_has(X86_FEATURE_IBS))
1282218cfe4eSBorislav Petkov return 0;
1283218cfe4eSBorislav Petkov
1284218cfe4eSBorislav Petkov /* check IBS cpuid feature flags */
1285218cfe4eSBorislav Petkov max_level = cpuid_eax(0x80000000);
1286218cfe4eSBorislav Petkov if (max_level < IBS_CPUID_FEATURES)
1287218cfe4eSBorislav Petkov return IBS_CAPS_DEFAULT;
1288218cfe4eSBorislav Petkov
1289218cfe4eSBorislav Petkov caps = cpuid_eax(IBS_CPUID_FEATURES);
1290218cfe4eSBorislav Petkov if (!(caps & IBS_CAPS_AVAIL))
1291218cfe4eSBorislav Petkov /* cpuid flags not valid */
1292218cfe4eSBorislav Petkov return IBS_CAPS_DEFAULT;
1293218cfe4eSBorislav Petkov
1294218cfe4eSBorislav Petkov return caps;
1295218cfe4eSBorislav Petkov }
1296218cfe4eSBorislav Petkov
get_ibs_caps(void)1297218cfe4eSBorislav Petkov u32 get_ibs_caps(void)
1298218cfe4eSBorislav Petkov {
1299218cfe4eSBorislav Petkov return ibs_caps;
1300218cfe4eSBorislav Petkov }
1301218cfe4eSBorislav Petkov
1302218cfe4eSBorislav Petkov EXPORT_SYMBOL(get_ibs_caps);
1303218cfe4eSBorislav Petkov
get_eilvt(int offset)1304218cfe4eSBorislav Petkov static inline int get_eilvt(int offset)
1305218cfe4eSBorislav Petkov {
1306218cfe4eSBorislav Petkov return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
1307218cfe4eSBorislav Petkov }
1308218cfe4eSBorislav Petkov
put_eilvt(int offset)1309218cfe4eSBorislav Petkov static inline int put_eilvt(int offset)
1310218cfe4eSBorislav Petkov {
1311218cfe4eSBorislav Petkov return !setup_APIC_eilvt(offset, 0, 0, 1);
1312218cfe4eSBorislav Petkov }
1313218cfe4eSBorislav Petkov
1314218cfe4eSBorislav Petkov /*
1315218cfe4eSBorislav Petkov * Check and reserve APIC extended interrupt LVT offset for IBS if available.
1316218cfe4eSBorislav Petkov */
ibs_eilvt_valid(void)1317218cfe4eSBorislav Petkov static inline int ibs_eilvt_valid(void)
1318218cfe4eSBorislav Petkov {
1319218cfe4eSBorislav Petkov int offset;
1320218cfe4eSBorislav Petkov u64 val;
1321218cfe4eSBorislav Petkov int valid = 0;
1322218cfe4eSBorislav Petkov
1323218cfe4eSBorislav Petkov preempt_disable();
1324218cfe4eSBorislav Petkov
1325218cfe4eSBorislav Petkov rdmsrl(MSR_AMD64_IBSCTL, val);
1326218cfe4eSBorislav Petkov offset = val & IBSCTL_LVT_OFFSET_MASK;
1327218cfe4eSBorislav Petkov
1328218cfe4eSBorislav Petkov if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
1329218cfe4eSBorislav Petkov pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n",
1330218cfe4eSBorislav Petkov smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
1331218cfe4eSBorislav Petkov goto out;
1332218cfe4eSBorislav Petkov }
1333218cfe4eSBorislav Petkov
1334218cfe4eSBorislav Petkov if (!get_eilvt(offset)) {
1335218cfe4eSBorislav Petkov pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n",
1336218cfe4eSBorislav Petkov smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
1337218cfe4eSBorislav Petkov goto out;
1338218cfe4eSBorislav Petkov }
1339218cfe4eSBorislav Petkov
1340218cfe4eSBorislav Petkov valid = 1;
1341218cfe4eSBorislav Petkov out:
1342218cfe4eSBorislav Petkov preempt_enable();
1343218cfe4eSBorislav Petkov
1344218cfe4eSBorislav Petkov return valid;
1345218cfe4eSBorislav Petkov }
1346218cfe4eSBorislav Petkov
setup_ibs_ctl(int ibs_eilvt_off)1347218cfe4eSBorislav Petkov static int setup_ibs_ctl(int ibs_eilvt_off)
1348218cfe4eSBorislav Petkov {
1349218cfe4eSBorislav Petkov struct pci_dev *cpu_cfg;
1350218cfe4eSBorislav Petkov int nodes;
1351218cfe4eSBorislav Petkov u32 value = 0;
1352218cfe4eSBorislav Petkov
1353218cfe4eSBorislav Petkov nodes = 0;
1354218cfe4eSBorislav Petkov cpu_cfg = NULL;
1355218cfe4eSBorislav Petkov do {
1356218cfe4eSBorislav Petkov cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD,
1357218cfe4eSBorislav Petkov PCI_DEVICE_ID_AMD_10H_NB_MISC,
1358218cfe4eSBorislav Petkov cpu_cfg);
1359218cfe4eSBorislav Petkov if (!cpu_cfg)
1360218cfe4eSBorislav Petkov break;
1361218cfe4eSBorislav Petkov ++nodes;
1362218cfe4eSBorislav Petkov pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
1363218cfe4eSBorislav Petkov | IBSCTL_LVT_OFFSET_VALID);
1364218cfe4eSBorislav Petkov pci_read_config_dword(cpu_cfg, IBSCTL, &value);
1365218cfe4eSBorislav Petkov if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) {
1366218cfe4eSBorislav Petkov pci_dev_put(cpu_cfg);
1367218cfe4eSBorislav Petkov pr_debug("Failed to setup IBS LVT offset, IBSCTL = 0x%08x\n",
1368218cfe4eSBorislav Petkov value);
1369218cfe4eSBorislav Petkov return -EINVAL;
1370218cfe4eSBorislav Petkov }
1371218cfe4eSBorislav Petkov } while (1);
1372218cfe4eSBorislav Petkov
1373218cfe4eSBorislav Petkov if (!nodes) {
1374218cfe4eSBorislav Petkov pr_debug("No CPU node configured for IBS\n");
1375218cfe4eSBorislav Petkov return -ENODEV;
1376218cfe4eSBorislav Petkov }
1377218cfe4eSBorislav Petkov
1378218cfe4eSBorislav Petkov return 0;
1379218cfe4eSBorislav Petkov }
1380218cfe4eSBorislav Petkov
1381218cfe4eSBorislav Petkov /*
1382218cfe4eSBorislav Petkov * This runs only on the current cpu. We try to find an LVT offset and
1383218cfe4eSBorislav Petkov * setup the local APIC. For this we must disable preemption. On
1384218cfe4eSBorislav Petkov * success we initialize all nodes with this offset. This updates then
1385218cfe4eSBorislav Petkov * the offset in the IBS_CTL per-node msr. The per-core APIC setup of
1386218cfe4eSBorislav Petkov * the IBS interrupt vector is handled by perf_ibs_cpu_notifier that
1387218cfe4eSBorislav Petkov * is using the new offset.
1388218cfe4eSBorislav Petkov */
force_ibs_eilvt_setup(void)1389218cfe4eSBorislav Petkov static void force_ibs_eilvt_setup(void)
1390218cfe4eSBorislav Petkov {
1391218cfe4eSBorislav Petkov int offset;
1392218cfe4eSBorislav Petkov int ret;
1393218cfe4eSBorislav Petkov
1394218cfe4eSBorislav Petkov preempt_disable();
1395218cfe4eSBorislav Petkov /* find the next free available EILVT entry, skip offset 0 */
1396218cfe4eSBorislav Petkov for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) {
1397218cfe4eSBorislav Petkov if (get_eilvt(offset))
1398218cfe4eSBorislav Petkov break;
1399218cfe4eSBorislav Petkov }
1400218cfe4eSBorislav Petkov preempt_enable();
1401218cfe4eSBorislav Petkov
1402218cfe4eSBorislav Petkov if (offset == APIC_EILVT_NR_MAX) {
1403218cfe4eSBorislav Petkov pr_debug("No EILVT entry available\n");
1404218cfe4eSBorislav Petkov return;
1405218cfe4eSBorislav Petkov }
1406218cfe4eSBorislav Petkov
1407218cfe4eSBorislav Petkov ret = setup_ibs_ctl(offset);
1408218cfe4eSBorislav Petkov if (ret)
1409218cfe4eSBorislav Petkov goto out;
1410218cfe4eSBorislav Petkov
1411218cfe4eSBorislav Petkov if (!ibs_eilvt_valid())
1412218cfe4eSBorislav Petkov goto out;
1413218cfe4eSBorislav Petkov
14141de392f5SJoe Perches pr_info("LVT offset %d assigned\n", offset);
1415218cfe4eSBorislav Petkov
1416218cfe4eSBorislav Petkov return;
1417218cfe4eSBorislav Petkov out:
1418218cfe4eSBorislav Petkov preempt_disable();
1419218cfe4eSBorislav Petkov put_eilvt(offset);
1420218cfe4eSBorislav Petkov preempt_enable();
1421218cfe4eSBorislav Petkov return;
1422218cfe4eSBorislav Petkov }
1423218cfe4eSBorislav Petkov
ibs_eilvt_setup(void)1424218cfe4eSBorislav Petkov static void ibs_eilvt_setup(void)
1425218cfe4eSBorislav Petkov {
1426218cfe4eSBorislav Petkov /*
1427218cfe4eSBorislav Petkov * Force LVT offset assignment for family 10h: The offsets are
1428218cfe4eSBorislav Petkov * not assigned by the BIOS for this family, so the OS is
1429218cfe4eSBorislav Petkov * responsible for doing it. If the OS assignment fails, fall
1430218cfe4eSBorislav Petkov * back to BIOS settings and try to setup this.
1431218cfe4eSBorislav Petkov */
1432218cfe4eSBorislav Petkov if (boot_cpu_data.x86 == 0x10)
1433218cfe4eSBorislav Petkov force_ibs_eilvt_setup();
1434218cfe4eSBorislav Petkov }
1435218cfe4eSBorislav Petkov
get_ibs_lvt_offset(void)1436218cfe4eSBorislav Petkov static inline int get_ibs_lvt_offset(void)
1437218cfe4eSBorislav Petkov {
1438218cfe4eSBorislav Petkov u64 val;
1439218cfe4eSBorislav Petkov
1440218cfe4eSBorislav Petkov rdmsrl(MSR_AMD64_IBSCTL, val);
1441218cfe4eSBorislav Petkov if (!(val & IBSCTL_LVT_OFFSET_VALID))
1442218cfe4eSBorislav Petkov return -EINVAL;
1443218cfe4eSBorislav Petkov
1444218cfe4eSBorislav Petkov return val & IBSCTL_LVT_OFFSET_MASK;
1445218cfe4eSBorislav Petkov }
1446218cfe4eSBorislav Petkov
setup_APIC_ibs(void)14479744f7b7SThomas Gleixner static void setup_APIC_ibs(void)
1448218cfe4eSBorislav Petkov {
1449218cfe4eSBorislav Petkov int offset;
1450218cfe4eSBorislav Petkov
1451218cfe4eSBorislav Petkov offset = get_ibs_lvt_offset();
1452218cfe4eSBorislav Petkov if (offset < 0)
1453218cfe4eSBorislav Petkov goto failed;
1454218cfe4eSBorislav Petkov
1455218cfe4eSBorislav Petkov if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0))
1456218cfe4eSBorislav Petkov return;
1457218cfe4eSBorislav Petkov failed:
1458218cfe4eSBorislav Petkov pr_warn("perf: IBS APIC setup failed on cpu #%d\n",
1459218cfe4eSBorislav Petkov smp_processor_id());
1460218cfe4eSBorislav Petkov }
1461218cfe4eSBorislav Petkov
clear_APIC_ibs(void)14629744f7b7SThomas Gleixner static void clear_APIC_ibs(void)
1463218cfe4eSBorislav Petkov {
1464218cfe4eSBorislav Petkov int offset;
1465218cfe4eSBorislav Petkov
1466218cfe4eSBorislav Petkov offset = get_ibs_lvt_offset();
1467218cfe4eSBorislav Petkov if (offset >= 0)
1468218cfe4eSBorislav Petkov setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1);
1469218cfe4eSBorislav Petkov }
1470218cfe4eSBorislav Petkov
x86_pmu_amd_ibs_starting_cpu(unsigned int cpu)14719744f7b7SThomas Gleixner static int x86_pmu_amd_ibs_starting_cpu(unsigned int cpu)
14729744f7b7SThomas Gleixner {
14739744f7b7SThomas Gleixner setup_APIC_ibs();
14749744f7b7SThomas Gleixner return 0;
14759744f7b7SThomas Gleixner }
14769744f7b7SThomas Gleixner
1477218cfe4eSBorislav Petkov #ifdef CONFIG_PM
1478218cfe4eSBorislav Petkov
perf_ibs_suspend(void)1479218cfe4eSBorislav Petkov static int perf_ibs_suspend(void)
1480218cfe4eSBorislav Petkov {
14819744f7b7SThomas Gleixner clear_APIC_ibs();
1482218cfe4eSBorislav Petkov return 0;
1483218cfe4eSBorislav Petkov }
1484218cfe4eSBorislav Petkov
perf_ibs_resume(void)1485218cfe4eSBorislav Petkov static void perf_ibs_resume(void)
1486218cfe4eSBorislav Petkov {
1487218cfe4eSBorislav Petkov ibs_eilvt_setup();
14889744f7b7SThomas Gleixner setup_APIC_ibs();
1489218cfe4eSBorislav Petkov }
1490218cfe4eSBorislav Petkov
1491218cfe4eSBorislav Petkov static struct syscore_ops perf_ibs_syscore_ops = {
1492218cfe4eSBorislav Petkov .resume = perf_ibs_resume,
1493218cfe4eSBorislav Petkov .suspend = perf_ibs_suspend,
1494218cfe4eSBorislav Petkov };
1495218cfe4eSBorislav Petkov
perf_ibs_pm_init(void)1496218cfe4eSBorislav Petkov static void perf_ibs_pm_init(void)
1497218cfe4eSBorislav Petkov {
1498218cfe4eSBorislav Petkov register_syscore_ops(&perf_ibs_syscore_ops);
1499218cfe4eSBorislav Petkov }
1500218cfe4eSBorislav Petkov
1501218cfe4eSBorislav Petkov #else
1502218cfe4eSBorislav Petkov
perf_ibs_pm_init(void)1503218cfe4eSBorislav Petkov static inline void perf_ibs_pm_init(void) { }
1504218cfe4eSBorislav Petkov
1505218cfe4eSBorislav Petkov #endif
1506218cfe4eSBorislav Petkov
x86_pmu_amd_ibs_dying_cpu(unsigned int cpu)15079744f7b7SThomas Gleixner static int x86_pmu_amd_ibs_dying_cpu(unsigned int cpu)
1508218cfe4eSBorislav Petkov {
15099744f7b7SThomas Gleixner clear_APIC_ibs();
15109744f7b7SThomas Gleixner return 0;
1511218cfe4eSBorislav Petkov }
1512218cfe4eSBorislav Petkov
amd_ibs_init(void)1513218cfe4eSBorislav Petkov static __init int amd_ibs_init(void)
1514218cfe4eSBorislav Petkov {
1515218cfe4eSBorislav Petkov u32 caps;
1516218cfe4eSBorislav Petkov
1517218cfe4eSBorislav Petkov caps = __get_ibs_caps();
1518218cfe4eSBorislav Petkov if (!caps)
1519218cfe4eSBorislav Petkov return -ENODEV; /* ibs not supported by the cpu */
1520218cfe4eSBorislav Petkov
1521218cfe4eSBorislav Petkov ibs_eilvt_setup();
1522218cfe4eSBorislav Petkov
1523218cfe4eSBorislav Petkov if (!ibs_eilvt_valid())
15249744f7b7SThomas Gleixner return -EINVAL;
1525218cfe4eSBorislav Petkov
1526218cfe4eSBorislav Petkov perf_ibs_pm_init();
15279744f7b7SThomas Gleixner
1528218cfe4eSBorislav Petkov ibs_caps = caps;
1529218cfe4eSBorislav Petkov /* make ibs_caps visible to other cpus: */
1530218cfe4eSBorislav Petkov smp_mb();
15319744f7b7SThomas Gleixner /*
15329744f7b7SThomas Gleixner * x86_pmu_amd_ibs_starting_cpu will be called from core on
15339744f7b7SThomas Gleixner * all online cpus.
15349744f7b7SThomas Gleixner */
15359744f7b7SThomas Gleixner cpuhp_setup_state(CPUHP_AP_PERF_X86_AMD_IBS_STARTING,
15367e164ce4SSedat Dilek "perf/x86/amd/ibs:starting",
15379744f7b7SThomas Gleixner x86_pmu_amd_ibs_starting_cpu,
15389744f7b7SThomas Gleixner x86_pmu_amd_ibs_dying_cpu);
1539218cfe4eSBorislav Petkov
154039b2ca75SRavi Bangoria return perf_event_ibs_init();
1541218cfe4eSBorislav Petkov }
1542218cfe4eSBorislav Petkov
1543218cfe4eSBorislav Petkov /* Since we need the pci subsystem to init ibs we can't do this earlier: */
1544218cfe4eSBorislav Petkov device_initcall(amd_ibs_init);
1545