linux/Documentation/RCU/NMI-RCU.rst
<<
>>
Prefs
   1.. _NMI_rcu_doc:
   2
   3Using RCU to Protect Dynamic NMI Handlers
   4=========================================
   5
   6
   7Although RCU is usually used to protect read-mostly data structures,
   8it is possible to use RCU to provide dynamic non-maskable interrupt
   9handlers, as well as dynamic irq handlers.  This document describes
  10how to do this, drawing loosely from Zwane Mwaikambo's NMI-timer
  11work in "arch/x86/kernel/traps.c".
  12
  13The relevant pieces of code are listed below, each followed by a
  14brief explanation::
  15
  16        static int dummy_nmi_callback(struct pt_regs *regs, int cpu)
  17        {
  18                return 0;
  19        }
  20
  21The dummy_nmi_callback() function is a "dummy" NMI handler that does
  22nothing, but returns zero, thus saying that it did nothing, allowing
  23the NMI handler to take the default machine-specific action::
  24
  25        static nmi_callback_t nmi_callback = dummy_nmi_callback;
  26
  27This nmi_callback variable is a global function pointer to the current
  28NMI handler::
  29
  30        void do_nmi(struct pt_regs * regs, long error_code)
  31        {
  32                int cpu;
  33
  34                nmi_enter();
  35
  36                cpu = smp_processor_id();
  37                ++nmi_count(cpu);
  38
  39                if (!rcu_dereference_sched(nmi_callback)(regs, cpu))
  40                        default_do_nmi(regs);
  41
  42                nmi_exit();
  43        }
  44
  45The do_nmi() function processes each NMI.  It first disables preemption
  46in the same way that a hardware irq would, then increments the per-CPU
  47count of NMIs.  It then invokes the NMI handler stored in the nmi_callback
  48function pointer.  If this handler returns zero, do_nmi() invokes the
  49default_do_nmi() function to handle a machine-specific NMI.  Finally,
  50preemption is restored.
  51
  52In theory, rcu_dereference_sched() is not needed, since this code runs
  53only on i386, which in theory does not need rcu_dereference_sched()
  54anyway.  However, in practice it is a good documentation aid, particularly
  55for anyone attempting to do something similar on Alpha or on systems
  56with aggressive optimizing compilers.
  57
  58Quick Quiz:
  59                Why might the rcu_dereference_sched() be necessary on Alpha, given that the code referenced by the pointer is read-only?
  60
  61:ref:`Answer to Quick Quiz <answer_quick_quiz_NMI>`
  62
  63Back to the discussion of NMI and RCU::
  64
  65        void set_nmi_callback(nmi_callback_t callback)
  66        {
  67                rcu_assign_pointer(nmi_callback, callback);
  68        }
  69
  70The set_nmi_callback() function registers an NMI handler.  Note that any
  71data that is to be used by the callback must be initialized up -before-
  72the call to set_nmi_callback().  On architectures that do not order
  73writes, the rcu_assign_pointer() ensures that the NMI handler sees the
  74initialized values::
  75
  76        void unset_nmi_callback(void)
  77        {
  78                rcu_assign_pointer(nmi_callback, dummy_nmi_callback);
  79        }
  80
  81This function unregisters an NMI handler, restoring the original
  82dummy_nmi_handler().  However, there may well be an NMI handler
  83currently executing on some other CPU.  We therefore cannot free
  84up any data structures used by the old NMI handler until execution
  85of it completes on all other CPUs.
  86
  87One way to accomplish this is via synchronize_rcu(), perhaps as
  88follows::
  89
  90        unset_nmi_callback();
  91        synchronize_rcu();
  92        kfree(my_nmi_data);
  93
  94This works because (as of v4.20) synchronize_rcu() blocks until all
  95CPUs complete any preemption-disabled segments of code that they were
  96executing.
  97Since NMI handlers disable preemption, synchronize_rcu() is guaranteed
  98not to return until all ongoing NMI handlers exit.  It is therefore safe
  99to free up the handler's data as soon as synchronize_rcu() returns.
 100
 101Important note: for this to work, the architecture in question must
 102invoke nmi_enter() and nmi_exit() on NMI entry and exit, respectively.
 103
 104.. _answer_quick_quiz_NMI:
 105
 106Answer to Quick Quiz:
 107        Why might the rcu_dereference_sched() be necessary on Alpha, given that the code referenced by the pointer is read-only?
 108
 109        The caller to set_nmi_callback() might well have
 110        initialized some data that is to be used by the new NMI
 111        handler.  In this case, the rcu_dereference_sched() would
 112        be needed, because otherwise a CPU that received an NMI
 113        just after the new handler was set might see the pointer
 114        to the new NMI handler, but the old pre-initialized
 115        version of the handler's data.
 116
 117        This same sad story can happen on other CPUs when using
 118        a compiler with aggressive pointer-value speculation
 119        optimizations.
 120
 121        More important, the rcu_dereference_sched() makes it
 122        clear to someone reading the code that the pointer is
 123        being protected by RCU-sched.
 124