svn commit: r185050 - head/usr.sbin/powerd

Robert Noland rnoland at FreeBSD.org
Tue Nov 18 19:28:44 PST 2008


On Tue, 2008-11-18 at 13:24 +0000, Alexander Motin wrote:
> Author: mav
> Date: Tue Nov 18 13:24:38 2008
> New Revision: 185050
> URL: http://svn.freebsd.org/changeset/base/185050
> 
> Log:
>   Set of powerd enchancements:
>   
>   1. Make it more SMP polite. Previous version uses average CPU load that
>   often leads to load underestimation. It make powerd with default
>   configuration unusable on systems with more then 2 CPUs. I propose to use
>   summary load instead of average one. IMO this is the best we can do without
>   specially tuned scheduler. Also as soon as measuring total load on SMP
>   systems is more useful then total idle, I have switched to it.
>   
>   2. Make powerd's operation independent from number and size of frequency
>   levels. I have added internal frequency counter which translated into real
>   frequencies only on a last stage and only as good as gone. Some systems may
>   have only several power levels, while others - many of them, so adaptation
>   time with previous approach was completely different.
>   
>   3. As part of previous I have changed adaptive mode to rise frequency on
>   demand up to 2 times and fall on 1/8 per time internal.
>   
>   4. For desktop (AC-powered) systems I have added one more mode - "hiadaptive".
>   It rises frequency twice faster, drops it 4 times slower, prefers twice
>   lower CPU load and has additional delay before leaving the highest frequency
>   after the period of maximum load. This mode was specially made to improve
>   interactivity of the systems where operation capabilities are more
>   significant then power consumption, but keeping maximum frequency all the
>   time is not needed.
>   
>   5. I have reduced default polling interval from 1/2 to 1/4 of second.
>   It is not so important for algorithm math now, but gives better system
>   interactivity.
>   
>   Discussed on:	mobile@

Somehow this seems to be too sensitive.  My laptop previously idled at
150Mhz... Occasionally bouncing up to maybe 450Mhz.  With the new algo,
it will sometimes drop to 900Mhz, but winds itself right back up to
1.8Ghz.

robert.

> Modified:
>   head/usr.sbin/powerd/powerd.8
>   head/usr.sbin/powerd/powerd.c
> 
> Modified: head/usr.sbin/powerd/powerd.8
> ==============================================================================
> --- head/usr.sbin/powerd/powerd.8	Tue Nov 18 12:42:59 2008	(r185049)
> +++ head/usr.sbin/powerd/powerd.8	Tue Nov 18 13:24:38 2008	(r185050)
> @@ -47,7 +47,8 @@ utility monitors the system state and se
>  accordingly.
>  It offers three modes (maximum, minimum, and adaptive) that can be
>  individually selected while on AC power or batteries.
> -The modes maximum, minimum, and adaptive may be abbreviated max, min, adp.
> +The modes maximum, minimum, adaptive and hiadaptive may be abbreviated
> +max, min, adp, hadp.
>  .Pp
>  Maximum mode chooses the highest performance values.
>  Minimum mode selects the lowest performance values to get the most power
> @@ -56,8 +57,10 @@ Adaptive mode attempts to strike a balan
>  the system appears idle and increasing it when the system is busy.
>  It offers a good balance between a small performance loss for greatly
>  increased power savings.
> -The default mode is
> -adaptive.
> +Hiadaptive mode is alike adaptive mode, but tuned for systems where
> +performance and interactivity are more important then power consumption.
> +It rises frequency faster, drops slower and keeps twice lower CPU load.
> +The default mode is adaptive for battery power and hiadaptive for the rest.
>  .Pp
>  The
>  .Nm
> @@ -72,10 +75,9 @@ Selects the
>  .Ar mode
>  to use while on battery power.
>  .It Fl i Ar percent
> -Specifies the CPU idle percent level when
> -adaptive
> +Specifies the CPU load percent level when adaptive
>  mode should begin to degrade performance to save power.
> -The default is 90% or higher.
> +The default is 50% or lower.
>  .It Fl n Ar mode
>  Selects the
>  .Ar mode
> @@ -83,16 +85,15 @@ to use normally when the AC line state i
>  .It Fl p Ar ival
>  Specifies a different polling interval (in milliseconds) for AC line state
>  and system idle levels.
> -The default is 500 ms.
> +The default is 250 ms.
>  .It Fl P Ar pidfile
>  Specifies an alternative file in which the process ID should be stored.
>  The default is
>  .Pa /var/run/powerd.pid .
>  .It Fl r Ar percent
> -Specifies the CPU idle percent level where
> -adaptive
> +Specifies the CPU load percent level where adaptive
>  mode should consider the CPU running and increase performance.
> -The default is 65% or lower.
> +The default is 75% or higther.
>  .It Fl v
>  Verbose mode.
>  Messages about power changes will be printed to stdout and
> 
> Modified: head/usr.sbin/powerd/powerd.c
> ==============================================================================
> --- head/usr.sbin/powerd/powerd.c	Tue Nov 18 12:42:59 2008	(r185049)
> +++ head/usr.sbin/powerd/powerd.c	Tue Nov 18 13:24:38 2008	(r185050)
> @@ -50,13 +50,14 @@ __FBSDID("$FreeBSD$");
>  #include <machine/apm_bios.h>
>  #endif
>  
> -#define DEFAULT_ACTIVE_PERCENT	65
> -#define DEFAULT_IDLE_PERCENT	90
> -#define DEFAULT_POLL_INTERVAL	500	/* Poll interval in milliseconds */
> +#define DEFAULT_ACTIVE_PERCENT	75
> +#define DEFAULT_IDLE_PERCENT	50
> +#define DEFAULT_POLL_INTERVAL	250	/* Poll interval in milliseconds */
>  
>  typedef enum {
>  	MODE_MIN,
>  	MODE_ADAPTIVE,
> +	MODE_HIADAPTIVE,
>  	MODE_MAX,
>  } modes_t;
>  
> @@ -77,7 +78,7 @@ const char *modes[] = {
>  #define DEVDPIPE	"/var/run/devd.pipe"
>  #define DEVCTL_MAXBUF	1024
>  
> -static int	read_usage_times(long *idle, long *total);
> +static int	read_usage_times(int *load);
>  static int	read_freqs(int *numfreqs, int **freqs, int **power);
>  static int	set_freq(int freq);
>  static void	acline_init(void);
> @@ -89,7 +90,7 @@ static void	parse_mode(char *arg, int *m
>  static void	usage(void);
>  
>  /* Sysctl data structures. */
> -static int	cp_time_mib[2];
> +static int	cp_times_mib[2];
>  static int	freq_mib[4];
>  static int	levels_mib[4];
>  static int	acline_mib[3];
> @@ -119,27 +120,49 @@ static int	devd_pipe = -1;
>  static struct timeval tried_devd;
>  
>  static int
> -read_usage_times(long *idle, long *total)
> +read_usage_times(int *load)
>  {
> -	static long idle_old, total_old;
> -	long cp_time[CPUSTATES], i, total_new;
> -	size_t cp_time_len;
> -	int error;
> +	static long *cp_times = NULL, *cp_times_old = NULL;
> +	static int ncpus = 0;
> +	size_t cp_times_len;
> +	int error, cpu, i, total;
> +
> +	if (cp_times == NULL) {
> +		cp_times_len = 0;
> +		error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
> +		if (error)
> +			return (error);
> +		if ((cp_times = malloc(cp_times_len)) == NULL)
> +			return (errno);
> +		if ((cp_times_old = malloc(cp_times_len)) == NULL) {
> +			free(cp_times);
> +			cp_times = NULL;
> +			return (errno);
> +		}
> +		ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
> +	}
>  
> -	cp_time_len = sizeof(cp_time);
> -	error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0);
> +	cp_times_len = sizeof(long) * CPUSTATES * ncpus;
> +	error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
>  	if (error)
>  		return (error);
> -	for (total_new = 0, i = 0; i < CPUSTATES; i++)
> -		total_new += cp_time[i];
> -
> -	if (idle)
> -		*idle = cp_time[CP_IDLE] - idle_old;
> -	if (total)
> -		*total = total_new - total_old;
> +		
> +	if (load) {
> +		*load = 0;
> +		for (cpu = 0; cpu < ncpus; cpu++) {
> +			total = 0;
> +			for (i = 0; i < CPUSTATES; i++) {
> +			    total += cp_times[cpu * CPUSTATES + i] -
> +				cp_times_old[cpu * CPUSTATES + i];
> +			}
> +			if (total == 0)
> +				continue;
> +			*load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] - 
> +			    cp_times_old[cpu * CPUSTATES + CP_IDLE]) * 100 / total;
> +		}
> +	}
>  
> -	idle_old = cp_time[CP_IDLE];
> -	total_old = total_new;
> +	memcpy(cp_times_old, cp_times, cp_times_len);
>  
>  	return (0);
>  }
> @@ -190,6 +213,21 @@ read_freqs(int *numfreqs, int **freqs, i
>  }
>  
>  static int
> +get_freq(void)
> +{
> +	size_t len;
> +	int curfreq;
> +	
> +	len = sizeof(curfreq);
> +	if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
> +		if (vflag)
> +			warn("error reading current CPU frequency");
> +		curfreq = 0;
> +	}
> +	return (curfreq);
> +}
> +
> +static int
>  set_freq(int freq)
>  {
>  
> @@ -201,6 +239,19 @@ set_freq(int freq)
>  	return (0);
>  }
>  
> +static int
> +get_freq_id(int freq, int *freqs, int numfreqs)
> +{
> +	int i = 1;
> +	
> +	while (i < numfreqs) {
> +		if (freqs[i] < freq)
> +			break;
> +		i++;
> +	}
> +	return (i - 1);
> +}
> +
>  /*
>   * Try to use ACPI to find the AC line status.  If this fails, fall back
>   * to APM.  If nothing succeeds, we'll just run in default mode.
> @@ -341,6 +392,8 @@ parse_mode(char *arg, int *mode, int ch)
>  		*mode = MODE_MAX;
>  	else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
>  		*mode = MODE_ADAPTIVE;
> +	else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
> +		*mode = MODE_HIADAPTIVE;
>  	else
>  		errx(1, "bad option: -%c %s", (char)ch, optarg);
>  }
> @@ -369,14 +422,14 @@ main(int argc, char * argv[])
>  	int nfds;
>  	struct pidfh *pfh = NULL;
>  	const char *pidfile = NULL;
> -	long idle, total;
> -	int curfreq, *freqs, i, *mwatts, numfreqs;
> +	int freq, curfreq, *freqs, i, j, *mwatts, numfreqs, load;
>  	int ch, mode, mode_ac, mode_battery, mode_none;
>  	uint64_t mjoules_used;
>  	size_t len;
>  
>  	/* Default mode for all AC states is adaptive. */
> -	mode_ac = mode_battery = mode_none = MODE_ADAPTIVE;
> +	mode_ac = mode_none = MODE_HIADAPTIVE;
> +	mode_battery = MODE_ADAPTIVE;
>  	cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
>  	cpu_idle_mark = DEFAULT_IDLE_PERCENT;
>  	poll_ival = DEFAULT_POLL_INTERVAL;
> @@ -418,7 +471,7 @@ main(int argc, char * argv[])
>  			break;
>  		case 'r':
>  			cpu_running_mark = atoi(optarg);
> -			if (cpu_running_mark < 0 || cpu_running_mark > 100) {
> +			if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
>  				warnx("%d is not a valid percent",
>  				    cpu_running_mark);
>  				usage();
> @@ -438,8 +491,8 @@ main(int argc, char * argv[])
>  
>  	/* Look up various sysctl MIBs. */
>  	len = 2;
> -	if (sysctlnametomib("kern.cp_time", cp_time_mib, &len))
> -		err(1, "lookup kern.cp_time");
> +	if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
> +		err(1, "lookup kern.cp_times");
>  	len = 4;
>  	if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
>  		err(1, "lookup freq");
> @@ -447,8 +500,8 @@ main(int argc, char * argv[])
>  	if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
>  		err(1, "lookup freq_levels");
>  
> -	/* Check if we can read the idle time and supported freqs. */
> -	if (read_usage_times(NULL, NULL))
> +	/* Check if we can read the load and supported freqs. */
> +	if (read_usage_times(NULL))
>  		err(1, "read_usage_times");
>  	if (read_freqs(&numfreqs, &freqs, &mwatts))
>  		err(1, "error reading supported CPU frequencies");
> @@ -483,6 +536,9 @@ main(int argc, char * argv[])
>  	signal(SIGINT, handle_sigs);
>  	signal(SIGTERM, handle_sigs);
>  
> +	freq = get_freq();
> +	if (freq < 1)
> +		freq = 1;
>  	/* Main loop. */
>  	for (;;) {
>  		FD_ZERO(&fdset);
> @@ -522,37 +578,30 @@ main(int argc, char * argv[])
>  		}
>  
>  		/* Read the current frequency. */
> -		len = sizeof(curfreq);
> -		if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
> -			if (vflag)
> -				warn("error reading current CPU frequency");
> +		if ((curfreq = get_freq()) == 0)
>  			continue;
> -		}
>  
> +		i = get_freq_id(curfreq, freqs, numfreqs);
> +	
>  		if (vflag) {
> -			for (i = 0; i < numfreqs; i++) {
> -				if (freqs[i] == curfreq)
> -					break;
> -			}
> -
>  			/* Keep a sum of all power actually used. */
> -			if (i < numfreqs && mwatts[i] != -1)
> +			if (mwatts[i] != -1)
>  				mjoules_used +=
>  				    (mwatts[i] * (poll_ival / 1000)) / 1000;
>  		}
>  
>  		/* Always switch to the lowest frequency in min mode. */
>  		if (mode == MODE_MIN) {
> -			if (curfreq != freqs[numfreqs - 1]) {
> +			freq = freqs[numfreqs - 1];
> +			if (curfreq != freq) {
>  				if (vflag) {
>  					printf("now operating on %s power; "
>  					    "changing frequency to %d MHz\n",
> -					    modes[acline_status],
> -					    freqs[numfreqs - 1]);
> +					    modes[acline_status], freq);
>  				}
> -				if (set_freq(freqs[numfreqs - 1]) != 0) {
> +				if (set_freq(freq) != 0) {
>  					warn("error setting CPU freq %d",
> -					    freqs[numfreqs - 1]);
> +					    freq);
>  					continue;
>  				}
>  			}
> @@ -561,16 +610,16 @@ main(int argc, char * argv[])
>  
>  		/* Always switch to the highest frequency in max mode. */
>  		if (mode == MODE_MAX) {
> -			if (curfreq != freqs[0]) {
> +			freq = freqs[0];
> +			if (curfreq != freq) {
>  				if (vflag) {
>  					printf("now operating on %s power; "
>  					    "changing frequency to %d MHz\n",
> -					    modes[acline_status],
> -					    freqs[0]);
> +					    modes[acline_status], freq);
>  				}
> -				if (set_freq(freqs[0]) != 0) {
> +				if (set_freq(freq) != 0) {
>  					warn("error setting CPU freq %d",
> -				    	    freqs[0]);
> +				    	    freq);
>  					continue;
>  				}
>  			}
> @@ -578,44 +627,59 @@ main(int argc, char * argv[])
>  		}
>  
>  		/* Adaptive mode; get the current CPU usage times. */
> -		if (read_usage_times(&idle, &total)) {
> +		if (read_usage_times(&load)) {
>  			if (vflag)
>  				warn("read_usage_times() failed");
>  			continue;
>  		}
> -
> -		/*
> -		 * If we're idle less than the active mark, bump up two levels.
> -		 * If we're idle more than the idle mark, drop down one level.
> -		 */
> -		for (i = 0; i < numfreqs - 1; i++) {
> -			if (freqs[i] == curfreq)
> -				break;
> -		}
> -		if (idle < (total * cpu_running_mark) / 100 &&
> -		    curfreq < freqs[0]) {
> -			i -= 2;
> -			if (i < 0)
> -				i = 0;
> -			if (vflag) {
> -				printf("idle time < %d%%, increasing clock"
> -				    " speed from %d MHz to %d MHz\n",
> -				    cpu_running_mark, curfreq, freqs[i]);
> +		
> +		if (mode == MODE_ADAPTIVE) {
> +			if (load > cpu_running_mark) {
> +				if (load > 95 || load > cpu_running_mark * 2)
> +					freq *= 2;
> +				else
> +					freq = freq * load / cpu_running_mark;
> +				if (freq > freqs[0])
> +					freq = freqs[0];
> +			} else if (load < cpu_idle_mark &&
> +			    curfreq * load < freqs[get_freq_id(
> +			    freq * 7 / 8, freqs, numfreqs)] * 
> +			    cpu_running_mark) {
> +				freq = freq * 7 / 8;
> +				if (freq < freqs[numfreqs - 1])
> +					freq = freqs[numfreqs - 1];
>  			}
> -			if (set_freq(freqs[i]))
> -				warn("error setting CPU frequency %d",
> -				    freqs[i]);
> -		} else if (idle > (total * cpu_idle_mark) / 100 &&
> -		    curfreq > freqs[numfreqs - 1]) {
> -			i++;
> +		} else { /* MODE_HIADAPTIVE */
> +			if (load > cpu_running_mark / 2) {
> +				if (load > 95 || load > cpu_running_mark)
> +					freq *= 4;
> +				else
> +					freq = freq * load * 2 / cpu_running_mark;
> +				if (freq > freqs[0] * 2)
> +					freq = freqs[0] * 2;
> +			} else if (load < cpu_idle_mark / 2 &&
> +			    curfreq * load < freqs[get_freq_id(
> +			    freq * 31 / 32, freqs, numfreqs)] * 
> +			    cpu_running_mark / 2) {
> +				freq = freq * 31 / 32;
> +				if (freq < freqs[numfreqs - 1])
> +					freq = freqs[numfreqs - 1];
> +			}
> +		}
> +		if (vflag) {
> +		    printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
> +			load, curfreq, i, freq);
> +		}
> +		j = get_freq_id(freq, freqs, numfreqs);
> +		if (i != j) {
>  			if (vflag) {
> -				printf("idle time > %d%%, decreasing clock"
> +				printf("changing clock"
>  				    " speed from %d MHz to %d MHz\n",
> -				    cpu_idle_mark, curfreq, freqs[i]);
> +				    freqs[i], freqs[j]);
>  			}
> -			if (set_freq(freqs[i]) != 0)
> +			if (set_freq(freqs[j]))
>  				warn("error setting CPU frequency %d",
> -				    freqs[i]);
> +				    freqs[j]);
>  		}
>  	}
>  	free(freqs);
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 195 bytes
Desc: This is a digitally signed message part
Url : http://lists.freebsd.org/pipermail/svn-src-all/attachments/20081119/466bf640/attachment.pgp


More information about the svn-src-all mailing list