diff -u --exclude-from /usr/src/exclude --new-file --recursive linux.20pre5/arch/i386/kernel/apm.c linux.20pre5-ac6/arch/i386/kernel/apm.c --- linux.20pre5/arch/i386/kernel/apm.c 2002-08-29 18:39:52.000000000 +0100 +++ linux.20pre5-ac6/arch/i386/kernel/apm.c 2002-08-26 14:19:32.000000000 +0100 @@ -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= number of ticks to ignore suspend * bounces * idle[-_]threshold= 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; } @@ -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;