VMM: Dynamically retrieve the interrupt vector for a virtio device.
[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  * Spinlocks and Spin-PDR locks (preemption detection/recovery)
6  *
7  * This file is part of Parlib.
8  * 
9  * Parlib is free software: you can redistribute it and/or modify
10  * it under the terms of the Lesser GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  * 
14  * Parlib is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * Lesser GNU General Public License for more details.
18  * 
19  * See COPYING.LESSER for details on the GNU Lesser General Public License.
20  * See COPYING for details on the GNU General Public License. */
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <parlib/assert.h>
25
26 #include <parlib/spinlock.h>
27 #include <parlib/vcore.h>
28 #include <parlib/uthread.h>
29
30 void spinlock_init(spinlock_t *lock)
31 {
32         lock->lock = 0;
33 }
34
35 int spinlock_trylock(spinlock_t *lock) 
36 {
37         if (lock->lock)
38                 return EBUSY;
39         return __sync_lock_test_and_set(&lock->lock, EBUSY);
40 }
41
42 void spinlock_lock(spinlock_t *lock) 
43 {
44         while (spinlock_trylock(lock))
45                 cpu_relax();
46 }
47
48 void spinlock_unlock(spinlock_t *lock) 
49 {
50         __sync_lock_release(&lock->lock, 0);
51 }
52
53 bool spinlock_locked(spinlock_t *lock)
54 {
55         return lock->lock != 0;
56 }
57
58 /* Two different versions, with and without CAS.  Default is with CAS. */
59 #ifndef CONFIG_SPINPDR_NO_CAS /* CAS version */
60
61 /* Spin-PRD locks (preemption detection/recovery).  Idea is to CAS and put the
62  * lockholder's vcoreid in the lock, and all spinners ensure that vcore runs. */
63 void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
64 {
65         pdr_lock->lock = SPINPDR_UNLOCKED;
66 }
67
68 /* Internal version of the locking func, doesn't care if notifs are disabled */
69 void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
70 {
71         uint32_t vcoreid = vcore_id();
72         uint32_t lock_val;
73         do {
74                 while ((lock_val = pdr_lock->lock) != SPINPDR_UNLOCKED) {
75                         ensure_vcore_runs(lock_val);
76                         cmb();
77                 }
78         } while (!atomic_cas_u32(&pdr_lock->lock, lock_val, vcoreid));
79         cmb();  /* just need a cmb, the CAS handles the CPU wmb/wrmb() */
80 }
81
82 void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
83 {
84         /* could make an arch-dependent 'release barrier' out of these */
85         wmb();  /* Need to prevent the compiler from reordering older stores. */
86         rwmb(); /* And no old reads passing either.   x86 makes both mbs a cmb() */
87         pdr_lock->lock = SPINPDR_UNLOCKED;
88 }
89
90 bool spin_pdr_locked(struct spin_pdr_lock *pdr_lock)
91 {
92         return pdr_lock->lock != SPINPDR_UNLOCKED;
93 }
94
95 #else /* NON-CAS version */
96
97 /* Using regular spinlocks, with SPINPDR_VCOREID_UNKNOWN (-1) meaning 'no
98  * lockholder advertised yet'.  There are two spots where the lockholder still
99  * holds the lock but hasn't advertised its vcoreid, and in those cases we
100  * ensure all vcores aren't preempted (linear scan). */
101 void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
102 {
103         spinlock_init(&pdr_lock->spinlock);
104         pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
105 }
106
107 void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
108 {
109         uint32_t vcoreid = vcore_id();
110         uint32_t ensure_tgt;
111         while (spinlock_trylock(&pdr_lock->spinlock)) {
112                 ensure_tgt = pdr_lock->lockholder;
113                 /* ensure will make sure *every* vcore runs if you pass it your self. */
114                 if (ensure_tgt == SPINPDR_VCOREID_UNKNOWN)
115                         ensure_tgt = vcoreid;
116                 ensure_vcore_runs(ensure_tgt);
117                 cpu_relax();
118         }
119         pdr_lock->lockholder = vcoreid;
120 }
121
122 void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
123 {
124         pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
125         spinlock_unlock(&pdr_lock->spinlock);
126 }
127
128 bool spin_pdr_locked(struct spin_pdr_lock *pdr_lock)
129 {
130         return spinlock_locked(&pdr_lock->spinlock);
131 }
132
133 #endif /* CONFIG_SPINPDR_NO_CAS */
134
135 void spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
136 {
137         /* Disable notifs, if we're an _M uthread */
138         uth_disable_notifs();
139         __spin_pdr_lock(pdr_lock);
140 }
141
142 void spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
143 {
144         __spin_pdr_unlock(pdr_lock);
145         /* Enable notifs, if we're an _M uthread */
146         uth_enable_notifs();
147 }