[PATCH] 2.3.37 Make power down reliable on SMP

From: Stephen Rothwell (sfr@linuxcare.com)
Date: Fri Jan 07 2000 - 11:17:23 EST


Hi All,

Something to try out on your SMP boxes ...

There is a particularly hackish hack in the middle of this patch
that is there purely to make the power off code only run on
CPU 0 of an SMP machine.

Comments welcome.

Cheers,
Stephen

--
Stephen Rothwell                    sfr@linuxcare.com
http://linuxcare.com.au/sfr/

diff -ruN 2.3.37/arch/i386/kernel/apm.c 2.3.37-APM/arch/i386/kernel/apm.c --- 2.3.37/arch/i386/kernel/apm.c Fri Jan 7 10:19:12 2000 +++ 2.3.37-APM/arch/i386/kernel/apm.c Sat Jan 8 03:11:02 2000 @@ -35,6 +35,7 @@ * Jan 1999, Version 1.9 * Oct 1999, Version 1.10 * Nov 1999, Version 1.11 + * Jan 2000, Version 1.12 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -110,6 +111,11 @@ * (reported by Panos Katsaloulis <teras@writeme.com>). * Real mode power off patch (Walter Hofmann * <Walter.Hofmann@physik.stud.uni-erlangen.de>). + * 1.12: Remove CONFIG_SMP as the compiler will optimize + * the code away anyway (smp_num_cpus == 1 in UP) + * noted by Artur Skawina <skawina@geocities.com>. + * Make power off under SMP work again. + * Fix thinko with initial engaging of BIOS. * * APM 1.1 Reference: * @@ -285,7 +291,7 @@ static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static struct apm_bios_struct * user_list = NULL; -static char driver_version[] = "1.11"; /* no spaces */ +static char driver_version[] = "1.12"; /* no spaces */ static char * apm_event_name[] = { "system standby", @@ -512,6 +518,7 @@ } } +#if 0 extern int hlt_counter; /* @@ -556,6 +563,15 @@ } } #endif +#endif + +#ifdef CONFIG_SMP +static int apm_magic(void * unused) +{ + while (1) + schedule(); +} +#endif static void apm_power_off(void) { @@ -567,6 +583,16 @@ * they are doing because they booted with the smp-power-off * kernel option. */ +#ifdef CONFIG_SMP + if (smp_hack == 2) { + /* Many bioses don't like being called from CPU != 0 */ + while (cpu_number_map[smp_processor_id()] != 0) { + kernel_thread(apm_magic, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); + schedule(); + } + } +#endif if (apm_enabled || (smp_hack == 2)) { #ifdef CONFIG_APM_REAL_MODE_POWER_OFF unsigned char po_bios_call[] = { @@ -586,18 +612,21 @@ } } -#ifdef CONFIG_APM_DO_ENABLE -static int __init apm_enable_power_management(void) +static int apm_enable_power_management(int enable) { u32 eax; + if ((enable == 0) && (apm_bios_info.flags & APM_BIOS_DISENGAGED)) + return APM_NOT_ENGAGED; if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL, - 1, &eax)) + enable, &eax)) return (eax >> 8) & 0xff; - apm_bios_info.flags &= ~APM_BIOS_DISABLED; + if (enable) + apm_bios_info.flags &= ~APM_BIOS_DISABLED; + else + apm_bios_info.flags |= APM_BIOS_DISABLED; return APM_SUCCESS; } -#endif static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) { @@ -645,12 +674,21 @@ } #endif -static int __init apm_engage_power_management(u_short device) +static int apm_engage_power_management(u_short device, int enable) { u32 eax; - if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, 1, &eax)) + if ((enable == 0) && (device == APM_DEVICE_ALL) + && (apm_bios_info.flags & APM_BIOS_DISABLED)) + return APM_DISABLED; + if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax)) return (eax >> 8) & 0xff; + if (device == APM_DEVICE_ALL) { + if (enable) + apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + else + apm_bios_info.flags |= APM_BIOS_DISENGAGED; + } return APM_SUCCESS; } @@ -987,6 +1025,10 @@ static void apm_mainloop(void) { DECLARE_WAITQUEUE(wait, current); + + if (smp_num_cpus > 1) + return; + apm_enabled = 1; add_wait_queue(&apm_waitqueue, &wait); @@ -1358,17 +1400,20 @@ * is booted with PM disabled but not in the docking station. * Unfortunate ... */ - error = apm_enable_power_management(); + error = apm_enable_power_management(1); if (error) { apm_error("enable power management", error); return -1; } } #endif - if (((apm_bios_info.flags & APM_BIOS_DISENGAGED) == 0) + if ((apm_bios_info.flags & APM_BIOS_DISENGAGED) && (apm_bios_info.version > 0x0100)) { - if (apm_engage_power_management(0x0001) == APM_SUCCESS) - apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + error = apm_engage_power_management(APM_DEVICE_ALL, 1); + if (error) { + apm_error("engage power management", error); + return -1; + } } /* Install our power off handler.. */ @@ -1541,20 +1586,19 @@ } #endif -#ifdef CONFIG_SMP + kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); + if (smp_num_cpus > 1) { printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); if (smp_hack) smp_hack = 2; APM_INIT_ERROR_RETURN; } -#endif create_proc_info_entry("apm", 0, 0, apm_get_info); misc_register(&apm_device); - kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); return 0; }

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Jan 07 2000 - 21:00:09 EST