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