Skip to content

Commit be367d0

Browse files
Ben Blumtorvalds
Ben Blum
authored andcommitted
cgroups: let ss->can_attach and ss->attach do whole threadgroups at a time
Alter the ss->can_attach and ss->attach functions to be able to deal with a whole threadgroup at a time, for use in cgroup_attach_proc. (This is a pre-patch to cgroup-procs-writable.patch.) Currently, new mode of the attach function can only tell the subsystem about the old cgroup of the threadgroup leader. No subsystem currently needs that information for each thread that's being moved, but if one were to be added (for example, one that counts tasks within a group) this bit would need to be reworked a bit to tell the subsystem the right information. [[email protected]: fix build] Signed-off-by: Ben Blum <[email protected]> Signed-off-by: Paul Menage <[email protected]> Acked-by: Li Zefan <[email protected]> Reviewed-by: Matt Helsley <[email protected]> Cc: "Eric W. Biederman" <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Dave Young <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent c378369 commit be367d0

File tree

9 files changed

+131
-30
lines changed

9 files changed

+131
-30
lines changed

Documentation/cgroups/cgroups.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,22 +521,28 @@ rmdir() will fail with it. From this behavior, pre_destroy() can be
521521
called multiple times against a cgroup.
522522

523523
int can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
524-
struct task_struct *task)
524+
struct task_struct *task, bool threadgroup)
525525
(cgroup_mutex held by caller)
526526

527527
Called prior to moving a task into a cgroup; if the subsystem
528528
returns an error, this will abort the attach operation. If a NULL
529529
task is passed, then a successful result indicates that *any*
530530
unspecified task can be moved into the cgroup. Note that this isn't
531531
called on a fork. If this method returns 0 (success) then this should
532-
remain valid while the caller holds cgroup_mutex.
532+
remain valid while the caller holds cgroup_mutex. If threadgroup is
533+
true, then a successful result indicates that all threads in the given
534+
thread's threadgroup can be moved together.
533535

534536
void attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
535-
struct cgroup *old_cgrp, struct task_struct *task)
537+
struct cgroup *old_cgrp, struct task_struct *task,
538+
bool threadgroup)
536539
(cgroup_mutex held by caller)
537540

538541
Called after the task has been attached to the cgroup, to allow any
539542
post-attachment activity that requires memory allocations or blocking.
543+
If threadgroup is true, the subsystem should take care of all threads
544+
in the specified thread's threadgroup. Currently does not support any
545+
subsystem that might need the old_cgrp for every thread in the group.
540546

541547
void fork(struct cgroup_subsy *ss, struct task_struct *task)
542548

include/linux/cgroup.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,11 @@ struct cgroup_subsys {
425425
struct cgroup *cgrp);
426426
int (*pre_destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp);
427427
void (*destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp);
428-
int (*can_attach)(struct cgroup_subsys *ss,
429-
struct cgroup *cgrp, struct task_struct *tsk);
428+
int (*can_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp,
429+
struct task_struct *tsk, bool threadgroup);
430430
void (*attach)(struct cgroup_subsys *ss, struct cgroup *cgrp,
431-
struct cgroup *old_cgrp, struct task_struct *tsk);
431+
struct cgroup *old_cgrp, struct task_struct *tsk,
432+
bool threadgroup);
432433
void (*fork)(struct cgroup_subsys *ss, struct task_struct *task);
433434
void (*exit)(struct cgroup_subsys *ss, struct task_struct *task);
434435
int (*populate)(struct cgroup_subsys *ss,

kernel/cgroup.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,7 +1552,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
15521552

15531553
for_each_subsys(root, ss) {
15541554
if (ss->can_attach) {
1555-
retval = ss->can_attach(ss, cgrp, tsk);
1555+
retval = ss->can_attach(ss, cgrp, tsk, false);
15561556
if (retval)
15571557
return retval;
15581558
}
@@ -1590,7 +1590,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
15901590

15911591
for_each_subsys(root, ss) {
15921592
if (ss->attach)
1593-
ss->attach(ss, cgrp, oldcgrp, tsk);
1593+
ss->attach(ss, cgrp, oldcgrp, tsk, false);
15941594
}
15951595
set_bit(CGRP_RELEASABLE, &oldcgrp->flags);
15961596
synchronize_rcu();

kernel/cgroup_freezer.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ static bool is_task_frozen_enough(struct task_struct *task)
159159
*/
160160
static int freezer_can_attach(struct cgroup_subsys *ss,
161161
struct cgroup *new_cgroup,
162-
struct task_struct *task)
162+
struct task_struct *task, bool threadgroup)
163163
{
164164
struct freezer *freezer;
165165

@@ -177,6 +177,19 @@ static int freezer_can_attach(struct cgroup_subsys *ss,
177177
if (freezer->state == CGROUP_FROZEN)
178178
return -EBUSY;
179179

180+
if (threadgroup) {
181+
struct task_struct *c;
182+
183+
rcu_read_lock();
184+
list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
185+
if (is_task_frozen_enough(c)) {
186+
rcu_read_unlock();
187+
return -EBUSY;
188+
}
189+
}
190+
rcu_read_unlock();
191+
}
192+
180193
return 0;
181194
}
182195

kernel/cpuset.c

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,9 +1324,10 @@ static int fmeter_getrate(struct fmeter *fmp)
13241324
static cpumask_var_t cpus_attach;
13251325

13261326
/* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */
1327-
static int cpuset_can_attach(struct cgroup_subsys *ss,
1328-
struct cgroup *cont, struct task_struct *tsk)
1327+
static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cont,
1328+
struct task_struct *tsk, bool threadgroup)
13291329
{
1330+
int ret;
13301331
struct cpuset *cs = cgroup_cs(cont);
13311332

13321333
if (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
@@ -1343,18 +1344,51 @@ static int cpuset_can_attach(struct cgroup_subsys *ss,
13431344
if (tsk->flags & PF_THREAD_BOUND)
13441345
return -EINVAL;
13451346

1346-
return security_task_setscheduler(tsk, 0, NULL);
1347+
ret = security_task_setscheduler(tsk, 0, NULL);
1348+
if (ret)
1349+
return ret;
1350+
if (threadgroup) {
1351+
struct task_struct *c;
1352+
1353+
rcu_read_lock();
1354+
list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
1355+
ret = security_task_setscheduler(c, 0, NULL);
1356+
if (ret) {
1357+
rcu_read_unlock();
1358+
return ret;
1359+
}
1360+
}
1361+
rcu_read_unlock();
1362+
}
1363+
return 0;
1364+
}
1365+
1366+
static void cpuset_attach_task(struct task_struct *tsk, nodemask_t *to,
1367+
struct cpuset *cs)
1368+
{
1369+
int err;
1370+
/*
1371+
* can_attach beforehand should guarantee that this doesn't fail.
1372+
* TODO: have a better way to handle failure here
1373+
*/
1374+
err = set_cpus_allowed_ptr(tsk, cpus_attach);
1375+
WARN_ON_ONCE(err);
1376+
1377+
task_lock(tsk);
1378+
cpuset_change_task_nodemask(tsk, to);
1379+
task_unlock(tsk);
1380+
cpuset_update_task_spread_flag(cs, tsk);
1381+
13471382
}
13481383

1349-
static void cpuset_attach(struct cgroup_subsys *ss,
1350-
struct cgroup *cont, struct cgroup *oldcont,
1351-
struct task_struct *tsk)
1384+
static void cpuset_attach(struct cgroup_subsys *ss, struct cgroup *cont,
1385+
struct cgroup *oldcont, struct task_struct *tsk,
1386+
bool threadgroup)
13521387
{
13531388
nodemask_t from, to;
13541389
struct mm_struct *mm;
13551390
struct cpuset *cs = cgroup_cs(cont);
13561391
struct cpuset *oldcs = cgroup_cs(oldcont);
1357-
int err;
13581392

13591393
if (cs == &top_cpuset) {
13601394
cpumask_copy(cpus_attach, cpu_possible_mask);
@@ -1363,15 +1397,19 @@ static void cpuset_attach(struct cgroup_subsys *ss,
13631397
guarantee_online_cpus(cs, cpus_attach);
13641398
guarantee_online_mems(cs, &to);
13651399
}
1366-
err = set_cpus_allowed_ptr(tsk, cpus_attach);
1367-
if (err)
1368-
return;
13691400

1370-
task_lock(tsk);
1371-
cpuset_change_task_nodemask(tsk, &to);
1372-
task_unlock(tsk);
1373-
cpuset_update_task_spread_flag(cs, tsk);
1401+
/* do per-task migration stuff possibly for each in the threadgroup */
1402+
cpuset_attach_task(tsk, &to, cs);
1403+
if (threadgroup) {
1404+
struct task_struct *c;
1405+
rcu_read_lock();
1406+
list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
1407+
cpuset_attach_task(c, &to, cs);
1408+
}
1409+
rcu_read_unlock();
1410+
}
13741411

1412+
/* change mm; only needs to be done once even if threadgroup */
13751413
from = oldcs->mems_allowed;
13761414
to = cs->mems_allowed;
13771415
mm = get_task_mm(tsk);

kernel/ns_cgroup.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ int ns_cgroup_clone(struct task_struct *task, struct pid *pid)
4242
* (hence either you are in the same cgroup as task, or in an
4343
* ancestor cgroup thereof)
4444
*/
45-
static int ns_can_attach(struct cgroup_subsys *ss,
46-
struct cgroup *new_cgroup, struct task_struct *task)
45+
static int ns_can_attach(struct cgroup_subsys *ss, struct cgroup *new_cgroup,
46+
struct task_struct *task, bool threadgroup)
4747
{
4848
if (current != task) {
4949
if (!capable(CAP_SYS_ADMIN))
@@ -56,6 +56,18 @@ static int ns_can_attach(struct cgroup_subsys *ss,
5656
if (!cgroup_is_descendant(new_cgroup, task))
5757
return -EPERM;
5858

59+
if (threadgroup) {
60+
struct task_struct *c;
61+
rcu_read_lock();
62+
list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
63+
if (!cgroup_is_descendant(new_cgroup, c)) {
64+
rcu_read_unlock();
65+
return -EPERM;
66+
}
67+
}
68+
rcu_read_unlock();
69+
}
70+
5971
return 0;
6072
}
6173

kernel/sched.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10377,8 +10377,7 @@ cpu_cgroup_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp)
1037710377
}
1037810378

1037910379
static int
10380-
cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
10381-
struct task_struct *tsk)
10380+
cpu_cgroup_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
1038210381
{
1038310382
#ifdef CONFIG_RT_GROUP_SCHED
1038410383
if (!sched_rt_can_attach(cgroup_tg(cgrp), tsk))
@@ -10388,15 +10387,45 @@ cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
1038810387
if (tsk->sched_class != &fair_sched_class)
1038910388
return -EINVAL;
1039010389
#endif
10390+
return 0;
10391+
}
1039110392

10393+
static int
10394+
cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
10395+
struct task_struct *tsk, bool threadgroup)
10396+
{
10397+
int retval = cpu_cgroup_can_attach_task(cgrp, tsk);
10398+
if (retval)
10399+
return retval;
10400+
if (threadgroup) {
10401+
struct task_struct *c;
10402+
rcu_read_lock();
10403+
list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
10404+
retval = cpu_cgroup_can_attach_task(cgrp, c);
10405+
if (retval) {
10406+
rcu_read_unlock();
10407+
return retval;
10408+
}
10409+
}
10410+
rcu_read_unlock();
10411+
}
1039210412
return 0;
1039310413
}
1039410414

1039510415
static void
1039610416
cpu_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
10397-
struct cgroup *old_cont, struct task_struct *tsk)
10417+
struct cgroup *old_cont, struct task_struct *tsk,
10418+
bool threadgroup)
1039810419
{
1039910420
sched_move_task(tsk);
10421+
if (threadgroup) {
10422+
struct task_struct *c;
10423+
rcu_read_lock();
10424+
list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
10425+
sched_move_task(c);
10426+
}
10427+
rcu_read_unlock();
10428+
}
1040010429
}
1040110430

1040210431
#ifdef CONFIG_FAIR_GROUP_SCHED

mm/memcontrol.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2612,7 +2612,8 @@ static int mem_cgroup_populate(struct cgroup_subsys *ss,
26122612
static void mem_cgroup_move_task(struct cgroup_subsys *ss,
26132613
struct cgroup *cont,
26142614
struct cgroup *old_cont,
2615-
struct task_struct *p)
2615+
struct task_struct *p,
2616+
bool threadgroup)
26162617
{
26172618
mutex_lock(&memcg_tasklist);
26182619
/*

security/device_cgroup.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
6161
struct cgroup_subsys devices_subsys;
6262

6363
static int devcgroup_can_attach(struct cgroup_subsys *ss,
64-
struct cgroup *new_cgroup, struct task_struct *task)
64+
struct cgroup *new_cgroup, struct task_struct *task,
65+
bool threadgroup)
6566
{
6667
if (current != task && !capable(CAP_SYS_ADMIN))
6768
return -EPERM;

0 commit comments

Comments
 (0)