sched, nohz: Track nr_busy_cpus in the sched_group_power
authorSuresh Siddha <suresh.b.siddha@intel.com>
Fri, 2 Dec 2011 01:07:33 +0000 (17:07 -0800)
committerIngo Molnar <mingo@elte.hu>
Tue, 6 Dec 2011 08:06:32 +0000 (09:06 +0100)
Introduce nr_busy_cpus in the struct sched_group_power [Not in sched_group
because sched groups are duplicated for the SD_OVERLAP scheduler domain]
and for each cpu that enters and exits idle, this parameter will
be updated in each scheduler group of the scheduler domain that this cpu
belongs to.

To avoid the frequent update of this state as the cpu enters
and exits idle, the update of the stat during idle exit is
delayed to the first timer tick that happens after the cpu becomes busy.
This is done using NOHZ_IDLE flag in the struct rq's nohz_flags.

Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20111202010832.555984323@sbsiddha-desk.sc.intel.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
include/linux/sched.h
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/sched.h
kernel/time/tick-sched.c

index 8db17b7622ecf620f757bda7af41c2875e51b1b3..295666cb5b86219ad1d7edcea451333beb34b314 100644 (file)
@@ -273,9 +273,11 @@ extern int runqueue_is_locked(int cpu);
 
 #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ)
 extern void select_nohz_load_balancer(int stop_tick);
+extern void set_cpu_sd_state_idle(void);
 extern int get_nohz_timer_target(void);
 #else
 static inline void select_nohz_load_balancer(int stop_tick) { }
+static inline void set_cpu_sd_state_idle(void);
 #endif
 
 /*
@@ -901,6 +903,10 @@ struct sched_group_power {
         * single CPU.
         */
        unsigned int power, power_orig;
+       /*
+        * Number of busy cpus in this group.
+        */
+       atomic_t nr_busy_cpus;
 };
 
 struct sched_group {
index 7f1da77b83f345925f30acbaba43b3acad53102e..699ff1499a8a394da0caabef3fea723df6bb4a7a 100644 (file)
@@ -6024,6 +6024,7 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd)
                return;
 
        update_group_power(sd, cpu);
+       atomic_set(&sg->sgp->nr_busy_cpus, sg->group_weight);
 }
 
 int __weak arch_sd_sibling_asym_packing(void)
index 50c06b0e9fab32a3cdf143f711848176b29a6572..e050563e97a4c90c3aa54929f1ff5ae18a982dc7 100644 (file)
@@ -4901,6 +4901,36 @@ static void nohz_balancer_kick(int cpu)
        return;
 }
 
+static inline void set_cpu_sd_state_busy(void)
+{
+       struct sched_domain *sd;
+       int cpu = smp_processor_id();
+
+       if (!test_bit(NOHZ_IDLE, nohz_flags(cpu)))
+               return;
+       clear_bit(NOHZ_IDLE, nohz_flags(cpu));
+
+       rcu_read_lock();
+       for_each_domain(cpu, sd)
+               atomic_inc(&sd->groups->sgp->nr_busy_cpus);
+       rcu_read_unlock();
+}
+
+void set_cpu_sd_state_idle(void)
+{
+       struct sched_domain *sd;
+       int cpu = smp_processor_id();
+
+       if (test_bit(NOHZ_IDLE, nohz_flags(cpu)))
+               return;
+       set_bit(NOHZ_IDLE, nohz_flags(cpu));
+
+       rcu_read_lock();
+       for_each_domain(cpu, sd)
+               atomic_dec(&sd->groups->sgp->nr_busy_cpus);
+       rcu_read_unlock();
+}
+
 /*
  * This routine will try to nominate the ilb (idle load balancing)
  * owner among the cpus whose ticks are stopped. ilb owner will do the idle
@@ -5135,6 +5165,7 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu)
        * We may be recently in ticked or tickless idle mode. At the first
        * busy tick after returning from idle, we will update the busy stats.
        */
+       set_cpu_sd_state_busy();
        if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu))))
                clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu));
 
index cf7d02662bc2af40691d5d97d1564519f8868793..91810f0ee3af18e20887accb7a62ef4dfea3ef4d 100644 (file)
@@ -1069,6 +1069,7 @@ extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
 enum rq_nohz_flag_bits {
        NOHZ_TICK_STOPPED,
        NOHZ_BALANCE_KICK,
+       NOHZ_IDLE,
 };
 
 #define nohz_flags(cpu)        (&cpu_rq(cpu)->nohz_flags)
index 40420644d0baa365c5d6d02027be9c1d59b0cd5a..31cc06163ed540df59acadb4ee194adc640a71c0 100644 (file)
@@ -296,6 +296,15 @@ void tick_nohz_stop_sched_tick(int inidle)
        cpu = smp_processor_id();
        ts = &per_cpu(tick_cpu_sched, cpu);
 
+       /*
+        * Update the idle state in the scheduler domain hierarchy
+        * when tick_nohz_stop_sched_tick() is called from the idle loop.
+        * State will be updated to busy during the first busy tick after
+        * exiting idle.
+        */
+       if (inidle)
+               set_cpu_sd_state_idle();
+
        /*
         * Call to tick_nohz_start_idle stops the last_update_time from being
         * updated. Thus, it must not be called in the event we are called from