]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
arm: mach-tegra: resolve tegra_cpu_lock race condition
authorSang-Hun Lee <sanlee@nvidia.com>
Mon, 11 Feb 2013 01:48:24 +0000 (17:48 -0800)
committerDan Willemsen <dwillemsen@nvidia.com>
Sat, 14 Sep 2013 20:02:37 +0000 (13:02 -0700)
Problem description:

 - lock used in cpuquiet, cpu-tegra3.c, tegra2-throttle.c, and tegra3_throttle
   originate from cpu-tegra.c, tegra_cpu_lock
 - edp_update_limit and tegra_auto_hotplug_governor need a protection from
   tegra_cpu_lock. These are also called by tegra_cpu_set_speed_cap
 - Some callers of tegra_cpu_set_speed_cap do not acquire tegra_cpu_lock,
   but some do

Fix description:

 - Create a locked variant and unlocked variant of tegra_cpu_set_speed_cap
   to make it explicit that tegra_cpu_lock is needed for tegra_cpu_set_speed_cap
 - Replace existing calls with new variants of tegra_cpu_set_speed_cap appropriately

Bug 1225764

Change-Id: I8aa6356df278375e3a9105023f66c8286e3fdbef
Signed-off-by: Sang-Hun Lee <sanlee@nvidia.com>
Reviewed-on: http://git-master/r/199238
(cherry picked from commit 91bdd64c3c80480195a5ccdb8cef969ca5a18afc)
Reviewed-on: http://git-master/r/207640
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
arch/arm/mach-tegra/cpu-tegra.c
arch/arm/mach-tegra/cpu-tegra.h
arch/arm/mach-tegra/cpu-tegra3.c
arch/arm/mach-tegra/cpuquiet.c
arch/arm/mach-tegra/tegra3_throttle.c

index d864e9b96ba4f76553d4af35ca8b1e3c80c20dc8..854f34777b8ffd09e1d51c7b79236b60a816dcc0 100644 (file)
@@ -7,7 +7,7 @@
  *     Colin Cross <ccross@google.com>
  *     Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
  *
- * Copyright (C) 2010-2012 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2010-2013 NVIDIA CORPORATION. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -66,7 +66,7 @@ static int force_policy_max_set(const char *arg, const struct kernel_param *kp)
 
        ret = param_set_bool(arg, kp);
        if ((ret == 0) && (old_policy != force_policy_max))
-               tegra_cpu_set_speed_cap(NULL);
+               tegra_cpu_set_speed_cap_locked(NULL);
 
        mutex_unlock(&tegra_cpu_lock);
        return ret;
@@ -99,7 +99,7 @@ static inline void _cpu_user_cap_set_locked(void)
                cpu_user_cap = freq_table[i].frequency;
        }
 #endif
-       tegra_cpu_set_speed_cap(NULL);
+       tegra_cpu_set_speed_cap_locked(NULL);
 }
 
 void tegra_cpu_user_cap_set(unsigned int speed_khz)
@@ -212,10 +212,11 @@ static unsigned int edp_predict_limit(unsigned int cpus)
        return limit;
 }
 
+/* Must be called while holding cpu_tegra_lock */
 static void edp_update_limit(void)
 {
        unsigned int limit = edp_predict_limit(cpumask_weight(&edp_cpumask));
-
+       BUG_ON(!mutex_is_locked(&tegra_cpu_lock));
 #ifdef CONFIG_TEGRA_EDP_EXACT_FREQ
        edp_limit = limit;
 #else
@@ -263,7 +264,7 @@ int tegra_edp_set_cur_state(struct thermal_cooling_device *cdev,
        tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, true, 0);
        if (target_cpu_speed[0]) {
                edp_update_limit();
-               tegra_cpu_set_speed_cap(NULL);
+               tegra_cpu_set_speed_cap_locked(NULL);
        }
        tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, false, 0);
        mutex_unlock(&tegra_cpu_lock);
@@ -299,7 +300,7 @@ int tegra_system_edp_alarm(bool alarm)
           or alarm is canceled */
        if (target_cpu_speed[0]) {
                edp_update_limit();
-               ret = tegra_cpu_set_speed_cap(NULL);
+               ret = tegra_cpu_set_speed_cap_locked(NULL);
        }
        if (!ret || !alarm)
                tegra_edp_throttle_cpu_now(0);
@@ -359,7 +360,7 @@ static int tegra_cpu_edp_notify(
                cpu_speed = tegra_getspeed(0);
                new_speed = edp_governor_speed(cpu_speed);
                if (new_speed < cpu_speed) {
-                       ret = tegra_cpu_set_speed_cap(NULL);
+                       ret = tegra_cpu_set_speed_cap_locked(NULL);
                        printk(KERN_DEBUG "cpu-tegra:%sforce EDP limit %u kHz"
                                "\n", ret ? " failed to " : " ", new_speed);
                }
@@ -378,7 +379,7 @@ static int tegra_cpu_edp_notify(
                tegra_cpu_dvfs_alter(
                        edp_thermal_index, &edp_cpumask, true, event);
                edp_update_limit();
-               tegra_cpu_set_speed_cap(NULL);
+               tegra_cpu_set_speed_cap_locked(NULL);
                mutex_unlock(&tegra_cpu_lock);
                break;
        }
@@ -615,7 +616,7 @@ void tegra_cpu_set_volt_cap(unsigned int cap)
        mutex_lock(&tegra_cpu_lock);
        if (cap != volt_capped_speed) {
                volt_capped_speed = cap;
-               tegra_cpu_set_speed_cap(NULL);
+               tegra_cpu_set_speed_cap_locked(NULL);
        }
        mutex_unlock(&tegra_cpu_lock);
        if (cap)
@@ -631,11 +632,12 @@ static unsigned int volt_cap_speed(unsigned int requested_speed)
        return requested_speed;
 }
 
-int tegra_cpu_set_speed_cap(unsigned int *speed_cap)
+/* Must be called with tegra_cpu_lock held */
+int tegra_cpu_set_speed_cap_locked(unsigned int *speed_cap)
 {
        int ret = 0;
        unsigned int new_speed = tegra_cpu_highest_speed();
-
+       BUG_ON(!mutex_is_locked(&tegra_cpu_lock));
 #ifdef CONFIG_TEGRA_EDP_LIMITS
        edp_update_limit();
 #endif
@@ -656,6 +658,16 @@ int tegra_cpu_set_speed_cap(unsigned int *speed_cap)
        return ret;
 }
 
+int tegra_cpu_set_speed_cap(unsigned int *speed_cap)
+{
+       int ret;
+       mutex_lock(&tegra_cpu_lock);
+       ret = tegra_cpu_set_speed_cap_locked(speed_cap);
+       mutex_unlock(&tegra_cpu_lock);
+       return ret;
+}
+
+
 int tegra_suspended_target(unsigned int target_freq)
 {
        unsigned int new_speed = target_freq;
@@ -689,7 +701,7 @@ static int tegra_target(struct cpufreq_policy *policy,
        freq = freq_table[idx].frequency;
 
        target_cpu_speed[policy->cpu] = freq;
-       ret = tegra_cpu_set_speed_cap(&new_speed);
+       ret = tegra_cpu_set_speed_cap_locked(&new_speed);
 _out:
        mutex_unlock(&tegra_cpu_lock);
 
@@ -712,7 +724,7 @@ static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
                unsigned int freq;
                is_suspended = false;
                tegra_cpu_edp_init(true);
-               tegra_cpu_set_speed_cap(&freq);
+               tegra_cpu_set_speed_cap_locked(&freq);
                pr_info("Tegra cpufreq resume: restoring frequency to %d kHz\n",
                        freq);
        }
@@ -844,7 +856,9 @@ static int __init tegra_cpufreq_init(void)
                return ret;
 
        freq_table = table_data->freq_table;
+       mutex_lock(&tegra_cpu_lock);
        tegra_cpu_edp_init(false);
+       mutex_unlock(&tegra_cpu_lock);
 
        ret = register_pm_notifier(&tegra_cpu_pm_notifier);
 
index 749ec6345cfa44c106f2484daa21acba7481a16e..84504bb0ccc189cdd8c71f7fc3c0bc193b0e0d16 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/cpu-tegra.h
  *
- * Copyright (c) 2011-2012, NVIDIA Corporation.
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
 unsigned int tegra_getspeed(unsigned int cpu);
 int tegra_update_cpu_speed(unsigned long rate);
 int tegra_cpu_set_speed_cap(unsigned int *speed_cap);
+int tegra_cpu_set_speed_cap_locked(unsigned int *speed_cap);
 void tegra_cpu_set_volt_cap(unsigned int cap);
 unsigned int tegra_count_slow_cpus(unsigned long speed_limit);
 unsigned int tegra_get_slowest_cpu_n(void);
index f26f449b482b040c4dd883b31fc971f1d3cd6dc4..d7d0250e424ecd9834c6dd69cdc3200b28e9f598 100644 (file)
@@ -3,7 +3,7 @@
  *
  * CPU auto-hotplug for Tegra3 CPUs
  *
- * Copyright (c) 2011-2012, NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2011-2013, NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@
 #define UP2Gn_DELAY_MS         100
 #define DOWN_DELAY_MS          2000
 
+/* tegra3_cpu_lock is tegra_cpu_lock from cpu-tegra.c */
 static struct mutex *tegra3_cpu_lock;
 
 static struct workqueue_struct *hotplug_wq;
@@ -160,7 +161,7 @@ static int hp_state_set(const char *arg, const struct kernel_param *kp)
                                hp_init_stats();
                        }
                        /* catch-up with governor target speed */
-                       tegra_cpu_set_speed_cap(NULL);
+                       tegra_cpu_set_speed_cap_locked(NULL);
                }
        } else
                pr_warn("%s: unable to set tegra hotplug state %s\n",
@@ -295,7 +296,7 @@ static void __cpuinit tegra_auto_hotplug_work_func(struct work_struct *work)
                                hp_stats_update(CONFIG_NR_CPUS, true);
                                hp_stats_update(0, false);
                                /* catch-up with governor target speed */
-                               tegra_cpu_set_speed_cap(NULL);
+                               tegra_cpu_set_speed_cap_locked(NULL);
                                break;
                        }
                }
@@ -309,7 +310,7 @@ static void __cpuinit tegra_auto_hotplug_work_func(struct work_struct *work)
                                hp_stats_update(CONFIG_NR_CPUS, false);
                                hp_stats_update(0, true);
                                /* catch-up with governor target speed */
-                               tegra_cpu_set_speed_cap(NULL);
+                               tegra_cpu_set_speed_cap_locked(NULL);
                        }
                } else {
                        switch (tegra_cpu_speed_balance()) {
@@ -373,7 +374,7 @@ static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
                }
        }
        /* update governor state machine */
-       tegra_cpu_set_speed_cap(NULL);
+       tegra_cpu_set_speed_cap_locked(NULL);
        mutex_unlock(tegra3_cpu_lock);
        return NOTIFY_OK;
 }
index a617fa262f46987ce586929b135153a1fc8c0cbe..a66cd52f435f531a1d93bf2bee565d82bec0e369 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Cpuquiet driver for Tegra CPUs
  *
- * Copyright (c) 2012 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -371,6 +371,7 @@ static void __cpuinit tegra_cpuquiet_work_func(struct work_struct *work)
                current_cluster = __apply_cluster_config(current_cluster,
                                        new_cluster);
 
+               tegra_cpu_set_speed_cap_locked(NULL);
                mutex_unlock(tegra_cpu_lock);
 
                if (current_cluster == TEGRA_CPQ_LP)
@@ -378,9 +379,9 @@ static void __cpuinit tegra_cpuquiet_work_func(struct work_struct *work)
                else
                        cpuquiet_device_free();
 
-               tegra_cpu_set_speed_cap(NULL);
-       } else
+       } else {
                mutex_unlock(tegra_cpu_lock);
+       }
 
        if (current_cluster == TEGRA_CPQ_G)
                __apply_core_config();
index 62ba41b5b79d1b4b09387068f751a4f017ddfd1e..90ab7c4b6dec95398af32818af4fcd5f5e01da53 100644 (file)
@@ -33,6 +33,7 @@
 #include "clock.h"
 #include "cpu-tegra.h"
 
+/* cpu_throttle_lock is tegra_cpu_lock from cpu-tegra.c */
 static struct mutex *cpu_throttle_lock;
 static DEFINE_MUTEX(bthrot_list_lock);
 static LIST_HEAD(bthrot_list);
@@ -218,9 +219,7 @@ tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
        if (cur_state == 0) {
                tegra_throttle_cap_freqs_update(NULL, -1, 1);/* uncap freqs */
 
-               mutex_lock(cpu_throttle_lock);
                tegra_cpu_set_speed_cap(NULL);
-               mutex_unlock(cpu_throttle_lock);
        } else {
                if (cur_state == 1 && direction == 0)
                        bthrot->throttle_count++;
@@ -234,9 +233,7 @@ tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
                        bthrot->cpu_cap_freq =
                                        bthrot->throt_tab[index].cap_freqs[0];
 
-                       mutex_lock(cpu_throttle_lock);
                        tegra_cpu_set_speed_cap(NULL);
-                       mutex_unlock(cpu_throttle_lock);
                }
        }