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