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
cgroupfs_find_mountpoint(char * buf,size_t maxlen)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
open_cgroup(char * name)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
add_cgroup(struct perf_evlist * evlist,char * str)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
close_cgroup(struct cgroup_sel * cgrp)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
parse_cgroups(const struct option * opt __maybe_unused,const char * str,int unset __maybe_unused)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 }
190