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 | ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT);
37 }
38 
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)39 static int create_event_hybrid(__u32 config_type, int *idx,
40 			       struct list_head *list,
41 			       struct perf_event_attr *attr, const char *name,
42 			       const char *metric_id,
43 			       struct list_head *config_terms,
44 			       struct perf_pmu *pmu)
45 {
46 	struct evsel *evsel;
47 	__u32 type = attr->type;
48 	__u64 config = attr->config;
49 
50 	config_hybrid_attr(attr, config_type, pmu->type);
51 	evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id,
52 					       pmu, config_terms);
53 	if (evsel)
54 		evsel->pmu_name = strdup(pmu->name);
55 	else
56 		return -ENOMEM;
57 
58 	attr->type = type;
59 	attr->config = config;
60 	return 0;
61 }
62 
pmu_cmp(struct parse_events_state * parse_state,struct perf_pmu * pmu)63 static int pmu_cmp(struct parse_events_state *parse_state,
64 		   struct perf_pmu *pmu)
65 {
66 	if (!parse_state->hybrid_pmu_name)
67 		return 0;
68 
69 	return strcmp(parse_state->hybrid_pmu_name, pmu->name);
70 }
71 
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)72 static int add_hw_hybrid(struct parse_events_state *parse_state,
73 			 struct list_head *list, struct perf_event_attr *attr,
74 			 const char *name, const char *metric_id,
75 			 struct list_head *config_terms)
76 {
77 	struct perf_pmu *pmu;
78 	int ret;
79 
80 	perf_pmu__for_each_hybrid_pmu(pmu) {
81 		LIST_HEAD(terms);
82 
83 		if (pmu_cmp(parse_state, pmu))
84 			continue;
85 
86 		copy_config_terms(&terms, config_terms);
87 		ret = create_event_hybrid(PERF_TYPE_HARDWARE,
88 					  &parse_state->idx, list, attr, name,
89 					  metric_id, &terms, pmu);
90 		free_config_terms(&terms);
91 		if (ret)
92 			return ret;
93 	}
94 
95 	return 0;
96 }
97 
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)98 static int create_raw_event_hybrid(int *idx, struct list_head *list,
99 				   struct perf_event_attr *attr,
100 				   const char *name,
101 				   const char *metric_id,
102 				   struct list_head *config_terms,
103 				   struct perf_pmu *pmu)
104 {
105 	struct evsel *evsel;
106 
107 	attr->type = pmu->type;
108 	evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id,
109 					       pmu, config_terms);
110 	if (evsel)
111 		evsel->pmu_name = strdup(pmu->name);
112 	else
113 		return -ENOMEM;
114 
115 	return 0;
116 }
117 
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)118 static int add_raw_hybrid(struct parse_events_state *parse_state,
119 			  struct list_head *list, struct perf_event_attr *attr,
120 			  const char *name, const char *metric_id,
121 			  struct list_head *config_terms)
122 {
123 	struct perf_pmu *pmu;
124 	int ret;
125 
126 	perf_pmu__for_each_hybrid_pmu(pmu) {
127 		LIST_HEAD(terms);
128 
129 		if (pmu_cmp(parse_state, pmu))
130 			continue;
131 
132 		copy_config_terms(&terms, config_terms);
133 		ret = create_raw_event_hybrid(&parse_state->idx, list, attr,
134 					      name, metric_id, &terms, pmu);
135 		free_config_terms(&terms);
136 		if (ret)
137 			return ret;
138 	}
139 
140 	return 0;
141 }
142 
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)143 int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state,
144 				     struct list_head *list,
145 				     struct perf_event_attr *attr,
146 				     const char *name, const char *metric_id,
147 				     struct list_head *config_terms,
148 				     bool *hybrid)
149 {
150 	*hybrid = false;
151 	if (attr->type == PERF_TYPE_SOFTWARE)
152 		return 0;
153 
154 	if (!perf_pmu__has_hybrid())
155 		return 0;
156 
157 	*hybrid = true;
158 	if (attr->type != PERF_TYPE_RAW) {
159 		return add_hw_hybrid(parse_state, list, attr, name, metric_id,
160 				     config_terms);
161 	}
162 
163 	return add_raw_hybrid(parse_state, list, attr, name, metric_id,
164 			      config_terms);
165 }
166 
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)167 int parse_events__add_cache_hybrid(struct list_head *list, int *idx,
168 				   struct perf_event_attr *attr,
169 				   const char *name,
170 				   const char *metric_id,
171 				   struct list_head *config_terms,
172 				   bool *hybrid,
173 				   struct parse_events_state *parse_state)
174 {
175 	struct perf_pmu *pmu;
176 	int ret;
177 
178 	*hybrid = false;
179 	if (!perf_pmu__has_hybrid())
180 		return 0;
181 
182 	*hybrid = true;
183 	perf_pmu__for_each_hybrid_pmu(pmu) {
184 		LIST_HEAD(terms);
185 
186 		if (pmu_cmp(parse_state, pmu))
187 			continue;
188 
189 		copy_config_terms(&terms, config_terms);
190 		ret = create_event_hybrid(PERF_TYPE_HW_CACHE, idx, list,
191 					  attr, name, metric_id, &terms, pmu);
192 		free_config_terms(&terms);
193 		if (ret)
194 			return ret;
195 	}
196 
197 	return 0;
198 }
199