Commit a424316ca154317367c7ddf89997d1c80e4a8051
Committed by
Linus Torvalds
1 parent
697f416108
Exists in
master
and in
4 other branches
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>
Showing 3 changed files with 152 additions and 3 deletions Side-by-side Diff
fs/proc/base.c
... | ... | @@ -67,6 +67,7 @@ |
67 | 67 | #include <linux/mount.h> |
68 | 68 | #include <linux/security.h> |
69 | 69 | #include <linux/ptrace.h> |
70 | +#include <linux/cgroup.h> | |
70 | 71 | #include <linux/cpuset.h> |
71 | 72 | #include <linux/audit.h> |
72 | 73 | #include <linux/poll.h> |
... | ... | @@ -2133,6 +2134,9 @@ |
2133 | 2134 | #ifdef CONFIG_CPUSETS |
2134 | 2135 | REG("cpuset", S_IRUGO, cpuset), |
2135 | 2136 | #endif |
2137 | +#ifdef CONFIG_CGROUPS | |
2138 | + REG("cgroup", S_IRUGO, cgroup), | |
2139 | +#endif | |
2136 | 2140 | INF("oom_score", S_IRUGO, oom_score), |
2137 | 2141 | REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), |
2138 | 2142 | #ifdef CONFIG_AUDITSYSCALL |
... | ... | @@ -2418,6 +2422,9 @@ |
2418 | 2422 | #endif |
2419 | 2423 | #ifdef CONFIG_CPUSETS |
2420 | 2424 | REG("cpuset", S_IRUGO, cpuset), |
2425 | +#endif | |
2426 | +#ifdef CONFIG_CGROUPS | |
2427 | + REG("cgroup", S_IRUGO, cgroup), | |
2421 | 2428 | #endif |
2422 | 2429 | INF("oom_score", S_IRUGO, oom_score), |
2423 | 2430 | REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), |
include/linux/cgroup.h
... | ... | @@ -29,6 +29,8 @@ |
29 | 29 | extern void cgroup_fork_callbacks(struct task_struct *p); |
30 | 30 | extern void cgroup_exit(struct task_struct *p, int run_callbacks); |
31 | 31 | |
32 | +extern struct file_operations proc_cgroup_operations; | |
33 | + | |
32 | 34 | /* Per-subsystem/per-cgroup state maintained by the system. */ |
33 | 35 | struct cgroup_subsys_state { |
34 | 36 | /* The cgroup that this subsystem is attached to. Useful |
kernel/cgroup.c
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | #include <linux/mutex.h> |
34 | 34 | #include <linux/mount.h> |
35 | 35 | #include <linux/pagemap.h> |
36 | +#include <linux/proc_fs.h> | |
36 | 37 | #include <linux/rcupdate.h> |
37 | 38 | #include <linux/sched.h> |
38 | 39 | #include <linux/seq_file.h> |
39 | 40 | |
40 | 41 | |
... | ... | @@ -247,13 +248,15 @@ |
247 | 248 | static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry); |
248 | 249 | static int cgroup_populate_dir(struct cgroup *cont); |
249 | 250 | static struct inode_operations cgroup_dir_inode_operations; |
251 | +static struct file_operations proc_cgroupstats_operations; | |
250 | 252 | |
253 | +static struct backing_dev_info cgroup_backing_dev_info = { | |
254 | + .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | |
255 | +}; | |
256 | + | |
251 | 257 | static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb) |
252 | 258 | { |
253 | 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 | 261 | if (inode) { |
259 | 262 | inode->i_mode = mode; |
260 | 263 | |
... | ... | @@ -1600,7 +1603,12 @@ |
1600 | 1603 | { |
1601 | 1604 | int err; |
1602 | 1605 | int i; |
1606 | + struct proc_dir_entry *entry; | |
1603 | 1607 | |
1608 | + err = bdi_init(&cgroup_backing_dev_info); | |
1609 | + if (err) | |
1610 | + return err; | |
1611 | + | |
1604 | 1612 | for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { |
1605 | 1613 | struct cgroup_subsys *ss = subsys[i]; |
1606 | 1614 | if (!ss->early_init) |
1607 | 1615 | |
1608 | 1616 | |
... | ... | @@ -1611,9 +1619,141 @@ |
1611 | 1619 | if (err < 0) |
1612 | 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 | 1626 | out: |
1627 | + if (err) | |
1628 | + bdi_destroy(&cgroup_backing_dev_info); | |
1629 | + | |
1615 | 1630 | return err; |
1616 | 1631 | } |
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 | +}; | |
1617 | 1757 | |
1618 | 1758 | /** |
1619 | 1759 | * cgroup_fork - attach newly forked task to its parents cgroup. |