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