ARM: tegra: cpuquiet: make userspace governor actions synchronous
Peter De Schrijver [Tue, 12 Feb 2013 15:51:26 +0000 (17:51 +0200)]
Userspace expects changes to happen synchronously. Implement this by waiting
with a (configureable) timeout for the action to happen.

Bug 1220065

Change-Id: I81301719707e4baf2b3aea62c38fc771ffe1205d
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-on: http://git-master/r/200013
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

arch/arm/mach-tegra/cpuquiet.c
drivers/cpuquiet/driver.c
drivers/cpuquiet/governors/balanced.c
drivers/cpuquiet/governors/runnable_threads.c
drivers/cpuquiet/governors/userspace.c
include/linux/cpuquiet.h

index 2fcfb54..745f508 100644 (file)
@@ -42,6 +42,7 @@
 #define INITIAL_STATE          TEGRA_CPQ_DISABLED
 #define UP_DELAY_MS            70
 #define DOWN_DELAY_MS          2000
+#define HOTPLUG_DELAY_MS       100
 
 static struct mutex *tegra_cpu_lock;
 static DEFINE_MUTEX(tegra_cpq_lock_stats);
@@ -54,11 +55,13 @@ static struct kobject *tegra_auto_sysfs_kobject;
 
 static wait_queue_head_t wait_no_lp;
 static wait_queue_head_t wait_enable;
+static wait_queue_head_t wait_cpu;
 
 static bool no_lp;
 static bool enable;
 static unsigned long up_delay;
 static unsigned long down_delay;
+static unsigned long hotplug_timeout;
 static int mp_overhead = 10;
 static unsigned int idle_top_freq;
 static unsigned int idle_bottom_freq;
@@ -183,14 +186,45 @@ static int update_core_config(unsigned int cpunumber, bool up)
        return ret;
 }
 
-static int tegra_quiesence_cpu(unsigned int cpunumber)
+static int tegra_quiesence_cpu(unsigned int cpunumber, bool sync)
 {
-        return update_core_config(cpunumber, false);
+       int err = 0;
+
+       err = update_core_config(cpunumber, false);
+       if (err || !sync)
+               return err;
+
+       err = wait_event_interruptible_timeout(wait_cpu,
+                                              !cpu_online(cpunumber),
+                                              hotplug_timeout);
+
+       if (err < 0)
+               return err;
+
+       if (err > 0)
+               return 0;
+       else
+               return -ETIMEDOUT;
 }
 
-static int tegra_wake_cpu(unsigned int cpunumber)
+static int tegra_wake_cpu(unsigned int cpunumber, bool sync)
 {
-        return update_core_config(cpunumber, true);
+       int err = 0;
+
+       err = update_core_config(cpunumber, true);
+       if (err || !sync)
+               return err;
+
+       err = wait_event_interruptible_timeout(wait_cpu, cpu_online(cpunumber),
+                                              hotplug_timeout);
+
+       if (err < 0)
+               return err;
+
+       if (err > 0)
+               return 0;
+       else
+               return -ETIMEDOUT;
 }
 
 static struct cpuquiet_driver tegra_cpuquiet_driver = {
@@ -394,6 +428,7 @@ static int __cpuinit cpu_online_notify(struct notifier_block *nfb,
 
                        mutex_unlock(tegra_cpu_lock);
                }
+               wake_up_interruptible(&wait_cpu);
                break;
        case CPU_ONLINE:
        case CPU_ONLINE_FROZEN:
@@ -407,6 +442,7 @@ static int __cpuinit cpu_online_notify(struct notifier_block *nfb,
 
                        mutex_unlock(tegra_cpu_lock);
                }
+               wake_up_interruptible(&wait_cpu);
                break;
        }
 
@@ -536,6 +572,7 @@ CPQ_BASIC_ATTRIBUTE(mp_overhead, 0644, int);
 CPQ_ATTRIBUTE(no_lp, 0644, bool, no_lp_callback);
 CPQ_ATTRIBUTE(up_delay, 0644, ulong, delay_callback);
 CPQ_ATTRIBUTE(down_delay, 0644, ulong, delay_callback);
+CPQ_ATTRIBUTE(hotplug_timeout, 0644, ulong, delay_callback);
 CPQ_ATTRIBUTE(enable, 0644, bool, enable_callback);
 
 static struct attribute *tegra_auto_attributes[] = {
@@ -546,6 +583,7 @@ static struct attribute *tegra_auto_attributes[] = {
        &idle_bottom_freq_attr.attr,
        &mp_overhead_attr.attr,
        &enable_attr.attr,
+       &hotplug_timeout_attr.attr,
        NULL,
 };
 
@@ -677,6 +715,7 @@ int __cpuinit tegra_auto_hotplug_init(struct mutex *cpulock)
 
        init_waitqueue_head(&wait_no_lp);
        init_waitqueue_head(&wait_enable);
+       init_waitqueue_head(&wait_cpu);
 
        /*
         * Not bound to the issuer CPU (=> high-priority), has rescue worker
@@ -697,6 +736,7 @@ int __cpuinit tegra_auto_hotplug_init(struct mutex *cpulock)
 
        up_delay = msecs_to_jiffies(UP_DELAY_MS);
        down_delay = msecs_to_jiffies(DOWN_DELAY_MS);
+       hotplug_timeout = msecs_to_jiffies(HOTPLUG_DELAY_MS);
        cpumask_clear(&cr_online_requests);
        cpumask_clear(&cr_offline_requests);
 
index fc83fa8..fb9912b 100644 (file)
@@ -106,12 +106,12 @@ static struct kobj_type ktype_cpu_stats = {
 };
 #endif
 
-int cpuquiet_quiesence_cpu(unsigned int cpunumber)
+int cpuquiet_quiesence_cpu(unsigned int cpunumber, bool sync)
 {
        int err = -EPERM;
 
        if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu)
-               err = cpuquiet_curr_driver->quiesence_cpu(cpunumber);
+               err = cpuquiet_curr_driver->quiesence_cpu(cpunumber, sync);
 
 #ifdef CONFIG_CPUQUIET_STATS
        if (!err)
@@ -122,12 +122,12 @@ int cpuquiet_quiesence_cpu(unsigned int cpunumber)
 }
 EXPORT_SYMBOL(cpuquiet_quiesence_cpu);
 
-int cpuquiet_wake_cpu(unsigned int cpunumber)
+int cpuquiet_wake_cpu(unsigned int cpunumber, bool sync)
 {
        int err = -EPERM;
 
        if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu)
-               err = cpuquiet_curr_driver->wake_cpu(cpunumber);
+               err = cpuquiet_curr_driver->wake_cpu(cpunumber, sync);
 
 #ifdef CONFIG_CPUQUIET_STATS
        if (!err)
index f187206..794ef1e 100644 (file)
@@ -341,9 +341,9 @@ static void balanced_work_func(struct work_struct *work)
        if (cpu < nr_cpu_ids) {
                last_change_time = now;
                if (up)
-                       cpuquiet_wake_cpu(cpu);
+                       cpuquiet_wake_cpu(cpu, false);
                else
-                       cpuquiet_quiesence_cpu(cpu);
+                       cpuquiet_quiesence_cpu(cpu, false);
        }
 }
 
index c91d456..79ba69d 100644 (file)
@@ -190,11 +190,11 @@ static void runnables_work_func(struct work_struct *work)
        if (action > 0) {
                cpu = cpumask_next_zero(0, cpu_online_mask);
                if (cpu < nr_cpu_ids)
-                       cpuquiet_wake_cpu(cpu);
+                       cpuquiet_wake_cpu(cpu, false);
        } else if (action < 0) {
                cpu = get_lightest_loaded_cpu_n();
                if (cpu < nr_cpu_ids)
-                       cpuquiet_quiesence_cpu(cpu);
+                       cpuquiet_quiesence_cpu(cpu, false);
        }
 }
 
index 664594d..12a3d86 100644 (file)
@@ -25,14 +25,16 @@ static DEFINE_MUTEX(userspace_mutex);
 
 static int governor_set(unsigned int cpu, bool active)
 {
+       int err;
+
        mutex_lock(&userspace_mutex);
        if (active)
-               cpuquiet_wake_cpu(cpu);
+               err = cpuquiet_wake_cpu(cpu, true);
        else
-               cpuquiet_quiesence_cpu(cpu);
+               err = cpuquiet_quiesence_cpu(cpu, true);
        mutex_unlock(&userspace_mutex);
 
-       return 0;
+       return err;
 }
 
 struct cpuquiet_governor userspace_governor = {
index 5558c01..1bcfecc 100644 (file)
@@ -37,14 +37,14 @@ struct cpuquiet_governor {
 
 struct cpuquiet_driver {
        char                    name[CPUQUIET_NAME_LEN];
-       int (*quiesence_cpu)    (unsigned int cpunumber);
-       int (*wake_cpu)         (unsigned int cpunumber);
+       int (*quiesence_cpu)    (unsigned int cpunumber, bool sync);
+       int (*wake_cpu)         (unsigned int cpunumber, bool sync);
 };
 
 extern int cpuquiet_register_governor(struct cpuquiet_governor *gov);
 extern void cpuquiet_unregister_governor(struct cpuquiet_governor *gov);
-extern int cpuquiet_quiesence_cpu(unsigned int cpunumber);
-extern int cpuquiet_wake_cpu(unsigned int cpunumber);
+extern int cpuquiet_quiesence_cpu(unsigned int cpunumber, bool sync);
+extern int cpuquiet_wake_cpu(unsigned int cpunumber, bool sync);
 extern int cpuquiet_register_driver(struct cpuquiet_driver *drv);
 extern void cpuquiet_unregister_driver(struct cpuquiet_driver *drv);
 extern int cpuquiet_add_group(struct attribute_group *attrs);