GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing docs.kernel.org — Entry/exit handling for exceptions, interrupts, syscalls and KVM — The Linux Kernel documentation Entry/exit handling for exceptions, interrupts, syscalls and KVM¶All transitions between execution domains require state updates which are subject to strict ordering constraints. State updates are required for the following:
The update order depends on the transition type and is explained below in the transition type sections: Syscalls, KVM, Interrupts and regular exceptions, NMI and NMI-like exceptions. Non-instrumentable code - noinstr¶Most instrumentation facilities depend on RCU, so instrumentation is prohibited for entry code before RCU starts watching and exit code after RCU stops watching. In addition, many architectures must save and restore register state, which means that (for example) a breakpoint in the breakpoint entry code would overwrite the debug registers of the initial breakpoint. Such code must be marked with the ‘noinstr’ attribute, placing that code into a
special section inaccessible to instrumentation and debug facilities. Some
functions are partially instrumentable, which is handled by marking them
noinstr and using noinstr void entry(void) { handle_entry(); // <-- must be 'noinstr' or '__always_inline' ... instrumentation_begin(); handle_context(); // <-- instrumentable code instrumentation_end(); ... handle_exit(); // <-- must be 'noinstr' or '__always_inline' } This allows verification of the ‘noinstr’ restrictions via objtool on supported architectures. Invoking non-instrumentable functions from instrumentable context has no restrictions and is useful to protect e.g. state switching which would cause malfunction if instrumented. All non-instrumentable entry/exit code sections before and after the RCU state transitions must run with interrupts disabled. Syscalls¶Syscall-entry code starts in assembly code and calls out into low-level C code after establishing low-level architecture-specific state and stack frames. This low-level C code must not be instrumented. The recommended syscall handling function invoked from low-level assembly code looks like this: noinstr void syscall(struct pt_regs *regs, long nr) { arch_syscall_enter(regs); result_reg(regs) = -ENOSYS; if (syscall_enter_from_user_mode_randomize_stack(regs, &nr)) { instrumentation_begin(); if (valid(nr) result_reg(regs) = invoke_syscall(regs, nr); instrumentation_end(); } syscall_exit_to_user_mode(regs); } This is the most resilent variant as it has always a guaranteed valid return code. The alternative variant is: noinstr void syscall(struct pt_regs *regs, long nr) { arch_syscall_enter(regs); if (syscall_enter_from_user_mode_randomize_stack(regs, &nr)) { instrumentation_begin(); if (valid(nr) result_reg(regs) = invoke_syscall(regs, nr); else result_reg(regs) = -ENOSYS; instrumentation_end(); } syscall_exit_to_user_mode(regs); } That works for most situations except when a probe/BPF attached to the syscall tracepoint sets an invalid syscall number e.g. -1 and also modifies the result register. So this variant will obviously overwrite the modified result with -ENOSYS.
and then invokes the various entry work functions like ptrace, seccomp, audit,
syscall tracing, etc. After all that is done, the instrumentable invoke_syscall
function can be invoked. The instrumentable code section then ends, after which
Do not nest syscalls. Nested syscalls will cause RCU and/or context tracking to print a warning. KVM¶Entering or exiting guest mode is very similar to syscalls. From the host kernel point of view the CPU goes off into user space when entering the guest and returns to the kernel on exit.
Task work handling is done separately for guest at the boundary of the
Do not nest KVM entry/exit transitions because doing so is nonsensical. Interrupts and regular exceptions¶Interrupts entry and exit handling is slightly more complex than syscalls and KVM transitions. If an interrupt is raised while the CPU executes in user space, the entry and exit handling is exactly the same as for syscalls. If the interrupt is raised while the CPU executes in kernel space the entry and exit handling is slightly different. RCU state is only updated when the interrupt is raised in the context of the CPU’s idle task. Otherwise, RCU will already be watching. Lockdep and tracing have to be updated unconditionally.
The architecture-specific part looks similar to syscall handling: noinstr void interrupt(struct pt_regs *regs, int nr) { arch_interrupt_enter(regs); state = irqentry_enter(regs); instrumentation_begin(); irq_enter_rcu(); invoke_irq_handler(regs, nr); irq_exit_rcu(); instrumentation_end(); irqentry_exit(regs, state); } Note that the invocation of the actual interrupt handler is within a
In theory, the preemption count could be updated in Note that soft interrupt handlers must run in BH context rather than in hard
interrupt context. Even though interrupt handlers are expected to run with local interrupts disabled, interrupt nesting is common from an entry/exit perspective. For example, softirq handling happens within an irqentry_{enter,exit}() block with local interrupts enabled. Also, although uncommon, nothing prevents an interrupt handler from re-enabling interrupts. Interrupt entry/exit code doesn’t strictly need to handle reentrancy, since it runs with local interrupts disabled. But NMIs can happen anytime, and a lot of the entry code is shared between the two. NMI and NMI-like exceptions¶NMIs and NMI-like exceptions (machine checks, double faults, debug interrupts, etc.) can hit any context and must be extra careful with the state. State changes for debug exceptions and machine-check exceptions depend on whether these exceptions happened in user-space (breakpoints or watchpoints) or in kernel mode (code patching). From user-space, they are treated like interrupts, while from kernel mode they are treated like NMIs. NMIs and other NMI-like exceptions handle state transitions without distinguishing between user-mode and kernel-mode origin. The state update on entry is handled in
The exit counterpart Note that the update of the preemption counter has to be the first
operation on enter and the last operation on exit. The reason is that both
lockdep and RCU rely on Architecture-specific code looks like this: noinstr void nmi(struct pt_regs *regs) { arch_nmi_enter(regs); state = irqentry_nmi_enter(regs); instrumentation_begin(); nmi_handler(regs); instrumentation_end(); irqentry_nmi_exit(regs); } and for e.g. a debug exception it can look like this: noinstr void debug(struct pt_regs *regs) { arch_nmi_enter(regs); debug_regs = save_debug_regs(); if (user_mode(regs)) { state = irqentry_enter(regs); instrumentation_begin(); user_mode_debug_handler(regs, debug_regs); instrumentation_end(); irqentry_exit(regs, state); } else { state = irqentry_nmi_enter(regs); instrumentation_begin(); kernel_mode_debug_handler(regs, debug_regs); instrumentation_end(); irqentry_nmi_exit(regs, state); } } There is no combined NMIs can happen in any context. For example, an NMI-like exception triggered while handling an NMI. So NMI entry code has to be reentrant and state updates need to handle nesting. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |