aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWanpeng Li <wanpeng.li@hotmail.com>2016-11-06 22:13:40 -0500
committerThomas Gleixner <tglx@linutronix.de>2016-11-09 16:03:14 -0500
commit8ca225520e278e41396dab0524989f4848626f83 (patch)
treec71f406bb7dc56ef41e7043672b5842593998f09
parentb2c5ea4f759190ee9f75687a00035c1a66d0d743 (diff)
x86/apic: Prevent tracing on apic_msr_write_eoi()
The following RCU lockdep warning led to adding irq_enter()/irq_exit() into smp_reschedule_interrupt(): RCU used illegally from idle CPU! rcu_scheduler_active = 1, debug_locks = 0 RCU used illegally from extended quiescent state! no locks held by swapper/1/0. do_trace_write_msr native_write_msr native_apic_msr_eoi_write smp_reschedule_interrupt reschedule_interrupt As Peterz pointed out: | So now we're making a very frequent interrupt slower because of debug | code. | | The thing is, many many smp_reschedule_interrupt() invocations don't | actually execute anything much at all and are only sent to tickle the | return to user path (which does the actual preemption). | | Having to do the whole irq_enter/irq_exit dance just for this unlikely | debug case totally blows. Use the wrmsr_notrace() variant in native_apic_msr_write_eoi, annotate the kvm variant with notrace and add a native_apic_eoi callback to the apic structure so KVM guests are covered as well. This allows to revert the irq_enter/irq_exit dance in smp_reschedule_interrupt(). Suggested-by: Peter Zijlstra <peterz@infradead.org> Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Wanpeng Li <wanpeng.li@hotmail.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Cc: kvm@vger.kernel.org Cc: Mike Galbraith <efault@gmx.de> Cc: Borislav Petkov <bp@alien8.de> Link: http://lkml.kernel.org/r/1478488420-5982-3-git-send-email-wanpeng.li@hotmail.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/include/asm/apic.h3
-rw-r--r--arch/x86/kernel/apic/apic.c1
-rw-r--r--arch/x86/kernel/kvm.c4
-rw-r--r--arch/x86/kernel/smp.c2
4 files changed, 5 insertions, 5 deletions
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index f5aaf6c83222..a5a0bcfde76a 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -196,7 +196,7 @@ static inline void native_apic_msr_write(u32 reg, u32 v)
196 196
197static inline void native_apic_msr_eoi_write(u32 reg, u32 v) 197static inline void native_apic_msr_eoi_write(u32 reg, u32 v)
198{ 198{
199 wrmsr(APIC_BASE_MSR + (APIC_EOI >> 4), APIC_EOI_ACK, 0); 199 wrmsr_notrace(APIC_BASE_MSR + (APIC_EOI >> 4), APIC_EOI_ACK, 0);
200} 200}
201 201
202static inline u32 native_apic_msr_read(u32 reg) 202static inline u32 native_apic_msr_read(u32 reg)
@@ -332,6 +332,7 @@ struct apic {
332 * on write for EOI. 332 * on write for EOI.
333 */ 333 */
334 void (*eoi_write)(u32 reg, u32 v); 334 void (*eoi_write)(u32 reg, u32 v);
335 void (*native_eoi_write)(u32 reg, u32 v);
335 u64 (*icr_read)(void); 336 u64 (*icr_read)(void);
336 void (*icr_write)(u32 low, u32 high); 337 void (*icr_write)(u32 low, u32 high);
337 void (*wait_icr_idle)(void); 338 void (*wait_icr_idle)(void);
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 88c657b057e2..2686894350a4 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2263,6 +2263,7 @@ void __init apic_set_eoi_write(void (*eoi_write)(u32 reg, u32 v))
2263 for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) { 2263 for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
2264 /* Should happen once for each apic */ 2264 /* Should happen once for each apic */
2265 WARN_ON((*drv)->eoi_write == eoi_write); 2265 WARN_ON((*drv)->eoi_write == eoi_write);
2266 (*drv)->native_eoi_write = (*drv)->eoi_write;
2266 (*drv)->eoi_write = eoi_write; 2267 (*drv)->eoi_write = eoi_write;
2267 } 2268 }
2268} 2269}
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index edbbfc854e39..aad52f1b0f9f 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -308,7 +308,7 @@ static void kvm_register_steal_time(void)
308 308
309static DEFINE_PER_CPU(unsigned long, kvm_apic_eoi) = KVM_PV_EOI_DISABLED; 309static DEFINE_PER_CPU(unsigned long, kvm_apic_eoi) = KVM_PV_EOI_DISABLED;
310 310
311static void kvm_guest_apic_eoi_write(u32 reg, u32 val) 311static notrace void kvm_guest_apic_eoi_write(u32 reg, u32 val)
312{ 312{
313 /** 313 /**
314 * This relies on __test_and_clear_bit to modify the memory 314 * This relies on __test_and_clear_bit to modify the memory
@@ -319,7 +319,7 @@ static void kvm_guest_apic_eoi_write(u32 reg, u32 val)
319 */ 319 */
320 if (__test_and_clear_bit(KVM_PV_EOI_BIT, this_cpu_ptr(&kvm_apic_eoi))) 320 if (__test_and_clear_bit(KVM_PV_EOI_BIT, this_cpu_ptr(&kvm_apic_eoi)))
321 return; 321 return;
322 apic_write(APIC_EOI, APIC_EOI_ACK); 322 apic->native_eoi_write(APIC_EOI, APIC_EOI_ACK);
323} 323}
324 324
325static void kvm_guest_cpu_init(void) 325static void kvm_guest_cpu_init(void)
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index c00cb64bc0a1..68f8cc222f25 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -261,10 +261,8 @@ static inline void __smp_reschedule_interrupt(void)
261 261
262__visible void smp_reschedule_interrupt(struct pt_regs *regs) 262__visible void smp_reschedule_interrupt(struct pt_regs *regs)
263{ 263{
264 irq_enter();
265 ack_APIC_irq(); 264 ack_APIC_irq();
266 __smp_reschedule_interrupt(); 265 __smp_reschedule_interrupt();
267 irq_exit();
268 /* 266 /*
269 * KVM uses this interrupt to force a cpu out of guest mode 267 * KVM uses this interrupt to force a cpu out of guest mode
270 */ 268 */