cpufreq: acme: fix race contidion when set/get freq.
authorSoohyun Kim <soohyuni.kim@samsung.com>
Fri, 30 Mar 2018 10:16:27 +0000 (19:16 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:33:21 +0000 (17:33 +0900)
Change-Id: I7a7d59366d81ae4544760548dbb8fac4fed5abe0
Signed-off-by: Soohyun Kim <soohyuni.kim@samsung.com>
drivers/cpufreq/exynos-acme.c
drivers/soc/samsung/exynos-cpupm.c
include/soc/samsung/exynos-cpupm.h

index a5af5b602c813138ac274044dc06971d772a460d..58661b628ca9cb5ff4d4d9af6d903e2751c21781 100644 (file)
@@ -144,8 +144,9 @@ static unsigned int get_freq(struct exynos_cpufreq_domain *domain)
 {
        unsigned int freq;
 
-       if (domain->need_awake)
-               disable_power_mode(cpumask_any(&domain->cpus), POWERMODE_TYPE_CLUSTER);
+       if (domain->need_awake &&
+               check_powerdown_state(cpumask_any(&domain->cpus), POWERMODE_TYPE_CLUSTER))
+               return domain->old;
 
        freq = (unsigned int)cal_dfs_get_rate(domain->cal_id);
        if (!freq) {
@@ -153,9 +154,6 @@ static unsigned int get_freq(struct exynos_cpufreq_domain *domain)
                freq = domain->old;
        }
 
-       if (domain->need_awake)
-               enable_power_mode(cpumask_any(&domain->cpus), POWERMODE_TYPE_CLUSTER);
-
        return freq;
 }
 
index 5eb9f67ce4d3c29eafb7dbd8f56d450f1f808ca9..a25bea07a7aff9f22d8a4e3ed31057b215ec1cc2 100644 (file)
@@ -422,6 +422,25 @@ struct exynos_cpupm {
 
 static DEFINE_PER_CPU(struct exynos_cpupm, cpupm);
 
+int check_powerdown_state(int cpu, int type)
+{
+       struct exynos_cpupm *pm;
+       struct power_mode *mode;
+       int pos;
+
+       pm = &per_cpu(cpupm, cpu);
+
+       for_each_mode(mode, pm->modes, pos) {
+               if (IS_NULL(mode))
+                       break;
+
+               if (mode->type == type)
+                       return check_state_powerdown(mode);
+       }
+
+       return 0;
+}
+
 /*
  * State of each cpu is managed by a structure declared by percpu, so there
  * is no need for protection for synchronization. However, when entering
@@ -455,6 +474,8 @@ void disable_power_mode(int cpu, int type)
        struct power_mode *mode;
        int pos;
 
+       spin_lock(&cpupm_lock);
+
        pm = &per_cpu(cpupm, cpu);
 
        for_each_mode(mode, pm->modes, pos) {
@@ -473,10 +494,14 @@ void disable_power_mode(int cpu, int type)
                         * The first mode disable request wakes the cpus to
                         * exit power mode
                         */
-                       if (atomic_inc_return(&mode->disable) == 1)
+                       if (atomic_inc_return(&mode->disable) == 1) {
+                               spin_unlock(&cpupm_lock);
                                awake_cpus(&mode->siblings);
+                               return;
+                       }
                }
        }
+       spin_unlock(&cpupm_lock);
 }
 
 void enable_power_mode(int cpu, int type)
@@ -485,6 +510,7 @@ void enable_power_mode(int cpu, int type)
        struct power_mode *mode;
        int pos;
 
+       spin_lock(&cpupm_lock);
        pm = &per_cpu(cpupm, cpu);
 
        for_each_mode(mode, pm->modes, pos) {
@@ -494,6 +520,7 @@ void enable_power_mode(int cpu, int type)
                if (mode->type == type)
                        atomic_dec(&mode->disable);
        }
+       spin_unlock(&cpupm_lock);
 }
 
 /* get sleep length of given cpu from tickless framework */
index 0914f0557062e3ff9396e94f6adf097b45f7ca8b..9610b14face76015bab93f1fdd366771d2a96bd1 100644 (file)
@@ -19,6 +19,7 @@ enum {
 
 extern void disable_power_mode(int cpu, int type);
 extern void enable_power_mode(int cpu, int type);
+extern int check_powerdown_state(int cpu, int type);
 
 #ifdef CONFIG_CPU_IDLE
 void exynos_update_ip_idle_status(int index, int idle);