191710728SThomas Gleixner /* SPDX-License-Identifier: GPL-2.0 */
291710728SThomas Gleixner #ifndef _LINUX_LOCAL_LOCK_H
391710728SThomas Gleixner # error "Do not include directly, include linux/local_lock.h"
491710728SThomas Gleixner #endif
591710728SThomas Gleixner 
691710728SThomas Gleixner #include <linux/percpu-defs.h>
791710728SThomas Gleixner #include <linux/lockdep.h>
891710728SThomas Gleixner 
9026659b9SThomas Gleixner #ifndef CONFIG_PREEMPT_RT
10026659b9SThomas Gleixner 
1191710728SThomas Gleixner typedef struct {
1291710728SThomas Gleixner #ifdef CONFIG_DEBUG_LOCK_ALLOC
1391710728SThomas Gleixner 	struct lockdep_map	dep_map;
1491710728SThomas Gleixner 	struct task_struct	*owner;
1591710728SThomas Gleixner #endif
1691710728SThomas Gleixner } local_lock_t;
1791710728SThomas Gleixner 
1851339d99SAlexei Starovoitov /* local_trylock() and local_trylock_irqsave() only work with local_trylock_t */
190aaddfb0SSebastian Andrzej Siewior typedef struct {
200aaddfb0SSebastian Andrzej Siewior 	local_lock_t	llock;
2151339d99SAlexei Starovoitov 	u8		acquired;
2251339d99SAlexei Starovoitov } local_trylock_t;
230aaddfb0SSebastian Andrzej Siewior 
2491710728SThomas Gleixner #ifdef CONFIG_DEBUG_LOCK_ALLOC
25d8bbd97aSThomas Gleixner # define LOCAL_LOCK_DEBUG_INIT(lockname)		\
2691710728SThomas Gleixner 	.dep_map = {					\
2791710728SThomas Gleixner 		.name = #lockname,			\
2891710728SThomas Gleixner 		.wait_type_inner = LD_WAIT_CONFIG,	\
29dfd5e3f5SPeter Zijlstra 		.lock_type = LD_LOCK_PERCPU,		\
30d8bbd97aSThomas Gleixner 	},						\
31d8bbd97aSThomas Gleixner 	.owner = NULL,
3291710728SThomas Gleixner 
3351339d99SAlexei Starovoitov # define LOCAL_TRYLOCK_DEBUG_INIT(lockname)		\
3451339d99SAlexei Starovoitov 	.llock = { LOCAL_LOCK_DEBUG_INIT((lockname).llock) },
3551339d99SAlexei Starovoitov 
local_lock_acquire(local_lock_t * l)3691710728SThomas Gleixner static inline void local_lock_acquire(local_lock_t *l)
3791710728SThomas Gleixner {
3891710728SThomas Gleixner 	lock_map_acquire(&l->dep_map);
3991710728SThomas Gleixner 	DEBUG_LOCKS_WARN_ON(l->owner);
4091710728SThomas Gleixner 	l->owner = current;
4191710728SThomas Gleixner }
4291710728SThomas Gleixner 
local_trylock_acquire(local_lock_t * l)430aaddfb0SSebastian Andrzej Siewior static inline void local_trylock_acquire(local_lock_t *l)
440aaddfb0SSebastian Andrzej Siewior {
450aaddfb0SSebastian Andrzej Siewior 	lock_map_acquire_try(&l->dep_map);
460aaddfb0SSebastian Andrzej Siewior 	DEBUG_LOCKS_WARN_ON(l->owner);
470aaddfb0SSebastian Andrzej Siewior 	l->owner = current;
480aaddfb0SSebastian Andrzej Siewior }
490aaddfb0SSebastian Andrzej Siewior 
local_lock_release(local_lock_t * l)5091710728SThomas Gleixner static inline void local_lock_release(local_lock_t *l)
5191710728SThomas Gleixner {
5291710728SThomas Gleixner 	DEBUG_LOCKS_WARN_ON(l->owner != current);
5391710728SThomas Gleixner 	l->owner = NULL;
5491710728SThomas Gleixner 	lock_map_release(&l->dep_map);
5591710728SThomas Gleixner }
5691710728SThomas Gleixner 
local_lock_debug_init(local_lock_t * l)57d8bbd97aSThomas Gleixner static inline void local_lock_debug_init(local_lock_t *l)
58d8bbd97aSThomas Gleixner {
59d8bbd97aSThomas Gleixner 	l->owner = NULL;
60d8bbd97aSThomas Gleixner }
6191710728SThomas Gleixner #else /* CONFIG_DEBUG_LOCK_ALLOC */
62d8bbd97aSThomas Gleixner # define LOCAL_LOCK_DEBUG_INIT(lockname)
6351339d99SAlexei Starovoitov # define LOCAL_TRYLOCK_DEBUG_INIT(lockname)
local_lock_acquire(local_lock_t * l)642d2f8f08SSebastian Andrzej Siewior static inline void local_lock_acquire(local_lock_t *l) { }
local_trylock_acquire(local_lock_t * l)650aaddfb0SSebastian Andrzej Siewior static inline void local_trylock_acquire(local_lock_t *l) { }
local_lock_release(local_lock_t * l)662d2f8f08SSebastian Andrzej Siewior static inline void local_lock_release(local_lock_t *l) { }
local_lock_debug_init(local_lock_t * l)672d2f8f08SSebastian Andrzej Siewior static inline void local_lock_debug_init(local_lock_t *l) { }
6891710728SThomas Gleixner #endif /* !CONFIG_DEBUG_LOCK_ALLOC */
6991710728SThomas Gleixner 
70d8bbd97aSThomas Gleixner #define INIT_LOCAL_LOCK(lockname)	{ LOCAL_LOCK_DEBUG_INIT(lockname) }
7151339d99SAlexei Starovoitov #define INIT_LOCAL_TRYLOCK(lockname)	{ LOCAL_TRYLOCK_DEBUG_INIT(lockname) }
72d8bbd97aSThomas Gleixner 
73d8bbd97aSThomas Gleixner #define __local_lock_init(lock)					\
74d8bbd97aSThomas Gleixner do {								\
75d8bbd97aSThomas Gleixner 	static struct lock_class_key __key;			\
76d8bbd97aSThomas Gleixner 								\
77d8bbd97aSThomas Gleixner 	debug_check_no_locks_freed((void *)lock, sizeof(*lock));\
78d8bbd97aSThomas Gleixner 	lockdep_init_map_type(&(lock)->dep_map, #lock, &__key,  \
79d8bbd97aSThomas Gleixner 			      0, LD_WAIT_CONFIG, LD_WAIT_INV,	\
80d8bbd97aSThomas Gleixner 			      LD_LOCK_PERCPU);			\
81d8bbd97aSThomas Gleixner 	local_lock_debug_init(lock);				\
82d8bbd97aSThomas Gleixner } while (0)
83d8bbd97aSThomas Gleixner 
8451339d99SAlexei Starovoitov #define __local_trylock_init(lock) __local_lock_init(lock.llock)
8551339d99SAlexei Starovoitov 
86c5bcab75SSebastian Andrzej Siewior #define __spinlock_nested_bh_init(lock)				\
87c5bcab75SSebastian Andrzej Siewior do {								\
88c5bcab75SSebastian Andrzej Siewior 	static struct lock_class_key __key;			\
89c5bcab75SSebastian Andrzej Siewior 								\
90c5bcab75SSebastian Andrzej Siewior 	debug_check_no_locks_freed((void *)lock, sizeof(*lock));\
91c5bcab75SSebastian Andrzej Siewior 	lockdep_init_map_type(&(lock)->dep_map, #lock, &__key,  \
92c5bcab75SSebastian Andrzej Siewior 			      0, LD_WAIT_CONFIG, LD_WAIT_INV,	\
93c5bcab75SSebastian Andrzej Siewior 			      LD_LOCK_NORMAL);			\
94c5bcab75SSebastian Andrzej Siewior 	local_lock_debug_init(lock);				\
95c5bcab75SSebastian Andrzej Siewior } while (0)
96c5bcab75SSebastian Andrzej Siewior 
9751339d99SAlexei Starovoitov #define __local_lock_acquire(lock)					\
9851339d99SAlexei Starovoitov 	do {								\
9951339d99SAlexei Starovoitov 		local_trylock_t *tl;					\
10051339d99SAlexei Starovoitov 		local_lock_t *l;					\
10151339d99SAlexei Starovoitov 									\
10251339d99SAlexei Starovoitov 		l = (local_lock_t *)this_cpu_ptr(lock);			\
10351339d99SAlexei Starovoitov 		tl = (local_trylock_t *)l;				\
10451339d99SAlexei Starovoitov 		_Generic((lock),					\
105*82efd569SVlastimil Babka 			__percpu local_trylock_t *: ({			\
10651339d99SAlexei Starovoitov 				lockdep_assert(tl->acquired == 0);	\
10751339d99SAlexei Starovoitov 				WRITE_ONCE(tl->acquired, 1);		\
10851339d99SAlexei Starovoitov 			}),						\
109*82efd569SVlastimil Babka 			__percpu local_lock_t *: (void)0);		\
11051339d99SAlexei Starovoitov 		local_lock_acquire(l);					\
11151339d99SAlexei Starovoitov 	} while (0)
11251339d99SAlexei Starovoitov 
11391710728SThomas Gleixner #define __local_lock(lock)					\
11491710728SThomas Gleixner 	do {							\
11591710728SThomas Gleixner 		preempt_disable();				\
11651339d99SAlexei Starovoitov 		__local_lock_acquire(lock);			\
11791710728SThomas Gleixner 	} while (0)
11891710728SThomas Gleixner 
11991710728SThomas Gleixner #define __local_lock_irq(lock)					\
12091710728SThomas Gleixner 	do {							\
12191710728SThomas Gleixner 		local_irq_disable();				\
12251339d99SAlexei Starovoitov 		__local_lock_acquire(lock);			\
12391710728SThomas Gleixner 	} while (0)
12491710728SThomas Gleixner 
12591710728SThomas Gleixner #define __local_lock_irqsave(lock, flags)			\
12691710728SThomas Gleixner 	do {							\
12791710728SThomas Gleixner 		local_irq_save(flags);				\
12851339d99SAlexei Starovoitov 		__local_lock_acquire(lock);			\
12951339d99SAlexei Starovoitov 	} while (0)
13051339d99SAlexei Starovoitov 
13151339d99SAlexei Starovoitov #define __local_trylock(lock)					\
13251339d99SAlexei Starovoitov 	({							\
13351339d99SAlexei Starovoitov 		local_trylock_t *tl;				\
13451339d99SAlexei Starovoitov 								\
13551339d99SAlexei Starovoitov 		preempt_disable();				\
13651339d99SAlexei Starovoitov 		tl = this_cpu_ptr(lock);			\
13751339d99SAlexei Starovoitov 		if (READ_ONCE(tl->acquired)) {			\
13851339d99SAlexei Starovoitov 			preempt_enable();			\
13951339d99SAlexei Starovoitov 			tl = NULL;				\
14051339d99SAlexei Starovoitov 		} else {					\
14151339d99SAlexei Starovoitov 			WRITE_ONCE(tl->acquired, 1);		\
14251339d99SAlexei Starovoitov 			local_trylock_acquire(			\
14351339d99SAlexei Starovoitov 				(local_lock_t *)tl);		\
14451339d99SAlexei Starovoitov 		}						\
14551339d99SAlexei Starovoitov 		!!tl;						\
14651339d99SAlexei Starovoitov 	})
14751339d99SAlexei Starovoitov 
14851339d99SAlexei Starovoitov #define __local_trylock_irqsave(lock, flags)			\
14951339d99SAlexei Starovoitov 	({							\
15051339d99SAlexei Starovoitov 		local_trylock_t *tl;				\
15151339d99SAlexei Starovoitov 								\
15251339d99SAlexei Starovoitov 		local_irq_save(flags);				\
15351339d99SAlexei Starovoitov 		tl = this_cpu_ptr(lock);			\
15451339d99SAlexei Starovoitov 		if (READ_ONCE(tl->acquired)) {			\
15551339d99SAlexei Starovoitov 			local_irq_restore(flags);		\
15651339d99SAlexei Starovoitov 			tl = NULL;				\
15751339d99SAlexei Starovoitov 		} else {					\
15851339d99SAlexei Starovoitov 			WRITE_ONCE(tl->acquired, 1);		\
15951339d99SAlexei Starovoitov 			local_trylock_acquire(			\
16051339d99SAlexei Starovoitov 				(local_lock_t *)tl);		\
16151339d99SAlexei Starovoitov 		}						\
16251339d99SAlexei Starovoitov 		!!tl;						\
16351339d99SAlexei Starovoitov 	})
16451339d99SAlexei Starovoitov 
16551339d99SAlexei Starovoitov #define __local_lock_release(lock)					\
16651339d99SAlexei Starovoitov 	do {								\
16751339d99SAlexei Starovoitov 		local_trylock_t *tl;					\
16851339d99SAlexei Starovoitov 		local_lock_t *l;					\
16951339d99SAlexei Starovoitov 									\
17051339d99SAlexei Starovoitov 		l = (local_lock_t *)this_cpu_ptr(lock);			\
17151339d99SAlexei Starovoitov 		tl = (local_trylock_t *)l;				\
17251339d99SAlexei Starovoitov 		local_lock_release(l);					\
17351339d99SAlexei Starovoitov 		_Generic((lock),					\
174*82efd569SVlastimil Babka 			__percpu local_trylock_t *: ({			\
17551339d99SAlexei Starovoitov 				lockdep_assert(tl->acquired == 1);	\
17651339d99SAlexei Starovoitov 				WRITE_ONCE(tl->acquired, 0);		\
17751339d99SAlexei Starovoitov 			}),						\
178*82efd569SVlastimil Babka 			__percpu local_lock_t *: (void)0);		\
17991710728SThomas Gleixner 	} while (0)
18091710728SThomas Gleixner 
18191710728SThomas Gleixner #define __local_unlock(lock)					\
18291710728SThomas Gleixner 	do {							\
18351339d99SAlexei Starovoitov 		__local_lock_release(lock);			\
18491710728SThomas Gleixner 		preempt_enable();				\
18591710728SThomas Gleixner 	} while (0)
18691710728SThomas Gleixner 
18791710728SThomas Gleixner #define __local_unlock_irq(lock)				\
18891710728SThomas Gleixner 	do {							\
18951339d99SAlexei Starovoitov 		__local_lock_release(lock);			\
19091710728SThomas Gleixner 		local_irq_enable();				\
19191710728SThomas Gleixner 	} while (0)
19291710728SThomas Gleixner 
19391710728SThomas Gleixner #define __local_unlock_irqrestore(lock, flags)			\
19491710728SThomas Gleixner 	do {							\
19551339d99SAlexei Starovoitov 		__local_lock_release(lock);			\
19691710728SThomas Gleixner 		local_irq_restore(flags);			\
19791710728SThomas Gleixner 	} while (0)
198026659b9SThomas Gleixner 
199c5bcab75SSebastian Andrzej Siewior #define __local_lock_nested_bh(lock)				\
200c5bcab75SSebastian Andrzej Siewior 	do {							\
201c5bcab75SSebastian Andrzej Siewior 		lockdep_assert_in_softirq();			\
202c5bcab75SSebastian Andrzej Siewior 		local_lock_acquire(this_cpu_ptr(lock));	\
203c5bcab75SSebastian Andrzej Siewior 	} while (0)
204c5bcab75SSebastian Andrzej Siewior 
205c5bcab75SSebastian Andrzej Siewior #define __local_unlock_nested_bh(lock)				\
206c5bcab75SSebastian Andrzej Siewior 	local_lock_release(this_cpu_ptr(lock))
207c5bcab75SSebastian Andrzej Siewior 
208026659b9SThomas Gleixner #else /* !CONFIG_PREEMPT_RT */
209026659b9SThomas Gleixner 
210026659b9SThomas Gleixner /*
211026659b9SThomas Gleixner  * On PREEMPT_RT local_lock maps to a per CPU spinlock, which protects the
212026659b9SThomas Gleixner  * critical section while staying preemptible.
213026659b9SThomas Gleixner  */
214026659b9SThomas Gleixner typedef spinlock_t local_lock_t;
21551339d99SAlexei Starovoitov typedef spinlock_t local_trylock_t;
216026659b9SThomas Gleixner 
217026659b9SThomas Gleixner #define INIT_LOCAL_LOCK(lockname) __LOCAL_SPIN_LOCK_UNLOCKED((lockname))
21851339d99SAlexei Starovoitov #define INIT_LOCAL_TRYLOCK(lockname) __LOCAL_SPIN_LOCK_UNLOCKED((lockname))
219026659b9SThomas Gleixner 
220026659b9SThomas Gleixner #define __local_lock_init(l)					\
221026659b9SThomas Gleixner 	do {							\
222026659b9SThomas Gleixner 		local_spin_lock_init((l));			\
223026659b9SThomas Gleixner 	} while (0)
224026659b9SThomas Gleixner 
22551339d99SAlexei Starovoitov #define __local_trylock_init(l)			__local_lock_init(l)
22651339d99SAlexei Starovoitov 
227026659b9SThomas Gleixner #define __local_lock(__lock)					\
228026659b9SThomas Gleixner 	do {							\
229026659b9SThomas Gleixner 		migrate_disable();				\
230026659b9SThomas Gleixner 		spin_lock(this_cpu_ptr((__lock)));		\
231026659b9SThomas Gleixner 	} while (0)
232026659b9SThomas Gleixner 
233026659b9SThomas Gleixner #define __local_lock_irq(lock)			__local_lock(lock)
234026659b9SThomas Gleixner 
235026659b9SThomas Gleixner #define __local_lock_irqsave(lock, flags)			\
236026659b9SThomas Gleixner 	do {							\
237026659b9SThomas Gleixner 		typecheck(unsigned long, flags);		\
238026659b9SThomas Gleixner 		flags = 0;					\
239026659b9SThomas Gleixner 		__local_lock(lock);				\
240026659b9SThomas Gleixner 	} while (0)
241026659b9SThomas Gleixner 
242026659b9SThomas Gleixner #define __local_unlock(__lock)					\
243026659b9SThomas Gleixner 	do {							\
244026659b9SThomas Gleixner 		spin_unlock(this_cpu_ptr((__lock)));		\
245026659b9SThomas Gleixner 		migrate_enable();				\
246026659b9SThomas Gleixner 	} while (0)
247026659b9SThomas Gleixner 
248026659b9SThomas Gleixner #define __local_unlock_irq(lock)		__local_unlock(lock)
249026659b9SThomas Gleixner 
250026659b9SThomas Gleixner #define __local_unlock_irqrestore(lock, flags)	__local_unlock(lock)
251026659b9SThomas Gleixner 
252c5bcab75SSebastian Andrzej Siewior #define __local_lock_nested_bh(lock)				\
253c5bcab75SSebastian Andrzej Siewior do {								\
254c5bcab75SSebastian Andrzej Siewior 	lockdep_assert_in_softirq_func();			\
255c5bcab75SSebastian Andrzej Siewior 	spin_lock(this_cpu_ptr(lock));				\
256c5bcab75SSebastian Andrzej Siewior } while (0)
257c5bcab75SSebastian Andrzej Siewior 
258c5bcab75SSebastian Andrzej Siewior #define __local_unlock_nested_bh(lock)				\
259c5bcab75SSebastian Andrzej Siewior do {								\
260c5bcab75SSebastian Andrzej Siewior 	spin_unlock(this_cpu_ptr((lock)));			\
261c5bcab75SSebastian Andrzej Siewior } while (0)
262c5bcab75SSebastian Andrzej Siewior 
26351339d99SAlexei Starovoitov #define __local_trylock(lock)					\
2640aaddfb0SSebastian Andrzej Siewior 	({							\
2650aaddfb0SSebastian Andrzej Siewior 		int __locked;					\
2660aaddfb0SSebastian Andrzej Siewior 								\
2670aaddfb0SSebastian Andrzej Siewior 		if (in_nmi() | in_hardirq()) {			\
2680aaddfb0SSebastian Andrzej Siewior 			__locked = 0;				\
2690aaddfb0SSebastian Andrzej Siewior 		} else {					\
2700aaddfb0SSebastian Andrzej Siewior 			migrate_disable();			\
2710aaddfb0SSebastian Andrzej Siewior 			__locked = spin_trylock(this_cpu_ptr((lock)));	\
2720aaddfb0SSebastian Andrzej Siewior 			if (!__locked)				\
2730aaddfb0SSebastian Andrzej Siewior 				migrate_enable();		\
2740aaddfb0SSebastian Andrzej Siewior 		}						\
2750aaddfb0SSebastian Andrzej Siewior 		__locked;					\
2760aaddfb0SSebastian Andrzej Siewior 	})
2770aaddfb0SSebastian Andrzej Siewior 
27851339d99SAlexei Starovoitov #define __local_trylock_irqsave(lock, flags)			\
2790aaddfb0SSebastian Andrzej Siewior 	({							\
2800aaddfb0SSebastian Andrzej Siewior 		typecheck(unsigned long, flags);		\
2810aaddfb0SSebastian Andrzej Siewior 		flags = 0;					\
28251339d99SAlexei Starovoitov 		__local_trylock(lock);				\
2830aaddfb0SSebastian Andrzej Siewior 	})
2840aaddfb0SSebastian Andrzej Siewior 
285026659b9SThomas Gleixner #endif /* CONFIG_PREEMPT_RT */
286