All of lore.kernel.org
 help / color / mirror / Atom feed
* CPUFreq dynamic speed governor, take 2.
@ 2003-10-31  7:04 Jon Anderson
  2003-11-01 18:09 ` Dominik Brodowski
  0 siblings, 1 reply; 5+ messages in thread
From: Jon Anderson @ 2003-10-31  7:04 UTC (permalink / raw)
  To: cpufreq

[-- Attachment #1: Type: text/plain, Size: 1249 bytes --]

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



[-- Attachment #2: 2.6.0-test9_cpufreq_dynamic.patch --]
[-- Type: text/x-patch, Size: 8341 bytes --]

diff -Naur linux-2.6.0-test9.orig/drivers/cpufreq/Kconfig linux-2.6.0-test9/drivers/cpufreq/Kconfig
--- linux-2.6.0-test9.orig/drivers/cpufreq/Kconfig	2003-10-25 14:44:40.000000000 -0400
+++ linux-2.6.0-test9/drivers/cpufreq/Kconfig	2003-10-30 21:29:38.509019128 -0500
@@ -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
diff -Naur linux-2.6.0-test9.orig/drivers/cpufreq/Makefile linux-2.6.0-test9/drivers/cpufreq/Makefile
--- linux-2.6.0-test9.orig/drivers/cpufreq/Makefile	2003-10-25 14:45:05.000000000 -0400
+++ linux-2.6.0-test9/drivers/cpufreq/Makefile	2003-10-30 21:30:49.939160096 -0500
@@ -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
diff -Naur linux-2.6.0-test9.orig/drivers/cpufreq/cpufreq_dynamic.c linux-2.6.0-test9/drivers/cpufreq/cpufreq_dynamic.c
--- linux-2.6.0-test9.orig/drivers/cpufreq/cpufreq_dynamic.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.0-test9/drivers/cpufreq/cpufreq_dynamic.c	2003-10-30 21:05:32.119903696 -0500
@@ -0,0 +1,154 @@
+/*
+ *  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,
+};
+
+
+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 <janderson@janderson.ca");
+MODULE_DESCRIPTION("Dynamic CPUfreq policy governor");
+MODULE_LICENSE("GPL");
+
+module_init(cpufreq_gov_dynamic_init);
+module_exit(cpufreq_gov_dynamic_exit);
diff -Naur linux-2.6.0-test9.orig/include/linux/cpufreq.h linux-2.6.0-test9/include/linux/cpufreq.h
--- linux-2.6.0-test9.orig/include/linux/cpufreq.h	2003-10-25 14:43:56.000000000 -0400
+++ linux-2.6.0-test9/include/linux/cpufreq.h	2003-10-30 21:33:11.784596312 -0500
@@ -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
 
 /*********************************************************************

[-- Attachment #3: cpufreq_dynamic.c --]
[-- Type: text/x-c, Size: 4731 bytes --]

/*
 *  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 <janderson@janderson.ca");
MODULE_DESCRIPTION("Dynamic CPUfreq policy governor");
MODULE_LICENSE("GPL");

module_init(cpufreq_gov_dynamic_init);
module_exit(cpufreq_gov_dynamic_exit);

[-- Attachment #4: Type: text/plain, Size: 143 bytes --]

_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: CPUFreq dynamic speed governor, take 2.
  2003-10-31  7:04 CPUFreq dynamic speed governor, take 2 Jon Anderson
@ 2003-11-01 18:09 ` Dominik Brodowski
  2003-11-02  2:37   ` Jon Anderson
  0 siblings, 1 reply; 5+ messages in thread
From: Dominik Brodowski @ 2003-11-01 18:09 UTC (permalink / raw)
  To: Jon Anderson; +Cc: 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.
 
On Fri, Oct 31, 2003 at 02:04:08AM -0500, Jon Anderson 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 :)

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].

> 2) People seem to prefer userspace tools to govern frequency.
Well, I surely don't. I prefer kernelspace.

> +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.

> +/** 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 \
> +};

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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: CPUFreq dynamic speed governor, take 2.
  2003-11-01 18:09 ` Dominik Brodowski
@ 2003-11-02  2:37   ` Jon Anderson
  2003-11-03  4:10     ` dynamic cpufreq governor Jon Anderson
  0 siblings, 1 reply; 5+ messages in thread
From: Jon Anderson @ 2003-11-02  2:37 UTC (permalink / raw)
  To: cpufreq

[-- Attachment #1: Type: text/plain, Size: 4775 bytes --]

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. :)

On Sat, 2003-11-01 at 13:09, Dominik Brodowski wrote:
> 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?  

> 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. 

> > 2) People seem to prefer userspace tools to govern frequency.
> Well, I surely don't. I prefer kernelspace.

Woo! Strongly agreed!

> > +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. ;)

> 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. 

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? 

> 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

[-- Attachment #2: cpufreq_dynamic.c --]
[-- Type: text/x-c, Size: 8275 bytes --]

/*
 *  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 <janderson@janderson.ca");
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);

[-- Attachment #3: Type: text/plain, Size: 143 bytes --]

_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq

^ permalink raw reply	[flat|nested] 5+ messages in thread

* dynamic cpufreq governor
@ 2003-11-03  4:10     ` Jon Anderson
  2003-11-03 22:55       ` Dominik Brodowski
  0 siblings, 1 reply; 5+ messages in thread
From: Jon Anderson @ 2003-11-03  4:10 UTC (permalink / raw)
  To: cpufreq

[-- Attachment #1: Type: text/plain, Size: 245 bytes --]

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

[-- Attachment #2: 2.6.0-test9_cpufreq_dynamic2.patch --]
[-- Type: text/x-patch, Size: 14121 bytes --]

diff -Naur linux-2.6.0-test9.orig/drivers/cpufreq/Kconfig linux-2.6.0-test9/drivers/cpufreq/Kconfig
--- linux-2.6.0-test9.orig/drivers/cpufreq/Kconfig	2003-10-25 14:44:40.000000000 -0400
+++ linux-2.6.0-test9/drivers/cpufreq/Kconfig	2003-11-02 22:20:42.627396856 -0500
@@ -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
diff -Naur linux-2.6.0-test9.orig/drivers/cpufreq/Makefile linux-2.6.0-test9/drivers/cpufreq/Makefile
--- linux-2.6.0-test9.orig/drivers/cpufreq/Makefile	2003-10-25 14:45:05.000000000 -0400
+++ linux-2.6.0-test9/drivers/cpufreq/Makefile	2003-11-02 22:20:58.350006656 -0500
@@ -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
diff -Naur linux-2.6.0-test9.orig/drivers/cpufreq/cpufreq_dynamic.c linux-2.6.0-test9/drivers/cpufreq/cpufreq_dynamic.c
--- linux-2.6.0-test9.orig/drivers/cpufreq/cpufreq_dynamic.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.0-test9/drivers/cpufreq/cpufreq_dynamic.c	2003-11-02 22:42:11.879272848 -0500
@@ -0,0 +1,258 @@
+/*
+ *  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 <janderson@janderson.ca");
+MODULE_DESCRIPTION("Dynamic CPUfreq policy governor");
+MODULE_LICENSE("GPL");
+
+module_init(cpufreq_gov_dynamic_init);
+module_exit(cpufreq_gov_dynamic_exit);
diff -Naur linux-2.6.0-test9.orig/include/linux/cpufreq.h linux-2.6.0-test9/include/linux/cpufreq.h
--- linux-2.6.0-test9.orig/include/linux/cpufreq.h	2003-10-25 14:43:56.000000000 -0400
+++ linux-2.6.0-test9/include/linux/cpufreq.h	2003-10-30 21:33:11.784596312 -0500
@@ -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
 
 /*********************************************************************

[-- Attachment #3: cpufreq_dynamic.c --]
[-- Type: text/x-c, Size: 10614 bytes --]

/*
 *  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 <janderson@janderson.ca");
MODULE_DESCRIPTION("Dynamic CPUfreq policy governor");
MODULE_LICENSE("GPL");

module_init(cpufreq_gov_dynamic_init);
module_exit(cpufreq_gov_dynamic_exit);

[-- Attachment #4: Type: text/plain, Size: 143 bytes --]

_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: dynamic cpufreq governor
  2003-11-03  4:10     ` dynamic cpufreq governor Jon Anderson
@ 2003-11-03 22:55       ` Dominik Brodowski
  0 siblings, 0 replies; 5+ messages in thread
From: Dominik Brodowski @ 2003-11-03 22:55 UTC (permalink / raw)
  To: Jon Anderson; +Cc: cpufreq

On Sat, Nov 01, 2003 at 09:37:42PM -0500, Jon Anderson wrote:
> 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.

> On Sat, 2003-11-01 at 13:09, Dominik Brodowski wrote:
> > 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. :)

Hey, I'm a student myself, and cpufreq was one big learning experience for
me, too!

> 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].

> 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 :-) 

> 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.

> > 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...

> 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.

> 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?]

> I suppose the name is moot though, since it won't be merged anyway.
see above
> Perhaps cpufreq_dynamic_ugly for now. ;)
"anderson" maybe?

> 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. 

> 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.

> I didn't bother changing the name, because of probable non-inclusion.
See above.

> +config CPU_FREQ_DEFAULT_GOV_DYNAMIC
already discussed

> +/* FREQ_MULT is 1/4 with FREQ_STEPS = 5 -> max * 1/4 * step# = step_freq  */
> +#define FREQ_MULT 1/(FREQ_STEPS - 1)
nice :-)

> +/* 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.

> +/** 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?

> +/* Struct to hold CPU status information. */
> +typedef struct cpufreq_dynamic_cpuinfo {
No typedef's please.

> +/* CPU Status information for all CPUs */
> +cpufreq_dynamic_cpuinfo cpufreq_dynamic_status[NR_CPUS];
Can be defined as static.

> +/* Prototype for the work queue callback function. */
> +void cpufreq_gov_dynamic_work(void *mt);
Can be defined as static, too.

> +/* Use work queues, rather than kernel timers. */
> +DECLARE_WORK(cpufreq_dynamic,cpufreq_gov_dynamic_work,NULL);
I think this can be static, too.

> +	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.

> +			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...

> +	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.

> +	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

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2003-11-03 22:55 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-10-31  7:04 CPUFreq dynamic speed governor, take 2 Jon Anderson
2003-11-01 18:09 ` Dominik Brodowski
2003-11-02  2:37   ` Jon Anderson
2003-11-03  4:10     ` dynamic cpufreq governor Jon Anderson
2003-11-03 22:55       ` Dominik Brodowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.