mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH v1 0/5] Modular find_busiest_group()
@ 2008-09-24 16:16 Vaidyanathan Srinivasan
  2008-09-24 16:17 ` [RFC PATCH v1 1/5] Load calculation for each group Vaidyanathan Srinivasan
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Vaidyanathan Srinivasan @ 2008-09-24 16:16 UTC (permalink / raw)
  To: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi, Peter Zijlstra
  Cc: Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Gautham R Shenoy, Andi Kleen, David Collier-Brown, Tim Connors,
	Max Krasnyansky, Vaidyanathan Srinivasan

Hi,

I have been building tunable sched_mc=n patches on top of existing
sched_mc_power_savings code and adding more stuff to
find_busiest_group().

Reference:

[1]Making power policy just work
http://lwn.net/Articles/287924/ 

[2][RFC v1] Tunable sched_mc_power_savings=n
http://lwn.net/Articles/287882/

[3][RFC PATCH v2 0/7] Tunable sched_mc_power_savings=n
http://lwn.net/Articles/297306/

Peter Zijlstra had suggested that it is a good idea to cleanup the
current code in find_busiest_group() before building on the existing
power saving balance infrastructure [4]. This becomes even more
important from the fact that there have been recent bugs in the power
savings code that was hard to detect and fix [5][6].

[4] http://lkml.org/lkml/2008/9/8/103

Reference to bugs:

[5] sched: Fix __load_balance_iterator() for cfq with only one task
http://lkml.org/lkml/2008/9/5/135

[6]sched: arch_reinit_sched_domains() must destroy domains to force rebuild
http://lkml.org/lkml/2008/8/29/191
http://lkml.org/lkml/2008/8/29/343

In an attempt to make the find_busiest_group() function modular and
extensible to more complex load balance decision, I have defined new data
structures and functions and made the find_busiest_group() function small
and readable.

** This is RFC patch, just compiled and booted, may have logic errors

Please let me know if the approach is correct.  I will continue to fix
the logic errors and make it functionally correct.

Thanks,
Vaidy

Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>

After applying the patch series, the function will look like this:

/*
 * find_busiest_group finds and returns the busiest CPU group within the
 * domain. It calculates and returns the amount of weighted load which
 * should be moved to restore balance via the imbalance parameter.
 */
static struct sched_group *
find_busiest_group(struct sched_domain *sd, int this_cpu,
		   unsigned long *imbalance, enum cpu_idle_type idle,
		   int *sd_idle, const cpumask_t *cpus, int *balance)
{
	struct sched_group *group = sd->groups;
	unsigned long max_pull;
	int load_idx;
	struct group_loads gl;
	struct sd_loads sdl;

	memset(&sdl, 0, sizeof(sdl));
	sdl.sd = sd;

	/* Get the load index corresponding to cpu idle state */
	load_idx = get_load_idx(sd, idle);

	do {
		int need_balance;

		need_balance = get_group_loads(group, this_cpu, cpus, idle,
					       load_idx, &gl);

		if (*sd_idle && gl.nr_running)
			*sd_idle = 0;

		if (!need_balance && balance) {
			*balance = 0;
			*imbalance = 0;
			return NULL;
		}

		/* Compare groups and find busiest non-local group */
		update_sd_loads(&sdl, &gl);
		/* Compare groups and find power saving candidates */
		update_powersavings_group_loads(&sdl, &gl, idle);

		group = group->next;
	} while (group != sd->groups);

	if (!sdl.busiest.group ||
	     sdl.local.load >= sdl.max_load ||
	     sdl.busiest.nr_running == 0)
		goto out_balanced;

	sdl.avg_load = (SCHED_LOAD_SCALE * sdl.load) / sdl.cpu_power;

	if (sdl.local.load >= sdl.avg_load ||
			100*sdl.load <= sd->imbalance_pct*sdl.local.load)
		goto out_balanced;

	if (sdl.busiest.group_imbalance)
		sdl.busiest.avg_load_per_task =
			min(sdl.busiest.avg_load_per_task, sdl.avg_load);

	/*
	 * We're trying to get all the cpus to the average_load, so we don't
	 * want to push ourselves above the average load, nor do we wish to
	 * reduce the max loaded cpu below the average load, as either of these
	 * actions would just result in more rebalancing later, and ping-pong
	 * tasks around. Thus we look for the minimum possible imbalance.
	 * Negative imbalances (*we* are more loaded than anyone else) will
	 * be counted as no imbalance for these purposes -- we can't fix that
	 * by pulling tasks to us. Be careful of negative numbers as they'll
	 * appear as very large values with unsigned longs.
	 */
	if (sdl.max_load <= sdl.busiest.avg_load_per_task)
		goto out_balanced;

	/*
	 * In the presence of smp nice balancing, certain scenarios can have
	 * max load less than avg load(as we skip the groups at or below
	 * its cpu_power, while calculating max_load..)
	 * In this condition attempt to adjust the imbalance parameter
	 * in the small_imbalance functions.
	 *
	 * Now if max_load is more than avg load, balancing is needed,
	 * find the exact number of tasks to be moved.
	 */
	if (sdl.max_load >= sdl.avg_load) {

		/* Don't want to pull so many tasks that
		 * a group would go idle
		 */
		max_pull = min(sdl.max_load - sdl.avg_load,
				sdl.max_load - sdl.busiest.avg_load_per_task);

		/* How much load to actually move to equalise the imbalance */
		*imbalance = min(max_pull * sdl.busiest.group->__cpu_power,
				(sdl.avg_load - sdl.local.load) *
				 sdl.local.group->__cpu_power) /
				 SCHED_LOAD_SCALE;

		/* If we have adjusted the required imbalance, then return */
		if (*imbalance >= sdl.busiest.avg_load_per_task)
			return sdl.busiest.group;

	}

	/*
	 * if *imbalance is less than the average load per runnable task
	 * there is no gaurantee that any tasks will be moved so we'll have
	 * a think about bumping its value to force at least one task to be
	 * moved
	 */
	*imbalance = 0;  /* Will be adjusted below */

	if (small_imbalance_one_task(&sdl, imbalance))
		return sdl.busiest.group;

	/* Further look for effective cpu power utilisation */
	small_imbalance_optimize_cpu_power(&sdl, imbalance);

	/*
	 * Un conditional return, we have tries all possible means to adjust
	 * the imbalance for effective task move
	 */
	return sdl.busiest.group;

out_balanced:
	/* Try opportuinity for power save balance */
	return powersavings_balance_group(&sdl, &gl, idle, imbalance);
}


---

Vaidyanathan Srinivasan (5):
      Split find_busiest_group()
      Small imbalance corrections
      Collect statistics required for powersave balance
      Calculate statistics for current load balance domain
      Load calculation for each group


 kernel/sched.c |  612 ++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 370 insertions(+), 242 deletions(-)

-- 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH v1 1/5] Load calculation for each group
  2008-09-24 16:16 [RFC PATCH v1 0/5] Modular find_busiest_group() Vaidyanathan Srinivasan
@ 2008-09-24 16:17 ` Vaidyanathan Srinivasan
  2008-09-24 16:17 ` [RFC PATCH v1 2/5] Calculate statistics for current load balance domain Vaidyanathan Srinivasan
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Vaidyanathan Srinivasan @ 2008-09-24 16:17 UTC (permalink / raw)
  To: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi, Peter Zijlstra
  Cc: Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Gautham R Shenoy, Andi Kleen, David Collier-Brown, Tim Connors,
	Max Krasnyansky, Vaidyanathan Srinivasan

Add data structures for per group stats, and function
to calculate the required stats.

Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
---

 kernel/sched.c |  114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 114 insertions(+), 0 deletions(-)

diff --git a/kernel/sched.c b/kernel/sched.c
index 9889080..abf60ea 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3064,6 +3064,120 @@ static int move_one_task(struct rq *this_rq, int this_cpu, struct rq *busiest,
 	return 0;
 }
 
+/* Helper functions for find_busiest_group */
+
+int get_load_idx(struct sched_domain *sd, enum cpu_idle_type idle)
+{
+	if (idle == CPU_NOT_IDLE)
+		return sd->busy_idx;
+	else if (idle == CPU_NEWLY_IDLE)
+		return sd->newidle_idx;
+	else
+		return sd->idle_idx;
+}
+
+/* Struct to return group stats */
+
+struct group_loads {
+	struct sched_group *group;
+	unsigned long nr_running;
+	unsigned long load;
+	unsigned long load_per_cpu;
+	unsigned long weighted_load;
+	unsigned long avg_load_per_task;
+	unsigned int group_imbalance;
+	int local_group;
+	int balance_cpu;
+};
+
+/* Helper function to calculate basic group level stats */
+
+int get_group_loads(struct sched_group *group, int this_cpu,
+		    const cpumask_t *valid_cpus, enum cpu_idle_type idle,
+		    int load_idx,
+		    struct group_loads *gl)
+{
+	struct rq *rq;
+	unsigned long load, min_load, max_load, avg_load_per_task_per_cpu;
+	int cpu;
+	int local_group = 0;
+	int first_idle_cpu = -1;
+	int need_balance = 1;
+
+	gl->group = group;
+	gl->nr_running = 0;
+	gl->load = 0;
+	gl->weighted_load = 0;
+	gl->avg_load_per_task = 0;
+	gl->group_imbalance = 0;
+	gl->balance_cpu = -1;
+	max_load = 0;
+	min_load = ~0UL;
+
+	gl->local_group = cpu_isset(this_cpu, group->cpumask);
+
+	for_each_cpu_mask_nr(cpu, group->cpumask) {
+		if (!cpu_isset(cpu, *valid_cpus))
+			continue;
+
+		rq = cpu_rq(cpu);
+
+		/* Bias balancing toward cpus of our domain */
+		if (gl->local_group) {
+			if (idle_cpu(cpu) && first_idle_cpu == -1)
+				first_idle_cpu = cpu;
+
+			load = target_load(cpu, load_idx);
+		} else {
+			load = source_load(cpu, load_idx);
+			if (load > max_load)
+				max_load = load;
+			if (load < min_load)
+				min_load = load;
+		}
+		gl->nr_running += rq->nr_running;
+		gl->load += load;
+		gl->weighted_load += weighted_cpuload(cpu);
+		gl->avg_load_per_task += cpu_avg_load_per_task(cpu);
+	}
+
+	/*
+	 * Consider the group unbalanced when the imbalance is larger
+	 * than the average weight of two tasks.
+	 *
+	 * APZ: with cgroup the avg task weight can vary wildly and
+	 *      might not be a suitable number - should we keep a
+	 *      normalized nr_running number somewhere that negates
+	 *      the hierarchy?
+	 */
+
+	avg_load_per_task_per_cpu = sg_div_cpu_power(group,
+				gl->avg_load_per_task * SCHED_LOAD_SCALE);
+
+	if (!gl->local_group &&
+	    ((max_load - min_load) > 2*avg_load_per_task_per_cpu))
+			gl->group_imbalance = 1;
+
+	if (local_group) {
+		if (first_idle_cpu != -1)
+			gl->balance_cpu = first_idle_cpu;
+		else
+			gl->balance_cpu = first_cpu(group->cpumask);
+
+		/*
+		 * First idle cpu or the first cpu(busiest) in this sched group
+		 * is eligible for doing load balancing at this and above
+		 * domains. In the newly idle case, we will allow all the cpu's
+		 * to do the newly idle load balance.
+		 */
+		if (idle != CPU_NEWLY_IDLE && gl->balance_cpu != this_cpu)
+			need_balance = 0;
+	}
+	gl->load_per_cpu = sg_div_cpu_power(group, gl->load * SCHED_LOAD_SCALE);
+
+	return need_balance;
+}
+
 /*
  * find_busiest_group finds and returns the busiest CPU group within the
  * domain. It calculates and returns the amount of weighted load which


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH v1 2/5] Calculate statistics for current load balance domain
  2008-09-24 16:16 [RFC PATCH v1 0/5] Modular find_busiest_group() Vaidyanathan Srinivasan
  2008-09-24 16:17 ` [RFC PATCH v1 1/5] Load calculation for each group Vaidyanathan Srinivasan
@ 2008-09-24 16:17 ` Vaidyanathan Srinivasan
  2008-09-24 16:18 ` [RFC PATCH v1 3/5] Collect statistics required for powersave balance Vaidyanathan Srinivasan
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Vaidyanathan Srinivasan @ 2008-09-24 16:17 UTC (permalink / raw)
  To: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi, Peter Zijlstra
  Cc: Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Gautham R Shenoy, Andi Kleen, David Collier-Brown, Tim Connors,
	Max Krasnyansky, Vaidyanathan Srinivasan

Add data structures and function to collect per sched domain 
statistics required for load balance decision.

Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
---

 kernel/sched.c |   41 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/kernel/sched.c b/kernel/sched.c
index abf60ea..8c394a4 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3178,6 +3178,47 @@ int get_group_loads(struct sched_group *group, int this_cpu,
 	return need_balance;
 }
 
+/* Struct to hold sched domain level loads */
+
+struct sd_loads {
+	struct sched_domain *sd;
+	unsigned long load;
+	unsigned long cpu_power;
+	unsigned long max_load;
+	unsigned long avg_load;
+	struct group_loads local;
+	struct group_loads busiest;
+	/* sched_mc balance groups */
+	struct group_loads min_load_group;
+	struct group_loads power_save_leader_group;
+};
+
+/* Function to aggregate per group loads to sched domain loads */
+
+void update_sd_loads(struct sd_loads *sdl, struct group_loads *gl)
+{
+	int group_capacity = gl->group->__cpu_power / SCHED_LOAD_SCALE;
+
+	if (gl->local_group)
+		sdl->local = *gl;
+	else {
+		sdl->load += gl->load;
+		sdl->cpu_power += gl->group->__cpu_power;
+
+
+		/* Find the busiest */
+		if (gl->load > sdl->load &&
+			(gl->nr_running > group_capacity ||
+			 gl->group_imbalance)) {
+
+			sdl->load = gl->load;
+			sdl->busiest = *gl;
+		}
+	}
+
+
+}
+
 /*
  * find_busiest_group finds and returns the busiest CPU group within the
  * domain. It calculates and returns the amount of weighted load which


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH v1 3/5] Collect statistics required for powersave balance
  2008-09-24 16:16 [RFC PATCH v1 0/5] Modular find_busiest_group() Vaidyanathan Srinivasan
  2008-09-24 16:17 ` [RFC PATCH v1 1/5] Load calculation for each group Vaidyanathan Srinivasan
  2008-09-24 16:17 ` [RFC PATCH v1 2/5] Calculate statistics for current load balance domain Vaidyanathan Srinivasan
@ 2008-09-24 16:18 ` Vaidyanathan Srinivasan
  2008-09-25 12:24   ` Gautham R Shenoy
  2008-09-24 16:18 ` [RFC PATCH v1 4/5] Small imbalance corrections Vaidyanathan Srinivasan
  2008-09-24 16:18 ` [RFC PATCH v1 5/5] Split find_busiest_group() Vaidyanathan Srinivasan
  4 siblings, 1 reply; 7+ messages in thread
From: Vaidyanathan Srinivasan @ 2008-09-24 16:18 UTC (permalink / raw)
  To: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi, Peter Zijlstra
  Cc: Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Gautham R Shenoy, Andi Kleen, David Collier-Brown, Tim Connors,
	Max Krasnyansky, Vaidyanathan Srinivasan

Update sched domain level statistics with the minimum load and
group leader who can pull more tasks. Also suggest a powersave
movement if the domain is otherwise balanced.

Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
---

 kernel/sched.c |   97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 97 insertions(+), 0 deletions(-)

diff --git a/kernel/sched.c b/kernel/sched.c
index 8c394a4..dd87061 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3219,6 +3219,103 @@ void update_sd_loads(struct sd_loads *sdl, struct group_loads *gl)
 
 }
 
+#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
+void update_powersavings_group_loads(struct sd_loads *sdl,
+				     struct group_loads *gl,
+				     enum cpu_idle_type idle)
+{
+	int group_capacity = gl->group->__cpu_power / SCHED_LOAD_SCALE;
+
+	/*
+	 * Busy processors will not participate in power savings
+	 * balance.
+	 */
+	if (idle == CPU_NOT_IDLE ||
+			!(sdl->sd->flags & SD_POWERSAVINGS_BALANCE))
+		return;
+
+	/*
+	 * If the local group is idle or completely loaded
+	 * no need to do power savings balance at this domain
+	 * Same case of non-local groups as well
+	 */
+	if (gl->nr_running >= group_capacity || gl->nr_running == 0)
+		return;
+
+	/*
+	 * Calculate the group which has the least non-idle load.
+	 * This is the group from where we need to pick up the load
+	 * for saving power
+	 */
+	if (!sdl->min_load_group.group)
+		sdl->min_load_group = *gl;
+	else {
+		if (gl->nr_running < sdl->min_load_group.nr_running)
+			sdl->min_load_group = *gl;
+		/* If the loads are equal, then prefer the cpu with
+		 * less logical number
+		 */
+		else if (gl->nr_running == sdl->min_load_group.nr_running &&
+			 first_cpu(gl->group->cpumask) <
+			 first_cpu(sdl->min_load_group.group->cpumask))
+			sdl->min_load_group = *gl;
+	}
+
+	/*
+	 * Calculate the group which is almost near its
+	 * capacity but still has some space to pick up some load
+	 * from other group and save more power
+	 */
+
+	if (gl->nr_running > 0 && gl->nr_running <= group_capacity - 1) {
+		if (!sdl->power_save_leader_group.group)
+			sdl->power_save_leader_group = *gl;
+		else {
+			if (gl->nr_running >
+			    sdl->power_save_leader_group.nr_running)
+				sdl->power_save_leader_group = *gl;
+			else if (gl->nr_running ==
+				 sdl->power_save_leader_group.nr_running &&
+				 first_cpu(gl->group->cpumask) <
+				 first_cpu(sdl->min_load_group.group->cpumask))
+				sdl->power_save_leader_group = *gl;
+		}
+	}
+}
+
+static struct sched_group *powersavings_balance_group(struct sd_loads *sdl,
+	struct group_loads *gl, enum cpu_idle_type idle,
+	unsigned long *imbalance)
+{
+	*imbalance = 0;
+	if (idle == CPU_NOT_IDLE || !(sdl->sd->flags & SD_POWERSAVINGS_BALANCE))
+		return NULL;
+
+	if (sdl->local.group == sdl->power_save_leader_group.group &&
+		sdl->power_save_leader_group.group !=
+		sdl->min_load_group.group) {
+		*imbalance = sdl->min_load_group.avg_load_per_task;
+		return sdl->min_load_group.group;
+	}
+
+	return NULL;
+}
+#else
+void update_powersavings_group_loads(struct sd_loads *sdl,
+			struct group_loads *gl, enum cpu_idle_type idle)
+{
+	return;
+}
+
+static struct sched_group *powersavings_balance_group(struct sd_loads *sdl,
+	struct group_loads *gl, enum cpu_idle_type idle,
+	unsigned long *imbalance)
+{
+	*imbalance = 0;
+	return NULL;
+}
+#endif
+
 /*
  * find_busiest_group finds and returns the busiest CPU group within the
  * domain. It calculates and returns the amount of weighted load which


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH v1 4/5] Small imbalance corrections
  2008-09-24 16:16 [RFC PATCH v1 0/5] Modular find_busiest_group() Vaidyanathan Srinivasan
                   ` (2 preceding siblings ...)
  2008-09-24 16:18 ` [RFC PATCH v1 3/5] Collect statistics required for powersave balance Vaidyanathan Srinivasan
@ 2008-09-24 16:18 ` Vaidyanathan Srinivasan
  2008-09-24 16:18 ` [RFC PATCH v1 5/5] Split find_busiest_group() Vaidyanathan Srinivasan
  4 siblings, 0 replies; 7+ messages in thread
From: Vaidyanathan Srinivasan @ 2008-09-24 16:18 UTC (permalink / raw)
  To: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi, Peter Zijlstra
  Cc: Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Gautham R Shenoy, Andi Kleen, David Collier-Brown, Tim Connors,
	Max Krasnyansky, Vaidyanathan Srinivasan

Add functions to bump up the imbalance to eventually initiate 
a task move to balance across groups.

Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
---

 kernel/sched.c |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 72 insertions(+), 0 deletions(-)

diff --git a/kernel/sched.c b/kernel/sched.c
index dd87061..1f38e2c 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3219,6 +3219,78 @@ void update_sd_loads(struct sd_loads *sdl, struct group_loads *gl)
 
 }
 
+/* Bump up imbalance to one task so that some task movement can happen */
+
+int small_imbalance_one_task(struct sd_loads *sdl, unsigned long *imbalance)
+{
+	unsigned int imbn;
+	imbn = 2;
+	if (sdl->local.nr_running) {
+		if (sdl->busiest.avg_load_per_task >
+			sdl->local.avg_load_per_task)
+			imbn = 1;
+	}
+
+	if (sdl->max_load - sdl->local.load +
+		2*sdl->busiest.avg_load_per_task >=
+				sdl->busiest.avg_load_per_task * imbn) {
+		*imbalance = sdl->busiest.avg_load_per_task;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Adjust imbalance to move task if the result of the move will
+ * yield better use of cpu power
+ */
+
+void small_imbalance_optimize_cpu_power(struct sd_loads *sdl,
+					unsigned long *imbalance)
+{
+	unsigned long tmp, pwr_now, pwr_move;
+	pwr_move = pwr_now = 0;
+
+	/*
+	 * OK, we don't have enough imbalance to justify moving tasks,
+	 * however we may be able to increase total CPU power used by
+	 * moving them.
+	 */
+
+	pwr_now += sdl->busiest.group->__cpu_power *
+			min(sdl->busiest.avg_load_per_task, sdl->max_load);
+	pwr_now += sdl->local.group->__cpu_power *
+			min(sdl->local.avg_load_per_task, sdl->local.load);
+	pwr_now /= SCHED_LOAD_SCALE;
+
+	/* Amount of load we'd subtract */
+	tmp = sg_div_cpu_power(sdl->busiest.group,
+			sdl->busiest.avg_load_per_task * SCHED_LOAD_SCALE);
+	if (sdl->max_load > tmp)
+		pwr_move += sdl->busiest.group->__cpu_power *
+			min(sdl->busiest.avg_load_per_task,
+			    sdl->max_load - tmp);
+
+	/* Amount of load we'd add */
+	if (sdl->max_load * sdl->busiest.group->__cpu_power <
+			sdl->busiest.avg_load_per_task * SCHED_LOAD_SCALE)
+		tmp = sg_div_cpu_power(sdl->local.group,
+				sdl->max_load *
+				sdl->busiest.group->__cpu_power);
+	else
+		tmp = sg_div_cpu_power(sdl->local.group,
+			sdl->busiest.avg_load_per_task * SCHED_LOAD_SCALE);
+	pwr_move += sdl->local.group->__cpu_power *
+			min(sdl->local.avg_load_per_task,
+			sdl->local.load + tmp);
+	pwr_move /= SCHED_LOAD_SCALE;
+
+	/* Move if we gain throughput */
+	if (pwr_move > pwr_now)
+		*imbalance = sdl->busiest.avg_load_per_task;
+
+}
+
 #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
 void update_powersavings_group_loads(struct sd_loads *sdl,
 				     struct group_loads *gl,


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH v1 5/5] Split find_busiest_group()
  2008-09-24 16:16 [RFC PATCH v1 0/5] Modular find_busiest_group() Vaidyanathan Srinivasan
                   ` (3 preceding siblings ...)
  2008-09-24 16:18 ` [RFC PATCH v1 4/5] Small imbalance corrections Vaidyanathan Srinivasan
@ 2008-09-24 16:18 ` Vaidyanathan Srinivasan
  4 siblings, 0 replies; 7+ messages in thread
From: Vaidyanathan Srinivasan @ 2008-09-24 16:18 UTC (permalink / raw)
  To: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi, Peter Zijlstra
  Cc: Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Gautham R Shenoy, Andi Kleen, David Collier-Brown, Tim Connors,
	Max Krasnyansky, Vaidyanathan Srinivasan

Make use of the previously defined helper functions and data
structures and split the find_busiest_group() function into
smaller modular functions.

Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
---

 kernel/sched.c |  324 +++++++++++---------------------------------------------
 1 files changed, 64 insertions(+), 260 deletions(-)

diff --git a/kernel/sched.c b/kernel/sched.c
index 1f38e2c..1cd2e60 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3387,7 +3387,6 @@ static struct sched_group *powersavings_balance_group(struct sd_loads *sdl,
 	return NULL;
 }
 #endif
-
 /*
  * find_busiest_group finds and returns the busiest CPU group within the
  * domain. It calculates and returns the amount of weighted load which
@@ -3398,208 +3397,55 @@ find_busiest_group(struct sched_domain *sd, int this_cpu,
 		   unsigned long *imbalance, enum cpu_idle_type idle,
 		   int *sd_idle, const cpumask_t *cpus, int *balance)
 {
-	struct sched_group *busiest = NULL, *this = NULL, *group = sd->groups;
-	unsigned long max_load, avg_load, total_load, this_load, total_pwr;
+	struct sched_group *group = sd->groups;
 	unsigned long max_pull;
-	unsigned long busiest_load_per_task, busiest_nr_running;
-	unsigned long this_load_per_task, this_nr_running;
-	int load_idx, group_imb = 0;
-#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
-	int power_savings_balance = 1;
-	unsigned long leader_nr_running = 0, min_load_per_task = 0;
-	unsigned long min_nr_running = ULONG_MAX;
-	struct sched_group *group_min = NULL, *group_leader = NULL;
-#endif
+	int load_idx;
+	struct group_loads gl;
+	struct sd_loads sdl;
 
-	max_load = this_load = total_load = total_pwr = 0;
-	busiest_load_per_task = busiest_nr_running = 0;
-	this_load_per_task = this_nr_running = 0;
+	memset(&sdl, 0, sizeof(sdl));
+	sdl.sd = sd;
 
-	if (idle == CPU_NOT_IDLE)
-		load_idx = sd->busy_idx;
-	else if (idle == CPU_NEWLY_IDLE)
-		load_idx = sd->newidle_idx;
-	else
-		load_idx = sd->idle_idx;
+	/* Get the load index corresponding to cpu idle state */
+	load_idx = get_load_idx(sd, idle);
 
 	do {
-		unsigned long load, group_capacity, max_cpu_load, min_cpu_load;
-		int local_group;
-		int i;
-		int __group_imb = 0;
-		unsigned int balance_cpu = -1, first_idle_cpu = 0;
-		unsigned long sum_nr_running, sum_weighted_load;
-		unsigned long sum_avg_load_per_task;
-		unsigned long avg_load_per_task;
-
-		local_group = cpu_isset(this_cpu, group->cpumask);
-
-		if (local_group)
-			balance_cpu = first_cpu(group->cpumask);
+		int need_balance;
 
-		/* Tally up the load of all CPUs in the group */
-		sum_weighted_load = sum_nr_running = avg_load = 0;
-		sum_avg_load_per_task = avg_load_per_task = 0;
-
-		max_cpu_load = 0;
-		min_cpu_load = ~0UL;
-
-		for_each_cpu_mask_nr(i, group->cpumask) {
-			struct rq *rq;
-
-			if (!cpu_isset(i, *cpus))
-				continue;
+		need_balance = get_group_loads(group, this_cpu, cpus, idle,
+					       load_idx, &gl);
 
-			rq = cpu_rq(i);
+		if (*sd_idle && gl.nr_running)
+			*sd_idle = 0;
 
-			if (*sd_idle && rq->nr_running)
-				*sd_idle = 0;
-
-			/* Bias balancing toward cpus of our domain */
-			if (local_group) {
-				if (idle_cpu(i) && !first_idle_cpu) {
-					first_idle_cpu = 1;
-					balance_cpu = i;
-				}
-
-				load = target_load(i, load_idx);
-			} else {
-				load = source_load(i, load_idx);
-				if (load > max_cpu_load)
-					max_cpu_load = load;
-				if (min_cpu_load > load)
-					min_cpu_load = load;
-			}
-
-			avg_load += load;
-			sum_nr_running += rq->nr_running;
-			sum_weighted_load += weighted_cpuload(i);
-
-			sum_avg_load_per_task += cpu_avg_load_per_task(i);
-		}
-
-		/*
-		 * First idle cpu or the first cpu(busiest) in this sched group
-		 * is eligible for doing load balancing at this and above
-		 * domains. In the newly idle case, we will allow all the cpu's
-		 * to do the newly idle load balance.
-		 */
-		if (idle != CPU_NEWLY_IDLE && local_group &&
-		    balance_cpu != this_cpu && balance) {
+		if (!need_balance && balance) {
 			*balance = 0;
-			goto ret;
+			*imbalance = 0;
+			return NULL;
 		}
 
-		total_load += avg_load;
-		total_pwr += group->__cpu_power;
+		/* Compare groups and find busiest non-local group */
+		update_sd_loads(&sdl, &gl);
+		/* Compare groups and find power saving candidates */
+		update_powersavings_group_loads(&sdl, &gl, idle);
 
-		/* Adjust by relative CPU power of the group */
-		avg_load = sg_div_cpu_power(group,
-				avg_load * SCHED_LOAD_SCALE);
-
-
-		/*
-		 * Consider the group unbalanced when the imbalance is larger
-		 * than the average weight of two tasks.
-		 *
-		 * APZ: with cgroup the avg task weight can vary wildly and
-		 *      might not be a suitable number - should we keep a
-		 *      normalized nr_running number somewhere that negates
-		 *      the hierarchy?
-		 */
-		avg_load_per_task = sg_div_cpu_power(group,
-				sum_avg_load_per_task * SCHED_LOAD_SCALE);
-
-		if ((max_cpu_load - min_cpu_load) > 2*avg_load_per_task)
-			__group_imb = 1;
-
-		group_capacity = group->__cpu_power / SCHED_LOAD_SCALE;
-
-		if (local_group) {
-			this_load = avg_load;
-			this = group;
-			this_nr_running = sum_nr_running;
-			this_load_per_task = sum_weighted_load;
-		} else if (avg_load > max_load &&
-			   (sum_nr_running > group_capacity || __group_imb)) {
-			max_load = avg_load;
-			busiest = group;
-			busiest_nr_running = sum_nr_running;
-			busiest_load_per_task = sum_weighted_load;
-			group_imb = __group_imb;
-		}
-
-#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
-		/*
-		 * Busy processors will not participate in power savings
-		 * balance.
-		 */
-		if (idle == CPU_NOT_IDLE ||
-				!(sd->flags & SD_POWERSAVINGS_BALANCE))
-			goto group_next;
-
-		/*
-		 * If the local group is idle or completely loaded
-		 * no need to do power savings balance at this domain
-		 */
-		if (local_group && (this_nr_running >= group_capacity ||
-				    !this_nr_running))
-			power_savings_balance = 0;
-
-		/*
-		 * If a group is already running at full capacity or idle,
-		 * don't include that group in power savings calculations
-		 */
-		if (!power_savings_balance || sum_nr_running >= group_capacity
-		    || !sum_nr_running)
-			goto group_next;
-
-		/*
-		 * Calculate the group which has the least non-idle load.
-		 * This is the group from where we need to pick up the load
-		 * for saving power
-		 */
-		if ((sum_nr_running < min_nr_running) ||
-		    (sum_nr_running == min_nr_running &&
-		     first_cpu(group->cpumask) <
-		     first_cpu(group_min->cpumask))) {
-			group_min = group;
-			min_nr_running = sum_nr_running;
-			min_load_per_task = sum_weighted_load /
-						sum_nr_running;
-		}
-
-		/*
-		 * Calculate the group which is almost near its
-		 * capacity but still has some space to pick up some load
-		 * from other group and save more power
-		 */
-		if (sum_nr_running <= group_capacity - 1) {
-			if (sum_nr_running > leader_nr_running ||
-			    (sum_nr_running == leader_nr_running &&
-			     first_cpu(group->cpumask) >
-			      first_cpu(group_leader->cpumask))) {
-				group_leader = group;
-				leader_nr_running = sum_nr_running;
-			}
-		}
-group_next:
-#endif
 		group = group->next;
 	} while (group != sd->groups);
 
-	if (!busiest || this_load >= max_load || busiest_nr_running == 0)
+	if (!sdl.busiest.group ||
+	     sdl.local.load >= sdl.max_load ||
+	     sdl.busiest.nr_running == 0)
 		goto out_balanced;
 
-	avg_load = (SCHED_LOAD_SCALE * total_load) / total_pwr;
+	sdl.avg_load = (SCHED_LOAD_SCALE * sdl.load) / sdl.cpu_power;
 
-	if (this_load >= avg_load ||
-			100*max_load <= sd->imbalance_pct*this_load)
+	if (sdl.local.load >= sdl.avg_load ||
+			100*sdl.load <= sd->imbalance_pct*sdl.local.load)
 		goto out_balanced;
 
-	busiest_load_per_task /= busiest_nr_running;
-	if (group_imb)
-		busiest_load_per_task = min(busiest_load_per_task, avg_load);
+	if (sdl.busiest.group_imbalance)
+		sdl.busiest.avg_load_per_task =
+			min(sdl.busiest.avg_load_per_task, sdl.avg_load);
 
 	/*
 	 * We're trying to get all the cpus to the average_load, so we don't
@@ -3612,26 +3458,38 @@ group_next:
 	 * by pulling tasks to us. Be careful of negative numbers as they'll
 	 * appear as very large values with unsigned longs.
 	 */
-	if (max_load <= busiest_load_per_task)
+	if (sdl.max_load <= sdl.busiest.avg_load_per_task)
 		goto out_balanced;
 
 	/*
 	 * In the presence of smp nice balancing, certain scenarios can have
 	 * max load less than avg load(as we skip the groups at or below
 	 * its cpu_power, while calculating max_load..)
+	 * In this condition attempt to adjust the imbalance parameter
+	 * in the small_imbalance functions.
+	 *
+	 * Now if max_load is more than avg load, balancing is needed,
+	 * find the exact number of tasks to be moved.
 	 */
-	if (max_load < avg_load) {
-		*imbalance = 0;
-		goto small_imbalance;
-	}
+	if (sdl.max_load >= sdl.avg_load) {
+
+		/* Don't want to pull so many tasks that
+		 * a group would go idle
+		 */
+		max_pull = min(sdl.max_load - sdl.avg_load,
+				sdl.max_load - sdl.busiest.avg_load_per_task);
+
+		/* How much load to actually move to equalise the imbalance */
+		*imbalance = min(max_pull * sdl.busiest.group->__cpu_power,
+				(sdl.avg_load - sdl.local.load) *
+				 sdl.local.group->__cpu_power) /
+				 SCHED_LOAD_SCALE;
 
-	/* Don't want to pull so many tasks that a group would go idle */
-	max_pull = min(max_load - avg_load, max_load - busiest_load_per_task);
+		/* If we have adjusted the required imbalance, then return */
+		if (*imbalance >= sdl.busiest.avg_load_per_task)
+			return sdl.busiest.group;
 
-	/* How much load to actually move to equalise the imbalance */
-	*imbalance = min(max_pull * busiest->__cpu_power,
-				(avg_load - this_load) * this->__cpu_power)
-			/ SCHED_LOAD_SCALE;
+	}
 
 	/*
 	 * if *imbalance is less than the average load per runnable task
@@ -3639,77 +3497,23 @@ group_next:
 	 * a think about bumping its value to force at least one task to be
 	 * moved
 	 */
-	if (*imbalance < busiest_load_per_task) {
-		unsigned long tmp, pwr_now, pwr_move;
-		unsigned int imbn;
-
-small_imbalance:
-		pwr_move = pwr_now = 0;
-		imbn = 2;
-		if (this_nr_running) {
-			this_load_per_task /= this_nr_running;
-			if (busiest_load_per_task > this_load_per_task)
-				imbn = 1;
-		} else
-			this_load_per_task = cpu_avg_load_per_task(this_cpu);
-
-		if (max_load - this_load + 2*busiest_load_per_task >=
-					busiest_load_per_task * imbn) {
-			*imbalance = busiest_load_per_task;
-			return busiest;
-		}
+	*imbalance = 0;  /* Will be adjusted below */
 
-		/*
-		 * OK, we don't have enough imbalance to justify moving tasks,
-		 * however we may be able to increase total CPU power used by
-		 * moving them.
-		 */
+	if (small_imbalance_one_task(&sdl, imbalance))
+		return sdl.busiest.group;
 
-		pwr_now += busiest->__cpu_power *
-				min(busiest_load_per_task, max_load);
-		pwr_now += this->__cpu_power *
-				min(this_load_per_task, this_load);
-		pwr_now /= SCHED_LOAD_SCALE;
-
-		/* Amount of load we'd subtract */
-		tmp = sg_div_cpu_power(busiest,
-				busiest_load_per_task * SCHED_LOAD_SCALE);
-		if (max_load > tmp)
-			pwr_move += busiest->__cpu_power *
-				min(busiest_load_per_task, max_load - tmp);
-
-		/* Amount of load we'd add */
-		if (max_load * busiest->__cpu_power <
-				busiest_load_per_task * SCHED_LOAD_SCALE)
-			tmp = sg_div_cpu_power(this,
-					max_load * busiest->__cpu_power);
-		else
-			tmp = sg_div_cpu_power(this,
-				busiest_load_per_task * SCHED_LOAD_SCALE);
-		pwr_move += this->__cpu_power *
-				min(this_load_per_task, this_load + tmp);
-		pwr_move /= SCHED_LOAD_SCALE;
+	/* Further look for effective cpu power utilisation */
+	small_imbalance_optimize_cpu_power(&sdl, imbalance);
 
-		/* Move if we gain throughput */
-		if (pwr_move > pwr_now)
-			*imbalance = busiest_load_per_task;
-	}
-
-	return busiest;
+	/*
+	 * Un conditional return, we have tries all possible means to adjust
+	 * the imbalance for effective task move
+	 */
+	return sdl.busiest.group;
 
 out_balanced:
-#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
-	if (idle == CPU_NOT_IDLE || !(sd->flags & SD_POWERSAVINGS_BALANCE))
-		goto ret;
-
-	if (this == group_leader && group_leader != group_min) {
-		*imbalance = min_load_per_task;
-		return group_min;
-	}
-#endif
-ret:
-	*imbalance = 0;
-	return NULL;
+	/* Try opportuinity for power save balance */
+	return powersavings_balance_group(&sdl, &gl, idle, imbalance);
 }
 
 /*


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH v1 3/5] Collect statistics required for powersave balance
  2008-09-24 16:18 ` [RFC PATCH v1 3/5] Collect statistics required for powersave balance Vaidyanathan Srinivasan
@ 2008-09-25 12:24   ` Gautham R Shenoy
  0 siblings, 0 replies; 7+ messages in thread
From: Gautham R Shenoy @ 2008-09-25 12:24 UTC (permalink / raw)
  To: Vaidyanathan Srinivasan
  Cc: Linux Kernel, Suresh B Siddha, Venkatesh Pallipadi,
	Peter Zijlstra, Ingo Molnar, Dipankar Sarma, Balbir Singh, Vatsa,
	Andi Kleen, David Collier-Brown, Tim Connors, Max Krasnyansky

On Wed, Sep 24, 2008 at 09:48:09PM +0530, Vaidyanathan Srinivasan wrote:
> Update sched domain level statistics with the minimum load and
> group leader who can pull more tasks. Also suggest a powersave
> movement if the domain is otherwise balanced.
> 
> Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
> ---
> 
>  kernel/sched.c |   97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 97 insertions(+), 0 deletions(-)
> 
> diff --git a/kernel/sched.c b/kernel/sched.c
> index 8c394a4..dd87061 100644
> --- a/kernel/sched.c
> +++ b/kernel/sched.c
> @@ -3219,6 +3219,103 @@ void update_sd_loads(struct sd_loads *sdl, struct group_loads *gl)
> 
>  }
> 
> +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
> +void update_powersavings_group_loads(struct sd_loads *sdl,
> +				     struct group_loads *gl,
> +				     enum cpu_idle_type idle)
> +{
> +	int group_capacity = gl->group->__cpu_power / SCHED_LOAD_SCALE;
> +
> +	/*
> +	 * Busy processors will not participate in power savings
> +	 * balance.
> +	 */
> +	if (idle == CPU_NOT_IDLE ||
> +			!(sdl->sd->flags & SD_POWERSAVINGS_BALANCE))
> +		return;
> +
> +	/*
> +	 * If the local group is idle or completely loaded
> +	 * no need to do power savings balance at this domain
> +	 * Same case of non-local groups as well
> +	 */
	Are there groups other than local and non-local ?
	If not, this comment can be condensed so that it can be applied
	to all groups :-)

> +	if (gl->nr_running >= group_capacity || gl->nr_running == 0)
> +		return;
> +
> +	/*
> +	 * Calculate the group which has the least non-idle load.
> +	 * This is the group from where we need to pick up the load
> +	 * for saving power
> +	 */
> +	if (!sdl->min_load_group.group)
> +		sdl->min_load_group = *gl;
> +	else {
> +		if (gl->nr_running < sdl->min_load_group.nr_running)
> +			sdl->min_load_group = *gl;
> +		/* If the loads are equal, then prefer the cpu with
> +		 * less logical number
> +		 */
> +		else if (gl->nr_running == sdl->min_load_group.nr_running &&
> +			 first_cpu(gl->group->cpumask) <
> +			 first_cpu(sdl->min_load_group.group->cpumask))
> +			sdl->min_load_group = *gl;
> +	}
> +
> +	/*
> +	 * Calculate the group which is almost near its
> +	 * capacity but still has some space to pick up some load
> +	 * from other group and save more power
> +	 */
> +
> +	if (gl->nr_running > 0 && gl->nr_running <= group_capacity - 1) {
> +		if (!sdl->power_save_leader_group.group)
> +			sdl->power_save_leader_group = *gl;
> +		else {
> +			if (gl->nr_running >
> +			    sdl->power_save_leader_group.nr_running)
> +				sdl->power_save_leader_group = *gl;
> +			else if (gl->nr_running ==
> +				 sdl->power_save_leader_group.nr_running &&
> +				 first_cpu(gl->group->cpumask) <
> +				 first_cpu(sdl->min_load_group.group->cpumask))
> +				sdl->power_save_leader_group = *gl;
> +		}
> +	}
> +}
> +
> +static struct sched_group *powersavings_balance_group(struct sd_loads *sdl,
> +	struct group_loads *gl, enum cpu_idle_type idle,
> +	unsigned long *imbalance)
> +{
> +	*imbalance = 0;
> +	if (idle == CPU_NOT_IDLE || !(sdl->sd->flags & SD_POWERSAVINGS_BALANCE))
> +		return NULL;
> +
> +	if (sdl->local.group == sdl->power_save_leader_group.group &&
> +		sdl->power_save_leader_group.group !=
> +		sdl->min_load_group.group) {
> +		*imbalance = sdl->min_load_group.avg_load_per_task;
> +		return sdl->min_load_group.group;
> +	}
> +
> +	return NULL;
> +}
> +#else
> +void update_powersavings_group_loads(struct sd_loads *sdl,
> +			struct group_loads *gl, enum cpu_idle_type idle)
> +{
> +	return;
> +}
> +
> +static struct sched_group *powersavings_balance_group(struct sd_loads *sdl,
> +	struct group_loads *gl, enum cpu_idle_type idle,
> +	unsigned long *imbalance)
> +{
> +	*imbalance = 0;
> +	return NULL;
> +}
> +#endif
> +
>  /*
>   * find_busiest_group finds and returns the busiest CPU group within the
>   * domain. It calculates and returns the amount of weighted load which

-- 
Thanks and Regards
gautham

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2008-09-25 12:26 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-09-24 16:16 [RFC PATCH v1 0/5] Modular find_busiest_group() Vaidyanathan Srinivasan
2008-09-24 16:17 ` [RFC PATCH v1 1/5] Load calculation for each group Vaidyanathan Srinivasan
2008-09-24 16:17 ` [RFC PATCH v1 2/5] Calculate statistics for current load balance domain Vaidyanathan Srinivasan
2008-09-24 16:18 ` [RFC PATCH v1 3/5] Collect statistics required for powersave balance Vaidyanathan Srinivasan
2008-09-25 12:24   ` Gautham R Shenoy
2008-09-24 16:18 ` [RFC PATCH v1 4/5] Small imbalance corrections Vaidyanathan Srinivasan
2008-09-24 16:18 ` [RFC PATCH v1 5/5] Split find_busiest_group() Vaidyanathan Srinivasan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®