APM driver patch summary
I wrote:
---------------------------------------------------------------------
Here is an updated list of the patches:
1. Notify listener of suspend before drivers (Russell King, me)
(appended)
2. Fix idle handling (Andreas Steinmetz)
http://marc.theaimsgroup.com/?l=linux-kernel&m=100754277600661&w=2 3. Control apm idle calling by runtime parameter (Andrej Borsenkow) http://marc.theaimsgroup.com/?l=linux-kernel&m=100852862320955&w=2 4. Detect failure to stop CPU on apm idle call (Andrej Borsenkow) http://marc.theaimsgroup.com/?l=linux-kernel&m=100869841008117&w=2
---------------------------------------------------------------------
I have just tried to combine these and I have run into trouble. Patch 4 applies on top of patch 3, but neither of these applies on top of patch 2. Can you guys sort these out into one big "fix idle calling" patch that includes a runtime parameter to control idle calling, which overrides a default selected either by CONFIG_APM_CPU_IDLE or by a bit of code that checks for CPU stoppage?
Or has someone already done this?
The latest Russell King (modified by me) patch is now at:
http://panopticon.csustan.edu/thood/apm.html
--
Thomas Hood
I'll have a look over the weekend.
Quoted 21 lines · On 22-Dec-2001 Thomas Hood wrote:
I wrote:
---------------------------------------------------------------------
Here is an updated list of the patches:
1. Notify listener of suspend before drivers (Russell King, me)
(appended)
2. Fix idle handling (Andreas Steinmetz)http://marc.theaimsgroup.com/?l=linux-kernel&m=100754277600661&w=2 3. Control apm idle calling by runtime parameter (Andrej Borsenkow) http://marc.theaimsgroup.com/?l=linux-kernel&m=100852862320955&w=2 4. Detect failure to stop CPU on apm idle call (Andrej Borsenkow) http://marc.theaimsgroup.com/?l=linux-kernel&m=100869841008117&w=2---------------------------------------------------------------------
I have just tried to combine these and I have run into trouble. Patch 4 applies on top of patch 3, but neither of these applies on top of patch 2. Can you guys sort these out into one big "fix idle calling" patch that includes a runtime parameter to control idle calling, which overrides a default selected either by
CONFIG_APM_CPU_IDLEor by a bit of code that checks for CPU stoppage?
Quoted 10 lines
Or has someone already done this?
The latest Russell King (modified by me) patch is now at:
http://panopticon.csustan.edu/thood/apm.html--
Thomas Hood
Andreas Steinmetz
D.O.M. Datenverarbeitung GmbH
Hi,
I merged 2., 3. and 4. (attached) with some modifications.
1. There is now a module parameter apm-idle-threshold which allows to override the compiled in idle percentage threshold above which BIOS idle calls are done.
2. I modified Andrej's mechanism to detect a defunct BIOS (stating 'does stop CPU' when it actually doesn't) to take into account that there's other interrupts than the timer interrupt that could reactivate the cpu. As there's 16 hardware interrupts on x86 (apm is arch specific anyway) I do use a leaky bucket counter for a maximum of 16 idle rounds until jiffies is increased. When the counter reaches zero it stays at this value and the system idle routine is called. If BIOS idle is a noop then the counter reaches zero fast, thus effectively halting the cpu.
Andrej, could you please test the patch if it works for your laptop?
Quoted 21 lines · On 22-Dec-2001 Thomas Hood wrote:
I wrote:
---------------------------------------------------------------------
Here is an updated list of the patches:
1. Notify listener of suspend before drivers (Russell King, me)
(appended)
2. Fix idle handling (Andreas Steinmetz)http://marc.theaimsgroup.com/?l=linux-kernel&m=100754277600661&w=2 3. Control apm idle calling by runtime parameter (Andrej Borsenkow) http://marc.theaimsgroup.com/?l=linux-kernel&m=100852862320955&w=2 4. Detect failure to stop CPU on apm idle call (Andrej Borsenkow) http://marc.theaimsgroup.com/?l=linux-kernel&m=100869841008117&w=2---------------------------------------------------------------------
I have just tried to combine these and I have run into trouble. Patch 4 applies on top of patch 3, but neither of these applies on top of patch 2. Can you guys sort these out into one big "fix idle calling" patch that includes a runtime parameter to control idle calling, which overrides a default selected either by
CONFIG_APM_CPU_IDLEor by a bit of code that checks for CPU stoppage?
Quoted 10 lines
Or has someone already done this?
The latest Russell King (modified by me) patch is now at:
http://panopticon.csustan.edu/thood/apm.html--
Thomas Hood
Andreas Steinmetz
D.O.M. Datenverarbeitung GmbH
tmp/apm.c renamed from tmp/apm.c.orig +93 -70
@@ -341,6 +341,11 @@#define APM_BIOS_MAGIC 0x4101/* +* idle percentage above which bios idle calls are done +*/ +#define BIOS_IDLE_TRIGGER 95 + +/* * Local variables */ static struct {
@@ -349,6 +354,8 @@ } apm_bios_entry;#ifdef CONFIG_APM_CPU_IDLE static int clock_slowed; +static int idle_enabled =1 ; +static int idle_threshold =BIOS_IDLE_TRIGGER ;#endif static int suspends_pending;static int standbys_pending;
@@ -735,63 +742,84 @@ } } -#if 0 -extern int hlt_counter; -/* -* If no process has been interested in this -* CPU for some time, we want to wake up the -* power management thread - we probably want +* If no process has really been interested in +* CPU for some time, we want to call BIOS +* power management - we probably want * to conserve power. */ #define HARD_IDLE_TIMEOUT (HZ/3) +#define IDLE_CALC_LIMIT (HZ*100) +#define IDLE_LEAKY_MAX 16 -/* This should wake up kapmd and ask it to slow the CPU */ -#define powermanagement_idle () do { } while (0) +static void (*sys_idle)(void ); +static unsigned int last_jiffies =0 ; +static unsigned int last_stime =0 ; +static int use_apm_idle =0 ;/** * apm_cpu_idle - cpu idling for APM capable Linux * * This is the idling function the kernel executes when APM is available. It -* tries to save processor time directly by using hlt instructions. A -* separate apm thread tries to do the BIOS power management. -* -* N.B. This is curently not used for kernels 2.4.x. +* tries to do BIOS powermanagement based on the average system idle time. +* Furthermore it calls the system default idle routine. */ static void apm_cpu_idle (void ) { -unsigned int start_idle; - - start_idle = jiffies; -while (1 ) { -if (!current->need_resched) { -if (jiffies - start_idle <HARD_IDLE_TIMEOUT ) { -if (!current_cpu_data.hlt_works_ok) -continue ; -if (hlt_counter) +int apm_is_idle =0 ; +unsigned int t1 = jiffies - last_jiffies; +unsigned int t2; + +recalc:if (t1 >IDLE_CALC_LIMIT ) + goto reset; + +if (t1 >HARD_IDLE_TIMEOUT ) { + t2 = current->times.tms_stime - last_stime; + t2 *=100 ; + t2 /= t1; +if (t2 > idle_threshold) + use_apm_idle =1 ; +else +reset: use_apm_idle =0 ; + last_jiffies = jiffies; + last_stime = current->times.tms_stime; + } + + t2 =IDLE_LEAKY_MAX ; + +while (!current->need_resched) { +if (use_apm_idle) { + t1 = jiffies; +switch (apm_do_idle ()) { +case 0 : apm_is_idle =1 ; +if (t1 != jiffies) { +if (t2) { + t2 =IDLE_LEAKY_MAX ; +continue ; + } + }else if (t2) { + t2--;continue ; -__cli (); -if (!current->need_resched) -safe_halt (); -else -__sti (); -continue ; + } +break ; +case 1 : apm_is_idle =1 ; +break ; } - -/* -* Ok, do some power management - we've been idle for too long -*/ -powermanagement_idle (); } -schedule (); -check_pgt_cache (); - start_idle = jiffies; +if (sys_idle) +sys_idle (); + + t1 = jiffies - last_jiffies; +if (t1 >HARD_IDLE_TIMEOUT ) + goto recalc; } + +if (apm_is_idle) +apm_do_busy (); }#endif -#endif #ifdef CONFIG_SMP static int apm_magic (void * unused)
@@ -1346,17 +1374,12 @@static void apm_mainloop (void ) { -int timeout =HZ ;DECLARE_WAITQUEUE (wait, current);add_wait_queue (&apm_waitqueue, &wait);set_current_state (TASK_INTERRUPTIBLE );for (;;) { -/* Nothing to do, just sleep for the timeout */ - timeout =2 * timeout; -if (timeout >APM_CHECK_TIMEOUT ) - timeout =APM_CHECK_TIMEOUT ; -schedule_timeout (timeout); +schedule_timeout (APM_CHECK_TIMEOUT );if (exit_kapmd)break ;
@@ -1366,34 +1389,6 @@*/ set_current_state (TASK_INTERRUPTIBLE );apm_event_handler (); -#ifdef CONFIG_APM_CPU_IDLE -if (!system_idle ()) -continue ; - -/* -* If we can idle... -*/ -if (apm_do_idle () !=-1 ) { -unsigned long start = jiffies; -while ((!exit_kapmd) &&system_idle ()) { -if (apm_do_idle ()) { -set_current_state (TASK_INTERRUPTIBLE ); -/* APM needs us to snooze .. either -the BIOS call failed (-1) or it -slowed the clock (1). We sleep -until it talks to us again */ -schedule_timeout (1 ); - } -if ((jiffies - start) >APM_CHECK_TIMEOUT ) { -apm_event_handler (); - start = jiffies; - } - } -apm_do_busy (); -apm_event_handler (); - timeout =1 ; - } -#endif }remove_wait_queue (&apm_waitqueue, &wait); }
@@ -1814,6 +1809,17 @@if ((strncmp (str,"realmode-power-off" ,18 ) ==0 ) || (strncmp (str,"realmode_power_off" ,18 ) ==0 )) apm_info.realmode_power_off = !invert; +#ifdef CONFIG_APM_CPU_IDLE +if ((strncmp (str,"apm-idle" ,8 ) ==0 ) || + (strncmp (str,"apm_idle" ,8 ) ==0 )) + idle_enabled = !invert; +if ((strncmp (str,"apm-idle-threshold=" ,19 ) ==0 ) || + (strncmp (str,"apm_idle_threshold=" ,19 ) ==0 )) { + idle_threshold =simple_strtol (str +19 ,NULL ,0 ); +if ( idle_threshold <0 || idle_threshold >99 ) + idle_threshold =BIOS_IDLE_TRIGGER ; + } +#endif str =strchr (str,',' );if (str !=NULL ) str +=strspn (str,", \t" );
@@ -1968,6 +1974,13 @@misc_register (&apm_device); +#ifdef CONFIG_APM_CPU_IDLE +if (idle_enabled) { + sys_idle = pm_idle; + pm_idle = apm_cpu_idle; + } +#endif +return 0 ; }
@@ -1975,6 +1988,11 @@ {int error; +#ifdef CONFIG_APM_CPU_IDLE +if (idle_enabled) + pm_idle = sys_idle; +#endif +if (((apm_info.bios.flags &APM_BIOS_DISENGAGED ) ==0 ) && (apm_info.connection_version >0x0100 )) { error =apm_engage_power_management (APM_DEVICE_ALL ,0 );
@@ -2012,5 +2030,11 @@MODULE_PARM (realmode_power_off,"i" );MODULE_PARM_DESC (realmode_power_off,"Switch to real mode before powering off" ); +#ifdef CONFIG_APM_CPU_IDLE +MODULE_PARM (apm_idle,"i" ); +MODULE_PARM_DESC (apm_idle,"Make BIOS idle calls when idle" ); +MODULE_PARM (apm_idle_threshold,"i" ); +MODULE_PARM_DESC(apm_idle_threshold, "System idle percentage for BIOS idle call
s");
+#endif
EXPORT_NO_SYMBOLS;
Thanks.
I have put Andreas's idle patch, Russell King's notification patch, and a combined patch up at:
http://panopticon.csustan.edu/thood/apm.html
--
Thomas Hood
Quoted 17 lines · Andreas Steinmetz
Hi,
I merged 2., 3. and 4. (attached) with some modifications.1. There is now a module parameter apm-idle-threshold which allows to override the compiled in idle percentage threshold above which BIOS idle calls are done.
2. I modified Andrej's mechanism to detect a defunct BIOS (stating 'does stop CPU' when it actually doesn't) to take into account that there's other interrupts than the timer interrupt that could reactivate the cpu. As there's 16 hardware interrupts on x86 (apm is arch specific anyway) I do use a leaky bucket counter for a maximum of 16 idle rounds until jiffies is increased. When the counter reaches zero it stays at this value and the system idle routine is called. If BIOS idle is a noop then the counter reaches zero fast, thus effectively halting the cpu.
Andrej, could you please test the patch if it works for your laptop?
Sorry for the delay, I was off before New Year and then could not test it ...
Quoted 7 lines · Andreas Steinmetz
Hi,
I merged 2., 3. and 4. (attached) with some modifications.1. There is now a module parameter apm-idle-threshold which allows to override the compiled in idle percentage threshold above which BIOS idle calls are done.
Quoted 9 lines
2. I modified Andrej's mechanism to detect a defunct BIOS (stating 'does stop CPU' when it actually doesn't) to take into account that there's other interrupts than the timer interrupt that could reactivate the cpu. As there's 16 hardware interrupts on x86 (apm is arch specific anyway) I do use a leaky bucket counter for a maximum of 16 idle rounds until jiffies is increased. When the counter reaches zero it stays at this value and the system idle routine is called. If BIOS idle is a noop then the counter reaches zero fast, thus effectively halting the cpu.
I do not think you need it. Either interrupt waked up somebody and set need_resched and we exit loop or nobody is ready to run and we can sleep again. Why complicate things any more than needed?
Quoted 2 lines
Andrej, could you please test the patch if it works for your laptop?
It does not work and I am very surprised it works for somebody (well, there are conditios when it will work). By default pm_idle is always NULL so we *never* actually call kernel function that really stops CPU. Main idle task is cpu_idle that does
if (pm_idle)
pm_idle()
or
default_idle
and CPU is halted in default_idle. So your patch just enters busy loop calling BIOS APM Idle over and over again just like it was before.
Attached patch makes apm_cpu_idle do the same and call either old pm_idle (a.k.a. sys_idle) or default_idle. I removed your interrupt handling - it does not actually affect the problem but it still is not needed IMHO. t1, t2 are changed from int into long because jiffies is long - not sure if it is really needed.
cheers and sorry for delay
-andrej
arch/i386/kernel/apm.c renamed from arch/i386/kernel/apm.c.combined +6 -10
@@ -761,6 +761,7 @@static void (*sys_idle)(void );static unsigned int last_jiffies =0 ;static unsigned int last_stime =0 ; +extern void default_idle (void );/** * apm_cpu_idle - cpu idling for APM capable Linux
@@ -774,8 +775,8 @@ {static int use_apm_idle =0 ;int apm_is_idle =0 ; -unsigned int t1 = jiffies - last_jiffies; -unsigned int t2; +unsigned long t1 = jiffies - last_jiffies; +unsigned long t2; recalc:if (t1 >IDLE_CALC_LIMIT ) goto reset;
@@ -799,15 +800,8 @@ t1 = jiffies;switch (apm_do_idle ()) {case 0 : apm_is_idle =1 ; -if (t1 != jiffies) { -if (t2) { - t2 =IDLE_LEAKY_MAX ; -continue ; - } - }else if (t2) { - t2--; +if (t1 != jiffies)continue ; - }break ;case 1 : apm_is_idle =1 ;break ;
@@ -816,6 +810,8 @@if (sys_idle)sys_idle (); +else +default_idle (); t1 = jiffies - last_jiffies;if (t1 >HARD_IDLE_TIMEOUT )
arch/i386/kernel/process.c renamed from arch/i386/kernel/process.c.org +3 -1
@@ -77,8 +77,10 @@/* * We use this if we don't have any better * idle routine.. +* It is also called from apm_cpu_idle if BIOS does not stop clock +* for us */ -static void default_idle (void ) +void default_idle (void ) {if (current_cpu_data.hlt_works_ok && !hlt_counter) {__cli ();
Quoted 26 lines · On 04-Jan-2002 Borsenkow Andrej wrote:
Sorry for the delay, I was off before New Year and then could not test it ...
Quoted 8 lines · Andreas Steinmetz
Hi,
I merged 2., 3. and 4. (attached) with some modifications.1. There is now a module parameter apm-idle-threshold which allows to override
the compiled in idle percentage threshold above which BIOS idle calls are done.Quoted 12 lines
2. I modified Andrej's mechanism to detect a defunct BIOS (stating 'does stop
CPU' when it actually doesn't) to take into account that there's other interrupts than the timer interrupt that could reactivate the cpu. As there's 16 hardware interrupts on x86 (apm is arch specific anyway) I do
use a leaky bucket counter for a maximum of 16 idle rounds until jiffies is
increased. When the counter reaches zero it stays at this value and the system idle routine is called. If BIOS idle is a noop then the counter reaches zero fast, thus effectively halting the cpu.
Quoted 4 lines
I do not think you need it. Either interrupt waked up somebody and set
need_reschedand we exit loop or nobody is ready to run and we can sleep again. Why complicate things any more than needed?
NIC interrupt with fragmented packet, usb, sound, ... - there's interrupts with nobody ready to run. Have a look at /proc/interrupts from time to time while your system is idle.
Quoted 8 lines
Quoted 2 lines
Andrej, could you please test the patch if it works for your laptop?
It does not work and I am very surprised it works for somebody (well, there are conditios when it will work). By default
pm_idleis always NULL so we *never* actually call kernel function that really stops CPU. Main idle task iscpu_idlethat does
Well, if your BIOS is not broken it works. That's why I asked you to test the patch.
Quoted 8 lines
if (
pm_idle)
pm_idle()
or
default_idleand CPU is halted in
default_idle. So your patch just enters busy loop calling BIOS APM Idle over and over again just like it was before.
Granted.
Quoted 6 lines
Attached patch makes
apm_cpu_idledo the same and call either oldpm_idle(a.k.a.sys_idle) ordefault_idle. I removed your interrupt handling - it does not actually affect the problem but it still is not needed IMHO. t1, t2 are changed from int into long because jiffies is long - not sure if it is really needed.
Please don't do it like this. It breaks apm module build for sure. I would suggest to implement the functionality of default_idle() into apm_cpu_idle(). Though I could do this right now I'd ask all participating parties to agree on a current code status on which to work on.
Quoted 6 lines
cheers and sorry for delay
-andrej
Andreas Steinmetz
D.O.M. Datenverarbeitung GmbH