[Nexus 4 Original Android Development] Skkip:
[MENTION=3750240]stratosk[/MENTION]:

as always i like to share my new findings on hotplug and ondemand. noticeable change to hotplug:

- introduce early_suspend/late_resume handler

- if any cpu is boosted we don't want to offline cores

- change last_change_time only if successful

/*
* Dynamic Hotplug for mako
*
* Copyright (C) 2013 Stratos Karafotis <stratosk@semaphore.gr>
*	(C) 2013 Skip <sskkiipp@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Depends on cpufreq average utilization and cpufreq_quick_get_util
* to get the CPU utilization (drivers/cpufreq/cpufreq.c):
*
* void cpufreq_notify_utilization(struct cpufreq_policy *policy,
*	unsigned int util)
* {
*	if (policy)
*	policy->util = (policy->util + util)/2;
* }
*
* unsigned int cpufreq_quick_get_util(unsigned int cpu)
* {
* struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
* unsigned int ret_util = 0;
*
* if (policy) {
* ret_util = policy->util;
* cpufreq_cpu_put(policy);
* }
*
* return ret_util;
* }
* EXPORT_SYMBOL(cpufreq_quick_get_util);
*
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/earlysuspend.h>

static unsigned int sampling_rate_ms = 500;
static unsigned int up_delay_multiplier = 2;
static unsigned int down_delay_multiplier = 5;
static unsigned int up_threshold = 75;
static unsigned int down_threshold = 25;
static unsigned int min_online = 2;
static unsigned int max_online = 4;
static unsigned int saved_min_online;
static unsigned long last_change_time;

/*************** Module parameters ****************/
module_param(sampling_rate_ms, uint, 0644);
module_param(up_delay_multiplier, uint, 0644);
module_param(down_delay_multiplier, uint, 0644);
module_param(up_threshold, uint, 0644);
module_param(down_threshold, uint, 0644);
module_param(min_online, uint, 0644);
module_param(max_online, uint, 0644);
/************ end of module parameters ************/

static struct workqueue_struct *workq;
static struct delayed_work dyn_hotplug;
static struct early_suspend suspend;

static void early_suspend(struct early_suspend *h)
{
saved_min_online = min_online;
min_online = 1;
}

static void __cpuinit late_resume(struct early_suspend *h)
{
unsigned int cpu;

for_each_possible_cpu(cpu)
if (!cpu_online(cpu) && num_online_cpus() < saved_min_online)
cpu_up(cpu);

min_online = saved_min_online;
}

/* Bring online the first found offline one */
static inline bool up_one(void)
{
unsigned int cpu;

/* All wanted CPUs or more are online, return */
if (num_online_cpus() >= max_online)
return false;

cpu = cpumask_next_zero(0, cpu_online_mask);
if (cpu < nr_cpu_ids)
cpu_up(cpu);

return true;
}

/* Iterate through online CPUs and bring down the one with lowest load*/
static inline bool down_one(void)
{
unsigned int cpu = nr_cpu_ids;
unsigned int rate = UINT_MAX;
unsigned int i;

/* Min online CPUs, return */
if (num_online_cpus() <= min_online)
return false;

for_each_online_cpu(i){

/* if any cpu is boosted we don't want to offline cores */
if (cpufreq_quick_get_boost(i))
return false;

if ((i > 0) && (rate > cpufreq_quick_get(i))) {
cpu = i;
rate = cpufreq_quick_get(i);
}
}

cpu_down(cpu);

return true;
}

/*
* Every sampling_rate_ms, check the average load of online CPUs. If the average
* load is above up_threshold bring online one more CPU if up_delay has expired.
* If the average load is below down_threshold offline one more CPU if the
* down_delay has expired.
*/
static __cpuinit void load_timer(struct work_struct *work)
{
unsigned int cpu;
unsigned int avg_load = 0;
unsigned long now = jiffies;

for_each_online_cpu(cpu)
avg_load += cpufreq_quick_get_util(cpu);

avg_load /= num_online_cpus();

if ((now - last_change_time) >= msecs_to_jiffies(sampling_rate_ms * up_delay_multiplier)) {
if (avg_load >= up_threshold) {
if (up_one())
last_change_time = now;
}
}

if ((now - last_change_time) >= msecs_to_jiffies(sampling_rate_ms * down_delay_multiplier)) {
if (avg_load <= down_threshold) {
if (down_one())
last_change_time = now;
}
}

queue_delayed_work(workq, &dyn_hotplug, msecs_to_jiffies(sampling_rate_ms));
}

static int __init dyn_hp_init(void)
{
workq = alloc_workqueue("dyn_hotplug_workqueue",
WQ_UNBOUND | WQ_RESCUER | WQ_FREEZABLE, 1);
if (!workq)
return -ENOMEM;

suspend.suspend = early_suspend;
suspend.resume = late_resume;
suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
register_early_suspend(&suspend);

INIT_DELAYED_WORK(&dyn_hotplug, load_timer);

/* Wait 15 Seconds after boot for everything to be ready*/
queue_delayed_work(workq, &dyn_hotplug, HZ*15);

return 0;
}

static void __exit dyn_hp_exit(void)
{
cancel_delayed_work(&dyn_hotplug);
flush_scheduled_work();
unregister_early_suspend(&suspend);
destroy_workqueue(workq);
}

MODULE_AUTHOR("Stratos Karafotis <stratosk@semaphore.gr");
MODULE_AUTHOR("Sebastian Küppers <sskkiipp@gmail.com>");
MODULE_DESCRIPTION("'dyn_hotplug' - A dynamic hotplug driver for mako");
MODULE_LICENSE("GPL");

module_init(dyn_hp_init);
module_exit(dyn_hp_exit);

next some ideas for ondemand :) just posting some snippets for better viewability:

- only boost cores available at the moment

- add boost instead of fixed value for lower battery usage

[...]
#define TOUCH_LOAD	(25)
#define TOUCH_LOAD_THRESHOLD	(50)
#define TOUCH_LOAD_DURATION	(500)
[...]
if (cur_load > max_load)
max_load = cur_load;
}

cpufreq_notify_utilization(policy, max_load);

if (cpufreq_quick_get_boost(policy->cpu) &&
(max_load <= dbs_tuners_ins.touch_load_threshold))
max_load += dbs_tuners_ins.touch_load;
[...]
static void input_timeout(unsigned long timeout)
{
int cpu;

get_online_cpus();

for_each_online_cpu(cpu) {
struct cpufreq_policy *policy;
policy = cpufreq_cpu_get(cpu);
if (!policy)
continue;
cpufreq_notify_boost(policy, false);
cpufreq_cpu_put(policy);
}

put_online_cpus();

touch = false;
}

static void dbs_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
int cpu;

get_online_cpus();

for_each_online_cpu(cpu) {
struct cpufreq_policy *policy;
policy = cpufreq_cpu_get(cpu);
if (!policy)
continue;
cpufreq_notify_boost(policy, true);
cpufreq_cpu_put(policy);
}

put_online_cpus();

if (!touch) {
touch = true;
input_timer.expires = jiffies + timer_delay;
add_timer(&input_timer);
} else
mod_timer(&input_timer, jiffies + timer_delay);
}
[...]

all this of course depending on cpufreq_quick_get_boost and cpufreq_notify_boost, which is nearly the same as util but only a boolean value.

Looking forward for your comments.

Greetings from Germany