blob: 2a92bc685ef1ac043f33e6cb04f61fd9705dddba [file] [log] [blame]
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +05301.. _NMI_rcu_doc:
2
Paul E. McKenney19306052005-09-06 15:16:35 -07003Using RCU to Protect Dynamic NMI Handlers
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +05304=========================================
Paul E. McKenney19306052005-09-06 15:16:35 -07005
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
Viresh Kumarf8408262021-01-14 17:05:30 +053011work in "arch/x86/kernel/traps.c".
Paul E. McKenney19306052005-09-06 15:16:35 -070012
13The relevant pieces of code are listed below, each followed by a
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053014brief explanation::
Paul E. McKenney19306052005-09-06 15:16:35 -070015
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
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053023the NMI handler to take the default machine-specific action::
Paul E. McKenney19306052005-09-06 15:16:35 -070024
25 static nmi_callback_t nmi_callback = dummy_nmi_callback;
26
27This nmi_callback variable is a global function pointer to the current
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053028NMI handler::
Paul E. McKenney19306052005-09-06 15:16:35 -070029
Harvey Harrisonb5606c22008-02-13 15:03:16 -080030 void do_nmi(struct pt_regs * regs, long error_code)
Paul E. McKenney19306052005-09-06 15:16:35 -070031 {
32 int cpu;
33
34 nmi_enter();
35
36 cpu = smp_processor_id();
37 ++nmi_count(cpu);
38
Paul E. McKenney50aec002010-04-09 15:39:12 -070039 if (!rcu_dereference_sched(nmi_callback)(regs, cpu))
Paul E. McKenney19306052005-09-06 15:16:35 -070040 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
Paul E. McKenney50aec002010-04-09 15:39:12 -070052In 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.
Paul E. McKenney19306052005-09-06 15:16:35 -070057
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053058Quick Quiz:
59 Why might the rcu_dereference_sched() be necessary on Alpha, given that the code referenced by the pointer is read-only?
Paul E. McKenney19306052005-09-06 15:16:35 -070060
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053061:ref:`Answer to Quick Quiz <answer_quick_quiz_NMI>`
Paul E. McKenney19306052005-09-06 15:16:35 -070062
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053063Back to the discussion of NMI and RCU::
Paul E. McKenney19306052005-09-06 15:16:35 -070064
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
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053074initialized values::
Paul E. McKenney19306052005-09-06 15:16:35 -070075
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
Paul E. McKenney4fea6ef2019-01-09 14:48:09 -080087One way to accomplish this is via synchronize_rcu(), perhaps as
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +053088follows::
Paul E. McKenney19306052005-09-06 15:16:35 -070089
90 unset_nmi_callback();
Paul E. McKenney4fea6ef2019-01-09 14:48:09 -080091 synchronize_rcu();
Paul E. McKenney19306052005-09-06 15:16:35 -070092 kfree(my_nmi_data);
93
Paul E. McKenney4fea6ef2019-01-09 14:48:09 -080094This 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
Paul E. McKenney19306052005-09-06 15:16:35 -070098not to return until all ongoing NMI handlers exit. It is therefore safe
Paul E. McKenney4fea6ef2019-01-09 14:48:09 -080099to free up the handler's data as soon as synchronize_rcu() returns.
Paul E. McKenney19306052005-09-06 15:16:35 -0700100
Paul E. McKenney32300752008-05-12 21:21:05 +0200101Important note: for this to work, the architecture in question must
Paul E. McKenneyb15a2e72011-06-07 17:05:34 -0700102invoke nmi_enter() and nmi_exit() on NMI entry and exit, respectively.
Paul E. McKenney32300752008-05-12 21:21:05 +0200103
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +0530104.. _answer_quick_quiz_NMI:
Paul E. McKenney19306052005-09-06 15:16:35 -0700105
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +0530106Answer 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?
Paul E. McKenney19306052005-09-06 15:16:35 -0700108
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +0530109 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.
Paul E. McKenney19306052005-09-06 15:16:35 -0700116
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +0530117 This same sad story can happen on other CPUs when using
118 a compiler with aggressive pointer-value speculation
119 optimizations.
Paul E. McKenney19306052005-09-06 15:16:35 -0700120
Madhuparna Bhowmik6705cae2019-10-29 03:12:52 +0530121 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.