xref: /openbmc/linux/tools/perf/util/cgroup.c (revision 890f0b0d)
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