xref: /openbsd-src/sys/kern/kern_lock.c (revision af1a00f9e0048050db8ad368227d3917fd4b7c7b)
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