xref: /linux/drivers/iommu/intel/perfmon.c (revision a6a5006dad572a53b5df3f47e1471d207ae9ba49)
1*a6a5006dSKan Liang // SPDX-License-Identifier: GPL-2.0-only
2*a6a5006dSKan Liang /*
3*a6a5006dSKan Liang  * Support Intel IOMMU PerfMon
4*a6a5006dSKan Liang  * Copyright(c) 2023 Intel Corporation.
5*a6a5006dSKan Liang  */
6*a6a5006dSKan Liang #define pr_fmt(fmt)	"DMAR: " fmt
7*a6a5006dSKan Liang #define dev_fmt(fmt)	pr_fmt(fmt)
8*a6a5006dSKan Liang 
9*a6a5006dSKan Liang #include <linux/dmar.h>
10*a6a5006dSKan Liang #include "iommu.h"
11*a6a5006dSKan Liang #include "perfmon.h"
12*a6a5006dSKan Liang 
13*a6a5006dSKan Liang static inline void __iomem *
14*a6a5006dSKan Liang get_perf_reg_address(struct intel_iommu *iommu, u32 offset)
15*a6a5006dSKan Liang {
16*a6a5006dSKan Liang 	u32 off = dmar_readl(iommu->reg + offset);
17*a6a5006dSKan Liang 
18*a6a5006dSKan Liang 	return iommu->reg + off;
19*a6a5006dSKan Liang }
20*a6a5006dSKan Liang 
21*a6a5006dSKan Liang int alloc_iommu_pmu(struct intel_iommu *iommu)
22*a6a5006dSKan Liang {
23*a6a5006dSKan Liang 	struct iommu_pmu *iommu_pmu;
24*a6a5006dSKan Liang 	int i, j, ret;
25*a6a5006dSKan Liang 	u64 perfcap;
26*a6a5006dSKan Liang 	u32 cap;
27*a6a5006dSKan Liang 
28*a6a5006dSKan Liang 	if (!ecap_pms(iommu->ecap))
29*a6a5006dSKan Liang 		return 0;
30*a6a5006dSKan Liang 
31*a6a5006dSKan Liang 	/* The IOMMU PMU requires the ECMD support as well */
32*a6a5006dSKan Liang 	if (!cap_ecmds(iommu->cap))
33*a6a5006dSKan Liang 		return -ENODEV;
34*a6a5006dSKan Liang 
35*a6a5006dSKan Liang 	perfcap = dmar_readq(iommu->reg + DMAR_PERFCAP_REG);
36*a6a5006dSKan Liang 	/* The performance monitoring is not supported. */
37*a6a5006dSKan Liang 	if (!perfcap)
38*a6a5006dSKan Liang 		return -ENODEV;
39*a6a5006dSKan Liang 
40*a6a5006dSKan Liang 	/* Sanity check for the number of the counters and event groups */
41*a6a5006dSKan Liang 	if (!pcap_num_cntr(perfcap) || !pcap_num_event_group(perfcap))
42*a6a5006dSKan Liang 		return -ENODEV;
43*a6a5006dSKan Liang 
44*a6a5006dSKan Liang 	/* The interrupt on overflow is required */
45*a6a5006dSKan Liang 	if (!pcap_interrupt(perfcap))
46*a6a5006dSKan Liang 		return -ENODEV;
47*a6a5006dSKan Liang 
48*a6a5006dSKan Liang 	iommu_pmu = kzalloc(sizeof(*iommu_pmu), GFP_KERNEL);
49*a6a5006dSKan Liang 	if (!iommu_pmu)
50*a6a5006dSKan Liang 		return -ENOMEM;
51*a6a5006dSKan Liang 
52*a6a5006dSKan Liang 	iommu_pmu->num_cntr = pcap_num_cntr(perfcap);
53*a6a5006dSKan Liang 	iommu_pmu->cntr_width = pcap_cntr_width(perfcap);
54*a6a5006dSKan Liang 	iommu_pmu->filter = pcap_filters_mask(perfcap);
55*a6a5006dSKan Liang 	iommu_pmu->cntr_stride = pcap_cntr_stride(perfcap);
56*a6a5006dSKan Liang 	iommu_pmu->num_eg = pcap_num_event_group(perfcap);
57*a6a5006dSKan Liang 
58*a6a5006dSKan Liang 	iommu_pmu->evcap = kcalloc(iommu_pmu->num_eg, sizeof(u64), GFP_KERNEL);
59*a6a5006dSKan Liang 	if (!iommu_pmu->evcap) {
60*a6a5006dSKan Liang 		ret = -ENOMEM;
61*a6a5006dSKan Liang 		goto free_pmu;
62*a6a5006dSKan Liang 	}
63*a6a5006dSKan Liang 
64*a6a5006dSKan Liang 	/* Parse event group capabilities */
65*a6a5006dSKan Liang 	for (i = 0; i < iommu_pmu->num_eg; i++) {
66*a6a5006dSKan Liang 		u64 pcap;
67*a6a5006dSKan Liang 
68*a6a5006dSKan Liang 		pcap = dmar_readq(iommu->reg + DMAR_PERFEVNTCAP_REG +
69*a6a5006dSKan Liang 				  i * IOMMU_PMU_CAP_REGS_STEP);
70*a6a5006dSKan Liang 		iommu_pmu->evcap[i] = pecap_es(pcap);
71*a6a5006dSKan Liang 	}
72*a6a5006dSKan Liang 
73*a6a5006dSKan Liang 	iommu_pmu->cntr_evcap = kcalloc(iommu_pmu->num_cntr, sizeof(u32 *), GFP_KERNEL);
74*a6a5006dSKan Liang 	if (!iommu_pmu->cntr_evcap) {
75*a6a5006dSKan Liang 		ret = -ENOMEM;
76*a6a5006dSKan Liang 		goto free_pmu_evcap;
77*a6a5006dSKan Liang 	}
78*a6a5006dSKan Liang 	for (i = 0; i < iommu_pmu->num_cntr; i++) {
79*a6a5006dSKan Liang 		iommu_pmu->cntr_evcap[i] = kcalloc(iommu_pmu->num_eg, sizeof(u32), GFP_KERNEL);
80*a6a5006dSKan Liang 		if (!iommu_pmu->cntr_evcap[i]) {
81*a6a5006dSKan Liang 			ret = -ENOMEM;
82*a6a5006dSKan Liang 			goto free_pmu_cntr_evcap;
83*a6a5006dSKan Liang 		}
84*a6a5006dSKan Liang 		/*
85*a6a5006dSKan Liang 		 * Set to the global capabilities, will adjust according
86*a6a5006dSKan Liang 		 * to per-counter capabilities later.
87*a6a5006dSKan Liang 		 */
88*a6a5006dSKan Liang 		for (j = 0; j < iommu_pmu->num_eg; j++)
89*a6a5006dSKan Liang 			iommu_pmu->cntr_evcap[i][j] = (u32)iommu_pmu->evcap[j];
90*a6a5006dSKan Liang 	}
91*a6a5006dSKan Liang 
92*a6a5006dSKan Liang 	iommu_pmu->cfg_reg = get_perf_reg_address(iommu, DMAR_PERFCFGOFF_REG);
93*a6a5006dSKan Liang 	iommu_pmu->cntr_reg = get_perf_reg_address(iommu, DMAR_PERFCNTROFF_REG);
94*a6a5006dSKan Liang 	iommu_pmu->overflow = get_perf_reg_address(iommu, DMAR_PERFOVFOFF_REG);
95*a6a5006dSKan Liang 
96*a6a5006dSKan Liang 	/*
97*a6a5006dSKan Liang 	 * Check per-counter capabilities. All counters should have the
98*a6a5006dSKan Liang 	 * same capabilities on Interrupt on Overflow Support and Counter
99*a6a5006dSKan Liang 	 * Width.
100*a6a5006dSKan Liang 	 */
101*a6a5006dSKan Liang 	for (i = 0; i < iommu_pmu->num_cntr; i++) {
102*a6a5006dSKan Liang 		cap = dmar_readl(iommu_pmu->cfg_reg +
103*a6a5006dSKan Liang 				 i * IOMMU_PMU_CFG_OFFSET +
104*a6a5006dSKan Liang 				 IOMMU_PMU_CFG_CNTRCAP_OFFSET);
105*a6a5006dSKan Liang 		if (!iommu_cntrcap_pcc(cap))
106*a6a5006dSKan Liang 			continue;
107*a6a5006dSKan Liang 
108*a6a5006dSKan Liang 		/*
109*a6a5006dSKan Liang 		 * It's possible that some counters have a different
110*a6a5006dSKan Liang 		 * capability because of e.g., HW bug. Check the corner
111*a6a5006dSKan Liang 		 * case here and simply drop those counters.
112*a6a5006dSKan Liang 		 */
113*a6a5006dSKan Liang 		if ((iommu_cntrcap_cw(cap) != iommu_pmu->cntr_width) ||
114*a6a5006dSKan Liang 		    !iommu_cntrcap_ios(cap)) {
115*a6a5006dSKan Liang 			iommu_pmu->num_cntr = i;
116*a6a5006dSKan Liang 			pr_warn("PMU counter capability inconsistent, counter number reduced to %d\n",
117*a6a5006dSKan Liang 				iommu_pmu->num_cntr);
118*a6a5006dSKan Liang 		}
119*a6a5006dSKan Liang 
120*a6a5006dSKan Liang 		/* Clear the pre-defined events group */
121*a6a5006dSKan Liang 		for (j = 0; j < iommu_pmu->num_eg; j++)
122*a6a5006dSKan Liang 			iommu_pmu->cntr_evcap[i][j] = 0;
123*a6a5006dSKan Liang 
124*a6a5006dSKan Liang 		/* Override with per-counter event capabilities */
125*a6a5006dSKan Liang 		for (j = 0; j < iommu_cntrcap_egcnt(cap); j++) {
126*a6a5006dSKan Liang 			cap = dmar_readl(iommu_pmu->cfg_reg + i * IOMMU_PMU_CFG_OFFSET +
127*a6a5006dSKan Liang 					 IOMMU_PMU_CFG_CNTREVCAP_OFFSET +
128*a6a5006dSKan Liang 					 (j * IOMMU_PMU_OFF_REGS_STEP));
129*a6a5006dSKan Liang 			iommu_pmu->cntr_evcap[i][iommu_event_group(cap)] = iommu_event_select(cap);
130*a6a5006dSKan Liang 			/*
131*a6a5006dSKan Liang 			 * Some events may only be supported by a specific counter.
132*a6a5006dSKan Liang 			 * Track them in the evcap as well.
133*a6a5006dSKan Liang 			 */
134*a6a5006dSKan Liang 			iommu_pmu->evcap[iommu_event_group(cap)] |= iommu_event_select(cap);
135*a6a5006dSKan Liang 		}
136*a6a5006dSKan Liang 	}
137*a6a5006dSKan Liang 
138*a6a5006dSKan Liang 	iommu_pmu->iommu = iommu;
139*a6a5006dSKan Liang 	iommu->pmu = iommu_pmu;
140*a6a5006dSKan Liang 
141*a6a5006dSKan Liang 	return 0;
142*a6a5006dSKan Liang 
143*a6a5006dSKan Liang free_pmu_cntr_evcap:
144*a6a5006dSKan Liang 	for (i = 0; i < iommu_pmu->num_cntr; i++)
145*a6a5006dSKan Liang 		kfree(iommu_pmu->cntr_evcap[i]);
146*a6a5006dSKan Liang 	kfree(iommu_pmu->cntr_evcap);
147*a6a5006dSKan Liang free_pmu_evcap:
148*a6a5006dSKan Liang 	kfree(iommu_pmu->evcap);
149*a6a5006dSKan Liang free_pmu:
150*a6a5006dSKan Liang 	kfree(iommu_pmu);
151*a6a5006dSKan Liang 
152*a6a5006dSKan Liang 	return ret;
153*a6a5006dSKan Liang }
154*a6a5006dSKan Liang 
155*a6a5006dSKan Liang void free_iommu_pmu(struct intel_iommu *iommu)
156*a6a5006dSKan Liang {
157*a6a5006dSKan Liang 	struct iommu_pmu *iommu_pmu = iommu->pmu;
158*a6a5006dSKan Liang 
159*a6a5006dSKan Liang 	if (!iommu_pmu)
160*a6a5006dSKan Liang 		return;
161*a6a5006dSKan Liang 
162*a6a5006dSKan Liang 	if (iommu_pmu->evcap) {
163*a6a5006dSKan Liang 		int i;
164*a6a5006dSKan Liang 
165*a6a5006dSKan Liang 		for (i = 0; i < iommu_pmu->num_cntr; i++)
166*a6a5006dSKan Liang 			kfree(iommu_pmu->cntr_evcap[i]);
167*a6a5006dSKan Liang 		kfree(iommu_pmu->cntr_evcap);
168*a6a5006dSKan Liang 	}
169*a6a5006dSKan Liang 	kfree(iommu_pmu->evcap);
170*a6a5006dSKan Liang 	kfree(iommu_pmu);
171*a6a5006dSKan Liang 	iommu->pmu = NULL;
172*a6a5006dSKan Liang }
173