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

Commit 079cb99c authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "cpufreq: interactive: Use load prediction provided by scheduler"

parents 1302ef8b e36e8fc7
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -312,9 +312,9 @@ information such as migration. If non-zero, this also implies governor
sampling windows are aligned across CPUs, with same timer_rate,
regardless what align_windows is set to. Default is zero.

use_migration_notif: If non-zero, reevaluate CPU's frequency
immediately after receiving notification from scheduler. If zero,
ignore scheduler notification. Default is zero.
use_migration_notif: If non-zero, schedule hrtimer to fire in 1ms
to reevaluate frequency of notified CPU, unless the hrtimer is already
pending. If zero, ignore scheduler notification. Default is zero.

max_freq_hysteresis: Each time freq evaluation chooses policy->max,
next max_freq_hysteresis is considered as hysteresis period. During
@@ -337,6 +337,16 @@ evaluation triggered by timer, min_sample_time is still always
enforced. fast_ramp_down has no effect if use_migration_notif is
set to zero. Default is zero.

enable_prediction: If non-zero, two frequencies will be calculated
during each sampling period: one based on busy time in previous sampling
period (f_prev), and the other based on prediction provided by scheduler
(f_pred). Max of both will be selected as final frequency. Hispeed
related logic, including both frequency selection and delay is ignored
if enable_prediction is set. If only f_pred but not f_prev picked
policy->max, max_freq_hysteresis period is not started/extended.
use_sched_load must be turned on before enabling this feature.
Default is zero.

3. The Governor Interface in the CPUfreq Core
=============================================

+127 −64
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/tick.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/hrtimer.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/slab.h>
@@ -37,6 +38,7 @@
struct cpufreq_interactive_policyinfo {
	struct timer_list policy_timer;
	struct timer_list policy_slack_timer;
	struct hrtimer notif_timer;
	spinlock_t load_lock; /* protects load tracking stat */
	u64 last_evaluated_jiffy;
	struct cpufreq_policy *policy;
@@ -52,6 +54,7 @@ struct cpufreq_interactive_policyinfo {
	struct rw_semaphore enable_sem;
	bool reject_notification;
	bool notif_pending;
	unsigned long notif_cpu;
	int governor_enabled;
	struct cpufreq_interactive_tunables *cached_tunables;
	struct sched_load *sl;
@@ -154,6 +157,9 @@ struct cpufreq_interactive_tunables {

	/* Ignore min_sample_time for notification */
	bool fast_ramp_down;

	/* Whether to enable prediction or not */
	bool enable_prediction;
};

/* For cases where we have single governor instance for system */
@@ -433,28 +439,43 @@ static u64 update_load(int cpu)
	return now;
}

static unsigned int sl_busy_to_laf(struct cpufreq_interactive_policyinfo *ppol,
				   unsigned long busy)
{
	struct cpufreq_interactive_tunables *tunables =
		ppol->policy->governor_data;

	busy *= ppol->policy->cpuinfo.max_freq;
	return div64_s64(busy, tunables->timer_rate) * 100;
}

#define NEW_TASK_RATIO 75
#define PRED_TOLERANCE_PCT 10
static void cpufreq_interactive_timer(unsigned long data)
{
	u64 now;
	s64 now;
	unsigned int delta_time;
	u64 cputime_speedadj;
	int cpu_load;
	int pol_load = 0;
	struct cpufreq_interactive_policyinfo *ppol = per_cpu(polinfo, data);
	struct cpufreq_interactive_tunables *tunables =
		ppol->policy->governor_data;
	struct sched_load *sl = ppol->sl;
	struct cpufreq_interactive_cpuinfo *pcpu;
	unsigned int new_freq;
	unsigned int loadadjfreq = 0, tmploadadjfreq;
	unsigned int prev_laf = 0, t_prevlaf;
	unsigned int pred_laf = 0, t_predlaf = 0;
	unsigned int prev_chfreq, pred_chfreq, chosen_freq;
	unsigned int index;
	unsigned long flags;
	unsigned long max_cpu;
	int i, fcpu;
	struct sched_load *sl;
	int cpu, i;
	int new_load_pct = 0;
	int prev_l, pred_l = 0;
	struct cpufreq_govinfo govinfo;
	bool skip_hispeed_logic, skip_min_sample_time;
	bool policy_max_fast_restore = false;
	bool jump_to_max_no_ts = false;
	bool jump_to_max = false;

	if (!down_read_trylock(&ppol->enable_sem))
@@ -462,89 +483,106 @@ static void cpufreq_interactive_timer(unsigned long data)
	if (!ppol->governor_enabled)
		goto exit;

	fcpu = cpumask_first(ppol->policy->related_cpus);
	now = ktime_to_us(ktime_get());

	spin_lock_irqsave(&ppol->target_freq_lock, flags);
	spin_lock(&ppol->load_lock);

	skip_hispeed_logic = tunables->ignore_hispeed_on_notif &&
						ppol->notif_pending;
	skip_hispeed_logic = tunables->enable_prediction ? true :
		tunables->ignore_hispeed_on_notif && ppol->notif_pending;
	skip_min_sample_time = tunables->fast_ramp_down && ppol->notif_pending;
	ppol->notif_pending = false;
	now = ktime_to_us(ktime_get());
	ppol->last_evaluated_jiffy = get_jiffies_64();

	if (tunables->use_sched_load)
		sched_get_cpus_busy(ppol->sl, ppol->policy->related_cpus);
		sched_get_cpus_busy(sl, ppol->policy->cpus);
	max_cpu = cpumask_first(ppol->policy->cpus);
	for_each_cpu(i, ppol->policy->cpus) {
		pcpu = &per_cpu(cpuinfo, i);
		sl = &ppol->sl[i - fcpu];
	i = 0;
	for_each_cpu(cpu, ppol->policy->cpus) {
		pcpu = &per_cpu(cpuinfo, cpu);
		if (tunables->use_sched_load) {
			cputime_speedadj = (u64)sl->prev_load *
					   ppol->policy->cpuinfo.max_freq;
			do_div(cputime_speedadj, tunables->timer_rate);
			t_prevlaf = sl_busy_to_laf(ppol, sl[i].prev_load);
			prev_l = t_prevlaf / ppol->target_freq;
			if (tunables->enable_prediction) {
				t_predlaf = sl_busy_to_laf(ppol,
						sl[i].predicted_load);
				pred_l = t_predlaf / ppol->target_freq;
			}
			if (sl[i].prev_load)
				new_load_pct = sl[i].new_task_load * 100 /
							sl[i].prev_load;
			else
				new_load_pct = 0;
			if (sl->prev_load)
				new_load_pct = sl->new_task_load * 100 /
						sl->prev_load;
		} else {
			now = update_load(i);
			now = update_load(cpu);
			delta_time = (unsigned int)
				(now - pcpu->cputime_speedadj_timestamp);
			if (WARN_ON_ONCE(!delta_time))
				continue;
			cputime_speedadj = pcpu->cputime_speedadj;
			do_div(cputime_speedadj, delta_time);
			t_prevlaf = (unsigned int)cputime_speedadj * 100;
			prev_l = t_prevlaf / ppol->target_freq;
		}
		tmploadadjfreq = (unsigned int)cputime_speedadj * 100;
		pcpu->loadadjfreq = tmploadadjfreq;

		if (tmploadadjfreq > loadadjfreq) {
			loadadjfreq = tmploadadjfreq;
			max_cpu = i;
		/* find max of loadadjfreq inside policy */
		if (t_prevlaf > prev_laf) {
			prev_laf = t_prevlaf;
			max_cpu = cpu;
		}
		cpu_load = tmploadadjfreq / ppol->target_freq;
		trace_cpufreq_interactive_cpuload(i, cpu_load, new_load_pct);
		pred_laf = max(t_predlaf, pred_laf);

		cpu_load = max(prev_l, pred_l);
		pol_load = max(pol_load, cpu_load);
		trace_cpufreq_interactive_cpuload(cpu, cpu_load, new_load_pct,
						  prev_l, pred_l);

		if (cpu_load >= tunables->go_hispeed_load &&
		/* save loadadjfreq for notification */
		pcpu->loadadjfreq = max(t_prevlaf, t_predlaf);

		/* detect heavy new task and jump to policy->max */
		if (prev_l >= tunables->go_hispeed_load &&
		    new_load_pct >= NEW_TASK_RATIO) {
			skip_hispeed_logic = true;
			jump_to_max = true;
		}
		i++;
	}
	spin_unlock(&ppol->load_lock);

	cpu_load = loadadjfreq / ppol->target_freq;
	tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime;

	prev_chfreq = choose_freq(ppol, prev_laf);
	pred_chfreq = choose_freq(ppol, pred_laf);
	chosen_freq = max(prev_chfreq, pred_chfreq);

	if (prev_chfreq < ppol->policy->max && pred_chfreq >= ppol->policy->max)
		if (!jump_to_max)
			jump_to_max_no_ts = true;

	if (now - ppol->max_freq_hyst_start_time <
	    tunables->max_freq_hysteresis &&
	    cpu_load >= tunables->go_hispeed_load &&
	    pol_load >= tunables->go_hispeed_load &&
	    ppol->target_freq < ppol->policy->max) {
		skip_hispeed_logic = true;
		skip_min_sample_time = true;
		policy_max_fast_restore = true;
		if (!jump_to_max)
			jump_to_max_no_ts = true;
	}

	if (policy_max_fast_restore || jump_to_max) {
	new_freq = chosen_freq;
	if (jump_to_max_no_ts || jump_to_max) {
		new_freq = ppol->policy->cpuinfo.max_freq;
	} else if (skip_hispeed_logic) {
		new_freq = choose_freq(ppol, loadadjfreq);
	} else if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) {
		if (ppol->target_freq < tunables->hispeed_freq) {
			new_freq = tunables->hispeed_freq;
		} else {
			new_freq = choose_freq(ppol, loadadjfreq);

			if (new_freq < tunables->hispeed_freq)
	} else if (!skip_hispeed_logic) {
		if (pol_load >= tunables->go_hispeed_load ||
		    tunables->boosted) {
			if (ppol->target_freq < tunables->hispeed_freq)
				new_freq = tunables->hispeed_freq;
			else
				new_freq = max(new_freq,
					       tunables->hispeed_freq);
		}
	} else {
		new_freq = choose_freq(ppol, loadadjfreq);
		if (new_freq > tunables->hispeed_freq &&
				ppol->policy->cur < tunables->hispeed_freq)
			new_freq = tunables->hispeed_freq;
	}

	if (now - ppol->max_freq_hyst_start_time <
@@ -557,7 +595,7 @@ static void cpufreq_interactive_timer(unsigned long data)
	    now - ppol->hispeed_validate_time <
	    freq_to_above_hispeed_delay(tunables, ppol->target_freq)) {
		trace_cpufreq_interactive_notyet(
			max_cpu, cpu_load, ppol->target_freq,
			max_cpu, pol_load, ppol->target_freq,
			ppol->policy->cur, new_freq);
		spin_unlock_irqrestore(&ppol->target_freq_lock, flags);
		goto rearm;
@@ -582,7 +620,7 @@ static void cpufreq_interactive_timer(unsigned long data)
		if (now - ppol->floor_validate_time <
				tunables->min_sample_time) {
			trace_cpufreq_interactive_notyet(
				max_cpu, cpu_load, ppol->target_freq,
				max_cpu, pol_load, ppol->target_freq,
				ppol->policy->cur, new_freq);
			spin_unlock_irqrestore(&ppol->target_freq_lock, flags);
			goto rearm;
@@ -601,24 +639,24 @@ static void cpufreq_interactive_timer(unsigned long data)
	 */

	if ((!tunables->boosted || new_freq > tunables->hispeed_freq)
	    && !policy_max_fast_restore) {
	    && !jump_to_max_no_ts) {
		ppol->floor_freq = new_freq;
		ppol->floor_validate_time = now;
	}

	if (new_freq >= ppol->policy->max && !policy_max_fast_restore)
	if (new_freq >= ppol->policy->max && !jump_to_max_no_ts)
		ppol->max_freq_hyst_start_time = now;

	if (ppol->target_freq == new_freq &&
			ppol->target_freq <= ppol->policy->cur) {
		trace_cpufreq_interactive_already(
			max_cpu, cpu_load, ppol->target_freq,
			max_cpu, pol_load, ppol->target_freq,
			ppol->policy->cur, new_freq);
		spin_unlock_irqrestore(&ppol->target_freq_lock, flags);
		goto rearm;
	}

	trace_cpufreq_interactive_target(max_cpu, cpu_load, ppol->target_freq,
	trace_cpufreq_interactive_target(max_cpu, pol_load, ppol->target_freq,
					 ppol->policy->cur, new_freq);

	ppol->target_freq = new_freq;
@@ -753,33 +791,51 @@ static int load_change_callback(struct notifier_block *nb, unsigned long val,
	struct cpufreq_interactive_tunables *tunables;
	unsigned long flags;

	if (speedchange_task == current)
		return 0;
	if (!ppol || ppol->reject_notification)
		return 0;

	if (!down_read_trylock(&ppol->enable_sem))
		return 0;
	if (!ppol->governor_enabled) {
	if (!ppol->governor_enabled)
		goto exit;

	tunables = ppol->policy->governor_data;
	if (!tunables->use_sched_load || !tunables->use_migration_notif)
		goto exit;

	spin_lock_irqsave(&ppol->target_freq_lock, flags);
	ppol->notif_pending = true;
	ppol->notif_cpu = cpu;
	spin_unlock_irqrestore(&ppol->target_freq_lock, flags);

	if (!hrtimer_is_queued(&ppol->notif_timer))
		hrtimer_start(&ppol->notif_timer, ms_to_ktime(1),
			      HRTIMER_MODE_REL);
exit:
	up_read(&ppol->enable_sem);
	return 0;
}
	tunables = ppol->policy->governor_data;
	if (!tunables->use_sched_load || !tunables->use_migration_notif) {

static enum hrtimer_restart cpufreq_interactive_hrtimer(struct hrtimer *timer)
{
	struct cpufreq_interactive_policyinfo *ppol = container_of(timer,
			struct cpufreq_interactive_policyinfo, notif_timer);
	int cpu;

	if (!down_read_trylock(&ppol->enable_sem))
		return 0;
	if (!ppol->governor_enabled) {
		up_read(&ppol->enable_sem);
		return 0;
	}

	cpu = ppol->notif_cpu;
	trace_cpufreq_interactive_load_change(cpu);
	spin_lock_irqsave(&ppol->target_freq_lock, flags);
	ppol->notif_pending = true;
	spin_unlock_irqrestore(&ppol->target_freq_lock, flags);
	del_timer(&ppol->policy_timer);
	del_timer(&ppol->policy_slack_timer);
	cpufreq_interactive_timer(cpu);

	up_read(&ppol->enable_sem);
	return 0;
	return HRTIMER_NORESTART;
}

static struct notifier_block load_notifier_block = {
@@ -994,6 +1050,7 @@ show_store_one(max_freq_hysteresis);
show_store_one(align_windows);
show_store_one(ignore_hispeed_on_notif);
show_store_one(fast_ramp_down);
show_store_one(enable_prediction);

static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables
		*tunables, char *buf)
@@ -1388,6 +1445,7 @@ show_store_gov_pol_sys(max_freq_hysteresis);
show_store_gov_pol_sys(align_windows);
show_store_gov_pol_sys(ignore_hispeed_on_notif);
show_store_gov_pol_sys(fast_ramp_down);
show_store_gov_pol_sys(enable_prediction);

#define gov_sys_attr_rw(_name)						\
static struct global_attr _name##_gov_sys =				\
@@ -1417,6 +1475,7 @@ gov_sys_pol_attr_rw(max_freq_hysteresis);
gov_sys_pol_attr_rw(align_windows);
gov_sys_pol_attr_rw(ignore_hispeed_on_notif);
gov_sys_pol_attr_rw(fast_ramp_down);
gov_sys_pol_attr_rw(enable_prediction);

static struct global_attr boostpulse_gov_sys =
	__ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys);
@@ -1443,6 +1502,7 @@ static struct attribute *interactive_attributes_gov_sys[] = {
	&align_windows_gov_sys.attr,
	&ignore_hispeed_on_notif_gov_sys.attr,
	&fast_ramp_down_gov_sys.attr,
	&enable_prediction_gov_sys.attr,
	NULL,
};

@@ -1470,6 +1530,7 @@ static struct attribute *interactive_attributes_gov_pol[] = {
	&align_windows_gov_pol.attr,
	&ignore_hispeed_on_notif_gov_pol.attr,
	&fast_ramp_down_gov_pol.attr,
	&enable_prediction_gov_pol.attr,
	NULL,
};

@@ -1544,6 +1605,8 @@ static struct cpufreq_interactive_policyinfo *get_policyinfo(
	ppol->policy_timer.function = cpufreq_interactive_timer;
	init_timer(&ppol->policy_slack_timer);
	ppol->policy_slack_timer.function = cpufreq_interactive_nop_timer;
	hrtimer_init(&ppol->notif_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	ppol->notif_timer.function = cpufreq_interactive_hrtimer;
	spin_lock_init(&ppol->load_lock);
	spin_lock_init(&ppol->target_freq_lock);
	init_rwsem(&ppol->enable_sem);
+13 −7
Original line number Diff line number Diff line
@@ -119,21 +119,27 @@ TRACE_EVENT(cpufreq_interactive_load_change,
);

TRACE_EVENT(cpufreq_interactive_cpuload,
	    TP_PROTO(unsigned long cpu_id, unsigned long load,
		     unsigned int new_task_pct),
	    TP_ARGS(cpu_id, load, new_task_pct),
	    TP_PROTO(unsigned long cpu_id, unsigned int load,
		     unsigned int new_task_pct, unsigned int prev,
		     unsigned int predicted),
	    TP_ARGS(cpu_id, load, new_task_pct, prev, predicted),
	    TP_STRUCT__entry(
		__field(unsigned long, cpu_id)
		__field(unsigned long, load)
		__field(unsigned long, new_task_pct)
		__field(unsigned int, load)
		__field(unsigned int, new_task_pct)
		__field(unsigned int, prev)
		__field(unsigned int, predicted)
	    ),
	    TP_fast_assign(
		__entry->cpu_id = cpu_id;
		__entry->load = load;
		__entry->new_task_pct = new_task_pct;
		__entry->prev = prev;
		__entry->predicted = predicted;
	    ),
	    TP_printk("cpu=%lu load=%lu new_task_pct=%lu", __entry->cpu_id,
		      __entry->load, __entry->new_task_pct)
	    TP_printk("cpu=%lu load=%u new_task_pct=%u prev=%u predicted=%u",
		      __entry->cpu_id, __entry->load, __entry->new_task_pct,
		      __entry->prev, __entry->predicted)
);

#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */