Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | |
Robert P. J. Day | c0d1f29 | 2008-04-21 22:44:50 +0000 | [diff] [blame] | 46 | DEFINE_SPINLOCK(driver_lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | |