generic-ipi: Allow cpus not yet online to call smp_call_function with irqs disabled
Subsystems (3)
PATCH 1/2 generic-ipi: Allow cpus not yet online to call smp_call_function with irqs disabled
Because of deadlock possiblities smp_call_function() is not allowed to be called with interrupts disabled. Add an exception for the cpu not yet online, as no one else can send smp call function interrupt to this cpu that is not yet online and as such deadlock condition is not possible.
--- kernel/smp.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-)
kernel/smp.c +34 -6
@@ -177,6 +177,11 @@ void generic_smp_call_function_interruptint cpu =get_cpu ();/* +* Shouldn't receive this interrupt on a cpu that is not yet online. +*/ +WARN_ON_ONCE (!cpu_online (cpu)); + +/* * Ensure entry is visible on call_function_queue after we have * entered the IPI. See comment in smp_call_function_many. * If we don't have this, then we may miss an entry on the list
@@ -230,6 +235,11 @@ void generic_smp_call_function_single_inunsigned int data_flags;LIST_HEAD (list); +/* +* Shouldn't receive this interrupt on a cpu that is not yet online. +*/ +WARN_ON_ONCE (!cpu_online (smp_processor_id ())); +spin_lock (&q->lock);list_replace_init (&q->list, &list);spin_unlock (&q->lock);
@@ -285,8 +295,14 @@ int smp_call_function_single(int cpu, vo*/ this_cpu =get_cpu (); -/* Can deadlock when called with interrupts disabled */ -WARN_ON_ONCE (irqs_disabled () && !oops_in_progress); +/* +* Can deadlock when called with interrupts disabled. +* We allow cpu's that are not yet online though, as no one else can +* send smp call function interrupt to this cpu and as such deadlocks +* can't happen. +*/ +WARN_ON_ONCE (cpu_online (this_cpu) &&irqs_disabled () + && !oops_in_progress);if (cpu == this_cpu) {local_irq_save (flags);
@@ -329,8 +345,14 @@ void __smp_call_function_single(int cpu, {csd_lock (data); -/* Can deadlock when called with interrupts disabled */ -WARN_ON_ONCE (wait &&irqs_disabled () && !oops_in_progress); +/* +* Can deadlock when called with interrupts disabled. +* We allow cpu's that are not yet online though, as no one else can +* send smp call function interrupt to this cpu and as such deadlocks +* can't happen. +*/ +WARN_ON_ONCE (cpu_online (smp_processor_id ()) && wait &&irqs_disabled () + && !oops_in_progress);generic_exec_single (cpu, data, wait); }
@@ -365,8 +387,14 @@ void smp_call_function_many(const structunsigned long flags;int cpu, next_cpu, this_cpu =smp_processor_id (); -/* Can deadlock when called with interrupts disabled */ -WARN_ON_ONCE (irqs_disabled () && !oops_in_progress); +/* +* Can deadlock when called with interrupts disabled. +* We allow cpu's that are not yet online though, as no one else can +* send smp call function interrupt to this cpu and as such deadlocks +* can't happen. +*/ +WARN_ON_ONCE (cpu_online (this_cpu) &&irqs_disabled () + && !oops_in_progress);/* So, what's a CPU they want? Ignoring this one. */ cpu =cpumask_first_and (mask, cpu_online_mask);
PATCH 2/2 x86: Rendezvous all the cpus for MTRR/PAT init
SDM Vol 3a section titled "MTRR considerations in MP systems" specifies the need for synchronizing the logical cpu's while initializing/updating MTRR.
Currently Linux kernel does the synchronization of all cpu's only when a single MTRR register is programmed/updated. During an AP online (during boot/cpu-online/resume) where we initialize all the MTRR/PAT registers, we don't follow this synchronization algorithm.
This can lead to scenarios where during a dynamic cpu online, that logical cpu is initializing MTRR/PAT with cache disabled (cr0.cd=1) etc while other logical HT sibling continue to run (also with cache disabled because of cr0.cd=1 on its sibling).
Starting from Westmere, VMX transitions with cr0.cd=1 don't work properly (because of some VMX performance optimizations) and the above scenario (with one logical cpu doing VMX activity and another logical cpu coming online) can result in system crash.
Fix the MTRR initialization by doing rendezvous of all the cpus. During boot and resume, we delay the MTRR/PAT init for APs till all the logical cpu's come online and the rendezvous process at the end of AP's bringup, will initialize the MTRR/PAT for all AP's.
For dynamic single cpu online, we synchronize all the logical cpus and do the MTRR/PAT init on the AP that is coming online.
--- arch/x86/include/asm/mtrr.h | 7 ++++++ arch/x86/kernel/cpu/mtrr/main.c | 46 ++++++++++++++++++++++++++++++++-------- arch/x86/kernel/smpboot.c | 14 ++++++++++++ arch/x86/power/cpu.c | 2 - kernel/cpu.c | 14 ++++++++++++ 5 files changed, 73 insertions(+), 10 deletions(-)
arch/x86/include/asm/mtrr.h +7 -0
@@ -121,8 +121,12 @@ extern int mtrr_del_page(int reg, unsignextern void mtrr_centaur_report_mcr (int mcr,u32 lo,u32 hi);extern void mtrr_ap_init (void );extern void mtrr_bp_init (void ); +extern void set_mtrr_aps_delayed_init (void ); +extern void mtrr_aps_init (void ); +extern void mtrr_bp_restore (void );extern int mtrr_trim_uncached_memory (unsigned long end_pfn);extern int amd_special_default_mtrr (void ); +extern u32 mtrr_aps_delayed_init;# else static inline u8 mtrr_type_lookup (u64 addr,u64 end) {
@@ -161,6 +165,9 @@ static inline void mtrr_centaur_report_m#define mtrr_ap_init () do {} while (0)#define mtrr_bp_init () do {} while (0) +#define set_mtrr_aps_delayed_init () do {} while (0) +#define mtrr_aps_init () do {} while (0) +#define mtrr_bp_restore () do {} while (0)# endif #ifdef CONFIG_COMPAT
arch/x86/kernel/cpu/mtrr/main.c +37 -9
82 lines not on this page · raw message
arch/x86/kernel/smpboot.c +14 -0
@@ -1118,9 +1118,22 @@ void __init native_smp_prepare_cpus(unsiif (is_uv_system ())uv_system_init (); + +set_mtrr_aps_delayed_init (); out:preempt_enable (); } + +void arch_enable_nonboot_cpus_begin (void ) +{ +set_mtrr_aps_delayed_init (); +} + +void arch_enable_nonboot_cpus_end (void ) +{ +mtrr_aps_init (); +} +/* * Early setup to make printk work. */
@@ -1142,6 +1155,7 @@ void __init native_smp_cpus_done(unsignesetup_ioapic_dest ();#endif check_nmi_watchdog (); +mtrr_aps_init (); }static int __initdata setup_possible_cpus =-1 ;
arch/x86/power/cpu.c +1 -1
@@ -224,7 +224,7 @@ static void __restore_processor_state(stfix_processor_context ();do_fpu_end (); -mtrr_ap_init (); +mtrr_bp_restore ();#ifdef CONFIG_X86_OLD_MCE mcheck_init (&boot_cpu_data);
kernel/cpu.c +14 -0
@@ -413,6 +413,14 @@ int disable_nonboot_cpus(void)return error; } +void __weak arch_enable_nonboot_cpus_begin (void ) +{ +} + +void __weak arch_enable_nonboot_cpus_end (void ) +{ +} +void __ref enable_nonboot_cpus (void ) {int cpu, error;
@@ -424,6 +432,9 @@ void __ref enable_nonboot_cpus(void) goto out;printk ("Enabling non-boot CPUs ...\n" ); + +arch_enable_nonboot_cpus_begin (); +for_each_cpu (cpu, frozen_cpus) { error =_cpu_up (cpu,1 );if (!error) {
@@ -432,6 +443,9 @@ void __ref enable_nonboot_cpus(void) }printk (KERN_WARNING "Error taking CPU%d up: %d\n" , cpu, error); } + +arch_enable_nonboot_cpus_end (); +cpumask_clear (frozen_cpus); out:cpu_maps_update_done ();