Merge tag 'selinux-pr-20200416' of git://git.kernel.org/pub/scm/linux/kernel/git...
[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         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 }