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

spinlock: lockbreak cleanup

The break_lock data structure and code for spinlocks is quite nasty.
Not only does it double the size of a spinlock but it changes locking to
a potentially less optimal trylock.

Put all of that under CONFIG_GENERIC_LOCKBREAK, and introduce a
__raw_spin_is_contended that uses the lock data itself to determine whether
there are waiters on the lock, to be used if CONFIG_GENERIC_LOCKBREAK is
not set.

Rename need_lockbreak to spin_needbreak, make it use spin_is_contended to
decouple it from the spinlock implementation, and make it typesafe (rwlocks
do not have any need_lockbreak sites -- why do they even get bloated up
with that break_lock then?).

Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Nick Piggin and committed by
Ingo Molnar
95c354fe a95d67f8

+72 -37
+5
arch/arm/Kconfig
··· 91 91 bool 92 92 default y 93 93 94 + config GENERIC_LOCKBREAK 95 + bool 96 + default y 97 + depends on SMP && PREEMPT 98 + 94 99 config RWSEM_GENERIC_SPINLOCK 95 100 bool 96 101 default y
+5
arch/ia64/Kconfig
··· 42 42 config SWIOTLB 43 43 bool 44 44 45 + config GENERIC_LOCKBREAK 46 + bool 47 + default y 48 + depends on SMP && PREEMPT 49 + 45 50 config RWSEM_XCHGADD_ALGORITHM 46 51 bool 47 52 default y
+5
arch/m32r/Kconfig
··· 235 235 # Define implied options from the CPU selection here 236 236 # 237 237 238 + config GENERIC_LOCKBREAK 239 + bool 240 + default y 241 + depends on SMP && PREEMPT 242 + 238 243 config RWSEM_GENERIC_SPINLOCK 239 244 bool 240 245 depends on M32R
+5
arch/mips/Kconfig
··· 694 694 695 695 endmenu 696 696 697 + config GENERIC_LOCKBREAK 698 + bool 699 + default y 700 + depends on SMP && PREEMPT 701 + 697 702 config RWSEM_GENERIC_SPINLOCK 698 703 bool 699 704 default y
+5
arch/parisc/Kconfig
··· 19 19 config STACK_GROWSUP 20 20 def_bool y 21 21 22 + config GENERIC_LOCKBREAK 23 + bool 24 + default y 25 + depends on SMP && PREEMPT 26 + 22 27 config RWSEM_GENERIC_SPINLOCK 23 28 def_bool y 24 29
+5
arch/powerpc/Kconfig
··· 53 53 bool 54 54 default y 55 55 56 + config GENERIC_LOCKBREAK 57 + bool 58 + default y 59 + depends on SMP && PREEMPT 60 + 56 61 config ARCH_HAS_ILOG2_U32 57 62 bool 58 63 default y
+5
arch/sparc64/Kconfig
··· 200 200 If in doubt, say N. 201 201 202 202 # Global things across all Sun machines. 203 + config GENERIC_LOCKBREAK 204 + bool 205 + default y 206 + depends on SMP && PREEMPT 207 + 203 208 config RWSEM_GENERIC_SPINLOCK 204 209 bool 205 210
+4
arch/x86/Kconfig
··· 19 19 config X86 20 20 def_bool y 21 21 22 + config GENERIC_LOCKBREAK 23 + def_bool y 24 + depends on SMP && PREEMPT 25 + 22 26 config GENERIC_TIME 23 27 def_bool y 24 28
+2 -1
fs/jbd/checkpoint.c
··· 347 347 break; 348 348 } 349 349 retry = __process_buffer(journal, jh, bhs,&batch_count); 350 - if (!retry && lock_need_resched(&journal->j_list_lock)){ 350 + if (!retry && (need_resched() || 351 + spin_needbreak(&journal->j_list_lock))) { 351 352 spin_unlock(&journal->j_list_lock); 352 353 retry = 1; 353 354 break;
+1 -1
fs/jbd/commit.c
··· 265 265 put_bh(bh); 266 266 } 267 267 268 - if (lock_need_resched(&journal->j_list_lock)) { 268 + if (need_resched() || spin_needbreak(&journal->j_list_lock)) { 269 269 spin_unlock(&journal->j_list_lock); 270 270 goto write_out_data; 271 271 }
+2 -1
fs/jbd2/checkpoint.c
··· 353 353 } 354 354 retry = __process_buffer(journal, jh, bhs, &batch_count, 355 355 transaction); 356 - if (!retry && lock_need_resched(&journal->j_list_lock)){ 356 + if (!retry && (need_resched() || 357 + spin_needbreak(&journal->j_list_lock))) { 357 358 spin_unlock(&journal->j_list_lock); 358 359 retry = 1; 359 360 break;
+1 -1
fs/jbd2/commit.c
··· 341 341 put_bh(bh); 342 342 } 343 343 344 - if (lock_need_resched(&journal->j_list_lock)) { 344 + if (need_resched() || spin_needbreak(&journal->j_list_lock)) { 345 345 spin_unlock(&journal->j_list_lock); 346 346 goto write_out_data; 347 347 }
+7 -14
include/linux/sched.h
··· 1922 1922 1923 1923 /* 1924 1924 * Does a critical section need to be broken due to another 1925 - * task waiting?: 1925 + * task waiting?: (technically does not depend on CONFIG_PREEMPT, 1926 + * but a general need for low latency) 1926 1927 */ 1927 - #if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) 1928 - # define need_lockbreak(lock) ((lock)->break_lock) 1929 - #else 1930 - # define need_lockbreak(lock) 0 1931 - #endif 1932 - 1933 - /* 1934 - * Does a critical section need to be broken due to another 1935 - * task waiting or preemption being signalled: 1936 - */ 1937 - static inline int lock_need_resched(spinlock_t *lock) 1928 + static inline int spin_needbreak(spinlock_t *lock) 1938 1929 { 1939 - if (need_lockbreak(lock) || need_resched()) 1940 - return 1; 1930 + #ifdef CONFIG_PREEMPT 1931 + return spin_is_contended(lock); 1932 + #else 1941 1933 return 0; 1934 + #endif 1942 1935 } 1943 1936 1944 1937 /*
+6
include/linux/spinlock.h
··· 120 120 121 121 #define spin_is_locked(lock) __raw_spin_is_locked(&(lock)->raw_lock) 122 122 123 + #ifdef CONFIG_GENERIC_LOCKBREAK 124 + #define spin_is_contended(lock) ((lock)->break_lock) 125 + #else 126 + #define spin_is_contended(lock) __raw_spin_is_contended(&(lock)->raw_lock) 127 + #endif 128 + 123 129 /** 124 130 * spin_unlock_wait - wait until the spinlock gets unlocked 125 131 * @lock: the spinlock in question.
+2 -2
include/linux/spinlock_types.h
··· 19 19 20 20 typedef struct { 21 21 raw_spinlock_t raw_lock; 22 - #if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) 22 + #ifdef CONFIG_GENERIC_LOCKBREAK 23 23 unsigned int break_lock; 24 24 #endif 25 25 #ifdef CONFIG_DEBUG_SPINLOCK ··· 35 35 36 36 typedef struct { 37 37 raw_rwlock_t raw_lock; 38 - #if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) 38 + #ifdef CONFIG_GENERIC_LOCKBREAK 39 39 unsigned int break_lock; 40 40 #endif 41 41 #ifdef CONFIG_DEBUG_SPINLOCK
+2
include/linux/spinlock_up.h
··· 64 64 # define __raw_spin_trylock(lock) ({ (void)(lock); 1; }) 65 65 #endif /* DEBUG_SPINLOCK */ 66 66 67 + #define __raw_spin_is_contended(lock) (((void)(lock), 0)) 68 + 67 69 #define __raw_read_can_lock(lock) (((void)(lock), 1)) 68 70 #define __raw_write_can_lock(lock) (((void)(lock), 1)) 69 71
+6 -10
kernel/sched.c
··· 4945 4945 */ 4946 4946 int cond_resched_lock(spinlock_t *lock) 4947 4947 { 4948 + int resched = need_resched() && system_state == SYSTEM_RUNNING; 4948 4949 int ret = 0; 4949 4950 4950 - if (need_lockbreak(lock)) { 4951 + if (spin_needbreak(lock) || resched) { 4951 4952 spin_unlock(lock); 4952 - cpu_relax(); 4953 - ret = 1; 4954 - spin_lock(lock); 4955 - } 4956 - if (need_resched() && system_state == SYSTEM_RUNNING) { 4957 - spin_release(&lock->dep_map, 1, _THIS_IP_); 4958 - _raw_spin_unlock(lock); 4959 - preempt_enable_no_resched(); 4960 - __cond_resched(); 4953 + if (resched && need_resched()) 4954 + __cond_resched(); 4955 + else 4956 + cpu_relax(); 4961 4957 ret = 1; 4962 4958 spin_lock(lock); 4963 4959 }
+1 -2
kernel/spinlock.c
··· 65 65 * even on CONFIG_PREEMPT, because lockdep assumes that interrupts are 66 66 * not re-enabled during lock-acquire (which the preempt-spin-ops do): 67 67 */ 68 - #if !defined(CONFIG_PREEMPT) || !defined(CONFIG_SMP) || \ 69 - defined(CONFIG_DEBUG_LOCK_ALLOC) 68 + #if !defined(CONFIG_GENERIC_LOCKBREAK) || defined(CONFIG_DEBUG_LOCK_ALLOC) 70 69 71 70 void __lockfunc _read_lock(rwlock_t *lock) 72 71 {
+3 -5
mm/memory.c
··· 513 513 if (progress >= 32) { 514 514 progress = 0; 515 515 if (need_resched() || 516 - need_lockbreak(src_ptl) || 517 - need_lockbreak(dst_ptl)) 516 + spin_needbreak(src_ptl) || spin_needbreak(dst_ptl)) 518 517 break; 519 518 } 520 519 if (pte_none(*src_pte)) { ··· 852 853 tlb_finish_mmu(*tlbp, tlb_start, start); 853 854 854 855 if (need_resched() || 855 - (i_mmap_lock && need_lockbreak(i_mmap_lock))) { 856 + (i_mmap_lock && spin_needbreak(i_mmap_lock))) { 856 857 if (i_mmap_lock) { 857 858 *tlbp = NULL; 858 859 goto out; ··· 1767 1768 1768 1769 restart_addr = zap_page_range(vma, start_addr, 1769 1770 end_addr - start_addr, details); 1770 - need_break = need_resched() || 1771 - need_lockbreak(details->i_mmap_lock); 1771 + need_break = need_resched() || spin_needbreak(details->i_mmap_lock); 1772 1772 1773 1773 if (restart_addr >= end_addr) { 1774 1774 /* We have now completed this vma: mark it so */