Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 23 Dec 2007 18:01:56 +0200
From:      Andrey <andrey.kosachenko@gmail.com>
To:        freebsd-acpi@FreeBSD.org
Subject:   powerd doesn't decrease CPU frequency in some cases
Message-ID:  <476E8674.5000303@gmail.com>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------050801090600030805090806
Content-Type: text/plain; charset=windows-1251; format=flowed
Content-Transfer-Encoding: 7bit

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@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@beastie][/home/silent]sudo sysctl dev.cpu.0.freq=2000
dev.cpu.0.freq: 300 -> 2001
Let check:
[silent@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

--------------050801090600030805090806
Content-Type: text/plain;
 name="powerd.c.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="powerd.c.diff"

--- /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;

--------------050801090600030805090806--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?476E8674.5000303>