162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support Intel IOMMU PerfMon 462306a36Sopenharmony_ci * Copyright(c) 2023 Intel Corporation. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#define pr_fmt(fmt) "DMAR: " fmt 762306a36Sopenharmony_ci#define dev_fmt(fmt) pr_fmt(fmt) 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dmar.h> 1062306a36Sopenharmony_ci#include "iommu.h" 1162306a36Sopenharmony_ci#include "perfmon.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciPMU_FORMAT_ATTR(event, "config:0-27"); /* ES: Events Select */ 1462306a36Sopenharmony_ciPMU_FORMAT_ATTR(event_group, "config:28-31"); /* EGI: Event Group Index */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic struct attribute *iommu_pmu_format_attrs[] = { 1762306a36Sopenharmony_ci &format_attr_event_group.attr, 1862306a36Sopenharmony_ci &format_attr_event.attr, 1962306a36Sopenharmony_ci NULL 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic struct attribute_group iommu_pmu_format_attr_group = { 2362306a36Sopenharmony_ci .name = "format", 2462306a36Sopenharmony_ci .attrs = iommu_pmu_format_attrs, 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* The available events are added in attr_update later */ 2862306a36Sopenharmony_cistatic struct attribute *attrs_empty[] = { 2962306a36Sopenharmony_ci NULL 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct attribute_group iommu_pmu_events_attr_group = { 3362306a36Sopenharmony_ci .name = "events", 3462306a36Sopenharmony_ci .attrs = attrs_empty, 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic cpumask_t iommu_pmu_cpu_mask; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic ssize_t 4062306a36Sopenharmony_cicpumask_show(struct device *dev, struct device_attribute *attr, char *buf) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, &iommu_pmu_cpu_mask); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct attribute *iommu_pmu_cpumask_attrs[] = { 4762306a36Sopenharmony_ci &dev_attr_cpumask.attr, 4862306a36Sopenharmony_ci NULL 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic struct attribute_group iommu_pmu_cpumask_attr_group = { 5262306a36Sopenharmony_ci .attrs = iommu_pmu_cpumask_attrs, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct attribute_group *iommu_pmu_attr_groups[] = { 5662306a36Sopenharmony_ci &iommu_pmu_format_attr_group, 5762306a36Sopenharmony_ci &iommu_pmu_events_attr_group, 5862306a36Sopenharmony_ci &iommu_pmu_cpumask_attr_group, 5962306a36Sopenharmony_ci NULL 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline struct iommu_pmu *dev_to_iommu_pmu(struct device *dev) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * The perf_event creates its own dev for each PMU. 6662306a36Sopenharmony_ci * See pmu_dev_alloc() 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci return container_of(dev_get_drvdata(dev), struct iommu_pmu, pmu); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define IOMMU_PMU_ATTR(_name, _format, _filter) \ 7262306a36Sopenharmony_ci PMU_FORMAT_ATTR(_name, _format); \ 7362306a36Sopenharmony_ci \ 7462306a36Sopenharmony_cistatic struct attribute *_name##_attr[] = { \ 7562306a36Sopenharmony_ci &format_attr_##_name.attr, \ 7662306a36Sopenharmony_ci NULL \ 7762306a36Sopenharmony_ci}; \ 7862306a36Sopenharmony_ci \ 7962306a36Sopenharmony_cistatic umode_t \ 8062306a36Sopenharmony_ci_name##_is_visible(struct kobject *kobj, struct attribute *attr, int i) \ 8162306a36Sopenharmony_ci{ \ 8262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); \ 8362306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = dev_to_iommu_pmu(dev); \ 8462306a36Sopenharmony_ci \ 8562306a36Sopenharmony_ci if (!iommu_pmu) \ 8662306a36Sopenharmony_ci return 0; \ 8762306a36Sopenharmony_ci return (iommu_pmu->filter & _filter) ? attr->mode : 0; \ 8862306a36Sopenharmony_ci} \ 8962306a36Sopenharmony_ci \ 9062306a36Sopenharmony_cistatic struct attribute_group _name = { \ 9162306a36Sopenharmony_ci .name = "format", \ 9262306a36Sopenharmony_ci .attrs = _name##_attr, \ 9362306a36Sopenharmony_ci .is_visible = _name##_is_visible, \ 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_requester_id_en, "config1:0", IOMMU_PMU_FILTER_REQUESTER_ID); 9762306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_domain_en, "config1:1", IOMMU_PMU_FILTER_DOMAIN); 9862306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_pasid_en, "config1:2", IOMMU_PMU_FILTER_PASID); 9962306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_ats_en, "config1:3", IOMMU_PMU_FILTER_ATS); 10062306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_page_table_en, "config1:4", IOMMU_PMU_FILTER_PAGE_TABLE); 10162306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_requester_id, "config1:16-31", IOMMU_PMU_FILTER_REQUESTER_ID); 10262306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_domain, "config1:32-47", IOMMU_PMU_FILTER_DOMAIN); 10362306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_pasid, "config2:0-21", IOMMU_PMU_FILTER_PASID); 10462306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_ats, "config2:24-28", IOMMU_PMU_FILTER_ATS); 10562306a36Sopenharmony_ciIOMMU_PMU_ATTR(filter_page_table, "config2:32-36", IOMMU_PMU_FILTER_PAGE_TABLE); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define iommu_pmu_en_requester_id(e) ((e) & 0x1) 10862306a36Sopenharmony_ci#define iommu_pmu_en_domain(e) (((e) >> 1) & 0x1) 10962306a36Sopenharmony_ci#define iommu_pmu_en_pasid(e) (((e) >> 2) & 0x1) 11062306a36Sopenharmony_ci#define iommu_pmu_en_ats(e) (((e) >> 3) & 0x1) 11162306a36Sopenharmony_ci#define iommu_pmu_en_page_table(e) (((e) >> 4) & 0x1) 11262306a36Sopenharmony_ci#define iommu_pmu_get_requester_id(filter) (((filter) >> 16) & 0xffff) 11362306a36Sopenharmony_ci#define iommu_pmu_get_domain(filter) (((filter) >> 32) & 0xffff) 11462306a36Sopenharmony_ci#define iommu_pmu_get_pasid(filter) ((filter) & 0x3fffff) 11562306a36Sopenharmony_ci#define iommu_pmu_get_ats(filter) (((filter) >> 24) & 0x1f) 11662306a36Sopenharmony_ci#define iommu_pmu_get_page_table(filter) (((filter) >> 32) & 0x1f) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define iommu_pmu_set_filter(_name, _config, _filter, _idx, _econfig) \ 11962306a36Sopenharmony_ci{ \ 12062306a36Sopenharmony_ci if ((iommu_pmu->filter & _filter) && iommu_pmu_en_##_name(_econfig)) { \ 12162306a36Sopenharmony_ci dmar_writel(iommu_pmu->cfg_reg + _idx * IOMMU_PMU_CFG_OFFSET + \ 12262306a36Sopenharmony_ci IOMMU_PMU_CFG_SIZE + \ 12362306a36Sopenharmony_ci (ffs(_filter) - 1) * IOMMU_PMU_CFG_FILTERS_OFFSET, \ 12462306a36Sopenharmony_ci iommu_pmu_get_##_name(_config) | IOMMU_PMU_FILTER_EN);\ 12562306a36Sopenharmony_ci } \ 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define iommu_pmu_clear_filter(_filter, _idx) \ 12962306a36Sopenharmony_ci{ \ 13062306a36Sopenharmony_ci if (iommu_pmu->filter & _filter) { \ 13162306a36Sopenharmony_ci dmar_writel(iommu_pmu->cfg_reg + _idx * IOMMU_PMU_CFG_OFFSET + \ 13262306a36Sopenharmony_ci IOMMU_PMU_CFG_SIZE + \ 13362306a36Sopenharmony_ci (ffs(_filter) - 1) * IOMMU_PMU_CFG_FILTERS_OFFSET, \ 13462306a36Sopenharmony_ci 0); \ 13562306a36Sopenharmony_ci } \ 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* 13962306a36Sopenharmony_ci * Define the event attr related functions 14062306a36Sopenharmony_ci * Input: _name: event attr name 14162306a36Sopenharmony_ci * _string: string of the event in sysfs 14262306a36Sopenharmony_ci * _g_idx: event group encoding 14362306a36Sopenharmony_ci * _event: event encoding 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci#define IOMMU_PMU_EVENT_ATTR(_name, _string, _g_idx, _event) \ 14662306a36Sopenharmony_ci PMU_EVENT_ATTR_STRING(_name, event_attr_##_name, _string) \ 14762306a36Sopenharmony_ci \ 14862306a36Sopenharmony_cistatic struct attribute *_name##_attr[] = { \ 14962306a36Sopenharmony_ci &event_attr_##_name.attr.attr, \ 15062306a36Sopenharmony_ci NULL \ 15162306a36Sopenharmony_ci}; \ 15262306a36Sopenharmony_ci \ 15362306a36Sopenharmony_cistatic umode_t \ 15462306a36Sopenharmony_ci_name##_is_visible(struct kobject *kobj, struct attribute *attr, int i) \ 15562306a36Sopenharmony_ci{ \ 15662306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); \ 15762306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = dev_to_iommu_pmu(dev); \ 15862306a36Sopenharmony_ci \ 15962306a36Sopenharmony_ci if (!iommu_pmu) \ 16062306a36Sopenharmony_ci return 0; \ 16162306a36Sopenharmony_ci return (iommu_pmu->evcap[_g_idx] & _event) ? attr->mode : 0; \ 16262306a36Sopenharmony_ci} \ 16362306a36Sopenharmony_ci \ 16462306a36Sopenharmony_cistatic struct attribute_group _name = { \ 16562306a36Sopenharmony_ci .name = "events", \ 16662306a36Sopenharmony_ci .attrs = _name##_attr, \ 16762306a36Sopenharmony_ci .is_visible = _name##_is_visible, \ 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(iommu_clocks, "event_group=0x0,event=0x001", 0x0, 0x001) 17162306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(iommu_requests, "event_group=0x0,event=0x002", 0x0, 0x002) 17262306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(pw_occupancy, "event_group=0x0,event=0x004", 0x0, 0x004) 17362306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(ats_blocked, "event_group=0x0,event=0x008", 0x0, 0x008) 17462306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(iommu_mrds, "event_group=0x1,event=0x001", 0x1, 0x001) 17562306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(iommu_mem_blocked, "event_group=0x1,event=0x020", 0x1, 0x020) 17662306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(pg_req_posted, "event_group=0x1,event=0x040", 0x1, 0x040) 17762306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(ctxt_cache_lookup, "event_group=0x2,event=0x001", 0x2, 0x001) 17862306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(ctxt_cache_hit, "event_group=0x2,event=0x002", 0x2, 0x002) 17962306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(pasid_cache_lookup, "event_group=0x2,event=0x004", 0x2, 0x004) 18062306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(pasid_cache_hit, "event_group=0x2,event=0x008", 0x2, 0x008) 18162306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(ss_nonleaf_lookup, "event_group=0x2,event=0x010", 0x2, 0x010) 18262306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(ss_nonleaf_hit, "event_group=0x2,event=0x020", 0x2, 0x020) 18362306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(fs_nonleaf_lookup, "event_group=0x2,event=0x040", 0x2, 0x040) 18462306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(fs_nonleaf_hit, "event_group=0x2,event=0x080", 0x2, 0x080) 18562306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(hpt_nonleaf_lookup, "event_group=0x2,event=0x100", 0x2, 0x100) 18662306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(hpt_nonleaf_hit, "event_group=0x2,event=0x200", 0x2, 0x200) 18762306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(iotlb_lookup, "event_group=0x3,event=0x001", 0x3, 0x001) 18862306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(iotlb_hit, "event_group=0x3,event=0x002", 0x3, 0x002) 18962306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(hpt_leaf_lookup, "event_group=0x3,event=0x004", 0x3, 0x004) 19062306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(hpt_leaf_hit, "event_group=0x3,event=0x008", 0x3, 0x008) 19162306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(int_cache_lookup, "event_group=0x4,event=0x001", 0x4, 0x001) 19262306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(int_cache_hit_nonposted, "event_group=0x4,event=0x002", 0x4, 0x002) 19362306a36Sopenharmony_ciIOMMU_PMU_EVENT_ATTR(int_cache_hit_posted, "event_group=0x4,event=0x004", 0x4, 0x004) 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic const struct attribute_group *iommu_pmu_attr_update[] = { 19662306a36Sopenharmony_ci &filter_requester_id_en, 19762306a36Sopenharmony_ci &filter_domain_en, 19862306a36Sopenharmony_ci &filter_pasid_en, 19962306a36Sopenharmony_ci &filter_ats_en, 20062306a36Sopenharmony_ci &filter_page_table_en, 20162306a36Sopenharmony_ci &filter_requester_id, 20262306a36Sopenharmony_ci &filter_domain, 20362306a36Sopenharmony_ci &filter_pasid, 20462306a36Sopenharmony_ci &filter_ats, 20562306a36Sopenharmony_ci &filter_page_table, 20662306a36Sopenharmony_ci &iommu_clocks, 20762306a36Sopenharmony_ci &iommu_requests, 20862306a36Sopenharmony_ci &pw_occupancy, 20962306a36Sopenharmony_ci &ats_blocked, 21062306a36Sopenharmony_ci &iommu_mrds, 21162306a36Sopenharmony_ci &iommu_mem_blocked, 21262306a36Sopenharmony_ci &pg_req_posted, 21362306a36Sopenharmony_ci &ctxt_cache_lookup, 21462306a36Sopenharmony_ci &ctxt_cache_hit, 21562306a36Sopenharmony_ci &pasid_cache_lookup, 21662306a36Sopenharmony_ci &pasid_cache_hit, 21762306a36Sopenharmony_ci &ss_nonleaf_lookup, 21862306a36Sopenharmony_ci &ss_nonleaf_hit, 21962306a36Sopenharmony_ci &fs_nonleaf_lookup, 22062306a36Sopenharmony_ci &fs_nonleaf_hit, 22162306a36Sopenharmony_ci &hpt_nonleaf_lookup, 22262306a36Sopenharmony_ci &hpt_nonleaf_hit, 22362306a36Sopenharmony_ci &iotlb_lookup, 22462306a36Sopenharmony_ci &iotlb_hit, 22562306a36Sopenharmony_ci &hpt_leaf_lookup, 22662306a36Sopenharmony_ci &hpt_leaf_hit, 22762306a36Sopenharmony_ci &int_cache_lookup, 22862306a36Sopenharmony_ci &int_cache_hit_nonposted, 22962306a36Sopenharmony_ci &int_cache_hit_posted, 23062306a36Sopenharmony_ci NULL 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic inline void __iomem * 23462306a36Sopenharmony_ciiommu_event_base(struct iommu_pmu *iommu_pmu, int idx) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return iommu_pmu->cntr_reg + idx * iommu_pmu->cntr_stride; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic inline void __iomem * 24062306a36Sopenharmony_ciiommu_config_base(struct iommu_pmu *iommu_pmu, int idx) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return iommu_pmu->cfg_reg + idx * IOMMU_PMU_CFG_OFFSET; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic inline struct iommu_pmu *iommu_event_to_pmu(struct perf_event *event) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci return container_of(event->pmu, struct iommu_pmu, pmu); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic inline u64 iommu_event_config(struct perf_event *event) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci u64 config = event->attr.config; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return (iommu_event_select(config) << IOMMU_EVENT_CFG_ES_SHIFT) | 25562306a36Sopenharmony_ci (iommu_event_group(config) << IOMMU_EVENT_CFG_EGI_SHIFT) | 25662306a36Sopenharmony_ci IOMMU_EVENT_CFG_INT; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic inline bool is_iommu_pmu_event(struct iommu_pmu *iommu_pmu, 26062306a36Sopenharmony_ci struct perf_event *event) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci return event->pmu == &iommu_pmu->pmu; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int iommu_pmu_validate_event(struct perf_event *event) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 26862306a36Sopenharmony_ci u32 event_group = iommu_event_group(event->attr.config); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (event_group >= iommu_pmu->num_eg) 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int iommu_pmu_validate_group(struct perf_event *event) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 27962306a36Sopenharmony_ci struct perf_event *sibling; 28062306a36Sopenharmony_ci int nr = 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * All events in a group must be scheduled simultaneously. 28462306a36Sopenharmony_ci * Check whether there is enough counters for all the events. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci for_each_sibling_event(sibling, event->group_leader) { 28762306a36Sopenharmony_ci if (!is_iommu_pmu_event(iommu_pmu, sibling) || 28862306a36Sopenharmony_ci sibling->state <= PERF_EVENT_STATE_OFF) 28962306a36Sopenharmony_ci continue; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (++nr > iommu_pmu->num_cntr) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int iommu_pmu_event_init(struct perf_event *event) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 30362306a36Sopenharmony_ci return -ENOENT; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* sampling not supported */ 30662306a36Sopenharmony_ci if (event->attr.sample_period) 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (event->cpu < 0) 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (iommu_pmu_validate_event(event)) 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci hwc->config = iommu_event_config(event); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return iommu_pmu_validate_group(event); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void iommu_pmu_event_update(struct perf_event *event) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 32362306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 32462306a36Sopenharmony_ci u64 prev_count, new_count, delta; 32562306a36Sopenharmony_ci int shift = 64 - iommu_pmu->cntr_width; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciagain: 32862306a36Sopenharmony_ci prev_count = local64_read(&hwc->prev_count); 32962306a36Sopenharmony_ci new_count = dmar_readq(iommu_event_base(iommu_pmu, hwc->idx)); 33062306a36Sopenharmony_ci if (local64_xchg(&hwc->prev_count, new_count) != prev_count) 33162306a36Sopenharmony_ci goto again; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * The counter width is enumerated. Always shift the counter 33562306a36Sopenharmony_ci * before using it. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci delta = (new_count << shift) - (prev_count << shift); 33862306a36Sopenharmony_ci delta >>= shift; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci local64_add(delta, &event->count); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic void iommu_pmu_start(struct perf_event *event, int flags) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 34662306a36Sopenharmony_ci struct intel_iommu *iommu = iommu_pmu->iommu; 34762306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 34862306a36Sopenharmony_ci u64 count; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) 35162306a36Sopenharmony_ci return; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (WARN_ON_ONCE(hwc->idx < 0 || hwc->idx >= IOMMU_PMU_IDX_MAX)) 35462306a36Sopenharmony_ci return; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (flags & PERF_EF_RELOAD) 35762306a36Sopenharmony_ci WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci hwc->state = 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Always reprogram the period */ 36262306a36Sopenharmony_ci count = dmar_readq(iommu_event_base(iommu_pmu, hwc->idx)); 36362306a36Sopenharmony_ci local64_set((&hwc->prev_count), count); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* 36662306a36Sopenharmony_ci * The error of ecmd will be ignored. 36762306a36Sopenharmony_ci * - The existing perf_event subsystem doesn't handle the error. 36862306a36Sopenharmony_ci * Only IOMMU PMU returns runtime HW error. We don't want to 36962306a36Sopenharmony_ci * change the existing generic interfaces for the specific case. 37062306a36Sopenharmony_ci * - It's a corner case caused by HW, which is very unlikely to 37162306a36Sopenharmony_ci * happen. There is nothing SW can do. 37262306a36Sopenharmony_ci * - The worst case is that the user will get <not count> with 37362306a36Sopenharmony_ci * perf command, which can give the user some hints. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci ecmd_submit_sync(iommu, DMA_ECMD_ENABLE, hwc->idx, 0); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci perf_event_update_userpage(event); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void iommu_pmu_stop(struct perf_event *event, int flags) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 38362306a36Sopenharmony_ci struct intel_iommu *iommu = iommu_pmu->iommu; 38462306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!(hwc->state & PERF_HES_STOPPED)) { 38762306a36Sopenharmony_ci ecmd_submit_sync(iommu, DMA_ECMD_DISABLE, hwc->idx, 0); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci iommu_pmu_event_update(event); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic inline int 39662306a36Sopenharmony_ciiommu_pmu_validate_per_cntr_event(struct iommu_pmu *iommu_pmu, 39762306a36Sopenharmony_ci int idx, struct perf_event *event) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci u32 event_group = iommu_event_group(event->attr.config); 40062306a36Sopenharmony_ci u32 select = iommu_event_select(event->attr.config); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!(iommu_pmu->cntr_evcap[idx][event_group] & select)) 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int iommu_pmu_assign_event(struct iommu_pmu *iommu_pmu, 40962306a36Sopenharmony_ci struct perf_event *event) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 41262306a36Sopenharmony_ci int idx; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * The counters which support limited events are usually at the end. 41662306a36Sopenharmony_ci * Schedule them first to accommodate more events. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci for (idx = iommu_pmu->num_cntr - 1; idx >= 0; idx--) { 41962306a36Sopenharmony_ci if (test_and_set_bit(idx, iommu_pmu->used_mask)) 42062306a36Sopenharmony_ci continue; 42162306a36Sopenharmony_ci /* Check per-counter event capabilities */ 42262306a36Sopenharmony_ci if (!iommu_pmu_validate_per_cntr_event(iommu_pmu, idx, event)) 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci clear_bit(idx, iommu_pmu->used_mask); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci if (idx < 0) 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci iommu_pmu->event_list[idx] = event; 43062306a36Sopenharmony_ci hwc->idx = idx; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* config events */ 43362306a36Sopenharmony_ci dmar_writeq(iommu_config_base(iommu_pmu, idx), hwc->config); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci iommu_pmu_set_filter(requester_id, event->attr.config1, 43662306a36Sopenharmony_ci IOMMU_PMU_FILTER_REQUESTER_ID, idx, 43762306a36Sopenharmony_ci event->attr.config1); 43862306a36Sopenharmony_ci iommu_pmu_set_filter(domain, event->attr.config1, 43962306a36Sopenharmony_ci IOMMU_PMU_FILTER_DOMAIN, idx, 44062306a36Sopenharmony_ci event->attr.config1); 44162306a36Sopenharmony_ci iommu_pmu_set_filter(pasid, event->attr.config1, 44262306a36Sopenharmony_ci IOMMU_PMU_FILTER_PASID, idx, 44362306a36Sopenharmony_ci event->attr.config1); 44462306a36Sopenharmony_ci iommu_pmu_set_filter(ats, event->attr.config2, 44562306a36Sopenharmony_ci IOMMU_PMU_FILTER_ATS, idx, 44662306a36Sopenharmony_ci event->attr.config1); 44762306a36Sopenharmony_ci iommu_pmu_set_filter(page_table, event->attr.config2, 44862306a36Sopenharmony_ci IOMMU_PMU_FILTER_PAGE_TABLE, idx, 44962306a36Sopenharmony_ci event->attr.config1); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int iommu_pmu_add(struct perf_event *event, int flags) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 45762306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 45862306a36Sopenharmony_ci int ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = iommu_pmu_assign_event(iommu_pmu, event); 46162306a36Sopenharmony_ci if (ret < 0) 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (flags & PERF_EF_START) 46762306a36Sopenharmony_ci iommu_pmu_start(event, 0); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void iommu_pmu_del(struct perf_event *event, int flags) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event); 47562306a36Sopenharmony_ci int idx = event->hw.idx; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci iommu_pmu_stop(event, PERF_EF_UPDATE); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci iommu_pmu_clear_filter(IOMMU_PMU_FILTER_REQUESTER_ID, idx); 48062306a36Sopenharmony_ci iommu_pmu_clear_filter(IOMMU_PMU_FILTER_DOMAIN, idx); 48162306a36Sopenharmony_ci iommu_pmu_clear_filter(IOMMU_PMU_FILTER_PASID, idx); 48262306a36Sopenharmony_ci iommu_pmu_clear_filter(IOMMU_PMU_FILTER_ATS, idx); 48362306a36Sopenharmony_ci iommu_pmu_clear_filter(IOMMU_PMU_FILTER_PAGE_TABLE, idx); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci iommu_pmu->event_list[idx] = NULL; 48662306a36Sopenharmony_ci event->hw.idx = -1; 48762306a36Sopenharmony_ci clear_bit(idx, iommu_pmu->used_mask); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci perf_event_update_userpage(event); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void iommu_pmu_enable(struct pmu *pmu) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = container_of(pmu, struct iommu_pmu, pmu); 49562306a36Sopenharmony_ci struct intel_iommu *iommu = iommu_pmu->iommu; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ecmd_submit_sync(iommu, DMA_ECMD_UNFREEZE, 0, 0); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void iommu_pmu_disable(struct pmu *pmu) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = container_of(pmu, struct iommu_pmu, pmu); 50362306a36Sopenharmony_ci struct intel_iommu *iommu = iommu_pmu->iommu; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ecmd_submit_sync(iommu, DMA_ECMD_FREEZE, 0, 0); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void iommu_pmu_counter_overflow(struct iommu_pmu *iommu_pmu) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct perf_event *event; 51162306a36Sopenharmony_ci u64 status; 51262306a36Sopenharmony_ci int i; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * Two counters may be overflowed very close. Always check 51662306a36Sopenharmony_ci * whether there are more to handle. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci while ((status = dmar_readq(iommu_pmu->overflow))) { 51962306a36Sopenharmony_ci for_each_set_bit(i, (unsigned long *)&status, iommu_pmu->num_cntr) { 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * Find the assigned event of the counter. 52262306a36Sopenharmony_ci * Accumulate the value into the event->count. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci event = iommu_pmu->event_list[i]; 52562306a36Sopenharmony_ci if (!event) { 52662306a36Sopenharmony_ci pr_warn_once("Cannot find the assigned event for counter %d\n", i); 52762306a36Sopenharmony_ci continue; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci iommu_pmu_event_update(event); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci dmar_writeq(iommu_pmu->overflow, status); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic irqreturn_t iommu_pmu_irq_handler(int irq, void *dev_id) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct intel_iommu *iommu = dev_id; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!dmar_readl(iommu->reg + DMAR_PERFINTRSTS_REG)) 54162306a36Sopenharmony_ci return IRQ_NONE; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci iommu_pmu_counter_overflow(iommu->pmu); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* Clear the status bit */ 54662306a36Sopenharmony_ci dmar_writel(iommu->reg + DMAR_PERFINTRSTS_REG, DMA_PERFINTRSTS_PIS); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return IRQ_HANDLED; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int __iommu_pmu_register(struct intel_iommu *iommu) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu->pmu; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci iommu_pmu->pmu.name = iommu->name; 55662306a36Sopenharmony_ci iommu_pmu->pmu.task_ctx_nr = perf_invalid_context; 55762306a36Sopenharmony_ci iommu_pmu->pmu.event_init = iommu_pmu_event_init; 55862306a36Sopenharmony_ci iommu_pmu->pmu.pmu_enable = iommu_pmu_enable; 55962306a36Sopenharmony_ci iommu_pmu->pmu.pmu_disable = iommu_pmu_disable; 56062306a36Sopenharmony_ci iommu_pmu->pmu.add = iommu_pmu_add; 56162306a36Sopenharmony_ci iommu_pmu->pmu.del = iommu_pmu_del; 56262306a36Sopenharmony_ci iommu_pmu->pmu.start = iommu_pmu_start; 56362306a36Sopenharmony_ci iommu_pmu->pmu.stop = iommu_pmu_stop; 56462306a36Sopenharmony_ci iommu_pmu->pmu.read = iommu_pmu_event_update; 56562306a36Sopenharmony_ci iommu_pmu->pmu.attr_groups = iommu_pmu_attr_groups; 56662306a36Sopenharmony_ci iommu_pmu->pmu.attr_update = iommu_pmu_attr_update; 56762306a36Sopenharmony_ci iommu_pmu->pmu.capabilities = PERF_PMU_CAP_NO_EXCLUDE; 56862306a36Sopenharmony_ci iommu_pmu->pmu.module = THIS_MODULE; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return perf_pmu_register(&iommu_pmu->pmu, iommu_pmu->pmu.name, -1); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic inline void __iomem * 57462306a36Sopenharmony_ciget_perf_reg_address(struct intel_iommu *iommu, u32 offset) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci u32 off = dmar_readl(iommu->reg + offset); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return iommu->reg + off; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ciint alloc_iommu_pmu(struct intel_iommu *iommu) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu; 58462306a36Sopenharmony_ci int i, j, ret; 58562306a36Sopenharmony_ci u64 perfcap; 58662306a36Sopenharmony_ci u32 cap; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!ecap_pms(iommu->ecap)) 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* The IOMMU PMU requires the ECMD support as well */ 59262306a36Sopenharmony_ci if (!cap_ecmds(iommu->cap)) 59362306a36Sopenharmony_ci return -ENODEV; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci perfcap = dmar_readq(iommu->reg + DMAR_PERFCAP_REG); 59662306a36Sopenharmony_ci /* The performance monitoring is not supported. */ 59762306a36Sopenharmony_ci if (!perfcap) 59862306a36Sopenharmony_ci return -ENODEV; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Sanity check for the number of the counters and event groups */ 60162306a36Sopenharmony_ci if (!pcap_num_cntr(perfcap) || !pcap_num_event_group(perfcap)) 60262306a36Sopenharmony_ci return -ENODEV; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* The interrupt on overflow is required */ 60562306a36Sopenharmony_ci if (!pcap_interrupt(perfcap)) 60662306a36Sopenharmony_ci return -ENODEV; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Check required Enhanced Command Capability */ 60962306a36Sopenharmony_ci if (!ecmd_has_pmu_essential(iommu)) 61062306a36Sopenharmony_ci return -ENODEV; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci iommu_pmu = kzalloc(sizeof(*iommu_pmu), GFP_KERNEL); 61362306a36Sopenharmony_ci if (!iommu_pmu) 61462306a36Sopenharmony_ci return -ENOMEM; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci iommu_pmu->num_cntr = pcap_num_cntr(perfcap); 61762306a36Sopenharmony_ci if (iommu_pmu->num_cntr > IOMMU_PMU_IDX_MAX) { 61862306a36Sopenharmony_ci pr_warn_once("The number of IOMMU counters %d > max(%d), clipping!", 61962306a36Sopenharmony_ci iommu_pmu->num_cntr, IOMMU_PMU_IDX_MAX); 62062306a36Sopenharmony_ci iommu_pmu->num_cntr = IOMMU_PMU_IDX_MAX; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci iommu_pmu->cntr_width = pcap_cntr_width(perfcap); 62462306a36Sopenharmony_ci iommu_pmu->filter = pcap_filters_mask(perfcap); 62562306a36Sopenharmony_ci iommu_pmu->cntr_stride = pcap_cntr_stride(perfcap); 62662306a36Sopenharmony_ci iommu_pmu->num_eg = pcap_num_event_group(perfcap); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci iommu_pmu->evcap = kcalloc(iommu_pmu->num_eg, sizeof(u64), GFP_KERNEL); 62962306a36Sopenharmony_ci if (!iommu_pmu->evcap) { 63062306a36Sopenharmony_ci ret = -ENOMEM; 63162306a36Sopenharmony_ci goto free_pmu; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* Parse event group capabilities */ 63562306a36Sopenharmony_ci for (i = 0; i < iommu_pmu->num_eg; i++) { 63662306a36Sopenharmony_ci u64 pcap; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci pcap = dmar_readq(iommu->reg + DMAR_PERFEVNTCAP_REG + 63962306a36Sopenharmony_ci i * IOMMU_PMU_CAP_REGS_STEP); 64062306a36Sopenharmony_ci iommu_pmu->evcap[i] = pecap_es(pcap); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci iommu_pmu->cntr_evcap = kcalloc(iommu_pmu->num_cntr, sizeof(u32 *), GFP_KERNEL); 64462306a36Sopenharmony_ci if (!iommu_pmu->cntr_evcap) { 64562306a36Sopenharmony_ci ret = -ENOMEM; 64662306a36Sopenharmony_ci goto free_pmu_evcap; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci for (i = 0; i < iommu_pmu->num_cntr; i++) { 64962306a36Sopenharmony_ci iommu_pmu->cntr_evcap[i] = kcalloc(iommu_pmu->num_eg, sizeof(u32), GFP_KERNEL); 65062306a36Sopenharmony_ci if (!iommu_pmu->cntr_evcap[i]) { 65162306a36Sopenharmony_ci ret = -ENOMEM; 65262306a36Sopenharmony_ci goto free_pmu_cntr_evcap; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * Set to the global capabilities, will adjust according 65662306a36Sopenharmony_ci * to per-counter capabilities later. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci for (j = 0; j < iommu_pmu->num_eg; j++) 65962306a36Sopenharmony_ci iommu_pmu->cntr_evcap[i][j] = (u32)iommu_pmu->evcap[j]; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci iommu_pmu->cfg_reg = get_perf_reg_address(iommu, DMAR_PERFCFGOFF_REG); 66362306a36Sopenharmony_ci iommu_pmu->cntr_reg = get_perf_reg_address(iommu, DMAR_PERFCNTROFF_REG); 66462306a36Sopenharmony_ci iommu_pmu->overflow = get_perf_reg_address(iommu, DMAR_PERFOVFOFF_REG); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* 66762306a36Sopenharmony_ci * Check per-counter capabilities. All counters should have the 66862306a36Sopenharmony_ci * same capabilities on Interrupt on Overflow Support and Counter 66962306a36Sopenharmony_ci * Width. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci for (i = 0; i < iommu_pmu->num_cntr; i++) { 67262306a36Sopenharmony_ci cap = dmar_readl(iommu_pmu->cfg_reg + 67362306a36Sopenharmony_ci i * IOMMU_PMU_CFG_OFFSET + 67462306a36Sopenharmony_ci IOMMU_PMU_CFG_CNTRCAP_OFFSET); 67562306a36Sopenharmony_ci if (!iommu_cntrcap_pcc(cap)) 67662306a36Sopenharmony_ci continue; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* 67962306a36Sopenharmony_ci * It's possible that some counters have a different 68062306a36Sopenharmony_ci * capability because of e.g., HW bug. Check the corner 68162306a36Sopenharmony_ci * case here and simply drop those counters. 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_ci if ((iommu_cntrcap_cw(cap) != iommu_pmu->cntr_width) || 68462306a36Sopenharmony_ci !iommu_cntrcap_ios(cap)) { 68562306a36Sopenharmony_ci iommu_pmu->num_cntr = i; 68662306a36Sopenharmony_ci pr_warn("PMU counter capability inconsistent, counter number reduced to %d\n", 68762306a36Sopenharmony_ci iommu_pmu->num_cntr); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Clear the pre-defined events group */ 69162306a36Sopenharmony_ci for (j = 0; j < iommu_pmu->num_eg; j++) 69262306a36Sopenharmony_ci iommu_pmu->cntr_evcap[i][j] = 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* Override with per-counter event capabilities */ 69562306a36Sopenharmony_ci for (j = 0; j < iommu_cntrcap_egcnt(cap); j++) { 69662306a36Sopenharmony_ci cap = dmar_readl(iommu_pmu->cfg_reg + i * IOMMU_PMU_CFG_OFFSET + 69762306a36Sopenharmony_ci IOMMU_PMU_CFG_CNTREVCAP_OFFSET + 69862306a36Sopenharmony_ci (j * IOMMU_PMU_OFF_REGS_STEP)); 69962306a36Sopenharmony_ci iommu_pmu->cntr_evcap[i][iommu_event_group(cap)] = iommu_event_select(cap); 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Some events may only be supported by a specific counter. 70262306a36Sopenharmony_ci * Track them in the evcap as well. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci iommu_pmu->evcap[iommu_event_group(cap)] |= iommu_event_select(cap); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci iommu_pmu->iommu = iommu; 70962306a36Sopenharmony_ci iommu->pmu = iommu_pmu; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cifree_pmu_cntr_evcap: 71462306a36Sopenharmony_ci for (i = 0; i < iommu_pmu->num_cntr; i++) 71562306a36Sopenharmony_ci kfree(iommu_pmu->cntr_evcap[i]); 71662306a36Sopenharmony_ci kfree(iommu_pmu->cntr_evcap); 71762306a36Sopenharmony_cifree_pmu_evcap: 71862306a36Sopenharmony_ci kfree(iommu_pmu->evcap); 71962306a36Sopenharmony_cifree_pmu: 72062306a36Sopenharmony_ci kfree(iommu_pmu); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_civoid free_iommu_pmu(struct intel_iommu *iommu) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu->pmu; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (!iommu_pmu) 73062306a36Sopenharmony_ci return; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (iommu_pmu->evcap) { 73362306a36Sopenharmony_ci int i; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci for (i = 0; i < iommu_pmu->num_cntr; i++) 73662306a36Sopenharmony_ci kfree(iommu_pmu->cntr_evcap[i]); 73762306a36Sopenharmony_ci kfree(iommu_pmu->cntr_evcap); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci kfree(iommu_pmu->evcap); 74062306a36Sopenharmony_ci kfree(iommu_pmu); 74162306a36Sopenharmony_ci iommu->pmu = NULL; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int iommu_pmu_set_interrupt(struct intel_iommu *iommu) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu->pmu; 74762306a36Sopenharmony_ci int irq, ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci irq = dmar_alloc_hwirq(IOMMU_IRQ_ID_OFFSET_PERF + iommu->seq_id, iommu->node, iommu); 75062306a36Sopenharmony_ci if (irq <= 0) 75162306a36Sopenharmony_ci return -EINVAL; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci snprintf(iommu_pmu->irq_name, sizeof(iommu_pmu->irq_name), "dmar%d-perf", iommu->seq_id); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci iommu->perf_irq = irq; 75662306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, iommu_pmu_irq_handler, 75762306a36Sopenharmony_ci IRQF_ONESHOT, iommu_pmu->irq_name, iommu); 75862306a36Sopenharmony_ci if (ret) { 75962306a36Sopenharmony_ci dmar_free_hwirq(irq); 76062306a36Sopenharmony_ci iommu->perf_irq = 0; 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic void iommu_pmu_unset_interrupt(struct intel_iommu *iommu) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci if (!iommu->perf_irq) 76962306a36Sopenharmony_ci return; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci free_irq(iommu->perf_irq, iommu); 77262306a36Sopenharmony_ci dmar_free_hwirq(iommu->perf_irq); 77362306a36Sopenharmony_ci iommu->perf_irq = 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int iommu_pmu_cpu_online(unsigned int cpu, struct hlist_node *node) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = hlist_entry_safe(node, typeof(*iommu_pmu), cpuhp_node); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (cpumask_empty(&iommu_pmu_cpu_mask)) 78162306a36Sopenharmony_ci cpumask_set_cpu(cpu, &iommu_pmu_cpu_mask); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (cpumask_test_cpu(cpu, &iommu_pmu_cpu_mask)) 78462306a36Sopenharmony_ci iommu_pmu->cpu = cpu; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic int iommu_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = hlist_entry_safe(node, typeof(*iommu_pmu), cpuhp_node); 79262306a36Sopenharmony_ci int target = cpumask_first(&iommu_pmu_cpu_mask); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * The iommu_pmu_cpu_mask has been updated when offline the CPU 79662306a36Sopenharmony_ci * for the first iommu_pmu. Migrate the other iommu_pmu to the 79762306a36Sopenharmony_ci * new target. 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ci if (target < nr_cpu_ids && target != iommu_pmu->cpu) { 80062306a36Sopenharmony_ci perf_pmu_migrate_context(&iommu_pmu->pmu, cpu, target); 80162306a36Sopenharmony_ci iommu_pmu->cpu = target; 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (!cpumask_test_and_clear_cpu(cpu, &iommu_pmu_cpu_mask)) 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci target = cpumask_any_but(cpu_online_mask, cpu); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (target < nr_cpu_ids) 81162306a36Sopenharmony_ci cpumask_set_cpu(target, &iommu_pmu_cpu_mask); 81262306a36Sopenharmony_ci else 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci perf_pmu_migrate_context(&iommu_pmu->pmu, cpu, target); 81662306a36Sopenharmony_ci iommu_pmu->cpu = target; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic int nr_iommu_pmu; 82262306a36Sopenharmony_cistatic enum cpuhp_state iommu_cpuhp_slot; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int iommu_pmu_cpuhp_setup(struct iommu_pmu *iommu_pmu) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!nr_iommu_pmu) { 82962306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 83062306a36Sopenharmony_ci "driver/iommu/intel/perfmon:online", 83162306a36Sopenharmony_ci iommu_pmu_cpu_online, 83262306a36Sopenharmony_ci iommu_pmu_cpu_offline); 83362306a36Sopenharmony_ci if (ret < 0) 83462306a36Sopenharmony_ci return ret; 83562306a36Sopenharmony_ci iommu_cpuhp_slot = ret; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci ret = cpuhp_state_add_instance(iommu_cpuhp_slot, &iommu_pmu->cpuhp_node); 83962306a36Sopenharmony_ci if (ret) { 84062306a36Sopenharmony_ci if (!nr_iommu_pmu) 84162306a36Sopenharmony_ci cpuhp_remove_multi_state(iommu_cpuhp_slot); 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci nr_iommu_pmu++; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void iommu_pmu_cpuhp_free(struct iommu_pmu *iommu_pmu) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci cpuhp_state_remove_instance(iommu_cpuhp_slot, &iommu_pmu->cpuhp_node); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (--nr_iommu_pmu) 85462306a36Sopenharmony_ci return; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci cpuhp_remove_multi_state(iommu_cpuhp_slot); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_civoid iommu_pmu_register(struct intel_iommu *iommu) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu->pmu; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (!iommu_pmu) 86462306a36Sopenharmony_ci return; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (__iommu_pmu_register(iommu)) 86762306a36Sopenharmony_ci goto err; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (iommu_pmu_cpuhp_setup(iommu_pmu)) 87062306a36Sopenharmony_ci goto unregister; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Set interrupt for overflow */ 87362306a36Sopenharmony_ci if (iommu_pmu_set_interrupt(iommu)) 87462306a36Sopenharmony_ci goto cpuhp_free; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cicpuhp_free: 87962306a36Sopenharmony_ci iommu_pmu_cpuhp_free(iommu_pmu); 88062306a36Sopenharmony_ciunregister: 88162306a36Sopenharmony_ci perf_pmu_unregister(&iommu_pmu->pmu); 88262306a36Sopenharmony_cierr: 88362306a36Sopenharmony_ci pr_err("Failed to register PMU for iommu (seq_id = %d)\n", iommu->seq_id); 88462306a36Sopenharmony_ci free_iommu_pmu(iommu); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_civoid iommu_pmu_unregister(struct intel_iommu *iommu) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct iommu_pmu *iommu_pmu = iommu->pmu; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (!iommu_pmu) 89262306a36Sopenharmony_ci return; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci iommu_pmu_unset_interrupt(iommu); 89562306a36Sopenharmony_ci iommu_pmu_cpuhp_free(iommu_pmu); 89662306a36Sopenharmony_ci perf_pmu_unregister(&iommu_pmu->pmu); 89762306a36Sopenharmony_ci} 898