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 /kernel | |
| 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>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/cgroup.c | 146 |
1 files changed, 143 insertions, 3 deletions
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. |
