xref: /linux/arch/x86/kernel/cpu/resctrl/intel_aet.c (revision 51541f6ca7718d8278e12fe80af80033268743b2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Resource Director Technology(RDT)
4  * - Intel Application Energy Telemetry
5  *
6  * Copyright (C) 2025 Intel Corporation
7  *
8  * Author:
9  *    Tony Luck <tony.luck@intel.com>
10  */
11 
12 #define pr_fmt(fmt)   "resctrl: " fmt
13 
14 #include <linux/bits.h>
15 #include <linux/compiler_types.h>
16 #include <linux/container_of.h>
17 #include <linux/err.h>
18 #include <linux/errno.h>
19 #include <linux/init.h>
20 #include <linux/intel_pmt_features.h>
21 #include <linux/intel_vsec.h>
22 #include <linux/io.h>
23 #include <linux/printk.h>
24 #include <linux/resctrl.h>
25 #include <linux/resctrl_types.h>
26 #include <linux/stddef.h>
27 #include <linux/topology.h>
28 #include <linux/types.h>
29 
30 #include "internal.h"
31 
32 /**
33  * struct pmt_event - Telemetry event.
34  * @id:		Resctrl event id.
35  * @idx:	Counter index within each per-RMID block of counters.
36  * @bin_bits:	Zero for integer valued events, else number bits in fraction
37  *		part of fixed-point.
38  */
39 struct pmt_event {
40 	enum resctrl_event_id	id;
41 	unsigned int		idx;
42 	unsigned int		bin_bits;
43 };
44 
45 #define EVT(_id, _idx, _bits) { .id = _id, .idx = _idx, .bin_bits = _bits }
46 
47 /**
48  * struct event_group - Events with the same feature type ("energy" or "perf") and GUID.
49  * @pfname:		PMT feature name ("energy" or "perf") of this event group.
50  * @pfg:		Points to the aggregated telemetry space information
51  *			returned by the intel_pmt_get_regions_by_feature()
52  *			call to the INTEL_PMT_TELEMETRY driver that contains
53  *			data for all telemetry regions of type @pfname.
54  *			Valid if the system supports the event group,
55  *			NULL otherwise.
56  * @guid:		Unique number per XML description file.
57  * @mmio_size:		Number of bytes of MMIO registers for this group.
58  * @num_events:		Number of events in this group.
59  * @evts:		Array of event descriptors.
60  */
61 struct event_group {
62 	/* Data fields for additional structures to manage this group. */
63 	const char			*pfname;
64 	struct pmt_feature_group	*pfg;
65 
66 	/* Remaining fields initialized from XML file. */
67 	u32				guid;
68 	size_t				mmio_size;
69 	unsigned int			num_events;
70 	struct pmt_event		evts[] __counted_by(num_events);
71 };
72 
73 #define XML_MMIO_SIZE(num_rmids, num_events, num_extra_status) \
74 		      (((num_rmids) * (num_events) + (num_extra_status)) * sizeof(u64))
75 
76 /*
77  * Link: https://github.com/intel/Intel-PMT/blob/main/xml/CWF/OOBMSM/RMID-ENERGY/cwf_aggregator.xml
78  */
79 static struct event_group energy_0x26696143 = {
80 	.pfname		= "energy",
81 	.guid		= 0x26696143,
82 	.mmio_size	= XML_MMIO_SIZE(576, 2, 3),
83 	.num_events	= 2,
84 	.evts		= {
85 		EVT(PMT_EVENT_ENERGY, 0, 18),
86 		EVT(PMT_EVENT_ACTIVITY, 1, 18),
87 	}
88 };
89 
90 /*
91  * Link: https://github.com/intel/Intel-PMT/blob/main/xml/CWF/OOBMSM/RMID-PERF/cwf_aggregator.xml
92  */
93 static struct event_group perf_0x26557651 = {
94 	.pfname		= "perf",
95 	.guid		= 0x26557651,
96 	.mmio_size	= XML_MMIO_SIZE(576, 7, 3),
97 	.num_events	= 7,
98 	.evts		= {
99 		EVT(PMT_EVENT_STALLS_LLC_HIT, 0, 0),
100 		EVT(PMT_EVENT_C1_RES, 1, 0),
101 		EVT(PMT_EVENT_UNHALTED_CORE_CYCLES, 2, 0),
102 		EVT(PMT_EVENT_STALLS_LLC_MISS, 3, 0),
103 		EVT(PMT_EVENT_AUTO_C6_RES, 4, 0),
104 		EVT(PMT_EVENT_UNHALTED_REF_CYCLES, 5, 0),
105 		EVT(PMT_EVENT_UOPS_RETIRED, 6, 0),
106 	}
107 };
108 
109 static struct event_group *known_event_groups[] = {
110 	&energy_0x26696143,
111 	&perf_0x26557651,
112 };
113 
114 #define for_each_event_group(_peg)						\
115 	for (_peg = known_event_groups;						\
116 	     _peg < &known_event_groups[ARRAY_SIZE(known_event_groups)];	\
117 	     _peg++)
118 
119 static bool skip_telem_region(struct telemetry_region *tr, struct event_group *e)
120 {
121 	if (tr->guid != e->guid)
122 		return true;
123 	if (tr->plat_info.package_id >= topology_max_packages()) {
124 		pr_warn("Bad package %u in guid 0x%x\n", tr->plat_info.package_id,
125 			tr->guid);
126 		return true;
127 	}
128 	if (tr->size != e->mmio_size) {
129 		pr_warn("MMIO space wrong size (%zu bytes) for guid 0x%x. Expected %zu bytes.\n",
130 			tr->size, e->guid, e->mmio_size);
131 		return true;
132 	}
133 
134 	return false;
135 }
136 
137 static bool group_has_usable_regions(struct event_group *e, struct pmt_feature_group *p)
138 {
139 	bool usable_regions = false;
140 
141 	for (int i = 0; i < p->count; i++) {
142 		if (skip_telem_region(&p->regions[i], e)) {
143 			/*
144 			 * Clear the address field of regions that did not pass the checks in
145 			 * skip_telem_region() so they will not be used by intel_aet_read_event().
146 			 * This is safe to do because intel_pmt_get_regions_by_feature() allocates
147 			 * a new pmt_feature_group structure to return to each caller and only makes
148 			 * use of the pmt_feature_group::kref field when intel_pmt_put_feature_group()
149 			 * returns the structure.
150 			 */
151 			p->regions[i].addr = NULL;
152 
153 			continue;
154 		}
155 		usable_regions = true;
156 	}
157 
158 	return usable_regions;
159 }
160 
161 static bool enable_events(struct event_group *e, struct pmt_feature_group *p)
162 {
163 	struct rdt_resource *r = &rdt_resources_all[RDT_RESOURCE_PERF_PKG].r_resctrl;
164 	int skipped_events = 0;
165 
166 	if (!group_has_usable_regions(e, p))
167 		return false;
168 
169 	for (int j = 0; j < e->num_events; j++) {
170 		if (!resctrl_enable_mon_event(e->evts[j].id, true,
171 					      e->evts[j].bin_bits, &e->evts[j]))
172 			skipped_events++;
173 	}
174 	if (e->num_events == skipped_events) {
175 		pr_info("No events enabled in %s %s:0x%x\n", r->name, e->pfname, e->guid);
176 		return false;
177 	}
178 
179 	return true;
180 }
181 
182 static enum pmt_feature_id lookup_pfid(const char *pfname)
183 {
184 	if (!strcmp(pfname, "energy"))
185 		return FEATURE_PER_RMID_ENERGY_TELEM;
186 	else if (!strcmp(pfname, "perf"))
187 		return FEATURE_PER_RMID_PERF_TELEM;
188 
189 	pr_warn("Unknown PMT feature name '%s'\n", pfname);
190 
191 	return FEATURE_INVALID;
192 }
193 
194 /*
195  * Request a copy of struct pmt_feature_group for each event group. If there is
196  * one, the returned structure has an array of telemetry_region structures,
197  * each element of the array describes one telemetry aggregator. The
198  * telemetry aggregators may have different GUIDs so obtain duplicate struct
199  * pmt_feature_group for event groups with same feature type but different
200  * GUID. Post-processing ensures an event group can only use the telemetry
201  * aggregators that match its GUID. An event group keeps a pointer to its
202  * struct pmt_feature_group to indicate that its events are successfully
203  * enabled.
204  */
205 bool intel_aet_get_events(void)
206 {
207 	struct pmt_feature_group *p;
208 	enum pmt_feature_id pfid;
209 	struct event_group **peg;
210 	bool ret = false;
211 
212 	for_each_event_group(peg) {
213 		pfid = lookup_pfid((*peg)->pfname);
214 		p = intel_pmt_get_regions_by_feature(pfid);
215 		if (IS_ERR_OR_NULL(p))
216 			continue;
217 		if (enable_events(*peg, p)) {
218 			(*peg)->pfg = p;
219 			ret = true;
220 		} else {
221 			intel_pmt_put_feature_group(p);
222 		}
223 	}
224 
225 	return ret;
226 }
227 
228 void __exit intel_aet_exit(void)
229 {
230 	struct event_group **peg;
231 
232 	for_each_event_group(peg) {
233 		if ((*peg)->pfg) {
234 			intel_pmt_put_feature_group((*peg)->pfg);
235 			(*peg)->pfg = NULL;
236 		}
237 	}
238 }
239 
240 #define DATA_VALID	BIT_ULL(63)
241 #define DATA_BITS	GENMASK_ULL(62, 0)
242 
243 /*
244  * Read counter for an event on a domain (summing all aggregators on the
245  * domain). If an aggregator hasn't received any data for a specific RMID,
246  * the MMIO read indicates that data is not valid.  Return success if at
247  * least one aggregator has valid data.
248  */
249 int intel_aet_read_event(int domid, u32 rmid, void *arch_priv, u64 *val)
250 {
251 	struct pmt_event *pevt = arch_priv;
252 	struct event_group *e;
253 	bool valid = false;
254 	u64 total = 0;
255 	u64 evtcount;
256 	void *pevt0;
257 	u32 idx;
258 
259 	pevt0 = pevt - pevt->idx;
260 	e = container_of(pevt0, struct event_group, evts);
261 	idx = rmid * e->num_events;
262 	idx += pevt->idx;
263 
264 	if (idx * sizeof(u64) + sizeof(u64) > e->mmio_size) {
265 		pr_warn_once("MMIO index %u out of range\n", idx);
266 		return -EIO;
267 	}
268 
269 	for (int i = 0; i < e->pfg->count; i++) {
270 		if (!e->pfg->regions[i].addr)
271 			continue;
272 		if (e->pfg->regions[i].plat_info.package_id != domid)
273 			continue;
274 		evtcount = readq(e->pfg->regions[i].addr + idx * sizeof(u64));
275 		if (!(evtcount & DATA_VALID))
276 			continue;
277 		total += evtcount & DATA_BITS;
278 		valid = true;
279 	}
280 
281 	if (valid)
282 		*val = total;
283 
284 	return valid ? 0 : -EINVAL;
285 }
286