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 #include <sys/types.h> 10 #include <sys/stat.h> 11 #include <fcntl.h> 12 13 int nr_cgroups; 14 15 static int 16 cgroupfs_find_mountpoint(char *buf, size_t maxlen) 17 { 18 FILE *fp; 19 char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1]; 20 char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path; 21 char *token, *saved_ptr = NULL; 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 */ 32 path_v1[0] = '\0'; 33 path_v2[0] = '\0'; 34 35 while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %" 36 __stringify(PATH_MAX)"s %*d %*d\n", 37 mountpoint, type, tokens) == 3) { 38 39 if (!path_v1[0] && !strcmp(type, "cgroup")) { 40 41 token = strtok_r(tokens, ",", &saved_ptr); 42 43 while (token != NULL) { 44 if (!strcmp(token, "perf_event")) { 45 strcpy(path_v1, mountpoint); 46 break; 47 } 48 token = strtok_r(NULL, ",", &saved_ptr); 49 } 50 } 51 52 if (!path_v2[0] && !strcmp(type, "cgroup2")) 53 strcpy(path_v2, mountpoint); 54 55 if (path_v1[0] && path_v2[0]) 56 break; 57 } 58 fclose(fp); 59 60 if (path_v1[0]) 61 path = path_v1; 62 else if (path_v2[0]) 63 path = path_v2; 64 else 65 return -1; 66 67 if (strlen(path) < maxlen) { 68 strcpy(buf, path); 69 return 0; 70 } 71 return -1; 72 } 73 74 static int open_cgroup(const char *name) 75 { 76 char path[PATH_MAX + 1]; 77 char mnt[PATH_MAX + 1]; 78 int fd; 79 80 81 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1)) 82 return -1; 83 84 snprintf(path, PATH_MAX, "%s/%s", mnt, name); 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 93 static struct cgroup *evlist__find_cgroup(struct perf_evlist *evlist, const char *str) 94 { 95 struct perf_evsel *counter; 96 struct cgroup *cgrp = NULL; 97 /* 98 * check if cgrp is already defined, if so we reuse it 99 */ 100 evlist__for_each_entry(evlist, counter) { 101 if (!counter->cgrp) 102 continue; 103 if (!strcmp(counter->cgrp->name, str)) { 104 cgrp = cgroup__get(counter->cgrp); 105 break; 106 } 107 } 108 109 return cgrp; 110 } 111 112 static struct cgroup *cgroup__new(const char *name) 113 { 114 struct cgroup *cgroup = zalloc(sizeof(*cgroup)); 115 116 if (cgroup != NULL) { 117 refcount_set(&cgroup->refcnt, 1); 118 119 cgroup->name = strdup(name); 120 if (!cgroup->name) 121 goto out_err; 122 cgroup->fd = open_cgroup(name); 123 if (cgroup->fd == -1) 124 goto out_free_name; 125 } 126 127 return cgroup; 128 129 out_free_name: 130 free(cgroup->name); 131 out_err: 132 free(cgroup); 133 return NULL; 134 } 135 136 struct cgroup *evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name) 137 { 138 struct cgroup *cgroup = evlist__find_cgroup(evlist, name); 139 140 return cgroup ?: cgroup__new(name); 141 } 142 143 static int add_cgroup(struct perf_evlist *evlist, const char *str) 144 { 145 struct perf_evsel *counter; 146 struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str); 147 int n; 148 149 if (!cgrp) 150 return -1; 151 /* 152 * find corresponding event 153 * if add cgroup N, then need to find event N 154 */ 155 n = 0; 156 evlist__for_each_entry(evlist, counter) { 157 if (n == nr_cgroups) 158 goto found; 159 n++; 160 } 161 162 cgroup__put(cgrp); 163 return -1; 164 found: 165 counter->cgrp = cgrp; 166 return 0; 167 } 168 169 static void cgroup__delete(struct cgroup *cgroup) 170 { 171 close(cgroup->fd); 172 zfree(&cgroup->name); 173 free(cgroup); 174 } 175 176 void cgroup__put(struct cgroup *cgrp) 177 { 178 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { 179 cgroup__delete(cgrp); 180 } 181 } 182 183 struct cgroup *cgroup__get(struct cgroup *cgroup) 184 { 185 if (cgroup) 186 refcount_inc(&cgroup->refcnt); 187 return cgroup; 188 } 189 190 static void evsel__set_default_cgroup(struct perf_evsel *evsel, struct cgroup *cgroup) 191 { 192 if (evsel->cgrp == NULL) 193 evsel->cgrp = cgroup__get(cgroup); 194 } 195 196 void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup) 197 { 198 struct perf_evsel *evsel; 199 200 evlist__for_each_entry(evlist, evsel) 201 evsel__set_default_cgroup(evsel, cgroup); 202 } 203 204 int parse_cgroups(const struct option *opt, const char *str, 205 int unset __maybe_unused) 206 { 207 struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; 208 struct perf_evsel *counter; 209 struct cgroup *cgrp = NULL; 210 const char *p, *e, *eos = str + strlen(str); 211 char *s; 212 int ret, i; 213 214 if (list_empty(&evlist->entries)) { 215 fprintf(stderr, "must define events before cgroups\n"); 216 return -1; 217 } 218 219 for (;;) { 220 p = strchr(str, ','); 221 e = p ? p : eos; 222 223 /* allow empty cgroups, i.e., skip */ 224 if (e - str) { 225 /* termination added */ 226 s = strndup(str, e - str); 227 if (!s) 228 return -1; 229 ret = add_cgroup(evlist, s); 230 free(s); 231 if (ret) 232 return -1; 233 } 234 /* nr_cgroups is increased een for empty cgroups */ 235 nr_cgroups++; 236 if (!p) 237 break; 238 str = p+1; 239 } 240 /* for the case one cgroup combine to multiple events */ 241 i = 0; 242 if (nr_cgroups == 1) { 243 evlist__for_each_entry(evlist, counter) { 244 if (i == 0) 245 cgrp = counter->cgrp; 246 else { 247 counter->cgrp = cgrp; 248 refcount_inc(&cgrp->refcnt); 249 } 250 i++; 251 } 252 } 253 return 0; 254 } 255