powerd doesn't decrease CPU frequency in some cases
Aragon Gouveia
aragon at phat.za.net
Mon Dec 24 01:43:08 PST 2007
Hi,
I recently experienced the exact same behaviour as you are experiencing.
I'm running an HP Pavilion dv2610ei. In my case, dev.cpu.0.freq_levels
shows frequencies 2201 and 2200 (2.2 GHz Core2Duo CPU), and setting
frequency to 2200 jumps to 2201. This was completely messing with powerd as
it does not expect a frequency change to jump to another level.
I wrote a powerd patch a while ago which adds a check condition and removes
a frequency level if it fails to get set. It's attached. Please feel free
to test it and report back. :)
Regards,
Aragon
| By Andrey <andrey.kosachenko at gmail.com>
| [ 2007-12-23 18:31 +0200 ]
> 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
> --- /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;
> _______________________________________________
> freebsd-acpi at freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-acpi
> To unsubscribe, send any mail to "freebsd-acpi-unsubscribe at freebsd.org"
-------------- next part --------------
--- powerd.c.orig 2007-06-13 21:05:11.000000000 +0200
+++ powerd.c 2007-11-10 23:59:09.000000000 +0200
@@ -79,6 +79,7 @@
static int read_usage_times(long *idle, long *total);
static int read_freqs(int *numfreqs, int **freqs, int **power);
+static void rm_freq(int *numfreqs, int rmfreq, int **freqs, int **power);
static int set_freq(int freq);
static void acline_init(void);
static void acline_read(void);
@@ -189,6 +190,41 @@
return (0);
}
+static void
+rm_freq(int *numfreqs, int rmfreq, int **freqs, int **power)
+{
+ int i, j=0, newfreqs[(*numfreqs)-1], newpower[(*numfreqs)-1];
+
+ if (*numfreqs < 2) {
+ // nothing more we can do
+ free(*freqs);
+ free(*power);
+ errx(1, "No more CPU frequencies to set");
+ }
+
+ for (i=0; i<*numfreqs; i++) {
+ if (i == rmfreq) continue;
+ newfreqs[j] = (*freqs)[i];
+ newpower[j] = (*power)[i];
+ j++;
+ }
+
+ free(*freqs);
+ free(*power);
+ (*numfreqs)--;
+ if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL)
+ err(1, "error removing CPU frequency");
+ if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
+ free(*freqs);
+ err(1, "error removing CPU frequency");
+ }
+
+ for (i=0; i<=j; i++) {
+ (*freqs)[i] = newfreqs[i];
+ (*power)[i] = newpower[i];
+ }
+}
+
static int
set_freq(int freq)
{
@@ -555,6 +591,13 @@
freqs[numfreqs - 1]);
continue;
}
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) == 0) {
+ if (curfreq != freqs[numfreqs-1]) {
+ if (vflag)
+ printf("error setting CPU frequency %d, removing from list\n", freqs[numfreqs-1]);
+ rm_freq(&numfreqs, numfreqs-1, &freqs, &mwatts);
+ }
+ }
}
continue;
}
@@ -573,6 +616,13 @@
freqs[0]);
continue;
}
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) == 0) {
+ if (curfreq != freqs[0]) {
+ if (vflag)
+ printf("error setting CPU frequency %d, removing from list\n", freqs[0]);
+ rm_freq(&numfreqs, 0, &freqs, &mwatts);
+ }
+ }
}
continue;
}
@@ -605,6 +655,15 @@
if (set_freq(freqs[i]))
warn("error setting CPU frequency %d",
freqs[i]);
+ // Check if it actually got set
+ len = sizeof(curfreq);
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) == 0) {
+ if (curfreq != freqs[i]) {
+ if (vflag)
+ printf("error setting CPU frequency %d, removing from list\n", freqs[i]);
+ rm_freq(&numfreqs, i, &freqs, &mwatts);
+ }
+ }
} else if (idle > (total * cpu_idle_mark) / 100 &&
curfreq > freqs[numfreqs - 1]) {
i++;
@@ -616,6 +675,15 @@
if (set_freq(freqs[i]) != 0)
warn("error setting CPU frequency %d",
freqs[i]);
+ // Check if it actually got set
+ len = sizeof(curfreq);
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) == 0) {
+ if (curfreq != freqs[i]) {
+ if (vflag)
+ printf("error setting CPU frequency %d, removing from list\n", freqs[i]);
+ rm_freq(&numfreqs, i, &freqs, &mwatts);
+ }
+ }
}
}
free(freqs);
More information about the freebsd-acpi
mailing list