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/zalloc.h> 7 #include <sys/types.h> 8 #include <sys/stat.h> 9 #include <fcntl.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <api/fs/fs.h> 13 14 int nr_cgroups; 15 16 static int open_cgroup(const char *name) 17 { 18 char path[PATH_MAX + 1]; 19 char mnt[PATH_MAX + 1]; 20 int fd; 21 22 23 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event")) 24 return -1; 25 26 scnprintf(path, PATH_MAX, "%s/%s", mnt, name); 27 28 fd = open(path, O_RDONLY); 29 if (fd == -1) 30 fprintf(stderr, "no access to cgroup %s\n", path); 31 32 return fd; 33 } 34 35 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str) 36 { 37 struct evsel *counter; 38 /* 39 * check if cgrp is already defined, if so we reuse it 40 */ 41 evlist__for_each_entry(evlist, counter) { 42 if (!counter->cgrp) 43 continue; 44 if (!strcmp(counter->cgrp->name, str)) 45 return cgroup__get(counter->cgrp); 46 } 47 48 return NULL; 49 } 50 51 static struct cgroup *cgroup__new(const char *name) 52 { 53 struct cgroup *cgroup = zalloc(sizeof(*cgroup)); 54 55 if (cgroup != NULL) { 56 refcount_set(&cgroup->refcnt, 1); 57 58 cgroup->name = strdup(name); 59 if (!cgroup->name) 60 goto out_err; 61 cgroup->fd = open_cgroup(name); 62 if (cgroup->fd == -1) 63 goto out_free_name; 64 } 65 66 return cgroup; 67 68 out_free_name: 69 zfree(&cgroup->name); 70 out_err: 71 free(cgroup); 72 return NULL; 73 } 74 75 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name) 76 { 77 struct cgroup *cgroup = evlist__find_cgroup(evlist, name); 78 79 return cgroup ?: cgroup__new(name); 80 } 81 82 static int add_cgroup(struct evlist *evlist, const char *str) 83 { 84 struct evsel *counter; 85 struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str); 86 int n; 87 88 if (!cgrp) 89 return -1; 90 /* 91 * find corresponding event 92 * if add cgroup N, then need to find event N 93 */ 94 n = 0; 95 evlist__for_each_entry(evlist, counter) { 96 if (n == nr_cgroups) 97 goto found; 98 n++; 99 } 100 101 cgroup__put(cgrp); 102 return -1; 103 found: 104 counter->cgrp = cgrp; 105 return 0; 106 } 107 108 static void cgroup__delete(struct cgroup *cgroup) 109 { 110 if (cgroup->fd >= 0) 111 close(cgroup->fd); 112 zfree(&cgroup->name); 113 free(cgroup); 114 } 115 116 void cgroup__put(struct cgroup *cgrp) 117 { 118 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { 119 cgroup__delete(cgrp); 120 } 121 } 122 123 struct cgroup *cgroup__get(struct cgroup *cgroup) 124 { 125 if (cgroup) 126 refcount_inc(&cgroup->refcnt); 127 return cgroup; 128 } 129 130 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup) 131 { 132 if (evsel->cgrp == NULL) 133 evsel->cgrp = cgroup__get(cgroup); 134 } 135 136 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup) 137 { 138 struct evsel *evsel; 139 140 evlist__for_each_entry(evlist, evsel) 141 evsel__set_default_cgroup(evsel, cgroup); 142 } 143 144 int parse_cgroups(const struct option *opt, const char *str, 145 int unset __maybe_unused) 146 { 147 struct evlist *evlist = *(struct evlist **)opt->value; 148 struct evsel *counter; 149 struct cgroup *cgrp = NULL; 150 const char *p, *e, *eos = str + strlen(str); 151 char *s; 152 int ret, i; 153 154 if (list_empty(&evlist->core.entries)) { 155 fprintf(stderr, "must define events before cgroups\n"); 156 return -1; 157 } 158 159 for (;;) { 160 p = strchr(str, ','); 161 e = p ? p : eos; 162 163 /* allow empty cgroups, i.e., skip */ 164 if (e - str) { 165 /* termination added */ 166 s = strndup(str, e - str); 167 if (!s) 168 return -1; 169 ret = add_cgroup(evlist, s); 170 free(s); 171 if (ret) 172 return -1; 173 } 174 /* nr_cgroups is increased een for empty cgroups */ 175 nr_cgroups++; 176 if (!p) 177 break; 178 str = p+1; 179 } 180 /* for the case one cgroup combine to multiple events */ 181 i = 0; 182 if (nr_cgroups == 1) { 183 evlist__for_each_entry(evlist, counter) { 184 if (i == 0) 185 cgrp = counter->cgrp; 186 else { 187 counter->cgrp = cgrp; 188 refcount_inc(&cgrp->refcnt); 189 } 190 i++; 191 } 192 } 193 return 0; 194 } 195 196 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id, 197 bool create, const char *path) 198 { 199 struct rb_node **p = &root->rb_node; 200 struct rb_node *parent = NULL; 201 struct cgroup *cgrp; 202 203 while (*p != NULL) { 204 parent = *p; 205 cgrp = rb_entry(parent, struct cgroup, node); 206 207 if (cgrp->id == id) 208 return cgrp; 209 210 if (cgrp->id < id) 211 p = &(*p)->rb_left; 212 else 213 p = &(*p)->rb_right; 214 } 215 216 if (!create) 217 return NULL; 218 219 cgrp = malloc(sizeof(*cgrp)); 220 if (cgrp == NULL) 221 return NULL; 222 223 cgrp->name = strdup(path); 224 if (cgrp->name == NULL) { 225 free(cgrp); 226 return NULL; 227 } 228 229 cgrp->fd = -1; 230 cgrp->id = id; 231 refcount_set(&cgrp->refcnt, 1); 232 233 rb_link_node(&cgrp->node, parent, p); 234 rb_insert_color(&cgrp->node, root); 235 236 return cgrp; 237 } 238 239 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id, 240 const char *path) 241 { 242 struct cgroup *cgrp; 243 244 down_write(&env->cgroups.lock); 245 cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path); 246 up_write(&env->cgroups.lock); 247 return cgrp; 248 } 249 250 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id) 251 { 252 struct cgroup *cgrp; 253 254 down_read(&env->cgroups.lock); 255 cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL); 256 up_read(&env->cgroups.lock); 257 return cgrp; 258 } 259 260 void perf_env__purge_cgroups(struct perf_env *env) 261 { 262 struct rb_node *node; 263 struct cgroup *cgrp; 264 265 down_write(&env->cgroups.lock); 266 while (!RB_EMPTY_ROOT(&env->cgroups.tree)) { 267 node = rb_first(&env->cgroups.tree); 268 cgrp = rb_entry(node, struct cgroup, node); 269 270 rb_erase(node, &env->cgroups.tree); 271 cgroup__put(cgrp); 272 } 273 up_write(&env->cgroups.lock); 274 } 275