Merge branch 'locking/nmi' into x86/entry
authorIngo Molnar <mingo@kernel.org>
Sat, 25 Jul 2020 08:14:55 +0000 (10:14 +0200)
committerIngo Molnar <mingo@kernel.org>
Sun, 26 Jul 2020 16:26:25 +0000 (18:26 +0200)
Resolve conflicts with ongoing lockdep work that fixed the NMI entry code.

Conflicts:
arch/x86/entry/common.c
arch/x86/include/asm/idtentry.h

Signed-off-by: Ingo Molnar <mingo@kernel.org>
1  2 
arch/x86/entry/common.c
arch/x86/include/asm/idtentry.h
arch/x86/kernel/traps.c

@@@ -198,6 -558,238 +198,40 @@@ SYSCALL_DEFINE0(ni_syscall
        return -ENOSYS;
  }
  
 -/**
 - * idtentry_enter - Handle state tracking on ordinary idtentries
 - * @regs:     Pointer to pt_regs of interrupted context
 - *
 - * Invokes:
 - *  - lockdep irqflag state tracking as low level ASM entry disabled
 - *    interrupts.
 - *
 - *  - Context tracking if the exception hit user mode.
 - *
 - *  - The hardirq tracer to keep the state consistent as low level ASM
 - *    entry disabled interrupts.
 - *
 - * As a precondition, this requires that the entry came from user mode,
 - * idle, or a kernel context in which RCU is watching.
 - *
 - * For kernel mode entries RCU handling is done conditional. If RCU is
 - * watching then the only RCU requirement is to check whether the tick has
 - * to be restarted. If RCU is not watching then rcu_irq_enter() has to be
 - * invoked on entry and rcu_irq_exit() on exit.
 - *
 - * Avoiding the rcu_irq_enter/exit() calls is an optimization but also
 - * solves the problem of kernel mode pagefaults which can schedule, which
 - * is not possible after invoking rcu_irq_enter() without undoing it.
 - *
 - * For user mode entries enter_from_user_mode() must be invoked to
 - * establish the proper context for NOHZ_FULL. Otherwise scheduling on exit
 - * would not be possible.
 - *
 - * Returns: An opaque object that must be passed to idtentry_exit()
 - *
 - * The return value must be fed into the state argument of
 - * idtentry_exit().
 - */
 -noinstr idtentry_state_t idtentry_enter(struct pt_regs *regs)
 -{
 -      idtentry_state_t ret = {
 -              .exit_rcu = false,
 -      };
 -
 -      if (user_mode(regs)) {
 -              check_user_regs(regs);
 -              enter_from_user_mode();
 -              return ret;
 -      }
 -
 -      /*
 -       * If this entry hit the idle task invoke rcu_irq_enter() whether
 -       * RCU is watching or not.
 -       *
 -       * Interupts can nest when the first interrupt invokes softirq
 -       * processing on return which enables interrupts.
 -       *
 -       * Scheduler ticks in the idle task can mark quiescent state and
 -       * terminate a grace period, if and only if the timer interrupt is
 -       * not nested into another interrupt.
 -       *
 -       * Checking for __rcu_is_watching() here would prevent the nesting
 -       * interrupt to invoke rcu_irq_enter(). If that nested interrupt is
 -       * the tick then rcu_flavor_sched_clock_irq() would wrongfully
 -       * assume that it is the first interupt and eventually claim
 -       * quiescient state and end grace periods prematurely.
 -       *
 -       * Unconditionally invoke rcu_irq_enter() so RCU state stays
 -       * consistent.
 -       *
 -       * TINY_RCU does not support EQS, so let the compiler eliminate
 -       * this part when enabled.
 -       */
 -      if (!IS_ENABLED(CONFIG_TINY_RCU) && is_idle_task(current)) {
 -              /*
 -               * If RCU is not watching then the same careful
 -               * sequence vs. lockdep and tracing is required
 -               * as in enter_from_user_mode().
 -               */
 -              lockdep_hardirqs_off(CALLER_ADDR0);
 -              rcu_irq_enter();
 -              instrumentation_begin();
 -              trace_hardirqs_off_finish();
 -              instrumentation_end();
 -
 -              ret.exit_rcu = true;
 -              return ret;
 -      }
 -
 -      /*
 -       * If RCU is watching then RCU only wants to check whether it needs
 -       * to restart the tick in NOHZ mode. rcu_irq_enter_check_tick()
 -       * already contains a warning when RCU is not watching, so no point
 -       * in having another one here.
 -       */
 -      instrumentation_begin();
 -      rcu_irq_enter_check_tick();
 -      /* Use the combo lockdep/tracing function */
 -      trace_hardirqs_off();
 -      instrumentation_end();
 -
 -      return ret;
 -}
 -
 -static void idtentry_exit_cond_resched(struct pt_regs *regs, bool may_sched)
 -{
 -      if (may_sched && !preempt_count()) {
 -              /* Sanity check RCU and thread stack */
 -              rcu_irq_exit_check_preempt();
 -              if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
 -                      WARN_ON_ONCE(!on_thread_stack());
 -              if (need_resched())
 -                      preempt_schedule_irq();
 -      }
 -      /* Covers both tracing and lockdep */
 -      trace_hardirqs_on();
 -}
 -
 -/**
 - * idtentry_exit - Handle return from exception that used idtentry_enter()
 - * @regs:     Pointer to pt_regs (exception entry regs)
 - * @state:    Return value from matching call to idtentry_enter()
 - *
 - * Depending on the return target (kernel/user) this runs the necessary
 - * preemption and work checks if possible and reguired and returns to
 - * the caller with interrupts disabled and no further work pending.
 - *
 - * This is the last action before returning to the low level ASM code which
 - * just needs to return to the appropriate context.
 - *
 - * Counterpart to idtentry_enter(). The return value of the entry
 - * function must be fed into the @state argument.
 - */
 -noinstr void idtentry_exit(struct pt_regs *regs, idtentry_state_t state)
 -{
 -      lockdep_assert_irqs_disabled();
 -
 -      /* Check whether this returns to user mode */
 -      if (user_mode(regs)) {
 -              prepare_exit_to_usermode(regs);
 -      } else if (regs->flags & X86_EFLAGS_IF) {
 -              /*
 -               * If RCU was not watching on entry this needs to be done
 -               * carefully and needs the same ordering of lockdep/tracing
 -               * and RCU as the return to user mode path.
 -               */
 -              if (state.exit_rcu) {
 -                      instrumentation_begin();
 -                      /* Tell the tracer that IRET will enable interrupts */
 -                      trace_hardirqs_on_prepare();
 -                      lockdep_hardirqs_on_prepare(CALLER_ADDR0);
 -                      instrumentation_end();
 -                      rcu_irq_exit();
 -                      lockdep_hardirqs_on(CALLER_ADDR0);
 -                      return;
 -              }
 -
 -              instrumentation_begin();
 -              idtentry_exit_cond_resched(regs, IS_ENABLED(CONFIG_PREEMPTION));
 -              instrumentation_end();
 -      } else {
 -              /*
 -               * IRQ flags state is correct already. Just tell RCU if it
 -               * was not watching on entry.
 -               */
 -              if (state.exit_rcu)
 -                      rcu_irq_exit();
 -      }
 -}
 -
 -/**
 - * idtentry_enter_user - Handle state tracking on idtentry from user mode
 - * @regs:     Pointer to pt_regs of interrupted context
 - *
 - * Invokes enter_from_user_mode() to establish the proper context for
 - * NOHZ_FULL. Otherwise scheduling on exit would not be possible.
 - */
 -noinstr void idtentry_enter_user(struct pt_regs *regs)
 -{
 -      check_user_regs(regs);
 -      enter_from_user_mode();
 -}
 -
 -/**
 - * idtentry_exit_user - Handle return from exception to user mode
 - * @regs:     Pointer to pt_regs (exception entry regs)
 - *
 - * Runs the necessary preemption and work checks and returns to the caller
 - * with interrupts disabled and no further work pending.
 - *
 - * This is the last action before returning to the low level ASM code which
 - * just needs to return to the appropriate context.
 - *
 - * Counterpart to idtentry_enter_user().
 - */
 -noinstr void idtentry_exit_user(struct pt_regs *regs)
 -{
 -      lockdep_assert_irqs_disabled();
 -
 -      prepare_exit_to_usermode(regs);
 -}
 -
+ noinstr bool idtentry_enter_nmi(struct pt_regs *regs)
+ {
 -      bool irq_state = lockdep_hardirqs_enabled(current);
++      bool irq_state = lockdep_hardirqs_enabled();
+       __nmi_enter();
+       lockdep_hardirqs_off(CALLER_ADDR0);
+       lockdep_hardirq_enter();
+       rcu_nmi_enter();
+       instrumentation_begin();
+       trace_hardirqs_off_finish();
+       ftrace_nmi_enter();
+       instrumentation_end();
+       return irq_state;
+ }
+ noinstr void idtentry_exit_nmi(struct pt_regs *regs, bool restore)
+ {
+       instrumentation_begin();
+       ftrace_nmi_exit();
+       if (restore) {
+               trace_hardirqs_on_prepare();
+               lockdep_hardirqs_on_prepare(CALLER_ADDR0);
+       }
+       instrumentation_end();
+       rcu_nmi_exit();
+       lockdep_hardirq_exit();
+       if (restore)
+               lockdep_hardirqs_on(CALLER_ADDR0);
+       __nmi_exit();
+ }
  #ifdef CONFIG_XEN_PV
  #ifndef CONFIG_PREEMPTION
  /*
  
  #include <asm/irq_stack.h>
  
 -void idtentry_enter_user(struct pt_regs *regs);
 -void idtentry_exit_user(struct pt_regs *regs);
 -
 -typedef struct idtentry_state {
 -      bool exit_rcu;
 -} idtentry_state_t;
 -
 -idtentry_state_t idtentry_enter(struct pt_regs *regs);
 -void idtentry_exit(struct pt_regs *regs, idtentry_state_t state);
 -
+ bool idtentry_enter_nmi(struct pt_regs *regs);
+ void idtentry_exit_nmi(struct pt_regs *regs, bool irq_state);
  /**
   * DECLARE_IDTENTRY - Declare functions for simple IDT entry points
   *                  No error code pushed by hardware
@@@ -649,17 -647,14 +649,14 @@@ DEFINE_IDTENTRY_RAW(exc_int3
                instrumentation_begin();
                do_int3_user(regs);
                instrumentation_end();
 -              idtentry_exit_user(regs);
 +              irqentry_exit_to_user_mode(regs);
        } else {
-               nmi_enter();
+               bool irq_state = idtentry_enter_nmi(regs);
                instrumentation_begin();
-               trace_hardirqs_off_finish();
                if (!do_int3(regs))
                        die("int3", regs, 0);
-               if (regs->flags & X86_EFLAGS_IF)
-                       trace_hardirqs_on_prepare();
                instrumentation_end();
-               nmi_exit();
+               idtentry_exit_nmi(regs, irq_state);
        }
  }
  
@@@ -905,8 -897,9 +899,9 @@@ static __always_inline void exc_debug_u
        instrumentation_begin();
  
        handle_debug(regs, dr6, true);
        instrumentation_end();
 -      idtentry_exit_user(regs);
 +      irqentry_exit_to_user_mode(regs);
  }
  
  #ifdef CONFIG_X86_64