1 /*-
2  * Copyright (c) 2010 Isilon Systems, Inc.
3  * Copyright (c) 2010 iX Systems, Inc.
4  * Copyright (c) 2010 Panasas, Inc.
5  * Copyright (c) 2013-2017 Mellanox Technologies, Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice unmodified, this list of conditions, and the following
13  *    disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #ifndef	_LINUXKPI_LINUX_SPINLOCK_H_
30 #define	_LINUXKPI_LINUX_SPINLOCK_H_
31 
32 #include <asm/atomic.h>
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/kdb.h>
38 
39 #include <linux/compiler.h>
40 #include <linux/rwlock.h>
41 #include <linux/bottom_half.h>
42 #include <linux/lockdep.h>
43 
44 typedef struct mtx spinlock_t;
45 
46 /*
47  * By defining CONFIG_SPIN_SKIP LinuxKPI spinlocks and asserts will be
48  * skipped during panic(). By default it is disabled due to
49  * performance reasons.
50  */
51 #ifdef CONFIG_SPIN_SKIP
52 #define	SPIN_SKIP(void)	unlikely(SCHEDULER_STOPPED() || kdb_active)
53 #else
54 #define	SPIN_SKIP(void) 0
55 #endif
56 
57 #define	spin_lock(_l) do {			\
58 	if (SPIN_SKIP())			\
59 		break;				\
60 	mtx_lock(_l);				\
61 	local_bh_disable();			\
62 } while (0)
63 
64 #define	spin_lock_bh(_l) do {			\
65 	spin_lock(_l);				\
66 	local_bh_disable();			\
67 } while (0)
68 
69 #define	spin_lock_irq(_l) do {			\
70 	spin_lock(_l);				\
71 } while (0)
72 
73 #define	spin_unlock(_l)	do {			\
74 	if (SPIN_SKIP())			\
75 		break;				\
76 	local_bh_enable();			\
77 	mtx_unlock(_l);				\
78 } while (0)
79 
80 #define	spin_unlock_bh(_l) do {			\
81 	local_bh_enable();			\
82 	spin_unlock(_l);			\
83 } while (0)
84 
85 #define	spin_unlock_irq(_l) do {		\
86 	spin_unlock(_l);			\
87 } while (0)
88 
89 #define	spin_trylock(_l) ({			\
90 	int __ret;				\
91 	if (SPIN_SKIP()) {			\
92 		__ret = 1;			\
93 	} else {				\
94 		__ret = mtx_trylock(_l);	\
95 		if (likely(__ret != 0))		\
96 			local_bh_disable();	\
97 	}					\
98 	__ret;					\
99 })
100 
101 #define	spin_trylock_irq(_l)			\
102 	spin_trylock(_l)
103 
104 #define	spin_trylock_irqsave(_l, flags) ({	\
105 	(flags) = 0;				\
106 	spin_trylock(_l);			\
107 })
108 
109 #define	spin_lock_nested(_l, _n) do {		\
110 	if (SPIN_SKIP())			\
111 		break;				\
112 	mtx_lock_flags(_l, MTX_DUPOK);		\
113 	local_bh_disable();			\
114 } while (0)
115 
116 #define	spin_lock_irqsave(_l, flags) do {	\
117 	(flags) = 0;				\
118 	spin_lock(_l);				\
119 } while (0)
120 
121 #define	spin_lock_irqsave_nested(_l, flags, _n) do {	\
122 	(flags) = 0;					\
123 	spin_lock_nested(_l, _n);			\
124 } while (0)
125 
126 #define	spin_unlock_irqrestore(_l, flags) do {		\
127 	(void)(flags);					\
128 	spin_unlock(_l);				\
129 } while (0)
130 
131 #ifdef WITNESS_ALL
132 /* NOTE: the maximum WITNESS name is 64 chars */
133 #define	__spin_lock_name(name, file, line)		\
134 	(((const char *){file ":" #line "-" name}) +	\
135 	(sizeof(file) > 16 ? sizeof(file) - 16 : 0))
136 #else
137 #define	__spin_lock_name(name, file, line)	name
138 #endif
139 #define	_spin_lock_name(...)		__spin_lock_name(__VA_ARGS__)
140 #define	spin_lock_name(name)		_spin_lock_name(name, __FILE__, __LINE__)
141 
142 #define	spin_lock_init(lock)	mtx_init(lock, spin_lock_name("lnxspin"), \
143 				  NULL, MTX_DEF | MTX_NOWITNESS | MTX_NEW)
144 
145 #define	spin_lock_destroy(_l)	mtx_destroy(_l)
146 
147 #define	DEFINE_SPINLOCK(lock)					\
148 	spinlock_t lock;					\
149 	MTX_SYSINIT(lock, &lock, spin_lock_name("lnxspin"), MTX_DEF)
150 
151 #define	assert_spin_locked(_l) do {		\
152 	if (SPIN_SKIP())			\
153 		break;				\
154 	mtx_assert(_l, MA_OWNED);		\
155 } while (0)
156 
157 #define	atomic_dec_and_lock_irqsave(cnt, lock, flags) \
158 	_atomic_dec_and_lock_irqsave(cnt, lock, &(flags))
159 static inline int
_atomic_dec_and_lock_irqsave(atomic_t * cnt,spinlock_t * lock,unsigned long * flags)160 _atomic_dec_and_lock_irqsave(atomic_t *cnt, spinlock_t *lock,
161     unsigned long *flags)
162 {
163 	if (atomic_add_unless(cnt, -1, 1))
164 		return (0);
165 
166 	spin_lock_irqsave(lock, *flags);
167 	if (atomic_dec_and_test(cnt))
168 		return (1);
169 	spin_unlock_irqrestore(lock, *flags);
170 	return (0);
171 }
172 
173 #endif					/* _LINUXKPI_LINUX_SPINLOCK_H_ */
174