VMM: Sync halting GPCs and interrupt injection
[akaros.git] / tests / linux-lock-hacks.h
1 /* basic locking code that compiles on linux.  #included directly into
2  * lock_test.  It's a .h so that make tests doesn't build it. */
3
4 #define ARCH_CL_SIZE 64
5 #define SPINLOCK_INITIALIZER {0}
6
7 typedef struct {
8         int lock;
9 } spinlock_t;
10
11 void __attribute__((noinline)) spinlock_init(spinlock_t *lock)
12 {
13         lock->lock = 0;
14 }
15
16 int __attribute__((noinline)) spinlock_trylock(spinlock_t *lock) 
17 {
18         if (lock->lock)
19                 return EBUSY;
20         return __sync_lock_test_and_set(&lock->lock, EBUSY);
21 }
22
23 void __attribute__((noinline)) spinlock_lock(spinlock_t *lock) 
24 {
25         while (spinlock_trylock(lock))
26                 cpu_relax();
27 }
28
29 void __attribute__((noinline)) spinlock_unlock(spinlock_t *lock) 
30 {
31         __sync_lock_release(&lock->lock, 0);
32 }
33
34 #define MCS_LOCK_INIT {0}
35 #define MCS_QNODE_INIT {0, 0}
36
37 typedef struct mcs_lock_qnode
38 {
39         struct mcs_lock_qnode *next;
40         int locked;
41 }__attribute__((aligned(ARCH_CL_SIZE))) mcs_lock_qnode_t;
42
43 /* included for the dummy init in lock_thread */
44 struct mcs_pdro_qnode
45 {
46         struct mcs_pdro_qnode *next;
47         int locked;
48         uint32_t vcoreid;
49 }__attribute__((aligned(ARCH_CL_SIZE)));
50
51 #define MCSPDRO_QNODE_INIT {0, 0, 0}
52 #define mcs_pdr_init(args...) {}
53
54 typedef struct mcs_lock
55 {
56         mcs_lock_qnode_t* lock;
57 } mcs_lock_t;
58
59 void __attribute__((noinline)) mcs_lock_init(struct mcs_lock *lock)
60 {
61         memset(lock, 0, sizeof(mcs_lock_t));
62 }
63
64 static inline mcs_lock_qnode_t *mcs_qnode_swap(mcs_lock_qnode_t **addr,
65                                                mcs_lock_qnode_t *val)
66 {
67         return (mcs_lock_qnode_t*) __sync_lock_test_and_set((void**)addr, val);
68 }
69
70 void __attribute__((noinline))
71 mcs_lock_lock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
72 {
73         qnode->next = 0;
74         cmb();  /* swap provides a CPU mb() */
75         mcs_lock_qnode_t *predecessor = mcs_qnode_swap(&lock->lock, qnode);
76         if (predecessor) {
77                 qnode->locked = 1;
78                 wmb();
79                 predecessor->next = qnode;
80                 /* no need for a wrmb(), since this will only get unlocked after they
81                  * read our previous write */
82                 while (qnode->locked)
83                         cpu_relax();
84         }
85         cmb();  /* just need a cmb, the swap handles the CPU wmb/wrmb() */
86 }
87
88 void __attribute__((noinline))
89 mcs_lock_unlock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
90 {
91         /* Check if someone is already waiting on us to unlock */
92         if (qnode->next == 0) {
93                 cmb();  /* no need for CPU mbs, since there's an atomic_swap() */
94                 /* Unlock it */
95                 mcs_lock_qnode_t *old_tail = mcs_qnode_swap(&lock->lock,0);
96                 /* no one else was already waiting, so we successfully unlocked and can
97                  * return */
98                 if (old_tail == qnode)
99                         return;
100                 /* someone else was already waiting on the lock (last one on the list),
101                  * and we accidentally took them off.  Try and put it back. */
102                 mcs_lock_qnode_t *usurper = mcs_qnode_swap(&lock->lock,old_tail);
103                 /* since someone else was waiting, they should have made themselves our
104                  * next.  spin (very briefly!) til it happens. */
105                 while (qnode->next == 0)
106                         cpu_relax();
107                 if (usurper) {
108                         /* an usurper is someone who snuck in before we could put the old
109                          * tail back.  They now have the lock.  Let's put whoever is
110                          * supposed to be next as their next one. */
111                         usurper->next = qnode->next;
112                 } else {
113                         /* No usurper meant we put things back correctly, so we should just
114                          * pass the lock / unlock whoever is next */
115                         qnode->next->locked = 0;
116                 }
117         } else {
118                 /* mb()s necessary since we didn't call an atomic_swap() */
119                 wmb();  /* need to make sure any previous writes don't pass unlocking */
120                 rwmb(); /* need to make sure any reads happen before the unlocking */
121                 /* simply unlock whoever is next */
122                 qnode->next->locked = 0;
123         }
124 }
125
126 /* CAS style mcs locks, kept around til we use them.  We're using the
127  * usurper-style, since RISCV doesn't have a real CAS (yet?). */
128 void __attribute__((noinline))
129 mcs_lock_unlock_cas(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
130 {
131         /* Check if someone is already waiting on us to unlock */
132         if (qnode->next == 0) {
133                 cmb();  /* no need for CPU mbs, since there's an atomic_cas() */
134                 /* If we're still the lock, just swap it with 0 (unlock) and return */
135                 if (__sync_bool_compare_and_swap((void**)&lock->lock, qnode, 0))
136                         return;
137                 /* We failed, someone is there and we are some (maybe a different)
138                  * thread's pred.  Since someone else was waiting, they should have made
139                  * themselves our next.  Spin (very briefly!) til it happens. */
140                 while (qnode->next == 0)
141                         cpu_relax();
142                 /* Alpha wants a read_barrier_depends() here */
143                 /* Now that we have a next, unlock them */
144                 qnode->next->locked = 0;
145         } else {
146                 /* mb()s necessary since we didn't call an atomic_swap() */
147                 wmb();  /* need to make sure any previous writes don't pass unlocking */
148                 rwmb(); /* need to make sure any reads happen before the unlocking */
149                 /* simply unlock whoever is next */
150                 qnode->next->locked = 0;
151         }
152 }