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