vmm: refactor userspace's emsr_fakewrite()
[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         /* See glibc-2.19-akaros/sysdeps/akaros/lowlevellock.h for details. */
49         parlib_static_assert(sizeof(struct spin_pdr_lock) == sizeof(int));
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(); /* 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 void spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
81 {
82         /* Disable notifs, if we're an _M uthread */
83         uth_disable_notifs();
84         __spin_pdr_lock(pdr_lock);
85 }
86
87 void spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
88 {
89         __spin_pdr_unlock(pdr_lock);
90         /* Enable notifs, if we're an _M uthread */
91         uth_enable_notifs();
92 }
93
94 bool spin_pdr_trylock(struct spin_pdr_lock *pdr_lock)
95 {
96         uint32_t lock_val;
97
98         uth_disable_notifs();
99         lock_val = ACCESS_ONCE(pdr_lock->lock);
100         if (lock_val != SPINPDR_UNLOCKED) {
101                 uth_enable_notifs();
102                 return FALSE;
103         }
104         if (atomic_cas_u32(&pdr_lock->lock, lock_val, vcore_id())) {
105                 return TRUE;
106         } else {
107                 uth_enable_notifs();
108                 return FALSE;
109         }
110 }