From owner-freebsd-acpi@FreeBSD.ORG Mon Dec 24 09:43:08 2007 Return-Path: Delivered-To: freebsd-acpi@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D434116A41B for ; Mon, 24 Dec 2007 09:43:08 +0000 (UTC) (envelope-from aragon@phat.za.net) Received: from mail.geek.sh (decoder.geek.sh [196.36.198.81]) by mx1.freebsd.org (Postfix) with ESMTP id 9D29013C4CE for ; Mon, 24 Dec 2007 09:43:07 +0000 (UTC) (envelope-from aragon@phat.za.net) Received: by mail.geek.sh (Postfix, from userid 1000) id 3DAAA24D29; Mon, 24 Dec 2007 11:15:11 +0200 (SAST) Date: Mon, 24 Dec 2007 11:15:11 +0200 From: Aragon Gouveia To: Andrey Message-ID: <20071224091511.GA25786@phat.za.net> References: <476E8674.5000303@gmail.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="17pEHd4RhPHOinZp" Content-Disposition: inline In-Reply-To: <476E8674.5000303@gmail.com> User-Agent: Mutt/1.4i X-Operating-System: FreeBSD 4.10-RELEASE-p2 i386 Cc: freebsd-acpi@FreeBSD.org Subject: Re: powerd doesn't decrease CPU frequency in some cases X-BeenThere: freebsd-acpi@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: ACPI and power management development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 24 Dec 2007 09:43:08 -0000 --17pEHd4RhPHOinZp Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 | [ 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@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 > --- /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@freebsd.org mailing list > http://lists.freebsd.org/mailman/listinfo/freebsd-acpi > To unsubscribe, send any mail to "freebsd-acpi-unsubscribe@freebsd.org" --17pEHd4RhPHOinZp Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="powerd-rmfreq.diff" --- 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); --17pEHd4RhPHOinZp--