CPUFreq dynamic speed governor, take 2.
I've been lurking on this list for a while now, but a few friends of mine encouraged me to post a chunk of code I wrote...
I wrote a simple "dynamic" frequency governor for use on my non-mobile P4 laptop, since the demandbased governor patch seems to be targeted at mobile CPUs. (...and doesn't work for me :)
I've been using the "dynamic" governor on my machine with the p4-clockmod driver, and it works beautifully. I don't notice any slowdown in X/Gnome2, though I'm sure there is quite a bit. I do however notice significant drops is heat output and a small yet noticeable increase in battery life.
The module itself just polls idle jiffies every 100ms, and bumps up the frequency if there's less than 20% idle, and down if there's more than 80%.
I've made a quick patch that should apply cleanly to 2.6.0-test9. Also included is the raw code, however ugly it may be.
From previous posts, I've gathered that:
1) This kind of stuff is probably patented, so it will never be merged anywhere. Oh, well.
2) People seem to prefer userspace tools to govern frequency. As far as I can tell, the kernel-based solution should use way less overhead, and doesn't run into certain limitations.
Comments, questions? Please let me know,
jon anderson
drivers/cpufreq/Kconfig +24 -0
@@ -35,6 +35,17 @@ programm shall be able to set the CPU dynamically without having to enable the userspace governor manually. +config CPU_FREQ_DEFAULT_GOV_DYNAMIC + bool "dynamic" + select CPU_FREQ_GOV_DYNAMIC + help + Use the CPUFreq governor 'dynamic' as the default. This will + adjust the CPU frequency automatically based on CPU idle time. + This should work on just about any CPU for which there is a + CPUFreq driver (p4-clockmod, etc...) + + This is experimental + endchoice config CPU_FREQ_GOV_PERFORMANCE
@@ -68,6 +79,19 @@ If in doubt, say Y. +config CPU_FREQ_GOV_DYNAMIC + tristate "Dynamic CPUFreq policy governor" + depends on CPU_FREQ + help + This CPUFreq governor checks CPU idle time 10 times a second, + and raises or lowers the CPU's speed as appropriate. + + This code is currently experimental, but works fantastically + for the author. + + It is best to say N for the moment. + + config CPU_FREQ_24_API bool "/proc/sys/cpu/ interface (2.4. / OLD)" depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE
drivers/cpufreq/Makefile +1 -0
@@ -5,6 +5,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o +obj-$(CONFIG_CPU_FREQ_GOV_DYNAMIC) += cpufreq_dynamic.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
drivers/cpufreq/cpufreq_dynamic.c +154 -0
154 lines not on this page · raw message
include/linux/cpufreq.h +3 -0
@@ -303,6 +303,9 @@#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE )extern struct cpufreq_governor cpufreq_gov_userspace;#define CPUFREQ_DEFAULT_GOVERNOR &cpufreq_gov_userspace +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_DYNAMIC ) +extern struct cpufreq_governor cpufreq_gov_dynamic; +#define CPUFREQ_DEFAULT_GOVERNOR &cpufreq_gov_dynamic#endif /*********************************************************************
/*
* linux/drivers/cpufreq/cpufreq_dynamic.c
*
* 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.
*
* *** Experimental! ***
*
* TODO:
* - Handle more than CPU 0. * - Put in a better stepping mechanism. * - Step downwards slower than stepping upwards. (I think this would * work more efficiently than as it is now.) * - Documenting in Documentation/cpufreq * - Documentation that is NOT crappy in this file. * - Proper debug printing.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/times.h>
#include <asm/timex.h>
#include <linux/kernel_stat.h>
/* How many times/second to poll */
#define POLL_FREQUENCY 10
/* Wait HZ/POLL_FREQUENCY jiffies in between polling. */ #define SLEEP_JIFFIES HZ/POLL_FREQUENCY
/* Minimum and maximum idle thresholds, in percent */ #define MIN_IDLE 20
#define MAX_IDLE 80
/* Internal definitions for temporary frequency table. Ug-ly. */ #define FREQ_MAX 0
#define FREQ_MIN -1
/** Should be a list, from FREQ_MAX to FREQ_MIN of CPU speeds in KHz.
* e.g. { FREQ_MAX,1500000,1000000,500000,FREQ_MIN }
*/
static const int32_t cpufreq_dynamic_freqs[] = {
FREQ_MAX, \/**
* FIXME: Find better way to find frequencies to switch to. *
* These values work on my non-mobile P4 2 GHz, but
* would probably just waste CPU cycles on most other
* CPUs. (I.e. these values would probably be invalid.) *
* 1500000, \
* 1000000, \
* 500000, \
* 250000, \ */
FREQ_MIN \ };
/* Kernel Timer for this module. (Timers are awesome!) */ struct timer_list cpufreq_dynamic_timer; unsigned long prev_idle; // CPU idle time, in jiffies. unsigned long percent_idle; // Idle percentage.uint8_t cpufreq_dynamic_cur_freq_id; // What ID from the stupid table we're at.
/**
* Timer callback
*/
static void cpufreq_governor_dynamic_callback(unsigned long t) {
percent_idle = POLL_FREQUENCY * 100 * (kstat_cpu(0).cpustat.idle - prev_idle) / HZ;
prev_idle = kstat_cpu(0).cpustat.idle;
if ((percent_idle > MAX_IDLE) || (percent_idle < MIN_IDLE)) { cpufreq_governor(0,CPUFREQ_GOV_LIMITS); }
cpufreq_dynamic_timer.expires = jiffies + SLEEP_JIFFIES;
add_timer(&cpufreq_dynamic_timer);
}/**
* The governor itself.
*
* DOCUMENT ME!
*/
static int cpufreq_governor_dynamic(struct cpufreq_policy *policy,
unsigned int event)
{
switch (event) {
case CPUFREQ_GOV_START:
cpufreq_dynamic_timer.expires = jiffies + SLEEP_JIFFIES;
cpufreq_dynamic_timer.function = cpufreq_governor_dynamic_callback;
cpufreq_dynamic_cur_freq_id = 0;
prev_idle = kstat_cpu(0).cpustat.idle;
init_timer(&cpufreq_dynamic_timer);
add_timer(&cpufreq_dynamic_timer);
case CPUFREQ_GOV_LIMITS:
if (percent_idle > MAX_IDLE) {
if (cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id] != FREQ_MIN) {
if (cpufreq_dynamic_freqs[++cpufreq_dynamic_cur_freq_id] == FREQ_MIN) {
__cpufreq_driver_target(policy,policy->min,CPUFREQ_RELATION_L);
} else {
//printk("cpufreq_dynamic: Attempting to downgrade frequency: %i KHz\n",cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id]);
__cpufreq_driver_target(policy,cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id],CPUFREQ_RELATION_L);
}
}
} else if (percent_idle < MIN_IDLE) {
if (cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id] != FREQ_MAX) {
if (cpufreq_dynamic_freqs[--cpufreq_dynamic_cur_freq_id] == FREQ_MAX) {
__cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L);
} else {
//printk("cpufreq_dynamic: Attempting to upgrade frequency: %i KHz\n",cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id]);
__cpufreq_driver_target(policy,cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id],CPUFREQ_RELATION_L);
}
}
}
break;
case CPUFREQ_GOV_STOP:
del_timer(&cpufreq_dynamic_timer);
break;
default:
break;
}
return 0;
}static struct cpufreq_governor cpufreq_gov_dynamic = {
.name = "dynamic",
.governor = cpufreq_governor_dynamic,
.owner = THIS_MODULE,
};EXPORT_SYMBOL(cpufreq_gov_dynamic);
static int __init cpufreq_gov_dynamic_init(void)
{
return cpufreq_register_governor(&cpufreq_gov_dynamic);
}static void __exit cpufreq_gov_dynamic_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_dynamic);
}MODULE_AUTHOR("Jonathan Anderson <[email protected]"); MODULE_DESCRIPTION("Dynamic CPUfreq policy governor"); MODULE_LICENSE("GPL");
module_init(cpufreq_gov_dynamic_init);module_exit(cpufreq_gov_dynamic_exit);
_______________________________________________
Cpufreq mailing list
[email protected]
http://www.linux.org.uk/mailman/listinfo/cpufreq
[Venkatesh, please read line 54++]
Hi Jon,
It seems to me that your governor uses exactly the same algorithm as the one proposed by Venkatesh Pallipadi, but maybe with some slightly different default values. So, I'd prefer if only one of these two were merged into CVS and/or the kernel. And, for that matter, I prefer Venkatesh's code at the moment as it's cleaner, supports different CPUs, ... - but see below for some details.
Quoted 3 lines · Jon Anderson
I wrote a simple "dynamic" frequency governor for use on my non-mobile P4 laptop, since the demandbased governor patch seems to be targeted at mobile CPUs. (...and doesn't work for me :)
That's not entirely correct -- it just has rather strict limits on the "transition latency", and several cpfureq drivers don't set the proper transition latency but the dummy value "CPUFREQ_ETERNAL" which disables dynamic governors [in kernelspace].
Quoted 1 line
2) People seem to prefer userspace tools to govern frequency.
Well, I surely don't. I prefer kernelspace.
Quoted 1 line
+config
CPU_FREQ_DEFAULT_GOV_DYNAMIC
"Dynamic" governors can't be default governor -- they can fail due to transition latency limits, and would leave the cpufreq core in an undefined state then. Also, the name "dynamic" is too generic as there likely will be different dynamic cpufreq governors available.
Quoted 8 lines
drivers/cpufreq/cpufreq_dynamic.c +8
-0+/** Should be a list, from FREQ_MAX to FREQ_MIN of CPU speeds in KHz. +* e.g. { FREQ_MAX,1500000,1000000,500000,FREQ_MIN } +*/ +static const int32_t cpufreq_dynamic_freqs[] = { +FREQ_MAX , \ +/** +* FIXME: Find better way to find frequencies to switch to. +*
drivers/cpufreq/cpufreq_dynamic.c line 62
Quoted 10 lines
drivers/cpufreq/cpufreq_dynamic.c +10
-0+* These values work on my non-mobile P4 2 GHz, but +* would probably just waste CPU cycles on most other +* CPUs. (I.e. these values would probably be invalid.) +* +* 1500000, \ +* 1000000, \ +* 500000, \ +* 250000, \ */ + FREQ_MIN \ +};
There's no valid reason for such a table. A governor should either calculate a percentage of operating power, and say whether to go up or down if this frequency = percentage * max_frequency isn't available.
Actually, this is a problem in Venkatesh's code: If the governor says "there's too few processing power available, let's increase the speed" it increases the current speed by 1 kHz, and says this is a "low limit" which means that the new speed must be higher than that. For the common frequency scaling implementations out there, which define only few frequency states, this is not a problem. For the geode driver, though, which has several thousand frequency states available IIRC, this is a big problem: switching from lowest to highest speed may take a couple of seconds or even minutes!
Dominik
Thanks for your input on this one. I have a few questions...If they're out of place, let me know, and I'll shut up. :)
Quoted 6 lines · Dominik Brodowski
It seems to me that your governor uses exactly the same algorithm as the one proposed by Venkatesh Pallipadi, but maybe with some slightly different default values. So, I'd prefer if only one of these two were merged into CVS and/or the kernel. And, for that matter, I prefer Venkatesh's code at the moment as it's cleaner, supports different CPUs, ... - but see below for some details.
I would actually say at this moment that I would probably prefer Venkatesh's code be merged over my own, for basically the same reasons. (I'm a student, and very much an amateur coder - a beginner in the kernel arena. This is very much a learning experience for me. :)
I just took a look through it, and it does seem to use a similar algorithm, though I don't understand some elements of it. Implementation seems to be drastically different. I don't understand in what way the two drivers differ in terms of CPU support. Please elaborate. (I don't understand how my governor doesn't support different CPUs - I thought it did.)
I did actually make a few major changes yesterday, the new file is attached. It now should support SMP, and should now step downwards at 1/5th the speed of stepping up. I will make a pile of additional changes based on your comments. (Thank you for taking the time to actually read through my code! I really appreciate it!)
Since this code won't be merged, should I continue to post my changes here, or is that inappropriate?
Quoted 4 lines
That's not entirely correct -- it just has rather strict limits on the "transition latency", and several cpfureq drivers don't set the proper transition latency but the dummy value "
CPUFREQ_ETERNAL" which disables dynamic governors [in kernelspace].
That is I noticed that in the code originally. I commented out the latency check, so I could try the dbs module with the P4-clockmod driver. This results in a machine lockup, which is why I originally wrote the "dynamic" module. I assume the lockup is because the dbs module seems to poll HZ times a second (10 times as often as mine.), which would overwhelm the p4-clockmod driver.
Quoted 2 lines
Quoted 1 line
2) People seem to prefer userspace tools to govern frequency.
Well, I surely don't. I prefer kernelspace.
Woo! Strongly agreed!
Quoted 5 lines
Quoted 1 line
+config
CPU_FREQ_DEFAULT_GOV_DYNAMIC"Dynamic" governors can't be default governor -- they can fail due to transition latency limits, and would leave the cpufreq core in an undefined state then. Also, the name "dynamic" is too generic as there likely will be different dynamic cpufreq governors available.
I guess it just seems a waste to have to compile in two governors, when the static governor would only be in use for the short period of time until a boot script sets up whichever dynamic governor. Point taken though.
I suppose the name is moot though, since it won't be merged anyway. Perhaps cpufreq_dynamic_ugly for now. ;)
Quoted 3 lines
There's no valid reason for such a table. A governor should either calculate a percentage of operating power, and say whether to go up or down if this frequency = percentage *
max_frequencyisn't available.
That's why the meat of the table was commented out, and left only FREQ_MIN and FREQ_MAX, which use policy->min and policy->max. I don't know how to get the available frequencies from the current driver, so that's why I used this. (I believe I even made a comment about the ugliness of this in my code.)
I had originally planned on using the array (not originally a static const) as a calculated table of available frequencies. Since I couldn't think of a good way to get a set of available frequencies, I made it as it is now.
What happens when a governor attempts to set a speed that the driver can't handle? Is it driver independant?
Quoted 8 lines
Actually, this is a problem in Venkatesh's code: If the governor says "there's too few processing power available, let's increase the speed" it increases the current speed by 1 kHz, and says this is a "low limit" which means that the new speed must be higher than that. For the common frequency scaling implementations out there, which define only few frequency states, this is not a problem. For the geode driver, though, which has several thousand frequency states available IIRC, this is a big problem: switching from lowest to highest speed may take a couple of seconds or even minutes!
Is there a better way to search for available frequencies? Would it be worthwhile to set a number of defined steps, say 'max', '2/3 max', '1/3 max', 'min' -> What happens when something like '2/3 max' isn't an available frequency?
--
jon anderson
/*
* linux/drivers/cpufreq/cpufreq_dynamic.c
*
* 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.
*
* *** Experimental! ***
*
* TODO:
* - *** DONE *** Handle more than CPU 0. * - Put in a better stepping mechanism. * - *** DONE *** Step downwards slower than stepping upwards. * (I think this would work more efficiently than as it is now.) * - Documenting in Documentation/cpufreq * - Documentation that is NOT crappy in this file. * - Proper debug printing.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/times.h>
#include <asm/timex.h>
#include <linux/kernel_stat.h>
/* How many times/second to poll */
#define POLL_FREQUENCY 10
/* Wait HZ/POLL_FREQUENCY jiffies in between polling. */ #define SLEEP_JIFFIES HZ/POLL_FREQUENCY
/* How many timer ticks to wait before decreasing frequency. */ #define DEC_TICKS 5
/* Minimum and maximum idle thresholds, in percent */ #define MIN_IDLE 20
#define MAX_IDLE 80
/* Internal definitions for temporary frequency table. Ug-ly. */ #define FREQ_MAX 0
#define FREQ_MIN -1
/** Should be a list, from FREQ_MAX to FREQ_MIN of CPU speeds in KHz.
* e.g. { FREQ_MAX,1500000,1000000,500000,FREQ_MIN }
*/
static const int32_t cpufreq_dynamic_freqs[] = {
FREQ_MAX, \/**
* FIXME: Find better way to find frequencies to switch to. *
* These values work on my non-mobile P4 2 GHz, but
* would probably just waste CPU cycles on most other
* CPUs. (I.e. these values would probably be invalid.) */
/* 1500000, \ 1000000, \ 500000, \ */ FREQ_MIN \ };
// Kernel Timer for this module. (Timers are awesome!) struct timer_list cpufreq_dynamic_timer[NR_CPUS];
// Struct to hold CPU status information.
typedef struct cpufreq_dynamic_cpuinfo {
unsigned long prev_idle; // CPU idle time, in jiffies. unsigned long percent_idle; // Idle percentage.
uint8_t cur_freq_id; // What ID from the stupid table we're at.
uint8_t dec_wait;
} cpufreq_dynamic_cpuinfo;
// CPU Status information for all CPUs cpufreq_dynamic_cpuinfo cpufreq_dynamic_status[NR_CPUS];
/**
* cpufreq_governor_dynamic_getidle
*
* Collects idle and idle percentage statistics about a given CPU.
*
* @param cur_cpu The cpu for which statistics are to be collected.
*
* Inline to save needless function call overhead...I think. If this is bad, please let me know. */
inline void cpufreq_governor_dynamic_getidle(unsigned int cur_cpu) {
//printk(KERN_DEBUG "cpufreq_dynamic: Calculating idle percentage for CPU%i\n",cur_cpu);
cpufreq_dynamic_status[cur_cpu].percent_idle = POLL_FREQUENCY * 100 * (kstat_cpu(cur_cpu).cpustat.idle - cpufreq_dynamic_status[cur_cpu].prev_idle) / HZ;
cpufreq_dynamic_status[cur_cpu].prev_idle = kstat_cpu(cur_cpu).cpustat.idle;
}/**
* cpufreq_governor_dynamic_callback
*
* This is the kernel timer callback function. It executes cpufreq_governor if idles are outside thresholds. *
* @param cur_cpu The cpu to work with. This parameter comes from timer.data.
*/
static void cpufreq_governor_dynamic_callback(unsigned long cur_cpu) {
cpufreq_governor_dynamic_getidle(cur_cpu);
//printk(KERN_DEBUG "cpufreq_dynamic: Timer callback for CPU%lu -> %lu%% idle.\n",cur_cpu,cpufreq_dynamic_status[cur_cpu].percent_idle);
if ((cpufreq_dynamic_status[cur_cpu].percent_idle > MAX_IDLE) || (cpufreq_dynamic_status[cur_cpu].percent_idle < MIN_IDLE)) {
cpufreq_governor(cur_cpu,CPUFREQ_GOV_LIMITS);
} else {
cpufreq_dynamic_status[cur_cpu].dec_wait = DEC_TICKS;
}
// Set the timer again.
cpufreq_dynamic_timer[cur_cpu].expires = jiffies + SLEEP_JIFFIES;
add_timer(&cpufreq_dynamic_timer[cur_cpu]);
}/**
* cpufreq_governor_dynamic
*
* The governor itself.
*
* More documentation should go here.
*/
static int cpufreq_governor_dynamic(struct cpufreq_policy *policy,unsigned int event) {
switch (event) {
case CPUFREQ_GOV_START:
printk(KERN_DEBUG "cpufreq_dynamic: Starting dynamic governor on CPU%i\n",policy->cpu);
// Init info for policy CPU.
cpufreq_dynamic_status[policy->cpu].cur_freq_id = 0; // Set initially to max speed.
cpufreq_dynamic_status[policy->cpu].prev_idle = kstat_cpu(policy->cpu).cpustat.idle; // Set previous idle.
cpufreq_governor_dynamic_getidle(policy->cpu); // Get initial idle time.
// Initialize the timer.
cpufreq_dynamic_timer[policy->cpu].function = cpufreq_governor_dynamic_callback;
cpufreq_dynamic_timer[policy->cpu].data = policy->cpu;
cpufreq_dynamic_timer[policy->cpu].expires = jiffies + SLEEP_JIFFIES;
init_timer(&cpufreq_dynamic_timer[policy->cpu]);
add_timer(&cpufreq_dynamic_timer[policy->cpu]);
printk(KERN_DEBUG "cpufreq_dynamic: dynamic governor started on CPU%i.\n",policy->cpu);
// No need to change the frequency until we've collected SLEEP_JIFFIES worth of stats.
break;
case CPUFREQ_GOV_LIMITS:
// If we're too idle, we want to decrease the CPU frequency.
if (cpufreq_dynamic_status[policy->cpu].percent_idle > MAX_IDLE) {
if (--cpufreq_dynamic_status[policy->cpu].dec_wait > 0) {
// Waiting a little before dropping frequency.
// Leaving room for something here, if need be.
// Don't do anything if we're already at minimum frequency.
} else if (cpufreq_dynamic_freqs[cpufreq_dynamic_status[policy->cpu].cur_freq_id] != FREQ_MIN) {
cpufreq_dynamic_status[policy->cpu].dec_wait = DEC_TICKS;
// If we're to step down to FREQ_MIN, use the policy's value.
if (cpufreq_dynamic_freqs[++cpufreq_dynamic_status[policy->cpu].cur_freq_id] == FREQ_MIN) {
printk(KERN_INFO "cpufreq_dynamic: Setting CPU%i to minimum frequency: %u KHz\n",policy->cpu,policy->min);
__cpufreq_driver_target(policy,policy->min,CPUFREQ_RELATION_L);
} else {
printk(KERN_INFO "cpufreq_dynamic: decreasing CPU%i frequency to %i KHz\n",policy->cpu,cpufreq_dynamic_freqs[cpufreq_dynamic_status[policy->cpu].cur_freq_id]);
__cpufreq_driver_target(policy,cpufreq_dynamic_freqs[cpufreq_dynamic_status[policy->cpu].cur_freq_id],CPUFREQ_RELATION_L);
}
}
// If we're not idle enough, we want to increase the CPU frequency.
} else if (cpufreq_dynamic_status[policy->cpu].percent_idle < MIN_IDLE) {
cpufreq_dynamic_status[policy->cpu].dec_wait = DEC_TICKS;
// Don't do anything if we're already at the max frequency.
if (cpufreq_dynamic_freqs[cpufreq_dynamic_status[policy->cpu].cur_freq_id] != FREQ_MAX) {
// If we're to step up to FREQ_MAX, use the policy's value.
if (cpufreq_dynamic_freqs[--cpufreq_dynamic_status[policy->cpu].cur_freq_id] == FREQ_MAX) {
printk(KERN_INFO "cpufreq_dynamic: Setting CPU%i to maximum frequency: %u KHz\n",policy->cpu,policy->max);
__cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L);
} else {
printk("cpufreq_dynamic: increasing CPU%i frequency to %i KHz\n",policy->cpu,cpufreq_dynamic_freqs[cpufreq_dynamic_status[policy->cpu].cur_freq_id]);
__cpufreq_driver_target(policy,cpufreq_dynamic_freqs[cpufreq_dynamic_status[policy->cpu].cur_freq_id],CPUFREQ_RELATION_L);
}
}
}
break;
case CPUFREQ_GOV_STOP:
printk(KERN_DEBUG "cpufreq_dynamic: Stopping dynamic governor.\n");
del_timer(&cpufreq_dynamic_timer[policy->cpu]);
break;
default:
break;
}
return 0;
}struct cpufreq_governor cpufreq_gov_dynamic = {
.name = "dynamic",
.governor = cpufreq_governor_dynamic,
.owner = THIS_MODULE,
};EXPORT_SYMBOL(cpufreq_gov_dynamic);
static int __init cpufreq_gov_dynamic_init(void)
{
return cpufreq_register_governor(&cpufreq_gov_dynamic);
}static void __exit cpufreq_gov_dynamic_exit(void)
{
unsigned int cur_cpu;
for (cur_cpu=0;cur_cpu<NR_CPUS;cur_cpu++) {
del_timer(&cpufreq_dynamic_timer[cur_cpu]);
}
cpufreq_unregister_governor(&cpufreq_gov_dynamic);
}MODULE_AUTHOR("Jonathan Anderson <[email protected]"); MODULE_DESCRIPTION("Dynamic CPUfreq policy governor"); MODULE_LICENSE("GPL");
//fs_initcall(cpufreq_gov_dynamic_init);module_init(cpufreq_gov_dynamic_init);module_exit(cpufreq_gov_dynamic_exit);
_______________________________________________
Cpufreq mailing list
[email protected]
http://www.linux.org.uk/mailman/listinfo/cpufreq
dynamic cpufreq governor
I've made some major changes. I would say that it's now 'not really ugly'. :)
I didn't bother changing the name, because of probable non-inclusion. Incredible learning experience though! Thanks!
Comments, questions, let me know.
jon anderson
drivers/cpufreq/Kconfig +26 -0
@@ -35,6 +35,21 @@ programm shall be able to set the CPU dynamically without having to enable the userspace governor manually. +config CPU_FREQ_DEFAULT_GOV_DYNAMIC + bool "dynamic" + select CPU_FREQ_GOV_DYNAMIC + help + Use the dynamic CPUFreq governor as the default. This will set + your CPU frequency based on how idle your CPU is at any given + time. + + Note: If your cpufreq driver's transition latency is too high, + this governor will have the same effect as the + 'performance' governor. (Set CPU to max, and leave it + there.) + + This is experimental. + endchoice config CPU_FREQ_GOV_PERFORMANCE
@@ -68,6 +83,17 @@ If in doubt, say Y. +config CPU_FREQ_GOV_DYNAMIC + tristate "Dynamic CPUFreq policy governor" + depends on CPU_FREQ + help + This CPUFreq governor checks CPU idle time 10 times a second, + and raises or lowers the CPU's speed as appropriate. This + governor is somewhat hesitant in dropping the CPUs frequency. + Frequency decreases happen 5 times slower than increases. + + It is best to say N for the moment. + config CPU_FREQ_24_API bool "/proc/sys/cpu/ interface (2.4. / OLD)" depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE
drivers/cpufreq/Makefile +1 -0
@@ -5,6 +5,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o +obj-$(CONFIG_CPU_FREQ_GOV_DYNAMIC) += cpufreq_dynamic.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
drivers/cpufreq/cpufreq_dynamic.c +258 -0
258 lines not on this page · raw message
include/linux/cpufreq.h +3 -0
@@ -303,6 +303,9 @@#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE )extern struct cpufreq_governor cpufreq_gov_userspace;#define CPUFREQ_DEFAULT_GOVERNOR &cpufreq_gov_userspace +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_DYNAMIC ) +extern struct cpufreq_governor cpufreq_gov_dynamic; +#define CPUFREQ_DEFAULT_GOVERNOR &cpufreq_gov_dynamic#endif /*********************************************************************
/*
* linux/drivers/cpufreq/cpufreq_dynamic.c
*
* 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.
*
* *** Experimental! ***
*
* TODO:
* - *** DONE *** Handle more than CPU 0. * - *** DONE *** Put in a better stepping mechanism. Room for improvement. * - *** DONE *** Make frequency tables for each CPU - I assume that it might be possible to have CPUs at different frequencies. * - *** DONE *** Step downwards slower than stepping upwards. * - Documenting in Documentation/cpufreq. Not needed (driver not to be committed.) * - *** DONE *** Documentation that is NOT crappy in this file. Need more. * - Proper debug printing. -> Need to figure out kernel conventions. * - *** DONE *** Handle transition latency. -> modprobe cpufreq_dynamic force_latency=1 * - *** DONE *** 2.6 Work queues are more appropriate. Switch to work queues from kernel timers. http://www.linuxjournal.com/article.php?sid=6916 convinced me.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/times.h>
#include <linux/workqueue.h>
#include <asm/timex.h>
#include <linux/kernel_stat.h>
/* How many times/second to poll */
#define POLL_FREQUENCY 10
/* Wait HZ/POLL_FREQUENCY jiffies in between polling. */ #define SLEEP_JIFFIES HZ/POLL_FREQUENCY
/* Definitions for transition latency */
#define MAX_LATENCY_SEC 1/POLL_FREQUENCY
#define MAX_LATENCY_NS MAX_LATENCY_SEC * (10^9)
/* How many ticks to wait before decreasing frequency. */ #define DEC_TICKS 5
/* Minimum and maximum idle thresholds, in percent */ #define MIN_IDLE 20
#define MAX_IDLE 80
/* Internal definitions for temporary frequency table. Ug-ly. */
#define FREQ_MAX -1 // unsigned int type -> 2's complement should mean this becomes max value
#define FREQ_MIN 0
/* How many frequency steps should we have */
#define FREQ_STEPS 5
/* FREQ_MULT is 1/4 with FREQ_STEPS = 5 -> max * 1/4 * step# = step_freq */ #define FREQ_MULT 1/(FREQ_STEPS - 1)
/* Only schedule the thing once. */
unsigned short cpufreq_gov_dynamic_work_queue_enabled = 0;
/* If transition latency is too high, this module can be force-enabled anyway. */ /* # modprobe cpufreq_dynamic force_latency=1 */
static unsigned short force_latency = 0; MODULE_PARM(force_latency,"i");
/** Should be a list, from FREQ_MAX to FREQ_MIN of CPU speeds in KHz. */ /* Managed semi-automatically in frequency steps. */ unsigned int cpufreq_dynamic_freqs[NR_CPUS][FREQ_STEPS];
/* Struct to hold CPU status information. */
typedef struct cpufreq_dynamic_cpuinfo {
unsigned long prev_idle; // CPU idle time, in jiffies.
unsigned long percent_idle; // Idle percentage.
unsigned short cur_freq_id; // What ID from the stupid table we're at.
unsigned short dec_wait; // Holds ticks until CPU frequency is to be bumped down. unsigned short enabled; // Don't do anything unless this governor is enabled on a specific CPU.
} cpufreq_dynamic_cpuinfo;
/* CPU Status information for all CPUs */ cpufreq_dynamic_cpuinfo cpufreq_dynamic_status[NR_CPUS];
/* Prototype for the work queue callback function. */ void cpufreq_gov_dynamic_work(void *mt);
/* Use work queues, rather than kernel timers. */ DECLARE_WORK(cpufreq_dynamic,cpufreq_gov_dynamic_work,NULL);
/**
* cpufreq_gov_dynamic_getidle
*
* Collects idle and idle percentage statistics about a given CPU.
*
* @param cur_cpu The cpu for which statistics are to be collected.
*
* Inline to save needless function call overhead...I think. If this is bad, please let me know. */
inline void cpufreq_gov_dynamic_getidle(unsigned int cur_cpu) {
//printk(KERN_DEBUG "cpufreq_dynamic: Calculating idle percentage for CPU%i\n",cur_cpu);
cpufreq_dynamic_status[cur_cpu].percent_idle = POLL_FREQUENCY * 100 * (kstat_cpu(cur_cpu).cpustat.idle - cpufreq_dynamic_status[cur_cpu].prev_idle) / HZ;
cpufreq_dynamic_status[cur_cpu].prev_idle = kstat_cpu(cur_cpu).cpustat.idle;
}/**
* cpufreq_gov_dynamic_work
*/
void cpufreq_gov_dynamic_work(void *mt) {
unsigned int cur_cpu;
for (cur_cpu = 0;cur_cpu < NR_CPUS;cur_cpu++) {
if (! cpufreq_dynamic_status[cur_cpu].enabled) {
cpufreq_gov_dynamic_getidle(cur_cpu);
//printk(KERN_DEBUG "cpufreq_dynamic: Timer callback for CPU%lu -> %lu%% idle.\n",cur_cpu,cpufreq_dynamic_status[cur_cpu].percent_idle);
if ((cpufreq_dynamic_status[cur_cpu].percent_idle > MAX_IDLE) || (cpufreq_dynamic_status[cur_cpu].percent_idle < MIN_IDLE)) {
cpufreq_governor(cur_cpu,CPUFREQ_GOV_LIMITS);
} else {
cpufreq_dynamic_status[cur_cpu].dec_wait = DEC_TICKS;
}
}
}
schedule_delayed_work(&cpufreq_dynamic,SLEEP_JIFFIES);
}/**
* cpufreq_gov_dynamic
*
* The governor itself.
*
* This function:
* 1) initializes CPU statistics, frequency tables, and the work queue.
* 2) gets called by the work queue callback if the frequency needs to be changed, changes frequencies
* 3) deletes work queues once the governor is stopped.
*/
static int cpufreq_gov_dynamic(struct cpufreq_policy *policy,unsigned int event) {
unsigned int step;
switch (event) {
case CPUFREQ_GOV_START:
printk(KERN_INFO "cpufreq_dynamic: Starting dynamic governor on CPU%i\n",policy->cpu);
if (policy->cpuinfo.transition_latency > MAX_LATENCY_NS) {
printk(KERN_NOTICE "cpufreq_dynamic: Transition latency exceeds maximum allowable latency\n");
if (! force_latency) {
printk(KERN_NOTICE "cpufreq_dynamic: Reverting to 'performance' behavior.\n");
__cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L);
return(0);
}
}
// Init info for policy CPU.
cpufreq_dynamic_status[policy->cpu].cur_freq_id = 0; // Set initially to max speed.
cpufreq_dynamic_status[policy->cpu].prev_idle = kstat_cpu(policy->cpu).cpustat.idle; // Set previous idle.
cpufreq_gov_dynamic_getidle(policy->cpu); // Get initial idle time.
// Calculate frequency steps.
cpufreq_dynamic_freqs[policy->cpu][0] = FREQ_MAX;
cpufreq_dynamic_freqs[policy->cpu][FREQ_STEPS-1] = FREQ_MIN;
printk(KERN_DEBUG "cpufreq_dynamic: Frequency steps: %u,",policy->max);
for (step=1;step<(FREQ_STEPS-1);step++) {
cpufreq_dynamic_freqs[policy->cpu][step] = policy->max * FREQ_MULT;
cpufreq_dynamic_freqs[policy->cpu][step] *= FREQ_STEPS - step - 1;
printk("%u,",cpufreq_dynamic_freqs[policy->cpu][step]);
}
printk("%u\n",policy->min);
// Enable the work queue timer.
if (! cpufreq_gov_dynamic_work_queue_enabled) {
schedule_work(&cpufreq_dynamic);
cpufreq_gov_dynamic_work_queue_enabled = 1;
}
//printk(KERN_DEBUG "cpufreq_dynamic: dynamic governor started on CPU%i.\n",policy->cpu);
// No need to change the frequency until we've collected SLEEP_JIFFIES worth of stats.
break;
case CPUFREQ_GOV_LIMITS:
// If the work queue hasn't been enabled, that would mean that the cpufreq driver hasn't passed the latency check, and the user hasn't forced the latency check to succeed, so we just revert to 'performance' behavior. This governor should be able to be used as a default governor.
if (! cpufreq_gov_dynamic_work_queue_enabled) {
__cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L);
return(0);
}
// If we're too idle, we want to decrease the CPU frequency.
if (cpufreq_dynamic_status[policy->cpu].percent_idle > MAX_IDLE) {
if (--cpufreq_dynamic_status[policy->cpu].dec_wait > 0) {
// Waiting a little before dropping frequency.
// Leaving room for something here, if need be.
// Don't do anything if we're already at minimum frequency.
} else if (cpufreq_dynamic_freqs[policy->cpu][cpufreq_dynamic_status[policy->cpu].cur_freq_id] != FREQ_MIN) {
cpufreq_dynamic_status[policy->cpu].dec_wait = DEC_TICKS;
// If we're to step down to FREQ_MIN, use the policy's value.
if (cpufreq_dynamic_freqs[policy->cpu][++cpufreq_dynamic_status[policy->cpu].cur_freq_id] == FREQ_MIN) {
//printk(KERN_INFO "cpufreq_dynamic: Setting CPU%i to minimum frequency: %u KHz\n",policy->cpu,policy->min);
__cpufreq_driver_target(policy,policy->min,CPUFREQ_RELATION_L);
} else {
//printk(KERN_INFO "cpufreq_dynamic: decreasing CPU%i frequency to %li KHz\n",policy->cpu,cpufreq_dynamic_freqs[policy->cpu][cpufreq_dynamic_status[policy->cpu].cur_freq_id]);
__cpufreq_driver_target(policy,cpufreq_dynamic_freqs[policy->cpu][cpufreq_dynamic_status[policy->cpu].cur_freq_id],CPUFREQ_RELATION_L);
}
}
// If we're not idle enough, we want to increase the CPU frequency.
} else if (cpufreq_dynamic_status[policy->cpu].percent_idle < MIN_IDLE) {
cpufreq_dynamic_status[policy->cpu].dec_wait = DEC_TICKS;
// Don't do anything if we're already at the max frequency.
if (cpufreq_dynamic_freqs[policy->cpu][cpufreq_dynamic_status[policy->cpu].cur_freq_id] != FREQ_MAX) {
// If we're to step up to FREQ_MAX, use the policy's value.
if (cpufreq_dynamic_freqs[policy->cpu][--cpufreq_dynamic_status[policy->cpu].cur_freq_id] == FREQ_MAX) {
//printk(KERN_INFO "cpufreq_dynamic: Setting CPU%i to maximum frequency: %u KHz\n",policy->cpu,policy->max);
__cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L);
} else {
//printk("cpufreq_dynamic: increasing CPU%i frequency to %li KHz\n",policy->cpu,cpufreq_dynamic_freqs[policy->cpu][cpufreq_dynamic_status[policy->cpu].cur_freq_id]);
__cpufreq_driver_target(policy,cpufreq_dynamic_freqs[policy->cpu][cpufreq_dynamic_status[policy->cpu].cur_freq_id],CPUFREQ_RELATION_L);
}
}
}
break;
case CPUFREQ_GOV_STOP:
//printk(KERN_DEBUG "cpufreq_dynamic: Stopping dynamic governor.\n");
if (cpufreq_gov_dynamic_work_queue_enabled) {
cancel_delayed_work(&cpufreq_dynamic);
cpufreq_gov_dynamic_work_queue_enabled = 0;
}
break;
default:
break;
}
return 0;
}// CPUFreq governor struct
struct cpufreq_governor cpufreq_governor_dynamic = {
.name = "dynamic",
.governor = cpufreq_gov_dynamic,
.owner = THIS_MODULE,
};EXPORT_SYMBOL(cpufreq_gov_dynamic);
// Module init
static int __init cpufreq_gov_dynamic_init(void)
{
return cpufreq_register_governor(&cpufreq_governor_dynamic);
}// Module exit
static void __exit cpufreq_gov_dynamic_exit(void)
{
cpufreq_unregister_governor(&cpufreq_governor_dynamic);
}MODULE_AUTHOR("Jonathan Anderson <[email protected]"); MODULE_DESCRIPTION("Dynamic CPUfreq policy governor"); MODULE_LICENSE("GPL");
module_init(cpufreq_gov_dynamic_init);module_exit(cpufreq_gov_dynamic_exit);
_______________________________________________
Cpufreq mailing list
[email protected]
http://www.linux.org.uk/mailman/listinfo/cpufreq
Re: dynamic cpufreq governor
In reply to Jon AndersonQuoted 2 lines · Jon Anderson
Thanks for your input on this one. I have a few questions...If they're out of place, let me know, and I'll shut up. :)
Discussion of cpufreq governors is never out of place on the cpufreq mailing list, IMHO.
Quoted 8 lines · Dominik Brodowski
Quoted 6 lines
It seems to me that your governor uses exactly the same algorithm as the one proposed by Venkatesh Pallipadi, but maybe with some slightly different default values. So, I'd prefer if only one of these two were merged into CVS and/or the kernel. And, for that matter, I prefer Venkatesh's code at the moment as it's cleaner, supports different CPUs, ... - but see below for some details.
Quoted 4 lines
I would actually say at this moment that I would probably prefer Venkatesh's code be merged over my own, for basically the same reasons. (I'm a student, and very much an amateur coder - a beginner in the kernel arena. This is very much a learning experience for me. :)
Hey, I'm a student myself, and cpufreq was one big learning experience for me, too!
Quoted 6 lines
I just took a look through it, and it does seem to use a similar algorithm, though I don't understand some elements of it. Implementation seems to be drastically different. I don't understand in what way the two drivers differ in terms of CPU support. Please elaborate. (I don't understand how my governor doesn't support different CPUs - I thought it did.)
It had not supported multiple CPUs, but it seems to do so now [haven't looked at the code yet, will do so in a moment].
Quoted 3 lines
I did actually make a few major changes yesterday, the new file is attached. It now should support SMP, and should now step downwards at 1/5th the speed of stepping up.
This changes causes your governor to use a different algorithm to Venkatesh's one, so one of the reasons why I preferred his governor over your is gone :-)
Quoted 2 lines
Since this code won't be merged, should I continue to post my changes here, or is that inappropriate?
a) I don't decide what's merged and what's not, that's Dave's job as maintainer of cpufreq.
b) IMO the previously posted code wasn't good enough to be merged -- this may have changed in the meantime [again, I haven't looked at the code yet], or it may change to do so in future. So there is still a chance it will eventually be merged.
c) Even if you say "I don't want this to be part of the kernel", it still would be a cpufreq governor which IMO can be discussed here.
Quoted 9 lines
Quoted 4 lines
That's not entirely correct -- it just has rather strict limits on the "transition latency", and several cpfureq drivers don't set the proper transition latency but the dummy value "
CPUFREQ_ETERNAL" which disables dynamic governors [in kernelspace].That is I noticed that in the code originally. I commented out the latency check, so I could try the dbs module with the P4-clockmod driver. This results in a machine lockup, which is why I originally wrote the "dynamic" module.
I'll investigate into the p4-clockmod lockup soon; I did not experience it on my speedstep-smi testing system, though...
Quoted 3 lines
I assume the lockup is because the dbs
module seems to poll HZ times a second (10 times as often as mine.), which would overwhelm the p4-clockmod driver.
Actually, there is no information by Intel (yet) on how often p4-clockmod transitions may be done, so this might or might not be true.
Quoted 4 lines
I guess it just seems a waste to have to compile in two governors, when the static governor would only be in use for the short period of time until a boot script sets up whichever dynamic governor. Point taken though.
Then compile the other one as a module. And cpufreq_performance.o is 3032 bytes large... [I'm surprised by the difference to cpufreq_powersave.o [2284 bytes], though... anybody interested in investigating the difference?]
Quoted 1 line
I suppose the name is moot though, since it won't be merged anyway.
see above
Quoted 1 line
Perhaps
cpufreq_dynamic_uglyfor now. ;)
"anderson" maybe?
Quoted 2 lines
What happens when a governor attempts to set a speed that the driver can't handle? Is it driver independant?
There seems to be some confusion about this [I really need to write a cpufreq HOWTO somewhen soon...]:
If a target frequency isn't supported by the CPU, the "relation" argument decides what is done instead:
if it's CPUFREQ_RELATION_L, the next higher frequency supported by the CPU is used [L stands for "lowest but not lower than"]
if it's CPUFREQ_RELATION_H, the next lower frequency supported by the CPU is used [H stands for "highest but not higher than"]
In most cases you probably want to guarantee a specific level of processing power, say n MHz. You then provide CPUFREQ_RELATION_L as argument.
Quoted 3 lines
Is there a better way to search for available frequencies? Would it be worthwhile to set a number of defined steps, say 'max', '2/3 max', '1/3 max', 'min'
IMHO yes.
Quoted 1 line
I didn't bother changing the name, because of probable non-inclusion.
See above.
Quoted 1 line
+config
CPU_FREQ_DEFAULT_GOV_DYNAMIC
already discussed
Review
Quoted 2 lines
(quoted diff) +2
-0+/* FREQ_MULT is 1/4 with FREQ_STEPS = 5 -> max * 1/4 * step# = step_freq */ +#define FREQ_MULT 1/(FREQ_STEPS - 1)
nice :-)
Review
Quoted 4 lines
(quoted diff) +4
-0+/* If transition latency is too high, this module can be force-enabled anyway. */ +/* # modprobe cpufreq_dynamic force_latency=1 */ +static unsigned short force_latency = 0; +MODULE_PARM(force_latency,"i");
This might allow users to break their hardware. That's a no-go. Better provide a module parameter which lets you set the sleep_interval, which in turn changes the latency limit in this file.
Review
Quoted 3 lines
(quoted diff) +3
-0+/** Should be a list, from FREQ_MAX to FREQ_MIN of CPU speeds in KHz. */ +/* Managed semi-automatically in frequency steps. */ +unsigned int cpufreq_dynamic_freqs[NR_CPUS][FREQ_STEPS];
Can you move this into cpufreq_dynamic_cpuinfo, please?
Review
Quoted 2 lines
(quoted diff) +2
-0+/* Struct to hold CPU status information. */ +typedef struct cpufreq_dynamic_cpuinfo {
No typedef's please.
Review
Quoted 2 lines
(quoted diff) +2
-0+/* CPU Status information for all CPUs */ +cpufreq_dynamic_cpuinfo cpufreq_dynamic_status[NR_CPUS];
Can be defined as static.
Review
Quoted 2 lines
(quoted diff) +2
-0+/* Prototype for the work queue callback function. */ +void cpufreq_gov_dynamic_work(void *mt);
Can be defined as static, too.
Review
Quoted 2 lines
(quoted diff) +2
-0+/* Use work queues, rather than kernel timers. */ +DECLARE_WORK(cpufreq_dynamic,cpufreq_gov_dynamic_work,NULL);
I think this can be static, too.
Review
Quoted 2 lines
(quoted diff) +2
-0+ for (cur_cpu = 0;cur_cpu < NR_CPUS;cur_cpu++) { + if (! cpufreq_dynamic_status[cur_cpu].enabled) {
Usually enabled != 0 means "enabled", contrary to your implementation.
Review
Quoted 2 lines
(quoted diff) +2
-0+ if ((cpufreq_dynamic_status[cur_cpu].percent_idle > MAX_IDLE) || (cpufreq_dynamic_status[cur_cpu].percent_idle < MIN_IDLE)) { + cpufreq_governor(cur_cpu,CPUFREQ_GOV_LIMITS);
I'll get to that later...
Review
Quoted 8 lines
(quoted diff) +8
-0+ case CPUFREQ_GOV_START: + printk(KERN_INFO "cpufreq_dynamic: Starting dynamic governor on CPU%i\n",policy->cpu); + + if (policy->cpuinfo.transition_latency > MAX_LATENCY_NS) { + printk(KERN_NOTICE "cpufreq_dynamic: Transition latency exceeds maximum allowable latency\n"); + if (! force_latency) { + printk(KERN_NOTICE "cpufreq_dynamic: Reverting to 'performance' behavior.\n"); + __cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L);
You shouldn't need this last call. Also, please return an error as result, not 0.
drivers/cpufreq/cpufreq_dynamic.c line 100
Quoted 1 line
drivers/cpufreq/cpufreq_dynamic.c +1
-0+case CPUFREQ_GOV_LIMITS :
I don't see you handling the case where policy->min and policy->max change. That's usually when CPUFREQ_GOV_LIMITS gets called. Also, I don't see the reason why you do the acutal setting here, after the indirect call from the workqueue work function. You can do all the stuff you're doing here directly there, as long as you use cpufreq_driver_target instead of __cpufreq_driver_target.
Other than that, your code is getting more and more interesting. Need to test it out somewhen.
Dominik