xref: /linux/tools/perf/util/cgroup.c (revision bd4af432cc71b5fbfe4833510359a6ad3ada250d)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <subcmd/parse-options.h>
3 #include "evsel.h"
4 #include "cgroup.h"
5 #include "evlist.h"
6 #include <linux/zalloc.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <api/fs/fs.h>
13 
14 int nr_cgroups;
15 
16 static int open_cgroup(const char *name)
17 {
18 	char path[PATH_MAX + 1];
19 	char mnt[PATH_MAX + 1];
20 	int fd;
21 
22 
23 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
24 		return -1;
25 
26 	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
27 
28 	fd = open(path, O_RDONLY);
29 	if (fd == -1)
30 		fprintf(stderr, "no access to cgroup %s\n", path);
31 
32 	return fd;
33 }
34 
35 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
36 {
37 	struct evsel *counter;
38 	/*
39 	 * check if cgrp is already defined, if so we reuse it
40 	 */
41 	evlist__for_each_entry(evlist, counter) {
42 		if (!counter->cgrp)
43 			continue;
44 		if (!strcmp(counter->cgrp->name, str))
45 			return cgroup__get(counter->cgrp);
46 	}
47 
48 	return NULL;
49 }
50 
51 static struct cgroup *cgroup__new(const char *name)
52 {
53 	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
54 
55 	if (cgroup != NULL) {
56 		refcount_set(&cgroup->refcnt, 1);
57 
58 		cgroup->name = strdup(name);
59 		if (!cgroup->name)
60 			goto out_err;
61 		cgroup->fd = open_cgroup(name);
62 		if (cgroup->fd == -1)
63 			goto out_free_name;
64 	}
65 
66 	return cgroup;
67 
68 out_free_name:
69 	zfree(&cgroup->name);
70 out_err:
71 	free(cgroup);
72 	return NULL;
73 }
74 
75 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
76 {
77 	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
78 
79 	return cgroup ?: cgroup__new(name);
80 }
81 
82 static int add_cgroup(struct evlist *evlist, const char *str)
83 {
84 	struct evsel *counter;
85 	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
86 	int n;
87 
88 	if (!cgrp)
89 		return -1;
90 	/*
91 	 * find corresponding event
92 	 * if add cgroup N, then need to find event N
93 	 */
94 	n = 0;
95 	evlist__for_each_entry(evlist, counter) {
96 		if (n == nr_cgroups)
97 			goto found;
98 		n++;
99 	}
100 
101 	cgroup__put(cgrp);
102 	return -1;
103 found:
104 	counter->cgrp = cgrp;
105 	return 0;
106 }
107 
108 static void cgroup__delete(struct cgroup *cgroup)
109 {
110 	close(cgroup->fd);
111 	zfree(&cgroup->name);
112 	free(cgroup);
113 }
114 
115 void cgroup__put(struct cgroup *cgrp)
116 {
117 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
118 		cgroup__delete(cgrp);
119 	}
120 }
121 
122 struct cgroup *cgroup__get(struct cgroup *cgroup)
123 {
124        if (cgroup)
125 		refcount_inc(&cgroup->refcnt);
126        return cgroup;
127 }
128 
129 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
130 {
131 	if (evsel->cgrp == NULL)
132 		evsel->cgrp = cgroup__get(cgroup);
133 }
134 
135 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
136 {
137 	struct evsel *evsel;
138 
139 	evlist__for_each_entry(evlist, evsel)
140 		evsel__set_default_cgroup(evsel, cgroup);
141 }
142 
143 int parse_cgroups(const struct option *opt, const char *str,
144 		  int unset __maybe_unused)
145 {
146 	struct evlist *evlist = *(struct evlist **)opt->value;
147 	struct evsel *counter;
148 	struct cgroup *cgrp = NULL;
149 	const char *p, *e, *eos = str + strlen(str);
150 	char *s;
151 	int ret, i;
152 
153 	if (list_empty(&evlist->core.entries)) {
154 		fprintf(stderr, "must define events before cgroups\n");
155 		return -1;
156 	}
157 
158 	for (;;) {
159 		p = strchr(str, ',');
160 		e = p ? p : eos;
161 
162 		/* allow empty cgroups, i.e., skip */
163 		if (e - str) {
164 			/* termination added */
165 			s = strndup(str, e - str);
166 			if (!s)
167 				return -1;
168 			ret = add_cgroup(evlist, s);
169 			free(s);
170 			if (ret)
171 				return -1;
172 		}
173 		/* nr_cgroups is increased een for empty cgroups */
174 		nr_cgroups++;
175 		if (!p)
176 			break;
177 		str = p+1;
178 	}
179 	/* for the case one cgroup combine to multiple events */
180 	i = 0;
181 	if (nr_cgroups == 1) {
182 		evlist__for_each_entry(evlist, counter) {
183 			if (i == 0)
184 				cgrp = counter->cgrp;
185 			else {
186 				counter->cgrp = cgrp;
187 				refcount_inc(&cgrp->refcnt);
188 			}
189 			i++;
190 		}
191 	}
192 	return 0;
193 }
194 
195 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
196 					bool create, const char *path)
197 {
198 	struct rb_node **p = &root->rb_node;
199 	struct rb_node *parent = NULL;
200 	struct cgroup *cgrp;
201 
202 	while (*p != NULL) {
203 		parent = *p;
204 		cgrp = rb_entry(parent, struct cgroup, node);
205 
206 		if (cgrp->id == id)
207 			return cgrp;
208 
209 		if (cgrp->id < id)
210 			p = &(*p)->rb_left;
211 		else
212 			p = &(*p)->rb_right;
213 	}
214 
215 	if (!create)
216 		return NULL;
217 
218 	cgrp = malloc(sizeof(*cgrp));
219 	if (cgrp == NULL)
220 		return NULL;
221 
222 	cgrp->name = strdup(path);
223 	if (cgrp->name == NULL) {
224 		free(cgrp);
225 		return NULL;
226 	}
227 
228 	cgrp->fd = -1;
229 	cgrp->id = id;
230 	refcount_set(&cgrp->refcnt, 1);
231 
232 	rb_link_node(&cgrp->node, parent, p);
233 	rb_insert_color(&cgrp->node, root);
234 
235 	return cgrp;
236 }
237 
238 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
239 			       const char *path)
240 {
241 	struct cgroup *cgrp;
242 
243 	down_write(&env->cgroups.lock);
244 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
245 	up_write(&env->cgroups.lock);
246 	return cgrp;
247 }
248 
249 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
250 {
251 	struct cgroup *cgrp;
252 
253 	down_read(&env->cgroups.lock);
254 	cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
255 	up_read(&env->cgroups.lock);
256 	return cgrp;
257 }
258 
259 void perf_env__purge_cgroups(struct perf_env *env)
260 {
261 	struct rb_node *node;
262 	struct cgroup *cgrp;
263 
264 	down_write(&env->cgroups.lock);
265 	while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
266 		node = rb_first(&env->cgroups.tree);
267 		cgrp = rb_entry(node, struct cgroup, node);
268 
269 		rb_erase(node, &env->cgroups.tree);
270 		cgroup__put(cgrp);
271 	}
272 	up_write(&env->cgroups.lock);
273 }
274