Blame view

Documentation/RCU/NMI-RCU.txt 4.16 KB
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
1
2
3
4
5
6
7
  Using RCU to Protect Dynamic NMI Handlers
  
  
  Although RCU is usually used to protect read-mostly data structures,
  it is possible to use RCU to provide dynamic non-maskable interrupt
  handlers, as well as dynamic irq handlers.  This document describes
  how to do this, drawing loosely from Zwane Mwaikambo's NMI-timer
25eb650a6   Wanlong Gao   doc: fix wrong ar...
8
9
  work in "arch/x86/oprofile/nmi_timer_int.c" and in
  "arch/x86/kernel/traps.c".
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  
  The relevant pieces of code are listed below, each followed by a
  brief explanation.
  
  	static int dummy_nmi_callback(struct pt_regs *regs, int cpu)
  	{
  		return 0;
  	}
  
  The dummy_nmi_callback() function is a "dummy" NMI handler that does
  nothing, but returns zero, thus saying that it did nothing, allowing
  the NMI handler to take the default machine-specific action.
  
  	static nmi_callback_t nmi_callback = dummy_nmi_callback;
  
  This nmi_callback variable is a global function pointer to the current
  NMI handler.
b5606c2d4   Harvey Harrison   remove final fast...
27
  	void do_nmi(struct pt_regs * regs, long error_code)
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
28
29
30
31
32
33
34
  	{
  		int cpu;
  
  		nmi_enter();
  
  		cpu = smp_processor_id();
  		++nmi_count(cpu);
50aec0024   Paul E. McKenney   rcu: Update docs ...
35
  		if (!rcu_dereference_sched(nmi_callback)(regs, cpu))
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
36
37
38
39
40
41
42
43
44
45
46
  			default_do_nmi(regs);
  
  		nmi_exit();
  	}
  
  The do_nmi() function processes each NMI.  It first disables preemption
  in the same way that a hardware irq would, then increments the per-CPU
  count of NMIs.  It then invokes the NMI handler stored in the nmi_callback
  function pointer.  If this handler returns zero, do_nmi() invokes the
  default_do_nmi() function to handle a machine-specific NMI.  Finally,
  preemption is restored.
50aec0024   Paul E. McKenney   rcu: Update docs ...
47
48
49
50
51
  In theory, rcu_dereference_sched() is not needed, since this code runs
  only on i386, which in theory does not need rcu_dereference_sched()
  anyway.  However, in practice it is a good documentation aid, particularly
  for anyone attempting to do something similar on Alpha or on systems
  with aggressive optimizing compilers.
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
52

50aec0024   Paul E. McKenney   rcu: Update docs ...
53
  Quick Quiz:  Why might the rcu_dereference_sched() be necessary on Alpha,
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  	     given that the code referenced by the pointer is read-only?
  
  
  Back to the discussion of NMI and RCU...
  
  	void set_nmi_callback(nmi_callback_t callback)
  	{
  		rcu_assign_pointer(nmi_callback, callback);
  	}
  
  The set_nmi_callback() function registers an NMI handler.  Note that any
  data that is to be used by the callback must be initialized up -before-
  the call to set_nmi_callback().  On architectures that do not order
  writes, the rcu_assign_pointer() ensures that the NMI handler sees the
  initialized values.
  
  	void unset_nmi_callback(void)
  	{
  		rcu_assign_pointer(nmi_callback, dummy_nmi_callback);
  	}
  
  This function unregisters an NMI handler, restoring the original
  dummy_nmi_handler().  However, there may well be an NMI handler
  currently executing on some other CPU.  We therefore cannot free
  up any data structures used by the old NMI handler until execution
  of it completes on all other CPUs.
4fea6ef0b   Paul E. McKenney   doc: Remove obsol...
80
  One way to accomplish this is via synchronize_rcu(), perhaps as
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
81
82
83
  follows:
  
  	unset_nmi_callback();
4fea6ef0b   Paul E. McKenney   doc: Remove obsol...
84
  	synchronize_rcu();
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
85
  	kfree(my_nmi_data);
4fea6ef0b   Paul E. McKenney   doc: Remove obsol...
86
87
88
89
  This works because (as of v4.20) synchronize_rcu() blocks until all
  CPUs complete any preemption-disabled segments of code that they were
  executing.
  Since NMI handlers disable preemption, synchronize_rcu() is guaranteed
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
90
  not to return until all ongoing NMI handlers exit.  It is therefore safe
4fea6ef0b   Paul E. McKenney   doc: Remove obsol...
91
  to free up the handler's data as soon as synchronize_rcu() returns.
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
92

32300751b   Paul E. McKenney   sched: 1Q08 RCU d...
93
  Important note: for this to work, the architecture in question must
b15a2e7d1   Paul E. McKenney   rcu: Fix RCU's NM...
94
  invoke nmi_enter() and nmi_exit() on NMI entry and exit, respectively.
32300751b   Paul E. McKenney   sched: 1Q08 RCU d...
95

19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
96
97
  
  Answer to Quick Quiz
50aec0024   Paul E. McKenney   rcu: Update docs ...
98
  	Why might the rcu_dereference_sched() be necessary on Alpha, given
19306059c   Paul E. McKenney   [PATCH] NMI: Upda...
99
100
101
  	that the code referenced by the pointer is read-only?
  
  	Answer: The caller to set_nmi_callback() might well have
50aec0024   Paul E. McKenney   rcu: Update docs ...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  		initialized some data that is to be used by the new NMI
  		handler.  In this case, the rcu_dereference_sched() would
  		be needed, because otherwise a CPU that received an NMI
  		just after the new handler was set might see the pointer
  		to the new NMI handler, but the old pre-initialized
  		version of the handler's data.
  
  		This same sad story can happen on other CPUs when using
  		a compiler with aggressive pointer-value speculation
  		optimizations.
  
  		More important, the rcu_dereference_sched() makes it
  		clear to someone reading the code that the pointer is
  		being protected by RCU-sched.