]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
023695d9 | 2 | #include "../perf.h" |
4b6ab94e | 3 | #include <subcmd/parse-options.h> |
023695d9 SE |
4 | #include "evsel.h" |
5 | #include "cgroup.h" | |
023695d9 | 6 | #include "evlist.h" |
aa8cc2f6 | 7 | #include <linux/stringify.h> |
7f7c536f | 8 | #include <linux/zalloc.h> |
bafae98e ACM |
9 | #include <sys/types.h> |
10 | #include <sys/stat.h> | |
11 | #include <fcntl.h> | |
023695d9 SE |
12 | |
13 | int nr_cgroups; | |
14 | ||
15 | static int | |
16 | cgroupfs_find_mountpoint(char *buf, size_t maxlen) | |
17 | { | |
18 | FILE *fp; | |
c168fbfb | 19 | char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1]; |
968ebff1 | 20 | char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path; |
621d2656 | 21 | char *token, *saved_ptr = NULL; |
023695d9 SE |
22 | |
23 | fp = fopen("/proc/mounts", "r"); | |
24 | if (!fp) | |
25 | return -1; | |
26 | ||
27 | /* | |
28 | * in order to handle split hierarchy, we need to scan /proc/mounts | |
29 | * and inspect every cgroupfs mount point to find one that has | |
30 | * perf_event subsystem | |
31 | */ | |
968ebff1 TH |
32 | path_v1[0] = '\0'; |
33 | path_v2[0] = '\0'; | |
34 | ||
aa8cc2f6 ACM |
35 | while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %" |
36 | __stringify(PATH_MAX)"s %*d %*d\n", | |
023695d9 SE |
37 | mountpoint, type, tokens) == 3) { |
38 | ||
968ebff1 | 39 | if (!path_v1[0] && !strcmp(type, "cgroup")) { |
023695d9 SE |
40 | |
41 | token = strtok_r(tokens, ",", &saved_ptr); | |
42 | ||
43 | while (token != NULL) { | |
44 | if (!strcmp(token, "perf_event")) { | |
968ebff1 | 45 | strcpy(path_v1, mountpoint); |
023695d9 SE |
46 | break; |
47 | } | |
48 | token = strtok_r(NULL, ",", &saved_ptr); | |
49 | } | |
50 | } | |
968ebff1 TH |
51 | |
52 | if (!path_v2[0] && !strcmp(type, "cgroup2")) | |
53 | strcpy(path_v2, mountpoint); | |
54 | ||
55 | if (path_v1[0] && path_v2[0]) | |
023695d9 SE |
56 | break; |
57 | } | |
58 | fclose(fp); | |
968ebff1 TH |
59 | |
60 | if (path_v1[0]) | |
61 | path = path_v1; | |
62 | else if (path_v2[0]) | |
63 | path = path_v2; | |
64 | else | |
023695d9 SE |
65 | return -1; |
66 | ||
968ebff1 TH |
67 | if (strlen(path) < maxlen) { |
68 | strcpy(buf, path); | |
023695d9 SE |
69 | return 0; |
70 | } | |
71 | return -1; | |
72 | } | |
73 | ||
3b569286 | 74 | static int open_cgroup(const char *name) |
023695d9 | 75 | { |
c168fbfb ACM |
76 | char path[PATH_MAX + 1]; |
77 | char mnt[PATH_MAX + 1]; | |
023695d9 SE |
78 | int fd; |
79 | ||
80 | ||
c168fbfb | 81 | if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1)) |
023695d9 SE |
82 | return -1; |
83 | ||
77f18153 | 84 | scnprintf(path, PATH_MAX, "%s/%s", mnt, name); |
023695d9 SE |
85 | |
86 | fd = open(path, O_RDONLY); | |
87 | if (fd == -1) | |
88 | fprintf(stderr, "no access to cgroup %s\n", path); | |
89 | ||
90 | return fd; | |
91 | } | |
92 | ||
3b569286 | 93 | static struct cgroup *evlist__find_cgroup(struct perf_evlist *evlist, const char *str) |
023695d9 SE |
94 | { |
95 | struct perf_evsel *counter; | |
023695d9 SE |
96 | /* |
97 | * check if cgrp is already defined, if so we reuse it | |
98 | */ | |
e5cadb93 | 99 | evlist__for_each_entry(evlist, counter) { |
fc9ffb9c | 100 | if (!counter->cgrp) |
023695d9 | 101 | continue; |
5dbe23e8 ACM |
102 | if (!strcmp(counter->cgrp->name, str)) |
103 | return cgroup__get(counter->cgrp); | |
023695d9 SE |
104 | } |
105 | ||
5dbe23e8 | 106 | return NULL; |
b80271f7 ACM |
107 | } |
108 | ||
3b569286 | 109 | static struct cgroup *cgroup__new(const char *name) |
923a0fb3 ACM |
110 | { |
111 | struct cgroup *cgroup = zalloc(sizeof(*cgroup)); | |
112 | ||
113 | if (cgroup != NULL) { | |
923a0fb3 ACM |
114 | refcount_set(&cgroup->refcnt, 1); |
115 | ||
3b569286 ACM |
116 | cgroup->name = strdup(name); |
117 | if (!cgroup->name) | |
118 | goto out_err; | |
923a0fb3 ACM |
119 | cgroup->fd = open_cgroup(name); |
120 | if (cgroup->fd == -1) | |
3b569286 | 121 | goto out_free_name; |
923a0fb3 ACM |
122 | } |
123 | ||
124 | return cgroup; | |
3b569286 ACM |
125 | |
126 | out_free_name: | |
d8f9da24 | 127 | zfree(&cgroup->name); |
923a0fb3 ACM |
128 | out_err: |
129 | free(cgroup); | |
130 | return NULL; | |
131 | } | |
132 | ||
3b569286 | 133 | struct cgroup *evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name) |
69239ec8 ACM |
134 | { |
135 | struct cgroup *cgroup = evlist__find_cgroup(evlist, name); | |
136 | ||
137 | return cgroup ?: cgroup__new(name); | |
138 | } | |
139 | ||
3b569286 | 140 | static int add_cgroup(struct perf_evlist *evlist, const char *str) |
b80271f7 ACM |
141 | { |
142 | struct perf_evsel *counter; | |
69239ec8 | 143 | struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str); |
b80271f7 ACM |
144 | int n; |
145 | ||
69239ec8 ACM |
146 | if (!cgrp) |
147 | return -1; | |
023695d9 SE |
148 | /* |
149 | * find corresponding event | |
150 | * if add cgroup N, then need to find event N | |
151 | */ | |
152 | n = 0; | |
e5cadb93 | 153 | evlist__for_each_entry(evlist, counter) { |
023695d9 SE |
154 | if (n == nr_cgroups) |
155 | goto found; | |
156 | n++; | |
157 | } | |
023695d9 | 158 | |
a53b6460 | 159 | cgroup__put(cgrp); |
023695d9 SE |
160 | return -1; |
161 | found: | |
023695d9 SE |
162 | counter->cgrp = cgrp; |
163 | return 0; | |
164 | } | |
165 | ||
9450d0d4 ACM |
166 | static void cgroup__delete(struct cgroup *cgroup) |
167 | { | |
168 | close(cgroup->fd); | |
169 | zfree(&cgroup->name); | |
170 | free(cgroup); | |
171 | } | |
172 | ||
a53b6460 | 173 | void cgroup__put(struct cgroup *cgrp) |
023695d9 | 174 | { |
79c5fe6d | 175 | if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { |
9450d0d4 | 176 | cgroup__delete(cgrp); |
023695d9 SE |
177 | } |
178 | } | |
179 | ||
fc9ffb9c ACM |
180 | struct cgroup *cgroup__get(struct cgroup *cgroup) |
181 | { | |
182 | if (cgroup) | |
183 | refcount_inc(&cgroup->refcnt); | |
184 | return cgroup; | |
185 | } | |
186 | ||
483322dd ACM |
187 | static void evsel__set_default_cgroup(struct perf_evsel *evsel, struct cgroup *cgroup) |
188 | { | |
189 | if (evsel->cgrp == NULL) | |
190 | evsel->cgrp = cgroup__get(cgroup); | |
191 | } | |
192 | ||
193 | void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup) | |
194 | { | |
195 | struct perf_evsel *evsel; | |
196 | ||
197 | evlist__for_each_entry(evlist, evsel) | |
198 | evsel__set_default_cgroup(evsel, cgroup); | |
199 | } | |
200 | ||
a6adc9bd | 201 | int parse_cgroups(const struct option *opt, const char *str, |
1d037ca1 | 202 | int unset __maybe_unused) |
023695d9 SE |
203 | { |
204 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | |
25f72f9e | 205 | struct perf_evsel *counter; |
3ca32f69 | 206 | struct cgroup *cgrp = NULL; |
023695d9 SE |
207 | const char *p, *e, *eos = str + strlen(str); |
208 | char *s; | |
25f72f9e | 209 | int ret, i; |
023695d9 SE |
210 | |
211 | if (list_empty(&evlist->entries)) { | |
212 | fprintf(stderr, "must define events before cgroups\n"); | |
213 | return -1; | |
214 | } | |
215 | ||
216 | for (;;) { | |
217 | p = strchr(str, ','); | |
218 | e = p ? p : eos; | |
219 | ||
220 | /* allow empty cgroups, i.e., skip */ | |
221 | if (e - str) { | |
222 | /* termination added */ | |
223 | s = strndup(str, e - str); | |
224 | if (!s) | |
225 | return -1; | |
226 | ret = add_cgroup(evlist, s); | |
3b569286 ACM |
227 | free(s); |
228 | if (ret) | |
023695d9 | 229 | return -1; |
023695d9 SE |
230 | } |
231 | /* nr_cgroups is increased een for empty cgroups */ | |
232 | nr_cgroups++; | |
233 | if (!p) | |
234 | break; | |
235 | str = p+1; | |
236 | } | |
25f72f9e | 237 | /* for the case one cgroup combine to multiple events */ |
238 | i = 0; | |
239 | if (nr_cgroups == 1) { | |
240 | evlist__for_each_entry(evlist, counter) { | |
241 | if (i == 0) | |
242 | cgrp = counter->cgrp; | |
243 | else { | |
244 | counter->cgrp = cgrp; | |
245 | refcount_inc(&cgrp->refcnt); | |
246 | } | |
247 | i++; | |
248 | } | |
249 | } | |
023695d9 SE |
250 | return 0; |
251 | } |