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

Commit 23969e24 authored by Jason Hrycay's avatar Jason Hrycay Committed by TARKZiM
Browse files

cpufreq_stats: Fix stats leak during update policy



When the cpufreq policy is moved from one CPU to another, the percpu
stats_table is overwritten and leaked. Properly free the old stats table
and ensure its protected in the non-sysfs paths of update policy and
acct_update_power. The sysfs entries are removed in the cpufreq core
driver before migrating the policy. We introduce a new spinlock
specifically for these operations to avoid needed to convert all the
other spinlocks into the irq safe variants since acct_update_power is
typically called in ISR context.

[ported to apq8084-common by Corinna Vinschen <xda@vinschen.de>]

Change-Id: I95ff24c07834065cd0fd3c763a488a9843097a1d
Signed-off-by: default avatarJason Hrycay <jason.hrycay@motorola.com>
Reviewed-on: https://gerrit.mot.com/921752


SLTApproved: Slta Waiver <sltawvr@motorola.com>
SME-Granted: SME Approvals Granted
Reviewed-by: default avatarIgor Kovalenko <igork@motorola.com>
parent 9e19e7af
Loading
Loading
Loading
Loading
+21 −3
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS);

static spinlock_t cpufreq_stats_lock;

static DEFINE_SPINLOCK(cpufreq_stats_table_lock);
static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */
static DEFINE_RT_MUTEX(uid_lock); /* uid_hash_table */

@@ -335,14 +336,16 @@ void acct_update_power(struct task_struct *task, cputime_t cputime) {
	int cpu_freq_i;
	int all_freq_i;
	unsigned long flags;
	unsigned long stl_flags;

	if (!task)
		return;

	cpu_num = task_cpu(task);
	spin_lock_irqsave(&cpufreq_stats_table_lock, stl_flags);
	stats = per_cpu(cpufreq_stats_table, cpu_num);
	if (!stats)
		return;
		goto out;

	all_freq_i = atomic_read(&stats->all_freq_i);

@@ -363,15 +366,18 @@ void acct_update_power(struct task_struct *task, cputime_t cputime) {

	powerstats = per_cpu(cpufreq_power_stats, cpu_num);
	if (!powerstats)
		return;
		goto out;

	cpu_freq_i = atomic_read(&stats->cpu_freq_i);
	if (cpu_freq_i == -1)
		return;
		goto out;

	curr = powerstats->curr[cpu_freq_i];
	if (task->cpu_power != ULLONG_MAX)
		task->cpu_power += curr * cputime_to_usecs(cputime);

out:
	spin_unlock_irqrestore(&cpufreq_stats_table_lock, stl_flags);
}
EXPORT_SYMBOL_GPL(acct_update_power);

@@ -643,7 +649,18 @@ error_alloc:

static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
{
	struct cpufreq_stats *old;
	struct cpufreq_stats *stat;
	unsigned long flags;

	spin_lock_irqsave(&cpufreq_stats_table_lock, flags);
	old = per_cpu(cpufreq_stats_table, policy->cpu);
	stat = per_cpu(cpufreq_stats_table, policy->last_cpu);

	if (old) {
		kfree(old->time_in_state);
		kfree(old);
	}

	pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n",
		policy->cpu, policy->last_cpu);
@@ -658,6 +675,7 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
			policy->last_cpu);
	per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL;
	stat->cpu = policy->cpu;
	spin_unlock_irqrestore(&cpufreq_stats_table_lock, flags);
}

static void cpufreq_powerstats_create(unsigned int cpu,