1#include "util.h"
2#include "../perf.h"
3#include "parse-options.h"
4#include "evsel.h"
5#include "cgroup.h"
6#include "debugfs.h" /* MAX_PATH, STR() */
7#include "evlist.h"
8
9int nr_cgroups;
10
11static int
12cgroupfs_find_mountpoint(char *buf, size_t maxlen)
13{
14	FILE *fp;
15	char mountpoint[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1];
16	char *token, *saved_ptr = NULL;
17	int found = 0;
18
19	fp = fopen("/proc/mounts", "r");
20	if (!fp)
21		return -1;
22
23	/*
24	 * in order to handle split hierarchy, we need to scan /proc/mounts
25	 * and inspect every cgroupfs mount point to find one that has
26	 * perf_event subsystem
27	 */
28	while (fscanf(fp, "%*s %"STR(MAX_PATH)"s %"STR(MAX_PATH)"s %"
29				STR(MAX_PATH)"s %*d %*d\n",
30				mountpoint, type, tokens) == 3) {
31
32		if (!strcmp(type, "cgroup")) {
33
34			token = strtok_r(tokens, ",", &saved_ptr);
35
36			while (token != NULL) {
37				if (!strcmp(token, "perf_event")) {
38					found = 1;
39					break;
40				}
41				token = strtok_r(NULL, ",", &saved_ptr);
42			}
43		}
44		if (found)
45			break;
46	}
47	fclose(fp);
48	if (!found)
49		return -1;
50
51	if (strlen(mountpoint) < maxlen) {
52		strcpy(buf, mountpoint);
53		return 0;
54	}
55	return -1;
56}
57
58static int open_cgroup(char *name)
59{
60	char path[MAX_PATH+1];
61	char mnt[MAX_PATH+1];
62	int fd;
63
64
65	if (cgroupfs_find_mountpoint(mnt, MAX_PATH+1))
66		return -1;
67
68	snprintf(path, MAX_PATH, "%s/%s", mnt, name);
69
70	fd = open(path, O_RDONLY);
71	if (fd == -1)
72		fprintf(stderr, "no access to cgroup %s\n", path);
73
74	return fd;
75}
76
77static int add_cgroup(struct perf_evlist *evlist, char *str)
78{
79	struct perf_evsel *counter;
80	struct cgroup_sel *cgrp = NULL;
81	int n;
82	/*
83	 * check if cgrp is already defined, if so we reuse it
84	 */
85	list_for_each_entry(counter, &evlist->entries, node) {
86		cgrp = counter->cgrp;
87		if (!cgrp)
88			continue;
89		if (!strcmp(cgrp->name, str))
90			break;
91
92		cgrp = NULL;
93	}
94
95	if (!cgrp) {
96		cgrp = zalloc(sizeof(*cgrp));
97		if (!cgrp)
98			return -1;
99
100		cgrp->name = str;
101
102		cgrp->fd = open_cgroup(str);
103		if (cgrp->fd == -1) {
104			free(cgrp);
105			return -1;
106		}
107	}
108
109	/*
110	 * find corresponding event
111	 * if add cgroup N, then need to find event N
112	 */
113	n = 0;
114	list_for_each_entry(counter, &evlist->entries, node) {
115		if (n == nr_cgroups)
116			goto found;
117		n++;
118	}
119	if (cgrp->refcnt == 0)
120		free(cgrp);
121
122	return -1;
123found:
124	cgrp->refcnt++;
125	counter->cgrp = cgrp;
126	return 0;
127}
128
129void close_cgroup(struct cgroup_sel *cgrp)
130{
131	if (!cgrp)
132		return;
133
134	/* XXX: not reentrant */
135	if (--cgrp->refcnt == 0) {
136		close(cgrp->fd);
137		free(cgrp->name);
138		free(cgrp);
139	}
140}
141
142int parse_cgroups(const struct option *opt __used, const char *str,
143		  int unset __used)
144{
145	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
146	const char *p, *e, *eos = str + strlen(str);
147	char *s;
148	int ret;
149
150	if (list_empty(&evlist->entries)) {
151		fprintf(stderr, "must define events before cgroups\n");
152		return -1;
153	}
154
155	for (;;) {
156		p = strchr(str, ',');
157		e = p ? p : eos;
158
159		/* allow empty cgroups, i.e., skip */
160		if (e - str) {
161			/* termination added */
162			s = strndup(str, e - str);
163			if (!s)
164				return -1;
165			ret = add_cgroup(evlist, s);
166			if (ret) {
167				free(s);
168				return -1;
169			}
170		}
171		/* nr_cgroups is increased een for empty cgroups */
172		nr_cgroups++;
173		if (!p)
174			break;
175		str = p+1;
176	}
177	return 0;
178}
179