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

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.