diff options
author | Paul Menage <menage@google.com> | 2007-10-19 02:39:35 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-19 14:53:36 -0400 |
commit | a424316ca154317367c7ddf89997d1c80e4a8051 (patch) | |
tree | ed349926c41aad5be6d62c9074ff72a0d9ac32c2 | |
parent | 697f41610863c9264a7ae26dac9a387c9dda8c84 (diff) |
Task Control Groups: add procfs interface
Add:
/proc/cgroups - general system info
/proc/*/cgroup - per-task cgroup membership info
[a.p.zijlstra@chello.nl: cgroups: bdi init hooks]
Signed-off-by: Paul Menage <menage@google.com>
Cc: Serge E. Hallyn <serue@us.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Jackson <pj@sgi.com>
Cc: Kirill Korotaev <dev@openvz.org>
Cc: Herbert Poetzl <herbert@13thfloor.at>
Cc: Srivatsa Vaddagiri <vatsa@in.ibm.com>
Cc: Cedric Le Goater <clg@fr.ibm.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/proc/base.c | 7 | ||||
-rw-r--r-- | include/linux/cgroup.h | 2 | ||||
-rw-r--r-- | kernel/cgroup.c | 146 |
3 files changed, 152 insertions, 3 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 4fe74d156416..0e9a9aa9df64 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -67,6 +67,7 @@ | |||
67 | #include <linux/mount.h> | 67 | #include <linux/mount.h> |
68 | #include <linux/security.h> | 68 | #include <linux/security.h> |
69 | #include <linux/ptrace.h> | 69 | #include <linux/ptrace.h> |
70 | #include <linux/cgroup.h> | ||
70 | #include <linux/cpuset.h> | 71 | #include <linux/cpuset.h> |
71 | #include <linux/audit.h> | 72 | #include <linux/audit.h> |
72 | #include <linux/poll.h> | 73 | #include <linux/poll.h> |
@@ -2133,6 +2134,9 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
2133 | #ifdef CONFIG_CPUSETS | 2134 | #ifdef CONFIG_CPUSETS |
2134 | REG("cpuset", S_IRUGO, cpuset), | 2135 | REG("cpuset", S_IRUGO, cpuset), |
2135 | #endif | 2136 | #endif |
2137 | #ifdef CONFIG_CGROUPS | ||
2138 | REG("cgroup", S_IRUGO, cgroup), | ||
2139 | #endif | ||
2136 | INF("oom_score", S_IRUGO, oom_score), | 2140 | INF("oom_score", S_IRUGO, oom_score), |
2137 | REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), | 2141 | REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), |
2138 | #ifdef CONFIG_AUDITSYSCALL | 2142 | #ifdef CONFIG_AUDITSYSCALL |
@@ -2419,6 +2423,9 @@ static const struct pid_entry tid_base_stuff[] = { | |||
2419 | #ifdef CONFIG_CPUSETS | 2423 | #ifdef CONFIG_CPUSETS |
2420 | REG("cpuset", S_IRUGO, cpuset), | 2424 | REG("cpuset", S_IRUGO, cpuset), |
2421 | #endif | 2425 | #endif |
2426 | #ifdef CONFIG_CGROUPS | ||
2427 | REG("cgroup", S_IRUGO, cgroup), | ||
2428 | #endif | ||
2422 | INF("oom_score", S_IRUGO, oom_score), | 2429 | INF("oom_score", S_IRUGO, oom_score), |
2423 | REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), | 2430 | REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), |
2424 | #ifdef CONFIG_AUDITSYSCALL | 2431 | #ifdef CONFIG_AUDITSYSCALL |
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index b21cf093ac62..a9553568118f 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -29,6 +29,8 @@ extern void cgroup_fork(struct task_struct *p); | |||
29 | extern void cgroup_fork_callbacks(struct task_struct *p); | 29 | extern void cgroup_fork_callbacks(struct task_struct *p); |
30 | extern void cgroup_exit(struct task_struct *p, int run_callbacks); | 30 | extern void cgroup_exit(struct task_struct *p, int run_callbacks); |
31 | 31 | ||
32 | extern struct file_operations proc_cgroup_operations; | ||
33 | |||
32 | /* Per-subsystem/per-cgroup state maintained by the system. */ | 34 | /* Per-subsystem/per-cgroup state maintained by the system. */ |
33 | struct cgroup_subsys_state { | 35 | struct cgroup_subsys_state { |
34 | /* The cgroup that this subsystem is attached to. Useful | 36 | /* The cgroup that this subsystem is attached to. Useful |
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index cc68fe68a60e..db245f19eb8a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
34 | #include <linux/mount.h> | 34 | #include <linux/mount.h> |
35 | #include <linux/pagemap.h> | 35 | #include <linux/pagemap.h> |
36 | #include <linux/proc_fs.h> | ||
36 | #include <linux/rcupdate.h> | 37 | #include <linux/rcupdate.h> |
37 | #include <linux/sched.h> | 38 | #include <linux/sched.h> |
38 | #include <linux/seq_file.h> | 39 | #include <linux/seq_file.h> |
@@ -247,13 +248,15 @@ static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, int mode); | |||
247 | static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry); | 248 | static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry); |
248 | static int cgroup_populate_dir(struct cgroup *cont); | 249 | static int cgroup_populate_dir(struct cgroup *cont); |
249 | static struct inode_operations cgroup_dir_inode_operations; | 250 | static struct inode_operations cgroup_dir_inode_operations; |
251 | static struct file_operations proc_cgroupstats_operations; | ||
252 | |||
253 | static struct backing_dev_info cgroup_backing_dev_info = { | ||
254 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | ||
255 | }; | ||
250 | 256 | ||
251 | static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb) | 257 | static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb) |
252 | { | 258 | { |
253 | struct inode *inode = new_inode(sb); | 259 | struct inode *inode = new_inode(sb); |
254 | static struct backing_dev_info cgroup_backing_dev_info = { | ||
255 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | ||
256 | }; | ||
257 | 260 | ||
258 | if (inode) { | 261 | if (inode) { |
259 | inode->i_mode = mode; | 262 | inode->i_mode = mode; |
@@ -1600,6 +1603,11 @@ int __init cgroup_init(void) | |||
1600 | { | 1603 | { |
1601 | int err; | 1604 | int err; |
1602 | int i; | 1605 | int i; |
1606 | struct proc_dir_entry *entry; | ||
1607 | |||
1608 | err = bdi_init(&cgroup_backing_dev_info); | ||
1609 | if (err) | ||
1610 | return err; | ||
1603 | 1611 | ||
1604 | for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { | 1612 | for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { |
1605 | struct cgroup_subsys *ss = subsys[i]; | 1613 | struct cgroup_subsys *ss = subsys[i]; |
@@ -1611,10 +1619,142 @@ int __init cgroup_init(void) | |||
1611 | if (err < 0) | 1619 | if (err < 0) |
1612 | goto out; | 1620 | goto out; |
1613 | 1621 | ||
1622 | entry = create_proc_entry("cgroups", 0, NULL); | ||
1623 | if (entry) | ||
1624 | entry->proc_fops = &proc_cgroupstats_operations; | ||
1625 | |||
1614 | out: | 1626 | out: |
1627 | if (err) | ||
1628 | bdi_destroy(&cgroup_backing_dev_info); | ||
1629 | |||
1615 | return err; | 1630 | return err; |
1616 | } | 1631 | } |
1617 | 1632 | ||
1633 | /* | ||
1634 | * proc_cgroup_show() | ||
1635 | * - Print task's cgroup paths into seq_file, one line for each hierarchy | ||
1636 | * - Used for /proc/<pid>/cgroup. | ||
1637 | * - No need to task_lock(tsk) on this tsk->cgroup reference, as it | ||
1638 | * doesn't really matter if tsk->cgroup changes after we read it, | ||
1639 | * and we take cgroup_mutex, keeping attach_task() from changing it | ||
1640 | * anyway. No need to check that tsk->cgroup != NULL, thanks to | ||
1641 | * the_top_cgroup_hack in cgroup_exit(), which sets an exiting tasks | ||
1642 | * cgroup to top_cgroup. | ||
1643 | */ | ||
1644 | |||
1645 | /* TODO: Use a proper seq_file iterator */ | ||
1646 | static int proc_cgroup_show(struct seq_file *m, void *v) | ||
1647 | { | ||
1648 | struct pid *pid; | ||
1649 | struct task_struct *tsk; | ||
1650 | char *buf; | ||
1651 | int retval; | ||
1652 | struct cgroupfs_root *root; | ||
1653 | |||
1654 | retval = -ENOMEM; | ||
1655 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
1656 | if (!buf) | ||
1657 | goto out; | ||
1658 | |||
1659 | retval = -ESRCH; | ||
1660 | pid = m->private; | ||
1661 | tsk = get_pid_task(pid, PIDTYPE_PID); | ||
1662 | if (!tsk) | ||
1663 | goto out_free; | ||
1664 | |||
1665 | retval = 0; | ||
1666 | |||
1667 | mutex_lock(&cgroup_mutex); | ||
1668 | |||
1669 | for_each_root(root) { | ||
1670 | struct cgroup_subsys *ss; | ||
1671 | struct cgroup *cont; | ||
1672 | int subsys_id; | ||
1673 | int count = 0; | ||
1674 | |||
1675 | /* Skip this hierarchy if it has no active subsystems */ | ||
1676 | if (!root->actual_subsys_bits) | ||
1677 | continue; | ||
1678 | for_each_subsys(root, ss) | ||
1679 | seq_printf(m, "%s%s", count++ ? "," : "", ss->name); | ||
1680 | seq_putc(m, ':'); | ||
1681 | get_first_subsys(&root->top_cgroup, NULL, &subsys_id); | ||
1682 | cont = task_cgroup(tsk, subsys_id); | ||
1683 | retval = cgroup_path(cont, buf, PAGE_SIZE); | ||
1684 | if (retval < 0) | ||
1685 | goto out_unlock; | ||
1686 | seq_puts(m, buf); | ||
1687 | seq_putc(m, '\n'); | ||
1688 | } | ||
1689 | |||
1690 | out_unlock: | ||
1691 | mutex_unlock(&cgroup_mutex); | ||
1692 | put_task_struct(tsk); | ||
1693 | out_free: | ||
1694 | kfree(buf); | ||
1695 | out: | ||
1696 | return retval; | ||
1697 | } | ||
1698 | |||
1699 | static int cgroup_open(struct inode *inode, struct file *file) | ||
1700 | { | ||
1701 | struct pid *pid = PROC_I(inode)->pid; | ||
1702 | return single_open(file, proc_cgroup_show, pid); | ||
1703 | } | ||
1704 | |||
1705 | struct file_operations proc_cgroup_operations = { | ||
1706 | .open = cgroup_open, | ||
1707 | .read = seq_read, | ||
1708 | .llseek = seq_lseek, | ||
1709 | .release = single_release, | ||
1710 | }; | ||
1711 | |||
1712 | /* Display information about each subsystem and each hierarchy */ | ||
1713 | static int proc_cgroupstats_show(struct seq_file *m, void *v) | ||
1714 | { | ||
1715 | int i; | ||
1716 | struct cgroupfs_root *root; | ||
1717 | |||
1718 | mutex_lock(&cgroup_mutex); | ||
1719 | seq_puts(m, "Hierarchies:\n"); | ||
1720 | for_each_root(root) { | ||
1721 | struct cgroup_subsys *ss; | ||
1722 | int first = 1; | ||
1723 | seq_printf(m, "%p: bits=%lx cgroups=%d (", root, | ||
1724 | root->subsys_bits, root->number_of_cgroups); | ||
1725 | for_each_subsys(root, ss) { | ||
1726 | seq_printf(m, "%s%s", first ? "" : ", ", ss->name); | ||
1727 | first = false; | ||
1728 | } | ||
1729 | seq_putc(m, ')'); | ||
1730 | if (root->sb) { | ||
1731 | seq_printf(m, " s_active=%d", | ||
1732 | atomic_read(&root->sb->s_active)); | ||
1733 | } | ||
1734 | seq_putc(m, '\n'); | ||
1735 | } | ||
1736 | seq_puts(m, "Subsystems:\n"); | ||
1737 | for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { | ||
1738 | struct cgroup_subsys *ss = subsys[i]; | ||
1739 | seq_printf(m, "%d: name=%s hierarchy=%p\n", | ||
1740 | i, ss->name, ss->root); | ||
1741 | } | ||
1742 | mutex_unlock(&cgroup_mutex); | ||
1743 | return 0; | ||
1744 | } | ||
1745 | |||
1746 | static int cgroupstats_open(struct inode *inode, struct file *file) | ||
1747 | { | ||
1748 | return single_open(file, proc_cgroupstats_show, 0); | ||
1749 | } | ||
1750 | |||
1751 | static struct file_operations proc_cgroupstats_operations = { | ||
1752 | .open = cgroupstats_open, | ||
1753 | .read = seq_read, | ||
1754 | .llseek = seq_lseek, | ||
1755 | .release = single_release, | ||
1756 | }; | ||
1757 | |||
1618 | /** | 1758 | /** |
1619 | * cgroup_fork - attach newly forked task to its parents cgroup. | 1759 | * cgroup_fork - attach newly forked task to its parents cgroup. |
1620 | * @tsk: pointer to task_struct of forking parent process. | 1760 | * @tsk: pointer to task_struct of forking parent process. |