Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

locking: Optimize lock_bh functions

Currently all _bh_ lock functions do two preempt_count operations:

local_bh_disable();
preempt_disable();

and for the unlock:

preempt_enable_no_resched();
local_bh_enable();

Since its a waste of perfectly good cycles to modify the same variable
twice when you can do it in one go; use the new
__local_bh_{dis,en}able_ip() functions that allow us to provide a
preempt_count value to add/sub.

So define SOFTIRQ_LOCK_OFFSET as the offset a _bh_ lock needs to
add/sub to be done in one go.

As a bonus it gets rid of the preempt_enable_no_resched() usage.

This reduces a 1000 loops of:

spin_lock_bh(&bh_lock);
spin_unlock_bh(&bh_lock);

from 53596 cycles to 51995 cycles. I didn't do enough measurements to
say for absolute sure that the result is significant but the the few
runs I did for each suggest it is so.

Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Cc: jacob.jun.pan@linux.intel.com
Cc: Mike Galbraith <bitbucket@online.de>
Cc: hpa@zytor.com
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: lenb@kernel.org
Cc: rjw@rjwysocki.net
Cc: rui.zhang@intel.com
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Link: http://lkml.kernel.org/r/20131119151338.GF3694@twins.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Peter Zijlstra and committed by
Ingo Molnar
9ea4c380 c726099e

+36 -23
+15
include/linux/preempt_mask.h
··· 79 79 #endif 80 80 81 81 /* 82 + * The preempt_count offset needed for things like: 83 + * 84 + * spin_lock_bh() 85 + * 86 + * Which need to disable both preemption (CONFIG_PREEMPT_COUNT) and 87 + * softirqs, such that unlock sequences of: 88 + * 89 + * spin_unlock(); 90 + * local_bh_enable(); 91 + * 92 + * Work as expected. 93 + */ 94 + #define SOFTIRQ_LOCK_OFFSET (SOFTIRQ_DISABLE_OFFSET + PREEMPT_CHECK_OFFSET) 95 + 96 + /* 82 97 * Are we running in atomic context? WARNING: this macro cannot 83 98 * always detect atomic context; in particular, it cannot know about 84 99 * held spinlocks in non-preemptible kernels. Thus it should not be
+4 -8
include/linux/rwlock_api_smp.h
··· 172 172 173 173 static inline void __raw_read_lock_bh(rwlock_t *lock) 174 174 { 175 - local_bh_disable(); 176 - preempt_disable(); 175 + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 177 176 rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_); 178 177 LOCK_CONTENDED(lock, do_raw_read_trylock, do_raw_read_lock); 179 178 } ··· 199 200 200 201 static inline void __raw_write_lock_bh(rwlock_t *lock) 201 202 { 202 - local_bh_disable(); 203 - preempt_disable(); 203 + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 204 204 rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_); 205 205 LOCK_CONTENDED(lock, do_raw_write_trylock, do_raw_write_lock); 206 206 } ··· 248 250 { 249 251 rwlock_release(&lock->dep_map, 1, _RET_IP_); 250 252 do_raw_read_unlock(lock); 251 - preempt_enable_no_resched(); 252 - local_bh_enable_ip((unsigned long)__builtin_return_address(0)); 253 + __local_bh_enable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 253 254 } 254 255 255 256 static inline void __raw_write_unlock_irqrestore(rwlock_t *lock, ··· 272 275 { 273 276 rwlock_release(&lock->dep_map, 1, _RET_IP_); 274 277 do_raw_write_unlock(lock); 275 - preempt_enable_no_resched(); 276 - local_bh_enable_ip((unsigned long)__builtin_return_address(0)); 278 + __local_bh_enable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 277 279 } 278 280 279 281 #endif /* __LINUX_RWLOCK_API_SMP_H */
+4 -8
include/linux/spinlock_api_smp.h
··· 131 131 132 132 static inline void __raw_spin_lock_bh(raw_spinlock_t *lock) 133 133 { 134 - local_bh_disable(); 135 - preempt_disable(); 134 + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 136 135 spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); 137 136 LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); 138 137 } ··· 173 174 { 174 175 spin_release(&lock->dep_map, 1, _RET_IP_); 175 176 do_raw_spin_unlock(lock); 176 - preempt_enable_no_resched(); 177 - local_bh_enable_ip((unsigned long)__builtin_return_address(0)); 177 + __local_bh_enable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 178 178 } 179 179 180 180 static inline int __raw_spin_trylock_bh(raw_spinlock_t *lock) 181 181 { 182 - local_bh_disable(); 183 - preempt_disable(); 182 + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 184 183 if (do_raw_spin_trylock(lock)) { 185 184 spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); 186 185 return 1; 187 186 } 188 - preempt_enable_no_resched(); 189 - local_bh_enable_ip((unsigned long)__builtin_return_address(0)); 187 + __local_bh_enable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); 190 188 return 0; 191 189 } 192 190
+11 -5
include/linux/spinlock_api_up.h
··· 24 24 * flags straight, to suppress compiler warnings of unused lock 25 25 * variables, and to add the proper checker annotations: 26 26 */ 27 + #define ___LOCK(lock) \ 28 + do { __acquire(lock); (void)(lock); } while (0) 29 + 27 30 #define __LOCK(lock) \ 28 - do { preempt_disable(); __acquire(lock); (void)(lock); } while (0) 31 + do { preempt_disable(); ___LOCK(lock); } while (0) 29 32 30 33 #define __LOCK_BH(lock) \ 31 - do { local_bh_disable(); __LOCK(lock); } while (0) 34 + do { __local_bh_disable_ip(_THIS_IP_, SOFTIRQ_LOCK_OFFSET); ___LOCK(lock); } while (0) 32 35 33 36 #define __LOCK_IRQ(lock) \ 34 37 do { local_irq_disable(); __LOCK(lock); } while (0) ··· 39 36 #define __LOCK_IRQSAVE(lock, flags) \ 40 37 do { local_irq_save(flags); __LOCK(lock); } while (0) 41 38 39 + #define ___UNLOCK(lock) \ 40 + do { __release(lock); (void)(lock); } while (0) 41 + 42 42 #define __UNLOCK(lock) \ 43 - do { preempt_enable(); __release(lock); (void)(lock); } while (0) 43 + do { preempt_enable(); ___UNLOCK(lock); } while (0) 44 44 45 45 #define __UNLOCK_BH(lock) \ 46 - do { preempt_enable_no_resched(); local_bh_enable(); \ 47 - __release(lock); (void)(lock); } while (0) 46 + do { __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_LOCK_OFFSET); \ 47 + ___UNLOCK(lock); } while (0) 48 48 49 49 #define __UNLOCK_IRQ(lock) \ 50 50 do { local_irq_enable(); __UNLOCK(lock); } while (0)
+2 -2
kernel/softirq.c
··· 107 107 /* 108 108 * Were softirqs turned off above: 109 109 */ 110 - if (softirq_count() == cnt) 110 + if (softirq_count() == (cnt & SOFTIRQ_MASK)) 111 111 trace_softirqs_off(ip); 112 112 raw_local_irq_restore(flags); 113 113 ··· 133 133 { 134 134 WARN_ON_ONCE(!irqs_disabled()); 135 135 136 - if (softirq_count() == cnt) 136 + if (softirq_count() == (cnt & SOFTIRQ_MASK)) 137 137 trace_softirqs_on(_RET_IP_); 138 138 preempt_count_sub(cnt); 139 139 }