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
14static struct fio_mutex *lock;
15
16struct cgroup_member {
17	struct flist_head list;
18	char *root;
19	unsigned int cgroup_nodelete;
20};
21
22static 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
50static 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) {
60err:
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
78void 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
100static 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
112static 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
132static 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 */
142static int cgroup_del_pid(struct thread_data *td, char *mnt)
143{
144	return cgroup_write_pid(td, mnt);
145}
146
147int 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
184err:
185	free(root);
186	return 1;
187}
188
189void 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
199static 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
206static void fio_exit cgroup_exit(void)
207{
208	fio_mutex_remove(lock);
209}
210