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