Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Copyright (C) 2013 Advanced Micro Devices, Inc.
4 : : *
5 : : * Author: Steven Kinney <Steven.Kinney@amd.com>
6 : : * Author: Suravee Suthikulpanit <Suraveee.Suthikulpanit@amd.com>
7 : : *
8 : : * Perf: amd_iommu - AMD IOMMU Performance Counter PMU implementation
9 : : */
10 : :
11 : : #define pr_fmt(fmt) "perf/amd_iommu: " fmt
12 : :
13 : : #include <linux/perf_event.h>
14 : : #include <linux/init.h>
15 : : #include <linux/cpumask.h>
16 : : #include <linux/slab.h>
17 : :
18 : : #include "../perf_event.h"
19 : : #include "iommu.h"
20 : :
21 : : #define COUNTER_SHIFT 16
22 : :
23 : : /* iommu pmu conf masks */
24 : : #define GET_CSOURCE(x) ((x)->conf & 0xFFULL)
25 : : #define GET_DEVID(x) (((x)->conf >> 8) & 0xFFFFULL)
26 : : #define GET_DOMID(x) (((x)->conf >> 24) & 0xFFFFULL)
27 : : #define GET_PASID(x) (((x)->conf >> 40) & 0xFFFFFULL)
28 : :
29 : : /* iommu pmu conf1 masks */
30 : : #define GET_DEVID_MASK(x) ((x)->conf1 & 0xFFFFULL)
31 : : #define GET_DOMID_MASK(x) (((x)->conf1 >> 16) & 0xFFFFULL)
32 : : #define GET_PASID_MASK(x) (((x)->conf1 >> 32) & 0xFFFFFULL)
33 : :
34 : : #define IOMMU_NAME_SIZE 16
35 : :
36 : : struct perf_amd_iommu {
37 : : struct list_head list;
38 : : struct pmu pmu;
39 : : struct amd_iommu *iommu;
40 : : char name[IOMMU_NAME_SIZE];
41 : : u8 max_banks;
42 : : u8 max_counters;
43 : : u64 cntr_assign_mask;
44 : : raw_spinlock_t lock;
45 : : };
46 : :
47 : : static LIST_HEAD(perf_amd_iommu_list);
48 : :
49 : : /*---------------------------------------------
50 : : * sysfs format attributes
51 : : *---------------------------------------------*/
52 : 0 : PMU_FORMAT_ATTR(csource, "config:0-7");
53 : 0 : PMU_FORMAT_ATTR(devid, "config:8-23");
54 : 0 : PMU_FORMAT_ATTR(domid, "config:24-39");
55 : 0 : PMU_FORMAT_ATTR(pasid, "config:40-59");
56 : 0 : PMU_FORMAT_ATTR(devid_mask, "config1:0-15");
57 : 0 : PMU_FORMAT_ATTR(domid_mask, "config1:16-31");
58 : 0 : PMU_FORMAT_ATTR(pasid_mask, "config1:32-51");
59 : :
60 : : static struct attribute *iommu_format_attrs[] = {
61 : : &format_attr_csource.attr,
62 : : &format_attr_devid.attr,
63 : : &format_attr_pasid.attr,
64 : : &format_attr_domid.attr,
65 : : &format_attr_devid_mask.attr,
66 : : &format_attr_pasid_mask.attr,
67 : : &format_attr_domid_mask.attr,
68 : : NULL,
69 : : };
70 : :
71 : : static struct attribute_group amd_iommu_format_group = {
72 : : .name = "format",
73 : : .attrs = iommu_format_attrs,
74 : : };
75 : :
76 : : /*---------------------------------------------
77 : : * sysfs events attributes
78 : : *---------------------------------------------*/
79 : : static struct attribute_group amd_iommu_events_group = {
80 : : .name = "events",
81 : : };
82 : :
83 : : struct amd_iommu_event_desc {
84 : : struct kobj_attribute attr;
85 : : const char *event;
86 : : };
87 : :
88 : 0 : static ssize_t _iommu_event_show(struct kobject *kobj,
89 : : struct kobj_attribute *attr, char *buf)
90 : : {
91 : 0 : struct amd_iommu_event_desc *event =
92 : 0 : container_of(attr, struct amd_iommu_event_desc, attr);
93 : 0 : return sprintf(buf, "%s\n", event->event);
94 : : }
95 : :
96 : : #define AMD_IOMMU_EVENT_DESC(_name, _event) \
97 : : { \
98 : : .attr = __ATTR(_name, 0444, _iommu_event_show, NULL), \
99 : : .event = _event, \
100 : : }
101 : :
102 : : static struct amd_iommu_event_desc amd_iommu_v2_event_descs[] = {
103 : : AMD_IOMMU_EVENT_DESC(mem_pass_untrans, "csource=0x01"),
104 : : AMD_IOMMU_EVENT_DESC(mem_pass_pretrans, "csource=0x02"),
105 : : AMD_IOMMU_EVENT_DESC(mem_pass_excl, "csource=0x03"),
106 : : AMD_IOMMU_EVENT_DESC(mem_target_abort, "csource=0x04"),
107 : : AMD_IOMMU_EVENT_DESC(mem_trans_total, "csource=0x05"),
108 : : AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pte_hit, "csource=0x06"),
109 : : AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pte_mis, "csource=0x07"),
110 : : AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pde_hit, "csource=0x08"),
111 : : AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pde_mis, "csource=0x09"),
112 : : AMD_IOMMU_EVENT_DESC(mem_dte_hit, "csource=0x0a"),
113 : : AMD_IOMMU_EVENT_DESC(mem_dte_mis, "csource=0x0b"),
114 : : AMD_IOMMU_EVENT_DESC(page_tbl_read_tot, "csource=0x0c"),
115 : : AMD_IOMMU_EVENT_DESC(page_tbl_read_nst, "csource=0x0d"),
116 : : AMD_IOMMU_EVENT_DESC(page_tbl_read_gst, "csource=0x0e"),
117 : : AMD_IOMMU_EVENT_DESC(int_dte_hit, "csource=0x0f"),
118 : : AMD_IOMMU_EVENT_DESC(int_dte_mis, "csource=0x10"),
119 : : AMD_IOMMU_EVENT_DESC(cmd_processed, "csource=0x11"),
120 : : AMD_IOMMU_EVENT_DESC(cmd_processed_inv, "csource=0x12"),
121 : : AMD_IOMMU_EVENT_DESC(tlb_inv, "csource=0x13"),
122 : : AMD_IOMMU_EVENT_DESC(ign_rd_wr_mmio_1ff8h, "csource=0x14"),
123 : : AMD_IOMMU_EVENT_DESC(vapic_int_non_guest, "csource=0x15"),
124 : : AMD_IOMMU_EVENT_DESC(vapic_int_guest, "csource=0x16"),
125 : : AMD_IOMMU_EVENT_DESC(smi_recv, "csource=0x17"),
126 : : AMD_IOMMU_EVENT_DESC(smi_blk, "csource=0x18"),
127 : : { /* end: all zeroes */ },
128 : : };
129 : :
130 : : /*---------------------------------------------
131 : : * sysfs cpumask attributes
132 : : *---------------------------------------------*/
133 : : static cpumask_t iommu_cpumask;
134 : :
135 : 0 : static ssize_t _iommu_cpumask_show(struct device *dev,
136 : : struct device_attribute *attr,
137 : : char *buf)
138 : : {
139 : 0 : return cpumap_print_to_pagebuf(true, buf, &iommu_cpumask);
140 : : }
141 : : static DEVICE_ATTR(cpumask, S_IRUGO, _iommu_cpumask_show, NULL);
142 : :
143 : : static struct attribute *iommu_cpumask_attrs[] = {
144 : : &dev_attr_cpumask.attr,
145 : : NULL,
146 : : };
147 : :
148 : : static struct attribute_group amd_iommu_cpumask_group = {
149 : : .attrs = iommu_cpumask_attrs,
150 : : };
151 : :
152 : : /*---------------------------------------------*/
153 : :
154 : 0 : static int get_next_avail_iommu_bnk_cntr(struct perf_event *event)
155 : : {
156 : 0 : struct perf_amd_iommu *piommu = container_of(event->pmu, struct perf_amd_iommu, pmu);
157 : 0 : int max_cntrs = piommu->max_counters;
158 : 0 : int max_banks = piommu->max_banks;
159 : 0 : u32 shift, bank, cntr;
160 : 0 : unsigned long flags;
161 : 0 : int retval;
162 : :
163 : 0 : raw_spin_lock_irqsave(&piommu->lock, flags);
164 : :
165 [ # # ]: 0 : for (bank = 0, shift = 0; bank < max_banks; bank++) {
166 [ # # ]: 0 : for (cntr = 0; cntr < max_cntrs; cntr++) {
167 : 0 : shift = bank + (bank*3) + cntr;
168 [ # # ]: 0 : if (piommu->cntr_assign_mask & BIT_ULL(shift)) {
169 : 0 : continue;
170 : : } else {
171 : 0 : piommu->cntr_assign_mask |= BIT_ULL(shift);
172 : 0 : event->hw.iommu_bank = bank;
173 : 0 : event->hw.iommu_cntr = cntr;
174 : 0 : retval = 0;
175 : 0 : goto out;
176 : : }
177 : : }
178 : : }
179 : : retval = -ENOSPC;
180 : 0 : out:
181 : 0 : raw_spin_unlock_irqrestore(&piommu->lock, flags);
182 : 0 : return retval;
183 : : }
184 : :
185 : 0 : static int clear_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu,
186 : : u8 bank, u8 cntr)
187 : : {
188 : 0 : unsigned long flags;
189 : 0 : int max_banks, max_cntrs;
190 : 0 : int shift = 0;
191 : :
192 : 0 : max_banks = perf_iommu->max_banks;
193 : 0 : max_cntrs = perf_iommu->max_counters;
194 : :
195 [ # # # # ]: 0 : if ((bank > max_banks) || (cntr > max_cntrs))
196 : : return -EINVAL;
197 : :
198 : 0 : shift = bank + cntr + (bank*3);
199 : :
200 : 0 : raw_spin_lock_irqsave(&perf_iommu->lock, flags);
201 : 0 : perf_iommu->cntr_assign_mask &= ~(1ULL<<shift);
202 : 0 : raw_spin_unlock_irqrestore(&perf_iommu->lock, flags);
203 : :
204 : 0 : return 0;
205 : : }
206 : :
207 : 0 : static int perf_iommu_event_init(struct perf_event *event)
208 : : {
209 : 0 : struct hw_perf_event *hwc = &event->hw;
210 : :
211 : : /* test the event attr type check for PMU enumeration */
212 [ # # ]: 0 : if (event->attr.type != event->pmu->type)
213 : : return -ENOENT;
214 : :
215 : : /*
216 : : * IOMMU counters are shared across all cores.
217 : : * Therefore, it does not support per-process mode.
218 : : * Also, it does not support event sampling mode.
219 : : */
220 [ # # # # ]: 0 : if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
221 : : return -EINVAL;
222 : :
223 [ # # ]: 0 : if (event->cpu < 0)
224 : : return -EINVAL;
225 : :
226 : : /* update the hw_perf_event struct with the iommu config data */
227 : 0 : hwc->conf = event->attr.config;
228 : 0 : hwc->conf1 = event->attr.config1;
229 : :
230 : 0 : return 0;
231 : : }
232 : :
233 : 0 : static inline struct amd_iommu *perf_event_2_iommu(struct perf_event *ev)
234 : : {
235 : 0 : return (container_of(ev->pmu, struct perf_amd_iommu, pmu))->iommu;
236 : : }
237 : :
238 : 0 : static void perf_iommu_enable_event(struct perf_event *ev)
239 : : {
240 : 0 : struct amd_iommu *iommu = perf_event_2_iommu(ev);
241 : 0 : struct hw_perf_event *hwc = &ev->hw;
242 : 0 : u8 bank = hwc->iommu_bank;
243 : 0 : u8 cntr = hwc->iommu_cntr;
244 : 0 : u64 reg = 0ULL;
245 : :
246 : 0 : reg = GET_CSOURCE(hwc);
247 : 0 : amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_COUNTER_SRC_REG, ®);
248 : :
249 : 0 : reg = GET_DEVID_MASK(hwc);
250 : 0 : reg = GET_DEVID(hwc) | (reg << 32);
251 [ # # ]: 0 : if (reg)
252 : 0 : reg |= BIT(31);
253 : 0 : amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DEVID_MATCH_REG, ®);
254 : :
255 : 0 : reg = GET_PASID_MASK(hwc);
256 : 0 : reg = GET_PASID(hwc) | (reg << 32);
257 [ # # ]: 0 : if (reg)
258 : 0 : reg |= BIT(31);
259 : 0 : amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_PASID_MATCH_REG, ®);
260 : :
261 : 0 : reg = GET_DOMID_MASK(hwc);
262 : 0 : reg = GET_DOMID(hwc) | (reg << 32);
263 [ # # ]: 0 : if (reg)
264 : 0 : reg |= BIT(31);
265 : 0 : amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DOMID_MATCH_REG, ®);
266 : 0 : }
267 : :
268 : 0 : static void perf_iommu_disable_event(struct perf_event *event)
269 : : {
270 : 0 : struct amd_iommu *iommu = perf_event_2_iommu(event);
271 : 0 : struct hw_perf_event *hwc = &event->hw;
272 : 0 : u64 reg = 0ULL;
273 : :
274 : 0 : amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr,
275 : : IOMMU_PC_COUNTER_SRC_REG, ®);
276 : 0 : }
277 : :
278 : 0 : static void perf_iommu_start(struct perf_event *event, int flags)
279 : : {
280 : 0 : struct hw_perf_event *hwc = &event->hw;
281 : :
282 [ # # # # ]: 0 : if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
283 : : return;
284 : :
285 [ # # ]: 0 : WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
286 : 0 : hwc->state = 0;
287 : :
288 [ # # ]: 0 : if (flags & PERF_EF_RELOAD) {
289 : 0 : u64 prev_raw_count = local64_read(&hwc->prev_count);
290 : 0 : struct amd_iommu *iommu = perf_event_2_iommu(event);
291 : :
292 : 0 : amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr,
293 : : IOMMU_PC_COUNTER_REG, &prev_raw_count);
294 : : }
295 : :
296 : 0 : perf_iommu_enable_event(event);
297 : 0 : perf_event_update_userpage(event);
298 : :
299 : : }
300 : :
301 : 0 : static void perf_iommu_read(struct perf_event *event)
302 : : {
303 : 0 : u64 count, prev, delta;
304 : 0 : struct hw_perf_event *hwc = &event->hw;
305 : 0 : struct amd_iommu *iommu = perf_event_2_iommu(event);
306 : :
307 [ # # ]: 0 : if (amd_iommu_pc_get_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr,
308 : : IOMMU_PC_COUNTER_REG, &count))
309 : 0 : return;
310 : :
311 : : /* IOMMU pc counter register is only 48 bits */
312 : 0 : count &= GENMASK_ULL(47, 0);
313 : :
314 : 0 : prev = local64_read(&hwc->prev_count);
315 [ # # ]: 0 : if (local64_cmpxchg(&hwc->prev_count, prev, count) != prev)
316 : : return;
317 : :
318 : : /* Handle 48-bit counter overflow */
319 : 0 : delta = (count << COUNTER_SHIFT) - (prev << COUNTER_SHIFT);
320 : 0 : delta >>= COUNTER_SHIFT;
321 : 0 : local64_add(delta, &event->count);
322 : : }
323 : :
324 : 0 : static void perf_iommu_stop(struct perf_event *event, int flags)
325 : : {
326 : 0 : struct hw_perf_event *hwc = &event->hw;
327 : :
328 [ # # ]: 0 : if (hwc->state & PERF_HES_UPTODATE)
329 : : return;
330 : :
331 : 0 : perf_iommu_disable_event(event);
332 [ # # ]: 0 : WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
333 : 0 : hwc->state |= PERF_HES_STOPPED;
334 : :
335 [ # # ]: 0 : if (hwc->state & PERF_HES_UPTODATE)
336 : : return;
337 : :
338 : 0 : perf_iommu_read(event);
339 : 0 : hwc->state |= PERF_HES_UPTODATE;
340 : : }
341 : :
342 : 0 : static int perf_iommu_add(struct perf_event *event, int flags)
343 : : {
344 : 0 : int retval;
345 : :
346 : 0 : event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
347 : :
348 : : /* request an iommu bank/counter */
349 : 0 : retval = get_next_avail_iommu_bnk_cntr(event);
350 [ # # ]: 0 : if (retval)
351 : : return retval;
352 : :
353 [ # # ]: 0 : if (flags & PERF_EF_START)
354 : 0 : perf_iommu_start(event, PERF_EF_RELOAD);
355 : :
356 : : return 0;
357 : : }
358 : :
359 : 0 : static void perf_iommu_del(struct perf_event *event, int flags)
360 : : {
361 : 0 : struct hw_perf_event *hwc = &event->hw;
362 : 0 : struct perf_amd_iommu *perf_iommu =
363 : 0 : container_of(event->pmu, struct perf_amd_iommu, pmu);
364 : :
365 : 0 : perf_iommu_stop(event, PERF_EF_UPDATE);
366 : :
367 : : /* clear the assigned iommu bank/counter */
368 : 0 : clear_avail_iommu_bnk_cntr(perf_iommu,
369 : 0 : hwc->iommu_bank, hwc->iommu_cntr);
370 : :
371 : 0 : perf_event_update_userpage(event);
372 : 0 : }
373 : :
374 : 0 : static __init int _init_events_attrs(void)
375 : : {
376 : 0 : int i = 0, j;
377 : 0 : struct attribute **attrs;
378 : :
379 [ # # ]: 0 : while (amd_iommu_v2_event_descs[i].attr.attr.name)
380 : 0 : i++;
381 : :
382 : 0 : attrs = kcalloc(i + 1, sizeof(struct attribute **), GFP_KERNEL);
383 [ # # ]: 0 : if (!attrs)
384 : : return -ENOMEM;
385 : :
386 [ # # ]: 0 : for (j = 0; j < i; j++)
387 : 0 : attrs[j] = &amd_iommu_v2_event_descs[j].attr.attr;
388 : :
389 : 0 : amd_iommu_events_group.attrs = attrs;
390 : 0 : return 0;
391 : : }
392 : :
393 : : static const struct attribute_group *amd_iommu_attr_groups[] = {
394 : : &amd_iommu_format_group,
395 : : &amd_iommu_cpumask_group,
396 : : &amd_iommu_events_group,
397 : : NULL,
398 : : };
399 : :
400 : : static const struct pmu iommu_pmu __initconst = {
401 : : .event_init = perf_iommu_event_init,
402 : : .add = perf_iommu_add,
403 : : .del = perf_iommu_del,
404 : : .start = perf_iommu_start,
405 : : .stop = perf_iommu_stop,
406 : : .read = perf_iommu_read,
407 : : .task_ctx_nr = perf_invalid_context,
408 : : .attr_groups = amd_iommu_attr_groups,
409 : : .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
410 : : };
411 : :
412 : 0 : static __init int init_one_iommu(unsigned int idx)
413 : : {
414 : 0 : struct perf_amd_iommu *perf_iommu;
415 : 0 : int ret;
416 : :
417 : 0 : perf_iommu = kzalloc(sizeof(struct perf_amd_iommu), GFP_KERNEL);
418 [ # # ]: 0 : if (!perf_iommu)
419 : : return -ENOMEM;
420 : :
421 : 0 : raw_spin_lock_init(&perf_iommu->lock);
422 : :
423 : 0 : perf_iommu->pmu = iommu_pmu;
424 : 0 : perf_iommu->iommu = get_amd_iommu(idx);
425 : 0 : perf_iommu->max_banks = amd_iommu_pc_get_max_banks(idx);
426 : 0 : perf_iommu->max_counters = amd_iommu_pc_get_max_counters(idx);
427 : :
428 [ # # ]: 0 : if (!perf_iommu->iommu ||
429 [ # # # # ]: 0 : !perf_iommu->max_banks ||
430 : : !perf_iommu->max_counters) {
431 : 0 : kfree(perf_iommu);
432 : 0 : return -EINVAL;
433 : : }
434 : :
435 : 0 : snprintf(perf_iommu->name, IOMMU_NAME_SIZE, "amd_iommu_%u", idx);
436 : :
437 : 0 : ret = perf_pmu_register(&perf_iommu->pmu, perf_iommu->name, -1);
438 [ # # ]: 0 : if (!ret) {
439 : 0 : pr_info("Detected AMD IOMMU #%d (%d banks, %d counters/bank).\n",
440 : : idx, perf_iommu->max_banks, perf_iommu->max_counters);
441 : 0 : list_add_tail(&perf_iommu->list, &perf_amd_iommu_list);
442 : : } else {
443 : 0 : pr_warn("Error initializing IOMMU %d.\n", idx);
444 : 0 : kfree(perf_iommu);
445 : : }
446 : : return ret;
447 : : }
448 : :
449 : 78 : static __init int amd_iommu_pc_init(void)
450 : : {
451 : 78 : unsigned int i, cnt = 0;
452 : 78 : int ret;
453 : :
454 : : /* Make sure the IOMMU PC resource is available */
455 [ - + ]: 78 : if (!amd_iommu_pc_supported())
456 : : return -ENODEV;
457 : :
458 : 0 : ret = _init_events_attrs();
459 [ # # ]: 0 : if (ret)
460 : : return ret;
461 : :
462 : : /*
463 : : * An IOMMU PMU is specific to an IOMMU, and can function independently.
464 : : * So we go through all IOMMUs and ignore the one that fails init
465 : : * unless all IOMMU are failing.
466 : : */
467 [ # # ]: 0 : for (i = 0; i < amd_iommu_get_num_iommus(); i++) {
468 : 0 : ret = init_one_iommu(i);
469 [ # # ]: 0 : if (!ret)
470 : 0 : cnt++;
471 : : }
472 : :
473 [ # # ]: 0 : if (!cnt) {
474 : 0 : kfree(amd_iommu_events_group.attrs);
475 : 0 : return -ENODEV;
476 : : }
477 : :
478 : : /* Init cpumask attributes to only core 0 */
479 : 0 : cpumask_set_cpu(0, &iommu_cpumask);
480 : 0 : return 0;
481 : : }
482 : :
483 : : device_initcall(amd_iommu_pc_init);
|