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