| /* |
| * Code related to setting up a blkio cgroup |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <mntent.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include "fio.h" |
| #include "flist.h" |
| #include "cgroup.h" |
| #include "smalloc.h" |
| |
| static struct fio_mutex *lock; |
| |
| struct cgroup_member { |
| struct flist_head list; |
| char *root; |
| unsigned int cgroup_nodelete; |
| }; |
| |
| static char *find_cgroup_mnt(struct thread_data *td) |
| { |
| char *mntpoint = NULL; |
| struct mntent *mnt; |
| FILE *f; |
| |
| f = setmntent("/proc/mounts", "r"); |
| if (!f) { |
| td_verror(td, errno, "setmntent /proc/mounts"); |
| return NULL; |
| } |
| |
| while ((mnt = getmntent(f)) != NULL) { |
| if (!strcmp(mnt->mnt_type, "cgroup") && |
| strstr(mnt->mnt_opts, "blkio")) |
| break; |
| } |
| |
| if (mnt) |
| mntpoint = smalloc_strdup(mnt->mnt_dir); |
| else |
| log_err("fio: cgroup blkio does not appear to be mounted\n"); |
| |
| endmntent(f); |
| return mntpoint; |
| } |
| |
| static void add_cgroup(struct thread_data *td, const char *name, |
| struct flist_head *clist) |
| { |
| struct cgroup_member *cm; |
| |
| cm = smalloc(sizeof(*cm)); |
| INIT_FLIST_HEAD(&cm->list); |
| cm->root = smalloc_strdup(name); |
| if (td->o.cgroup_nodelete) |
| cm->cgroup_nodelete = 1; |
| fio_mutex_down(lock); |
| flist_add_tail(&cm->list, clist); |
| fio_mutex_up(lock); |
| } |
| |
| void cgroup_kill(struct flist_head *clist) |
| { |
| struct flist_head *n, *tmp; |
| struct cgroup_member *cm; |
| |
| fio_mutex_down(lock); |
| |
| flist_for_each_safe(n, tmp, clist) { |
| cm = flist_entry(n, struct cgroup_member, list); |
| if (!cm->cgroup_nodelete) |
| rmdir(cm->root); |
| flist_del(&cm->list); |
| sfree(cm->root); |
| sfree(cm); |
| } |
| |
| fio_mutex_up(lock); |
| } |
| |
| static char *get_cgroup_root(struct thread_data *td, char *mnt) |
| { |
| char *str = malloc(64); |
| |
| if (td->o.cgroup) |
| sprintf(str, "%s/%s", mnt, td->o.cgroup); |
| else |
| sprintf(str, "%s/%s", mnt, td->o.name); |
| |
| return str; |
| } |
| |
| static int write_int_to_file(struct thread_data *td, const char *path, |
| const char *filename, unsigned int val, |
| const char *onerr) |
| { |
| char tmp[256]; |
| FILE *f; |
| |
| sprintf(tmp, "%s/%s", path, filename); |
| f = fopen(tmp, "w"); |
| if (!f) { |
| td_verror(td, errno, onerr); |
| return 1; |
| } |
| |
| fprintf(f, "%u", val); |
| fclose(f); |
| return 0; |
| |
| } |
| |
| static int cgroup_write_pid(struct thread_data *td, const char *root) |
| { |
| unsigned int val = td->pid; |
| |
| return write_int_to_file(td, root, "tasks", val, "cgroup write pid"); |
| } |
| |
| /* |
| * Move pid to root class |
| */ |
| static int cgroup_del_pid(struct thread_data *td, char *mnt) |
| { |
| return cgroup_write_pid(td, mnt); |
| } |
| |
| int cgroup_setup(struct thread_data *td, struct flist_head *clist, char **mnt) |
| { |
| char *root; |
| |
| if (!*mnt) { |
| *mnt = find_cgroup_mnt(td); |
| if (!*mnt) |
| return 1; |
| } |
| |
| /* |
| * Create container, if it doesn't exist |
| */ |
| root = get_cgroup_root(td, *mnt); |
| if (mkdir(root, 0755) < 0) { |
| int __e = errno; |
| |
| if (__e != EEXIST) { |
| td_verror(td, __e, "cgroup mkdir"); |
| log_err("fio: path %s\n", root); |
| goto err; |
| } |
| } else |
| add_cgroup(td, root, clist); |
| |
| if (td->o.cgroup_weight) { |
| if (write_int_to_file(td, root, "blkio.weight", |
| td->o.cgroup_weight, |
| "cgroup open weight")) |
| goto err; |
| } |
| |
| if (!cgroup_write_pid(td, root)) { |
| free(root); |
| return 0; |
| } |
| |
| err: |
| free(root); |
| return 1; |
| } |
| |
| void cgroup_shutdown(struct thread_data *td, char **mnt) |
| { |
| if (*mnt == NULL) |
| return; |
| if (!td->o.cgroup_weight && !td->o.cgroup) |
| return; |
| |
| cgroup_del_pid(td, *mnt); |
| } |
| |
| static void fio_init cgroup_init(void) |
| { |
| lock = fio_mutex_init(1); |
| } |
| |
| static void fio_exit cgroup_exit(void) |
| { |
| fio_mutex_remove(lock); |
| } |