Fix the license on certain Parlib files
[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->lock = 0;
18 }
19
20 int spinlock_trylock(spinlock_t *lock) 
21 {
22         if (lock->lock)
23                 return EBUSY;
24         return __sync_lock_test_and_set(&lock->lock, EBUSY);
25 }
26
27 void spinlock_lock(spinlock_t *lock) 
28 {
29         while (spinlock_trylock(lock))
30                 cpu_relax();
31 }
32
33 void spinlock_unlock(spinlock_t *lock) 
34 {
35         __sync_lock_release(&lock->lock, 0);
36 }
37
38 bool spinlock_locked(spinlock_t *lock)
39 {
40         return lock->lock != 0;
41 }
42
43 /* Two different versions, with and without CAS.  Default is with CAS. */
44 #ifndef CONFIG_SPINPDR_NO_CAS /* CAS version */
45
46 /* Spin-PRD locks (preemption detection/recovery).  Idea is to CAS and put the
47  * lockholder's vcoreid in the lock, and all spinners ensure that vcore runs. */
48 void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
49 {
50         pdr_lock->lock = SPINPDR_UNLOCKED;
51 }
52
53 /* Internal version of the locking func, doesn't care if notifs are disabled */
54 void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
55 {
56         uint32_t vcoreid = vcore_id();
57         uint32_t lock_val;
58         do {
59                 while ((lock_val = pdr_lock->lock) != SPINPDR_UNLOCKED) {
60                         ensure_vcore_runs(lock_val);
61                         cmb();
62                 }
63         } while (!atomic_cas_u32(&pdr_lock->lock, lock_val, vcoreid));
64         cmb();  /* just need a cmb, the CAS handles the CPU wmb/wrmb() */
65 }
66
67 void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
68 {
69         /* could make an arch-dependent 'release barrier' out of these */
70         wmb();  /* Need to prevent the compiler from reordering older stores. */
71         rwmb(); /* And no old reads passing either.   x86 makes both mbs a cmb() */
72         pdr_lock->lock = SPINPDR_UNLOCKED;
73 }
74
75 bool spin_pdr_locked(struct spin_pdr_lock *pdr_lock)
76 {
77         return pdr_lock->lock != SPINPDR_UNLOCKED;
78 }
79
80 #else /* NON-CAS version */
81
82 /* Using regular spinlocks, with SPINPDR_VCOREID_UNKNOWN (-1) meaning 'no
83  * lockholder advertised yet'.  There are two spots where the lockholder still
84  * holds the lock but hasn't advertised its vcoreid, and in those cases we
85  * ensure all vcores aren't preempted (linear scan). */
86 void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
87 {
88         spinlock_init(&pdr_lock->spinlock);
89         pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
90 }
91
92 void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
93 {
94         uint32_t vcoreid = vcore_id();
95         uint32_t ensure_tgt;
96         while (spinlock_trylock(&pdr_lock->spinlock)) {
97                 ensure_tgt = pdr_lock->lockholder;
98                 /* ensure will make sure *every* vcore runs if you pass it your self. */
99                 if (ensure_tgt == SPINPDR_VCOREID_UNKNOWN)
100                         ensure_tgt = vcoreid;
101                 ensure_vcore_runs(ensure_tgt);
102                 cpu_relax();
103         }
104         pdr_lock->lockholder = vcoreid;
105 }
106
107 void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
108 {
109         pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
110         spinlock_unlock(&pdr_lock->spinlock);
111 }
112
113 bool spin_pdr_locked(struct spin_pdr_lock *pdr_lock)
114 {
115         return spinlock_locked(&pdr_lock->spinlock);
116 }
117
118 #endif /* CONFIG_SPINPDR_NO_CAS */
119
120 void spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
121 {
122         /* Disable notifs, if we're an _M uthread */
123         uth_disable_notifs();
124         __spin_pdr_lock(pdr_lock);
125 }
126
127 void spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
128 {
129         __spin_pdr_unlock(pdr_lock);
130         /* Enable notifs, if we're an _M uthread */
131         uth_enable_notifs();
132 }