commit 86e5bcfd0a39960e68475a5a6108088176d98aae Author: Wolfgang Mauerer Date: Sun Jan 24 23:13:30 2010 +0100 x86: Fix root domain state restoring on exception return diff --git a/arch/x86/kernel/ipipe.c b/arch/x86/kernel/ipipe.c index 4442d96..b8a6ec7 100644 --- a/arch/x86/kernel/ipipe.c +++ b/arch/x86/kernel/ipipe.c @@ -702,19 +702,22 @@ static int __ipipe_xlate_signo[] = { int __ipipe_handle_exception(struct pt_regs *regs, long error_code, int vector) { - unsigned long flags; + bool restore_flags = false; + unsigned long flags = 0; + unsigned long regflags; + unsigned long rawflags; /* Pick up the root domain state of the interrupted context. */ local_save_flags(flags); - if (ipipe_root_domain_p) { + if (ipipe_root_domain_p && irqs_disabled_hw()) { /* * Replicate hw interrupt state into the virtual mask before * calling the I-pipe event handler over the root domain. Also * required later when calling the Linux exception handler. */ - if (irqs_disabled_hw()) - local_irq_disable(); + local_irq_save(flags); + restore_flags = true; } #ifdef CONFIG_KGDB /* catch exception KGDB is interested in over non-root domains */ @@ -725,16 +728,32 @@ int __ipipe_handle_exception(struct pt_regs *regs, long error_code, int vector) #endif /* CONFIG_KGDB */ if (unlikely(ipipe_trap_notify(vector, regs))) { - local_irq_restore_nosync(flags); + if (restore_flags) + local_irq_restore_nosync(flags); return 1; } - /* - * 32-bit: In case we migrated to root domain inside the event - * handler, restore the original IF from exception entry as the - * low-level return code will evaluate it. - */ - __fixup_if(raw_irqs_disabled_flags(flags), regs); + /* NOTE: We don't need a fixup when we entered via non-root domain, + * and also not when we entered over a root domain with interrupts + * enabled. */ + if (restore_flags) { + regflags = regs->flags; + rawflags = __raw_local_save_flags(); + + printk("before fixup: rawflags: %lx, flags: %lx, " + "regs->flags: %lx\n", rawflags, flags, regs->flags); + + __fixup_if(raw_irqs_disabled_flags(flags), regs); + + printk("after fixup: rawflags: %lx, flags: %lx, " + "regs->flags: %lx\n", rawflags, flags, regs->flags); + +// if (regs->flags != regflags) { +// dump_stack(); +// ipipe_trace_panic_dump(); +// panic("__fixup_if did something for real!\n"); +// } + } if (unlikely(!ipipe_root_domain_p)) { /* Detect unhandled faults over non-root domains. */ @@ -770,21 +789,26 @@ int __ipipe_handle_exception(struct pt_regs *regs, long error_code, int vector) * Relevant for 64-bit: Restore root domain state as the low-level * return code will not align it to regs.flags. */ - local_irq_restore_nosync(flags); + if (restore_flags) + local_irq_restore_nosync(flags); return 0; } int __ipipe_divert_exception(struct pt_regs *regs, int vector) { - unsigned long flags; - - /* Same root state handling as in __ipipe_handle_exception. */ - local_save_flags(flags); + bool restore_flags = false; + unsigned long flags = 0; if (ipipe_root_domain_p) { - if (irqs_disabled_hw()) - local_irq_disable(); + if (irqs_disabled_hw()) { + /* + * Same root state handling as in + * __ipipe_handle_exception. + */ + local_irq_save(flags); + restore_flags = true; + } } #ifdef CONFIG_KGDB /* catch int1 and int3 over non-root domains */ @@ -804,16 +828,20 @@ int __ipipe_divert_exception(struct pt_regs *regs, int vector) #endif /* CONFIG_KGDB */ if (unlikely(ipipe_trap_notify(vector, regs))) { - local_irq_restore_nosync(flags); + if (restore_flags) + local_irq_restore_nosync(flags); return 1; } + if (likely(ipipe_root_domain_p)) { + /* see __ipipe_handle_exception */ + __fixup_if(raw_irqs_disabled(), regs); + } + /* - * 32-bit: Due to possible migration inside the event handler, we have - * to restore IF so that low-level return code sets the root domain - * state correctly. + * No need to restore root state in the 64-bit case, the Linux handler + * and the return code will take care of it. */ - __fixup_if(raw_irqs_disabled_flags(flags), regs); return 0; }