1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/err.h>
3 #include <linux/zalloc.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <sys/param.h>
9 #include "evlist.h"
10 #include "evsel.h"
11 #include "parse-events.h"
12 #include "parse-events-hybrid.h"
13 #include "debug.h"
14 #include "pmu.h"
15 #include "pmu-hybrid.h"
16 #include "perf.h"
17
config_hybrid_attr(struct perf_event_attr * attr,int type,int pmu_type)18 static void config_hybrid_attr(struct perf_event_attr *attr,
19 int type, int pmu_type)
20 {
21 /*
22 * attr.config layout for type PERF_TYPE_HARDWARE and
23 * PERF_TYPE_HW_CACHE
24 *
25 * PERF_TYPE_HARDWARE: 0xEEEEEEEE000000AA
26 * AA: hardware event ID
27 * EEEEEEEE: PMU type ID
28 * PERF_TYPE_HW_CACHE: 0xEEEEEEEE00DDCCBB
29 * BB: hardware cache ID
30 * CC: hardware cache op ID
31 * DD: hardware cache op result ID
32 * EEEEEEEE: PMU type ID
33 * If the PMU type ID is 0, the PERF_TYPE_RAW will be applied.
34 */
35 attr->type = type;
36 attr->config = (attr->config & PERF_HW_EVENT_MASK) |
37 ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT);
38 }
39
create_event_hybrid(__u32 config_type,int * idx,struct list_head * list,struct perf_event_attr * attr,const char * name,const char * metric_id,struct list_head * config_terms,struct perf_pmu * pmu)40 static int create_event_hybrid(__u32 config_type, int *idx,
41 struct list_head *list,
42 struct perf_event_attr *attr, const char *name,
43 const char *metric_id,
44 struct list_head *config_terms,
45 struct perf_pmu *pmu)
46 {
47 struct evsel *evsel;
48 __u32 type = attr->type;
49 __u64 config = attr->config;
50
51 config_hybrid_attr(attr, config_type, pmu->type);
52
53 /*
54 * Some hybrid hardware cache events are only available on one CPU
55 * PMU. For example, the 'L1-dcache-load-misses' is only available
56 * on cpu_core, while the 'L1-icache-loads' is only available on
57 * cpu_atom. We need to remove "not supported" hybrid cache events.
58 */
59 if (attr->type == PERF_TYPE_HW_CACHE
60 && !is_event_supported(attr->type, attr->config))
61 return 0;
62
63 evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id,
64 pmu, config_terms);
65 if (evsel) {
66 evsel->pmu_name = strdup(pmu->name);
67 if (!evsel->pmu_name)
68 return -ENOMEM;
69 } else
70 return -ENOMEM;
71 attr->type = type;
72 attr->config = config;
73 return 0;
74 }
75
pmu_cmp(struct parse_events_state * parse_state,struct perf_pmu * pmu)76 static int pmu_cmp(struct parse_events_state *parse_state,
77 struct perf_pmu *pmu)
78 {
79 if (parse_state->evlist && parse_state->evlist->hybrid_pmu_name)
80 return strcmp(parse_state->evlist->hybrid_pmu_name, pmu->name);
81
82 if (parse_state->hybrid_pmu_name)
83 return strcmp(parse_state->hybrid_pmu_name, pmu->name);
84
85 return 0;
86 }
87
add_hw_hybrid(struct parse_events_state * parse_state,struct list_head * list,struct perf_event_attr * attr,const char * name,const char * metric_id,struct list_head * config_terms)88 static int add_hw_hybrid(struct parse_events_state *parse_state,
89 struct list_head *list, struct perf_event_attr *attr,
90 const char *name, const char *metric_id,
91 struct list_head *config_terms)
92 {
93 struct perf_pmu *pmu;
94 int ret;
95
96 perf_pmu__for_each_hybrid_pmu(pmu) {
97 LIST_HEAD(terms);
98
99 if (pmu_cmp(parse_state, pmu))
100 continue;
101
102 copy_config_terms(&terms, config_terms);
103 ret = create_event_hybrid(PERF_TYPE_HARDWARE,
104 &parse_state->idx, list, attr, name,
105 metric_id, &terms, pmu);
106 free_config_terms(&terms);
107 if (ret)
108 return ret;
109 }
110
111 return 0;
112 }
113
create_raw_event_hybrid(int * idx,struct list_head * list,struct perf_event_attr * attr,const char * name,const char * metric_id,struct list_head * config_terms,struct perf_pmu * pmu)114 static int create_raw_event_hybrid(int *idx, struct list_head *list,
115 struct perf_event_attr *attr,
116 const char *name,
117 const char *metric_id,
118 struct list_head *config_terms,
119 struct perf_pmu *pmu)
120 {
121 struct evsel *evsel;
122
123 attr->type = pmu->type;
124 evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id,
125 pmu, config_terms);
126 if (evsel)
127 evsel->pmu_name = strdup(pmu->name);
128 else
129 return -ENOMEM;
130
131 return 0;
132 }
133
add_raw_hybrid(struct parse_events_state * parse_state,struct list_head * list,struct perf_event_attr * attr,const char * name,const char * metric_id,struct list_head * config_terms)134 static int add_raw_hybrid(struct parse_events_state *parse_state,
135 struct list_head *list, struct perf_event_attr *attr,
136 const char *name, const char *metric_id,
137 struct list_head *config_terms)
138 {
139 struct perf_pmu *pmu;
140 int ret;
141
142 perf_pmu__for_each_hybrid_pmu(pmu) {
143 LIST_HEAD(terms);
144
145 if (pmu_cmp(parse_state, pmu))
146 continue;
147
148 copy_config_terms(&terms, config_terms);
149 ret = create_raw_event_hybrid(&parse_state->idx, list, attr,
150 name, metric_id, &terms, pmu);
151 free_config_terms(&terms);
152 if (ret)
153 return ret;
154 }
155
156 return 0;
157 }
158
parse_events__add_numeric_hybrid(struct parse_events_state * parse_state,struct list_head * list,struct perf_event_attr * attr,const char * name,const char * metric_id,struct list_head * config_terms,bool * hybrid)159 int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state,
160 struct list_head *list,
161 struct perf_event_attr *attr,
162 const char *name, const char *metric_id,
163 struct list_head *config_terms,
164 bool *hybrid)
165 {
166 *hybrid = false;
167 if (attr->type == PERF_TYPE_SOFTWARE)
168 return 0;
169
170 if (!perf_pmu__has_hybrid())
171 return 0;
172
173 *hybrid = true;
174 if (attr->type != PERF_TYPE_RAW) {
175 return add_hw_hybrid(parse_state, list, attr, name, metric_id,
176 config_terms);
177 }
178
179 return add_raw_hybrid(parse_state, list, attr, name, metric_id,
180 config_terms);
181 }
182
parse_events__add_cache_hybrid(struct list_head * list,int * idx,struct perf_event_attr * attr,const char * name,const char * metric_id,struct list_head * config_terms,bool * hybrid,struct parse_events_state * parse_state)183 int parse_events__add_cache_hybrid(struct list_head *list, int *idx,
184 struct perf_event_attr *attr,
185 const char *name,
186 const char *metric_id,
187 struct list_head *config_terms,
188 bool *hybrid,
189 struct parse_events_state *parse_state)
190 {
191 struct perf_pmu *pmu;
192 int ret;
193
194 *hybrid = false;
195 if (!perf_pmu__has_hybrid())
196 return 0;
197
198 *hybrid = true;
199 perf_pmu__for_each_hybrid_pmu(pmu) {
200 LIST_HEAD(terms);
201
202 if (pmu_cmp(parse_state, pmu))
203 continue;
204
205 copy_config_terms(&terms, config_terms);
206 ret = create_event_hybrid(PERF_TYPE_HW_CACHE, idx, list,
207 attr, name, metric_id, &terms, pmu);
208 free_config_terms(&terms);
209 if (ret)
210 return ret;
211 }
212
213 return 0;
214 }
215