Skip to contentSkip to conversation

Make processor module unloading safe

PATCH New Shaohua Li 1 participant 1 message Sep 6, 2005 ACPI

lore
Share & tools
lore

Shaohua Li

author
#0 lore raw

PATCH 1/1 Make processor module unloading safe

Hi,
pm_idle isn’t a per-cpu data. In MP case, it is possible one CPU (eg. the boot CPU) changed pm_idle, but other CPUs it’s not ready for it. The ACPI module is such a case, CPU 0 changed pm_idle, which caused all CPUs start using pm_idle, but other CPUs actually aren’t ready (their idle information isn't initialized). The worse case is unloading ACPI module. If non-boot CPU first freed their idle information, current code doesn’t know it, and the CPUs possibly will use the freed memory again till boot cpu restore pm_idle. This patch tries to fix the gaps.

Makes pm_idle a per-cpu data might be a better idea, but it’s platform independent, so I didn't touch it but just fix the ACPI issue.

Thanks,
Shaohua

---

 linux-2.6.13-root/arch/i386/kernel/apm.c        |    2 -
 linux-2.6.13-root/arch/i386/kernel/process.c    |    4 +--
 linux-2.6.13-root/arch/ia64/kernel/process.c    |    4 +--
 linux-2.6.13-root/arch/x86_64/kernel/process.c  |    4 +--
 linux-2.6.13-root/drivers/acpi/processor_idle.c |   26 +++++++++++++++++++-----
 linux-2.6.13-root/include/asm-i386/system.h     |    3 +-
 linux-2.6.13-root/include/asm-ia64/system.h     |    3 +-
 linux-2.6.13-root/include/asm-x86_64/system.h   |    3 +-
 linux-2.6.13-root/kernel/sched.c                |    1 
 9 files changed, 35 insertions(+), 15 deletions(-)
drivers/acpi/processor_idle.c renamed from drivers/acpi/processor_idle.c~remove_cpu_idle_save +21 -5
diff -puN drivers/acpi/processor_idle.c~remove_cpu_idle_save drivers/acpi/processor_idle.c
@@ -75,6 +75,8 @@ module_param(bm_history, uint, 0644);
                                 Power Management
    -------------------------------------------------------------------------- */
 
+static void acpi_processor_idle(void);
+static DEFINE_PER_CPU(void (*)(void), acpi_cpu_idle);
 /*
  * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3.
  * For now disable this. Probably a bug somewhere else.
@@ -170,7 +172,7 @@ acpi_processor_power_activate (
 static atomic_t 	c3_cpu_count;
 
 
-static void acpi_processor_idle (void)
+static void __acpi_processor_idle (void)
 {
 	struct acpi_processor	*pr = NULL;
 	struct acpi_processor_cx *cx = NULL;
@@ -891,13 +893,13 @@ int acpi_processor_cst_has_changed (stru
 		return_VALUE(-ENODEV);
 
 	/* Fall back to the default idle loop */
-	pm_idle = pm_idle_save;
-	synchronize_sched();  /* Relies on interrupts forcing exit from idle. */
+	per_cpu(acpi_cpu_idle, pr->id) = NULL;
+	cpu_idle_wait(cpumask_of_cpu(pr->id));
 
 	pr->flags.power = 0;
 	result = acpi_processor_get_power_info(pr);
 	if ((pr->flags.power == 1) && (pr->flags.power_setup_done))
-		pm_idle = acpi_processor_idle;
+		per_cpu(acpi_cpu_idle, pr->id) = __acpi_processor_idle;
 
 	return_VALUE(result);
 }
@@ -983,6 +985,17 @@ static struct file_operations acpi_proce
 	.release	= single_release,
 };
 
+static void acpi_processor_idle(void)
+{
+	int cpu = raw_smp_processor_id();
+	if (per_cpu(acpi_cpu_idle, cpu))
+		per_cpu(acpi_cpu_idle, cpu)();
+	else if (pm_idle_save)
+		pm_idle_save();
+	else
+		safe_halt();
+}
+
 int acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device)
 {
 	acpi_status		status = 0;
@@ -1046,6 +1059,7 @@ int acpi_processor_power_init(struct acp
 	}
 
 	pr->flags.power_setup_done = 1;
+	per_cpu(acpi_cpu_idle, pr->id) = __acpi_processor_idle;
 
 	return_VALUE(0);
 }
@@ -1055,6 +1069,8 @@ int acpi_processor_power_exit(struct acp
 	ACPI_FUNCTION_TRACE("acpi_processor_power_exit");
 
 	pr->flags.power_setup_done = 0;
+	per_cpu(acpi_cpu_idle, pr->id) = NULL;
+	cpu_idle_wait(cpumask_of_cpu(pr->id));
 
 	if (acpi_device_dir(device))
 		remove_proc_entry(ACPI_PROCESSOR_FILE_POWER,acpi_device_dir(device));
@@ -1068,7 +1084,7 @@ int acpi_processor_power_exit(struct acp
 		 * (pm_idle), Wait for all processors to update cached/local
 		 * copies of pm_idle before proceeding.
 		 */
-		cpu_idle_wait();
+		cpu_idle_wait(cpu_online_map);
 	}
 
 	return_VALUE(0);
arch/i386/kernel/process.c renamed from arch/i386/kernel/process.c~remove_cpu_idle_save +2 -2
diff -puN arch/i386/kernel/process.c~remove_cpu_idle_save arch/i386/kernel/process.c
@@ -207,7 +207,7 @@ void cpu_idle(void)
 	}
 }
 
-void cpu_idle_wait(void)
+void cpu_idle_wait(cpumask_t wait)
 {
 	unsigned int cpu, this_cpu = get_cpu();
 	cpumask_t map;
@@ -216,7 +216,7 @@ void cpu_idle_wait(void)
 	put_cpu();
 
 	cpus_clear(map);
-	for_each_online_cpu(cpu) {
+	for_each_cpu_mask(cpu, wait) {
 		per_cpu(cpu_idle_state, cpu) = 1;
 		cpu_set(cpu, map);
 	}
arch/x86_64/kernel/process.c renamed from arch/x86_64/kernel/process.c~remove_cpu_idle_save +2 -2
diff -puN arch/x86_64/kernel/process.c~remove_cpu_idle_save arch/x86_64/kernel/process.c
@@ -128,7 +128,7 @@ static void poll_idle (void)
 	}
 }
 
-void cpu_idle_wait(void)
+void cpu_idle_wait(cpumask_t wait)
 {
 	unsigned int cpu, this_cpu = get_cpu();
 	cpumask_t map;
@@ -137,7 +137,7 @@ void cpu_idle_wait(void)
 	put_cpu();
 
 	cpus_clear(map);
-	for_each_online_cpu(cpu) {
+	for_each_cpu_mask(cpu, wait) {
 		per_cpu(cpu_idle_state, cpu) = 1;
 		cpu_set(cpu, map);
 	}
arch/ia64/kernel/process.c renamed from arch/ia64/kernel/process.c~remove_cpu_idle_save +2 -2
diff -puN arch/ia64/kernel/process.c~remove_cpu_idle_save arch/ia64/kernel/process.c
@@ -231,7 +231,7 @@ static inline void play_dead(void)
 }
 #endif /* CONFIG_HOTPLUG_CPU */
 
-void cpu_idle_wait(void)
+void cpu_idle_wait(cpumask_t wait)
 {
 	unsigned int cpu, this_cpu = get_cpu();
 	cpumask_t map;
@@ -240,7 +240,7 @@ void cpu_idle_wait(void)
 	put_cpu();
 
 	cpus_clear(map);
-	for_each_online_cpu(cpu) {
+	for_each_cpu_mask(cpu, wait) {
 		per_cpu(cpu_idle_state, cpu) = 1;
 		cpu_set(cpu, map);
 	}
include/asm-x86_64/system.h renamed from include/asm-x86_64/system.h~remove_cpu_idle_save +2 -1
diff -puN include/asm-x86_64/system.h~remove_cpu_idle_save include/asm-x86_64/system.h
@@ -326,7 +326,8 @@ static inline unsigned long __cmpxchg(vo
 /* For spinlocks etc */
 #define local_irq_save(x) 	do { warn_if_not_ulong(x); __asm__ __volatile__("# local_irq_save \n\t pushfq ; popq %0 ; cli":"=g" (x): /* no input */ :"memory"); } while (0)
 
-void cpu_idle_wait(void);
+#include <linux/cpumask.h>
+void cpu_idle_wait(cpumask_t wait);
 
 /*
  * disable hlt during certain critical i/o operations
include/asm-i386/system.h renamed from include/asm-i386/system.h~remove_cpu_idle_save +2 -1
diff -puN include/asm-i386/system.h~remove_cpu_idle_save include/asm-i386/system.h
@@ -466,7 +466,8 @@ void disable_hlt(void);
 void enable_hlt(void);
 
 extern int es7000_plat;
-void cpu_idle_wait(void);
+#include <linux/cpumask.h>
+void cpu_idle_wait(cpumask_t wait);
 
 extern unsigned long arch_align_stack(unsigned long sp);
 
include/asm-ia64/system.h renamed from include/asm-ia64/system.h~remove_cpu_idle_save +2 -1
diff -puN include/asm-ia64/system.h~remove_cpu_idle_save include/asm-ia64/system.h
@@ -276,7 +276,8 @@ extern void ia64_load_extra (struct task
 
 #define ia64_platform_is(x) (strcmp(x, platform_name) == 0)
 
-void cpu_idle_wait(void);
+#include <linux/cpumask.h>
+void cpu_idle_wait(cpumask_t wait);
 
 #define arch_align_stack(x) (x)
 
arch/i386/kernel/apm.c renamed from arch/i386/kernel/apm.c~remove_cpu_idle_save +1 -1
diff -puN arch/i386/kernel/apm.c~remove_cpu_idle_save arch/i386/kernel/apm.c
@@ -2370,7 +2370,7 @@ static void __exit apm_exit(void)
 		 * (pm_idle), Wait for all processors to update cached/local
 		 * copies of pm_idle before proceeding.
 		 */
-		cpu_idle_wait();
+		cpu_idle_wait(cpu_online_map);
 	}
 	if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)
 	    && (apm_info.connection_version > 0x0100)) {
kernel/sched.c renamed from kernel/sched.c~remove_cpu_idle_save +1 -0
diff -puN kernel/sched.c~remove_cpu_idle_save kernel/sched.c
@@ -3771,6 +3771,7 @@ EXPORT_SYMBOL(cpu_present_map);
 
 #ifndef CONFIG_SMP
 cpumask_t cpu_online_map = CPU_MASK_ALL;
+EXPORT_SYMBOL(cpu_online_map);
 cpumask_t cpu_possible_map = CPU_MASK_ALL;
 #endif
 

_

-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf