Add spin_pdr_trylock
[akaros.git] / user / parlib / spinlock.c
1 /* Copyright (c) 2013 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * Kevin Klues <klueska@cs.berkeley.edu>
4  *
5  * See LICENSE for details. */
6
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <parlib/assert.h>
10
11 #include <parlib/spinlock.h>
12 #include <parlib/vcore.h>
13 #include <parlib/uthread.h>
14
15 void spinlock_init(spinlock_t *lock)
16 {
17         lock->locked = FALSE;
18 }
19
20 /* Returns TRUE if we grabbed the lock */
21 bool spinlock_trylock(spinlock_t *lock)
22 {
23         if (lock->locked)
24                 return FALSE;
25         return !__sync_lock_test_and_set(&lock->locked, TRUE);
26 }
27
28 void spinlock_lock(spinlock_t *lock) 
29 {
30         while (!spinlock_trylock(lock))
31                 cpu_relax();
32 }
33
34 void spinlock_unlock(spinlock_t *lock) 
35 {
36         __sync_lock_release(&lock->locked, FALSE);
37 }
38
39 bool spinlock_locked(spinlock_t *lock)
40 {
41         return lock->locked;
42 }
43
44 /* Spin-PRD locks (preemption detection/recovery).  Idea is to CAS and put the
45  * lockholder's vcoreid in the lock, and all spinners ensure that vcore runs. */
46 void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
47 {
48         pdr_lock->lock = SPINPDR_UNLOCKED;
49 }
50
51 /* Internal version of the locking func, doesn't care if notifs are disabled */
52 void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
53 {
54         uint32_t vcoreid = vcore_id();
55         uint32_t lock_val;
56         do {
57                 while ((lock_val = pdr_lock->lock) != SPINPDR_UNLOCKED) {
58                         ensure_vcore_runs(lock_val);
59                         cmb();
60                 }
61         } while (!atomic_cas_u32(&pdr_lock->lock, lock_val, vcoreid));
62         cmb();  /* just need a cmb, the CAS handles the CPU wmb/wrmb() */
63 }
64
65 void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
66 {
67         /* could make an arch-dependent 'release barrier' out of these */
68         wmb();  /* Need to prevent the compiler from reordering older stores. */
69         rwmb(); /* And no old reads passing either.   x86 makes both mbs a cmb() */
70         pdr_lock->lock = SPINPDR_UNLOCKED;
71 }
72
73 bool spin_pdr_locked(struct spin_pdr_lock *pdr_lock)
74 {
75         return pdr_lock->lock != SPINPDR_UNLOCKED;
76 }
77
78 void spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
79 {
80         /* Disable notifs, if we're an _M uthread */
81         uth_disable_notifs();
82         __spin_pdr_lock(pdr_lock);
83 }
84
85 void spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
86 {
87         __spin_pdr_unlock(pdr_lock);
88         /* Enable notifs, if we're an _M uthread */
89         uth_enable_notifs();
90 }
91
92 bool spin_pdr_trylock(struct spin_pdr_lock *pdr_lock)
93 {
94         uint32_t lock_val;
95
96         uth_disable_notifs();
97         lock_val = ACCESS_ONCE(pdr_lock->lock);
98         if (lock_val != SPINPDR_UNLOCKED) {
99                 uth_enable_notifs();
100                 return FALSE;
101         }
102         if (atomic_cas_u32(&pdr_lock->lock, lock_val, vcore_id())) {
103                 return TRUE;
104         } else {
105                 uth_enable_notifs();
106                 return FALSE;
107         }
108 }