powerd doesn't decrease CPU frequency in some cases
Andrey
andrey.kosachenko at gmail.com
Sun Dec 23 08:30:54 PST 2007
Good time of the day.
I've noticed that powerd isn't able to decrease CPU frequency on my
laptop (HP Compaq 6710b) as soon as frequency gets highest level.
I've pottered a bit in the sources and it seems found the root of the issue.
So those who are interested in the subject let consider it.
For instance my system reports the following frequency levels:
[silent at beastie][/home/silent]sysctl dev.cpu.0.freq_levels
dev.cpu.0.freq_levels: 2001/35000 2000/35000 1750/30625 1600/25000
1400/21875 1200/16000 1050/14000 900/12000 800/14000 700/12250 600/10500
500/8750 400/7000 300/5250
If I try to adjust current frequency to 2000 MHz then I'll get:
[silent at beastie][/home/silent]sudo sysctl dev.cpu.0.freq=2000
dev.cpu.0.freq: 300 -> 2001
Let check:
[silent at beastie][/home/silent]sysctl dev.cpu.0.freq
dev.cpu.0.freq: 2001
Thus, as you can see, I have level "2000" which system reports me but
actually I can't to adjust those one exactly because it silently becomes
"2001"
Well... If I'm not mistaken powerd calculates the current "CPU idle
mark" and if it is more then adopted value (by default 90%) then it
shifts CPU frequency value 1 step down. In my case powerd sticks at
"2001". It is obvious because when powerd decreases CPU frequency from
the highest frequency level we'll get the following scenario:
+-----------------------+
| dev.cpu.0.freq=2001 +--<-+
+-----------+-----------+ |
| |
Y |
+-----------+-----------+ |
| "CPU idle" > 90% | |
+-----------+-----------+ |
| |
Y ^
+-----------+-----------+ ^
| powerd shifts freq. | ^
| 1 step down: | |
| "2001" -> "2000" | |
+-----------+-----------+ |
| |
Y |
+-----------+-----------+ |
| actually we have here | |
| dev.cpu.0.freq == 2001+-->-+
+-----------------------+
According to the things mentioned above I've came to conclusion that in
my case it is not a good idea to rely on frequency levels reported by
the system. (Also I saw many sysctl mibs (dev.cpu.0.freq) of many other
people. And there were "strange" frequency levels like "2000" and
"2001". Of course I can't state that their systems' behaviors fit my
case. But still...)
So the simple way out I see is to teach powerd recognize "fake"
frequency levels. Here I suggest a very simple workaround (and may be
quite ugly... sorry I'm not sure if it is my cup of tee) which allows me
to overcome the issue. And I hope it can be useful for smb. else.
Also I'd like to hear opinions of others. May be there exists another
and simpler way to overcome an issue or even I've missed something or
not aware of something.
Thank you.
--
Sincerely,
Andrey Kosachenko
-------------- next part --------------
--- /usr/src/usr.sbin/powerd/powerd.c 2007-06-13 22:05:11.000000000 +0300
+++ /home/silent/Data/powerd/powerd.c 2007-12-23 16:52:50.000000000 +0200
@@ -79,6 +79,8 @@
static int read_usage_times(long *idle, long *total);
static int read_freqs(int *numfreqs, int **freqs, int **power);
+static int reduct_freqs(int *numfreqs, int **freqs, int **power);
+static int get_freq(void);
static int set_freq(int freq);
static void acline_init(void);
static void acline_read(void);
@@ -189,6 +191,68 @@
return (0);
}
+
+static int
+reduct_freqs(int *numfreqs, int **freqs, int **power)
+{
+ int i = 0;
+ int k = 0;
+ int curfreq = 0;
+ int mem_frequency = get_freq();
+ for (i = 0; i < *numfreqs; i++) {
+ if (vflag) {
+ printf("Checking frequency %5d - ", (*freqs)[i]);
+ }
+
+ if (set_freq((*freqs)[i]) == 0) {
+ curfreq = get_freq();
+ if (curfreq > 0 && curfreq == (*freqs)[i]) {
+ if (vflag) {
+ printf("[ OK ]\n");
+ }
+ }
+ else {
+ if (vflag) {
+ printf("[FAIL] -> excluding frequency from levels list\n");
+ }
+
+ --(*numfreqs);
+ for (k = i; k < (*numfreqs); k++) {
+ (*freqs)[k] = (*freqs)[k + 1];
+ (*power)[k] = (*power)[k + 1];
+ }
+
+ (*freqs)[(*numfreqs)] = 0;
+ (*power)[(*numfreqs)] = 0;
+ }
+ }
+ }
+
+ if (mem_frequency > 0) {
+ set_freq(mem_frequency);
+ }
+
+ return (0);
+}
+
+
+static int
+get_freq(void)
+{
+ int curfreq = 0;
+ size_t len;
+
+ len = sizeof(curfreq);
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
+ if (vflag)
+ warn("error reading current CPU frequency");
+ return 0;
+ }
+
+ return (curfreq);
+}
+
+
static int
set_freq(int freq)
{
@@ -452,7 +516,11 @@
err(1, "read_usage_times");
if (read_freqs(&numfreqs, &freqs, &mwatts))
err(1, "error reading supported CPU frequencies");
-
+
+ if (reduct_freqs(&numfreqs, &freqs, &mwatts) != 0) {
+ warn("cannot exclude lame frequencies from list");
+ }
+
/* Run in the background unless in verbose mode. */
if (!vflag) {
pid_t otherpid;
More information about the freebsd-acpi
mailing list