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

ARC: LLOCK/SCOND based rwlock

With LLOCK/SCOND, the rwlock counter can be atomically updated w/o need
for a guarding spin lock.

This in turn elides the EXchange instruction based spinning which causes
the cacheline transition to exclusive state and concurrent spinning
across cores would cause the line to keep bouncing around.
LLOCK/SCOND based implementation is superior as spinning on LLOCK keeps
the cacheline in shared state.

Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>

+166 -10
+164 -10
arch/arc/include/asm/spinlock.h
··· 75 75 smp_mb(); 76 76 } 77 77 78 + /* 79 + * Read-write spinlocks, allowing multiple readers but only one writer. 80 + * Unfair locking as Writers could be starved indefinitely by Reader(s) 81 + */ 82 + 83 + static inline void arch_read_lock(arch_rwlock_t *rw) 84 + { 85 + unsigned int val; 86 + 87 + smp_mb(); 88 + 89 + /* 90 + * zero means writer holds the lock exclusively, deny Reader. 91 + * Otherwise grant lock to first/subseq reader 92 + * 93 + * if (rw->counter > 0) { 94 + * rw->counter--; 95 + * ret = 1; 96 + * } 97 + */ 98 + 99 + __asm__ __volatile__( 100 + "1: llock %[val], [%[rwlock]] \n" 101 + " brls %[val], %[WR_LOCKED], 1b\n" /* <= 0: spin while write locked */ 102 + " sub %[val], %[val], 1 \n" /* reader lock */ 103 + " scond %[val], [%[rwlock]] \n" 104 + " bnz 1b \n" 105 + " \n" 106 + : [val] "=&r" (val) 107 + : [rwlock] "r" (&(rw->counter)), 108 + [WR_LOCKED] "ir" (0) 109 + : "memory", "cc"); 110 + 111 + smp_mb(); 112 + } 113 + 114 + /* 1 - lock taken successfully */ 115 + static inline int arch_read_trylock(arch_rwlock_t *rw) 116 + { 117 + unsigned int val, got_it = 0; 118 + 119 + smp_mb(); 120 + 121 + __asm__ __volatile__( 122 + "1: llock %[val], [%[rwlock]] \n" 123 + " brls %[val], %[WR_LOCKED], 4f\n" /* <= 0: already write locked, bail */ 124 + " sub %[val], %[val], 1 \n" /* counter-- */ 125 + " scond %[val], [%[rwlock]] \n" 126 + " bnz 1b \n" /* retry if collided with someone */ 127 + " mov %[got_it], 1 \n" 128 + " \n" 129 + "4: ; --- done --- \n" 130 + 131 + : [val] "=&r" (val), 132 + [got_it] "+&r" (got_it) 133 + : [rwlock] "r" (&(rw->counter)), 134 + [WR_LOCKED] "ir" (0) 135 + : "memory", "cc"); 136 + 137 + smp_mb(); 138 + 139 + return got_it; 140 + } 141 + 142 + static inline void arch_write_lock(arch_rwlock_t *rw) 143 + { 144 + unsigned int val; 145 + 146 + smp_mb(); 147 + 148 + /* 149 + * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__), 150 + * deny writer. Otherwise if unlocked grant to writer 151 + * Hence the claim that Linux rwlocks are unfair to writers. 152 + * (can be starved for an indefinite time by readers). 153 + * 154 + * if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) { 155 + * rw->counter = 0; 156 + * ret = 1; 157 + * } 158 + */ 159 + 160 + __asm__ __volatile__( 161 + "1: llock %[val], [%[rwlock]] \n" 162 + " brne %[val], %[UNLOCKED], 1b \n" /* while !UNLOCKED spin */ 163 + " mov %[val], %[WR_LOCKED] \n" 164 + " scond %[val], [%[rwlock]] \n" 165 + " bnz 1b \n" 166 + " \n" 167 + : [val] "=&r" (val) 168 + : [rwlock] "r" (&(rw->counter)), 169 + [UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__), 170 + [WR_LOCKED] "ir" (0) 171 + : "memory", "cc"); 172 + 173 + smp_mb(); 174 + } 175 + 176 + /* 1 - lock taken successfully */ 177 + static inline int arch_write_trylock(arch_rwlock_t *rw) 178 + { 179 + unsigned int val, got_it = 0; 180 + 181 + smp_mb(); 182 + 183 + __asm__ __volatile__( 184 + "1: llock %[val], [%[rwlock]] \n" 185 + " brne %[val], %[UNLOCKED], 4f \n" /* !UNLOCKED, bail */ 186 + " mov %[val], %[WR_LOCKED] \n" 187 + " scond %[val], [%[rwlock]] \n" 188 + " bnz 1b \n" /* retry if collided with someone */ 189 + " mov %[got_it], 1 \n" 190 + " \n" 191 + "4: ; --- done --- \n" 192 + 193 + : [val] "=&r" (val), 194 + [got_it] "+&r" (got_it) 195 + : [rwlock] "r" (&(rw->counter)), 196 + [UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__), 197 + [WR_LOCKED] "ir" (0) 198 + : "memory", "cc"); 199 + 200 + smp_mb(); 201 + 202 + return got_it; 203 + } 204 + 205 + static inline void arch_read_unlock(arch_rwlock_t *rw) 206 + { 207 + unsigned int val; 208 + 209 + smp_mb(); 210 + 211 + /* 212 + * rw->counter++; 213 + */ 214 + __asm__ __volatile__( 215 + "1: llock %[val], [%[rwlock]] \n" 216 + " add %[val], %[val], 1 \n" 217 + " scond %[val], [%[rwlock]] \n" 218 + " bnz 1b \n" 219 + " \n" 220 + : [val] "=&r" (val) 221 + : [rwlock] "r" (&(rw->counter)) 222 + : "memory", "cc"); 223 + 224 + smp_mb(); 225 + } 226 + 227 + static inline void arch_write_unlock(arch_rwlock_t *rw) 228 + { 229 + smp_mb(); 230 + 231 + rw->counter = __ARCH_RW_LOCK_UNLOCKED__; 232 + 233 + smp_mb(); 234 + } 235 + 78 236 #else /* !CONFIG_ARC_HAS_LLSC */ 79 237 80 238 static inline void arch_spin_lock(arch_spinlock_t *lock) ··· 306 148 smp_mb(); 307 149 } 308 150 309 - #endif 310 - 311 151 /* 312 152 * Read-write spinlocks, allowing multiple readers but only one writer. 153 + * Unfair locking as Writers could be starved indefinitely by Reader(s) 313 154 * 314 155 * The spinlock itself is contained in @counter and access to it is 315 156 * serialized with @lock_mutex. 316 - * 317 - * Unfair locking as Writers could be starved indefinitely by Reader(s) 318 157 */ 319 - 320 - /* Would read_trylock() succeed? */ 321 - #define arch_read_can_lock(x) ((x)->counter > 0) 322 - 323 - /* Would write_trylock() succeed? */ 324 - #define arch_write_can_lock(x) ((x)->counter == __ARCH_RW_LOCK_UNLOCKED__) 325 158 326 159 /* 1 - lock taken successfully */ 327 160 static inline int arch_read_trylock(arch_rwlock_t *rw) ··· 383 234 rw->counter = __ARCH_RW_LOCK_UNLOCKED__; 384 235 arch_spin_unlock(&(rw->lock_mutex)); 385 236 } 237 + 238 + #endif 239 + 240 + #define arch_read_can_lock(x) ((x)->counter > 0) 241 + #define arch_write_can_lock(x) ((x)->counter == __ARCH_RW_LOCK_UNLOCKED__) 386 242 387 243 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock) 388 244 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
+2
arch/arc/include/asm/spinlock_types.h
··· 26 26 */ 27 27 typedef struct { 28 28 volatile unsigned int counter; 29 + #ifndef CONFIG_ARC_HAS_LLSC 29 30 arch_spinlock_t lock_mutex; 31 + #endif 30 32 } arch_rwlock_t; 31 33 32 34 #define __ARCH_RW_LOCK_UNLOCKED__ 0x01000000