OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [Documentation/] [RCU/] [NMI-RCU.txt] - Blame information for rev 3

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.