1 #ifndef __RWLOCK_H__
2 #define __RWLOCK_H__
3
4 #include <xen/percpu.h>
5 #include <xen/preempt.h>
6 #include <xen/smp.h>
7 #include <xen/spinlock.h>
8
9 #include <asm/atomic.h>
10 #include <asm/system.h>
11
12 typedef struct {
13 atomic_t cnts;
14 spinlock_t lock;
15 } rwlock_t;
16
17 #define RW_LOCK_UNLOCKED { \
18 .cnts = ATOMIC_INIT(0), \
19 .lock = SPIN_LOCK_UNLOCKED \
20 }
21
22 #define DEFINE_RWLOCK(l) rwlock_t l = RW_LOCK_UNLOCKED
23 #define rwlock_init(l) (*(l) = (rwlock_t)RW_LOCK_UNLOCKED)
24
25 /* Writer states & reader shift and bias. */
26 #define _QW_SHIFT 14 /* Writer flags shift */
27 #define _QW_CPUMASK ((1U << _QW_SHIFT) - 1) /* Writer CPU mask */
28 #define _QW_WAITING (1U << _QW_SHIFT) /* A writer is waiting */
29 #define _QW_LOCKED (3U << _QW_SHIFT) /* A writer holds the lock */
30 #define _QW_WMASK (3U << _QW_SHIFT) /* Writer mask */
31 #define _QR_SHIFT (_QW_SHIFT + 2) /* Reader count shift */
32 #define _QR_BIAS (1U << _QR_SHIFT)
33
34 void queue_read_lock_slowpath(rwlock_t *lock);
35 void queue_write_lock_slowpath(rwlock_t *lock);
36
_is_write_locked_by_me(unsigned int cnts)37 static inline bool _is_write_locked_by_me(unsigned int cnts)
38 {
39 BUILD_BUG_ON((_QW_CPUMASK + 1) < NR_CPUS);
40 BUILD_BUG_ON(NR_CPUS * _QR_BIAS > INT_MAX);
41 return (cnts & _QW_WMASK) == _QW_LOCKED &&
42 (cnts & _QW_CPUMASK) == smp_processor_id();
43 }
44
_can_read_lock(unsigned int cnts)45 static inline bool _can_read_lock(unsigned int cnts)
46 {
47 /*
48 * If write locked by the caller, no other readers are possible.
49 * Not allowing the lock holder to read_lock() another
50 * INT_MAX >> _QR_SHIFT times ought to be fine.
51 */
52 return cnts <= INT_MAX &&
53 (!(cnts & _QW_WMASK) || _is_write_locked_by_me(cnts));
54 }
55
56 /*
57 * _read_trylock - try to acquire read lock of a queue rwlock.
58 * @lock : Pointer to queue rwlock structure.
59 * Return: 1 if lock acquired, 0 if failed.
60 */
_read_trylock(rwlock_t * lock)61 static inline int _read_trylock(rwlock_t *lock)
62 {
63 u32 cnts;
64
65 preempt_disable();
66 check_lock(&lock->lock.debug, true);
67 cnts = atomic_read(&lock->cnts);
68 if ( likely(_can_read_lock(cnts)) )
69 {
70 cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts);
71 /*
72 * atomic_add_return() is a full barrier so no need for an
73 * arch_lock_acquire_barrier().
74 */
75 if ( likely(_can_read_lock(cnts)) )
76 {
77 lock_enter(&lock->lock.debug);
78 return 1;
79 }
80 atomic_sub(_QR_BIAS, &lock->cnts);
81 }
82 preempt_enable();
83 return 0;
84 }
85
86 /*
87 * _read_lock - acquire read lock of a queue rwlock.
88 * @lock: Pointer to queue rwlock structure.
89 */
_read_lock(rwlock_t * lock)90 static inline void _read_lock(rwlock_t *lock)
91 {
92 u32 cnts;
93
94 preempt_disable();
95 cnts = atomic_add_return(_QR_BIAS, &lock->cnts);
96 /*
97 * atomic_add_return() is a full barrier so no need for an
98 * arch_lock_acquire_barrier().
99 */
100 if ( likely(_can_read_lock(cnts)) )
101 {
102 /* The slow path calls check_lock() via spin_lock(). */
103 check_lock(&lock->lock.debug, false);
104 lock_enter(&lock->lock.debug);
105 return;
106 }
107
108 /* The slowpath will decrement the reader count, if necessary. */
109 queue_read_lock_slowpath(lock);
110 /*
111 * queue_read_lock_slowpath() is using spinlock and therefore is a
112 * full barrier. So no need for an arch_lock_acquire_barrier().
113 */
114 }
115
_read_lock_irq(rwlock_t * lock)116 static inline void _read_lock_irq(rwlock_t *lock)
117 {
118 ASSERT(local_irq_is_enabled());
119 local_irq_disable();
120 _read_lock(lock);
121 }
122
_read_lock_irqsave(rwlock_t * lock)123 static inline unsigned long _read_lock_irqsave(rwlock_t *lock)
124 {
125 unsigned long flags;
126 local_irq_save(flags);
127 _read_lock(lock);
128 return flags;
129 }
130
131 /*
132 * _read_unlock - release read lock of a queue rwlock.
133 * @lock : Pointer to queue rwlock structure.
134 */
_read_unlock(rwlock_t * lock)135 static inline void _read_unlock(rwlock_t *lock)
136 {
137 lock_exit(&lock->lock.debug);
138
139 arch_lock_release_barrier();
140 /*
141 * Atomically decrement the reader count
142 */
143 atomic_sub(_QR_BIAS, &lock->cnts);
144 preempt_enable();
145 }
146
_read_unlock_irq(rwlock_t * lock)147 static inline void _read_unlock_irq(rwlock_t *lock)
148 {
149 _read_unlock(lock);
150 local_irq_enable();
151 }
152
_read_unlock_irqrestore(rwlock_t * lock,unsigned long flags)153 static inline void _read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
154 {
155 _read_unlock(lock);
156 local_irq_restore(flags);
157 }
158
_rw_is_locked(const rwlock_t * lock)159 static inline int _rw_is_locked(const rwlock_t *lock)
160 {
161 return atomic_read(&lock->cnts);
162 }
163
_write_lock_val(void)164 static inline unsigned int _write_lock_val(void)
165 {
166 return _QW_LOCKED | smp_processor_id();
167 }
168
169 /*
170 * queue_write_lock - acquire write lock of a queue rwlock.
171 * @lock : Pointer to queue rwlock structure.
172 */
_write_lock(rwlock_t * lock)173 static inline void _write_lock(rwlock_t *lock)
174 {
175 preempt_disable();
176 /*
177 * Optimize for the unfair lock case where the fair flag is 0.
178 *
179 * atomic_cmpxchg() is a full barrier so no need for an
180 * arch_lock_acquire_barrier().
181 */
182 if ( atomic_cmpxchg(&lock->cnts, 0, _write_lock_val()) == 0 )
183 {
184 /* The slow path calls check_lock() via spin_lock(). */
185 check_lock(&lock->lock.debug, false);
186 lock_enter(&lock->lock.debug);
187 return;
188 }
189
190 queue_write_lock_slowpath(lock);
191 /*
192 * queue_write_lock_slowpath() is using spinlock and therefore is a
193 * full barrier. So no need for an arch_lock_acquire_barrier().
194 */
195 }
196
_write_lock_irq(rwlock_t * lock)197 static inline void _write_lock_irq(rwlock_t *lock)
198 {
199 ASSERT(local_irq_is_enabled());
200 local_irq_disable();
201 _write_lock(lock);
202 }
203
_write_lock_irqsave(rwlock_t * lock)204 static inline unsigned long _write_lock_irqsave(rwlock_t *lock)
205 {
206 unsigned long flags;
207
208 local_irq_save(flags);
209 _write_lock(lock);
210 return flags;
211 }
212
213 /*
214 * queue_write_trylock - try to acquire write lock of a queue rwlock.
215 * @lock : Pointer to queue rwlock structure.
216 * Return: 1 if lock acquired, 0 if failed.
217 */
_write_trylock(rwlock_t * lock)218 static inline int _write_trylock(rwlock_t *lock)
219 {
220 u32 cnts;
221
222 preempt_disable();
223 check_lock(&lock->lock.debug, true);
224 cnts = atomic_read(&lock->cnts);
225 if ( unlikely(cnts) ||
226 unlikely(atomic_cmpxchg(&lock->cnts, 0, _write_lock_val()) != 0) )
227 {
228 preempt_enable();
229 return 0;
230 }
231
232 lock_enter(&lock->lock.debug);
233
234 /*
235 * atomic_cmpxchg() is a full barrier so no need for an
236 * arch_lock_acquire_barrier().
237 */
238 return 1;
239 }
240
_write_unlock(rwlock_t * lock)241 static inline void _write_unlock(rwlock_t *lock)
242 {
243 ASSERT(_is_write_locked_by_me(atomic_read(&lock->cnts)));
244
245 lock_exit(&lock->lock.debug);
246
247 arch_lock_release_barrier();
248 atomic_and(~(_QW_CPUMASK | _QW_WMASK), &lock->cnts);
249 preempt_enable();
250 }
251
_write_unlock_irq(rwlock_t * lock)252 static inline void _write_unlock_irq(rwlock_t *lock)
253 {
254 _write_unlock(lock);
255 local_irq_enable();
256 }
257
_write_unlock_irqrestore(rwlock_t * lock,unsigned long flags)258 static inline void _write_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
259 {
260 _write_unlock(lock);
261 local_irq_restore(flags);
262 }
263
_rw_is_write_locked(const rwlock_t * lock)264 static inline int _rw_is_write_locked(const rwlock_t *lock)
265 {
266 return (atomic_read(&lock->cnts) & _QW_WMASK) == _QW_LOCKED;
267 }
268
read_lock(rwlock_t * l)269 static always_inline void read_lock(rwlock_t *l)
270 {
271 _read_lock(l);
272 block_lock_speculation();
273 }
274
read_lock_irq(rwlock_t * l)275 static always_inline void read_lock_irq(rwlock_t *l)
276 {
277 _read_lock_irq(l);
278 block_lock_speculation();
279 }
280
281 #define read_lock_irqsave(l, f) \
282 ({ \
283 BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \
284 ((f) = _read_lock_irqsave(l)); \
285 block_lock_speculation(); \
286 })
287
288 #define read_unlock(l) _read_unlock(l)
289 #define read_unlock_irq(l) _read_unlock_irq(l)
290 #define read_unlock_irqrestore(l, f) _read_unlock_irqrestore(l, f)
291 #define read_trylock(l) lock_evaluate_nospec(_read_trylock(l))
292
write_lock(rwlock_t * l)293 static always_inline void write_lock(rwlock_t *l)
294 {
295 _write_lock(l);
296 block_lock_speculation();
297 }
298
write_lock_irq(rwlock_t * l)299 static always_inline void write_lock_irq(rwlock_t *l)
300 {
301 _write_lock_irq(l);
302 block_lock_speculation();
303 }
304
305 #define write_lock_irqsave(l, f) \
306 ({ \
307 BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \
308 ((f) = _write_lock_irqsave(l)); \
309 block_lock_speculation(); \
310 })
311 #define write_trylock(l) lock_evaluate_nospec(_write_trylock(l))
312
313 #define write_unlock(l) _write_unlock(l)
314 #define write_unlock_irq(l) _write_unlock_irq(l)
315 #define write_unlock_irqrestore(l, f) _write_unlock_irqrestore(l, f)
316
317 #define rw_is_locked(l) _rw_is_locked(l)
318 #define rw_is_write_locked(l) _rw_is_write_locked(l)
319 #define rw_is_write_locked_by_me(l) \
320 lock_evaluate_nospec(_is_write_locked_by_me(atomic_read(&(l)->cnts)))
321
322
323 typedef struct percpu_rwlock percpu_rwlock_t;
324
325 struct percpu_rwlock {
326 rwlock_t rwlock;
327 bool writer_activating;
328 #ifndef NDEBUG
329 percpu_rwlock_t **percpu_owner;
330 #endif
331 };
332
333 #ifndef NDEBUG
334 #define PERCPU_RW_LOCK_UNLOCKED(owner) { RW_LOCK_UNLOCKED, 0, owner }
_percpu_rwlock_owner_check(percpu_rwlock_t ** per_cpudata,percpu_rwlock_t * percpu_rwlock)335 static inline void _percpu_rwlock_owner_check(percpu_rwlock_t **per_cpudata,
336 percpu_rwlock_t *percpu_rwlock)
337 {
338 ASSERT(per_cpudata == percpu_rwlock->percpu_owner);
339 }
340 #else
341 #define PERCPU_RW_LOCK_UNLOCKED(owner) { RW_LOCK_UNLOCKED, 0 }
342 #define _percpu_rwlock_owner_check(data, lock) ((void)0)
343 #endif
344
345 #define DEFINE_PERCPU_RWLOCK_RESOURCE(l, owner) \
346 percpu_rwlock_t l = PERCPU_RW_LOCK_UNLOCKED(&get_per_cpu_var(owner))
347 #define percpu_rwlock_resource_init(l, owner) \
348 (*(l) = (percpu_rwlock_t)PERCPU_RW_LOCK_UNLOCKED(&get_per_cpu_var(owner)))
349
_percpu_read_lock(percpu_rwlock_t ** per_cpudata,percpu_rwlock_t * percpu_rwlock)350 static always_inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata,
351 percpu_rwlock_t *percpu_rwlock)
352 {
353 /* Validate the correct per_cpudata variable has been provided. */
354 _percpu_rwlock_owner_check(per_cpudata, percpu_rwlock);
355
356 /* We cannot support recursion on the same lock. */
357 ASSERT(this_cpu_ptr(per_cpudata) != percpu_rwlock);
358 /*
359 * Detect using a second percpu_rwlock_t simulatenously and fallback
360 * to standard read_lock.
361 */
362 if ( unlikely(this_cpu_ptr(per_cpudata) != NULL ) )
363 {
364 read_lock(&percpu_rwlock->rwlock);
365 return;
366 }
367
368 /* Indicate this cpu is reading. */
369 preempt_disable();
370 this_cpu_ptr(per_cpudata) = percpu_rwlock;
371 smp_mb();
372 /* Check if a writer is waiting. */
373 if ( unlikely(percpu_rwlock->writer_activating) )
374 {
375 /* Let the waiting writer know we aren't holding the lock. */
376 this_cpu_ptr(per_cpudata) = NULL;
377 /* Wait using the read lock to keep the lock fair. */
378 read_lock(&percpu_rwlock->rwlock);
379 /* Set the per CPU data again and continue. */
380 this_cpu_ptr(per_cpudata) = percpu_rwlock;
381 /* Drop the read lock because we don't need it anymore. */
382 read_unlock(&percpu_rwlock->rwlock);
383 }
384 else
385 {
386 /* Other branch already has a speculation barrier in read_lock(). */
387 block_lock_speculation();
388 /* All other paths have implicit check_lock() calls via read_lock(). */
389 check_lock(&percpu_rwlock->rwlock.lock.debug, false);
390 }
391
392 lock_enter(&percpu_rwlock->rwlock.lock.debug);
393 }
394
_percpu_read_unlock(percpu_rwlock_t ** per_cpudata,percpu_rwlock_t * percpu_rwlock)395 static inline void _percpu_read_unlock(percpu_rwlock_t **per_cpudata,
396 percpu_rwlock_t *percpu_rwlock)
397 {
398 /* Validate the correct per_cpudata variable has been provided. */
399 _percpu_rwlock_owner_check(per_cpudata, percpu_rwlock);
400
401 /* Verify the read lock was taken for this lock */
402 ASSERT(this_cpu_ptr(per_cpudata) != NULL);
403
404 lock_exit(&percpu_rwlock->rwlock.lock.debug);
405
406 /*
407 * Detect using a second percpu_rwlock_t simulatenously and fallback
408 * to standard read_unlock.
409 */
410 if ( unlikely(this_cpu_ptr(per_cpudata) != percpu_rwlock ) )
411 {
412 read_unlock(&percpu_rwlock->rwlock);
413 return;
414 }
415 this_cpu_ptr(per_cpudata) = NULL;
416 smp_wmb();
417 preempt_enable();
418 }
419
420 /* Don't inline percpu write lock as it's a complex function. */
421 void _percpu_write_lock(percpu_rwlock_t **per_cpudata,
422 percpu_rwlock_t *percpu_rwlock);
423
_percpu_write_unlock(percpu_rwlock_t ** per_cpudata,percpu_rwlock_t * percpu_rwlock)424 static inline void _percpu_write_unlock(percpu_rwlock_t **per_cpudata,
425 percpu_rwlock_t *percpu_rwlock)
426 {
427 /* Validate the correct per_cpudata variable has been provided. */
428 _percpu_rwlock_owner_check(per_cpudata, percpu_rwlock);
429
430 ASSERT(percpu_rwlock->writer_activating);
431 percpu_rwlock->writer_activating = 0;
432
433 lock_exit(&percpu_rwlock->rwlock.lock.debug);
434
435 write_unlock(&percpu_rwlock->rwlock);
436 }
437
438 #define percpu_rw_is_write_locked(l) _rw_is_write_locked(&((l)->rwlock))
439
440 #define percpu_read_lock(percpu, lock) \
441 _percpu_read_lock(&get_per_cpu_var(percpu), lock)
442 #define percpu_read_unlock(percpu, lock) \
443 _percpu_read_unlock(&get_per_cpu_var(percpu), lock)
444
445 #define percpu_write_lock(percpu, lock) \
446 ({ \
447 _percpu_write_lock(&get_per_cpu_var(percpu), lock); \
448 block_lock_speculation(); \
449 })
450 #define percpu_write_unlock(percpu, lock) \
451 _percpu_write_unlock(&get_per_cpu_var(percpu), lock)
452
453 #define DEFINE_PERCPU_RWLOCK_GLOBAL(name) DEFINE_PER_CPU(percpu_rwlock_t *, \
454 name)
455 #define DECLARE_PERCPU_RWLOCK_GLOBAL(name) DECLARE_PER_CPU(percpu_rwlock_t *, \
456 name)
457
458 #endif /* __RWLOCK_H__ */
459