📄 cgroup.c
字号:
struct inode *inode; int i; BUG_ON(sb->s_root != NULL); ret = cgroup_get_rootdir(sb); if (ret) goto drop_new_super; inode = sb->s_root->d_inode; mutex_lock(&inode->i_mutex); mutex_lock(&cgroup_mutex); /* * We're accessing css_set_count without locking * css_set_lock here, but that's OK - it can only be * increased by someone holding cgroup_lock, and * that's us. The worst that can happen is that we * have some link structures left over */ ret = allocate_cg_links(css_set_count, &tmp_cg_links); if (ret) { mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); goto drop_new_super; } ret = rebind_subsystems(root, root->subsys_bits); if (ret == -EBUSY) { mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); goto drop_new_super; } /* EBUSY should be the only error here */ BUG_ON(ret); list_add(&root->root_list, &roots); root_count++; sb->s_root->d_fsdata = &root->top_cgroup; root->top_cgroup.dentry = sb->s_root; /* Link the top cgroup in this hierarchy into all * the css_set objects */ write_lock(&css_set_lock); for (i = 0; i < CSS_SET_TABLE_SIZE; i++) { struct hlist_head *hhead = &css_set_table[i]; struct hlist_node *node; struct css_set *cg; hlist_for_each_entry(cg, node, hhead, hlist) { struct cg_cgroup_link *link; BUG_ON(list_empty(&tmp_cg_links)); link = list_entry(tmp_cg_links.next, struct cg_cgroup_link, cgrp_link_list); list_del(&link->cgrp_link_list); link->cg = cg; list_add(&link->cgrp_link_list, &root->top_cgroup.css_sets); list_add(&link->cg_link_list, &cg->cg_links); } } write_unlock(&css_set_lock); free_cg_links(&tmp_cg_links); BUG_ON(!list_empty(&cgrp->sibling)); BUG_ON(!list_empty(&cgrp->children)); BUG_ON(root->number_of_cgroups != 1); cgroup_populate_dir(cgrp); mutex_unlock(&inode->i_mutex); mutex_unlock(&cgroup_mutex); } return simple_set_mnt(mnt, sb); drop_new_super: up_write(&sb->s_umount); deactivate_super(sb); free_cg_links(&tmp_cg_links); return ret;}static void cgroup_kill_sb(struct super_block *sb) { struct cgroupfs_root *root = sb->s_fs_info; struct cgroup *cgrp = &root->top_cgroup; int ret; struct cg_cgroup_link *link; struct cg_cgroup_link *saved_link; BUG_ON(!root); BUG_ON(root->number_of_cgroups != 1); BUG_ON(!list_empty(&cgrp->children)); BUG_ON(!list_empty(&cgrp->sibling)); mutex_lock(&cgroup_mutex); /* Rebind all subsystems back to the default hierarchy */ ret = rebind_subsystems(root, 0); /* Shouldn't be able to fail ... */ BUG_ON(ret); /* * Release all the links from css_sets to this hierarchy's * root cgroup */ write_lock(&css_set_lock); list_for_each_entry_safe(link, saved_link, &cgrp->css_sets, cgrp_link_list) { list_del(&link->cg_link_list); list_del(&link->cgrp_link_list); kfree(link); } write_unlock(&css_set_lock); if (!list_empty(&root->root_list)) { list_del(&root->root_list); root_count--; } mutex_unlock(&cgroup_mutex); kfree(root); kill_litter_super(sb);}static struct file_system_type cgroup_fs_type = { .name = "cgroup", .get_sb = cgroup_get_sb, .kill_sb = cgroup_kill_sb,};static inline struct cgroup *__d_cgrp(struct dentry *dentry){ return dentry->d_fsdata;}static inline struct cftype *__d_cft(struct dentry *dentry){ return dentry->d_fsdata;}/** * cgroup_path - generate the path of a cgroup * @cgrp: the cgroup in question * @buf: the buffer to write the path into * @buflen: the length of the buffer * * Called with cgroup_mutex held. Writes path of cgroup into buf. * Returns 0 on success, -errno on error. */int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen){ char *start; if (cgrp == dummytop) { /* * Inactive subsystems have no dentry for their root * cgroup */ strcpy(buf, "/"); return 0; } start = buf + buflen; *--start = '\0'; for (;;) { int len = cgrp->dentry->d_name.len; if ((start -= len) < buf) return -ENAMETOOLONG; memcpy(start, cgrp->dentry->d_name.name, len); cgrp = cgrp->parent; if (!cgrp) break; if (!cgrp->parent) continue; if (--start < buf) return -ENAMETOOLONG; *start = '/'; } memmove(buf, start, buf + buflen - start); return 0;}/* * Return the first subsystem attached to a cgroup's hierarchy, and * its subsystem id. */static void get_first_subsys(const struct cgroup *cgrp, struct cgroup_subsys_state **css, int *subsys_id){ const struct cgroupfs_root *root = cgrp->root; const struct cgroup_subsys *test_ss; BUG_ON(list_empty(&root->subsys_list)); test_ss = list_entry(root->subsys_list.next, struct cgroup_subsys, sibling); if (css) { *css = cgrp->subsys[test_ss->subsys_id]; BUG_ON(!*css); } if (subsys_id) *subsys_id = test_ss->subsys_id;}/** * cgroup_attach_task - attach task 'tsk' to cgroup 'cgrp' * @cgrp: the cgroup the task is attaching to * @tsk: the task to be attached * * Call holding cgroup_mutex. May take task_lock of * the task 'tsk' during call. */int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk){ int retval = 0; struct cgroup_subsys *ss; struct cgroup *oldcgrp; struct css_set *cg = tsk->cgroups; struct css_set *newcg; struct cgroupfs_root *root = cgrp->root; int subsys_id; get_first_subsys(cgrp, NULL, &subsys_id); /* Nothing to do if the task is already in that cgroup */ oldcgrp = task_cgroup(tsk, subsys_id); if (cgrp == oldcgrp) return 0; for_each_subsys(root, ss) { if (ss->can_attach) { retval = ss->can_attach(ss, cgrp, tsk); if (retval) return retval; } } /* * Locate or allocate a new css_set for this task, * based on its final set of cgroups */ newcg = find_css_set(cg, cgrp); if (!newcg) return -ENOMEM; task_lock(tsk); if (tsk->flags & PF_EXITING) { task_unlock(tsk); put_css_set(newcg); return -ESRCH; } rcu_assign_pointer(tsk->cgroups, newcg); task_unlock(tsk); /* Update the css_set linked lists if we're using them */ write_lock(&css_set_lock); if (!list_empty(&tsk->cg_list)) { list_del(&tsk->cg_list); list_add(&tsk->cg_list, &newcg->tasks); } write_unlock(&css_set_lock); for_each_subsys(root, ss) { if (ss->attach) ss->attach(ss, cgrp, oldcgrp, tsk); } set_bit(CGRP_RELEASABLE, &oldcgrp->flags); synchronize_rcu(); put_css_set(cg); return 0;}/* * Attach task with pid 'pid' to cgroup 'cgrp'. Call with cgroup_mutex * held. May take task_lock of task */static int attach_task_by_pid(struct cgroup *cgrp, u64 pid){ struct task_struct *tsk; int ret; if (pid) { rcu_read_lock(); tsk = find_task_by_vpid(pid); if (!tsk || tsk->flags & PF_EXITING) { rcu_read_unlock(); return -ESRCH; } get_task_struct(tsk); rcu_read_unlock(); if ((current->euid) && (current->euid != tsk->uid) && (current->euid != tsk->suid)) { put_task_struct(tsk); return -EACCES; } } else { tsk = current; get_task_struct(tsk); } ret = cgroup_attach_task(cgrp, tsk); put_task_struct(tsk); return ret;}static int cgroup_tasks_write(struct cgroup *cgrp, struct cftype *cft, u64 pid){ int ret; if (!cgroup_lock_live_group(cgrp)) return -ENODEV; ret = attach_task_by_pid(cgrp, pid); cgroup_unlock(); return ret;}/* The various types of files and directories in a cgroup file system */enum cgroup_filetype { FILE_ROOT, FILE_DIR, FILE_TASKLIST, FILE_NOTIFY_ON_RELEASE, FILE_RELEASE_AGENT,};/** * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. * @cgrp: the cgroup to be checked for liveness * * On success, returns true; the lock should be later released with * cgroup_unlock(). On failure returns false with no lock held. */bool cgroup_lock_live_group(struct cgroup *cgrp){ mutex_lock(&cgroup_mutex); if (cgroup_is_removed(cgrp)) { mutex_unlock(&cgroup_mutex); return false; } return true;}static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, const char *buffer){ BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); if (!cgroup_lock_live_group(cgrp)) return -ENODEV; strcpy(cgrp->root->release_agent_path, buffer); cgroup_unlock(); return 0;}static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, struct seq_file *seq){ if (!cgroup_lock_live_group(cgrp)) return -ENODEV; seq_puts(seq, cgrp->root->release_agent_path); seq_putc(seq, '\n'); cgroup_unlock(); return 0;}/* A buffer size big enough for numbers or short strings */#define CGROUP_LOCAL_BUFFER_SIZE 64static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, struct file *file, const char __user *userbuf, size_t nbytes, loff_t *unused_ppos){ char buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; char *end; if (!nbytes) return -EINVAL; if (nbytes >= sizeof(buffer)) return -E2BIG; if (copy_from_user(buffer, userbuf, nbytes)) return -EFAULT; buffer[nbytes] = 0; /* nul-terminate */ strstrip(buffer); if (cft->write_u64) { u64 val = simple_strtoull(buffer, &end, 0); if (*end) return -EINVAL; retval = cft->write_u64(cgrp, cft, val); } else { s64 val = simple_strtoll(buffer, &end, 0); if (*end) return -EINVAL; retval = cft->write_s64(cgrp, cft, val); } if (!retval) retval = nbytes; return retval;}static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, struct file *file, const char __user *userbuf, size_t nbytes, loff_t *unused_ppos){ char local_buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; size_t max_bytes = cft->max_write_len; char *buffer = local_buffer; if (!max_bytes) max_bytes = sizeof(local_buffer) - 1; if (nbytes >= max_bytes) return -E2BIG; /* Allocate a dynamic buffer if we need one */ if (nbytes >= sizeof(local_buffer)) { buffer = kmalloc(nbytes + 1, GFP_KERNEL); if (buffer == NULL) return -ENOMEM; } if (nbytes && copy_from_user(buffer, userbuf, nbytes)) { retval = -EFAULT; goto out; } buffer[nbytes] = 0; /* nul-terminate */ strstrip(buffer); retval = cft->write_string(cgrp, cft, buffer); if (!retval) retval = nbytes;out: if (buffer != local_buffer) kfree(buffer); return retval;}static ssize_t cgroup_file_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos){ struct cftype *cft = __d_cft(file->f_dentry); struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent); if (!cft || cgroup_is_removed(cgrp)) return -ENODEV; if (cft->write) return cft->write(cgrp, cft, file, buf, nbytes, ppos); if (cft->write_u64 || cft->write_s64) return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos); if (cft->write_string) return cgroup_write_string(cgrp, cft, file, buf, nbytes, ppos); if (cft->trigger) { int ret = cft->trigger(cgrp, (unsigned int)cft->private); return ret ? ret : nbytes; } return -EINVAL;}static ssize_t cgroup_read_u64(struct cgroup *cgrp, struct cftype *cft, struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ char tmp[CGROUP_LOCAL_BUFFER_SIZE]; u64 val = cft->read_u64(cgrp, cft); int len = sprintf(tmp, "%llu\n", (unsigned long long) val); return simple_read_from_buffer(buf, nbytes, ppos, tmp, len);}static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ char tmp[CGROUP_LOCAL_BUFFER_SIZE]; s64 val = cft->read_s64(cgrp, cft); int len = sprintf(tmp, "%lld\n", (long long) val); return simple_read_from_buffer(buf, nbytes, ppos, tmp, len);}static ssize_t cgroup_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ struct cftype *cft = __d_cft(file->f_dentry); struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent); if (!cft || cgroup_is_removed(cgrp))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -