From 990b5d60956471419705300af885f6dff9e12796 Mon Sep 17 00:00:00 2001
From: Wei Wang <wvw@google.com>
Date: Mon, 11 Jun 2018 16:14:51 -0700
Subject: [PATCH] kernel: initialize and free cpufreq stats properly

Initialize task's cpufreq to NULL including for idle
Make sure free task's cpufreq when free task struct

Bug: 110044919
Change-Id: Ie4629d0ebe3ef4b72dffea3ee613b15f40a57142
Signed-off-by: Wei Wang <wvw@google.com>
---
 drivers/cpufreq/cpufreq_stats.c | 23 ++++++++++++++++++++---
 include/linux/cpufreq.h         |  4 ++++
 kernel/fork.c                   |  4 ++++
 kernel/sched/core.c             |  9 +++++++--
 4 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 7f34d683f407..9ce798816e84 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -449,14 +449,24 @@ static int cpufreq_stats_update(struct cpufreq_stats *stats)
 
 void cpufreq_task_stats_init(struct task_struct *p)
 {
-	size_t alloc_size;
-	void *temp;
 	unsigned long flags;
-
 	spin_lock_irqsave(&task_time_in_state_lock, flags);
 	p->time_in_state = NULL;
 	spin_unlock_irqrestore(&task_time_in_state_lock, flags);
 	WRITE_ONCE(p->max_state, 0);
+	spin_lock_irqsave(&task_concurrent_active_time_lock, flags);
+	p->concurrent_active_time = NULL;
+	spin_unlock_irqrestore(&task_concurrent_active_time_lock, flags);
+	spin_lock_irqsave(&task_concurrent_policy_time_lock, flags);
+	p->concurrent_policy_time = NULL;
+	spin_unlock_irqrestore(&task_concurrent_policy_time_lock, flags);
+}
+
+void cpufreq_task_stats_alloc(struct task_struct *p)
+{
+	size_t alloc_size;
+	void *temp;
+	unsigned long flags;
 
 	if (!cpufreq_stats_initialized)
 		return;
@@ -1008,6 +1018,13 @@ static int process_notifier(struct notifier_block *self,
 	return NOTIFY_OK;
 }
 
+void cpufreq_task_stats_free(struct task_struct *p)
+{
+	kfree(p->time_in_state);
+	kfree(p->concurrent_active_time);
+	kfree(p->concurrent_policy_time);
+}
+
 static const struct seq_operations uid_time_in_state_seq_ops = {
 	.start = uid_seq_start,
 	.next = uid_seq_next,
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index d89bbfdb70d4..0a035ccb7585 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -717,6 +717,8 @@ unsigned long cpufreq_scale_max_freq_capacity(int cpu);
 
 void acct_update_power(struct task_struct *p, cputime_t cputime);
 void cpufreq_task_stats_init(struct task_struct *p);
+void cpufreq_task_stats_alloc(struct task_struct *p);
+void cpufreq_task_stats_free(struct task_struct *p);
 void cpufreq_task_stats_remove_uids(uid_t uid_start, uid_t uid_end);
 int  proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
 	struct pid *pid, struct task_struct *p);
@@ -729,6 +731,8 @@ int single_uid_time_in_state_open(struct inode *inode, struct file *file);
 static inline void acct_update_power(struct task_struct *p,
 	cputime_t cputime) {}
 static inline void cpufreq_task_stats_init(struct task_struct *p) {}
+static inline void cpufreq_task_stats_alloc(struct task_struct *p) {}
+static inline void cpufreq_task_stats_free(struct task_struct *p) {}
 static inline void cpufreq_task_stats_exit(struct task_struct *p) {}
 static inline void cpufreq_task_stats_remove_uids(uid_t uid_start,
 	uid_t uid_end) {}
diff --git a/kernel/fork.c b/kernel/fork.c
index 2c3077d1890b..eeeb410abe38 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -78,6 +78,7 @@
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
+#include <linux/cpufreq.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -227,6 +228,9 @@ static void account_kernel_stack(unsigned long *stack, int account)
 
 void free_task(struct task_struct *tsk)
 {
+#ifdef CONFIG_CPU_FREQ_STAT
+	cpufreq_task_stats_free(tsk);
+#endif
 	account_kernel_stack(tsk->stack, -1);
 	arch_release_thread_stack(tsk->stack);
 	free_thread_stack(tsk->stack);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 414a36a7bcb7..7004fb5e9296 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2179,6 +2179,10 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p)
 	memset(&p->se.statistics, 0, sizeof(p->se.statistics));
 #endif
 
+#ifdef CONFIG_CPU_FREQ_STAT
+	cpufreq_task_stats_init(p);
+#endif
+
 	RB_CLEAR_NODE(&p->dl.rb_node);
 	init_dl_task_timer(&p->dl);
 	__dl_clear_params(p);
@@ -2256,11 +2260,12 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p)
 	unsigned long flags;
 	int cpu = get_cpu();
 
+	__sched_fork(clone_flags, p);
+
 #ifdef CONFIG_CPU_FREQ_STAT
-	cpufreq_task_stats_init(p);
+	cpufreq_task_stats_alloc(p);
 #endif
 
-	__sched_fork(clone_flags, p);
 	/*
 	 * We mark the process as running here. This guarantees that
 	 * nobody will actually run it, and a signal or other external
-- 
GitLab