replace typeof with __typeof__
[fio.git] / cgroup.c
1 /*
2  * Code related to setting up a blkio cgroup
3  */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <mntent.h>
7 #include <sys/stat.h>
8 #include "fio.h"
9 #include "flist.h"
10 #include "cgroup.h"
11 #include "smalloc.h"
12
13 static struct fio_sem *lock;
14
15 struct cgroup_member {
16         struct flist_head list;
17         char *root;
18         unsigned int cgroup_nodelete;
19 };
20
21 static char *find_cgroup_mnt(struct thread_data *td)
22 {
23         char *mntpoint = NULL;
24         struct mntent *mnt, dummy;
25         char buf[256] = {0};
26         FILE *f;
27
28         f = setmntent("/proc/mounts", "r");
29         if (!f) {
30                 td_verror(td, errno, "setmntent /proc/mounts");
31                 return NULL;
32         }
33
34         while ((mnt = getmntent_r(f, &dummy, buf, sizeof(buf))) != NULL) {
35                 if (!strcmp(mnt->mnt_type, "cgroup") &&
36                     strstr(mnt->mnt_opts, "blkio"))
37                         break;
38         }
39
40         if (mnt)
41                 mntpoint = smalloc_strdup(mnt->mnt_dir);
42         else
43                 log_err("fio: cgroup blkio does not appear to be mounted\n");
44
45         endmntent(f);
46         return mntpoint;
47 }
48
49 static void add_cgroup(struct thread_data *td, const char *name,
50                         struct flist_head *clist)
51 {
52         struct cgroup_member *cm;
53
54         if (!lock)
55                 return;
56
57         cm = smalloc(sizeof(*cm));
58         if (!cm) {
59 err:
60                 log_err("fio: failed to allocate cgroup member\n");
61                 return;
62         }
63
64         INIT_FLIST_HEAD(&cm->list);
65         cm->root = smalloc_strdup(name);
66         if (!cm->root) {
67                 sfree(cm);
68                 goto err;
69         }
70         if (td->o.cgroup_nodelete)
71                 cm->cgroup_nodelete = 1;
72         fio_sem_down(lock);
73         flist_add_tail(&cm->list, clist);
74         fio_sem_up(lock);
75 }
76
77 void cgroup_kill(struct flist_head *clist)
78 {
79         struct flist_head *n, *tmp;
80         struct cgroup_member *cm;
81
82         if (!lock)
83                 return;
84
85         fio_sem_down(lock);
86
87         flist_for_each_safe(n, tmp, clist) {
88                 cm = flist_entry(n, struct cgroup_member, list);
89                 if (!cm->cgroup_nodelete)
90                         rmdir(cm->root);
91                 flist_del(&cm->list);
92                 sfree(cm->root);
93                 sfree(cm);
94         }
95
96         fio_sem_up(lock);
97 }
98
99 static char *get_cgroup_root(struct thread_data *td, char *mnt)
100 {
101         char *str = malloc(64);
102
103         if (td->o.cgroup)
104                 sprintf(str, "%s/%s", mnt, td->o.cgroup);
105         else
106                 sprintf(str, "%s/%s", mnt, td->o.name);
107
108         return str;
109 }
110
111 static int write_int_to_file(struct thread_data *td, const char *path,
112                              const char *filename, unsigned int val,
113                              const char *onerr)
114 {
115         char tmp[256];
116         FILE *f;
117
118         sprintf(tmp, "%s/%s", path, filename);
119         f = fopen(tmp, "w");
120         if (!f) {
121                 td_verror(td, errno, onerr);
122                 return 1;
123         }
124
125         fprintf(f, "%u", val);
126         fclose(f);
127         return 0;
128
129 }
130
131 static int cgroup_write_pid(struct thread_data *td, const char *root)
132 {
133         unsigned int val = td->pid;
134
135         return write_int_to_file(td, root, "tasks", val, "cgroup write pid");
136 }
137
138 /*
139  * Move pid to root class
140  */
141 static int cgroup_del_pid(struct thread_data *td, char *mnt)
142 {
143         return cgroup_write_pid(td, mnt);
144 }
145
146 int cgroup_setup(struct thread_data *td, struct flist_head *clist, char **mnt)
147 {
148         char *root;
149
150         if (!clist)
151                 return 1;
152
153         if (!*mnt) {
154                 *mnt = find_cgroup_mnt(td);
155                 if (!*mnt)
156                         return 1;
157         }
158
159         /*
160          * Create container, if it doesn't exist
161          */
162         root = get_cgroup_root(td, *mnt);
163         if (mkdir(root, 0755) < 0) {
164                 int __e = errno;
165
166                 if (__e != EEXIST) {
167                         td_verror(td, __e, "cgroup mkdir");
168                         log_err("fio: path %s\n", root);
169                         goto err;
170                 }
171         } else
172                 add_cgroup(td, root, clist);
173
174         if (td->o.cgroup_weight) {
175                 if (write_int_to_file(td, root, "blkio.weight",
176                                         td->o.cgroup_weight,
177                                         "cgroup open weight"))
178                         goto err;
179         }
180
181         if (!cgroup_write_pid(td, root)) {
182                 free(root);
183                 return 0;
184         }
185
186 err:
187         free(root);
188         return 1;
189 }
190
191 void cgroup_shutdown(struct thread_data *td, char **mnt)
192 {
193         if (*mnt == NULL)
194                 return;
195         if (!td->o.cgroup_weight && !td->o.cgroup)
196                 return;
197
198         cgroup_del_pid(td, *mnt);
199 }
200
201 static void fio_init cgroup_init(void)
202 {
203         lock = fio_sem_init(FIO_SEM_UNLOCKED);
204         if (!lock)
205                 log_err("fio: failed to allocate cgroup lock\n");
206 }
207
208 static void fio_exit cgroup_exit(void)
209 {
210         fio_sem_remove(lock);
211 }