xref: /linux/arch/x86/kernel/cpu/resctrl/intel_aet.c (revision 8f6b6ad69b50bf16bb762ffafbfa44a4884f9a17)
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/compiler_types.h>
15 #include <linux/err.h>
16 #include <linux/init.h>
17 #include <linux/intel_pmt_features.h>
18 #include <linux/intel_vsec.h>
19 #include <linux/resctrl.h>
20 #include <linux/resctrl_types.h>
21 #include <linux/stddef.h>
22 #include <linux/types.h>
23 
24 #include "internal.h"
25 
26 /**
27  * struct pmt_event - Telemetry event.
28  * @id:		Resctrl event id.
29  * @idx:	Counter index within each per-RMID block of counters.
30  * @bin_bits:	Zero for integer valued events, else number bits in fraction
31  *		part of fixed-point.
32  */
33 struct pmt_event {
34 	enum resctrl_event_id	id;
35 	unsigned int		idx;
36 	unsigned int		bin_bits;
37 };
38 
39 #define EVT(_id, _idx, _bits) { .id = _id, .idx = _idx, .bin_bits = _bits }
40 
41 /**
42  * struct event_group - Events with the same feature type ("energy" or "perf") and GUID.
43  * @pfname:		PMT feature name ("energy" or "perf") of this event group.
44  * @pfg:		Points to the aggregated telemetry space information
45  *			returned by the intel_pmt_get_regions_by_feature()
46  *			call to the INTEL_PMT_TELEMETRY driver that contains
47  *			data for all telemetry regions of type @pfname.
48  *			Valid if the system supports the event group,
49  *			NULL otherwise.
50  * @guid:		Unique number per XML description file.
51  * @mmio_size:		Number of bytes of MMIO registers for this group.
52  * @num_events:		Number of events in this group.
53  * @evts:		Array of event descriptors.
54  */
55 struct event_group {
56 	/* Data fields for additional structures to manage this group. */
57 	const char			*pfname;
58 	struct pmt_feature_group	*pfg;
59 
60 	/* Remaining fields initialized from XML file. */
61 	u32				guid;
62 	size_t				mmio_size;
63 	unsigned int			num_events;
64 	struct pmt_event		evts[] __counted_by(num_events);
65 };
66 
67 #define XML_MMIO_SIZE(num_rmids, num_events, num_extra_status) \
68 		      (((num_rmids) * (num_events) + (num_extra_status)) * sizeof(u64))
69 
70 /*
71  * Link: https://github.com/intel/Intel-PMT/blob/main/xml/CWF/OOBMSM/RMID-ENERGY/cwf_aggregator.xml
72  */
73 static struct event_group energy_0x26696143 = {
74 	.pfname		= "energy",
75 	.guid		= 0x26696143,
76 	.mmio_size	= XML_MMIO_SIZE(576, 2, 3),
77 	.num_events	= 2,
78 	.evts		= {
79 		EVT(PMT_EVENT_ENERGY, 0, 18),
80 		EVT(PMT_EVENT_ACTIVITY, 1, 18),
81 	}
82 };
83 
84 /*
85  * Link: https://github.com/intel/Intel-PMT/blob/main/xml/CWF/OOBMSM/RMID-PERF/cwf_aggregator.xml
86  */
87 static struct event_group perf_0x26557651 = {
88 	.pfname		= "perf",
89 	.guid		= 0x26557651,
90 	.mmio_size	= XML_MMIO_SIZE(576, 7, 3),
91 	.num_events	= 7,
92 	.evts		= {
93 		EVT(PMT_EVENT_STALLS_LLC_HIT, 0, 0),
94 		EVT(PMT_EVENT_C1_RES, 1, 0),
95 		EVT(PMT_EVENT_UNHALTED_CORE_CYCLES, 2, 0),
96 		EVT(PMT_EVENT_STALLS_LLC_MISS, 3, 0),
97 		EVT(PMT_EVENT_AUTO_C6_RES, 4, 0),
98 		EVT(PMT_EVENT_UNHALTED_REF_CYCLES, 5, 0),
99 		EVT(PMT_EVENT_UOPS_RETIRED, 6, 0),
100 	}
101 };
102 
103 static struct event_group *known_event_groups[] = {
104 	&energy_0x26696143,
105 	&perf_0x26557651,
106 };
107 
108 #define for_each_event_group(_peg)						\
109 	for (_peg = known_event_groups;						\
110 	     _peg < &known_event_groups[ARRAY_SIZE(known_event_groups)];	\
111 	     _peg++)
112 
113 /* Stub for now */
114 static bool enable_events(struct event_group *e, struct pmt_feature_group *p)
115 {
116 	return false;
117 }
118 
119 static enum pmt_feature_id lookup_pfid(const char *pfname)
120 {
121 	if (!strcmp(pfname, "energy"))
122 		return FEATURE_PER_RMID_ENERGY_TELEM;
123 	else if (!strcmp(pfname, "perf"))
124 		return FEATURE_PER_RMID_PERF_TELEM;
125 
126 	pr_warn("Unknown PMT feature name '%s'\n", pfname);
127 
128 	return FEATURE_INVALID;
129 }
130 
131 /*
132  * Request a copy of struct pmt_feature_group for each event group. If there is
133  * one, the returned structure has an array of telemetry_region structures,
134  * each element of the array describes one telemetry aggregator. The
135  * telemetry aggregators may have different GUIDs so obtain duplicate struct
136  * pmt_feature_group for event groups with same feature type but different
137  * GUID. Post-processing ensures an event group can only use the telemetry
138  * aggregators that match its GUID. An event group keeps a pointer to its
139  * struct pmt_feature_group to indicate that its events are successfully
140  * enabled.
141  */
142 bool intel_aet_get_events(void)
143 {
144 	struct pmt_feature_group *p;
145 	enum pmt_feature_id pfid;
146 	struct event_group **peg;
147 	bool ret = false;
148 
149 	for_each_event_group(peg) {
150 		pfid = lookup_pfid((*peg)->pfname);
151 		p = intel_pmt_get_regions_by_feature(pfid);
152 		if (IS_ERR_OR_NULL(p))
153 			continue;
154 		if (enable_events(*peg, p)) {
155 			(*peg)->pfg = p;
156 			ret = true;
157 		} else {
158 			intel_pmt_put_feature_group(p);
159 		}
160 	}
161 
162 	return ret;
163 }
164 
165 void __exit intel_aet_exit(void)
166 {
167 	struct event_group **peg;
168 
169 	for_each_event_group(peg) {
170 		if ((*peg)->pfg) {
171 			intel_pmt_put_feature_group((*peg)->pfg);
172 			(*peg)->pfg = NULL;
173 		}
174 	}
175 }
176