x86 NMI-safe INT3 and Page Fault (v5)
A message that is not in the archive (sent off-list or not yet received): 20080417165839.GA25198@Krystal
A message that is not in the archive (sent off-list or not yet received): 20080417165944.GB25198@Krystal
RFC PATCH 1/1 x86 NMI-safe INT3 and Page Fault (v5)
(hopefully finally CCing LKML) :)
Implements an alternative iret with popf and return so trap and exception handlers can return to the NMI handler without issuing iret. iret would cause NMIs to be reenabled prematurely. x86_32 uses popf and far return. x86_64 has to copy the return instruction pointer to the top of the previous stack, issue a popf, loads the previous esp and issue a near return (ret).
It allows placing immediate values (and therefore optimized trace_marks) in NMI code since returning from a breakpoint would be valid. Accessing vmalloc'd memory, which allows executing module code or accessing vmapped or vmalloc'd areas from NMI context, would also be valid. This is very useful to tracers like LTTng.
This patch makes all faults, traps and exception safe to be called from NMI context *except* single-stepping, which requires iret to restore the TF (trap flag) and jump to the return address in a single instruction. Sorry, no kprobes support in NMI handlers because of this limitation. We cannot single-step an NMI handler, because iret must set the TF flag and return back to the instruction to single-step in a single instruction. This cannot be emulated with popf/lret, because lret would be single-stepped. It does not apply to immediate values because they do not use single-stepping. This code detects if the TF flag is set and uses the iret path for single-stepping, even if it reactivates NMIs prematurely.
alpha and avr32 use the active count bit 31. This patch moves them to 28.
TODO : test alpha and avr32 active count modification
TODO : add paravirt support for the iret alternative. Currently, paravirt kernels running on bare metal still use iret in traps nested over nmi handlers.
tested on x86_32 (tests implemented in a separate patch) :
- instrumented the return path to export the EIP, CS and EFLAGS values when taken so we know the return path code has been executed.
- trace_mark, using immediate values, with 10ms delay with the breakpoint activated. Runs well through the return path.
- tested vmalloc faults in NMI handler by placing a non-optimized marker in the NMI handler (so no breakpoint is executed) and connecting a probe which touches every pages of a 20MB vmalloc'd buffer. It executes trough the return path without problem.
- Tested with and without preemption
tested on x86_64
- instrumented the return path to export the EIP, CS and EFLAGS values when taken so we know the return path code has been executed.
- trace_mark, using immediate values, with 10ms delay with the breakpoint activated. Runs well through the return path.
To test on x86_64 :
- Test without preemption
- Test vmalloc faults
- Test on Intel 64 bits CPUs.
Changelog since v1 :
- x86_64 fixes.
Changelog since v2 :
- fix paravirt build
Changelog since v3 :
- Include modifications suggested by Jeremy
Changelog since v3 :
- including hardirq.h in entry_32/64.S is a bad idea (non ifndef'd C code), define HARDNMI_MASK in the .S files directly.
--- arch/x86/kernel/entry_32.S | 27 ++++++++++++++++++- arch/x86/kernel/entry_64.S | 26 ++++++++++++++++++ include/asm-alpha/thread_info.h | 2 - include/asm-avr32/thread_info.h | 2 - include/asm-x86/irqflags.h | 55 ++++++++++++++++++++++++++++++++++++++++ include/asm-x86/paravirt.h | 2 + include/linux/hardirq.h | 24 ++++++++++++++++- 7 files changed, 133 insertions(+), 5 deletions(-)
include/linux/hardirq.h +22 -2
@@ -22,10 +22,13 @@* PREEMPT_MASK: 0x000000ff * SOFTIRQ_MASK: 0x0000ff00 * HARDIRQ_MASK: 0x0fff0000 +* HARDNMI_MASK: 0x40000000 */ #define PREEMPT_BITS 8#define SOFTIRQ_BITS 8 +#define HARDNMI_BITS 1 +#ifndef HARDIRQ_BITS #define HARDIRQ_BITS 12
@@ -45,16 +48,19 @@#define PREEMPT_SHIFT 0#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) +#define HARDNMI_SHIFT (30)#define __IRQ_MASK (x) ((1UL << (x))-1)#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) +#define HARDNMI_MASK (__IRQ_MASK(HARDNMI_BITS) << HARDNMI_SHIFT)#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT)#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) +#define HARDNMI_OFFSET (1UL << HARDNMI_SHIFT)#if PREEMPT_ACTIVE < (1 << (HARDIRQ_SHIFT +HARDIRQ_BITS ))#error PREEMPT_ACTIVE is too low!
@@ -63,6 +69,7 @@#define hardirq_count () (preempt_count() & HARDIRQ_MASK)#define softirq_count () (preempt_count() & SOFTIRQ_MASK)#define irq_count () (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) +#define hardnmi_count () (preempt_count() & HARDNMI_MASK)/* * Are we doing bottom half or hardware interrupt processing?
@@ -71,6 +78,7 @@#define in_irq () (hardirq_count())#define in_softirq () (softirq_count())#define in_interrupt () (irq_count()) +#define in_nmi () (hardnmi_count())/* * Are we running in atomic context? WARNING: this macro cannot
@@ -159,7 +167,19 @@ extern void irq_enter(void);*/ extern void irq_exit (void ); -#define nmi_enter () do { lockdep_off(); __irq_enter(); } while (0) -#define nmi_exit () do { __irq_exit(); lockdep_on(); } while (0) +#define nmi_enter () \ + do { \ + lockdep_off(); \ + BUG_ON(hardnmi_count()); \ + add_preempt_count(HARDNMI_OFFSET); \ + __irq_enter(); \ + } while (0) + +#define nmi_exit () \ + do { \ + __irq_exit(); \ + sub_preempt_count(HARDNMI_OFFSET); \ + lockdep_on(); \ + } while (0)#endif /* LINUX_HARDIRQ_H */
arch/x86/kernel/entry_32.S +26 -1
@@ -75,11 +75,12 @@ DF_MASK = 0x00000400 NT_MASK = 0x00004000 VM_MASK = 0x00020000 +#define HARDNMI_MASK 0x40000000 + #ifdef CONFIG_PREEMPT #define preempt_stop(clobbers) DISABLE_INTERRUPTS(clobbers); TRACE_IRQS_OFF #else #define preempt_stop(clobbers) -#define resume_kernel restore_nocheck #endif .macro TRACE_IRQS_IRET
@@ -265,6 +266,8 @@ END(ret_from_exception) #ifdef CONFIG_PREEMPT ENTRY(resume_kernel) DISABLE_INTERRUPTS(CLBR_ANY) + testl $HARDNMI_MASK,TI_preempt_count(%ebp) # nested over NMI ? + jnz return_to_nmi cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? jnz restore_nocheck need_resched:
@@ -276,6 +279,12 @@ need_resched: call preempt_schedule_irq jmp need_resched END(resume_kernel) +#else +ENTRY(resume_kernel) + testl $HARDNMI_MASK,TI_preempt_count(%ebp) # nested over NMI ? + jnz return_to_nmi + jmp restore_nocheck +END(resume_kernel) #endif CFI_ENDPROC
@@ -411,6 +420,22 @@ restore_nocheck_notrace: CFI_ADJUST_CFA_OFFSET -4 irq_return: INTERRUPT_RETURN +return_to_nmi: + testl $X86_EFLAGS_TF, PT_EFLAGS(%esp) + jnz restore_nocheck /* + * If single-stepping an NMI handler, + * use the normal iret path instead of + * the popf/lret because lret would be + * single-stepped. It should not + * happen : it will reactivate NMIs + * prematurely. + */ + TRACE_IRQS_IRET + RESTORE_REGS + addl $4, %esp # skip orig_eax/error_code + CFI_ADJUST_CFA_OFFSET -4 + INTERRUPT_RETURN_NMI_SAFE + .section .fixup,"ax" iret_exc: pushl $0 # no error code
arch/x86/kernel/entry_64.S +26 -0
@@ -54,6 +54,8 @@ .code64 +#define HARDNMI_MASK 0x40000000 + #ifndef CONFIG_PREEMPT #define retint_kernel retint_restore_args #endif
@@ -581,12 +583,27 @@ retint_restore_args: /* return to kernel * The iretq could re-enable interrupts: */ TRACE_IRQS_IRETQ + testl $HARDNMI_MASK,threadinfo_preempt_count(%rcx) + jnz return_to_nmi /* Nested over NMI ? */ restore_args: RESTORE_ARGS 0,8,0 irq_return: INTERRUPT_RETURN +return_to_nmi: /* + * If single-stepping an NMI handler, + * use the normal iret path instead of + * the popf/lret because lret would be + * single-stepped. It should not + * happen : it will reactivate NMIs + * prematurely. + */ + testw $X86_EFLAGS_TF,EFLAGS-ARGOFFSET(%rsp) /* trap flag? */ + jnz restore_args + RESTORE_ARGS 0,8,0 + INTERRUPT_RETURN_NMI_SAFE + .section __ex_table, "a" .quad irq_return, bad_iret .previous
@@ -802,6 +819,10 @@ END(spurious_interrupt) .macro paranoidexit trace=1 /* ebx: no swapgs flag */ paranoid_exit\trace: + GET_THREAD_INFO(%rcx) + testl $HARDNMI_MASK,threadinfo_preempt_count(%rcx) + jnz paranoid_return_to_nmi\trace /* Nested over NMI ? */ +paranoid_exit_no_nmi\trace: testl %ebx,%ebx /* swapgs needed? */ jnz paranoid_restore\trace testl $3,CS(%rsp)
@@ -814,6 +835,11 @@ paranoid_swapgs\trace: paranoid_restore\trace: RESTORE_ALL 8 jmp irq_return +paranoid_return_to_nmi\trace: + testw $X86_EFLAGS_TF,EFLAGS-0(%rsp) /* trap flag? */ + jnz paranoid_exit_no_nmi\trace + RESTORE_ALL 8 + INTERRUPT_RETURN_NMI_SAFE paranoid_userspace\trace: GET_THREAD_INFO(%rcx) movl threadinfo_flags(%rcx),%ebx
include/asm-x86/irqflags.h +55 -0
67 lines not on this page · raw message
include/asm-alpha/thread_info.h +1 -1
@@ -57,7 +57,7 @@ register struct thread_info *__current_t#endif /* __ASSEMBLY__ */ -#define PREEMPT_ACTIVE 0x40000000 +#define PREEMPT_ACTIVE 0x10000000/* * Thread information flags:
include/asm-avr32/thread_info.h +1 -1
@@ -70,7 +70,7 @@ static inline struct thread_info *curren#endif /* !__ASSEMBLY__ */ -#define PREEMPT_ACTIVE 0x40000000 +#define PREEMPT_ACTIVE 0x10000000/* * Thread information flags
include/asm-x86/paravirt.h +2 -0
@@ -1358,6 +1358,8 @@ static inline unsigned long __raw_local_PARA_SITE (PARA_PATCH (pv_cpu_ops, PV_CPU_iret),CLBR_NONE , \ jmp *%cs:pv_cpu_ops+PV_CPU_iret) +#define INTERRUPT_RETURN_NMI_SAFE INTERRUPT_RETURN +#define DISABLE_INTERRUPTS (clobbers) \ PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_irq_disable), clobbers, \ PV_SAVE_REGS; \
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
include/linux/hardirq.h extern void irq_enter(void); · line 176
Quoted 7 lines · On Thu, 17 Apr 2008 16:14:10 -0400 Mathieu Desnoyers…
include/linux/hardirq.h +7
-0+#define nmi_enter () \ + do { \ + lockdep_off(); \ + BUG_ON(hardnmi_count()); \ + add_preempt_count(HARDNMI_OFFSET); \ + __irq_enter(); \ + } while (0)
<did it _have_ to be a macro?>
Doing BUG() inside an NMI should be OK most of the time. But the BUG-handling code does want to know if we're in interrupt context - at least for the "fatal exception in interrupt" stuff, and probably other things.
But afacit the failure to include HARDNMI_MASK in
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))
will prevent that.
So.
Should we or should we not make in_interrupt() return true in NMI? "should", I expect.
If not, we'd need to do something else to communicate the current processing state down to the BUG-handling code.
Quoted 13 lines · * Andrew Morton ([email protected]) wrote:
include/linux/hardirq.h extern void irq_enter(void); · line 176
Quoted 7 lines · On Thu, 17 Apr 2008 16:14:10 -0400 Mathieu Desnoyers…
include/linux/hardirq.h +7
-0+#define nmi_enter () \ + do { \ + lockdep_off(); \ + BUG_ON(hardnmi_count()); \ + add_preempt_count(HARDNMI_OFFSET); \ + __irq_enter(); \ + } while (0)<did it _have_ to be a macro?>
isn't this real macro art work ? ;) I kept the same coding style that was already there, which mimics the irq_enter/irq_exit macros. Changing all of them at once could be done in a separate patch.
Quoted 11 lines
Doing
BUG()inside an NMI should be OK most of the time. But the BUG-handling code does want to know if we're in interrupt context - at least for the "fatal exception in interrupt" stuff, and probably other things.But afacit the failure to include
HARDNMI_MASKin#define
irq_count()(preempt_count()& (HARDIRQ_MASK|SOFTIRQ_MASK))will prevent that.
Quoted 8 lines
So.
Should we or should we not make
in_interrupt()return true in NMI? "should", I expect.If not, we'd need to do something else to communicate the current processing state down to the BUG-handling code.
You bring an interesting question. In practice, since this BUG_ON could only happen if we have an NMI nested over another NMI or an nmi which fails to decrement its HARDNMI_MASK. Given that the HARDIRQ_MASK is incremented right after the HARDNMI_MASK increment (the reverse is also true), really bad things (TM) must have happened for the BUG_ON to be triggered outside of the __irq_enter()/__irq_exit() scope of the NMI below the buggy one.
But since this code is there to extract as much information as possible when things go wrong, I would say it's safer to, at least, add HARDNMI_MASK to irq_count().
Instead, though, I think we could add :
if (in_nmi())
panic("Fatal exception in non-maskable interrupt");
to die(). That would be clearer. I just added it to x86_32, but can't find where x86_64 reports the "fatal exception in interrupt" and friends message. Any idea ?
By dealing with this case specifically, I think we don't really have to add HARDNMI_MASK to irq_count(), considering it's normally an HARDIRQ too.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 19 lines · On Thu, 17 Apr 2008 17:16:25 -0400 Mathieu Desnoyers…
Quoted 6 lines
Should we or should we not make
in_interrupt()return true in NMI? "should", I expect.If not, we'd need to do something else to communicate the current processing state down to the BUG-handling code.
You bring an interesting question. In practice, since this
BUG_ONcould only happen if we have an NMI nested over another NMI or an nmi which fails to decrement itsHARDNMI_MASK. Given that theHARDIRQ_MASKis incremented right after theHARDNMI_MASKincrement (the reverse is also true), really bad things (TM) must have happened for theBUG_ONto be triggered outside of the __irq_enter()/__irq_exit() scope of the NMI below the buggy one.But since this code is there to extract as much information as possible when things go wrong, I would say it's safer to, at least, add
HARDNMI_MASKtoirq_count().
Quoted 6 lines
Instead, though, I think we could add :
if (
in_nmi())
panic("Fatal exception in non-maskable interrupt");to
die().
But that's just one site. There might be (now, or in the future) other code under BUG() which tests in_interrupt().
And most of the places where we test for in_interrupt() and in_irq() probably want that to return true is we're in NMI too. After all, it's an interrupt.
Quoted 3 lines
That would be clearer. I just added it to
x86_32, but can't find wherex86_64reports the "fatal exception in interrupt" and friends message. Any idea ?
Dunno - maybe it just doesn't have it. Maybe it was never the right thing to do.
Quoted 3 lines
By dealing with this case specifically, I think we don't really have to add
HARDNMI_MASKtoirq_count(), considering it's normally an HARDIRQ too.
Quoted 3 lines · Mathieu Desnoyers
It allows placing immediate values (and therefore optimized
trace_marks) in NMI code
Only if all your trace_mark infrastructure is lock less.
-Andi
Quoted 9 lines · * Andi Kleen ([email protected]) wrote:
Quoted 3 lines · Mathieu Desnoyers
It allows placing immediate values (and therefore optimized
trace_marks) in NMI codeOnly if all your
trace_markinfrastructure is lock less.-Andi
It uses RCU-style updates and has been designed to be lockless from the ground up.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 11 lines · Mathieu Desnoyers
Quoted 7 lines · * Andi Kleen ([email protected]) wrote:
Quoted 2 lines · Mathieu Desnoyers
It allows placing immediate values (and therefore optimized
trace_marks) in NMI codeOnly if all your
trace_markinfrastructure is lock less.-Andi
It uses RCU-style updates and has been designed to be lockless from the ground up.
Wrong. If it causes vmalloc faults it is not lockless.
-Andi
Quoted 10 lines · * Andi Kleen ([email protected]) wrote:
Quoted 9 lines · Mathieu Desnoyers
Quoted 7 lines · * Andi Kleen ([email protected]) wrote:
Quoted 2 lines · Mathieu Desnoyers
It allows placing immediate values (and therefore optimized
trace_marks) in NMI codeOnly if all your
trace_markinfrastructure is lock less.-Andi
Quoted 7 lines
Quoted 2 lines
It uses RCU-style updates and has been designed to be lockless from the ground up.
Wrong. If it causes vmalloc faults it is not lockless.
-Andi
Could you point me where vmalloc_fault accesses a data structure for which updates are protected by disabling interrupts ? I am curious.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 3 lines · Mathieu Desnoyers
It uses RCU-style updates and has been designed to be lockless from the ground up.
RCU is not necessarily NMI safe. In most cases RCU needs writer locks which you cannot do with NMIs.
-Andi
Quoted 10 lines · * Andi Kleen ([email protected]) wrote:
Quoted 3 lines · Mathieu Desnoyers
It uses RCU-style updates and has been designed to be lockless from the ground up.
RCU is not necessarily NMI safe. In most cases RCU needs writer locks which you cannot do with NMIs.
-Andi
RCU-style updates are done outside of NMIs, in sleepable context. That's just required when the probes connected on markers must be registered/unregistered.
The NMI context is the RCU read side. It only have to get the probe function pointers to call along with the private data pointers.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 7 lines · Mathieu Desnoyers
(hopefully finally CCing LKML) :)
Implements an alternative iret with popf and return so trap and exception handlers can return to the NMI handler without issuing iret. iret would cause NMIs to be reenabled prematurely.
x86_32uses popf and far return.x86_64has to copy the return instruction pointer to the top of the previous stack, issue a popf, loads the previous esp and issue a near return (ret).
sounds expensive. Does it slow down normal loads?
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
H. Peter Anvin
reviewerQuoted 11 lines · Pavel Machek
Quoted 7 lines · Mathieu Desnoyers
(hopefully finally CCing LKML) :)
Implements an alternative iret with popf and return so trap and exception handlers can return to the NMI handler without issuing iret. iret would cause NMIs to be reenabled prematurely.
x86_32uses popf and far return.x86_64has to copy the return instruction pointer to the top of the previous stack, issue a popf, loads the previous esp and issue a near return (ret).sounds expensive. Does it slow down normal loads?
It should *only* be used to return from NMI, #MC or INT3 (breakpoint), which should never happen in normal operation, and even then only when interrupting another NMI or #MC handler.
-hpa
Quoted 12 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 11 lines · Pavel Machek
Quoted 10 lines · Mathieu Desnoyers
(hopefully finally CCing LKML) :)
Implements an alternative iret with popf and return so trap and exception handlers can return to the NMI handler without issuing iret. iret would cause
NMIs to be reenabled prematurely.x86_32uses popf and far return.x86_64has to
copy the return instruction pointer to the top of the previous stack, issue a
popf, loads the previous esp and issue a near return (ret).
Quoted 8 lines
Quoted 1 line
sounds expensive. Does it slow down normal loads?
It should *only* be used to return from NMI, #MC or INT3 (breakpoint), which should never happen in normal operation, and even then only when interrupting another NMI or #MC handler.
-hpa
Sorry Pavel, for some reason you message did not reach my inbox. hpa is right : this code path is only taken to return to the NMI handler from a trap or exception or, possibly, machine check exception.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 12 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 11 lines · Pavel Machek
Quoted 10 lines · Mathieu Desnoyers
(hopefully finally CCing LKML) :)
Implements an alternative iret with popf and return so trap and exception handlers can return to the NMI handler without issuing iret. iret would cause
NMIs to be reenabled prematurely.x86_32uses popf and far return.x86_64has to
copy the return instruction pointer to the top of the previous stack, issue a
popf, loads the previous esp and issue a near return (ret).
Quoted 8 lines
Quoted 1 line
sounds expensive. Does it slow down normal loads?
It should *only* be used to return from NMI, #MC or INT3 (breakpoint), which should never happen in normal operation, and even then only when interrupting another NMI or #MC handler.
-hpa
Just to be clear : the added cost on normal interrupt return is to add a supplementary test of the thread flags already loaded in registers and a conditional branch. This is used to detect if we are nested over an NMI handler. I doubt anyone ever notice an impact caused by this added test/branch.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 19 lines · * Mathieu Desnoyers ([email protected]) wrote:
Quoted 18 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 11 lines · Pavel Machek
Quoted 10 lines · Mathieu Desnoyers
(hopefully finally CCing LKML) :)
Implements an alternative iret with popf and return so trap and exception handlers can return to the NMI handler without issuing iret. iret would cause
NMIs to be reenabled prematurely.x86_32uses popf and far return.x86_64has to
copy the return instruction pointer to the top of the previous stack, issue a
popf, loads the previous esp and issue a near return (ret).Quoted 1 line
sounds expensive. Does it slow down normal loads?
It should *only* be used to return from NMI, #MC or INT3 (breakpoint), which should never happen in normal operation, and even then only when interrupting another NMI or #MC handler.
Quoted 5 lines
Quoted 2 lines
-hpa
Just to be clear : the added cost on normal interrupt return is to add a supplementary test of the thread flags already loaded in registers and
err, by thread flag, I meant thread preempt count. And it's not in registers, so it has to be read from the data cache (it's clearly already there).
Quoted 10 lines
a conditional branch. This is used to detect if we are nested over an NMI handler. I doubt anyone ever notice an impact caused by this added test/branch.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
H. Peter Anvin
reviewerQuoted 7 lines · Mathieu Desnoyers
Just to be clear : the added cost on normal interrupt return is to add a supplementary test of the thread flags already loaded in registers and a conditional branch. This is used to detect if we are nested over an NMI handler. I doubt anyone ever notice an impact caused by this added test/branch.
Why the **** would you do this except in the handful of places where you actually *could* be nested over an NMI handler (basically #MC, #DB and INT3)?
-hpa
Quoted 7 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 5 lines · Mathieu Desnoyers
Just to be clear : the added cost on normal interrupt return is to add a supplementary test of the thread flags already loaded in registers and a conditional branch. This is used to detect if we are nested over an NMI handler. I doubt anyone ever notice an impact caused by this added test/branch.
Quoted 6 lines
Why the **** would you do this except in the handful of places where you actually *could* be nested over an NMI handler (basically #MC, #DB and INT3)?
-hpa
Because I would have to do a more invasive code modification, since they currently share their return path with normal interrupts. I agree that the next step is to tune the patchset to only target traps and exceptions which may happen on top of an NMI. I'll change it in my next patchset version.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 7 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 5 lines · Mathieu Desnoyers
Just to be clear : the added cost on normal interrupt return is to add a supplementary test of the thread flags already loaded in registers and a conditional branch. This is used to detect if we are nested over an NMI handler. I doubt anyone ever notice an impact caused by this added test/branch.
Quoted 6 lines
Why the **** would you do this except in the handful of places where you actually *could* be nested over an NMI handler (basically #MC, #DB and INT3)?
-hpa
There is also the page fault case. I think putting this test in ret_from_exception would be both safe (it is executed for any exception return) and fast (exceptions are rare).
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 8 lines · Mathieu Desnoyers
Quoted 7 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 5 lines · Mathieu Desnoyers
Just to be clear : the added cost on normal interrupt return is to add a supplementary test of the thread flags already loaded in registers and a conditional branch. This is used to detect if we are nested over an NMI handler. I doubt anyone ever notice an impact caused by this added test/branch.
Quoted 8 lines
Quoted 3 lines
Why the **** would you do this except in the handful of places where you actually *could* be nested over an NMI handler (basically #MC, #DB and INT3)?
There is also the page fault case. I think putting this test in
ret_from_exceptionwould be both safe (it is executed for any exception return) and fast (exceptions are rare).
Eh? I thought that page fault is one of the hottest paths in kernel (along with syscall and packet receive/send)...
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
H. Peter Anvin
reviewerQuoted 8 lines · Pavel Machek
Quoted 4 lines
There is also the page fault case. I think putting this test in
ret_from_exceptionwould be both safe (it is executed for any exception return) and fast (exceptions are rare).Eh? I thought that page fault is one of the hottest paths in kernel (along with syscall and packet receive/send)...
Pavel
Yeah, and the concept of handling page faults inside an NMI handler is pure fantasy.
-hpa
Quoted 8 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 6 lines · Pavel Machek
Quoted 3 lines
There is also the page fault case. I think putting this test in
ret_from_exceptionwould be both safe (it is executed for any exception return) and fast (exceptions are rare).Eh? I thought that page fault is one of the hottest paths in kernel (along with syscall and packet receive/send)...
Pavel
On x86_64, we can pinpoint only the page faults returning to the kernel, which are rare and only caused by vmalloc accesses. Ideally we could do the same on x86_32.
Quoted 5 lines
Yeah, and the concept of handling page faults inside an NMI handler is pure fantasy.
-hpa
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
H. Peter Anvin
reviewerQuoted 5 lines · Mathieu Desnoyers
Quoted 4 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 3 lines · Pavel Machek
Quoted 3 lines
There is also the page fault case. I think putting this test in
ret_from_exceptionwould be both safe (it is executed for any exception return) and fast (exceptions are rare).
Quoted 8 lines
Quoted 3 lines
Quoted 3 lines
Eh? I thought that page fault is one of the hottest paths in kernel (along with syscall and packet receive/send)...
PavelOn
x86_64, we can pinpoint only the page faults returning to the kernel, which are rare and only caused by vmalloc accesses. Ideally we could do the same onx86_32.
Pinpoint, how? Ultimately you need a runtime test, and you better be showing that people are going to die unless before you add a cycle to the page fault path. I'm only slightly exaggerating that.
-hpa
Quoted 9 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 8 lines · Mathieu Desnoyers
Quoted 7 lines · * H. Peter Anvin ([email protected]) wrote:
Quoted 6 lines · Pavel Machek
Quoted 3 lines
There is also the page fault case. I think putting this test in
ret_from_exceptionwould be both safe (it is executed for any exception return) and fast (exceptions are rare).Eh? I thought that page fault is one of the hottest paths in kernel (along with syscall and packet receive/send)...
Pavel
Quoted 8 lines
Quoted 3 lines
On
x86_64, we can pinpoint only the page faults returning to the kernel, which are rare and only caused by vmalloc accesses. Ideally we could do the same onx86_32.Pinpoint, how? Ultimately you need a runtime test, and you better be showing that people are going to die unless before you add a cycle to the page fault path. I'm only slightly exaggerating that.
On x86_32, ret_from_exception identifies the return path taken to return from an exception. By dulicating the check_userspace code both in the ret_from_intr and in the ret_from_exception (that's only 4 instructions), we can know if we are in the specific condition of returning to the kernel from an exception without any supplementary test. Therefore, we can do the nmi nesting test only in the specific return-to-kernel-from-exception case without slowing down any critical code.
Something similar is done on x86_64. That will appear in my next version.
Thanks,
Mathieu
Quoted 2 lines
-hpa
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68