18d59ecb2SHans Petter Selasky /*-
28d59ecb2SHans Petter Selasky  * Copyright (c) 2010 Isilon Systems, Inc.
38d59ecb2SHans Petter Selasky  * Copyright (c) 2010 iX Systems, Inc.
48d59ecb2SHans Petter Selasky  * Copyright (c) 2010 Panasas, Inc.
54f688d19SHans Petter Selasky  * Copyright (c) 2013-2017 Mellanox Technologies, Ltd.
68d59ecb2SHans Petter Selasky  * All rights reserved.
78d59ecb2SHans Petter Selasky  *
88d59ecb2SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
98d59ecb2SHans Petter Selasky  * modification, are permitted provided that the following conditions
108d59ecb2SHans Petter Selasky  * are met:
118d59ecb2SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
128d59ecb2SHans Petter Selasky  *    notice unmodified, this list of conditions, and the following
138d59ecb2SHans Petter Selasky  *    disclaimer.
148d59ecb2SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
158d59ecb2SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
168d59ecb2SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
178d59ecb2SHans Petter Selasky  *
188d59ecb2SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
198d59ecb2SHans Petter Selasky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
208d59ecb2SHans Petter Selasky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
218d59ecb2SHans Petter Selasky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
228d59ecb2SHans Petter Selasky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
238d59ecb2SHans Petter Selasky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
248d59ecb2SHans Petter Selasky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
258d59ecb2SHans Petter Selasky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
268d59ecb2SHans Petter Selasky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
278d59ecb2SHans Petter Selasky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
288d59ecb2SHans Petter Selasky  */
29307f78f3SVladimir Kondratyev #ifndef	_LINUXKPI_LINUX_MUTEX_H_
30307f78f3SVladimir Kondratyev #define	_LINUXKPI_LINUX_MUTEX_H_
318d59ecb2SHans Petter Selasky 
328d59ecb2SHans Petter Selasky #include <sys/param.h>
334f688d19SHans Petter Selasky #include <sys/proc.h>
348d59ecb2SHans Petter Selasky #include <sys/lock.h>
358d59ecb2SHans Petter Selasky #include <sys/sx.h>
368d59ecb2SHans Petter Selasky 
3742bb5861SJean-Sébastien Pédron #include <linux/kernel.h>
3842bb5861SJean-Sébastien Pédron #include <linux/list.h>
398d59ecb2SHans Petter Selasky #include <linux/spinlock.h>
407708d3d7SEmmanuel Vadot #include <asm/atomic.h>
418d59ecb2SHans Petter Selasky 
428d59ecb2SHans Petter Selasky typedef struct mutex {
438d59ecb2SHans Petter Selasky 	struct sx sx;
448d59ecb2SHans Petter Selasky } mutex_t;
458d59ecb2SHans Petter Selasky 
464f688d19SHans Petter Selasky /*
474f688d19SHans Petter Selasky  * By defining CONFIG_NO_MUTEX_SKIP LinuxKPI mutexes and asserts will
484f688d19SHans Petter Selasky  * not be skipped during panic().
494f688d19SHans Petter Selasky  */
504f688d19SHans Petter Selasky #ifdef CONFIG_NO_MUTEX_SKIP
514f688d19SHans Petter Selasky #define	MUTEX_SKIP(void) 0
524f688d19SHans Petter Selasky #else
534f688d19SHans Petter Selasky #define	MUTEX_SKIP(void) unlikely(SCHEDULER_STOPPED() || kdb_active)
544f688d19SHans Petter Selasky #endif
554f688d19SHans Petter Selasky 
564f688d19SHans Petter Selasky #define	mutex_lock(_m) do {			\
574f688d19SHans Petter Selasky 	if (MUTEX_SKIP())			\
584f688d19SHans Petter Selasky 		break;				\
594f688d19SHans Petter Selasky 	sx_xlock(&(_m)->sx);			\
604f688d19SHans Petter Selasky } while (0)
614f688d19SHans Petter Selasky 
628d59ecb2SHans Petter Selasky #define	mutex_lock_nested(_m, _s)	mutex_lock(_m)
634f688d19SHans Petter Selasky #define	mutex_lock_nest_lock(_m, _s)	mutex_lock(_m)
644f688d19SHans Petter Selasky 
654f688d19SHans Petter Selasky #define	mutex_lock_interruptible(_m) ({		\
664f688d19SHans Petter Selasky 	MUTEX_SKIP() ? 0 :			\
6794944062SHans Petter Selasky 	linux_mutex_lock_interruptible(_m);	\
684f688d19SHans Petter Selasky })
694f688d19SHans Petter Selasky 
702d946b2eSEmmanuel Vadot #define	mutex_lock_interruptible_nested(m, c)	mutex_lock_interruptible(m)
712d946b2eSEmmanuel Vadot 
721bbbe083SHans Petter Selasky /*
731bbbe083SHans Petter Selasky  * Reuse the interruptable method since the SX
741bbbe083SHans Petter Selasky  * lock handles both signals and interrupts:
751bbbe083SHans Petter Selasky  */
761bbbe083SHans Petter Selasky #define	mutex_lock_killable(_m) ({		\
771bbbe083SHans Petter Selasky 	MUTEX_SKIP() ? 0 :			\
781bbbe083SHans Petter Selasky 	linux_mutex_lock_interruptible(_m);	\
791bbbe083SHans Petter Selasky })
801bbbe083SHans Petter Selasky 
811bbbe083SHans Petter Selasky #define	mutex_lock_killable_nested(_m, _sub)	\
821bbbe083SHans Petter Selasky 	mutex_lock_killable(_m)
831bbbe083SHans Petter Selasky 
844f688d19SHans Petter Selasky #define	mutex_unlock(_m) do {			\
854f688d19SHans Petter Selasky 	if (MUTEX_SKIP())			\
864f688d19SHans Petter Selasky 		break;				\
874f688d19SHans Petter Selasky 	sx_xunlock(&(_m)->sx);			\
884f688d19SHans Petter Selasky } while (0)
894f688d19SHans Petter Selasky 
904f688d19SHans Petter Selasky #define	mutex_trylock(_m) ({			\
914f688d19SHans Petter Selasky 	MUTEX_SKIP() ? 1 :			\
924f688d19SHans Petter Selasky 	!!sx_try_xlock(&(_m)->sx);		\
934f688d19SHans Petter Selasky })
944f688d19SHans Petter Selasky 
95f4824a02SHans Petter Selasky enum mutex_trylock_recursive_enum {
96f4824a02SHans Petter Selasky 	MUTEX_TRYLOCK_FAILED = 0,
97f4824a02SHans Petter Selasky 	MUTEX_TRYLOCK_SUCCESS = 1,
98f4824a02SHans Petter Selasky 	MUTEX_TRYLOCK_RECURSIVE = 2,
99f4824a02SHans Petter Selasky };
100f4824a02SHans Petter Selasky 
101f4824a02SHans Petter Selasky static inline __must_check enum mutex_trylock_recursive_enum
mutex_trylock_recursive(struct mutex * lock)102f4824a02SHans Petter Selasky mutex_trylock_recursive(struct mutex *lock)
103f4824a02SHans Petter Selasky {
104f4824a02SHans Petter Selasky 	if (unlikely(sx_xholder(&lock->sx) == curthread))
105f4824a02SHans Petter Selasky 		return (MUTEX_TRYLOCK_RECURSIVE);
106f4824a02SHans Petter Selasky 
107f4824a02SHans Petter Selasky 	return (mutex_trylock(lock));
108f4824a02SHans Petter Selasky }
109f4824a02SHans Petter Selasky 
1104f688d19SHans Petter Selasky #define	mutex_init(_m) \
1114f688d19SHans Petter Selasky 	linux_mutex_init(_m, mutex_name(#_m), SX_NOWITNESS)
1124f688d19SHans Petter Selasky 
113d003cc43SEmmanuel Vadot #define	__mutex_init(_m, _n, _l) \
114d003cc43SEmmanuel Vadot 	linux_mutex_init(_m, _n, SX_NOWITNESS)
115d003cc43SEmmanuel Vadot 
1164f688d19SHans Petter Selasky #define	mutex_init_witness(_m) \
1174f688d19SHans Petter Selasky 	linux_mutex_init(_m, mutex_name(#_m), SX_DUPOK)
1184f688d19SHans Petter Selasky 
1194f688d19SHans Petter Selasky #define	mutex_destroy(_m) \
1204f688d19SHans Petter Selasky 	linux_mutex_destroy(_m)
1214f688d19SHans Petter Selasky 
1224f688d19SHans Petter Selasky static inline bool
mutex_is_locked(mutex_t * m)1234f688d19SHans Petter Selasky mutex_is_locked(mutex_t *m)
1244f688d19SHans Petter Selasky {
1254f688d19SHans Petter Selasky 	return ((struct thread *)SX_OWNER(m->sx.sx_lock) != NULL);
1264f688d19SHans Petter Selasky }
1274f688d19SHans Petter Selasky 
1284f688d19SHans Petter Selasky static inline bool
mutex_is_owned(mutex_t * m)1294f688d19SHans Petter Selasky mutex_is_owned(mutex_t *m)
1304f688d19SHans Petter Selasky {
1314f688d19SHans Petter Selasky 	return (sx_xlocked(&m->sx));
1324f688d19SHans Petter Selasky }
1334f688d19SHans Petter Selasky 
atomic_dec_and_mutex_lock(atomic_t * cnt,struct mutex * m)1347708d3d7SEmmanuel Vadot static inline int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *m)
1357708d3d7SEmmanuel Vadot {
1367708d3d7SEmmanuel Vadot 	if (atomic_dec_and_test(cnt)) {
1377708d3d7SEmmanuel Vadot 		mutex_lock(m);
1387708d3d7SEmmanuel Vadot 		return (1);
1397708d3d7SEmmanuel Vadot 	}
1407708d3d7SEmmanuel Vadot 
1417708d3d7SEmmanuel Vadot 	return (0);
1427708d3d7SEmmanuel Vadot }
1437708d3d7SEmmanuel Vadot 
1444f688d19SHans Petter Selasky #ifdef WITNESS_ALL
1454f688d19SHans Petter Selasky /* NOTE: the maximum WITNESS name is 64 chars */
1464f688d19SHans Petter Selasky #define	__mutex_name(name, file, line)		\
1474f688d19SHans Petter Selasky 	(((const char *){file ":" #line "-" name}) +	\
1484f688d19SHans Petter Selasky 	(sizeof(file) > 16 ? sizeof(file) - 16 : 0))
1494f688d19SHans Petter Selasky #else
1504f688d19SHans Petter Selasky #define	__mutex_name(name, file, line)	name
1514f688d19SHans Petter Selasky #endif
1524f688d19SHans Petter Selasky #define	_mutex_name(...)	__mutex_name(__VA_ARGS__)
1534f688d19SHans Petter Selasky #define	mutex_name(name)	_mutex_name(name, __FILE__, __LINE__)
1548d59ecb2SHans Petter Selasky 
1558d59ecb2SHans Petter Selasky #define	DEFINE_MUTEX(lock)						\
1568d59ecb2SHans Petter Selasky 	mutex_t lock;							\
1574f688d19SHans Petter Selasky 	SX_SYSINIT_FLAGS(lock, &(lock).sx, mutex_name(#lock), SX_DUPOK)
1588d59ecb2SHans Petter Selasky 
1598d59ecb2SHans Petter Selasky static inline void
linux_mutex_init(mutex_t * m,const char * name,int flags)1604f688d19SHans Petter Selasky linux_mutex_init(mutex_t *m, const char *name, int flags)
1618d59ecb2SHans Petter Selasky {
1624f688d19SHans Petter Selasky 	memset(m, 0, sizeof(*m));
1634f688d19SHans Petter Selasky 	sx_init_flags(&m->sx, name, flags);
1648d59ecb2SHans Petter Selasky }
1658d59ecb2SHans Petter Selasky 
1664f688d19SHans Petter Selasky static inline void
linux_mutex_destroy(mutex_t * m)1674f688d19SHans Petter Selasky linux_mutex_destroy(mutex_t *m)
1684f688d19SHans Petter Selasky {
1694f688d19SHans Petter Selasky 	if (mutex_is_owned(m))
1704f688d19SHans Petter Selasky 		mutex_unlock(m);
1714f688d19SHans Petter Selasky 	sx_destroy(&m->sx);
1724f688d19SHans Petter Selasky }
1738d59ecb2SHans Petter Selasky 
17494944062SHans Petter Selasky extern int linux_mutex_lock_interruptible(mutex_t *m);
17594944062SHans Petter Selasky 
176307f78f3SVladimir Kondratyev #endif					/* _LINUXKPI_LINUX_MUTEX_H_ */
177