kernel - Force NFSv3 for diskless nfs mount
[dragonfly.git] / sys / kern / kern_mplock.c
blob018df7832f6c3e4966534d28da1ecb3ea82c5735
1 /*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
36 * Helper functions for MP lock acquisition and release.
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/proc.h>
43 #include <sys/rtprio.h>
44 #include <sys/queue.h>
45 #include <sys/sysctl.h>
46 #include <sys/kthread.h>
47 #include <machine/cpu.h>
48 #include <sys/lock.h>
49 #include <sys/caps.h>
50 #include <sys/spinlock.h>
51 #include <sys/ktr.h>
53 #include <sys/thread2.h>
54 #include <sys/mplock2.h>
55 #include <sys/spinlock2.h>
57 #ifdef SMP
58 static int chain_mplock = 0;
59 static int bgl_yield = 10;
60 static __int64_t mplock_contention_count = 0;
62 SYSCTL_INT(_lwkt, OID_AUTO, chain_mplock, CTLFLAG_RW, &chain_mplock, 0, "");
63 SYSCTL_INT(_lwkt, OID_AUTO, bgl_yield_delay, CTLFLAG_RW, &bgl_yield, 0, "");
64 SYSCTL_QUAD(_lwkt, OID_AUTO, mplock_contention_count, CTLFLAG_RW,
65 &mplock_contention_count, 0, "spinning due to MPLOCK contention");
68 * Kernel Trace
70 #if !defined(KTR_GIANT_CONTENTION)
71 #define KTR_GIANT_CONTENTION KTR_ALL
72 #endif
74 KTR_INFO_MASTER(giant);
75 KTR_INFO(KTR_GIANT_CONTENTION, giant, beg, 0,
76 "thread=%p held %s:%-5d want %s:%-5d",
77 sizeof(void *) * 3 + sizeof(int) * 2);
78 KTR_INFO(KTR_GIANT_CONTENTION, giant, end, 1,
79 "thread=%p held %s:%-5d want %s:%-5d",
80 sizeof(void *) * 3 + sizeof(int) * 2);
82 #define loggiant(name) \
83 KTR_LOG(giant_ ## name, curthread, \
84 mp_lock_holder_file, mp_lock_holder_line, \
85 file, line)
87 int mp_lock;
88 int cpu_contention_mask;
89 const char *mp_lock_holder_file; /* debugging */
90 int mp_lock_holder_line; /* debugging */
93 * Sets up the initial MP lock state near the start of the kernel boot
95 void
96 cpu_get_initial_mplock(void)
98 mp_lock = 0; /* cpu 0 */
99 curthread->td_mpcount = 1;
103 * This code is called from the get_mplock() inline when the mplock
104 * is not already held. td_mpcount has already been predisposed
105 * (incremented).
107 void
108 _get_mplock_predisposed(const char *file, int line)
110 globaldata_t gd = mycpu;
112 if (gd->gd_intr_nesting_level) {
113 panic("Attempt to acquire mplock not already held "
114 "in hard section, ipi or interrupt %s:%d",
115 file, line);
117 if (atomic_cmpset_int(&mp_lock, -1, gd->gd_cpuid) == 0)
118 _get_mplock_contested(file, line);
119 #ifdef INVARIANTS
120 mp_lock_holder_file = file;
121 mp_lock_holder_line = line;
122 #endif
126 * Called when the MP lock could not be trvially acquired. The caller
127 * has already bumped td_mpcount.
129 void
130 _get_mplock_contested(const char *file, int line)
132 globaldata_t gd = mycpu;
133 int ov;
134 int nv;
135 const void **stkframe = (const void **)&file;
137 ++mplock_contention_count;
138 for (;;) {
139 ov = mp_lock;
140 nv = gd->gd_cpuid;
141 if (ov == gd->gd_cpuid)
142 break;
143 if (ov == -1) {
144 if (atomic_cmpset_int(&mp_lock, ov, gd->gd_cpuid))
145 break;
146 } else {
147 gd->gd_curthread->td_mplock_stallpc = stkframe[-1];
148 loggiant(beg);
149 lwkt_switch();
150 loggiant(end);
151 KKASSERT(gd->gd_cpuid == mp_lock);
152 break;
158 * Called if td_mpcount went negative or if td_mpcount + td_xpcount is 0
159 * and we were unable to release the MP lock. Handles sanity checks
160 * and conflicts.
162 * It is possible for the inline release to have raced an interrupt which
163 * get/rel'd the MP lock, causing the inline's cmpset to fail. If this
164 * case occurs mp_lock will either already be in a released state or it
165 * will have already been acquired by another cpu.
167 void
168 _rel_mplock_contested(void)
170 globaldata_t gd = mycpu;
171 thread_t td = gd->gd_curthread;
172 int ov;
174 KKASSERT(td->td_mpcount >= 0);
175 if (td->td_mpcount + td->td_xpcount == 0) {
176 for (;;) {
177 ov = mp_lock;
178 if (ov != gd->gd_cpuid)
179 break;
180 if (atomic_cmpset_int(&mp_lock, ov, -1))
181 break;
187 * Called when try_mplock() fails.
189 * The inline bumped td_mpcount so we have to undo it.
191 * It is possible to race an interrupt which acquired and released the
192 * MP lock. When combined with the td_mpcount decrement we do the MP lock
193 * can wind up in any state and possibly not even owned by us.
195 * It is also possible for this function to be called even if td_mpcount > 1
196 * if someone bumped it and raced an interrupt which then called try_mpock().
198 void
199 _try_mplock_contested(const char *file, int line)
201 globaldata_t gd = mycpu;
202 thread_t td = gd->gd_curthread;
203 int ov;
205 --td->td_mpcount;
206 KKASSERT(td->td_mpcount >= 0);
207 ++mplock_contention_count;
209 if (td->td_mpcount + td->td_xpcount == 0) {
210 for (;;) {
211 ov = mp_lock;
212 if (ov != gd->gd_cpuid)
213 break;
214 if (atomic_cmpset_int(&mp_lock, ov, -1))
215 break;
221 * Called when cpu_try_mplock() fails.
223 * The inline did not touch td_mpcount so we do not either.
225 void
226 _cpu_try_mplock_contested(const char *file, int line)
228 ++mplock_contention_count;
232 * Temporarily yield the MP lock. This is part of lwkt_user_yield()
233 * which is kinda hackish. The MP lock cannot be yielded if inherited
234 * due to a preemption.
236 void
237 yield_mplock(thread_t td)
239 int savecnt;
241 if (td->td_xpcount == 0) {
242 savecnt = td->td_mpcount;
243 td->td_mpcount = 1;
244 rel_mplock();
245 DELAY(bgl_yield);
246 get_mplock();
247 td->td_mpcount = savecnt;
251 #if 0
254 * The rel_mplock() code will call this function after releasing the
255 * last reference on the MP lock if cpu_contention_mask is non-zero.
257 * We then chain an IPI to a single other cpu potentially needing the
258 * lock. This is a bit heuristical and we can wind up with IPIs flying
259 * all over the place.
261 static void lwkt_mp_lock_uncontested_remote(void *arg __unused);
263 void
264 lwkt_mp_lock_uncontested(void)
266 globaldata_t gd;
267 globaldata_t dgd;
268 cpumask_t mask;
269 cpumask_t tmpmask;
270 int cpuid;
272 if (chain_mplock) {
273 gd = mycpu;
274 clr_mplock_contention_mask(gd);
275 mask = cpu_contention_mask;
276 tmpmask = ~((1 << gd->gd_cpuid) - 1);
278 if (mask) {
279 if (mask & tmpmask)
280 cpuid = bsfl(mask & tmpmask);
281 else
282 cpuid = bsfl(mask);
283 atomic_clear_int(&cpu_contention_mask, 1 << cpuid);
284 dgd = globaldata_find(cpuid);
285 lwkt_send_ipiq(dgd, lwkt_mp_lock_uncontested_remote, NULL);
291 * The idea is for this IPI to interrupt a potentially lower priority
292 * thread, such as a user thread, to allow the scheduler to reschedule
293 * a higher priority kernel thread that needs the MP lock.
295 * For now we set the LWKT reschedule flag which generates an AST in
296 * doreti, though theoretically it is also possible to possibly preempt
297 * here if the underlying thread was operating in user mode. Nah.
299 static void
300 lwkt_mp_lock_uncontested_remote(void *arg __unused)
302 need_lwkt_resched();
305 #endif
307 #endif /* SMP */