xref: /linux/kernel/locking/qspinlock.h (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
1*ac08f68fSKumar Kartikeya Dwivedi /* SPDX-License-Identifier: GPL-2.0-or-later */
2*ac08f68fSKumar Kartikeya Dwivedi /*
3*ac08f68fSKumar Kartikeya Dwivedi  * Queued spinlock defines
4*ac08f68fSKumar Kartikeya Dwivedi  *
5*ac08f68fSKumar Kartikeya Dwivedi  * This file contains macro definitions and functions shared between different
6*ac08f68fSKumar Kartikeya Dwivedi  * qspinlock slow path implementations.
7*ac08f68fSKumar Kartikeya Dwivedi  */
8*ac08f68fSKumar Kartikeya Dwivedi #ifndef __LINUX_QSPINLOCK_H
9*ac08f68fSKumar Kartikeya Dwivedi #define __LINUX_QSPINLOCK_H
10*ac08f68fSKumar Kartikeya Dwivedi 
11*ac08f68fSKumar Kartikeya Dwivedi #include <asm-generic/percpu.h>
12*ac08f68fSKumar Kartikeya Dwivedi #include <linux/percpu-defs.h>
13*ac08f68fSKumar Kartikeya Dwivedi #include <asm-generic/qspinlock.h>
14*ac08f68fSKumar Kartikeya Dwivedi #include <asm-generic/mcs_spinlock.h>
15*ac08f68fSKumar Kartikeya Dwivedi 
16*ac08f68fSKumar Kartikeya Dwivedi #define _Q_MAX_NODES	4
17*ac08f68fSKumar Kartikeya Dwivedi 
18*ac08f68fSKumar Kartikeya Dwivedi /*
19*ac08f68fSKumar Kartikeya Dwivedi  * The pending bit spinning loop count.
20*ac08f68fSKumar Kartikeya Dwivedi  * This heuristic is used to limit the number of lockword accesses
21*ac08f68fSKumar Kartikeya Dwivedi  * made by atomic_cond_read_relaxed when waiting for the lock to
22*ac08f68fSKumar Kartikeya Dwivedi  * transition out of the "== _Q_PENDING_VAL" state. We don't spin
23*ac08f68fSKumar Kartikeya Dwivedi  * indefinitely because there's no guarantee that we'll make forward
24*ac08f68fSKumar Kartikeya Dwivedi  * progress.
25*ac08f68fSKumar Kartikeya Dwivedi  */
26*ac08f68fSKumar Kartikeya Dwivedi #ifndef _Q_PENDING_LOOPS
27*ac08f68fSKumar Kartikeya Dwivedi #define _Q_PENDING_LOOPS	1
28*ac08f68fSKumar Kartikeya Dwivedi #endif
29*ac08f68fSKumar Kartikeya Dwivedi 
30*ac08f68fSKumar Kartikeya Dwivedi /*
31*ac08f68fSKumar Kartikeya Dwivedi  * On 64-bit architectures, the mcs_spinlock structure will be 16 bytes in
32*ac08f68fSKumar Kartikeya Dwivedi  * size and four of them will fit nicely in one 64-byte cacheline. For
33*ac08f68fSKumar Kartikeya Dwivedi  * pvqspinlock, however, we need more space for extra data. To accommodate
34*ac08f68fSKumar Kartikeya Dwivedi  * that, we insert two more long words to pad it up to 32 bytes. IOW, only
35*ac08f68fSKumar Kartikeya Dwivedi  * two of them can fit in a cacheline in this case. That is OK as it is rare
36*ac08f68fSKumar Kartikeya Dwivedi  * to have more than 2 levels of slowpath nesting in actual use. We don't
37*ac08f68fSKumar Kartikeya Dwivedi  * want to penalize pvqspinlocks to optimize for a rare case in native
38*ac08f68fSKumar Kartikeya Dwivedi  * qspinlocks.
39*ac08f68fSKumar Kartikeya Dwivedi  */
40*ac08f68fSKumar Kartikeya Dwivedi struct qnode {
41*ac08f68fSKumar Kartikeya Dwivedi 	struct mcs_spinlock mcs;
42*ac08f68fSKumar Kartikeya Dwivedi #ifdef CONFIG_PARAVIRT_SPINLOCKS
43*ac08f68fSKumar Kartikeya Dwivedi 	long reserved[2];
44*ac08f68fSKumar Kartikeya Dwivedi #endif
45*ac08f68fSKumar Kartikeya Dwivedi };
46*ac08f68fSKumar Kartikeya Dwivedi 
47*ac08f68fSKumar Kartikeya Dwivedi /*
48*ac08f68fSKumar Kartikeya Dwivedi  * We must be able to distinguish between no-tail and the tail at 0:0,
49*ac08f68fSKumar Kartikeya Dwivedi  * therefore increment the cpu number by one.
50*ac08f68fSKumar Kartikeya Dwivedi  */
51*ac08f68fSKumar Kartikeya Dwivedi 
52*ac08f68fSKumar Kartikeya Dwivedi static inline __pure u32 encode_tail(int cpu, int idx)
53*ac08f68fSKumar Kartikeya Dwivedi {
54*ac08f68fSKumar Kartikeya Dwivedi 	u32 tail;
55*ac08f68fSKumar Kartikeya Dwivedi 
56*ac08f68fSKumar Kartikeya Dwivedi 	tail  = (cpu + 1) << _Q_TAIL_CPU_OFFSET;
57*ac08f68fSKumar Kartikeya Dwivedi 	tail |= idx << _Q_TAIL_IDX_OFFSET; /* assume < 4 */
58*ac08f68fSKumar Kartikeya Dwivedi 
59*ac08f68fSKumar Kartikeya Dwivedi 	return tail;
60*ac08f68fSKumar Kartikeya Dwivedi }
61*ac08f68fSKumar Kartikeya Dwivedi 
62*ac08f68fSKumar Kartikeya Dwivedi static inline __pure struct mcs_spinlock *decode_tail(u32 tail,
63*ac08f68fSKumar Kartikeya Dwivedi 						      struct qnode __percpu *qnodes)
64*ac08f68fSKumar Kartikeya Dwivedi {
65*ac08f68fSKumar Kartikeya Dwivedi 	int cpu = (tail >> _Q_TAIL_CPU_OFFSET) - 1;
66*ac08f68fSKumar Kartikeya Dwivedi 	int idx = (tail &  _Q_TAIL_IDX_MASK) >> _Q_TAIL_IDX_OFFSET;
67*ac08f68fSKumar Kartikeya Dwivedi 
68*ac08f68fSKumar Kartikeya Dwivedi 	return per_cpu_ptr(&qnodes[idx].mcs, cpu);
69*ac08f68fSKumar Kartikeya Dwivedi }
70*ac08f68fSKumar Kartikeya Dwivedi 
71*ac08f68fSKumar Kartikeya Dwivedi static inline __pure
72*ac08f68fSKumar Kartikeya Dwivedi struct mcs_spinlock *grab_mcs_node(struct mcs_spinlock *base, int idx)
73*ac08f68fSKumar Kartikeya Dwivedi {
74*ac08f68fSKumar Kartikeya Dwivedi 	return &((struct qnode *)base + idx)->mcs;
75*ac08f68fSKumar Kartikeya Dwivedi }
76*ac08f68fSKumar Kartikeya Dwivedi 
77*ac08f68fSKumar Kartikeya Dwivedi #define _Q_LOCKED_PENDING_MASK (_Q_LOCKED_MASK | _Q_PENDING_MASK)
78*ac08f68fSKumar Kartikeya Dwivedi 
79*ac08f68fSKumar Kartikeya Dwivedi #if _Q_PENDING_BITS == 8
80*ac08f68fSKumar Kartikeya Dwivedi /**
81*ac08f68fSKumar Kartikeya Dwivedi  * clear_pending - clear the pending bit.
82*ac08f68fSKumar Kartikeya Dwivedi  * @lock: Pointer to queued spinlock structure
83*ac08f68fSKumar Kartikeya Dwivedi  *
84*ac08f68fSKumar Kartikeya Dwivedi  * *,1,* -> *,0,*
85*ac08f68fSKumar Kartikeya Dwivedi  */
86*ac08f68fSKumar Kartikeya Dwivedi static __always_inline void clear_pending(struct qspinlock *lock)
87*ac08f68fSKumar Kartikeya Dwivedi {
88*ac08f68fSKumar Kartikeya Dwivedi 	WRITE_ONCE(lock->pending, 0);
89*ac08f68fSKumar Kartikeya Dwivedi }
90*ac08f68fSKumar Kartikeya Dwivedi 
91*ac08f68fSKumar Kartikeya Dwivedi /**
92*ac08f68fSKumar Kartikeya Dwivedi  * clear_pending_set_locked - take ownership and clear the pending bit.
93*ac08f68fSKumar Kartikeya Dwivedi  * @lock: Pointer to queued spinlock structure
94*ac08f68fSKumar Kartikeya Dwivedi  *
95*ac08f68fSKumar Kartikeya Dwivedi  * *,1,0 -> *,0,1
96*ac08f68fSKumar Kartikeya Dwivedi  *
97*ac08f68fSKumar Kartikeya Dwivedi  * Lock stealing is not allowed if this function is used.
98*ac08f68fSKumar Kartikeya Dwivedi  */
99*ac08f68fSKumar Kartikeya Dwivedi static __always_inline void clear_pending_set_locked(struct qspinlock *lock)
100*ac08f68fSKumar Kartikeya Dwivedi {
101*ac08f68fSKumar Kartikeya Dwivedi 	WRITE_ONCE(lock->locked_pending, _Q_LOCKED_VAL);
102*ac08f68fSKumar Kartikeya Dwivedi }
103*ac08f68fSKumar Kartikeya Dwivedi 
104*ac08f68fSKumar Kartikeya Dwivedi /*
105*ac08f68fSKumar Kartikeya Dwivedi  * xchg_tail - Put in the new queue tail code word & retrieve previous one
106*ac08f68fSKumar Kartikeya Dwivedi  * @lock : Pointer to queued spinlock structure
107*ac08f68fSKumar Kartikeya Dwivedi  * @tail : The new queue tail code word
108*ac08f68fSKumar Kartikeya Dwivedi  * Return: The previous queue tail code word
109*ac08f68fSKumar Kartikeya Dwivedi  *
110*ac08f68fSKumar Kartikeya Dwivedi  * xchg(lock, tail), which heads an address dependency
111*ac08f68fSKumar Kartikeya Dwivedi  *
112*ac08f68fSKumar Kartikeya Dwivedi  * p,*,* -> n,*,* ; prev = xchg(lock, node)
113*ac08f68fSKumar Kartikeya Dwivedi  */
114*ac08f68fSKumar Kartikeya Dwivedi static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail)
115*ac08f68fSKumar Kartikeya Dwivedi {
116*ac08f68fSKumar Kartikeya Dwivedi 	/*
117*ac08f68fSKumar Kartikeya Dwivedi 	 * We can use relaxed semantics since the caller ensures that the
118*ac08f68fSKumar Kartikeya Dwivedi 	 * MCS node is properly initialized before updating the tail.
119*ac08f68fSKumar Kartikeya Dwivedi 	 */
120*ac08f68fSKumar Kartikeya Dwivedi 	return (u32)xchg_relaxed(&lock->tail,
121*ac08f68fSKumar Kartikeya Dwivedi 				 tail >> _Q_TAIL_OFFSET) << _Q_TAIL_OFFSET;
122*ac08f68fSKumar Kartikeya Dwivedi }
123*ac08f68fSKumar Kartikeya Dwivedi 
124*ac08f68fSKumar Kartikeya Dwivedi #else /* _Q_PENDING_BITS == 8 */
125*ac08f68fSKumar Kartikeya Dwivedi 
126*ac08f68fSKumar Kartikeya Dwivedi /**
127*ac08f68fSKumar Kartikeya Dwivedi  * clear_pending - clear the pending bit.
128*ac08f68fSKumar Kartikeya Dwivedi  * @lock: Pointer to queued spinlock structure
129*ac08f68fSKumar Kartikeya Dwivedi  *
130*ac08f68fSKumar Kartikeya Dwivedi  * *,1,* -> *,0,*
131*ac08f68fSKumar Kartikeya Dwivedi  */
132*ac08f68fSKumar Kartikeya Dwivedi static __always_inline void clear_pending(struct qspinlock *lock)
133*ac08f68fSKumar Kartikeya Dwivedi {
134*ac08f68fSKumar Kartikeya Dwivedi 	atomic_andnot(_Q_PENDING_VAL, &lock->val);
135*ac08f68fSKumar Kartikeya Dwivedi }
136*ac08f68fSKumar Kartikeya Dwivedi 
137*ac08f68fSKumar Kartikeya Dwivedi /**
138*ac08f68fSKumar Kartikeya Dwivedi  * clear_pending_set_locked - take ownership and clear the pending bit.
139*ac08f68fSKumar Kartikeya Dwivedi  * @lock: Pointer to queued spinlock structure
140*ac08f68fSKumar Kartikeya Dwivedi  *
141*ac08f68fSKumar Kartikeya Dwivedi  * *,1,0 -> *,0,1
142*ac08f68fSKumar Kartikeya Dwivedi  */
143*ac08f68fSKumar Kartikeya Dwivedi static __always_inline void clear_pending_set_locked(struct qspinlock *lock)
144*ac08f68fSKumar Kartikeya Dwivedi {
145*ac08f68fSKumar Kartikeya Dwivedi 	atomic_add(-_Q_PENDING_VAL + _Q_LOCKED_VAL, &lock->val);
146*ac08f68fSKumar Kartikeya Dwivedi }
147*ac08f68fSKumar Kartikeya Dwivedi 
148*ac08f68fSKumar Kartikeya Dwivedi /**
149*ac08f68fSKumar Kartikeya Dwivedi  * xchg_tail - Put in the new queue tail code word & retrieve previous one
150*ac08f68fSKumar Kartikeya Dwivedi  * @lock : Pointer to queued spinlock structure
151*ac08f68fSKumar Kartikeya Dwivedi  * @tail : The new queue tail code word
152*ac08f68fSKumar Kartikeya Dwivedi  * Return: The previous queue tail code word
153*ac08f68fSKumar Kartikeya Dwivedi  *
154*ac08f68fSKumar Kartikeya Dwivedi  * xchg(lock, tail)
155*ac08f68fSKumar Kartikeya Dwivedi  *
156*ac08f68fSKumar Kartikeya Dwivedi  * p,*,* -> n,*,* ; prev = xchg(lock, node)
157*ac08f68fSKumar Kartikeya Dwivedi  */
158*ac08f68fSKumar Kartikeya Dwivedi static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail)
159*ac08f68fSKumar Kartikeya Dwivedi {
160*ac08f68fSKumar Kartikeya Dwivedi 	u32 old, new;
161*ac08f68fSKumar Kartikeya Dwivedi 
162*ac08f68fSKumar Kartikeya Dwivedi 	old = atomic_read(&lock->val);
163*ac08f68fSKumar Kartikeya Dwivedi 	do {
164*ac08f68fSKumar Kartikeya Dwivedi 		new = (old & _Q_LOCKED_PENDING_MASK) | tail;
165*ac08f68fSKumar Kartikeya Dwivedi 		/*
166*ac08f68fSKumar Kartikeya Dwivedi 		 * We can use relaxed semantics since the caller ensures that
167*ac08f68fSKumar Kartikeya Dwivedi 		 * the MCS node is properly initialized before updating the
168*ac08f68fSKumar Kartikeya Dwivedi 		 * tail.
169*ac08f68fSKumar Kartikeya Dwivedi 		 */
170*ac08f68fSKumar Kartikeya Dwivedi 	} while (!atomic_try_cmpxchg_relaxed(&lock->val, &old, new));
171*ac08f68fSKumar Kartikeya Dwivedi 
172*ac08f68fSKumar Kartikeya Dwivedi 	return old;
173*ac08f68fSKumar Kartikeya Dwivedi }
174*ac08f68fSKumar Kartikeya Dwivedi #endif /* _Q_PENDING_BITS == 8 */
175*ac08f68fSKumar Kartikeya Dwivedi 
176*ac08f68fSKumar Kartikeya Dwivedi /**
177*ac08f68fSKumar Kartikeya Dwivedi  * queued_fetch_set_pending_acquire - fetch the whole lock value and set pending
178*ac08f68fSKumar Kartikeya Dwivedi  * @lock : Pointer to queued spinlock structure
179*ac08f68fSKumar Kartikeya Dwivedi  * Return: The previous lock value
180*ac08f68fSKumar Kartikeya Dwivedi  *
181*ac08f68fSKumar Kartikeya Dwivedi  * *,*,* -> *,1,*
182*ac08f68fSKumar Kartikeya Dwivedi  */
183*ac08f68fSKumar Kartikeya Dwivedi #ifndef queued_fetch_set_pending_acquire
184*ac08f68fSKumar Kartikeya Dwivedi static __always_inline u32 queued_fetch_set_pending_acquire(struct qspinlock *lock)
185*ac08f68fSKumar Kartikeya Dwivedi {
186*ac08f68fSKumar Kartikeya Dwivedi 	return atomic_fetch_or_acquire(_Q_PENDING_VAL, &lock->val);
187*ac08f68fSKumar Kartikeya Dwivedi }
188*ac08f68fSKumar Kartikeya Dwivedi #endif
189*ac08f68fSKumar Kartikeya Dwivedi 
190*ac08f68fSKumar Kartikeya Dwivedi /**
191*ac08f68fSKumar Kartikeya Dwivedi  * set_locked - Set the lock bit and own the lock
192*ac08f68fSKumar Kartikeya Dwivedi  * @lock: Pointer to queued spinlock structure
193*ac08f68fSKumar Kartikeya Dwivedi  *
194*ac08f68fSKumar Kartikeya Dwivedi  * *,*,0 -> *,0,1
195*ac08f68fSKumar Kartikeya Dwivedi  */
196*ac08f68fSKumar Kartikeya Dwivedi static __always_inline void set_locked(struct qspinlock *lock)
197*ac08f68fSKumar Kartikeya Dwivedi {
198*ac08f68fSKumar Kartikeya Dwivedi 	WRITE_ONCE(lock->locked, _Q_LOCKED_VAL);
199*ac08f68fSKumar Kartikeya Dwivedi }
200*ac08f68fSKumar Kartikeya Dwivedi 
201*ac08f68fSKumar Kartikeya Dwivedi #endif /* __LINUX_QSPINLOCK_H */
202