xref: /OK3568_Linux_fs/kernel/tools/perf/util/cgroup.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <subcmd/parse-options.h>
3*4882a593Smuzhiyun #include "evsel.h"
4*4882a593Smuzhiyun #include "cgroup.h"
5*4882a593Smuzhiyun #include "evlist.h"
6*4882a593Smuzhiyun #include "rblist.h"
7*4882a593Smuzhiyun #include "metricgroup.h"
8*4882a593Smuzhiyun #include "stat.h"
9*4882a593Smuzhiyun #include <linux/zalloc.h>
10*4882a593Smuzhiyun #include <sys/types.h>
11*4882a593Smuzhiyun #include <sys/stat.h>
12*4882a593Smuzhiyun #include <fcntl.h>
13*4882a593Smuzhiyun #include <stdlib.h>
14*4882a593Smuzhiyun #include <string.h>
15*4882a593Smuzhiyun #include <api/fs/fs.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun int nr_cgroups;
18*4882a593Smuzhiyun 
open_cgroup(const char * name)19*4882a593Smuzhiyun static int open_cgroup(const char *name)
20*4882a593Smuzhiyun {
21*4882a593Smuzhiyun 	char path[PATH_MAX + 1];
22*4882a593Smuzhiyun 	char mnt[PATH_MAX + 1];
23*4882a593Smuzhiyun 	int fd;
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
27*4882a593Smuzhiyun 		return -1;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	fd = open(path, O_RDONLY);
32*4882a593Smuzhiyun 	if (fd == -1)
33*4882a593Smuzhiyun 		fprintf(stderr, "no access to cgroup %s\n", path);
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	return fd;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun 
evlist__find_cgroup(struct evlist * evlist,const char * str)38*4882a593Smuzhiyun static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	struct evsel *counter;
41*4882a593Smuzhiyun 	/*
42*4882a593Smuzhiyun 	 * check if cgrp is already defined, if so we reuse it
43*4882a593Smuzhiyun 	 */
44*4882a593Smuzhiyun 	evlist__for_each_entry(evlist, counter) {
45*4882a593Smuzhiyun 		if (!counter->cgrp)
46*4882a593Smuzhiyun 			continue;
47*4882a593Smuzhiyun 		if (!strcmp(counter->cgrp->name, str))
48*4882a593Smuzhiyun 			return cgroup__get(counter->cgrp);
49*4882a593Smuzhiyun 	}
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	return NULL;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
cgroup__new(const char * name,bool do_open)54*4882a593Smuzhiyun static struct cgroup *cgroup__new(const char *name, bool do_open)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	if (cgroup != NULL) {
59*4882a593Smuzhiyun 		refcount_set(&cgroup->refcnt, 1);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 		cgroup->name = strdup(name);
62*4882a593Smuzhiyun 		if (!cgroup->name)
63*4882a593Smuzhiyun 			goto out_err;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 		if (do_open) {
66*4882a593Smuzhiyun 			cgroup->fd = open_cgroup(name);
67*4882a593Smuzhiyun 			if (cgroup->fd == -1)
68*4882a593Smuzhiyun 				goto out_free_name;
69*4882a593Smuzhiyun 		} else {
70*4882a593Smuzhiyun 			cgroup->fd = -1;
71*4882a593Smuzhiyun 		}
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	return cgroup;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun out_free_name:
77*4882a593Smuzhiyun 	zfree(&cgroup->name);
78*4882a593Smuzhiyun out_err:
79*4882a593Smuzhiyun 	free(cgroup);
80*4882a593Smuzhiyun 	return NULL;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
evlist__findnew_cgroup(struct evlist * evlist,const char * name)83*4882a593Smuzhiyun struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	return cgroup ?: cgroup__new(name, true);
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun 
add_cgroup(struct evlist * evlist,const char * str)90*4882a593Smuzhiyun static int add_cgroup(struct evlist *evlist, const char *str)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	struct evsel *counter;
93*4882a593Smuzhiyun 	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
94*4882a593Smuzhiyun 	int n;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	if (!cgrp)
97*4882a593Smuzhiyun 		return -1;
98*4882a593Smuzhiyun 	/*
99*4882a593Smuzhiyun 	 * find corresponding event
100*4882a593Smuzhiyun 	 * if add cgroup N, then need to find event N
101*4882a593Smuzhiyun 	 */
102*4882a593Smuzhiyun 	n = 0;
103*4882a593Smuzhiyun 	evlist__for_each_entry(evlist, counter) {
104*4882a593Smuzhiyun 		if (n == nr_cgroups)
105*4882a593Smuzhiyun 			goto found;
106*4882a593Smuzhiyun 		n++;
107*4882a593Smuzhiyun 	}
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	cgroup__put(cgrp);
110*4882a593Smuzhiyun 	return -1;
111*4882a593Smuzhiyun found:
112*4882a593Smuzhiyun 	counter->cgrp = cgrp;
113*4882a593Smuzhiyun 	return 0;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
cgroup__delete(struct cgroup * cgroup)116*4882a593Smuzhiyun static void cgroup__delete(struct cgroup *cgroup)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	if (cgroup->fd >= 0)
119*4882a593Smuzhiyun 		close(cgroup->fd);
120*4882a593Smuzhiyun 	zfree(&cgroup->name);
121*4882a593Smuzhiyun 	free(cgroup);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
cgroup__put(struct cgroup * cgrp)124*4882a593Smuzhiyun void cgroup__put(struct cgroup *cgrp)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
127*4882a593Smuzhiyun 		cgroup__delete(cgrp);
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
cgroup__get(struct cgroup * cgroup)131*4882a593Smuzhiyun struct cgroup *cgroup__get(struct cgroup *cgroup)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun        if (cgroup)
134*4882a593Smuzhiyun 		refcount_inc(&cgroup->refcnt);
135*4882a593Smuzhiyun        return cgroup;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
evsel__set_default_cgroup(struct evsel * evsel,struct cgroup * cgroup)138*4882a593Smuzhiyun static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	if (evsel->cgrp == NULL)
141*4882a593Smuzhiyun 		evsel->cgrp = cgroup__get(cgroup);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
evlist__set_default_cgroup(struct evlist * evlist,struct cgroup * cgroup)144*4882a593Smuzhiyun void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	struct evsel *evsel;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	evlist__for_each_entry(evlist, evsel)
149*4882a593Smuzhiyun 		evsel__set_default_cgroup(evsel, cgroup);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
parse_cgroups(const struct option * opt,const char * str,int unset __maybe_unused)152*4882a593Smuzhiyun int parse_cgroups(const struct option *opt, const char *str,
153*4882a593Smuzhiyun 		  int unset __maybe_unused)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	struct evlist *evlist = *(struct evlist **)opt->value;
156*4882a593Smuzhiyun 	struct evsel *counter;
157*4882a593Smuzhiyun 	struct cgroup *cgrp = NULL;
158*4882a593Smuzhiyun 	const char *p, *e, *eos = str + strlen(str);
159*4882a593Smuzhiyun 	char *s;
160*4882a593Smuzhiyun 	int ret, i;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	if (list_empty(&evlist->core.entries)) {
163*4882a593Smuzhiyun 		fprintf(stderr, "must define events before cgroups\n");
164*4882a593Smuzhiyun 		return -1;
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	for (;;) {
168*4882a593Smuzhiyun 		p = strchr(str, ',');
169*4882a593Smuzhiyun 		e = p ? p : eos;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 		/* allow empty cgroups, i.e., skip */
172*4882a593Smuzhiyun 		if (e - str) {
173*4882a593Smuzhiyun 			/* termination added */
174*4882a593Smuzhiyun 			s = strndup(str, e - str);
175*4882a593Smuzhiyun 			if (!s)
176*4882a593Smuzhiyun 				return -1;
177*4882a593Smuzhiyun 			ret = add_cgroup(evlist, s);
178*4882a593Smuzhiyun 			free(s);
179*4882a593Smuzhiyun 			if (ret)
180*4882a593Smuzhiyun 				return -1;
181*4882a593Smuzhiyun 		}
182*4882a593Smuzhiyun 		/* nr_cgroups is increased een for empty cgroups */
183*4882a593Smuzhiyun 		nr_cgroups++;
184*4882a593Smuzhiyun 		if (!p)
185*4882a593Smuzhiyun 			break;
186*4882a593Smuzhiyun 		str = p+1;
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 	/* for the case one cgroup combine to multiple events */
189*4882a593Smuzhiyun 	i = 0;
190*4882a593Smuzhiyun 	if (nr_cgroups == 1) {
191*4882a593Smuzhiyun 		evlist__for_each_entry(evlist, counter) {
192*4882a593Smuzhiyun 			if (i == 0)
193*4882a593Smuzhiyun 				cgrp = counter->cgrp;
194*4882a593Smuzhiyun 			else {
195*4882a593Smuzhiyun 				counter->cgrp = cgrp;
196*4882a593Smuzhiyun 				refcount_inc(&cgrp->refcnt);
197*4882a593Smuzhiyun 			}
198*4882a593Smuzhiyun 			i++;
199*4882a593Smuzhiyun 		}
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 	return 0;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun 
evlist__expand_cgroup(struct evlist * evlist,const char * str,struct rblist * metric_events,bool open_cgroup)204*4882a593Smuzhiyun int evlist__expand_cgroup(struct evlist *evlist, const char *str,
205*4882a593Smuzhiyun 			  struct rblist *metric_events, bool open_cgroup)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun 	struct evlist *orig_list, *tmp_list;
208*4882a593Smuzhiyun 	struct evsel *pos, *evsel, *leader;
209*4882a593Smuzhiyun 	struct rblist orig_metric_events;
210*4882a593Smuzhiyun 	struct cgroup *cgrp = NULL;
211*4882a593Smuzhiyun 	const char *p, *e, *eos = str + strlen(str);
212*4882a593Smuzhiyun 	int ret = -1;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	if (evlist->core.nr_entries == 0) {
215*4882a593Smuzhiyun 		fprintf(stderr, "must define events before cgroups\n");
216*4882a593Smuzhiyun 		return -EINVAL;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	orig_list = evlist__new();
220*4882a593Smuzhiyun 	tmp_list = evlist__new();
221*4882a593Smuzhiyun 	if (orig_list == NULL || tmp_list == NULL) {
222*4882a593Smuzhiyun 		fprintf(stderr, "memory allocation failed\n");
223*4882a593Smuzhiyun 		return -ENOMEM;
224*4882a593Smuzhiyun 	}
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	/* save original events and init evlist */
227*4882a593Smuzhiyun 	perf_evlist__splice_list_tail(orig_list, &evlist->core.entries);
228*4882a593Smuzhiyun 	evlist->core.nr_entries = 0;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	if (metric_events) {
231*4882a593Smuzhiyun 		orig_metric_events = *metric_events;
232*4882a593Smuzhiyun 		rblist__init(metric_events);
233*4882a593Smuzhiyun 	} else {
234*4882a593Smuzhiyun 		rblist__init(&orig_metric_events);
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	for (;;) {
238*4882a593Smuzhiyun 		p = strchr(str, ',');
239*4882a593Smuzhiyun 		e = p ? p : eos;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 		/* allow empty cgroups, i.e., skip */
242*4882a593Smuzhiyun 		if (e - str) {
243*4882a593Smuzhiyun 			/* termination added */
244*4882a593Smuzhiyun 			char *name = strndup(str, e - str);
245*4882a593Smuzhiyun 			if (!name)
246*4882a593Smuzhiyun 				goto out_err;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 			cgrp = cgroup__new(name, open_cgroup);
249*4882a593Smuzhiyun 			free(name);
250*4882a593Smuzhiyun 			if (cgrp == NULL)
251*4882a593Smuzhiyun 				goto out_err;
252*4882a593Smuzhiyun 		} else {
253*4882a593Smuzhiyun 			cgrp = NULL;
254*4882a593Smuzhiyun 		}
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 		leader = NULL;
257*4882a593Smuzhiyun 		evlist__for_each_entry(orig_list, pos) {
258*4882a593Smuzhiyun 			evsel = evsel__clone(pos);
259*4882a593Smuzhiyun 			if (evsel == NULL)
260*4882a593Smuzhiyun 				goto out_err;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 			cgroup__put(evsel->cgrp);
263*4882a593Smuzhiyun 			evsel->cgrp = cgroup__get(cgrp);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 			if (evsel__is_group_leader(pos))
266*4882a593Smuzhiyun 				leader = evsel;
267*4882a593Smuzhiyun 			evsel->leader = leader;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 			evlist__add(tmp_list, evsel);
270*4882a593Smuzhiyun 		}
271*4882a593Smuzhiyun 		/* cgroup__new() has a refcount, release it here */
272*4882a593Smuzhiyun 		cgroup__put(cgrp);
273*4882a593Smuzhiyun 		nr_cgroups++;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 		if (metric_events) {
276*4882a593Smuzhiyun 			perf_stat__collect_metric_expr(tmp_list);
277*4882a593Smuzhiyun 			if (metricgroup__copy_metric_events(tmp_list, cgrp,
278*4882a593Smuzhiyun 							    metric_events,
279*4882a593Smuzhiyun 							    &orig_metric_events) < 0)
280*4882a593Smuzhiyun 				break;
281*4882a593Smuzhiyun 		}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 		perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries);
284*4882a593Smuzhiyun 		tmp_list->core.nr_entries = 0;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 		if (!p) {
287*4882a593Smuzhiyun 			ret = 0;
288*4882a593Smuzhiyun 			break;
289*4882a593Smuzhiyun 		}
290*4882a593Smuzhiyun 		str = p+1;
291*4882a593Smuzhiyun 	}
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun out_err:
294*4882a593Smuzhiyun 	evlist__delete(orig_list);
295*4882a593Smuzhiyun 	evlist__delete(tmp_list);
296*4882a593Smuzhiyun 	rblist__exit(&orig_metric_events);
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	return ret;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
__cgroup__findnew(struct rb_root * root,uint64_t id,bool create,const char * path)301*4882a593Smuzhiyun static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
302*4882a593Smuzhiyun 					bool create, const char *path)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct rb_node **p = &root->rb_node;
305*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
306*4882a593Smuzhiyun 	struct cgroup *cgrp;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	while (*p != NULL) {
309*4882a593Smuzhiyun 		parent = *p;
310*4882a593Smuzhiyun 		cgrp = rb_entry(parent, struct cgroup, node);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 		if (cgrp->id == id)
313*4882a593Smuzhiyun 			return cgrp;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 		if (cgrp->id < id)
316*4882a593Smuzhiyun 			p = &(*p)->rb_left;
317*4882a593Smuzhiyun 		else
318*4882a593Smuzhiyun 			p = &(*p)->rb_right;
319*4882a593Smuzhiyun 	}
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	if (!create)
322*4882a593Smuzhiyun 		return NULL;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	cgrp = malloc(sizeof(*cgrp));
325*4882a593Smuzhiyun 	if (cgrp == NULL)
326*4882a593Smuzhiyun 		return NULL;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	cgrp->name = strdup(path);
329*4882a593Smuzhiyun 	if (cgrp->name == NULL) {
330*4882a593Smuzhiyun 		free(cgrp);
331*4882a593Smuzhiyun 		return NULL;
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	cgrp->fd = -1;
335*4882a593Smuzhiyun 	cgrp->id = id;
336*4882a593Smuzhiyun 	refcount_set(&cgrp->refcnt, 1);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	rb_link_node(&cgrp->node, parent, p);
339*4882a593Smuzhiyun 	rb_insert_color(&cgrp->node, root);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	return cgrp;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
cgroup__findnew(struct perf_env * env,uint64_t id,const char * path)344*4882a593Smuzhiyun struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
345*4882a593Smuzhiyun 			       const char *path)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun 	struct cgroup *cgrp;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	down_write(&env->cgroups.lock);
350*4882a593Smuzhiyun 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
351*4882a593Smuzhiyun 	up_write(&env->cgroups.lock);
352*4882a593Smuzhiyun 	return cgrp;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
cgroup__find(struct perf_env * env,uint64_t id)355*4882a593Smuzhiyun struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	struct cgroup *cgrp;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	down_read(&env->cgroups.lock);
360*4882a593Smuzhiyun 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
361*4882a593Smuzhiyun 	up_read(&env->cgroups.lock);
362*4882a593Smuzhiyun 	return cgrp;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun 
perf_env__purge_cgroups(struct perf_env * env)365*4882a593Smuzhiyun void perf_env__purge_cgroups(struct perf_env *env)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun 	struct rb_node *node;
368*4882a593Smuzhiyun 	struct cgroup *cgrp;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	down_write(&env->cgroups.lock);
371*4882a593Smuzhiyun 	while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
372*4882a593Smuzhiyun 		node = rb_first(&env->cgroups.tree);
373*4882a593Smuzhiyun 		cgrp = rb_entry(node, struct cgroup, node);
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 		rb_erase(node, &env->cgroups.tree);
376*4882a593Smuzhiyun 		cgroup__put(cgrp);
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 	up_write(&env->cgroups.lock);
379*4882a593Smuzhiyun }
380