diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2014-05-15 05:00:44 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2014-05-20 02:58:53 -0400 |
commit | bae8f567344a7cb6a23ca6e13096ba785c69eb42 (patch) | |
tree | a0d91d9abf8405646ffbd5201d8c1b7f2627563c | |
parent | 2bf29df7460f4038f84ac5dea3cbe582d6d4af82 (diff) |
s390/spinlock,rwlock: always to a load-and-test first
In case a lock is contended it is better to do a load-and-test first
before trying to get the lock with compare-and-swap. This helps to avoid
unnecessary cache invalidations of the cacheline for the lock if the
CPU has to wait for the lock. For an uncontended lock doing the
compare-and-swap directly is a bit better, if the CPU does not have the
cacheline in its cache yet the compare-and-swap will get it read-write
immediately while a load-and-test would get it read-only first.
Always to the load-and-test first to avoid the cacheline invalidations
for the contended case outweight the potential read-only to read-write
cacheline upgrade for the uncontended case.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/spinlock.h | 50 | ||||
-rw-r--r-- | arch/s390/lib/spinlock.c | 29 |
2 files changed, 46 insertions, 33 deletions
diff --git a/arch/s390/include/asm/spinlock.h b/arch/s390/include/asm/spinlock.h index 5a0b2882ad48..96879f7ad6da 100644 --- a/arch/s390/include/asm/spinlock.h +++ b/arch/s390/include/asm/spinlock.h | |||
@@ -59,7 +59,9 @@ static inline int arch_spin_is_locked(arch_spinlock_t *lp) | |||
59 | 59 | ||
60 | static inline int arch_spin_trylock_once(arch_spinlock_t *lp) | 60 | static inline int arch_spin_trylock_once(arch_spinlock_t *lp) |
61 | { | 61 | { |
62 | return _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL); | 62 | barrier(); |
63 | return likely(arch_spin_value_unlocked(*lp) && | ||
64 | _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL)); | ||
63 | } | 65 | } |
64 | 66 | ||
65 | static inline int arch_spin_tryrelease_once(arch_spinlock_t *lp) | 67 | static inline int arch_spin_tryrelease_once(arch_spinlock_t *lp) |
@@ -69,20 +71,20 @@ static inline int arch_spin_tryrelease_once(arch_spinlock_t *lp) | |||
69 | 71 | ||
70 | static inline void arch_spin_lock(arch_spinlock_t *lp) | 72 | static inline void arch_spin_lock(arch_spinlock_t *lp) |
71 | { | 73 | { |
72 | if (unlikely(!arch_spin_trylock_once(lp))) | 74 | if (!arch_spin_trylock_once(lp)) |
73 | arch_spin_lock_wait(lp); | 75 | arch_spin_lock_wait(lp); |
74 | } | 76 | } |
75 | 77 | ||
76 | static inline void arch_spin_lock_flags(arch_spinlock_t *lp, | 78 | static inline void arch_spin_lock_flags(arch_spinlock_t *lp, |
77 | unsigned long flags) | 79 | unsigned long flags) |
78 | { | 80 | { |
79 | if (unlikely(!arch_spin_trylock_once(lp))) | 81 | if (!arch_spin_trylock_once(lp)) |
80 | arch_spin_lock_wait_flags(lp, flags); | 82 | arch_spin_lock_wait_flags(lp, flags); |
81 | } | 83 | } |
82 | 84 | ||
83 | static inline int arch_spin_trylock(arch_spinlock_t *lp) | 85 | static inline int arch_spin_trylock(arch_spinlock_t *lp) |
84 | { | 86 | { |
85 | if (unlikely(!arch_spin_trylock_once(lp))) | 87 | if (!arch_spin_trylock_once(lp)) |
86 | return arch_spin_trylock_retry(lp); | 88 | return arch_spin_trylock_retry(lp); |
87 | return 1; | 89 | return 1; |
88 | } | 90 | } |
@@ -128,19 +130,29 @@ extern void _raw_write_lock_wait(arch_rwlock_t *lp); | |||
128 | extern void _raw_write_lock_wait_flags(arch_rwlock_t *lp, unsigned long flags); | 130 | extern void _raw_write_lock_wait_flags(arch_rwlock_t *lp, unsigned long flags); |
129 | extern int _raw_write_trylock_retry(arch_rwlock_t *lp); | 131 | extern int _raw_write_trylock_retry(arch_rwlock_t *lp); |
130 | 132 | ||
133 | static inline int arch_read_trylock_once(arch_rwlock_t *rw) | ||
134 | { | ||
135 | unsigned int old = ACCESS_ONCE(rw->lock); | ||
136 | return likely((int) old >= 0 && | ||
137 | _raw_compare_and_swap(&rw->lock, old, old + 1)); | ||
138 | } | ||
139 | |||
140 | static inline int arch_write_trylock_once(arch_rwlock_t *rw) | ||
141 | { | ||
142 | unsigned int old = ACCESS_ONCE(rw->lock); | ||
143 | return likely(old == 0 && | ||
144 | _raw_compare_and_swap(&rw->lock, 0, 0x80000000)); | ||
145 | } | ||
146 | |||
131 | static inline void arch_read_lock(arch_rwlock_t *rw) | 147 | static inline void arch_read_lock(arch_rwlock_t *rw) |
132 | { | 148 | { |
133 | unsigned int old; | 149 | if (!arch_read_trylock_once(rw)) |
134 | old = rw->lock & 0x7fffffffU; | ||
135 | if (!_raw_compare_and_swap(&rw->lock, old, old + 1)) | ||
136 | _raw_read_lock_wait(rw); | 150 | _raw_read_lock_wait(rw); |
137 | } | 151 | } |
138 | 152 | ||
139 | static inline void arch_read_lock_flags(arch_rwlock_t *rw, unsigned long flags) | 153 | static inline void arch_read_lock_flags(arch_rwlock_t *rw, unsigned long flags) |
140 | { | 154 | { |
141 | unsigned int old; | 155 | if (!arch_read_trylock_once(rw)) |
142 | old = rw->lock & 0x7fffffffU; | ||
143 | if (!_raw_compare_and_swap(&rw->lock, old, old + 1)) | ||
144 | _raw_read_lock_wait_flags(rw, flags); | 156 | _raw_read_lock_wait_flags(rw, flags); |
145 | } | 157 | } |
146 | 158 | ||
@@ -155,13 +167,13 @@ static inline void arch_read_unlock(arch_rwlock_t *rw) | |||
155 | 167 | ||
156 | static inline void arch_write_lock(arch_rwlock_t *rw) | 168 | static inline void arch_write_lock(arch_rwlock_t *rw) |
157 | { | 169 | { |
158 | if (unlikely(!_raw_compare_and_swap(&rw->lock, 0, 0x80000000))) | 170 | if (!arch_write_trylock_once(rw)) |
159 | _raw_write_lock_wait(rw); | 171 | _raw_write_lock_wait(rw); |
160 | } | 172 | } |
161 | 173 | ||
162 | static inline void arch_write_lock_flags(arch_rwlock_t *rw, unsigned long flags) | 174 | static inline void arch_write_lock_flags(arch_rwlock_t *rw, unsigned long flags) |
163 | { | 175 | { |
164 | if (unlikely(!_raw_compare_and_swap(&rw->lock, 0, 0x80000000))) | 176 | if (!arch_write_trylock_once(rw)) |
165 | _raw_write_lock_wait_flags(rw, flags); | 177 | _raw_write_lock_wait_flags(rw, flags); |
166 | } | 178 | } |
167 | 179 | ||
@@ -172,18 +184,16 @@ static inline void arch_write_unlock(arch_rwlock_t *rw) | |||
172 | 184 | ||
173 | static inline int arch_read_trylock(arch_rwlock_t *rw) | 185 | static inline int arch_read_trylock(arch_rwlock_t *rw) |
174 | { | 186 | { |
175 | unsigned int old; | 187 | if (!arch_read_trylock_once(rw)) |
176 | old = rw->lock & 0x7fffffffU; | 188 | return _raw_read_trylock_retry(rw); |
177 | if (likely(_raw_compare_and_swap(&rw->lock, old, old + 1))) | 189 | return 1; |
178 | return 1; | ||
179 | return _raw_read_trylock_retry(rw); | ||
180 | } | 190 | } |
181 | 191 | ||
182 | static inline int arch_write_trylock(arch_rwlock_t *rw) | 192 | static inline int arch_write_trylock(arch_rwlock_t *rw) |
183 | { | 193 | { |
184 | if (likely(_raw_compare_and_swap(&rw->lock, 0, 0x80000000))) | 194 | if (!arch_write_trylock_once(rw)) |
185 | return 1; | 195 | return _raw_write_trylock_retry(rw); |
186 | return _raw_write_trylock_retry(rw); | 196 | return 1; |
187 | } | 197 | } |
188 | 198 | ||
189 | #define arch_read_relax(lock) cpu_relax() | 199 | #define arch_read_relax(lock) cpu_relax() |
diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 3f0e682b7e62..284d879a9b8c 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c | |||
@@ -100,12 +100,9 @@ int arch_spin_trylock_retry(arch_spinlock_t *lp) | |||
100 | { | 100 | { |
101 | int count; | 101 | int count; |
102 | 102 | ||
103 | for (count = spin_retry; count > 0; count--) { | 103 | for (count = spin_retry; count > 0; count--) |
104 | if (arch_spin_is_locked(lp)) | ||
105 | continue; | ||
106 | if (arch_spin_trylock_once(lp)) | 104 | if (arch_spin_trylock_once(lp)) |
107 | return 1; | 105 | return 1; |
108 | } | ||
109 | return 0; | 106 | return 0; |
110 | } | 107 | } |
111 | EXPORT_SYMBOL(arch_spin_trylock_retry); | 108 | EXPORT_SYMBOL(arch_spin_trylock_retry); |
@@ -120,9 +117,9 @@ void _raw_read_lock_wait(arch_rwlock_t *rw) | |||
120 | smp_yield(); | 117 | smp_yield(); |
121 | count = spin_retry; | 118 | count = spin_retry; |
122 | } | 119 | } |
123 | if (!arch_read_can_lock(rw)) | 120 | old = ACCESS_ONCE(rw->lock); |
121 | if ((int) old < 0) | ||
124 | continue; | 122 | continue; |
125 | old = rw->lock & 0x7fffffffU; | ||
126 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) | 123 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) |
127 | return; | 124 | return; |
128 | } | 125 | } |
@@ -140,9 +137,9 @@ void _raw_read_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) | |||
140 | smp_yield(); | 137 | smp_yield(); |
141 | count = spin_retry; | 138 | count = spin_retry; |
142 | } | 139 | } |
143 | if (!arch_read_can_lock(rw)) | 140 | old = ACCESS_ONCE(rw->lock); |
141 | if ((int) old < 0) | ||
144 | continue; | 142 | continue; |
145 | old = rw->lock & 0x7fffffffU; | ||
146 | local_irq_disable(); | 143 | local_irq_disable(); |
147 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) | 144 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) |
148 | return; | 145 | return; |
@@ -156,9 +153,9 @@ int _raw_read_trylock_retry(arch_rwlock_t *rw) | |||
156 | int count = spin_retry; | 153 | int count = spin_retry; |
157 | 154 | ||
158 | while (count-- > 0) { | 155 | while (count-- > 0) { |
159 | if (!arch_read_can_lock(rw)) | 156 | old = ACCESS_ONCE(rw->lock); |
157 | if ((int) old < 0) | ||
160 | continue; | 158 | continue; |
161 | old = rw->lock & 0x7fffffffU; | ||
162 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) | 159 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) |
163 | return 1; | 160 | return 1; |
164 | } | 161 | } |
@@ -168,6 +165,7 @@ EXPORT_SYMBOL(_raw_read_trylock_retry); | |||
168 | 165 | ||
169 | void _raw_write_lock_wait(arch_rwlock_t *rw) | 166 | void _raw_write_lock_wait(arch_rwlock_t *rw) |
170 | { | 167 | { |
168 | unsigned int old; | ||
171 | int count = spin_retry; | 169 | int count = spin_retry; |
172 | 170 | ||
173 | while (1) { | 171 | while (1) { |
@@ -175,7 +173,8 @@ void _raw_write_lock_wait(arch_rwlock_t *rw) | |||
175 | smp_yield(); | 173 | smp_yield(); |
176 | count = spin_retry; | 174 | count = spin_retry; |
177 | } | 175 | } |
178 | if (!arch_write_can_lock(rw)) | 176 | old = ACCESS_ONCE(rw->lock); |
177 | if (old) | ||
179 | continue; | 178 | continue; |
180 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) | 179 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) |
181 | return; | 180 | return; |
@@ -185,6 +184,7 @@ EXPORT_SYMBOL(_raw_write_lock_wait); | |||
185 | 184 | ||
186 | void _raw_write_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) | 185 | void _raw_write_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) |
187 | { | 186 | { |
187 | unsigned int old; | ||
188 | int count = spin_retry; | 188 | int count = spin_retry; |
189 | 189 | ||
190 | local_irq_restore(flags); | 190 | local_irq_restore(flags); |
@@ -193,7 +193,8 @@ void _raw_write_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) | |||
193 | smp_yield(); | 193 | smp_yield(); |
194 | count = spin_retry; | 194 | count = spin_retry; |
195 | } | 195 | } |
196 | if (!arch_write_can_lock(rw)) | 196 | old = ACCESS_ONCE(rw->lock); |
197 | if (old) | ||
197 | continue; | 198 | continue; |
198 | local_irq_disable(); | 199 | local_irq_disable(); |
199 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) | 200 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) |
@@ -204,10 +205,12 @@ EXPORT_SYMBOL(_raw_write_lock_wait_flags); | |||
204 | 205 | ||
205 | int _raw_write_trylock_retry(arch_rwlock_t *rw) | 206 | int _raw_write_trylock_retry(arch_rwlock_t *rw) |
206 | { | 207 | { |
208 | unsigned int old; | ||
207 | int count = spin_retry; | 209 | int count = spin_retry; |
208 | 210 | ||
209 | while (count-- > 0) { | 211 | while (count-- > 0) { |
210 | if (!arch_write_can_lock(rw)) | 212 | old = ACCESS_ONCE(rw->lock); |
213 | if (old) | ||
211 | continue; | 214 | continue; |
212 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) | 215 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) |
213 | return 1; | 216 | return 1; |