Mark Rutland
2020-Dec-09 18:15 UTC
[PATCH v2 05/12] x86: rework arch_local_irq_restore() to not use popf
On Fri, Nov 20, 2020 at 12:59:43PM +0100, Peter Zijlstra wrote:> On Fri, Nov 20, 2020 at 12:46:23PM +0100, Juergen Gross wrote: > > +static __always_inline void arch_local_irq_restore(unsigned long flags) > > +{ > > + if (!arch_irqs_disabled_flags(flags)) > > + arch_local_irq_enable(); > > +} > > If someone were to write horrible code like: > > local_irq_disable(); > local_irq_save(flags); > local_irq_enable(); > local_irq_restore(flags); > > we'd be up some creek without a paddle... now I don't _think_ we have > genius code like that, but I'd feel saver if we can haz an assertion in > there somewhere...I've cobbled that together locally (i'll post it momentarily), and gave it a spin on both arm64 and x86, whereupon it exploded at boot time on x86. In arch/x86/kernel/apic/io_apic.c's timer_irq_works() we do: local_irq_save(flags); local_irq_enable(); [ trigger an IRQ here ] local_irq_restore(flags); ... and in check_timer() we call that a number of times after either a local_irq_save() or local_irq_disable(), eventually trailing with a local_irq_disable() that will balance things up before calling local_irq_restore(). I guess that timer_irq_works() should instead do: local_irq_save(flags); local_irq_enable(); ... local_irq_disable(); local_irq_restore(flags); ... assuming we consider that legitimate? With that, and all the calls to local_irq_disable() in check_timer() removed (diff below) I get a clean boot under QEMU with the assertion hacked in and DEBUG_LOCKDEP enabled. Thanks Mark. ---->8---- diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 7b3c7e0d4a09..e79e665a3aeb 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -1631,6 +1631,7 @@ static int __init timer_irq_works(void) else delay_without_tsc(); + local_irq_disable(); local_irq_restore(flags); /* @@ -2191,7 +2192,6 @@ static inline void __init check_timer(void) goto out; } panic_if_irq_remap("timer doesn't work through Interrupt-remapped IO-APIC"); - local_irq_disable(); clear_IO_APIC_pin(apic1, pin1); if (!no_pin1) apic_printk(APIC_QUIET, KERN_ERR "..MP-BIOS bug: " @@ -2215,7 +2215,6 @@ static inline void __init check_timer(void) /* * Cleanup, just in case ... */ - local_irq_disable(); legacy_pic->mask(0); clear_IO_APIC_pin(apic2, pin2); apic_printk(APIC_QUIET, KERN_INFO "....... failed.\n"); @@ -2232,7 +2231,6 @@ static inline void __init check_timer(void) apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); goto out; } - local_irq_disable(); legacy_pic->mask(0); apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); apic_printk(APIC_QUIET, KERN_INFO "..... failed.\n"); @@ -2251,7 +2249,6 @@ static inline void __init check_timer(void) apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); goto out; } - local_irq_disable(); apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n"); if (apic_is_x2apic_enabled()) apic_printk(APIC_QUIET, KERN_INFO
Thomas Gleixner
2020-Dec-09 18:54 UTC
[PATCH v2 05/12] x86: rework arch_local_irq_restore() to not use popf
On Wed, Dec 09 2020 at 18:15, Mark Rutland wrote:> In arch/x86/kernel/apic/io_apic.c's timer_irq_works() we do: > > local_irq_save(flags); > local_irq_enable(); > > [ trigger an IRQ here ] > > local_irq_restore(flags); > > ... and in check_timer() we call that a number of times after either a > local_irq_save() or local_irq_disable(), eventually trailing with a > local_irq_disable() that will balance things up before calling > local_irq_restore(). > > I guess that timer_irq_works() should instead do: > > local_irq_save(flags); > local_irq_enable(); > ... > local_irq_disable(); > local_irq_restore(flags); > > ... assuming we consider that legitimate?Nah. That's old and insane gunk. Thanks, tglx --- arch/x86/kernel/apic/io_apic.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -1618,21 +1618,16 @@ static void __init delay_without_tsc(voi static int __init timer_irq_works(void) { unsigned long t1 = jiffies; - unsigned long flags; if (no_timer_check) return 1; - local_save_flags(flags); local_irq_enable(); - if (boot_cpu_has(X86_FEATURE_TSC)) delay_with_tsc(); else delay_without_tsc(); - local_irq_restore(flags); - /* * Expect a few ticks at least, to be sure some possible * glue logic does not lock up after one or two first @@ -1641,10 +1636,10 @@ static int __init timer_irq_works(void) * least one tick may be lost due to delays. */ - /* jiffies wrap? */ - if (time_after(jiffies, t1 + 4)) - return 1; - return 0; + local_irq_disable(); + + /* Did jiffies advance? */ + return time_after(jiffies, t1 + 4); } /* @@ -2117,13 +2112,12 @@ static inline void __init check_timer(vo struct irq_cfg *cfg = irqd_cfg(irq_data); int node = cpu_to_node(0); int apic1, pin1, apic2, pin2; - unsigned long flags; int no_pin1 = 0; if (!global_clock_event) return; - local_irq_save(flags); + local_irq_disable(); /* * get/set the timer IRQ vector: @@ -2191,7 +2185,6 @@ static inline void __init check_timer(vo goto out; } panic_if_irq_remap("timer doesn't work through Interrupt-remapped IO-APIC"); - local_irq_disable(); clear_IO_APIC_pin(apic1, pin1); if (!no_pin1) apic_printk(APIC_QUIET, KERN_ERR "..MP-BIOS bug: " @@ -2215,7 +2208,6 @@ static inline void __init check_timer(vo /* * Cleanup, just in case ... */ - local_irq_disable(); legacy_pic->mask(0); clear_IO_APIC_pin(apic2, pin2); apic_printk(APIC_QUIET, KERN_INFO "....... failed.\n"); @@ -2232,7 +2224,6 @@ static inline void __init check_timer(vo apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); goto out; } - local_irq_disable(); legacy_pic->mask(0); apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); apic_printk(APIC_QUIET, KERN_INFO "..... failed.\n"); @@ -2251,7 +2242,6 @@ static inline void __init check_timer(vo apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); goto out; } - local_irq_disable(); apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n"); if (apic_is_x2apic_enabled()) apic_printk(APIC_QUIET, KERN_INFO @@ -2260,7 +2250,7 @@ static inline void __init check_timer(vo panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a " "report. Then try booting with the 'noapic' option.\n"); out: - local_irq_restore(flags); + local_irq_enable(); } /*