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