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

ARM: 7446/1: spinlock: use ticket algorithm for ARMv6+ locking implementation

Ticket spinlocks ensure locking fairness by introducing a FIFO-like
nature to the granting of lock acquisitions and also reducing the
thundering herd effect when spinning on a lock by allowing the cacheline
to remain in a shared state amongst the waiting CPUs. This is especially
important on systems where memory-access times are not necessarily
uniform when accessing the lock structure (for example, on a
multi-cluster platform where the lock is allocated into L1 when a CPU
releases it).

This patch implements the ticket spinlock algorithm for ARM, replacing
the simpler implementation for ARMv6+ processors.

Reviewed-by: Nicolas Pitre <nico@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Will Deacon and committed by
Russell King
546c2896 575320d6

+63 -26
+48 -24
arch/arm/include/asm/spinlock.h
··· 59 59 } 60 60 61 61 /* 62 - * ARMv6 Spin-locking. 62 + * ARMv6 ticket-based spin-locking. 63 63 * 64 - * We exclusively read the old value. If it is zero, we may have 65 - * won the lock, so we try exclusively storing it. A memory barrier 66 - * is required after we get a lock, and before we release it, because 67 - * V6 CPUs are assumed to have weakly ordered memory. 68 - * 69 - * Unlocked value: 0 70 - * Locked value: 1 64 + * A memory barrier is required after we get a lock, and before we 65 + * release it, because V6 CPUs are assumed to have weakly ordered 66 + * memory. 71 67 */ 72 68 73 - #define arch_spin_is_locked(x) ((x)->lock != 0) 74 69 #define arch_spin_unlock_wait(lock) \ 75 70 do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0) 76 71 ··· 74 79 static inline void arch_spin_lock(arch_spinlock_t *lock) 75 80 { 76 81 unsigned long tmp; 82 + u32 newval; 83 + arch_spinlock_t lockval; 77 84 78 85 __asm__ __volatile__( 79 - "1: ldrex %0, [%1]\n" 80 - " teq %0, #0\n" 81 - WFE("ne") 82 - " strexeq %0, %2, [%1]\n" 83 - " teqeq %0, #0\n" 86 + "1: ldrex %0, [%3]\n" 87 + " add %1, %0, %4\n" 88 + " strex %2, %1, [%3]\n" 89 + " teq %2, #0\n" 84 90 " bne 1b" 85 - : "=&r" (tmp) 86 - : "r" (&lock->lock), "r" (1) 91 + : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) 92 + : "r" (&lock->slock), "I" (1 << TICKET_SHIFT) 87 93 : "cc"); 94 + 95 + while (lockval.tickets.next != lockval.tickets.owner) { 96 + wfe(); 97 + lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); 98 + } 88 99 89 100 smp_mb(); 90 101 } ··· 98 97 static inline int arch_spin_trylock(arch_spinlock_t *lock) 99 98 { 100 99 unsigned long tmp; 100 + u32 slock; 101 101 102 102 __asm__ __volatile__( 103 - " ldrex %0, [%1]\n" 104 - " teq %0, #0\n" 105 - " strexeq %0, %2, [%1]" 106 - : "=&r" (tmp) 107 - : "r" (&lock->lock), "r" (1) 103 + " ldrex %0, [%2]\n" 104 + " subs %1, %0, %0, ror #16\n" 105 + " addeq %0, %0, %3\n" 106 + " strexeq %1, %0, [%2]" 107 + : "=&r" (slock), "=&r" (tmp) 108 + : "r" (&lock->slock), "I" (1 << TICKET_SHIFT) 108 109 : "cc"); 109 110 110 111 if (tmp == 0) { ··· 119 116 120 117 static inline void arch_spin_unlock(arch_spinlock_t *lock) 121 118 { 119 + unsigned long tmp; 120 + u32 slock; 121 + 122 122 smp_mb(); 123 123 124 124 __asm__ __volatile__( 125 - " str %1, [%0]\n" 126 - : 127 - : "r" (&lock->lock), "r" (0) 125 + " mov %1, #1\n" 126 + "1: ldrex %0, [%2]\n" 127 + " uadd16 %0, %0, %1\n" 128 + " strex %1, %0, [%2]\n" 129 + " teq %1, #0\n" 130 + " bne 1b" 131 + : "=&r" (slock), "=&r" (tmp) 132 + : "r" (&lock->slock) 128 133 : "cc"); 129 134 130 135 dsb_sev(); 131 136 } 137 + 138 + static inline int arch_spin_is_locked(arch_spinlock_t *lock) 139 + { 140 + struct __raw_tickets tickets = ACCESS_ONCE(lock->tickets); 141 + return tickets.owner != tickets.next; 142 + } 143 + 144 + static inline int arch_spin_is_contended(arch_spinlock_t *lock) 145 + { 146 + struct __raw_tickets tickets = ACCESS_ONCE(lock->tickets); 147 + return (tickets.next - tickets.owner) > 1; 148 + } 149 + #define arch_spin_is_contended arch_spin_is_contended 132 150 133 151 /* 134 152 * RWLOCKS
+15 -2
arch/arm/include/asm/spinlock_types.h
··· 5 5 # error "please don't include this file directly" 6 6 #endif 7 7 8 + #define TICKET_SHIFT 16 9 + 8 10 typedef struct { 9 - volatile unsigned int lock; 11 + union { 12 + u32 slock; 13 + struct __raw_tickets { 14 + #ifdef __ARMEB__ 15 + u16 next; 16 + u16 owner; 17 + #else 18 + u16 owner; 19 + u16 next; 20 + #endif 21 + } tickets; 22 + }; 10 23 } arch_spinlock_t; 11 24 12 - #define __ARCH_SPIN_LOCK_UNLOCKED { 0 } 25 + #define __ARCH_SPIN_LOCK_UNLOCKED { { 0 } } 13 26 14 27 typedef struct { 15 28 volatile unsigned int lock;