Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 24 Dec 2007 11:15:11 +0200
From:      Aragon Gouveia <aragon@phat.za.net>
To:        Andrey <andrey.kosachenko@gmail.com>
Cc:        freebsd-acpi@FreeBSD.org
Subject:   Re: powerd doesn't decrease CPU frequency in some cases
Message-ID:  <20071224091511.GA25786@phat.za.net>
In-Reply-To: <476E8674.5000303@gmail.com>
References:  <476E8674.5000303@gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--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 <andrey.kosachenko@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@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--



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