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 close(cgroup->fd); 111 zfree(&cgroup->name); 112 free(cgroup); 113 } 114 115 void cgroup__put(struct cgroup *cgrp) 116 { 117 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { 118 cgroup__delete(cgrp); 119 } 120 } 121 122 struct cgroup *cgroup__get(struct cgroup *cgroup) 123 { 124 if (cgroup) 125 refcount_inc(&cgroup->refcnt); 126 return cgroup; 127 } 128 129 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup) 130 { 131 if (evsel->cgrp == NULL) 132 evsel->cgrp = cgroup__get(cgroup); 133 } 134 135 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup) 136 { 137 struct evsel *evsel; 138 139 evlist__for_each_entry(evlist, evsel) 140 evsel__set_default_cgroup(evsel, cgroup); 141 } 142 143 int parse_cgroups(const struct option *opt, const char *str, 144 int unset __maybe_unused) 145 { 146 struct evlist *evlist = *(struct evlist **)opt->value; 147 struct evsel *counter; 148 struct cgroup *cgrp = NULL; 149 const char *p, *e, *eos = str + strlen(str); 150 char *s; 151 int ret, i; 152 153 if (list_empty(&evlist->core.entries)) { 154 fprintf(stderr, "must define events before cgroups\n"); 155 return -1; 156 } 157 158 for (;;) { 159 p = strchr(str, ','); 160 e = p ? p : eos; 161 162 /* allow empty cgroups, i.e., skip */ 163 if (e - str) { 164 /* termination added */ 165 s = strndup(str, e - str); 166 if (!s) 167 return -1; 168 ret = add_cgroup(evlist, s); 169 free(s); 170 if (ret) 171 return -1; 172 } 173 /* nr_cgroups is increased een for empty cgroups */ 174 nr_cgroups++; 175 if (!p) 176 break; 177 str = p+1; 178 } 179 /* for the case one cgroup combine to multiple events */ 180 i = 0; 181 if (nr_cgroups == 1) { 182 evlist__for_each_entry(evlist, counter) { 183 if (i == 0) 184 cgrp = counter->cgrp; 185 else { 186 counter->cgrp = cgrp; 187 refcount_inc(&cgrp->refcnt); 188 } 189 i++; 190 } 191 } 192 return 0; 193 } 194 195 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id, 196 bool create, const char *path) 197 { 198 struct rb_node **p = &root->rb_node; 199 struct rb_node *parent = NULL; 200 struct cgroup *cgrp; 201 202 while (*p != NULL) { 203 parent = *p; 204 cgrp = rb_entry(parent, struct cgroup, node); 205 206 if (cgrp->id == id) 207 return cgrp; 208 209 if (cgrp->id < id) 210 p = &(*p)->rb_left; 211 else 212 p = &(*p)->rb_right; 213 } 214 215 if (!create) 216 return NULL; 217 218 cgrp = malloc(sizeof(*cgrp)); 219 if (cgrp == NULL) 220 return NULL; 221 222 cgrp->name = strdup(path); 223 if (cgrp->name == NULL) { 224 free(cgrp); 225 return NULL; 226 } 227 228 cgrp->fd = -1; 229 cgrp->id = id; 230 refcount_set(&cgrp->refcnt, 1); 231 232 rb_link_node(&cgrp->node, parent, p); 233 rb_insert_color(&cgrp->node, root); 234 235 return cgrp; 236 } 237 238 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id, 239 const char *path) 240 { 241 struct cgroup *cgrp; 242 243 down_write(&env->cgroups.lock); 244 cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path); 245 up_write(&env->cgroups.lock); 246 return cgrp; 247 } 248 249 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id) 250 { 251 struct cgroup *cgrp; 252 253 down_read(&env->cgroups.lock); 254 cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL); 255 up_read(&env->cgroups.lock); 256 return cgrp; 257 } 258 259 void perf_env__purge_cgroups(struct perf_env *env) 260 { 261 struct rb_node *node; 262 struct cgroup *cgrp; 263 264 down_write(&env->cgroups.lock); 265 while (!RB_EMPTY_ROOT(&env->cgroups.tree)) { 266 node = rb_first(&env->cgroups.tree); 267 cgrp = rb_entry(node, struct cgroup, node); 268 269 rb_erase(node, &env->cgroups.tree); 270 cgroup__put(cgrp); 271 } 272 up_write(&env->cgroups.lock); 273 } 274