| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
| /* | |
| * Queued spinlock defines | |
| * | |
| * This file contains macro definitions and functions shared between different | |
| * qspinlock slow path implementations. | |
| */ | |
| #ifndef __LINUX_QSPINLOCK_H | |
| #define __LINUX_QSPINLOCK_H | |
| #include <asm-generic/percpu.h> | |
| #include <linux/percpu-defs.h> | |
| #include <asm-generic/qspinlock.h> | |
| #include <asm-generic/mcs_spinlock.h> | |
| #define _Q_MAX_NODES 4 | |
| /* | |
| * The pending bit spinning loop count. | |
| * This heuristic is used to limit the number of lockword accesses | |
| * made by atomic_cond_read_relaxed when waiting for the lock to | |
| * transition out of the "== _Q_PENDING_VAL" state. We don't spin | |
| * indefinitely because there's no guarantee that we'll make forward | |
| * progress. | |
| */ | |
| #ifndef _Q_PENDING_LOOPS | |
| #define _Q_PENDING_LOOPS 1 | |
| #endif | |
| /* | |
| * On 64-bit architectures, the mcs_spinlock structure will be 16 bytes in | |
| * size and four of them will fit nicely in one 64-byte cacheline. For | |
| * pvqspinlock, however, we need more space for extra data. To accommodate | |
| * that, we insert two more long words to pad it up to 32 bytes. IOW, only | |
| * two of them can fit in a cacheline in this case. That is OK as it is rare | |
| * to have more than 2 levels of slowpath nesting in actual use. We don't | |
| * want to penalize pvqspinlocks to optimize for a rare case in native | |
| * qspinlocks. | |
| */ | |
| struct qnode { | |
| struct mcs_spinlock mcs; | |
| #ifdef CONFIG_PARAVIRT_SPINLOCKS | |
| long reserved[2]; | |
| #endif | |
| }; | |
| /* | |
| * We must be able to distinguish between no-tail and the tail at 0:0, | |
| * therefore increment the cpu number by one. | |
| */ | |
| static inline __pure u32 encode_tail(int cpu, int idx) | |
| { | |
| u32 tail; | |
| tail = (cpu + 1) << _Q_TAIL_CPU_OFFSET; | |
| tail |= idx << _Q_TAIL_IDX_OFFSET; /* assume < 4 */ | |
| return tail; | |
| } | |
| static inline __pure struct mcs_spinlock *decode_tail(u32 tail, | |
| struct qnode __percpu *qnodes) | |
| { | |
| int cpu = (tail >> _Q_TAIL_CPU_OFFSET) - 1; | |
| int idx = (tail & _Q_TAIL_IDX_MASK) >> _Q_TAIL_IDX_OFFSET; | |
| return per_cpu_ptr(&qnodes[idx].mcs, cpu); | |
| } | |
| static inline __pure | |
| struct mcs_spinlock *grab_mcs_node(struct mcs_spinlock *base, int idx) | |
| { | |
| return &((struct qnode *)base + idx)->mcs; | |
| } | |
| #define _Q_LOCKED_PENDING_MASK (_Q_LOCKED_MASK | _Q_PENDING_MASK) | |
| #if _Q_PENDING_BITS == 8 | |
| /** | |
| * clear_pending - clear the pending bit. | |
| * @lock: Pointer to queued spinlock structure | |
| * | |
| * *,1,* -> *,0,* | |
| */ | |
| static __always_inline void clear_pending(struct qspinlock *lock) | |
| { | |
| WRITE_ONCE(lock->pending, 0); | |
| } | |
| /** | |
| * clear_pending_set_locked - take ownership and clear the pending bit. | |
| * @lock: Pointer to queued spinlock structure | |
| * | |
| * *,1,0 -> *,0,1 | |
| * | |
| * Lock stealing is not allowed if this function is used. | |
| */ | |
| static __always_inline void clear_pending_set_locked(struct qspinlock *lock) | |
| { | |
| WRITE_ONCE(lock->locked_pending, _Q_LOCKED_VAL); | |
| } | |
| /* | |
| * xchg_tail - Put in the new queue tail code word & retrieve previous one | |
| * @lock : Pointer to queued spinlock structure | |
| * @tail : The new queue tail code word | |
| * Return: The previous queue tail code word | |
| * | |
| * xchg(lock, tail), which heads an address dependency | |
| * | |
| * p,*,* -> n,*,* ; prev = xchg(lock, node) | |
| */ | |
| static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail) | |
| { | |
| /* | |
| * We can use relaxed semantics since the caller ensures that the | |
| * MCS node is properly initialized before updating the tail. | |
| */ | |
| return (u32)xchg_relaxed(&lock->tail, | |
| tail >> _Q_TAIL_OFFSET) << _Q_TAIL_OFFSET; | |
| } | |
| #else /* _Q_PENDING_BITS == 8 */ | |
| /** | |
| * clear_pending - clear the pending bit. | |
| * @lock: Pointer to queued spinlock structure | |
| * | |
| * *,1,* -> *,0,* | |
| */ | |
| static __always_inline void clear_pending(struct qspinlock *lock) | |
| { | |
| atomic_andnot(_Q_PENDING_VAL, &lock->val); | |
| } | |
| /** | |
| * clear_pending_set_locked - take ownership and clear the pending bit. | |
| * @lock: Pointer to queued spinlock structure | |
| * | |
| * *,1,0 -> *,0,1 | |
| */ | |
| static __always_inline void clear_pending_set_locked(struct qspinlock *lock) | |
| { | |
| atomic_add(-_Q_PENDING_VAL + _Q_LOCKED_VAL, &lock->val); | |
| } | |
| /** | |
| * xchg_tail - Put in the new queue tail code word & retrieve previous one | |
| * @lock : Pointer to queued spinlock structure | |
| * @tail : The new queue tail code word | |
| * Return: The previous queue tail code word | |
| * | |
| * xchg(lock, tail) | |
| * | |
| * p,*,* -> n,*,* ; prev = xchg(lock, node) | |
| */ | |
| static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail) | |
| { | |
| u32 old, new; | |
| old = atomic_read(&lock->val); | |
| do { | |
| new = (old & _Q_LOCKED_PENDING_MASK) | tail; | |
| /* | |
| * We can use relaxed semantics since the caller ensures that | |
| * the MCS node is properly initialized before updating the | |
| * tail. | |
| */ | |
| } while (!atomic_try_cmpxchg_relaxed(&lock->val, &old, new)); | |
| return old; | |
| } | |
| #endif /* _Q_PENDING_BITS == 8 */ | |
| /** | |
| * queued_fetch_set_pending_acquire - fetch the whole lock value and set pending | |
| * @lock : Pointer to queued spinlock structure | |
| * Return: The previous lock value | |
| * | |
| * *,*,* -> *,1,* | |
| */ | |
| #ifndef queued_fetch_set_pending_acquire | |
| static __always_inline u32 queued_fetch_set_pending_acquire(struct qspinlock *lock) | |
| { | |
| return atomic_fetch_or_acquire(_Q_PENDING_VAL, &lock->val); | |
| } | |
| #endif | |
| /** | |
| * set_locked - Set the lock bit and own the lock | |
| * @lock: Pointer to queued spinlock structure | |
| * | |
| * *,*,0 -> *,0,1 | |
| */ | |
| static __always_inline void set_locked(struct qspinlock *lock) | |
| { | |
| WRITE_ONCE(lock->locked, _Q_LOCKED_VAL); | |
| } | |
| #endif /* __LINUX_QSPINLOCK_H */ |