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

Commit a0a014ad authored by Rohit Gupta's avatar Rohit Gupta
Browse files

cpufreq: schedutil: Cache tunables on governor exit



Currently when all the related CPUs from a policy go offline or the
governor is switched, cpufreq framework calls sugov_exit() that
frees the governor tunables. When any of the related CPUs comes back
online or governor is switched back to schedutil sugov_init() gets
called which allocates a fresh set of tunables that are set to
default values. This can cause the userspace settings to those
tunables to be lost across governor switches or when an entire
cluster is hotplugged out.
To prevent this, save the tunable values on governor exit. Restore
these values to the newly allocated tunables on governor init.

Change-Id: I671d4d0e1a4e63e948bfddb0005367df33c0c249
Signed-off-by: default avatarRohit Gupta <rohgup@codeaurora.org>
parent 958428f0
Loading
Loading
Loading
Loading
+47 −1
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ struct sugov_cpu {

static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
static unsigned int stale_ns;
static DEFINE_PER_CPU(struct sugov_tunables *, cached_tunables);

/************************ Governor internals ***********************/

@@ -747,6 +748,30 @@ static struct sugov_tunables *sugov_tunables_alloc(struct sugov_policy *sg_polic
	return tunables;
}

static void sugov_tunables_save(struct cpufreq_policy *policy,
		struct sugov_tunables *tunables)
{
	int cpu;
	struct sugov_tunables *cached = per_cpu(cached_tunables, policy->cpu);

	if (!have_governor_per_policy())
		return;

	if (!cached) {
		cached = kzalloc(sizeof(*tunables), GFP_KERNEL);
		if (!cached)
			return;

		for_each_cpu(cpu, policy->related_cpus)
			per_cpu(cached_tunables, cpu) = cached;
	}

	cached->pl = tunables->pl;
	cached->hispeed_load = tunables->hispeed_load;
	cached->hispeed_freq = tunables->hispeed_freq;
	cached->rate_limit_us = tunables->rate_limit_us;
}

static void sugov_tunables_free(struct sugov_tunables *tunables)
{
	if (!have_governor_per_policy())
@@ -755,6 +780,23 @@ static void sugov_tunables_free(struct sugov_tunables *tunables)
	kfree(tunables);
}

static void sugov_tunables_restore(struct cpufreq_policy *policy)
{
	struct sugov_policy *sg_policy = policy->governor_data;
	struct sugov_tunables *tunables = sg_policy->tunables;
	struct sugov_tunables *cached = per_cpu(cached_tunables, policy->cpu);

	if (!cached)
		return;

	tunables->pl = cached->pl;
	tunables->hispeed_load = cached->hispeed_load;
	tunables->hispeed_freq = cached->hispeed_freq;
	tunables->rate_limit_us = cached->rate_limit_us;
	sg_policy->freq_update_delay_ns =
		tunables->rate_limit_us * NSEC_PER_USEC;
}

static int sugov_init(struct cpufreq_policy *policy)
{
	struct sugov_policy *sg_policy;
@@ -805,6 +847,8 @@ static int sugov_init(struct cpufreq_policy *policy)
	sg_policy->tunables = tunables;
	stale_ns = sched_ravg_window + (sched_ravg_window >> 3);

	sugov_tunables_restore(policy);

	ret = kobject_init_and_add(&tunables->attr_set.kobj, &sugov_tunables_ktype,
				   get_governor_parent_kobj(policy), "%s",
				   schedutil_gov.name);
@@ -844,8 +888,10 @@ static void sugov_exit(struct cpufreq_policy *policy)

	count = gov_attr_set_put(&tunables->attr_set, &sg_policy->tunables_hook);
	policy->governor_data = NULL;
	if (!count)
	if (!count) {
		sugov_tunables_save(policy, tunables);
		sugov_tunables_free(tunables);
	}

	mutex_unlock(&global_tunables_lock);