diff options
Diffstat (limited to 'Documentation/cli-sti-removal.txt')
-rw-r--r-- | Documentation/cli-sti-removal.txt | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/Documentation/cli-sti-removal.txt b/Documentation/cli-sti-removal.txt new file mode 100644 index 000000000000..0223c9d20331 --- /dev/null +++ b/Documentation/cli-sti-removal.txt | |||
@@ -0,0 +1,133 @@ | |||
1 | |||
2 | #### cli()/sti() removal guide, started by Ingo Molnar <mingo@redhat.com> | ||
3 | |||
4 | |||
5 | as of 2.5.28, five popular macros have been removed on SMP, and | ||
6 | are being phased out on UP: | ||
7 | |||
8 | cli(), sti(), save_flags(flags), save_flags_cli(flags), restore_flags(flags) | ||
9 | |||
10 | until now it was possible to protect driver code against interrupt | ||
11 | handlers via a cli(), but from now on other, more lightweight methods | ||
12 | have to be used for synchronization, such as spinlocks or semaphores. | ||
13 | |||
14 | for example, driver code that used to do something like: | ||
15 | |||
16 | struct driver_data; | ||
17 | |||
18 | irq_handler (...) | ||
19 | { | ||
20 | .... | ||
21 | driver_data.finish = 1; | ||
22 | driver_data.new_work = 0; | ||
23 | .... | ||
24 | } | ||
25 | |||
26 | ... | ||
27 | |||
28 | ioctl_func (...) | ||
29 | { | ||
30 | ... | ||
31 | cli(); | ||
32 | ... | ||
33 | driver_data.finish = 0; | ||
34 | driver_data.new_work = 2; | ||
35 | ... | ||
36 | sti(); | ||
37 | ... | ||
38 | } | ||
39 | |||
40 | was SMP-correct because the cli() function ensured that no | ||
41 | interrupt handler (amongst them the above irq_handler()) function | ||
42 | would execute while the cli()-ed section is executing. | ||
43 | |||
44 | but from now on a more direct method of locking has to be used: | ||
45 | |||
46 | spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; | ||
47 | struct driver_data; | ||
48 | |||
49 | irq_handler (...) | ||
50 | { | ||
51 | unsigned long flags; | ||
52 | .... | ||
53 | spin_lock_irqsave(&driver_lock, flags); | ||
54 | .... | ||
55 | driver_data.finish = 1; | ||
56 | driver_data.new_work = 0; | ||
57 | .... | ||
58 | spin_unlock_irqrestore(&driver_lock, flags); | ||
59 | .... | ||
60 | } | ||
61 | |||
62 | ... | ||
63 | |||
64 | ioctl_func (...) | ||
65 | { | ||
66 | ... | ||
67 | spin_lock_irq(&driver_lock); | ||
68 | ... | ||
69 | driver_data.finish = 0; | ||
70 | driver_data.new_work = 2; | ||
71 | ... | ||
72 | spin_unlock_irq(&driver_lock); | ||
73 | ... | ||
74 | } | ||
75 | |||
76 | the above code has a number of advantages: | ||
77 | |||
78 | - the locking relation is easier to understand - actual lock usage | ||
79 | pinpoints the critical sections. cli() usage is too opaque. | ||
80 | Easier to understand means it's easier to debug. | ||
81 | |||
82 | - it's faster, because spinlocks are faster to acquire than the | ||
83 | potentially heavily-used IRQ lock. Furthermore, your driver does | ||
84 | not have to wait eg. for a big heavy SCSI interrupt to finish, | ||
85 | because the driver_lock spinlock is only used by your driver. | ||
86 | cli() on the other hand was used by many drivers, and extended | ||
87 | the critical section to the whole IRQ handler function - creating | ||
88 | serious lock contention. | ||
89 | |||
90 | |||
91 | to make the transition easier, we've still kept the cli(), sti(), | ||
92 | save_flags(), save_flags_cli() and restore_flags() macros defined | ||
93 | on UP systems - but their usage will be phased out until 2.6 is | ||
94 | released. | ||
95 | |||
96 | drivers that want to disable local interrupts (interrupts on the | ||
97 | current CPU), can use the following five macros: | ||
98 | |||
99 | local_irq_disable(), local_irq_enable(), local_save_flags(flags), | ||
100 | local_irq_save(flags), local_irq_restore(flags) | ||
101 | |||
102 | but beware, their meaning and semantics are much simpler, far from | ||
103 | that of the old cli(), sti(), save_flags(flags) and restore_flags(flags) | ||
104 | SMP meaning: | ||
105 | |||
106 | local_irq_disable() => turn local IRQs off | ||
107 | |||
108 | local_irq_enable() => turn local IRQs on | ||
109 | |||
110 | local_save_flags(flags) => save the current IRQ state into flags. The | ||
111 | state can be on or off. (on some | ||
112 | architectures there's even more bits in it.) | ||
113 | |||
114 | local_irq_save(flags) => save the current IRQ state into flags and | ||
115 | disable interrupts. | ||
116 | |||
117 | local_irq_restore(flags) => restore the IRQ state from flags. | ||
118 | |||
119 | (local_irq_save can save both irqs on and irqs off state, and | ||
120 | local_irq_restore can restore into both irqs on and irqs off state.) | ||
121 | |||
122 | another related change is that synchronize_irq() now takes a parameter: | ||
123 | synchronize_irq(irq). This change too has the purpose of making SMP | ||
124 | synchronization more lightweight - this way you can wait for your own | ||
125 | interrupt handler to finish, no need to wait for other IRQ sources. | ||
126 | |||
127 | |||
128 | why were these changes done? The main reason was the architectural burden | ||
129 | of maintaining the cli()/sti() interface - it became a real problem. The | ||
130 | new interrupt system is much more streamlined, easier to understand, debug, | ||
131 | and it's also a bit faster - the same happened to it that will happen to | ||
132 | cli()/sti() using drivers once they convert to spinlocks :-) | ||
133 | |||