11fb2daa6STony Luck // SPDX-License-Identifier: GPL-2.0-only 21fb2daa6STony Luck /* 31fb2daa6STony Luck * Resource Director Technology(RDT) 41fb2daa6STony Luck * - Intel Application Energy Telemetry 51fb2daa6STony Luck * 61fb2daa6STony Luck * Copyright (C) 2025 Intel Corporation 71fb2daa6STony Luck * 81fb2daa6STony Luck * Author: 91fb2daa6STony Luck * Tony Luck <tony.luck@intel.com> 101fb2daa6STony Luck */ 111fb2daa6STony Luck 121fb2daa6STony Luck #define pr_fmt(fmt) "resctrl: " fmt 131fb2daa6STony Luck 1451541f6cSTony Luck #include <linux/bits.h> 158f6b6ad6STony Luck #include <linux/compiler_types.h> 1651541f6cSTony Luck #include <linux/container_of.h> 17f4e0cd80STony Luck #include <linux/cpumask.h> 181fb2daa6STony Luck #include <linux/err.h> 1951541f6cSTony Luck #include <linux/errno.h> 20f4e0cd80STony Luck #include <linux/gfp_types.h> 211fb2daa6STony Luck #include <linux/init.h> 221fb2daa6STony Luck #include <linux/intel_pmt_features.h> 231fb2daa6STony Luck #include <linux/intel_vsec.h> 2451541f6cSTony Luck #include <linux/io.h> 257e6df961STony Luck #include <linux/printk.h> 26f4e0cd80STony Luck #include <linux/rculist.h> 27f4e0cd80STony Luck #include <linux/rcupdate.h> 281fb2daa6STony Luck #include <linux/resctrl.h> 298f6b6ad6STony Luck #include <linux/resctrl_types.h> 30f4e0cd80STony Luck #include <linux/slab.h> 311fb2daa6STony Luck #include <linux/stddef.h> 327e6df961STony Luck #include <linux/topology.h> 338f6b6ad6STony Luck #include <linux/types.h> 341fb2daa6STony Luck 351fb2daa6STony Luck #include "internal.h" 361fb2daa6STony Luck 371fb2daa6STony Luck /** 388f6b6ad6STony Luck * struct pmt_event - Telemetry event. 398f6b6ad6STony Luck * @id: Resctrl event id. 408f6b6ad6STony Luck * @idx: Counter index within each per-RMID block of counters. 418f6b6ad6STony Luck * @bin_bits: Zero for integer valued events, else number bits in fraction 428f6b6ad6STony Luck * part of fixed-point. 438f6b6ad6STony Luck */ 448f6b6ad6STony Luck struct pmt_event { 458f6b6ad6STony Luck enum resctrl_event_id id; 468f6b6ad6STony Luck unsigned int idx; 478f6b6ad6STony Luck unsigned int bin_bits; 488f6b6ad6STony Luck }; 498f6b6ad6STony Luck 508f6b6ad6STony Luck #define EVT(_id, _idx, _bits) { .id = _id, .idx = _idx, .bin_bits = _bits } 518f6b6ad6STony Luck 528f6b6ad6STony Luck /** 531fb2daa6STony Luck * struct event_group - Events with the same feature type ("energy" or "perf") and GUID. 541fb2daa6STony Luck * @pfname: PMT feature name ("energy" or "perf") of this event group. 55*842e7f97STony Luck * Used by boot rdt= option. 561fb2daa6STony Luck * @pfg: Points to the aggregated telemetry space information 571fb2daa6STony Luck * returned by the intel_pmt_get_regions_by_feature() 581fb2daa6STony Luck * call to the INTEL_PMT_TELEMETRY driver that contains 591fb2daa6STony Luck * data for all telemetry regions of type @pfname. 601fb2daa6STony Luck * Valid if the system supports the event group, 611fb2daa6STony Luck * NULL otherwise. 62*842e7f97STony Luck * @force_off: True when "rdt" command line or architecture code disables 63*842e7f97STony Luck * this event group. 64*842e7f97STony Luck * @force_on: True when "rdt" command line overrides disable of this 65*842e7f97STony Luck * event group. 668f6b6ad6STony Luck * @guid: Unique number per XML description file. 678f6b6ad6STony Luck * @mmio_size: Number of bytes of MMIO registers for this group. 688f6b6ad6STony Luck * @num_events: Number of events in this group. 698f6b6ad6STony Luck * @evts: Array of event descriptors. 701fb2daa6STony Luck */ 711fb2daa6STony Luck struct event_group { 721fb2daa6STony Luck /* Data fields for additional structures to manage this group. */ 731fb2daa6STony Luck const char *pfname; 741fb2daa6STony Luck struct pmt_feature_group *pfg; 75*842e7f97STony Luck bool force_off, force_on; 768f6b6ad6STony Luck 778f6b6ad6STony Luck /* Remaining fields initialized from XML file. */ 788f6b6ad6STony Luck u32 guid; 798f6b6ad6STony Luck size_t mmio_size; 808f6b6ad6STony Luck unsigned int num_events; 818f6b6ad6STony Luck struct pmt_event evts[] __counted_by(num_events); 828f6b6ad6STony Luck }; 838f6b6ad6STony Luck 848f6b6ad6STony Luck #define XML_MMIO_SIZE(num_rmids, num_events, num_extra_status) \ 858f6b6ad6STony Luck (((num_rmids) * (num_events) + (num_extra_status)) * sizeof(u64)) 868f6b6ad6STony Luck 878f6b6ad6STony Luck /* 888f6b6ad6STony Luck * Link: https://github.com/intel/Intel-PMT/blob/main/xml/CWF/OOBMSM/RMID-ENERGY/cwf_aggregator.xml 898f6b6ad6STony Luck */ 908f6b6ad6STony Luck static struct event_group energy_0x26696143 = { 918f6b6ad6STony Luck .pfname = "energy", 928f6b6ad6STony Luck .guid = 0x26696143, 938f6b6ad6STony Luck .mmio_size = XML_MMIO_SIZE(576, 2, 3), 948f6b6ad6STony Luck .num_events = 2, 958f6b6ad6STony Luck .evts = { 968f6b6ad6STony Luck EVT(PMT_EVENT_ENERGY, 0, 18), 978f6b6ad6STony Luck EVT(PMT_EVENT_ACTIVITY, 1, 18), 988f6b6ad6STony Luck } 998f6b6ad6STony Luck }; 1008f6b6ad6STony Luck 1018f6b6ad6STony Luck /* 1028f6b6ad6STony Luck * Link: https://github.com/intel/Intel-PMT/blob/main/xml/CWF/OOBMSM/RMID-PERF/cwf_aggregator.xml 1038f6b6ad6STony Luck */ 1048f6b6ad6STony Luck static struct event_group perf_0x26557651 = { 1058f6b6ad6STony Luck .pfname = "perf", 1068f6b6ad6STony Luck .guid = 0x26557651, 1078f6b6ad6STony Luck .mmio_size = XML_MMIO_SIZE(576, 7, 3), 1088f6b6ad6STony Luck .num_events = 7, 1098f6b6ad6STony Luck .evts = { 1108f6b6ad6STony Luck EVT(PMT_EVENT_STALLS_LLC_HIT, 0, 0), 1118f6b6ad6STony Luck EVT(PMT_EVENT_C1_RES, 1, 0), 1128f6b6ad6STony Luck EVT(PMT_EVENT_UNHALTED_CORE_CYCLES, 2, 0), 1138f6b6ad6STony Luck EVT(PMT_EVENT_STALLS_LLC_MISS, 3, 0), 1148f6b6ad6STony Luck EVT(PMT_EVENT_AUTO_C6_RES, 4, 0), 1158f6b6ad6STony Luck EVT(PMT_EVENT_UNHALTED_REF_CYCLES, 5, 0), 1168f6b6ad6STony Luck EVT(PMT_EVENT_UOPS_RETIRED, 6, 0), 1178f6b6ad6STony Luck } 1181fb2daa6STony Luck }; 1191fb2daa6STony Luck 1201fb2daa6STony Luck static struct event_group *known_event_groups[] = { 1218f6b6ad6STony Luck &energy_0x26696143, 1228f6b6ad6STony Luck &perf_0x26557651, 1231fb2daa6STony Luck }; 1241fb2daa6STony Luck 1251fb2daa6STony Luck #define for_each_event_group(_peg) \ 1261fb2daa6STony Luck for (_peg = known_event_groups; \ 1271fb2daa6STony Luck _peg < &known_event_groups[ARRAY_SIZE(known_event_groups)]; \ 1281fb2daa6STony Luck _peg++) 1291fb2daa6STony Luck 130*842e7f97STony Luck bool intel_handle_aet_option(bool force_off, char *tok) 131*842e7f97STony Luck { 132*842e7f97STony Luck struct event_group **peg; 133*842e7f97STony Luck bool ret = false; 134*842e7f97STony Luck u32 guid = 0; 135*842e7f97STony Luck char *name; 136*842e7f97STony Luck 137*842e7f97STony Luck if (!tok) 138*842e7f97STony Luck return false; 139*842e7f97STony Luck 140*842e7f97STony Luck name = strsep(&tok, ":"); 141*842e7f97STony Luck if (tok && kstrtou32(tok, 16, &guid)) 142*842e7f97STony Luck return false; 143*842e7f97STony Luck 144*842e7f97STony Luck for_each_event_group(peg) { 145*842e7f97STony Luck if (strcmp(name, (*peg)->pfname)) 146*842e7f97STony Luck continue; 147*842e7f97STony Luck if (guid && (*peg)->guid != guid) 148*842e7f97STony Luck continue; 149*842e7f97STony Luck if (force_off) 150*842e7f97STony Luck (*peg)->force_off = true; 151*842e7f97STony Luck else 152*842e7f97STony Luck (*peg)->force_on = true; 153*842e7f97STony Luck ret = true; 154*842e7f97STony Luck } 155*842e7f97STony Luck 156*842e7f97STony Luck return ret; 157*842e7f97STony Luck } 158*842e7f97STony Luck 1597e6df961STony Luck static bool skip_telem_region(struct telemetry_region *tr, struct event_group *e) 1607e6df961STony Luck { 1617e6df961STony Luck if (tr->guid != e->guid) 1627e6df961STony Luck return true; 1637e6df961STony Luck if (tr->plat_info.package_id >= topology_max_packages()) { 1647e6df961STony Luck pr_warn("Bad package %u in guid 0x%x\n", tr->plat_info.package_id, 1657e6df961STony Luck tr->guid); 1667e6df961STony Luck return true; 1677e6df961STony Luck } 1687e6df961STony Luck if (tr->size != e->mmio_size) { 1697e6df961STony Luck pr_warn("MMIO space wrong size (%zu bytes) for guid 0x%x. Expected %zu bytes.\n", 1707e6df961STony Luck tr->size, e->guid, e->mmio_size); 1717e6df961STony Luck return true; 1727e6df961STony Luck } 1737e6df961STony Luck 1747e6df961STony Luck return false; 1757e6df961STony Luck } 1767e6df961STony Luck 1777e6df961STony Luck static bool group_has_usable_regions(struct event_group *e, struct pmt_feature_group *p) 1787e6df961STony Luck { 1797e6df961STony Luck bool usable_regions = false; 1807e6df961STony Luck 1817e6df961STony Luck for (int i = 0; i < p->count; i++) { 1827e6df961STony Luck if (skip_telem_region(&p->regions[i], e)) { 1837e6df961STony Luck /* 1847e6df961STony Luck * Clear the address field of regions that did not pass the checks in 1857e6df961STony Luck * skip_telem_region() so they will not be used by intel_aet_read_event(). 1867e6df961STony Luck * This is safe to do because intel_pmt_get_regions_by_feature() allocates 1877e6df961STony Luck * a new pmt_feature_group structure to return to each caller and only makes 1887e6df961STony Luck * use of the pmt_feature_group::kref field when intel_pmt_put_feature_group() 1897e6df961STony Luck * returns the structure. 1907e6df961STony Luck */ 1917e6df961STony Luck p->regions[i].addr = NULL; 1927e6df961STony Luck 1937e6df961STony Luck continue; 1947e6df961STony Luck } 1957e6df961STony Luck usable_regions = true; 1967e6df961STony Luck } 1977e6df961STony Luck 1987e6df961STony Luck return usable_regions; 1997e6df961STony Luck } 2007e6df961STony Luck 2011fb2daa6STony Luck static bool enable_events(struct event_group *e, struct pmt_feature_group *p) 2021fb2daa6STony Luck { 2037e6df961STony Luck struct rdt_resource *r = &rdt_resources_all[RDT_RESOURCE_PERF_PKG].r_resctrl; 2047e6df961STony Luck int skipped_events = 0; 2057e6df961STony Luck 206*842e7f97STony Luck if (e->force_off) 207*842e7f97STony Luck return false; 208*842e7f97STony Luck 2097e6df961STony Luck if (!group_has_usable_regions(e, p)) 2101fb2daa6STony Luck return false; 2117e6df961STony Luck 2127e6df961STony Luck for (int j = 0; j < e->num_events; j++) { 2137e6df961STony Luck if (!resctrl_enable_mon_event(e->evts[j].id, true, 2147e6df961STony Luck e->evts[j].bin_bits, &e->evts[j])) 2157e6df961STony Luck skipped_events++; 2167e6df961STony Luck } 2177e6df961STony Luck if (e->num_events == skipped_events) { 2187e6df961STony Luck pr_info("No events enabled in %s %s:0x%x\n", r->name, e->pfname, e->guid); 2197e6df961STony Luck return false; 2207e6df961STony Luck } 2217e6df961STony Luck 2227e6df961STony Luck return true; 2231fb2daa6STony Luck } 2241fb2daa6STony Luck 2251fb2daa6STony Luck static enum pmt_feature_id lookup_pfid(const char *pfname) 2261fb2daa6STony Luck { 2271fb2daa6STony Luck if (!strcmp(pfname, "energy")) 2281fb2daa6STony Luck return FEATURE_PER_RMID_ENERGY_TELEM; 2291fb2daa6STony Luck else if (!strcmp(pfname, "perf")) 2301fb2daa6STony Luck return FEATURE_PER_RMID_PERF_TELEM; 2311fb2daa6STony Luck 2321fb2daa6STony Luck pr_warn("Unknown PMT feature name '%s'\n", pfname); 2331fb2daa6STony Luck 2341fb2daa6STony Luck return FEATURE_INVALID; 2351fb2daa6STony Luck } 2361fb2daa6STony Luck 2371fb2daa6STony Luck /* 2381fb2daa6STony Luck * Request a copy of struct pmt_feature_group for each event group. If there is 2391fb2daa6STony Luck * one, the returned structure has an array of telemetry_region structures, 2401fb2daa6STony Luck * each element of the array describes one telemetry aggregator. The 2411fb2daa6STony Luck * telemetry aggregators may have different GUIDs so obtain duplicate struct 2421fb2daa6STony Luck * pmt_feature_group for event groups with same feature type but different 2431fb2daa6STony Luck * GUID. Post-processing ensures an event group can only use the telemetry 2441fb2daa6STony Luck * aggregators that match its GUID. An event group keeps a pointer to its 2451fb2daa6STony Luck * struct pmt_feature_group to indicate that its events are successfully 2461fb2daa6STony Luck * enabled. 2471fb2daa6STony Luck */ 2481fb2daa6STony Luck bool intel_aet_get_events(void) 2491fb2daa6STony Luck { 2501fb2daa6STony Luck struct pmt_feature_group *p; 2511fb2daa6STony Luck enum pmt_feature_id pfid; 2521fb2daa6STony Luck struct event_group **peg; 2531fb2daa6STony Luck bool ret = false; 2541fb2daa6STony Luck 2551fb2daa6STony Luck for_each_event_group(peg) { 2561fb2daa6STony Luck pfid = lookup_pfid((*peg)->pfname); 2571fb2daa6STony Luck p = intel_pmt_get_regions_by_feature(pfid); 2581fb2daa6STony Luck if (IS_ERR_OR_NULL(p)) 2591fb2daa6STony Luck continue; 2601fb2daa6STony Luck if (enable_events(*peg, p)) { 2611fb2daa6STony Luck (*peg)->pfg = p; 2621fb2daa6STony Luck ret = true; 2631fb2daa6STony Luck } else { 2641fb2daa6STony Luck intel_pmt_put_feature_group(p); 2651fb2daa6STony Luck } 2661fb2daa6STony Luck } 2671fb2daa6STony Luck 2681fb2daa6STony Luck return ret; 2691fb2daa6STony Luck } 2701fb2daa6STony Luck 2711fb2daa6STony Luck void __exit intel_aet_exit(void) 2721fb2daa6STony Luck { 2731fb2daa6STony Luck struct event_group **peg; 2741fb2daa6STony Luck 2751fb2daa6STony Luck for_each_event_group(peg) { 2761fb2daa6STony Luck if ((*peg)->pfg) { 2771fb2daa6STony Luck intel_pmt_put_feature_group((*peg)->pfg); 2781fb2daa6STony Luck (*peg)->pfg = NULL; 2791fb2daa6STony Luck } 2801fb2daa6STony Luck } 2811fb2daa6STony Luck } 28251541f6cSTony Luck 28351541f6cSTony Luck #define DATA_VALID BIT_ULL(63) 28451541f6cSTony Luck #define DATA_BITS GENMASK_ULL(62, 0) 28551541f6cSTony Luck 28651541f6cSTony Luck /* 28751541f6cSTony Luck * Read counter for an event on a domain (summing all aggregators on the 28851541f6cSTony Luck * domain). If an aggregator hasn't received any data for a specific RMID, 28951541f6cSTony Luck * the MMIO read indicates that data is not valid. Return success if at 29051541f6cSTony Luck * least one aggregator has valid data. 29151541f6cSTony Luck */ 29251541f6cSTony Luck int intel_aet_read_event(int domid, u32 rmid, void *arch_priv, u64 *val) 29351541f6cSTony Luck { 29451541f6cSTony Luck struct pmt_event *pevt = arch_priv; 29551541f6cSTony Luck struct event_group *e; 29651541f6cSTony Luck bool valid = false; 29751541f6cSTony Luck u64 total = 0; 29851541f6cSTony Luck u64 evtcount; 29951541f6cSTony Luck void *pevt0; 30051541f6cSTony Luck u32 idx; 30151541f6cSTony Luck 30251541f6cSTony Luck pevt0 = pevt - pevt->idx; 30351541f6cSTony Luck e = container_of(pevt0, struct event_group, evts); 30451541f6cSTony Luck idx = rmid * e->num_events; 30551541f6cSTony Luck idx += pevt->idx; 30651541f6cSTony Luck 30751541f6cSTony Luck if (idx * sizeof(u64) + sizeof(u64) > e->mmio_size) { 30851541f6cSTony Luck pr_warn_once("MMIO index %u out of range\n", idx); 30951541f6cSTony Luck return -EIO; 31051541f6cSTony Luck } 31151541f6cSTony Luck 31251541f6cSTony Luck for (int i = 0; i < e->pfg->count; i++) { 31351541f6cSTony Luck if (!e->pfg->regions[i].addr) 31451541f6cSTony Luck continue; 31551541f6cSTony Luck if (e->pfg->regions[i].plat_info.package_id != domid) 31651541f6cSTony Luck continue; 31751541f6cSTony Luck evtcount = readq(e->pfg->regions[i].addr + idx * sizeof(u64)); 31851541f6cSTony Luck if (!(evtcount & DATA_VALID)) 31951541f6cSTony Luck continue; 32051541f6cSTony Luck total += evtcount & DATA_BITS; 32151541f6cSTony Luck valid = true; 32251541f6cSTony Luck } 32351541f6cSTony Luck 32451541f6cSTony Luck if (valid) 32551541f6cSTony Luck *val = total; 32651541f6cSTony Luck 32751541f6cSTony Luck return valid ? 0 : -EINVAL; 32851541f6cSTony Luck } 329f4e0cd80STony Luck 330f4e0cd80STony Luck void intel_aet_mon_domain_setup(int cpu, int id, struct rdt_resource *r, 331f4e0cd80STony Luck struct list_head *add_pos) 332f4e0cd80STony Luck { 333f4e0cd80STony Luck struct rdt_perf_pkg_mon_domain *d; 334f4e0cd80STony Luck int err; 335f4e0cd80STony Luck 336f4e0cd80STony Luck d = kzalloc_node(sizeof(*d), GFP_KERNEL, cpu_to_node(cpu)); 337f4e0cd80STony Luck if (!d) 338f4e0cd80STony Luck return; 339f4e0cd80STony Luck 340f4e0cd80STony Luck d->hdr.id = id; 341f4e0cd80STony Luck d->hdr.type = RESCTRL_MON_DOMAIN; 342f4e0cd80STony Luck d->hdr.rid = RDT_RESOURCE_PERF_PKG; 343f4e0cd80STony Luck cpumask_set_cpu(cpu, &d->hdr.cpu_mask); 344f4e0cd80STony Luck list_add_tail_rcu(&d->hdr.list, add_pos); 345f4e0cd80STony Luck 346f4e0cd80STony Luck err = resctrl_online_mon_domain(r, &d->hdr); 347f4e0cd80STony Luck if (err) { 348f4e0cd80STony Luck list_del_rcu(&d->hdr.list); 349f4e0cd80STony Luck synchronize_rcu(); 350f4e0cd80STony Luck kfree(d); 351f4e0cd80STony Luck } 352f4e0cd80STony Luck } 353