To: linux-kernel@vger.rutgers.edu
Subject: [PATCH] 2.3.37 Make power down reliable on SMP
Date: Sat, 08 Jan 2000 03:17:23 +1100
From: Stephen Rothwell <sfr@linuxcare.com>
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/