This is how to reproduce the bug:

int main() {
    struct sched_param param = {.sched_priority=1};

    if (fork() > 0)
        exit(0);

    setsid();

    if (sched_setscheduler(0, SCHED_RR, &param) < 0){
        perror("failed to sched_setscheduler()");
        return -1;
    }

    while(1)
        ;
}

  # ./test
  # mount -t cgroup -o cpu xxx /cgroup
  # cat /cgroup/cpu.rt_runtime_us
  950000
  # echo 940000 > /cgroup/cpu.rt_runtime_us
  Device or Resource busy

An autogroup has been created and there's an RT task in it. RT tasks in
autogroups are redirected to the root group and task_group() should
return &root_task_group, but it's broken and returns the autogroup.

We should reset p->sched_task_group when changing a normal task to an
RT task which is in autogroup.

Fixes: 8323f26ce342 ("sched: Fix race in task_group()")
Cc: <[email protected]> # 3.6+
Reported-by: Zhang Wei <[email protected]>
Signed-off-by: Zefan Li <[email protected]>
---
 kernel/sched/core.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 89e7283..fccde96 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3414,6 +3414,7 @@ static int __sched_setscheduler(struct task_struct *p,
        const struct sched_class *prev_class;
        struct rq *rq;
        int reset_on_fork;
+       bool reset_task_group = false;
 
        /* may grab non-irq protected spin_locks */
        BUG_ON(in_interrupt());
@@ -3546,10 +3547,13 @@ change:
                 * assigned.
                 */
                if (rt_bandwidth_enabled() && rt_policy(policy) &&
-                               task_group(p)->rt_bandwidth.rt_runtime == 0 &&
-                               !task_group_is_autogroup(task_group(p))) {
-                       task_rq_unlock(rq, p, &flags);
-                       return -EPERM;
+                   task_group(p)->rt_bandwidth.rt_runtime == 0) {
+                       if (!task_group_is_autogroup(task_group(p))) {
+                               task_rq_unlock(rq, p, &flags);
+                               return -EPERM;
+                       } else {
+                               reset_task_group = true;
+                       }
                }
 #endif
 #ifdef CONFIG_SMP
@@ -3615,6 +3619,9 @@ change:
        prev_class = p->sched_class;
        __setscheduler(rq, p, attr);
 
+       if (reset_task_group)
+               p->sched_task_group = &root_task_group;
+
        if (running)
                p->sched_class->set_curr_task(rq);
        if (queued) {
-- 
1.8.0.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to