patch-2.4.21 linux-2.4.21/arch/i386/kernel/apm.c
Next file: linux-2.4.21/arch/i386/kernel/bluesmoke.c
Previous file: linux-2.4.21/arch/i386/kernel/apic.c
Back to the patch index
Back to the overall index
- Lines: 228
- Date:
2003-06-13 07:51:29.000000000 -0700
- Orig file:
linux-2.4.20/arch/i386/kernel/apm.c
- Orig date:
2002-11-28 15:53:09.000000000 -0800
diff -urN linux-2.4.20/arch/i386/kernel/apm.c linux-2.4.21/arch/i386/kernel/apm.c
@@ -247,6 +247,7 @@
* powering off
* [no-]debug log some debugging messages
* [no-]power[-_]off power off on shutdown
+ * [no-]smp Use apm even on an SMP box
* bounce[-_]interval=<n> number of ticks to ignore suspend
* bounces
* idle[-_]threshold=<n> System idle percentage above which to
@@ -393,6 +394,7 @@
static int got_clock_diff;
#endif
static int debug;
+static int smp = 0;
static int apm_disabled = -1;
#ifdef CONFIG_SMP
static int power_off;
@@ -494,6 +496,40 @@
}
/*
+ * Lock APM functionality to physical CPU 0
+ */
+
+#ifdef CONFIG_SMP
+
+static unsigned long apm_save_cpus(void)
+{
+ unsigned long x = current->cpus_allowed;
+ /* Some bioses don't like being called from CPU != 0 */
+ if (cpu_number_map(smp_processor_id()) != 0) {
+ set_cpus_allowed(current, 1 << cpu_logical_map(0));
+ if (unlikely(cpu_number_map(smp_processor_id()) != 0))
+ BUG();
+ }
+ return x;
+}
+
+static inline void apm_restore_cpus(unsigned long mask)
+{
+ set_cpus_allowed(current, mask);
+}
+
+#else
+
+/*
+ * No CPU lockdown needed on a uniprocessor
+ */
+
+#define apm_save_cpus() 0
+#define apm_restore_cpus(x) (void)(x)
+
+#endif
+
+/*
* These are the actual BIOS calls. Depending on APM_ZERO_SEGS and
* apm_info.allow_ints, we are being really paranoid here! Not only
* are interrupts disabled, but all the segment registers (except SS)
@@ -566,7 +602,8 @@
{
APM_DECL_SEGS
unsigned long flags;
-
+ unsigned long cpus = apm_save_cpus();
+
__save_flags(flags);
APM_DO_CLI;
APM_DO_SAVE_SEGS;
@@ -588,6 +625,9 @@
: "memory", "cc");
APM_DO_RESTORE_SEGS;
__restore_flags(flags);
+
+ apm_restore_cpus(cpus);
+
return *eax & 0xff;
}
@@ -611,6 +651,8 @@
APM_DECL_SEGS
unsigned long flags;
+ unsigned long cpus = apm_save_cpus();
+
__save_flags(flags);
APM_DO_CLI;
APM_DO_SAVE_SEGS;
@@ -636,6 +678,9 @@
}
APM_DO_RESTORE_SEGS;
__restore_flags(flags);
+
+ apm_restore_cpus(cpus);
+
return error;
}
@@ -751,10 +796,12 @@
if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax)) {
static unsigned long t;
- if (time_after(jiffies, t + 10 * HZ)) {
+ /* This always fails on some SMP boards running UP kernels.
+ * Only report the failure the first 5 times.
+ */
+ if (++t < 5) {
printk(KERN_DEBUG "apm_do_idle failed (%d)\n",
(eax >> 8) & 0xff);
- t = jiffies;
}
return -1;
}
@@ -888,17 +935,12 @@
/*
* This may be called on an SMP machine.
*/
-#ifdef CONFIG_SMP
- /* Some bioses don't like being called from CPU != 0 */
- if (cpu_number_map(smp_processor_id()) != 0) {
- current->cpus_allowed = 1;
- schedule();
- if (unlikely(cpu_number_map(smp_processor_id()) != 0))
- BUG();
- }
-#endif
if (apm_info.realmode_power_off)
+ {
+ (void)apm_save_cpus();
machine_real_restart(po_bios_call, sizeof(po_bios_call));
+ /* Never returns */
+ }
else
(void) set_system_power_state(APM_STATE_OFF);
}
@@ -1074,6 +1116,19 @@
}
if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
return 1;
+ if (error == APM_NOT_ENGAGED) {
+ static int tried;
+ int eng_error;
+ if (tried++ == 0) {
+ eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1);
+ if (eng_error) {
+ apm_error("set display", error);
+ apm_error("engage interface", eng_error);
+ return 0;
+ } else
+ return apm_console_blank(blank);
+ }
+ }
apm_error("set display", error);
return 0;
}
@@ -1397,7 +1452,7 @@
as = fp->private_data;
if (check_apm_user(as, "read"))
return -EIO;
- if (count < sizeof(apm_event_t))
+ if ((int)count < sizeof(apm_event_t))
return -EINVAL;
if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))
return -EAGAIN;
@@ -1571,7 +1626,7 @@
p = buf;
- if ((smp_num_cpus == 1) &&
+ if ((smp_num_cpus == 1 || smp) &&
!(error = apm_get_power_status(&bx, &cx, &dx))) {
ac_line_status = (bx >> 8) & 0xff;
battery_status = bx & 0xff;
@@ -1716,7 +1771,7 @@
}
}
- if (debug) {
+ if (debug && (smp_num_cpus == 1 || smp )) {
error = apm_get_power_status(&bx, &cx, &dx);
if (error)
printk(KERN_INFO "apm: power status not available\n");
@@ -1760,7 +1815,7 @@
pm_power_off = apm_power_off;
register_sysrq_key('o', &sysrq_poweroff_op);
- if (smp_num_cpus == 1) {
+ if (smp_num_cpus == 1 || smp) {
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
console_blank_hook = apm_console_blank;
#endif
@@ -1799,6 +1854,11 @@
str += 3;
if (strncmp(str, "debug", 5) == 0)
debug = !invert;
+ if (strncmp(str, "smp", 3) == 0)
+ {
+ smp = !invert;
+ idle_threshold = 100;
+ }
if ((strncmp(str, "power-off", 9) == 0) ||
(strncmp(str, "power_off", 9) == 0))
power_off = !invert;
@@ -1903,7 +1963,7 @@
printk(KERN_NOTICE "apm: disabled on user request.\n");
return -ENODEV;
}
- if ((smp_num_cpus > 1) && !power_off) {
+ if ((smp_num_cpus > 1) && !power_off && !smp) {
printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
return -ENODEV;
}
@@ -1957,7 +2017,7 @@
kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
- if (smp_num_cpus > 1) {
+ if (smp_num_cpus > 1 && !smp) {
printk(KERN_NOTICE
"apm: disabled - APM is not SMP safe (power off active).\n");
return 0;
@@ -2025,5 +2085,8 @@
MODULE_PARM(idle_period, "i");
MODULE_PARM_DESC(idle_period,
"Period (in sec/100) over which to caculate the idle percentage");
+MODULE_PARM(smp, "i");
+MODULE_PARM_DESC(smp,
+ "Set this to enable APM use on an SMP platform. Use with caution on older systems");
EXPORT_NO_SYMBOLS;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)