GNU Linux-libre 4.14.324-gnu1
[releases.git] / tools / perf / util / cgroup.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include "util.h"
3 #include "../perf.h"
4 #include <subcmd/parse-options.h>
5 #include "evsel.h"
6 #include "cgroup.h"
7 #include "evlist.h"
8 #include <linux/stringify.h>
9
10 int nr_cgroups;
11
12 static int
13 cgroupfs_find_mountpoint(char *buf, size_t maxlen)
14 {
15         FILE *fp;
16         char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
17         char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
18         char *token, *saved_ptr = NULL;
19
20         fp = fopen("/proc/mounts", "r");
21         if (!fp)
22                 return -1;
23
24         /*
25          * in order to handle split hierarchy, we need to scan /proc/mounts
26          * and inspect every cgroupfs mount point to find one that has
27          * perf_event subsystem
28          */
29         path_v1[0] = '\0';
30         path_v2[0] = '\0';
31
32         while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
33                                 __stringify(PATH_MAX)"s %*d %*d\n",
34                                 mountpoint, type, tokens) == 3) {
35
36                 if (!path_v1[0] && !strcmp(type, "cgroup")) {
37
38                         token = strtok_r(tokens, ",", &saved_ptr);
39
40                         while (token != NULL) {
41                                 if (!strcmp(token, "perf_event")) {
42                                         strcpy(path_v1, mountpoint);
43                                         break;
44                                 }
45                                 token = strtok_r(NULL, ",", &saved_ptr);
46                         }
47                 }
48
49                 if (!path_v2[0] && !strcmp(type, "cgroup2"))
50                         strcpy(path_v2, mountpoint);
51
52                 if (path_v1[0] && path_v2[0])
53                         break;
54         }
55         fclose(fp);
56
57         if (path_v1[0])
58                 path = path_v1;
59         else if (path_v2[0])
60                 path = path_v2;
61         else
62                 return -1;
63
64         if (strlen(path) < maxlen) {
65                 strcpy(buf, path);
66                 return 0;
67         }
68         return -1;
69 }
70
71 static int open_cgroup(char *name)
72 {
73         char path[PATH_MAX + 1];
74         char mnt[PATH_MAX + 1];
75         int fd;
76
77
78         if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
79                 return -1;
80
81         scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
82
83         fd = open(path, O_RDONLY);
84         if (fd == -1)
85                 fprintf(stderr, "no access to cgroup %s\n", path);
86
87         return fd;
88 }
89
90 static int add_cgroup(struct perf_evlist *evlist, char *str)
91 {
92         struct perf_evsel *counter;
93         struct cgroup_sel *cgrp = NULL;
94         int n;
95         /*
96          * check if cgrp is already defined, if so we reuse it
97          */
98         evlist__for_each_entry(evlist, counter) {
99                 cgrp = counter->cgrp;
100                 if (!cgrp)
101                         continue;
102                 if (!strcmp(cgrp->name, str)) {
103                         refcount_inc(&cgrp->refcnt);
104                         break;
105                 }
106
107                 cgrp = NULL;
108         }
109
110         if (!cgrp) {
111                 cgrp = zalloc(sizeof(*cgrp));
112                 if (!cgrp)
113                         return -1;
114
115                 cgrp->name = str;
116                 refcount_set(&cgrp->refcnt, 1);
117
118                 cgrp->fd = open_cgroup(str);
119                 if (cgrp->fd == -1) {
120                         free(cgrp);
121                         return -1;
122                 }
123         }
124
125         /*
126          * find corresponding event
127          * if add cgroup N, then need to find event N
128          */
129         n = 0;
130         evlist__for_each_entry(evlist, counter) {
131                 if (n == nr_cgroups)
132                         goto found;
133                 n++;
134         }
135         if (refcount_dec_and_test(&cgrp->refcnt))
136                 free(cgrp);
137
138         return -1;
139 found:
140         counter->cgrp = cgrp;
141         return 0;
142 }
143
144 void close_cgroup(struct cgroup_sel *cgrp)
145 {
146         if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
147                 close(cgrp->fd);
148                 zfree(&cgrp->name);
149                 free(cgrp);
150         }
151 }
152
153 int parse_cgroups(const struct option *opt __maybe_unused, const char *str,
154                   int unset __maybe_unused)
155 {
156         struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
157         const char *p, *e, *eos = str + strlen(str);
158         char *s;
159         int ret;
160
161         if (list_empty(&evlist->entries)) {
162                 fprintf(stderr, "must define events before cgroups\n");
163                 return -1;
164         }
165
166         for (;;) {
167                 p = strchr(str, ',');
168                 e = p ? p : eos;
169
170                 /* allow empty cgroups, i.e., skip */
171                 if (e - str) {
172                         /* termination added */
173                         s = strndup(str, e - str);
174                         if (!s)
175                                 return -1;
176                         ret = add_cgroup(evlist, s);
177                         if (ret) {
178                                 free(s);
179                                 return -1;
180                         }
181                 }
182                 /* nr_cgroups is increased een for empty cgroups */
183                 nr_cgroups++;
184                 if (!p)
185                         break;
186                 str = p+1;
187         }
188         return 0;
189 }