diff options
| author | Ben Blum <bblum@andrew.cmu.edu> | 2011-05-26 19:25:20 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-26 20:12:34 -0400 |
| commit | 74a1166dfe1135dcc168d35fa5261aa7e087011b (patch) | |
| tree | a7add70f0344e2352b8d0d6beb10aef85c6585f7 | |
| parent | f780bdb7c1c73009cb57adcf99ef50027d80bf3c (diff) | |
cgroups: make procs file writable
Make procs file writable to move all threads by tgid at once.
Add functionality that enables users to move all threads in a threadgroup
at once to a cgroup by writing the tgid to the 'cgroup.procs' file. This
current implementation makes use of a per-threadgroup rwsem that's taken
for reading in the fork() path to prevent newly forking threads within the
threadgroup from "escaping" while the move is in progress.
Signed-off-by: Ben Blum <bblum@andrew.cmu.edu>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Matt Helsley <matthltc@us.ibm.com>
Reviewed-by: Paul Menage <menage@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | Documentation/cgroups/cgroups.txt | 9 | ||||
| -rw-r--r-- | kernel/cgroup.c | 439 |
2 files changed, 401 insertions, 47 deletions
diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index b3bd3bdbe202..8c4f3466c894 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt | |||
| @@ -236,7 +236,8 @@ containing the following files describing that cgroup: | |||
| 236 | - cgroup.procs: list of tgids in the cgroup. This list is not | 236 | - cgroup.procs: list of tgids in the cgroup. This list is not |
| 237 | guaranteed to be sorted or free of duplicate tgids, and userspace | 237 | guaranteed to be sorted or free of duplicate tgids, and userspace |
| 238 | should sort/uniquify the list if this property is required. | 238 | should sort/uniquify the list if this property is required. |
| 239 | This is a read-only file, for now. | 239 | Writing a thread group id into this file moves all threads in that |
| 240 | group into this cgroup. | ||
| 240 | - notify_on_release flag: run the release agent on exit? | 241 | - notify_on_release flag: run the release agent on exit? |
| 241 | - release_agent: the path to use for release notifications (this file | 242 | - release_agent: the path to use for release notifications (this file |
| 242 | exists in the top cgroup only) | 243 | exists in the top cgroup only) |
| @@ -430,6 +431,12 @@ You can attach the current shell task by echoing 0: | |||
| 430 | 431 | ||
| 431 | # echo 0 > tasks | 432 | # echo 0 > tasks |
| 432 | 433 | ||
| 434 | You can use the cgroup.procs file instead of the tasks file to move all | ||
| 435 | threads in a threadgroup at once. Echoing the pid of any task in a | ||
| 436 | threadgroup to cgroup.procs causes all tasks in that threadgroup to be | ||
| 437 | be attached to the cgroup. Writing 0 to cgroup.procs moves all tasks | ||
| 438 | in the writing task's threadgroup. | ||
| 439 | |||
| 433 | Note: Since every task is always a member of exactly one cgroup in each | 440 | Note: Since every task is always a member of exactly one cgroup in each |
| 434 | mounted hierarchy, to remove a task from its current cgroup you must | 441 | mounted hierarchy, to remove a task from its current cgroup you must |
| 435 | move it into a new cgroup (possibly the root cgroup) by writing to the | 442 | move it into a new cgroup (possibly the root cgroup) by writing to the |
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 38fb0ad1cb46..5e6a9745f0e7 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
| @@ -1735,6 +1735,76 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) | |||
| 1735 | } | 1735 | } |
| 1736 | EXPORT_SYMBOL_GPL(cgroup_path); | 1736 | EXPORT_SYMBOL_GPL(cgroup_path); |
| 1737 | 1737 | ||
| 1738 | /* | ||
| 1739 | * cgroup_task_migrate - move a task from one cgroup to another. | ||
| 1740 | * | ||
| 1741 | * 'guarantee' is set if the caller promises that a new css_set for the task | ||
| 1742 | * will already exist. If not set, this function might sleep, and can fail with | ||
| 1743 | * -ENOMEM. Otherwise, it can only fail with -ESRCH. | ||
| 1744 | */ | ||
| 1745 | static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp, | ||
| 1746 | struct task_struct *tsk, bool guarantee) | ||
| 1747 | { | ||
| 1748 | struct css_set *oldcg; | ||
| 1749 | struct css_set *newcg; | ||
| 1750 | |||
| 1751 | /* | ||
| 1752 | * get old css_set. we need to take task_lock and refcount it, because | ||
| 1753 | * an exiting task can change its css_set to init_css_set and drop its | ||
| 1754 | * old one without taking cgroup_mutex. | ||
| 1755 | */ | ||
| 1756 | task_lock(tsk); | ||
| 1757 | oldcg = tsk->cgroups; | ||
| 1758 | get_css_set(oldcg); | ||
| 1759 | task_unlock(tsk); | ||
| 1760 | |||
| 1761 | /* locate or allocate a new css_set for this task. */ | ||
| 1762 | if (guarantee) { | ||
| 1763 | /* we know the css_set we want already exists. */ | ||
| 1764 | struct cgroup_subsys_state *template[CGROUP_SUBSYS_COUNT]; | ||
| 1765 | read_lock(&css_set_lock); | ||
| 1766 | newcg = find_existing_css_set(oldcg, cgrp, template); | ||
| 1767 | BUG_ON(!newcg); | ||
| 1768 | get_css_set(newcg); | ||
| 1769 | read_unlock(&css_set_lock); | ||
| 1770 | } else { | ||
| 1771 | might_sleep(); | ||
| 1772 | /* find_css_set will give us newcg already referenced. */ | ||
| 1773 | newcg = find_css_set(oldcg, cgrp); | ||
| 1774 | if (!newcg) { | ||
| 1775 | put_css_set(oldcg); | ||
| 1776 | return -ENOMEM; | ||
| 1777 | } | ||
| 1778 | } | ||
| 1779 | put_css_set(oldcg); | ||
| 1780 | |||
| 1781 | /* if PF_EXITING is set, the tsk->cgroups pointer is no longer safe. */ | ||
| 1782 | task_lock(tsk); | ||
| 1783 | if (tsk->flags & PF_EXITING) { | ||
| 1784 | task_unlock(tsk); | ||
| 1785 | put_css_set(newcg); | ||
| 1786 | return -ESRCH; | ||
| 1787 | } | ||
| 1788 | rcu_assign_pointer(tsk->cgroups, newcg); | ||
| 1789 | task_unlock(tsk); | ||
| 1790 | |||
| 1791 | /* Update the css_set linked lists if we're using them */ | ||
| 1792 | write_lock(&css_set_lock); | ||
| 1793 | if (!list_empty(&tsk->cg_list)) | ||
| 1794 | list_move(&tsk->cg_list, &newcg->tasks); | ||
| 1795 | write_unlock(&css_set_lock); | ||
| 1796 | |||
| 1797 | /* | ||
| 1798 | * We just gained a reference on oldcg by taking it from the task. As | ||
| 1799 | * trading it for newcg is protected by cgroup_mutex, we're safe to drop | ||
| 1800 | * it here; it will be freed under RCU. | ||
| 1801 | */ | ||
| 1802 | put_css_set(oldcg); | ||
| 1803 | |||
| 1804 | set_bit(CGRP_RELEASABLE, &oldcgrp->flags); | ||
| 1805 | return 0; | ||
| 1806 | } | ||
| 1807 | |||
| 1738 | /** | 1808 | /** |
| 1739 | * cgroup_attach_task - attach task 'tsk' to cgroup 'cgrp' | 1809 | * cgroup_attach_task - attach task 'tsk' to cgroup 'cgrp' |
| 1740 | * @cgrp: the cgroup the task is attaching to | 1810 | * @cgrp: the cgroup the task is attaching to |
| @@ -1745,11 +1815,9 @@ EXPORT_SYMBOL_GPL(cgroup_path); | |||
| 1745 | */ | 1815 | */ |
| 1746 | int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) | 1816 | int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) |
| 1747 | { | 1817 | { |
| 1748 | int retval = 0; | 1818 | int retval; |
| 1749 | struct cgroup_subsys *ss, *failed_ss = NULL; | 1819 | struct cgroup_subsys *ss, *failed_ss = NULL; |
| 1750 | struct cgroup *oldcgrp; | 1820 | struct cgroup *oldcgrp; |
| 1751 | struct css_set *cg; | ||
| 1752 | struct css_set *newcg; | ||
| 1753 | struct cgroupfs_root *root = cgrp->root; | 1821 | struct cgroupfs_root *root = cgrp->root; |
| 1754 | 1822 | ||
| 1755 | /* Nothing to do if the task is already in that cgroup */ | 1823 | /* Nothing to do if the task is already in that cgroup */ |
| @@ -1780,36 +1848,9 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) | |||
| 1780 | } | 1848 | } |
| 1781 | } | 1849 | } |
| 1782 | 1850 | ||
| 1783 | task_lock(tsk); | 1851 | retval = cgroup_task_migrate(cgrp, oldcgrp, tsk, false); |
| 1784 | cg = tsk->cgroups; | 1852 | if (retval) |
| 1785 | get_css_set(cg); | ||
| 1786 | task_unlock(tsk); | ||
| 1787 | /* | ||
| 1788 | * Locate or allocate a new css_set for this task, | ||
| 1789 | * based on its final set of cgroups | ||
| 1790 | */ | ||
| 1791 | newcg = find_css_set(cg, cgrp); | ||
| 1792 | put_css_set(cg); | ||
| 1793 | if (!newcg) { | ||
| 1794 | retval = -ENOMEM; | ||
| 1795 | goto out; | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | task_lock(tsk); | ||
| 1799 | if (tsk->flags & PF_EXITING) { | ||
| 1800 | task_unlock(tsk); | ||
| 1801 | put_css_set(newcg); | ||
| 1802 | retval = -ESRCH; | ||
| 1803 | goto out; | 1853 | goto out; |
| 1804 | } | ||
| 1805 | rcu_assign_pointer(tsk->cgroups, newcg); | ||
| 1806 | task_unlock(tsk); | ||
| 1807 | |||
| 1808 | /* Update the css_set linked lists if we're using them */ | ||
| 1809 | write_lock(&css_set_lock); | ||
| 1810 | if (!list_empty(&tsk->cg_list)) | ||
| 1811 | list_move(&tsk->cg_list, &newcg->tasks); | ||
| 1812 | write_unlock(&css_set_lock); | ||
| 1813 | 1854 | ||
| 1814 | for_each_subsys(root, ss) { | 1855 | for_each_subsys(root, ss) { |
| 1815 | if (ss->pre_attach) | 1856 | if (ss->pre_attach) |
| @@ -1819,9 +1860,8 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) | |||
| 1819 | if (ss->attach) | 1860 | if (ss->attach) |
| 1820 | ss->attach(ss, cgrp, oldcgrp, tsk); | 1861 | ss->attach(ss, cgrp, oldcgrp, tsk); |
| 1821 | } | 1862 | } |
| 1822 | set_bit(CGRP_RELEASABLE, &oldcgrp->flags); | 1863 | |
| 1823 | synchronize_rcu(); | 1864 | synchronize_rcu(); |
| 1824 | put_css_set(cg); | ||
| 1825 | 1865 | ||
| 1826 | /* | 1866 | /* |
| 1827 | * wake up rmdir() waiter. the rmdir should fail since the cgroup | 1867 | * wake up rmdir() waiter. the rmdir should fail since the cgroup |
| @@ -1871,49 +1911,356 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) | |||
| 1871 | EXPORT_SYMBOL_GPL(cgroup_attach_task_all); | 1911 | EXPORT_SYMBOL_GPL(cgroup_attach_task_all); |
| 1872 | 1912 | ||
| 1873 | /* | 1913 | /* |
| 1874 | * Attach task with pid 'pid' to cgroup 'cgrp'. Call with cgroup_mutex | 1914 | * cgroup_attach_proc works in two stages, the first of which prefetches all |
| 1875 | * held. May take task_lock of task | 1915 | * new css_sets needed (to make sure we have enough memory before committing |
| 1916 | * to the move) and stores them in a list of entries of the following type. | ||
| 1917 | * TODO: possible optimization: use css_set->rcu_head for chaining instead | ||
| 1918 | */ | ||
| 1919 | struct cg_list_entry { | ||
| 1920 | |||
