1*af1a00f9Sjsg /* $OpenBSD: kern_lock.c,v 1.75 2024/07/03 01:36:50 jsg Exp $ */
20b878aa3Sniklas
307feb63cScsapuntz /*
485b318e2Smpi * Copyright (c) 2017 Visa Hankala
511f61d64Smpi * Copyright (c) 2014 David Gwynne <dlg@openbsd.org>
6710c2d98Smpi * Copyright (c) 2004 Artur Grabowski <art@openbsd.org>
7710c2d98Smpi *
854292cf9Smpi * Permission to use, copy, modify, and distribute this software for any
954292cf9Smpi * purpose with or without fee is hereby granted, provided that the above
1054292cf9Smpi * copyright notice and this permission notice appear in all copies.
11710c2d98Smpi *
1254292cf9Smpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1354292cf9Smpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1454292cf9Smpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1554292cf9Smpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1654292cf9Smpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1754292cf9Smpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1854292cf9Smpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19710c2d98Smpi */
2007feb63cScsapuntz
2107feb63cScsapuntz #include <sys/param.h>
2207feb63cScsapuntz #include <sys/systm.h>
23012ea299Sniklas #include <sys/sched.h>
245a26f776Svisa #include <sys/atomic.h>
2525753472Svisa #include <sys/witness.h>
26ac549d3fSmpi #include <sys/mutex.h>
2707feb63cScsapuntz
285a26f776Svisa #include <ddb/db_output.h>
295a26f776Svisa
30fe074e2aStedu #ifdef MP_LOCKDEBUG
315a26f776Svisa #ifndef DDB
325a26f776Svisa #error "MP_LOCKDEBUG requires DDB"
33fe074e2aStedu #endif
34012ea299Sniklas
355a26f776Svisa /* CPU-dependent timing, this needs to be settable from ddb. */
368d3b4fd4Sdv int __mp_lock_spinout = INT_MAX;
375a26f776Svisa #endif /* MP_LOCKDEBUG */
385a26f776Svisa
395a26f776Svisa #ifdef MULTIPROCESSOR
405a26f776Svisa
4151f2d5aaSvisa #include <sys/mplock.h>
4251f2d5aaSvisa struct __mp_lock kernel_lock;
4351f2d5aaSvisa
44012ea299Sniklas /*
45012ea299Sniklas * Functions for manipulating the kernel_lock. We put them here
46012ea299Sniklas * so that they show up in profiles.
47012ea299Sniklas */
48012ea299Sniklas
49012ea299Sniklas void
_kernel_lock_init(void)50012ea299Sniklas _kernel_lock_init(void)
51012ea299Sniklas {
52012ea299Sniklas __mp_lock_init(&kernel_lock);
53012ea299Sniklas }
54012ea299Sniklas
55012ea299Sniklas /*
56012ea299Sniklas * Acquire/release the kernel lock. Intended for use in the scheduler
57012ea299Sniklas * and the lower half of the kernel.
58012ea299Sniklas */
59012ea299Sniklas
60012ea299Sniklas void
_kernel_lock(void)618c00de5eSvisa _kernel_lock(void)
62012ea299Sniklas {
63012ea299Sniklas SCHED_ASSERT_UNLOCKED();
64012ea299Sniklas __mp_lock(&kernel_lock);
65012ea299Sniklas }
66012ea299Sniklas
67012ea299Sniklas void
_kernel_unlock(void)68012ea299Sniklas _kernel_unlock(void)
69012ea299Sniklas {
70012ea299Sniklas __mp_unlock(&kernel_lock);
71012ea299Sniklas }
72ca86693bSuebayasi
73ca86693bSuebayasi int
_kernel_lock_held(void)74ca86693bSuebayasi _kernel_lock_held(void)
75ca86693bSuebayasi {
768ed6e35eSmpi if (panicstr || db_active)
7770a7648cSmpi return 1;
78ba0fc568Smpi return (__mp_lock_held(&kernel_lock, curcpu()));
79ca86693bSuebayasi }
8025753472Svisa
815a26f776Svisa #ifdef __USE_MI_MPLOCK
825a26f776Svisa
835a26f776Svisa /* Ticket lock implementation */
845a26f776Svisa
855a26f776Svisa #include <machine/cpu.h>
865a26f776Svisa
8725753472Svisa void
___mp_lock_init(struct __mp_lock * mpl,const struct lock_type * type)88e0c5510eSguenther ___mp_lock_init(struct __mp_lock *mpl, const struct lock_type *type)
8925753472Svisa {
905a26f776Svisa memset(mpl->mpl_cpus, 0, sizeof(mpl->mpl_cpus));
915a26f776Svisa mpl->mpl_users = 0;
925a26f776Svisa mpl->mpl_ticket = 1;
935a26f776Svisa
945a26f776Svisa #ifdef WITNESS
9525753472Svisa mpl->mpl_lock_obj.lo_name = type->lt_name;
9625753472Svisa mpl->mpl_lock_obj.lo_type = type;
9725753472Svisa if (mpl == &kernel_lock)
9825753472Svisa mpl->mpl_lock_obj.lo_flags = LO_WITNESS | LO_INITIALIZED |
9925753472Svisa LO_SLEEPABLE | (LO_CLASS_KERNEL_LOCK << LO_CLASSSHIFT);
10025753472Svisa WITNESS_INIT(&mpl->mpl_lock_obj, type);
1015a26f776Svisa #endif
10225753472Svisa }
1035a26f776Svisa
1045a26f776Svisa static __inline void
__mp_lock_spin(struct __mp_lock * mpl,u_int me)1055a26f776Svisa __mp_lock_spin(struct __mp_lock *mpl, u_int me)
1065a26f776Svisa {
107531d8034Smpi struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
1086dae24f4Smpi #ifdef MP_LOCKDEBUG
1095a26f776Svisa int nticks = __mp_lock_spinout;
1106dae24f4Smpi #endif
1115a26f776Svisa
112531d8034Smpi spc->spc_spinning++;
1135a26f776Svisa while (mpl->mpl_ticket != me) {
1145a26f776Svisa CPU_BUSY_CYCLE();
1155a26f776Svisa
1166dae24f4Smpi #ifdef MP_LOCKDEBUG
1175a26f776Svisa if (--nticks <= 0) {
118c9289374Sclaudio db_printf("%s: %p lock spun out\n", __func__, mpl);
1195a26f776Svisa db_enter();
1205a26f776Svisa nticks = __mp_lock_spinout;
1215a26f776Svisa }
1225a26f776Svisa #endif
1235a26f776Svisa }
124531d8034Smpi spc->spc_spinning--;
1256dae24f4Smpi }
1265a26f776Svisa
1275a26f776Svisa void
__mp_lock(struct __mp_lock * mpl)1288c00de5eSvisa __mp_lock(struct __mp_lock *mpl)
1295a26f776Svisa {
1305a26f776Svisa struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
1315a26f776Svisa unsigned long s;
1325a26f776Svisa
1335a26f776Svisa #ifdef WITNESS
134ba0fc568Smpi if (!__mp_lock_held(mpl, curcpu()))
1355a26f776Svisa WITNESS_CHECKORDER(&mpl->mpl_lock_obj,
1368c00de5eSvisa LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
1375a26f776Svisa #endif
1385a26f776Svisa
1395a26f776Svisa s = intr_disable();
1405a26f776Svisa if (cpu->mplc_depth++ == 0)
1415a26f776Svisa cpu->mplc_ticket = atomic_inc_int_nv(&mpl->mpl_users);
1425a26f776Svisa intr_restore(s);
1435a26f776Svisa
1445a26f776Svisa __mp_lock_spin(mpl, cpu->mplc_ticket);
1455a26f776Svisa membar_enter_after_atomic();
1465a26f776Svisa
1478c00de5eSvisa WITNESS_LOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
1485a26f776Svisa }
1495a26f776Svisa
1505a26f776Svisa void
__mp_unlock(struct __mp_lock * mpl)1518c00de5eSvisa __mp_unlock(struct __mp_lock *mpl)
1525a26f776Svisa {
1535a26f776Svisa struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
1545a26f776Svisa unsigned long s;
1555a26f776Svisa
1565a26f776Svisa #ifdef MP_LOCKDEBUG
157ba0fc568Smpi if (!__mp_lock_held(mpl, curcpu())) {
1585a26f776Svisa db_printf("__mp_unlock(%p): not held lock\n", mpl);
1595a26f776Svisa db_enter();
1605a26f776Svisa }
1615a26f776Svisa #endif
1625a26f776Svisa
1638c00de5eSvisa WITNESS_UNLOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
1645a26f776Svisa
1655a26f776Svisa s = intr_disable();
1665a26f776Svisa if (--cpu->mplc_depth == 0) {
1675a26f776Svisa membar_exit();
1685a26f776Svisa mpl->mpl_ticket++;
1695a26f776Svisa }
1705a26f776Svisa intr_restore(s);
1715a26f776Svisa }
1725a26f776Svisa
1735a26f776Svisa int
__mp_release_all(struct __mp_lock * mpl)1748c00de5eSvisa __mp_release_all(struct __mp_lock *mpl)
1755a26f776Svisa {
1765a26f776Svisa struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
1775a26f776Svisa unsigned long s;
1785a26f776Svisa int rv;
1795a26f776Svisa #ifdef WITNESS
1805a26f776Svisa int i;
1815a26f776Svisa #endif
1825a26f776Svisa
1835a26f776Svisa s = intr_disable();
1845a26f776Svisa rv = cpu->mplc_depth;
1855a26f776Svisa #ifdef WITNESS
1865a26f776Svisa for (i = 0; i < rv; i++)
1878c00de5eSvisa WITNESS_UNLOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
1885a26f776Svisa #endif
1895a26f776Svisa cpu->mplc_depth = 0;
1905a26f776Svisa membar_exit();
1915a26f776Svisa mpl->mpl_ticket++;
1925a26f776Svisa intr_restore(s);
1935a26f776Svisa
1945a26f776Svisa return (rv);
1955a26f776Svisa }
1965a26f776Svisa
1975a26f776Svisa void
__mp_acquire_count(struct __mp_lock * mpl,int count)1988c00de5eSvisa __mp_acquire_count(struct __mp_lock *mpl, int count)
1995a26f776Svisa {
2005a26f776Svisa while (count--)
2018c00de5eSvisa __mp_lock(mpl);
2025a26f776Svisa }
2035a26f776Svisa
2045a26f776Svisa int
__mp_lock_held(struct __mp_lock * mpl,struct cpu_info * ci)205ba0fc568Smpi __mp_lock_held(struct __mp_lock *mpl, struct cpu_info *ci)
2065a26f776Svisa {
207ba0fc568Smpi struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[CPU_INFO_UNIT(ci)];
2085a26f776Svisa
2095a26f776Svisa return (cpu->mplc_ticket == mpl->mpl_ticket && cpu->mplc_depth > 0);
2105a26f776Svisa }
2115a26f776Svisa
2125a26f776Svisa #endif /* __USE_MI_MPLOCK */
21325753472Svisa
214012ea299Sniklas #endif /* MULTIPROCESSOR */
215710c2d98Smpi
216710c2d98Smpi
217710c2d98Smpi #ifdef __USE_MI_MUTEX
218710c2d98Smpi void
__mtx_init(struct mutex * mtx,int wantipl)219710c2d98Smpi __mtx_init(struct mutex *mtx, int wantipl)
220710c2d98Smpi {
221710c2d98Smpi mtx->mtx_owner = NULL;
222710c2d98Smpi mtx->mtx_wantipl = wantipl;
223710c2d98Smpi mtx->mtx_oldipl = IPL_NONE;
224710c2d98Smpi }
225710c2d98Smpi
226710c2d98Smpi #ifdef MULTIPROCESSOR
227710c2d98Smpi void
mtx_enter(struct mutex * mtx)2288c00de5eSvisa mtx_enter(struct mutex *mtx)
229710c2d98Smpi {
230531d8034Smpi struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
231710c2d98Smpi #ifdef MP_LOCKDEBUG
232710c2d98Smpi int nticks = __mp_lock_spinout;
233710c2d98Smpi #endif
234710c2d98Smpi
2358c00de5eSvisa WITNESS_CHECKORDER(MUTEX_LOCK_OBJECT(mtx),
2368c00de5eSvisa LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
2378c00de5eSvisa
238531d8034Smpi spc->spc_spinning++;
2398c00de5eSvisa while (mtx_enter_try(mtx) == 0) {
24055c63681Sbluhm do {
241710c2d98Smpi CPU_BUSY_CYCLE();
242710c2d98Smpi #ifdef MP_LOCKDEBUG
243710c2d98Smpi if (--nticks == 0) {
24455c63681Sbluhm db_printf("%s: %p lock spun out\n",
24555c63681Sbluhm __func__, mtx);
246710c2d98Smpi db_enter();
247710c2d98Smpi nticks = __mp_lock_spinout;
248710c2d98Smpi }
249710c2d98Smpi #endif
25055c63681Sbluhm } while (mtx->mtx_owner != NULL);
251710c2d98Smpi }
252531d8034Smpi spc->spc_spinning--;
253710c2d98Smpi }
254710c2d98Smpi
255710c2d98Smpi int
mtx_enter_try(struct mutex * mtx)2568c00de5eSvisa mtx_enter_try(struct mutex *mtx)
257710c2d98Smpi {
258710c2d98Smpi struct cpu_info *owner, *ci = curcpu();
259710c2d98Smpi int s;
260710c2d98Smpi
261fdf140beSmpi /* Avoid deadlocks after panic or in DDB */
262fdf140beSmpi if (panicstr || db_active)
263fdf140beSmpi return (1);
264fdf140beSmpi
265710c2d98Smpi if (mtx->mtx_wantipl != IPL_NONE)
266710c2d98Smpi s = splraise(mtx->mtx_wantipl);
267710c2d98Smpi
268710c2d98Smpi owner = atomic_cas_ptr(&mtx->mtx_owner, NULL, ci);
269710c2d98Smpi #ifdef DIAGNOSTIC
270710c2d98Smpi if (__predict_false(owner == ci))
271710c2d98Smpi panic("mtx %p: locking against myself", mtx);
272710c2d98Smpi #endif
273710c2d98Smpi if (owner == NULL) {
274710c2d98Smpi membar_enter_after_atomic();
275710c2d98Smpi if (mtx->mtx_wantipl != IPL_NONE)
276710c2d98Smpi mtx->mtx_oldipl = s;
277710c2d98Smpi #ifdef DIAGNOSTIC
278710c2d98Smpi ci->ci_mutex_level++;
279710c2d98Smpi #endif
2808c00de5eSvisa WITNESS_LOCK(MUTEX_LOCK_OBJECT(mtx), LOP_EXCLUSIVE);
281710c2d98Smpi return (1);
282710c2d98Smpi }
283710c2d98Smpi
284710c2d98Smpi if (mtx->mtx_wantipl != IPL_NONE)
285710c2d98Smpi splx(s);
286710c2d98Smpi
287710c2d98Smpi return (0);
288710c2d98Smpi }
289710c2d98Smpi #else
290710c2d98Smpi void
mtx_enter(struct mutex * mtx)2918c00de5eSvisa mtx_enter(struct mutex *mtx)
292710c2d98Smpi {
293710c2d98Smpi struct cpu_info *ci = curcpu();
294710c2d98Smpi
2951bb039c8Smpi /* Avoid deadlocks after panic or in DDB */
2961bb039c8Smpi if (panicstr || db_active)
2971bb039c8Smpi return;
2981bb039c8Smpi
299643f9e39Svisa WITNESS_CHECKORDER(MUTEX_LOCK_OBJECT(mtx),
300643f9e39Svisa LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
301643f9e39Svisa
302710c2d98Smpi #ifdef DIAGNOSTIC
303710c2d98Smpi if (__predict_false(mtx->mtx_owner == ci))
304710c2d98Smpi panic("mtx %p: locking against myself", mtx);
305710c2d98Smpi #endif
306710c2d98Smpi
307710c2d98Smpi if (mtx->mtx_wantipl != IPL_NONE)
308710c2d98Smpi mtx->mtx_oldipl = splraise(mtx->mtx_wantipl);
309710c2d98Smpi
310710c2d98Smpi mtx->mtx_owner = ci;
311710c2d98Smpi
312710c2d98Smpi #ifdef DIAGNOSTIC
313710c2d98Smpi ci->ci_mutex_level++;
314710c2d98Smpi #endif
315643f9e39Svisa WITNESS_LOCK(MUTEX_LOCK_OBJECT(mtx), LOP_EXCLUSIVE);
316710c2d98Smpi }
317710c2d98Smpi
318710c2d98Smpi int
mtx_enter_try(struct mutex * mtx)3198c00de5eSvisa mtx_enter_try(struct mutex *mtx)
320710c2d98Smpi {
3218c00de5eSvisa mtx_enter(mtx);
322710c2d98Smpi return (1);
323710c2d98Smpi }
324710c2d98Smpi #endif
325710c2d98Smpi
326710c2d98Smpi void
mtx_leave(struct mutex * mtx)3278c00de5eSvisa mtx_leave(struct mutex *mtx)
328710c2d98Smpi {
329710c2d98Smpi int s;
330710c2d98Smpi
3311bb039c8Smpi /* Avoid deadlocks after panic or in DDB */
3321bb039c8Smpi if (panicstr || db_active)
3331bb039c8Smpi return;
3341bb039c8Smpi
335710c2d98Smpi MUTEX_ASSERT_LOCKED(mtx);
3368c00de5eSvisa WITNESS_UNLOCK(MUTEX_LOCK_OBJECT(mtx), LOP_EXCLUSIVE);
337710c2d98Smpi
338710c2d98Smpi #ifdef DIAGNOSTIC
339710c2d98Smpi curcpu()->ci_mutex_level--;
340710c2d98Smpi #endif
341710c2d98Smpi
342710c2d98Smpi s = mtx->mtx_oldipl;
343710c2d98Smpi #ifdef MULTIPROCESSOR
3446a986ed6Svisa membar_exit();
345710c2d98Smpi #endif
346710c2d98Smpi mtx->mtx_owner = NULL;
347710c2d98Smpi if (mtx->mtx_wantipl != IPL_NONE)
348710c2d98Smpi splx(s);
349710c2d98Smpi }
3503fbde817Svisa
3513fbde817Svisa #ifdef DDB
3523fbde817Svisa void
db_mtx_enter(struct db_mutex * mtx)3533fbde817Svisa db_mtx_enter(struct db_mutex *mtx)
3543fbde817Svisa {
3553fbde817Svisa struct cpu_info *ci = curcpu(), *owner;
3563fbde817Svisa unsigned long s;
3573fbde817Svisa
3583fbde817Svisa #ifdef DIAGNOSTIC
3593fbde817Svisa if (__predict_false(mtx->mtx_owner == ci))
3603fbde817Svisa panic("%s: mtx %p: locking against myself", __func__, mtx);
3613fbde817Svisa #endif
3623fbde817Svisa
3633fbde817Svisa s = intr_disable();
3643fbde817Svisa
3653fbde817Svisa for (;;) {
3663fbde817Svisa owner = atomic_cas_ptr(&mtx->mtx_owner, NULL, ci);
3673fbde817Svisa if (owner == NULL)
3683fbde817Svisa break;
3693fbde817Svisa CPU_BUSY_CYCLE();
3703fbde817Svisa }
3713fbde817Svisa membar_enter_after_atomic();
3723fbde817Svisa
3733fbde817Svisa mtx->mtx_intr_state = s;
3743fbde817Svisa
3753fbde817Svisa #ifdef DIAGNOSTIC
3763fbde817Svisa ci->ci_mutex_level++;
3773fbde817Svisa #endif
3783fbde817Svisa }
3793fbde817Svisa
3803fbde817Svisa void
db_mtx_leave(struct db_mutex * mtx)3813fbde817Svisa db_mtx_leave(struct db_mutex *mtx)
3823fbde817Svisa {
3833fbde817Svisa #ifdef DIAGNOSTIC
3843fbde817Svisa struct cpu_info *ci = curcpu();
3853fbde817Svisa #endif
3863fbde817Svisa unsigned long s;
3873fbde817Svisa
3883fbde817Svisa #ifdef DIAGNOSTIC
3893fbde817Svisa if (__predict_false(mtx->mtx_owner != ci))
3903fbde817Svisa panic("%s: mtx %p: not owned by this CPU", __func__, mtx);
3913fbde817Svisa ci->ci_mutex_level--;
3923fbde817Svisa #endif
3933fbde817Svisa
3943fbde817Svisa s = mtx->mtx_intr_state;
3953fbde817Svisa #ifdef MULTIPROCESSOR
3963fbde817Svisa membar_exit();
3973fbde817Svisa #endif
3983fbde817Svisa mtx->mtx_owner = NULL;
3993fbde817Svisa intr_restore(s);
4003fbde817Svisa }
4013fbde817Svisa #endif /* DDB */
402710c2d98Smpi #endif /* __USE_MI_MUTEX */
40385b318e2Smpi
40485b318e2Smpi #ifdef WITNESS
40585b318e2Smpi void
_mtx_init_flags(struct mutex * m,int ipl,const char * name,int flags,const struct lock_type * type)40685b318e2Smpi _mtx_init_flags(struct mutex *m, int ipl, const char *name, int flags,
407e0c5510eSguenther const struct lock_type *type)
40885b318e2Smpi {
40985b318e2Smpi struct lock_object *lo = MUTEX_LOCK_OBJECT(m);
41085b318e2Smpi
41185b318e2Smpi lo->lo_flags = MTX_LO_FLAGS(flags);
41285b318e2Smpi if (name != NULL)
41385b318e2Smpi lo->lo_name = name;
41485b318e2Smpi else
41585b318e2Smpi lo->lo_name = type->lt_name;
41685b318e2Smpi WITNESS_INIT(lo, type);
41785b318e2Smpi
41885b318e2Smpi _mtx_init(m, ipl);
41985b318e2Smpi }
42085b318e2Smpi #endif /* WITNESS */
421