Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 18static 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
 40static 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
 76static 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
 88static 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
114static 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
134static 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
159int 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
183int 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}