Skip to content
Snippets Groups Projects
Select Git revision
  • 90e362f4a75d0911ca75e5cd95591a6cf1f169dc
  • master default
  • android-container
  • nanopc-t4
  • for-kernelci
  • WIP-syscall
  • v4.16-rc5
  • v4.16-rc4
  • v4.16-rc3
  • v4.16-rc2
  • v4.16-rc1
  • v4.15
  • v4.15-rc9
  • v4.15-rc8
  • v4.15-rc7
  • v4.15-rc6
  • v4.15-rc5
  • v4.15-rc4
  • v4.15-rc3
  • v4.15-rc2
  • v4.15-rc1
  • v4.14
  • v4.14-rc8
  • v4.14-rc7
  • v4.14-rc6
  • v4.14-rc5
26 results

idle_task.c

Blame
    • Thomas Gleixner's avatar
      90e362f4
      sched: Provide update_curr callbacks for stop/idle scheduling classes · 90e362f4
      Thomas Gleixner authored
      
      Chris bisected a NULL pointer deference in task_sched_runtime() to
      commit 6e998916 'sched/cputime: Fix clock_nanosleep()/clock_gettime()
      inconsistency'.
      
      Chris observed crashes in atop or other /proc walking programs when he
      started fork bombs on his machine.  He assumed that this is a new exit
      race, but that does not make any sense when looking at that commit.
      
      What's interesting is that, the commit provides update_curr callbacks
      for all scheduling classes except stop_task and idle_task.
      
      While nothing can ever hit that via the clock_nanosleep() and
      clock_gettime() interfaces, which have been the target of the commit in
      question, the author obviously forgot that there are other code paths
      which invoke task_sched_runtime()
      
      do_task_stat(()
       thread_group_cputime_adjusted()
         thread_group_cputime()
           task_cputime()
             task_sched_runtime()
              if (task_current(rq, p) && task_on_rq_queued(p)) {
                update_rq_clock(rq);
                up->sched_class->update_curr(rq);
              }
      
      If the stats are read for a stomp machine task, aka 'migration/N' and
      that task is current on its cpu, this will happily call the NULL pointer
      of stop_task->update_curr.  Ooops.
      
      Chris observation that this happens faster when he runs the fork bomb
      makes sense as the fork bomb will kick migration threads more often so
      the probability to hit the issue will increase.
      
      Add the missing update_curr callbacks to the scheduler classes stop_task
      and idle_task.  While idle tasks cannot be monitored via /proc we have
      other means to hit the idle case.
      
      Fixes: 6e998916 'sched/cputime: Fix clock_nanosleep()/clock_gettime() inconsistency'
      Reported-by: default avatarChris Mason <clm@fb.com>
      Reported-and-tested-by: default avatarBorislav Petkov <bp@alien8.de>
      Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
      Cc: Ingo Molnar <mingo@kernel.org>
      Cc: Stanislaw Gruszka <sgruszka@redhat.com>
      Cc: Peter Zijlstra <peterz@infradead.org>
      Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
      90e362f4
      History
      sched: Provide update_curr callbacks for stop/idle scheduling classes
      Thomas Gleixner authored
      
      Chris bisected a NULL pointer deference in task_sched_runtime() to
      commit 6e998916 'sched/cputime: Fix clock_nanosleep()/clock_gettime()
      inconsistency'.
      
      Chris observed crashes in atop or other /proc walking programs when he
      started fork bombs on his machine.  He assumed that this is a new exit
      race, but that does not make any sense when looking at that commit.
      
      What's interesting is that, the commit provides update_curr callbacks
      for all scheduling classes except stop_task and idle_task.
      
      While nothing can ever hit that via the clock_nanosleep() and
      clock_gettime() interfaces, which have been the target of the commit in
      question, the author obviously forgot that there are other code paths
      which invoke task_sched_runtime()
      
      do_task_stat(()
       thread_group_cputime_adjusted()
         thread_group_cputime()
           task_cputime()
             task_sched_runtime()
              if (task_current(rq, p) && task_on_rq_queued(p)) {
                update_rq_clock(rq);
                up->sched_class->update_curr(rq);
              }
      
      If the stats are read for a stomp machine task, aka 'migration/N' and
      that task is current on its cpu, this will happily call the NULL pointer
      of stop_task->update_curr.  Ooops.
      
      Chris observation that this happens faster when he runs the fork bomb
      makes sense as the fork bomb will kick migration threads more often so
      the probability to hit the issue will increase.
      
      Add the missing update_curr callbacks to the scheduler classes stop_task
      and idle_task.  While idle tasks cannot be monitored via /proc we have
      other means to hit the idle case.
      
      Fixes: 6e998916 'sched/cputime: Fix clock_nanosleep()/clock_gettime() inconsistency'
      Reported-by: default avatarChris Mason <clm@fb.com>
      Reported-and-tested-by: default avatarBorislav Petkov <bp@alien8.de>
      Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
      Cc: Ingo Molnar <mingo@kernel.org>
      Cc: Stanislaw Gruszka <sgruszka@redhat.com>
      Cc: Peter Zijlstra <peterz@infradead.org>
      Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    idle_task.c 2.25 KiB
    #include "sched.h"
    
    /*
     * idle-task scheduling class.
     *
     * (NOTE: these are not related to SCHED_IDLE tasks which are
     *  handled in sched/fair.c)
     */
    
    #ifdef CONFIG_SMP
    static int
    select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags)
    {
    	return task_cpu(p); /* IDLE tasks as never migrated */
    }
    #endif /* CONFIG_SMP */
    
    /*
     * Idle tasks are unconditionally rescheduled:
     */
    static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int flags)
    {
    	resched_curr(rq);
    }
    
    static struct task_struct *
    pick_next_task_idle(struct rq *rq, struct task_struct *prev)
    {
    	put_prev_task(rq, prev);
    
    	schedstat_inc(rq, sched_goidle);
    	return rq->idle;
    }
    
    /*
     * It is not legal to sleep in the idle task - print a warning
     * message if some code attempts to do it:
     */
    static void
    dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
    {
    	raw_spin_unlock_irq(&rq->lock);
    	printk(KERN_ERR "bad: scheduling from the idle thread!\n");
    	dump_stack();
    	raw_spin_lock_irq(&rq->lock);
    }
    
    static void put_prev_task_idle(struct rq *rq, struct task_struct *prev)
    {
    	idle_exit_fair(rq);
    	rq_last_tick_reset(rq);
    }
    
    static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
    {
    }
    
    static void set_curr_task_idle(struct rq *rq)
    {
    }
    
    static void switched_to_idle(struct rq *rq, struct task_struct *p)
    {
    	BUG();
    }
    
    static void
    prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
    {
    	BUG();
    }
    
    static unsigned int get_rr_interval_idle(struct rq *rq, struct task_struct *task)
    {
    	return 0;
    }
    
    static void update_curr_idle(struct rq *rq)
    {
    }
    
    /*
     * Simple, special scheduling class for the per-CPU idle tasks:
     */
    const struct sched_class idle_sched_class = {
    	/* .next is NULL */
    	/* no enqueue/yield_task for idle tasks */
    
    	/* dequeue is not valid, we print a debug message there: */
    	.dequeue_task		= dequeue_task_idle,
    
    	.check_preempt_curr	= check_preempt_curr_idle,
    
    	.pick_next_task		= pick_next_task_idle,
    	.put_prev_task		= put_prev_task_idle,
    
    #ifdef CONFIG_SMP
    	.select_task_rq		= select_task_rq_idle,
    #endif
    
    	.set_curr_task          = set_curr_task_idle,
    	.task_tick		= task_tick_idle,
    
    	.get_rr_interval	= get_rr_interval_idle,
    
    	.prio_changed		= prio_changed_idle,
    	.switched_to		= switched_to_idle,
    	.update_curr		= update_curr_idle,
    };