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