Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[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 <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         if (cgroup->fd >= 0)
111                 close(cgroup->fd);
112         zfree(&cgroup->name);
113         free(cgroup);
114 }
115
116 void cgroup__put(struct cgroup *cgrp)
117 {
118         if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
119                 cgroup__delete(cgrp);
120         }
121 }
122
123 struct cgroup *cgroup__get(struct cgroup *cgroup)
124 {
125        if (cgroup)
126                 refcount_inc(&cgroup->refcnt);
127        return cgroup;
128 }
129
130 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
131 {
132         if (evsel->cgrp == NULL)
133                 evsel->cgrp = cgroup__get(cgroup);
134 }
135
136 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
137 {
138         struct evsel *evsel;
139
140         evlist__for_each_entry(evlist, evsel)
141                 evsel__set_default_cgroup(evsel, cgroup);
142 }
143
144 int parse_cgroups(const struct option *opt, const char *str,
145                   int unset __maybe_unused)
146 {
147         struct evlist *evlist = *(struct evlist **)opt->value;
148         struct evsel *counter;
149         struct cgroup *cgrp = NULL;
150         const char *p, *e, *eos = str + strlen(str);
151         char *s;
152         int ret, i;
153
154         if (list_empty(&evlist->core.entries)) {
155                 fprintf(stderr, "must define events before cgroups\n");
156                 return -1;
157         }
158
159         for (;;) {
160                 p = strchr(str, ',');
161                 e = p ? p : eos;
162
163                 /* allow empty cgroups, i.e., skip */
164                 if (e - str) {
165                         /* termination added */
166                         s = strndup(str, e - str);
167                         if (!s)
168                                 return -1;
169                         ret = add_cgroup(evlist, s);
170                         free(s);
171                         if (ret)
172                                 return -1;
173                 }
174                 /* nr_cgroups is increased een for empty cgroups */
175                 nr_cgroups++;
176                 if (!p)
177                         break;
178                 str = p+1;
179         }
180         /* for the case one cgroup combine to multiple events */
181         i = 0;
182         if (nr_cgroups == 1) {
183                 evlist__for_each_entry(evlist, counter) {
184                         if (i == 0)
185                                 cgrp = counter->cgrp;
186                         else {
187                                 counter->cgrp = cgrp;
188                                 refcount_inc(&cgrp->refcnt);
189                         }
190                         i++;
191                 }
192         }
193         return 0;
194 }
195
196 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
197                                         bool create, const char *path)
198 {
199         struct rb_node **p = &root->rb_node;
200         struct rb_node *parent = NULL;
201         struct cgroup *cgrp;
202
203         while (*p != NULL) {
204                 parent = *p;
205                 cgrp = rb_entry(parent, struct cgroup, node);
206
207                 if (cgrp->id == id)
208                         return cgrp;
209
210                 if (cgrp->id < id)
211                         p = &(*p)->rb_left;
212                 else
213                         p = &(*p)->rb_right;
214         }
215
216         if (!create)
217                 return NULL;
218
219         cgrp = malloc(sizeof(*cgrp));
220         if (cgrp == NULL)
221                 return NULL;
222
223         cgrp->name = strdup(path);
224         if (cgrp->name == NULL) {
225                 free(cgrp);
226                 return NULL;
227         }
228
229         cgrp->fd = -1;
230         cgrp->id = id;
231         refcount_set(&cgrp->refcnt, 1);
232
233         rb_link_node(&cgrp->node, parent, p);
234         rb_insert_color(&cgrp->node, root);
235
236         return cgrp;
237 }
238
239 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
240                                const char *path)
241 {
242         struct cgroup *cgrp;
243
244         down_write(&env->cgroups.lock);
245         cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
246         up_write(&env->cgroups.lock);
247         return cgrp;
248 }
249
250 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
251 {
252         struct cgroup *cgrp;
253
254         down_read(&env->cgroups.lock);
255         cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
256         up_read(&env->cgroups.lock);
257         return cgrp;
258 }
259
260 void perf_env__purge_cgroups(struct perf_env *env)
261 {
262         struct rb_node *node;
263         struct cgroup *cgrp;
264
265         down_write(&env->cgroups.lock);
266         while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
267                 node = rb_first(&env->cgroups.tree);
268                 cgrp = rb_entry(node, struct cgroup, node);
269
270                 rb_erase(node, &env->cgroups.tree);
271                 cgroup__put(cgrp);
272         }
273         up_write(&env->cgroups.lock);
274 }