kernel - Introduce hard code sections, simplify critical sections & mplocks
[dragonfly.git] / sys / sys / mplock2.h
1 /*
2  * SYS/MPLOCK2.H
3  *
4  * Implement the MP lock.  Note that debug operations
5  */
6 #ifndef _SYS_MPLOCK2_H_
7 #define _SYS_MPLOCK2_H_
8
9 #ifndef _MACHINE_ATOMIC_H_
10 #include <machine/atomic.h>
11 #endif
12 #ifndef _SYS_THREAD_H_
13 #include <sys/thread.h>
14 #endif
15 #ifndef _SYS_GLOBALDATA_H_
16 #include <sys/globaldata.h>
17 #endif
18
19 #ifdef SMP
20
21 #define get_mplock()            get_mplock_debug(__FILE__, __LINE__)
22 #define try_mplock()            try_mplock_debug(__FILE__, __LINE__)
23 #define cpu_try_mplock()        cpu_try_mplock_debug(__FILE__, __LINE__)
24
25 void _get_mplock_predisposed(const char *file, int line);
26 void _get_mplock_contested(const char *file, int line);
27 void _try_mplock_contested(const char *file, int line);
28 void _cpu_try_mplock_contested(const char *file, int line);
29 void _rel_mplock_contested(void);
30 void cpu_get_initial_mplock(void);
31 void handle_cpu_contention_mask(void);
32 void yield_mplock(struct thread *td);
33
34 extern int mp_lock;
35 extern int cpu_contention_mask;
36 extern const char *mp_lock_holder_file;
37 extern int mp_lock_holder_line;
38
39 /*
40  * Acquire the MP lock, block until we get it.
41  *
42  * In order to acquire the MP lock we must first pre-dispose td_mpcount
43  * for the acquisition and then get the actual lock.
44  *
45  * The mplock must check a number of conditions and it is better to
46  * leave it to a procedure if we cannot get it trivially.
47  */
48 static __inline
49 void
50 get_mplock_debug(const char *file, int line)
51 {
52         globaldata_t gd = mycpu;
53         thread_t td = gd->gd_curthread;
54
55         ++td->td_mpcount;
56         if (mp_lock != gd->gd_cpuid)
57                 _get_mplock_predisposed(file, line);
58 }
59
60 /*
61  * Release the MP lock
62  *
63  * In order to release the MP lock we must first pre-dispose td_mpcount
64  * for the release and then, if it is 0, release the actual lock.
65  *
66  * The contested function is called only if we are unable to release the
67  * Actual lock.  This can occur if we raced an interrupt after decrementing
68  * td_mpcount to 0 and the interrupt acquired and released the lock.
69  *
70  * The function also catches the td_mpcount underflow case because the
71  * lock will be in a released state and thus fail the subsequent release.
72  */
73 static __inline
74 void
75 rel_mplock(void)
76 {
77         globaldata_t gd = mycpu;
78         thread_t td = gd->gd_curthread;
79         int n;
80
81         n = --td->td_mpcount;
82         if (n <= 0 && atomic_cmpset_int(&mp_lock, gd->gd_cpuid, -1) == 0)
83                 _rel_mplock_contested();
84 }
85
86 /*
87  * Attempt to acquire the MP lock, returning 0 on failure and 1 on success.
88  *
89  * The contested function is called on failure and typically serves simply
90  * to log the attempt (if debugging enabled).
91  */
92 static __inline
93 int
94 try_mplock_debug(const char *file, int line)
95 {
96         globaldata_t gd = mycpu;
97         thread_t td = gd->gd_curthread;
98
99         ++td->td_mpcount;
100         if (mp_lock != gd->gd_cpuid &&
101             atomic_cmpset_int(&mp_lock, -1, gd->gd_cpuid) == 0) {
102                 _try_mplock_contested(file, line);
103                 return(0);
104         }
105 #ifdef INVARIANTS
106         mp_lock_holder_file = file;
107         mp_lock_holder_line = line;
108 #endif
109         return(1);
110 }
111
112 /*
113  * Low level acquisition of the MP lock ignoring curthred->td_mpcount
114  *
115  * This version of try_mplock() is used when the caller has already
116  * predisposed td->td_mpcount.
117  *
118  * Returns non-zero on success, 0 on failure.
119  *
120  * WARNING: Must be called from within a critical section if td_mpcount is
121  *          zero, otherwise an itnerrupt race can cause the lock to be lost.
122  */
123 static __inline
124 int
125 cpu_try_mplock_debug(const char *file, int line)
126 {
127         globaldata_t gd = mycpu;
128
129         if (mp_lock != gd->gd_cpuid &&
130             atomic_cmpset_int(&mp_lock, -1, gd->gd_cpuid) == 0) {
131                 _cpu_try_mplock_contested(file, line);
132                 return(0);
133         }
134 #ifdef INVARIANTS
135         mp_lock_holder_file = file;
136         mp_lock_holder_line = line;
137 #endif
138         return(1);
139 }
140
141 /*
142  * A cpu wanted the MP lock but could not get it.  This function is also
143  * called directly from the LWKT scheduler.
144  *
145  * Reentrant, may be called even if the cpu is already contending the MP
146  * lock.
147  */
148 static __inline
149 void
150 set_cpu_contention_mask(globaldata_t gd)
151 {
152         atomic_set_int(&cpu_contention_mask, gd->gd_cpumask);
153 }
154
155 /*
156  * A cpu is no longer contending for the MP lock after previously contending
157  * for it.
158  *
159  * Reentrant, may be called even if the cpu was not previously contending
160  * the MP lock.
161  */
162 static __inline
163 void
164 clr_cpu_contention_mask(globaldata_t gd)
165 {
166         atomic_clear_int(&cpu_contention_mask, gd->gd_cpumask);
167 }
168
169 static __inline
170 int
171 owner_mplock(void)
172 {
173         return (mp_lock);
174 }
175
176 /*
177  * Low level release of the MP lock ignoring curthread->td_mpcount
178  *
179  * WARNING: Caller must be in a critical section, otherwise the
180  *          mp_lock can be lost from an interrupt race and we would
181  *          end up clearing someone else's lock.
182  */
183 static __inline void
184 cpu_rel_mplock(int cpu)
185 {
186         (void)atomic_cmpset_int(&mp_lock, cpu, -1);
187 }
188
189 #define MP_LOCK_HELD(gd)                        \
190         (mp_lock == gd->gd_cpuid)
191
192 #define ASSERT_MP_LOCK_HELD(td)                 \
193         KASSERT(MP_LOCK_HELD(td->td_gd),        \
194                 ("MP_LOCK_HELD: Not held thread %p", td))
195
196 #else
197
198 /*
199  * UNI-PROCESSOR BUILD - Degenerate case macros
200  */
201 #define get_mplock()
202 #define rel_mplock()
203 #define try_mplock()            1
204 #define owner_mplock()          0
205 #define MP_LOCK_HELD(gd)        (!0)
206 #define ASSERT_MP_LOCK_HELD(td)
207
208 #endif
209
210 #endif