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