Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cec55a2 authored by Joonwoo Park's avatar Joonwoo Park
Browse files

sched: Prevent race conditions where upmigrate_min_nice changes



When upmigrate_min_nice is changed dec_nr_big_small_task() can trigger
BUG_ON(rq->nr_big_tasks < 0).  This happens when there is a task which was
considered as non-big task due to its nice > upmigrate_min_nice and later
upmigrate_min_nice is changed to higher value so the task becomes big task.
In this case runqueue still has nr_big_tasks = 0 incorrectly with current
implementation.  Consequently next scheduler tick sees a big task to
schedule and try to decrease nr_big_tasks which is already 0.

Introduce sched_upmigrate_min_nice which is updated atomically and re-count
the number of big and small tasks to fix BUG_ON() triggering.

Change-Id: I6f5fc62ed22bbe5c52ec71613082a6e64f406e58
Signed-off-by: default avatarJoonwoo Park <joonwoop@codeaurora.org>
parent f6e9e18e
Loading
Loading
Loading
Loading
+37 −15
Original line number Diff line number Diff line
@@ -1324,6 +1324,7 @@ unsigned int __read_mostly sysctl_sched_downmigrate_pct = 60;
 * Tasks whose nice value is > sysctl_sched_upmigrate_min_nice are never
 * considered as "big" tasks.
 */
static int __read_mostly sched_upmigrate_min_nice = 15;
int __read_mostly sysctl_sched_upmigrate_min_nice = 15;

/*
@@ -1374,6 +1375,8 @@ void set_hmp_defaults(void)
	sched_init_task_load_windows =
		div64_u64((u64)sysctl_sched_init_task_load_pct *
			  (u64)sched_ravg_window, 100);

	sched_upmigrate_min_nice = sysctl_sched_upmigrate_min_nice;
}

u32 sched_get_init_task_load(struct task_struct *p)
@@ -1470,7 +1473,7 @@ static inline int is_big_task(struct task_struct *p)
	int nice = TASK_NICE(p);

	/* Todo: Provide cgroup-based control as well? */
	if (nice > sysctl_sched_upmigrate_min_nice)
	if (nice > sched_upmigrate_min_nice)
		return 0;

	load = scale_load_to_cpu(load, task_cpu(p));
@@ -1658,7 +1661,7 @@ static int task_will_fit(struct task_struct *p, int cpu)
			return 1;
	} else {
		/* Todo: Provide cgroup-based control as well? */
		if (nice > sysctl_sched_upmigrate_min_nice)
		if (nice > sched_upmigrate_min_nice)
			return 1;

		load = scale_load_to_cpu(task_load(p), cpu);
@@ -2197,11 +2200,16 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
	int ret;
	unsigned int *data = (unsigned int *)table->data;
	unsigned int old_val = *data;
	int update_min_nice = 0;

	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);

	if (ret || !write || !sched_enable_hmp)
		return ret;

	if (write && (old_val == *data))
		return 0;

	if (data == &sysctl_sched_min_runtime) {
		sched_min_runtime = ((u64) sysctl_sched_min_runtime) * 1000;
		return 0;
@@ -2212,6 +2220,23 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
		return -EINVAL;
	}

	if (data == (unsigned int *)&sysctl_sched_upmigrate_min_nice)
		update_min_nice = 1;

	if (update_min_nice) {
		if ((*(int *)data) < -20 || (*(int *)data) > 19) {
			*data = old_val;
			return -EINVAL;
		}
	} else {
		/* all tunables other than min_nice are in percentage */
		if (sysctl_sched_downmigrate_pct >
		    sysctl_sched_upmigrate_pct || *data > 100) {
			*data = old_val;
			return -EINVAL;
		}
	}

	/*
	 * Big/Small task tunable change will need to re-classify tasks on
	 * runqueue as big and small and set their counters appropriately.
@@ -2220,18 +2245,16 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
	 * includes taking runqueue lock of all online cpus and re-initiatizing
	 * their big/small counter values based on changed criteria.
	 */
	if ((*data != old_val) &&
		(data == &sysctl_sched_upmigrate_pct ||
		data == &sysctl_sched_small_task_pct)) {
	if ((data == &sysctl_sched_upmigrate_pct ||
	     data == &sysctl_sched_small_task_pct || update_min_nice)) {
		get_online_cpus();
		pre_big_small_task_count_change(cpu_online_mask);
	}

	set_hmp_defaults();

	if ((*data != old_val) &&
		(data == &sysctl_sched_upmigrate_pct ||
		data == &sysctl_sched_small_task_pct)) {
	if ((data == &sysctl_sched_upmigrate_pct ||
	     data == &sysctl_sched_small_task_pct || update_min_nice)) {
		post_big_small_task_count_change(cpu_online_mask);
		put_online_cpus();
	}
@@ -2346,8 +2369,7 @@ static inline int migration_needed(struct rq *rq, struct task_struct *p)
		return 0;

	/* Todo: cgroup-based control? */
	if (nice > sysctl_sched_upmigrate_min_nice &&
		rq->capacity > min_capacity)
	if (nice > sched_upmigrate_min_nice && rq->capacity > min_capacity)
		return MOVE_TO_LITTLE_CPU;

	if (!task_will_fit(p, cpu_of(rq)))
+1 −1
Original line number Diff line number Diff line
@@ -414,7 +414,7 @@ static struct ctl_table kern_table[] = {
		.data		= &sysctl_sched_upmigrate_min_nice,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec,
		.proc_handler	= sched_hmp_proc_update_handler,
	},
	{
		.procname	= "sched_prefer_idle",