1a6a5006dSKan Liang // SPDX-License-Identifier: GPL-2.0-only
2a6a5006dSKan Liang /*
3a6a5006dSKan Liang * Support Intel IOMMU PerfMon
4a6a5006dSKan Liang * Copyright(c) 2023 Intel Corporation.
5a6a5006dSKan Liang */
6a6a5006dSKan Liang #define pr_fmt(fmt) "DMAR: " fmt
7a6a5006dSKan Liang #define dev_fmt(fmt) pr_fmt(fmt)
8a6a5006dSKan Liang
9a6a5006dSKan Liang #include <linux/dmar.h>
10a6a5006dSKan Liang #include "iommu.h"
11a6a5006dSKan Liang #include "perfmon.h"
12a6a5006dSKan Liang
137232ab8bSKan Liang PMU_FORMAT_ATTR(event, "config:0-27"); /* ES: Events Select */
147232ab8bSKan Liang PMU_FORMAT_ATTR(event_group, "config:28-31"); /* EGI: Event Group Index */
157232ab8bSKan Liang
167232ab8bSKan Liang static struct attribute *iommu_pmu_format_attrs[] = {
177232ab8bSKan Liang &format_attr_event_group.attr,
187232ab8bSKan Liang &format_attr_event.attr,
197232ab8bSKan Liang NULL
207232ab8bSKan Liang };
217232ab8bSKan Liang
227232ab8bSKan Liang static struct attribute_group iommu_pmu_format_attr_group = {
237232ab8bSKan Liang .name = "format",
247232ab8bSKan Liang .attrs = iommu_pmu_format_attrs,
257232ab8bSKan Liang };
267232ab8bSKan Liang
277232ab8bSKan Liang /* The available events are added in attr_update later */
287232ab8bSKan Liang static struct attribute *attrs_empty[] = {
297232ab8bSKan Liang NULL
307232ab8bSKan Liang };
317232ab8bSKan Liang
327232ab8bSKan Liang static struct attribute_group iommu_pmu_events_attr_group = {
337232ab8bSKan Liang .name = "events",
347232ab8bSKan Liang .attrs = attrs_empty,
357232ab8bSKan Liang };
367232ab8bSKan Liang
3746284c6cSKan Liang static cpumask_t iommu_pmu_cpu_mask;
3846284c6cSKan Liang
3946284c6cSKan Liang static ssize_t
cpumask_show(struct device * dev,struct device_attribute * attr,char * buf)4046284c6cSKan Liang cpumask_show(struct device *dev, struct device_attribute *attr, char *buf)
4146284c6cSKan Liang {
4246284c6cSKan Liang return cpumap_print_to_pagebuf(true, buf, &iommu_pmu_cpu_mask);
4346284c6cSKan Liang }
4446284c6cSKan Liang static DEVICE_ATTR_RO(cpumask);
4546284c6cSKan Liang
4646284c6cSKan Liang static struct attribute *iommu_pmu_cpumask_attrs[] = {
4746284c6cSKan Liang &dev_attr_cpumask.attr,
4846284c6cSKan Liang NULL
4946284c6cSKan Liang };
5046284c6cSKan Liang
5146284c6cSKan Liang static struct attribute_group iommu_pmu_cpumask_attr_group = {
5246284c6cSKan Liang .attrs = iommu_pmu_cpumask_attrs,
5346284c6cSKan Liang };
5446284c6cSKan Liang
557232ab8bSKan Liang static const struct attribute_group *iommu_pmu_attr_groups[] = {
567232ab8bSKan Liang &iommu_pmu_format_attr_group,
577232ab8bSKan Liang &iommu_pmu_events_attr_group,
5846284c6cSKan Liang &iommu_pmu_cpumask_attr_group,
597232ab8bSKan Liang NULL
607232ab8bSKan Liang };
617232ab8bSKan Liang
dev_to_iommu_pmu(struct device * dev)627232ab8bSKan Liang static inline struct iommu_pmu *dev_to_iommu_pmu(struct device *dev)
637232ab8bSKan Liang {
647232ab8bSKan Liang /*
657232ab8bSKan Liang * The perf_event creates its own dev for each PMU.
667232ab8bSKan Liang * See pmu_dev_alloc()
677232ab8bSKan Liang */
687232ab8bSKan Liang return container_of(dev_get_drvdata(dev), struct iommu_pmu, pmu);
697232ab8bSKan Liang }
707232ab8bSKan Liang
717232ab8bSKan Liang #define IOMMU_PMU_ATTR(_name, _format, _filter) \
727232ab8bSKan Liang PMU_FORMAT_ATTR(_name, _format); \
737232ab8bSKan Liang \
747232ab8bSKan Liang static struct attribute *_name##_attr[] = { \
757232ab8bSKan Liang &format_attr_##_name.attr, \
767232ab8bSKan Liang NULL \
777232ab8bSKan Liang }; \
787232ab8bSKan Liang \
797232ab8bSKan Liang static umode_t \
807232ab8bSKan Liang _name##_is_visible(struct kobject *kobj, struct attribute *attr, int i) \
817232ab8bSKan Liang { \
827232ab8bSKan Liang struct device *dev = kobj_to_dev(kobj); \
837232ab8bSKan Liang struct iommu_pmu *iommu_pmu = dev_to_iommu_pmu(dev); \
847232ab8bSKan Liang \
857232ab8bSKan Liang if (!iommu_pmu) \
867232ab8bSKan Liang return 0; \
877232ab8bSKan Liang return (iommu_pmu->filter & _filter) ? attr->mode : 0; \
887232ab8bSKan Liang } \
897232ab8bSKan Liang \
907232ab8bSKan Liang static struct attribute_group _name = { \
917232ab8bSKan Liang .name = "format", \
927232ab8bSKan Liang .attrs = _name##_attr, \
937232ab8bSKan Liang .is_visible = _name##_is_visible, \
947232ab8bSKan Liang };
957232ab8bSKan Liang
967232ab8bSKan Liang IOMMU_PMU_ATTR(filter_requester_id_en, "config1:0", IOMMU_PMU_FILTER_REQUESTER_ID);
977232ab8bSKan Liang IOMMU_PMU_ATTR(filter_domain_en, "config1:1", IOMMU_PMU_FILTER_DOMAIN);
987232ab8bSKan Liang IOMMU_PMU_ATTR(filter_pasid_en, "config1:2", IOMMU_PMU_FILTER_PASID);
997232ab8bSKan Liang IOMMU_PMU_ATTR(filter_ats_en, "config1:3", IOMMU_PMU_FILTER_ATS);
1007232ab8bSKan Liang IOMMU_PMU_ATTR(filter_page_table_en, "config1:4", IOMMU_PMU_FILTER_PAGE_TABLE);
1017232ab8bSKan Liang IOMMU_PMU_ATTR(filter_requester_id, "config1:16-31", IOMMU_PMU_FILTER_REQUESTER_ID);
1027232ab8bSKan Liang IOMMU_PMU_ATTR(filter_domain, "config1:32-47", IOMMU_PMU_FILTER_DOMAIN);
1037232ab8bSKan Liang IOMMU_PMU_ATTR(filter_pasid, "config2:0-21", IOMMU_PMU_FILTER_PASID);
1047232ab8bSKan Liang IOMMU_PMU_ATTR(filter_ats, "config2:24-28", IOMMU_PMU_FILTER_ATS);
1057232ab8bSKan Liang IOMMU_PMU_ATTR(filter_page_table, "config2:32-36", IOMMU_PMU_FILTER_PAGE_TABLE);
1067232ab8bSKan Liang
1077232ab8bSKan Liang #define iommu_pmu_en_requester_id(e) ((e) & 0x1)
1087232ab8bSKan Liang #define iommu_pmu_en_domain(e) (((e) >> 1) & 0x1)
1097232ab8bSKan Liang #define iommu_pmu_en_pasid(e) (((e) >> 2) & 0x1)
1107232ab8bSKan Liang #define iommu_pmu_en_ats(e) (((e) >> 3) & 0x1)
1117232ab8bSKan Liang #define iommu_pmu_en_page_table(e) (((e) >> 4) & 0x1)
1127232ab8bSKan Liang #define iommu_pmu_get_requester_id(filter) (((filter) >> 16) & 0xffff)
1137232ab8bSKan Liang #define iommu_pmu_get_domain(filter) (((filter) >> 32) & 0xffff)
1147232ab8bSKan Liang #define iommu_pmu_get_pasid(filter) ((filter) & 0x3fffff)
1157232ab8bSKan Liang #define iommu_pmu_get_ats(filter) (((filter) >> 24) & 0x1f)
1167232ab8bSKan Liang #define iommu_pmu_get_page_table(filter) (((filter) >> 32) & 0x1f)
1177232ab8bSKan Liang
1187232ab8bSKan Liang #define iommu_pmu_set_filter(_name, _config, _filter, _idx, _econfig) \
1197232ab8bSKan Liang { \
1207232ab8bSKan Liang if ((iommu_pmu->filter & _filter) && iommu_pmu_en_##_name(_econfig)) { \
1217232ab8bSKan Liang dmar_writel(iommu_pmu->cfg_reg + _idx * IOMMU_PMU_CFG_OFFSET + \
1227232ab8bSKan Liang IOMMU_PMU_CFG_SIZE + \
1237232ab8bSKan Liang (ffs(_filter) - 1) * IOMMU_PMU_CFG_FILTERS_OFFSET, \
1247232ab8bSKan Liang iommu_pmu_get_##_name(_config) | IOMMU_PMU_FILTER_EN);\
1257232ab8bSKan Liang } \
1267232ab8bSKan Liang }
1277232ab8bSKan Liang
1287232ab8bSKan Liang #define iommu_pmu_clear_filter(_filter, _idx) \
1297232ab8bSKan Liang { \
1307232ab8bSKan Liang if (iommu_pmu->filter & _filter) { \
1317232ab8bSKan Liang dmar_writel(iommu_pmu->cfg_reg + _idx * IOMMU_PMU_CFG_OFFSET + \
1327232ab8bSKan Liang IOMMU_PMU_CFG_SIZE + \
1337232ab8bSKan Liang (ffs(_filter) - 1) * IOMMU_PMU_CFG_FILTERS_OFFSET, \
1347232ab8bSKan Liang 0); \
1357232ab8bSKan Liang } \
1367232ab8bSKan Liang }
1377232ab8bSKan Liang
1387232ab8bSKan Liang /*
1397232ab8bSKan Liang * Define the event attr related functions
1407232ab8bSKan Liang * Input: _name: event attr name
1417232ab8bSKan Liang * _string: string of the event in sysfs
1427232ab8bSKan Liang * _g_idx: event group encoding
1437232ab8bSKan Liang * _event: event encoding
1447232ab8bSKan Liang */
1457232ab8bSKan Liang #define IOMMU_PMU_EVENT_ATTR(_name, _string, _g_idx, _event) \
1467232ab8bSKan Liang PMU_EVENT_ATTR_STRING(_name, event_attr_##_name, _string) \
1477232ab8bSKan Liang \
1487232ab8bSKan Liang static struct attribute *_name##_attr[] = { \
1497232ab8bSKan Liang &event_attr_##_name.attr.attr, \
1507232ab8bSKan Liang NULL \
1517232ab8bSKan Liang }; \
1527232ab8bSKan Liang \
1537232ab8bSKan Liang static umode_t \
1547232ab8bSKan Liang _name##_is_visible(struct kobject *kobj, struct attribute *attr, int i) \
1557232ab8bSKan Liang { \
1567232ab8bSKan Liang struct device *dev = kobj_to_dev(kobj); \
1577232ab8bSKan Liang struct iommu_pmu *iommu_pmu = dev_to_iommu_pmu(dev); \
1587232ab8bSKan Liang \
1597232ab8bSKan Liang if (!iommu_pmu) \
1607232ab8bSKan Liang return 0; \
1617232ab8bSKan Liang return (iommu_pmu->evcap[_g_idx] & _event) ? attr->mode : 0; \
1627232ab8bSKan Liang } \
1637232ab8bSKan Liang \
1647232ab8bSKan Liang static struct attribute_group _name = { \
1657232ab8bSKan Liang .name = "events", \
1667232ab8bSKan Liang .attrs = _name##_attr, \
1677232ab8bSKan Liang .is_visible = _name##_is_visible, \
1687232ab8bSKan Liang };
1697232ab8bSKan Liang
1707232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(iommu_clocks, "event_group=0x0,event=0x001", 0x0, 0x001)
1717232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(iommu_requests, "event_group=0x0,event=0x002", 0x0, 0x002)
1727232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(pw_occupancy, "event_group=0x0,event=0x004", 0x0, 0x004)
1737232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(ats_blocked, "event_group=0x0,event=0x008", 0x0, 0x008)
1747232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(iommu_mrds, "event_group=0x1,event=0x001", 0x1, 0x001)
1757232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(iommu_mem_blocked, "event_group=0x1,event=0x020", 0x1, 0x020)
1767232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(pg_req_posted, "event_group=0x1,event=0x040", 0x1, 0x040)
1777232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(ctxt_cache_lookup, "event_group=0x2,event=0x001", 0x2, 0x001)
1787232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(ctxt_cache_hit, "event_group=0x2,event=0x002", 0x2, 0x002)
1797232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(pasid_cache_lookup, "event_group=0x2,event=0x004", 0x2, 0x004)
1807232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(pasid_cache_hit, "event_group=0x2,event=0x008", 0x2, 0x008)
1817232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(ss_nonleaf_lookup, "event_group=0x2,event=0x010", 0x2, 0x010)
1827232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(ss_nonleaf_hit, "event_group=0x2,event=0x020", 0x2, 0x020)
1837232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(fs_nonleaf_lookup, "event_group=0x2,event=0x040", 0x2, 0x040)
1847232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(fs_nonleaf_hit, "event_group=0x2,event=0x080", 0x2, 0x080)
1857232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(hpt_nonleaf_lookup, "event_group=0x2,event=0x100", 0x2, 0x100)
1867232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(hpt_nonleaf_hit, "event_group=0x2,event=0x200", 0x2, 0x200)
1877232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(iotlb_lookup, "event_group=0x3,event=0x001", 0x3, 0x001)
1887232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(iotlb_hit, "event_group=0x3,event=0x002", 0x3, 0x002)
1897232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(hpt_leaf_lookup, "event_group=0x3,event=0x004", 0x3, 0x004)
1907232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(hpt_leaf_hit, "event_group=0x3,event=0x008", 0x3, 0x008)
1917232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(int_cache_lookup, "event_group=0x4,event=0x001", 0x4, 0x001)
1927232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(int_cache_hit_nonposted, "event_group=0x4,event=0x002", 0x4, 0x002)
1937232ab8bSKan Liang IOMMU_PMU_EVENT_ATTR(int_cache_hit_posted, "event_group=0x4,event=0x004", 0x4, 0x004)
1947232ab8bSKan Liang
1957232ab8bSKan Liang static const struct attribute_group *iommu_pmu_attr_update[] = {
1967232ab8bSKan Liang &filter_requester_id_en,
1977232ab8bSKan Liang &filter_domain_en,
1987232ab8bSKan Liang &filter_pasid_en,
1997232ab8bSKan Liang &filter_ats_en,
2007232ab8bSKan Liang &filter_page_table_en,
2017232ab8bSKan Liang &filter_requester_id,
2027232ab8bSKan Liang &filter_domain,
2037232ab8bSKan Liang &filter_pasid,
2047232ab8bSKan Liang &filter_ats,
2057232ab8bSKan Liang &filter_page_table,
2067232ab8bSKan Liang &iommu_clocks,
2077232ab8bSKan Liang &iommu_requests,
2087232ab8bSKan Liang &pw_occupancy,
2097232ab8bSKan Liang &ats_blocked,
2107232ab8bSKan Liang &iommu_mrds,
2117232ab8bSKan Liang &iommu_mem_blocked,
2127232ab8bSKan Liang &pg_req_posted,
2137232ab8bSKan Liang &ctxt_cache_lookup,
2147232ab8bSKan Liang &ctxt_cache_hit,
2157232ab8bSKan Liang &pasid_cache_lookup,
2167232ab8bSKan Liang &pasid_cache_hit,
2177232ab8bSKan Liang &ss_nonleaf_lookup,
2187232ab8bSKan Liang &ss_nonleaf_hit,
2197232ab8bSKan Liang &fs_nonleaf_lookup,
2207232ab8bSKan Liang &fs_nonleaf_hit,
2217232ab8bSKan Liang &hpt_nonleaf_lookup,
2227232ab8bSKan Liang &hpt_nonleaf_hit,
2237232ab8bSKan Liang &iotlb_lookup,
2247232ab8bSKan Liang &iotlb_hit,
2257232ab8bSKan Liang &hpt_leaf_lookup,
2267232ab8bSKan Liang &hpt_leaf_hit,
2277232ab8bSKan Liang &int_cache_lookup,
2287232ab8bSKan Liang &int_cache_hit_nonposted,
2297232ab8bSKan Liang &int_cache_hit_posted,
2307232ab8bSKan Liang NULL
2317232ab8bSKan Liang };
2327232ab8bSKan Liang
2337232ab8bSKan Liang static inline void __iomem *
iommu_event_base(struct iommu_pmu * iommu_pmu,int idx)2347232ab8bSKan Liang iommu_event_base(struct iommu_pmu *iommu_pmu, int idx)
2357232ab8bSKan Liang {
2367232ab8bSKan Liang return iommu_pmu->cntr_reg + idx * iommu_pmu->cntr_stride;
2377232ab8bSKan Liang }
2387232ab8bSKan Liang
2397232ab8bSKan Liang static inline void __iomem *
iommu_config_base(struct iommu_pmu * iommu_pmu,int idx)2407232ab8bSKan Liang iommu_config_base(struct iommu_pmu *iommu_pmu, int idx)
2417232ab8bSKan Liang {
2427232ab8bSKan Liang return iommu_pmu->cfg_reg + idx * IOMMU_PMU_CFG_OFFSET;
2437232ab8bSKan Liang }
2447232ab8bSKan Liang
iommu_event_to_pmu(struct perf_event * event)2457232ab8bSKan Liang static inline struct iommu_pmu *iommu_event_to_pmu(struct perf_event *event)
2467232ab8bSKan Liang {
2477232ab8bSKan Liang return container_of(event->pmu, struct iommu_pmu, pmu);
2487232ab8bSKan Liang }
2497232ab8bSKan Liang
iommu_event_config(struct perf_event * event)2507232ab8bSKan Liang static inline u64 iommu_event_config(struct perf_event *event)
2517232ab8bSKan Liang {
2527232ab8bSKan Liang u64 config = event->attr.config;
2537232ab8bSKan Liang
2547232ab8bSKan Liang return (iommu_event_select(config) << IOMMU_EVENT_CFG_ES_SHIFT) |
2557232ab8bSKan Liang (iommu_event_group(config) << IOMMU_EVENT_CFG_EGI_SHIFT) |
2567232ab8bSKan Liang IOMMU_EVENT_CFG_INT;
2577232ab8bSKan Liang }
2587232ab8bSKan Liang
is_iommu_pmu_event(struct iommu_pmu * iommu_pmu,struct perf_event * event)2597232ab8bSKan Liang static inline bool is_iommu_pmu_event(struct iommu_pmu *iommu_pmu,
2607232ab8bSKan Liang struct perf_event *event)
2617232ab8bSKan Liang {
2627232ab8bSKan Liang return event->pmu == &iommu_pmu->pmu;
2637232ab8bSKan Liang }
2647232ab8bSKan Liang
iommu_pmu_validate_event(struct perf_event * event)2657232ab8bSKan Liang static int iommu_pmu_validate_event(struct perf_event *event)
2667232ab8bSKan Liang {
2677232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
2687232ab8bSKan Liang u32 event_group = iommu_event_group(event->attr.config);
2697232ab8bSKan Liang
2707232ab8bSKan Liang if (event_group >= iommu_pmu->num_eg)
2717232ab8bSKan Liang return -EINVAL;
2727232ab8bSKan Liang
2737232ab8bSKan Liang return 0;
2747232ab8bSKan Liang }
2757232ab8bSKan Liang
iommu_pmu_validate_group(struct perf_event * event)2767232ab8bSKan Liang static int iommu_pmu_validate_group(struct perf_event *event)
2777232ab8bSKan Liang {
2787232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
2797232ab8bSKan Liang struct perf_event *sibling;
2807232ab8bSKan Liang int nr = 0;
2817232ab8bSKan Liang
2827232ab8bSKan Liang /*
2837232ab8bSKan Liang * All events in a group must be scheduled simultaneously.
2847232ab8bSKan Liang * Check whether there is enough counters for all the events.
2857232ab8bSKan Liang */
2867232ab8bSKan Liang for_each_sibling_event(sibling, event->group_leader) {
2877232ab8bSKan Liang if (!is_iommu_pmu_event(iommu_pmu, sibling) ||
2887232ab8bSKan Liang sibling->state <= PERF_EVENT_STATE_OFF)
2897232ab8bSKan Liang continue;
2907232ab8bSKan Liang
2917232ab8bSKan Liang if (++nr > iommu_pmu->num_cntr)
2927232ab8bSKan Liang return -EINVAL;
2937232ab8bSKan Liang }
2947232ab8bSKan Liang
2957232ab8bSKan Liang return 0;
2967232ab8bSKan Liang }
2977232ab8bSKan Liang
iommu_pmu_event_init(struct perf_event * event)2987232ab8bSKan Liang static int iommu_pmu_event_init(struct perf_event *event)
2997232ab8bSKan Liang {
3007232ab8bSKan Liang struct hw_perf_event *hwc = &event->hw;
3017232ab8bSKan Liang
3027232ab8bSKan Liang if (event->attr.type != event->pmu->type)
3037232ab8bSKan Liang return -ENOENT;
3047232ab8bSKan Liang
3057232ab8bSKan Liang /* sampling not supported */
3067232ab8bSKan Liang if (event->attr.sample_period)
3077232ab8bSKan Liang return -EINVAL;
3087232ab8bSKan Liang
3097232ab8bSKan Liang if (event->cpu < 0)
3107232ab8bSKan Liang return -EINVAL;
3117232ab8bSKan Liang
3127232ab8bSKan Liang if (iommu_pmu_validate_event(event))
3137232ab8bSKan Liang return -EINVAL;
3147232ab8bSKan Liang
3157232ab8bSKan Liang hwc->config = iommu_event_config(event);
3167232ab8bSKan Liang
3177232ab8bSKan Liang return iommu_pmu_validate_group(event);
3187232ab8bSKan Liang }
3197232ab8bSKan Liang
iommu_pmu_event_update(struct perf_event * event)3207232ab8bSKan Liang static void iommu_pmu_event_update(struct perf_event *event)
3217232ab8bSKan Liang {
3227232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
3237232ab8bSKan Liang struct hw_perf_event *hwc = &event->hw;
3247232ab8bSKan Liang u64 prev_count, new_count, delta;
3257232ab8bSKan Liang int shift = 64 - iommu_pmu->cntr_width;
3267232ab8bSKan Liang
3277232ab8bSKan Liang again:
3287232ab8bSKan Liang prev_count = local64_read(&hwc->prev_count);
3297232ab8bSKan Liang new_count = dmar_readq(iommu_event_base(iommu_pmu, hwc->idx));
3307232ab8bSKan Liang if (local64_xchg(&hwc->prev_count, new_count) != prev_count)
3317232ab8bSKan Liang goto again;
3327232ab8bSKan Liang
3337232ab8bSKan Liang /*
3347232ab8bSKan Liang * The counter width is enumerated. Always shift the counter
3357232ab8bSKan Liang * before using it.
3367232ab8bSKan Liang */
3377232ab8bSKan Liang delta = (new_count << shift) - (prev_count << shift);
3387232ab8bSKan Liang delta >>= shift;
3397232ab8bSKan Liang
3407232ab8bSKan Liang local64_add(delta, &event->count);
3417232ab8bSKan Liang }
3427232ab8bSKan Liang
iommu_pmu_start(struct perf_event * event,int flags)3437232ab8bSKan Liang static void iommu_pmu_start(struct perf_event *event, int flags)
3447232ab8bSKan Liang {
3457232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
3467232ab8bSKan Liang struct intel_iommu *iommu = iommu_pmu->iommu;
3477232ab8bSKan Liang struct hw_perf_event *hwc = &event->hw;
3487232ab8bSKan Liang u64 count;
3497232ab8bSKan Liang
3507232ab8bSKan Liang if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
3517232ab8bSKan Liang return;
3527232ab8bSKan Liang
3537232ab8bSKan Liang if (WARN_ON_ONCE(hwc->idx < 0 || hwc->idx >= IOMMU_PMU_IDX_MAX))
3547232ab8bSKan Liang return;
3557232ab8bSKan Liang
3567232ab8bSKan Liang if (flags & PERF_EF_RELOAD)
3577232ab8bSKan Liang WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
3587232ab8bSKan Liang
3597232ab8bSKan Liang hwc->state = 0;
3607232ab8bSKan Liang
3617232ab8bSKan Liang /* Always reprogram the period */
3627232ab8bSKan Liang count = dmar_readq(iommu_event_base(iommu_pmu, hwc->idx));
3637232ab8bSKan Liang local64_set((&hwc->prev_count), count);
3647232ab8bSKan Liang
3657232ab8bSKan Liang /*
3667232ab8bSKan Liang * The error of ecmd will be ignored.
3677232ab8bSKan Liang * - The existing perf_event subsystem doesn't handle the error.
3687232ab8bSKan Liang * Only IOMMU PMU returns runtime HW error. We don't want to
3697232ab8bSKan Liang * change the existing generic interfaces for the specific case.
3707232ab8bSKan Liang * - It's a corner case caused by HW, which is very unlikely to
3717232ab8bSKan Liang * happen. There is nothing SW can do.
3727232ab8bSKan Liang * - The worst case is that the user will get <not count> with
3737232ab8bSKan Liang * perf command, which can give the user some hints.
3747232ab8bSKan Liang */
3757232ab8bSKan Liang ecmd_submit_sync(iommu, DMA_ECMD_ENABLE, hwc->idx, 0);
3767232ab8bSKan Liang
3777232ab8bSKan Liang perf_event_update_userpage(event);
3787232ab8bSKan Liang }
3797232ab8bSKan Liang
iommu_pmu_stop(struct perf_event * event,int flags)3807232ab8bSKan Liang static void iommu_pmu_stop(struct perf_event *event, int flags)
3817232ab8bSKan Liang {
3827232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
3837232ab8bSKan Liang struct intel_iommu *iommu = iommu_pmu->iommu;
3847232ab8bSKan Liang struct hw_perf_event *hwc = &event->hw;
3857232ab8bSKan Liang
3867232ab8bSKan Liang if (!(hwc->state & PERF_HES_STOPPED)) {
3877232ab8bSKan Liang ecmd_submit_sync(iommu, DMA_ECMD_DISABLE, hwc->idx, 0);
3887232ab8bSKan Liang
3897232ab8bSKan Liang iommu_pmu_event_update(event);
3907232ab8bSKan Liang
3917232ab8bSKan Liang hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
3927232ab8bSKan Liang }
3937232ab8bSKan Liang }
3947232ab8bSKan Liang
3957232ab8bSKan Liang static inline int
iommu_pmu_validate_per_cntr_event(struct iommu_pmu * iommu_pmu,int idx,struct perf_event * event)3967232ab8bSKan Liang iommu_pmu_validate_per_cntr_event(struct iommu_pmu *iommu_pmu,
3977232ab8bSKan Liang int idx, struct perf_event *event)
3987232ab8bSKan Liang {
3997232ab8bSKan Liang u32 event_group = iommu_event_group(event->attr.config);
4007232ab8bSKan Liang u32 select = iommu_event_select(event->attr.config);
4017232ab8bSKan Liang
4027232ab8bSKan Liang if (!(iommu_pmu->cntr_evcap[idx][event_group] & select))
4037232ab8bSKan Liang return -EINVAL;
4047232ab8bSKan Liang
4057232ab8bSKan Liang return 0;
4067232ab8bSKan Liang }
4077232ab8bSKan Liang
iommu_pmu_assign_event(struct iommu_pmu * iommu_pmu,struct perf_event * event)4087232ab8bSKan Liang static int iommu_pmu_assign_event(struct iommu_pmu *iommu_pmu,
4097232ab8bSKan Liang struct perf_event *event)
4107232ab8bSKan Liang {
4117232ab8bSKan Liang struct hw_perf_event *hwc = &event->hw;
4127232ab8bSKan Liang int idx;
4137232ab8bSKan Liang
4147232ab8bSKan Liang /*
4157232ab8bSKan Liang * The counters which support limited events are usually at the end.
4167232ab8bSKan Liang * Schedule them first to accommodate more events.
4177232ab8bSKan Liang */
4187232ab8bSKan Liang for (idx = iommu_pmu->num_cntr - 1; idx >= 0; idx--) {
4197232ab8bSKan Liang if (test_and_set_bit(idx, iommu_pmu->used_mask))
4207232ab8bSKan Liang continue;
4217232ab8bSKan Liang /* Check per-counter event capabilities */
4227232ab8bSKan Liang if (!iommu_pmu_validate_per_cntr_event(iommu_pmu, idx, event))
4237232ab8bSKan Liang break;
4247232ab8bSKan Liang clear_bit(idx, iommu_pmu->used_mask);
4257232ab8bSKan Liang }
4267232ab8bSKan Liang if (idx < 0)
4277232ab8bSKan Liang return -EINVAL;
4287232ab8bSKan Liang
4297232ab8bSKan Liang iommu_pmu->event_list[idx] = event;
4307232ab8bSKan Liang hwc->idx = idx;
4317232ab8bSKan Liang
4327232ab8bSKan Liang /* config events */
4337232ab8bSKan Liang dmar_writeq(iommu_config_base(iommu_pmu, idx), hwc->config);
4347232ab8bSKan Liang
4357232ab8bSKan Liang iommu_pmu_set_filter(requester_id, event->attr.config1,
4367232ab8bSKan Liang IOMMU_PMU_FILTER_REQUESTER_ID, idx,
4377232ab8bSKan Liang event->attr.config1);
4387232ab8bSKan Liang iommu_pmu_set_filter(domain, event->attr.config1,
4397232ab8bSKan Liang IOMMU_PMU_FILTER_DOMAIN, idx,
4407232ab8bSKan Liang event->attr.config1);
441*f3ccbb6bSXuchun Shang iommu_pmu_set_filter(pasid, event->attr.config2,
4427232ab8bSKan Liang IOMMU_PMU_FILTER_PASID, idx,
4437232ab8bSKan Liang event->attr.config1);
4447232ab8bSKan Liang iommu_pmu_set_filter(ats, event->attr.config2,
4457232ab8bSKan Liang IOMMU_PMU_FILTER_ATS, idx,
4467232ab8bSKan Liang event->attr.config1);
4477232ab8bSKan Liang iommu_pmu_set_filter(page_table, event->attr.config2,
4487232ab8bSKan Liang IOMMU_PMU_FILTER_PAGE_TABLE, idx,
4497232ab8bSKan Liang event->attr.config1);
4507232ab8bSKan Liang
4517232ab8bSKan Liang return 0;
4527232ab8bSKan Liang }
4537232ab8bSKan Liang
iommu_pmu_add(struct perf_event * event,int flags)4547232ab8bSKan Liang static int iommu_pmu_add(struct perf_event *event, int flags)
4557232ab8bSKan Liang {
4567232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
4577232ab8bSKan Liang struct hw_perf_event *hwc = &event->hw;
4587232ab8bSKan Liang int ret;
4597232ab8bSKan Liang
4607232ab8bSKan Liang ret = iommu_pmu_assign_event(iommu_pmu, event);
4617232ab8bSKan Liang if (ret < 0)
4627232ab8bSKan Liang return ret;
4637232ab8bSKan Liang
4647232ab8bSKan Liang hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
4657232ab8bSKan Liang
4667232ab8bSKan Liang if (flags & PERF_EF_START)
4677232ab8bSKan Liang iommu_pmu_start(event, 0);
4687232ab8bSKan Liang
4697232ab8bSKan Liang return 0;
4707232ab8bSKan Liang }
4717232ab8bSKan Liang
iommu_pmu_del(struct perf_event * event,int flags)4727232ab8bSKan Liang static void iommu_pmu_del(struct perf_event *event, int flags)
4737232ab8bSKan Liang {
4747232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu_event_to_pmu(event);
4757232ab8bSKan Liang int idx = event->hw.idx;
4767232ab8bSKan Liang
4777232ab8bSKan Liang iommu_pmu_stop(event, PERF_EF_UPDATE);
4787232ab8bSKan Liang
4797232ab8bSKan Liang iommu_pmu_clear_filter(IOMMU_PMU_FILTER_REQUESTER_ID, idx);
4807232ab8bSKan Liang iommu_pmu_clear_filter(IOMMU_PMU_FILTER_DOMAIN, idx);
4817232ab8bSKan Liang iommu_pmu_clear_filter(IOMMU_PMU_FILTER_PASID, idx);
4827232ab8bSKan Liang iommu_pmu_clear_filter(IOMMU_PMU_FILTER_ATS, idx);
4837232ab8bSKan Liang iommu_pmu_clear_filter(IOMMU_PMU_FILTER_PAGE_TABLE, idx);
4847232ab8bSKan Liang
4857232ab8bSKan Liang iommu_pmu->event_list[idx] = NULL;
4867232ab8bSKan Liang event->hw.idx = -1;
4877232ab8bSKan Liang clear_bit(idx, iommu_pmu->used_mask);
4887232ab8bSKan Liang
4897232ab8bSKan Liang perf_event_update_userpage(event);
4907232ab8bSKan Liang }
4917232ab8bSKan Liang
iommu_pmu_enable(struct pmu * pmu)4927232ab8bSKan Liang static void iommu_pmu_enable(struct pmu *pmu)
4937232ab8bSKan Liang {
4947232ab8bSKan Liang struct iommu_pmu *iommu_pmu = container_of(pmu, struct iommu_pmu, pmu);
4957232ab8bSKan Liang struct intel_iommu *iommu = iommu_pmu->iommu;
4967232ab8bSKan Liang
4977232ab8bSKan Liang ecmd_submit_sync(iommu, DMA_ECMD_UNFREEZE, 0, 0);
4987232ab8bSKan Liang }
4997232ab8bSKan Liang
iommu_pmu_disable(struct pmu * pmu)5007232ab8bSKan Liang static void iommu_pmu_disable(struct pmu *pmu)
5017232ab8bSKan Liang {
5027232ab8bSKan Liang struct iommu_pmu *iommu_pmu = container_of(pmu, struct iommu_pmu, pmu);
5037232ab8bSKan Liang struct intel_iommu *iommu = iommu_pmu->iommu;
5047232ab8bSKan Liang
5057232ab8bSKan Liang ecmd_submit_sync(iommu, DMA_ECMD_FREEZE, 0, 0);
5067232ab8bSKan Liang }
5077232ab8bSKan Liang
iommu_pmu_counter_overflow(struct iommu_pmu * iommu_pmu)5084a0d4265SKan Liang static void iommu_pmu_counter_overflow(struct iommu_pmu *iommu_pmu)
5094a0d4265SKan Liang {
5104a0d4265SKan Liang struct perf_event *event;
5114a0d4265SKan Liang u64 status;
5124a0d4265SKan Liang int i;
5134a0d4265SKan Liang
5144a0d4265SKan Liang /*
5154a0d4265SKan Liang * Two counters may be overflowed very close. Always check
5164a0d4265SKan Liang * whether there are more to handle.
5174a0d4265SKan Liang */
5184a0d4265SKan Liang while ((status = dmar_readq(iommu_pmu->overflow))) {
5194a0d4265SKan Liang for_each_set_bit(i, (unsigned long *)&status, iommu_pmu->num_cntr) {
5204a0d4265SKan Liang /*
5214a0d4265SKan Liang * Find the assigned event of the counter.
5224a0d4265SKan Liang * Accumulate the value into the event->count.
5234a0d4265SKan Liang */
5244a0d4265SKan Liang event = iommu_pmu->event_list[i];
5254a0d4265SKan Liang if (!event) {
5264a0d4265SKan Liang pr_warn_once("Cannot find the assigned event for counter %d\n", i);
5274a0d4265SKan Liang continue;
5284a0d4265SKan Liang }
5294a0d4265SKan Liang iommu_pmu_event_update(event);
5304a0d4265SKan Liang }
5314a0d4265SKan Liang
5324a0d4265SKan Liang dmar_writeq(iommu_pmu->overflow, status);
5334a0d4265SKan Liang }
5344a0d4265SKan Liang }
5354a0d4265SKan Liang
iommu_pmu_irq_handler(int irq,void * dev_id)5364a0d4265SKan Liang static irqreturn_t iommu_pmu_irq_handler(int irq, void *dev_id)
5374a0d4265SKan Liang {
5384a0d4265SKan Liang struct intel_iommu *iommu = dev_id;
5394a0d4265SKan Liang
5404a0d4265SKan Liang if (!dmar_readl(iommu->reg + DMAR_PERFINTRSTS_REG))
5414a0d4265SKan Liang return IRQ_NONE;
5424a0d4265SKan Liang
5434a0d4265SKan Liang iommu_pmu_counter_overflow(iommu->pmu);
5444a0d4265SKan Liang
5454a0d4265SKan Liang /* Clear the status bit */
5464a0d4265SKan Liang dmar_writel(iommu->reg + DMAR_PERFINTRSTS_REG, DMA_PERFINTRSTS_PIS);
5474a0d4265SKan Liang
5484a0d4265SKan Liang return IRQ_HANDLED;
5494a0d4265SKan Liang }
5504a0d4265SKan Liang
__iommu_pmu_register(struct intel_iommu * iommu)5517232ab8bSKan Liang static int __iommu_pmu_register(struct intel_iommu *iommu)
5527232ab8bSKan Liang {
5537232ab8bSKan Liang struct iommu_pmu *iommu_pmu = iommu->pmu;
5547232ab8bSKan Liang
5557232ab8bSKan Liang iommu_pmu->pmu.name = iommu->name;
5567232ab8bSKan Liang iommu_pmu->pmu.task_ctx_nr = perf_invalid_context;
5577232ab8bSKan Liang iommu_pmu->pmu.event_init = iommu_pmu_event_init;
5587232ab8bSKan Liang iommu_pmu->pmu.pmu_enable = iommu_pmu_enable;
5597232ab8bSKan Liang iommu_pmu->pmu.pmu_disable = iommu_pmu_disable;
5607232ab8bSKan Liang iommu_pmu->pmu.add = iommu_pmu_add;
5617232ab8bSKan Liang iommu_pmu->pmu.del = iommu_pmu_del;
5627232ab8bSKan Liang iommu_pmu->pmu.start = iommu_pmu_start;
5637232ab8bSKan Liang iommu_pmu->pmu.stop = iommu_pmu_stop;
5647232ab8bSKan Liang iommu_pmu->pmu.read = iommu_pmu_event_update;
5657232ab8bSKan Liang iommu_pmu->pmu.attr_groups = iommu_pmu_attr_groups;
5667232ab8bSKan Liang iommu_pmu->pmu.attr_update = iommu_pmu_attr_update;
5677232ab8bSKan Liang iommu_pmu->pmu.capabilities = PERF_PMU_CAP_NO_EXCLUDE;
5687232ab8bSKan Liang iommu_pmu->pmu.module = THIS_MODULE;
5697232ab8bSKan Liang
5707232ab8bSKan Liang return perf_pmu_register(&iommu_pmu->pmu, iommu_pmu->pmu.name, -1);
5717232ab8bSKan Liang }
5727232ab8bSKan Liang
573a6a5006dSKan Liang static inline void __iomem *
get_perf_reg_address(struct intel_iommu * iommu,u32 offset)574a6a5006dSKan Liang get_perf_reg_address(struct intel_iommu *iommu, u32 offset)
575a6a5006dSKan Liang {
576a6a5006dSKan Liang u32 off = dmar_readl(iommu->reg + offset);
577a6a5006dSKan Liang
578a6a5006dSKan Liang return iommu->reg + off;
579a6a5006dSKan Liang }
580a6a5006dSKan Liang
alloc_iommu_pmu(struct intel_iommu * iommu)581a6a5006dSKan Liang int alloc_iommu_pmu(struct intel_iommu *iommu)
582a6a5006dSKan Liang {
583a6a5006dSKan Liang struct iommu_pmu *iommu_pmu;
584a6a5006dSKan Liang int i, j, ret;
585a6a5006dSKan Liang u64 perfcap;
586a6a5006dSKan Liang u32 cap;
587a6a5006dSKan Liang
588a6a5006dSKan Liang if (!ecap_pms(iommu->ecap))
589a6a5006dSKan Liang return 0;
590a6a5006dSKan Liang
591a6a5006dSKan Liang /* The IOMMU PMU requires the ECMD support as well */
592a6a5006dSKan Liang if (!cap_ecmds(iommu->cap))
593a6a5006dSKan Liang return -ENODEV;
594a6a5006dSKan Liang
595a6a5006dSKan Liang perfcap = dmar_readq(iommu->reg + DMAR_PERFCAP_REG);
596a6a5006dSKan Liang /* The performance monitoring is not supported. */
597a6a5006dSKan Liang if (!perfcap)
598a6a5006dSKan Liang return -ENODEV;
599a6a5006dSKan Liang
600a6a5006dSKan Liang /* Sanity check for the number of the counters and event groups */
601a6a5006dSKan Liang if (!pcap_num_cntr(perfcap) || !pcap_num_event_group(perfcap))
602a6a5006dSKan Liang return -ENODEV;
603a6a5006dSKan Liang
604a6a5006dSKan Liang /* The interrupt on overflow is required */
605a6a5006dSKan Liang if (!pcap_interrupt(perfcap))
606a6a5006dSKan Liang return -ENODEV;
607a6a5006dSKan Liang
6087232ab8bSKan Liang /* Check required Enhanced Command Capability */
6097232ab8bSKan Liang if (!ecmd_has_pmu_essential(iommu))
6107232ab8bSKan Liang return -ENODEV;
6117232ab8bSKan Liang
612a6a5006dSKan Liang iommu_pmu = kzalloc(sizeof(*iommu_pmu), GFP_KERNEL);
613a6a5006dSKan Liang if (!iommu_pmu)
614a6a5006dSKan Liang return -ENOMEM;
615a6a5006dSKan Liang
616a6a5006dSKan Liang iommu_pmu->num_cntr = pcap_num_cntr(perfcap);
6177232ab8bSKan Liang if (iommu_pmu->num_cntr > IOMMU_PMU_IDX_MAX) {
6187232ab8bSKan Liang pr_warn_once("The number of IOMMU counters %d > max(%d), clipping!",
6197232ab8bSKan Liang iommu_pmu->num_cntr, IOMMU_PMU_IDX_MAX);
6207232ab8bSKan Liang iommu_pmu->num_cntr = IOMMU_PMU_IDX_MAX;
6217232ab8bSKan Liang }
6227232ab8bSKan Liang
623a6a5006dSKan Liang iommu_pmu->cntr_width = pcap_cntr_width(perfcap);
624a6a5006dSKan Liang iommu_pmu->filter = pcap_filters_mask(perfcap);
625a6a5006dSKan Liang iommu_pmu->cntr_stride = pcap_cntr_stride(perfcap);
626a6a5006dSKan Liang iommu_pmu->num_eg = pcap_num_event_group(perfcap);
627a6a5006dSKan Liang
628a6a5006dSKan Liang iommu_pmu->evcap = kcalloc(iommu_pmu->num_eg, sizeof(u64), GFP_KERNEL);
629a6a5006dSKan Liang if (!iommu_pmu->evcap) {
630a6a5006dSKan Liang ret = -ENOMEM;
631a6a5006dSKan Liang goto free_pmu;
632a6a5006dSKan Liang }
633a6a5006dSKan Liang
634a6a5006dSKan Liang /* Parse event group capabilities */
635a6a5006dSKan Liang for (i = 0; i < iommu_pmu->num_eg; i++) {
636a6a5006dSKan Liang u64 pcap;
637a6a5006dSKan Liang
638a6a5006dSKan Liang pcap = dmar_readq(iommu->reg + DMAR_PERFEVNTCAP_REG +
639a6a5006dSKan Liang i * IOMMU_PMU_CAP_REGS_STEP);
640a6a5006dSKan Liang iommu_pmu->evcap[i] = pecap_es(pcap);
641a6a5006dSKan Liang }
642a6a5006dSKan Liang
643a6a5006dSKan Liang iommu_pmu->cntr_evcap = kcalloc(iommu_pmu->num_cntr, sizeof(u32 *), GFP_KERNEL);
644a6a5006dSKan Liang if (!iommu_pmu->cntr_evcap) {
645a6a5006dSKan Liang ret = -ENOMEM;
646a6a5006dSKan Liang goto free_pmu_evcap;
647a6a5006dSKan Liang }
648a6a5006dSKan Liang for (i = 0; i < iommu_pmu->num_cntr; i++) {
649a6a5006dSKan Liang iommu_pmu->cntr_evcap[i] = kcalloc(iommu_pmu->num_eg, sizeof(u32), GFP_KERNEL);
650a6a5006dSKan Liang if (!iommu_pmu->cntr_evcap[i]) {
651a6a5006dSKan Liang ret = -ENOMEM;
652a6a5006dSKan Liang goto free_pmu_cntr_evcap;
653a6a5006dSKan Liang }
654a6a5006dSKan Liang /*
655a6a5006dSKan Liang * Set to the global capabilities, will adjust according
656a6a5006dSKan Liang * to per-counter capabilities later.
657a6a5006dSKan Liang */
658a6a5006dSKan Liang for (j = 0; j < iommu_pmu->num_eg; j++)
659a6a5006dSKan Liang iommu_pmu->cntr_evcap[i][j] = (u32)iommu_pmu->evcap[j];
660a6a5006dSKan Liang }
661a6a5006dSKan Liang
662a6a5006dSKan Liang iommu_pmu->cfg_reg = get_perf_reg_address(iommu, DMAR_PERFCFGOFF_REG);
663a6a5006dSKan Liang iommu_pmu->cntr_reg = get_perf_reg_address(iommu, DMAR_PERFCNTROFF_REG);
664a6a5006dSKan Liang iommu_pmu->overflow = get_perf_reg_address(iommu, DMAR_PERFOVFOFF_REG);
665a6a5006dSKan Liang
666a6a5006dSKan Liang /*
667a6a5006dSKan Liang * Check per-counter capabilities. All counters should have the
668a6a5006dSKan Liang * same capabilities on Interrupt on Overflow Support and Counter
669a6a5006dSKan Liang * Width.
670a6a5006dSKan Liang */
671a6a5006dSKan Liang for (i = 0; i < iommu_pmu->num_cntr; i++) {
672a6a5006dSKan Liang cap = dmar_readl(iommu_pmu->cfg_reg +
673a6a5006dSKan Liang i * IOMMU_PMU_CFG_OFFSET +
674a6a5006dSKan Liang IOMMU_PMU_CFG_CNTRCAP_OFFSET);
675a6a5006dSKan Liang if (!iommu_cntrcap_pcc(cap))
676a6a5006dSKan Liang continue;
677a6a5006dSKan Liang
678a6a5006dSKan Liang /*
679a6a5006dSKan Liang * It's possible that some counters have a different
680a6a5006dSKan Liang * capability because of e.g., HW bug. Check the corner
681a6a5006dSKan Liang * case here and simply drop those counters.
682a6a5006dSKan Liang */
683a6a5006dSKan Liang if ((iommu_cntrcap_cw(cap) != iommu_pmu->cntr_width) ||
684a6a5006dSKan Liang !iommu_cntrcap_ios(cap)) {
685a6a5006dSKan Liang iommu_pmu->num_cntr = i;
686a6a5006dSKan Liang pr_warn("PMU counter capability inconsistent, counter number reduced to %d\n",
687a6a5006dSKan Liang iommu_pmu->num_cntr);
688a6a5006dSKan Liang }
689a6a5006dSKan Liang
690a6a5006dSKan Liang /* Clear the pre-defined events group */
691a6a5006dSKan Liang for (j = 0; j < iommu_pmu->num_eg; j++)
692a6a5006dSKan Liang iommu_pmu->cntr_evcap[i][j] = 0;
693a6a5006dSKan Liang
694a6a5006dSKan Liang /* Override with per-counter event capabilities */
695a6a5006dSKan Liang for (j = 0; j < iommu_cntrcap_egcnt(cap); j++) {
696a6a5006dSKan Liang cap = dmar_readl(iommu_pmu->cfg_reg + i * IOMMU_PMU_CFG_OFFSET +
697a6a5006dSKan Liang IOMMU_PMU_CFG_CNTREVCAP_OFFSET +
698a6a5006dSKan Liang (j * IOMMU_PMU_OFF_REGS_STEP));
699a6a5006dSKan Liang iommu_pmu->cntr_evcap[i][iommu_event_group(cap)] = iommu_event_select(cap);
700a6a5006dSKan Liang /*
701a6a5006dSKan Liang * Some events may only be supported by a specific counter.
702a6a5006dSKan Liang * Track them in the evcap as well.
703a6a5006dSKan Liang */
704a6a5006dSKan Liang iommu_pmu->evcap[iommu_event_group(cap)] |= iommu_event_select(cap);
705a6a5006dSKan Liang }
706a6a5006dSKan Liang }
707a6a5006dSKan Liang
708a6a5006dSKan Liang iommu_pmu->iommu = iommu;
709a6a5006dSKan Liang iommu->pmu = iommu_pmu;
710a6a5006dSKan Liang
711a6a5006dSKan Liang return 0;
712a6a5006dSKan Liang
713a6a5006dSKan Liang free_pmu_cntr_evcap:
714a6a5006dSKan Liang for (i = 0; i < iommu_pmu->num_cntr; i++)
715a6a5006dSKan Liang kfree(iommu_pmu->cntr_evcap[i]);
716a6a5006dSKan Liang kfree(iommu_pmu->cntr_evcap);
717a6a5006dSKan Liang free_pmu_evcap:
718a6a5006dSKan Liang kfree(iommu_pmu->evcap);
719a6a5006dSKan Liang free_pmu:
720a6a5006dSKan Liang kfree(iommu_pmu);
721a6a5006dSKan Liang
722a6a5006dSKan Liang return ret;
723a6a5006dSKan Liang }
724a6a5006dSKan Liang
free_iommu_pmu(struct intel_iommu * iommu)725a6a5006dSKan Liang void free_iommu_pmu(struct intel_iommu *iommu)
726a6a5006dSKan Liang {
727a6a5006dSKan Liang struct iommu_pmu *iommu_pmu = iommu->pmu;
728a6a5006dSKan Liang
729a6a5006dSKan Liang if (!iommu_pmu)
730a6a5006dSKan Liang return;
731a6a5006dSKan Liang
732a6a5006dSKan Liang if (iommu_pmu->evcap) {
733a6a5006dSKan Liang int i;
734a6a5006dSKan Liang
735a6a5006dSKan Liang for (i = 0; i < iommu_pmu->num_cntr; i++)
736a6a5006dSKan Liang kfree(iommu_pmu->cntr_evcap[i]);
737a6a5006dSKan Liang kfree(iommu_pmu->cntr_evcap);
738a6a5006dSKan Liang }
739a6a5006dSKan Liang kfree(iommu_pmu->evcap);
740a6a5006dSKan Liang kfree(iommu_pmu);
741a6a5006dSKan Liang iommu->pmu = NULL;
742a6a5006dSKan Liang }
7437232ab8bSKan Liang
iommu_pmu_set_interrupt(struct intel_iommu * iommu)7444a0d4265SKan Liang static int iommu_pmu_set_interrupt(struct intel_iommu *iommu)
7454a0d4265SKan Liang {
7464a0d4265SKan Liang struct iommu_pmu *iommu_pmu = iommu->pmu;
7474a0d4265SKan Liang int irq, ret;
7484a0d4265SKan Liang
7494a0d4265SKan Liang irq = dmar_alloc_hwirq(IOMMU_IRQ_ID_OFFSET_PERF + iommu->seq_id, iommu->node, iommu);
7504a0d4265SKan Liang if (irq <= 0)
7514a0d4265SKan Liang return -EINVAL;
7524a0d4265SKan Liang
7534a0d4265SKan Liang snprintf(iommu_pmu->irq_name, sizeof(iommu_pmu->irq_name), "dmar%d-perf", iommu->seq_id);
7544a0d4265SKan Liang
7554a0d4265SKan Liang iommu->perf_irq = irq;
7564a0d4265SKan Liang ret = request_threaded_irq(irq, NULL, iommu_pmu_irq_handler,
7574a0d4265SKan Liang IRQF_ONESHOT, iommu_pmu->irq_name, iommu);
7584a0d4265SKan Liang if (ret) {
7594a0d4265SKan Liang dmar_free_hwirq(irq);
7604a0d4265SKan Liang iommu->perf_irq = 0;
7614a0d4265SKan Liang return ret;
7624a0d4265SKan Liang }
7634a0d4265SKan Liang return 0;
7644a0d4265SKan Liang }
7654a0d4265SKan Liang
iommu_pmu_unset_interrupt(struct intel_iommu * iommu)7664a0d4265SKan Liang static void iommu_pmu_unset_interrupt(struct intel_iommu *iommu)
7674a0d4265SKan Liang {
7684a0d4265SKan Liang if (!iommu->perf_irq)
7694a0d4265SKan Liang return;
7704a0d4265SKan Liang
7714a0d4265SKan Liang free_irq(iommu->perf_irq, iommu);
7724a0d4265SKan Liang dmar_free_hwirq(iommu->perf_irq);
7734a0d4265SKan Liang iommu->perf_irq = 0;
7744a0d4265SKan Liang }
7754a0d4265SKan Liang
iommu_pmu_cpu_online(unsigned int cpu,struct hlist_node * node)77616812c96SKan Liang static int iommu_pmu_cpu_online(unsigned int cpu, struct hlist_node *node)
7777232ab8bSKan Liang {
77816812c96SKan Liang struct iommu_pmu *iommu_pmu = hlist_entry_safe(node, typeof(*iommu_pmu), cpuhp_node);
77916812c96SKan Liang
78046284c6cSKan Liang if (cpumask_empty(&iommu_pmu_cpu_mask))
78146284c6cSKan Liang cpumask_set_cpu(cpu, &iommu_pmu_cpu_mask);
78246284c6cSKan Liang
78316812c96SKan Liang if (cpumask_test_cpu(cpu, &iommu_pmu_cpu_mask))
78416812c96SKan Liang iommu_pmu->cpu = cpu;
78516812c96SKan Liang
78646284c6cSKan Liang return 0;
78746284c6cSKan Liang }
78846284c6cSKan Liang
iommu_pmu_cpu_offline(unsigned int cpu,struct hlist_node * node)78916812c96SKan Liang static int iommu_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node)
79046284c6cSKan Liang {
79116812c96SKan Liang struct iommu_pmu *iommu_pmu = hlist_entry_safe(node, typeof(*iommu_pmu), cpuhp_node);
79216812c96SKan Liang int target = cpumask_first(&iommu_pmu_cpu_mask);
79316812c96SKan Liang
79416812c96SKan Liang /*
79516812c96SKan Liang * The iommu_pmu_cpu_mask has been updated when offline the CPU
79616812c96SKan Liang * for the first iommu_pmu. Migrate the other iommu_pmu to the
79716812c96SKan Liang * new target.
79816812c96SKan Liang */
79916812c96SKan Liang if (target < nr_cpu_ids && target != iommu_pmu->cpu) {
80016812c96SKan Liang perf_pmu_migrate_context(&iommu_pmu->pmu, cpu, target);
80116812c96SKan Liang iommu_pmu->cpu = target;
80216812c96SKan Liang return 0;
80316812c96SKan Liang }
80446284c6cSKan Liang
80546284c6cSKan Liang if (!cpumask_test_and_clear_cpu(cpu, &iommu_pmu_cpu_mask))
80646284c6cSKan Liang return 0;
80746284c6cSKan Liang
80846284c6cSKan Liang target = cpumask_any_but(cpu_online_mask, cpu);
80946284c6cSKan Liang
81046284c6cSKan Liang if (target < nr_cpu_ids)
81146284c6cSKan Liang cpumask_set_cpu(target, &iommu_pmu_cpu_mask);
81246284c6cSKan Liang else
81316812c96SKan Liang return 0;
81446284c6cSKan Liang
81516812c96SKan Liang perf_pmu_migrate_context(&iommu_pmu->pmu, cpu, target);
81616812c96SKan Liang iommu_pmu->cpu = target;
81746284c6cSKan Liang
81846284c6cSKan Liang return 0;
81946284c6cSKan Liang }
82046284c6cSKan Liang
82146284c6cSKan Liang static int nr_iommu_pmu;
82216812c96SKan Liang static enum cpuhp_state iommu_cpuhp_slot;
82346284c6cSKan Liang
iommu_pmu_cpuhp_setup(struct iommu_pmu * iommu_pmu)82446284c6cSKan Liang static int iommu_pmu_cpuhp_setup(struct iommu_pmu *iommu_pmu)
82546284c6cSKan Liang {
82646284c6cSKan Liang int ret;
82746284c6cSKan Liang
82816812c96SKan Liang if (!nr_iommu_pmu) {
82916812c96SKan Liang ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
83046284c6cSKan Liang "driver/iommu/intel/perfmon:online",
83146284c6cSKan Liang iommu_pmu_cpu_online,
83246284c6cSKan Liang iommu_pmu_cpu_offline);
83316812c96SKan Liang if (ret < 0)
83446284c6cSKan Liang return ret;
83516812c96SKan Liang iommu_cpuhp_slot = ret;
83616812c96SKan Liang }
83716812c96SKan Liang
83816812c96SKan Liang ret = cpuhp_state_add_instance(iommu_cpuhp_slot, &iommu_pmu->cpuhp_node);
83916812c96SKan Liang if (ret) {
84016812c96SKan Liang if (!nr_iommu_pmu)
84116812c96SKan Liang cpuhp_remove_multi_state(iommu_cpuhp_slot);
84216812c96SKan Liang return ret;
84316812c96SKan Liang }
84416812c96SKan Liang nr_iommu_pmu++;
84516812c96SKan Liang
84616812c96SKan Liang return 0;
84746284c6cSKan Liang }
84846284c6cSKan Liang
iommu_pmu_cpuhp_free(struct iommu_pmu * iommu_pmu)84946284c6cSKan Liang static void iommu_pmu_cpuhp_free(struct iommu_pmu *iommu_pmu)
85046284c6cSKan Liang {
85116812c96SKan Liang cpuhp_state_remove_instance(iommu_cpuhp_slot, &iommu_pmu->cpuhp_node);
85216812c96SKan Liang
85346284c6cSKan Liang if (--nr_iommu_pmu)
8547232ab8bSKan Liang return;
8557232ab8bSKan Liang
85616812c96SKan Liang cpuhp_remove_multi_state(iommu_cpuhp_slot);
8577232ab8bSKan Liang }
85846284c6cSKan Liang
iommu_pmu_register(struct intel_iommu * iommu)85946284c6cSKan Liang void iommu_pmu_register(struct intel_iommu *iommu)
86046284c6cSKan Liang {
86146284c6cSKan Liang struct iommu_pmu *iommu_pmu = iommu->pmu;
86246284c6cSKan Liang
86346284c6cSKan Liang if (!iommu_pmu)
86446284c6cSKan Liang return;
86546284c6cSKan Liang
86646284c6cSKan Liang if (__iommu_pmu_register(iommu))
86746284c6cSKan Liang goto err;
86846284c6cSKan Liang
86946284c6cSKan Liang if (iommu_pmu_cpuhp_setup(iommu_pmu))
87046284c6cSKan Liang goto unregister;
87146284c6cSKan Liang
8724a0d4265SKan Liang /* Set interrupt for overflow */
8734a0d4265SKan Liang if (iommu_pmu_set_interrupt(iommu))
8744a0d4265SKan Liang goto cpuhp_free;
8754a0d4265SKan Liang
87646284c6cSKan Liang return;
87746284c6cSKan Liang
8784a0d4265SKan Liang cpuhp_free:
8794a0d4265SKan Liang iommu_pmu_cpuhp_free(iommu_pmu);
88046284c6cSKan Liang unregister:
88146284c6cSKan Liang perf_pmu_unregister(&iommu_pmu->pmu);
88246284c6cSKan Liang err:
88346284c6cSKan Liang pr_err("Failed to register PMU for iommu (seq_id = %d)\n", iommu->seq_id);
88446284c6cSKan Liang free_iommu_pmu(iommu);
8857232ab8bSKan Liang }
8867232ab8bSKan Liang
iommu_pmu_unregister(struct intel_iommu * iommu)8877232ab8bSKan Liang void iommu_pmu_unregister(struct intel_iommu *iommu)
8887232ab8bSKan Liang {
88946284c6cSKan Liang struct iommu_pmu *iommu_pmu = iommu->pmu;
89046284c6cSKan Liang
89146284c6cSKan Liang if (!iommu_pmu)
89246284c6cSKan Liang return;
89346284c6cSKan Liang
8944a0d4265SKan Liang iommu_pmu_unset_interrupt(iommu);
89546284c6cSKan Liang iommu_pmu_cpuhp_free(iommu_pmu);
89646284c6cSKan Liang perf_pmu_unregister(&iommu_pmu->pmu);
8977232ab8bSKan Liang }
898